// Reports Module function Reports() { const [period, setPeriod] = React.useState('6m'); const [events, setEvents] = React.useState([]); const [budgets, setBudgets] = React.useState([]); const [freelancers, setFreelancers] = React.useState([]); const [loading, setLoading] = React.useState(true); const [toast, setToast] = React.useState(null); const showToast = (msg) => { setToast({ msg }); setTimeout(() => setToast(null), 3000); }; React.useEffect(() => { Promise.all([ sb.from('events').select('*').order('event_date', { ascending: false }), sb.from('budgets').select('*').order('issued_date', { ascending: false }), sb.from('freelancers').select('*'), ]).then(([ev, bud, fr]) => { setEvents(ev.data || []); setBudgets(bud.data || []); setFreelancers(fr.data || []); setLoading(false); }); }, []); const fmtMoney = (v) => { const n = parseFloat(v) || 0; if (n >= 1000000) return `R$ ${(n / 1000000).toFixed(1)}M`; if (n >= 1000) return `R$ ${(n / 1000).toFixed(0)}k`; return `R$ ${n.toLocaleString('pt-BR')}`; }; const now = new Date(); // Compute revenue by last 6 months from approved budgets const revenueByMonth = (() => { const months = period === '3m' ? 3 : period === '12m' ? 12 : 6; const result = []; for (let i = months - 1; i >= 0; i--) { const d = new Date(now.getFullYear(), now.getMonth() - i, 1); const label = d.toLocaleDateString('pt-BR', { month: 'short' }).replace('.', ''); const yStart = d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-01'; const last = new Date(d.getFullYear(), d.getMonth() + 1, 0); const yEnd = last.getFullYear() + '-' + String(last.getMonth() + 1).padStart(2, '0') + '-' + String(last.getDate()).padStart(2, '0'); const total = budgets .filter(b => b.status === 'Aprovado' && b.issued_date && b.issued_date >= yStart && b.issued_date <= yEnd) .reduce((s, b) => s + (parseFloat(b.total) || 0), 0); result.push({ label, value: total }); } return result; })(); // KPIs from real data const approvedBudgets = budgets.filter(b => b.status === 'Aprovado'); const totalRevenue = approvedBudgets.reduce((s, b) => s + (parseFloat(b.total) || 0), 0); const completedEvents = events.filter(e => e.status === 'Concluído').length; const activeFreelancers = freelancers.filter(f => f.status === 'Ativo' || f.status === 'Escalado').length; const pendingBudgets = budgets.filter(b => b.status === 'Pendente' || b.status === 'Em análise').length; const kpis = [ { label: 'Receita aprovada', value: totalRevenue > 0 ? fmtMoney(totalRevenue) : '—', color: DS.colors.primary, icon: 'budget' }, { label: 'Eventos concluídos', value: String(completedEvents), color: DS.colors.success, icon: 'calendar' }, { label: 'Freelancers ativos', value: String(activeFreelancers), color: '#8B5CF6', icon: 'users' }, { label: 'Orçamentos pendentes', value: String(pendingBudgets), color: DS.colors.warning, icon: 'clock' }, ]; // Top events by budget const topEvents = [...events] .sort((a, b) => (parseFloat(b.budget_estimate || b.budget || 0)) - (parseFloat(a.budget_estimate || a.budget || 0))) .slice(0, 5); // Category breakdown from events const CAT_COLORS = ['#3B82F6', '#10B981', '#8B5CF6', '#F59E0B', '#EF4444']; const catCounts = {}; const catRevenue = {}; events.forEach(e => { const t = e.event_type || e.type || 'Outros'; catCounts[t] = (catCounts[t] || 0) + 1; catRevenue[t] = (catRevenue[t] || 0) + (parseFloat(e.budget_estimate || e.budget || 0)); }); const totalCatRevenue = Object.values(catRevenue).reduce((s, v) => s + v, 0) || 1; const categoryBreakdown = Object.entries(catRevenue) .sort((a, b) => b[1] - a[1]) .map(([label, value], i) => ({ label, value, pct: Math.round((value / totalCatRevenue) * 100), color: CAT_COLORS[i % CAT_COLORS.length] })); // Freelancer performance from payments JSONB const freelancerPerf = freelancers .map(f => { const payments = Array.isArray(f.payments) ? f.payments : []; const totalPaid = payments.reduce((s, p) => s + (parseFloat(p.valor) || 0), 0); return { name: f.name, role: f.role || f.specialty || '—', events: payments.length, paid: totalPaid }; }) .filter(f => f.paid > 0 || f.events > 0) .sort((a, b) => b.paid - a.paid) .slice(0, 5); const statusVariant = { 'Confirmado': 'success', 'Em andamento': 'primary', 'Planejamento': 'warning', 'Concluído': 'success', 'Orçamento': 'default' }; if (loading) { return (
Análise de desempenho e resultados