feat: migrate UI to @cameleer/design-system, add backend endpoints
Backend:
- Add agent_events table (V5) and lifecycle event recording
- Add route catalog endpoint (GET /routes/catalog)
- Add route metrics endpoint (GET /routes/metrics)
- Add agent events endpoint (GET /agents/events-log)
- Enrich AgentInstanceResponse with tps, errorRate, activeRoutes, uptimeSeconds
- Add TimescaleDB retention/compression policies (V6)
Frontend:
- Replace custom Mission Control UI with @cameleer/design-system components
- Rebuild all pages: Dashboard, ExchangeDetail, RoutesMetrics, AgentHealth,
AgentInstance, RBAC, AuditLog, OIDC, DatabaseAdmin, OpenSearchAdmin, Swagger
- New LayoutShell with design system AppShell, Sidebar, TopBar, CommandPalette
- Consume design system from Gitea npm registry (@cameleer/design-system@0.0.1)
- Add .npmrc for scoped registry, update Dockerfile with REGISTRY_TOKEN arg
CI:
- Pass REGISTRY_TOKEN build-arg to UI Docker build step
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 17:38:39 +01:00
|
|
|
import { Outlet, useNavigate, useLocation } from 'react-router';
|
2026-03-19 18:06:49 +01:00
|
|
|
import { AppShell, Sidebar, TopBar, CommandPalette, CommandPaletteProvider, GlobalFilterProvider, useCommandPalette, Dropdown, Avatar } from '@cameleer/design-system';
|
feat: migrate UI to @cameleer/design-system, add backend endpoints
Backend:
- Add agent_events table (V5) and lifecycle event recording
- Add route catalog endpoint (GET /routes/catalog)
- Add route metrics endpoint (GET /routes/metrics)
- Add agent events endpoint (GET /agents/events-log)
- Enrich AgentInstanceResponse with tps, errorRate, activeRoutes, uptimeSeconds
- Add TimescaleDB retention/compression policies (V6)
Frontend:
- Replace custom Mission Control UI with @cameleer/design-system components
- Rebuild all pages: Dashboard, ExchangeDetail, RoutesMetrics, AgentHealth,
AgentInstance, RBAC, AuditLog, OIDC, DatabaseAdmin, OpenSearchAdmin, Swagger
- New LayoutShell with design system AppShell, Sidebar, TopBar, CommandPalette
- Consume design system from Gitea npm registry (@cameleer/design-system@0.0.1)
- Add .npmrc for scoped registry, update Dockerfile with REGISTRY_TOKEN arg
CI:
- Pass REGISTRY_TOKEN build-arg to UI Docker build step
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 17:38:39 +01:00
|
|
|
import { useRouteCatalog } from '../api/queries/catalog';
|
|
|
|
|
import { useAuthStore } from '../auth/auth-store';
|
|
|
|
|
import { useMemo, useCallback } from 'react';
|
|
|
|
|
import type { SidebarApp } from '@cameleer/design-system';
|
|
|
|
|
|
|
|
|
|
function LayoutContent() {
|
|
|
|
|
const navigate = useNavigate();
|
|
|
|
|
const location = useLocation();
|
|
|
|
|
const { data: catalog } = useRouteCatalog();
|
2026-03-19 18:06:49 +01:00
|
|
|
const { username, roles, logout } = useAuthStore();
|
feat: migrate UI to @cameleer/design-system, add backend endpoints
Backend:
- Add agent_events table (V5) and lifecycle event recording
- Add route catalog endpoint (GET /routes/catalog)
- Add route metrics endpoint (GET /routes/metrics)
- Add agent events endpoint (GET /agents/events-log)
- Enrich AgentInstanceResponse with tps, errorRate, activeRoutes, uptimeSeconds
- Add TimescaleDB retention/compression policies (V6)
Frontend:
- Replace custom Mission Control UI with @cameleer/design-system components
- Rebuild all pages: Dashboard, ExchangeDetail, RoutesMetrics, AgentHealth,
AgentInstance, RBAC, AuditLog, OIDC, DatabaseAdmin, OpenSearchAdmin, Swagger
- New LayoutShell with design system AppShell, Sidebar, TopBar, CommandPalette
- Consume design system from Gitea npm registry (@cameleer/design-system@0.0.1)
- Add .npmrc for scoped registry, update Dockerfile with REGISTRY_TOKEN arg
CI:
- Pass REGISTRY_TOKEN build-arg to UI Docker build step
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 17:38:39 +01:00
|
|
|
const { open: paletteOpen, setOpen: setPaletteOpen } = useCommandPalette();
|
|
|
|
|
|
|
|
|
|
const sidebarApps: SidebarApp[] = useMemo(() => {
|
|
|
|
|
if (!catalog) return [];
|
|
|
|
|
return catalog.map((app: any) => ({
|
|
|
|
|
id: app.appId,
|
|
|
|
|
name: app.appId,
|
|
|
|
|
health: app.health as 'live' | 'stale' | 'dead',
|
|
|
|
|
exchangeCount: app.exchangeCount,
|
|
|
|
|
routes: (app.routes || []).map((r: any) => ({
|
|
|
|
|
id: r.routeId,
|
|
|
|
|
name: r.routeId,
|
|
|
|
|
exchangeCount: r.exchangeCount,
|
|
|
|
|
})),
|
|
|
|
|
agents: (app.agents || []).map((a: any) => ({
|
|
|
|
|
id: a.id,
|
|
|
|
|
name: a.name,
|
|
|
|
|
status: a.status as 'live' | 'stale' | 'dead',
|
|
|
|
|
tps: a.tps,
|
|
|
|
|
})),
|
|
|
|
|
}));
|
|
|
|
|
}, [catalog]);
|
|
|
|
|
|
|
|
|
|
const breadcrumb = useMemo(() => {
|
|
|
|
|
const parts = location.pathname.split('/').filter(Boolean);
|
|
|
|
|
return parts.map((part, i) => ({
|
|
|
|
|
label: part,
|
|
|
|
|
href: '/' + parts.slice(0, i + 1).join('/'),
|
|
|
|
|
}));
|
|
|
|
|
}, [location.pathname]);
|
|
|
|
|
|
|
|
|
|
const handlePaletteSelect = useCallback((result: any) => {
|
|
|
|
|
if (result.path) navigate(result.path);
|
|
|
|
|
setPaletteOpen(false);
|
|
|
|
|
}, [navigate, setPaletteOpen]);
|
|
|
|
|
|
|
|
|
|
const isAdmin = roles.includes('ADMIN');
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<AppShell
|
|
|
|
|
sidebar={
|
|
|
|
|
<Sidebar
|
|
|
|
|
apps={sidebarApps}
|
|
|
|
|
/>
|
|
|
|
|
}
|
|
|
|
|
>
|
2026-03-19 18:06:49 +01:00
|
|
|
<div style={{ display: 'flex', alignItems: 'center' }}>
|
|
|
|
|
<TopBar
|
|
|
|
|
breadcrumb={breadcrumb}
|
|
|
|
|
user={username ? { name: username } : undefined}
|
|
|
|
|
/>
|
|
|
|
|
{username && (
|
|
|
|
|
<Dropdown
|
|
|
|
|
trigger={<Avatar name={username} size="sm" />}
|
|
|
|
|
items={[
|
|
|
|
|
{ label: `Signed in as ${username}`, disabled: true },
|
|
|
|
|
{ divider: true, label: '' },
|
|
|
|
|
{ label: 'Logout', onClick: () => { logout(); navigate('/login'); } },
|
|
|
|
|
]}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
feat: migrate UI to @cameleer/design-system, add backend endpoints
Backend:
- Add agent_events table (V5) and lifecycle event recording
- Add route catalog endpoint (GET /routes/catalog)
- Add route metrics endpoint (GET /routes/metrics)
- Add agent events endpoint (GET /agents/events-log)
- Enrich AgentInstanceResponse with tps, errorRate, activeRoutes, uptimeSeconds
- Add TimescaleDB retention/compression policies (V6)
Frontend:
- Replace custom Mission Control UI with @cameleer/design-system components
- Rebuild all pages: Dashboard, ExchangeDetail, RoutesMetrics, AgentHealth,
AgentInstance, RBAC, AuditLog, OIDC, DatabaseAdmin, OpenSearchAdmin, Swagger
- New LayoutShell with design system AppShell, Sidebar, TopBar, CommandPalette
- Consume design system from Gitea npm registry (@cameleer/design-system@0.0.1)
- Add .npmrc for scoped registry, update Dockerfile with REGISTRY_TOKEN arg
CI:
- Pass REGISTRY_TOKEN build-arg to UI Docker build step
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 17:38:39 +01:00
|
|
|
<CommandPalette
|
|
|
|
|
open={paletteOpen}
|
|
|
|
|
onClose={() => setPaletteOpen(false)}
|
|
|
|
|
onSelect={handlePaletteSelect}
|
|
|
|
|
data={[]}
|
|
|
|
|
/>
|
|
|
|
|
<main style={{ flex: 1, overflow: 'auto', padding: '1.5rem' }}>
|
|
|
|
|
<Outlet />
|
|
|
|
|
</main>
|
|
|
|
|
</AppShell>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function LayoutShell() {
|
|
|
|
|
return (
|
|
|
|
|
<CommandPaletteProvider>
|
|
|
|
|
<GlobalFilterProvider>
|
|
|
|
|
<LayoutContent />
|
|
|
|
|
</GlobalFilterProvider>
|
|
|
|
|
</CommandPaletteProvider>
|
|
|
|
|
);
|
|
|
|
|
}
|