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.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 ── */}
{/* ── Row 3: Fases + Disciplinas ── */}
{phases.map(p => (
))}
{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 });