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-04-02 18:29:25 +02:00
|
|
|
import {
|
|
|
|
|
AppShell,
|
|
|
|
|
Sidebar,
|
|
|
|
|
SidebarTree,
|
|
|
|
|
StatusDot,
|
|
|
|
|
TopBar,
|
|
|
|
|
CommandPalette,
|
|
|
|
|
CommandPaletteProvider,
|
|
|
|
|
GlobalFilterProvider,
|
|
|
|
|
ToastProvider,
|
|
|
|
|
BreadcrumbProvider,
|
|
|
|
|
useCommandPalette,
|
|
|
|
|
useGlobalFilters,
|
|
|
|
|
useStarred,
|
|
|
|
|
} from '@cameleer/design-system';
|
|
|
|
|
import type { SearchResult, SidebarTreeNode } from '@cameleer/design-system';
|
2026-04-02 22:29:44 +02:00
|
|
|
import { Box, Settings, FileText, ChevronRight, Square, Pause, Star, X } from 'lucide-react';
|
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';
|
feat: replace UI with design system example pages wired to real API
Migrate all page components from the @cameleer/design-system v0.0.3
example UI, replacing mock data with real backend API hooks. This brings
richer visuals (KpiStrip, GroupCard, RouteFlow, ProcessorTimeline,
DateRangePicker, expandable rows) while preserving all existing API
integration, auth, and routing infrastructure.
Pages migrated: Dashboard, RoutesMetrics, RouteDetail, ExchangeDetail,
AgentHealth, AgentInstance, OidcConfig, AuditLog, RBAC (Users/Groups/Roles).
Also enhanced LayoutShell CommandPalette with real search data from catalog.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:42:16 +01:00
|
|
|
import { useAgents } from '../api/queries/agents';
|
2026-04-01 21:39:27 +02:00
|
|
|
import { useSearchExecutions, useAttributeKeys } from '../api/queries/executions';
|
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 { useAuthStore } from '../auth/auth-store';
|
2026-04-02 18:29:25 +02:00
|
|
|
import { useState, useMemo, useCallback, useEffect, useRef, createElement } from 'react';
|
2026-04-02 22:29:44 +02:00
|
|
|
import type { ReactNode } from 'react';
|
2026-03-28 14:01:52 +01:00
|
|
|
import { ContentTabs } from './ContentTabs';
|
|
|
|
|
import { useScope } from '../hooks/useScope';
|
2026-04-02 18:29:25 +02:00
|
|
|
import {
|
|
|
|
|
buildAppTreeNodes,
|
|
|
|
|
buildAdminTreeNodes,
|
2026-04-02 22:29:44 +02:00
|
|
|
formatCount,
|
2026-04-02 18:29:25 +02:00
|
|
|
readCollapsed,
|
|
|
|
|
writeCollapsed,
|
|
|
|
|
} from './sidebar-utils';
|
|
|
|
|
import type { SidebarApp } from './sidebar-utils';
|
feat: replace UI with design system example pages wired to real API
Migrate all page components from the @cameleer/design-system v0.0.3
example UI, replacing mock data with real backend API hooks. This brings
richer visuals (KpiStrip, GroupCard, RouteFlow, ProcessorTimeline,
DateRangePicker, expandable rows) while preserving all existing API
integration, auth, and routing infrastructure.
Pages migrated: Dashboard, RoutesMetrics, RouteDetail, ExchangeDetail,
AgentHealth, AgentInstance, OidcConfig, AuditLog, RBAC (Users/Groups/Roles).
Also enhanced LayoutShell CommandPalette with real search data from catalog.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:42:16 +01:00
|
|
|
|
2026-04-02 18:29:25 +02:00
|
|
|
/* ------------------------------------------------------------------ */
|
2026-04-02 22:29:44 +02:00
|
|
|
/* Search data builder */
|
2026-04-02 18:29:25 +02:00
|
|
|
/* ------------------------------------------------------------------ */
|
feat: replace UI with design system example pages wired to real API
Migrate all page components from the @cameleer/design-system v0.0.3
example UI, replacing mock data with real backend API hooks. This brings
richer visuals (KpiStrip, GroupCard, RouteFlow, ProcessorTimeline,
DateRangePicker, expandable rows) while preserving all existing API
integration, auth, and routing infrastructure.
Pages migrated: Dashboard, RoutesMetrics, RouteDetail, ExchangeDetail,
AgentHealth, AgentInstance, OidcConfig, AuditLog, RBAC (Users/Groups/Roles).
Also enhanced LayoutShell CommandPalette with real search data from catalog.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:42:16 +01:00
|
|
|
|
|
|
|
|
function buildSearchData(
|
|
|
|
|
catalog: any[] | undefined,
|
|
|
|
|
agents: any[] | undefined,
|
2026-04-01 21:39:27 +02:00
|
|
|
attrKeys: string[] | undefined,
|
feat: replace UI with design system example pages wired to real API
Migrate all page components from the @cameleer/design-system v0.0.3
example UI, replacing mock data with real backend API hooks. This brings
richer visuals (KpiStrip, GroupCard, RouteFlow, ProcessorTimeline,
DateRangePicker, expandable rows) while preserving all existing API
integration, auth, and routing infrastructure.
Pages migrated: Dashboard, RoutesMetrics, RouteDetail, ExchangeDetail,
AgentHealth, AgentInstance, OidcConfig, AuditLog, RBAC (Users/Groups/Roles).
Also enhanced LayoutShell CommandPalette with real search data from catalog.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:42:16 +01:00
|
|
|
): SearchResult[] {
|
|
|
|
|
if (!catalog) return [];
|
|
|
|
|
const results: SearchResult[] = [];
|
|
|
|
|
|
|
|
|
|
for (const app of catalog) {
|
|
|
|
|
const liveAgents = (app.agents || []).filter((a: any) => a.status === 'live').length;
|
|
|
|
|
results.push({
|
|
|
|
|
id: app.appId,
|
|
|
|
|
category: 'application',
|
|
|
|
|
title: app.appId,
|
2026-04-02 18:29:25 +02:00
|
|
|
badges: [{ label: (app.health || 'unknown').toUpperCase(), color: healthToSearchColor(app.health) }],
|
feat: replace UI with design system example pages wired to real API
Migrate all page components from the @cameleer/design-system v0.0.3
example UI, replacing mock data with real backend API hooks. This brings
richer visuals (KpiStrip, GroupCard, RouteFlow, ProcessorTimeline,
DateRangePicker, expandable rows) while preserving all existing API
integration, auth, and routing infrastructure.
Pages migrated: Dashboard, RoutesMetrics, RouteDetail, ExchangeDetail,
AgentHealth, AgentInstance, OidcConfig, AuditLog, RBAC (Users/Groups/Roles).
Also enhanced LayoutShell CommandPalette with real search data from catalog.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:42:16 +01:00
|
|
|
meta: `${(app.routes || []).length} routes · ${(app.agents || []).length} agents (${liveAgents} live) · ${(app.exchangeCount ?? 0).toLocaleString()} exchanges`,
|
2026-03-28 14:01:52 +01:00
|
|
|
path: `/exchanges/${app.appId}`,
|
feat: replace UI with design system example pages wired to real API
Migrate all page components from the @cameleer/design-system v0.0.3
example UI, replacing mock data with real backend API hooks. This brings
richer visuals (KpiStrip, GroupCard, RouteFlow, ProcessorTimeline,
DateRangePicker, expandable rows) while preserving all existing API
integration, auth, and routing infrastructure.
Pages migrated: Dashboard, RoutesMetrics, RouteDetail, ExchangeDetail,
AgentHealth, AgentInstance, OidcConfig, AuditLog, RBAC (Users/Groups/Roles).
Also enhanced LayoutShell CommandPalette with real search data from catalog.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:42:16 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for (const route of (app.routes || [])) {
|
|
|
|
|
results.push({
|
2026-04-01 21:33:23 +02:00
|
|
|
id: `${app.appId}/${route.routeId}`,
|
feat: replace UI with design system example pages wired to real API
Migrate all page components from the @cameleer/design-system v0.0.3
example UI, replacing mock data with real backend API hooks. This brings
richer visuals (KpiStrip, GroupCard, RouteFlow, ProcessorTimeline,
DateRangePicker, expandable rows) while preserving all existing API
integration, auth, and routing infrastructure.
Pages migrated: Dashboard, RoutesMetrics, RouteDetail, ExchangeDetail,
AgentHealth, AgentInstance, OidcConfig, AuditLog, RBAC (Users/Groups/Roles).
Also enhanced LayoutShell CommandPalette with real search data from catalog.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:42:16 +01:00
|
|
|
category: 'route',
|
|
|
|
|
title: route.routeId,
|
|
|
|
|
badges: [{ label: app.appId }],
|
|
|
|
|
meta: `${(route.exchangeCount ?? 0).toLocaleString()} exchanges`,
|
2026-03-28 14:01:52 +01:00
|
|
|
path: `/exchanges/${app.appId}/${route.routeId}`,
|
feat: replace UI with design system example pages wired to real API
Migrate all page components from the @cameleer/design-system v0.0.3
example UI, replacing mock data with real backend API hooks. This brings
richer visuals (KpiStrip, GroupCard, RouteFlow, ProcessorTimeline,
DateRangePicker, expandable rows) while preserving all existing API
integration, auth, and routing infrastructure.
Pages migrated: Dashboard, RoutesMetrics, RouteDetail, ExchangeDetail,
AgentHealth, AgentInstance, OidcConfig, AuditLog, RBAC (Users/Groups/Roles).
Also enhanced LayoutShell CommandPalette with real search data from catalog.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:42:16 +01:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (agents) {
|
|
|
|
|
for (const agent of agents) {
|
|
|
|
|
results.push({
|
fix: update agent field names in frontend to match backend DTO
The AgentInstanceResponse backend DTO uses instanceId, displayName,
applicationId, status — but the stale schema.d.ts still had id, name,
application, state. This caused the runtime table to show no data.
- Update schema.d.ts AgentInstanceResponse fields
- Fix AgentHealth: row.id→instanceId, row.name→displayName,
row.application→applicationId, inst.id→instanceId
- Fix AgentInstance: agent.id→instanceId, agent.name→displayName
- Fix ExchangeHeader: agent.id→instanceId, agent.state→status
- Fix LayoutShell search: agent.state→status, agentTps→tps
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 20:36:31 +02:00
|
|
|
id: agent.instanceId,
|
feat: replace UI with design system example pages wired to real API
Migrate all page components from the @cameleer/design-system v0.0.3
example UI, replacing mock data with real backend API hooks. This brings
richer visuals (KpiStrip, GroupCard, RouteFlow, ProcessorTimeline,
DateRangePicker, expandable rows) while preserving all existing API
integration, auth, and routing infrastructure.
Pages migrated: Dashboard, RoutesMetrics, RouteDetail, ExchangeDetail,
AgentHealth, AgentInstance, OidcConfig, AuditLog, RBAC (Users/Groups/Roles).
Also enhanced LayoutShell CommandPalette with real search data from catalog.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:42:16 +01:00
|
|
|
category: 'agent',
|
fix: update agent field names in frontend to match backend DTO
The AgentInstanceResponse backend DTO uses instanceId, displayName,
applicationId, status — but the stale schema.d.ts still had id, name,
application, state. This caused the runtime table to show no data.
- Update schema.d.ts AgentInstanceResponse fields
- Fix AgentHealth: row.id→instanceId, row.name→displayName,
row.application→applicationId, inst.id→instanceId
- Fix AgentInstance: agent.id→instanceId, agent.name→displayName
- Fix ExchangeHeader: agent.id→instanceId, agent.state→status
- Fix LayoutShell search: agent.state→status, agentTps→tps
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 20:36:31 +02:00
|
|
|
title: agent.displayName,
|
2026-04-02 18:29:25 +02:00
|
|
|
badges: [{ label: (agent.status || 'unknown').toUpperCase(), color: healthToSearchColor((agent.status || '').toLowerCase()) }],
|
fix: update agent field names in frontend to match backend DTO
The AgentInstanceResponse backend DTO uses instanceId, displayName,
applicationId, status — but the stale schema.d.ts still had id, name,
application, state. This caused the runtime table to show no data.
- Update schema.d.ts AgentInstanceResponse fields
- Fix AgentHealth: row.id→instanceId, row.name→displayName,
row.application→applicationId, inst.id→instanceId
- Fix AgentInstance: agent.id→instanceId, agent.name→displayName
- Fix ExchangeHeader: agent.id→instanceId, agent.state→status
- Fix LayoutShell search: agent.state→status, agentTps→tps
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 20:36:31 +02:00
|
|
|
meta: `${agent.applicationId} · ${agent.version || ''}${agent.tps != null ? ` · ${agent.tps.toFixed(1)} msg/s` : ''}`,
|
|
|
|
|
path: `/runtime/${agent.applicationId}/${agent.instanceId}`,
|
feat: replace UI with design system example pages wired to real API
Migrate all page components from the @cameleer/design-system v0.0.3
example UI, replacing mock data with real backend API hooks. This brings
richer visuals (KpiStrip, GroupCard, RouteFlow, ProcessorTimeline,
DateRangePicker, expandable rows) while preserving all existing API
integration, auth, and routing infrastructure.
Pages migrated: Dashboard, RoutesMetrics, RouteDetail, ExchangeDetail,
AgentHealth, AgentInstance, OidcConfig, AuditLog, RBAC (Users/Groups/Roles).
Also enhanced LayoutShell CommandPalette with real search data from catalog.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:42:16 +01:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-01 21:39:27 +02:00
|
|
|
if (attrKeys) {
|
|
|
|
|
for (const key of attrKeys) {
|
|
|
|
|
results.push({
|
|
|
|
|
id: `attr-key-${key}`,
|
|
|
|
|
category: 'attribute',
|
|
|
|
|
title: key,
|
|
|
|
|
meta: 'attribute key',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
feat: replace UI with design system example pages wired to real API
Migrate all page components from the @cameleer/design-system v0.0.3
example UI, replacing mock data with real backend API hooks. This brings
richer visuals (KpiStrip, GroupCard, RouteFlow, ProcessorTimeline,
DateRangePicker, expandable rows) while preserving all existing API
integration, auth, and routing infrastructure.
Pages migrated: Dashboard, RoutesMetrics, RouteDetail, ExchangeDetail,
AgentHealth, AgentInstance, OidcConfig, AuditLog, RBAC (Users/Groups/Roles).
Also enhanced LayoutShell CommandPalette with real search data from catalog.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:42:16 +01:00
|
|
|
return results;
|
|
|
|
|
}
|
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
|
|
|
|
2026-04-02 18:29:25 +02:00
|
|
|
function healthToSearchColor(health: string): string {
|
|
|
|
|
switch (health) {
|
|
|
|
|
case 'live': return 'success';
|
|
|
|
|
case 'stale': return 'warning';
|
|
|
|
|
case 'dead': return 'error';
|
|
|
|
|
default: return 'auto';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 08:57:24 +01:00
|
|
|
function formatDuration(ms: number): string {
|
|
|
|
|
if (ms >= 60_000) return `${(ms / 1000).toFixed(0)}s`;
|
|
|
|
|
if (ms >= 1000) return `${(ms / 1000).toFixed(2)}s`;
|
|
|
|
|
return `${ms}ms`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function statusToColor(status: string): string {
|
|
|
|
|
switch (status) {
|
|
|
|
|
case 'COMPLETED': return 'success';
|
|
|
|
|
case 'FAILED': return 'error';
|
|
|
|
|
case 'RUNNING': return 'running';
|
|
|
|
|
default: return 'warning';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function useDebouncedValue<T>(value: T, delayMs: number): T {
|
|
|
|
|
const [debounced, setDebounced] = useState(value);
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const timer = setTimeout(() => setDebounced(value), delayMs);
|
|
|
|
|
return () => clearTimeout(timer);
|
|
|
|
|
}, [value, delayMs]);
|
|
|
|
|
return debounced;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 18:29:25 +02:00
|
|
|
/* ------------------------------------------------------------------ */
|
2026-04-02 22:29:44 +02:00
|
|
|
/* Icon factories */
|
2026-04-02 18:29:25 +02:00
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
|
|
|
|
|
function makeStatusDot(health: string) {
|
|
|
|
|
return createElement(StatusDot, { variant: health as any });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function makeChevron() {
|
|
|
|
|
return createElement(ChevronRight, { size: 14 });
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 19:15:46 +02:00
|
|
|
function makeStopIcon() {
|
|
|
|
|
return createElement(Square, { size: 12, style: { color: 'var(--error)' } });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function makePauseIcon() {
|
|
|
|
|
return createElement(Pause, { size: 12, style: { color: 'var(--amber)' } });
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 18:29:25 +02:00
|
|
|
/* ------------------------------------------------------------------ */
|
2026-04-02 22:29:44 +02:00
|
|
|
/* Starred items */
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
|
|
|
|
|
interface StarredItem {
|
|
|
|
|
starKey: string;
|
|
|
|
|
label: string;
|
|
|
|
|
icon?: ReactNode;
|
|
|
|
|
path: string;
|
|
|
|
|
parentApp?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function collectStarredItems(apps: SidebarApp[], starredIds: Set<string>): StarredItem[] {
|
|
|
|
|
const items: StarredItem[] = [];
|
|
|
|
|
for (const app of apps) {
|
|
|
|
|
if (starredIds.has(`app:${app.id}`)) {
|
|
|
|
|
items.push({ starKey: `app:${app.id}`, label: app.name, icon: makeStatusDot(app.health), path: `/apps/${app.id}` });
|
|
|
|
|
}
|
|
|
|
|
for (const route of app.routes) {
|
2026-04-02 22:36:28 +02:00
|
|
|
const key = `route:${app.id}/${route.id}`;
|
2026-04-02 22:29:44 +02:00
|
|
|
if (starredIds.has(key)) {
|
|
|
|
|
items.push({ starKey: key, label: route.name, path: `/apps/${app.id}/${route.id}`, parentApp: app.name });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return items;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function StarredList({ items, onNavigate, onRemove }: { items: StarredItem[]; onNavigate: (path: string) => void; onRemove: (key: string) => void }) {
|
|
|
|
|
if (items.length === 0) return null;
|
|
|
|
|
return (
|
|
|
|
|
<div style={{ padding: '4px 0' }}>
|
|
|
|
|
{items.map((item) => (
|
|
|
|
|
<div
|
|
|
|
|
key={item.starKey}
|
|
|
|
|
style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '4px 12px', cursor: 'pointer', fontSize: 12, color: 'var(--sidebar-text)', borderRadius: 4 }}
|
|
|
|
|
onClick={() => onNavigate(item.path)}
|
|
|
|
|
role="button"
|
|
|
|
|
tabIndex={0}
|
|
|
|
|
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') onNavigate(item.path); }}
|
|
|
|
|
>
|
|
|
|
|
{item.icon && <span style={{ display: 'flex', alignItems: 'center', color: 'var(--sidebar-muted)' }}>{item.icon}</span>}
|
|
|
|
|
<span style={{ flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
|
|
|
|
{item.label}
|
|
|
|
|
{item.parentApp && <span style={{ color: 'var(--sidebar-muted)', marginLeft: 4, fontSize: 10 }}>{item.parentApp}</span>}
|
|
|
|
|
</span>
|
|
|
|
|
<button
|
|
|
|
|
style={{ background: 'none', border: 'none', padding: 2, cursor: 'pointer', color: 'var(--sidebar-muted)', display: 'flex', alignItems: 'center', opacity: 0.6 }}
|
|
|
|
|
onClick={(e) => { e.stopPropagation(); onRemove(item.starKey); }}
|
|
|
|
|
aria-label={`Remove ${item.label} from starred`}
|
|
|
|
|
>
|
|
|
|
|
<X size={10} />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
/* Section state keys */
|
2026-04-02 18:29:25 +02:00
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
|
|
|
|
|
const SK_APPS = 'sidebar:section:apps';
|
|
|
|
|
const SK_ADMIN = 'sidebar:section:admin';
|
|
|
|
|
const SK_COLLAPSED = 'sidebar:collapsed';
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
/* Main layout content */
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
|
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
|
|
|
function LayoutContent() {
|
|
|
|
|
const navigate = useNavigate();
|
|
|
|
|
const location = useLocation();
|
2026-03-26 12:21:10 +01:00
|
|
|
const { timeRange } = useGlobalFilters();
|
|
|
|
|
const { data: catalog } = useRouteCatalog(timeRange.start.toISOString(), timeRange.end.toISOString());
|
feat: replace UI with design system example pages wired to real API
Migrate all page components from the @cameleer/design-system v0.0.3
example UI, replacing mock data with real backend API hooks. This brings
richer visuals (KpiStrip, GroupCard, RouteFlow, ProcessorTimeline,
DateRangePicker, expandable rows) while preserving all existing API
integration, auth, and routing infrastructure.
Pages migrated: Dashboard, RoutesMetrics, RouteDetail, ExchangeDetail,
AgentHealth, AgentInstance, OidcConfig, AuditLog, RBAC (Users/Groups/Roles).
Also enhanced LayoutShell CommandPalette with real search data from catalog.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:42:16 +01:00
|
|
|
const { data: agents } = useAgents();
|
2026-04-01 21:39:27 +02:00
|
|
|
const { data: attributeKeys } = useAttributeKeys();
|
feat: replace UI with design system example pages wired to real API
Migrate all page components from the @cameleer/design-system v0.0.3
example UI, replacing mock data with real backend API hooks. This brings
richer visuals (KpiStrip, GroupCard, RouteFlow, ProcessorTimeline,
DateRangePicker, expandable rows) while preserving all existing API
integration, auth, and routing infrastructure.
Pages migrated: Dashboard, RoutesMetrics, RouteDetail, ExchangeDetail,
AgentHealth, AgentInstance, OidcConfig, AuditLog, RBAC (Users/Groups/Roles).
Also enhanced LayoutShell CommandPalette with real search data from catalog.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:42:16 +01:00
|
|
|
const { username, 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();
|
2026-03-28 14:01:52 +01:00
|
|
|
const { scope, setTab } = useScope();
|
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
|
|
|
|
2026-04-02 18:29:25 +02:00
|
|
|
// --- Starred items ------------------------------------------------
|
2026-04-02 22:29:44 +02:00
|
|
|
const { starredIds, isStarred, toggleStar } = useStarred();
|
2026-04-02 18:29:25 +02:00
|
|
|
|
|
|
|
|
// --- Sidebar collapse ---------------------------------------------
|
|
|
|
|
const [sidebarCollapsed, setSidebarCollapsed] = useState(() => readCollapsed(SK_COLLAPSED, false));
|
|
|
|
|
const handleCollapseToggle = useCallback(() => {
|
|
|
|
|
setSidebarCollapsed((prev) => {
|
|
|
|
|
writeCollapsed(SK_COLLAPSED, !prev);
|
|
|
|
|
return !prev;
|
|
|
|
|
});
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
// --- Sidebar filter -----------------------------------------------
|
|
|
|
|
const [filterQuery, setFilterQuery] = useState('');
|
|
|
|
|
|
|
|
|
|
// --- Section open states ------------------------------------------
|
2026-04-02 18:36:43 +02:00
|
|
|
const isAdminPage = location.pathname.startsWith('/admin');
|
|
|
|
|
const [appsOpen, setAppsOpen] = useState(() => isAdminPage ? false : readCollapsed(SK_APPS, true));
|
|
|
|
|
const [adminOpen, setAdminOpen] = useState(() => isAdminPage ? true : readCollapsed(SK_ADMIN, false));
|
2026-04-02 22:29:44 +02:00
|
|
|
const [starredOpen, setStarredOpen] = useState(true);
|
2026-04-02 18:29:25 +02:00
|
|
|
|
2026-04-02 22:29:44 +02:00
|
|
|
// Accordion: entering admin collapses apps + starred; leaving restores
|
|
|
|
|
const opsStateRef = useRef({ apps: appsOpen, starred: starredOpen });
|
2026-04-02 18:29:25 +02:00
|
|
|
const prevAdminRef = useRef(isAdminPage);
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (isAdminPage && !prevAdminRef.current) {
|
2026-04-02 22:29:44 +02:00
|
|
|
opsStateRef.current = { apps: appsOpen, starred: starredOpen };
|
2026-04-02 18:29:25 +02:00
|
|
|
setAppsOpen(false);
|
2026-04-02 22:29:44 +02:00
|
|
|
setStarredOpen(false);
|
2026-04-02 18:29:25 +02:00
|
|
|
setAdminOpen(true);
|
|
|
|
|
} else if (!isAdminPage && prevAdminRef.current) {
|
|
|
|
|
setAppsOpen(opsStateRef.current.apps);
|
2026-04-02 22:29:44 +02:00
|
|
|
setStarredOpen(opsStateRef.current.starred);
|
2026-04-02 18:29:25 +02:00
|
|
|
setAdminOpen(false);
|
|
|
|
|
}
|
|
|
|
|
prevAdminRef.current = isAdminPage;
|
|
|
|
|
}, [isAdminPage]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
|
|
|
|
|
|
const toggleApps = useCallback(() => {
|
|
|
|
|
if (isAdminPage) {
|
|
|
|
|
navigate('/exchanges');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
setAppsOpen((prev) => {
|
|
|
|
|
writeCollapsed(SK_APPS, !prev);
|
|
|
|
|
return !prev;
|
|
|
|
|
});
|
|
|
|
|
}, [isAdminPage, navigate]);
|
|
|
|
|
|
2026-04-02 22:29:44 +02:00
|
|
|
const toggleAdmin = useCallback(() => {
|
|
|
|
|
if (!isAdminPage) {
|
|
|
|
|
navigate('/admin/rbac');
|
2026-04-02 18:32:26 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2026-04-02 18:29:25 +02:00
|
|
|
setAdminOpen((prev) => {
|
|
|
|
|
writeCollapsed(SK_ADMIN, !prev);
|
|
|
|
|
return !prev;
|
|
|
|
|
});
|
2026-04-02 22:29:44 +02:00
|
|
|
}, [isAdminPage, navigate]);
|
2026-04-02 18:29:25 +02:00
|
|
|
|
|
|
|
|
// --- Build SidebarApp[] from catalog ------------------------------
|
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 sidebarApps: SidebarApp[] = useMemo(() => {
|
|
|
|
|
if (!catalog) return [];
|
2026-04-01 18:24:39 +02:00
|
|
|
const cmp = (a: string, b: string) => a.localeCompare(b);
|
|
|
|
|
return [...catalog]
|
|
|
|
|
.sort((a: any, b: any) => cmp(a.appId, b.appId))
|
|
|
|
|
.map((app: any) => ({
|
|
|
|
|
id: app.appId,
|
|
|
|
|
name: app.appId,
|
|
|
|
|
health: app.health as 'live' | 'stale' | 'dead',
|
|
|
|
|
exchangeCount: app.exchangeCount,
|
|
|
|
|
routes: [...(app.routes || [])]
|
|
|
|
|
.sort((a: any, b: any) => cmp(a.routeId, b.routeId))
|
|
|
|
|
.map((r: any) => ({
|
|
|
|
|
id: r.routeId,
|
|
|
|
|
name: r.routeId,
|
|
|
|
|
exchangeCount: r.exchangeCount,
|
2026-04-02 19:15:46 +02:00
|
|
|
routeState: r.routeState ?? undefined,
|
2026-04-01 18:24:39 +02:00
|
|
|
})),
|
2026-04-02 22:29:44 +02:00
|
|
|
agents: [],
|
2026-04-01 18:24:39 +02:00
|
|
|
}));
|
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
|
|
|
}, [catalog]);
|
|
|
|
|
|
2026-04-02 18:29:25 +02:00
|
|
|
// --- Tree nodes ---------------------------------------------------
|
|
|
|
|
const appTreeNodes: SidebarTreeNode[] = useMemo(
|
2026-04-02 19:15:46 +02:00
|
|
|
() => buildAppTreeNodes(sidebarApps, makeStatusDot, makeChevron, makeStopIcon, makePauseIcon),
|
2026-04-02 18:29:25 +02:00
|
|
|
[sidebarApps],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const adminTreeNodes: SidebarTreeNode[] = useMemo(
|
|
|
|
|
() => buildAdminTreeNodes(),
|
|
|
|
|
[],
|
|
|
|
|
);
|
|
|
|
|
|
2026-04-02 22:29:44 +02:00
|
|
|
// --- Starred items ------------------------------------------------
|
|
|
|
|
const starredItems = useMemo(
|
|
|
|
|
() => collectStarredItems(sidebarApps, starredIds),
|
|
|
|
|
[sidebarApps, starredIds],
|
|
|
|
|
);
|
|
|
|
|
|
2026-04-02 18:29:25 +02:00
|
|
|
// --- Reveal path for SidebarTree auto-expand ----------------------
|
|
|
|
|
const sidebarRevealPath = (location.state as any)?.sidebarReveal ?? null;
|
|
|
|
|
|
2026-04-02 22:29:44 +02:00
|
|
|
useEffect(() => {
|
|
|
|
|
if (!sidebarRevealPath) return;
|
|
|
|
|
if (sidebarRevealPath.startsWith('/apps') && !appsOpen) setAppsOpen(true);
|
|
|
|
|
if (sidebarRevealPath.startsWith('/admin') && !adminOpen) setAdminOpen(true);
|
|
|
|
|
}, [sidebarRevealPath]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
|
|
|
|
|
|
const effectiveSelectedPath = sidebarRevealPath ?? location.pathname;
|
|
|
|
|
|
2026-04-02 18:29:25 +02:00
|
|
|
// --- Exchange full-text search via command palette -----------------
|
|
|
|
|
const [paletteQuery, setPaletteQuery] = useState('');
|
|
|
|
|
const debouncedQuery = useDebouncedValue(paletteQuery, 300);
|
|
|
|
|
const { data: exchangeResults } = useSearchExecutions(
|
|
|
|
|
{
|
|
|
|
|
text: debouncedQuery || undefined,
|
|
|
|
|
applicationId: scope.appId || undefined,
|
|
|
|
|
routeId: scope.routeId || undefined,
|
|
|
|
|
offset: 0,
|
|
|
|
|
limit: 10,
|
|
|
|
|
},
|
|
|
|
|
false,
|
|
|
|
|
);
|
|
|
|
|
|
2026-03-25 08:57:24 +01:00
|
|
|
const catalogData = useMemo(
|
2026-04-01 21:39:27 +02:00
|
|
|
() => buildSearchData(catalog, agents as any[], attributeKeys),
|
|
|
|
|
[catalog, agents, attributeKeys],
|
feat: replace UI with design system example pages wired to real API
Migrate all page components from the @cameleer/design-system v0.0.3
example UI, replacing mock data with real backend API hooks. This brings
richer visuals (KpiStrip, GroupCard, RouteFlow, ProcessorTimeline,
DateRangePicker, expandable rows) while preserving all existing API
integration, auth, and routing infrastructure.
Pages migrated: Dashboard, RoutesMetrics, RouteDetail, ExchangeDetail,
AgentHealth, AgentInstance, OidcConfig, AuditLog, RBAC (Users/Groups/Roles).
Also enhanced LayoutShell CommandPalette with real search data from catalog.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:42:16 +01:00
|
|
|
);
|
|
|
|
|
|
2026-04-01 21:22:50 +02:00
|
|
|
const catalogRef = useRef(catalogData);
|
|
|
|
|
if (catalogData !== catalogRef.current && JSON.stringify(catalogData) !== JSON.stringify(catalogRef.current)) {
|
|
|
|
|
catalogRef.current = catalogData;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 08:57:24 +01:00
|
|
|
const searchData: SearchResult[] = useMemo(() => {
|
|
|
|
|
const exchangeItems: SearchResult[] = (exchangeResults?.data || []).map((e: any) => ({
|
|
|
|
|
id: e.executionId,
|
|
|
|
|
category: 'exchange' as const,
|
|
|
|
|
title: e.executionId,
|
|
|
|
|
badges: [{ label: e.status, color: statusToColor(e.status) }],
|
fix: update frontend field names for identity rename (applicationId, instanceId)
The backend identity rename (applicationName → applicationId,
agentId → instanceId) was not reflected in the frontend. This caused
drilldown to fail (detail.applicationName was undefined, disabling
the diagram fetch) and various display issues.
Updated schema.d.ts, ExchangeHeader, ExecutionDiagram, Dashboard,
AgentHealth, AgentInstance, LayoutShell, LogTab, InfoTab, DetailPanel,
ExchangesPage, and tracing-store.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 18:22:16 +02:00
|
|
|
meta: `${e.routeId} · ${e.applicationId ?? ''} · ${formatDuration(e.durationMs)}`,
|
|
|
|
|
path: `/exchanges/${e.applicationId ?? ''}/${e.routeId}/${e.executionId}`,
|
2026-03-25 08:57:24 +01:00
|
|
|
serverFiltered: true,
|
2026-03-25 09:29:07 +01:00
|
|
|
matchContext: e.highlight ?? undefined,
|
2026-03-25 08:57:24 +01:00
|
|
|
}));
|
2026-03-27 08:13:58 +01:00
|
|
|
|
|
|
|
|
const attributeItems: SearchResult[] = [];
|
|
|
|
|
if (debouncedQuery) {
|
|
|
|
|
const q = debouncedQuery.toLowerCase();
|
|
|
|
|
for (const e of exchangeResults?.data || []) {
|
|
|
|
|
if (!e.attributes) continue;
|
|
|
|
|
for (const [key, value] of Object.entries(e.attributes as Record<string, string>)) {
|
|
|
|
|
if (key.toLowerCase().includes(q) || String(value).toLowerCase().includes(q)) {
|
|
|
|
|
attributeItems.push({
|
|
|
|
|
id: `${e.executionId}-attr-${key}`,
|
|
|
|
|
category: 'attribute' as const,
|
|
|
|
|
title: `${key} = "${value}"`,
|
|
|
|
|
badges: [{ label: e.status, color: statusToColor(e.status) }],
|
fix: update frontend field names for identity rename (applicationId, instanceId)
The backend identity rename (applicationName → applicationId,
agentId → instanceId) was not reflected in the frontend. This caused
drilldown to fail (detail.applicationName was undefined, disabling
the diagram fetch) and various display issues.
Updated schema.d.ts, ExchangeHeader, ExecutionDiagram, Dashboard,
AgentHealth, AgentInstance, LayoutShell, LogTab, InfoTab, DetailPanel,
ExchangesPage, and tracing-store.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 18:22:16 +02:00
|
|
|
meta: `${e.executionId} · ${e.routeId} · ${e.applicationId ?? ''}`,
|
|
|
|
|
path: `/exchanges/${e.applicationId ?? ''}/${e.routeId}/${e.executionId}`,
|
2026-03-27 08:13:58 +01:00
|
|
|
serverFiltered: true,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-01 21:22:50 +02:00
|
|
|
return [...catalogRef.current, ...exchangeItems, ...attributeItems];
|
|
|
|
|
}, [catalogRef.current, exchangeResults, debouncedQuery]);
|
2026-03-25 08:57:24 +01:00
|
|
|
|
2026-04-02 18:29:25 +02:00
|
|
|
// --- Breadcrumb ---------------------------------------------------
|
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 breadcrumb = useMemo(() => {
|
2026-04-02 17:22:06 +02:00
|
|
|
if (isAdminPage) {
|
|
|
|
|
const LABELS: Record<string, string> = {
|
|
|
|
|
admin: 'Admin',
|
|
|
|
|
rbac: 'Users & Roles',
|
|
|
|
|
audit: 'Audit Log',
|
|
|
|
|
oidc: 'OIDC',
|
|
|
|
|
database: 'Database',
|
|
|
|
|
clickhouse: 'ClickHouse',
|
|
|
|
|
appconfig: 'App Config',
|
|
|
|
|
};
|
|
|
|
|
const parts = location.pathname.split('/').filter(Boolean);
|
|
|
|
|
return parts.map((part, i) => ({
|
|
|
|
|
label: LABELS[part] ?? part,
|
|
|
|
|
...(i < parts.length - 1 ? { href: '/' + parts.slice(0, i + 1).join('/') } : {}),
|
|
|
|
|
}));
|
|
|
|
|
}
|
2026-03-28 14:26:36 +01:00
|
|
|
const items: { label: string; href?: string }[] = [
|
|
|
|
|
{ label: 'All Applications', href: `/${scope.tab}` },
|
|
|
|
|
];
|
|
|
|
|
if (scope.appId) {
|
|
|
|
|
items.push({ label: scope.appId, href: `/${scope.tab}/${scope.appId}` });
|
|
|
|
|
}
|
|
|
|
|
if (scope.routeId) {
|
|
|
|
|
items.push({ label: scope.routeId });
|
|
|
|
|
}
|
|
|
|
|
if (items.length > 0 && !scope.routeId && !scope.appId) {
|
|
|
|
|
delete items[items.length - 1].href;
|
|
|
|
|
}
|
|
|
|
|
return items;
|
2026-04-02 17:22:06 +02:00
|
|
|
}, [location.pathname, isAdminPage, scope.tab, scope.appId, scope.routeId]);
|
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
|
|
|
|
2026-04-02 18:29:25 +02:00
|
|
|
// --- Callbacks ----------------------------------------------------
|
2026-03-23 18:17:38 +01:00
|
|
|
const handleLogout = useCallback(() => {
|
|
|
|
|
logout();
|
|
|
|
|
navigate('/login');
|
|
|
|
|
}, [logout, navigate]);
|
|
|
|
|
|
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 handlePaletteSelect = useCallback((result: any) => {
|
feat: replace UI with design system example pages wired to real API
Migrate all page components from the @cameleer/design-system v0.0.3
example UI, replacing mock data with real backend API hooks. This brings
richer visuals (KpiStrip, GroupCard, RouteFlow, ProcessorTimeline,
DateRangePicker, expandable rows) while preserving all existing API
integration, auth, and routing infrastructure.
Pages migrated: Dashboard, RoutesMetrics, RouteDetail, ExchangeDetail,
AgentHealth, AgentInstance, OidcConfig, AuditLog, RBAC (Users/Groups/Roles).
Also enhanced LayoutShell CommandPalette with real search data from catalog.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:42:16 +01:00
|
|
|
if (result.path) {
|
2026-03-31 15:26:36 +02:00
|
|
|
const state: Record<string, unknown> = { sidebarReveal: result.path };
|
|
|
|
|
if (result.category === 'exchange' || result.category === 'attribute') {
|
|
|
|
|
const parts = result.path.split('/').filter(Boolean);
|
|
|
|
|
if (parts.length === 4 && parts[0] === 'exchanges') {
|
|
|
|
|
state.selectedExchange = {
|
|
|
|
|
executionId: parts[3],
|
fix: update frontend field names for identity rename (applicationId, instanceId)
The backend identity rename (applicationName → applicationId,
agentId → instanceId) was not reflected in the frontend. This caused
drilldown to fail (detail.applicationName was undefined, disabling
the diagram fetch) and various display issues.
Updated schema.d.ts, ExchangeHeader, ExecutionDiagram, Dashboard,
AgentHealth, AgentInstance, LayoutShell, LogTab, InfoTab, DetailPanel,
ExchangesPage, and tracing-store.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 18:22:16 +02:00
|
|
|
applicationId: parts[1],
|
2026-03-31 15:26:36 +02:00
|
|
|
routeId: parts[2],
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
navigate(result.path, { state });
|
feat: replace UI with design system example pages wired to real API
Migrate all page components from the @cameleer/design-system v0.0.3
example UI, replacing mock data with real backend API hooks. This brings
richer visuals (KpiStrip, GroupCard, RouteFlow, ProcessorTimeline,
DateRangePicker, expandable rows) while preserving all existing API
integration, auth, and routing infrastructure.
Pages migrated: Dashboard, RoutesMetrics, RouteDetail, ExchangeDetail,
AgentHealth, AgentInstance, OidcConfig, AuditLog, RBAC (Users/Groups/Roles).
Also enhanced LayoutShell CommandPalette with real search data from catalog.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:42:16 +01:00
|
|
|
}
|
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
|
|
|
setPaletteOpen(false);
|
|
|
|
|
}, [navigate, setPaletteOpen]);
|
|
|
|
|
|
2026-03-27 23:33:39 +01:00
|
|
|
const handlePaletteSubmit = useCallback((query: string) => {
|
2026-03-28 14:01:52 +01:00
|
|
|
const baseParts = ['/exchanges'];
|
|
|
|
|
if (scope.appId) baseParts.push(scope.appId);
|
|
|
|
|
if (scope.routeId) baseParts.push(scope.routeId);
|
|
|
|
|
navigate(`${baseParts.join('/')}?text=${encodeURIComponent(query)}`);
|
|
|
|
|
}, [navigate, scope.appId, scope.routeId]);
|
|
|
|
|
|
2026-03-28 16:57:12 +01:00
|
|
|
const handleSidebarNavigate = useCallback((path: string) => {
|
2026-04-01 20:55:19 +02:00
|
|
|
const state = { sidebarReveal: path };
|
|
|
|
|
|
2026-03-28 16:57:12 +01:00
|
|
|
const appMatch = path.match(/^\/apps\/([^/]+)(?:\/(.+))?$/);
|
2026-03-28 14:01:52 +01:00
|
|
|
if (appMatch) {
|
|
|
|
|
const [, sAppId, sRouteId] = appMatch;
|
2026-04-01 20:55:19 +02:00
|
|
|
navigate(sRouteId ? `/${scope.tab}/${sAppId}/${sRouteId}` : `/${scope.tab}/${sAppId}`, { state });
|
2026-03-28 14:01:52 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-28 16:57:12 +01:00
|
|
|
const agentMatch = path.match(/^\/agents\/([^/]+)(?:\/(.+))?$/);
|
2026-03-28 14:01:52 +01:00
|
|
|
if (agentMatch) {
|
|
|
|
|
const [, sAppId, sInstanceId] = agentMatch;
|
2026-04-01 20:55:19 +02:00
|
|
|
navigate(sInstanceId ? `/runtime/${sAppId}/${sInstanceId}` : `/runtime/${sAppId}`, { state });
|
2026-03-28 16:57:12 +01:00
|
|
|
return;
|
2026-03-28 14:01:52 +01:00
|
|
|
}
|
2026-03-28 16:57:12 +01:00
|
|
|
|
2026-04-01 20:55:19 +02:00
|
|
|
navigate(path, { state });
|
2026-03-28 14:01:52 +01:00
|
|
|
}, [navigate, scope.tab]);
|
2026-03-27 23:33:39 +01:00
|
|
|
|
2026-04-02 18:29:25 +02:00
|
|
|
// --- Render -------------------------------------------------------
|
2026-04-02 22:29:44 +02:00
|
|
|
const camelLogo = (
|
|
|
|
|
<img src="/favicon.svg" alt="" style={{ width: 28, height: 24 }} />
|
|
|
|
|
);
|
|
|
|
|
|
2026-04-02 18:29:25 +02:00
|
|
|
const sidebarElement = (
|
|
|
|
|
<Sidebar
|
|
|
|
|
collapsed={sidebarCollapsed}
|
|
|
|
|
onCollapseToggle={handleCollapseToggle}
|
|
|
|
|
searchValue={filterQuery}
|
|
|
|
|
onSearchChange={setFilterQuery}
|
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
|
|
|
>
|
2026-04-02 18:29:25 +02:00
|
|
|
<Sidebar.Header
|
2026-04-02 22:29:44 +02:00
|
|
|
logo={camelLogo}
|
2026-04-02 18:29:25 +02:00
|
|
|
title="Cameleer"
|
|
|
|
|
/>
|
|
|
|
|
|
2026-04-02 22:29:44 +02:00
|
|
|
{/* Applications section */}
|
2026-04-02 18:29:25 +02:00
|
|
|
<Sidebar.Section
|
|
|
|
|
icon={createElement(Box, { size: 16 })}
|
|
|
|
|
label="Applications"
|
|
|
|
|
open={appsOpen}
|
|
|
|
|
onToggle={toggleApps}
|
|
|
|
|
>
|
|
|
|
|
<SidebarTree
|
|
|
|
|
nodes={appTreeNodes}
|
2026-04-02 22:29:44 +02:00
|
|
|
selectedPath={effectiveSelectedPath}
|
2026-04-02 18:29:25 +02:00
|
|
|
isStarred={isStarred}
|
|
|
|
|
onToggleStar={toggleStar}
|
|
|
|
|
filterQuery={filterQuery}
|
|
|
|
|
persistKey="apps"
|
|
|
|
|
autoRevealPath={sidebarRevealPath}
|
|
|
|
|
onNavigate={handleSidebarNavigate}
|
|
|
|
|
/>
|
|
|
|
|
</Sidebar.Section>
|
|
|
|
|
|
2026-04-02 22:29:44 +02:00
|
|
|
{/* Starred section — only when there are starred items */}
|
|
|
|
|
{starredItems.length > 0 && (
|
2026-04-02 18:29:25 +02:00
|
|
|
<Sidebar.Section
|
2026-04-02 22:29:44 +02:00
|
|
|
icon={createElement(Star, { size: 16 })}
|
|
|
|
|
label="Starred"
|
|
|
|
|
open={starredOpen}
|
|
|
|
|
onToggle={() => setStarredOpen((v) => !v)}
|
2026-04-02 18:29:25 +02:00
|
|
|
>
|
2026-04-02 22:29:44 +02:00
|
|
|
<StarredList
|
|
|
|
|
items={starredItems}
|
2026-04-02 18:29:25 +02:00
|
|
|
onNavigate={handleSidebarNavigate}
|
2026-04-02 22:29:44 +02:00
|
|
|
onRemove={toggleStar}
|
2026-04-02 18:29:25 +02:00
|
|
|
/>
|
|
|
|
|
</Sidebar.Section>
|
|
|
|
|
)}
|
|
|
|
|
|
2026-04-02 22:32:53 +02:00
|
|
|
{/* Admin section — stays in place, expands when on admin pages */}
|
|
|
|
|
<Sidebar.Section
|
|
|
|
|
icon={createElement(Settings, { size: 16 })}
|
|
|
|
|
label="Admin"
|
|
|
|
|
open={adminOpen}
|
|
|
|
|
onToggle={toggleAdmin}
|
|
|
|
|
active={isAdminPage}
|
|
|
|
|
>
|
|
|
|
|
<SidebarTree
|
|
|
|
|
nodes={adminTreeNodes}
|
|
|
|
|
selectedPath={location.pathname}
|
|
|
|
|
isStarred={isStarred}
|
|
|
|
|
onToggleStar={toggleStar}
|
|
|
|
|
filterQuery={filterQuery}
|
|
|
|
|
persistKey="admin"
|
|
|
|
|
autoRevealPath={sidebarRevealPath}
|
|
|
|
|
onNavigate={handleSidebarNavigate}
|
|
|
|
|
/>
|
|
|
|
|
</Sidebar.Section>
|
|
|
|
|
|
|
|
|
|
{/* Footer */}
|
2026-04-02 18:29:25 +02:00
|
|
|
<Sidebar.Footer>
|
|
|
|
|
<Sidebar.FooterLink
|
|
|
|
|
icon={createElement(FileText, { size: 16 })}
|
|
|
|
|
label="API Docs"
|
|
|
|
|
active={location.pathname === '/api-docs'}
|
|
|
|
|
onClick={() => handleSidebarNavigate('/api-docs')}
|
|
|
|
|
/>
|
|
|
|
|
</Sidebar.Footer>
|
|
|
|
|
</Sidebar>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<AppShell sidebar={sidebarElement}>
|
2026-04-02 17:22:06 +02:00
|
|
|
<TopBar
|
|
|
|
|
breadcrumb={breadcrumb}
|
|
|
|
|
user={username ? { name: username } : undefined}
|
|
|
|
|
onLogout={handleLogout}
|
|
|
|
|
/>
|
|
|
|
|
<CommandPalette
|
|
|
|
|
open={paletteOpen}
|
|
|
|
|
onClose={() => setPaletteOpen(false)}
|
|
|
|
|
onOpen={() => setPaletteOpen(true)}
|
|
|
|
|
onSelect={handlePaletteSelect}
|
|
|
|
|
onSubmit={handlePaletteSubmit}
|
|
|
|
|
onQueryChange={setPaletteQuery}
|
|
|
|
|
data={searchData}
|
|
|
|
|
/>
|
2026-03-28 14:01:52 +01:00
|
|
|
|
|
|
|
|
{!isAdminPage && (
|
2026-03-28 14:42:58 +01:00
|
|
|
<ContentTabs active={scope.tab} onChange={setTab} scope={scope} />
|
2026-03-28 14:01:52 +01:00
|
|
|
)}
|
|
|
|
|
|
2026-04-02 18:32:26 +02:00
|
|
|
<main style={{ flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden', minHeight: 0 }}>
|
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
|
|
|
<Outlet />
|
|
|
|
|
</main>
|
|
|
|
|
</AppShell>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function LayoutShell() {
|
|
|
|
|
return (
|
2026-03-23 18:17:38 +01:00
|
|
|
<ToastProvider>
|
|
|
|
|
<CommandPaletteProvider>
|
|
|
|
|
<GlobalFilterProvider>
|
2026-03-26 11:40:37 +01:00
|
|
|
<BreadcrumbProvider>
|
|
|
|
|
<LayoutContent />
|
|
|
|
|
</BreadcrumbProvider>
|
2026-03-23 18:17:38 +01:00
|
|
|
</GlobalFilterProvider>
|
|
|
|
|
</CommandPaletteProvider>
|
|
|
|
|
</ToastProvider>
|
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
|
|
|
);
|
|
|
|
|
}
|