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