import * as React from "react"; import { HistoryChartProps, ChartDataPoint } from "./HistoryChart.models"; import HistoryChartView from "./HistoryChart.view"; import { ReportPeriod } from "../../features/report"; type DecoratedPeriod = ReportPeriod & { id: string; label: string; }; const TAB_TO_KEY: Record = { Weekly: "weekly", Monthly: "monthly", Yearly: "yearly", FYLY: "fyly" }; function getAmount(p: ReportPeriod, mode: "expense" | "income") { return mode === "expense" ? p.expenses.sum : p.incomes.sum; } function mergeMetric(a: any, b: any) { const sum = a.sum + b.sum; const count = a.count + b.count; return { ...a, sum, count, average: count > 0 ? sum / count : 0, transactions: a.transactions || b.transactions ? [ ...(a.transactions || []), ...(b.transactions || []) ] : undefined }; } function mergeBuckets( buckets: any[], key: "weekly" | "monthly" | "yearly" | "fyly" ): DecoratedPeriod[] { const map = new Map(); for (const bucket of buckets) { const periods = (bucket.periods[key] || []) as DecoratedPeriod[]; for (const p of periods) { const existing = map.get(p.id); if (!existing) { map.set(p.id, { ...p, expenses: { ...p.expenses }, incomes: { ...p.incomes } }); } else { map.set(p.id, { ...existing, expenses: mergeMetric(existing.expenses, p.expenses), incomes: mergeMetric(existing.incomes, p.incomes) }); } } } return Array.from(map.values()).sort( (a, b) => new Date(a.start).getTime() - new Date(b.start).getTime() ); } function attachComparison( points: ChartDataPoint[], key: "weekly" | "monthly" | "yearly" | "fyly" ): ChartDataPoint[] { const getCompareIndex = (i: number) => { if (key === "weekly") return i - 4; if (key === "monthly") return i - 12; if (key === "yearly") return i - 1; if (key === "fyly") return i - 1; return -1; }; return points.map((p, i) => { const ci = getCompareIndex(i); return { ...p, compare: ci >= 0 && points[ci] ? { id: points[ci].id, label: points[ci].label, amount: points[ci].amount } : undefined }; }); } function buildChartData( reportData: HistoryChartProps["reportData"], key: "weekly" | "monthly" | "yearly" | "fyly", mode: "expense" | "income", comparison: boolean ): ChartDataPoint[] { const merged = mergeBuckets(reportData.buckets, key); console.log("Merged periods:", merged); let points: ChartDataPoint[] = merged.map((p) => ({ id: p.id, label: p.label, amount: getAmount(p, mode) })); if (comparison) { points = attachComparison(points, key); } return points; } export default function HistoryChart(props: HistoryChartProps) { const { tabs, reportData, mode, periodType, comparison, selectedPeriodId, setSelectedPeriodId } = props; const [activeTab, setActiveTab] = React.useState(tabs[0] || ""); const [startIndex, setStartIndex] = React.useState(0); const activeDataKey = TAB_TO_KEY[activeTab]; const currentData = React.useMemo(() => { return buildChartData(reportData, activeDataKey, mode, comparison); }, [reportData, activeDataKey, mode, comparison]); const maxAmount = currentData.length > 0 ? Math.max( ...currentData.flatMap((d) => comparison ? [d.amount, ...(d.compare ? [d.compare.amount] : [])] : [d.amount] ), 1 ) : 1; const visibleCountMap = { weekly: 6, monthly: 4, yearly: 4, fyly: 4 }; const visibleCount = visibleCountMap[activeDataKey] ?? 4; const total = currentData.length; const clampedStartIndex = Math.min( startIndex, Math.max(total - visibleCount, 0) ); React.useEffect(() => { if (startIndex !== clampedStartIndex) { setStartIndex(clampedStartIndex); } }, [startIndex, clampedStartIndex]); const visibleData = currentData.slice( clampedStartIndex, clampedStartIndex + visibleCount ); React.useEffect(() => { setSelectedPeriodId(null); }, [activeTab, periodType]); React.useEffect(() => { if ( selectedPeriodId && !visibleData.some((p) => p.id === selectedPeriodId) ) { setSelectedPeriodId(null); } }, [visibleData, selectedPeriodId]); return ( ); }