import { useState, useMemo } from 'react' import { useParams, Link } from 'react-router-dom' import { ChevronRight } from 'lucide-react' import styles from './AgentHealth.module.css' // Layout import { TopBar } from '../../design-system/layout/TopBar/TopBar' // Composites import { GroupCard } from '../../design-system/composites/GroupCard/GroupCard' import { DataTable } from '../../design-system/composites/DataTable/DataTable' import { LineChart } from '../../design-system/composites/LineChart/LineChart' import { EventFeed } from '../../design-system/composites/EventFeed/EventFeed' import { DetailPanel } from '../../design-system/composites/DetailPanel/DetailPanel' import type { Column } from '../../design-system/composites/DataTable/types' // Primitives import { StatusDot } from '../../design-system/primitives/StatusDot/StatusDot' import { MonoText } from '../../design-system/primitives/MonoText/MonoText' import { Badge } from '../../design-system/primitives/Badge/Badge' import { StatCard } from '../../design-system/primitives/StatCard/StatCard' import { ProgressBar } from '../../design-system/primitives/ProgressBar/ProgressBar' // Global filters import { useGlobalFilters } from '../../design-system/providers/GlobalFilterProvider' // Mock data import { agents, type AgentHealth as AgentHealthData } from '../../mocks/agents' import { agentEvents } from '../../mocks/agentEvents' // ── URL scope parsing ──────────────────────────────────────────────────────── type Scope = | { level: 'all' } | { level: 'app'; appId: string } function useScope(): Scope { const { '*': rest } = useParams() const segments = rest?.split('/').filter(Boolean) ?? [] if (segments.length >= 1) return { level: 'app', appId: segments[0] } return { level: 'all' } } // ── Data grouping ──────────────────────────────────────────────────────────── interface AppGroup { appId: string instances: AgentHealthData[] liveCount: number staleCount: number deadCount: number totalTps: number totalActiveRoutes: number totalRoutes: number } function groupByApp(agentList: AgentHealthData[]): AppGroup[] { const map = new Map() for (const a of agentList) { const list = map.get(a.appId) ?? [] list.push(a) map.set(a.appId, list) } return Array.from(map.entries()).map(([appId, instances]) => ({ appId, instances, liveCount: instances.filter((i) => i.status === 'live').length, staleCount: instances.filter((i) => i.status === 'stale').length, deadCount: instances.filter((i) => i.status === 'dead').length, totalTps: instances.reduce((s, i) => s + i.tps, 0), totalActiveRoutes: instances.reduce((s, i) => s + i.activeRoutes, 0), totalRoutes: instances.reduce((s, i) => s + i.totalRoutes, 0), })) } function appHealth(group: AppGroup): 'success' | 'warning' | 'error' { if (group.deadCount > 0) return 'error' if (group.staleCount > 0) return 'warning' return 'success' } // ── Trend data (mock) ──────────────────────────────────────────────────────── function buildTrendData(agent: AgentHealthData) { const now = Date.now() const points = 20 const interval = (3 * 60 * 60 * 1000) / points const throughput = Array.from({ length: points }, (_, i) => ({ x: new Date(now - (points - i) * interval), y: Math.max(0, agent.tps + (Math.random() - 0.5) * 4), })) const errorRate = Array.from({ length: points }, (_, i) => ({ x: new Date(now - (points - i) * interval), y: Math.max(0, (agent.errorRate ? parseFloat(agent.errorRate) : 0.5) + (Math.random() - 0.5) * 2), })) return { throughput, errorRate } } // ── Breadcrumb ─────────────────────────────────────────────────────────────── function buildBreadcrumb(scope: Scope) { const crumbs: { label: string; href?: string }[] = [ { label: 'Applications', href: '/apps' }, { label: 'Agents', href: '/agents' }, ] if (scope.level === 'app') { crumbs.push({ label: scope.appId }) } return crumbs } // ── AgentHealth page ───────────────────────────────────────────────────────── export function AgentHealth() { const scope = useScope() const { isInTimeRange } = useGlobalFilters() const [selectedInstance, setSelectedInstance] = useState(null) const [panelOpen, setPanelOpen] = useState(false) // Filter agents by scope const filteredAgents = useMemo(() => { if (scope.level === 'all') return agents return agents.filter((a) => a.appId === scope.appId) }, [scope]) const groups = useMemo(() => groupByApp(filteredAgents), [filteredAgents]) // Aggregate stats const totalInstances = filteredAgents.length const liveCount = filteredAgents.filter((a) => a.status === 'live').length const staleCount = filteredAgents.filter((a) => a.status === 'stale').length const deadCount = filteredAgents.filter((a) => a.status === 'dead').length const totalTps = filteredAgents.reduce((s, a) => s + a.tps, 0) const totalActiveRoutes = filteredAgents.reduce((s, a) => s + a.activeRoutes, 0) const totalRoutes = filteredAgents.reduce((s, a) => s + a.totalRoutes, 0) // Filter events by global time range const filteredEvents = agentEvents.filter((e) => isInTimeRange(e.timestamp)) // Build trend data for selected instance const trendData = selectedInstance ? buildTrendData(selectedInstance) : null // Column definitions for the instance DataTable const instanceColumns: Column[] = useMemo(() => [ { key: 'status', header: '', width: '12px', render: (_val, row) => ( ), }, { key: 'name', header: 'Instance', render: (_val, row) => ( {row.name} ), }, { key: 'state', header: 'State', render: (_val, row) => ( ), }, { key: 'uptime', header: 'Uptime', render: (_val, row) => ( {row.uptime} ), }, { key: 'tps', header: 'TPS', render: (_val, row) => ( {row.tps.toFixed(1)}/s ), }, { key: 'errorRate', header: 'Errors', render: (_val, row) => ( {row.errorRate ?? '0 err/h'} ), }, { key: 'lastSeen', header: 'Heartbeat', render: (_val, row) => ( {row.lastSeen} ), }, ], []) function handleInstanceClick(inst: AgentHealthData) { setSelectedInstance(inst) setPanelOpen(true) } // Detail panel tabs const detailTabs = selectedInstance ? [ { label: 'Overview', value: 'overview', content: (
Status
Application {selectedInstance.appId}
Version {selectedInstance.version}
Uptime {selectedInstance.uptime}
Last Seen {selectedInstance.lastSeen}
Throughput {selectedInstance.tps.toFixed(1)}/s
Errors {selectedInstance.errorRate ?? '0 err/h'}
Routes {selectedInstance.activeRoutes}/{selectedInstance.totalRoutes} active
Memory
85 ? 'error' : selectedInstance.memoryUsagePct > 70 ? 'warning' : 'success'} /> {selectedInstance.memoryUsagePct}%
CPU
85 ? 'error' : selectedInstance.cpuUsagePct > 70 ? 'warning' : 'success'} /> {selectedInstance.cpuUsagePct}%
), }, { label: 'Performance', value: 'performance', content: trendData ? (
Throughput (msg/s)
Error Rate (err/h)
) : null, }, ] : [] const isFullWidth = scope.level !== 'all' return ( <>
{/* Stat strip */}
0 ? 'warning' : 'amber'} detail={ {liveCount} live {staleCount} stale {deadCount} dead } /> {groups.filter((g) => g.deadCount === 0 && g.staleCount === 0).length} healthy {groups.filter((g) => g.staleCount > 0 && g.deadCount === 0).length} degraded {groups.filter((g) => g.deadCount > 0).length} critical } /> {totalActiveRoutes}/{totalRoutes}} accent={totalActiveRoutes === 0 ? 'error' : totalActiveRoutes < totalRoutes ? 'warning' : 'success'} detail={totalActiveRoutes < totalRoutes ? `${totalRoutes - totalActiveRoutes} suspended` : 'all routes active'} /> 0 ? 'error' : 'success'} detail={deadCount > 0 ? 'requires attention' : 'all healthy'} />
{/* Scope trail + badges */}
{scope.level !== 'all' && ( <> All Agents {scope.appId} )} 0 ? 'error' : staleCount > 0 ? 'warning' : 'success'} variant="filled" />
{/* Group cards grid */}
{groups.map((group) => ( } meta={
{group.totalTps.toFixed(1)} msg/s {group.totalActiveRoutes}/{group.totalRoutes} routes
} footer={group.deadCount > 0 ? (
Single point of failure — {group.deadCount === group.instances.length ? 'no redundancy' : `${group.deadCount} dead instance${group.deadCount > 1 ? 's' : ''}`}
) : undefined} > columns={instanceColumns} data={group.instances} onRowClick={handleInstanceClick} selectedId={panelOpen ? selectedInstance?.id : undefined} pageSize={50} flush />
))}
{/* EventFeed */} {filteredEvents.length > 0 && (
Timeline {filteredEvents.length} events
)}
{/* Detail panel (portals itself) */} {selectedInstance && ( setPanelOpen(false)} title={selectedInstance.name} tabs={detailTabs} /> )} ) }