[PULSE-47] Add uptime monitoring dashboard #15
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-47
Summary
Changes
lib/api/uptime.ts— TypeScript API client with types for monitors, checks, daily stats, and status response; functions for all uptime endpointsapp/sites/[id]/uptime/page.tsx— Full uptime page:https:///http://) + domain prefix URL inputapp/sites/[id]/page.tsx— Added "Uptime" navigation button to dashboard headerTest Plan
/api/health— verify full URL is constructed correctlyGreptile Overview
Greptile Summary
This PR implements a comprehensive uptime monitoring dashboard for PULSE-47, adding the ability to track endpoint availability and response times over a 90-day period.
Key Changes
lib/api/uptime.ts): Added TypeScript client with well-defined types for monitors, checks, and daily stats. All API functions follow existing patterns in the codebase.app/sites/[id]/uptime/page.tsx): Implemented full-featured monitoring page with:app/sites/[id]/page.tsx): Added "Uptime" button to site dashboard header for quick accessCode Quality
Minor Issues Found
useEffect(line 906) - should include all dependenciesconfirm()dialog instead of custom modal for delete confirmationConfidence Score: 4/5
Important Files Changed
Sequence Diagram
3 files reviewed, 3 comments
Edit Code Review Agent Settings | Greptile
@@ -0,0 +1,1055 @@'use client'use
window.confirminstead of nativeconfirmfor better compatibility and clarityNote: 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
@@ -0,0 +637,4 @@}}, 30000)return () => clearInterval(interval)}, [siteId])auto-refresh will silently fail if user loses network connection or auth token expires, leaving stale data visible without indication to user. Consider adding visual feedback or error handling
Prompt To Fix With AI
@@ -0,0 +907,4 @@setFormData({ ...formData, url: `${protocol}${siteDomain}` })}// eslint-disable-next-line react-hooks/exhaustive-deps}, [])disabling exhaustive-deps can lead to stale closure bugs
Prompt To Fix With AI
@@ -0,0 +907,4 @@setFormData({ ...formData, url: `${protocol}${siteDomain}` })}// eslint-disable-next-line react-hooks/exhaustive-deps}, [])Already fixed in the backend
Greptile Overview
Greptile Summary
This PR adds an uptime monitoring dashboard under
/sites/:id/uptime, including an overall status card, per-monitor 90‑day status bars with hover tooltips, expandable response-time charts + recent checks, and monitor CRUD modals with a protocol dropdown + site-domain-prefixed URL builder. It also adds an "Uptime" navigation button to the main site dashboard header.Main issues to address before merge are in the new uptime page: it assumes
uptimeData.monitorsis always an array, but the API client types it as nullable and the current?? []usage won’t preventnullfrom flowing intomonitors.length/map, which can crash rendering. Separately, the status bar hover logic clears the wrong state on mouse leave, causing the tooltip to remain visible after hover ends.Confidence Score: 3/5
monitors: nullcan crash rendering and the tooltip hover state currently leaves tooltips stuck open; both are straightforward fixes but should be addressed before merging.Important Files Changed
monitorsis typed nullable which the UI must handle safely.monitors: nulland a tooltip hover-state bug that leaves tooltips stuck open.Sequence Diagram
@@ -0,0 +637,4 @@}}, 30000)return () => clearInterval(interval)}, [siteId])Issue: Auto-refresh could fail silently on network loss or auth expiry, leaving stale data with no indication.
Fix: On refresh failure we now call toast.error(...) so the user sees “Could not refresh uptime data. Check your connection or sign in again.”
Why: Users need feedback when background refresh fails so they know data may be stale and can reconnect or re-auth.
3 files reviewed, 2 comments
Edit Code Review Agent Settings | Greptile
@@ -0,0 +1,1055 @@'use client'Null monitors breaks render
UptimeStatusResponse.monitorsis typed asMonitorStatus[] | null(seelib/api/uptime.ts), but the page doesconst monitors = uptimeData?.monitors ?? []and then callsmonitors.length/monitors.map(...). If the API returnsmonitors: null, this becomesnull(because??doesn’t coalesce inside the optional chain) and will throw at render.Also appears at
app/sites/[id]/uptime/page.tsx:749(usesmonitors.length > 0).Prompt To Fix With AI
@@ -0,0 +248,4 @@const handleMouseEnter = (e: React.MouseEvent, date: string, stat: UptimeDailyStat | undefined) => {const rect = (e.target as HTMLElement).getBoundingClientRect()setTooltipPos({ x: rect.left + rect.width / 2, y: rect.top })setHoveredDay({ date, stat })Tooltip never clears
onMouseLeaveonly clearshoveredDay, butStatusBarTooltipusesvisible={hoveredDay !== null}so the tooltip stays stuck open after hover ends. This will be reproducible whenever you move the mouse off a day bar.Also appears at
app/sites/[id]/uptime/page.tsx:266-276(same hover state).Prompt To Fix With AI
@@ -0,0 +1,1055 @@'use client'Issue: Using bare confirm was flagged for compatibility/clarity.
Fix: Replaced with window.confirm(...) so the global is explicit.
Why: Explicit window.confirm is clearer and avoids any non-browser or strict global assumptions.
@@ -0,0 +1,1055 @@'use client'Issue: monitors can be null; using ?? [] was flagged as potentially leaving a non-array.
Fix: Use Array.isArray(uptimeData?.monitors) ? uptimeData.monitors : [] so we always render with an array.
Why: Ensures .length and .map() are safe regardless of API response.
@@ -0,0 +248,4 @@const handleMouseEnter = (e: React.MouseEvent, date: string, stat: UptimeDailyStat | undefined) => {const rect = (e.target as HTMLElement).getBoundingClientRect()setTooltipPos({ x: rect.left + rect.width / 2, y: rect.top })setHoveredDay({ date, stat })Issue: Tooltip could stay visible after hover ended.
Fix: Added onMouseLeave on the status bar container to clear hoveredDay when the pointer leaves the whole block; bars still clear on their own onMouseLeave.
Why: Clearing at the container level guarantees the tooltip hides when the cursor leaves the component, including when moving over gaps or the fixed tooltip.
Greptile Overview
Greptile Summary
This PR adds an uptime monitoring dashboard under
app/sites/[id]/uptime/page.tsx, backed by a new uptime API client (lib/api/uptime.ts) and a new "Uptime" navigation button from the site dashboard (app/sites/[id]/page.tsx). The uptime page fetches site + uptime status, renders per-monitor 90‑day status bars with hover tooltips, expands monitors to show response-time charts and recent checks, and provides monitor CRUD via modals.Key integration points:
useAuth) for edit permissions.apiRequestclient and existinggetSiteAPI.@ciphera-net/uiplus Recharts for charting.There are a couple of merge-blocking runtime issues to address in the uptime page: handling
monitors: nullsafely during render, and preventing the URL protocol dropdown state from getting out of sync when editing an existing monitor URL.Confidence Score: 3/5
monitorsas always an array even though it is typed as nullable, which can throw at render, and the monitor form’s protocol dropdown can become inconsistent with an edited monitor’s existing URL, causing silent URL rewrites during edits.Important Files Changed
/sites/:id/uptime; no functional issues found in the change.Sequence Diagram
3 files reviewed, 2 comments
Edit Code Review Agent Settings | Greptile
@@ -0,0 +1,1055 @@'use client'Protocol state can desync
protocolis initialized fromformData.urlonce, but when opening the edit modal you setformData.urlto an existing monitor URL without updatingprotocol. If the saved monitor URL ishttp://...andprotocolremainshttps://, any path edit or protocol toggle rebuild will silently rewrite the URL with the wrong scheme. DeriveprotocolfromformData.url(or sync it in an effect whenformData.urlchanges) so editing preserves the monitor’s actual scheme.Prompt To Fix With AI
@@ -0,0 +706,4 @@if (loading) return <LoadingOverlay logoSrc="/pulse_icon_no_margins.png" title="Uptime" />if (!site) return <div className="p-8 text-neutral-500">Site not found</div>monitorscan benullUptimeStatusResponse.monitorsis typed asMonitorStatus[] | null(lib/api/uptime.ts), but this page still doesconst monitors = uptimeData?.monitors ?? []. If the API returnsmonitors: null, this expression evaluates tonull(because the optional chain returnsnull, which??does not replace), and the subsequentmonitors.length/monitors.map(...)will throw during render. Use an explicit array guard (e.g.,Array.isArray(uptimeData?.monitors) ? uptimeData.monitors : []) before reading.length/.map.Prompt To Fix With AI
@@ -0,0 +1,1055 @@'use client'Issue: protocol was initialized once from formData.url, so when opening edit with a monitor that had http://, protocol could still be https:// and path edits used the wrong scheme.
Fix: Protocol is now derived from formData.url at render time (formData.url.startsWith('http://') ? 'http://' : 'https://'); protocol state was removed and handleProtocolChange only updates formData.url.
Why: Editing must reflect the saved monitor’s scheme; deriving from formData.url keeps the dropdown and URL in sync whenever the parent sets formData (e.g. when opening edit).
@@ -0,0 +706,4 @@if (loading) return <LoadingOverlay logoSrc="/pulse_icon_no_margins.png" title="Uptime" />if (!site) return <div className="p-8 text-neutral-500">Site not found</div>alreayd fixed
Greptile Overview
Greptile Summary
This PR adds a comprehensive uptime monitoring dashboard with 90-day status visualization, response time charts, and full CRUD operations for monitors. The implementation includes:
lib/api/uptime.ts) with properly typed interfaces for monitors, checks, daily stats, and status responsesKey strengths:
Confidence Score: 4/5
Important Files Changed
Sequence Diagram