From ebf842cf6dd1d23fcac010989fb60dcfdd23a973 Mon Sep 17 00:00:00 2001 From: Julien Froidefond Date: Sun, 30 Nov 2025 12:19:34 +0100 Subject: [PATCH] feat: improve balance calculations in statistics page and charts with enhanced formatting for large values --- app/statistics/page.tsx | 77 ++++++++++++++++---- components/statistics/balance-line-chart.tsx | 13 +++- components/statistics/monthly-chart.tsx | 14 +++- 3 files changed, 87 insertions(+), 17 deletions(-) diff --git a/app/statistics/page.tsx b/app/statistics/page.tsx index 2c3eca7..cc9cf7e 100644 --- a/app/statistics/page.tsx +++ b/app/statistics/page.tsx @@ -181,7 +181,7 @@ export default function StatisticsPage() { .map(([month, values]) => ({ month: new Date(month + "-01").toLocaleDateString("fr-FR", { month: "short", - year: "2-digit", + year: "numeric", }), revenus: Math.round(values.income), depenses: Math.round(values.expenses), @@ -232,18 +232,40 @@ export default function StatisticsPage() { (a, b) => new Date(a.date).getTime() - new Date(b.date).getTime() ); - // Start with sum of initial balances for filtered accounts + // Calculate starting balance: initialBalance + transactions before startDate let runningBalance = 0; - if (selectedAccounts.includes("all")) { - runningBalance = data.accounts.reduce( - (sum, acc) => sum + (acc.initialBalance || 0), - 0, - ); - } else { - runningBalance = data.accounts - .filter((acc) => selectedAccounts.includes(acc.id)) - .reduce((sum, acc) => sum + (acc.initialBalance || 0), 0); - } + const accountsToUse = selectedAccounts.includes("all") + ? data.accounts + : data.accounts.filter((acc) => selectedAccounts.includes(acc.id)); + + // Start with initial balances + runningBalance = accountsToUse.reduce( + (sum, acc) => sum + (acc.initialBalance || 0), + 0, + ); + + // Add all transactions before the start date for these accounts + const accountsToUseIds = new Set(accountsToUse.map((a) => a.id)); + data.transactions + .filter((t) => { + // Filter by account + if (!accountsToUseIds.has(t.accountId)) return false; + // Filter by category if needed + if (!selectedCategories.includes("all")) { + if (selectedCategories.includes("uncategorized")) { + if (t.categoryId) return false; + } else { + if (!t.categoryId || !selectedCategories.includes(t.categoryId)) + return false; + } + } + // Only transactions before startDate + const transactionDate = new Date(t.date); + return transactionDate < startDate; + }) + .forEach((t) => { + runningBalance += t.amount; + }); const aggregatedBalanceByDate = new Map(); sortedFilteredTransactions.forEach((t) => { @@ -257,6 +279,7 @@ export default function StatisticsPage() { date: new Date(date).toLocaleDateString("fr-FR", { day: "2-digit", month: "short", + year: "numeric", }), solde: Math.round(balance), })); @@ -267,10 +290,31 @@ export default function StatisticsPage() { accountBalances.set(account.id, new Map()); }); - // Calculate running balance per account (start with initialBalance) + // Calculate running balance per account + // Start with initialBalance + transactions before startDate const accountRunningBalances = new Map(); data.accounts.forEach((account) => { - accountRunningBalances.set(account.id, account.initialBalance || 0); + let accountBalance = account.initialBalance || 0; + // Add transactions before startDate for this account + data.transactions + .filter((t) => { + if (t.accountId !== account.id) return false; + // Filter by category if needed + if (!selectedCategories.includes("all")) { + if (selectedCategories.includes("uncategorized")) { + if (t.categoryId) return false; + } else { + if (!t.categoryId || !selectedCategories.includes(t.categoryId)) + return false; + } + } + const transactionDate = new Date(t.date); + return transactionDate < startDate; + }) + .forEach((t) => { + accountBalance += t.amount; + }); + accountRunningBalances.set(account.id, accountBalance); }); sortedFilteredTransactions.forEach((t) => { @@ -292,8 +336,10 @@ export default function StatisticsPage() { const sortedDates = Array.from(allDates).sort(); const lastBalances = new Map(); + // Initialize with the starting balance (initialBalance + transactions before startDate) data.accounts.forEach((account) => { - lastBalances.set(account.id, account.initialBalance || 0); + const startingBalance = accountRunningBalances.get(account.id) || 0; + lastBalances.set(account.id, startingBalance); }); const perAccountBalanceData = sortedDates.map((date) => { @@ -301,6 +347,7 @@ export default function StatisticsPage() { date: new Date(date).toLocaleDateString("fr-FR", { day: "2-digit", month: "short", + year: "numeric", }), }; diff --git a/components/statistics/balance-line-chart.tsx b/components/statistics/balance-line-chart.tsx index dedcfc7..d9ce6a2 100644 --- a/components/statistics/balance-line-chart.tsx +++ b/components/statistics/balance-line-chart.tsx @@ -90,7 +90,18 @@ export function BalanceLineChart({ className="text-xs" interval="preserveStartEnd" /> - `${v}€`} /> + { + // Format compact pour les grandes valeurs + if (Math.abs(v) >= 1000) { + return `${(v / 1000).toFixed(1)}k€`; + } + return `${Math.round(v)}€`; + }} + tick={{ fill: "var(--muted-foreground)" }} + /> formatCurrency(value)} contentStyle={{ diff --git a/components/statistics/monthly-chart.tsx b/components/statistics/monthly-chart.tsx index f5132d5..89adae6 100644 --- a/components/statistics/monthly-chart.tsx +++ b/components/statistics/monthly-chart.tsx @@ -37,9 +37,21 @@ export function MonthlyChart({ data, formatCurrency }: MonthlyChartProps) { - `${v}€`} /> + { + // Format compact pour les grandes valeurs + if (Math.abs(v) >= 1000) { + return `${(v / 1000).toFixed(1)}k€`; + } + return `${Math.round(v)}€`; + }} + tick={{ fill: "var(--muted-foreground)" }} + /> formatCurrency(value)} + labelFormatter={(label) => label} contentStyle={{ backgroundColor: "var(--card)", border: "1px solid var(--border)",