import React, { useState, useMemo } from 'react'; import { useCRM } from '../context/CRMContext'; import { BarChart3, TrendingUp, PieChart, Package, ArrowUpRight, ArrowDownRight, Download, Calendar, Filter, DollarSign, Activity, AlertCircle, Boxes } from 'lucide-react'; import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, LineChart, Line, AreaChart, Area } from 'recharts'; import clsx from 'clsx'; const Reports: React.FC = () => { const { orders, inventory, transactions, sales } = useCRM(); const [period, setPeriod] = useState<'30d' | '3m' | '6m' | 'ytd' | '1y' | 'all'>('30d'); const [activeTab, setActiveTab] = useState<'financial' | 'products' | 'inventory' | 'sales'>('financial'); // --- FILTERING LOGIC --- const filteredSales = useMemo(() => { const now = new Date(); const cutoff = new Date(); if (period === '30d') cutoff.setDate(now.getDate() - 30); else if (period === '3m') cutoff.setMonth(now.getMonth() - 3); else if (period === '6m') cutoff.setMonth(now.getMonth() - 6); else if (period === 'ytd') cutoff.setMonth(0, 1); // Jan 1st of current year else if (period === '1y') cutoff.setFullYear(now.getFullYear() - 1); else if (period === 'all') return sales; // No filter return sales.filter(s => new Date(s.date) >= cutoff); }, [sales, period]); // 1. Financial Metrics const financialStats = useMemo(() => { // Revenue const totalRevenue = filteredSales.reduce((acc, s) => acc + s.total, 0); // Cost of Goods Sold (COGS) based on captured costPrice at time of sale const totalCost = filteredSales.reduce((acc, s) => { const saleCost = s.items.reduce((iAcc, item) => iAcc + ((item.costPrice || 0) * item.quantity), 0); return acc + saleCost; }, 0); const totalProfit = totalRevenue - totalCost; const margin = totalRevenue > 0 ? (totalProfit / totalRevenue) * 100 : 0; return { totalRevenue, totalCost, totalProfit, margin }; }, [filteredSales]); // 2. Product/Arbitrage Metrics const productStats = useMemo(() => { const productPerformance: Record = {}; filteredSales.forEach(s => { s.items.forEach(item => { if (!productPerformance[item.name]) { productPerformance[item.name] = { profit: 0, revenue: 0, quantity: 0 }; } const itemRevenue = item.salePrice; const itemCost = item.costPrice || 0; productPerformance[item.name].profit += (itemRevenue - itemCost) * item.quantity; productPerformance[item.name].revenue += itemRevenue * item.quantity; productPerformance[item.name].quantity += item.quantity; }); }); const sortedProducts = Object.entries(productPerformance) .map(([name, stats]) => ({ name, ...stats })) .sort((a, b) => b.profit - a.profit); return { topProducts: sortedProducts.slice(0, 5), lowMarginProducts: sortedProducts.filter(p => p.revenue > 0 && (p.profit / p.revenue) < 0.10).slice(0, 5) }; }, [filteredSales]); // 3. Inventory Stats (Unchanged) const inventoryStats = useMemo(() => { const totalValue = inventory.reduce((acc, i) => acc + (i.avgCostBRL * i.quantity), 0); const totalItems = inventory.reduce((acc, i) => acc + i.quantity, 0); const deadStock = inventory.filter(i => i.quantity > 0).slice(0, 3); return { totalValue, totalItems, deadStock }; }, [inventory]); // 4. Chart Data - Responsive to Period const chartData = useMemo(() => { const dataMap: Record = {}; filteredSales.forEach(sale => { if (sale.status === 'Cancelled' || sale.status === 'Returned') return; const d = new Date(sale.date); // Dynamic grouping key let key = ''; if (period === '30d') { key = d.toLocaleDateString('pt-BR', { day: '2-digit', month: '2-digit' }); // Daily for 30d } else { key = d.toLocaleDateString('pt-BR', { month: 'short', year: '2-digit' }); // Monthly for others } if (!dataMap[key]) dataMap[key] = { receita: 0, lucro: 0, date: d.getTime() }; const saleCost = sale.items.reduce((acc, item) => acc + ((item.costPrice || 0) * item.quantity), 0); dataMap[key].receita += sale.total; dataMap[key].lucro += (sale.total - saleCost); }); return Object.entries(dataMap) .map(([name, vals]) => ({ name, ...vals })) .sort((a, b) => a.date - b.date); // Sort chronologically }, [filteredSales, period]); // Recalc on filter change // --- EXPORT --- const handleExport = () => { const csvContent = "data:text/csv;charset=utf-8," + "Categoria,Valor\n" + `Receita Total,${financialStats.totalRevenue}\n` + `Custo Total,${financialStats.totalCost}\n` + `Lucro Liquido,${financialStats.totalProfit}\n`; const encodedUri = encodeURI(csvContent); const link = document.createElement("a"); link.setAttribute("href", encodedUri); link.setAttribute("download", "relatorio_arbitra.csv"); document.body.appendChild(link); link.click(); }; return (
{/* HEADER */}

Relatórios & Analytics

Visão geral de performance, financeiro e estoque.

{[ { id: '30d', label: '30D' }, { id: '3m', label: '3M' }, { id: '6m', label: '6M' }, { id: 'ytd', label: 'YTD' }, { id: '1y', label: '1 Ano' }, { id: 'all', label: 'Tudo' } ].map((p) => ( ))}
{/* KPI CARDS */}
{[ { label: 'Receita Total', value: `R$ ${financialStats.totalRevenue.toLocaleString('pt-BR')}`, icon: DollarSign, color: 'text-emerald-400', sub: '+12% vs. mês anterior' }, { label: 'Lucro Líquido', value: `R$ ${financialStats.totalProfit.toLocaleString('pt-BR')}`, icon: TrendingUp, color: 'text-indigo-400', sub: `${financialStats.margin.toFixed(1)}% Margem` }, { label: 'Valor em Estoque', value: `R$ ${inventoryStats.totalValue.toLocaleString('pt-BR')}`, icon: Package, color: 'text-blue-400', sub: `${inventoryStats.totalItems} iten(s)` }, { label: 'Pedidos Realizados', value: orders.length, icon: Activity, color: 'text-amber-400', sub: 'Volume total' }, ].map((stat, idx) => (
+2.5%

{stat.value}

{stat.label}

{stat.sub}

))}
{/* TABS */}
{[ { id: 'financial', label: 'Financeiro', icon: DollarSign }, { id: 'products', label: 'Produtos', icon: Package }, { id: 'inventory', label: 'Estoque', icon: Boxes }, { id: 'sales', label: 'Vendas', icon: BarChart3 } ].map(tab => ( ))}
{/* CONTENT AREA */}
{/* LEFT: MAIN CHART using Recharts */}

Performance Financeira

`R$${value / 1000}k`} />
{/* RIGHT: METRIC LISTS */}
{/* TOP PRODUCTS */}

Produtos + Lucrativos

{productStats.topProducts.length > 0 ? productStats.topProducts.map((p, i) => (
{i + 1}

{p.name}

{p.quantity} vendidos

R$ {p.profit.toLocaleString('pt-BR', { maximumFractionDigits: 0 })}
)) : (

Sem dados de venda.

)}
{/* ALERTS */}

Alertas de Estoque

Baixo Giro

3 produtos não vendem há 30 dias.

Reabastecer

5 SKUs abaixo do estoque mínimo.

); }; export default Reports;