ui fixes for snapshot report

This commit is contained in:
2026-05-17 19:14:45 +05:30
parent ad62d7dd9c
commit 13f091a82c
12 changed files with 110 additions and 123 deletions

View File

@@ -50,4 +50,5 @@ export interface DashboardConfig {
export interface DashboardProps {
config: DashboardConfig;
data: ReportData;
onModeChange?: (state: DashboardState) => void;
}

View File

@@ -12,10 +12,14 @@ export default function Dashboard(props: DashboardProps) {
});
const toggleMode = () => {
setState(prev => ({
...prev,
mode: prev.mode === "expense" ? "income" : "expense",
}));
setState(prev => {
const next = {
...prev,
mode: prev.mode === "expense" ? "income" as const : "expense" as const,
};
props.onModeChange?.(next);
return next;
});
};
const togglePeriodType = () => {

View File

@@ -9,15 +9,14 @@ import { ChartDataPoint } from "./HistoryChart.models";
// ─── Tab → PeriodKey ─────────────────────────────────────────
const TAB_TO_KEY: Record<string, PeriodKey> = {
Daily: "daily",
Weekly: "weekly",
Monthly: "monthly",
Yearly: "yearly",
"Financial Year": "fyly",
"All Time": "full",
"All Time": "all",
};
export function tabToKey(tab: string): PeriodKey {
return TAB_TO_KEY[tab] ?? "full";
return TAB_TO_KEY[tab] ?? "all";
}
// ─── Comparison ──────────────────────────────────────────────
@@ -27,10 +26,9 @@ function attachComparison(
key: PeriodKey
): ChartDataPoint[] {
const getCompareIndex = (i: number) => {
if (key === "daily") return i - 7;
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;
};
@@ -64,7 +62,7 @@ export function buildChartData(
let points: ChartDataPoint[] = merged.map((p) => ({
id: p.id,
label: p.label,
amount: getAmount(p, mode),
amount: getAmount(p),
}));
if (comparison) {

View File

@@ -35,11 +35,10 @@ export default function HistoryChart(props: HistoryChartProps) {
: 1;
const visibleCountMap = {
daily: 7,
weekly: 6,
monthly: 4,
yearly: 4,
fyly: 4,
full: 4,
all: 4,
};
const visibleCount = visibleCountMap[activeDataKey] ?? 4;

View File

@@ -13,7 +13,6 @@ function extractTransactions(
reportData: ReportData,
selectedPeriodId: string | null,
selectedGroupKey: GroupKey | null,
mode: "expense" | "income"
): Transaction[] {
const buckets = filterBuckets(reportData.buckets, selectedGroupKey);
if (selectedPeriodId) {
@@ -23,20 +22,16 @@ function extractTransactions(
if (!selected) return [];
return mode === "expense"
? (selected.expenses.transactions || [])
: (selected.incomes.transactions || []);
return selected.metric.transactions || [];
}
const periods = mergeBucketPeriods(buckets, "full");
const periods = mergeBucketPeriods(buckets, "all");
if (!periods.length) return [];
const full = periods[0];
return mode === "expense"
? (full.expenses.transactions || [])
: (full.incomes.transactions || []);
return full.metric.transactions || [];
}
// ─── Main adapter ────────────────────────────────────────────
@@ -47,10 +42,9 @@ export function buildLatestItems(
selectedGroupKey: GroupKey | null,
mode: "expense" | "income"
): LatestItem[] {
const txns = extractTransactions(reportData, selectedPeriodId, selectedGroupKey, mode);
const txns = extractTransactions(reportData, selectedPeriodId, selectedGroupKey);
return txns
.filter((t) => (mode === "expense" ? t.amount < 0 : t.amount >= 0))
.sort(
(a, b) =>
new Date(b.occurred_at).getTime() -

View File

@@ -43,17 +43,17 @@ export function extractTopTags(
const tags = bucket.group_key.tags;
if (!tags || tags.length === 0) continue;
// Prefer FULL if available
const fullPeriods = (bucket.periods.full || []) as DecoratedPeriod[];
// Prefer ALL if available
const allPeriods = (bucket.periods.all || []) as DecoratedPeriod[];
const periodsToUse = selectedPeriodId
? (Object.values(bucket.periods).flat() as DecoratedPeriod[])
: fullPeriods;
: allPeriods;
const period = findPeriod(periodsToUse, selectedPeriodId);
if (!period) continue;
const amount = getAmount(period, mode);
const amount = getAmount(period);
for (const tag of tags) {
tagMap.set(tag, (tagMap.get(tag) || 0) + amount);

View File

@@ -2,11 +2,12 @@ import {
ReportPeriod,
ReportBucket,
GroupKey,
PeriodType,
} from "../features/report";
// ─── Types ────────────────────────────────────────────────────
export type PeriodKey = "weekly" | "monthly" | "yearly" | "fyly" | "full";
export type PeriodKey = PeriodType;
export type DecoratedPeriod = ReportPeriod & {
id: string;
@@ -16,11 +17,10 @@ export type DecoratedPeriod = ReportPeriod & {
// ─── Period helpers ───────────────────────────────────────────
const PREFIX_TO_KEY: Record<string, PeriodKey> = {
D: "daily",
W: "weekly",
M: "monthly",
Y: "yearly",
FY: "fyly",
FULL: "full",
ALL: "all",
};
/**
@@ -29,19 +29,16 @@ const PREFIX_TO_KEY: Record<string, PeriodKey> = {
*/
export function periodIdToKey(periodId: string): PeriodKey {
const prefix = periodId.split(":")[0];
return PREFIX_TO_KEY[prefix] ?? "full";
return PREFIX_TO_KEY[prefix] ?? "all";
}
// ─── Metric helpers ───────────────────────────────────────────
export function getAmount(
period: ReportPeriod,
mode: "expense" | "income"
): number {
return mode === "expense" ? period.expenses.sum : period.incomes.sum;
export function getAmount(period: ReportPeriod): number {
return period.metric.sum;
}
function mergeMetric(a: ReportPeriod["expenses"], b: ReportPeriod["expenses"]) {
function mergeMetric(a: ReportPeriod["metric"], b: ReportPeriod["metric"]) {
const sum = a.sum + b.sum;
const count = a.count + b.count;
@@ -78,14 +75,12 @@ export function mergeBucketPeriods(
if (!existing) {
map.set(p.id, {
...p,
expenses: { ...p.expenses },
incomes: { ...p.incomes },
metric: { ...p.metric },
});
} else {
map.set(p.id, {
...existing,
expenses: mergeMetric(existing.expenses, p.expenses),
incomes: mergeMetric(existing.incomes, p.incomes),
metric: mergeMetric(existing.metric, p.metric),
});
}
}
@@ -126,7 +121,7 @@ export function matchesGroupKey(
selected: GroupKey
): boolean {
for (const [dim, values] of Object.entries(selected)) {
const bucketValues = bucket.group_key[dim as keyof GroupKey];
const bucketValues = bucket.group_key[dim];
if (!bucketValues) return false;
if (!(values as string[]).every((v) => bucketValues.includes(v)))
return false;