// 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)=>(
))}
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}
);
})}
{/* 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 });