diff --git a/src/components/LatestItems/LatestItems.tsx b/src/components/LatestItems/LatestItems.tsx index 9c9f247..9d0aef3 100644 --- a/src/components/LatestItems/LatestItems.tsx +++ b/src/components/LatestItems/LatestItems.tsx @@ -1,3 +1,5 @@ +// components/LatestItems/LatestItems.tsx + import * as React from "react"; import { List, @@ -7,64 +9,161 @@ import { Avatar, Typography, Box, - Button, + IconButton } from "@mui/material"; +import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; +import ExpandLessIcon from "@mui/icons-material/ExpandLess"; -export interface LatestItem { - id: string | number; - icon: React.ReactNode; - iconBgColor?: string; - title: string; - subtitle: string; - amount: string; - timeAgo: string; +import { ReportData, Transaction, ReportPeriod } from "../../features/report"; +import { formatCurrency } from "../ProgressCard/ProgressCard.utils"; + +type Props = { + reportData: ReportData; + mode: "expense" | "income"; + selectedPeriodId: string | null; + accentColor: string; +}; + +type DecoratedPeriod = ReportPeriod & { + id: string; + label: string; +}; + +function mergePeriods( + reportData: ReportData, + key: "weekly" | "monthly" | "yearly" | "fyly" | "full" +): DecoratedPeriod[] { + const map = new Map(); + + for (const bucket of reportData.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, + transactions: [...(p.expenses.transactions || [])], + }, + incomes: { + ...p.incomes, + transactions: [...(p.incomes.transactions || [])], + }, + }); + } else { + existing.expenses.transactions?.push(...(p.expenses.transactions || [])); + existing.incomes.transactions?.push(...(p.incomes.transactions || [])); + } + } + } + + return Array.from(map.values()); } -export interface LatestItemsListProps { - title?: string; - items: LatestItem[]; - onViewAll?: () => void; - accentColor: any; +function extractTransactions( + reportData: ReportData, + selectedPeriodId: string | null, + mode: "expense" | "income", +): Transaction[] { + let periods: DecoratedPeriod[] = []; + + if (selectedPeriodId) { + const prefix = selectedPeriodId.split(":")[0]; + + const map: any = { + W: "weekly", + M: "monthly", + Y: "yearly", + FY: "fyly", + FULL: "full" + }; + + const key = map[prefix]; + + periods = mergePeriods(reportData, key); + const selected = periods.find(p => p.id === selectedPeriodId); + + if (!selected) return []; + + return mode === "expense" + ? (selected.expenses.transactions || []) + : (selected.incomes.transactions || []); + } + + // default → FULL + periods = mergePeriods(reportData, "full"); + + if (!periods.length) return []; + + const full = periods[0]; + + return mode === "expense" + ? (full.expenses.transactions || []) + : (full.incomes.transactions || []); } export default function LatestItems({ - title = "Recent Transactions", - items, - onViewAll, - accentColor, -}: LatestItemsListProps) { + reportData, + mode, + selectedPeriodId, + accentColor +}: Props) { + const [expanded, setExpanded] = React.useState(false); + + const items = React.useMemo(() => { + const txns = extractTransactions(reportData, selectedPeriodId, mode); + + return txns + .filter((t) => (mode === "expense" ? t.amount < 0 : t.amount >= 0)) + .sort( + (a, b) => + new Date(b.occurred_at).getTime() - + new Date(a.occurred_at).getTime() + ) + .map((t, index) => ({ + id: index + 1, + title: t.payee.name, + subtitle: t.tags.map((tag) => tag.name).join(", "), + amount: formatCurrency(t.amount), + timeAgo: new Date(t.occurred_at).toLocaleDateString("en-IN"), + })); + }, [reportData, selectedPeriodId, mode]); + + const isPeriodSelected = Boolean(selectedPeriodId); + + const visibleItems = React.useMemo(() => { + if (!isPeriodSelected) return items.slice(0, 5); + if (expanded) return items; + return items.slice(0, 5); + }, [items, isPeriodSelected, expanded]); + return ( - {/* Header */} - {title} + Recent Transactions - {onViewAll && ( - + + {isPeriodSelected && items.length > 5 && ( + setExpanded((p) => !p)}> + {expanded ? : } + )} - {/* List */} - {items.map((item, index) => ( + {visibleItems.map((item, index) => ( @@ -72,20 +171,17 @@ export default function LatestItems({ variant="rounded" sx={{ bgcolor: `${accentColor}22`, - color: "inherit", width: 48, height: 48, borderRadius: 3, mr: 2, }} - > - {item.icon} - + /> - + + {item.title} } @@ -97,10 +193,10 @@ export default function LatestItems({ /> - + {item.amount} - + {item.timeAgo} diff --git a/src/dashboard-config.ts b/src/dashboard-config.ts index 5d7bab9..26b44ec 100644 --- a/src/dashboard-config.ts +++ b/src/dashboard-config.ts @@ -29,15 +29,13 @@ export const configuration: DashboardConfig = { size: 12, }, }, - // { - // id: "latest", - // title: 'Recent Transactions', - // component: LatestItems, - // dataKey: "latest", - // style: { - // size: 12, - // }, - // }, + { + id: "items", + component: LatestItems, + style: { + size: 12, + }, + }, ], style: { palette: {