Implement Overview Tab Stats Caching with Loading Behavior#9
Implement Overview Tab Stats Caching with Loading Behavior#9manishyad375375 wants to merge 25 commits into
Conversation
This workflow automates the deployment of static content to GitHub Pages on pushes to the main branch.
Set basePath and assetPrefix to include repo name and added trailingSlash option.
|
Warning Rate limit exceeded@manishyad375375 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 4 minutes and 46 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (5)
WalkthroughThis PR implements a localStorage-based caching system for dashboard statistics, adds utility functions for calculating and formatting stats from transaction data, updates the dashboard overview page to load and persist cached stats, modifies the chart component to accept dynamic transaction data with loading states, removes an outdated GitHub Actions workflow, and updates Next.js configuration for production deployment with GitHub Pages support. Changes
Sequence DiagramsequenceDiagram
actor User
participant Dashboard as Dashboard Page
participant Cache as localStorage
participant Calculator as Stats Calculator
participant ChartComp as Chart Component
User->>Dashboard: Load page
Dashboard->>Cache: getCachedStats()
Cache-->>Dashboard: Return cached stats (or defaults)
Dashboard->>ChartComp: Render with cached data
ChartComp-->>User: Display chart (mock or cached)
Dashboard->>Dashboard: Fetch fresh transactions (background)
Dashboard->>Calculator: calculateStatsFromTransactions()
Calculator-->>Dashboard: Return computed stats
Dashboard->>Cache: setCachedStats()
Cache-->>Cache: Update localStorage
Dashboard->>ChartComp: Pass fresh transactions + isLoading
ChartComp->>ChartComp: processTransactionData()
ChartComp-->>User: Display updated chart
Dashboard-->>User: Display updated stats + timestamp
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Areas requiring attention:
Possibly related issues
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
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: 9
🧹 Nitpick comments (5)
dashboard/next.config.ts (1)
3-9: ThebasePath: ''approach is valid; however, consider using an explicit environment variable to decouple fromNODE_ENVfor clarity and flexibility.Empty
basePathis the standard Next.js way to indicate no base path prefix and is fully supported in version 14.2.16. However, your current setup couples GitHub Pages-specific configuration toNODE_ENV === 'production', which can be fragile—if the build runs withNODE_ENV=productionin a different context (e.g., local builds, other CI systems), it will incorrectly apply the/StablePay-MerchantDashboardprefix.Since your GitHub Actions workflow already explicitly sets
NODE_ENV: productiononly in the Pages build job, the current implementation works in practice. To improve clarity and safety for future maintenance, consider adding an explicit environment variable:-const isProd = process.env.NODE_ENV === 'production' +const isGhPages = process.env.GITHUB_PAGES === 'true' const nextConfig: NextConfig = { output: 'export', - basePath: isProd ? '/StablePay-MerchantDashboard' : '', - assetPrefix: isProd ? '/StablePay-MerchantDashboard/' : '', + basePath: isGhPages ? '/StablePay-MerchantDashboard' : undefined, + assetPrefix: isGhPages ? '/StablePay-MerchantDashboard/' : undefined, trailingSlash: true,Then update your workflow to set
GITHUB_PAGES: 'true'in the build step. Usingundefinedinstead of empty string is cleaner (Next.js treats undefined properties as unset), and the explicit flag makes the intent unmistakable.dashboard/app/page.tsx (1)
63-105:showTAgating is good, but formatting helpers currently reintroduce “T/A” for legitimate zeros.
Even whenshowTAis false,formatRevenue(0)/formatSuccessRate(0)can still return"T/A"(depending on their implementation), which conflicts with “T/A only until first fetch”.dashboard/OVERVIEW_STATS_IMPLEMENTATION.md (1)
140-150: Minor wording polish: “First-time user” hyphenation.
This is purely doc style, but helps consistency.dashboard/lib/stats-cache.ts (1)
55-69: ClarifylastUpdatedownership (cache overwrites caller-provided timestamps).
setCachedStatsforceslastUpdated: Date.now()(Line 61–65), even if the caller already computed one. That’s fine, but then callers shouldn’t set it (orsetCachedStatsshould respect it).dashboard/components/dashboard/chart/index.tsx (1)
59-66: Mock fallback may be misleading when no transactions have ever been fetched.
Iftransactionsis empty (Line 59–66), the chart shows mock data, but the Overview stats are showing “T/A”/instructional messaging—this can feel inconsistent.Also applies to: 120-131
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
.github/workflows/nextjs.yml(1 hunks).github/workflows/static.yml(1 hunks)dashboard/OVERVIEW_STATS_IMPLEMENTATION.md(1 hunks)dashboard/app/page.tsx(4 hunks)dashboard/components/dashboard/chart/index.tsx(6 hunks)dashboard/lib/calculate-stats.ts(1 hunks)dashboard/lib/stats-cache.ts(1 hunks)dashboard/next.config.mjs(0 hunks)dashboard/next.config.ts(1 hunks)public/.nojekyll(1 hunks)
💤 Files with no reviewable changes (1)
- dashboard/next.config.mjs
🧰 Additional context used
🧬 Code graph analysis (2)
dashboard/lib/calculate-stats.ts (2)
dashboard/lib/transaction-service.ts (1)
TransactionEvent(7-15)dashboard/lib/stats-cache.ts (1)
OverviewStats(6-13)
dashboard/components/dashboard/chart/index.tsx (2)
dashboard/lib/transaction-service.ts (1)
TransactionEvent(7-15)dashboard/components/ui/tabs.tsx (1)
TabsContent(66-66)
🪛 LanguageTool
dashboard/OVERVIEW_STATS_IMPLEMENTATION.md
[grammar] ~142-~142: Use a hyphen to join words.
Context: ...ehavior Scenarios ### Scenario 1: First Time User (No Cache) 1. Page loads 2. Sh...
(QB_NEW_EN_HYPHEN)
[style] ~238-~238: Some style guides suggest that commas should set off the year in a month-day-year date.
Context: ... Implementation Date: December 13, 2025 Version: 1.0.0 Status: ✅ Co...
(MISSING_COMMA_AFTER_YEAR)
🪛 markdownlint-cli2 (0.18.1)
dashboard/OVERVIEW_STATS_IMPLEMENTATION.md
47-47: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
79-79: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🔇 Additional comments (3)
public/.nojekyll (1)
1-1: Clarify redundancy:.nojekyllin bothpublic/and workflow-generated./out/.The file correctly signals GitHub Pages to bypass Jekyll processing. However, the AI summary indicates the workflow also creates a
.nojekyllmarker in the./outdirectory during static export. If both mechanisms are in place, thepublic/version may be redundant.Consider documenting whether:
- Both files are intentional safeguards (no action needed; this is fine).
- Only one is necessary and the other can be removed.
.github/workflows/nextjs.yml (1)
20-23:defaults.run.working-directoryis a good simplification; double-check any non-runsteps still use repo-root paths.
You correctly kept cache/artifact paths rooted atdashboard/...for action steps (Line 39–47, Line 59–63).dashboard/app/page.tsx (1)
34-38: Cache-on-mount flow is clean and keeps UI responsive.
Loading cached stats inuseEffect(Line 34–38) matches the “instant cached display” objective.
| - name: Build with Next.js | ||
| run: ${{ steps.detect-package-manager.outputs.runner }} next build | ||
| run: npm run build | ||
| env: | ||
| NODE_ENV: production | ||
|
|
||
| - name: Add .nojekyll file | ||
| run: touch ./out/.nojekyll | ||
|
|
||
| - name: Upload artifact | ||
| uses: actions/upload-pages-artifact@v3 | ||
| with: | ||
| path: ./out | ||
| path: ./dashboard/out | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's examine the workflow file to understand the context
cat -n .github/workflows/nextjs.yml | head -70Repository: DjedAlliance/StablePay-MerchantDashboard
Length of output: 2242
🏁 Script executed:
# Check the working directory and full context of the build commands
cat -n .github/workflows/nextjs.yml | sed -n '40,70p'Repository: DjedAlliance/StablePay-MerchantDashboard
Length of output: 1209
🏁 Script executed:
# Look for the Next.js configuration
fd -name "next.config.*" dashboard/Repository: DjedAlliance/StablePay-MerchantDashboard
Length of output: 314
🏁 Script executed:
# Check the dashboard package.json for build script definition
cat dashboard/package.jsonRepository: DjedAlliance/StablePay-MerchantDashboard
Length of output: 2694
🏁 Script executed:
# Check if there are any other relevant files that might affect output
fd -name "tsconfig.json\|jsconfig.json" dashboard/Repository: DjedAlliance/StablePay-MerchantDashboard
Length of output: 314
🏁 Script executed:
# Search for next.config files in dashboard
fd 'next\.config' dashboard/Repository: DjedAlliance/StablePay-MerchantDashboard
Length of output: 108
🏁 Script executed:
# If next.config.js exists, examine it
if [ -f dashboard/next.config.js ]; then cat dashboard/next.config.js; fi
if [ -f dashboard/next.config.mjs ]; then cat dashboard/next.config.mjs; fi
if [ -f dashboard/next.config.ts ]; then cat dashboard/next.config.ts; fiRepository: DjedAlliance/StablePay-MerchantDashboard
Length of output: 539
🏁 Script executed:
# Also check for any build or export configuration that might affect output
grep -r "output.*dir\|out.*dir\|export" dashboard/next.config.* 2>/dev/null || echo "No match found"Repository: DjedAlliance/StablePay-MerchantDashboard
Length of output: 129
🏁 Script executed:
# Let's check GitHub Actions documentation behavior and any other workflow files for similar patterns
# First, let's see if there are any other workflow files that use upload-pages-artifact
fd -type f '\.yml$|\.yaml$' .github/workflows/ | head -20Repository: DjedAlliance/StablePay-MerchantDashboard
Length of output: 254
🏁 Script executed:
# Check if there are any issues or comments in the codebase about the out directory
rg "dashboard/out|\.\/out" --type yaml --type mdRepository: DjedAlliance/StablePay-MerchantDashboard
Length of output: 187
The artifact upload path is incorrect and will cause the workflow to fail.
The job sets working-directory: ./dashboard (line 22), but line 62 references path: ./dashboard/out, which will not be found from that context. Since npm run build produces the out/ directory (as configured in next.config.ts with output: 'export'), the path should be:
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./outUsing the relative path ./out matches the working directory context established at the job level and aligns with how the .nojekyll step correctly references ./out/.nojekyll on line 57.
🤖 Prompt for AI Agents
In .github/workflows/nextjs.yml around lines 51 to 63, the Upload artifact step
uses an incorrect path (`./dashboard/out`) while the job's working-directory is
set to ./dashboard; change the artifact path to the relative ./out to match the
working-directory (and keep consistency with the earlier .nojekyll step), i.e.,
update the with.path value to ./out so the artifact upload finds the exported
Next.js output directory.
| // Update stats when transactions change | ||
| useEffect(() => { | ||
| if (hasFetched && transactions.length > 0) { | ||
| setIsLoadingFresh(true); | ||
|
|
||
| // Calculate fresh stats from transactions | ||
| const calculatedStats = calculateStatsFromTransactions(transactions); | ||
| const newStats: OverviewStats = { | ||
| ...calculatedStats, | ||
| lastUpdated: Date.now(), | ||
| }; | ||
|
|
||
| // Update state and cache | ||
| setStats(newStats); | ||
| setCachedStats(newStats); | ||
|
|
||
| // Remove loading indicator after a brief delay for smooth UX | ||
| setTimeout(() => { | ||
| setIsLoadingFresh(false); | ||
| }, 500); | ||
| } | ||
| }, [transactions, hasFetched]); |
There was a problem hiding this comment.
Handle “fetched but empty” transactions (currently leaves stale cache + misleading header).
Because the effect only runs when transactions.length > 0 (Line 42), a successful fetch returning [] won’t update lastUpdated, won’t persist “empty” to cache, and lastUpdatedText stays at “Fetch transactions to get analysis”.
Consider updating on hasFetched regardless of length, and compute stats from an empty array.
- useEffect(() => {
- if (hasFetched && transactions.length > 0) {
+ useEffect(() => {
+ if (hasFetched) {
setIsLoadingFresh(true);
- const calculatedStats = calculateStatsFromTransactions(transactions);
+ const calculatedStats = calculateStatsFromTransactions(transactions ?? []);
const newStats: OverviewStats = {
...calculatedStats,
lastUpdated: Date.now(),
};
setStats(newStats);
setCachedStats(newStats);
// ...
}
}, [transactions, hasFetched]);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Update stats when transactions change | |
| useEffect(() => { | |
| if (hasFetched && transactions.length > 0) { | |
| setIsLoadingFresh(true); | |
| // Calculate fresh stats from transactions | |
| const calculatedStats = calculateStatsFromTransactions(transactions); | |
| const newStats: OverviewStats = { | |
| ...calculatedStats, | |
| lastUpdated: Date.now(), | |
| }; | |
| // Update state and cache | |
| setStats(newStats); | |
| setCachedStats(newStats); | |
| // Remove loading indicator after a brief delay for smooth UX | |
| setTimeout(() => { | |
| setIsLoadingFresh(false); | |
| }, 500); | |
| } | |
| }, [transactions, hasFetched]); | |
| // Update stats when transactions change | |
| useEffect(() => { | |
| if (hasFetched) { | |
| setIsLoadingFresh(true); | |
| // Calculate fresh stats from transactions | |
| const calculatedStats = calculateStatsFromTransactions(transactions ?? []); | |
| const newStats: OverviewStats = { | |
| ...calculatedStats, | |
| lastUpdated: Date.now(), | |
| }; | |
| // Update state and cache | |
| setStats(newStats); | |
| setCachedStats(newStats); | |
| // Remove loading indicator after a brief delay for smooth UX | |
| setTimeout(() => { | |
| setIsLoadingFresh(false); | |
| }, 500); | |
| } | |
| }, [transactions, hasFetched]); |
🤖 Prompt for AI Agents
In dashboard/app/page.tsx around lines 40 to 61, the effect currently only
updates stats when transactions.length > 0 which leaves stale cached stats and
an unchanged lastUpdatedText when a fetch returns an empty array; change the
effect to run whenever hasFetched becomes true (remove the length guard),
compute fresh stats from the transactions array even if empty (use
calculateStatsFromTransactions(transactions)), build newStats with lastUpdated:
Date.now(), setStats(newStats) and setCachedStats(newStats), and still manage
setIsLoadingFresh (set true before computing and clear after the brief timeout)
so an empty successful fetch persists the empty-state stats and updates the
header.
| setTimeout(() => { | ||
| setIsLoadingFresh(false); | ||
| }, 500); | ||
| } | ||
| }, [transactions, hasFetched]); |
There was a problem hiding this comment.
Add setTimeout cleanup to avoid state updates after unmount / overlapping refreshes.
The timeout (Line 57–59) isn’t cleared if the component unmounts or if a second refresh starts before 500ms.
- setTimeout(() => {
- setIsLoadingFresh(false);
- }, 500);
+ const t = window.setTimeout(() => setIsLoadingFresh(false), 500);
+ return () => window.clearTimeout(t);
}
}, [transactions, hasFetched]);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| setTimeout(() => { | |
| setIsLoadingFresh(false); | |
| }, 500); | |
| } | |
| }, [transactions, hasFetched]); | |
| const t = window.setTimeout(() => setIsLoadingFresh(false), 500); | |
| return () => window.clearTimeout(t); | |
| } | |
| }, [transactions, hasFetched]); |
🤖 Prompt for AI Agents
In dashboard/app/page.tsx around lines 57–61, the setTimeout started to flip
setIsLoadingFresh(false) isn’t cleared on unmount or when the effect re-runs;
change the effect to store the timeout ID (use a let/const id or better a useRef
to persist across renders), call clearTimeout(id) before creating a new timeout
and return a cleanup function that clears the timeout so no state update happens
after unmount or overlapping refreshes.
| // Process real transaction data into chart format | ||
| const processTransactionData = React.useMemo(() => { | ||
| if (!transactions || transactions.length === 0) { | ||
| // Return mock data if no transactions | ||
| return { | ||
| week: mockData.chartData.week, | ||
| month: mockData.chartData.month, | ||
| year: mockData.chartData.year, | ||
| }; | ||
| } | ||
|
|
||
| // Group transactions by date | ||
| const groupedByDate: Record<string, { revenue: number; count: number; fees: number }> = {}; | ||
|
|
||
| transactions.forEach((tx) => { | ||
| const date = tx.date || new Date().toISOString().split('T')[0]; | ||
| if (!groupedByDate[date]) { | ||
| groupedByDate[date] = { revenue: 0, count: 0, fees: 0 }; | ||
| } | ||
| const amount = parseFloat(tx.amountBC || '0'); | ||
| groupedByDate[date].revenue += amount; | ||
| groupedByDate[date].count += 1; | ||
| // Estimate fees as 1% of transaction amount | ||
| groupedByDate[date].fees += amount * 0.01; | ||
| }); | ||
|
|
||
| // Convert to array and sort by date | ||
| const sortedData = Object.entries(groupedByDate) | ||
| .map(([date, data]) => ({ | ||
| date, | ||
| revenue: Math.round(data.revenue * 100) / 100, | ||
| transactions: data.count, | ||
| fees: Math.round(data.fees * 100) / 100, | ||
| })) | ||
| .sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()); | ||
|
|
||
| // Filter data based on time period | ||
| const now = new Date(); | ||
| const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); | ||
| const monthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000); | ||
| const yearAgo = new Date(now.getTime() - 365 * 24 * 60 * 60 * 1000); | ||
|
|
||
| return { | ||
| week: sortedData.filter(d => new Date(d.date) >= weekAgo), | ||
| month: sortedData.filter(d => new Date(d.date) >= monthAgo), | ||
| year: sortedData.filter(d => new Date(d.date) >= yearAgo), | ||
| }; | ||
| }, [transactions]); | ||
|
|
There was a problem hiding this comment.
Fix build blocker: TransactionEvent doesn’t have date (use timestamp).
const date = tx.date || ... (Line 72) won’t type-check against TransactionEvent as defined (timestamp?: Date), and will likely break the build.
transactions.forEach((tx) => {
- const date = tx.date || new Date().toISOString().split('T')[0];
+ const date = (tx.timestamp ? new Date(tx.timestamp) : new Date())
+ .toISOString()
+ .slice(0, 10);
if (!groupedByDate[date]) {
groupedByDate[date] = { revenue: 0, count: 0, fees: 0 };
}
const amount = parseFloat(tx.amountBC || '0');
groupedByDate[date].revenue += amount;(Optionally also skip invalid amounts instead of accumulating NaN.)
🤖 Prompt for AI Agents
dashboard/components/dashboard/chart/index.tsx around lines 57 to 105: The code
reads tx.date which doesn't exist on TransactionEvent (it uses timestamp), so
replace usage of tx.date with tx.timestamp (convert to a Date/string as needed)
and ensure you handle undefined timestamp by falling back to new Date(); also
validate/parse tx.amountBC safely (skip or treat as 0 when parseFloat yields
NaN) instead of allowing NaN to propagate into aggregates, and keep fees
computed from the validated numeric amount.
| export function calculateStatsFromTransactions( | ||
| transactions: TransactionEvent[] | ||
| ): Omit<OverviewStats, 'lastUpdated'> { | ||
| const totalTransactions = transactions.length; | ||
|
|
||
| // Calculate total revenue in BC (blockchain currency) | ||
| const totalRevenue = transactions.reduce((sum, tx) => { | ||
| return sum + parseFloat(tx.amountBC); | ||
| }, 0); | ||
|
|
||
| // For blockchain transactions, all fetched transactions are successful | ||
| // In a real-world scenario, you might filter by status | ||
| const successfulTransactions = totalTransactions; | ||
| const successRate = totalTransactions > 0 | ||
| ? (successfulTransactions / totalTransactions) * 100 | ||
| : 0; | ||
|
|
||
| // Blockchain transactions don't have failed or pending states | ||
| // These would come from off-chain data or different contract events | ||
| const failedTransactions = 0; | ||
| const pendingTransactions = 0; | ||
|
|
||
| return { | ||
| transactionsProcessed: totalTransactions, | ||
| revenueGenerated: Math.round(totalRevenue * 100) / 100, // Round to 2 decimals | ||
| successRate: Math.round(successRate * 10) / 10, // Round to 1 decimal | ||
| failedTransactions, | ||
| pendingTransactions, | ||
| }; | ||
| } |
There was a problem hiding this comment.
Guard against NaN when parsing amountBC (NaN will poison totals).
parseFloat(tx.amountBC) (Line 21) can produce NaN, making totalRevenue become NaN.
- const totalRevenue = transactions.reduce((sum, tx) => {
- return sum + parseFloat(tx.amountBC);
- }, 0);
+ const totalRevenue = transactions.reduce((sum, tx) => {
+ const n = Number(tx.amountBC);
+ return sum + (Number.isFinite(n) ? n : 0);
+ }, 0);🤖 Prompt for AI Agents
In dashboard/lib/calculate-stats.ts around lines 14 to 43,
parseFloat(tx.amountBC) can return NaN and poison totalRevenue; fix by
normalizing each tx amount before summing (parseFloat or Number, then check
isNaN) and treat invalid values as 0 (or skip with a debug/log entry), e.g.,
convert, guard with isNaN and fall back to 0, then sum the guarded values and
keep the existing rounding logic for revenueGenerated.
| export function formatRevenue(revenue: number): string { | ||
| if (revenue === 0) return 'T/A'; | ||
|
|
||
| // Format with commas and 2 decimal places | ||
| return revenue.toLocaleString('en-US', { | ||
| minimumFractionDigits: 2, | ||
| maximumFractionDigits: 2, | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Formats success rate for display | ||
| * @param rate - Success rate percentage | ||
| * @returns Formatted string with % symbol | ||
| */ | ||
| export function formatSuccessRate(rate: number): string { | ||
| if (rate === 0) return 'T/A'; | ||
| return `${rate.toFixed(1)}%`; | ||
| } |
There was a problem hiding this comment.
Don’t return “T/A” for zero; let the caller decide NA-vs-zero.
formatRevenue(0) and formatSuccessRate(0) returning 'T/A' (Line 51, Line 66) causes “T/A” to show even after a successful fetch with empty/zero values.
export function formatRevenue(revenue: number): string {
- if (revenue === 0) return 'T/A';
-
// Format with commas and 2 decimal places
return revenue.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
}
export function formatSuccessRate(rate: number): string {
- if (rate === 0) return 'T/A';
return `${rate.toFixed(1)}%`;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export function formatRevenue(revenue: number): string { | |
| if (revenue === 0) return 'T/A'; | |
| // Format with commas and 2 decimal places | |
| return revenue.toLocaleString('en-US', { | |
| minimumFractionDigits: 2, | |
| maximumFractionDigits: 2, | |
| }); | |
| } | |
| /** | |
| * Formats success rate for display | |
| * @param rate - Success rate percentage | |
| * @returns Formatted string with % symbol | |
| */ | |
| export function formatSuccessRate(rate: number): string { | |
| if (rate === 0) return 'T/A'; | |
| return `${rate.toFixed(1)}%`; | |
| } | |
| export function formatRevenue(revenue: number): string { | |
| // Format with commas and 2 decimal places | |
| return revenue.toLocaleString('en-US', { | |
| minimumFractionDigits: 2, | |
| maximumFractionDigits: 2, | |
| }); | |
| } | |
| /** | |
| * Formats success rate for display | |
| * @param rate - Success rate percentage | |
| * @returns Formatted string with % symbol | |
| */ | |
| export function formatSuccessRate(rate: number): string { | |
| return `${rate.toFixed(1)}%`; | |
| } |
🤖 Prompt for AI Agents
In dashboard/lib/calculate-stats.ts around lines 50 to 68, the helper functions
currently return 'T/A' for revenue===0 and rate===0 which forces "T/A" into the
UI; remove those zero-checks so the functions always return a numeric formatted
string (e.g. revenue formatted with commas and two decimals, and success rate
formatted with one decimal and a trailing '%'), and let the caller decide when
to display "T/A" or other NA indicators.
| export function getCachedStats(): OverviewStats { | ||
| if (typeof window === 'undefined') { | ||
| return DEFAULT_STATS; | ||
| } | ||
|
|
||
| try { | ||
| const cached = localStorage.getItem(STATS_CACHE_KEY); | ||
| if (cached) { | ||
| const stats: OverviewStats = JSON.parse(cached); | ||
| return stats; | ||
| } | ||
| } catch (error) { | ||
| console.warn('Failed to parse cached stats:', error); | ||
| } | ||
|
|
||
| return DEFAULT_STATS; | ||
| } |
There was a problem hiding this comment.
Validate/merge cached JSON to avoid runtime errors from corrupted localStorage.
JSON.parse result is trusted as OverviewStats (Line 41–43). A malformed or partial value can break the UI.
if (cached) {
- const stats: OverviewStats = JSON.parse(cached);
- return stats;
+ const parsed: Partial<OverviewStats> = JSON.parse(cached);
+ return {
+ ...DEFAULT_STATS,
+ ...parsed,
+ lastUpdated: Number(parsed.lastUpdated ?? 0) || 0,
+ };
}| ``` | ||
| 1. Component Mount | ||
| ↓ | ||
| 2. Load cached stats from localStorage | ||
| ↓ | ||
| 3. Display cached values (or T/A if no cache) | ||
| ↓ | ||
| 4. When transactions are fetched | ||
| ↓ | ||
| 5. Calculate fresh stats | ||
| ↓ | ||
| 6. Show loading indicator (keeping cached values visible) | ||
| ↓ | ||
| 7. Update UI with fresh data | ||
| ↓ | ||
| 8. Save to localStorage cache | ||
| ``` | ||
|
|
||
| ### Cache Structure | ||
|
|
||
| ```typescript | ||
| interface OverviewStats { | ||
| transactionsProcessed: number; | ||
| revenueGenerated: number; | ||
| successRate: number; | ||
| failedTransactions: number; | ||
| pendingTransactions: number; | ||
| lastUpdated: number; // Unix timestamp in milliseconds | ||
| } | ||
| ``` | ||
|
|
||
| Stored in localStorage as: | ||
| ``` | ||
| Key: 'stablepay_overview_stats' | ||
| Value: JSON string of OverviewStats | ||
| ``` | ||
|
|
There was a problem hiding this comment.
Add languages to fenced code blocks to satisfy markdownlint (MD040).
The two fences around the data-flow and cache-key sections are missing a language spec.
-```
+```text
...
-```
+```
-Stored in localStorage as:
-```
+Stored in localStorage as:
+```text
Key: 'stablepay_overview_stats'
Value: JSON string of OverviewStats
-```
+```🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
47-47: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
79-79: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
dashboard/OVERVIEW_STATS_IMPLEMENTATION.md around lines 47 to 83: the two fenced
code blocks (the data-flow diagram and the "Stored in localStorage as" snippet)
are missing a language spec causing markdownlint MD040; update both opening
fences to include a language (use "text") so they become ```text and keep the
existing closing ``` unchanged.
Updated Next.js workflow to support a dashboard directory and adjusted caching and installation steps accordingly.
🚀 Additional Update: GitHub Pages Deployment IncludedIssue #4 ResolutionThis PR also includes the GitHub Pages deployment configuration that resolves Issue #4. 🔗 Live Deployment: https://manishyad375375.github.io/StablePay-MerchantDashboard/ Deployment Changes Included1. Next.js Configuration (
|
|
@manishyad375375 It would be appreciated if you can add screen recording of the changes metioned |
|
@manishyad375375 Can you resolve merger conflicts and add a video recording of changes you have did. |
|
Sure 👍 |
|
Sure I will do and let you know through mail once it's done.
Regards
Manish
22BAI10283
School of Computing Science engineering and Artificial Intelligence
VIT Bhopal University
Bhopal-Indore Highway, Kothri Kalan
Dist: Sehore., Madhya Pradesh - 466114
Mobile : +91-8307649067
-----------------------------------------------------------------------------------
…-----------------------------------------------------------------------------------
On Tue, Jun 2, 2026, 1:52 PM Amrit Kumar Yadav ***@***.***> wrote:
*DeveloperAmrit* left a comment
(DjedAlliance/StablePay-MerchantDashboard#9)
<#9 (comment)>
@manishyad375375 <https://github.com/manishyad375375> Can you resolve
merger conflicts and add a video recording of changes you have did.
—
Reply to this email directly, view it on GitHub
<#9?email_source=notifications&email_token=A5MNRGZ4LWWQEACJHXLAKLT452FCRA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTINRQGAYTQMBVHA4KM4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJLDGN5XXIZLSL5RWY2LDNM#issuecomment-4600180588>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/A5MNRG6HUDR6XYA4OBJ37D3452FCRAVCNFSM6AAAAACZWYT2JOVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHM2DMMBQGE4DANJYHA>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
📊 Overview
This PR implements localStorage caching for the Overview tab statistics, ensuring that stats display immediately on page load and update gracefully when fresh data is fetched.
✨ Features Implemented
📝 Changes Made
New Files Created
dashboard/lib/stats-cache.tsgetCachedStats(),setCachedStats(),clearStatsCache(),hasCachedStats(),getFormattedUpdateTime()dashboard/lib/calculate-stats.tscalculateStatsFromTransactions(),formatRevenue(),formatSuccessRate()dashboard/OVERVIEW_STATS_IMPLEMENTATION.mdModified Files
dashboard/app/page.tsxdashboard/components/dashboard/chart/index.tsx📊 Stats Displayed
amountBCfrom transactions🔄 Data Flow
🎯 Acceptance Criteria
📦 Cache Structure
Stored in localStorage:
stablepay_overview_stats🔧 Integration
useTransactionshook📚 Documentation
Complete implementation documentation available in
dashboard/OVERVIEW_STATS_IMPLEMENTATION.md🐛 Known Limitations
🔍 Code Quality
windowobject)Implementation Date: December 13, 2025
Status: ✅ Ready for Review
Summary by CodeRabbit
Release Notes
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.