From ceaeca70cc39617fbb88f47616fcb4f59c92ae23 Mon Sep 17 00:00:00 2001 From: Vishesh 'ironeagle' Bangotra Date: Mon, 18 May 2026 14:18:36 +0530 Subject: [PATCH 1/5] common component props --- src/components/Dashboard/Dashboard.models.ts | 7 ++ src/components/Dashboard/Dashboard.tsx | 14 +-- src/components/Dashboard/Dashboard.view.tsx | 28 ++---- .../HistoryChart/HistoryChart.models.ts | 30 +------ src/components/HistoryChart/HistoryChart.tsx | 9 +- .../HistoryChart/HistoryChart.view.tsx | 13 ++- .../LatestItems/LatestItems.adapter.ts | 58 ++---------- src/components/LatestItems/LatestItems.tsx | 20 ++--- .../ProgressCard/TopPayees.adapter.ts | 56 +++--------- src/components/ProgressCard/TopPayees.tsx | 22 ++--- .../ProgressCard/TopTags.adapter.ts | 66 +++----------- src/components/ProgressCard/TopTags.tsx | 22 ++--- src/components/report.helpers.ts | 88 +++++++++++++++++++ src/components/report.props.ts | 17 ++++ 14 files changed, 191 insertions(+), 259 deletions(-) create mode 100644 src/components/report.props.ts diff --git a/src/components/Dashboard/Dashboard.models.ts b/src/components/Dashboard/Dashboard.models.ts index e22879e..87f9224 100644 --- a/src/components/Dashboard/Dashboard.models.ts +++ b/src/components/Dashboard/Dashboard.models.ts @@ -16,6 +16,13 @@ export interface DashboardState { comparison: boolean; } +export interface DashboardStateSetters { + setSelectedPeriodId: (id: DashboardSelectedPeriodId) => void; + setSelectedGroupKey: (groupKey: GroupKey | null) => void; + togglePeriodType: () => void; + toggleComparison: () => void; +} + export interface DashboardSection { id: string; title?: string; diff --git a/src/components/Dashboard/Dashboard.tsx b/src/components/Dashboard/Dashboard.tsx index 4ecd870..50fb4b9 100644 --- a/src/components/Dashboard/Dashboard.tsx +++ b/src/components/Dashboard/Dashboard.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import DashboardView from "./Dashboard.view"; -import { DashboardProps, DashboardState } from "./Dashboard.models"; +import { DashboardProps, DashboardState, DashboardStateSetters } from "./Dashboard.models"; export default function Dashboard(props: DashboardProps) { const [state, setState] = React.useState({ @@ -49,16 +49,20 @@ export default function Dashboard(props: DashboardProps) { setState(prev => ({ ...prev, selectedGroupKey: groupKey })); }; + const stateSetters: DashboardStateSetters = { + togglePeriodType, + toggleComparison, + setSelectedPeriodId, + setSelectedGroupKey, + }; + return ( ); } diff --git a/src/components/Dashboard/Dashboard.view.tsx b/src/components/Dashboard/Dashboard.view.tsx index f3837fe..dba16b8 100644 --- a/src/components/Dashboard/Dashboard.view.tsx +++ b/src/components/Dashboard/Dashboard.view.tsx @@ -10,16 +10,13 @@ import { } from "@mui/material"; import { useTheme, alpha } from "@mui/material/styles"; import { GroupKey } from "../../features/report"; -import { DashboardProps, DashboardState } from "./Dashboard.models"; +import { DashboardProps, DashboardState, DashboardStateSetters } from "./Dashboard.models"; interface ViewProps extends DashboardProps { state: DashboardState; setState: React.Dispatch>; toggleFlow: (event: React.MouseEvent, newFlow: "outflows" | "inflows" | null) => void; - togglePeriodType: () => void; - setSelectedPeriodId: (id: string | null) => void; - setSelectedGroupKey: (groupKey: GroupKey | null) => void; - toggleComparison: () => void; + stateSetters: DashboardStateSetters; } export default function DashboardView({ @@ -28,14 +25,12 @@ export default function DashboardView({ state, setState, toggleFlow, - togglePeriodType, - toggleComparison, - setSelectedPeriodId, - setSelectedGroupKey, + stateSetters, }: ViewProps) { const theme = useTheme(); const themeMode = theme.palette.mode; - const { flow, periodType, comparison, selectedPeriodId, selectedGroupKey } = state; + const { flow, selectedGroupKey } = state; + const { setSelectedGroupKey } = stateSetters; // Resolve colors with fallbacks const colors = React.useMemo(() => { @@ -121,17 +116,8 @@ export default function DashboardView({ colorScheme={colors} // State management - flow={flow} - - periodType={periodType} - comparison={comparison} - selectedPeriodId={selectedPeriodId} - selectedGroupKey={selectedGroupKey} - - togglePeriodType={togglePeriodType} - toggleComparison={toggleComparison} - setSelectedPeriodId={setSelectedPeriodId} - setSelectedGroupKey={setSelectedGroupKey} + state={state} + stateSetters={stateSetters} isFetching={arguments[0].isFetching} /> diff --git a/src/components/HistoryChart/HistoryChart.models.ts b/src/components/HistoryChart/HistoryChart.models.ts index 0c63cc0..79b5561 100644 --- a/src/components/HistoryChart/HistoryChart.models.ts +++ b/src/components/HistoryChart/HistoryChart.models.ts @@ -1,9 +1,4 @@ -import { - DashboardFlow, - DashboardPeriodType, - DashboardSelectedPeriodId -} from "../Dashboard"; -import { ReportData } from "../../features/report"; +import { ComponentProps } from "../report.props"; export interface _ChartDataPoint { id: string; @@ -16,27 +11,6 @@ export interface ChartDataPoint extends _ChartDataPoint { compare?: _ChartDataPoint; } -export interface HistoryChartProps { - header: string; - summary?: string; +export interface HistoryChartProps extends ComponentProps { tabs: string[]; - - reportData: ReportData; - - colorScheme: { - primary: string; - light: string; - text: string; - }; - - flow: DashboardFlow; - periodType: DashboardPeriodType; - selectedPeriodId: DashboardSelectedPeriodId; - comparison: boolean; - - togglePeriodType: () => void; - setSelectedPeriodId: (id: string | null) => void; - toggleComparison: () => void; - - isFetching?: boolean; } diff --git a/src/components/HistoryChart/HistoryChart.tsx b/src/components/HistoryChart/HistoryChart.tsx index 98ba837..5dca757 100644 --- a/src/components/HistoryChart/HistoryChart.tsx +++ b/src/components/HistoryChart/HistoryChart.tsx @@ -7,12 +7,13 @@ export default function HistoryChart(props: HistoryChartProps) { const { tabs, reportData, - flow, - comparison, - selectedPeriodId, - setSelectedPeriodId + state, + stateSetters, } = props; + const { flow, comparison, selectedPeriodId } = state; + const { setSelectedPeriodId } = stateSetters; + const [activeTab, setActiveTab] = React.useState(tabs[0] || ""); const [startIndex, setStartIndex] = React.useState(0); diff --git a/src/components/HistoryChart/HistoryChart.view.tsx b/src/components/HistoryChart/HistoryChart.view.tsx index 3b72287..5622a78 100644 --- a/src/components/HistoryChart/HistoryChart.view.tsx +++ b/src/components/HistoryChart/HistoryChart.view.tsx @@ -35,14 +35,8 @@ export default function HistoryChartView(props: ViewProps) { tabs, colorScheme, - flow, - periodType, - selectedPeriodId, - comparison, - - togglePeriodType, - setSelectedPeriodId, - toggleComparison, + state, + stateSetters, activeTab, setActiveTab, @@ -55,6 +49,9 @@ export default function HistoryChartView(props: ViewProps) { activeDataKey, } = props; + const { flow, periodType, selectedPeriodId, comparison } = state; + const { togglePeriodType, setSelectedPeriodId, toggleComparison } = stateSetters; + const theme = useTheme(); const isDark = theme.palette.mode === "dark"; diff --git a/src/components/LatestItems/LatestItems.adapter.ts b/src/components/LatestItems/LatestItems.adapter.ts index d35e8ec..5720ab6 100644 --- a/src/components/LatestItems/LatestItems.adapter.ts +++ b/src/components/LatestItems/LatestItems.adapter.ts @@ -1,67 +1,19 @@ -import { ReportData, Transaction, GroupKey } from "../../features/report"; +import { ReportData, GroupKey } from "../../features/report"; import { - mergeBucketPeriods, - periodIdToKey, formatCurrency, - filterBuckets, + extractFilteredTransactions, } from "../report.helpers"; import { LatestItem } from "./LatestItems.models"; -// ─── Transaction extraction ───────────────────────────────── - -function extractTransactions( - reportData: ReportData, - selectedPeriodId: string | null, - selectedGroupKey: GroupKey | null, -): Transaction[] { - // 1. Get raw transactions - let rawTxns: Transaction[] = []; - - if (selectedPeriodId) { - const key = periodIdToKey(selectedPeriodId); - const periods = mergeBucketPeriods(reportData.buckets, key); - const selected = periods.find((p) => p.id === selectedPeriodId); - rawTxns = selected?.metric.transactions || []; - } else { - const periods = mergeBucketPeriods(reportData.buckets, "all"); - if (periods.length > 0) { - rawTxns = periods[0].metric.transactions || []; - } - } - - // 2. Filter by group key - if (selectedGroupKey) { - rawTxns = rawTxns.filter(txn => { - let match = true; - if (selectedGroupKey.tags && selectedGroupKey.tags.length > 0) { - if (!txn.tags) match = false; - else { - const txnTags = txn.tags.map(t => typeof t === "string" ? t : t.name); - if (!selectedGroupKey.tags.every(selectedTag => txnTags.includes(selectedTag))) match = false; - } - } - if (match && selectedGroupKey.payee && selectedGroupKey.payee.length > 0) { - if (!txn.payee || !txn.payee.name) match = false; - else { - if (!selectedGroupKey.payee.includes(txn.payee.name)) match = false; - } - } - return match; - }); - } - - return rawTxns; -} - // ─── Main adapter ──────────────────────────────────────────── export function buildLatestItems( reportData: ReportData, - selectedPeriodId: string | null, - selectedGroupKey: GroupKey | null, + selectedPeriodId: string | null | undefined, + selectedGroupKey: GroupKey | null | undefined, flow: "outflows" | "inflows" ): LatestItem[] { - const txns = extractTransactions(reportData, selectedPeriodId, selectedGroupKey); + const txns = extractFilteredTransactions(reportData, selectedPeriodId, selectedGroupKey); return txns .sort( diff --git a/src/components/LatestItems/LatestItems.tsx b/src/components/LatestItems/LatestItems.tsx index badc744..ac2d356 100644 --- a/src/components/LatestItems/LatestItems.tsx +++ b/src/components/LatestItems/LatestItems.tsx @@ -1,27 +1,19 @@ import * as React from "react"; -import { ReportData, GroupKey } from "../../features/report"; +import { ComponentProps } from "../report.props"; import { buildLatestItems } from "./LatestItems.adapter"; import LatestItemsView from "./LatestItems.view"; -type Props = { - reportData: ReportData; - flow: "outflows" | "inflows"; - header: string; - selectedPeriodId: string | null; - selectedGroupKey?: GroupKey | null; - accentColor: string; - isFetching?: boolean; -}; +type Props = ComponentProps; export default function LatestItems({ reportData, - flow, + state, + stateSetters, header, - selectedPeriodId, - selectedGroupKey = null, - accentColor, + accentColor = "", isFetching, }: Props) { + const { flow, selectedPeriodId, selectedGroupKey } = state; const [visibleCount, setVisibleCount] = React.useState(5); const allItems = React.useMemo(() => { diff --git a/src/components/ProgressCard/TopPayees.adapter.ts b/src/components/ProgressCard/TopPayees.adapter.ts index af71bea..dffbdc9 100644 --- a/src/components/ProgressCard/TopPayees.adapter.ts +++ b/src/components/ProgressCard/TopPayees.adapter.ts @@ -1,5 +1,8 @@ -import { mergeBucketPeriods, periodIdToKey } from "../report.helpers"; import { GroupKey, ReportData } from "../../features/report"; +import { + extractFilteredTransactions, + aggregateTransactions, +} from "../report.helpers"; export interface PayeeItem { name: string; @@ -12,54 +15,17 @@ export function extractTopPayees( selectedPeriodId?: string | null, selectedGroupKey?: GroupKey | null ): { items: PayeeItem[]; total: number } { - const payeeMap = new Map(); + const txns = extractFilteredTransactions(reportData, selectedPeriodId, selectedGroupKey); - let targetPeriods = []; - - if (selectedPeriodId) { - const key = periodIdToKey(selectedPeriodId); - const periods = mergeBucketPeriods(reportData.buckets, key); - const selected = periods.find((p) => p.id === selectedPeriodId); - if (selected) { - targetPeriods.push(selected); + const { items, total } = aggregateTransactions(txns, (txn) => { + if (txn.payee && txn.payee.name) { + return [txn.payee.name]; } - } else { - // If no specific period is selected, aggregate over the "all" period bucket - targetPeriods = mergeBucketPeriods(reportData.buckets, "all"); - } - - for (const p of targetPeriods) { - let txns = p.metric.transactions || []; - - if (selectedGroupKey?.tags && selectedGroupKey.tags.length > 0) { - txns = txns.filter(txn => { - if (!txn.tags) return false; - const txnTags = txn.tags.map(t => typeof t === "string" ? t : t.name); - return selectedGroupKey.tags!.every(selectedTag => txnTags.includes(selectedTag)); - }); - } - - for (const txn of txns) { - if (txn.payee && txn.payee.name) { - const current = payeeMap.get(txn.payee.name) || 0; - payeeMap.set(txn.payee.name, current + txn.amount); - } - } - } - - let items: PayeeItem[] = []; - let total = 0; - - for (const [name, amount] of payeeMap.entries()) { - items.push({ name, amount }); - total += amount; - } - - // Sort descending by amount - items.sort((a, b) => b.amount - a.amount); + return []; + }); return { - items: items.slice(0, 4), // Top 4 + items, total, }; } diff --git a/src/components/ProgressCard/TopPayees.tsx b/src/components/ProgressCard/TopPayees.tsx index ea161da..3103496 100644 --- a/src/components/ProgressCard/TopPayees.tsx +++ b/src/components/ProgressCard/TopPayees.tsx @@ -1,30 +1,24 @@ import * as React from "react"; import { Box, Paper, Typography } from "@mui/material"; -import { ReportData, GroupKey } from "../../features/report"; +import { ComponentProps } from "../report.props"; import ProgressCard from "./ProgressCard"; import { extractTopPayees } from "./TopPayees.adapter"; -type Props = { - reportData: ReportData; - flow: "outflows" | "inflows"; - header: string; - selectedPeriodId?: string | null; - selectedGroupKey?: GroupKey | null; - setSelectedGroupKey?: (key: GroupKey | null) => void; +interface Props extends ComponentProps { compact?: boolean; - isFetching?: boolean; -}; +} export default function TopPayees({ reportData, - flow, + state, + stateSetters, header, - selectedPeriodId, - selectedGroupKey, - setSelectedGroupKey, compact = true, isFetching, }: Props) { + const { flow, selectedPeriodId, selectedGroupKey } = state; + const { setSelectedGroupKey } = stateSetters; + const { items, total } = React.useMemo(() => { return extractTopPayees(reportData, flow, selectedPeriodId, selectedGroupKey); }, [reportData, flow, selectedPeriodId, selectedGroupKey]); diff --git a/src/components/ProgressCard/TopTags.adapter.ts b/src/components/ProgressCard/TopTags.adapter.ts index eb4cf1f..871fb94 100644 --- a/src/components/ProgressCard/TopTags.adapter.ts +++ b/src/components/ProgressCard/TopTags.adapter.ts @@ -1,11 +1,9 @@ -import { ReportData } from "../../features/report"; +import { ReportData, GroupKey } from "../../features/report"; import { - mergeBucketPeriods, - periodIdToKey, + extractFilteredTransactions, + aggregateTransactions, } from "../report.helpers"; -import { GroupKey } from "../../features/report"; - export interface TagItem { tag: string; amount: number; @@ -17,55 +15,17 @@ export function extractTopTags( selectedPeriodId?: string | null, selectedGroupKey?: GroupKey | null ): { items: TagItem[]; total: number } { - const tagMap = new Map(); + const txns = extractFilteredTransactions(reportData, selectedPeriodId, selectedGroupKey); - let periodKey: ReturnType = "all"; - if (selectedPeriodId) { - periodKey = periodIdToKey(selectedPeriodId); - } - - const periods = mergeBucketPeriods(reportData.buckets, periodKey); - - let period = periods[0]; - if (selectedPeriodId) { - period = periods.find(p => p.id === selectedPeriodId) || period; - } else if (periods.length > 0) { - period = periods.reduce((latest, p) => - new Date(p.start).getTime() > new Date(latest.start).getTime() - ? p - : latest - ); - } - - if (period && period.metric && period.metric.transactions) { - let txns = period.metric.transactions; - if (selectedGroupKey?.payee && selectedGroupKey.payee.length > 0) { - txns = txns.filter(txn => - txn.payee?.name && selectedGroupKey.payee!.includes(txn.payee.name) - ); + const { items, total } = aggregateTransactions(txns, (txn) => { + if (txn.tags && txn.tags.length > 0) { + return txn.tags.map((t) => (typeof t === "string" ? t : t.name)); } + return ["Untagged"]; + }); - for (const txn of txns) { - if (txn.tags && txn.tags.length > 0) { - for (const tagObj of txn.tags) { - const tagName = typeof tagObj === "string" ? tagObj : tagObj.name; - tagMap.set(tagName, (tagMap.get(tagName) || 0) + txn.amount); - } - } else { - tagMap.set("Untagged", (tagMap.get("Untagged") || 0) + txn.amount); - } - } - } - - const arr = Array.from(tagMap.entries()).map(([tag, amount]) => ({ - tag, - amount, - })); - - arr.sort((a, b) => b.amount - a.amount); - - const top = arr.slice(0, 4); - const total = top.reduce((sum, t) => sum + t.amount, 0); - - return { items: top, total }; + return { + items: items.map((item) => ({ tag: item.name, amount: item.amount })), + total, + }; } diff --git a/src/components/ProgressCard/TopTags.tsx b/src/components/ProgressCard/TopTags.tsx index 08b2af3..c3aa263 100644 --- a/src/components/ProgressCard/TopTags.tsx +++ b/src/components/ProgressCard/TopTags.tsx @@ -1,30 +1,24 @@ import * as React from "react"; import { Box, Paper, Typography } from "@mui/material"; -import { ReportData, GroupKey } from "../../features/report"; +import { ComponentProps } from "../report.props"; import ProgressCard from "./ProgressCard"; import { extractTopTags } from "./TopTags.adapter"; -type Props = { - reportData: ReportData; - flow: "outflows" | "inflows"; - header: string; - selectedPeriodId?: string | null; - selectedGroupKey?: GroupKey | null; - setSelectedGroupKey?: (key: GroupKey | null) => void; +interface Props extends ComponentProps { compact?: boolean; - isFetching?: boolean; -}; +} export default function TopTags({ reportData, - flow, + state, + stateSetters, header, - selectedPeriodId, - selectedGroupKey, - setSelectedGroupKey, compact = true, isFetching, }: Props) { + const { flow, selectedPeriodId, selectedGroupKey } = state; + const { setSelectedGroupKey } = stateSetters; + const { items, total } = React.useMemo(() => { return extractTopTags(reportData, flow, selectedPeriodId, selectedGroupKey); }, [reportData, flow, selectedPeriodId, selectedGroupKey]); diff --git a/src/components/report.helpers.ts b/src/components/report.helpers.ts index 0b21725..df441a2 100644 --- a/src/components/report.helpers.ts +++ b/src/components/report.helpers.ts @@ -3,6 +3,8 @@ import { ReportBucket, GroupKey, PeriodType, + ReportData, + Transaction, } from "../features/report"; // ─── Types ──────────────────────────────────────────────────── @@ -140,3 +142,89 @@ export function filterBuckets( if (!selectedGroupKey) return buckets; return buckets.filter((b) => matchesGroupKey(b, selectedGroupKey)); } + +export function extractFilteredTransactions( + reportData: ReportData, + selectedPeriodId: string | null | undefined, + selectedGroupKey: GroupKey | null | undefined +): Transaction[] { + let txns: Transaction[] = []; + + if (selectedPeriodId) { + const key = periodIdToKey(selectedPeriodId); + const periods = mergeBucketPeriods(reportData.buckets, key); + const selected = periods.find((p) => p.id === selectedPeriodId); + txns = selected?.metric.transactions || []; + } else { + const periods = mergeBucketPeriods(reportData.buckets, "all"); + if (periods.length > 0) { + const period = periods.reduce((latest, p) => + new Date(p.start).getTime() > new Date(latest.start).getTime() + ? p + : latest + , periods[0]); + txns = period?.metric.transactions || []; + } + } + + if (selectedGroupKey) { + txns = txns.filter((txn) => { + let match = true; + if (selectedGroupKey.tags && selectedGroupKey.tags.length > 0) { + if (!txn.tags) { + match = false; + } else { + const txnTags = txn.tags.map((t: any) => + typeof t === "string" ? t : t.name + ); + if ( + !selectedGroupKey.tags.every((selectedTag) => + txnTags.includes(selectedTag) + ) + ) { + match = false; + } + } + } + if (match && selectedGroupKey.payee && selectedGroupKey.payee.length > 0) { + if (!txn.payee || !txn.payee.name) { + match = false; + } else { + if (!selectedGroupKey.payee.includes(txn.payee.name)) { + match = false; + } + } + } + return match; + }); + } + + return txns; +} + +export function aggregateTransactions( + transactions: Transaction[], + keyExtractor: (txn: Transaction) => string[], + limit = 4 +): { items: { name: string; amount: number }[]; total: number } { + const map = new Map(); + + for (const txn of transactions) { + const keys = keyExtractor(txn); + for (const key of keys) { + map.set(key, (map.get(key) || 0) + txn.amount); + } + } + + const items = Array.from(map.entries()).map(([name, amount]) => ({ + name, + amount, + })); + + items.sort((a, b) => b.amount - a.amount); + + const top = items.slice(0, limit); + const total = top.reduce((sum, item) => sum + item.amount, 0); + + return { items: top, total }; +} diff --git a/src/components/report.props.ts b/src/components/report.props.ts new file mode 100644 index 0000000..1f7e04e --- /dev/null +++ b/src/components/report.props.ts @@ -0,0 +1,17 @@ +import { ReportData } from "../features/report"; +import { DashboardState, DashboardStateSetters } from "./Dashboard"; + +export interface ComponentProps { + reportData: ReportData; + state: DashboardState; + stateSetters: DashboardStateSetters; + isFetching?: boolean; + header: string; + summary?: string; + accentColor?: string; + colorScheme?: { + primary: string; + light: string; + text: string; + }; +} -- 2.49.1 From 340cb6628d8178475640c4a7f100a53dba24c791 Mon Sep 17 00:00:00 2001 From: Vishesh 'ironeagle' Bangotra Date: Mon, 18 May 2026 16:00:43 +0530 Subject: [PATCH 2/5] common component props --- src/Dashboard.tsx | 1 - src/components/Dashboard/Dashboard.models.ts | 36 ++-- src/components/Dashboard/Dashboard.tsx | 154 +++++++++++++++--- src/components/Dashboard/Dashboard.view.tsx | 129 --------------- .../HistoryChart/HistoryChart.models.ts | 6 - .../HistoryChart/HistoryChart.props.ts | 21 +++ src/components/HistoryChart/HistoryChart.tsx | 8 +- .../HistoryChart/HistoryChart.view.tsx | 60 +++---- .../ProgressCard/ProgressCard.props.ts | 14 ++ src/components/ProgressCard/ProgressCard.tsx | 25 --- .../ProgressCard/ProgressCard.view.tsx | 114 ++++++------- src/components/ProgressCard/TopPayees.tsx | 36 ++-- src/components/ProgressCard/TopTags.tsx | 36 ++-- src/components/report.props.ts | 17 -- src/dashboard-config.ts | 12 -- 15 files changed, 303 insertions(+), 366 deletions(-) delete mode 100644 src/components/Dashboard/Dashboard.view.tsx create mode 100644 src/components/HistoryChart/HistoryChart.props.ts create mode 100644 src/components/ProgressCard/ProgressCard.props.ts delete mode 100644 src/components/ProgressCard/ProgressCard.tsx delete mode 100644 src/components/report.props.ts diff --git a/src/Dashboard.tsx b/src/Dashboard.tsx index ffcf1e9..215a350 100644 --- a/src/Dashboard.tsx +++ b/src/Dashboard.tsx @@ -161,7 +161,6 @@ export default function Dashboard() { config={configuration} data={data} isFetching={report.isFetching} - onFlowChange={handleFlowChange} /> ); diff --git a/src/components/Dashboard/Dashboard.models.ts b/src/components/Dashboard/Dashboard.models.ts index 87f9224..87fe839 100644 --- a/src/components/Dashboard/Dashboard.models.ts +++ b/src/components/Dashboard/Dashboard.models.ts @@ -19,27 +19,23 @@ export interface DashboardState { export interface DashboardStateSetters { setSelectedPeriodId: (id: DashboardSelectedPeriodId) => void; setSelectedGroupKey: (groupKey: GroupKey | null) => void; + toggleFlow: () => void; togglePeriodType: () => void; toggleComparison: () => void; } export interface DashboardSection { id: string; - title?: string; - summary?: string; + title: string; component: React.ComponentType; + summary?: string; settings?: Record; - isList?: boolean; - style?: { - size?: number; - [key: string]: any; - }; } export interface ColorDefinition { primary: string; - background?: string; - text?: string; + background: string; + text: string; } export interface ThemeAwarePalette { @@ -49,14 +45,28 @@ export interface ThemeAwarePalette { export interface DashboardConfig { sections: DashboardSection[]; - style?: { - palette?: Record; + style: { + palette: Record; }; } export interface DashboardProps { config: DashboardConfig; data: ReportData; - isFetching?: boolean; - onFlowChange?: (state: DashboardState) => void; + isFetching: boolean; +} + + +export interface ComponentProps extends DashboardSection { + reportData: ReportData; + + state: DashboardState; + stateSetters: DashboardStateSetters; + isFetching: boolean; + + colorScheme: { + primary: string; + light: string; + text: string; + }; } diff --git a/src/components/Dashboard/Dashboard.tsx b/src/components/Dashboard/Dashboard.tsx index 50fb4b9..0c5ff56 100644 --- a/src/components/Dashboard/Dashboard.tsx +++ b/src/components/Dashboard/Dashboard.tsx @@ -1,8 +1,23 @@ import * as React from "react"; -import DashboardView from "./Dashboard.view"; +import { + Box, + Container, + Grid, + ToggleButton, + ToggleButtonGroup, + Button +} from "@mui/material"; +import { useTheme, alpha } from "@mui/material/styles"; import { DashboardProps, DashboardState, DashboardStateSetters } from "./Dashboard.models"; -export default function Dashboard(props: DashboardProps) { +export default function Dashboard({ + config, + data, + isFetching, +}: DashboardProps) { + const theme = useTheme(); + const themeMode = theme.palette.mode; + const [state, setState] = React.useState({ flow: "outflows", periodType: "rolling", @@ -11,20 +26,11 @@ export default function Dashboard(props: DashboardProps) { comparison: false, }); - const toggleFlow = ( - event: React.MouseEvent, - newFlow: "outflows" | "inflows" | null - ) => { - if (newFlow === null) return; - - setState(prev => { - if (prev.flow === newFlow) return prev; - - const next = { ...prev, flow: newFlow }; - props.onFlowChange?.(next); - - return next; - }); + const toggleFlow = () => { + setState(prev => ({ + ...prev, + flow: prev.flow === "outflows" ? "inflows" : "outflows", + })); }; const togglePeriodType = () => { @@ -52,17 +58,119 @@ export default function Dashboard(props: DashboardProps) { const stateSetters: DashboardStateSetters = { togglePeriodType, toggleComparison, + toggleFlow, setSelectedPeriodId, setSelectedGroupKey, }; + const { flow, selectedGroupKey } = state; + + const colors = React.useMemo(() => { + const palette = config.style.palette[flow]; + const modeColors = palette[themeMode]; + + return { + primary: modeColors.primary, + light: modeColors.background || alpha(modeColors.primary, 0.1), + text: + modeColors.text || + (themeMode === "light" ? theme.palette.text.primary : "#fff"), + }; + + // if (modeColors) { + // return { + // primary: modeColors.primary, + // light: modeColors.background || alpha(modeColors.primary, 0.1), + // text: + // modeColors.text || + // (themeMode === "light" ? theme.palette.text.primary : "#fff"), + // }; + // } + // + // const themeColor = + // flow === "outflows" ? theme.palette.error : theme.palette.success; + // + // return { + // primary: themeColor.main, + // light: alpha(themeColor.main, themeMode === "light" ? 0.08 : 0.15), + // text: themeColor.main, + // }; + }, [config.style?.palette, flow, themeMode, theme.palette]); + return ( - + + + + Outflows + Inflows + + + {selectedGroupKey && Object.keys(selectedGroupKey).length > 0 && ( + + )} + + + + {config.sections.map((section) => { + const Component = section.component; + + return ( + + + + ); + })} + + ); } diff --git a/src/components/Dashboard/Dashboard.view.tsx b/src/components/Dashboard/Dashboard.view.tsx deleted file mode 100644 index dba16b8..0000000 --- a/src/components/Dashboard/Dashboard.view.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import * as React from "react"; -import { - Box, - Container, - Grid, - Typography, - ToggleButton, - ToggleButtonGroup, - Button -} from "@mui/material"; -import { useTheme, alpha } from "@mui/material/styles"; -import { GroupKey } from "../../features/report"; -import { DashboardProps, DashboardState, DashboardStateSetters } from "./Dashboard.models"; - -interface ViewProps extends DashboardProps { - state: DashboardState; - setState: React.Dispatch>; - toggleFlow: (event: React.MouseEvent, newFlow: "outflows" | "inflows" | null) => void; - stateSetters: DashboardStateSetters; -} - -export default function DashboardView({ - config, - data, - state, - setState, - toggleFlow, - stateSetters, -}: ViewProps) { - const theme = useTheme(); - const themeMode = theme.palette.mode; - const { flow, selectedGroupKey } = state; - const { setSelectedGroupKey } = stateSetters; - - // Resolve colors with fallbacks - const colors = React.useMemo(() => { - const palette = config.style?.palette?.[flow]; - const modeColors = palette ? palette[themeMode] : null; - - if (modeColors) { - return { - primary: modeColors.primary, - light: modeColors.background || alpha(modeColors.primary, 0.1), - text: modeColors.text || (themeMode === 'light' ? theme.palette.text.primary : '#fff') - }; - } - - // Fallback to standard theme colors - const themeColor = flow === 'outflows' ? theme.palette.error : theme.palette.success; - return { - primary: themeColor.main, - light: alpha(themeColor.main, themeMode === 'light' ? 0.08 : 0.15), - text: themeColor.main - }; - }, [config.style?.palette, flow, themeMode, theme.palette]); - - return ( - - - - Outflows - Inflows - - - {selectedGroupKey && Object.keys(selectedGroupKey).length > 0 && ( - - )} - - - - {config.sections.map((section) => { - const Component = section.component; - - return ( - - - - ); - })} - - - ); -} diff --git a/src/components/HistoryChart/HistoryChart.models.ts b/src/components/HistoryChart/HistoryChart.models.ts index 79b5561..08f69f6 100644 --- a/src/components/HistoryChart/HistoryChart.models.ts +++ b/src/components/HistoryChart/HistoryChart.models.ts @@ -1,5 +1,3 @@ -import { ComponentProps } from "../report.props"; - export interface _ChartDataPoint { id: string; label: string; @@ -10,7 +8,3 @@ export interface _ChartDataPoint { export interface ChartDataPoint extends _ChartDataPoint { compare?: _ChartDataPoint; } - -export interface HistoryChartProps extends ComponentProps { - tabs: string[]; -} diff --git a/src/components/HistoryChart/HistoryChart.props.ts b/src/components/HistoryChart/HistoryChart.props.ts new file mode 100644 index 0000000..0e1cc61 --- /dev/null +++ b/src/components/HistoryChart/HistoryChart.props.ts @@ -0,0 +1,21 @@ +import * as React from "react"; +import { ComponentProps } from "../Dashboard"; +import { ChartDataPoint } from "./HistoryChart.models"; + +export interface HistoryChartProps extends ComponentProps { + settings: { + tabs: string[]; + }; +} + +export interface HistoryChartViewProps extends HistoryChartProps { + activeTab: string; + setActiveTab: (v: string) => void; + currentData: ChartDataPoint[]; + visibleData: ChartDataPoint[]; + maxAmount: number; + visibleCount: number; + startIndex: number; + setStartIndex: React.Dispatch>; + activeDataKey: string; +} diff --git a/src/components/HistoryChart/HistoryChart.tsx b/src/components/HistoryChart/HistoryChart.tsx index 5dca757..95291c6 100644 --- a/src/components/HistoryChart/HistoryChart.tsx +++ b/src/components/HistoryChart/HistoryChart.tsx @@ -1,18 +1,22 @@ import * as React from "react"; -import { HistoryChartProps } from "./HistoryChart.models"; import HistoryChartView from "./HistoryChart.view"; import { buildChartData, tabToKey } from "./HistoryChart.adapter"; +import { HistoryChartProps } from "./HistoryChart.props"; + export default function HistoryChart(props: HistoryChartProps) { const { - tabs, + settings, reportData, state, stateSetters, + + isFetching, } = props; const { flow, comparison, selectedPeriodId } = state; const { setSelectedPeriodId } = stateSetters; + const { tabs } = settings; const [activeTab, setActiveTab] = React.useState(tabs[0] || ""); const [startIndex, setStartIndex] = React.useState(0); diff --git a/src/components/HistoryChart/HistoryChart.view.tsx b/src/components/HistoryChart/HistoryChart.view.tsx index 5622a78..c02923c 100644 --- a/src/components/HistoryChart/HistoryChart.view.tsx +++ b/src/components/HistoryChart/HistoryChart.view.tsx @@ -11,43 +11,31 @@ import IconButton from "@mui/material/IconButton"; import ChevronLeftIcon from "@mui/icons-material/ChevronLeft"; import ChevronRightIcon from "@mui/icons-material/ChevronRight"; import { - ChartDataPoint, - HistoryChartProps, -} from "./HistoryChart.models"; + HistoryChartViewProps, +} from "./HistoryChart.props"; import { formatDisplay } from "./HistoryChart.utils"; -interface ViewProps extends HistoryChartProps { - activeTab: string; - setActiveTab: (v: string) => void; - currentData: ChartDataPoint[]; - visibleData: ChartDataPoint[]; - maxAmount: number; - visibleCount: number; - startIndex: number; - setStartIndex: React.Dispatch>; - activeDataKey: string; -} +export default function HistoryChartView({ + title, + summary, + settings, -export default function HistoryChartView(props: ViewProps) { - const { - header, - summary, - tabs, - colorScheme, + state, + stateSetters, + isFetching, - state, - stateSetters, + colorScheme, - activeTab, - setActiveTab, - currentData, - visibleData, - maxAmount, - visibleCount, - startIndex, - setStartIndex, - activeDataKey, - } = props; + activeTab, + setActiveTab, + currentData, + visibleData, + maxAmount, + visibleCount, + startIndex, + setStartIndex, + activeDataKey, +}: HistoryChartViewProps) { const { flow, periodType, selectedPeriodId, comparison } = state; const { togglePeriodType, setSelectedPeriodId, toggleComparison } = stateSetters; @@ -89,13 +77,13 @@ export default function HistoryChartView(props: ViewProps) { border: "1px solid", borderColor: "divider", bgcolor: isDark ? "background.paper" : colorScheme.light, - opacity: props.isFetching ? 0.6 : 1, + opacity: isFetching ? 0.6 : 1, transition: "opacity 0.3s ease", - pointerEvents: props.isFetching ? "none" : "auto", + pointerEvents: isFetching ? "none" : "auto", }} > - {header} + {title} {summary && ( @@ -105,7 +93,7 @@ export default function HistoryChartView(props: ViewProps) { )} - {tabs.map((tab) => ( + {settings.tabs.map((tab) => ( {tab} diff --git a/src/components/ProgressCard/ProgressCard.props.ts b/src/components/ProgressCard/ProgressCard.props.ts new file mode 100644 index 0000000..5ff8517 --- /dev/null +++ b/src/components/ProgressCard/ProgressCard.props.ts @@ -0,0 +1,14 @@ +import { ComponentProps } from "../Dashboard"; + +export interface ProgressCardProps extends ComponentProps { + settings: { + compact: boolean; + }; +} + +export interface ProgressCardViewProps extends ProgressCardProps { + progressAmount: number; + totalAmount: number; + selected: boolean; + onClick: () => void; +} diff --git a/src/components/ProgressCard/ProgressCard.tsx b/src/components/ProgressCard/ProgressCard.tsx deleted file mode 100644 index b42c2e4..0000000 --- a/src/components/ProgressCard/ProgressCard.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import * as React from "react"; -import ProgressCardView from "./ProgressCard.view"; -import { ProgressCardProps } from "./ProgressCard.models"; -import { getPercentage, formatCurrency } from "../report.helpers"; - -export default function ProgressCard(props: ProgressCardProps) { - const { progressAmount, totalAmount, compact = false } = props; - - const percentage = getPercentage(progressAmount, totalAmount); - - const formattedProgress = formatCurrency(progressAmount); - const formattedTotal = formatCurrency(totalAmount); - - return ( - - ); -} diff --git a/src/components/ProgressCard/ProgressCard.view.tsx b/src/components/ProgressCard/ProgressCard.view.tsx index 34472bf..8d04a74 100644 --- a/src/components/ProgressCard/ProgressCard.view.tsx +++ b/src/components/ProgressCard/ProgressCard.view.tsx @@ -8,93 +8,83 @@ import { linearProgressClasses } from "@mui/material"; import { useTheme, alpha } from "@mui/material/styles"; -import { ProgressCardProps } from "./ProgressCard.models"; - -interface ViewProps extends ProgressCardProps { - percentage: number; - formattedProgress: string; - formattedTotal: string; - selected?: boolean; - onClick?: () => void; -} +import { getPercentage, formatCurrency } from "../report.helpers"; +import { ProgressCardViewProps } from "./ProgressCard.props"; export default function ProgressCardView({ - header, - colorTheme = "info", - percentage, - formattedProgress, - formattedTotal, - compact = false, + title, + settings, + + isFetching, + + colorScheme, + + progressAmount, + totalAmount, selected, onClick, -}: ViewProps) { +}: ProgressCardViewProps) { const theme = useTheme(); const isDark = theme.palette.mode === "dark"; + const percentage = getPercentage(progressAmount, totalAmount); + const formattedProgress = formatCurrency(progressAmount); + const formattedTotal = formatCurrency(totalAmount); + return ( { - const baseColor = theme.palette[colorTheme]?.main || theme.palette.primary.main; - const lightColor = theme.palette[colorTheme]?.light || theme.palette.primary.light; - return isDark - ? `linear-gradient(135deg, ${alpha(baseColor, 0.9)} 0%, ${alpha(baseColor, 0.3)} 100%)` - : `linear-gradient(135deg, ${baseColor} 0%, ${lightColor} 100%)`; - }, + bgcolor: isDark ? "background.paper" : colorScheme.light, color: "#fff", display: "flex", flexDirection: "column", - alignItems: compact ? "flex-start" : "center", + alignItems: settings.compact ? "flex-start" : "center", justifyContent: "center", position: "relative", overflow: "hidden", - border: selected + border: selected ? `2px solid #fff` - : isDark ? "1px solid rgba(255,255,255,0.1)" : "none", - boxShadow: (theme) => { - const baseShadow = `0 ${compact ? 6 : 12}px ${compact ? 12 : 24}px -10px ${ - isDark - ? "rgba(0,0,0,0.5)" - : theme.palette[colorTheme]?.main || theme.palette.primary.main - }`; - return selected - ? `${baseShadow}, 0 0 0 2px ${theme.palette.background.paper}, 0 0 0 4px ${theme.palette[colorTheme]?.main || theme.palette.primary.main}` - : baseShadow; - }, - opacity: arguments[0].isFetching ? 0.6 : 1, - pointerEvents: arguments[0].isFetching ? "none" : "auto", + : isDark + ? "1px solid rgba(255,255,255,0.1)" + : "none", + boxShadow: "none", + opacity: isFetching ? 0.6 : 1, + pointerEvents: isFetching ? "none" : "auto", }} > - - {header} + {title} - + {formattedProgress} @@ -108,24 +98,24 @@ export default function ProgressCardView({ /> of {formattedTotal} - + diff --git a/src/components/ProgressCard/TopPayees.tsx b/src/components/ProgressCard/TopPayees.tsx index 3103496..37786d5 100644 --- a/src/components/ProgressCard/TopPayees.tsx +++ b/src/components/ProgressCard/TopPayees.tsx @@ -1,21 +1,19 @@ import * as React from "react"; import { Box, Paper, Typography } from "@mui/material"; -import { ComponentProps } from "../report.props"; -import ProgressCard from "./ProgressCard"; +import ProgressCardView from "./ProgressCard.view"; import { extractTopPayees } from "./TopPayees.adapter"; +import { ProgressCardProps } from "./ProgressCard.props"; -interface Props extends ComponentProps { - compact?: boolean; -} +export default function TopPayees(props: ProgressCardProps) { + const { + title, -export default function TopPayees({ - reportData, - state, - stateSetters, - header, - compact = true, - isFetching, -}: Props) { + reportData, + state, + stateSetters, + + isFetching, + } = props const { flow, selectedPeriodId, selectedGroupKey } = state; const { setSelectedGroupKey } = stateSetters; @@ -39,7 +37,7 @@ export default function TopPayees({ }} > - {header} + {title} {items.map((item) => { - const isSelected = selectedGroupKey?.payee?.includes(item.name); + const isSelected = !!selectedGroupKey?.payee?.includes(item.name); return ( - { if (setSelectedGroupKey) { let newKey = selectedGroupKey ? { ...selectedGroupKey } : {}; diff --git a/src/components/ProgressCard/TopTags.tsx b/src/components/ProgressCard/TopTags.tsx index c3aa263..402ba7a 100644 --- a/src/components/ProgressCard/TopTags.tsx +++ b/src/components/ProgressCard/TopTags.tsx @@ -1,21 +1,19 @@ import * as React from "react"; import { Box, Paper, Typography } from "@mui/material"; -import { ComponentProps } from "../report.props"; -import ProgressCard from "./ProgressCard"; +import ProgressCardView from "./ProgressCard.view"; import { extractTopTags } from "./TopTags.adapter"; +import { ProgressCardProps } from "./ProgressCard.props"; -interface Props extends ComponentProps { - compact?: boolean; -} +export default function TopTags(props: ProgressCardProps) { + const { + title, -export default function TopTags({ - reportData, - state, - stateSetters, - header, - compact = true, - isFetching, -}: Props) { + reportData, + state, + stateSetters, + + isFetching, + } = props const { flow, selectedPeriodId, selectedGroupKey } = state; const { setSelectedGroupKey } = stateSetters; @@ -39,7 +37,7 @@ export default function TopTags({ }} > - {header} + {title} {items.map((item) => { - const isSelected = selectedGroupKey?.tags?.includes(item.tag); + const isSelected = !!selectedGroupKey?.tags?.includes(item.tag); return ( - { if (setSelectedGroupKey) { let newKey = selectedGroupKey ? { ...selectedGroupKey } : {}; diff --git a/src/components/report.props.ts b/src/components/report.props.ts deleted file mode 100644 index 1f7e04e..0000000 --- a/src/components/report.props.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { ReportData } from "../features/report"; -import { DashboardState, DashboardStateSetters } from "./Dashboard"; - -export interface ComponentProps { - reportData: ReportData; - state: DashboardState; - stateSetters: DashboardStateSetters; - isFetching?: boolean; - header: string; - summary?: string; - accentColor?: string; - colorScheme?: { - primary: string; - light: string; - text: string; - }; -} diff --git a/src/dashboard-config.ts b/src/dashboard-config.ts index 8f2b7f6..43dc137 100644 --- a/src/dashboard-config.ts +++ b/src/dashboard-config.ts @@ -14,9 +14,6 @@ export const configuration: DashboardConfig = { settings: { tabs: ["Weekly", "Monthly"], }, - style: { - size: 12, - }, }, { id: "top-categories", @@ -25,9 +22,6 @@ export const configuration: DashboardConfig = { settings: { compact: true, }, - style: { - size: 12, - }, }, { id: "top-payees", @@ -36,17 +30,11 @@ export const configuration: DashboardConfig = { settings: { compact: true, }, - style: { - size: 12, - }, }, { id: "items", title: 'Recent Transactions', component: LatestItems, - style: { - size: 12, - }, }, ], style: { -- 2.49.1 From eb45304207fd5f081cd3362ee6d2c801b9aadd54 Mon Sep 17 00:00:00 2001 From: Vishesh 'ironeagle' Bangotra Date: Mon, 18 May 2026 16:08:54 +0530 Subject: [PATCH 3/5] common component props --- .../LatestItems/LatestItems.models.ts | 9 ------- src/components/LatestItems/LatestItems.tsx | 27 ++++++++++--------- .../LatestItems/LatestItems.view.tsx | 10 ++++--- 3 files changed, 20 insertions(+), 26 deletions(-) diff --git a/src/components/LatestItems/LatestItems.models.ts b/src/components/LatestItems/LatestItems.models.ts index a58bd99..336ea01 100644 --- a/src/components/LatestItems/LatestItems.models.ts +++ b/src/components/LatestItems/LatestItems.models.ts @@ -5,12 +5,3 @@ export interface LatestItem { amount: string; timeAgo: string; } - -export interface LatestItemsViewProps { - items: LatestItem[]; - header: string; - accentColor: string; - canExpand: boolean; - onExpand: () => void; - isFetching?: boolean; -} diff --git a/src/components/LatestItems/LatestItems.tsx b/src/components/LatestItems/LatestItems.tsx index ac2d356..0b182e3 100644 --- a/src/components/LatestItems/LatestItems.tsx +++ b/src/components/LatestItems/LatestItems.tsx @@ -1,21 +1,24 @@ import * as React from "react"; -import { ComponentProps } from "../report.props"; import { buildLatestItems } from "./LatestItems.adapter"; import LatestItemsView from "./LatestItems.view"; +import { LatestItemsProps } from "./LatestItems.props"; -type Props = ComponentProps; +export default function LatestItems(props: LatestItemsProps) { + const { + reportData, + state, + stateSetters, + isFetching, + } = props; -export default function LatestItems({ - reportData, - state, - stateSetters, - header, - accentColor = "", - isFetching, -}: Props) { const { flow, selectedPeriodId, selectedGroupKey } = state; const [visibleCount, setVisibleCount] = React.useState(5); + // Reset count when flow changes to start clean + React.useEffect(() => { + setVisibleCount(5); + }, [flow]); + const allItems = React.useMemo(() => { return buildLatestItems(reportData, selectedPeriodId, selectedGroupKey, flow); }, [reportData, selectedPeriodId, selectedGroupKey, flow]); @@ -28,11 +31,9 @@ export default function LatestItems({ return ( setVisibleCount((prev) => prev + 5)} /> ); diff --git a/src/components/LatestItems/LatestItems.view.tsx b/src/components/LatestItems/LatestItems.view.tsx index 2be8d7d..2932573 100644 --- a/src/components/LatestItems/LatestItems.view.tsx +++ b/src/components/LatestItems/LatestItems.view.tsx @@ -10,21 +10,23 @@ import { IconButton, } from "@mui/material"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; -import { LatestItemsViewProps } from "./LatestItems.models"; +import { LatestItemsViewProps } from "./LatestItems.props"; export default function LatestItemsView({ items, - header, - accentColor, + title, canExpand, onExpand, isFetching, + colorScheme, }: LatestItemsViewProps) { + const accentColor = colorScheme?.primary || ""; + return ( - {header} + {title} -- 2.49.1 From 980dd6c111715f91e5d11ce5cb7cab755d430f1e Mon Sep 17 00:00:00 2001 From: Vishesh 'ironeagle' Bangotra Date: Mon, 18 May 2026 16:10:01 +0530 Subject: [PATCH 4/5] flow fixes --- src/Dashboard.tsx | 1 + src/components/Dashboard/Dashboard.models.ts | 1 + src/components/Dashboard/Dashboard.tsx | 38 ++++++++++++++++--- .../LatestItems/LatestItems.props.ts | 10 +++++ .../ProgressCard/ProgressCard.models.ts | 11 ------ src/components/ProgressCard/index.ts | 4 +- 6 files changed, 46 insertions(+), 19 deletions(-) create mode 100644 src/components/LatestItems/LatestItems.props.ts delete mode 100644 src/components/ProgressCard/ProgressCard.models.ts diff --git a/src/Dashboard.tsx b/src/Dashboard.tsx index 215a350..ffcf1e9 100644 --- a/src/Dashboard.tsx +++ b/src/Dashboard.tsx @@ -161,6 +161,7 @@ export default function Dashboard() { config={configuration} data={data} isFetching={report.isFetching} + onFlowChange={handleFlowChange} /> ); diff --git a/src/components/Dashboard/Dashboard.models.ts b/src/components/Dashboard/Dashboard.models.ts index 87fe839..5ec4cf3 100644 --- a/src/components/Dashboard/Dashboard.models.ts +++ b/src/components/Dashboard/Dashboard.models.ts @@ -54,6 +54,7 @@ export interface DashboardProps { config: DashboardConfig; data: ReportData; isFetching: boolean; + onFlowChange?: (state: DashboardState) => void; } diff --git a/src/components/Dashboard/Dashboard.tsx b/src/components/Dashboard/Dashboard.tsx index 0c5ff56..8ca2035 100644 --- a/src/components/Dashboard/Dashboard.tsx +++ b/src/components/Dashboard/Dashboard.tsx @@ -8,12 +8,13 @@ import { Button } from "@mui/material"; import { useTheme, alpha } from "@mui/material/styles"; -import { DashboardProps, DashboardState, DashboardStateSetters } from "./Dashboard.models"; +import { DashboardProps, DashboardState, DashboardStateSetters, DashboardFlow } from "./Dashboard.models"; export default function Dashboard({ config, data, isFetching, + onFlowChange, }: DashboardProps) { const theme = useTheme(); const themeMode = theme.palette.mode; @@ -27,10 +28,35 @@ export default function Dashboard({ }); const toggleFlow = () => { - setState(prev => ({ - ...prev, - flow: prev.flow === "outflows" ? "inflows" : "outflows", - })); + setState(prev => { + const nextFlow: DashboardFlow = prev.flow === "outflows" ? "inflows" : "outflows"; + const nextState: DashboardState = { + ...prev, + flow: nextFlow, + selectedGroupKey: null, + selectedPeriodId: null, + }; + onFlowChange?.(nextState); + return nextState; + }); + }; + + const handleFlowChange = ( + _event: React.MouseEvent, + newFlow: DashboardFlow | null + ) => { + if (newFlow !== null && newFlow !== state.flow) { + setState(prev => { + const nextState: DashboardState = { + ...prev, + flow: newFlow, + selectedGroupKey: null, + selectedPeriodId: null, + }; + onFlowChange?.(nextState); + return nextState; + }); + } }; const togglePeriodType = () => { @@ -119,7 +145,7 @@ export default function Dashboard({ void; +} diff --git a/src/components/ProgressCard/ProgressCard.models.ts b/src/components/ProgressCard/ProgressCard.models.ts deleted file mode 100644 index a4f91f3..0000000 --- a/src/components/ProgressCard/ProgressCard.models.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface ProgressCardProps { - header: string; - summary?: string; - progressAmount: number; - totalAmount: number; - colorTheme?: "primary" | "secondary" | "error" | "info" | "success" | "warning"; - compact?: boolean; - selected?: boolean; - onClick?: () => void; - isFetching?: boolean; -} diff --git a/src/components/ProgressCard/index.ts b/src/components/ProgressCard/index.ts index f242754..c2d6d76 100644 --- a/src/components/ProgressCard/index.ts +++ b/src/components/ProgressCard/index.ts @@ -1,2 +1,2 @@ -export { default } from "./ProgressCard"; -export * from "./ProgressCard.models"; +export { default } from "./ProgressCard.view"; +export * from "./ProgressCard.props"; -- 2.49.1 From 98bf77d7e83112cc33a244e5e1d2ec7f3334c3a8 Mon Sep 17 00:00:00 2001 From: Vishesh 'ironeagle' Bangotra Date: Mon, 18 May 2026 23:26:06 +0530 Subject: [PATCH 5/5] coloring fixes --- .../ProgressCard/ProgressCard.view.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/ProgressCard/ProgressCard.view.tsx b/src/components/ProgressCard/ProgressCard.view.tsx index 8d04a74..d535550 100644 --- a/src/components/ProgressCard/ProgressCard.view.tsx +++ b/src/components/ProgressCard/ProgressCard.view.tsx @@ -41,8 +41,8 @@ export default function ProgressCardView({ borderRadius: settings.compact ? 3 : 4, transform: selected ? "scale(1.02)" : "scale(1)", transition: "transform 0.2s ease, box-shadow 0.2s ease", - bgcolor: isDark ? "background.paper" : colorScheme.light, - color: "#fff", + bgcolor: colorScheme.light, + color: colorScheme.text, display: "flex", flexDirection: "column", alignItems: settings.compact ? "flex-start" : "center", @@ -50,10 +50,10 @@ export default function ProgressCardView({ position: "relative", overflow: "hidden", border: selected - ? `2px solid #fff` + ? `2px solid ${colorScheme.primary}` : isDark ? "1px solid rgba(255,255,255,0.1)" - : "none", + : "1px solid rgba(0,0,0,0.06)", boxShadow: "none", opacity: isFetching ? 0.6 : 1, pointerEvents: isFetching ? "none" : "auto", @@ -92,7 +92,7 @@ export default function ProgressCardView({ @@ -103,7 +103,7 @@ export default function ProgressCardView({ opacity: 0.85, fontWeight: 500, display: "block", - color: "rgba(255,255,255,0.9)", + color: alpha(colorScheme.text, 0.85), }} > of {formattedTotal} @@ -118,12 +118,12 @@ export default function ProgressCardView({ height: settings.compact ? 6 : 10, borderRadius: 5, [`&.${linearProgressClasses.colorPrimary}`]: { - backgroundColor: "rgba(0, 0, 0, 0.25)", + backgroundColor: isDark ? "rgba(255, 255, 255, 0.12)" : "rgba(0, 0, 0, 0.08)", }, [`& .${linearProgressClasses.bar}`]: { borderRadius: 5, - backgroundColor: "#fff", - boxShadow: "0 0 8px rgba(255,255,255,0.4)", + backgroundColor: colorScheme.primary, + boxShadow: `0 0 8px ${alpha(colorScheme.primary, 0.4)}`, }, }} /> -- 2.49.1