Compare commits

...

5 Commits

Author SHA1 Message Date
089381125f TopTags 2026-05-07 15:24:17 +05:30
1a5261ab25 removed dataKey 2026-05-07 15:23:58 +05:30
55b604f700 all periods 2026-05-07 15:23:50 +05:30
04b72d5843 correct comparison 2026-05-07 15:12:27 +05:30
b97e350b1b fixed height of bar 2026-05-07 15:09:04 +05:30
6 changed files with 141 additions and 23 deletions

View File

@@ -19,7 +19,6 @@ export interface DashboardSection {
title?: string; title?: string;
summary?: string; summary?: string;
component: React.ComponentType<any>; component: React.ComponentType<any>;
dataKey: string;
settings?: Record<string, any>; settings?: Record<string, any>;
isList?: boolean; isList?: boolean;
style?: { style?: {

View File

@@ -8,11 +8,12 @@ type DecoratedPeriod = ReportPeriod & {
label: string; label: string;
}; };
const TAB_TO_KEY: Record<string, "weekly" | "monthly" | "yearly" | "fyly"> = { const TAB_TO_KEY: Record<string, "weekly" | "monthly" | "yearly" | "fyly" | "full"> = {
Weekly: "weekly", Weekly: "weekly",
Monthly: "monthly", Monthly: "monthly",
Yearly: "yearly", Yearly: "yearly",
FYLY: "fyly" 'Financial Year': "fyly",
'All Time': "full"
}; };
function getAmount(p: ReportPeriod, mode: "expense" | "income") { function getAmount(p: ReportPeriod, mode: "expense" | "income") {
@@ -39,7 +40,7 @@ function mergeMetric(a: any, b: any) {
function mergeBuckets( function mergeBuckets(
buckets: any[], buckets: any[],
key: "weekly" | "monthly" | "yearly" | "fyly" key: "weekly" | "monthly" | "yearly" | "fyly" | "full"
): DecoratedPeriod[] { ): DecoratedPeriod[] {
const map = new Map<string, DecoratedPeriod>(); const map = new Map<string, DecoratedPeriod>();
@@ -70,9 +71,38 @@ function mergeBuckets(
); );
} }
function attachComparison(
points: ChartDataPoint[],
key: "weekly" | "monthly" | "yearly" | "fyly" | "full"
): ChartDataPoint[] {
const getCompareIndex = (i: number) => {
if (key === "weekly") return i - 4;
if (key === "monthly") return i - 12;
if (key === "yearly") return i - 1;
if (key === "fyly") return i - 1;
return -1;
};
return points.map((p, i) => {
const ci = getCompareIndex(i);
return {
...p,
compare:
ci >= 0 && points[ci]
? {
id: points[ci].id,
label: points[ci].label,
amount: points[ci].amount
}
: undefined
};
});
}
function buildChartData( function buildChartData(
reportData: HistoryChartProps["reportData"], reportData: HistoryChartProps["reportData"],
key: "weekly" | "monthly" | "yearly" | "fyly", key: "weekly" | "monthly" | "yearly" | "fyly" | "full",
mode: "expense" | "income", mode: "expense" | "income",
comparison: boolean comparison: boolean
): ChartDataPoint[] { ): ChartDataPoint[] {
@@ -86,17 +116,7 @@ function buildChartData(
})); }));
if (comparison) { if (comparison) {
points = points.map((p, i) => ({ points = attachComparison(points, key);
...p,
compare:
i > 0
? {
id: points[i - 1].id,
label: points[i - 1].label,
amount: points[i - 1].amount
}
: undefined
}));
} }
return points; return points;
@@ -138,7 +158,8 @@ export default function HistoryChart(props: HistoryChartProps) {
weekly: 6, weekly: 6,
monthly: 4, monthly: 4,
yearly: 4, yearly: 4,
fyly: 4 fyly: 4,
full: 4,
}; };
const visibleCount = visibleCountMap[activeDataKey] ?? 4; const visibleCount = visibleCountMap[activeDataKey] ?? 4;

View File

@@ -148,7 +148,8 @@ export default function HistoryChartView(props: ViewProps) {
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
alignItems: "center", alignItems: "center",
cursor: "pointer" cursor: "pointer",
height: "100%"
}} }}
> >
<Box sx={{ display: "flex", alignItems: "flex-end", gap: 1, height: "100%" }}> <Box sx={{ display: "flex", alignItems: "flex-end", gap: 1, height: "100%" }}>

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,

View File

@@ -67,6 +67,7 @@ export interface ReportBucket {
monthly?: ReportPeriod[]; monthly?: ReportPeriod[];
yearly?: ReportPeriod[]; yearly?: ReportPeriod[];
fyly?: ReportPeriod[]; fyly?: ReportPeriod[];
full?: ReportPeriod[];
}; };
} }
@@ -75,7 +76,7 @@ export interface ReportBucket {
// ----------------------------- // -----------------------------
export interface ReportData { export interface ReportData {
periods: ("weekly" | "monthly" | "yearly" | "fyly")[]; periods: ("weekly" | "monthly" | "yearly" | "fyly" | "full")[];
rolling: boolean; rolling: boolean;
report_date?: string; report_date?: string;