From 5e3e554693195a3f11ca3fc90cdd7020a249c35d Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Sat, 25 Apr 2026 13:12:41 -0400 Subject: [PATCH] feat(ui): migrate health page to design system Monitor archetype Hero gauge + status copy moved into a MonitorCard variant=terminal, preserving the ccs doctor / live-ops aesthetic per locked decision #4. HealthStatsBar replaced by a 4-column KpiRow with severity tones (Checks / Passed / Warnings / Errors). Each HealthGroupSection becomes its own MonitorCard while keeping the existing accordion behavior intact -- so check expansion, sort-by-severity, and live monitoring continue to work unchanged. PageHeader carries the version badge, last-scan relative time, auto-refresh hint, ccs doctor copy action, and refresh button. Pure layout + visual hierarchy migration -- no new metrics, no new features. LOC: 263 -> ~245. --- ui/src/pages/health.tsx | 360 ++++++++++++++++++++-------------------- 1 file changed, 184 insertions(+), 176 deletions(-) diff --git a/ui/src/pages/health.tsx b/ui/src/pages/health.tsx index 5239fd5c..211de300 100644 --- a/ui/src/pages/health.tsx +++ b/ui/src/pages/health.tsx @@ -1,15 +1,31 @@ -import { Button } from '@/components/ui/button'; +import { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + Activity, + AlertTriangle, + CheckCircle2, + Copy, + Cpu, + Info, + RefreshCw, + Terminal, +} from 'lucide-react'; +import { toast } from 'sonner'; import { Badge } from '@/components/ui/badge'; +import { Button } from '@/components/ui/button'; import { Skeleton } from '@/components/ui/skeleton'; -import { RefreshCw, Terminal, Copy, Cpu } from 'lucide-react'; import { HealthGauge } from '@/components/health/health-gauge'; -import { HealthStatsBar } from '@/components/health/health-stats-bar'; import { HealthGroupSection } from '@/components/health/health-group-section'; import { useHealth, type HealthGroup } from '@/hooks/use-health'; import { cn } from '@/lib/utils'; -import { toast } from 'sonner'; -import { useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; +import { PageShell, PageHeader } from '@/components/page-shell'; +import { + MonitorLayout, + KpiRow, + KpiCard, + MonitorGrid, + MonitorCard, +} from '@/components/monitor-layout'; function getOverallStatus(summary: { passed: number; warnings: number; errors: number }) { if (summary.errors > 0) return 'error'; @@ -41,57 +57,16 @@ function sortGroupsByIssues(groups: HealthGroup[]): HealthGroup[] { }); } -function TerminalHeader() { - return ( -
- $ - ccs doctor -
- ); -} - -function LoadingSkeleton() { - return ( -
- {/* Hero skeleton */} -
-
- -
- - - -
-
-
- - {/* Stats skeleton */} - - - {/* Groups skeleton */} -
- {[1, 2, 3, 4].map((i) => ( - - ))} -
-
- ); -} - export function HealthPage() { const { t } = useTranslation(); const { data, isLoading, refetch, dataUpdatedAt } = useHealth(); - // Use dataUpdatedAt directly instead of storing in state - const lastRefresh = dataUpdatedAt; - - // Update relative time display by forcing re-render every second + // Force re-render every second so the relative-time label stays fresh. const [tick, setTick] = useState(0); useEffect(() => { - const interval = setInterval(() => setTick((t) => t + 1), 1000); + const interval = setInterval(() => setTick((n) => n + 1), 1000); return () => clearInterval(interval); }, []); - // Consume tick to prevent unused variable warning void tick; const copyDoctorCommand = () => { @@ -105,94 +80,46 @@ export function HealthPage() { }; if (isLoading && !data) { - return ; + return ; } const overallStatus = data ? getOverallStatus(data.summary) : 'ok'; const sortedGroups = data?.groups ? sortGroupsByIssues(data.groups) : []; return ( -
- {/* Hero Section - Terminal-inspired control center header */} -
- {/* Subtle scan lines effect */} -
- - {/* Grid pattern background */} -
-
-
- -
- {/* Left: Health Gauge - excludes info from percentage */} - {data && ( -
- -
- )} - - {/* Center: Title and status */} -
- {/* Terminal prompt */} - - - {/* Main title */} -
-

- {t('health.systemHealth')} -

- {data?.version && ( - - {t('health.build', { version: data.version })} - - )} -
- - {/* Status message */} -
- - {t('health.lastScan')} - - {lastRefresh ? formatRelativeTime(lastRefresh, t) : '--'} - - | - {t('health.autoRefresh')} - 30s -
-
- - {/* Right: Actions */} -
+ + + {t('health.systemHealth')} + {data?.version && ( + + {t('health.build', { version: data.version })} + + )} + + } + description={ + + + {t('health.lastScan')} + {dataUpdatedAt ? formatRelativeTime(dataUpdatedAt, t) : '--'} + | + {t('health.autoRefresh')} + 30s + + } + actions={ + <> + + } + /> + + + } + /> + } + /> + 0 ? 'warning' : 'default'} + icon={} + /> + 0 ? 'negative' : 'default'} + icon={} + /> + + ) + } + > + + {/* Hero: overall health gauge with terminal aesthetic */} + {data && ( + + $ + ccs doctor + + } + meta={data.summary.info > 0 ? `${data.summary.info} info` : undefined} + > +
+ +
+

Status

+

+ {overallStatus === 'ok' && 'All systems nominal'} + {overallStatus === 'warning' && 'Attention required'} + {overallStatus === 'error' && 'Errors detected'} +

+

+ {data.summary.passed}/{data.summary.total - data.summary.info} non-informational + checks passing +

+
+
+
+ )} + + {/* Each health group becomes its own MonitorCard */} + {sortedGroups.map((group, index) => ( + + c.status === 'error' || c.status === 'warning') + } + /> + + ))} +
+ + {/* Footer */} +
+
+ + {t('health.version')} {data?.version ?? '--'} + + + {t('health.platform')}{' '} + + {typeof navigator !== 'undefined' ? navigator.platform : 'linux'} + + +
+
+
+ {t('health.liveMonitoring')}
-
+
+
+ ); +} - {/* Stats Bar */} - {data && ( -
- -
- )} - - {/* Health Check Groups - Single column layout */} - {sortedGroups.length > 0 && ( -
- {sortedGroups.map((group, index) => ( - c.status === 'error' || c.status === 'warning') - } - /> +function HealthLoadingSkeleton() { + return ( + + } /> + + {[1, 2, 3, 4].map((i) => ( + + ))} + + } + > + + + + + {[1, 2, 3].map((i) => ( + + + ))} -
- )} - - {/* Footer metadata */} -
-
- - {t('health.version')} {data?.version ?? '--'} - - - {t('health.platform')}{' '} - - {typeof navigator !== 'undefined' ? navigator.platform : 'linux'} - - -
-
-
- {t('health.liveMonitoring')} -
-
-
+ + + ); }