From a06808a2a2b6d0abee5dccd0a0e82fceb652e50c Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Mon, 23 Mar 2026 21:50:16 +0100 Subject: [PATCH] fix: align AgentHealth page with mock design - DetailPanel: switch from tabs to flat children layout (fixes stale tab state bug), add position:fixed override, key on agent id - Stat strip: colored status breakdown (live/stale/dead), msg/s detail on TPS, "requires attention" on dead count - Scope trail: simplified to "X/Y live" label - Event card header: rename "Event Log" to "Timeline" with count badge - Remove unused Breadcrumb, scopeItems, groupHealth Co-Authored-By: Claude Opus 4.6 (1M context) --- .../pages/AgentHealth/AgentHealth.module.css | 33 +++++++++ ui/src/pages/AgentHealth/AgentHealth.tsx | 72 +++++++------------ 2 files changed, 60 insertions(+), 45 deletions(-) diff --git a/ui/src/pages/AgentHealth/AgentHealth.module.css b/ui/src/pages/AgentHealth/AgentHealth.module.css index f5e220a9..0c7fa67f 100644 --- a/ui/src/pages/AgentHealth/AgentHealth.module.css +++ b/ui/src/pages/AgentHealth/AgentHealth.module.css @@ -244,3 +244,36 @@ font-size: 12px; color: var(--text-muted); } + +/* Status breakdown in stat card */ +.statusBreakdown { + display: flex; + gap: 8px; + font-size: 11px; +} + +.statusLive { color: var(--success); } +.statusStale { color: var(--warning); } +.statusDead { color: var(--error); } + +/* Scope trail */ +.scopeLabel { + font-size: 13px; + font-weight: 600; + color: var(--text-secondary); +} + +/* DetailPanel override */ +.detailPanelOverride { + position: fixed; + top: 0; + right: 0; + height: 100vh; + z-index: 100; + box-shadow: -4px 0 24px rgba(0, 0, 0, 0.12); +} + +.panelDivider { + border-top: 1px solid var(--border-subtle); + margin: 16px 0; +} diff --git a/ui/src/pages/AgentHealth/AgentHealth.tsx b/ui/src/pages/AgentHealth/AgentHealth.tsx index 19d69ad1..de3b217d 100644 --- a/ui/src/pages/AgentHealth/AgentHealth.tsx +++ b/ui/src/pages/AgentHealth/AgentHealth.tsx @@ -2,7 +2,7 @@ import { useMemo, useState } from 'react'; import { useParams, useNavigate } from 'react-router'; import { StatCard, StatusDot, Badge, MonoText, - GroupCard, EventFeed, Breadcrumb, Alert, + GroupCard, EventFeed, Alert, DetailPanel, ProgressBar, LineChart, } from '@cameleer/design-system'; import styles from './AgentHealth.module.css'; @@ -189,24 +189,6 @@ export default function AgentHealth() { const activeRoutes = (agents || []).filter((a: any) => a.status === 'LIVE').reduce((sum: number, a: any) => sum + (a.activeRoutes || 0), 0); const totalTps = (agents || []).filter((a: any) => a.status === 'LIVE').reduce((sum: number, a: any) => sum + (a.tps || 0), 0); - const groupHealth: 'live' | 'stale' | 'dead' = useMemo(() => { - if (!appId) return 'live'; - const groupAgents = agentsByApp[appId] || []; - if (groupAgents.some((a: any) => a.status === 'DEAD')) return 'dead'; - if (groupAgents.some((a: any) => a.status === 'STALE')) return 'stale'; - return 'live'; - }, [appId, agentsByApp]); - - const scopeItems = useMemo(() => { - const items: { label: string; href?: string }[] = [ - { label: 'Agent Health', href: '/agents' }, - ]; - if (appId) { - items.push({ label: appId }); - } - return items; - }, [appId]); - const feedEvents = useMemo(() => (events || []).map((e: any) => ({ id: String(e.id), @@ -225,22 +207,25 @@ export default function AgentHealth() { return (
- + + {liveCount} live + {staleCount} stale + {deadCount} dead + + } + /> - - 0 ? 'error' : undefined} /> + + 0 ? 'error' : undefined} detail={deadCount > 0 ? 'requires attention' : undefined} />
- - {!appId && } - {appId && ( - - )} + {liveCount}/{(agents || []).length} live
@@ -349,29 +334,26 @@ export default function AgentHealth() { {feedEvents.length > 0 && (
-
Event Log
+
+ Timeline + +
)} {selectedAgent && ( setSelectedAgent(null)} - tabs={[ - { - label: 'Overview', - value: 'overview', - content: , - }, - { - label: 'Performance', - value: 'performance', - content: , - }, - ]} - /> + className={styles.detailPanelOverride} + > + +
+ + )}
);