/* global React */
// Slide-over panel: Nueva Recomendación
// HU.1.1 (registro), HU.1.2 (clasificación), HU.1.4 (asignación inicial)
const { useState: useStateN } = React;
function NuevaRecomendacionPanel({ open, onClose, onSave }) {
const I = window.Icons;
const [step, setStep] = useStateN(1);
// ----- Estado del formulario (mock — no se persiste) -----
const [form, setForm] = useStateN({
mecanismo: 'MESICIC',
ronda: 'V',
idIntl: '',
titulo: '',
texto: '',
eje: '',
tipo: 'Normativa',
prioridad: 'Alta',
capArt: '',
archivos: [
{ name: 'reporte_MESICIC_RondaV_PRY.pdf', size: '2.4 MB', type: 'pdf' },
],
instituciones: [
{ sigla: 'SENAC', rol: 'Líder', plazo: '12 jun 2026' },
],
});
const update = (k, v) => setForm(f => ({ ...f, [k]: v }));
if (!open) return null;
// Indicador de pasos
const steps = [
{ n: 1, label: 'Mecanismo y texto' },
{ n: 2, label: 'Clasificación' },
{ n: 3, label: 'Asignación inicial' },
{ n: 4, label: 'Revisión y guardado' },
];
const progress = (step / steps.length) * 100;
return (
{/* Header */}
{/* Stepper */}
{steps.map(s => (
s.n ? ' is-done' : ''}`}
onClick={() => setStep(s.n)}
>
{step > s.n ? : s.n}
{s.label}
))}
{/* Body */}
{step === 1 && }
{step === 2 && }
{step === 3 && }
{step === 4 && }
{/* Footer */}
Los campos marcados con * son obligatorios para registrar la recomendación.
Cancelar
Guardar borrador
{step < 4 ? (
setStep(s => Math.min(4, s + 1))}>
Continuar
) : (
{ onSave && onSave(form); onClose(); }}>
Registrar recomendación
)}
);
}
// ====================================================================
// PASO 1 — Mecanismo, ronda y texto
// ====================================================================
function StepMecanismo({ form, update }) {
const I = window.Icons;
const mecs = [
{ id: 'MESICIC', org: 'OEA', desc: 'Mecanismo de Seguimiento de la Convención Interamericana contra la Corrupción' },
{ id: 'UNCAC', org: 'ONU', desc: 'United Nations Convention against Corruption' },
{ id: 'OCDE', org: 'OCDE', desc: 'Working Group on Bribery in Intl. Business Transactions' },
];
return (
<>
{mecs.map(m => (
update('mecanismo', m.id)}
>
{m.id}
{m.id}
{m.org}
{m.desc}
{form.mecanismo === m.id && (
)}
))}
update('ronda', e.target.value)} placeholder="Ronda V" />
update('titulo', e.target.value)}
placeholder="Fortalecimiento del régimen de declaración jurada de funcionarios públicos" />
>
);
}
// ====================================================================
// PASO 2 — Clasificación
// ====================================================================
function StepClasificacion({ form, update }) {
const I = window.Icons;
return (
<>
{window.EJES_TEMATICOS.map(e => (
update('eje', e)}
>
{e}
))}
{['Alta', 'Media', 'Baja'].map(p => (
update('prioridad', p)}
>
{p}
))}
update('capArt', e.target.value)}
placeholder="Capítulo II · Art. 16" />
Buscar y vincular (opcional)
>
);
}
// ====================================================================
// PASO 3 — Asignación de instituciones y plazos
// ====================================================================
function StepAsignacion({ form, update }) {
const I = window.Icons;
const removeInst = i => update('instituciones', form.instituciones.filter((_, idx) => idx !== i));
const addInst = () => update('instituciones', [...form.instituciones, { sigla: '', rol: 'Apoyo', plazo: '' }]);
return (
<>
Solo la CGR puede asignar instituciones responsables.
Una recomendación puede tener múltiples instituciones, cada una con su plazo interno
propio. Las instituciones recibirán notificación por correo y bandeja interna al guardar.
{form.instituciones.map((inst, i) => (
{
const next = [...form.instituciones]; next[i] = { ...next[i], sigla: e.target.value };
update('instituciones', next);
}}>
Seleccionar…
{window.INSTITUCIONES.map(o => {o.nombre} )}
{
const next = [...form.instituciones]; next[i] = { ...next[i], rol: e.target.value };
update('instituciones', next);
}}>
Líder
Coordinación
Apoyo
{
const next = [...form.instituciones]; next[i] = { ...next[i], plazo: e.target.value };
update('instituciones', next);
}} />
removeInst(i)} disabled={form.instituciones.length === 1}>
))}
Agregar otra institución
{[
{ code: 'D-7', label: 'Aviso de proximidad', def: true },
{ code: 'D-3', label: 'Recordatorio', def: true },
{ code: 'D-1', label: 'Urgencia', def: true },
{ code: 'D', label: 'Vencimiento', def: true },
{ code: 'D+1', label: 'Sin avance', def: true },
].map(a => (
{a.code}
{a.label}
))}
>
);
}
// ====================================================================
// PASO 4 — Revisión
// ====================================================================
function StepRevision({ form }) {
const I = window.Icons;
return (
<>
{form.mecanismo}
Ronda {form.ronda}
{form.titulo || '— título pendiente —'}
{form.mecanismo.slice(0,3).toUpperCase()}-{form.ronda.replace(/\s+/g,'')}-•••
Texto de la recomendación
{form.texto || 'Sin texto cargado.'}
Eje temático {form.eje || '—'}
Tipo de medida {form.tipo}
Prioridad {form.prioridad}
Capítulo / Artículo {form.capArt || '—'}
Asignación inicial
{form.instituciones.map((a, i) => (
{a.sigla || '— sin asignar —'}
Rol {a.rol}
Plazo {a.plazo || 'sin plazo'}
{a.plazo ? 'Alertas activas' : 'Sin alertas'}
))}
Documentos adjuntos
{form.archivos.length === 0
?
Sin adjuntos.
:
{form.archivos.map((f, i) => (
{f.name} · {f.size}
))}
}
Al registrar, la recomendación quedará con estado inicial "No Cumplido" y
se enviará notificación a las {form.instituciones.length} institución(es) asignada(s).
>
);
}
// ====================================================================
// Helpers de formulario
// ====================================================================
function FormSection({ title, sub, children }) {
return (
);
}
function Field({ label, hint, children }) {
return (
{label}
{children}
{hint && {hint} }
);
}
function FileDrop({ archivos }) {
const I = window.Icons;
return (
Arrastra archivos aquí o haz clic para seleccionar
PDF, DOCX, XLSX, PNG, JPG · máx. 25 MB cada uno
{archivos.length > 0 && (
{archivos.map((f, i) => (
{f.name}
· {f.size}
))}
)}
);
}
Object.assign(window, { NuevaRecomendacionPanel, FormSection, Field, FileDrop });