major refactor of the dashboard and react-openapi integration #1

Merged
aetos merged 44 commits from period-selection into main 2026-05-07 11:00:54 +00:00
2 changed files with 100 additions and 4 deletions
Showing only changes of commit 089381125f - Show all commits

View File

@@ -0,0 +1,98 @@
import * as React from "react";
import { Box } from "@mui/material";
import { ReportData, ReportPeriod } from "../../features/report";
import ProgressCard from "./ProgressCard";
type Props = {
reportData: ReportData;
mode: "expense" | "income";
selectedPeriodId?: string | null;
compact?: boolean;
};
type DecoratedPeriod = ReportPeriod & {
id: string;
label: string;
};
function getAmount(p: ReportPeriod, mode: "expense" | "income") {
return mode === "expense" ? p.expenses.sum : p.incomes.sum;
}
function findPeriod(
periods: DecoratedPeriod[],
selectedPeriodId?: string | null
) {
if (!periods.length) return null;
if (selectedPeriodId) {
const match = periods.find((p) => p.id === selectedPeriodId);
if (match) return match;
}
// fallback → latest
return periods.reduce((latest, p) =>
new Date(p.start).getTime() > new Date(latest.start).getTime()
? p
: latest
);
}
export default function TopTags({
reportData,
mode,
selectedPeriodId,
compact = true
}: Props) {
const { items, total } = React.useMemo(() => {
const tagMap = new Map<string, number>();
for (const bucket of reportData.buckets) {
const tags = bucket.group_key.tags;
if (!tags || tags.length === 0) continue;
// Prefer FULL if available
const fullPeriods = (bucket.periods.full || []) as DecoratedPeriod[];
const periodsToUse =
selectedPeriodId
? Object.values(bucket.periods).flat() as DecoratedPeriod[]
: fullPeriods;
const period = findPeriod(periodsToUse, selectedPeriodId);
if (!period) continue;
const amount = getAmount(period, mode);
for (const tag of tags) {
tagMap.set(tag, (tagMap.get(tag) || 0) + 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, 5);
const total = top.reduce((sum, t) => sum + t.amount, 0);
return { items: top, total };
}, [reportData, mode, selectedPeriodId]);
return (
<Box sx={{ display: "grid", gap: 2 }}>
{items.map((item) => (
<ProgressCard
key={item.tag}
header={item.tag}
progressAmount={item.amount}
totalAmount={total}
compact={compact}
/>
))}
</Box>
);
}

View File

@@ -1,7 +1,7 @@
import HistoryChart from "./components/HistoryChart"; import HistoryChart from "./components/HistoryChart";
import ProgressCard from "./components/ProgressCard";
import LatestItems from "./components/LatestItems"; import LatestItems from "./components/LatestItems";
import { DashboardConfig } from "./components/Dashboard"; import { DashboardConfig } from "./components/Dashboard";
import TopTags from "./components/ProgressCard/TopTags";
export const configuration: DashboardConfig = { export const configuration: DashboardConfig = {
sections: [ sections: [
@@ -10,7 +10,6 @@ export const configuration: DashboardConfig = {
title: "Breakdown", title: "Breakdown",
summary: "Interactive chronological tracking", summary: "Interactive chronological tracking",
component: HistoryChart, component: HistoryChart,
dataKey: "chartData",
settings: { settings: {
tabs: ["Weekly", "Monthly"], tabs: ["Weekly", "Monthly"],
// tabs: ["Weekly", "Monthly", "Yearly", "Financial Year", "All Time"], // tabs: ["Weekly", "Monthly", "Yearly", "Financial Year", "All Time"],
@@ -22,8 +21,7 @@ export const configuration: DashboardConfig = {
{ {
id: "top-payees", id: "top-payees",
title: 'Top Payees', title: 'Top Payees',
component: ProgressCard, component: TopTags,
dataKey: "topPayees",
isList: true, isList: true,
settings: { settings: {
compact: true, compact: true,