feat: improve balance calculations in statistics page and charts with enhanced formatting for large values
This commit is contained in:
@@ -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<string, number>();
|
||||
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<string, number>();
|
||||
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<string, number>();
|
||||
// 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",
|
||||
}),
|
||||
};
|
||||
|
||||
|
||||
@@ -90,7 +90,18 @@ export function BalanceLineChart({
|
||||
className="text-xs"
|
||||
interval="preserveStartEnd"
|
||||
/>
|
||||
<YAxis className="text-xs" tickFormatter={(v) => `${v}€`} />
|
||||
<YAxis
|
||||
className="text-xs"
|
||||
width={80}
|
||||
tickFormatter={(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)" }}
|
||||
/>
|
||||
<Tooltip
|
||||
formatter={(value: number) => formatCurrency(value)}
|
||||
contentStyle={{
|
||||
|
||||
@@ -37,9 +37,21 @@ export function MonthlyChart({ data, formatCurrency }: MonthlyChartProps) {
|
||||
<BarChart data={data}>
|
||||
<CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
|
||||
<XAxis dataKey="month" className="text-xs" />
|
||||
<YAxis className="text-xs" tickFormatter={(v) => `${v}€`} />
|
||||
<YAxis
|
||||
className="text-xs"
|
||||
width={80}
|
||||
tickFormatter={(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)" }}
|
||||
/>
|
||||
<Tooltip
|
||||
formatter={(value: number) => formatCurrency(value)}
|
||||
labelFormatter={(label) => label}
|
||||
contentStyle={{
|
||||
backgroundColor: "var(--card)",
|
||||
border: "1px solid var(--border)",
|
||||
|
||||
Reference in New Issue
Block a user