2026-03-18 20:06:25 +01:00
|
|
|
import { useMemo, useCallback } from 'react'
|
|
|
|
|
import { Routes, Route, Navigate, useNavigate } from 'react-router-dom'
|
2026-03-18 10:06:41 +01:00
|
|
|
import { Dashboard } from './pages/Dashboard/Dashboard'
|
2026-03-18 10:22:23 +01:00
|
|
|
import { Metrics } from './pages/Metrics/Metrics'
|
|
|
|
|
import { RouteDetail } from './pages/RouteDetail/RouteDetail'
|
|
|
|
|
import { ExchangeDetail } from './pages/ExchangeDetail/ExchangeDetail'
|
|
|
|
|
import { AgentHealth } from './pages/AgentHealth/AgentHealth'
|
2026-03-18 15:12:28 +01:00
|
|
|
import { Inventory } from './pages/Inventory/Inventory'
|
2026-03-18 17:50:41 +01:00
|
|
|
import { Admin } from './pages/Admin/Admin'
|
|
|
|
|
import { ApiDocs } from './pages/ApiDocs/ApiDocs'
|
2026-03-18 10:06:41 +01:00
|
|
|
|
2026-03-18 20:06:25 +01:00
|
|
|
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 } from './mocks/sidebar'
|
|
|
|
|
|
|
|
|
|
/** 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') {
|
|
|
|
|
// /apps/:id — already a sidebar node path
|
|
|
|
|
return result.path
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (result.category === 'route') {
|
|
|
|
|
// /routes/:id — already a sidebar node path
|
|
|
|
|
return result.path
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (result.category === 'agent') {
|
|
|
|
|
// /agents/:appId/:agentId — already a sidebar node path
|
|
|
|
|
return result.path
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (result.category === 'exchange') {
|
|
|
|
|
// /exchanges/:id — no sidebar entry; resolve to the parent route
|
|
|
|
|
const exchange = exchanges.find((e) => e.id === result.id)
|
|
|
|
|
if (exchange) {
|
|
|
|
|
return `/routes/${exchange.route}`
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result.path
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-18 09:07:31 +01:00
|
|
|
export default function App() {
|
2026-03-18 20:06:25 +01:00
|
|
|
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],
|
|
|
|
|
)
|
|
|
|
|
|
2026-03-18 10:06:41 +01:00
|
|
|
return (
|
2026-03-18 20:06:25 +01:00
|
|
|
<>
|
|
|
|
|
<Routes>
|
|
|
|
|
<Route path="/" element={<Navigate to="/apps" replace />} />
|
|
|
|
|
<Route path="/apps" element={<Dashboard />} />
|
|
|
|
|
<Route path="/apps/:id" element={<Dashboard />} />
|
|
|
|
|
<Route path="/metrics" element={<Metrics />} />
|
|
|
|
|
<Route path="/routes/:id" element={<RouteDetail />} />
|
|
|
|
|
<Route path="/exchanges/:id" element={<ExchangeDetail />} />
|
|
|
|
|
<Route path="/agents/*" element={<AgentHealth />} />
|
|
|
|
|
<Route path="/admin" element={<Admin />} />
|
|
|
|
|
<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}
|
|
|
|
|
/>
|
|
|
|
|
</>
|
2026-03-18 10:06:41 +01:00
|
|
|
)
|
2026-03-18 09:03:33 +01:00
|
|
|
}
|