import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend, } from "recharts"; import type { Balance, Account } from "../../types/api"; interface BalanceChartProps { data: Balance[]; accounts: Account[]; className?: string; } interface ChartDataPoint { date: string; balance: number; account_id: string; } interface AggregatedDataPoint { date: string; [key: string]: string | number; } interface TooltipProps { active?: boolean; payload?: Array<{ name: string; value: number; color: string; }>; label?: string; } export default function BalanceChart({ data, accounts, className, }: BalanceChartProps) { // Create a lookup map for account info const accountMap = accounts.reduce( (map, account) => { map[account.id] = account; return map; }, {} as Record, ); // Helper function to get bank name from institution_id const getBankName = (institutionId: string): string => { const bankMapping: Record = { REVOLUT_REVOLT21: "Revolut", NUBANK_NUPBBR25: "Nu Pagamentos", BANCOBPI_BBPIPTPL: "Banco BPI", // Add more mappings as needed }; return bankMapping[institutionId] || institutionId.split("_")[0]; }; // Helper function to create display name for account const getAccountDisplayName = (accountId: string): string => { const account = accountMap[accountId]; if (account) { const bankName = getBankName(account.institution_id); const accountName = account.name || `Account ${accountId.split("-")[1]}`; return `${bankName} - ${accountName}`; } return `Account ${accountId.split("-")[1]}`; }; // Process balance data for the chart const chartData = data .filter((balance) => balance.balance_type === "closingBooked") .map((balance) => ({ date: new Date(balance.reference_date).toLocaleDateString("en-GB"), // DD/MM/YYYY format balance: balance.balance_amount, account_id: balance.account_id, })) .sort( (a, b) => new Date(a.date.split("/").reverse().join("/")).getTime() - new Date(b.date.split("/").reverse().join("/")).getTime(), ); // Group by account and aggregate const accountBalances: { [key: string]: ChartDataPoint[] } = {}; chartData.forEach((item) => { if (!accountBalances[item.account_id]) { accountBalances[item.account_id] = []; } accountBalances[item.account_id].push(item); }); // Create aggregated data points const aggregatedData: { [key: string]: AggregatedDataPoint } = {}; Object.entries(accountBalances).forEach(([accountId, balances]) => { balances.forEach((balance) => { if (!aggregatedData[balance.date]) { aggregatedData[balance.date] = { date: balance.date }; } aggregatedData[balance.date][accountId] = balance.balance; }); }); const finalData = Object.values(aggregatedData).sort( (a, b) => new Date(a.date.split("/").reverse().join("/")).getTime() - new Date(b.date.split("/").reverse().join("/")).getTime(), ); const colors = ["#3B82F6", "#10B981", "#F59E0B", "#EF4444", "#8B5CF6"]; const CustomTooltip = ({ active, payload, label }: TooltipProps) => { if (active && payload && payload.length) { return (

Date: {label}

{payload.map((entry, index) => (

{getAccountDisplayName(entry.name)}: € {entry.value.toLocaleString()}

))}
); } return null; }; if (finalData.length === 0) { return (

Balance Progress

No balance data available
); } return (

Balance Progress Over Time

{ // Convert DD/MM/YYYY back to a proper date for formatting const [day, month, year] = value.split("/"); const date = new Date(year, month - 1, day); return date.toLocaleDateString("en-GB", { month: "short", day: "numeric", }); }} /> `€${value.toLocaleString()}`} /> } /> {Object.keys(accountBalances).map((accountId, index) => ( ))}
); }