/* global React */
// Mis Compromisos Asignados — vista de la Institución Reportante
// HU.2.1 (cargar avance), HU.2.2 (historial), HU.2.3 (devolución para ajuste)
const { useState: useStateM, useMemo: useMemoM } = React;
function MisCompromisosScreen({ user, onOpenDetalle, onCargar }) {
const I = window.Icons;
const all = window.MIS_COMPROMISOS;
const [filter, setFilter] = useStateM('todos');
const [view, setView] = useStateM('cards'); // cards | tabla
// Conteos por estado de ciclo
const counts = useMemoM(() => {
const c = { todos: all.length, pending: 0, draft: 0, in_review: 0, returned: 0, approved: 0, overdue: 0 };
all.forEach(x => { c[x.estadoCiclo] = (c[x.estadoCiclo] || 0) + 1; });
return c;
}, [all]);
const filtered = useMemoM(() => {
if (filter === 'todos') return all;
return all.filter(c => c.estadoCiclo === filter);
}, [all, filter]);
// KPIs derivados
const total = all.length;
const cumplidos = all.filter(c => c.estado === 'ok').length;
const parciales = all.filter(c => c.estado === 'partial').length;
const noCump = all.filter(c => c.estado === 'bad').length;
const proximos = all.filter(c => c.d >= 0 && c.d <= 7).length;
// Hay devoluciones?
const devoluciones = all.filter(c => c.estadoCiclo === 'returned');
return (
{/* Header */}
{user.org}
Mis Compromisos Asignados
{total} recomendaciones internacionales bajo responsabilidad de tu institución.
Carga avances, adjunta evidencias y atiende las observaciones de la CGR.
Exportar mi cartera
Consultar a la CGR
{/* Banner: devolución pendiente (HU.2.3) */}
{devoluciones.length > 0 && (
{devoluciones.length} reporte devuelto para ajuste
{devoluciones[0].id} — {devoluciones[0].text}.
Observación de {devoluciones[0].observador} · {devoluciones[0].obsFecha}.
onCargar(devoluciones[0].id)}>
Atender observación
)}
{/* KPI strip */}
{total}
Compromisos asignados
{cumplidos} cumpl. · {parciales} parc. · {noCump} no cumpl.
{proximos}
Plazos próximos (D-7)
Requieren acción esta semana
{counts.returned || 0}
Devueltos para ajuste
Editar lo observado
{counts.in_review || 0}
En revisión por CGR
Esperando validación
{counts.approved || 0}
Aprobados
Ciclo cerrado por CGR
{/* Tabs por estado de ciclo */}
Todos
Pendientes de cargar
Devueltos para ajuste
Borradores
En revisión
Aprobados
Vencidos
setView('cards')}>
Tarjetas
setView('tabla')}>
Tabla
{/* Cards / lista */}
{view === 'cards' ? (
{filtered.map(c => (
))}
) : (
ID
Recomendación
Estado del ciclo
Vence
Estado cumplim.
Último avance
{filtered.map(c => {
const meta = window.cicloMeta(c.estadoCiclo);
const dCls = c.d < 0 ? 'is-overdue' : c.d <= 3 ? 'is-soon' : '';
return (
onOpenDetalle(c.id)}>
{c.id}
{c.text}
{c.mecanismo} · {c.ronda}
· Rol: {c.rol}
{c.plazo}
{c.d < 0 ? `D+${Math.abs(c.d)}` : c.d === 0 ? 'Hoy' : `D-${c.d}`}
{c.ultimoAvance}
e.stopPropagation()}>
onCargar(c.id)}>
{c.estadoCiclo === 'pending' || c.estadoCiclo === 'overdue' ? 'Cargar' : c.estadoCiclo === 'returned' ? 'Atender' : 'Ver'}
);
})}
)}
);
}
// ---------------- Card individual ----------------
function MisCompromisoCard({ c, onOpen, onCargar }) {
const I = window.Icons;
const meta = window.cicloMeta(c.estadoCiclo);
const ICON = I[meta.icon];
const isUrgent = c.d < 0 || c.estadoCiclo === 'returned' || c.estadoCiclo === 'overdue';
const isSoon = c.d >= 0 && c.d <= 7;
const dueText =
c.d < 0 ? `Vencido hace ${Math.abs(c.d)} día${Math.abs(c.d) !== 1 ? 's' : ''}` :
c.d === 0 ? 'Vence hoy' :
c.d <= 7 ? `Vence en ${c.d} día${c.d !== 1 ? 's' : ''}` :
`Vence el ${c.plazo}`;
const ctaLabel =
c.estadoCiclo === 'pending' || c.estadoCiclo === 'overdue' ? 'Cargar avance' :
c.estadoCiclo === 'returned' ? 'Atender observación' :
c.estadoCiclo === 'draft' ? 'Continuar borrador' :
c.estadoCiclo === 'in_review' ? 'Ver avance enviado' :
'Ver detalle';
return (
{c.mecanismo}
{c.id}
· {c.ronda}
onOpen(c.id)}>
{c.text}
{meta.label}
Rol: {c.rol}
{c.lider !== '—' && c.rol !== 'Líder' && · Líder: {c.lider} }
{/* Observación destacada si está devuelto */}
{c.estadoCiclo === 'returned' && c.observacion && (
Observación de la CGR
· {c.obsFecha}
{c.observacion}
{c.observador}
)}
{dueText}
{c.avances}
{c.evidencias}
· {c.ultimoAvance}
onCargar(c.id)}
>
{c.estadoCiclo === 'returned' ? : }
{ctaLabel}
onOpen(c.id)}>
Ver historial
);
}
// ---------------- Tab pill (filter) ----------------
function CicloTab({ id, count, tone, active, onSet, children }) {
return (
onSet(id)}
>
{tone && }
{children}
{count != null && {count} }
);
}
// ---------------- Pill compacta ciclo ----------------
function CicloPill({ ciclo }) {
const I = window.Icons;
const meta = window.cicloMeta(ciclo);
const ICON = I[meta.icon];
return (
{meta.label}
);
}
Object.assign(window, { MisCompromisosScreen, CicloPill });