feat: Agent Health page with progressive filtering and GroupCard component

Add URL-driven Agent Health page (/agents, /agents/:appId,
/agents/:appId/:instanceId) that progressively narrows from all
applications to a single instance with trend charts. Create
generic GroupCard composite for grouping instances by application.
Expand mock data to 8 instances across 4 apps with varied states.
Split sidebar Agents header into navigable link + collapse chevron.
Update agent tree paths to /agents/:appId/:instanceId. Add EventFeed
with lifecycle events. Change SidebarAgent.tps from string to number.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-18 18:22:14 +01:00
parent e69e5ab5fe
commit 8f93ea41ed
18 changed files with 990 additions and 380 deletions

55
src/mocks/agentEvents.ts Normal file
View File

@@ -0,0 +1,55 @@
import type { FeedEvent } from '../design-system/composites/EventFeed/EventFeed'
const MINUTE = 60_000
const HOUR = 3_600_000
export const agentEvents: FeedEvent[] = [
{
id: 'evt-1',
severity: 'error',
message: '[notification-hub] notif-1 status changed to DEAD — no heartbeat for 47m',
timestamp: new Date(Date.now() - 47 * MINUTE),
},
{
id: 'evt-2',
severity: 'warning',
message: '[payment-svc] pay-2 status changed to STALE — missed 3 consecutive heartbeats',
timestamp: new Date(Date.now() - 3 * MINUTE),
},
{
id: 'evt-3',
severity: 'success',
message: '[order-service] ord-3 started — instance joined cluster (v3.2.1)',
timestamp: new Date(Date.now() - 2 * HOUR - 15 * MINUTE),
},
{
id: 'evt-4',
severity: 'warning',
message: '[payment-svc] pay-2 error rate elevated: 12 err/h (threshold: 10 err/h)',
timestamp: new Date(Date.now() - 5 * MINUTE),
},
{
id: 'evt-5',
severity: 'running',
message: '[order-service] Route "order-validation" added to ord-1, ord-2, ord-3',
timestamp: new Date(Date.now() - 1 * HOUR - 30 * MINUTE),
},
{
id: 'evt-6',
severity: 'running',
message: '[shipment-svc] Configuration updated — retry policy changed to 3 attempts with exponential backoff',
timestamp: new Date(Date.now() - 4 * HOUR),
},
{
id: 'evt-7',
severity: 'success',
message: '[shipment-svc] ship-1 and ship-2 upgraded to v3.2.0 — rolling restart complete',
timestamp: new Date(Date.now() - 7 * 24 * HOUR),
},
{
id: 'evt-8',
severity: 'error',
message: '[notification-hub] notif-1 failed health check — memory allocation error',
timestamp: new Date(Date.now() - 48 * MINUTE),
},
]

View File

@@ -1,9 +1,10 @@
export interface AgentHealth {
id: string
name: string
appId: string
service: string
version: string
tps: string
tps: number
lastSeen: string
status: 'live' | 'stale' | 'dead'
errorRate?: string
@@ -15,61 +16,48 @@ export interface AgentHealth {
}
export const agents: AgentHealth[] = [
// order-service: 3 instances, all live
{
id: 'prod-1',
name: 'prod-1',
service: 'order-service',
version: 'v3.2.1',
tps: '14.2/s',
lastSeen: '12s ago',
status: 'live',
uptime: '14d 6h',
memoryUsagePct: 62,
cpuUsagePct: 24,
activeRoutes: 3,
totalRoutes: 3,
id: 'ord-1', name: 'ord-1', appId: 'order-service', service: 'order-service', version: 'v3.2.1',
tps: 14.2, lastSeen: '12s ago', status: 'live', uptime: '14d 6h',
memoryUsagePct: 62, cpuUsagePct: 24, activeRoutes: 3, totalRoutes: 3,
},
{
id: 'prod-2',
name: 'prod-2',
service: 'payment-svc',
version: 'v3.2.1',
tps: '11.8/s',
lastSeen: '8s ago',
status: 'live',
errorRate: '3 err/h',
uptime: '14d 6h',
memoryUsagePct: 71,
cpuUsagePct: 31,
activeRoutes: 2,
totalRoutes: 2,
id: 'ord-2', name: 'ord-2', appId: 'order-service', service: 'order-service', version: 'v3.2.1',
tps: 11.8, lastSeen: '8s ago', status: 'live', errorRate: '3 err/h', uptime: '14d 6h',
memoryUsagePct: 71, cpuUsagePct: 31, activeRoutes: 3, totalRoutes: 3,
},
{
id: 'prod-3',
name: 'prod-3',
service: 'shipment-svc',
version: 'v3.2.0',
tps: '12.1/s',
lastSeen: '5s ago',
status: 'live',
uptime: '7d 14h',
memoryUsagePct: 55,
cpuUsagePct: 19,
activeRoutes: 2,
totalRoutes: 2,
id: 'ord-3', name: 'ord-3', appId: 'order-service', service: 'order-service', version: 'v3.2.1',
tps: 8.4, lastSeen: '4s ago', status: 'live', uptime: '2h 15m',
memoryUsagePct: 38, cpuUsagePct: 12, activeRoutes: 3, totalRoutes: 3,
},
// payment-svc: 2 instances, one stale
{
id: 'pay-1', name: 'pay-1', appId: 'payment-svc', service: 'payment-svc', version: 'v3.2.1',
tps: 9.7, lastSeen: '6s ago', status: 'live', uptime: '14d 6h',
memoryUsagePct: 58, cpuUsagePct: 22, activeRoutes: 2, totalRoutes: 2,
},
{
id: 'prod-4',
name: 'prod-4',
service: 'shipment-svc',
version: 'v3.2.0',
tps: '9.1/s',
lastSeen: '3s ago',
status: 'live',
uptime: '7d 14h',
memoryUsagePct: 48,
cpuUsagePct: 15,
activeRoutes: 2,
totalRoutes: 2,
id: 'pay-2', name: 'pay-2', appId: 'payment-svc', service: 'payment-svc', version: 'v3.2.1',
tps: 0.3, lastSeen: '3m ago', status: 'stale', errorRate: '12 err/h', uptime: '14d 6h',
memoryUsagePct: 82, cpuUsagePct: 67, activeRoutes: 1, totalRoutes: 2,
},
// shipment-svc: 2 instances, both live
{
id: 'ship-1', name: 'ship-1', appId: 'shipment-svc', service: 'shipment-svc', version: 'v3.2.0',
tps: 12.1, lastSeen: '5s ago', status: 'live', uptime: '7d 14h',
memoryUsagePct: 55, cpuUsagePct: 19, activeRoutes: 2, totalRoutes: 2,
},
{
id: 'ship-2', name: 'ship-2', appId: 'shipment-svc', service: 'shipment-svc', version: 'v3.2.0',
tps: 9.1, lastSeen: '3s ago', status: 'live', uptime: '7d 14h',
memoryUsagePct: 48, cpuUsagePct: 15, activeRoutes: 2, totalRoutes: 2,
},
// notification-hub: 1 instance, DEAD
{
id: 'notif-1', name: 'notif-1', appId: 'notification-hub', service: 'notification-hub', version: 'v3.1.9',
tps: 0, lastSeen: '47m ago', status: 'dead', errorRate: '0 err/h', uptime: '0',
memoryUsagePct: 0, cpuUsagePct: 0, activeRoutes: 0, totalRoutes: 1,
},
]

View File

@@ -8,7 +8,7 @@ export interface SidebarAgent {
id: string
name: string
status: 'live' | 'stale' | 'dead'
tps: string
tps: number
}
export interface SidebarApp {
@@ -31,8 +31,9 @@ export const SIDEBAR_APPS: SidebarApp[] = [
{ id: 'order-enrichment', name: 'order-enrichment', exchangeCount: 541 },
],
agents: [
{ id: 'prod-1', name: 'prod-1', status: 'live', tps: '14.2/s' },
{ id: 'prod-2', name: 'prod-2', status: 'live', tps: '11.8/s' },
{ id: 'ord-1', name: 'ord-1', status: 'live', tps: 14.2 },
{ id: 'ord-2', name: 'ord-2', status: 'live', tps: 11.8 },
{ id: 'ord-3', name: 'ord-3', status: 'live', tps: 8.4 },
],
},
{
@@ -45,7 +46,8 @@ export const SIDEBAR_APPS: SidebarApp[] = [
{ id: 'payment-validate', name: 'payment-validate', exchangeCount: 498 },
],
agents: [
{ id: 'prod-2', name: 'prod-2', status: 'live', tps: '11.8/s' },
{ id: 'pay-1', name: 'pay-1', status: 'live', tps: 9.7 },
{ id: 'pay-2', name: 'pay-2', status: 'stale', tps: 0.3 },
],
},
{
@@ -58,8 +60,8 @@ export const SIDEBAR_APPS: SidebarApp[] = [
{ id: 'shipment-track', name: 'shipment-track', exchangeCount: 923 },
],
agents: [
{ id: 'prod-3', name: 'prod-3', status: 'live', tps: '12.1/s' },
{ id: 'prod-4', name: 'prod-4', status: 'live', tps: '9.1/s' },
{ id: 'ship-1', name: 'ship-1', status: 'live', tps: 12.1 },
{ id: 'ship-2', name: 'ship-2', status: 'live', tps: 9.1 },
],
},
{
@@ -70,6 +72,8 @@ export const SIDEBAR_APPS: SidebarApp[] = [
routes: [
{ id: 'notification-dispatch', name: 'notification-dispatch', exchangeCount: 471 },
],
agents: [],
agents: [
{ id: 'notif-1', name: 'notif-1', status: 'dead', tps: 0 },
],
},
]