fixed compare
This commit is contained in:
@@ -4,38 +4,18 @@ import { ChartDataPoint } from "../components/HistoryChart";
|
||||
import * as React from "react";
|
||||
import MonetizationOnIcon from "@mui/icons-material/MonetizationOn";
|
||||
|
||||
// ---------------- ICON ----------------
|
||||
import {
|
||||
buildDailyBuckets,
|
||||
buildWeeklyRolling,
|
||||
buildWeeklyCalendar,
|
||||
buildMonthlyRolling,
|
||||
buildMonthlyCalendar
|
||||
} from "./periodBuilders";
|
||||
|
||||
const DEFAULT_ICON = React.createElement(MonetizationOnIcon, {
|
||||
sx: { color: "#388e3c" }
|
||||
});
|
||||
|
||||
// ---------------- HELPERS ----------------
|
||||
const format = (d: Date) =>
|
||||
`${d.getDate()} ${d.toLocaleString("default", { month: "short" })}`;
|
||||
|
||||
const startOfDay = (d: Date) => {
|
||||
const x = new Date(d);
|
||||
x.setHours(0, 0, 0, 0);
|
||||
return x;
|
||||
};
|
||||
|
||||
const endOfDay = (d: Date) => {
|
||||
const x = new Date(d);
|
||||
x.setHours(23, 59, 59, 999);
|
||||
return x;
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
const shiftDate = (d: Date, days: number) =>
|
||||
new Date(d.getTime() + days * 86400000);
|
||||
|
||||
// ---------------- LATEST ----------------
|
||||
export async function fetchLatestTransactions(
|
||||
type: "expense" | "income"
|
||||
): Promise<LatestItem[]> {
|
||||
@@ -66,35 +46,17 @@ export async function fetchLatestTransactions(
|
||||
iconBgColor:
|
||||
type === "expense" ? "#ffebee" : "#e8f5e9",
|
||||
title: exp.payee?.name || exp.payee || "Unknown Payee",
|
||||
subtitle: exp.category?.name || exp.account?.name || "Transaction",
|
||||
subtitle:
|
||||
exp.category?.name || exp.account?.name || "Transaction",
|
||||
amount: `Rs ${Math.abs(exp.amount || 0)}`,
|
||||
timeAgo: diffDays === 0 ? "Today" : `${diffDays} days ago`
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------- TYPES ----------------
|
||||
export interface ChartSeries {
|
||||
rolling: ChartDataPoint[];
|
||||
calendar: ChartDataPoint[];
|
||||
}
|
||||
|
||||
export interface ChartData {
|
||||
daily: ChartDataPoint[];
|
||||
weekly: ChartSeries;
|
||||
monthly: ChartSeries;
|
||||
}
|
||||
|
||||
export interface AggregatedDashboardData {
|
||||
chartData: ChartData;
|
||||
totalAmount: number;
|
||||
topPayees: Array<{ payeeName: string; amount: number }>;
|
||||
}
|
||||
|
||||
// ---------------- AGGREGATION ----------------
|
||||
export async function fetchAggregatedData(
|
||||
type: "expense" | "income"
|
||||
): Promise<AggregatedDashboardData> {
|
||||
) {
|
||||
const res = await api.get("/expenses", { params: { limit: 0 } });
|
||||
const all: any[] = res.data?.items || res.data || [];
|
||||
|
||||
@@ -108,126 +70,19 @@ export async function fetchAggregatedData(
|
||||
|
||||
const normalize = (amt: number) => Math.abs(amt);
|
||||
|
||||
// ---------------- DAILY ----------------
|
||||
const dailyBuckets: 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 {
|
||||
buckets: dailyBuckets,
|
||||
weekStart,
|
||||
weekEnd,
|
||||
prevWeekStart,
|
||||
prevWeekEnd
|
||||
} = buildDailyBuckets(now);
|
||||
|
||||
const weekStart = getStartOfWeek(now);
|
||||
const weekEnd = endOfDay(new Date(weekStart.getTime() + 6 * 86400000));
|
||||
const prevWeekStart = shiftDate(weekStart, -7);
|
||||
const prevWeekEnd = shiftDate(weekEnd, -7);
|
||||
const weeklyRolling = buildWeeklyRolling(now);
|
||||
const weeklyCalendar = buildWeeklyCalendar(now);
|
||||
const monthlyRolling = buildMonthlyRolling(now);
|
||||
const monthlyCalendar = buildMonthlyCalendar(now);
|
||||
|
||||
// ---------------- WEEKLY ----------------
|
||||
const weeklyRolling: any[] = [];
|
||||
const weeklyCalendar: any[] = [];
|
||||
|
||||
const currentWeekStart = getStartOfWeek(now);
|
||||
|
||||
// rolling (last 5 weeks)
|
||||
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));
|
||||
|
||||
weeklyRolling.push({
|
||||
label: `${format(start)} - ${format(end)}`,
|
||||
start,
|
||||
end,
|
||||
amount: 0,
|
||||
compare: 0,
|
||||
prevStart: shiftDate(start, -7),
|
||||
prevEnd: shiftDate(end, -7)
|
||||
});
|
||||
}
|
||||
|
||||
// calendar weeks
|
||||
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));
|
||||
|
||||
weeklyCalendar.push({
|
||||
label: `${format(start)} - ${format(end)}`,
|
||||
start,
|
||||
end,
|
||||
amount: 0,
|
||||
compare: 0,
|
||||
prevStart: shiftDate(start, -7),
|
||||
prevEnd: shiftDate(end, -7)
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------- MONTHLY ----------------
|
||||
const monthlyRolling: any[] = [];
|
||||
const monthlyCalendar: any[] = [];
|
||||
|
||||
// rolling (last 12 months)
|
||||
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.setMonth(prevStart.getMonth() - 1);
|
||||
const prevEnd = new Date(end);
|
||||
prevEnd.setMonth(prevEnd.getMonth() - 1);
|
||||
|
||||
monthlyRolling.push({
|
||||
label: `${d.toLocaleString("default", { month: "short" })}-${String(
|
||||
d.getFullYear()
|
||||
).slice(2)}`,
|
||||
start,
|
||||
end,
|
||||
amount: 0,
|
||||
compare: 0,
|
||||
prevStart,
|
||||
prevEnd
|
||||
});
|
||||
}
|
||||
|
||||
// calendar (Jan–Dec)
|
||||
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);
|
||||
|
||||
monthlyCalendar.push({
|
||||
label: `${start.toLocaleString("default", { month: "short" })}-${String(
|
||||
start.getFullYear()
|
||||
).slice(2)}`,
|
||||
start,
|
||||
end,
|
||||
amount: 0,
|
||||
compare: 0,
|
||||
prevStart,
|
||||
prevEnd
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------- LOOP ----------------
|
||||
for (const item of all) {
|
||||
const d = new Date(
|
||||
item.occurred_at || item.created_at || Date.now()
|
||||
@@ -237,46 +92,37 @@ export async function fetchAggregatedData(
|
||||
if (!isValid(amtRaw)) continue;
|
||||
|
||||
const amt = normalize(amtRaw);
|
||||
|
||||
totalAmount += amt;
|
||||
|
||||
const payee = item.payee?.name || item.payee || "Unknown";
|
||||
payeeMap[payee] = (payeeMap[payee] || 0) + amt;
|
||||
|
||||
// DAILY
|
||||
const day = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"][d.getDay()];
|
||||
|
||||
if (d >= weekStart && d <= weekEnd) {
|
||||
const day = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"][d.getDay()];
|
||||
if (dailyBuckets[day]) {
|
||||
dailyBuckets[day].amount += amt;
|
||||
}
|
||||
}
|
||||
|
||||
if (d >= prevWeekStart && d <= prevWeekEnd) {
|
||||
const day = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"][d.getDay()];
|
||||
if (dailyBuckets[day]) {
|
||||
dailyBuckets[day].compare += amt;
|
||||
}
|
||||
}
|
||||
|
||||
// WEEKLY
|
||||
for (const b of weeklyRolling) {
|
||||
if (d >= b.start && d <= b.end) b.amount += amt;
|
||||
if (d >= b.prevStart && d <= b.prevEnd) b.compare += amt;
|
||||
}
|
||||
for (const b of weeklyCalendar) {
|
||||
if (d >= b.start && d <= b.end) b.amount += amt;
|
||||
if (d >= b.prevStart && d <= b.prevEnd) b.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;
|
||||
}
|
||||
};
|
||||
|
||||
// MONTHLY
|
||||
for (const b of monthlyRolling) {
|
||||
if (d >= b.start && d <= b.end) b.amount += amt;
|
||||
if (d >= b.prevStart && d <= b.prevEnd) b.compare += amt;
|
||||
}
|
||||
for (const b of monthlyCalendar) {
|
||||
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[]): ChartDataPoint[] =>
|
||||
@@ -286,7 +132,7 @@ export async function fetchAggregatedData(
|
||||
compareAmount: x.compare
|
||||
}));
|
||||
|
||||
const chartData: ChartData = {
|
||||
const chartData = {
|
||||
daily: Object.entries(dailyBuckets).map(([k, v]: any) => ({
|
||||
id: k,
|
||||
amount: v.amount,
|
||||
@@ -302,7 +148,6 @@ export async function fetchAggregatedData(
|
||||
}
|
||||
};
|
||||
|
||||
// highlight max (current only)
|
||||
Object.values(chartData).forEach((group: any) => {
|
||||
const arr = Array.isArray(group) ? group : group.rolling;
|
||||
if (!arr?.length) return;
|
||||
@@ -322,7 +167,6 @@ export async function fetchAggregatedData(
|
||||
return { chartData, totalAmount, topPayees };
|
||||
}
|
||||
|
||||
// ---------------- EXPORTS ----------------
|
||||
export const fetchAggregatedExpenses = () =>
|
||||
fetchAggregatedData("expense");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user