import React, { useEffect, useState } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { getRole, createRole, updateRole, fetchPermissionsByModule, } from '../api/rbac'; import { useNotification } from '../context/NotificationContext'; // Nombres legibles de módulos para la UI const MODULE_LABELS = { usuarios: 'Usuarios', pedimentos: 'Pedimentos', partidas: 'Partidas', remesas: 'Remesas', coves: 'COVEs', edocuments: 'E-Documents', acuses: 'Acuses', documentos: 'Documentos', vucem: 'Ventanilla Única (VUCEM)', reportes: 'Reportes', datastage: 'DataStage', organizacion: 'Organización', notificaciones: 'Notificaciones', cards: 'Dashboard / Cards', }; export default function ProfileForm() { const { id } = useParams(); const isEditing = Boolean(id); const navigate = useNavigate(); const { showMessage } = useNotification(); const [name, setName] = useState(''); const [description, setDescription] = useState(''); const [isAdminRole, setIsAdminRole] = useState(false); // Permisos de la API: { modulo: [{ id, codename, descripcion, modulo }] } const [permsByModule, setPermsByModule] = useState({}); const [loadingPerms, setLoadingPerms] = useState(true); // IDs numéricos de permisos seleccionados const [selectedPermIds, setSelectedPermIds] = useState(new Set()); const [loadingRole, setLoadingRole] = useState(isEditing); const [submitting, setSubmitting] = useState(false); useEffect(() => { if (typeof window !== 'undefined' && !document.getElementById('profiles-animations')) { const style = document.createElement('style'); style.id = 'profiles-animations'; style.innerHTML = ` @keyframes fadeInUpProfiles { 0% { opacity: 0; transform: translateY(24px); } 100% { opacity: 1; transform: translateY(0); } } .fade-in-up-profiles { animation: fadeInUpProfiles 0.6s cubic-bezier(0.22, 1, 0.36, 1) both; } @keyframes bounce-slow { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-8px); } } .animate-bounce-slow { animation: bounce-slow 2.2s infinite; } `; document.head.appendChild(style); } }, []); // Cargar catálogo de permisos desde API useEffect(() => { fetchPermissionsByModule() .then(data => { setPermsByModule(data && typeof data === 'object' ? data : {}); }) .catch(() => showMessage('Error al cargar catálogo de permisos', 'error')) .finally(() => setLoadingPerms(false)); }, []); // Cargar datos del rol si es edición useEffect(() => { if (!isEditing) return; getRole(id) .then(role => { setName(role.nombre || ''); setDescription(role.descripcion || ''); setIsAdminRole(role.is_admin_role || false); // Permisos vienen como [{ id, codename, descripcion, modulo }] const permList = Array.isArray(role.permissions) ? role.permissions : []; setSelectedPermIds(new Set(permList.map(p => p.id))); setLoadingRole(false); }) .catch(err => { showMessage(err.message || 'Error al cargar perfil', 'error'); setLoadingRole(false); }); }, [id, isEditing]); // Módulos en el orden en que llegan de la API const modules = Object.keys(permsByModule); const togglePerm = (permId) => { setSelectedPermIds(prev => { const next = new Set(prev); if (next.has(permId)) next.delete(permId); else next.add(permId); return next; }); }; const toggleModule = (module) => { const moduleIds = (permsByModule[module] || []).map(p => p.id); const allSelected = moduleIds.every(id => selectedPermIds.has(id)); setSelectedPermIds(prev => { const next = new Set(prev); if (allSelected) { moduleIds.forEach(id => next.delete(id)); } else { moduleIds.forEach(id => next.add(id)); } return next; }); }; const totalPerms = modules.reduce((acc, m) => acc + (permsByModule[m]?.length ?? 0), 0); const selectAll = () => { const all = modules.flatMap(m => (permsByModule[m] || []).map(p => p.id)); setSelectedPermIds(new Set(all)); }; const clearAll = () => setSelectedPermIds(new Set()); const handleSubmit = async (e) => { e.preventDefault(); if (!name.trim()) { showMessage('El nombre del perfil es obligatorio', 'error'); return; } setSubmitting(true); try { const payload = { nombre: name.trim(), descripcion: description.trim(), permission_ids: [...selectedPermIds], }; if (isEditing) { await updateRole(id, payload); showMessage('Perfil actualizado correctamente', 'success'); } else { await createRole(payload); showMessage('Perfil creado correctamente', 'success'); } navigate('/profiles'); } catch (err) { showMessage(err.message || 'Error al guardar perfil', 'error'); } finally { setSubmitting(false); } }; if (loadingRole) { return (
); } return (
{/* Header */}

{isEditing ? 'Editar Perfil' : 'Nuevo Perfil'}

{isEditing ? `Editando: ${name}` : 'Define nombre y permisos del nuevo perfil'}

{/* Datos del perfil */}

Datos del perfil

Identifica el perfil dentro de la organización

setName(e.target.value)} required disabled={isAdminRole} placeholder="Ej. Operador de VUCEM" className="w-full px-3 py-2 border border-slate-300 rounded-md shadow-sm focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all bg-white text-slate-900 placeholder-slate-400 text-sm disabled:bg-slate-100 disabled:text-slate-500" /> {isAdminRole && (

Perfil de administrador — el nombre es fijo

)}
setDescription(e.target.value)} placeholder="Descripción opcional del perfil" className="w-full px-3 py-2 border border-slate-300 rounded-md shadow-sm focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all bg-white text-slate-900 placeholder-slate-400 text-sm" />
{/* Permisos */}

Permisos

{loadingPerms ? 'Cargando catálogo…' : `${selectedPermIds.size} de ${totalPerms} permisos seleccionados`}

{!loadingPerms && (
)}
{loadingPerms ? (
) : modules.length === 0 ? (
No se pudo cargar el catálogo de permisos
) : (
{modules.map(module => { const modulePerms = permsByModule[module] || []; const selectedCount = modulePerms.filter(p => selectedPermIds.has(p.id)).length; const allSelected = selectedCount === modulePerms.length && modulePerms.length > 0; const someSelected = selectedCount > 0 && !allSelected; return (
{/* Cabecera módulo */}
toggleModule(module)} >
{allSelected && ( )} {someSelected && (
)}
{MODULE_LABELS[module] ?? module}
{selectedCount}/{modulePerms.length}
{/* Permisos del módulo */}
{modulePerms.map(perm => { const active = selectedPermIds.has(perm.id); return ( ); })}
); })}
)}
{/* Botones */}
); }