feat: integrate monthly chart with collapsible feature in transactions page; update transaction period to default to last 3 months

This commit is contained in:
Julien Froidefond
2025-12-21 07:31:29 +01:00
parent 53798176a0
commit aa2c656c00
5 changed files with 766 additions and 51 deletions

View File

@@ -1,9 +1,12 @@
"use client";
import { useState } from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { ChevronDown, ChevronUp } from "lucide-react";
import {
BarChart,
Bar,
LineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
@@ -11,6 +14,11 @@ import {
ResponsiveContainer,
Legend,
} from "recharts";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
interface MonthlyChartData {
month: string;
@@ -22,54 +30,117 @@ interface MonthlyChartData {
interface MonthlyChartProps {
data: MonthlyChartData[];
formatCurrency: (amount: number) => string;
collapsible?: boolean;
defaultExpanded?: boolean;
showDots?: boolean;
}
export function MonthlyChart({ data, formatCurrency }: MonthlyChartProps) {
export function MonthlyChart({
data,
formatCurrency,
collapsible = false,
defaultExpanded = true,
showDots = true,
}: MonthlyChartProps) {
const [isExpanded, setIsExpanded] = useState(defaultExpanded);
const chartContent = (
<>
{data.length > 0 ? (
<div className="h-[300px]">
<ResponsiveContainer width="100%" height="100%">
<LineChart data={data}>
<CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
<XAxis dataKey="month" className="text-xs" />
<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 `${v.toFixed(0)}`;
}}
tick={{ fill: "var(--muted-foreground)" }}
/>
<Tooltip
formatter={(value: number) => formatCurrency(value)}
labelFormatter={(label) => label}
contentStyle={{
backgroundColor: "var(--card)",
border: "1px solid var(--border)",
borderRadius: "8px",
}}
/>
<Legend />
<Line
type="monotone"
dataKey="revenus"
name="Revenus"
stroke="#22c55e"
strokeWidth={2}
dot={showDots ? { fill: "#22c55e", r: 4 } : false}
activeDot={showDots ? { r: 6 } : false}
/>
<Line
type="monotone"
dataKey="depenses"
name="Dépenses"
stroke="#ef4444"
strokeWidth={2}
dot={showDots ? { fill: "#ef4444", r: 4 } : false}
activeDot={showDots ? { r: 6 } : false}
/>
</LineChart>
</ResponsiveContainer>
</div>
) : (
<div className="h-[300px] flex items-center justify-center text-muted-foreground">
Pas de données pour cette période
</div>
)}
</>
);
if (!collapsible) {
return (
<Card>
<CardHeader>
<CardTitle>Revenus vs Dépenses par mois</CardTitle>
</CardHeader>
<CardContent>{chartContent}</CardContent>
</Card>
);
}
return (
<Card>
<CardHeader>
<CardTitle>Revenus vs Dépenses par mois</CardTitle>
</CardHeader>
<CardContent>
{data.length > 0 ? (
<div className="h-[300px]">
<ResponsiveContainer width="100%" height="100%">
<BarChart data={data}>
<CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
<XAxis dataKey="month" className="text-xs" />
<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)",
borderRadius: "8px",
}}
/>
<Legend />
<Bar dataKey="revenus" fill="#22c55e" radius={[4, 4, 0, 0]} />
<Bar dataKey="depenses" fill="#ef4444" radius={[4, 4, 0, 0]} />
</BarChart>
</ResponsiveContainer>
</div>
) : (
<div className="h-[300px] flex items-center justify-center text-muted-foreground">
Pas de données pour cette période
</div>
)}
</CardContent>
<Collapsible open={isExpanded} onOpenChange={setIsExpanded}>
<CardHeader className="flex flex-row items-center justify-between space-y-0 py-3 px-6">
<CardTitle className="text-base font-semibold">
Revenus vs Dépenses par mois
</CardTitle>
<CollapsibleTrigger asChild>
<Button variant="ghost" size="sm" className="h-8">
{isExpanded ? (
<>
<ChevronUp className="w-4 h-4 mr-1" />
Réduire
</>
) : (
<>
<ChevronDown className="w-4 h-4 mr-1" />
Afficher
</>
)}
</Button>
</CollapsibleTrigger>
</CardHeader>
<CollapsibleContent>
<CardContent className="pt-0">{chartContent}</CardContent>
</CollapsibleContent>
</Collapsible>
</Card>
);
}