From 62b5c56c560b9243ad6f7e65ef53fff7577a6e9d Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Thu, 2 Apr 2026 23:06:01 +0200 Subject: [PATCH] feat: event-type icons for agent event feeds Icons now reflect event type (UserPlus for registration, Skull for dead, HeartPulse for recovery, Route for state changes, etc.) while severity still drives the color. Updated in both AgentInstance and AgentHealth pages. Co-Authored-By: Claude Opus 4.6 (1M context) --- ui/src/pages/AgentHealth/AgentHealth.tsx | 44 +++++++++++++++----- ui/src/pages/AgentInstance/AgentInstance.tsx | 41 ++++++++++++++---- 2 files changed, 65 insertions(+), 20 deletions(-) diff --git a/ui/src/pages/AgentHealth/AgentHealth.tsx b/ui/src/pages/AgentHealth/AgentHealth.tsx index 94ab1e36..d1281c19 100644 --- a/ui/src/pages/AgentHealth/AgentHealth.tsx +++ b/ui/src/pages/AgentHealth/AgentHealth.tsx @@ -1,6 +1,6 @@ import { useState, useMemo, useCallback } from 'react'; import { useParams, useNavigate } from 'react-router'; -import { ExternalLink, RefreshCw, Pencil } from 'lucide-react'; +import { ExternalLink, RefreshCw, Pencil, UserPlus, UserMinus, Play, Square, Clock, Skull, HeartPulse, Route, Send, Activity } from 'lucide-react'; import { StatCard, StatusDot, Badge, MonoText, GroupCard, DataTable, EventFeed, @@ -195,22 +195,44 @@ export default function AgentHealth() { // Map events to FeedEvent const feedEvents: FeedEvent[] = useMemo(() => { + const eventIcon = (type: string) => { + switch (type) { + case 'REGISTERED': return ; + case 'DEREGISTERED': return ; + case 'AGENT_STARTED': return ; + case 'AGENT_STOPPED': return ; + case 'WENT_STALE': return ; + case 'WENT_DEAD': return ; + case 'RECOVERED': return ; + case 'ROUTE_STATE_CHANGED': return ; + case 'COMMAND_DELIVERED': + case 'COMMAND_ACKNOWLEDGED': return ; + default: return ; + } + }; + + const eventSeverity = (type: string): FeedEvent['severity'] => { + switch (type) { + case 'WENT_DEAD': + case 'AGENT_STOPPED': + case 'DEREGISTERED': return 'error'; + case 'WENT_STALE': return 'warning'; + case 'RECOVERED': + case 'REGISTERED': + case 'AGENT_STARTED': return 'success'; + default: return 'running'; + } + }; + const mapped = (events ?? []).map((e: { id: number; instanceId: string; eventType: string; detail: string; timestamp: string }) => ({ id: String(e.id), - severity: - e.eventType === 'WENT_DEAD' - ? ('error' as const) - : e.eventType === 'WENT_STALE' - ? ('warning' as const) - : e.eventType === 'RECOVERED' - ? ('success' as const) - : ('running' as const), + severity: eventSeverity(e.eventType), + icon: eventIcon(e.eventType), message: `${e.instanceId}: ${e.eventType}${e.detail ? ' \u2014 ' + e.detail : ''}`, timestamp: new Date(e.timestamp), })); return eventSortAsc ? mapped.toReversed() : mapped; - }, [events, eventSortAsc], - ); + }, [events, eventSortAsc]); // Column definitions for the instance DataTable const instanceColumns: Column[] = useMemo( diff --git a/ui/src/pages/AgentInstance/AgentInstance.tsx b/ui/src/pages/AgentInstance/AgentInstance.tsx index 33c8190e..6e05e184 100644 --- a/ui/src/pages/AgentInstance/AgentInstance.tsx +++ b/ui/src/pages/AgentInstance/AgentInstance.tsx @@ -1,6 +1,6 @@ import { useMemo, useState } from 'react'; import { useParams, Link } from 'react-router'; -import { RefreshCw, ChevronRight } from 'lucide-react'; +import { RefreshCw, ChevronRight, UserPlus, UserMinus, Play, Square, Clock, Skull, HeartPulse, Route, Send, Activity } from 'lucide-react'; import { StatCard, StatusDot, Badge, LineChart, AreaChart, BarChart, EventFeed, Spinner, EmptyState, SectionHeader, MonoText, @@ -82,18 +82,41 @@ export default function AgentInstance() { ); const feedEvents = useMemo(() => { + const eventIcon = (type: string) => { + switch (type) { + case 'REGISTERED': return ; + case 'DEREGISTERED': return ; + case 'AGENT_STARTED': return ; + case 'AGENT_STOPPED': return ; + case 'WENT_STALE': return ; + case 'WENT_DEAD': return ; + case 'RECOVERED': return ; + case 'ROUTE_STATE_CHANGED': return ; + case 'COMMAND_DELIVERED': + case 'COMMAND_ACKNOWLEDGED': return ; + default: return ; + } + }; + + const eventSeverity = (type: string): FeedEvent['severity'] => { + switch (type) { + case 'WENT_DEAD': + case 'AGENT_STOPPED': + case 'DEREGISTERED': return 'error'; + case 'WENT_STALE': return 'warning'; + case 'RECOVERED': + case 'REGISTERED': + case 'AGENT_STARTED': return 'success'; + default: return 'running'; + } + }; + const mapped = (events || []) .filter((e: any) => !instanceId || e.instanceId === instanceId) .map((e: any) => ({ id: String(e.id), - severity: - e.eventType === 'WENT_DEAD' - ? ('error' as const) - : e.eventType === 'WENT_STALE' - ? ('warning' as const) - : e.eventType === 'RECOVERED' - ? ('success' as const) - : ('running' as const), + severity: eventSeverity(e.eventType), + icon: eventIcon(e.eventType), message: `${e.eventType}${e.detail ? ' \u2014 ' + e.detail : ''}`, timestamp: new Date(e.timestamp), }));