import { ChartViewToggle, MoMChart, MoMPercentChart, } from '@/components/charts'; import { PercentageTrendIndicator } from '@/components/dashboard/percentage-trend-indicator'; import { EncryptedText } from '@/components/encrypted-text'; import { AmountDisplay } from '@/components/ui/amount-display'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card'; import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, } from '@/components/ui/chart'; import { convertSingleAccountData, useChartViews, } from '@/hooks/use-chart-views'; import { useLocale } from '@/hooks/use-locale'; import { Account } from '@/types/account'; import { formatMonthFromYearMonth } from '@/utils/date'; import { __ } from '@/utils/i18n'; import { format, subMonths } from 'date-fns'; import { useEffect, useMemo, useRef, useState } from 'react'; import { Bar, BarChart, XAxis } from 'recharts'; interface BalanceDataPoint { month: string; timestamp: number; value: number; } interface AccountBalanceData { data: BalanceDataPoint[]; account: { id: string; name: string; name_iv: string; type: string; currency_code: string; }; } interface AccountBalanceChartProps { account: Account; loading?: boolean; refreshKey?: number; onBalanceClick?: () => void; } function createXAxisFormatter(locale: string) { return function formatXAxisLabel(value: string): string { return formatMonthFromYearMonth(value, locale); }; } function formatChartCurrency( value: number, currencyCode: string, locale: string, ): string { return new Intl.NumberFormat(locale, { style: 'currency', currency: currencyCode, minimumFractionDigits: 0, maximumFractionDigits: 0, }).format(value / 100); } function calculateTrend( data: BalanceDataPoint[], monthsBack: number, ): { percentage: number; previousValue: number; currentValue: number } | null { if (data.length < 2) return null; const currentIndex = data.length - 1; const previousIndex = Math.max(0, data.length - 1 - monthsBack); if (currentIndex === previousIndex) return null; const currentValue = data[currentIndex].value; const previousValue = data[previousIndex].value; if (previousValue === 0) return null; return { percentage: ((currentValue - previousValue) / Math.abs(previousValue)) * 100, previousValue, currentValue, }; } export function AccountBalanceChart({ account, loading: initialLoading, refreshKey, onBalanceClick, }: AccountBalanceChartProps) { const locale = useLocale(); const [balanceData, setBalanceData] = useState( null, ); const [isLoading, setIsLoading] = useState(true); useEffect(() => { async function fetchBalanceData() { setIsLoading(true); try { const now = new Date(); const to = format(now, 'yyyy-MM-dd'); const from = format(subMonths(now, 12), 'yyyy-MM-dd'); const params = new URLSearchParams({ from, to }); const response = await fetch( `/api/dashboard/account/${account.id}/balance-evolution?${params.toString()}`, ); const data = await response.json(); setBalanceData(data); } catch (error) { console.error('Failed to fetch balance data:', error); } finally { setIsLoading(false); } } fetchBalanceData(); }, [account.id, refreshKey]); const { chartData, currentBalance, monthlyTrend, yearlyTrend } = useMemo(() => { if (!balanceData?.data?.length) { return { chartData: [], currentBalance: 0, monthlyTrend: null, yearlyTrend: null, }; } const data = balanceData.data; const current = data[data.length - 1]?.value ?? 0; return { chartData: data, currentBalance: current, monthlyTrend: calculateTrend(data, 1), yearlyTrend: calculateTrend(data, data.length - 1), }; }, [balanceData]); // Convert data for useChartViews hook const { data: hookData, accounts: hookAccounts } = useMemo(() => { return convertSingleAccountData( chartData, account.id, account.type, account.currency_code, ); }, [chartData, account.id, account.type, account.currency_code]); const chartViews = useChartViews({ data: hookData, accounts: hookAccounts, initialView: 'stacked', hasStackedView: true, }); const chartConfig: ChartConfig = { value: { label: ( ), color: 'var(--color-chart-2)', }, }; const formatXAxisLabel = useMemo( () => createXAxisFormatter(locale), [locale], ); const valueFormatter = (value: number): string => { return formatChartCurrency(value, account.currency_code, locale); }; const scrollContainerRef = useRef(null); const minBarWidth = 50; const minChartWidth = chartData.length * minBarWidth; useEffect(() => { if (scrollContainerRef.current) { scrollContainerRef.current.scrollLeft = scrollContainerRef.current.scrollWidth; } }, [chartData]); if (initialLoading || isLoading) { return ( {__('Balance evolution')}
); } if (chartData.length === 0) { return ( {__('Balance evolution')}
{__('No balance data available')}
); } return (
{__('Balance evolution')}
{chartViews.currentView === 'stacked' && (
} />
)} {chartViews.currentView === 'mom' && ( )} {chartViews.currentView === 'mom_percent' && ( )}
); }