// SGAB v2 — Dashboard const { useState, useEffect } = React; function Dashboard({ user, toast, onNav }) { const db = SGAB.getDB(); const { fmt } = SGAB; const isAdmin = user.perfil === 'administrador'; // ── FINANCIAL KPIs ───────────────────────────────────────── const receber = db.financeiro.receber; const pagar = db.financeiro.pagar; const totalReceber = receber.filter(r=>r.status!=='Recebida').reduce((s,r)=>s+r.valor,0); const totalRecebido = receber.filter(r=>r.status==='Recebida').reduce((s,r)=>s+r.valor,0); const totalPagar = pagar.filter(p=>p.status==='Pendente').reduce((s,p)=>s+p.valor,0); const totalPago = pagar.filter(p=>p.status==='Paga').reduce((s,p)=>s+p.valor,0); const saldo = totalRecebido - totalPago; const osTotal = db.ordens.length; const osConcluidas = db.ordens.filter(o=>o.status==='Concluída').length; const osAgendadas = db.ordens.filter(o=>o.status==='Agendada').length; const alertas = SGAB.getAlertas(db); // Calibrações vencendo em 30 dias const calibVencendo = db.equipamentos.filter(eq => { const d = fmt.daysTo(eq.proxCalib); return d !== null && d <= 30; }).length; // Estoque abaixo do mínimo const estoqueBaixo = db.estoque.filter(p => p.qtd <= p.estoqMin).length; // Faturamento mensal simulado (últimos 6 meses) const meses = ['Dez','Jan','Fev','Mar','Abr','Mai']; const fatMensal = [1800, 2400, 1600, 3200, 2800, 3500]; const maxFat = Math.max(...fatMensal); // Ordens recentes const ordensRecentes = [...db.ordens].sort((a,b)=>b.data.localeCompare(a.data)).slice(0,5); // Próximas calibrações const proximasCalib = db.equipamentos .filter(eq=>eq.proxCalib) .sort((a,b)=>a.proxCalib.localeCompare(b.proxCalib)) .slice(0,4); const tiposOS = ['Calibração RBC','Calibração Rastreada','Manutenção Corretiva','Manutenção Preventiva']; const tipoColors = ['#1E6FD9','#16A34A','#D97706','#8B5CF6']; const tipoCount = tiposOS.map(t => db.ordens.filter(o=>o.tipo===t).length); const tipoMax = Math.max(...tipoCount, 1); return (
{/* ALERTAS TOP */} {alertas.length > 0 && (
Atenção necessária — {alertas.length} alerta(s)
{alertas.slice(0,4).map((al,i)=>{ const target = {calibracao:'equipamentos',receber:'financeiro',pagar:'financeiro',estoque:'estoque'}[al.tipo]; return onNav(target):undefined}/>; })}
{alertas.length > 4 && }
)} {/* KPI GRID */} {isAdmin ? (
r.status!=='Recebida').length} conta(s) pendente(s)`} icon="financeiro" color="#D97706" onClick={()=>onNav('financeiro')}/> onNav('financeiro')}/> =0?'#1E6FD9':'#DC2626'} onClick={()=>onNav('financeiro')}/> p.status==='Pendente').length} conta(s) pendente(s)`} icon="arrowDown" color="#DC2626" onClick={()=>onNav('financeiro')}/>
) : null}
onNav('ordens')}/> onNav('agenda')}/> onNav('equipamentos')}/> 0?'#DC2626':'#16A34A'} onClick={()=>onNav('estoque')}/>
{/* MAIN GRID */}
{/* FATURAMENTO CHART */} {isAdmin && (

Faturamento — Últimos 6 Meses

Valores recebidos por mês

Mai 2026
{meses.map((m,i)=>(
{fmt.money(fatMensal[i]).replace('R$\u00a0','').replace(',00','')}
{m}
))}
)} {/* TIPOS DE SERVIÇO */}

Serviços por Tipo

{tiposOS.map((t,i)=>(
{t} {tipoCount[i]}
))}
onNav('clientes')} onMouseEnter={e=>{e.currentTarget.style.transform='translateY(-1px)';e.currentTarget.style.boxShadow='0 4px 12px rgba(22,163,74,0.18)';}} onMouseLeave={e=>{e.currentTarget.style.transform='';e.currentTarget.style.boxShadow='';}} style={{padding:'10px 14px',borderRadius:10,background:'#F0FDF4',border:'1px solid #BBF7D0',cursor:'pointer',transition:'all 0.15s'}}>

Clientes

{db.clientes.filter(c=>c.ativo).length}

onNav('equipamentos')} onMouseEnter={e=>{e.currentTarget.style.transform='translateY(-1px)';e.currentTarget.style.boxShadow='0 4px 12px rgba(30,64,175,0.18)';}} onMouseLeave={e=>{e.currentTarget.style.transform='';e.currentTarget.style.boxShadow='';}} style={{padding:'10px 14px',borderRadius:10,background:'#EFF6FF',border:'1px solid #BFDBFE',cursor:'pointer',transition:'all 0.15s'}}>

Equipamentos

{db.equipamentos.filter(e=>e.ativo).length}

{/* ORDENS RECENTES */}

Últimas Ordens de Serviço

onNav('ordens')}>Ver todas
{ordensRecentes.map(os=>{ const cli = db.clientes.find(c=>c.id===os.clienteId); return (
onNav('ordens')} style={{display:'flex',alignItems:'center',gap:12,padding:'10px 8px',borderRadius:8,cursor:'pointer',transition:'background 0.1s',marginBottom:4}} onMouseEnter={e=>e.currentTarget.style.background='#F8FAFF'} onMouseLeave={e=>e.currentTarget.style.background=''}>
{os.numero}
{cli?.fantasia} — {os.tipo}
{fmt.date(os.data)}
); })}
{/* PRÓXIMAS CALIBRAÇÕES */}

Vencimento de Calibrações

onNav('equipamentos')}>Ver todos
{proximasCalib.map(eq=>{ const cli = db.clientes.find(c=>c.id===eq.clienteId); const dias = fmt.daysTo(eq.proxCalib); const cor = dias!==null&&dias<0?'#DC2626':dias!==null&&dias<=7?'#DC2626':dias!==null&&dias<=30?'#D97706':'#16A34A'; const bgCor = dias!==null&&dias<0?'#FEF2F2':dias!==null&&dias<=7?'#FEF2F2':dias!==null&&dias<=30?'#FFFBEB':'#F0FDF4'; return (
{eq.tag} — {eq.modelo}
{cli?.fantasia} • {eq.tipoCalib}
{dias===null?'—':dias<0?`${-dias}d vencida`:`${dias}d`}
{fmt.date(eq.proxCalib)}
); })}
); } Object.assign(window, { Dashboard });