calcuation fixes
This commit is contained in:
@@ -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,22 +116,22 @@ 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>
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user