Implementación AnomalIA - Fix de dropdowns y permisos.
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Optimized Build and Deploy / remote-build-and-deploy (push) Successful in 5m17s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Optimized Build and Deploy / remote-build-and-deploy (push) Successful in 5m17s
				
			This commit is contained in:
		| @@ -1,8 +1,7 @@ | ||||
| import React, { createContext, useState, useContext, type ReactNode, useEffect } from 'react'; | ||||
| import type { LoginResponseDto } from '../models/dtos/Usuarios/LoginResponseDto'; | ||||
| import React, { createContext, useState, useContext, type ReactNode, useEffect, useCallback } from 'react'; | ||||
| import { jwtDecode } from 'jwt-decode'; | ||||
| import { getAlertas, marcarAlertaLeida, marcarGrupoComoLeido, type AlertaGenericaDto } from '../services/Anomalia/alertaService'; | ||||
|  | ||||
| // Interfaz para los datos del usuario que guardaremos en el contexto | ||||
| export interface UserContextData { | ||||
|   userId: number; | ||||
|   username: string; | ||||
| @@ -11,33 +10,37 @@ export interface UserContextData { | ||||
|   debeCambiarClave: boolean; | ||||
|   perfil: string; | ||||
|   idPerfil: number; | ||||
|   permissions: string[]; // Guardamos los codAcc | ||||
|   permissions: string[]; | ||||
| } | ||||
|  | ||||
| // Interfaz para el payload decodificado del JWT | ||||
| interface DecodedJwtPayload { | ||||
|   sub: string;          // User ID (viene como string) | ||||
|   name: string;         // Username | ||||
|   given_name?: string;  // Nombre (estándar, pero verifica tu token) | ||||
|   family_name?: string; // Apellido (estándar, pero verifica tu token) | ||||
|   role: string | string[]; // Puede ser uno o varios roles | ||||
|   sub: string; | ||||
|   name: string; | ||||
|   given_name?: string; | ||||
|   family_name?: string; | ||||
|   role: string | string[]; | ||||
|   perfil: string; | ||||
|   idPerfil: string;     // (viene como string) | ||||
|   debeCambiarClave: string; // (viene como string "True" o "False") | ||||
|   permission?: string | string[]; // Nuestros claims de permiso (codAcc) | ||||
|   [key: string]: any;   // Para otros claims | ||||
|   idPerfil: string; | ||||
|   debeCambiarClave: string; | ||||
|   permission?: string | string[]; | ||||
|   [key: string]: any; | ||||
| } | ||||
|  | ||||
| interface AuthContextType { | ||||
|   isAuthenticated: boolean; | ||||
|   user: UserContextData | null; // Usar el tipo extendido | ||||
|   user: UserContextData | null; | ||||
|   token: string | null; | ||||
|   isLoading: boolean; | ||||
|   alertas: AlertaGenericaDto[]; | ||||
|   showForcedPasswordChangeModal: boolean; | ||||
|   isPasswordChangeForced: boolean; | ||||
|  | ||||
|   marcarAlertaComoLeida: (idAlerta: number) => Promise<void>; | ||||
|   marcarGrupoDeAlertasLeido: (tipoAlerta: string, idEntidad: number) => Promise<void>; | ||||
|  | ||||
|   setShowForcedPasswordChangeModal: (show: boolean) => void; | ||||
|   passwordChangeCompleted: () => void; | ||||
|   login: (apiLoginResponse: LoginResponseDto) => void; // Recibe el DTO de la API | ||||
|   login: (apiLoginResponse: any) => void; // DTO no definido aquí, usamos any | ||||
|   logout: () => void; | ||||
| } | ||||
|  | ||||
| @@ -50,24 +53,57 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => | ||||
|   const [isLoading, setIsLoading] = useState<boolean>(true); | ||||
|   const [showForcedPasswordChangeModal, setShowForcedPasswordChangeModal] = useState<boolean>(false); | ||||
|   const [isPasswordChangeForced, setIsPasswordChangeForced] = useState<boolean>(false); | ||||
|   const [alertas, setAlertas] = useState<AlertaGenericaDto[]>([]); | ||||
|  | ||||
|   const processTokenAndSetUser = (jwtToken: string) => { | ||||
|   const fetchAlertas = useCallback(async (currentUser: UserContextData | null) => { | ||||
|     if (currentUser && (currentUser.esSuperAdmin || currentUser.permissions.includes('AL001'))) { | ||||
|       try { | ||||
|         const data = await getAlertas(); | ||||
|         setAlertas(data || []); | ||||
|       } catch (error) { | ||||
|         console.error("Error al obtener alertas en AuthContext:", error); | ||||
|         setAlertas([]); | ||||
|       } | ||||
|     } else { | ||||
|       setAlertas([]); | ||||
|     } | ||||
|   }, []); | ||||
|  | ||||
|   const marcarAlertaComoLeida = async (idAlerta: number) => { | ||||
|     try { | ||||
|       await marcarAlertaLeida(idAlerta); | ||||
|       await fetchAlertas(user); // Refresca el estado global | ||||
|     } catch (error) { | ||||
|       console.error("Error al marcar alerta como leída:", error); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const marcarGrupoDeAlertasLeido = async (tipoAlerta: string, idEntidad: number) => { | ||||
|     try { | ||||
|       await marcarGrupoComoLeido({ tipoAlerta, idEntidad }); | ||||
|       await fetchAlertas(user); // Refresca el estado global | ||||
|     } catch (error) { | ||||
|       console.error(`Error al marcar grupo ${tipoAlerta}/${idEntidad} como leído:`, error); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const logout = useCallback(() => { | ||||
|     localStorage.removeItem('authToken'); | ||||
|     setToken(null); | ||||
|     setUser(null); | ||||
|     setIsAuthenticated(false); | ||||
|     setShowForcedPasswordChangeModal(false); | ||||
|     setIsPasswordChangeForced(false); | ||||
|     setAlertas([]); | ||||
|   }, []); | ||||
|  | ||||
|   const processTokenAndSetUser = useCallback((jwtToken: string) => { | ||||
|     try { | ||||
|       const decodedToken = jwtDecode<DecodedJwtPayload>(jwtToken); | ||||
|  | ||||
|       // Verificar expiración (opcional, pero buena práctica aquí también) | ||||
|       const currentTime = Date.now() / 1000; | ||||
|       if (decodedToken.exp && decodedToken.exp < currentTime) { | ||||
|         console.warn("Token expirado al procesar."); | ||||
|         logout(); // Llama a logout que limpia todo | ||||
|         return; | ||||
|         logout(); return; | ||||
|       } | ||||
|  | ||||
|       let permissions: string[] = []; | ||||
|       if (decodedToken.permission) { | ||||
|         permissions = Array.isArray(decodedToken.permission) ? decodedToken.permission : [decodedToken.permission]; | ||||
|       } | ||||
|  | ||||
|       const userForContext: UserContextData = { | ||||
|         userId: parseInt(decodedToken.sub, 10), | ||||
|         username: decodedToken.name, | ||||
| @@ -75,27 +111,23 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => | ||||
|         esSuperAdmin: decodedToken.role === "SuperAdmin" || (Array.isArray(decodedToken.role) && decodedToken.role.includes("SuperAdmin")), | ||||
|         debeCambiarClave: decodedToken.debeCambiarClave?.toLowerCase() === 'true', | ||||
|         idPerfil: decodedToken.idPerfil ? parseInt(decodedToken.idPerfil, 10) : 0, | ||||
|         permissions: permissions, | ||||
|         perfil: decodedToken.perfil || 'Usuario' // Asignar un valor por defecto si no existe | ||||
|         permissions: Array.isArray(decodedToken.permission) ? decodedToken.permission : (decodedToken.permission ? [decodedToken.permission] : []), | ||||
|         perfil: decodedToken.perfil || 'Usuario' | ||||
|       }; | ||||
|  | ||||
|       setToken(jwtToken); | ||||
|       setUser(userForContext); | ||||
|       setIsAuthenticated(true); | ||||
|       localStorage.setItem('authToken', jwtToken); | ||||
|       localStorage.setItem('authUser', JSON.stringify(userForContext)); // Guardar el usuario procesado | ||||
|  | ||||
|       // Lógica para el modal de cambio de clave | ||||
|       if (userForContext.debeCambiarClave) { | ||||
|         setShowForcedPasswordChangeModal(true); | ||||
|         setIsPasswordChangeForced(true); | ||||
|       } | ||||
|  | ||||
|     } catch (error) { | ||||
|       console.error("Error al decodificar o procesar token:", error); | ||||
|       logout(); // Limpiar estado si el token es inválido | ||||
|       console.error("Error al decodificar token:", error); | ||||
|       logout(); | ||||
|     } | ||||
|   }; | ||||
|   }, [logout]); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     setIsLoading(true); | ||||
| @@ -104,20 +136,18 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => | ||||
|       processTokenAndSetUser(storedToken); | ||||
|     } | ||||
|     setIsLoading(false); | ||||
|   }, []); | ||||
|   }, [processTokenAndSetUser]); | ||||
|  | ||||
|   const login = (apiLoginResponse: LoginResponseDto) => { | ||||
|     processTokenAndSetUser(apiLoginResponse.token); // Procesar el token recibido | ||||
|   }; | ||||
|   useEffect(() => { | ||||
|     if (user && isAuthenticated) { | ||||
|         fetchAlertas(user); | ||||
|         const intervalId = setInterval(() => fetchAlertas(user), 300000); // Refresca cada 5 mins | ||||
|         return () => clearInterval(intervalId); | ||||
|     } | ||||
|   }, [user, isAuthenticated, fetchAlertas]); | ||||
|  | ||||
|   const logout = () => { | ||||
|     localStorage.removeItem('authToken'); | ||||
|     localStorage.removeItem('authUser'); | ||||
|     setToken(null); | ||||
|     setUser(null); | ||||
|     setIsAuthenticated(false); | ||||
|     setShowForcedPasswordChangeModal(false); | ||||
|     setIsPasswordChangeForced(false); | ||||
|   const login = (apiLoginResponse: any) => { | ||||
|     processTokenAndSetUser(apiLoginResponse.token); | ||||
|   }; | ||||
|  | ||||
|   const passwordChangeCompleted = () => { | ||||
| @@ -138,6 +168,7 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => | ||||
|     <AuthContext.Provider value={{ | ||||
|       isAuthenticated, user, token, isLoading, | ||||
|       showForcedPasswordChangeModal, isPasswordChangeForced, | ||||
|       alertas, marcarAlertaComoLeida, marcarGrupoDeAlertasLeido, | ||||
|       setShowForcedPasswordChangeModal, passwordChangeCompleted, | ||||
|       login, logout | ||||
|     }}> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user