/* Dashboard Executivo — CEO / Sócios */ const { useState:dS, useMemo:dM } = React; function Dashboard({profile, onToast}){ const [period,setPeriod]=dS('mes'); const [units,setUnits]=dS([]); // [] = todas const restricted = profile.unit ? [profile.unit] : null; // gestor vê só a unidade const activeUnits = restricted || (units.length?units:MX.units.map(u=>u.id)); const isAll = !restricted && units.length===0; const pf = period==='mes'?1 : period==='tri'?2.96 : 7.0; const pLabel = period==='mes'?'no mês':period==='tri'?'no trimestre':'no ano'; const prevLabel = period==='mes'?'vs. abril':period==='tri'?'vs. tri. anterior':'vs. 2025'; const selUnits = MX.units.filter(u=>activeUnits.includes(u.id)); const fatBase = selUnits.reduce((a,u)=>a+u.fatMes,0); const metaBase = selUnits.reduce((a,u)=>a+u.metaMes,0); const fat = Math.round(fatBase*pf); const meta = Math.round(metaBase*pf); const casosAtivos = selUnits.reduce((a,u)=>a+u.casosAtivos,0); const casosEnc = selUnits.reduce((a,u)=>a+u.casosEnc,0); const share = fatBase / MX.units.reduce((a,u)=>a+u.fatMes,0); // série da linha const consolSerie = activeUnits.reduce((acc,id)=>{ const s=MX.seriePorUnidade[id]; return acc.map((v,i)=>v+s[i]); }, new Array(MX.meses.length).fill(0)); const metaSerie = MX.serieMetaConsol.map(v=>Math.round(v*share)); const anoAntSerie = MX.serieAnoAnterior.map(v=>Math.round(v*share)); const lineSeries=[ {name:'Realizado', data:consolSerie, color:'#b68b4c', fill:true}, {name:'Meta', data:metaSerie, color:'#3a4159', dashed:true}, {name:'Ano anterior', data:anoAntSerie, color:'#c2b9a6', dashed:true}, ]; const unitBars = MX.units.map(u=>({label:u.nome.split(' ')[0], value:Math.round(u.fatMes*pf), color: activeUnits.includes(u.id)? u.cor : '#dcd5c7'})); const areaDonut = MX.areas.map(a=>({label:a.nome, value:Math.round(a.fatMes*pf*share), color:a.cor})); const ranked = [...MX.people].filter(p=>activeUnits.includes(p.unit)).sort((a,b)=>b.fatMes-a.fatMes).slice(0,7); const maxRank = ranked.length?ranked[0].fatMes:1; const casosFiltrados = MX.casos.filter(c=>activeUnits.includes(c.unit)); const criticos = casosFiltrados.filter(c=>c.critico || c.status==='suspenso'); const f = (n)=>MX.fmtBRL(n,{compact:true}); const toggleUnit=(id)=> setUnits(prev=> prev.includes(id)? prev.filter(x=>x!==id) : [...prev,id]); return (
{/* Controles */}
{!restricted && ( )} {MX.units.map(u=>{ const on = restricted? u.id===restricted[0] : units.includes(u.id); const disabled = restricted && u.id!==restricted[0]; return ( ); })}
{[['mes','Mês'],['tri','Trimestre'],['ano','Ano']].map(([k,l])=>( ))}
{/* KPIs */}
{/* Linha + Gauge */}

Evolução do faturamento

{isAll?'Consolidado — todas as unidades':selUnits.map(u=>u.nome).join(', ')} · últimos 8 meses
{lineSeries.map((s,i)=>(
{s.name}
))}
MX.fmtBRL(v,{compact:true}).replace('R$ ','')}/>

Meta global

Realizado {MX.fmtBRL(fat)}
Meta {pLabel} {MX.fmtBRL(meta)}
Diferença =meta?'var(--pos)':'var(--neg)'}}>{MX.fmtBRL(fat-meta,{sign:true})}
{/* Unidades + Áreas */}

Faturamento por unidade

{pLabel.charAt(0).toUpperCase()+pLabel.slice(1)} · ranking entre filiais
MX.fmtBRL(v,{compact:true}).replace('R$ ','')} height={210}/>

Receita por área

Distribuição por especialidade
a+x.casos,0))} centerLabel="casos" fmt={f}/>
{/* Top colaboradores + Indicadores */}

Top colaboradores

Por faturamento {pLabel}
{ranked.map((p,i)=>(
{i+1}
{p.nome} {MX.fmtBRL(Math.round(p.fatMes*pf),{compact:true})}
{p.cargo} · {MX.unitById(p.unit).nome}
))}

Indicadores financeiros

{[ {l:'Realizado vs. a receber',v:f(Math.round(MX.totals.aReceber*share)),s:'em aberto'}, {l:'Ticket médio',v:f(MX.totals.ticketMedio),s:'por caso'}, {l:'Inadimplência',v:MX.totals.inadimplencia+'%',s:'da carteira'}, {l:'Casos encerrados',v:MX.fmtNum(casosEnc),s:pLabel}, ].map((x,i)=>(
{x.l}
{x.v}
{x.s}
))}

Casos que exigem atenção

{criticos.length} casos críticos ou suspensos
{criticos.slice(0,4).map(c=>(
{c.titulo}
{c.id} · {c.cliente}
{c.critico?'Crítico':'Suspenso'}
))}
); } window.Dashboard = Dashboard;