major refactor of the dashboard and react-openapi integration #1
61
src/features/report/report.models.ts
Normal file
61
src/features/report/report.models.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
export interface Payor {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface Payee {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface Account {
|
||||
name: string;
|
||||
number: string;
|
||||
}
|
||||
|
||||
export interface Tag {
|
||||
name: string;
|
||||
icon: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface Transaction {
|
||||
payor: Payor;
|
||||
payee: Payee;
|
||||
amount: number;
|
||||
account: Account;
|
||||
tags: Tag[]
|
||||
occurred_at: Date
|
||||
}
|
||||
|
||||
export interface _PeriodData {
|
||||
sum: number;
|
||||
count: number;
|
||||
average: number;
|
||||
txns: Transaction[];
|
||||
}
|
||||
|
||||
export interface PeriodData extends _PeriodData {
|
||||
compare?: _PeriodData;
|
||||
}
|
||||
|
||||
export interface PeriodGroup {
|
||||
group_key: string[];
|
||||
expenses: PeriodData[];
|
||||
incomes: PeriodData[];
|
||||
}
|
||||
|
||||
export interface Period {
|
||||
id: string;
|
||||
label: string;
|
||||
start: Date;
|
||||
end: Date;
|
||||
groups: PeriodGroup[];
|
||||
}
|
||||
|
||||
export interface ReportData {
|
||||
period: "weekly" | "monthly" | "yearly" | "fyly" | "full";
|
||||
rolling?: boolean;
|
||||
report_date?: string;
|
||||
group_by?: ("payee" | "tags")[];
|
||||
ignore_self?: boolean;
|
||||
buckets: Period[];
|
||||
}
|
||||
116
src/features/report/report.utils.ts
Normal file
116
src/features/report/report.utils.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import { ReportData } from "./report.models";
|
||||
|
||||
/* ---------- ID BUILDING ---------- */
|
||||
|
||||
function formatDate(d: Date): string {
|
||||
const y = d.getUTCFullYear();
|
||||
const m = String(d.getUTCMonth() + 1).padStart(2, "0");
|
||||
const day = String(d.getUTCDate()).padStart(2, "0");
|
||||
return `${y}-${m}-${day}`;
|
||||
}
|
||||
|
||||
function buildPeriodId(
|
||||
type: ReportData["period"],
|
||||
start: Date,
|
||||
end: Date
|
||||
): string {
|
||||
const s = formatDate(start);
|
||||
const e = formatDate(end);
|
||||
|
||||
switch (type) {
|
||||
case "weekly":
|
||||
return `W:${s}_${e}`;
|
||||
case "monthly":
|
||||
return `M:${s}_${e}`;
|
||||
case "yearly":
|
||||
return `Y:${s}_${e}`;
|
||||
case "fyly":
|
||||
return `FY:${s}_${e}`;
|
||||
case "full":
|
||||
return `FULL:${s}_${e}`;
|
||||
default:
|
||||
return `${s}_${e}`;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- LABEL BUILDING ---------- */
|
||||
|
||||
const dayFmt = new Intl.DateTimeFormat("en-GB", {
|
||||
day: "numeric",
|
||||
month: "short",
|
||||
timeZone: "UTC",
|
||||
});
|
||||
|
||||
const monthDayFmt = new Intl.DateTimeFormat("en-GB", {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
timeZone: "UTC",
|
||||
});
|
||||
|
||||
const monthFmt = new Intl.DateTimeFormat("en-GB", {
|
||||
month: "short",
|
||||
timeZone: "UTC",
|
||||
});
|
||||
|
||||
const yearFmt = new Intl.DateTimeFormat("en-GB", {
|
||||
year: "numeric",
|
||||
timeZone: "UTC",
|
||||
});
|
||||
|
||||
function sameMonth(a: Date, b: Date) {
|
||||
return (
|
||||
a.getUTCFullYear() === b.getUTCFullYear() &&
|
||||
a.getUTCMonth() === b.getUTCMonth()
|
||||
);
|
||||
}
|
||||
|
||||
function buildLabel(
|
||||
type: ReportData["period"],
|
||||
start: Date,
|
||||
end: Date
|
||||
): string {
|
||||
switch (type) {
|
||||
case "weekly":
|
||||
if (sameMonth(start, end)) {
|
||||
const sDay = start.getUTCDate();
|
||||
const eDay = end.getUTCDate();
|
||||
const m = monthFmt.format(start);
|
||||
return `${sDay} ${m} - ${eDay} ${m}`;
|
||||
}
|
||||
return `${dayFmt.format(start)} - ${dayFmt.format(end)}`;
|
||||
|
||||
case "monthly":
|
||||
if (sameMonth(start, end)) {
|
||||
return `${monthFmt.format(start)} ${yearFmt.format(start)}`;
|
||||
}
|
||||
return `${monthDayFmt.format(start)} - ${monthDayFmt.format(end)}`;
|
||||
|
||||
case "yearly":
|
||||
return yearFmt.format(start);
|
||||
|
||||
case "fyly": {
|
||||
const startY = start.getUTCFullYear();
|
||||
const endY = end.getUTCFullYear();
|
||||
return `FY ${startY}–${String(endY).slice(-2)}`;
|
||||
}
|
||||
|
||||
case "full":
|
||||
return `${monthFmt.format(start)} ${yearFmt.format(start)} - ${monthFmt.format(end)} ${yearFmt.format(end)}`;
|
||||
|
||||
default:
|
||||
return `${monthDayFmt.format(start)} - ${monthDayFmt.format(end)}`;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- MAIN ---------- */
|
||||
|
||||
export function prepareReport(reportData: ReportData): ReportData {
|
||||
return {
|
||||
...reportData,
|
||||
buckets: reportData.buckets.map((p) => ({
|
||||
...p,
|
||||
id: buildPeriodId(reportData.period, p.start, p.end),
|
||||
label: buildLabel(reportData.period, p.start, p.end),
|
||||
})),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user