calcuation fixes

This commit is contained in:
2026-04-04 22:35:17 +05:30
parent 6abed4e72a
commit 4eca3b7124
2 changed files with 75 additions and 65 deletions

View File

@@ -13,16 +13,28 @@ import LatestItemsList, { LatestItem } from "./components/LatestItemsList";
import HistoryChart from "./components/HistoryChart"; import HistoryChart from "./components/HistoryChart";
import { import {
fetchLatestExpenses, fetchLatestTransactions,
fetchAggregatedExpenses, fetchAggregatedExpenses,
fetchAggregatedIncome, fetchAggregatedIncome,
AggregatedDashboardData AggregatedDashboardData
} from "./utils/dashboardLoader"; } from "./utils/dashboardLoader";
export default function Dashboard() { export default function Dashboard() {
const [latest, setLatest] = React.useState<LatestItem[]>([]); const [latest, setLatest] = React.useState<{
const [aggregated, setAggregated] = expense: LatestItem[];
React.useState<AggregatedDashboardData | null>(null); income: LatestItem[];
}>({
expense: [],
income: []
});
const [aggregated, setAggregated] = React.useState<{
expense: AggregatedDashboardData | null;
income: AggregatedDashboardData | null;
}>({
expense: null,
income: null
});
const [mode, setMode] = const [mode, setMode] =
React.useState<"expense" | "income">("expense"); React.useState<"expense" | "income">("expense");
@@ -30,21 +42,33 @@ export default function Dashboard() {
const [loading, setLoading] = React.useState(true); const [loading, setLoading] = React.useState(true);
const [error, setError] = React.useState<string | null>(null); const [error, setError] = React.useState<string | null>(null);
// -------- LOAD DATA -------- // -------- LOAD ONCE --------
React.useEffect(() => { React.useEffect(() => {
async function loadData() { async function loadData() {
try { try {
setLoading(true); setLoading(true);
const latestData = await fetchLatestExpenses(); const [
latestExpense,
latestIncome,
expenseData,
incomeData
] = await Promise.all([
fetchLatestTransactions("expense"),
fetchLatestTransactions("income"),
fetchAggregatedExpenses(),
fetchAggregatedIncome()
]);
const aggData = setLatest({
mode === "expense" expense: latestExpense,
? await fetchAggregatedExpenses() income: latestIncome
: await fetchAggregatedIncome(); });
setLatest(latestData); setAggregated({
setAggregated(aggData); expense: expenseData,
income: incomeData
});
} catch (err: any) { } catch (err: any) {
console.error(err); console.error(err);
@@ -55,7 +79,10 @@ export default function Dashboard() {
} }
loadData(); loadData();
}, [mode]); }, []);
const currentData = aggregated[mode];
const currentLatest = latest[mode];
// -------- UI STATES -------- // -------- UI STATES --------
if (loading) { if (loading) {
@@ -89,25 +116,25 @@ export default function Dashboard() {
</Box> </Box>
<Grid container spacing={4} direction="row"> <Grid container spacing={4} direction="row">
{/* LEFT → 1/3 (4 cols) */} {/* LEFT → 1/3 */}
<Grid size={4}> <Grid size={4}>
<LatestItemsList <LatestItemsList
title="Recent Transactions" title={`Recent ${mode === "expense" ? "Expenses" : "Income"}`}
items={latest} items={currentLatest}
onViewAll={() => {}} onViewAll={() => {}}
/> />
</Grid> </Grid>
{/* RIGHT → 2/3 (8 cols) */} {/* RIGHT → 2/3 */}
<Grid size={8}> <Grid size={8}>
<HistoryChart <HistoryChart
header={`${mode === "expense" ? "Expense" : "Income"} Breakdown`} header={`${mode === "expense" ? "Expense" : "Income"} Breakdown`}
summary="Interactive chronological tracking" summary="Interactive chronological tracking"
tabs={["Today", "Week", "Month", "Year"]} tabs={["Week", "Month", "Year"]}
data={aggregated?.chartData || {}} data={currentData?.chartData || {}}
/> />
</Grid> </Grid>
</Grid> </Grid>
</Container> </Container>
); );
} }

View File

@@ -10,32 +10,41 @@ const DEFAULT_ICON = React.createElement(MonetizationOnIcon, {
}); });
// ---------------- LATEST ---------------- // ---------------- LATEST ----------------
export async function fetchLatestExpenses(): Promise<LatestItem[]> { export async function fetchLatestTransactions(
type: "expense" | "income"
): Promise<LatestItem[]> {
const res = await api.get('/expenses', { const res = await api.get('/expenses', {
params: { limit: 10, sort: '-occurred_at' } params: { limit: 100, sort: '-occurred_at' }
}); });
const items = res.data?.items || res.data || []; const items = res.data?.items || res.data || [];
return items.map((exp: any, index: number) => { const isValid = (amt: number) =>
const time = new Date( type === "expense" ? amt < 0 : amt > 0;
exp.occurred_at || exp.created_at || Date.now()
).getTime();
const diffDays = Math.floor( return items
Math.abs(Date.now() - time) / (1000 * 60 * 60 * 24) .filter((item: any) => isValid(Number(item.amount) || 0))
); .slice(0, 10)
.map((exp: any, index: number) => {
const time = new Date(
exp.occurred_at || exp.created_at || Date.now()
).getTime();
return { const diffDays = Math.floor(
id: exp.id || index, Math.abs(Date.now() - time) / (1000 * 60 * 60 * 24)
icon: DEFAULT_ICON, );
iconBgColor: "#e8f5e9",
title: exp.payee?.name || exp.payee || "Unknown Payee", return {
subtitle: exp.category?.name || exp.account?.name || "Transaction", id: exp.id || index,
amount: `Rs ${exp.amount || 0}`, icon: DEFAULT_ICON,
timeAgo: diffDays === 0 ? "Today" : `${diffDays} days ago` 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`
};
});
} }
// ---------------- TYPES ---------------- // ---------------- TYPES ----------------
@@ -62,12 +71,6 @@ export async function fetchAggregatedData(
const normalize = (amt: number) => Math.abs(amt); const normalize = (amt: number) => Math.abs(amt);
// ---------------- BUCKETS ----------------
const todayBuckets: Record<string, number> = {
"12am":0,"3am":0,"6am":0,"9am":0,
"12pm":0,"3pm":0,"6pm":0,"9pm":0
};
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
@@ -134,25 +137,6 @@ 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;
// ---- TODAY
if (
d.getDate() === now.getDate() &&
d.getMonth() === now.getMonth() &&
d.getFullYear() === now.getFullYear()
) {
const hr = d.getHours();
let label = "12am";
if (hr >= 3 && hr < 6) label = "3am";
else if (hr >= 6 && hr < 9) label = "6am";
else if (hr >= 9 && hr < 12) label = "9am";
else if (hr >= 12 && hr < 15) label = "12pm";
else if (hr >= 15 && hr < 18) label = "3pm";
else if (hr >= 18 && hr < 21) label = "6pm";
else if (hr >= 21) label = "9pm";
todayBuckets[label] += 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()];
@@ -192,7 +176,6 @@ export async function fetchAggregatedData(
})); }));
const chartData = { const chartData = {
today: toPoints(todayBuckets),
week: toPoints(weekBuckets), week: toPoints(weekBuckets),
month: toPoints(monthBuckets), month: toPoints(monthBuckets),
year: toPoints(yearBuckets) year: toPoints(yearBuckets)