removed client data massaging with backend report using feature/report
This commit is contained in:
9
src/features/report/report.api.ts
Normal file
9
src/features/report/report.api.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { api } from "../../../react-openapi";
|
||||
|
||||
export async function fetchReport(params: {
|
||||
period: "weekly" | "monthly" | "yearly" | "fyly";
|
||||
rolling?: boolean;
|
||||
}) {
|
||||
const res = await api.get("/reports", { params });
|
||||
return res.data;
|
||||
}
|
||||
96
src/features/report/report.mapper.ts
Normal file
96
src/features/report/report.mapper.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import {
|
||||
AggregatedDashboardData,
|
||||
ChartData,
|
||||
ChartDataPoint,
|
||||
} from "../../components/HistoryChart";
|
||||
|
||||
type ReportBucket = any;
|
||||
|
||||
const sumBucket = (bucket: ReportBucket, flow: "expenses" | "incomes") =>
|
||||
bucket.groups.reduce(
|
||||
(acc: number, g: any) => acc + (g?.[flow]?.sum || 0),
|
||||
0
|
||||
);
|
||||
|
||||
const toLabel = (start: string, end: string, type: "weekly" | "monthly") => {
|
||||
const s = new Date(start);
|
||||
const e = new Date(end);
|
||||
|
||||
if (type === "monthly") {
|
||||
return s.toLocaleString("default", { month: "short" });
|
||||
}
|
||||
|
||||
return `${s.getDate()}–${e.getDate()} ${e.toLocaleString("default", {
|
||||
month: "short",
|
||||
})}`;
|
||||
};
|
||||
|
||||
const toPoints = (
|
||||
buckets: ReportBucket[],
|
||||
type: "weekly" | "monthly",
|
||||
flow: "expenses" | "incomes"
|
||||
): ChartDataPoint[] => {
|
||||
return buckets.map((b, i) => {
|
||||
const amount = sumBucket(b, flow);
|
||||
const prev = buckets[i - 1];
|
||||
|
||||
return {
|
||||
id: toLabel(b.start, b.end, type),
|
||||
amount,
|
||||
compare: prev
|
||||
? {
|
||||
id: toLabel(prev.start, prev.end, type),
|
||||
amount: sumBucket(prev, flow),
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export function mapReportToDashboard(
|
||||
weekly: ReportBucket[],
|
||||
monthly: ReportBucket[],
|
||||
type: "expense" | "income"
|
||||
): AggregatedDashboardData {
|
||||
const flow = type === "expense" ? "expenses" : "incomes";
|
||||
|
||||
const chartData: ChartData = {
|
||||
daily: [],
|
||||
|
||||
weekly: {
|
||||
rolling: toPoints(weekly, "weekly", flow),
|
||||
calendar: toPoints(weekly, "weekly", flow),
|
||||
},
|
||||
|
||||
monthly: {
|
||||
rolling: toPoints(monthly, "monthly", flow),
|
||||
calendar: toPoints(monthly, "monthly", flow),
|
||||
},
|
||||
};
|
||||
|
||||
const totalAmount = weekly.reduce(
|
||||
(acc, b) => acc + sumBucket(b, flow),
|
||||
0
|
||||
);
|
||||
|
||||
const payeeMap: Record<string, number> = {};
|
||||
|
||||
for (const b of weekly) {
|
||||
for (const g of b.groups) {
|
||||
const key = g.group_key || "Unknown";
|
||||
const amt = g?.[flow]?.sum || 0;
|
||||
payeeMap[key] = (payeeMap[key] || 0) + amt;
|
||||
}
|
||||
}
|
||||
|
||||
const topPayees = Object.entries(payeeMap)
|
||||
.map(([payeeName, amount]) => ({ payeeName, amount }))
|
||||
.sort((a, b) => b.amount - a.amount)
|
||||
.slice(0, 5);
|
||||
|
||||
return {
|
||||
chartData,
|
||||
totalAmount,
|
||||
topPayees,
|
||||
};
|
||||
}
|
||||
114
src/features/report/report.service.ts
Normal file
114
src/features/report/report.service.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { fetchReport } from "./report.api";
|
||||
import {
|
||||
AggregatedDashboardData,
|
||||
ChartData,
|
||||
ChartDataPoint,
|
||||
} from "../components/HistoryChart";
|
||||
|
||||
type ReportBucket = any; // replace with generated type if available
|
||||
|
||||
function sumBucket(bucket: ReportBucket, flow: "expenses" | "incomes") {
|
||||
return bucket.groups.reduce(
|
||||
(acc: number, g: any) => acc + (g?.[flow]?.sum || 0),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
function toLabel(start: string, end: string, type: "weekly" | "monthly") {
|
||||
const s = new Date(start);
|
||||
const e = new Date(end);
|
||||
|
||||
if (type === "monthly") {
|
||||
return s.toLocaleString("default", { month: "short" });
|
||||
}
|
||||
|
||||
const sd = s.getDate();
|
||||
const ed = e.getDate();
|
||||
const m = e.toLocaleString("default", { month: "short" });
|
||||
return `${sd}–${ed} ${m}`;
|
||||
}
|
||||
|
||||
function toChartPoints(
|
||||
buckets: ReportBucket[],
|
||||
type: "weekly" | "monthly",
|
||||
flow: "expenses" | "incomes"
|
||||
): ChartDataPoint[] {
|
||||
return buckets.map((b, i) => {
|
||||
const amount = sumBucket(b, flow);
|
||||
|
||||
const prev = buckets[i - 1];
|
||||
const compareAmount = prev ? sumBucket(prev, flow) : 0;
|
||||
|
||||
return {
|
||||
id: toLabel(b.start, b.end, type),
|
||||
amount,
|
||||
compare: prev
|
||||
? {
|
||||
id: toLabel(prev.start, prev.end, type),
|
||||
amount: compareAmount,
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function buildChartData(
|
||||
weekly: ReportBucket[],
|
||||
monthly: ReportBucket[],
|
||||
flow: "expenses" | "incomes"
|
||||
): ChartData {
|
||||
return {
|
||||
daily: [], // not supported by /reports → keep empty or drop
|
||||
weekly: {
|
||||
rolling: toChartPoints(weekly, "weekly", flow),
|
||||
calendar: toChartPoints(weekly, "weekly", flow), // same unless backend differentiates
|
||||
},
|
||||
monthly: {
|
||||
rolling: toChartPoints(monthly, "monthly", flow),
|
||||
calendar: toChartPoints(monthly, "monthly", flow),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function getTopPayees(buckets: ReportBucket[], flow: "expenses" | "incomes") {
|
||||
const map: Record<string, number> = {};
|
||||
|
||||
for (const b of buckets) {
|
||||
for (const g of b.groups) {
|
||||
const key = g.group_key || "Unknown";
|
||||
const amt = g?.[flow]?.sum || 0;
|
||||
map[key] = (map[key] || 0) + amt;
|
||||
}
|
||||
}
|
||||
|
||||
return Object.entries(map)
|
||||
.map(([payeeName, amount]) => ({ payeeName, amount }))
|
||||
.sort((a, b) => b.amount - a.amount)
|
||||
.slice(0, 5);
|
||||
}
|
||||
|
||||
export async function getDashboardData(
|
||||
type: "expense" | "income"
|
||||
): Promise<AggregatedDashboardData> {
|
||||
const flow = type === "expense" ? "expenses" : "incomes";
|
||||
|
||||
const [weeklyBuckets, monthlyBuckets] = await Promise.all([
|
||||
fetchReport({ period: "weekly", rolling: true }),
|
||||
fetchReport({ period: "monthly", rolling: true }),
|
||||
]);
|
||||
|
||||
const chartData = buildChartData(weeklyBuckets, monthlyBuckets, flow);
|
||||
|
||||
const totalAmount = weeklyBuckets.reduce(
|
||||
(acc: number, b: any) => acc + sumBucket(b, flow),
|
||||
0
|
||||
);
|
||||
|
||||
const topPayees = getTopPayees(weeklyBuckets, flow);
|
||||
|
||||
return {
|
||||
chartData,
|
||||
totalAmount,
|
||||
topPayees,
|
||||
};
|
||||
}
|
||||
17
src/features/report/useReport.ts
Normal file
17
src/features/report/useReport.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { fetchReport } from "./report.api";
|
||||
|
||||
export interface ReportParams {
|
||||
period: "weekly" | "monthly" | "yearly" | "fyly";
|
||||
rolling?: boolean;
|
||||
report_date?: string;
|
||||
group_by?: ("flow" | "payee" | "tags")[];
|
||||
ignore_self?: boolean;
|
||||
}
|
||||
|
||||
export function useReport(params: ReportParams) {
|
||||
return useQuery({
|
||||
queryKey: ["report", params],
|
||||
queryFn: () => fetchReport(params),
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user