feat: show route control bar on topology diagram
When no exchange is selected, the topology-only diagram now shows the RouteControlBar above it (if the agent supports routeControl or replay and the user has OPERATOR/ADMIN role). This fixes a gap where suspended routes with no recent exchanges had no way to be resumed from the UI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,12 +4,15 @@ import { useGlobalFilters, useToast } from '@cameleer/design-system';
|
|||||||
import { useExecutionDetail } from '../../api/queries/executions';
|
import { useExecutionDetail } from '../../api/queries/executions';
|
||||||
import { useDiagramByRoute } from '../../api/queries/diagrams';
|
import { useDiagramByRoute } from '../../api/queries/diagrams';
|
||||||
import { useRouteCatalog } from '../../api/queries/catalog';
|
import { useRouteCatalog } from '../../api/queries/catalog';
|
||||||
|
import { useAgents } from '../../api/queries/agents';
|
||||||
import { useApplicationConfig, useUpdateApplicationConfig } from '../../api/queries/commands';
|
import { useApplicationConfig, useUpdateApplicationConfig } from '../../api/queries/commands';
|
||||||
import type { TapDefinition, ConfigUpdateResponse } from '../../api/queries/commands';
|
import type { TapDefinition, ConfigUpdateResponse } from '../../api/queries/commands';
|
||||||
|
import { useAuthStore } from '../../auth/auth-store';
|
||||||
import { useTracingStore } from '../../stores/tracing-store';
|
import { useTracingStore } from '../../stores/tracing-store';
|
||||||
import type { NodeAction, NodeConfig } from '../../components/ProcessDiagram/types';
|
import type { NodeAction, NodeConfig } from '../../components/ProcessDiagram/types';
|
||||||
import { TapConfigModal } from '../../components/TapConfigModal';
|
import { TapConfigModal } from '../../components/TapConfigModal';
|
||||||
import { ExchangeHeader } from './ExchangeHeader';
|
import { ExchangeHeader } from './ExchangeHeader';
|
||||||
|
import { RouteControlBar } from './RouteControlBar';
|
||||||
import { ExecutionDiagram } from '../../components/ExecutionDiagram/ExecutionDiagram';
|
import { ExecutionDiagram } from '../../components/ExecutionDiagram/ExecutionDiagram';
|
||||||
import { ProcessDiagram } from '../../components/ProcessDiagram';
|
import { ProcessDiagram } from '../../components/ProcessDiagram';
|
||||||
import styles from './ExchangesPage.module.css';
|
import styles from './ExchangesPage.module.css';
|
||||||
@@ -148,6 +151,30 @@ function DiagramPanel({ appId, routeId, exchangeId, onCorrelatedSelect, onClearS
|
|||||||
const diagramQuery = useDiagramByRoute(appId, routeId);
|
const diagramQuery = useDiagramByRoute(appId, routeId);
|
||||||
|
|
||||||
const { data: catalog } = useRouteCatalog(timeFrom, timeTo);
|
const { data: catalog } = useRouteCatalog(timeFrom, timeTo);
|
||||||
|
|
||||||
|
// Route state + capabilities for topology-only control bar
|
||||||
|
const { data: agents } = useAgents(undefined, appId);
|
||||||
|
const roles = useAuthStore((s) => s.roles);
|
||||||
|
const canControl = roles.some(r => r === 'OPERATOR' || r === 'ADMIN');
|
||||||
|
const { hasRouteControl, hasReplay } = useMemo(() => {
|
||||||
|
if (!agents) return { hasRouteControl: false, hasReplay: false };
|
||||||
|
const agentList = agents as any[];
|
||||||
|
return {
|
||||||
|
hasRouteControl: agentList.some((a: any) => a.capabilities?.routeControl === true),
|
||||||
|
hasReplay: agentList.some((a: any) => a.capabilities?.replay === true),
|
||||||
|
};
|
||||||
|
}, [agents]);
|
||||||
|
const routeState = useMemo(() => {
|
||||||
|
if (!catalog) return undefined;
|
||||||
|
for (const app of catalog as any[]) {
|
||||||
|
if (app.applicationId !== appId) continue;
|
||||||
|
for (const r of app.routes || []) {
|
||||||
|
if (r.routeId === routeId) return (r.routeState ?? 'started') as 'started' | 'stopped' | 'suspended';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}, [catalog, appId, routeId]);
|
||||||
|
|
||||||
const knownRouteIds = useMemo(() => {
|
const knownRouteIds = useMemo(() => {
|
||||||
const ids = new Set<string>();
|
const ids = new Set<string>();
|
||||||
if (catalog) {
|
if (catalog) {
|
||||||
@@ -313,10 +340,19 @@ function DiagramPanel({ appId, routeId, exchangeId, onCorrelatedSelect, onClearS
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// No exchange selected: show topology-only diagram
|
// No exchange selected: show topology-only diagram with route control bar
|
||||||
if (diagramQuery.data) {
|
if (diagramQuery.data) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{canControl && (hasRouteControl || hasReplay) && (
|
||||||
|
<RouteControlBar
|
||||||
|
application={appId}
|
||||||
|
routeId={routeId}
|
||||||
|
routeState={routeState}
|
||||||
|
hasRouteControl={hasRouteControl}
|
||||||
|
hasReplay={hasReplay}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<ProcessDiagram
|
<ProcessDiagram
|
||||||
application={appId}
|
application={appId}
|
||||||
routeId={routeId}
|
routeId={routeId}
|
||||||
|
|||||||
Reference in New Issue
Block a user