Ya perdí el hilo de los cambios pero ahi van.
This commit is contained in:
		| @@ -0,0 +1,256 @@ | ||||
| import React, { useState, useEffect, useCallback } from 'react'; | ||||
| import { | ||||
|     Box, Typography, TextField, Button, Paper, | ||||
|     Table, TableBody, TableCell, TableContainer, TableHead, TableRow, | ||||
|     CircularProgress, Alert, TablePagination, Tooltip, Autocomplete, | ||||
|     MenuItem, | ||||
|     FormControl, | ||||
|     InputLabel, | ||||
|     Select | ||||
| } from '@mui/material'; | ||||
| import type { UsuarioDto } from '../../../models/dtos/Usuarios/UsuarioDto'; | ||||
| import FilterListIcon from '@mui/icons-material/FilterList'; | ||||
| import usuarioService from '../../../services/Usuarios/usuarioService'; | ||||
| import type { UsuarioHistorialDto } from '../../../models/dtos/Usuarios/Auditoria/UsuarioHistorialDto'; | ||||
| import { usePermissions } from '../../../hooks/usePermissions'; | ||||
|  | ||||
| const GestionarAuditoriaUsuariosPage: React.FC = () => { | ||||
|     const [historial, setHistorial] = useState<UsuarioHistorialDto[]>([]); | ||||
|     const [loading, setLoading] = useState(true); | ||||
|     const [error, setError] = useState<string | null>(null); | ||||
|  | ||||
|     // Filtros | ||||
|     const [filtroFechaDesde, setFiltroFechaDesde] = useState(''); | ||||
|     const [filtroFechaHasta, setFiltroFechaHasta] = useState(''); | ||||
|     const [filtroIdUsuarioAfectado, setFiltroIdUsuarioAfectado] = useState<UsuarioDto | null>(null); | ||||
|     const [filtroIdUsuarioModifico, setFiltroIdUsuarioModifico] = useState<UsuarioDto | null>(null); | ||||
|     const [filtroTipoMod, setFiltroTipoMod] = useState(''); | ||||
|  | ||||
|     const [usuariosParaDropdown, setUsuariosParaDropdown] = useState<UsuarioDto[]>([]); | ||||
|     const [tiposModificacionParaDropdown, setTiposModificacionParaDropdown] = useState<string[]>([]); | ||||
|     const [loadingDropdowns, setLoadingDropdowns] = useState(false); | ||||
|  | ||||
|     const [page, setPage] = useState(0); | ||||
|     const [rowsPerPage, setRowsPerPage] = useState(10); | ||||
|  | ||||
|     const { tienePermiso, isSuperAdmin } = usePermissions(); | ||||
|     const puedeVerAuditoria = isSuperAdmin || tienePermiso("AU001"); // O el permiso que definas | ||||
|  | ||||
|     const fetchDropdownData = useCallback(async () => { | ||||
|         if (!puedeVerAuditoria) return; | ||||
|         setLoadingDropdowns(true); | ||||
|         try { | ||||
|             const usuariosData = await usuarioService.getAllUsuarios(); // Asumiendo que tienes este método | ||||
|             setUsuariosParaDropdown(usuariosData); | ||||
|  | ||||
|             // Opción B para Tipos de Modificación (desde backend) | ||||
|             // const tiposModData = await apiClient.get<string[]>('/auditoria/tipos-modificacion'); // Ajusta el endpoint si lo creas | ||||
|             // setTiposModificacionParaDropdown(tiposModData.data); | ||||
|  | ||||
|             // Opción A (Hardcodeado en Frontend - más simple para empezar) | ||||
|             setTiposModificacionParaDropdown([ | ||||
|                 "Creado", "Insertada", | ||||
|                 "Actualizado", "Modificada", | ||||
|                 "Eliminado", "Eliminada", | ||||
|                 "Baja", "Alta", | ||||
|                 "Liquidada", | ||||
|                 "Eliminado (Cascada)" | ||||
|             ].sort()); | ||||
|  | ||||
|         } catch (err) { | ||||
|             console.error("Error al cargar datos para dropdowns de auditoría:", err); | ||||
|             setError("Error al cargar opciones de filtro."); // O un error más específico | ||||
|         } finally { | ||||
|             setLoadingDropdowns(false); | ||||
|         } | ||||
|     }, [puedeVerAuditoria]); | ||||
|  | ||||
|     const cargarHistorial = useCallback(async () => { | ||||
|         if (!puedeVerAuditoria) { | ||||
|             setError("No tiene permiso para ver el historial de auditoría."); | ||||
|             setLoading(false); | ||||
|             return; | ||||
|         } | ||||
|         setLoading(true); setError(null); | ||||
|         try { | ||||
|             let data; | ||||
|             // Ahora usamos los IDs de los objetos UsuarioDto seleccionados | ||||
|             const idAfectado = filtroIdUsuarioAfectado ? filtroIdUsuarioAfectado.id : null; | ||||
|             const idModifico = filtroIdUsuarioModifico ? filtroIdUsuarioModifico.id : null; | ||||
|  | ||||
|             if (idAfectado) { // Si se seleccionó un usuario afectado específico | ||||
|                 data = await usuarioService.getHistorialDeUsuario(idAfectado, { | ||||
|                     fechaDesde: filtroFechaDesde || undefined, | ||||
|                     fechaHasta: filtroFechaHasta || undefined, | ||||
|                 }); | ||||
|             } else { // Sino, buscar en todo el historial con los otros filtros | ||||
|                 data = await usuarioService.getTodoElHistorialDeUsuarios({ | ||||
|                     fechaDesde: filtroFechaDesde || undefined, | ||||
|                     fechaHasta: filtroFechaHasta || undefined, | ||||
|                     idUsuarioModifico: idModifico || undefined, | ||||
|                     tipoModificacion: filtroTipoMod || undefined, | ||||
|                 }); | ||||
|             } | ||||
|             setHistorial(data); | ||||
|         } catch (err: any) { | ||||
|             console.error(err); | ||||
|             setError(err.response?.data?.message || 'Error al cargar el historial de usuarios.'); | ||||
|         } finally { | ||||
|             setLoading(false); | ||||
|         } | ||||
|     }, [puedeVerAuditoria, filtroFechaDesde, filtroFechaHasta, filtroIdUsuarioAfectado, filtroIdUsuarioModifico, filtroTipoMod]); | ||||
|  | ||||
|     useEffect(() => { | ||||
|         fetchDropdownData(); | ||||
|     }, [fetchDropdownData]); // Cargar al montar | ||||
|  | ||||
|     useEffect(() => { | ||||
|         // Cargar historial cuando los filtros cambian o al inicio si puedeVerAuditoria está listo | ||||
|         if (puedeVerAuditoria) { | ||||
|             cargarHistorial(); | ||||
|         } | ||||
|     }, [cargarHistorial, puedeVerAuditoria]); // Quitar dependencias de filtro directo para evitar llamadas múltiples, handleFiltrar se encarga. | ||||
|  | ||||
|     const handleFiltrar = () => { | ||||
|         setPage(0); | ||||
|         cargarHistorial(); // cargarHistorial ahora usa los estados de filtro directamente | ||||
|     }; | ||||
|  | ||||
|     const formatDate = (dateString: string) => { | ||||
|         return new Date(dateString).toLocaleString('es-AR', { | ||||
|             day: '2-digit', month: '2-digit', year: 'numeric', | ||||
|             hour: '2-digit', minute: '2-digit', second: '2-digit' | ||||
|         }); | ||||
|     }; | ||||
|  | ||||
|     const handleChangePage = (_event: unknown, newPage: number) => setPage(newPage); | ||||
|     const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>) => { | ||||
|         setRowsPerPage(parseInt(event.target.value, 10)); setPage(0); | ||||
|     }; | ||||
|     const displayData = historial.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage); | ||||
|  | ||||
|     if (!loading && !puedeVerAuditoria) { | ||||
|         return <Box sx={{ p: 2 }}><Alert severity="error">{error || "Acceso denegado."}</Alert></Box>; | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     return ( | ||||
|         <Box sx={{ p: 2 }}> | ||||
|             <Typography variant="h4" gutterBottom>Auditoría de Usuarios</Typography> | ||||
|             <Paper sx={{ p: 2, mb: 2 }}> | ||||
|                 <Typography variant="h6" gutterBottom>Filtros <FilterListIcon fontSize="small" /></Typography> | ||||
|                 <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 2, alignItems: 'center', mb: 2 }}> | ||||
|                     <Autocomplete | ||||
|                         options={usuariosParaDropdown} | ||||
|                         getOptionLabel={(option) => `${option.nombre} ${option.apellido} (${option.user})`} | ||||
|                         value={filtroIdUsuarioAfectado} | ||||
|                         onChange={(_event, newValue) => { | ||||
|                             setFiltroIdUsuarioAfectado(newValue); | ||||
|                         }} | ||||
|                         isOptionEqualToValue={(option, value) => option.id === value.id} | ||||
|                         renderInput={(params) => ( | ||||
|                             <TextField {...params} label="Usuario Afectado (Opcional)" size="small" sx={{ minWidth: 250 }} /> | ||||
|                         )} | ||||
|                         loading={loadingDropdowns} | ||||
|                         disabled={loadingDropdowns} | ||||
|                         sx={{ flexGrow: 1 }} | ||||
|                     /> | ||||
|                     <TextField label="Fecha Desde" type="date" size="small" value={filtroFechaDesde} onChange={(e) => setFiltroFechaDesde(e.target.value)} InputLabelProps={{ shrink: true }} /> | ||||
|                     <TextField label="Fecha Hasta" type="date" size="small" value={filtroFechaHasta} onChange={(e) => setFiltroFechaHasta(e.target.value)} InputLabelProps={{ shrink: true }} /> | ||||
|  | ||||
|                     <Autocomplete | ||||
|                         options={usuariosParaDropdown} | ||||
|                         getOptionLabel={(option) => `${option.nombre} ${option.apellido} (${option.user})`} | ||||
|                         value={filtroIdUsuarioModifico} | ||||
|                         onChange={(_event, newValue) => { | ||||
|                             setFiltroIdUsuarioModifico(newValue); | ||||
|                         }} | ||||
|                         isOptionEqualToValue={(option, value) => option.id === value.id} | ||||
|                         renderInput={(params) => ( | ||||
|                             <TextField {...params} label="Usuario Modificó (Opcional)" size="small" sx={{ minWidth: 250 }} /> | ||||
|                         )} | ||||
|                         loading={loadingDropdowns} | ||||
|                         disabled={loadingDropdowns} | ||||
|                         sx={{ flexGrow: 1 }} | ||||
|                     /> | ||||
|  | ||||
|                     <FormControl size="small" sx={{ minWidth: 200, flexGrow: 1 }} disabled={loadingDropdowns}> | ||||
|                         <InputLabel>Tipo Modificación</InputLabel> | ||||
|                         <Select | ||||
|                             value={filtroTipoMod} | ||||
|                             label="Tipo Modificación" | ||||
|                             onChange={(e) => setFiltroTipoMod(e.target.value as string)} | ||||
|                         > | ||||
|                             <MenuItem value=""><em>Todos</em></MenuItem> | ||||
|                             {tiposModificacionParaDropdown.map((tipo) => ( | ||||
|                                 <MenuItem key={tipo} value={tipo}>{tipo}</MenuItem> | ||||
|                             ))} | ||||
|                         </Select> | ||||
|                     </FormControl> | ||||
|  | ||||
|                     <Button variant="outlined" onClick={handleFiltrar} size="small" disabled={loading || loadingDropdowns}>Filtrar</Button> | ||||
|                 </Box> | ||||
|             </Paper> | ||||
|  | ||||
|             {loading && <Box sx={{ display: 'flex', justifyContent: 'center', my: 2 }}><CircularProgress /></Box>} | ||||
|             {error && !loading && <Alert severity="error" sx={{ my: 2 }}>{error}</Alert>} | ||||
|  | ||||
|             {!loading && !error && ( | ||||
|                 <TableContainer component={Paper}> | ||||
|                     <Table size="small"> | ||||
|                         <TableHead> | ||||
|                             <TableRow sx={{ backgroundColor: 'action.hover' }}> | ||||
|                                 <TableCell>Fecha</TableCell> | ||||
|                                 <TableCell>Usuario Afectado (ID)</TableCell> | ||||
|                                 <TableCell>Username Afectado</TableCell> | ||||
|                                 <TableCell>Acción</TableCell> | ||||
|                                 <TableCell>Modificado Por (ID)</TableCell> | ||||
|                                 <TableCell>Nombre Modificador</TableCell> | ||||
|                                 <TableCell>Detalles (Simplificado)</TableCell> | ||||
|                             </TableRow> | ||||
|                         </TableHead> | ||||
|                         <TableBody> | ||||
|                             {displayData.length === 0 ? ( | ||||
|                                 <TableRow><TableCell colSpan={7} align="center">No se encontraron registros de historial.</TableCell></TableRow> | ||||
|                             ) : ( | ||||
|                                 displayData.map((h) => ( | ||||
|                                     <TableRow key={h.idHist} hover> | ||||
|                                         <TableCell><Tooltip title={h.fechaModificacion}><Box>{formatDate(h.fechaModificacion)}</Box></Tooltip></TableCell> | ||||
|                                         <TableCell>{h.idUsuarioAfectado}</TableCell> | ||||
|                                         <TableCell>{h.userAfectado}</TableCell> | ||||
|                                         <TableCell>{h.tipoModificacion}</TableCell> | ||||
|                                         <TableCell>{h.idUsuarioModifico}</TableCell> | ||||
|                                         <TableCell>{h.nombreUsuarioModifico}</TableCell> | ||||
|                                         <TableCell> | ||||
|                                             <Tooltip title={ | ||||
|                                                 `User: ${h.userAnt || '-'} -> ${h.userNvo}\n` + | ||||
|                                                 `Nombre: ${h.nombreAnt || '-'} -> ${h.nombreNvo}\n` + | ||||
|                                                 `Apellido: ${h.apellidoAnt || '-'} -> ${h.apellidoNvo}\n` + | ||||
|                                                 `Habilitado: ${h.habilitadaAnt ?? '-'} -> ${h.habilitadaNva}\n` + | ||||
|                                                 `SupAdmin: ${h.supAdminAnt ?? '-'} -> ${h.supAdminNvo}\n` + | ||||
|                                                 `Perfil: ${h.nombrePerfilAnt || h.idPerfilAnt || '-'} -> ${h.nombrePerfilNvo} (${h.idPerfilNvo})\n` + | ||||
|                                                 `CambiaClave: ${h.debeCambiarClaveAnt ?? '-'} -> ${h.debeCambiarClaveNva}` | ||||
|                                             }> | ||||
|                                                 <Box sx={{ maxWidth: 200, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}> | ||||
|                                                     {`User: ${h.userAnt?.substring(0, 5)}..->${h.userNvo.substring(0, 5)}.., Nom: ${h.nombreAnt?.substring(0, 3)}..->${h.nombreNvo.substring(0, 3)}..`} | ||||
|                                                 </Box> | ||||
|                                             </Tooltip> | ||||
|                                         </TableCell> | ||||
|                                     </TableRow> | ||||
|                                 )) | ||||
|                             )} | ||||
|                         </TableBody> | ||||
|                     </Table> | ||||
|                     <TablePagination | ||||
|                         rowsPerPageOptions={[10, 25, 50, 100]} component="div" count={historial.length} | ||||
|                         rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage} | ||||
|                         onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:" | ||||
|                     /> | ||||
|                 </TableContainer> | ||||
|             )} | ||||
|         </Box> | ||||
|     ); | ||||
| }; | ||||
|  | ||||
| export default GestionarAuditoriaUsuariosPage; | ||||
| @@ -6,6 +6,7 @@ const usuariosSubModules = [ | ||||
|   { label: 'Perfiles', path: 'perfiles' }, | ||||
|   { label: 'Permisos (Definición)', path: 'permisos' }, | ||||
|   { label: 'Usuarios', path: 'gestion-usuarios' }, | ||||
|   { label: 'Auditoría Usuarios', path: 'auditoria-usuarios' }, | ||||
| ]; | ||||
|  | ||||
| const UsuariosIndexPage: React.FC = () => { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user