import { useState } from "react"; import { ChevronRight } from "lucide-react"; import { Cell, Pie, PieChart, ResponsiveContainer, Tooltip } from "recharts"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/Card"; import { CategoryBreakdownModal } from "@/components/categories/CategoryBreakdownModal"; import { getCategoryIcon } from "@/lib/icons"; import { formatCurrency } from "@/lib/format"; import { useTranslation } from "@/lib/i18n"; import { translateCategoryName } from "@/lib/categoryLabels"; import type { CategoryBreakdownItem } from "@/types"; interface SpendingByCategoryCardProps { items: CategoryBreakdownItem[]; } function DonutTooltip({ active, payload }: { active?: boolean; payload?: Array<{ payload: CategoryBreakdownItem }> }) { if (!active || !payload?.length) return null; const item = payload[0].payload; return (

{translateCategoryName(item.name)}

{formatCurrency(item.amount)} ยท {item.percent.toFixed(1)}%

); } export function SpendingByCategoryCard({ items }: SpendingByCategoryCardProps) { const { t } = useTranslation(); // The breakdown lives in a modal, not expanded inline โ€” a category with // many subcategories (or many split purchases) would otherwise push this // row taller than the fixed-height donut next to it, throwing the // side-by-side layout out of alignment. const [breakdownItem, setBreakdownItem] = useState(null); const hasData = items.length > 0; // Recharts needs a numeric dataKey โ€” API amounts arrive as strings (Decimal // is serialized as string to avoid float precision loss). const chartData = items.map((item) => ({ ...item, amount: Number(item.amount) })); return ( {t("dashboard.spendingByCategoryTitle")} {!hasData ? (

{t("dashboard.noExpensesThisMonth")}

) : (
{items.map((item) => ( ))} } />
    {items.map((item) => { const Icon = getCategoryIcon(item.icon); const hasChildren = item.children.length > 0; return (
  • {translateCategoryName(item.name)} {formatCurrency(item.amount)} {/* Fixed-width slot on every row, populated or not โ€” a chevron shown only on rows with children would shift their amount left relative to every other row's. */} {hasChildren && ( )}
  • ); })}
)}
{breakdownItem && breakdownItem.category_id !== null && ( setBreakdownItem(null)} categoryId={breakdownItem.category_id} categoryName={breakdownItem.name} totalAmount={breakdownItem.amount} children={breakdownItem.children} /> )}
); }