From df5cf9fbb60cc204b5420145bebd07486c0dd0c7 Mon Sep 17 00:00:00 2001 From: Vishesh 'ironeagle' Bangotra Date: Thu, 7 May 2026 15:04:28 +0530 Subject: [PATCH] reading reportData instead of customized data. self massaging data for needed format --- .../HistoryChart/HistoryChart.models.ts | 15 +- src/components/HistoryChart/HistoryChart.tsx | 164 ++++++++++++++++-- .../HistoryChart/HistoryChart.utils.ts | 16 -- .../HistoryChart/HistoryChart.view.tsx | 127 ++++---------- src/features/report/index.ts | 1 + 5 files changed, 189 insertions(+), 134 deletions(-) diff --git a/src/components/HistoryChart/HistoryChart.models.ts b/src/components/HistoryChart/HistoryChart.models.ts index 9a9de10..cf6e2fc 100644 --- a/src/components/HistoryChart/HistoryChart.models.ts +++ b/src/components/HistoryChart/HistoryChart.models.ts @@ -3,9 +3,11 @@ import { DashboardPeriodType, DashboardSelectedPeriodId } from "../Dashboard"; +import { ReportData } from "../../features/report"; export interface _ChartDataPoint { id: string; + label: string; amount: number; highlighted?: boolean; } @@ -14,26 +16,19 @@ export interface ChartDataPoint extends _ChartDataPoint { compare?: _ChartDataPoint; } -export interface ChartData { - weekly?: Record; - monthly?: Record; - // yearly?: Record; - // fyly?: Record; - // full?: Record; -} - export interface HistoryChartProps { header: string; summary?: string; tabs: string[]; - data: ChartData; + + reportData: ReportData; + colorScheme: { primary: string; light: string; text: string; }; - // State management mode: DashboardMode; periodType: DashboardPeriodType; selectedPeriodId: DashboardSelectedPeriodId; diff --git a/src/components/HistoryChart/HistoryChart.tsx b/src/components/HistoryChart/HistoryChart.tsx index cd30188..ba7ee4a 100644 --- a/src/components/HistoryChart/HistoryChart.tsx +++ b/src/components/HistoryChart/HistoryChart.tsx @@ -1,45 +1,179 @@ import * as React from "react"; -import { ChartDataPoint, HistoryChartProps, ChartData } from "./HistoryChart.models"; +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 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 = points.map((p, i) => ({ + ...p, + compare: + i > 0 + ? { + id: points[i - 1].id, + label: points[i - 1].label, + amount: points[i - 1].amount + } + : undefined + })); + } + + return points; +} export default function HistoryChart(props: HistoryChartProps) { - const { tabs, data, mode, periodType, comparison } = props; + const { + tabs, + reportData, + mode, + periodType, + comparison, + selectedPeriodId, + setSelectedPeriodId + } = props; const [activeTab, setActiveTab] = React.useState(tabs[0] || ""); const [startIndex, setStartIndex] = React.useState(0); - const activeDataKey = activeTab.toLowerCase() as keyof ChartData; + const activeDataKey = TAB_TO_KEY[activeTab]; - let rawData: ChartDataPoint[] = []; - - const section = data[activeDataKey]; - rawData = section?.[periodType] || []; - - const currentData = rawData; + 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?.amount ?? 0] : [d.amount] + comparison + ? [d.amount, ...(d.compare ? [d.compare.amount] : [])] + : [d.amount] ), 1 ) : 1; - const visibleCountMap = { daily: 7, weekly: 6, monthly: 4 }; - // const visibleCountMap = { daily: 7, weekly: 6, monthly: 4, yearly: 4, fyly: 4, full: 4 }; - const visibleCount = visibleCountMap[activeDataKey]; + 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)); + 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 ( diff --git a/src/components/HistoryChart/HistoryChart.utils.ts b/src/components/HistoryChart/HistoryChart.utils.ts index b4bea05..a7bed97 100644 --- a/src/components/HistoryChart/HistoryChart.utils.ts +++ b/src/components/HistoryChart/HistoryChart.utils.ts @@ -25,19 +25,3 @@ export const formatDisplay = ( return `₹ ${formatShort(base)} (${sign}${formatShort(Math.abs(diff))})`; }; - -export const formatLabel = (label: string, type: string) => { - if (type === "monthly") return label; - - if (type === "weekly") { - const parts = label.split(" - "); - if (parts.length === 2) { - const [start, end] = parts; - const startDay = start.split(" ")[0]; - const [endDay, month] = end.split(" "); - return `${startDay}–${endDay} ${month}`; - } - } - - return label; -}; diff --git a/src/components/HistoryChart/HistoryChart.view.tsx b/src/components/HistoryChart/HistoryChart.view.tsx index 13a4cf2..4dbec6f 100644 --- a/src/components/HistoryChart/HistoryChart.view.tsx +++ b/src/components/HistoryChart/HistoryChart.view.tsx @@ -14,7 +14,7 @@ import { ChartDataPoint, HistoryChartProps, } from "./HistoryChart.models"; -import { formatDisplay, formatLabel } from "./HistoryChart.utils"; +import { formatDisplay } from "./HistoryChart.utils"; interface ViewProps extends HistoryChartProps { activeTab: string; @@ -35,7 +35,6 @@ export default function HistoryChartView(props: ViewProps) { tabs, colorScheme, - // State management mode, periodType, selectedPeriodId, @@ -45,7 +44,6 @@ export default function HistoryChartView(props: ViewProps) { setSelectedPeriodId, toggleComparison, - // HistoryChart state management activeTab, setActiveTab, currentData, @@ -85,10 +83,9 @@ export default function HistoryChartView(props: ViewProps) { border: "1px solid", borderColor: "divider", bgcolor: isDark ? "background.paper" : colorScheme.light, - transition: 'background-color 0.3s ease, border-color 0.3s ease' }} > - + {header} @@ -106,12 +103,10 @@ export default function HistoryChartView(props: ViewProps) { ))} - + Rolling - - Calendar - + Calendar Compare @@ -143,19 +122,7 @@ export default function HistoryChartView(props: ViewProps) { {currentData.length > 0 ? ( {canGoLeft && ( - + )} @@ -166,92 +133,66 @@ export default function HistoryChartView(props: ViewProps) { const compareHeight = comparison ? ((point.compare?.amount ?? 0) / maxAmount) * 100 : 0; - const labelHeight = Math.max(currentHeight, compareHeight); + const isSelected = selectedPeriodId === point.id; - const display = formatDisplay(point, activeTab.toLowerCase(), comparison); + const display = formatDisplay(point, activeDataKey, comparison); return ( setSelectedPeriodId(isSelected ? null : point.id)} + onClick={() => + setSelectedPeriodId(isSelected ? null : point.id) + } sx={{ flex: 1, display: "flex", flexDirection: "column", alignItems: "center", - justifyContent: "flex-end", - height: "100%", cursor: "pointer" }} > - - - {isSelected ? `SELECTED: ${display}` : display} - - + {comparison && ( - + )} - - - {formatLabel(point.id, activeDataKey)} - + + {point.label} + - - {point.compare ? formatLabel(point.compare.id, activeDataKey) : "placeholder"} + {comparison && point.compare && ( + + {point.compare.label} - + )} + + + {display} + ); })} {canGoRight && ( - + )} diff --git a/src/features/report/index.ts b/src/features/report/index.ts index 246b4ac..9092544 100644 --- a/src/features/report/index.ts +++ b/src/features/report/index.ts @@ -4,6 +4,7 @@ export { export type { Transaction, ReportData, + ReportPeriod, } from './report.models' export { prepareReport