/* global React */
// Compromisos: listado + filtros avanzados + acciones (Admin / Súper Admin CGR)
// HU.1.1, HU.1.2, HU.1.3, HU.1.4, HU.5.3 — mismo patrón de diseño que Instituciones.
const { useState: useStateC, useMemo: useMemoC } = React;
// ---------------- Helpers para alta de recomendación ----------------
const MESES_ABBR_C = ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic'];
function fmtFechaISO_C(iso) {
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(iso || '');
if (!m) return iso || 'Sin plazo';
return parseInt(m[3], 10) + ' ' + MESES_ABBR_C[parseInt(m[2], 10) - 1] + ' ' + m[1];
}
function diasHastaISO_C(iso) {
const d = new Date((iso || '') + 'T00:00:00');
if (isNaN(d.getTime())) return 999;
const hoy = new Date(); hoy.setHours(0, 0, 0, 0);
return Math.round((d - hoy) / 86400000);
}
const ESTADO_LABEL_C = { ok: 'Cumplido', partial: 'Parcial', bad: 'No cumplido', pending: 'Pendiente', overdue: 'Vencido sin avance' };
// ---------------- KPI tira ----------------
function CompKpi({ icon, label, num, sub, tone }) {
const I = window.Icons;
return (
{React.createElement(I[icon], { size: 15 })}
{num}
{label}
{sub &&
{sub}
}
);
}
// ---------------- Fila de la tabla ----------------
function CompRow({ c, onOpen }) {
const I = window.Icons;
const dCls = c.d < 0 ? 'is-overdue' : c.d <= 3 ? 'is-soon' : c.d <= 7 ? 'is-warnish' : '';
return (
onOpen(c.id)}>
{c.id}
{c.idIntl && {c.idIntl} }
{c.text}
{c.mecanismo}
{c.eje}
{c.tipo} · {c.prioridad}
{c.instituciones.slice(0, 2).map(i => {i} )}
{c.instituciones.length > 2 && +{c.instituciones.length - 2} }
{c.plazo}
{c.d < 0 ? `D+${Math.abs(c.d)} (vencido)` : c.d === 0 ? 'Vence hoy' : `D-${c.d}`}
);
}
// ---------------- Pantalla principal ----------------
function CompromisosScreen({ onOpenDetalle, onOpenNueva }) {
const I = window.Icons;
const [all, setAll] = window.usePersistentState('compromisos', () => window.COMPROMISOS_FULL.slice());
const handleSaveRec = (form) => {
const cat = window.INSTITUCIONES_CATALOG || [];
const siglaDe = (id) => { const f = cat.find(x => x.id === id); return f ? f.sigla : id; };
const insts = (form.instituciones || []).filter(r => r.institucionId).map(r => siglaDe(r.institucionId));
const plazos = (form.instituciones || []).map(r => r.plazo).filter(Boolean).sort();
const plazoISO = plazos[0] || '';
const prefijo = (form.mecanismo || 'REC').slice(0, 3).toUpperCase();
const rondaClean = (form.ronda || '').replace(/\s+/g, '') || 'S';
const nuevo = {
id: prefijo + '-' + rondaClean + '-' + String(Date.now()).slice(-4),
idIntl: form.idIntl || '',
mecanismo: form.mecanismo,
ronda: form.ronda,
eje: form.eje,
tipo: form.tipo,
prioridad: form.prioridad,
instituciones: insts.length ? insts : ['—'],
plazo: plazoISO ? fmtFechaISO_C(plazoISO) : 'Sin plazo',
d: plazoISO ? diasHastaISO_C(plazoISO) : 999,
estado: 'pending',
text: form.titulo,
textoCompleto: form.texto,
lastUpdate: 'recién',
avances: 0, vinc: 0,
};
setAll(list => [nuevo, ...list]);
setView('list');
};
const [q, setQ] = useStateC('');
const [mech, setMech] = useStateC('Todos');
const [estado, setEstado] = useStateC('Todos');
const [tipo, setTipo] = useStateC('Todos');
const [inst, setInst] = useStateC('Todas');
const [eje, setEje] = useStateC('Todos los ejes');
const [advOpen, setAdvOpen] = useStateC(false);
const [view, setView] = useStateC('list'); // 'list' | 'form'
const MECH_OPTS = [{ v: 'Todos', l: 'Todos' }, { v: 'MESICIC', l: 'MESICIC' }, { v: 'UNCAC', l: 'UNCAC' }, { v: 'OCDE', l: 'OCDE' }];
const ESTADO_OPTS = [
{ v: 'Todos', l: 'Todos' },
{ v: 'Cumplidos', l: 'Cumplidos' },
{ v: 'Parciales', l: 'Parciales' },
{ v: 'No cumplidos', l: 'No cumplidos' },
{ v: 'Vencidos sin avance', l: 'Vencidos sin avance' },
];
const TIPO_OPTS = [{ v: 'Todos', l: 'Todos' }, { v: 'Normativa', l: 'Normativa' }, { v: 'Administrativa', l: 'Administrativa' }, { v: 'Operativa', l: 'Operativa' }];
const INST_OPTS = ['Todas', 'SENAC', 'Ministerio de Hacienda', 'Ministerio Público', 'Ministerio del Interior', 'Ministerio de Justicia', 'SEPRELAD', 'Poder Judicial', 'Contraloría General'];
const EJE_OPTS = ['Todos los ejes', ...window.cgrData.ejes()];
const filtered = useMemoC(() => {
return all.filter(c => {
if (q && !(`${c.id} ${c.text} ${c.instituciones.join(' ')}`).toLowerCase().includes(q.toLowerCase())) return false;
if (mech !== 'Todos' && c.mecanismo !== mech) return false;
if (estado === 'Cumplidos' && c.estado !== 'ok') return false;
if (estado === 'Parciales' && c.estado !== 'partial') return false;
if (estado === 'No cumplidos' && c.estado !== 'bad') return false;
if (estado === 'Vencidos sin avance' && c.d >= 0) return false;
if (tipo !== 'Todos' && c.tipo !== tipo) return false;
if (eje !== 'Todos los ejes' && c.eje !== eje) return false;
if (inst !== 'Todas' && !c.instituciones.some(i =>
i.toLowerCase().includes(inst.toLowerCase()) || inst.toLowerCase().includes(i.toLowerCase()))) return false;
return true;
});
}, [all, q, mech, estado, tipo, inst, eje]);
const advCount = [mech !== 'Todos', estado !== 'Todos', tipo !== 'Todos', inst !== 'Todas', eje !== 'Todos los ejes'].filter(Boolean).length;
const resetAdv = () => { setMech('Todos'); setEstado('Todos'); setTipo('Todos'); setInst('Todas'); setEje('Todos los ejes'); };
const clearAll = () => { setQ(''); resetAdv(); };
const activeChips = [];
if (mech !== 'Todos') activeChips.push({ key: 'mech', group: 'Mecanismo', label: mech, reset: () => setMech('Todos') });
if (estado !== 'Todos') activeChips.push({ key: 'estado', group: 'Estado', label: estado, reset: () => setEstado('Todos') });
if (tipo !== 'Todos') activeChips.push({ key: 'tipo', group: 'Tipo', label: tipo, reset: () => setTipo('Todos') });
if (inst !== 'Todas') activeChips.push({ key: 'inst', group: 'Institución',label: inst, reset: () => setInst('Todas') });
if (eje !== 'Todos los ejes') activeChips.push({ key: 'eje', group: 'Eje', label: eje, reset: () => setEje('Todos los ejes') });
// Vista de formulario (nueva recomendación) — ocupa la misma área que el listado
if (view === 'form') {
return (
setView('list')}
onSave={handleSaveRec}
/>
);
}
return (
Inventario de recomendaciones
Compromisos
Inventario único de las recomendaciones internacionales de Paraguay (MESICIC, UNCAC y OCDE).
Registra, clasifica, asigna instituciones y da seguimiento al cumplimiento.
window.cgrStore.exportRows(filtered, [
{ key: 'id', label: 'ID interno' },
{ key: 'idIntl', label: 'ID organismo' },
{ key: 'mecanismo', label: 'Mecanismo' },
{ key: 'ronda', label: 'Ronda' },
{ key: 'text', label: 'Recomendación' },
{ key: 'eje', label: 'Eje' },
{ key: 'tipo', label: 'Tipo' },
{ key: 'prioridad', label: 'Prioridad' },
{ key: 'instituciones', label: 'Instituciones', format: v => (v || []).join(', ') },
{ key: 'plazo', label: 'Vence' },
{ key: 'estado', label: 'Estado', format: v => ESTADO_LABEL_C[v] || v },
], 'compromisos_CGR', 'Compromisos')}
>
Exportar
setView('form')}>
Nueva recomendación
{/* Buscador + filtros avanzados */}
{advOpen && (
Filtros avanzados
Filtra el inventario por mecanismo, estado de cumplimiento, tipo de medida, institución responsable o eje temático. Los cambios se aplican al instante y se combinan con el buscador.
Limpiar filtros
Mecanismo
{MECH_OPTS.map(o => (
setMech(o.v)}>{o.l}
))}
Estado de cumplimiento
{ESTADO_OPTS.map(o => (
setEstado(o.v)}>{o.l}
))}
Tipo de medida
{TIPO_OPTS.map(o => (
setTipo(o.v)}>{o.l}
))}
Institución responsable
setInst(e.target.value)}>
{INST_OPTS.map(o => {o === 'Todas' ? 'Todas las instituciones' : o} )}
Eje temático
setEje(e.target.value)}>
{EJE_OPTS.map(o => {o} )}
)}
{!advOpen && advCount > 0 && (
{advCount} filtro{advCount === 1 ? '' : 's'} activo{advCount === 1 ? '' : 's'}:
{activeChips.map(a => (
{a.group}:
{a.label}
))}
Limpiar todo
)}
{filtered.length === 0 ? (
Sin resultados con los filtros aplicados
Intenta con otras palabras clave.
{(q || advCount > 0) && (
<>{' '}Limpiar todos los filtros .>
)}
) : (
<>
ID
Recomendación
Mecanismo
Clasificación
Instituciones
Vence
Estado
{filtered.map(c => )}
Mostrando {filtered.length} de {all.length} recomendaciones
1
>
)}
);
}
Object.assign(window, { CompromisosScreen });