diff --git a/ui/package-lock.json b/ui/package-lock.json index 544aa78a..40bec6e1 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -8,7 +8,7 @@ "name": "ui", "version": "0.0.0", "dependencies": { - "@cameleer/design-system": "^0.1.11", + "@cameleer/design-system": "^0.1.12", "@tanstack/react-query": "^5.90.21", "openapi-fetch": "^0.17.0", "react": "^19.2.4", @@ -276,9 +276,9 @@ } }, "node_modules/@cameleer/design-system": { - "version": "0.1.11", - "resolved": "https://gitea.siegeln.net/api/packages/cameleer/npm/%40cameleer%2Fdesign-system/-/0.1.11/design-system-0.1.11.tgz", - "integrity": "sha512-u32cvvxOSwdDkL3WCiHjMZmdT+KxEcVWEYsg0zpm7CZmbwE98mlW1jZmIw7LRHLfdhb5jH6METRrsGsN6ke44g==", + "version": "0.1.12", + "resolved": "https://gitea.siegeln.net/api/packages/cameleer/npm/%40cameleer%2Fdesign-system/-/0.1.12/design-system-0.1.12.tgz", + "integrity": "sha512-7qXwa5UMzkN7OHCP+gVJYE53eeo/F2PYzt2XcmnsQpsoYcqlNdxmaf9Btl7wgkl1g5MbKGUxUlnlqAUkUbusAw==", "dependencies": { "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/ui/package.json b/ui/package.json index 12169212..c2a8f198 100644 --- a/ui/package.json +++ b/ui/package.json @@ -14,7 +14,7 @@ "generate-api:live": "curl -s http://localhost:8081/api/v1/api-docs -o src/api/openapi.json && openapi-typescript src/api/openapi.json -o src/api/schema.d.ts" }, "dependencies": { - "@cameleer/design-system": "^0.1.11", + "@cameleer/design-system": "^0.1.12", "@tanstack/react-query": "^5.90.21", "openapi-fetch": "^0.17.0", "react": "^19.2.4", diff --git a/ui/src/components/LayoutShell.tsx b/ui/src/components/LayoutShell.tsx index 39627f62..0ee06a13 100644 --- a/ui/src/components/LayoutShell.tsx +++ b/ui/src/components/LayoutShell.tsx @@ -1,5 +1,5 @@ import { Outlet, useNavigate, useLocation } from 'react-router'; -import { AppShell, Sidebar, TopBar, CommandPalette, CommandPaletteProvider, GlobalFilterProvider, ToastProvider, useCommandPalette } from '@cameleer/design-system'; +import { AppShell, Sidebar, TopBar, CommandPalette, CommandPaletteProvider, GlobalFilterProvider, ToastProvider, BreadcrumbProvider, useCommandPalette } from '@cameleer/design-system'; import type { SidebarApp, SearchResult } from '@cameleer/design-system'; import { useRouteCatalog } from '../api/queries/catalog'; import { useAgents } from '../api/queries/agents'; @@ -143,10 +143,23 @@ function LayoutContent() { }, [catalogData, exchangeResults]); const breadcrumb = useMemo(() => { + const LABELS: Record = { + apps: 'Applications', + agents: 'Agents', + exchanges: 'Exchanges', + routes: 'Routes', + admin: 'Admin', + 'api-docs': 'API Docs', + rbac: 'Users & Roles', + audit: 'Audit Log', + oidc: 'OIDC', + database: 'Database', + opensearch: 'OpenSearch', + }; const parts = location.pathname.split('/').filter(Boolean); return parts.map((part, i) => ({ - label: part, - href: '/' + parts.slice(0, i + 1).join('/'), + label: LABELS[part] ?? part, + ...(i < parts.length - 1 ? { href: '/' + parts.slice(0, i + 1).join('/') } : {}), })); }, [location.pathname]); @@ -195,7 +208,9 @@ export function LayoutShell() { - + + + diff --git a/ui/src/pages/AgentHealth/AgentHealth.module.css b/ui/src/pages/AgentHealth/AgentHealth.module.css index 7253a6ad..401fcd22 100644 --- a/ui/src/pages/AgentHealth/AgentHealth.module.css +++ b/ui/src/pages/AgentHealth/AgentHealth.module.css @@ -31,35 +31,6 @@ .routesWarning { color: var(--warning); } .routesError { color: var(--error); } -/* Scope breadcrumb trail */ -.scopeTrail { - display: flex; - align-items: center; - gap: 6px; - margin-bottom: 12px; - font-size: 12px; -} - -.scopeLink { - color: var(--amber); - text-decoration: none; - font-weight: 500; -} - -.scopeLink:hover { - text-decoration: underline; -} - -.scopeSep { - color: var(--text-muted); - font-size: 10px; -} - -.scopeCurrent { - color: var(--text-primary); - font-weight: 600; - font-family: var(--font-mono); -} /* Section header */ .sectionTitle { diff --git a/ui/src/pages/AgentHealth/AgentHealth.tsx b/ui/src/pages/AgentHealth/AgentHealth.tsx index 2b35260f..7427cd78 100644 --- a/ui/src/pages/AgentHealth/AgentHealth.tsx +++ b/ui/src/pages/AgentHealth/AgentHealth.tsx @@ -1,5 +1,5 @@ import { useState, useMemo } from 'react'; -import { useParams, Link } from 'react-router'; +import { useParams } from 'react-router'; import { StatCard, StatusDot, Badge, MonoText, ProgressBar, GroupCard, DataTable, LineChart, EventFeed, DetailPanel, @@ -463,15 +463,7 @@ export default function AgentHealth() { /> - {/* Scope trail + badges */} -
- {appId && ( - <> - All Agents - - {appId} - - )} +
0 ? 'error' : staleCount > 0 ? 'warning' : 'success'} diff --git a/ui/src/pages/ExchangeDetail/ExchangeDetail.tsx b/ui/src/pages/ExchangeDetail/ExchangeDetail.tsx index b7649da6..841aa7cf 100644 --- a/ui/src/pages/ExchangeDetail/ExchangeDetail.tsx +++ b/ui/src/pages/ExchangeDetail/ExchangeDetail.tsx @@ -2,8 +2,8 @@ import { useState, useMemo, useCallback, useEffect } from 'react' import { useParams, useNavigate } from 'react-router' import { Badge, StatusDot, MonoText, CodeBlock, InfoCallout, - ProcessorTimeline, Breadcrumb, Spinner, RouteFlow, useToast, - LogViewer, ButtonGroup, SectionHeader, + ProcessorTimeline, Spinner, RouteFlow, useToast, + LogViewer, ButtonGroup, SectionHeader, useBreadcrumb, } from '@cameleer/design-system' import type { ProcessorStep, RouteNode, NodeBadge, LogEntry, ButtonGroupItem } from '@cameleer/design-system' import { useExecutionDetail, useProcessorSnapshot } from '../../api/queries/executions' @@ -256,6 +256,15 @@ export default function ExchangeDetail() { return correlationData.data }, [correlationData]) + // Set semantic breadcrumb in TopBar when detail is loaded + const breadcrumbItems = useMemo(() => detail ? [ + { label: 'Applications', href: '/apps' }, + { label: detail.applicationName || 'App', href: `/apps/${detail.applicationName}` }, + { label: detail.routeId, href: `/apps/${detail.applicationName}/${detail.routeId}` }, + { label: detail.executionId?.slice(0, 12) || '' }, + ] : null, [detail?.applicationName, detail?.routeId, detail?.executionId]) + useBreadcrumb(breadcrumbItems) + // Exchange logs from OpenSearch (filtered by exchangeId via MDC) const { data: rawLogs } = useApplicationLogs( detail?.applicationName, @@ -288,11 +297,6 @@ export default function ExchangeDetail() { if (!detail) { return (
- Exchange "{id}" not found.
) @@ -304,14 +308,6 @@ export default function ExchangeDetail() { return (
- {/* Breadcrumb */} - - {/* Exchange header card */}