feat: add Timeline/Flow toggle to Exchange Detail

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-23 18:22:45 +01:00
parent 651cf9de6e
commit a86f56f588

View File

@@ -2,10 +2,12 @@ import React, { useState, useMemo } from 'react';
import { useParams, useNavigate } from 'react-router';
import {
Badge, StatusDot, MonoText, CodeBlock, InfoCallout,
ProcessorTimeline, Breadcrumb, Spinner,
ProcessorTimeline, Breadcrumb, Spinner, SegmentedTabs, RouteFlow,
} from '@cameleer/design-system';
import { useExecutionDetail, useProcessorSnapshot } from '../../api/queries/executions';
import { useCorrelationChain } from '../../api/queries/correlation';
import { useDiagramByRoute } from '../../api/queries/diagrams';
import { mapDiagramToRouteNodes } from '../../utils/diagram-mapping';
import styles from './ExchangeDetail.module.css';
function countProcessors(nodes: any[]): number {
@@ -17,8 +19,10 @@ export default function ExchangeDetail() {
const navigate = useNavigate();
const { data: detail, isLoading } = useExecutionDetail(id ?? null);
const [selectedProcessor, setSelectedProcessor] = useState<number | null>(null);
const [viewMode, setViewMode] = useState<'timeline' | 'flow'>('timeline');
const { data: snapshot } = useProcessorSnapshot(id ?? null, selectedProcessor);
const { data: correlationData } = useCorrelationChain(detail?.correlationId ?? null);
const { data: diagram } = useDiagramByRoute(detail?.groupName, detail?.routeId);
const processors = useMemo(() => {
if (!detail?.children) return [];
@@ -115,18 +119,38 @@ export default function ExchangeDetail() {
<div className={styles.timelineSection}>
<div className={styles.timelineHeader}>
<span className={styles.timelineTitle}>Processor Timeline</span>
<span className={styles.timelineTitle}>Processors</span>
<SegmentedTabs
tabs={[
{ label: 'Timeline', value: 'timeline' },
{ label: 'Flow', value: 'flow' },
]}
active={viewMode}
onChange={(v) => setViewMode(v as 'timeline' | 'flow')}
/>
</div>
<div className={styles.timelineBody}>
{processors.length > 0 ? (
<ProcessorTimeline
processors={processors}
totalMs={detail.durationMs}
onProcessorClick={(_p, i) => setSelectedProcessor(i)}
selectedIndex={selectedProcessor ?? undefined}
/>
{viewMode === 'timeline' ? (
processors.length > 0 ? (
<ProcessorTimeline
processors={processors}
totalMs={detail.durationMs}
onProcessorClick={(_p, i) => setSelectedProcessor(i)}
selectedIndex={selectedProcessor ?? undefined}
/>
) : (
<InfoCallout>No processor data available</InfoCallout>
)
) : (
<InfoCallout>No processor data available</InfoCallout>
diagram ? (
<RouteFlow
nodes={mapDiagramToRouteNodes(diagram.nodes || [], detail.processors || detail.children || [])}
onNodeClick={(_node, i) => setSelectedProcessor(i)}
selectedIndex={selectedProcessor ?? undefined}
/>
) : (
<Spinner />
)
)}
</div>
</div>