diff --git a/cosiap_api/administracion/views.py b/cosiap_api/administracion/views.py
index 92d87567395de02e1834babf2db436edfec94b60..2513f625bc3e2008c16f4f867f6086faa95f0fa1 100644
--- a/cosiap_api/administracion/views.py
+++ b/cosiap_api/administracion/views.py
@@ -13,7 +13,7 @@ class AbrirCerrarConvocatoria(BasePermissionAPIView):
APIView con la lógica para abrir la convocatoria.
'''
- permission_classes_list = [IsAuthenticated, es_admin]
+ permission_classes_list = [IsAuthenticated]
permission_classes_update = [IsAuthenticated, es_admin]
def get(self, request, *args, **kwargs):
diff --git a/cosiap_api/static/images/Admin_Home.jpeg b/cosiap_api/static/images/Admin_Home.jpeg
new file mode 100644
index 0000000000000000000000000000000000000000..0c824d60b22ec9e9cf31ad8b42f8833d947075cb
Binary files /dev/null and b/cosiap_api/static/images/Admin_Home.jpeg differ
diff --git a/cosiap_api/static/images/Solicitante_Home.webp b/cosiap_api/static/images/Solicitante_Home.webp
new file mode 100644
index 0000000000000000000000000000000000000000..7f99ac6fd9cf674d696c4e735b94f3e31bc0f725
Binary files /dev/null and b/cosiap_api/static/images/Solicitante_Home.webp differ
diff --git a/cosiap_api/static/images/logo.jpg b/cosiap_api/static/images/logo.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..4fa94824569f6a8b813e57b5f8f9d5ec318e825f
Binary files /dev/null and b/cosiap_api/static/images/logo.jpg differ
diff --git a/cosiap_frontend/src/App.css b/cosiap_frontend/src/App.css
index b090b5c59c21e10f39c27f2fb08ea721a47016e8..b38c3e9d95644db0b73724fff6c3941552f80bbc 100644
--- a/cosiap_frontend/src/App.css
+++ b/cosiap_frontend/src/App.css
@@ -1110,4 +1110,124 @@ th, td {
justify-content: center;
gap: 8px;
transition: background-color 0.3s ease;
+}
+
+.toggle-switch {
+ position: relative;
+ display: inline-block;
+ width: 50px;
+ height: 24px;
+}
+
+.toggle-switch input {
+ opacity: 0;
+ width: 0;
+ height: 0;
+}
+
+.toggle-switch .slider {
+ position: absolute;
+ cursor: pointer;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: #ccc;
+ transition: 0.4s;
+ border-radius: 24px;
+}
+
+.toggle-switch .slider:before {
+ position: absolute;
+ content: "";
+ height: 18px;
+ width: 18px;
+ left: 4px;
+ bottom: 3px;
+ background-color: white;
+ transition: 0.4s;
+ border-radius: 50%;
+}
+
+.toggle-switch input:checked + .slider {
+ background-color: #4caf50;
+}
+
+.toggle-switch input:checked + .slider:before {
+ transform: translateX(26px);
+}
+
+.toggle-switch .slider:active:before {
+ width: 22px;
+}
+
+.home-solicitante-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 20px;
+ background-color: #f9e8d7;
+ border-radius: 16px;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+}
+
+.home-admin-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 20px;
+ background-color: #e4896f;
+ border-radius: 16px;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+}
+
+.info-card {
+ flex: 1;
+ padding: 20px;
+ max-width: 50%;
+ justify-content: center;
+}
+
+.title {
+ color: #8d2f2f;
+ font-size: 24px;
+ font-weight: bold;
+ margin-bottom: 16px;
+}
+
+.home-ul {
+ list-style-type: disc;
+ margin-top: 12px;
+ padding-left: 40px;
+}
+
+.home-ul li {
+ margin-bottom: 8px;
+ font-size: 16px;
+ line-height: 1.5;
+}
+
+.start-button {
+ background-color: #8d2f2f;
+ color: white;
+ border: none;
+ padding: 10px 20px;
+ border-radius: 8px;
+ font-size: 16px;
+ margin-top: 16px;
+ cursor: pointer;
+}
+
+.home-image-container {
+ flex: 1;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 20px;
+}
+
+.home-image-container img {
+ max-width: 100%;
+ height: auto;
+ border-radius: 16px;
}
\ No newline at end of file
diff --git a/cosiap_frontend/src/api.js b/cosiap_frontend/src/api.js
index 3a680a4215701d69336ea038825d395b62e29a66..4b9837076f0312f54da6b432dfdf58e2432d457b 100644
--- a/cosiap_frontend/src/api.js
+++ b/cosiap_frontend/src/api.js
@@ -191,6 +191,10 @@ const api = {
updateMinuta: (data) => ax.put('api/plantillas/minuta/',data),
getConvenio: () => ax.get('api/plantillas/convenio'),
updateConvenio: (data) => ax.put('api/plantillas/convenio/',data),
+ },
+ convocatoria: {
+ get: () => ax.get('api/administracion/convocatoria/'),
+ put: (data) => ax.put('api/administracion/convocatoria/', data)
}
};
diff --git a/cosiap_frontend/src/components/Inicio.jsx b/cosiap_frontend/src/components/Inicio.jsx
index 16cacd1ea86cbfb92e04ac975d25cf57c0bfd76c..64541c48eb29971998fe8db77b4bc351fd1675bb 100644
--- a/cosiap_frontend/src/components/Inicio.jsx
+++ b/cosiap_frontend/src/components/Inicio.jsx
@@ -1,10 +1,63 @@
+import { useAutenticacion } from "@/components/common/utility/Autenticador";
+import MainContainer from "@/components/common/utility/MainContainer";
+import '@/App.css'
+import { useNavigate } from 'react-router-dom';
export default function Inicio() {
-
+ const { isAdmin } = useAutenticacion();
+ const navigate = useNavigate();
return (
- <>
-
- >
+
+ {isAdmin ? (
+
+
+
SISTEMA COSIAP
+
+ Como Administrador podrás:
+
+
+
+ - Gestionar las modalidades de apoyo pertenecientes a una convocatoria.
+ - Gestionar usuarios (Administradores y Solicitantes)
+ - Gestionar solicitudes de apoyos.
+ - Gestionar reportes de solicitudes (filtros y configuraciones personalizadas)
+ - Gestionar formatos personalizados para la realizacion de solicitudes.
+
+
+
+
+
+
+
+
+ ) : (
+
+
+
SISTEMA COSIAP
+
+ Como solicitante podrás aspirar a un apoyo económico relacionado con
+ una modalidad vigente en el periodo actual.
+
+
+
+ - Existe la posibilidad de que tu solicitud sea rechazada.
+ - No puedes adquirir más de dos apoyos en menos de un año.
+
+
+
+
+
+
+
+
+ )}
+
);
-}
\ No newline at end of file
+}
diff --git a/cosiap_frontend/src/components/common/layouts/LayoutBaseNavigation.jsx b/cosiap_frontend/src/components/common/layouts/LayoutBaseNavigation.jsx
index fa94ef28e4ba933d1bec62b9454f7ff61cc2f05b..0db9c6f7a5f388f0d3c0873619945f01ac60824e 100644
--- a/cosiap_frontend/src/components/common/layouts/LayoutBaseNavigation.jsx
+++ b/cosiap_frontend/src/components/common/layouts/LayoutBaseNavigation.jsx
@@ -6,75 +6,31 @@ import SettingsToggle from "@/components/common/layouts/LayoutsNavigation/Settin
import Settings from "@/components/common/layouts/LayoutsNavigation/Settings";
import { useAutenticacion } from "@/components/common/utility/Autenticador";
-const linksItemsAdmin=[
- {
- nameIcon: 'library_books',
- nameItem: 'Modalidades',
- navigate: '/modalidades',
- isSelected: false
- },
- {
- nameIcon: 'group',
- nameItem: 'Usuarios',
- navigate: '/usuarios',
- isSelected: false
- },
- {
- nameIcon: 'list_alt',
- nameItem: 'Solicitudes',
- navigate: '/solicitudes',
- isSelected: false
- },
- {
- nameIcon: 'description',
- nameItem: 'Formatos',
- navigate: '/formatos',
- isSelected: false
- },
+const linksItemsAdmin = [
+ { nameIcon: 'library_books', nameItem: 'Modalidades', navigate: '/modalidades', isSelected: false },
+ { nameIcon: 'group', nameItem: 'Usuarios', navigate: '/usuarios', isSelected: false },
+ { nameIcon: 'list_alt', nameItem: 'Solicitudes', navigate: '/solicitudes', isSelected: false },
+ { nameIcon: 'description', nameItem: 'Formatos', navigate: '/formatos', isSelected: false },
];
const linksItemsSolicitante = [
- {
- nameIcon: 'library_books',
- nameItem: 'Modalidades',
- navigate: '/modalidades',
- isSelected: false
+ { nameIcon: 'library_books', nameItem: 'Modalidades', navigate: '/modalidades', isSelected: false },
+ { nameIcon: 'history', nameItem: 'Historial', navigate: '/historial', isSelected: false },
+ {
+ nameIcon: 'help', nameItem: 'Ayuda', isSelected: false,
+ subMenu: [{ nameItem: 'Manual', isSelected: false, navigate: '/ayuda/manual' }]
},
- {
- nameIcon: 'history',
- nameItem: 'Historial',
- navigate: '/historial',
- isSelected: false
- },
- {
- nameIcon: 'help',
- nameItem: 'Ayuda',
- isSelected: false,
- subMenu: [
- {
- nameItem: 'Manual',
- isSelected: false,
- navigate: '/ayuda/manual',
- }
- ]
- },
- {
- nameIcon: 'account_circle',
- nameItem: 'Perfil',
- navigate: '/perfil',
- isSelected: false
- }
+ { nameIcon: 'account_circle', nameItem: 'Perfil', navigate: '/perfil', isSelected: false },
];
export default function LayoutBaseNavigation() {
const [viewMenu, setViewMenu] = useState(false);
const [selectedNav, setSelectedNav] = useState('vertical');
const [viewSettings, setViewSettings] = useState(false);
- const {isAdmin} = useAutenticacion();
- const [links, setLinks] = useState(isAdmin != undefined && isAdmin ? linksItemsAdmin : linksItemsSolicitante);
+ const { isAdmin } = useAutenticacion();
+ const [links, setLinks] = useState(isAdmin ? linksItemsAdmin : linksItemsSolicitante);
const settingsRef = useRef();
const settingsToggleRef = useRef();
-
const location = useLocation();
const [sectionURL, setSectionURL] = useState("");
@@ -88,69 +44,47 @@ export default function LayoutBaseNavigation() {
useEffect(() => {
const savedNavStyle = localStorage.getItem('selectedNav');
- if (savedNavStyle) {
- setSelectedNav(savedNavStyle);
- }
+ if (savedNavStyle) setSelectedNav(savedNavStyle);
}, []);
useEffect(() => {
localStorage.setItem('selectedNav', selectedNav);
}, [selectedNav]);
- useEffect(() => {
- const updatedLinks = links.map(link => {
- const isSelected = link.nameItem.toLowerCase() === sectionURL.toLowerCase() ||
- link.navigate?.split('/').pop().toLowerCase() === sectionURL.toLowerCase();
-
- const updatedSubMenu = link.subMenu?.map(sub => ({
- ...sub,
- isSelected: sub.nameItem.toLowerCase() === sectionURL.toLowerCase() ||
- sub.navigate?.split('/').pop().toLowerCase() === sectionURL.toLowerCase()
- }));
-
- const isParentSelected = updatedSubMenu?.some(sub => sub.isSelected);
-
- return {
- ...link,
- isSelected: isSelected || isParentSelected,
- ...(updatedSubMenu && { subMenu: updatedSubMenu })
- };
- });
-
- setLinks(updatedLinks);
- }, [sectionURL]);
-
useEffect(() => {
function handleClickOutside(event) {
- if (settingsRef.current && !settingsRef.current.contains(event.target) &&
- settingsToggleRef.current && !settingsToggleRef.current.contains(event.target)) {
+ if (
+ settingsRef.current && !settingsRef.current.contains(event.target) &&
+ settingsToggleRef.current && !settingsToggleRef.current.contains(event.target)
+ ) {
setViewSettings(false);
}
}
document.addEventListener("mousedown", handleClickOutside);
- return () => {
- document.removeEventListener("mousedown", handleClickOutside);
- };
+ return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
return (
{selectedNav === 'horizontal' && (
-
+
)}
{selectedNav === 'vertical' && (
-
+
)}
setViewSettings(!viewSettings)}
>
+
{viewSettings && (
-
+
+
+
)}
diff --git a/cosiap_frontend/src/components/common/layouts/LayoutsNavigation/Settings.jsx b/cosiap_frontend/src/components/common/layouts/LayoutsNavigation/Settings.jsx
index 2047f000e1b1a3b6f6ba57d54946282717f29286..0b589ad48644221bc3c9877e4e09c732709bf4bc 100644
--- a/cosiap_frontend/src/components/common/layouts/LayoutsNavigation/Settings.jsx
+++ b/cosiap_frontend/src/components/common/layouts/LayoutsNavigation/Settings.jsx
@@ -1,4 +1,35 @@
+import '@/App.css'
+import api from '@/api';
+import { useEffect, useState } from "react";
+import { useAutenticacion } from "@/components/common/utility/Autenticador";
+
export default function Settings( {settingsRef, selectedNav, setSelectedNav} ){
+
+ const [convocatoria, setConvocatoria] = useState(true);
+ const { isAdmin } = useAutenticacion();
+
+ useEffect(() => {
+ const fetchConvocatoria = async () => {
+ try {
+ const response = await api.convocatoria.get();
+ setConvocatoria(response.data.convocatoria_is_open);
+ } catch (error) {
+ setConvocatoria(false);
+ }
+ };
+ fetchConvocatoria();
+ }, []);
+
+ const handleConvocatoria = async () => {
+ try {
+ const nuevaConvocatoria = !convocatoria
+ await api.convocatoria.put({'nuevo_estado': nuevaConvocatoria});
+ setConvocatoria(nuevaConvocatoria);
+ } catch (error) {
+ setConvocatoria(false);
+ }
+ };
+
return (
@@ -24,6 +55,27 @@ export default function Settings( {settingsRef, selectedNav, setSelectedNav} ){
Horizontal
+
+ {/* Renderiza el switch solo si es administrador */}
+ {isAdmin && (
+
+
+
+ )}
+
diff --git a/cosiap_frontend/src/components/modalidades/ListaModalidades.jsx b/cosiap_frontend/src/components/modalidades/ListaModalidades.jsx
index 059fcc71f79292d64f50a0cc91e70c43da55e4fb..8681a724a3af5c2d36cbc99708507891514897e8 100644
--- a/cosiap_frontend/src/components/modalidades/ListaModalidades.jsx
+++ b/cosiap_frontend/src/components/modalidades/ListaModalidades.jsx
@@ -16,6 +16,7 @@ export default function ListaModalidades({
}) {
const [modalidades, setModalidades] = useState([]);
const navigate = useNavigate();
+ const [convocatoria, setConvocatoria] = useState(true);
const obtenerModalidades = async () => {
setViewPageLoader(true);
@@ -35,6 +36,18 @@ export default function ListaModalidades({
obtenerModalidades();
}, []);
+ useEffect(() => {
+ const fetchConvocatoria = async () => {
+ try {
+ const response = await api.convocatoria.get();
+ setConvocatoria(response.data.convocatoria_is_open);
+ } catch (error) {
+ setConvocatoria(false);
+ }
+ };
+ fetchConvocatoria();
+}, []);
+
return (
{/* Botón centrado debajo del título */}
@@ -65,19 +78,23 @@ export default function ListaModalidades({
>
-
-
+ {/* Botón de Editar o Solicitar basado en las condiciones */}
+ {isAdmin ? (
+
+ ) : convocatoria ? (
+
+ ) : null}
+
);
})
diff --git a/cosiap_frontend/src/components/modalidades/Modalidad.jsx b/cosiap_frontend/src/components/modalidades/Modalidad.jsx
index f8ae535e699f5d944c8f404ff1aa99723be84630..033d5fb8639e3529fce851ce3dcb2839f4fd620f 100644
--- a/cosiap_frontend/src/components/modalidades/Modalidad.jsx
+++ b/cosiap_frontend/src/components/modalidades/Modalidad.jsx
@@ -23,6 +23,7 @@ const SolicitarModalidad = () => {
const [respuesta, setRespuesta] = useState([]);
const [alertMessage, setAlertMessage] = useState(''); // Estado para el mensaje de alerta
const [isSuccess, setIsSuccess] = useState(false);
+ const [convocatoria, setConvocatoria] = useState(true);
const navigate = useNavigate();
const obtenerModalidad = async () => {
@@ -68,6 +69,21 @@ const SolicitarModalidad = () => {
}
};
+ useEffect(() => {
+ const fetchConvocatoria = async () => {
+ try {
+ const response = await api.convocatoria.get();
+ setConvocatoria(response.data.convocatoria_is_open);
+ if (response.data.convocatoria_is_open === false){
+ navigate('/404');
+ }
+ } catch (error) {
+ setConvocatoria(false);
+ }
+ };
+ fetchConvocatoria();
+ }, []);
+
useEffect(() => {
obtenerModalidad();
@@ -262,7 +278,7 @@ const SolicitarModalidad = () => {
return (
- modalidad && (
+ (modalidad && convocatoria) && (
{/* Alerta */}