// Caririaçu Guincho — Tab Geral (dashboard do veículo) const { useState: useStateTG, useEffect: useEffectTG, useMemo: useMemoTG } = React; const TabGeral = ({ guincho, periodo }) => { const [dados, setDados] = useStateTG({ abast: [], desp: [], serv: [], rec: [] }); const [loading, setLoading] = useStateTG(true); useEffectTG(() => { let alive = true; (async () => { setLoading(true); const range = { gte: periodo.inicio, lte: periodo.fim + 'T23:59:59' }; const [abast, desp, serv, rec] = await Promise.all([ sb.from('abastecimentos').select('*') .eq('guincho_id', guincho.id).gte('data', range.gte).lte('data', range.lte), sb.from('despesas_veiculo').select('*') .eq('guincho_id', guincho.id).gte('data', range.gte).lte('data', range.lte), sb.from('manutencoes').select('*') .eq('guincho_id', guincho.id).gte('data', periodo.inicio).lte('data', periodo.fim), sb.from('assistencias').select('*') .eq('guincho_id', guincho.id).eq('status', 'finalizada') .gte('data', range.gte).lte('data', range.lte), ]); if (!alive) return; setDados({ abast: abast.data || [], desp: desp.data || [], serv: serv.data || [], rec: rec.data || [], }); setLoading(false); })(); return () => { alive = false; }; }, [guincho.id, periodo.inicio, periodo.fim]); const metricas = useMemoTG(() => { const sumAbast = dados.abast.reduce((s, x) => s + Number(x.valor_total || 0), 0); const sumDesp = dados.desp.reduce((s, x) => s + Number(x.valor || 0), 0); const sumServ = dados.serv.reduce((s, x) => s + Number(x.valor || 0), 0); const sumRec = dados.rec.reduce((s, x) => s + Number(x.valor || 0), 0); const custo = sumAbast + sumDesp + sumServ; const saldo = sumRec - custo; const dias = Math.max(1, Math.ceil((new Date(periodo.fim) - new Date(periodo.inicio)) / 86400000)); const kms = dados.abast.map(x => Number(x.km)).filter(k => k > 0).sort((a, b) => a - b); const kmRodado = kms.length >= 2 ? kms[kms.length - 1] - kms[0] : 0; const volume = dados.abast.reduce((s, x) => s + Number(x.litros || 0), 0); const consumo = kmRodado > 0 && volume > 0 ? kmRodado / volume : null; return { sumAbast, sumDesp, sumServ, sumRec, custo, saldo, dias, kmRodado, volume, consumo, saldoPorDia: saldo / dias, saldoPorKm: kmRodado ? saldo / kmRodado : null, custoPorDia: custo / dias, custoPorKm: kmRodado ? custo / kmRodado : null, recPorDia: sumRec / dias, recPorKm: kmRodado ? sumRec / kmRodado : null, }; }, [dados, periodo]); const dadosGrafico = useMemoTG(() => { const out = {}; const add = (data, key, val) => { const k = data.slice(0, 7); // YYYY-MM out[k] = out[k] || { abast: 0, desp: 0, receita: 0 }; out[k][key] += Number(val || 0); }; dados.abast.forEach(x => add(x.data, 'abast', x.valor_total)); dados.desp.forEach(x => add(x.data, 'desp', x.valor)); dados.rec.forEach(x => add(x.data, 'receita', x.valor)); return out; }, [dados]); if (loading) return