F4: align dashboard with technical findings and INF workflow

This commit is contained in:
2026-09-07 22:57:00 -03:00
parent c999354645
commit 7a910d7505
+16 -14
View File
@@ -3,8 +3,10 @@ import { Link } from 'react-router';
import { useAuth } from '../auth/AuthContext';
import { Alert, LoadingBlock, errorMessage } from '../components/Feedback';
import { Icon } from '../components/Icon';
import { getDashboardSummary, getHealth } from '../lib/api';
import type { DashboardSummary, HealthResponse } from '../lib/api';
import { getHealth } from '../lib/api';
import type { HealthResponse } from '../lib/api';
import { getDashboardSummaryF4 } from '../lib/dashboardF4Api';
import type { DashboardSummaryF4 } from '../lib/dashboardF4Api';
import { actionLabel, formatDate, formatDateOnly } from '../lib/format';
function todayDate(): string {
@@ -15,19 +17,19 @@ function todayDate(): string {
export function DashboardPage() {
const { user, hasPermission } = useAuth();
const [summary, setSummary] = useState<DashboardSummary | null>(null);
const [summary, setSummary] = useState<DashboardSummaryF4 | null>(null);
const [health, setHealth] = useState<HealthResponse | null>(null);
const [error, setError] = useState('');
useEffect(() => {
Promise.all([getDashboardSummary(), getHealth()])
Promise.all([getDashboardSummaryF4(), getHealth()])
.then(([dashboard, service]) => { setSummary(dashboard); setHealth(service); })
.catch((requestError) => setError(errorMessage(requestError)));
}, []);
return <section>
<div className="page-heading dashboard-heading">
<div><span className="eyebrow">RESUMEN OPERATIVO</span><h1>Buen día, {user?.firstName}</h1><p>Lo que requiere atención y las próximas tareas de inspección.</p></div>
<div><span className="eyebrow">RESUMEN OPERATIVO</span><h1>Buen día, {user?.firstName}</h1><p>Lo que requiere atención en Inspecciones, Hallazgos, verificaciones e Informes.</p></div>
<span className={`health-pill ${health?.status === 'ok' ? 'ok' : ''}`}><span />{health?.status === 'ok' ? 'Sistema operativo' : 'Verificando sistema'}</span>
</div>
@@ -37,23 +39,23 @@ export function DashboardPage() {
<div className="stat-grid operational-stat-grid">
<Link className="stat-card" to="/inspecciones?status=PLANNED"><div className="stat-icon blue"><Icon name="calendar" /></div><div><small>INSPECCIONES PLANIFICADAS</small><strong>{summary.counts.plannedInspections}</strong><span>Pendientes de iniciar</span></div></Link>
<Link className="stat-card" to="/hallazgos?workflow=VERIFICATION_OVERDUE"><div className="stat-icon danger"><Icon name="alert" /></div><div><small>VERIFICACIONES VENCIDAS</small><strong>{summary.counts.overdueControls}</strong><span>Requieren control operativo</span></div></Link>
<Link className="stat-card" to="/hallazgos"><div className="stat-icon observed"><Icon name="alert" /></div><div><small>HALLAZGOS ABIERTOS</small><strong>{summary.counts.openFindings}</strong><span>Seguimiento activo</span></div></Link>
<Link className="stat-card" to="/inventarios?quick=validation"><div className="stat-icon violet"><Icon name="layers" /></div><div><small>REGISTROS A VALIDAR</small><strong>{summary.counts.assetsNeedValidation}</strong><span>Datos pendientes de revisión</span></div></Link>
<Link className="stat-card" to="/hallazgos"><div className="stat-icon observed"><Icon name="alert" /></div><div><small>HALLAZGOS ABIERTOS</small><strong>{summary.counts.openFindings}</strong><span>Seguimiento técnico activo</span></div></Link>
<Link className="stat-card" to="/informes?view=pending"><div className="stat-icon violet"><Icon name="clipboard" /></div><div><small>ACTAS SIN INF</small><strong>{summary.counts.sealedActsWithoutReport}</strong><span>Actas selladas pendientes de Informe</span></div></Link>
</div>
<div className="dashboard-section-heading compact"><div><span className="eyebrow">INVENTARIOS Y SEGUIMIENTO</span><h2>Estado general</h2></div></div>
<div className="dashboard-section-heading compact"><div><span className="eyebrow">INVENTARIOS Y DOCUMENTOS</span><h2>Estado general</h2></div></div>
<div className="stat-grid secondary-stat-grid">
<Link className="stat-card compact" to="/inventarios"><div className="stat-icon blue"><Icon name="layers" /></div><div><small>INVENTARIO</small><strong>{summary.counts.totalAssets}</strong><span>Elementos registrados en los inventarios</span></div></Link>
<Link className="stat-card compact" to="/inventarios"><div className="stat-icon blue"><Icon name="layers" /></div><div><small>INVENTARIO</small><strong>{summary.counts.totalAssets}</strong><span>Elementos registrados</span></div></Link>
<Link className="stat-card compact" to="/inventarios?quick=location"><div className="stat-icon gray"><Icon name="map" /></div><div><small>SIN UBICACIÓN</small><strong>{summary.counts.assetsWithoutGeometry}</strong><span>Sin geometría registrada</span></div></Link>
<Link className="stat-card compact" to="/hallazgos?workflow=WAITING_COMPANY"><div className="stat-icon gray"><Icon name="clipboard" /></div><div><small>ESPERANDO RESPUESTA</small><strong>{summary.counts.awaitingCompanyResponse}</strong><span>{summary.counts.overdueCompanyResponses} vencidas · {summary.counts.companyResponsesDueNext7Days} vencen en 7 días</span></div></Link>
<Link className="stat-card compact" to="/hallazgos?workflow=TO_VERIFY"><div className="stat-icon green"><Icon name="calendar" /></div><div><small>PRÓXIMAS VERIFICACIONES</small><strong>{summary.counts.controlsNext30Days}</strong><span>{summary.counts.awaitingVerificationSchedule} pendientes de programar</span></div></Link>
<Link className="stat-card compact" to="/informes"><div className="stat-icon gray"><Icon name="audit" /></div><div><small>INF EN PREPARACIÓN</small><strong>{summary.counts.reportsWorking}</strong><span>{summary.counts.reportsOfficialized} oficializados en GEDO</span></div></Link>
<Link className="stat-card compact" to="/hallazgos/planificacion"><div className="stat-icon green"><Icon name="calendar" /></div><div><small>PRÓXIMAS VERIFICACIONES</small><strong>{summary.counts.controlsNext30Days}</strong><span>{summary.counts.findingsWithoutControlDate} Hallazgos abiertos sin fecha de control</span></div></Link>
</div>
{hasPermission('inspection_findings.read') && <article className="panel upcoming-controls-panel">
<div className="panel-heading"><div><span className="eyebrow">AGENDA</span><h2>Próximos controles</h2><p className="section-copy">Hallazgos que tienen una fecha de verificación programada.</p></div><Link className="text-link" to="/hallazgos">Ver hallazgos <Icon name="chevron" size={15} /></Link></div>
<div className="panel-heading"><div><span className="eyebrow">AGENDA</span><h2>Próximos controles</h2><p className="section-copy">Hallazgos abiertos con una fecha de verificación definida.</p></div><Link className="text-link" to="/hallazgos/planificacion">Planificar verificaciones <Icon name="chevron" size={15} /></Link></div>
<div className="activity-list">
{summary.upcomingControls.length === 0 && <p className="muted">Todavía no hay controles programados.</p>}
{summary.upcomingControls.map((control) => <Link className="activity-item control-item" key={control.id} to={`/inspecciones/actas/${control.actId}`}><span className={`activity-dot ${control.nextControlOn < todayDate() ? 'overdue-dot' : ''}`} /><div><strong>{control.title}</strong><p>{control.assetCode} · {control.assetName}</p></div><time>{formatDateOnly(control.nextControlOn)}</time></Link>)}
{summary.upcomingControls.map((control) => <Link className="activity-item control-item" key={control.id} to={`/hallazgos/${control.id}`}><span className={`activity-dot ${control.nextControlOn < todayDate() ? 'overdue-dot' : ''}`} /><div><strong>{control.title}</strong><p>{control.assetCode} · {control.assetName} · {control.actCode}</p></div><time>{formatDateOnly(control.nextControlOn)}</time></Link>)}
</div>
</article>}
@@ -66,7 +68,7 @@ export function DashboardPage() {
<article className="panel quick-panel">
<span className="eyebrow">ACCESOS RÁPIDOS</span><h2>Administración</h2>
<div className="quick-links">
<Link to="/inventarios"><Icon name="layers" /><span><strong>Inventarios</strong><small>Inventario operativo organizado por empresa</small></span><Icon name="chevron" /></Link>
<Link to="/inventarios"><Icon name="layers" /><span><strong>Inventarios</strong><small>Inventario operativo organizado por empresa y área</small></span><Icon name="chevron" /></Link>
{hasPermission('asset_types.read') && <Link to="/admin/asset-types"><Icon name="layers" /><span><strong>Configuración de Inventarios</strong><small>Tipos y campos configurables</small></span><Icon name="chevron" /></Link>}
{hasPermission('users.read') && <Link to="/admin/users"><Icon name="users" /><span><strong>Usuarios</strong><small>Accesos y roles</small></span><Icon name="chevron" /></Link>}
</div>