// Wave 1 — Dashboard screens (4): Executivo, KPIs históricos, Churn Monitor, Relatórios // All wrap AppShell and share atoms from this file. // ---------- Shared page atoms ---------- const PageContainer = ({ title, subtitle, badge, actions, children, maxWidth = 1500 }) => (
{badge &&
{badge}
}

{title}

{subtitle &&

{subtitle}

}
{actions &&
{actions}
}
{children}
); const Tile = ({ icon, label, value, delta, deltaDir = 'up', sub, accent }) => (
{icon && } {label}
{value}
{delta && {delta}} {sub && {sub}}
); // Mini sparkline const Spark = ({ data, stroke = '#a78bfa', fill = 'rgba(139,92,246,.15)', height = 40 }) => { const max = Math.max(...data), min = Math.min(...data); const w = 120, h = height; const pts = data.map((v, i) => [i * (w / (data.length - 1)), h - ((v - min) / (max - min || 1)) * (h - 4) - 2]); const path = pts.map((p, i) => (i === 0 ? `M${p[0]},${p[1]}` : `L${p[0]},${p[1]}`)).join(' '); const area = path + ` L${w},${h} L0,${h} Z`; return ( ); }; // Donut chart const Donut = ({ segments, size = 140, thickness = 18 }) => { const r = (size - thickness) / 2; const c = 2 * Math.PI * r; const total = segments.reduce((s, x) => s + x.value, 0); let offset = 0; return ( {segments.map((s, i) => { const len = (s.value / total) * c; const dash = `${len} ${c - len}`; const el = ; offset += len; return el; })} ); }; // Bar chart const Bars = ({ series, labels, height = 200 }) => { const max = Math.max(...series.flatMap(s => s.data)); return (
{labels.map((lbl, i) => (
{series.map((s, si) => (
))}
{lbl}
))}
{series.map((s, i) => (
{s.name}
))}
); }; // ==================== 1. Dashboard Executivo ==================== const DashExecutivo = ({ onNavigate }) => ( Consolidado pela IA · atualizado há 12 min} title="Dashboard Executivo" subtitle="Visão estratégica consolidada de todas as áreas — use para reuniões de diretoria e tomada de decisão semanal." actions={<> } > {/* Top KPIs */}
{/* Revenue trend + breakdown */}
Receita × Despesa · últimos 6 meses
Mensal
Receita por módulo
{[ { lbl: 'CRM & Vendas', v: '42%', c: '#8b5cf6' }, { lbl: 'PDV Fiscal', v: '28%', c: '#3b82f6' }, { lbl: 'Assinaturas', v: '18%', c: '#06b6d4' }, { lbl: 'Serviços', v: '12%', c: '#22c55e' }, ].map(x => (
{x.lbl} {x.v}
))}
{/* AI insights */}
Insights da IA · 4 sugestões para esta semana
{[ { icon: 'trending-up', title: 'Oportunidade de upsell em 12 clientes', sub: 'Usuários no plano Essencial já usam 80% dos recursos — sugira upgrade para Profissional. Potencial +R$ 14.880 MRR.' }, { icon: 'alert', title: '3 clientes com alto risco de churn', sub: 'Distribuidora RC parou de emitir NF-e há 22 dias. Agende follow-up imediato.' }, { icon: 'dollar', title: '8 faturas em atraso somam R$ 5.850', sub: 'Enviar lembrete automático por WhatsApp poderia recuperar ~60% em 72h.' }, { icon: 'users', title: 'Contratação acima da média no setor', sub: 'Você contratou 4 CLTs este trimestre (média do setor: 1,2). FGTS provisionado OK.' }, ].map((x, i) => (
{x.title}
{x.sub}
))}
{/* Per-module quick strip */}
{[ { label: 'CRM · pipeline', value: 'R$ 148.900', sub: '28 negócios em aberto · conversão 32,4%', spark: [20,22,28,34,40,44,48], color: '#8b5cf6', to: 'crm' }, { label: 'Financeiro · MRR', value: 'R$ 87.340', sub: 'Próximos 30d: R$ 91.200 previsto', spark: [60,64,68,72,75,82,87], color: '#3b82f6', to: 'fluxo' }, { label: 'RH · folha abril', value: 'R$ 68.420', sub: '17 CLTs · eSocial OK · fechamento dia 25', spark: [58,60,63,66,67,68,68], color: '#22c55e', to: 'folha' }, { label: 'Suporte · SLA', value: '96,4%', sub: '12 tickets abertos · 4,8/5 CSAT', spark: [92,94,93,95,96,96,97], color: '#06b6d4', to: 'tickets' }, ].map(m => ( ))}
); // ==================== 2. KPIs Históricos ==================== const DashKPIs = ({ onNavigate }) => { const [range, setRange] = React.useState('12m'); const [kpi, setKpi] = React.useState('mrr'); const kpis = { mrr: { label: 'MRR (Receita recorrente)', unit: 'R$', data: [42,46,51,55,58,62,64,68,71,75,82,87], goal: 85 }, cac: { label: 'CAC (Custo por aquisição)', unit: 'R$', data: [420,410,395,388,372,360,355,348,340,335,328,322], goal: 350 }, ltv: { label: 'LTV (Lifetime Value)', unit: 'R$', data: [2800,2850,2920,2980,3040,3110,3180,3250,3320,3410,3480,3570], goal: 3500 }, nps: { label: 'NPS', unit: '', data: [42,44,48,51,53,55,58,62,64,66,68,71], goal: 70 }, churn: { label: 'Churn mensal', unit: '%', data: [4.2,4.0,3.8,3.5,3.4,3.1,2.9,2.8,2.6,2.4,2.2,2.1], goal: 3.0 }, }; const labels12 = ['Mai/25','Jun','Jul','Ago','Set','Out','Nov','Dez','Jan/26','Fev','Mar','Abr']; const cur = kpis[kpi]; const last = cur.data[cur.data.length - 1]; const first = cur.data[0]; const delta = (((last - first) / first) * 100).toFixed(1); return (
{['3m','6m','12m','24m'].map(r => ( ))}
} > {/* KPI selector chips */}
{Object.entries(kpis).map(([id, k]) => ( ))}
{/* Main chart */}
{cur.label}
Últimos 12 meses · atualizado em 17/abr
{cur.unit === 'R$' ? BRLShort(last * 1000) : last + cur.unit}
= 0 ? 'up' : 'down'}`}> = 0 ? 'arrowUp' : 'arrowDown'} size={11}/> {delta > 0 ? '+' : ''}{delta}% vs. 12m atrás
{/* Secondary strip */}
); }; const KPILineChart = ({ data, labels, goal, unit }) => { const max = Math.max(...data, goal) * 1.1; const min = Math.min(...data, goal) * 0.9; const w = 1000, h = 240; const pad = 30; const pts = data.map((v, i) => [pad + i * ((w - pad * 2) / (data.length - 1)), h - pad - ((v - min) / (max - min)) * (h - pad * 2)]); const path = pts.map((p, i) => (i === 0 ? `M${p[0]},${p[1]}` : `L${p[0]},${p[1]}`)).join(' '); const area = path + ` L${pts[pts.length-1][0]},${h-pad} L${pts[0][0]},${h-pad} Z`; const goalY = h - pad - ((goal - min) / (max - min)) * (h - pad * 2); return ( {[0,1,2,3].map(i => { const y = pad + i * ((h - pad*2) / 3); return ; })} meta {pts.map((p, i) => ( {labels[i]} ))} ); }; // ==================== 3. Churn Monitor ==================== const DashChurn = ({ onNavigate }) => { const risks = [ { id: 1, client: 'Distribuidora RC Ltda', score: 87, level: 'alto', mrr: 3200, days: 22, reason: 'Parou de emitir NF-e · Último login há 22 dias · Contrato vence em 18 dias', owner: 'Bruno Tavares', actions: ['Ligar', 'WhatsApp'] }, { id: 2, client: 'Construtora AV', score: 74, level: 'alto', mrr: 2400, days: 15, reason: 'Uso do PDV caiu 68% · Abriu 3 tickets de suporte esta semana', owner: 'Camila Rocha', actions: ['Ligar'] }, { id: 3, client: 'Mecânica Rápida', score: 71, level: 'alto', mrr: 540, days: 18, reason: 'Boleto atrasado em 14 dias · Não responde mensagens', owner: 'João Pedro', actions: ['Cobrar'] }, { id: 4, client: 'Pet Shop Amigo Fiel', score: 58, level: 'medio', mrr: 680, days: 8, reason: 'Downgrade solicitado no último contato', owner: 'Camila Rocha', actions: ['Retenção'] }, { id: 5, client: 'Hamburgueria 22', score: 52, level: 'medio', mrr: 1240, days: 5, reason: 'Baixa adoção de funcionalidades novas (IA Marketing)', owner: 'João Pedro', actions: [] }, { id: 6, client: 'Farmácia Bem Estar', score: 48, level: 'medio', mrr: 960, days: 3, reason: 'Pediu integração com sistema legado', owner: 'Bruno Tavares', actions: [] }, ]; const levelColor = (l) => l === 'alto' ? '#ef4444' : l === 'medio' ? '#f59e0b' : '#22c55e'; return ( ML atualizado há 6h · 284 clientes analisados} title="Churn Monitor" subtitle="Clientes em risco de cancelamento identificados pelo modelo de machine learning. Aja nos últimos 30 dias para reverter." actions={<> } >
Clientes em risco
Ordenado por score de churn
{risks.map(r => ( ))}
Cliente Score MRR Inatividade Motivo principal Responsável Ações
{r.client}
{r.score}
{BRL(r.mrr)} {r.days} dias {r.reason}
{r.owner.split(' ')[0]}
Principais fatores no modelo de churn
Importância relativa
{[ { f: 'Dias sem login', w: 92 }, { f: 'Queda de uso de módulos', w: 78 }, { f: 'Tickets de suporte abertos', w: 64 }, { f: 'Atraso em pagamentos', w: 56 }, { f: 'NPS da última pesquisa', w: 44 }, { f: 'Tempo de contrato restante', w: 31 }, ].map((f, i) => (
{f.f}
{f.w}%
))}
); }; // ==================== 4. Relatórios ==================== const DashRelatorios = ({ onNavigate }) => { const categories = [ { id: 'fin', label: 'Financeiro', icon: 'wallet', count: 12, color: '#8b5cf6' }, { id: 'crm', label: 'Comercial', icon: 'target', count: 9, color: '#3b82f6' }, { id: 'rh', label: 'RH & Folha', icon: 'briefcase', count: 8, color: '#22c55e' }, { id: 'fis', label: 'Fiscal', icon: 'shield', count: 6, color: '#f59e0b' }, { id: 'op', label: 'Operacional', icon: 'grid', count: 5, color: '#06b6d4' }, ]; const [active, setActive] = React.useState('fin'); const reportsByCategory = { fin: [ { name: 'DRE Gerencial', desc: 'Demonstrativo de resultados consolidado por centro de custo', freq: 'Mensal', last: '01/04/26' }, { name: 'Fluxo de Caixa Realizado vs Projetado', desc: 'Comparação de entradas e saídas', freq: 'Semanal', last: '15/04/26' }, { name: 'Inadimplência por Cliente', desc: 'Faturas em atraso agrupadas', freq: 'Diário', last: '18/04/26' }, { name: 'Conciliação OFX', desc: 'Divergências entre extrato e sistema', freq: 'Semanal', last: '15/04/26' }, { name: 'Margem por Produto/Serviço', desc: 'Rentabilidade por SKU', freq: 'Mensal', last: '01/04/26' }, ], crm: [ { name: 'Funil de Vendas', desc: 'Conversão por etapa do pipeline', freq: 'Semanal', last: '15/04/26' }, { name: 'Performance por Vendedor', desc: 'Metas, comissões, taxa de fechamento', freq: 'Mensal', last: '01/04/26' }, { name: 'Análise de Perdas', desc: 'Motivos de perda de oportunidades', freq: 'Mensal', last: '01/04/26' }, ], rh: [ { name: 'Folha de Pagamento CLT', desc: 'Fechamento mensal com encargos', freq: 'Mensal', last: '25/03/26' }, { name: 'eSocial — Eventos do Mês', desc: 'Eventos enviados e pendentes', freq: 'Mensal', last: '10/04/26' }, { name: 'Banco de Horas', desc: 'Saldo por funcionário', freq: 'Mensal', last: '01/04/26' }, ], fis: [ { name: 'NF-e Emitidas vs Canceladas', desc: 'Relatório fiscal mensal', freq: 'Mensal', last: '01/04/26' }, { name: 'SPED Fiscal', desc: 'Arquivos EFD ICMS/IPI', freq: 'Mensal', last: '01/04/26' }, ], op: [ { name: 'Projetos em Andamento', desc: 'Status de entrega e saúde', freq: 'Semanal', last: '15/04/26' }, { name: 'Tickets de Suporte', desc: 'Volume, SLA, CSAT', freq: 'Semanal', last: '15/04/26' }, ], }; return ( } > {/* Favoritos + recentes */}
Favoritos
{[ { name: 'DRE Gerencial · Abril/26', date: 'Executado há 3h' }, { name: 'Funil de Vendas · Semanal', date: 'Executado hoje' }, { name: 'Folha CLT · Março/26', date: 'Executado em 25/03' }, ].map((r, i) => (
{r.name}
{r.date}
))}
Agendados
{[ { name: 'DRE mensal', sched: 'Todo dia 1º · envio para financeiro@padaria...', next: 'Próximo: 01/mai' }, { name: 'Fluxo de caixa semanal', sched: 'Todas as segundas 08h', next: 'Próximo: 21/abr' }, { name: 'NPS mensal', sched: 'Último dia útil · CEO + diretoria', next: 'Próximo: 30/abr' }, ].map((r, i) => (
{r.name}
{r.sched}
{r.next.replace('Próximo: ', '')}
))}
{/* Category tabs + grid */}
{categories.map(c => ( ))}
{(reportsByCategory[active] || []).map((r, i) => (
{r.name}
{r.desc}
{r.freq} · Última exec: {r.last}
))}
); }; Object.assign(window, { DashExecutivo, DashKPIs, DashChurn, DashRelatorios, PageContainer, Tile, Spark, Donut, Bars, });