"use client"; import { useState } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { CategoryIcon } from "@/components/ui/category-icon"; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend, } from "recharts"; import { ChevronDown, ChevronUp, Layers, List } from "lucide-react"; import type { Category } from "@/lib/types"; interface CategoryTrendDataPoint { month: string; [categoryId: string]: string | number; } interface CategoryTrendChartProps { data: CategoryTrendDataPoint[]; dataByParent: CategoryTrendDataPoint[]; categories: Category[]; formatCurrency: (amount: number) => string; maxCategories?: number; } const CATEGORY_COLORS = [ "#ef4444", // red "#f59e0b", // amber "#eab308", // yellow "#22c55e", // green "#06b6d4", // cyan "#3b82f6", // blue "#6366f1", // indigo "#8b5cf6", // violet "#ec4899", // pink "#f97316", // orange ]; export function CategoryTrendChart({ data, dataByParent, categories, formatCurrency, maxCategories = 5, }: CategoryTrendChartProps) { const [isExpanded, setIsExpanded] = useState(false); const [selectedCategories, setSelectedCategories] = useState([]); const [groupByParent, setGroupByParent] = useState(true); // Use the appropriate dataset based on groupByParent const currentData = groupByParent ? dataByParent : data; // Get top categories by total amount const categoryTotals = new Map(); currentData.forEach((point) => { Object.keys(point).forEach((key) => { if (key !== "month" && typeof point[key] === "number") { const current = categoryTotals.get(key) || 0; categoryTotals.set(key, current + (point[key] as number)); } }); }); const topCategories = Array.from(categoryTotals.entries()) .sort((a, b) => b[1] - a[1]) .slice(0, maxCategories) .map(([id]) => id); const displayCategories = isExpanded ? Array.from(categoryTotals.keys()) : topCategories; const categoriesToShow = selectedCategories.length > 0 ? selectedCategories : displayCategories; // Helper function to get category name // When groupByParent is true, the categoryId in dataByParent is already the parent ID const getCategoryName = (categoryId: string): string => { if (categoryId === "uncategorized") return "Non catégorisé"; const category = categories.find((c) => c.id === categoryId); return category?.name || "Inconnu"; }; // Helper function to get category info (for color, icon) // When groupByParent is true, the categoryId in dataByParent is already the parent ID const getCategoryInfo = (categoryId: string): Category | null => { if (categoryId === "uncategorized") return null; return categories.find((c) => c.id === categoryId) || null; }; return ( Évolution des dépenses par catégorie
{categoryTotals.size > maxCategories && ( )}
{currentData.length > 0 && categoriesToShow.length > 0 ? (
{ if (Math.abs(v) >= 1000) { return `${(v / 1000).toFixed(1)}k€`; } return `${Math.round(v)}€`; }} tick={{ fill: "var(--muted-foreground)" }} /> formatCurrency(value)} contentStyle={{ backgroundColor: "var(--card)", border: "1px solid var(--border)", borderRadius: "8px", }} /> { // Get all category IDs from data const allCategoryIds = Array.from(categoryTotals.keys()); return (
{allCategoryIds.map((categoryId) => { const categoryInfo = getCategoryInfo(categoryId); const categoryName = getCategoryName(categoryId); if (!categoryInfo && categoryId !== "uncategorized") return null; const isInDisplayCategories = displayCategories.includes(categoryId); const isSelected = selectedCategories.length === 0 ? isInDisplayCategories : selectedCategories.includes(categoryId); return ( ); })}
); }} /> {categoriesToShow.map((categoryId, index) => { const categoryInfo = getCategoryInfo(categoryId); const categoryName = getCategoryName(categoryId); if (!categoryInfo && categoryId !== "uncategorized") return null; const isSelected = selectedCategories.length === 0 || selectedCategories.includes(categoryId); return ( 0} /> ); })}
) : (
Pas de données pour cette période
)}
); }