/* 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.

{/* 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}.

)} {/* 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
{/* Cards / lista */} {view === 'cards' ? (
{filtered.map(c => ( ))}
) : (
{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)}> ); })}
ID Recomendación Estado del ciclo Vence Estado cumplim. Último avance
{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()}>
)}
); } // ---------------- 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}
{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}
); } // ---------------- Tab pill (filter) ---------------- function CicloTab({ id, count, tone, active, onSet, children }) { return ( ); } // ---------------- 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 });