Diseño de un AuditoriaController con un patrón para añadir endpoints de historial para diferentes entidades.
Implementación de la lógica de servicio y repositorio para obtener datos de las tablas _H para:
Usuarios (gral_Usuarios_H)
Pagos de Distribuidores (cue_PagosDistribuidor_H)
Notas de Crédito/Débito (cue_CreditosDebitos_H)
Entradas/Salidas de Distribuidores (dist_EntradasSalidas_H)
Entradas/Salidas de Canillitas (dist_EntradasSalidasCanillas_H)
Novedades de Canillitas (dist_dtNovedadesCanillas_H)
Ajustes Manuales de Saldo (cue_SaldoAjustesHistorial)
Tipos de Pago (cue_dtTipopago_H)
Canillitas (Maestro) (dist_dtCanillas_H)
Distribuidores (Maestro) (dist_dtDistribuidores_H)
Empresas (Maestro) (dist_dtEmpresas_H)
DTOs específicos para cada tipo de historial, incluyendo NombreUsuarioModifico.
Frontend:
Servicio auditoriaService.ts con métodos para llamar a cada endpoint de historial.
Página AuditoriaGeneralPage.tsx con:
Selector de "Tipo de Entidad a Auditar".
Filtros comunes (Fechas, Usuario Modificador, Tipo de Modificación, ID Entidad).
Un DataGrid que muestra las columnas dinámicamente según el tipo de entidad seleccionada.
Lógica para cargar los datos correspondientes.
DTOs de historial en TypeScript.
Actualizaciones en AppRoutes.tsx y MainLayout.tsx para la nueva sección de Auditoría (restringida a SuperAdmin).
This commit is contained in:
2025-06-09 19:37:07 -03:00
parent 35e24ab7d2
commit 437b1e8864
98 changed files with 3683 additions and 325 deletions

View File

@@ -25,6 +25,7 @@ import GestionarSalidasOtrosDestinosPage from '../pages/Distribucion/GestionarSa
import GestionarEntradasSalidasDistPage from '../pages/Distribucion/GestionarEntradasSalidasDistPage';
import GestionarEntradasSalidasCanillaPage from '../pages/Distribucion/GestionarEntradasSalidasCanillaPage';
import GestionarControlDevolucionesPage from '../pages/Distribucion/GestionarControlDevolucionesPage';
import GestionarParadasCanillaPage from '../pages/Distribucion/GestionarParadasCanillaPage';
// Impresión
import ImpresionIndexPage from '../pages/Impresion/ImpresionIndexPage';
@@ -77,6 +78,7 @@ import ReporteListadoDistMensualPage from '../pages/Reportes/ReporteListadoDistM
// Auditorias
import GestionarAuditoriaUsuariosPage from '../pages/Usuarios/Auditoria/GestionarAuditoriaUsuariosPage';
import AuditoriaGeneralPage from '../pages/Auditoria/AuditoriaGeneralPage';
// --- ProtectedRoute y PublicRoute SIN CAMBIOS ---
@@ -144,6 +146,7 @@ const AppRoutes = () => {
<Route path="salidas-otros-destinos" element={<GestionarSalidasOtrosDestinosPage />} />
<Route path="canillas" element={<GestionarCanillitasPage />} />
<Route path="canillas/:idCanilla/novedades" element={<GestionarNovedadesCanillaPage />} />
<Route path="canillas/:idCanilla/paradas" element={<GestionarParadasCanillaPage />} />
<Route path="distribuidores" element={<GestionarDistribuidoresPage />} />
<Route path="otros-destinos" element={<GestionarOtrosDestinosPage />} />
<Route path="zonas" element={<GestionarZonasPage />} />
@@ -245,17 +248,21 @@ const AppRoutes = () => {
<Route path="auditoria-usuarios" element={<GestionarAuditoriaUsuariosPage />} />
</Route>
{/* Módulo de Auditoías (anidado) */}
<Route path="auditoria"
element={
<SectionProtectedRoute onlySuperAdmin={true} sectionName="Auditoría">
<Outlet />
</SectionProtectedRoute>
}
>
<Route index element={<Navigate to="general" replace />} />
<Route path="general" element={<AuditoriaGeneralPage />} />
</Route>
{/* Ruta catch-all DENTRO del layout protegido */}
<Route path="*" element={<Navigate to="/" replace />} />
</Route> {/* Cierre de la ruta padre "/" */}
{/* Podrías tener un catch-all global aquí si una ruta no coincide EN ABSOLUTO,
pero el path="*" dentro de la ruta "/" ya debería manejar la mayoría de los casos
después de un login exitoso.
Si un usuario no autenticado intenta una ruta inválida, ProtectedRoute lo manda a /login.
*/}
{/* <Route path="*" element={<Navigate to="/login" replace />} /> */}
</Routes>
</BrowserRouter>
);

View File

@@ -1,21 +1,26 @@
// src/routes/SectionProtectedRoute.tsx
import React from 'react';
import { Navigate, Outlet } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';
import { usePermissions } from '../hooks/usePermissions';
import { Box, CircularProgress } from '@mui/material';
import { Alert, Box, CircularProgress } from '@mui/material';
interface SectionProtectedRouteProps {
requiredPermission: string;
requiredPermission?: string | null; // Hacerlo opcional
onlySuperAdmin?: boolean; // Nueva prop
sectionName: string;
children?: React.ReactNode;
}
const SectionProtectedRoute: React.FC<SectionProtectedRouteProps> = ({ requiredPermission, sectionName, children }) => {
const { isAuthenticated, isLoading: authIsLoading } = useAuth(); // isLoading de AuthContext
const SectionProtectedRoute: React.FC<SectionProtectedRouteProps> = ({
requiredPermission,
onlySuperAdmin = false, // Default a false
sectionName,
children
}) => {
const { isAuthenticated, isLoading: authIsLoading } = useAuth();
const { tienePermiso, isSuperAdmin, currentUser } = usePermissions();
if (authIsLoading) { // Esperar a que el AuthContext termine su carga inicial
if (authIsLoading) {
return (
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '80vh' }}>
<CircularProgress />
@@ -26,26 +31,37 @@ const SectionProtectedRoute: React.FC<SectionProtectedRouteProps> = ({ requiredP
if (!isAuthenticated) {
return <Navigate to="/login" replace />;
}
// En este punto, si está autenticado, currentUser debería estar disponible.
// Si currentUser pudiera ser null aun estando autenticado (poco probable con tu AuthContext),
// se necesitaría un manejo adicional o un spinner aquí.
if (!currentUser) {
// Esto sería un estado inesperado si isAuthenticated es true.
// Podrías redirigir a login o mostrar un error genérico.
console.error("SectionProtectedRoute: Usuario autenticado pero currentUser es null.");
return <Navigate to="/login" replace />; // O un error más específico
return <Navigate to="/login" replace />;
}
let canAccessSection = false;
if (onlySuperAdmin) {
canAccessSection = isSuperAdmin;
} else if (requiredPermission) {
canAccessSection = isSuperAdmin || tienePermiso(requiredPermission);
} else {
// Si no es onlySuperAdmin y no hay requiredPermission, por defecto se permite si está autenticado
// Esto podría ser para secciones públicas post-login pero sin permiso específico.
// O podrías querer que siempre haya un requiredPermission o onlySuperAdmin.
// Por ahora, lo dejaremos pasar si no se especifica ninguno y no es onlySuperAdmin.
// Sin embargo, para los SSxxx, siempre habrá un requiredPermission.
// Este else es más un fallback teórico.
canAccessSection = true;
}
const canAccessSection = isSuperAdmin || tienePermiso(requiredPermission);
if (!canAccessSection) {
console.error('SectionProtectedRoute: Usuario autenticado pero sin acceso a sección ', sectionName);
return <Navigate to="/" replace />;
return (
<Box sx={{p: 3, display:'flex', justifyContent:'center', mt: 2}}>
<Alert severity="error" sx={{width: '100%', maxWidth: 'md'}}>
No tiene permiso para acceder a la sección de {sectionName}.
</Alert>
</Box>
);
}
// Si children se proporciona (como <SectionProtectedRoute><IndexPage/></SectionProtectedRoute>), renderiza children.
// Si no (como <Route element={<SectionProtectedRoute ... />} > <Route .../> </Route>), renderiza Outlet.
return children ? <>{children}</> : <Outlet />;
};