[PULSE-54] Analytics chart improvements and live dashboard #24
Reference in New Issue
Block a user
No description provided.
Delete Branch "staging"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Work Item
PULSE-54
Summary
Changes
components/dashboard/Chart.tsx: Metric label above chart; Y-axis layout (24px margin/width); compactformatAxisDurationfor ticks; sparklines;lastUpdatedAtprop for "Live · Xs ago" with green dot; shortened Compare label; trend context always shown; export chart button (html-to-image).app/sites/[id]/page.tsx:loadData(silent)for 30s polling;lastUpdatedAtstate and 1s tick; passlastUpdatedAtto Chart; removed header polling text.app/share/[id]/page.tsx:loadDashboard(true)in 30s interval;lastUpdatedAtstate and 1s tick; passlastUpdatedAtto Chart; removed header polling text.lib/utils/format.ts: AddedformatUpdatedAgo()for polling copy.package.json: Addedhtml-to-image.CHANGELOG.md: 0.5.0-alpha entry.Test Plan
Greptile Overview
Greptile Summary
This PR implements real-time dashboard updates with a 30-second polling interval and live status indicators. The chart component receives major UX enhancements including sparklines in KPI cards, a "Live · Xs ago" indicator with animated green dot, improved empty states, and PNG export functionality via
html-to-image. The dashboard pages (app/sites/[id]/page.tsxandapp/share/[id]/page.tsx) now support silent data refreshing to avoid loading spinners during background updates.Key Changes:
html-to-image@1.11.13for chart export featureIssues Found:
useEffecthooks (lines 134-139 in sites page, 84-93 in share page), causing stale closures that may reference outdated state or captured valuesConfidence Score: 3/5
app/sites/[id]/page.tsxandapp/share/[id]/page.tsx- fix the useEffect dependency arrays to includeloadDataandloadRealtimefunctionsImportant Files Changed
Sequence Diagram
7 files reviewed, 3 comments
Edit Code Review Agent Settings | Greptile
@@ -78,23 +80,13 @@ export default function PublicDashboardPage() {}Missing
loadDashboardandloadRealtimein dependency array causes stale closure. The interval callback captures old function references, which may reference outdated state or props.Prompt To Fix With AI
@@ -130,3 +131,2 @@loadData()}if (isSettingsLoaded) loadData()const interval = setInterval(() => {Missing
loadDataandloadRealtimein dependency array causes stale closure. The interval callback captures old function references.Prompt To Fix With AI
Additional Comments (1)
components/dashboard/Chart.tsxAverage line label can overflow outside chart area with long duration strings. Consider using
insideTopRightposition or truncating the label.Prompt To Fix With AI
@@ -78,23 +80,13 @@ export default function PublicDashboardPage() {}Issue: The interval callback captured old loadDashboard and loadRealtime references, causing stale closures as state/props changed.
Fix: Wrapped both functions in useCallback and added them to the effect dependency array.
Why: Ensures the interval always calls the latest functions that reflect current state and props.
@@ -130,3 +131,2 @@loadData()}if (isSettingsLoaded) loadData()const interval = setInterval(() => {Issue: The interval callback captured old loadData and loadRealtime references.
Fix: Both loadData and loadRealtime are memoized with useCallback and included in the effect dependency array.
Why: Keeps the interval callback in sync with the latest loader implementations and current dependencies.
Greptile Overview
Greptile Summary
Implements live dashboard with 30-second auto-refresh for charts and KPIs, plus real-time "Live · Xs ago" indicator. Chart improvements include sparklines in KPI cards, better empty states, export-to-PNG functionality, clearer comparison labels, and improved mobile/accessibility support.
Key changes:
html-to-imagelibraryIssues found:
loadData,loadDashboard, andloadRealtimefunctions are NOT wrapped inuseCallback, causing stale closure bugs in interval callbacks. WhendateRange,todayInterval, ormultiDayIntervalchange, the interval continues calling old function versions with outdated dependencies.Confidence Score: 2/5
useCallback, but the current code shows no evidence of these fixes. The polling intervals will continue calling functions with stale dependencies (olddateRange,todayInterval,multiDayIntervalvalues), causing incorrect data fetching after user changes settings.app/sites/[id]/page.tsxandapp/share/[id]/page.tsx- both needuseCallbackwrappers for data loading functions to prevent stale closures in polling intervalsImportant Files Changed
Sequence Diagram
7 files reviewed, 5 comments
Edit Code Review Agent Settings | Greptile
@@ -3,3 +3,3 @@import { useAuth } from '@/lib/auth/context'import { useEffect, useState } from 'react'import { useCallback, useEffect, useState } from 'react'import { useParams, useRouter } from 'next/navigation'Import
useCallbackfrom react to fix stale closure issues in interval callbacks.Prompt To Fix With AI
Additional Comments (4)
app/sites/[id]/page.tsxPrevious review thread claimed
loadDatawas wrapped inuseCallbackto fix stale closures, but the current code doesn't show this. The interval callback at line 135 still captures the initialloadDatareference.Prompt To Fix With AI
app/sites/[id]/page.tsxMissing
useCallbackwrapper despite previous thread claiming fix. The interval needs the latest reference.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI
app/share/[id]/page.tsxPrevious thread claimed
loadDashboardwas wrapped inuseCallback, but it's not present in current code. Interval at line 87 captures stale reference.Prompt To Fix With AI
app/share/[id]/page.tsxMissing
useCallbackwrapper for consistency with claimed fix in previous thread.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI
@@ -3,3 +3,3 @@import { useAuth } from '@/lib/auth/context'import { useEffect, useState } from 'react'import { useCallback, useEffect, useState } from 'react'import { useParams, useRouter } from 'next/navigation'Already addressed in the current code. Fixed.