calcuation fixes
This commit is contained in:
@@ -9,12 +9,35 @@ const DEFAULT_ICON = React.createElement(MonetizationOnIcon, {
|
|||||||
sx: { color: "#388e3c" }
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
// ---------------- LATEST ----------------
|
// ---------------- LATEST ----------------
|
||||||
export async function fetchLatestTransactions(
|
export async function fetchLatestTransactions(
|
||||||
type: "expense" | "income"
|
type: "expense" | "income"
|
||||||
): Promise<LatestItem[]> {
|
): Promise<LatestItem[]> {
|
||||||
const res = await api.get('/expenses', {
|
const res = await api.get("/expenses", {
|
||||||
params: { limit: 100, sort: '-occurred_at' }
|
params: { limit: 100, sort: "-occurred_at" }
|
||||||
});
|
});
|
||||||
|
|
||||||
const items = res.data?.items || res.data || [];
|
const items = res.data?.items || res.data || [];
|
||||||
@@ -54,11 +77,11 @@ export interface AggregatedDashboardData {
|
|||||||
topPayees: Array<{ payeeName: string; amount: number }>;
|
topPayees: Array<{ payeeName: string; amount: number }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------- GENERIC AGGREGATOR ----------------
|
// ---------------- AGGREGATION ----------------
|
||||||
export async function fetchAggregatedData(
|
export async function fetchAggregatedData(
|
||||||
type: "expense" | "income"
|
type: "expense" | "income"
|
||||||
): Promise<AggregatedDashboardData> {
|
): Promise<AggregatedDashboardData> {
|
||||||
const res = await api.get('/expenses', { params: { limit: 0 } });
|
const res = await api.get("/expenses", { params: { limit: 0 } });
|
||||||
const all: any[] = res.data?.items || res.data || [];
|
const all: any[] = res.data?.items || res.data || [];
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
@@ -71,56 +94,58 @@ export async function fetchAggregatedData(
|
|||||||
|
|
||||||
const normalize = (amt: number) => Math.abs(amt);
|
const normalize = (amt: number) => Math.abs(amt);
|
||||||
|
|
||||||
|
// ---------------- WEEK ----------------
|
||||||
const weekBuckets: Record<string, number> = {
|
const weekBuckets: Record<string, number> = {
|
||||||
"Mon":0,"Tue":0,"Wed":0,"Thu":0,
|
Mon: 0, Tue: 0, Wed: 0, Thu: 0,
|
||||||
"Fri":0,"Sat":0,"Sun":0
|
Fri: 0, Sat: 0, Sun: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
const monthBuckets: Record<string, { amount: number; range: string }> = {};
|
const weekStart = getStartOfWeek(now);
|
||||||
const yearBuckets: Record<string, { amount: number; range: string }> = {};
|
const weekEnd = endOfDay(new Date(weekStart.getTime() + 6 * 86400000));
|
||||||
|
|
||||||
const getStartOfWeek = (d: Date) => {
|
// ---------------- MONTH (5 rolling weeks) ----------------
|
||||||
const date = new Date(d);
|
const monthBuckets: {
|
||||||
const day = date.getDay() || 7;
|
label: string;
|
||||||
if (day !== 1) date.setDate(date.getDate() - (day - 1));
|
start: Date;
|
||||||
date.setHours(0,0,0,0);
|
end: Date;
|
||||||
return date;
|
amount: number;
|
||||||
};
|
}[] = [];
|
||||||
|
|
||||||
const format = (d: Date) =>
|
|
||||||
`${d.getDate()} ${d.toLocaleString('default', { month: 'short' })}`;
|
|
||||||
|
|
||||||
// -------- MONTH (5 rolling weeks) --------
|
|
||||||
for (let i = 0; i < 5; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
const end = new Date(now);
|
const end = endOfDay(new Date(now.getTime() - i * 7 * 86400000));
|
||||||
end.setDate(end.getDate() - i * 7);
|
const start = startOfDay(new Date(end.getTime() - 6 * 86400000));
|
||||||
|
|
||||||
const start = new Date(end);
|
monthBuckets.push({
|
||||||
start.setDate(start.getDate() - 6);
|
label: `${format(start)} - ${format(end)}`,
|
||||||
|
start,
|
||||||
const key = `${format(start)} - ${format(end)}`;
|
end,
|
||||||
|
amount: 0
|
||||||
monthBuckets[key] = { amount: 0, range: key };
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------- YEAR (12 months rolling) --------
|
// ---------------- YEAR (12 months) ----------------
|
||||||
|
const yearBuckets: {
|
||||||
|
label: string;
|
||||||
|
start: Date;
|
||||||
|
end: Date;
|
||||||
|
amount: number;
|
||||||
|
}[] = [];
|
||||||
|
|
||||||
for (let i = 0; i < 12; i++) {
|
for (let i = 0; i < 12; i++) {
|
||||||
const d = new Date(now);
|
const d = new Date(now);
|
||||||
d.setMonth(d.getMonth() - i);
|
d.setMonth(d.getMonth() - i);
|
||||||
|
|
||||||
const start = new Date(d.getFullYear(), d.getMonth(), 1);
|
const start = new Date(d.getFullYear(), d.getMonth(), 1);
|
||||||
const end = new Date(d.getFullYear(), d.getMonth() + 1, 0);
|
const end = endOfDay(new Date(d.getFullYear(), d.getMonth() + 1, 0));
|
||||||
|
|
||||||
const label = d.toLocaleString('default', { month: 'short' });
|
yearBuckets.push({
|
||||||
const range = `${format(start)} - ${format(end)}`;
|
label: d.toLocaleString("default", { month: "short" }),
|
||||||
|
start,
|
||||||
yearBuckets[label] = { amount: 0, range };
|
end,
|
||||||
|
amount: 0
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const weekStart = getStartOfWeek(now);
|
|
||||||
const weekEnd = new Date(weekStart);
|
|
||||||
weekEnd.setDate(weekStart.getDate() + 6);
|
|
||||||
|
|
||||||
// ---------------- LOOP ----------------
|
// ---------------- LOOP ----------------
|
||||||
for (const item of all) {
|
for (const item of all) {
|
||||||
const d = new Date(
|
const d = new Date(
|
||||||
@@ -137,7 +162,7 @@ export async function fetchAggregatedData(
|
|||||||
const payee = item.payee?.name || item.payee || "Unknown";
|
const payee = item.payee?.name || item.payee || "Unknown";
|
||||||
payeeMap[payee] = (payeeMap[payee] || 0) + amt;
|
payeeMap[payee] = (payeeMap[payee] || 0) + amt;
|
||||||
|
|
||||||
// ---- WEEK
|
// WEEK
|
||||||
if (d >= weekStart && d <= weekEnd) {
|
if (d >= weekStart && d <= weekEnd) {
|
||||||
const day = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"][d.getDay()];
|
const day = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"][d.getDay()];
|
||||||
if (weekBuckets[day] !== undefined) {
|
if (weekBuckets[day] !== undefined) {
|
||||||
@@ -145,25 +170,17 @@ export async function fetchAggregatedData(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- MONTH
|
// MONTH
|
||||||
Object.entries(monthBuckets).forEach(([_, obj]) => {
|
monthBuckets.forEach(b => {
|
||||||
const [startStr, endStr] = obj.range.split(" - ");
|
if (d >= b.start && d <= b.end) {
|
||||||
const start = new Date(startStr);
|
b.amount += amt;
|
||||||
const end = new Date(endStr);
|
|
||||||
|
|
||||||
if (d >= start && d <= end) {
|
|
||||||
obj.amount += amt;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ---- YEAR
|
// YEAR
|
||||||
Object.entries(yearBuckets).forEach(([_, obj]) => {
|
yearBuckets.forEach(b => {
|
||||||
const [startStr, endStr] = obj.range.split(" - ");
|
if (d >= b.start && d <= b.end) {
|
||||||
const start = new Date(startStr);
|
b.amount += amt;
|
||||||
const end = new Date(endStr);
|
|
||||||
|
|
||||||
if (d >= start && d <= end) {
|
|
||||||
obj.amount += amt;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -184,7 +201,9 @@ export async function fetchAggregatedData(
|
|||||||
// highlight max
|
// highlight max
|
||||||
Object.values(chartData).forEach(group => {
|
Object.values(chartData).forEach(group => {
|
||||||
let max = group[0];
|
let max = group[0];
|
||||||
for (const g of group) if (g.amount > max.amount) max = g;
|
for (const g of group) {
|
||||||
|
if (g.amount > max.amount) max = g;
|
||||||
|
}
|
||||||
if (max.amount > 0) max.highlighted = true;
|
if (max.amount > 0) max.highlighted = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user