major refactor of the dashboard and react-openapi integration #1
@@ -1,3 +1,13 @@
|
||||
export {
|
||||
useReport
|
||||
} from './useReport'
|
||||
export type {
|
||||
Transaction,
|
||||
PeriodData,
|
||||
PeriodGroup,
|
||||
Period,
|
||||
ReportData,
|
||||
} from './report.models'
|
||||
export {
|
||||
prepareReport
|
||||
} from './report.utils'
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
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 getWeekOfMonth = (date: Date) => {
|
||||
const firstDay = new Date(date.getFullYear(), date.getMonth(), 1);
|
||||
return Math.ceil((date.getDate() + firstDay.getDay()) / 7);
|
||||
};
|
||||
|
||||
const findCompareBucket = (
|
||||
current: ReportBucket,
|
||||
buckets: ReportBucket[],
|
||||
type: "weekly" | "monthly"
|
||||
): ReportBucket | undefined => {
|
||||
const start = new Date(current.start);
|
||||
|
||||
if (type === "monthly") {
|
||||
const targetYear = start.getFullYear() - 1;
|
||||
const targetMonth = start.getMonth();
|
||||
|
||||
return buckets.find(b => {
|
||||
const d = new Date(b.start);
|
||||
return (
|
||||
d.getFullYear() === targetYear &&
|
||||
d.getMonth() === targetMonth
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (type === "weekly") {
|
||||
const weekIndex = getWeekOfMonth(start); // you must define this
|
||||
const target = new Date(start);
|
||||
target.setMonth(target.getMonth() - 1);
|
||||
|
||||
return buckets.find(b => {
|
||||
const d = new Date(b.start);
|
||||
return (
|
||||
d.getFullYear() === target.getFullYear() &&
|
||||
d.getMonth() === target.getMonth() &&
|
||||
getWeekOfMonth(d) === weekIndex
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const toPoints = (
|
||||
buckets: ReportBucket[],
|
||||
type: "weekly" | "monthly",
|
||||
flow: "expenses" | "incomes"
|
||||
): ChartDataPoint[] => {
|
||||
return buckets.map((b) => {
|
||||
const amount = sumBucket(b, flow);
|
||||
const prev = findCompareBucket(b, buckets, type);
|
||||
|
||||
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[],
|
||||
payeeBuckets: 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> = {};
|
||||
|
||||
const sourceForPayees = (payeeBuckets && payeeBuckets.length > 0) ? payeeBuckets : weekly;
|
||||
|
||||
for (const b of sourceForPayees) {
|
||||
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)
|
||||
// .filter(([name]) => name !== "Unknown")
|
||||
.map(([payeeName, amount]) => ({ payeeName, amount }))
|
||||
.sort((a, b) => b.amount - a.amount)
|
||||
.slice(0, 5);
|
||||
|
||||
return {
|
||||
chartData,
|
||||
totalAmount,
|
||||
topPayees,
|
||||
};
|
||||
}
|
||||
@@ -1,14 +1,18 @@
|
||||
import { useResourceByName } from "../../../react-openapi";
|
||||
|
||||
export interface ReportParams {
|
||||
period: "weekly" | "monthly" | "yearly" | "fyly";
|
||||
period: "weekly" | "monthly" | "yearly" | "fyly" | "full";
|
||||
rolling?: boolean;
|
||||
report_date?: string;
|
||||
group_by?: ("flow" | "payee" | "tags")[];
|
||||
group_by?: ("payee" | "tags")[];
|
||||
ignore_self?: boolean;
|
||||
}
|
||||
|
||||
export function useReport(params: ReportParams) {
|
||||
const { useList } = useResourceByName("reports");
|
||||
if (params.group_by) {
|
||||
// @ts-ignore
|
||||
params.group_by = params.group_by[0]
|
||||
}
|
||||
return useList(params);
|
||||
}
|
||||
Reference in New Issue
Block a user