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]) => ({
|
.map(([month, values]) => ({
|
||||||
month: new Date(month + "-01").toLocaleDateString("fr-FR", {
|
month: new Date(month + "-01").toLocaleDateString("fr-FR", {
|
||||||
month: "short",
|
month: "short",
|
||||||
year: "2-digit",
|
year: "numeric",
|
||||||
}),
|
}),
|
||||||
revenus: Math.round(values.income),
|
revenus: Math.round(values.income),
|
||||||
depenses: Math.round(values.expenses),
|
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()
|
(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;
|
let runningBalance = 0;
|
||||||
if (selectedAccounts.includes("all")) {
|
const accountsToUse = selectedAccounts.includes("all")
|
||||||
runningBalance = data.accounts.reduce(
|
? data.accounts
|
||||||
(sum, acc) => sum + (acc.initialBalance || 0),
|
: data.accounts.filter((acc) => selectedAccounts.includes(acc.id));
|
||||||
0,
|
|
||||||
);
|
// Start with initial balances
|
||||||
} else {
|
runningBalance = accountsToUse.reduce(
|
||||||
runningBalance = data.accounts
|
(sum, acc) => sum + (acc.initialBalance || 0),
|
||||||
.filter((acc) => selectedAccounts.includes(acc.id))
|
0,
|
||||||
.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>();
|
const aggregatedBalanceByDate = new Map<string, number>();
|
||||||
sortedFilteredTransactions.forEach((t) => {
|
sortedFilteredTransactions.forEach((t) => {
|
||||||
@@ -257,6 +279,7 @@ export default function StatisticsPage() {
|
|||||||
date: new Date(date).toLocaleDateString("fr-FR", {
|
date: new Date(date).toLocaleDateString("fr-FR", {
|
||||||
day: "2-digit",
|
day: "2-digit",
|
||||||
month: "short",
|
month: "short",
|
||||||
|
year: "numeric",
|
||||||
}),
|
}),
|
||||||
solde: Math.round(balance),
|
solde: Math.round(balance),
|
||||||
}));
|
}));
|
||||||
@@ -267,10 +290,31 @@ export default function StatisticsPage() {
|
|||||||
accountBalances.set(account.id, new Map());
|
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>();
|
const accountRunningBalances = new Map<string, number>();
|
||||||
data.accounts.forEach((account) => {
|
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) => {
|
sortedFilteredTransactions.forEach((t) => {
|
||||||
@@ -292,8 +336,10 @@ export default function StatisticsPage() {
|
|||||||
|
|
||||||
const sortedDates = Array.from(allDates).sort();
|
const sortedDates = Array.from(allDates).sort();
|
||||||
const lastBalances = new Map<string, number>();
|
const lastBalances = new Map<string, number>();
|
||||||
|
// Initialize with the starting balance (initialBalance + transactions before startDate)
|
||||||
data.accounts.forEach((account) => {
|
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) => {
|
const perAccountBalanceData = sortedDates.map((date) => {
|
||||||
@@ -301,6 +347,7 @@ export default function StatisticsPage() {
|
|||||||
date: new Date(date).toLocaleDateString("fr-FR", {
|
date: new Date(date).toLocaleDateString("fr-FR", {
|
||||||
day: "2-digit",
|
day: "2-digit",
|
||||||
month: "short",
|
month: "short",
|
||||||
|
year: "numeric",
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,18 @@ export function BalanceLineChart({
|
|||||||
className="text-xs"
|
className="text-xs"
|
||||||
interval="preserveStartEnd"
|
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
|
<Tooltip
|
||||||
formatter={(value: number) => formatCurrency(value)}
|
formatter={(value: number) => formatCurrency(value)}
|
||||||
contentStyle={{
|
contentStyle={{
|
||||||
|
|||||||
@@ -37,9 +37,21 @@ export function MonthlyChart({ data, formatCurrency }: MonthlyChartProps) {
|
|||||||
<BarChart data={data}>
|
<BarChart data={data}>
|
||||||
<CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
|
<CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
|
||||||
<XAxis dataKey="month" className="text-xs" />
|
<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
|
<Tooltip
|
||||||
formatter={(value: number) => formatCurrency(value)}
|
formatter={(value: number) => formatCurrency(value)}
|
||||||
|
labelFormatter={(label) => label}
|
||||||
contentStyle={{
|
contentStyle={{
|
||||||
backgroundColor: "var(--card)",
|
backgroundColor: "var(--card)",
|
||||||
border: "1px solid var(--border)",
|
border: "1px solid var(--border)",
|
||||||
|
|||||||
Reference in New Issue
Block a user