import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis } from "recharts"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/Card"; import { formatCurrency, getIntlLocale, pluralizeRu } from "@/lib/format"; import { useTranslation, type Language } from "@/lib/i18n"; import { translateCategoryName } from "@/lib/categoryLabels"; import type { CategorySpendingReport } from "@/types"; interface CategorySpendingChartProps { report: CategorySpendingReport | undefined; isLoading: boolean; } function monthKey(year: number, month: number): string { return `${year}-${String(month).padStart(2, "0")}-01`; } function formatMonthLabel(key: string): string { return new Intl.DateTimeFormat(getIntlLocale(), { month: "short", year: "numeric" }).format( new Date(`${key}T00:00:00`) ); } function transactionsCountLabel(count: number, language: Language): string { if (language === "ru") return pluralizeRu(count, "транзакция", "транзакции", "транзакций"); return count === 1 ? "transaction" : "transactions"; } /** Same landmark idea as the Net Worth chart: year ticks only when the bars * span more than one calendar year, so a 5-year report reads at a glance. */ function computeYearTicks(keys: string[]): string[] { if (keys.length < 2) return []; const startYear = Number(keys[0].slice(0, 4)); const endYear = Number(keys[keys.length - 1].slice(0, 4)); if (startYear === endYear) return []; const ticks: string[] = []; for (let year = startYear + 1; year <= endYear; year++) { const jan = `${year}-01-01`; if (keys.includes(jan)) ticks.push(jan); } return ticks; } function ChartTooltip({ active, payload }: { active?: boolean; payload?: Array<{ payload: { key: string; amount: number } }> }) { if (!active || !payload?.length) return null; const point = payload[0].payload; return (

{formatMonthLabel(point.key)}

{formatCurrency(point.amount)}

); } export function CategorySpendingChart({ report, isLoading }: CategorySpendingChartProps) { const { t, language } = useTranslation(); const chartData = report?.series.map((point) => ({ key: monthKey(point.year, point.month), amount: Number(point.amount), })) ?? []; const yearTicks = computeYearTicks(chartData.map((point) => point.key)); return (
{report ? translateCategoryName(report.category_name) : t("reports.defaultChartTitle")}

{isLoading || !report ? "…" : formatCurrency(report.total_amount)}

{report && (

{t("reports.averagePerMonth", { amount: formatCurrency(report.average_per_month) })} ·{" "} {report.transaction_count} {transactionsCountLabel(report.transaction_count, language)}

)}
{isLoading || !report || chartData.length === 0 ? (
{isLoading ? t("common.loading") : t("reports.noDataForPeriod")}
) : ( {yearTicks.length > 0 && ( value.slice(0, 4)} axisLine={false} tickLine={false} tick={{ fill: "var(--text-muted)", fontSize: 11 }} interval="preserveStartEnd" /> )} } cursor={{ fill: "var(--surface-2)" }} /> )}
{chartData.length > 1 && (
{formatMonthLabel(chartData[0].key)} {formatMonthLabel(chartData[chartData.length - 1].key)}
)}
); }