All checks were successful
Build & Publish / publish (push) Successful in 43s
- Rename Metrics to Routes with /routes, /routes/:appId, /routes/:appId/:routeId - Sidebar: Routes is now a collapsible tree (apps > routes) like Applications/Agents - KPI header matching mock-v3-metrics-dashboard: throughput with sparkline, error rate, latency percentiles (P50/P95/P99), active routes with mini donut, in-flight exchanges - Same KPI header used consistently across all 3 levels with scoped data - Route detail level shows per-processor performance table and RouteFlow diagram - Added appId to RouteMetricRow and filled missing route entries in mock data - Fix sidebar section toggle indentation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
108 lines
4.1 KiB
TypeScript
108 lines
4.1 KiB
TypeScript
import { useMemo, useCallback } from 'react'
|
|
import { Routes, Route, Navigate, useNavigate } from 'react-router-dom'
|
|
import { Dashboard } from './pages/Dashboard/Dashboard'
|
|
import { Routes as RoutesPage } from './pages/Routes/Routes'
|
|
import { ExchangeDetail } from './pages/ExchangeDetail/ExchangeDetail'
|
|
import { AgentHealth } from './pages/AgentHealth/AgentHealth'
|
|
import { AgentInstance } from './pages/AgentInstance/AgentInstance'
|
|
import { Inventory } from './pages/Inventory/Inventory'
|
|
import { AuditLog } from './pages/Admin/AuditLog/AuditLog'
|
|
import { OidcConfig } from './pages/Admin/OidcConfig/OidcConfig'
|
|
import { UserManagement } from './pages/Admin/UserManagement/UserManagement'
|
|
import { ApiDocs } from './pages/ApiDocs/ApiDocs'
|
|
|
|
import { CommandPalette } from './design-system/composites/CommandPalette/CommandPalette'
|
|
import type { SearchResult } from './design-system/composites/CommandPalette/types'
|
|
import { useCommandPalette } from './design-system/providers/CommandPaletteProvider'
|
|
import { useGlobalFilters } from './design-system/providers/GlobalFilterProvider'
|
|
import { buildSearchData } from './mocks/searchData'
|
|
import { exchanges } from './mocks/exchanges'
|
|
import { routes } from './mocks/routes'
|
|
import { agents } from './mocks/agents'
|
|
import { SIDEBAR_APPS, buildRouteToAppMap } from './mocks/sidebar'
|
|
|
|
const routeToApp = buildRouteToAppMap()
|
|
|
|
/** Compute which sidebar path to reveal for a given search result */
|
|
function computeSidebarRevealPath(result: SearchResult): string | undefined {
|
|
if (!result.path) return undefined
|
|
|
|
if (result.category === 'application') {
|
|
return result.path
|
|
}
|
|
|
|
if (result.category === 'route') {
|
|
return result.path
|
|
}
|
|
|
|
if (result.category === 'agent') {
|
|
return result.path
|
|
}
|
|
|
|
if (result.category === 'exchange') {
|
|
const exchange = exchanges.find((e) => e.id === result.id)
|
|
if (exchange) {
|
|
const appId = routeToApp.get(exchange.route)
|
|
if (appId) return `/apps/${appId}/${exchange.route}`
|
|
}
|
|
}
|
|
|
|
return result.path
|
|
}
|
|
|
|
export default function App() {
|
|
const navigate = useNavigate()
|
|
const { open: paletteOpen, setOpen } = useCommandPalette()
|
|
const { isInTimeRange, statusFilters } = useGlobalFilters()
|
|
|
|
const filteredSearchData = useMemo(() => {
|
|
// Filter exchanges by time range and status
|
|
let filteredExchanges = exchanges.filter((e) => isInTimeRange(e.timestamp))
|
|
if (statusFilters.size > 0) {
|
|
filteredExchanges = filteredExchanges.filter((e) => statusFilters.has(e.status))
|
|
}
|
|
return buildSearchData(filteredExchanges, routes, agents)
|
|
}, [isInTimeRange, statusFilters])
|
|
|
|
const handleSelect = useCallback(
|
|
(result: SearchResult) => {
|
|
if (result.path) {
|
|
const sidebarReveal = computeSidebarRevealPath(result)
|
|
navigate(result.path, { state: sidebarReveal ? { sidebarReveal } : undefined })
|
|
}
|
|
setOpen(false)
|
|
},
|
|
[navigate, setOpen],
|
|
)
|
|
|
|
return (
|
|
<>
|
|
<Routes>
|
|
<Route path="/" element={<Navigate to="/apps" replace />} />
|
|
<Route path="/apps" element={<Dashboard />} />
|
|
<Route path="/apps/:id" element={<Dashboard />} />
|
|
<Route path="/apps/:id/:routeId" element={<Dashboard />} />
|
|
<Route path="/routes" element={<RoutesPage />} />
|
|
<Route path="/routes/:appId" element={<RoutesPage />} />
|
|
<Route path="/routes/:appId/:routeId" element={<RoutesPage />} />
|
|
<Route path="/exchanges/:id" element={<ExchangeDetail />} />
|
|
<Route path="/agents/:appId/:instanceId" element={<AgentInstance />} />
|
|
<Route path="/agents/*" element={<AgentHealth />} />
|
|
<Route path="/admin" element={<Navigate to="/admin/rbac" replace />} />
|
|
<Route path="/admin/audit" element={<AuditLog />} />
|
|
<Route path="/admin/oidc" element={<OidcConfig />} />
|
|
<Route path="/admin/rbac" element={<UserManagement />} />
|
|
<Route path="/api-docs" element={<ApiDocs />} />
|
|
<Route path="/inventory" element={<Inventory />} />
|
|
</Routes>
|
|
<CommandPalette
|
|
open={paletteOpen}
|
|
onClose={() => setOpen(false)}
|
|
onOpen={() => setOpen(true)}
|
|
data={filteredSearchData}
|
|
onSelect={handleSelect}
|
|
/>
|
|
</>
|
|
)
|
|
}
|