/* 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,
ordenadas por plazo interno de carga de avances. Para cada una verás el estado
de cumplimiento vigente, el último reporte enviado, las observaciones de la CGR
pendientes de atender y el indicador D-7 / D-3 / D-1 cuando el plazo se acerca.
Carga tu avance con descripción y evidencias (PDF, JPG, PNG, XLSX, DOCX o URLs),
consulta el historial completo de versiones y atiende las devoluciones recibidas.
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 });