// Caririaçu Guincho — Dashboard Screen const { useState: useStateD, useEffect: useEffectD } = React; const MetricCard = ({ icon, iconColor, label, value, sub, onClick }) => ( { e.currentTarget.style.boxShadow = `0 8px 24px ${iconColor}25`; e.currentTarget.style.transform = 'translateY(-2px)'; e.currentTarget.style.borderColor = iconColor + '50'; } : undefined} onMouseLeave={onClick ? e => { e.currentTarget.style.boxShadow = '0 1px 3px rgba(0,0,0,0.07)'; e.currentTarget.style.transform = ''; e.currentTarget.style.borderColor = '#E2E8F0'; } : undefined}>
{value}
{label}
{sub &&
{sub}
}
); const DashboardScreen = ({ onNav }) => { const [loading, setLoading] = useStateD(true); const [assistencias, setAssistencias] = useStateD([]); const [motoristas, setMotoristas] = useStateD([]); const [transacoes, setTransacoes] = useStateD([]); const [frota, setFrota] = useStateD([]); const [backupBannerHidden, setBackupBannerHidden] = useStateD(false); useEffectD(() => { (async () => { const [a, m, t, f] = await Promise.all([ db.list('assistencias', { order: 'data' }), db.list('motoristas', { order: 'nome', asc: true }), db.list('transacoes', { order: 'data' }), db.list('frota', { order: 'code', asc: true }) ]); setAssistencias(a); setMotoristas(m); setTransacoes(t); setFrota(f); setLoading(false); })(); }, []); const fmtBRL = v => Number(v || 0).toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' }); const stats = { abertas: assistencias.filter(a => a.status === 'aberta').length, atendimento: assistencias.filter(a => a.status === 'atendimento').length, agendadas: assistencias.filter(a => a.status === 'agendada').length, finalizadas: assistencias.filter(a => a.status === 'finalizada').length, canceladas: assistencias.filter(a => a.status === 'cancelada').length, motoristasDisponiveis: motoristas.filter(m => m.status === 'disponivel').length, motoristasTotais: motoristas.length, ativas: assistencias.filter(a => a.status === 'aberta' || a.status === 'atendimento').length, }; const hojeStr = new Date().toISOString().slice(0,10); const transHoje = transacoes.filter(t => t.data === hojeStr); const entradasHoje = transHoje.filter(t => t.tipo === 'entrada').reduce((s,t) => s + Number(t.valor||0), 0); const saidasHoje = transHoje.filter(t => t.tipo === 'saida').reduce((s,t) => s + Number(t.valor||0), 0); const saldoDia = entradasHoje - saidasHoje; const alertas = [ ...assistencias.filter(a => a.status === 'aberta').slice(0, 3).map(a => ({ id: 'a' + a.id, type: 'danger', msg: `Assistência ${a.numero || '#' + a.id} aberta sem atendimento`, link: 'assistencias' })), ...frota.filter(f => f.status === 'manutencao').slice(0, 2).map(g => ({ id: 'f' + g.id, type: 'warning', msg: `Guincho ${g.code} (${g.placa}) em manutenção`, link: 'frota' })), ]; if (loading) return (
{[...Array(5)].map((_, i) => )}
); const isEmpty = assistencias.length === 0 && motoristas.length === 0 && frota.length === 0; const lastBackup = localStorage.getItem('caririacu_last_backup'); const diasDesdeBackup = lastBackup ? Math.floor((new Date() - new Date(lastBackup)) / (1000 * 60 * 60 * 24)) : null; const backupVencido = !lastBackup || diasDesdeBackup > 7; return (
{!isEmpty && backupVencido && !backupBannerHidden && ( onNav('configuracoes')} onMouseEnter={e => e.currentTarget.style.transform = 'translateY(-1px)'} onMouseLeave={e => e.currentTarget.style.transform = ''}>
{!lastBackup ? '⚠️ Você ainda não fez nenhum backup dos dados' : `⚠️ Último backup há ${diasDesdeBackup} dias — recomendado fazer um novo`}
Clique aqui para ir em Configurações → Backup e baixar um arquivo de segurança
)} {isEmpty && (
Bem-vindo ao Caririaçu Guincho!
Comece cadastrando seus motoristas e veículos da frota, depois registre as primeiras assistências.
onNav('motoristas')}>Cadastrar Motoristas
)}
onNav('assistencias')} /> onNav('assistencias')} /> onNav('assistencias')} /> onNav('assistencias')} /> onNav('assistencias')} />
onNav('motoristas')} onMouseEnter={e=>{ e.currentTarget.style.boxShadow='0 8px 24px rgba(16,185,129,0.2)'; e.currentTarget.style.transform='translateY(-2px)'; }} onMouseLeave={e=>{ e.currentTarget.style.boxShadow='0 1px 3px rgba(0,0,0,0.07)'; e.currentTarget.style.transform=''; }}>
Motoristas Disponíveis {stats.motoristasDisponiveis}/{stats.motoristasTotais}
{motoristas.slice(0, 6).map(m => (
w[0]).join('').slice(0,2).toUpperCase()} size={28} />
))} {motoristas.length === 0 && Sem motoristas cadastrados}
onNav('financeiro')} onMouseEnter={e=>{ e.currentTarget.style.boxShadow='0 8px 24px rgba(16,185,129,0.2)'; e.currentTarget.style.transform='translateY(-2px)'; }} onMouseLeave={e=>{ e.currentTarget.style.boxShadow='0 1px 3px rgba(0,0,0,0.07)'; e.currentTarget.style.transform=''; }}>
Saldo do Dia
= 0 ? '#10B981' : '#EF4444', fontFamily: 'DM Mono, monospace' }}> {fmtBRL(saldoDia)}
Entradas: {fmtBRL(entradasHoje)} · Saídas: {fmtBRL(saidasHoje)}
onNav('assistencias')} onMouseEnter={e=>{ e.currentTarget.style.boxShadow='0 8px 24px rgba(245,158,11,0.4)'; e.currentTarget.style.transform='translateY(-2px)'; }} onMouseLeave={e=>{ e.currentTarget.style.boxShadow='0 1px 3px rgba(0,0,0,0.07)'; e.currentTarget.style.transform=''; }}>
Assistências Ativas Agora
0 ? 'pulse 2s infinite' : 'none' }}>{stats.ativas}
em campo neste momento
{alertas.length > 0 && (
Alertas {alertas.length}
{alertas.map(a => (
onNav(a.link)} style={{ padding: '11px 20px', display: 'flex', alignItems: 'center', gap: 10, borderBottom: '1px solid #F8FAFC', cursor: 'pointer', transition: 'background 0.15s' }} onMouseEnter={e => e.currentTarget.style.background = '#FFF7ED'} onMouseLeave={e => e.currentTarget.style.background = ''}> {a.msg}
))}
)}
Últimas Assistências
onNav('assistencias')}>Ver todas →
{assistencias.length === 0 ? (
Nenhuma assistência registrada ainda
) : (
{['Nº','Placa','Cliente','Serviço','Motorista','Status','Valor'].map(h => ( ))} {assistencias.slice(0, 6).map((a, i) => ( onNav('assistencias')} onMouseEnter={e => e.currentTarget.style.background = '#EFF6FF'} onMouseLeave={e => e.currentTarget.style.background = i % 2 ? '#F8FAFC' : '#fff'}> ))}
{h}
{a.numero || `#${a.id}`} {a.placa || '—'} {a.cliente} {a.servico} {a.motorista_nome ? (
w[0]).join('').slice(0,2).toUpperCase()} size={24} /> {a.motorista_nome.split(' ')[0]}
) : }
R$ {Number(a.valor).toFixed(2)}
)}
); }; Object.assign(window, { DashboardScreen });