import { api } from "../../react-openapi"; import { LatestItem } from "../components/LatestItemsList"; import { ChartDataPoint } from "../types/historyChart"; import * as React from "react"; import { format } from "./dateUtils"; import MonetizationOnIcon from "@mui/icons-material/MonetizationOn"; import { buildDailyBuckets, buildWeeklyRolling, buildWeeklyCalendar, buildMonthlyRolling, buildMonthlyCalendar } from "./periodBuilders"; const DEFAULT_ICON = React.createElement(MonetizationOnIcon, { sx: { color: "#388e3c" } }); export async function fetchLatestTransactions( type: "expense" | "income" ): Promise { const res = await api.get("/expenses", { params: { limit: 100, sort: "-occurred_at" } }); const items = res.data?.items || res.data || []; const isValid = (amt: number) => type === "expense" ? amt < 0 : amt > 0; return items .filter((item: any) => isValid(Number(item.amount) || 0)) .slice(0, 5) .map((exp: any, index: number) => { const time = new Date( exp.occurred_at || exp.created_at || Date.now() ).getTime(); const diffDays = Math.floor( Math.abs(Date.now() - time) / (1000 * 60 * 60 * 24) ); return { id: exp.id || index, icon: DEFAULT_ICON, iconBgColor: type === "expense" ? "#ffebee" : "#e8f5e9", title: exp.payee?.name || exp.payee || "Unknown Payee", subtitle: exp.category?.name || exp.account?.name || "Transaction", amount: `Rs ${Math.abs(exp.amount || 0)}`, timeAgo: diffDays === 0 ? "Today" : `${diffDays} days ago` }; }); } export async function fetchAggregatedData( type: "expense" | "income" ) { const res = await api.get("/expenses", { params: { limit: 0 } }); const all: any[] = res.data?.items || res.data || []; const now = new Date(); let totalAmount = 0; const payeeMap: Record = {}; 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 = () => fetchAggregatedData("expense"); export const fetchAggregatedIncome = () => fetchAggregatedData("income");