"use client"; import { useState, useMemo } from "react"; import { PageLayout, LoadingState, PageHeader } from "@/components/layout"; import { StatsSummaryCards, MonthlyChart, CategoryPieChart, BalanceLineChart, TopExpensesList, } from "@/components/statistics"; import { useBankingData } from "@/lib/hooks"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; type Period = "3months" | "6months" | "12months" | "all"; export default function StatisticsPage() { const { data, isLoading } = useBankingData(); const [period, setPeriod] = useState("6months"); const [selectedAccount, setSelectedAccount] = useState("all"); const stats = useMemo(() => { if (!data) return null; const now = new Date(); let startDate: Date; switch (period) { case "3months": startDate = new Date(now.getFullYear(), now.getMonth() - 3, 1); break; case "6months": startDate = new Date(now.getFullYear(), now.getMonth() - 6, 1); break; case "12months": startDate = new Date(now.getFullYear(), now.getMonth() - 12, 1); break; default: startDate = new Date(0); } let transactions = data.transactions.filter( (t) => new Date(t.date) >= startDate ); if (selectedAccount !== "all") { transactions = transactions.filter( (t) => t.accountId === selectedAccount ); } // Monthly breakdown const monthlyData = new Map(); transactions.forEach((t) => { const monthKey = t.date.substring(0, 7); const current = monthlyData.get(monthKey) || { income: 0, expenses: 0 }; if (t.amount >= 0) { current.income += t.amount; } else { current.expenses += Math.abs(t.amount); } monthlyData.set(monthKey, current); }); const monthlyChartData = Array.from(monthlyData.entries()) .sort((a, b) => a[0].localeCompare(b[0])) .map(([month, values]) => ({ month: new Date(month + "-01").toLocaleDateString("fr-FR", { month: "short", year: "2-digit", }), revenus: Math.round(values.income), depenses: Math.round(values.expenses), solde: Math.round(values.income - values.expenses), })); // Category breakdown (expenses only) const categoryTotals = new Map(); transactions .filter((t) => t.amount < 0) .forEach((t) => { const catId = t.categoryId || "uncategorized"; const current = categoryTotals.get(catId) || 0; categoryTotals.set(catId, current + Math.abs(t.amount)); }); const categoryChartData = Array.from(categoryTotals.entries()) .map(([categoryId, total]) => { const category = data.categories.find((c) => c.id === categoryId); return { name: category?.name || "Non catégorisé", value: Math.round(total), color: category?.color || "#94a3b8", icon: category?.icon || "HelpCircle", }; }) .sort((a, b) => b.value - a.value) .slice(0, 8); // Top expenses const topExpenses = transactions .filter((t) => t.amount < 0) .sort((a, b) => a.amount - b.amount) .slice(0, 5); // Summary const totalIncome = transactions .filter((t) => t.amount >= 0) .reduce((sum, t) => sum + t.amount, 0); const totalExpenses = transactions .filter((t) => t.amount < 0) .reduce((sum, t) => sum + Math.abs(t.amount), 0); const avgMonthlyExpenses = monthlyData.size > 0 ? totalExpenses / monthlyData.size : 0; // Balance evolution - Aggregated const allTransactionsForBalance = data.transactions.filter( (t) => new Date(t.date) >= startDate ); const sortedAllTransactions = [...allTransactionsForBalance].sort( (a, b) => new Date(a.date).getTime() - new Date(b.date).getTime() ); let runningBalance = 0; const aggregatedBalanceByDate = new Map(); sortedAllTransactions.forEach((t) => { runningBalance += t.amount; aggregatedBalanceByDate.set(t.date, runningBalance); }); const aggregatedBalanceData = Array.from( aggregatedBalanceByDate.entries() ).map(([date, balance]) => ({ date: new Date(date).toLocaleDateString("fr-FR", { day: "2-digit", month: "short", }), solde: Math.round(balance), })); // Balance evolution - Per account const accountBalances = new Map>(); data.accounts.forEach((account) => { accountBalances.set(account.id, new Map()); }); // Calculate running balance per account const accountRunningBalances = new Map(); data.accounts.forEach((account) => { accountRunningBalances.set(account.id, 0); }); sortedAllTransactions.forEach((t) => { const currentBalance = accountRunningBalances.get(t.accountId) || 0; const newBalance = currentBalance + t.amount; accountRunningBalances.set(t.accountId, newBalance); const accountDates = accountBalances.get(t.accountId); if (accountDates) { accountDates.set(t.date, newBalance); } }); // Merge all dates and create data points const allDates = new Set(); accountBalances.forEach((dates) => { dates.forEach((_, date) => allDates.add(date)); }); const sortedDates = Array.from(allDates).sort(); const lastBalances = new Map(); data.accounts.forEach((account) => { lastBalances.set(account.id, 0); }); const perAccountBalanceData = sortedDates.map((date) => { const point: { date: string; [key: string]: string | number } = { date: new Date(date).toLocaleDateString("fr-FR", { day: "2-digit", month: "short", }), }; data.accounts.forEach((account) => { const accountDates = accountBalances.get(account.id); if (accountDates?.has(date)) { lastBalances.set(account.id, accountDates.get(date)!); } point[account.id] = Math.round(lastBalances.get(account.id) || 0); }); return point; }); return { monthlyChartData, categoryChartData, topExpenses, totalIncome, totalExpenses, avgMonthlyExpenses, aggregatedBalanceData, perAccountBalanceData, transactionCount: transactions.length, }; }, [data, period, selectedAccount]); const formatCurrency = (amount: number) => { return new Intl.NumberFormat("fr-FR", { style: "currency", currency: "EUR", }).format(amount); }; if (isLoading || !data || !stats) { return ; } return ( } />
); }