2026-04-04 17:12:16 +02:00
|
|
|
import { Outlet, useNavigate, useLocation } from 'react-router';
|
2026-04-06 01:17:17 +02:00
|
|
|
import { config } from '../config';
|
2026-04-02 18:29:25 +02:00
|
|
|
import {
|
|
|
|
|
AppShell,
|
|
|
|
|
Sidebar,
|
|
|
|
|
SidebarTree,
|
|
|
|
|
StatusDot,
|
|
|
|
|
TopBar,
|
2026-04-10 17:06:03 +02:00
|
|
|
SearchTrigger,
|
|
|
|
|
AutoRefreshToggle,
|
|
|
|
|
ButtonGroup,
|
|
|
|
|
TimeRangeDropdown,
|
2026-04-02 18:29:25 +02:00
|
|
|
CommandPalette,
|
|
|
|
|
CommandPaletteProvider,
|
|
|
|
|
GlobalFilterProvider,
|
|
|
|
|
ToastProvider,
|
|
|
|
|
BreadcrumbProvider,
|
|
|
|
|
useCommandPalette,
|
|
|
|
|
useGlobalFilters,
|
|
|
|
|
useStarred,
|
|
|
|
|
} from '@cameleer/design-system';
|
2026-04-10 20:10:08 +02:00
|
|
|
import type { SearchResult, SidebarTreeNode, DropdownItem, ButtonGroupItem, ExchangeStatus } from '@cameleer/design-system';
|
2026-04-15 15:28:42 +02:00
|
|
|
import sidebarLogo from '@cameleer/design-system/assets/cameleer-logo.svg';
|
fix: resolve UI glitches and improve consistency
- Sidebar: make +App button more subtle (lower opacity, brightens on hover)
- Sidebar: add filter chips to hide empty routes and offline/stale apps
- Sidebar: hide filter chips and +App button when sidebar is collapsed
- Exchange table: reorder columns to Status, Attributes, App, Route, Started, Duration; remove ExchangeId and Agent columns
- Exchange detail log tab: query by exchangeId only (no applicationId required), filter by processorId when processor selected
- KPI tooltips: styled tooltips with current/previous values, time period labels, percentage change, themed with DS variables
- KPI tooltips: fix overflow by left-aligning first two and right-aligning last two
- Exchange detail: show full datetime (YYYY-MM-DD HH:mm:ss.SSS) for start/end times
- Status labels: unify to title-case (Completed, Failed, Running) across all views
- Status filter buttons: match title-case labels (Completed, Warning, Failed, Running)
- Create app: show full external URL using routingDomain from env config or window.location.origin fallback
- Create app: add Runtime Type selector and Custom Arguments to Resources tab
- Create app: add Sensitive Keys tab with agent defaults, global keys, and app-specific keys (matching admin page design)
- Create app: add placeholder text to all Input fields for consistency
- Update design-system to 0.1.52 (sidebar collapse toggle fix)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:41:36 +02:00
|
|
|
import { Box, Settings, FileText, ChevronRight, Square, Pause, Star, X, User, Plus, EyeOff } from 'lucide-react';
|
2026-04-08 12:12:29 +02:00
|
|
|
import { AboutMeDialog } from './AboutMeDialog';
|
2026-04-07 11:45:02 +02:00
|
|
|
import css from './LayoutShell.module.css';
|
2026-04-03 11:01:29 +02:00
|
|
|
import { useQueryClient } from '@tanstack/react-query';
|
2026-04-08 23:43:14 +02:00
|
|
|
import { useCatalog } 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';
|
2026-04-02 23:34:52 +02:00
|
|
|
import { useUsers, useGroups, useRoles } from '../api/queries/admin/rbac';
|
2026-04-08 16:50:31 +02:00
|
|
|
import { useEnvironments } from '../api/queries/admin/environments';
|
2026-04-02 23:34:52 +02:00
|
|
|
import type { UserDetail, GroupDetail, RoleDetail } from '../api/queries/admin/rbac';
|
2026-04-14 09:10:41 +02:00
|
|
|
import { useAuthStore, useIsAdmin, useCanControl } from '../auth/auth-store';
|
2026-04-04 17:12:16 +02:00
|
|
|
import { useEnvironmentStore } from '../api/environment-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';
|
2026-04-04 15:42:26 +02:00
|
|
|
import { EnvironmentSelector } from './EnvironmentSelector';
|
2026-03-28 14:01:52 +01:00
|
|
|
import { useScope } from '../hooks/useScope';
|
2026-04-09 08:28:31 +02:00
|
|
|
import { formatDuration } from '../utils/format-utils';
|
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';
|
2026-04-11 23:12:30 +02:00
|
|
|
import { useServerCapabilities } from '../api/queries/capabilities';
|
2026-04-02 18:29:25 +02:00
|
|
|
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) {
|
2026-04-08 23:43:14 +02:00
|
|
|
const slug = app.slug || app.appId;
|
|
|
|
|
const name = app.displayName || slug;
|
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 liveAgents = (app.agents || []).filter((a: any) => a.status === 'live').length;
|
|
|
|
|
results.push({
|
2026-04-08 23:43:14 +02:00
|
|
|
id: slug,
|
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: 'application',
|
2026-04-08 23:43:14 +02:00
|
|
|
title: name,
|
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-04-08 23:43:14 +02:00
|
|
|
path: `/exchanges/${slug}`,
|
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-08 23:43:14 +02:00
|
|
|
id: `${slug}/${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,
|
2026-04-08 23:43:14 +02:00
|
|
|
badges: [{ label: name }],
|
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: `${(route.exchangeCount ?? 0).toLocaleString()} exchanges`,
|
2026-04-08 23:43:14 +02:00
|
|
|
path: `/exchanges/${slug}/${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 23:34:52 +02:00
|
|
|
function buildAdminSearchData(
|
|
|
|
|
users: UserDetail[] | undefined,
|
|
|
|
|
groups: GroupDetail[] | undefined,
|
|
|
|
|
roles: RoleDetail[] | undefined,
|
|
|
|
|
): SearchResult[] {
|
|
|
|
|
const results: SearchResult[] = [];
|
|
|
|
|
|
|
|
|
|
if (users) {
|
|
|
|
|
for (const u of users) {
|
|
|
|
|
results.push({
|
|
|
|
|
id: `user:${u.userId}`,
|
|
|
|
|
category: 'user',
|
|
|
|
|
title: u.displayName || u.userId,
|
|
|
|
|
meta: u.userId,
|
|
|
|
|
path: '/admin/rbac',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (groups) {
|
|
|
|
|
for (const g of groups) {
|
|
|
|
|
results.push({
|
|
|
|
|
id: `group:${g.id}`,
|
|
|
|
|
category: 'group',
|
|
|
|
|
title: g.name,
|
|
|
|
|
meta: g.parentGroupId ? `parent: ${g.parentGroupId}` : 'top-level group',
|
|
|
|
|
path: '/admin/rbac',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (roles) {
|
|
|
|
|
for (const r of roles) {
|
|
|
|
|
results.push({
|
|
|
|
|
id: `role:${r.id}`,
|
|
|
|
|
category: 'role',
|
|
|
|
|
title: r.name,
|
|
|
|
|
meta: r.scope,
|
|
|
|
|
path: '/admin/rbac',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return results;
|
|
|
|
|
}
|
|
|
|
|
|
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 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 (
|
refactor: UI consistency — shared CSS, design system colors, no inline styles
Phase 1: Extract 6 shared CSS modules (table-section, log-panel,
rate-colors, refresh-indicator, chart-card, section-card) eliminating
~135 duplicate class definitions across 11 files.
Phase 2: Replace all hardcoded hex colors in CSS modules with design
system variables. Strip ~55 hex fallbacks from var() patterns. Fix 4
undefined variable names (--accent, --bg-base, --surface, --bg-surface-raised).
Phase 3: Replace ~45 hardcoded hex values in ProcessDiagram SVG
components with var() CSS custom properties. Fix Dashboard.tsx color prop.
Phase 4: Create CSS modules for AdminLayout, DatabaseAdminPage,
OidcCallback (previously 100% inline). Extract shared PageLoader
component (replaces 3 copy-pasted spinner patterns). Move AppsTab
static inline styles to CSS classes. Extract LayoutShell StarredList styles.
58 files changed, net -219 lines.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 14:55:54 +02:00
|
|
|
<div className={css.starredList}>
|
2026-04-02 22:29:44 +02:00
|
|
|
{items.map((item) => (
|
|
|
|
|
<div
|
|
|
|
|
key={item.starKey}
|
2026-04-07 11:45:02 +02:00
|
|
|
className={css.starredItem}
|
2026-04-02 22:29:44 +02:00
|
|
|
onClick={() => onNavigate(item.path)}
|
|
|
|
|
role="button"
|
|
|
|
|
tabIndex={0}
|
|
|
|
|
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') onNavigate(item.path); }}
|
|
|
|
|
>
|
refactor: UI consistency — shared CSS, design system colors, no inline styles
Phase 1: Extract 6 shared CSS modules (table-section, log-panel,
rate-colors, refresh-indicator, chart-card, section-card) eliminating
~135 duplicate class definitions across 11 files.
Phase 2: Replace all hardcoded hex colors in CSS modules with design
system variables. Strip ~55 hex fallbacks from var() patterns. Fix 4
undefined variable names (--accent, --bg-base, --surface, --bg-surface-raised).
Phase 3: Replace ~45 hardcoded hex values in ProcessDiagram SVG
components with var() CSS custom properties. Fix Dashboard.tsx color prop.
Phase 4: Create CSS modules for AdminLayout, DatabaseAdminPage,
OidcCallback (previously 100% inline). Extract shared PageLoader
component (replaces 3 copy-pasted spinner patterns). Move AppsTab
static inline styles to CSS classes. Extract LayoutShell StarredList styles.
58 files changed, net -219 lines.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 14:55:54 +02:00
|
|
|
{item.icon && <span className={css.starredIconWrap}>{item.icon}</span>}
|
|
|
|
|
<span className={css.starredLabel}>
|
2026-04-02 22:29:44 +02:00
|
|
|
{item.label}
|
2026-04-07 11:45:02 +02:00
|
|
|
{item.parentApp && <span className={css.starredParentApp}>{item.parentApp}</span>}
|
2026-04-02 22:29:44 +02:00
|
|
|
</span>
|
|
|
|
|
<button
|
refactor: UI consistency — shared CSS, design system colors, no inline styles
Phase 1: Extract 6 shared CSS modules (table-section, log-panel,
rate-colors, refresh-indicator, chart-card, section-card) eliminating
~135 duplicate class definitions across 11 files.
Phase 2: Replace all hardcoded hex colors in CSS modules with design
system variables. Strip ~55 hex fallbacks from var() patterns. Fix 4
undefined variable names (--accent, --bg-base, --surface, --bg-surface-raised).
Phase 3: Replace ~45 hardcoded hex values in ProcessDiagram SVG
components with var() CSS custom properties. Fix Dashboard.tsx color prop.
Phase 4: Create CSS modules for AdminLayout, DatabaseAdminPage,
OidcCallback (previously 100% inline). Extract shared PageLoader
component (replaces 3 copy-pasted spinner patterns). Move AppsTab
static inline styles to CSS classes. Extract LayoutShell StarredList styles.
58 files changed, net -219 lines.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 14:55:54 +02:00
|
|
|
className={css.starredRemoveBtn}
|
2026-04-02 22:29:44 +02:00
|
|
|
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
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
|
2026-04-10 17:06:03 +02:00
|
|
|
const STATUS_ITEMS: ButtonGroupItem[] = [
|
fix: resolve UI glitches and improve consistency
- Sidebar: make +App button more subtle (lower opacity, brightens on hover)
- Sidebar: add filter chips to hide empty routes and offline/stale apps
- Sidebar: hide filter chips and +App button when sidebar is collapsed
- Exchange table: reorder columns to Status, Attributes, App, Route, Started, Duration; remove ExchangeId and Agent columns
- Exchange detail log tab: query by exchangeId only (no applicationId required), filter by processorId when processor selected
- KPI tooltips: styled tooltips with current/previous values, time period labels, percentage change, themed with DS variables
- KPI tooltips: fix overflow by left-aligning first two and right-aligning last two
- Exchange detail: show full datetime (YYYY-MM-DD HH:mm:ss.SSS) for start/end times
- Status labels: unify to title-case (Completed, Failed, Running) across all views
- Status filter buttons: match title-case labels (Completed, Warning, Failed, Running)
- Create app: show full external URL using routingDomain from env config or window.location.origin fallback
- Create app: add Runtime Type selector and Custom Arguments to Resources tab
- Create app: add Sensitive Keys tab with agent defaults, global keys, and app-specific keys (matching admin page design)
- Create app: add placeholder text to all Input fields for consistency
- Update design-system to 0.1.52 (sidebar collapse toggle fix)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:41:36 +02:00
|
|
|
{ value: 'completed', label: 'Completed', color: 'var(--success)' },
|
|
|
|
|
{ value: 'warning', label: 'Warning', color: 'var(--warning)' },
|
|
|
|
|
{ value: 'failed', label: 'Failed', color: 'var(--error)' },
|
2026-04-10 17:06:03 +02:00
|
|
|
{ value: 'running', label: 'Running', color: 'var(--running)' },
|
|
|
|
|
]
|
|
|
|
|
|
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-04-03 11:01:29 +02:00
|
|
|
const queryClient = useQueryClient();
|
2026-04-10 17:06:03 +02:00
|
|
|
const globalFilters = useGlobalFilters();
|
|
|
|
|
const { timeRange, autoRefresh, refreshTimeRange } = globalFilters;
|
2026-04-04 15:42:26 +02:00
|
|
|
|
2026-04-11 23:12:30 +02:00
|
|
|
// --- Server capabilities ------------------------------------------
|
|
|
|
|
const { data: capabilities } = useServerCapabilities();
|
|
|
|
|
|
2026-04-06 15:51:15 +02:00
|
|
|
// --- Role checks ----------------------------------------------------
|
|
|
|
|
const isAdmin = useIsAdmin();
|
2026-04-14 09:10:41 +02:00
|
|
|
const canControl = useCanControl();
|
2026-04-06 15:51:15 +02:00
|
|
|
|
2026-04-04 15:42:26 +02:00
|
|
|
// --- Environment filtering -----------------------------------------
|
2026-04-04 17:12:16 +02:00
|
|
|
const selectedEnv = useEnvironmentStore((s) => s.environment);
|
|
|
|
|
const setSelectedEnvRaw = useEnvironmentStore((s) => s.setEnvironment);
|
2026-04-04 15:42:26 +02:00
|
|
|
|
2026-04-08 23:43:14 +02:00
|
|
|
const { data: catalog } = useCatalog(selectedEnv);
|
2026-04-04 15:47:48 +02:00
|
|
|
const { data: allAgents } = useAgents(); // unfiltered — for environment discovery
|
|
|
|
|
const { data: agents } = useAgents(undefined, undefined, selectedEnv); // filtered — for sidebar/search
|
2026-04-01 21:39:27 +02:00
|
|
|
const { data: attributeKeys } = useAttributeKeys();
|
2026-04-08 16:50:31 +02:00
|
|
|
const { data: envRecords = [] } = useEnvironments();
|
2026-04-02 23:34:52 +02:00
|
|
|
|
2026-04-08 16:50:31 +02:00
|
|
|
// Merge environments from both the environments table and agent heartbeats
|
2026-04-04 15:42:26 +02:00
|
|
|
const environments: string[] = useMemo(() => {
|
|
|
|
|
const envSet = new Set<string>();
|
2026-04-08 16:50:31 +02:00
|
|
|
for (const e of envRecords) envSet.add(e.slug);
|
|
|
|
|
if (allAgents) {
|
|
|
|
|
for (const a of allAgents as any[]) {
|
|
|
|
|
envSet.add(a.environmentId || 'default');
|
|
|
|
|
}
|
2026-04-04 15:42:26 +02:00
|
|
|
}
|
2026-04-08 16:50:31 +02:00
|
|
|
if (envSet.size === 0) envSet.add('default');
|
2026-04-04 15:42:26 +02:00
|
|
|
return [...envSet].sort();
|
2026-04-08 16:50:31 +02:00
|
|
|
}, [allAgents, envRecords]);
|
2026-04-04 15:42:26 +02:00
|
|
|
|
2026-04-02 23:34:52 +02:00
|
|
|
// --- Admin search data (only fetched on admin pages) ----------------
|
2026-04-03 14:48:30 +02:00
|
|
|
const isAdminPage = location.pathname.startsWith('/admin');
|
|
|
|
|
const { data: adminUsers } = useUsers(isAdminPage);
|
|
|
|
|
const { data: adminGroups } = useGroups(isAdminPage);
|
|
|
|
|
const { data: adminRoles } = useRoles(isAdminPage);
|
2026-04-02 23:34:52 +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
|
|
|
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('');
|
fix: resolve UI glitches and improve consistency
- Sidebar: make +App button more subtle (lower opacity, brightens on hover)
- Sidebar: add filter chips to hide empty routes and offline/stale apps
- Sidebar: hide filter chips and +App button when sidebar is collapsed
- Exchange table: reorder columns to Status, Attributes, App, Route, Started, Duration; remove ExchangeId and Agent columns
- Exchange detail log tab: query by exchangeId only (no applicationId required), filter by processorId when processor selected
- KPI tooltips: styled tooltips with current/previous values, time period labels, percentage change, themed with DS variables
- KPI tooltips: fix overflow by left-aligning first two and right-aligning last two
- Exchange detail: show full datetime (YYYY-MM-DD HH:mm:ss.SSS) for start/end times
- Status labels: unify to title-case (Completed, Failed, Running) across all views
- Status filter buttons: match title-case labels (Completed, Warning, Failed, Running)
- Create app: show full external URL using routingDomain from env config or window.location.origin fallback
- Create app: add Runtime Type selector and Custom Arguments to Resources tab
- Create app: add Sensitive Keys tab with agent defaults, global keys, and app-specific keys (matching admin page design)
- Create app: add placeholder text to all Input fields for consistency
- Update design-system to 0.1.52 (sidebar collapse toggle fix)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:41:36 +02:00
|
|
|
const [hideEmptyRoutes, setHideEmptyRoutes] = useState(() => readCollapsed('sidebar:hideEmptyRoutes', false));
|
|
|
|
|
const [hideOfflineApps, setHideOfflineApps] = useState(() => readCollapsed('sidebar:hideOfflineApps', false));
|
|
|
|
|
|
|
|
|
|
const toggleHideEmptyRoutes = useCallback(() => {
|
|
|
|
|
setHideEmptyRoutes((prev) => { writeCollapsed('sidebar:hideEmptyRoutes', !prev); return !prev; });
|
|
|
|
|
}, []);
|
|
|
|
|
const toggleHideOfflineApps = useCallback(() => {
|
|
|
|
|
setHideOfflineApps((prev) => { writeCollapsed('sidebar:hideOfflineApps', !prev); return !prev; });
|
|
|
|
|
}, []);
|
2026-04-02 18:29:25 +02:00
|
|
|
|
2026-04-04 17:12:16 +02:00
|
|
|
const setSelectedEnv = useCallback((env: string | undefined) => {
|
|
|
|
|
setSelectedEnvRaw(env);
|
|
|
|
|
setFilterQuery('');
|
|
|
|
|
if (location.search) {
|
|
|
|
|
navigate(location.pathname, { replace: true });
|
|
|
|
|
}
|
|
|
|
|
queryClient.invalidateQueries();
|
|
|
|
|
}, [setSelectedEnvRaw, navigate, location.pathname, location.search, queryClient]);
|
|
|
|
|
|
2026-04-02 18:29:25 +02:00
|
|
|
// --- Section open states ------------------------------------------
|
2026-04-02 18:36:43 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2026-04-03 10:49:54 +02:00
|
|
|
if (appsOpen) {
|
|
|
|
|
// Already open — navigate to all applications
|
2026-04-03 14:48:30 +02:00
|
|
|
if (!autoRefresh) {
|
|
|
|
|
refreshTimeRange();
|
|
|
|
|
queryClient.invalidateQueries();
|
|
|
|
|
}
|
2026-04-03 10:49:54 +02:00
|
|
|
navigate(`/${scope.tab}`);
|
|
|
|
|
} else {
|
|
|
|
|
setAppsOpen(true);
|
|
|
|
|
writeCollapsed(SK_APPS, true);
|
|
|
|
|
}
|
2026-04-03 14:48:30 +02:00
|
|
|
}, [isAdminPage, appsOpen, navigate, scope.tab, autoRefresh, refreshTimeRange, queryClient]);
|
2026-04-02 18:29:25 +02:00
|
|
|
|
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]
|
2026-04-08 23:43:14 +02:00
|
|
|
.sort((a: any, b: any) => cmp(a.slug, b.slug))
|
2026-04-01 18:24:39 +02:00
|
|
|
.map((app: any) => ({
|
2026-04-08 23:43:14 +02:00
|
|
|
id: app.slug,
|
|
|
|
|
name: app.displayName || app.slug,
|
2026-04-09 08:00:54 +02:00
|
|
|
health: (app.health === 'offline' ? 'dead' : app.health) as SidebarApp['health'],
|
|
|
|
|
healthTooltip: app.healthTooltip,
|
2026-04-01 18:24:39 +02:00
|
|
|
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]);
|
|
|
|
|
|
fix: resolve UI glitches and improve consistency
- Sidebar: make +App button more subtle (lower opacity, brightens on hover)
- Sidebar: add filter chips to hide empty routes and offline/stale apps
- Sidebar: hide filter chips and +App button when sidebar is collapsed
- Exchange table: reorder columns to Status, Attributes, App, Route, Started, Duration; remove ExchangeId and Agent columns
- Exchange detail log tab: query by exchangeId only (no applicationId required), filter by processorId when processor selected
- KPI tooltips: styled tooltips with current/previous values, time period labels, percentage change, themed with DS variables
- KPI tooltips: fix overflow by left-aligning first two and right-aligning last two
- Exchange detail: show full datetime (YYYY-MM-DD HH:mm:ss.SSS) for start/end times
- Status labels: unify to title-case (Completed, Failed, Running) across all views
- Status filter buttons: match title-case labels (Completed, Warning, Failed, Running)
- Create app: show full external URL using routingDomain from env config or window.location.origin fallback
- Create app: add Runtime Type selector and Custom Arguments to Resources tab
- Create app: add Sensitive Keys tab with agent defaults, global keys, and app-specific keys (matching admin page design)
- Create app: add placeholder text to all Input fields for consistency
- Update design-system to 0.1.52 (sidebar collapse toggle fix)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:41:36 +02:00
|
|
|
// --- Apply sidebar filters -----------------------------------------
|
|
|
|
|
const filteredSidebarApps: SidebarApp[] = useMemo(() => {
|
|
|
|
|
let apps = sidebarApps;
|
|
|
|
|
if (hideOfflineApps) {
|
|
|
|
|
apps = apps.filter((a) => a.health !== 'dead' && a.health !== 'stale');
|
|
|
|
|
}
|
|
|
|
|
if (hideEmptyRoutes) {
|
|
|
|
|
apps = apps
|
|
|
|
|
.map((a) => ({
|
|
|
|
|
...a,
|
|
|
|
|
routes: a.routes.filter((r) => r.exchangeCount > 0),
|
|
|
|
|
}))
|
|
|
|
|
.filter((a) => a.exchangeCount > 0 || a.routes.length > 0);
|
|
|
|
|
}
|
|
|
|
|
return apps;
|
|
|
|
|
}, [sidebarApps, hideOfflineApps, hideEmptyRoutes]);
|
|
|
|
|
|
2026-04-02 18:29:25 +02:00
|
|
|
// --- Tree nodes ---------------------------------------------------
|
|
|
|
|
const appTreeNodes: SidebarTreeNode[] = useMemo(
|
fix: resolve UI glitches and improve consistency
- Sidebar: make +App button more subtle (lower opacity, brightens on hover)
- Sidebar: add filter chips to hide empty routes and offline/stale apps
- Sidebar: hide filter chips and +App button when sidebar is collapsed
- Exchange table: reorder columns to Status, Attributes, App, Route, Started, Duration; remove ExchangeId and Agent columns
- Exchange detail log tab: query by exchangeId only (no applicationId required), filter by processorId when processor selected
- KPI tooltips: styled tooltips with current/previous values, time period labels, percentage change, themed with DS variables
- KPI tooltips: fix overflow by left-aligning first two and right-aligning last two
- Exchange detail: show full datetime (YYYY-MM-DD HH:mm:ss.SSS) for start/end times
- Status labels: unify to title-case (Completed, Failed, Running) across all views
- Status filter buttons: match title-case labels (Completed, Warning, Failed, Running)
- Create app: show full external URL using routingDomain from env config or window.location.origin fallback
- Create app: add Runtime Type selector and Custom Arguments to Resources tab
- Create app: add Sensitive Keys tab with agent defaults, global keys, and app-specific keys (matching admin page design)
- Create app: add placeholder text to all Input fields for consistency
- Update design-system to 0.1.52 (sidebar collapse toggle fix)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:41:36 +02:00
|
|
|
() => buildAppTreeNodes(filteredSidebarApps, makeStatusDot, makeChevron, makeStopIcon, makePauseIcon),
|
|
|
|
|
[filteredSidebarApps],
|
2026-04-02 18:29:25 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const adminTreeNodes: SidebarTreeNode[] = useMemo(
|
2026-04-11 23:12:30 +02:00
|
|
|
() => buildAdminTreeNodes({ infrastructureEndpoints: capabilities?.infrastructureEndpoints }),
|
|
|
|
|
[capabilities?.infrastructureEndpoints],
|
2026-04-02 18:29:25 +02:00
|
|
|
);
|
|
|
|
|
|
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
|
|
|
|
|
|
2026-04-09 07:13:04 +02:00
|
|
|
// Normalize path so sidebar highlights the app regardless of which tab is active.
|
|
|
|
|
// Sidebar nodes use /exchanges/{slug} paths, so map /dashboard/{slug}, /apps/{slug}, etc.
|
|
|
|
|
const effectiveSelectedPath = useMemo(() => {
|
|
|
|
|
const raw = sidebarRevealPath ?? location.pathname;
|
|
|
|
|
const match = raw.match(/^\/(exchanges|dashboard|apps|runtime)\/([^/]+)(\/.*)?$/);
|
|
|
|
|
if (match) return `/exchanges/${match[2]}${match[3] ?? ''}`;
|
|
|
|
|
return raw;
|
|
|
|
|
}, [sidebarRevealPath, location.pathname]);
|
2026-04-02 22:29:44 +02:00
|
|
|
|
2026-04-08 12:12:29 +02:00
|
|
|
// --- About Me dialog -----------------------------------------------
|
|
|
|
|
const [aboutMeOpen, setAboutMeOpen] = useState(false);
|
|
|
|
|
const userMenuItems: DropdownItem[] = useMemo(() => [
|
|
|
|
|
{ label: 'About Me', icon: createElement(User, { size: 14 }), onClick: () => setAboutMeOpen(true) },
|
|
|
|
|
], []);
|
|
|
|
|
|
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-04-02 23:34:52 +02:00
|
|
|
const adminSearchData: SearchResult[] = useMemo(
|
|
|
|
|
() => buildAdminSearchData(adminUsers, adminGroups, adminRoles),
|
|
|
|
|
[adminUsers, adminGroups, adminRoles],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const operationalSearchData: SearchResult[] = useMemo(() => {
|
|
|
|
|
if (isAdminPage) return [];
|
|
|
|
|
|
2026-03-25 08:57:24 +01:00
|
|
|
const exchangeItems: SearchResult[] = (exchangeResults?.data || []).map((e: any) => ({
|
|
|
|
|
id: e.executionId,
|
|
|
|
|
category: 'exchange' as const,
|
2026-04-09 18:51:49 +02:00
|
|
|
title: `...${e.executionId.slice(-8)}`,
|
2026-03-25 08:57:24 +01:00
|
|
|
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];
|
2026-04-02 23:34:52 +02:00
|
|
|
}, [isAdminPage, catalogRef.current, exchangeResults, debouncedQuery]);
|
|
|
|
|
|
|
|
|
|
const searchData = isAdminPage ? adminSearchData : operationalSearchData;
|
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();
|
2026-04-04 17:12:16 +02:00
|
|
|
useEnvironmentStore.getState().setEnvironment(undefined);
|
2026-03-23 18:17:38 +01:00
|
|
|
navigate('/login');
|
|
|
|
|
}, [logout, navigate]);
|
|
|
|
|
|
2026-04-02 23:38:06 +02:00
|
|
|
const ADMIN_CATEGORIES = new Set(['user', 'group', 'role']);
|
|
|
|
|
const ADMIN_TAB_MAP: Record<string, string> = { user: 'users', group: 'groups', role: 'roles' };
|
|
|
|
|
|
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-04-02 23:38:06 +02:00
|
|
|
if (ADMIN_CATEGORIES.has(result.category)) {
|
|
|
|
|
const itemId = result.id.split(':').slice(1).join(':');
|
|
|
|
|
navigate(result.path, {
|
|
|
|
|
state: { tab: ADMIN_TAB_MAP[result.category], highlight: itemId },
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
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],
|
|
|
|
|
applicationId: parts[1],
|
|
|
|
|
routeId: parts[2],
|
|
|
|
|
};
|
|
|
|
|
}
|
2026-03-31 15:26:36 +02:00
|
|
|
}
|
2026-04-02 23:38:06 +02:00
|
|
|
navigate(result.path, { state });
|
2026-03-31 15:26:36 +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
|
|
|
}
|
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-04-02 23:38:06 +02:00
|
|
|
if (isAdminPage) {
|
2026-04-02 23:53:09 +02:00
|
|
|
// Find first matching admin result and navigate to it
|
|
|
|
|
const q = query.toLowerCase();
|
|
|
|
|
const match = adminSearchData.find(
|
|
|
|
|
(r) => r.title.toLowerCase().includes(q) || r.meta.toLowerCase().includes(q),
|
|
|
|
|
);
|
|
|
|
|
if (match) {
|
|
|
|
|
handlePaletteSelect(match);
|
|
|
|
|
} else {
|
|
|
|
|
navigate('/admin/rbac');
|
|
|
|
|
}
|
2026-04-02 23:38:06 +02:00
|
|
|
} else {
|
|
|
|
|
const baseParts = ['/exchanges'];
|
|
|
|
|
if (scope.appId) baseParts.push(scope.appId);
|
|
|
|
|
if (scope.routeId) baseParts.push(scope.routeId);
|
|
|
|
|
navigate(`${baseParts.join('/')}?text=${encodeURIComponent(query)}`);
|
|
|
|
|
}
|
2026-04-02 23:53:09 +02:00
|
|
|
}, [isAdminPage, adminSearchData, handlePaletteSelect, navigate, scope.appId, scope.routeId]);
|
2026-03-28 14:01:52 +01:00
|
|
|
|
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-04-03 11:01:29 +02:00
|
|
|
// When not auto-refreshing, treat navigation as a manual refresh
|
|
|
|
|
if (!autoRefresh) {
|
2026-04-03 14:48:30 +02:00
|
|
|
refreshTimeRange();
|
2026-04-03 11:01:29 +02:00
|
|
|
queryClient.invalidateQueries();
|
|
|
|
|
}
|
|
|
|
|
|
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-08 18:02:29 +02:00
|
|
|
if (scope.tab === 'apps') {
|
|
|
|
|
navigate(`/apps/${sAppId}`, { state });
|
2026-04-13 11:24:15 +02:00
|
|
|
} else if (scope.tab === 'runtime') {
|
|
|
|
|
// Runtime tab has no route-level view — stay at app level
|
|
|
|
|
navigate(`/runtime/${sAppId}`, { state });
|
2026-04-06 16:13:39 +02:00
|
|
|
} else {
|
|
|
|
|
navigate(sRouteId ? `/${scope.tab}/${sAppId}/${sRouteId}` : `/${scope.tab}/${sAppId}`, { state });
|
|
|
|
|
}
|
2026-03-28 14:01:52 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-09 07:13:04 +02:00
|
|
|
const exchangeMatch = path.match(/^\/exchanges\/([^/]+)(?:\/(.+))?$/);
|
|
|
|
|
if (exchangeMatch) {
|
|
|
|
|
const [, sAppId, sRouteId] = exchangeMatch;
|
|
|
|
|
if (scope.tab === 'apps') {
|
|
|
|
|
navigate(`/apps/${sAppId}`, { state });
|
2026-04-13 11:24:15 +02:00
|
|
|
} else if (scope.tab === 'runtime') {
|
|
|
|
|
navigate(`/runtime/${sAppId}`, { state });
|
2026-04-09 07:13:04 +02:00
|
|
|
} else {
|
|
|
|
|
navigate(sRouteId ? `/${scope.tab}/${sAppId}/${sRouteId}` : `/${scope.tab}/${sAppId}`, { state });
|
|
|
|
|
}
|
|
|
|
|
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-04-03 14:48:30 +02:00
|
|
|
}, [navigate, scope.tab, autoRefresh, refreshTimeRange, queryClient]);
|
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 = (
|
2026-04-06 22:47:31 +02:00
|
|
|
<img src={sidebarLogo} alt="" style={{ width: 28, height: 28 }} />
|
2026-04-02 22:29:44 +02:00
|
|
|
);
|
|
|
|
|
|
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:42:06 +02:00
|
|
|
version={__APP_VERSION__}
|
2026-04-02 18:29:25 +02:00
|
|
|
/>
|
|
|
|
|
|
fix: resolve UI glitches and improve consistency
- Sidebar: make +App button more subtle (lower opacity, brightens on hover)
- Sidebar: add filter chips to hide empty routes and offline/stale apps
- Sidebar: hide filter chips and +App button when sidebar is collapsed
- Exchange table: reorder columns to Status, Attributes, App, Route, Started, Duration; remove ExchangeId and Agent columns
- Exchange detail log tab: query by exchangeId only (no applicationId required), filter by processorId when processor selected
- KPI tooltips: styled tooltips with current/previous values, time period labels, percentage change, themed with DS variables
- KPI tooltips: fix overflow by left-aligning first two and right-aligning last two
- Exchange detail: show full datetime (YYYY-MM-DD HH:mm:ss.SSS) for start/end times
- Status labels: unify to title-case (Completed, Failed, Running) across all views
- Status filter buttons: match title-case labels (Completed, Warning, Failed, Running)
- Create app: show full external URL using routingDomain from env config or window.location.origin fallback
- Create app: add Runtime Type selector and Custom Arguments to Resources tab
- Create app: add Sensitive Keys tab with agent defaults, global keys, and app-specific keys (matching admin page design)
- Create app: add placeholder text to all Input fields for consistency
- Update design-system to 0.1.52 (sidebar collapse toggle fix)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:41:36 +02:00
|
|
|
{/* Sidebar filters */}
|
|
|
|
|
{!sidebarCollapsed && <div className={css.sidebarFilters}>
|
|
|
|
|
<button
|
|
|
|
|
className={`${css.filterChip} ${hideEmptyRoutes ? css.filterChipActive : ''}`}
|
|
|
|
|
onClick={toggleHideEmptyRoutes}
|
|
|
|
|
title="Hide routes with 0 executions"
|
|
|
|
|
>
|
|
|
|
|
<span className={css.filterChipIcon}><EyeOff size={10} /></span>
|
|
|
|
|
Empty routes
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
className={`${css.filterChip} ${hideOfflineApps ? css.filterChipActive : ''}`}
|
|
|
|
|
onClick={toggleHideOfflineApps}
|
|
|
|
|
title="Hide stale and disconnected apps"
|
|
|
|
|
>
|
|
|
|
|
<span className={css.filterChipIcon}><EyeOff size={10} /></span>
|
|
|
|
|
Offline apps
|
|
|
|
|
</button>
|
|
|
|
|
</div>}
|
|
|
|
|
|
2026-04-02 22:29:44 +02:00
|
|
|
{/* Applications section */}
|
2026-04-14 09:10:41 +02:00
|
|
|
<div className={css.appSectionWrap}>
|
fix: resolve UI glitches and improve consistency
- Sidebar: make +App button more subtle (lower opacity, brightens on hover)
- Sidebar: add filter chips to hide empty routes and offline/stale apps
- Sidebar: hide filter chips and +App button when sidebar is collapsed
- Exchange table: reorder columns to Status, Attributes, App, Route, Started, Duration; remove ExchangeId and Agent columns
- Exchange detail log tab: query by exchangeId only (no applicationId required), filter by processorId when processor selected
- KPI tooltips: styled tooltips with current/previous values, time period labels, percentage change, themed with DS variables
- KPI tooltips: fix overflow by left-aligning first two and right-aligning last two
- Exchange detail: show full datetime (YYYY-MM-DD HH:mm:ss.SSS) for start/end times
- Status labels: unify to title-case (Completed, Failed, Running) across all views
- Status filter buttons: match title-case labels (Completed, Warning, Failed, Running)
- Create app: show full external URL using routingDomain from env config or window.location.origin fallback
- Create app: add Runtime Type selector and Custom Arguments to Resources tab
- Create app: add Sensitive Keys tab with agent defaults, global keys, and app-specific keys (matching admin page design)
- Create app: add placeholder text to all Input fields for consistency
- Update design-system to 0.1.52 (sidebar collapse toggle fix)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:41:36 +02:00
|
|
|
{canControl && !sidebarCollapsed && (
|
2026-04-14 09:10:41 +02:00
|
|
|
<button
|
|
|
|
|
className={css.addAppBtn}
|
|
|
|
|
onClick={(e) => { e.stopPropagation(); navigate('/apps/new'); }}
|
|
|
|
|
title="Create App"
|
|
|
|
|
>
|
|
|
|
|
<Plus size={12} /> App
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
|
|
|
|
<Sidebar.Section
|
|
|
|
|
icon={createElement(Box, { size: 16 })}
|
|
|
|
|
label="Applications"
|
|
|
|
|
open={appsOpen}
|
|
|
|
|
onToggle={toggleApps}
|
|
|
|
|
>
|
|
|
|
|
<SidebarTree
|
|
|
|
|
nodes={appTreeNodes}
|
|
|
|
|
selectedPath={effectiveSelectedPath}
|
|
|
|
|
isStarred={isStarred}
|
|
|
|
|
onToggleStar={toggleStar}
|
|
|
|
|
filterQuery={filterQuery}
|
|
|
|
|
persistKey="apps"
|
|
|
|
|
autoRevealPath={sidebarRevealPath}
|
|
|
|
|
onNavigate={handleSidebarNavigate}
|
|
|
|
|
/>
|
|
|
|
|
</Sidebar.Section>
|
|
|
|
|
</div>
|
2026-04-02 18:29:25 +02:00
|
|
|
|
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-06 15:51:15 +02:00
|
|
|
{/* Admin section — only visible to ADMIN role */}
|
|
|
|
|
{isAdmin && (
|
|
|
|
|
<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>
|
|
|
|
|
)}
|
2026-04-02 22:32:53 +02:00
|
|
|
|
|
|
|
|
{/* 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}
|
2026-04-04 15:47:48 +02:00
|
|
|
environment={
|
2026-04-04 15:43:43 +02:00
|
|
|
<EnvironmentSelector
|
|
|
|
|
environments={environments}
|
|
|
|
|
value={selectedEnv}
|
|
|
|
|
onChange={setSelectedEnv}
|
|
|
|
|
/>
|
2026-04-04 15:47:48 +02:00
|
|
|
}
|
2026-04-02 17:22:06 +02:00
|
|
|
user={username ? { name: username } : undefined}
|
2026-04-08 12:12:29 +02:00
|
|
|
userMenuItems={userMenuItems}
|
2026-04-02 17:22:06 +02:00
|
|
|
onLogout={handleLogout}
|
2026-04-15 15:28:42 +02:00
|
|
|
onNavigate={navigate}
|
2026-04-10 17:06:03 +02:00
|
|
|
>
|
|
|
|
|
<SearchTrigger onClick={() => setPaletteOpen(true)} />
|
|
|
|
|
<ButtonGroup
|
|
|
|
|
items={STATUS_ITEMS}
|
|
|
|
|
value={globalFilters.statusFilters}
|
2026-04-10 20:10:08 +02:00
|
|
|
onChange={(sel) => {
|
|
|
|
|
type S = 'completed' | 'warning' | 'failed' | 'running'
|
|
|
|
|
const selected = sel as Set<S>
|
2026-04-10 17:06:03 +02:00
|
|
|
const current = globalFilters.statusFilters
|
|
|
|
|
for (const v of selected) {
|
2026-04-10 20:10:08 +02:00
|
|
|
if (!current.has(v)) globalFilters.toggleStatus(v)
|
2026-04-10 17:06:03 +02:00
|
|
|
}
|
|
|
|
|
for (const v of current) {
|
2026-04-10 20:10:08 +02:00
|
|
|
if (!selected.has(v)) globalFilters.toggleStatus(v)
|
2026-04-10 17:06:03 +02:00
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
<TimeRangeDropdown
|
|
|
|
|
value={globalFilters.timeRange}
|
|
|
|
|
onChange={globalFilters.setTimeRange}
|
|
|
|
|
/>
|
|
|
|
|
<AutoRefreshToggle
|
|
|
|
|
active={globalFilters.autoRefresh}
|
|
|
|
|
onChange={globalFilters.setAutoRefresh}
|
|
|
|
|
/>
|
|
|
|
|
</TopBar>
|
2026-04-08 12:12:29 +02:00
|
|
|
<AboutMeDialog open={aboutMeOpen} onClose={() => setAboutMeOpen(false)} />
|
2026-04-02 17:22:06 +02:00
|
|
|
<CommandPalette
|
2026-04-02 23:53:09 +02:00
|
|
|
key={isAdminPage ? 'admin' : 'ops'}
|
2026-04-02 17:22:06 +02:00
|
|
|
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
|
|
|
)}
|
|
|
|
|
|
refactor: UI consistency — shared CSS, design system colors, no inline styles
Phase 1: Extract 6 shared CSS modules (table-section, log-panel,
rate-colors, refresh-indicator, chart-card, section-card) eliminating
~135 duplicate class definitions across 11 files.
Phase 2: Replace all hardcoded hex colors in CSS modules with design
system variables. Strip ~55 hex fallbacks from var() patterns. Fix 4
undefined variable names (--accent, --bg-base, --surface, --bg-surface-raised).
Phase 3: Replace ~45 hardcoded hex values in ProcessDiagram SVG
components with var() CSS custom properties. Fix Dashboard.tsx color prop.
Phase 4: Create CSS modules for AdminLayout, DatabaseAdminPage,
OidcCallback (previously 100% inline). Extract shared PageLoader
component (replaces 3 copy-pasted spinner patterns). Move AppsTab
static inline styles to CSS classes. Extract LayoutShell StarredList styles.
58 files changed, net -219 lines.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 14:55:54 +02:00
|
|
|
<main className={css.mainContent}>
|
2026-04-04 17:12:16 +02:00
|
|
|
<Outlet key={selectedEnv ?? '__all__'} />
|
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
|
|
|
</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
|
|
|
);
|
|
|
|
|
}
|