// Caririaçu Guincho — Tab Abastecimento const { useState: useStateTA, useEffect: useEffectTA, useMemo: useMemoTA } = React; const TabAbastecimento = ({ guincho, periodo }) => { const [items, setItems] = useStateTA([]); const [combustiveis, setCombustiveis] = useStateTA([]); const [postos, setPostos] = useStateTA([]); const [edit, setEdit] = useStateTA(null); const [tick, setTick] = useStateTA(0); useEffectTA(() => { sb.from('abastecimentos').select('*') .eq('guincho_id', guincho.id) .gte('data', periodo.inicio) .lte('data', periodo.fim + 'T23:59:59') .order('data', { ascending: false }) .then(r => setItems(r.data || [])); }, [guincho.id, periodo.inicio, periodo.fim, tick]); useEffectTA(() => { db.list('combustiveis', { eq: { ativo: true }, order: 'nome', asc: true }).then(setCombustiveis); db.list('postos', { eq: { ativo: true }, order: 'nome', asc: true }).then(setPostos); }, []); // calcula consumo desde o último tanque cheio para cada abastecimento const itemsComConsumo = useMemoTA(() => { const byKm = [...items].sort((a, b) => a.km - b.km); const out = byKm.map((x, i) => { if (!x.tanque_cheio) return { ...x, consumo: null }; const anterior = byKm.slice(0, i).reverse().find(y => y.tanque_cheio); if (!anterior) return { ...x, consumo: null }; const litrosEntre = byKm .filter(y => y.km > anterior.km && y.km <= x.km) .reduce((s, y) => s + Number(y.litros), 0); const kmEntre = x.km - anterior.km; return { ...x, consumo: (litrosEntre > 0 && kmEntre > 0) ? kmEntre / litrosEntre : null }; }); return out.sort((a, b) => new Date(b.data) - new Date(a.data)); }, [items]); const metricas = useMemoTA(() => { const total = items.reduce((s, x) => s + Number(x.valor_total || 0), 0); const volume = items.reduce((s, x) => s + Number(x.litros || 0), 0); const consumos = itemsComConsumo.map(x => x.consumo).filter(c => c != null); const consumoMedio = consumos.length ? consumos.reduce((s, c) => s + c, 0) / consumos.length : null; const kms = items.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; return { total, volume, consumoMedio, custoKm: kmRodado ? total / kmRodado : null }; }, [items, itemsComConsumo]); const dadosGrafico = useMemoTA(() => { const out = {}; items.forEach(x => { const k = x.data.slice(0, 7); out[k] = out[k] || { valor: 0 }; out[k].valor += Number(x.valor_total || 0); }); return out; }, [items]); const novo = () => setEdit({ data: new Date().toISOString().slice(0, 16), combustivel_id: combustiveis[0]?.id || null, combustivel: combustiveis[0]?.nome || '', litros: '', valor_litro: '', valor_total: '', km: guincho.km_atual || 0, tanque_cheio: true, posto_id: null, posto: '', forma_pagto: '', obs: '' }); const salvar = async () => { if (!edit.litros || !edit.valor_total || !edit.km) { showToast('Preencha litros, valor total e km', 'warning'); return; } const payload = { ...edit, guincho_id: guincho.id, litros: parseFloat(edit.litros), valor_litro: parseFloat(edit.valor_litro || (edit.valor_total / edit.litros)), valor_total: parseFloat(edit.valor_total), km: parseInt(edit.km), }; const result = edit.id ? await db.update('abastecimentos', edit.id, payload) : await db.insert('abastecimentos', payload); if (!result) return; setEdit(null); setTick(t => t + 1); showToast('Abastecimento salvo', 'success'); }; const excluir = async () => { if (!confirm('Excluir este abastecimento? A transação financeira vinculada também será removida.')) return; const ok = await db.remove('abastecimentos', edit.id); if (!ok) return; setEdit(null); setTick(t => t + 1); showToast('Abastecimento excluído', 'info'); }; return ( <> {/* Cards */}