|
|
|
|
@@ -1,17 +1,15 @@
|
|
|
|
|
import { useState, useMemo, useCallback } from 'react';
|
|
|
|
|
import { useParams } from 'react-router';
|
|
|
|
|
import { useGlobalFilters } from '@cameleer/design-system';
|
|
|
|
|
import { useSearchExecutions, useExecutionDetail } from '../../api/queries/executions';
|
|
|
|
|
import { useExecutionDetail } from '../../api/queries/executions';
|
|
|
|
|
import { useDiagramByRoute } from '../../api/queries/diagrams';
|
|
|
|
|
import { useRouteCatalog } from '../../api/queries/catalog';
|
|
|
|
|
import type { ExecutionSummary } from '../../api/types';
|
|
|
|
|
import { ExchangeList } from './ExchangeList';
|
|
|
|
|
import { ExchangeHeader } from './ExchangeHeader';
|
|
|
|
|
import { ExecutionDiagram } from '../../components/ExecutionDiagram/ExecutionDiagram';
|
|
|
|
|
import { ProcessDiagram } from '../../components/ProcessDiagram';
|
|
|
|
|
import styles from './ExchangesPage.module.css';
|
|
|
|
|
|
|
|
|
|
// Lazy-import the full-width Dashboard for the no-route-scope view
|
|
|
|
|
// The full-width Dashboard table — used at every scope level
|
|
|
|
|
import Dashboard from '../Dashboard/Dashboard';
|
|
|
|
|
|
|
|
|
|
export default function ExchangesPage() {
|
|
|
|
|
@@ -19,45 +17,44 @@ export default function ExchangesPage() {
|
|
|
|
|
appId?: string; routeId?: string; exchangeId?: string;
|
|
|
|
|
}>();
|
|
|
|
|
|
|
|
|
|
// If no route is scoped, render the existing full-width Dashboard table
|
|
|
|
|
// No route scoped: full-width Dashboard
|
|
|
|
|
if (!routeId) {
|
|
|
|
|
return <Dashboard />;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Route is scoped: render 3-column layout
|
|
|
|
|
// Route scoped: 50:50 split — Dashboard table on left, diagram on right
|
|
|
|
|
return (
|
|
|
|
|
<RouteExchangeView appId={appId!} routeId={routeId} initialExchangeId={exchangeId} />
|
|
|
|
|
<div className={styles.splitView}>
|
|
|
|
|
<div className={styles.leftPanel}>
|
|
|
|
|
<Dashboard />
|
|
|
|
|
</div>
|
|
|
|
|
<div className={styles.rightPanel}>
|
|
|
|
|
<DiagramPanel appId={appId!} routeId={routeId} exchangeId={exchangeId} />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── 3-column view when route is scoped ─────────────────────────────────────
|
|
|
|
|
// ─── Right panel: diagram + optional execution overlay ──────────────────────
|
|
|
|
|
|
|
|
|
|
interface RouteExchangeViewProps {
|
|
|
|
|
interface DiagramPanelProps {
|
|
|
|
|
appId: string;
|
|
|
|
|
routeId: string;
|
|
|
|
|
initialExchangeId?: string;
|
|
|
|
|
exchangeId?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function RouteExchangeView({ appId, routeId, initialExchangeId }: RouteExchangeViewProps) {
|
|
|
|
|
const [selectedExchangeId, setSelectedExchangeId] = useState<string | undefined>(initialExchangeId);
|
|
|
|
|
function DiagramPanel({ appId, routeId, exchangeId }: DiagramPanelProps) {
|
|
|
|
|
const { timeRange } = useGlobalFilters();
|
|
|
|
|
const timeFrom = timeRange.start.toISOString();
|
|
|
|
|
const timeTo = timeRange.end.toISOString();
|
|
|
|
|
|
|
|
|
|
// Fetch exchanges for this route
|
|
|
|
|
const { data: searchResult } = useSearchExecutions(
|
|
|
|
|
{ timeFrom, timeTo, routeId, application: appId, sortField: 'startTime', sortDir: 'desc', offset: 0, limit: 50 },
|
|
|
|
|
true,
|
|
|
|
|
);
|
|
|
|
|
const exchanges: ExecutionSummary[] = searchResult?.data || [];
|
|
|
|
|
// Fetch execution detail if an exchange is selected
|
|
|
|
|
const { data: detail } = useExecutionDetail(exchangeId ?? null);
|
|
|
|
|
|
|
|
|
|
// Fetch execution detail for selected exchange
|
|
|
|
|
const { data: detail } = useExecutionDetail(selectedExchangeId ?? null);
|
|
|
|
|
|
|
|
|
|
// Fetch diagram for topology-only view (when no exchange selected)
|
|
|
|
|
// Fetch diagram for topology-only view
|
|
|
|
|
const diagramQuery = useDiagramByRoute(appId, routeId);
|
|
|
|
|
|
|
|
|
|
// Known route IDs for drill-down resolution
|
|
|
|
|
// Known route IDs for drill-down
|
|
|
|
|
const { data: catalog } = useRouteCatalog(timeFrom, timeTo);
|
|
|
|
|
const knownRouteIds = useMemo(() => {
|
|
|
|
|
const ids = new Set<string>();
|
|
|
|
|
@@ -71,43 +68,35 @@ function RouteExchangeView({ appId, routeId, initialExchangeId }: RouteExchangeV
|
|
|
|
|
return ids;
|
|
|
|
|
}, [catalog]);
|
|
|
|
|
|
|
|
|
|
const handleExchangeSelect = useCallback((ex: ExecutionSummary) => {
|
|
|
|
|
setSelectedExchangeId(ex.executionId);
|
|
|
|
|
}, []);
|
|
|
|
|
// If exchange selected: show header + ExecutionDiagram
|
|
|
|
|
if (exchangeId && detail) {
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<ExchangeHeader detail={detail} />
|
|
|
|
|
<ExecutionDiagram
|
|
|
|
|
executionId={exchangeId}
|
|
|
|
|
executionDetail={detail}
|
|
|
|
|
knownRouteIds={knownRouteIds}
|
|
|
|
|
/>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No exchange: show topology-only ProcessDiagram
|
|
|
|
|
if (diagramQuery.data) {
|
|
|
|
|
return (
|
|
|
|
|
<ProcessDiagram
|
|
|
|
|
application={appId}
|
|
|
|
|
routeId={routeId}
|
|
|
|
|
diagramLayout={diagramQuery.data}
|
|
|
|
|
knownRouteIds={knownRouteIds}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className={styles.threeColumn}>
|
|
|
|
|
<ExchangeList
|
|
|
|
|
exchanges={exchanges}
|
|
|
|
|
selectedId={selectedExchangeId}
|
|
|
|
|
onSelect={handleExchangeSelect}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<div className={styles.rightPanel}>
|
|
|
|
|
{selectedExchangeId && detail ? (
|
|
|
|
|
<>
|
|
|
|
|
<ExchangeHeader detail={detail} />
|
|
|
|
|
<ExecutionDiagram
|
|
|
|
|
executionId={selectedExchangeId}
|
|
|
|
|
executionDetail={detail}
|
|
|
|
|
knownRouteIds={knownRouteIds}
|
|
|
|
|
/>
|
|
|
|
|
</>
|
|
|
|
|
) : (
|
|
|
|
|
diagramQuery.data ? (
|
|
|
|
|
<ProcessDiagram
|
|
|
|
|
application={appId}
|
|
|
|
|
routeId={routeId}
|
|
|
|
|
diagramLayout={diagramQuery.data}
|
|
|
|
|
knownRouteIds={knownRouteIds}
|
|
|
|
|
/>
|
|
|
|
|
) : (
|
|
|
|
|
<div className={styles.emptyRight}>
|
|
|
|
|
Select an exchange to view execution details
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
<div className={styles.emptyRight}>
|
|
|
|
|
Loading diagram...
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|