diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index 5901188..4f1ff31 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -69,7 +69,7 @@ function Home() { setLoading(true); setError(null); - // Helper for local date string YYYY-MM-DD to fix timezone issues + // Helper for local date string YYYY-MM-DD const toLocalDate = (d: Date) => { const year = d.getFullYear(); const month = String(d.getMonth() + 1).padStart(2, '0'); @@ -77,56 +77,74 @@ function Home() { return `${year}-${month}-${day}`; }; - // Calculate dates for today, yesterday, and last week same day + // Helper to check if two dates are the same day (using local time) + const isSameDay = (d1: Date, d2: Date) => { + return d1.getFullYear() === d2.getFullYear() && + d1.getMonth() === d2.getMonth() && + d1.getDate() === d2.getDate(); + }; + const today = new Date(); const yesterday = new Date(today); yesterday.setDate(yesterday.getDate() - 1); - const lastWeek = new Date(today); - lastWeek.setDate(lastWeek.getDate() - 7); + const lastWeekSameDay = new Date(today); + lastWeekSameDay.setDate(lastWeekSameDay.getDate() - 7); - const todayStr = toLocalDate(today); - const yesterdayStr = toLocalDate(yesterday); - const lastWeekStr = toLocalDate(lastWeek); + // Calculate range for fetching: 8 days ago to Tomorrow (to cover timezone shifts) + const rangeStart = new Date(today); + rangeStart.setDate(rangeStart.getDate() - 8); + const rangeEnd = new Date(today); + rangeEnd.setDate(rangeEnd.getDate() + 1); - // Generate last 7 days date strings (from 6 days ago to today) - const last7Days: string[] = []; - for (let i = 6; i >= 0; i--) { - const d = new Date(today); - d.setDate(d.getDate() - i); - last7Days.push(toLocalDate(d)); - } + const rangeStartStr = toLocalDate(rangeStart); + const rangeEndStr = toLocalDate(rangeEnd); - // Load accounts, recent transactions, today/yesterday stats... AND last 7 days - const [accountsData, transactionsData, categoriesData, ledgersData, settingsData, budgetsData, todayData, yesterdayData, lastWeekData, streakData, ...last7DaysData] = await Promise.all([ + // Parallel fetch optimized: Single request for transaction history + const [accountsData, recentTxData, categoriesData, ledgersData, settingsData, budgetsData, streakData, rangeTxData] = await Promise.all([ getAccounts(), - getTransactions({ page: 1, pageSize: 5 }), // Recent + getTransactions({ page: 1, pageSize: 5 }), // Recent list (server sort) getCategories(), getLedgers().catch(() => []), getSettings().catch(() => null), getBudgets().catch(() => []), - getTransactions({ startDate: todayStr, endDate: todayStr, type: 'expense', pageSize: 100 }), - getTransactions({ startDate: yesterdayStr, endDate: yesterdayStr, type: 'expense', pageSize: 100 }), - getTransactions({ startDate: lastWeekStr, endDate: lastWeekStr, type: 'expense', pageSize: 100 }), // Last week same day getStreakInfo().catch(() => null), - // Fetch last 7 days data - ...last7Days.map(dateStr => - getTransactions({ startDate: dateStr, endDate: dateStr, type: 'expense', pageSize: 100 }) - ), + getTransactions({ startDate: rangeStartStr, endDate: rangeEndStr, type: 'expense', pageSize: 1000 }), // Bulk fetch ]); setAccounts(accountsData || []); - setRecentTransactions(transactionsData?.items || []); + setRecentTransactions(recentTxData?.items || []); setCategories(categoriesData || []); setLedgers(ledgersData || []); setSettings(settingsData); + setStreakInfo(streakData); - // Calculate daily spends - setTodaySpend(calculateTotalExpense(todayData.items)); - setTodayTransactions(todayData.items || []); - setYesterdaySpend(calculateTotalExpense(yesterdayData.items)); - setLastWeekSpend(calculateTotalExpense(lastWeekData.items)); + // In-Memory Aggregation for Daily Spend (Fixes Timezone Issues) + const allRangeTx = rangeTxData?.items || []; - // Calculate monthly budget stats (Normalized) + // Today + const todayTxItems = allRangeTx.filter(t => isSameDay(new Date(t.transactionDate), today)); + setTodayTransactions(todayTxItems); + setTodaySpend(calculateTotalExpense(todayTxItems)); + + // Yesterday + const yesterdayTxItems = allRangeTx.filter(t => isSameDay(new Date(t.transactionDate), yesterday)); + setYesterdaySpend(calculateTotalExpense(yesterdayTxItems)); + + // Last Week Same Day + const lastWeekTxItems = allRangeTx.filter(t => isSameDay(new Date(t.transactionDate), lastWeekSameDay)); + setLastWeekSpend(calculateTotalExpense(lastWeekTxItems)); + + // Last 7 Days Array (for chart) + const last7DaysSpendArray = []; + for (let i = 6; i >= 0; i--) { + const d = new Date(today); + d.setDate(d.getDate() - i); + const dayTx = allRangeTx.filter(t => isSameDay(new Date(t.transactionDate), d)); + last7DaysSpendArray.push(calculateTotalExpense(dayTx)); + } + setLast7DaysSpend(last7DaysSpendArray); + + // Monthly Budget Stats let mTotal = 0; let mSpent = 0; (budgetsData || []).forEach((b: any) => { @@ -136,22 +154,11 @@ function Home() { else if (b.periodType === 'yearly') multiplier = 1 / 12; mTotal += b.amount * multiplier; - // We add the actual spent amount regardless of period, as it contributes to "Monthly Spending" in a general sense - // But strictly speaking, Daily spent resets daily. - // For the purpose of "Monthly Overview", we just want to know if there IS a budget. mSpent += (b.spent || 0); }); setMonthlyBudgetTotal(mTotal); setMonthlyBudgetSpentTotal(mSpent); - // Set streak info - setStreakInfo(streakData); - - // Calculate last 7 days spending array - const last7DaysSpendArray = last7DaysData.map(dayData => - calculateTotalExpense(dayData?.items || []) - ); - setLast7DaysSpend(last7DaysSpendArray); } catch (err) { setError(err instanceof Error ? err.message : '加载数据失败'); console.error('Failed to load home page data:', err);