enabled latest items

This commit is contained in:
2026-05-07 17:29:09 +05:30
parent f213a9455b
commit 052c5a3026
2 changed files with 146 additions and 52 deletions

View File

@@ -1,3 +1,5 @@
// components/LatestItems/LatestItems.tsx
import * as React from "react"; import * as React from "react";
import { import {
List, List,
@@ -7,64 +9,161 @@ import {
Avatar, Avatar,
Typography, Typography,
Box, Box,
Button, IconButton
} from "@mui/material"; } from "@mui/material";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
export interface LatestItem { import { ReportData, Transaction, ReportPeriod } from "../../features/report";
id: string | number; import { formatCurrency } from "../ProgressCard/ProgressCard.utils";
icon: React.ReactNode;
iconBgColor?: string; type Props = {
title: string; reportData: ReportData;
subtitle: string; mode: "expense" | "income";
amount: string; selectedPeriodId: string | null;
timeAgo: string; accentColor: string;
};
type DecoratedPeriod = ReportPeriod & {
id: string;
label: string;
};
function mergePeriods(
reportData: ReportData,
key: "weekly" | "monthly" | "yearly" | "fyly" | "full"
): DecoratedPeriod[] {
const map = new Map<string, DecoratedPeriod>();
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 { function extractTransactions(
title?: string; reportData: ReportData,
items: LatestItem[]; selectedPeriodId: string | null,
onViewAll?: () => void; mode: "expense" | "income",
accentColor: any; ): 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({ export default function LatestItems({
title = "Recent Transactions", reportData,
items, mode,
onViewAll, selectedPeriodId,
accentColor, accentColor
}: LatestItemsListProps) { }: 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 ( return (
<Box sx={{ width: "100%", bgcolor: "background.paper", borderRadius: 4, p: 2 }}> <Box sx={{ width: "100%", bgcolor: "background.paper", borderRadius: 4, p: 2 }}>
{/* Header */}
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", mb: 2, px: 2 }}> <Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", mb: 2, px: 2 }}>
<Typography variant="h6" fontWeight="bold"> <Typography variant="h6" fontWeight="bold">
{title} Recent Transactions
</Typography> </Typography>
{onViewAll && (
<Button {isPeriodSelected && items.length > 5 && (
variant="text" <IconButton size="small" onClick={() => setExpanded((p) => !p)}>
color="inherit" {expanded ? <ExpandLessIcon /> : <ExpandMoreIcon />}
size="small" </IconButton>
sx={{ textTransform: "none", color: "text.secondary", fontWeight: "medium" }}
onClick={onViewAll}
>
view all
</Button>
)} )}
</Box> </Box>
{/* List */}
<List disablePadding> <List disablePadding>
{items.map((item, index) => ( {visibleItems.map((item, index) => (
<ListItem <ListItem
key={item.id} key={item.id}
sx={{ sx={{
px: { xs: 1, sm: 2 }, px: { xs: 1, sm: 2 },
py: 2, py: 2,
mb: index !== items.length - 1 ? 1 : 0, mb: index !== visibleItems.length - 1 ? 1 : 0,
borderRadius: 3, borderRadius: 3,
"&:hover": { bgcolor: "action.hover" }, "&:hover": { bgcolor: "action.hover" },
transition: "background-color 0.2s ease",
}} }}
> >
<ListItemAvatar> <ListItemAvatar>
@@ -72,20 +171,17 @@ export default function LatestItems({
variant="rounded" variant="rounded"
sx={{ sx={{
bgcolor: `${accentColor}22`, bgcolor: `${accentColor}22`,
color: "inherit",
width: 48, width: 48,
height: 48, height: 48,
borderRadius: 3, borderRadius: 3,
mr: 2, mr: 2,
}} }}
> />
{item.icon}
</Avatar>
</ListItemAvatar> </ListItemAvatar>
<ListItemText <ListItemText
primary={ primary={
<Typography variant="subtitle1" fontWeight={600} color="text.primary"> <Typography variant="subtitle1" fontWeight={600}>
{item.title} {item.title}
</Typography> </Typography>
} }
@@ -97,10 +193,10 @@ export default function LatestItems({
/> />
<Box sx={{ textAlign: "right" }}> <Box sx={{ textAlign: "right" }}>
<Typography variant="subtitle1" fontWeight={700} color="text.primary"> <Typography variant="subtitle1" fontWeight={700}>
{item.amount} {item.amount}
</Typography> </Typography>
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', mt: 0.5 }}> <Typography variant="caption" color="text.secondary">
{item.timeAgo} {item.timeAgo}
</Typography> </Typography>
</Box> </Box>

View File

@@ -29,15 +29,13 @@ export const configuration: DashboardConfig = {
size: 12, size: 12,
}, },
}, },
// { {
// id: "latest", id: "items",
// title: 'Recent Transactions', component: LatestItems,
// component: LatestItems, style: {
// dataKey: "latest", size: 12,
// style: { },
// size: 12, },
// },
// },
], ],
style: { style: {
palette: { palette: {