import React, { useCallback, useMemo, useState } from "react"; import { Box, Typography, Accordion, AccordionSummary, AccordionDetails, } from "@mui/material"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import { ListCellRenderer, CurrencyField, applyDisplayFormat, formatCurrency } from "../../react-openapi"; import type { ExpenseItem, ExpenseFieldConfigs } from "./types"; import { monthKey, monthLabel } from "./types"; import { ExpenseDetail } from "./ExpenseDetail"; interface GroupedMonth { key: string; items: ExpenseItem[]; total: number; currency: string; } function groupByMonth(items: ExpenseItem[]): GroupedMonth[] { const map = new Map(); for (const item of items) { const key = monthKey(item.occurred_at); const list = map.get(key) ?? []; list.push(item); map.set(key, list); } return [...map.entries()] .map(([key, list]) => { const sorted = [...list].sort( (a, b) => new Date(b.occurred_at ?? 0).getTime() - new Date(a.occurred_at ?? 0).getTime(), ); const currency = sorted.find((it) => it.account?.currency)?.account?.currency ?? "INR"; const total = sorted.reduce((sum, it) => sum + (it.amount ?? 0), 0); return { key, items: sorted, total, currency }; }) .sort((a, b) => b.key.localeCompare(a.key)); } interface ExpenseCardProps { item: ExpenseItem; expanded: boolean; currency: string; fields: ExpenseFieldConfigs; onToggle: (id: string) => void; } const ExpenseCard = React.memo(function ExpenseCard({ item, expanded, currency, fields, onToggle }: ExpenseCardProps) { const itemCurrency = item.account?.currency ?? currency; return ( onToggle(isExpanded ? item.id : "")} TransitionProps={{ unmountOnExit: true }} sx={{ border: "1px solid", borderColor: expanded ? "primary.main" : "divider", borderRadius: 2, overflow: "hidden", boxShadow: "none", "&:before": { display: "none" }, transition: "border-color 160ms ease, background-color 160ms ease", "&:hover": { borderColor: "primary.light" }, }} > } sx={{ "& .MuiAccordionSummary-content": { alignItems: "center", gap: 2, minWidth: 0, py: 0.5, }, }} > {item.entity?.logo ? ( ) : ( )} {item.entity ? applyDisplayFormat(item.entity, fields.formats.entity) : "Unknown"} {item.account?.name && ( )} ); }); interface ExpenseListProps { items: ExpenseItem[]; fields: ExpenseFieldConfigs; } export function ExpenseList({ items, fields }: ExpenseListProps) { const [expandedId, setExpandedId] = useState(null); const groups = useMemo(() => groupByMonth(items), [items]); const handleToggle = useCallback((id: string) => { setExpandedId((prev) => (prev === id ? null : id)); }, []); return ( {groups.map((group) => ( {monthLabel(group.key)} {group.items.length} transaction{group.items.length === 1 ? "" : "s"} {formatCurrency(group.total, group.currency)} {group.items.map((item) => ( ))} ))} ); } export { groupByMonth };