feat(overview): Implement Stats Caching and Graph Updates#25
feat(overview): Implement Stats Caching and Graph Updates#25pranjal29092005 wants to merge 3 commits intoDjedAlliance:mainfrom
Conversation
WalkthroughAdds localStorage-backed client-side caching for Overview stats, displays cached values immediately while fetching fresh transactions, updates cache and UI when fresh data arrives, and replaces mock chart data with transformed transaction-derived data for week/month/year views. Changes
sequenceDiagram
autonumber
participant Browser as Browser (DashboardOverview)
participant Local as localStorage
participant Hook as useTransactions (fetch)
participant Chart as Chart Component
Note over Browser,Local: On mount
Browser->>Local: read STATS_CACHE_KEY
Local-->>Browser: cachedStats or null
Browser->>Chart: render with cachedStats (or "T/A")
Browser->>Hook: start fetch transactions (async)
Hook-->>Browser: hasFetched=false (initial)
alt while fetching
Browser->>Browser: show cached values + updating banner
Chart->>Chart: render with cached/placeholder data
end
Hook-->>Browser: fresh transactions (success)
Browser->>Browser: compute real stats, update state
Browser->>Local: write STATS_CACHE_KEY with timestamp
Browser->>Chart: provide transformed chartData (week/month/year)
Chart-->>Browser: re-render with fresh data
alt fetch fails
Hook-->>Browser: error
Browser->>Browser: keep cachedStats, show error/stop updating banner
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (4)
dashboard/components/dashboard/chart/index.tsx (3)
93-94: Consider making the fee percentage configurable.The 1% fee is hardcoded. If the actual fee structure differs or changes, this will display incorrect data. Consider extracting this to a constant or configuration value.
+const FEE_PERCENTAGE = 0.01; // 1% fee - adjust as needed + // Inside transformToChartData: const revenue = parseFloat(tx.amountBC); - const fees = revenue * 0.01; // Assume 1% fee + const fees = revenue * FEE_PERCENTAGE;
71-85: Year period iterates 365 times but only needs 12 months.For the "year" period, the loop runs 365 times but
dateFormatreturns only the month name (e.g., "Jan"). This creates redundant iterations since the Map deduplicates by key. Consider iterating by month instead for better efficiency.+ // For year, iterate by month instead of day + if (period === "year") { + for (let i = 11; i >= 0; i--) { + const date = new Date(now.getFullYear(), now.getMonth() - i, 1); + const dateKey = dateFormat(date); + if (!dataPoints.has(dateKey)) { + dataPoints.set(dateKey, { revenue: 0, count: 0, fees: 0 }); + } + } + } else { // Initialize all dates with zero values for (let i = daysToInclude - 1; i >= 0; i--) { const date = new Date(now); date.setDate(date.getDate() - i); const dateKey = dateFormat(date); if (!dataPoints.has(dateKey)) { dataPoints.set(dateKey, { revenue: 0, count: 0, fees: 0 }); } } + }
269-277: Memoize chart data transformation to avoid recalculating on every render.
transformToChartDatais called for all three tabs on every render, reprocessing all transactions each time. Consider memoizing the results withuseMemo.+ const chartData = React.useMemo(() => ({ + week: transformToChartData("week"), + month: transformToChartData("month"), + year: transformToChartData("year"), + }), [transactions, hasFetched]); + // In JSX: <TabsContent value="week" className="space-y-4"> - {renderChart(transformToChartData("week"))} + {renderChart(chartData.week)} </TabsContent> <TabsContent value="month" className="space-y-4"> - {renderChart(transformToChartData("month"))} + {renderChart(chartData.month)} </TabsContent> <TabsContent value="year" className="space-y-4"> - {renderChart(transformToChartData("year"))} + {renderChart(chartData.year)} </TabsContent>dashboard/app/page.tsx (1)
24-34: Consider adding cache expiry for stats.Unlike the transactions cache in
useTransactionswhich has aCACHE_EXPIRY, the stats cache has no expiry mechanism. Very old cached stats could be misleading to users. Consider adding expiry validation similar to the transactions hook.const STATS_CACHE_KEY = 'stablepay_stats_cache'; +const STATS_CACHE_EXPIRY = 24 * 60 * 60 * 1000; // 24 hours // In useEffect: if (cached) { try { const parsedStats: CachedStats = JSON.parse(cached); + const isExpired = Date.now() - parsedStats.timestamp > STATS_CACHE_EXPIRY; + if (!isExpired) { setCachedStats(parsedStats); + } } catch (err) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
dashboard/app/page.tsx(4 hunks)dashboard/components/dashboard/chart/index.tsx(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
dashboard/components/dashboard/chart/index.tsx (2)
dashboard/hooks/use-transactions.ts (1)
useTransactions(13-85)dashboard/types/dashboard.ts (2)
TimePeriod(30-30)ChartDataPoint(11-16)
🔇 Additional comments (7)
dashboard/components/dashboard/chart/index.tsx (1)
44-44: LGTM on hook integration.The
useTransactionshook is correctly integrated to providetransactionsandhasFetchedstate for data-driven chart rendering.dashboard/app/page.tsx (6)
41-52: LGTM on cache loading.The cache loading effect correctly runs once on mount with proper error handling for malformed cached data.
54-59: LGTM on stats calculation.The calculation logic is straightforward. The comments clearly explain that failed/pending are always 0 for blockchain data.
61-76: LGTM on cache update logic.The effect correctly updates localStorage when fresh data is fetched. The dependency array, while verbose, satisfies React's exhaustive-deps rule.
85-100: LGTM on display stats logic.The fallback chain (fresh → cached → defaults) correctly implements the PR requirement to show cached values instantly while fresh data loads.
103-141: LGTM on stats configuration.The stats array correctly implements all five required statistics with appropriate fallbacks to "T/A" when no data is available.
143-169: LGTM on conditional UI banners.The header description and conditional banners correctly communicate the data state to users (no data, cached data with timestamp, or real-time data).
… resets on fetch completion/failure) - Fix transaction date bug (use tx.timestamp instead of new Date()) - Add 24-hour cache expiry for stats to prevent stale data - Make fee percentage configurable (FEE_PERCENTAGE constant) - Optimize year period iteration (iterate by month instead of 365 days) - Memoize chart data transformation to avoid unnecessary recalculations
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
dashboard/app/page.tsx (4)
24-35: Cache constants/types look fine; consider namespacing the key to env/app.
If you run multiple dashboard apps on the same origin, a more specific key (e.g., including app name + chain/network) reduces collision risk.
42-56: GuardlocalStoragereads for “unavailable storage” cases.
getItemcan throw in some browsers/modes; you already handle JSON parse errors, but not storage access errors.useEffect(() => { - const cached = localStorage.getItem(STATS_CACHE_KEY); - if (cached) { - try { - const parsedStats: CachedStats = JSON.parse(cached); - const isExpired = Date.now() - parsedStats.timestamp > STATS_CACHE_EXPIRY; - if (!isExpired) { - setCachedStats(parsedStats); - } - } catch (err) { - console.warn('Failed to parse cached stats:', err); - } - } + try { + const cached = localStorage.getItem(STATS_CACHE_KEY); + if (!cached) return; + const parsedStats: CachedStats = JSON.parse(cached); + const isExpired = Date.now() - parsedStats.timestamp > STATS_CACHE_EXPIRY; + if (!isExpired) setCachedStats(parsedStats); + } catch (err) { + console.warn("Failed to load cached stats:", err); + } }, []);
58-64: Revenue math viaparseFloatmay introduce precision drift.
Iftx.amountBCis currency-like, consider using an integer base unit (or a decimal/bignumber library) and format at the end.
155-160: Header/banner messaging is coherent with cached-first UX.
Minor: consider whether emojis are desired in the dashboard UI copy (“Note”, “Updating”).Also applies to: 163-177
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
dashboard/app/page.tsx(4 hunks)dashboard/components/dashboard/chart/index.tsx(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- dashboard/components/dashboard/chart/index.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
dashboard/app/page.tsx (1)
dashboard/hooks/use-transactions.ts (1)
useTransactions(13-85)
🔇 Additional comments (2)
dashboard/app/page.tsx (2)
82-91: Nice: this addresses the prior “loading banner never resets” concern.
isLoadingFreshnow resets whenloadingbecomesfalse(including failures).
93-109: The code at lines 93-109 correctly implements "cached-first on failure" behavior and does not require modification. TheuseTransactionshook only setshasFetched=trueon successful fetch (line 50), not on error (catch block at lines 63-65 does not sethasFetched). Therefore,displayStatswill appropriately fall back tocachedStatswhen a fetch fails, rather than override cached data with zeros.
…ions in try/catch for error handling - Cache zero-transaction states (remove transactions.length check) - Improve error messages for localStorage failures - Remove emojis from UI copy for professional appearance
Description
This PR implements stats caching and graph updates for the Overview tab as requested in #5.
Changes
Files Modified
dashboard/app/page.tsx- Overview page with stats cachingdashboard/components/dashboard/chart/index.tsx- Chart with real dataTesting
npm run buildHow to Test
Closes #5
Summary by CodeRabbit
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.