const { useState } = React; // ─── Health Barometer ──────────────────────────────────────────────────────── function HealthBarometer({ spi, cpi, physReal, physPlan }) { const score = ((spi + cpi) / 2 + physReal / physPlan) / 2; const status = score >= 0.97 ? 'ok' : score >= 0.92 ? 'at_risk' : 'delayed'; const color = statusColor(status); const label = score >= 0.97 ? 'SAUDÁVEL' : score >= 0.92 ? 'EM RISCO' : 'CRÍTICO'; // Circular gauge via SVG const r = 48, cx = 70, cy = 70, circ = 2 * Math.PI * r; const filled = Math.min(score, 1) * circ; return (
{Math.round(score * 100)}% {label} ÍNDICE GERAL
{[{l:'SPI',v:spi},{l:'CPI',v:cpi},{l:'Físico',v:`${physReal}%`},{l:'Plano',v:`${physPlan}%`}].map(({l,v}) => (
{l}
{typeof v === 'number' ? v.toFixed(2) : v}
))}
); } // ─── Milestone Row ─────────────────────────────────────────────────────────── function MilestoneRow({ m }) { const col = statusColor(m.status); const bg = m.status === 'critical' ? '#FFF5F5' : m.status === 'at_risk' ? '#FFFBEB' : '#F0FDF4'; return (
{m.days}
DIAS
{m.name}
{m.id} · {m.date}
{statusLabel(m.status).replace('⚠ ','').replace('✓ ','')}
); } // ─── Screen 1: Dashboard Principal ────────────────────────────────────────── function Screen1Dashboard() { const D = window.SIGP_DATA; const { evm, project, phases, disciplines, milestones, curvaSData } = D; return ( {/* ── Row 1: KPI Cards ── */}
Avanço Físico
37,4% real
Planejado: 41,2% · Δ −3,8pp
Conclusão Prevista
mar/2027
Baseline: jan/2027
+45 dias de desvio
Contingência
{fmtBRL(project.contingencyRemaining)}
de {fmtBRL(project.contingencyOriginal)}
{Math.round((project.contingencyRemaining/project.contingencyOriginal)*100)}% disponível
{/* ── Row 2: Curva S + Barômetro + Marcos ── */}
{milestones.map(m => )}
{/* ── Row 3: Fases + Disciplinas ── */}
{phases.map(p => (
{p.name}
))}
{disciplines.map(d => (
))}
⚠ Disciplinas em desvio crítico
Civil: −9pp vs plano · solo expansivo e produtividade abaixo do previsto
Mecânica: −13pp vs plano · aguardando PO caldeira e destilador (lead time crítico)
); } Object.assign(window, { Screen1Dashboard });