/* === value-tracker.jsx =============================================== RoAI Value Tracker — full configurable surface. Replaces the old PreviewRoAI in preview.jsx. Features: - Period selector (3m / 6m / 12m / Tudo) - Alert banner for overdue plans - 3 hero KPI cards (Pipeline Total / Realizado / Taxa de Conversão) - Pipeline de Valor (5 status cards) - Charts: Ganhos por Status horizontal bars + Composição donut - Summary panels: Ganhos by status / Planos by status - Planos de Ação Kanban with drag/drop + add/edit - Side panel: Assistente de Valor (chat + quick actions) ===================================================================== */ const VT_COLUMNS = [ { id: 'backlog', label: 'Backlog' }, { id: 'planejado', label: 'Planejado' }, { id: 'emAndamento', label: 'Em Andamento' }, { id: 'concluido', label: 'Concluído' }, { id: 'cancelado', label: 'Cancelado' }, ]; function PreviewRoAI({ state, onSetState }) { const vt = state.valueTracker; const setVT = (patch) => onSetState({ ...state, valueTracker: { ...vt, ...(typeof patch === 'function' ? patch(vt) : patch) } }); /* Assistant collapsed by default — the browser-body frame is narrow */ const [collapsedAssistant, setCollapsedAssistant] = React.useState(true); return (
{vt.alert && }
{!collapsedAssistant && ( setCollapsedAssistant(true)} /> )} {collapsedAssistant && ( )}
); } /* ================================================================== HEADER ================================================================== */ function VTHeader({ state, setVT }) { const vt = state.valueTracker; const periods = [ { id: '3m', label: '3 meses' }, { id: '6m', label: '6 meses' }, { id: '12m', label: '12 meses' }, { id: 'all', label: 'Tudo' }, ]; return (

RoAI - Value Tracker

{periods.map(p => ( ))}
); } /* ================================================================== ALERT BANNER ================================================================== */ function VTAlert({ alert }) { return (
{alert.message}
); } /* ================================================================== HERO KPIs (3 cards) ================================================================== */ function VTHeroKPIs({ vt, setVT, state }) { const k = vt.kpis; return (
{k.pipelineTotal.label}
{' · '}
{k.realizado.label}
{k.conversao.label}
); } function VTConversionDonut({ pct }) { const r = 16; const C = 2 * Math.PI * r; const dash = (pct / 100) * C; return ( {pct}% ); } /* ================================================================== PIPELINE DE VALOR (5 status cards) ================================================================== */ function VTPipelineValor({ vt, setVT, state }) { return (
Pipeline de Valor
{vt.pipelineValor.map((p, i) => (
))}
); } /* ================================================================== CHARTS: Ganhos por Status (horizontal) + Composição (donut) ================================================================== */ function VTCharts({ vt, state }) { const STATUS_COLOR = { realizado: { color: '#10B981', label: 'Realizado' }, bloqueado: { color: '#EF4444', label: 'Bloqueado (vencido)' }, emAndamento: { color: '#F59E0B', label: 'Em andamento' }, semPlano: { color: '#9CA3AF', label: 'Sem plano' }, }; const maxValor = Math.max(...vt.ganhos.map(g => g.valor)); return (
Ganhos Identificados por Status
{vt.ganhos.map((g, i) => { const stat = STATUS_COLOR[g.status] || STATUS_COLOR.semPlano; const pct = (g.valor / maxValor) * 100; return (
{g.label}
); })}
{Object.values(STATUS_COLOR).map((s, i) => ( {s.label} ))}
Composição do Pipeline
{vt.composicao.map((c, i) => ( {c.label} ))}
); } function VTDonut({ data }) { const size = 160; const r = 60; const cx = size / 2, cy = size / 2; const total = data.reduce((s, d) => s + d.pct, 0); let acc = 0; return ( {data.map((d, i) => { const startAngle = (acc / total) * 2 * Math.PI - Math.PI / 2; const endAngle = ((acc + d.pct) / total) * 2 * Math.PI - Math.PI / 2; acc += d.pct; const x1 = cx + r * Math.cos(startAngle); const y1 = cy + r * Math.sin(startAngle); const x2 = cx + r * Math.cos(endAngle); const y2 = cy + r * Math.sin(endAngle); const largeArc = (endAngle - startAngle) > Math.PI ? 1 : 0; const path = `M ${cx} ${cy} L ${x1} ${y1} A ${r} ${r} 0 ${largeArc} 1 ${x2} ${y2} Z`; return ; })} ); } /* ================================================================== SUMMARY by status (Ganhos / Planos) ================================================================== */ function VTSummary({ vt, state }) { return (
); } function SummaryPanel({ title, items, idPrefix }) { const total = items.reduce((s, x) => s + x.count, 0); const max = Math.max(...items.map(x => x.count)); return (
{title} Total: {total}
{items.map((it, i) => (
))}
); } Object.assign(window, { PreviewRoAI, VT_COLUMNS });