/* global React */ // Nuevo / Editar Usuario (HU.4.1) — pantalla interna, formulario único validado. // Mismo patrón que "Nueva institución". const { useState: useStateNU, useMemo: useMemoNU, useEffect: useEffectNU } = React; const ROLES_CATALOG = [ { id: 'superadmin', label: 'Súper Administrador', desc: 'Acceso total: gestiona usuarios, instituciones y configuración global.' }, { id: 'admin', label: 'Administrador', desc: 'Registra recomendaciones, asigna instituciones, revisa y aprueba avances.' }, { id: 'institucion', label: 'Enlace Institucional', desc: 'Carga avances y evidencias de los compromisos de su institución.' }, { id: 'cancilleria', label: 'Cancillería · MRE', desc: 'Solo lectura del tablero país e informes consolidados.' }, ]; const ROLE_PILL_CLS = { superadmin: 'role-super', admin: 'role-admin', institucion: 'role-inst', cancilleria: 'role-canc', }; // ---------------- Validación ---------------- function validateUser(form, items, editingId) { const e = {}; const otras = (items || []).filter((it) => !editingId || it.id !== editingId); if (!form.rol) e.rol = 'Selecciona un rol.'; const nombre = (form.nombre || '').trim(); if (!nombre) e.nombre = 'Ingresa el nombre.'; else if (nombre.length < 2) e.nombre = 'Mínimo 2 caracteres.'; const apellido = (form.apellido || '').trim(); if (!apellido) e.apellido = 'Ingresa el apellido.'; else if (apellido.length < 2) e.apellido = 'Mínimo 2 caracteres.'; if (form.rol === 'institucion' && !form.institucionId) { e.institucionId = 'Selecciona la institución del usuario.'; } else if (form.rol === 'institucion' && form.institucionId) { const inst = (window.cgrData ? window.cgrData.instituciones() : []).find(i => i.id === form.institucionId); if (inst && inst.estado === 'desactivado') { e.institucionId = 'Esa institución está desactivada. Elige una institución activa.'; } } const tel = (form.telefono || '').trim(); if (tel && !/^[+0-9()\s-]{6,20}$/.test(tel)) { e.telefono = 'Ingresa un teléfono válido.'; } const email = (form.email || '').trim(); if (!email) { e.email = 'Ingresa el correo institucional.'; } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(email)) { e.email = 'Ingresa un correo válido, ej. nombre@institucion.gob'; } else if (otras.some((it) => (it.email || '').trim().toLowerCase() === email.toLowerCase())) { e.email = 'Ya existe un usuario con este correo. Usa otro diferente.'; } return e; } function splitNombre(full) { const p = (full || '').trim().split(/\s+/); return { nombre: p[0] || '', apellido: p.slice(1).join(' ') }; } function NuevoUsuarioForm({ onCancel, onSave, editing, items }) { const I = window.Icons; const isEdit = !!editing; const initForm = () => { if (editing) { const { nombre, apellido } = splitNombre(editing.nombre); const inst = window.cgrData.instituciones().find(i => i.nombre === editing.institucion); return { rol: editing.rol || '', nombre, apellido, documento: editing.documento || '', telefono: editing.telefono || '', institucionId: inst ? inst.id : '', cargo: editing.cargo || '', email: editing.email || '', esContacto: window.cgrData ? window.cgrData.esContactoPrincipal(editing) : !!editing.esContacto, }; } return { rol: '', nombre: '', apellido: '', documento: '', telefono: '', institucionId: '', cargo: '', email: '', esContacto: false }; }; const [form, setForm] = useStateNU(initForm); const [touched, setTouched] = useStateNU({}); const [submitAttempted, setSubmitAttempted] = useStateNU(false); useEffectNU(() => { setForm(initForm()); setTouched({}); setSubmitAttempted(false); }, [editing]); const errors = useMemoNU( () => validateUser(form, items, editing && editing.id), [form, items, editing] ); const isValid = Object.keys(errors).length === 0; const update = (k, v) => setForm(f => ({ ...f, [k]: v })); const blur = (k) => setTouched(t => ({ ...t, [k]: true })); const showErr = (k) => (touched[k] || submitAttempted) && errors[k]; const requiereInst = form.rol === 'institucion'; const handleSubmit = () => { if (!isValid) { setSubmitAttempted(true); return; } const rolObj = ROLES_CATALOG.find(r => r.id === form.rol); const inst = window.cgrData.instituciones().find(i => i.id === form.institucionId); const nombreCompleto = `${form.nombre.trim()} ${form.apellido.trim()}`.trim(); const initials = ((form.nombre.trim()[0] || '') + (form.apellido.trim()[0] || '')).toUpperCase() || '··'; const institucionLabel = requiereInst ? (inst ? inst.nombre : '') : (form.cargo.trim() || (editing ? editing.institucion : '') || 'CGR'); onSave && onSave({ ...(editing || {}), nombre: nombreCompleto, initials, email: form.email.trim(), rol: form.rol, rolLabel: rolObj ? rolObj.label : '', institucion: institucionLabel, cargo: form.cargo.trim(), telefono: form.telefono.trim(), documento: form.documento.trim(), esContacto: requiereInst ? form.esContacto : false, }); }; return (

{isEdit ? `Editar ${editing.nombre}` : 'Nuevo usuario'}

{isEdit ? 'Actualiza los datos, el rol o la institución de la cuenta. Los cambios quedan registrados.' : 'Completa los datos para crear una cuenta de acceso. El rol define a qué módulos y datos accede.'}

{/* Rol — tarjetas seleccionables (primero) */}
{ROLES_CATALOG.map(r => ( ))}
{showErr('rol') &&
{errors.rol}
}
{/* Nombre + Apellido */}
update('nombre', e.target.value)} onBlur={() => blur('nombre')} /> {showErr('nombre') ?
{errors.nombre}
:
Nombre de pila de la persona.
}
update('apellido', e.target.value)} onBlur={() => blur('apellido')} /> {showErr('apellido') ?
{errors.apellido}
:
Apellidos de la persona.
}
{/* Documento + Teléfono */}
update('documento', e.target.value)} />
Opcional. Solo visible para los administradores.
update('telefono', e.target.value)} onBlur={() => blur('telefono')} /> {showErr('telefono') ?
{errors.telefono}
:
Opcional. Para alertas SMS si están habilitadas.
}
{/* Institución + Cargo */}
{showErr('institucionId') ?
{errors.institucionId}
:
{requiereInst ? 'Solo verá los compromisos de esta institución.' : 'Opcional para roles internos de la CGR.'}
}
update('cargo', e.target.value)} />
Opcional.
{/* Contacto principal de la institución — opcional */} {requiereInst && form.institucionId && (
El contacto principal recibe las alertas y notificaciones de las acciones de la institución. Puedes cambiarlo más adelante.
)} {/* Correo */}
update('email', e.target.value)} onBlur={() => blur('email')} /> {showErr('email') ?
{errors.email}
:
Será el identificador único y la dirección de invitación.
}
); } Object.assign(window, { NuevoUsuarioForm });