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),
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,17 +1,10 @@
|
|||||||
import { api } from "../../react-openapi";
|
import { api } from "../../react-openapi";
|
||||||
import { LatestItem } from "../components/LatestItemsList";
|
import { LatestItem } from "../components/LatestItems";
|
||||||
import { ChartDataPoint } from "../types/historyChart";
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { format } from "./dateUtils";
|
|
||||||
import MonetizationOnIcon from "@mui/icons-material/MonetizationOn";
|
import MonetizationOnIcon from "@mui/icons-material/MonetizationOn";
|
||||||
|
|
||||||
import {
|
import { fetchReport } from "../features/report/report.api";
|
||||||
buildDailyBuckets,
|
import { mapReportToDashboard } from "../features/report/report.mapper";
|
||||||
buildWeeklyRolling,
|
|
||||||
buildWeeklyCalendar,
|
|
||||||
buildMonthlyRolling,
|
|
||||||
buildMonthlyCalendar
|
|
||||||
} from "./periodBuilders";
|
|
||||||
|
|
||||||
const DEFAULT_ICON = React.createElement(MonetizationOnIcon, {
|
const DEFAULT_ICON = React.createElement(MonetizationOnIcon, {
|
||||||
sx: { color: "#388e3c" }
|
sx: { color: "#388e3c" }
|
||||||
@@ -58,131 +51,12 @@ export async function fetchLatestTransactions(
|
|||||||
export async function fetchAggregatedData(
|
export async function fetchAggregatedData(
|
||||||
type: "expense" | "income"
|
type: "expense" | "income"
|
||||||
) {
|
) {
|
||||||
const res = await api.get("/expenses", { params: { limit: 0 } });
|
const [weekly, monthly] = await Promise.all([
|
||||||
const all: any[] = res.data?.items || res.data || [];
|
fetchReport({ period: "weekly", rolling: true }),
|
||||||
|
fetchReport({ period: "monthly", rolling: true }),
|
||||||
|
]);
|
||||||
|
|
||||||
const now = new Date();
|
return mapReportToDashboard(weekly.buckets, monthly.buckets, type);
|
||||||
|
|
||||||
let totalAmount = 0;
|
|
||||||
const payeeMap: Record<string, number> = {};
|
|
||||||
|
|
||||||
const isValid = (amt: number) =>
|
|
||||||
type === "expense" ? amt < 0 : amt > 0;
|
|
||||||
|
|
||||||
const normalize = (amt: number) => Math.abs(amt);
|
|
||||||
|
|
||||||
const {
|
|
||||||
buckets: dailyBuckets,
|
|
||||||
weekStart,
|
|
||||||
weekEnd,
|
|
||||||
prevWeekStart,
|
|
||||||
prevWeekEnd
|
|
||||||
} = buildDailyBuckets(now);
|
|
||||||
|
|
||||||
const weeklyRolling = buildWeeklyRolling(now);
|
|
||||||
const weeklyCalendar = buildWeeklyCalendar(now);
|
|
||||||
const monthlyRolling = buildMonthlyRolling(now);
|
|
||||||
const monthlyCalendar = buildMonthlyCalendar(now);
|
|
||||||
|
|
||||||
for (const item of all) {
|
|
||||||
const d = new Date(
|
|
||||||
item.occurred_at || item.created_at || Date.now()
|
|
||||||
);
|
|
||||||
|
|
||||||
const amtRaw = Number(item.amount) || 0;
|
|
||||||
if (!isValid(amtRaw)) continue;
|
|
||||||
|
|
||||||
const amt = normalize(amtRaw);
|
|
||||||
totalAmount += amt;
|
|
||||||
|
|
||||||
const payee = item.payee?.name || item.payee || "Unknown";
|
|
||||||
payeeMap[payee] = (payeeMap[payee] || 0) + amt;
|
|
||||||
|
|
||||||
const day = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"][d.getDay()];
|
|
||||||
|
|
||||||
if (d >= weekStart && d <= weekEnd) {
|
|
||||||
if (dailyBuckets[day]) {
|
|
||||||
dailyBuckets[day].amount += amt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (d >= prevWeekStart && d <= prevWeekEnd) {
|
|
||||||
if (dailyBuckets[day]) {
|
|
||||||
dailyBuckets[day].compare += amt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const apply = (arr: any[]) => {
|
|
||||||
for (const b of arr) {
|
|
||||||
if (d >= b.start && d <= b.end) b.amount += amt;
|
|
||||||
if (d >= b.prevStart && d <= b.prevEnd)
|
|
||||||
b.compare += amt;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
apply(weeklyRolling);
|
|
||||||
apply(weeklyCalendar);
|
|
||||||
apply(monthlyRolling);
|
|
||||||
apply(monthlyCalendar);
|
|
||||||
}
|
|
||||||
|
|
||||||
const toPoints = (arr: any[], type: "weekly" | "monthly"): ChartDataPoint[] =>
|
|
||||||
arr.map((x) => {
|
|
||||||
let compareLabel: string | undefined;
|
|
||||||
|
|
||||||
if (x.prevStart && x.prevEnd) {
|
|
||||||
if (type === "monthly") {
|
|
||||||
const year = String(x.prevStart.getFullYear()).slice(2);
|
|
||||||
compareLabel = `${x.prevStart.toLocaleString("default", {
|
|
||||||
month: "short"
|
|
||||||
})}-${year}`;
|
|
||||||
} else {
|
|
||||||
const year = String(x.prevEnd.getFullYear()).slice(2);
|
|
||||||
compareLabel = `${format(x.prevStart)} - ${format(x.prevEnd)} ${year}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: x.label,
|
|
||||||
amount: x.amount,
|
|
||||||
compareAmount: x.compare,
|
|
||||||
compareLabel
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const chartData = {
|
|
||||||
daily: Object.entries(dailyBuckets).map(([k, v]: any) => ({
|
|
||||||
id: k,
|
|
||||||
amount: v.amount,
|
|
||||||
compareAmount: v.compare
|
|
||||||
})),
|
|
||||||
weekly: {
|
|
||||||
rolling: toPoints(weeklyRolling, "weekly"),
|
|
||||||
calendar: toPoints(weeklyCalendar, "weekly")
|
|
||||||
},
|
|
||||||
monthly: {
|
|
||||||
rolling: toPoints(monthlyRolling, "monthly"),
|
|
||||||
calendar: toPoints(monthlyCalendar, "monthly")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.values(chartData).forEach((group: any) => {
|
|
||||||
const arr = Array.isArray(group) ? group : group.rolling;
|
|
||||||
if (!arr?.length) return;
|
|
||||||
|
|
||||||
let max = arr[0];
|
|
||||||
for (const g of arr) {
|
|
||||||
if (g.amount > max.amount) max = g;
|
|
||||||
}
|
|
||||||
if (max.amount > 0) max.highlighted = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
const topPayees = Object.entries(payeeMap)
|
|
||||||
.map(([name, amt]) => ({ payeeName: name, amount: amt }))
|
|
||||||
.sort((a, b) => b.amount - a.amount)
|
|
||||||
.slice(0, 5);
|
|
||||||
|
|
||||||
return { chartData, totalAmount, topPayees };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchAggregatedExpenses = () =>
|
export const fetchAggregatedExpenses = () =>
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
export const format = (d: Date) =>
|
|
||||||
`${d.getDate()} ${d.toLocaleString("default", { month: "short" })}`;
|
|
||||||
|
|
||||||
export const startOfDay = (d: Date) => {
|
|
||||||
const x = new Date(d);
|
|
||||||
x.setHours(0, 0, 0, 0);
|
|
||||||
return x;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const endOfDay = (d: Date) => {
|
|
||||||
const x = new Date(d);
|
|
||||||
x.setHours(23, 59, 59, 999);
|
|
||||||
return x;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getStartOfWeek = (d: Date) => {
|
|
||||||
const date = new Date(d);
|
|
||||||
const day = date.getDay() || 7;
|
|
||||||
if (day !== 1) date.setDate(date.getDate() - (day - 1));
|
|
||||||
return startOfDay(date);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const shiftDate = (d: Date, days: number) =>
|
|
||||||
new Date(d.getTime() + days * 86400000);
|
|
||||||
|
|
||||||
export const getWeekIndex = (date: Date) => {
|
|
||||||
const firstDay = new Date(date.getFullYear(), date.getMonth(), 1);
|
|
||||||
const firstWeekStart = getStartOfWeek(firstDay);
|
|
||||||
return Math.floor(
|
|
||||||
(startOfDay(date).getTime() - firstWeekStart.getTime()) /
|
|
||||||
(7 * 86400000)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,178 +0,0 @@
|
|||||||
import {
|
|
||||||
format,
|
|
||||||
endOfDay,
|
|
||||||
getStartOfWeek,
|
|
||||||
shiftDate,
|
|
||||||
getWeekIndex
|
|
||||||
} from "./dateUtils";
|
|
||||||
|
|
||||||
export const buildDailyBuckets = (now: Date) => {
|
|
||||||
const buckets: Record<string, any> = {
|
|
||||||
Mon: { amount: 0, compare: 0 },
|
|
||||||
Tue: { amount: 0, compare: 0 },
|
|
||||||
Wed: { amount: 0, compare: 0 },
|
|
||||||
Thu: { amount: 0, compare: 0 },
|
|
||||||
Fri: { amount: 0, compare: 0 },
|
|
||||||
Sat: { amount: 0, compare: 0 },
|
|
||||||
Sun: { amount: 0, compare: 0 }
|
|
||||||
};
|
|
||||||
|
|
||||||
const weekStart = getStartOfWeek(now);
|
|
||||||
const weekEnd = endOfDay(new Date(weekStart.getTime() + 6 * 86400000));
|
|
||||||
const prevWeekStart = shiftDate(weekStart, -7);
|
|
||||||
const prevWeekEnd = shiftDate(weekEnd, -7);
|
|
||||||
|
|
||||||
return { buckets, weekStart, weekEnd, prevWeekStart, prevWeekEnd };
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPrevMonthWeek = (start: Date) => {
|
|
||||||
const prevMonthDate = new Date(start);
|
|
||||||
prevMonthDate.setMonth(prevMonthDate.getMonth() - 1);
|
|
||||||
|
|
||||||
const prevMonthFirst = new Date(
|
|
||||||
prevMonthDate.getFullYear(),
|
|
||||||
prevMonthDate.getMonth(),
|
|
||||||
1
|
|
||||||
);
|
|
||||||
|
|
||||||
const prevFirstWeekStart = getStartOfWeek(prevMonthFirst);
|
|
||||||
const weekIndex = getWeekIndex(start);
|
|
||||||
|
|
||||||
const prevStart = new Date(
|
|
||||||
prevFirstWeekStart.getTime() + weekIndex * 7 * 86400000
|
|
||||||
);
|
|
||||||
const prevEnd = endOfDay(new Date(prevStart.getTime() + 6 * 86400000));
|
|
||||||
|
|
||||||
return { prevStart, prevEnd };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const buildWeeklyRolling = (now: Date) => {
|
|
||||||
const arr: any[] = [];
|
|
||||||
const currentWeekStart = getStartOfWeek(now);
|
|
||||||
|
|
||||||
for (let i = 4; i >= 0; i--) {
|
|
||||||
const start = new Date(
|
|
||||||
currentWeekStart.getTime() - i * 7 * 86400000
|
|
||||||
);
|
|
||||||
const end = endOfDay(new Date(start.getTime() + 6 * 86400000));
|
|
||||||
|
|
||||||
const { prevStart, prevEnd } = getPrevMonthWeek(start);
|
|
||||||
|
|
||||||
arr.push({
|
|
||||||
label: `${format(start)} - ${format(end)}`,
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
amount: 0,
|
|
||||||
compare: 0,
|
|
||||||
prevStart,
|
|
||||||
prevEnd
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return arr;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const buildWeeklyCalendar = (now: Date) => {
|
|
||||||
const arr: any[] = [];
|
|
||||||
|
|
||||||
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
||||||
const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0);
|
|
||||||
const firstWeekStart = getStartOfWeek(startOfMonth);
|
|
||||||
|
|
||||||
const totalWeeks =
|
|
||||||
Math.ceil(
|
|
||||||
(endOfMonth.getTime() - firstWeekStart.getTime()) /
|
|
||||||
(7 * 86400000)
|
|
||||||
) + 1;
|
|
||||||
|
|
||||||
for (let i = 0; i < totalWeeks; i++) {
|
|
||||||
const start = new Date(
|
|
||||||
firstWeekStart.getTime() + i * 7 * 86400000
|
|
||||||
);
|
|
||||||
const end = endOfDay(new Date(start.getTime() + 6 * 86400000));
|
|
||||||
|
|
||||||
const { prevStart, prevEnd } = getPrevMonthWeek(start);
|
|
||||||
|
|
||||||
arr.push({
|
|
||||||
label: `${format(start)} - ${format(end)}`,
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
amount: 0,
|
|
||||||
compare: 0,
|
|
||||||
prevStart,
|
|
||||||
prevEnd
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return arr;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const buildMonthlyRolling = (now: Date) => {
|
|
||||||
const arr: any[] = [];
|
|
||||||
|
|
||||||
for (let i = 11; i >= 0; i--) {
|
|
||||||
const d = new Date(now);
|
|
||||||
d.setMonth(d.getMonth() - i);
|
|
||||||
|
|
||||||
const start = new Date(d.getFullYear(), d.getMonth(), 1);
|
|
||||||
const end =
|
|
||||||
i === 0
|
|
||||||
? endOfDay(now)
|
|
||||||
: endOfDay(new Date(d.getFullYear(), d.getMonth() + 1, 0));
|
|
||||||
|
|
||||||
const prevStart = new Date(start);
|
|
||||||
prevStart.setFullYear(prevStart.getFullYear() - 1);
|
|
||||||
|
|
||||||
let prevEnd = new Date(end);
|
|
||||||
prevEnd.setFullYear(prevEnd.getFullYear() - 1);
|
|
||||||
|
|
||||||
if (i === 0) {
|
|
||||||
prevEnd = new Date(prevStart);
|
|
||||||
prevEnd.setDate(now.getDate());
|
|
||||||
prevEnd = endOfDay(prevEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
arr.push({
|
|
||||||
label: `${d.toLocaleString("default", {
|
|
||||||
month: "short"
|
|
||||||
})}-${String(d.getFullYear()).slice(2)}`,
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
amount: 0,
|
|
||||||
compare: 0,
|
|
||||||
prevStart,
|
|
||||||
prevEnd
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return arr;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const buildMonthlyCalendar = (now: Date) => {
|
|
||||||
const arr: any[] = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < 12; i++) {
|
|
||||||
const start = new Date(now.getFullYear(), i, 1);
|
|
||||||
const end = endOfDay(new Date(now.getFullYear(), i + 1, 0));
|
|
||||||
|
|
||||||
const prevStart = new Date(start);
|
|
||||||
prevStart.setFullYear(prevStart.getFullYear() - 1);
|
|
||||||
|
|
||||||
const prevEnd = new Date(end);
|
|
||||||
prevEnd.setFullYear(prevEnd.getFullYear() - 1);
|
|
||||||
|
|
||||||
arr.push({
|
|
||||||
label: `${start.toLocaleString("default", {
|
|
||||||
month: "short"
|
|
||||||
})}-${String(start.getFullYear()).slice(2)}`,
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
amount: 0,
|
|
||||||
compare: 0,
|
|
||||||
prevStart,
|
|
||||||
prevEnd
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return arr;
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user