Implementación AnomalIA - Fix de dropdowns y permisos.
All checks were successful
Optimized Build and Deploy / remote-build-and-deploy (push) Successful in 5m17s

This commit is contained in:
2025-06-30 15:26:14 -03:00
parent 95aa09d62a
commit c96d259892
59 changed files with 1430 additions and 337 deletions

View File

@@ -1,14 +1,13 @@
// src/layouts/MainLayout.tsx
import React, { type ReactNode, useState, useEffect, useMemo } // << AÑADIR useMemo
from 'react';
import React, { type ReactNode, useState, useEffect, useMemo } from 'react';
import {
Box, AppBar, Toolbar, Typography, Tabs, Tab, Paper,
IconButton, Menu, MenuItem, ListItemIcon, ListItemText, Divider,
Button
Button, Badge
} from '@mui/material';
import AccountCircle from '@mui/icons-material/AccountCircle';
import LockResetIcon from '@mui/icons-material/LockReset';
import LogoutIcon from '@mui/icons-material/Logout';
import NotificationsIcon from '@mui/icons-material/Notifications';
import { useAuth } from '../contexts/AuthContext';
import ChangePasswordModal from '../components/Modals/Usuarios/ChangePasswordModal';
import { useNavigate, useLocation } from 'react-router-dom';
@@ -18,6 +17,16 @@ interface MainLayoutProps {
children: ReactNode;
}
// --- Helper para dar nombres legibles a los tipos de alerta ---
const getTipoAlertaLabel = (tipoAlerta: string): string => {
switch (tipoAlerta) {
case 'DevolucionAnomala': return 'Devoluciones Anómalas';
case 'ComportamientoSistema': return 'Anomalías del Sistema';
case 'FaltaDeDatos': return 'Falta de Datos';
default: return tipoAlerta;
}
};
// Definición original de módulos
const allAppModules = [
{ label: 'Inicio', path: '/', requiredPermission: null }, // Inicio siempre visible
@@ -31,22 +40,36 @@ const allAppModules = [
];
const MainLayout: React.FC<MainLayoutProps> = ({ children }) => {
// Obtenemos todo lo necesario del AuthContext, INCLUYENDO LAS ALERTAS
const {
user, // user ya está disponible aquí
logout,
isAuthenticated,
isPasswordChangeForced,
showForcedPasswordChangeModal,
setShowForcedPasswordChangeModal,
passwordChangeCompleted
user, logout, isAuthenticated, isPasswordChangeForced,
showForcedPasswordChangeModal, setShowForcedPasswordChangeModal,
passwordChangeCompleted,
alertas
} = useAuth();
const { tienePermiso, isSuperAdmin } = usePermissions(); // <<--- OBTENER HOOK DE PERMISOS
// El resto de los hooks locales no cambian
const { tienePermiso, isSuperAdmin } = usePermissions();
const navigate = useNavigate();
const location = useLocation();
const [selectedTab, setSelectedTab] = useState<number | false>(false);
const [anchorElUserMenu, setAnchorElUserMenu] = useState<null | HTMLElement>(null);
const [anchorElAlertasMenu, setAnchorElAlertasMenu] = useState<null | HTMLElement>(null);
// --- Agrupación de alertas para el menú ---
const gruposDeAlertas = useMemo(() => {
if (!alertas || !Array.isArray(alertas)) return [];
const groups = alertas.reduce((acc, alerta) => {
const label = getTipoAlertaLabel(alerta.tipoAlerta);
acc[label] = (acc[label] || 0) + 1;
return acc;
}, {} as Record<string, number>);
return Object.entries(groups); // Devuelve [['Devoluciones Anómalas', 5], ...]
}, [alertas]);
const numAlertas = alertas.length;
const accessibleModules = useMemo(() => {
if (!isAuthenticated) return [];
@@ -92,6 +115,17 @@ const MainLayout: React.FC<MainLayoutProps> = ({ children }) => {
setAnchorElUserMenu(null);
};
// Handlers para el nuevo menú de alertas
const handleOpenAlertasMenu = (event: React.MouseEvent<HTMLElement>) => {
setAnchorElAlertasMenu(event.currentTarget);
};
const handleCloseAlertasMenu = () => {
setAnchorElAlertasMenu(null);
};
const handleNavigateToAlertas = () => { navigate('/anomalias/alertas'); handleCloseAlertasMenu(); };
const handleChangePasswordClick = () => {
setShowForcedPasswordChangeModal(true);
handleCloseUserMenu();
@@ -133,7 +167,6 @@ const MainLayout: React.FC<MainLayoutProps> = ({ children }) => {
);
}
// Si no hay módulos accesibles después del login (y no es el cambio de clave forzado)
// Esto podría pasar si un usuario no tiene permiso para NINGUNA sección, ni siquiera Inicio.
// Deberías redirigir a login o mostrar un mensaje de "Sin acceso".
@@ -162,6 +195,37 @@ const MainLayout: React.FC<MainLayoutProps> = ({ children }) => {
)}
{isAuthenticated && (
<>
<IconButton onClick={handleOpenAlertasMenu} color="inherit">
<Badge badgeContent={numAlertas} color="error">
<NotificationsIcon />
</Badge>
</IconButton>
<Menu
id="alertas-menu"
anchorEl={anchorElAlertasMenu}
open={Boolean(anchorElAlertasMenu)}
onClose={() => setAnchorElAlertasMenu(null)}
>
<MenuItem disabled>
<ListItemText primary={`Tienes ${numAlertas} alertas pendientes.`} />
</MenuItem>
<Divider />
{gruposDeAlertas.map(([label, count]) => (
<MenuItem key={label} onClick={handleNavigateToAlertas}>
<ListItemIcon><Badge badgeContent={count} color="error" sx={{mr: 2}} /></ListItemIcon>
<ListItemText>{label}</ListItemText>
</MenuItem>
))}
{numAlertas > 0 && <Divider />}
<MenuItem onClick={handleNavigateToAlertas}>
<ListItemText sx={{textAlign: 'center'}}>Ver Todas las Alertas</ListItemText>
</MenuItem>
</Menu>
<IconButton
size="large"
aria-label="Cuenta del usuario"