import { useState, useRef, useCallback } from "react"; import { useTranslation } from "react-i18next"; import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, Legend, CartesianGrid, } from "recharts"; import { Eye } from "lucide-react"; import type { CategoryOverTimeData, CategoryBreakdownItem } from "../../shared/types"; import { ChartPatternDefs, getPatternFill } from "../../utils/chartPatterns"; import ChartContextMenu from "../shared/ChartContextMenu"; const cadFormatter = (value: number) => new Intl.NumberFormat("en-CA", { style: "currency", currency: "CAD", maximumFractionDigits: 0 }).format(value); function formatMonth(month: string): string { const [year, m] = month.split("-"); const date = new Date(Number(year), Number(m) - 1); return date.toLocaleDateString("default", { month: "short", year: "2-digit" }); } interface CategoryOverTimeChartProps { data: CategoryOverTimeData; hiddenCategories: Set; onToggleHidden: (categoryName: string) => void; onShowAll: () => void; onViewDetails: (item: CategoryBreakdownItem) => void; } export default function CategoryOverTimeChart({ data, hiddenCategories, onToggleHidden, onShowAll, onViewDetails, }: CategoryOverTimeChartProps) { const { t } = useTranslation(); const hoveredRef = useRef(null); const [contextMenu, setContextMenu] = useState<{ x: number; y: number; name: string } | null>(null); const visibleCategories = data.categories.filter((name) => !hiddenCategories.has(name)); const categoryEntries = visibleCategories.map((name, index) => ({ name, color: data.colors[name], index, })); const handleContextMenu = useCallback((e: React.MouseEvent) => { if (!hoveredRef.current) return; e.preventDefault(); setContextMenu({ x: e.clientX, y: e.clientY, name: hoveredRef.current }); }, []); if (data.data.length === 0) { return (

{t("dashboard.noData")}

); } return (
{hiddenCategories.size > 0 && (
{t("charts.hiddenCategories")}: {Array.from(hiddenCategories).map((name) => ( ))}
)}
({ color: c.color, index: c.index }))} /> cadFormatter(v)} tick={{ fill: "var(--muted-foreground)", fontSize: 12 }} stroke="var(--border)" width={80} /> cadFormatter(value ?? 0)} labelFormatter={(label) => formatMonth(String(label))} contentStyle={{ backgroundColor: "var(--card)", border: "1px solid var(--border)", borderRadius: "8px", color: "var(--foreground)", }} labelStyle={{ color: "var(--foreground)" }} itemStyle={{ color: "var(--foreground)" }} /> {categoryEntries.map((c) => ( { hoveredRef.current = c.name; }} onMouseLeave={() => { hoveredRef.current = null; }} cursor="context-menu" /> ))}
{contextMenu && ( onToggleHidden(contextMenu.name)} onViewDetails={() => { const color = data.colors[contextMenu.name] || "#9ca3af"; const categoryId = data.categoryIds[contextMenu.name] ?? null; onViewDetails({ category_id: categoryId, category_name: contextMenu.name, category_color: color, total: 0, }); }} onClose={() => setContextMenu(null)} /> )}
); }