feat: enhance transactions page with total amount and count display; integrate collapsible statistics card and update chart data handling

This commit is contained in:
Julien Froidefond
2025-12-21 07:35:35 +01:00
parent aa2c656c00
commit b4dace0673
2 changed files with 113 additions and 15 deletions

View File

@@ -2,7 +2,14 @@
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import { PageLayout, PageHeader } from "@/components/layout"; import { PageLayout, PageHeader } from "@/components/layout";
import { RefreshCw, Maximize2, Minimize2 } from "lucide-react"; import {
RefreshCw,
Maximize2,
Minimize2,
Receipt,
Euro,
ChevronDown,
} from "lucide-react";
import { import {
TransactionFilters, TransactionFilters,
TransactionBulkActions, TransactionBulkActions,
@@ -16,6 +23,12 @@ import { OFXImportDialog } from "@/components/import/ofx-import-dialog";
import { MonthlyChart } from "@/components/statistics"; import { MonthlyChart } from "@/components/statistics";
import { useQueryClient } from "@tanstack/react-query"; import { useQueryClient } from "@tanstack/react-query";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import { Upload } from "lucide-react"; import { Upload } from "lucide-react";
import { import {
Dialog, Dialog,
@@ -98,7 +111,12 @@ export default function TransactionsPage() {
}); });
// Chart data // Chart data
const { monthlyData, isLoading: isLoadingChart } = useTransactionsChartData({ const {
monthlyData,
isLoading: isLoadingChart,
totalAmount: chartTotalAmount,
totalCount: chartTotalCount,
} = useTransactionsChartData({
selectedAccounts, selectedAccounts,
selectedCategories, selectedCategories,
period, period,
@@ -138,6 +156,10 @@ export default function TransactionsPage() {
? Math.round((uncategorizedCount / totalTransactions) * 100) ? Math.round((uncategorizedCount / totalTransactions) * 100)
: 0; : 0;
// Use total from chart data (all filtered transactions) or fallback to paginated data
const totalAmount = chartTotalAmount ?? 0;
const displayTotalCount = chartTotalCount ?? totalTransactions;
// For filter comboboxes, we'll use empty arrays for now // For filter comboboxes, we'll use empty arrays for now
// They can be enhanced later with separate queries if needed // They can be enhanced later with separate queries if needed
const transactionsForAccountFilter: never[] = []; const transactionsForAccountFilter: never[] = [];
@@ -199,19 +221,80 @@ export default function TransactionsPage() {
transactionsForCategoryFilter={transactionsForCategoryFilter} transactionsForCategoryFilter={transactionsForCategoryFilter}
/> />
{(!isLoadingChart || !isLoadingTransactions) && (
<Card className="mb-6">
<Collapsible defaultOpen={true}>
<CardHeader className="flex flex-row items-center justify-between space-y-0 py-3 px-6">
<CardTitle className="text-base font-semibold">
Statistiques
</CardTitle>
<CollapsibleTrigger asChild>
<Button variant="ghost" size="sm" className="h-8">
<ChevronDown className="w-4 h-4 mr-1" />
Réduire
</Button>
</CollapsibleTrigger>
</CardHeader>
<CollapsibleContent>
<CardContent className="pt-0">
{/* Summary cards */}
{!isLoadingTransactions && (
<div className="grid gap-4 grid-cols-2 mb-6">
<Card>
<CardContent className="pt-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-muted-foreground">
Nombre de transactions
</p>
<p className="text-2xl font-bold mt-1">
{displayTotalCount}
</p>
</div>
<Receipt className="w-8 h-8 text-muted-foreground" />
</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-muted-foreground">
Total
</p>
<p
className={`text-2xl font-bold mt-1 ${
totalAmount >= 0
? "text-emerald-600"
: "text-red-600"
}`}
>
{formatCurrency(totalAmount)}
</p>
</div>
<Euro className="w-8 h-8 text-muted-foreground" />
</div>
</CardContent>
</Card>
</div>
)}
{/* Chart */}
{!isLoadingChart && monthlyData.length > 0 && ( {!isLoadingChart && monthlyData.length > 0 && (
<div className="mb-6">
<MonthlyChart <MonthlyChart
data={monthlyData} data={monthlyData}
formatCurrency={formatCurrency} formatCurrency={formatCurrency}
collapsible collapsible={false}
defaultExpanded={true}
showDots={ showDots={
period !== "all" && period !== "all" &&
(period === "12months" || monthlyData.length <= 12) (period === "12months" || monthlyData.length <= 12)
} }
/> />
</div> )}
</CardContent>
</CollapsibleContent>
</Collapsible>
</Card>
)} )}
<TransactionBulkActions <TransactionBulkActions

View File

@@ -191,10 +191,25 @@ export function useTransactionsChartData({
return categoryChartData; return categoryChartData;
}, [transactionsData, metadata]); }, [transactionsData, metadata]);
// Calculate total amount and count from all filtered transactions
const totalAmount = useMemo(() => {
if (!transactionsData) return 0;
return transactionsData.transactions.reduce(
(sum, t) => sum + t.amount,
0
);
}, [transactionsData]);
const totalCount = useMemo(() => {
return transactionsData?.total || 0;
}, [transactionsData?.total]);
return { return {
monthlyData, monthlyData,
categoryData, categoryData,
isLoading, isLoading,
totalAmount,
totalCount,
}; };
} }