import React, { useState, useEffect, useCallback } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { Box, Typography, Button, Paper, CircularProgress, Alert } from '@mui/material'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import SaveIcon from '@mui/icons-material/Save'; import perfilService from '../../services/Usuarios/perfilService'; import type { PermisoAsignadoDto } from '../../models/dtos/Usuarios/PermisoAsignadoDto'; import type { PerfilDto } from '../../models/dtos/Usuarios/PerfilDto'; import { usePermissions as usePagePermissions } from '../../hooks/usePermissions'; import axios from 'axios'; import PermisosChecklist from '../../components/Modals/Usuarios/PermisosChecklist'; const SECCION_PERMISSIONS_PREFIX = "SS"; const getModuloFromSeccionCodAcc = (codAcc: string): string | null => { if (codAcc === "SS001") return "Distribución"; if (codAcc === "SS007") return "Suscripciones"; if (codAcc === "SS002") return "Contables"; if (codAcc === "SS003") return "Impresión"; if (codAcc === "SS004") return "Reportes"; if (codAcc === "SS006") return "Usuarios"; if (codAcc === "SS005") return "Radios"; return null; }; const getModuloConceptualDelPermiso = (permisoModulo: string): string => { const moduloLower = permisoModulo.toLowerCase(); if (moduloLower.includes("distribuidores") || moduloLower.includes("canillas") || moduloLower.includes("publicaciones distribución") || moduloLower.includes("zonas distribuidores") || moduloLower.includes("movimientos distribuidores") || moduloLower.includes("empresas") || moduloLower.includes("otros destinos") || moduloLower.includes("ctrl. devoluciones") || moduloLower.includes("movimientos canillas") || moduloLower.includes("salidas otros destinos")) { return "Distribución"; } if (moduloLower.includes("suscripciones")) { return "Suscripciones"; } if (moduloLower.includes("cuentas pagos") || moduloLower.includes("cuentas notas") || moduloLower.includes("cuentas tipos pagos")) { return "Contables"; } if (moduloLower.includes("impresión tiradas") || moduloLower.includes("impresión bobinas") || moduloLower.includes("impresión plantas") || moduloLower.includes("estados bobinas") || moduloLower.includes("tipos bobinas")) { return "Impresión"; } if (moduloLower.includes("usuarios") || moduloLower.includes("perfiles")) { return "Usuarios"; } if (moduloLower.includes("reportes")) { return "Reportes"; } if (moduloLower.includes("permisos")) { return "Permisos (Definición)"; } if (moduloLower.includes("radios")) { return "Radios"; } return permisoModulo; }; const AsignarPermisosAPerfilPage: React.FC = () => { const { idPerfil } = useParams<{ idPerfil: string }>(); const navigate = useNavigate(); const { tienePermiso: tienePermisoPagina, isSuperAdmin } = usePagePermissions(); const puedeAsignar = isSuperAdmin || tienePermisoPagina("PU004"); const [perfil, setPerfil] = useState(null); const [permisosDisponibles, setPermisosDisponibles] = useState([]); const [permisosSeleccionados, setPermisosSeleccionados] = useState>(new Set()); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [error, setError] = useState(null); const [successMessage, setSuccessMessage] = useState(null); const idPerfilNum = Number(idPerfil); const cargarDatos = useCallback(async () => { if (!puedeAsignar) { setError("Acceso denegado. No tiene permiso para asignar permisos."); setLoading(false); return; } if (isNaN(idPerfilNum)) { setError("ID de Perfil inválido."); setLoading(false); return; } setLoading(true); setError(null); setSuccessMessage(null); try { const [perfilData, permisosData] = await Promise.all([ perfilService.getPerfilById(idPerfilNum), perfilService.getPermisosPorPerfil(idPerfilNum) // Esto devuelve todos los permisos con su estado 'asignado' ]); setPerfil(perfilData); setPermisosDisponibles(permisosData); setPermisosSeleccionados(new Set(permisosData.filter(p => p.asignado).map(p => p.id))); } catch (err) { console.error(err); setError('Error al cargar datos del perfil o permisos.'); if (axios.isAxiosError(err) && err.response?.status === 404) { setError(`Perfil con ID ${idPerfilNum} no encontrado.`); } } finally { setLoading(false); } }, [idPerfilNum, puedeAsignar]); useEffect(() => { cargarDatos(); }, [cargarDatos]); const handlePermisoChange = useCallback(( permisoId: number, asignadoViaCheckboxHijo: boolean, // Este valor es el 'e.target.checked' si el clic fue en un hijo esPermisoSeccionClick = false, moduloConceptualAsociado?: string // Este es el módulo conceptual del padre SSxxx o del grupo del hijo ) => { setPermisosSeleccionados(prevSelected => { const newSelected = new Set(prevSelected); const permisoActual = permisosDisponibles.find(p => p.id === permisoId); if (!permisoActual) return prevSelected; const permisosDelModuloHijo = moduloConceptualAsociado ? permisosDisponibles.filter(p => { const mc = getModuloConceptualDelPermiso(p.modulo); // Usar la función helper return mc === moduloConceptualAsociado && !p.codAcc.startsWith(SECCION_PERMISSIONS_PREFIX); }) : []; if (esPermisoSeccionClick && moduloConceptualAsociado) { const idPermisoSeccion = permisoActual.id; const estabaSeccionSeleccionada = prevSelected.has(idPermisoSeccion); const todosHijosEstabanSeleccionados = permisosDelModuloHijo.length > 0 && permisosDelModuloHijo.every(p => prevSelected.has(p.id)); const ningunHijoEstabaSeleccionado = permisosDelModuloHijo.every(p => !prevSelected.has(p.id)); if (!estabaSeccionSeleccionada) { // Estaba Off, pasa a "Solo Sección" (Indeterminate si hay hijos) newSelected.add(idPermisoSeccion); // NO se marcan los hijos } else if (estabaSeccionSeleccionada && (ningunHijoEstabaSeleccionado || !todosHijosEstabanSeleccionados) && permisosDelModuloHijo.length > 0) { // Estaba "Solo Sección" o "Parcial Hijos", pasa a "Sección + Todos los Hijos" newSelected.add(idPermisoSeccion); // Asegurar permisosDelModuloHijo.forEach(p => newSelected.add(p.id)); } else { // Estaba "Sección + Todos los Hijos" (o no había hijos), pasa a Off newSelected.delete(idPermisoSeccion); permisosDelModuloHijo.forEach(p => newSelected.delete(p.id)); } } else if (!esPermisoSeccionClick && moduloConceptualAsociado) { // Clic en un permiso hijo if (asignadoViaCheckboxHijo) { newSelected.add(permisoId); const permisoSeccionPadre = permisosDisponibles.find( ps => ps.codAcc.startsWith(SECCION_PERMISSIONS_PREFIX) && getModuloFromSeccionCodAcc(ps.codAcc) === moduloConceptualAsociado ); if (permisoSeccionPadre && !newSelected.has(permisoSeccionPadre.id)) { newSelected.add(permisoSeccionPadre.id); // Marcar padre si no estaba } } else { // Desmarcando un hijo newSelected.delete(permisoId); const permisoSeccionPadre = permisosDisponibles.find( ps => ps.codAcc.startsWith(SECCION_PERMISSIONS_PREFIX) && getModuloFromSeccionCodAcc(ps.codAcc) === moduloConceptualAsociado ); if (permisoSeccionPadre) { const algunOtroHijoSeleccionado = permisosDelModuloHijo.some(p => p.id !== permisoId && newSelected.has(p.id)); if (!algunOtroHijoSeleccionado && newSelected.has(permisoSeccionPadre.id)) { // Si era el último hijo y el padre estaba marcado, NO desmarcamos el padre automáticamente. // El estado indeterminate se encargará visualmente. // Si quisiéramos que se desmarque el padre, aquí iría: newSelected.delete(permisoSeccionPadre.id); } } } } else { // Permiso sin módulo conceptual asociado (ej: "Permisos (Definición)") if (asignadoViaCheckboxHijo) { newSelected.add(permisoId); } else { newSelected.delete(permisoId); } } if (successMessage) setSuccessMessage(null); if (error) setError(null); return newSelected; }); }, [permisosDisponibles, successMessage, error]); const handleGuardarCambios = async () => { if (!puedeAsignar || !perfil) return; setSaving(true); setError(null); setSuccessMessage(null); try { await perfilService.updatePermisosPorPerfil(perfil.id, { permisosIds: Array.from(permisosSeleccionados) }); setSuccessMessage('Permisos actualizados correctamente.'); await cargarDatos(); } catch (err: any) { console.error(err); const message = axios.isAxiosError(err) && err.response?.data?.message ? err.response.data.message : 'Error al guardar los permisos.'; setError(message); } finally { setSaving(false); } }; if (loading) { return ; } if (error && !perfil) { return {error}; } if (!puedeAsignar) { return Acceso denegado.; } if (!perfil && !loading) { return Perfil no encontrado o error al cargar.; } return ( Asignar Permisos al Perfil: {perfil?.nombrePerfil || 'Cargando...'} ID Perfil: {perfil?.id} {error && !successMessage && {error}} {successMessage && {successMessage}} ); }; export default AsignarPermisosAPerfilPage;