/* global React */ // Nueva / Editar Institución — pantalla interna (ocupa el área de contenido) // Formulario único con validación. HU.4.3 · Súper Admin · CGR const { useState: useStateNI, useEffect: useEffectNI, useMemo: useMemoNI } = React; // Tipos derivados de las instituciones colaboradoras de la CGR const TIPOS_INST = [ { id: 'min', label: 'Ministerio', desc: 'Carteras del Poder Ejecutivo. Ej. Ministerio de Economía y Finanzas' }, { id: 'sec', label: 'Secretaría Nacional', desc: 'Secretarías de Estado y nacionales. Ej. SEPRELAD, SENABICO' }, { id: 'sub', label: 'Subsecretaría', desc: 'Dependiente de un ministerio. Ej. Subsecretaría de Estado de Tributación' }, { id: 'ctrl', label: 'Órgano de Control', desc: 'Control externo del Estado. Ej. CGR, Auditoría General del Poder Ejecutivo' }, { id: 'extra', label: 'Órgano Extra-Poder', desc: 'Autonomía constitucional. Ej. Ministerio Público, Procuraduría, Justicia Electoral, Consejo de la Magistratura' }, { id: 'pod', label: 'Poder del Estado', desc: 'Ej. Corte Suprema de Justicia (Poder Judicial)' }, { id: 'dir', label: 'Dirección Nacional', desc: 'Ej. Dirección Nacional de Contrataciones Públicas' }, { id: 'ente', label: 'Ente Autónomo o Autárquico', desc: 'Entes descentralizados del Estado' }, { id: 'gob', label: 'Gobernación', desc: 'Gobiernos departamentales' }, { id: 'mun', label: 'Municipalidad', desc: 'Gobiernos municipales' }, ]; const EMPTY_INST = { nombre: '', tipo: '', sigla: '', web: '', contacto: '', descripcion: '', estado: 'activo', }; const NOMBRE_MAX = 120; const DESC_MAX = 200; // ---------------- Validación ---------------- // Devuelve { campo: 'mensaje' }. Objeto vacío = formulario válido. // items = catálogo actual; editingId = id de la institución en edición (para no compararse consigo misma). function validateInst(form, items, editingId) { const e = {}; const otras = (items || []).filter((it) => !editingId || it.id !== editingId); const nombre = (form.nombre || '').trim(); if (!nombre) { e.nombre = 'Ingresa el nombre de la institución.'; } else if (nombre.length < 3) { e.nombre = 'El nombre debe tener al menos 3 caracteres.'; } else if (nombre.length > NOMBRE_MAX) { e.nombre = `Máximo ${NOMBRE_MAX} caracteres.`; } else if (otras.some((it) => (it.nombre || '').trim().toLowerCase() === nombre.toLowerCase())) { e.nombre = 'Ya existe una institución con este nombre. Usa uno diferente.'; } if (!form.tipo) { e.tipo = 'Selecciona un tipo de institución.'; } const sigla = (form.sigla || '').trim(); if (!sigla) { e.sigla = 'Ingresa la sigla.'; } else if (!/^[A-ZÁÉÍÓÚÑ0-9]{2,8}$/.test(sigla)) { e.sigla = 'Entre 2 y 8 caracteres: solo letras mayúsculas y números.'; } else if (otras.some((it) => (it.sigla || '').trim().toUpperCase() === sigla.toUpperCase())) { e.sigla = 'Ya existe una institución con esta sigla. Usa una diferente.'; } const web = (form.web || '').trim(); if (web) { const urlOk = /^https?:\/\/([a-z0-9](-?[a-z0-9])*\.)+[a-z]{2,}(:\d{2,5})?(\/[^\s]*)?$/i.test(web); if (!urlOk) { e.web = 'Ingresa una URL válida, ej. https://www.institucion.gov.py'; } } if ((form.descripcion || '').length > DESC_MAX) { e.descripcion = `Máximo ${DESC_MAX} caracteres.`; } return e; } // Usuarios ya registrados que pertenecen a esta institución (para elegir el contacto principal). function usuariosDeInst(editing) { if (!editing) return []; const sig = (editing.sigla || '').toLowerCase(); const nom = (editing.nombre || '').toLowerCase(); return window.cgrData.usuarios().filter((u) => { if (!window.cgrData.usuarioSeleccionable(u)) return false; const seg = (u.institucion || '').toLowerCase().split('·')[0].trim(); return (sig && seg === sig) || (nom && (seg === nom || nom.startsWith(seg) || seg.startsWith(nom))); }); } function NuevaInstitucionForm({ onCancel, onSave, editing, items }) { const I = window.Icons; const isEdit = !!editing; const [form, setForm] = useStateNI(() => editing ? { nombre: editing.nombre || '', tipo: editing.tipo || '', sigla: editing.sigla || '', web: editing.web || '', contacto: editing.contacto || '', descripcion: editing.descripcion || '', estado: editing.estado || 'activo', } : EMPTY_INST ); const [touched, setTouched] = useStateNI({}); const [submitAttempted, setSubmitAttempted] = useStateNI(false); // Resincroniza si cambia el objeto en edición useEffectNI(() => { setTouched({}); setSubmitAttempted(false); if (editing) { setForm({ nombre: editing.nombre || '', tipo: editing.tipo || '', sigla: editing.sigla || '', web: editing.web || '', contacto: editing.contacto || '', descripcion: editing.descripcion || '', estado: editing.estado || 'activo', }); } else { setForm(EMPTY_INST); } }, [editing]); const errors = useMemoNI( () => validateInst(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 slugId = (form.sigla || '') .toUpperCase() .replace(/[^A-Z0-9]+/g, '-') .replace(/^-|-$/g, ''); const handleSubmit = () => { if (!isValid) { setSubmitAttempted(true); return; } onSave && onSave({ ...form, nombre: form.nombre.trim(), sigla: form.sigla.trim() }); }; const tiposCat = (window.cgrData ? window.cgrData.tiposInstitucion() : TIPOS_INST.map(t => t.label)); const tipoActual = TIPOS_INST.find((t) => t.label === form.tipo); return (

{isEdit ? `Editar ${editing.sigla}` : 'Nueva institución'}

{isEdit ? 'Actualiza los datos de identificación, el tipo o el estado operativo de la institución. Los cambios quedan registrados.' : 'Completa los datos para registrar una nueva institución reportante en el catálogo oficial.'}

{/* Nombre — fila completa */}
update('nombre', e.target.value)} onBlur={() => blur('nombre')} /> {showErr('nombre') ?
{errors.nombre}
:
Razón social conforme a decreto o ley.
}
{/* Tipo + Sigla */}
{showErr('tipo') ?
{errors.tipo}
:
{tipoActual ? tipoActual.desc : 'Define dónde se ubica en el catálogo.'}
}
update('sigla', e.target.value.toUpperCase())} onBlur={() => blur('sigla')} /> {showErr('sigla') ?
{errors.sigla}
:
2 a 8 caracteres, mayúsculas y números.
}
{/* ID interno + Sitio web */}
{slugId || '— sigla pendiente —'}
Se genera automáticamente a partir de la sigla.
update('web', e.target.value)} onBlur={() => blur('web')} /> {showErr('web') ?
{errors.web}
:
Opcional. Debe iniciar con http:// o https://
}
{/* Contacto principal — fila completa */}
{usuariosDeInst(editing).length === 0 ? ( <>
Opcional. Es la persona que recibe las alertas y notificaciones de la institución. Podrás elegirla aquí una vez que la institución tenga usuarios.
) : ( <>
Opcional. Es la persona que recibe las alertas y notificaciones de las acciones asignadas a esta institución.
)}
{/* Descripción — fila completa */}