diff --git a/frontends/dbal/src/ServerStatusPanel.tsx b/frontends/dbal/src/ServerStatusPanel.tsx new file mode 100644 index 000000000..ed273bce3 --- /dev/null +++ b/frontends/dbal/src/ServerStatusPanel.tsx @@ -0,0 +1,106 @@ +"use client" + +import { useEffect, useMemo, useState } from 'react' +import type { ServerHealth, StatusResponse } from './status' + +const statusPalette: Record = { + online: 'bg-emerald-500/90', + degraded: 'bg-amber-500/80', + offline: 'bg-rose-500/80', +} + +export function ServerStatusPanel() { + const [health, setHealth] = useState([]) + const [lastUpdated, setLastUpdated] = useState('') + const [error, setError] = useState(null) + const [loading, setLoading] = useState(true) + + useEffect(() => { + let cancelled = false + + const fetchStatus = async () => { + try { + const response = await fetch('/api/status', { cache: 'no-store' }) + if (!response.ok) { + throw new Error('Status endpoint failed') + } + + const data: StatusResponse = await response.json() + if (cancelled) return + + setHealth(data.statuses) + setLastUpdated(data.updatedAt) + setError(null) + } catch (err) { + if (cancelled) return + setError(err instanceof Error ? err.message : 'Unable to load status') + } finally { + if (!cancelled) setLoading(false) + } + } + + void fetchStatus() + + const refreshInterval = setInterval(fetchStatus, 30_000) + return () => { + cancelled = true + clearInterval(refreshInterval) + } + }, []) + + const summary = useMemo(() => { + if (error) { + return 'Status unavailable right now' + } + + if (health.length === 0) { + return 'Initializing status feed...' + } + + const degraded = health.some(item => item.status !== 'online') + return degraded ? 'Some systems need attention' : 'All systems nominal' + }, [health, error]) + + return ( +
+
+

Server status

+

Observability Feed

+

{summary}

+
+ +
+ {loading && health.length === 0 ? ( +
Loading status...
+ ) : error ? ( +
+

{error}

+

Try refreshing the page in a few moments.

+
+ ) : ( + health.map(item => ( +
+
+
+ +

{item.name}

+
+ + {item.status} + +
+

{item.message}

+
+ {item.latencyMs != null ? `${item.latencyMs.toFixed(0)} ms` : 'Latency unknown'} + Updated {lastUpdated ? new Date(lastUpdated).toLocaleTimeString() : '—'} +
+
+ )) + )} +
+
+ ) +}