diff --git a/cosiap_frontend/src/api.js b/cosiap_frontend/src/api.js index 9a2a5ed1c05a4c190988509430d91bfce494316f..824fdc9ecdce79c7615d7f7448bba0d9cd10c5c5 100644 --- a/cosiap_frontend/src/api.js +++ b/cosiap_frontend/src/api.js @@ -56,6 +56,9 @@ const api = { restablecerPassword: (data) => ax.post(`api/usuarios/restablecer-password/`, data), nuevaPassword: (uidb64, token, data) => ax.post(`api/usuarios/nueva-password/${uidb64}/${token}/`, data), + getId: () => ax.get('api/usuarios/uid/'), + getIsCompleteData: () => ax.get('api/usuarios/user-complete-data/'), + administradores: { get: () => ax.get('api/usuarios/administradores'), post: (data) => ax.post('api/usuarios/administradores/', data), diff --git a/cosiap_frontend/src/components/common/layouts/LayoutBaseNavigation.jsx b/cosiap_frontend/src/components/common/layouts/LayoutBaseNavigation.jsx index da255dd87a27500fd9e67a11aac9ca431a4fae8c..d0e3cab6fc30781220291706a7ac958294e2391e 100644 --- a/cosiap_frontend/src/components/common/layouts/LayoutBaseNavigation.jsx +++ b/cosiap_frontend/src/components/common/layouts/LayoutBaseNavigation.jsx @@ -4,6 +4,28 @@ import Navbar from "@/components/common/layouts/LayoutsNavigation/Navbar/Navbar" import Sidebar from "@/components/common/layouts/LayoutsNavigation/Sidebar/Sidebar"; import SettingsToggle from "@/components/common/layouts/LayoutsNavigation/SettingsToggle"; 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 + }, +]; const linksItemsSolicitante = [ { @@ -48,14 +70,18 @@ export default function LayoutBaseNavigation() { const [viewMenu, setViewMenu] = useState(false); const [selectedNav, setSelectedNav] = useState('vertical'); const [viewSettings, setViewSettings] = useState(false); - const [links, setLinks] = useState([...linksItemsSolicitante]); - + const {isAdmin} = useAutenticacion(); + const [links, setLinks] = useState(isAdmin != undefined && isAdmin ? linksItemsAdmin : linksItemsSolicitante); const settingsRef = useRef(); const settingsToggleRef = useRef(); const location = useLocation(); const [sectionURL, setSectionURL] = useState(""); + useEffect(() => { + setLinks(isAdmin ? linksItemsAdmin : linksItemsSolicitante); + }, [isAdmin]); + useEffect(() => { setSectionURL(location.pathname.split('/').filter(Boolean).pop()); }, [location]); diff --git a/cosiap_frontend/src/components/common/utility/Autenticador.jsx b/cosiap_frontend/src/components/common/utility/Autenticador.jsx index 0a9d8e14e5b58011bce5f658d65f367db16fdc6b..22f83e33955948f5546062c197daf9dcc0c343a5 100644 --- a/cosiap_frontend/src/components/common/utility/Autenticador.jsx +++ b/cosiap_frontend/src/components/common/utility/Autenticador.jsx @@ -4,6 +4,7 @@ import { useContext, createContext, useState, + useRef, } from "react"; import api from '@/api' import PropTypes from 'prop-types'; @@ -23,7 +24,9 @@ export const useAutenticacion = () => { export const Autenticador = ({ setViewPageLoader, children }) => { const [token, setToken] = useState(); - const [isAdmin, setIsAdmin] = useState(false); + const [isAdmin, setIsAdmin] = useState(); + const [uid, setUid] = useState(); + const interceptadoresRef = useRef({}); useEffect(() => { const buscarUsuario = async () => { @@ -34,7 +37,9 @@ export const Autenticador = ({ setViewPageLoader, children }) => { configurarInterceptors(response.data.access); const responseAd = await api.usuarios.admin.is_admin(); setIsAdmin(responseAd.data.user_is_admin); - console.log(isAdmin) + //Extraemos el id + const responseUid = await api.usuarios.getId(); + setUid(responseUid.data.user_id); } catch { setToken(null); setIsAdmin(false); @@ -45,46 +50,56 @@ export const Autenticador = ({ setViewPageLoader, children }) => { }, []); const configurarInterceptors = (token) => { - const interceptadorAccess = api.axios.interceptors.request.use((config) => { - config.headers.Authorization = !config.__retry && token ? `Bearer ${token}` : config.headers.Authorization; - return config; - }); + // Si ya existen interceptores, primero eliminarlos + if (interceptadoresRef.current.requestInterceptor) { + api.axios.interceptors.request.eject(interceptadoresRef.current.requestInterceptor); + } + if (interceptadoresRef.current.responseInterceptor) { + api.axios.interceptors.response.eject(interceptadoresRef.current.responseInterceptor); + } - const interceptadorRefresh = api.axios.interceptors.response.use( - (response) => response, async (error) => { - const requestOriginal = error.config; - - if (error.response.status === 403 && error.response.data.message === 'Unauthorized') { - try { - const response = await api.usuarios.token.refresh(); - setToken(response.data.access); - requestOriginal.headers.Authorization = `Bearer ${response.data.access}`; - requestOriginal.__retry = true; - const responseAd = await api.usuarios.admin.is_admin(); - setIsAdmin(responseAd.data.user_is_admin); + if (token) { + const requestInterceptor = api.axios.interceptors.request.use((config) => { + config.headers.Authorization = !config.__retry && token ? `Bearer ${token}` : config.headers.Authorization; + return config; + }); - return api(requestOriginal); - } catch { - setToken(null); - setIsAdmin(false); + const responseInterceptor = api.axios.interceptors.response.use( + (response) => response, async (error) => { + const requestOriginal = error.config; + + if (error.response.status === 403 && error.response.data.message === 'Unauthorized') { + try { + const response = await api.usuarios.token.refresh(); + setToken(response.data.access); + requestOriginal.headers.Authorization = `Bearer ${response.data.access}`; + requestOriginal.__retry = true; + const responseAd = await api.usuarios.admin.is_admin(); + setIsAdmin(responseAd.data.user_is_admin); + //Extraemos el id + const responseUid = await api.usuarios.getId(); + setUid(responseUid.data.user_id); + return api(requestOriginal); + } catch { + setToken(null); + setIsAdmin(undefined); + setUid(undefined); + } } - } - return Promise.reject(error); - }, - ); + return Promise.reject(error); + }, + ); - // Eject interceptors on component unmount - return () => { - api.axios.interceptors.request.eject(interceptadorAccess); - api.axios.interceptors.response.eject(interceptadorRefresh); - }; + // Guardar los identificadores de interceptores + interceptadoresRef.current.requestInterceptor = requestInterceptor; + interceptadoresRef.current.responseInterceptor = responseInterceptor; + } }; useLayoutEffect(() => { if (token) { - const ejectInterceptors = configurarInterceptors(token); - return () => ejectInterceptors(); + configurarInterceptors(token); } }, [token]); @@ -92,21 +107,27 @@ export const Autenticador = ({ setViewPageLoader, children }) => { setViewPageLoader(true); try { const response = await api.usuarios.token.logout(); - console.log('Logout succesful ', response); + console.log('Logout successful', response); + // Eliminar los interceptores configurados para el token actual + configurarInterceptors(token); + // Limpiar el estado de autenticación setToken(null); + setIsAdmin(undefined); + setUid(undefined); } catch (error) { - console.error('Logout unsuccesful ', error); + console.error('Logout unsuccessful', error); } setViewPageLoader(false); }; return ( - + {children} ); }; Autenticador.propTypes = { - children: PropTypes.node, + setViewPageLoader: PropTypes.func.isRequired, + children: PropTypes.node.isRequired, }; diff --git a/cosiap_frontend/src/components/common/utility/LoginRequiredRoute.jsx b/cosiap_frontend/src/components/common/utility/LoginRequiredRoute.jsx index 33607d6acda8f2e8f130cf4e9fd66f7a4924cec9..3ff2a8d895bfced329152b9bdd28ac3dea797583 100644 --- a/cosiap_frontend/src/components/common/utility/LoginRequiredRoute.jsx +++ b/cosiap_frontend/src/components/common/utility/LoginRequiredRoute.jsx @@ -4,8 +4,7 @@ import { Navigate, Outlet } from "react-router-dom"; import api from "@/api"; export const LoginRequiredRoute = () => { - const { token, isAdmin } = useAutenticacion(); - console.log(isAdmin) + const { token } = useAutenticacion(); if (token === null) { return ; } else if (token === undefined) { @@ -16,33 +15,15 @@ export const LoginRequiredRoute = () => { }; export const IsAdminRequiredRoute = ({ setViewPageLoader }) => { - const [isAdmin, setIsAdmin] = useState(false); - const [hasCheckedAdmin, setHasCheckedAdmin] = useState(false); // Nuevo estado para verificar si ya se hizo la comprobación - - useEffect(() => { - const verificarAdmin = async () => { - setViewPageLoader(true); // Muestra el loader - try { - const response = await api.usuarios.admin.is_admin(); - setIsAdmin(response.data.user_is_admin); - } catch (error) { - console.error('Determinación de permisos admin ha fallado', error); - setIsAdmin(false); - } finally { - setHasCheckedAdmin(true); // Indica que la verificación ha terminado - setViewPageLoader(false); // Oculta el loader después de recibir la respuesta - } - }; - - verificarAdmin(); - }, [setViewPageLoader]); - - if (!hasCheckedAdmin) { - // Mientras no se haya verificado si es admin o no, retorna null + const {isAdmin} = useAutenticacion(); + + if (isAdmin === false) { + return ; + } else if (isAdmin === undefined) { return null; + }else { + return ; } - - return isAdmin ? : ; }; export const IsLogged = () => { diff --git a/cosiap_frontend/src/components/users/Login/LoginForm.jsx b/cosiap_frontend/src/components/users/Login/LoginForm.jsx index 57d0a42c7a6039357361faa68558fe99816db920..7383d8a602e4801270dfe6f7a7f06cfd69b6a126 100644 --- a/cosiap_frontend/src/components/users/Login/LoginForm.jsx +++ b/cosiap_frontend/src/components/users/Login/LoginForm.jsx @@ -8,15 +8,14 @@ import {LoginInputCURP} from '@/components/users/Login/LoginInputCURP' import { LoginInputPassword } from "@/components/users/Login/LoginInputPassword"; import { useState } from "react"; import {ErrorDisplay} from '@/components/common/ui/ErrorDisplay' -import { useNavigate } from "react-router-dom"; import {useAutenticacion} from "@/components/common/utility/Autenticador" export function LoginForm( {setViewPageLoader} ) { - const [loginError, setLoginError] = useState('') - const navigate = useNavigate(); - const { setToken, setIsAdmin, isAdmin } = useAutenticacion(); + const [loginError, setLoginError] = useState(''); + + const { setToken, setIsAdmin, configurarInterceptors, setUid } = useAutenticacion(); const { register, handleSubmit, @@ -30,11 +29,15 @@ export function LoginForm( {setViewPageLoader} ) { try { const response = await api.usuarios.token.login(data); console.log("Login successful:", response.data); - setToken(response.data.access) + setToken(response.data.access); + //Configuramos inteceptores + configurarInterceptors(response.data.access); //Establecemos si es admin o no const responseAd = await api.usuarios.admin.is_admin(); setIsAdmin(responseAd.data.user_is_admin); - navigate("/inicio"); + //Extraemos el id + const responseUid = await api.usuarios.getId(); + setUid(responseUid.data.user_id); } catch (error) { console.error("Login failed:", error); setLoginError(error.response.data.detail) @@ -66,7 +69,7 @@ export function LoginForm( {setViewPageLoader} ) { register={register} errors={errors.password ? errors.password.message : undefined} /> - +