// Dashboard View const Dashboard = ({ leads, onOpenLead }) => { const ativos = leads.filter((l) => l.stage !== 'Fechado'); const fechados = leads.filter((l) => l.stage === 'Fechado'); const totalPipeline = ativos.reduce((s, l) => s + l.valor, 0); const totalFechado = fechados.reduce((s, l) => s + l.valor, 0); const taxaConv = leads.length ? Math.round(fechados.length / leads.length * 100) : 0; const hoje = new Date().toISOString().split('T')[0]; const semanaAtras = new Date(Date.now() - 7 * 86400000).toISOString().split('T')[0]; const novos = leads.filter((l) => l.entrada >= semanaAtras).length; const followups = leads.filter((l) => l.followup && l.followup <= hoje).length; const porEtapa = STAGES.map((s) => ({ stage: s, count: leads.filter((l) => l.stage === s).length, valor: leads.filter((l) => l.stage === s).reduce((a, l) => a + l.valor, 0) })); const maxCount = Math.max(...porEtapa.map((e) => e.count), 1); const porOrigem = ORIGENS.map((o) => ({ origem: o, count: leads.filter((l) => l.origem === o).length })).filter((o) => o.count > 0).sort((a, b) => b.count - a.count); const porMes = (() => { const map = {}; leads.forEach((l) => { const m = l.entrada.slice(0, 7); map[m] = (map[m] || 0) + 1; }); return Object.entries(map).sort().slice(-6).map(([m, c]) => { const [y, mo] = m.split('-'); const meses = ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez']; return { label: meses[parseInt(mo) - 1], count: c }; }); })(); const maxMes = Math.max(...porMes.map((m) => m.count), 1); const metricCards = [ { label: 'Total de Leads Ativos', value: ativos.length, sub: `${leads.length} total`, color: '#4f7cff', icon: '◈' }, { label: 'Valor do Pipeline', value: formatCurrency(totalPipeline), sub: `${formatCurrency(totalFechado)} fechado`, color: '#4ade80', icon: '◉' }, { label: 'Taxa de Conversão', value: taxaConv, sub: `${fechados.length} de ${leads.length} leads`, color: '#a78bfa', icon: '◈' }, { label: 'Leads Novos (7d)', value: novos, sub: 'últimos 7 dias', color: '#38bdf8', icon: '◇' }, { label: 'Follow-ups Pendentes', value: followups, sub: 'até hoje', color: followups > 0 ? '#facc15' : '#4ade80', icon: '◆' }]; return (
Dashboard
Visão geral do funil de vendas
{/* Metric cards */}
{metricCards.map((c, i) =>
{c.icon}
{c.value}
{c.label}
{c.sub}
)}
{/* Por etapa */}
Leads por Etapa
{porEtapa.map(({ stage, count, valor }) =>
{stage} {count} lead{count !== 1 ? 's' : ''} · {formatCurrency(valor)}
)}
{/* Por mês */}
Entradas por Mês
{porMes.map(({ label, count }) =>
{count}
{label}
)}
{/* Por origem */}
Origem dos Leads
{porOrigem.map(({ origem, count }) =>
{origem}
{count}
)}
{/* Follow-ups table */}
Follow-ups Pendentes
{['Empresa', 'Responsável', 'Etapa', 'Valor', 'Follow-up'].map((h) => )} {leads.filter((l) => l.followup).sort((a, b) => a.followup.localeCompare(b.followup)).slice(0, 6).map((l) => onOpenLead(l)}> )}
{h}
{l.empresa} {l.responsavel} {l.stage} {formatCurrency(l.valor)} {formatDate(l.followup)}
); }; const ds = { page: { flex: 1, overflowY: 'auto', padding: '24px 28px', background: '#0d1020' }, header: { display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 24 }, pageTitle: { fontSize: 22, fontWeight: 700, color: '#e8eaf0' }, pageSubtitle: { fontSize: 13, color: '#4a4f6a', marginTop: 3 }, metricsRow: { display: 'flex', gap: 14, marginBottom: 20, flexWrap: 'wrap' }, metricCard: { flex: '1 1 160px', background: '#151828', border: '1px solid #1e2235', borderRadius: 12, padding: '16px 18px', display: 'flex', flexDirection: 'column', gap: 4 }, metricIcon: { width: 32, height: 32, borderRadius: 8, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 16, marginBottom: 4 }, metricValue: { fontSize: 22, fontWeight: 800, color: '#e8eaf0' }, metricLabel: { fontSize: 11, color: '#8b90a7', fontWeight: 500, lineHeight: 1.3 }, metricSub: { fontSize: 10, color: '#4a4f6a', marginTop: 2 }, chartsRow: { display: 'flex', gap: 14, marginBottom: 20, flexWrap: 'wrap' }, chartCard: { flex: '1 1 260px', background: '#151828', border: '1px solid #1e2235', borderRadius: 12, padding: '18px 20px' }, chartTitle: { fontSize: 13, fontWeight: 700, color: '#c7d2ff', letterSpacing: 0.3 }, barBg: { height: 5, background: '#1e2235', borderRadius: 3, overflow: 'hidden' }, barFill: { height: '100%', borderRadius: 3, transition: 'width 0.4s' }, th: { fontSize: 10, color: '#4a4f6a', fontWeight: 700, letterSpacing: 0.8, padding: '6px 10px', textAlign: 'left', borderBottom: '1px solid #1e2235' }, td: { fontSize: 12, color: '#8b90a7', padding: '10px 10px', borderBottom: '1px solid #111525' }, tr: { cursor: 'pointer', transition: 'background 0.1s' }, badge: { fontSize: 10, fontWeight: 700, padding: '2px 8px', borderRadius: 20, display: 'inline-block' } }; Object.assign(window, { Dashboard });