feat: implement aggregated and per-account balance visualization in statistics page with interactive chart options
This commit is contained in:
@@ -119,27 +119,84 @@ export default function StatisticsPage() {
|
||||
const avgMonthlyExpenses =
|
||||
monthlyData.size > 0 ? totalExpenses / monthlyData.size : 0;
|
||||
|
||||
// Balance evolution
|
||||
const sortedTransactions = [...transactions].sort(
|
||||
// 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 balanceByDate = new Map<string, number>();
|
||||
sortedTransactions.forEach((t) => {
|
||||
const aggregatedBalanceByDate = new Map<string, number>();
|
||||
sortedAllTransactions.forEach((t) => {
|
||||
runningBalance += t.amount;
|
||||
balanceByDate.set(t.date, runningBalance);
|
||||
aggregatedBalanceByDate.set(t.date, runningBalance);
|
||||
});
|
||||
|
||||
const balanceChartData = Array.from(balanceByDate.entries()).map(
|
||||
([date, balance]) => ({
|
||||
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<string, Map<string, number>>();
|
||||
data.accounts.forEach((account) => {
|
||||
accountBalances.set(account.id, new Map());
|
||||
});
|
||||
|
||||
// Calculate running balance per account
|
||||
const accountRunningBalances = new Map<string, number>();
|
||||
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<string>();
|
||||
accountBalances.forEach((dates) => {
|
||||
dates.forEach((_, date) => allDates.add(date));
|
||||
});
|
||||
|
||||
const sortedDates = Array.from(allDates).sort();
|
||||
const lastBalances = new Map<string, number>();
|
||||
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",
|
||||
}),
|
||||
solde: Math.round(balance),
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
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,
|
||||
@@ -148,7 +205,8 @@ export default function StatisticsPage() {
|
||||
totalIncome,
|
||||
totalExpenses,
|
||||
avgMonthlyExpenses,
|
||||
balanceChartData,
|
||||
aggregatedBalanceData,
|
||||
perAccountBalanceData,
|
||||
transactionCount: transactions.length,
|
||||
};
|
||||
}, [data, period, selectedAccount]);
|
||||
@@ -219,7 +277,9 @@ export default function StatisticsPage() {
|
||||
formatCurrency={formatCurrency}
|
||||
/>
|
||||
<BalanceLineChart
|
||||
data={stats.balanceChartData}
|
||||
aggregatedData={stats.aggregatedBalanceData}
|
||||
perAccountData={stats.perAccountBalanceData}
|
||||
accounts={data.accounts}
|
||||
formatCurrency={formatCurrency}
|
||||
/>
|
||||
<TopExpensesList
|
||||
|
||||
Reference in New Issue
Block a user