refactor(ui): remove detail panel slide-in and inspect column from exchange table
Some checks failed
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 1m12s
CI / docker (push) Successful in 1m5s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Has been cancelled

Row click now navigates directly to the split view with diagram.
Removed: DetailPanel, inspect column, unused imports (ExternalLink,
ProcessorTimeline, RouteFlow, useExecutionDetail, useDiagramLayout,
buildFlowSegments).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-28 14:32:20 +01:00
parent 91171590e6
commit 5af20d0f63

View File

@@ -1,12 +1,9 @@
import { useState, useMemo, useCallback } from 'react'
import { useParams, useNavigate, useSearchParams } from 'react-router'
import { ExternalLink, AlertTriangle, X, Search } from 'lucide-react'
import { AlertTriangle, X, Search } from 'lucide-react'
import {
DataTable,
DetailPanel,
ShortcutsBar,
ProcessorTimeline,
RouteFlow,
KpiStrip,
StatusDot,
MonoText,
@@ -18,11 +15,8 @@ import {
useSearchExecutions,
useExecutionStats,
useStatsTimeseries,
useExecutionDetail,
} from '../../api/queries/executions'
import { useDiagramLayout } from '../../api/queries/diagrams'
import type { ExecutionSummary } from '../../api/types'
import { buildFlowSegments } from '../../utils/diagram-mapping'
import styles from './Dashboard.module.css'
// Row type extends ExecutionSummary with an `id` field for DataTable
@@ -199,7 +193,6 @@ export default function Dashboard() {
const [searchParams, setSearchParams] = useSearchParams()
const textFilter = searchParams.get('text') || undefined
const [selectedId, setSelectedId] = useState<string | undefined>()
const [panelOpen, setPanelOpen] = useState(false)
const [sortField, setSortField] = useState<string>('startTime')
const [sortDir, setSortDir] = useState<'asc' | 'desc'>('desc')
@@ -236,8 +229,6 @@ export default function Dashboard() {
},
!textFilter,
)
const { data: detail } = useExecutionDetail(selectedId ?? null)
const { data: diagram } = useDiagramLayout(detail?.diagramContentHash ?? null)
// ─── Rows ────────────────────────────────────────────────────────────────
const rows: Row[] = useMemo(
@@ -333,39 +324,15 @@ export default function Dashboard() {
[totalCount, failedCount, successRate, throughput, exchangeTrend, successRateDelta, errorDelta, sparkExchanges, sparkErrors, sparkLatency, sparkThroughput, stats?.p99LatencyMs],
)
// ─── Table columns with inspect action ───────────────────────────────────
const columns: Column<Row>[] = useMemo(() => {
const inspectCol: Column<Row> = {
key: 'correlationId',
header: '',
width: '36px',
render: (_: unknown, row: Row) => (
<button
className={styles.inspectLink}
title="Inspect exchange"
onClick={(e) => {
e.stopPropagation()
navigate(`/exchanges/${row.applicationName}/${row.routeId}/${row.executionId}`)
}}
>
<ExternalLink size={14} />
</button>
),
}
const base = buildBaseColumns()
const [statusCol, ...rest] = base
return [statusCol, inspectCol, ...rest]
}, [navigate])
// ─── Table columns ──────────────────────────────────────────────────────
const columns: Column<Row>[] = useMemo(() => buildBaseColumns(), [])
// ─── Row click / detail panel ────────────────────────────────────────────
const selectedRow = useMemo(
() => rows.find((r) => r.id === selectedId),
[rows, selectedId],
)
// ─── Row click → navigate to diagram view ────────────────────────────────
function handleRowClick(row: Row) {
setSelectedId(row.id)
setPanelOpen(true)
// Navigate to the split view with diagram
navigate(`/exchanges/${row.applicationName}/${row.routeId}/${row.executionId}`)
}
function handleRowAccent(row: Row): 'error' | 'warning' | undefined {
@@ -373,24 +340,6 @@ export default function Dashboard() {
return undefined
}
// ─── Detail panel data ───────────────────────────────────────────────────
const procList = detail
? (detail.processors ?? [])
: []
const routeFlows = useMemo(() => {
if (diagram?.nodes) {
return buildFlowSegments(diagram.nodes || [], procList).flows
}
return []
}, [diagram, procList])
const flatProcs = useMemo(() => flattenProcessors(procList), [procList])
// Error info from detail
const errorClass = detail?.errorMessage?.split(':')[0] ?? ''
const errorMsg = detail?.errorMessage ?? ''
return (
<>
{/* Scrollable content */}
@@ -450,91 +399,6 @@ export default function Dashboard() {
{/* Shortcuts bar */}
<ShortcutsBar shortcuts={SHORTCUTS} />
{/* Detail panel — auto-portals to AppShell level via design system */}
{selectedRow && detail && (
<DetailPanel
open={panelOpen}
onClose={() => setPanelOpen(false)}
title={`${detail.routeId} \u2014 ${selectedRow.executionId.slice(0, 12)}`}
>
<div className={styles.panelSection}>
<button
className={styles.openDetailLink}
onClick={() => navigate(`/exchanges/${detail.applicationName}/${detail.routeId}/${detail.executionId}`)}
>
Open full details &#x2192;
</button>
</div>
<div className={styles.panelSection}>
<div className={styles.panelSectionTitle}>Overview</div>
<div className={styles.overviewGrid}>
<div className={styles.overviewRow}>
<span className={styles.overviewLabel}>Status</span>
<span className={styles.statusCell}>
<StatusDot variant={statusToVariant(detail.status)} />
<span>{statusLabel(detail.status)}</span>
</span>
</div>
<div className={styles.overviewRow}>
<span className={styles.overviewLabel}>Duration</span>
<MonoText size="sm">{formatDuration(detail.durationMs)}</MonoText>
</div>
<div className={styles.overviewRow}>
<span className={styles.overviewLabel}>Route</span>
<span>{detail.routeId}</span>
</div>
<div className={styles.overviewRow}>
<span className={styles.overviewLabel}>Agent</span>
<MonoText size="sm">{detail.agentId ?? '\u2014'}</MonoText>
</div>
<div className={styles.overviewRow}>
<span className={styles.overviewLabel}>Correlation</span>
<MonoText size="xs">{detail.correlationId ?? '\u2014'}</MonoText>
</div>
<div className={styles.overviewRow}>
<span className={styles.overviewLabel}>Timestamp</span>
<MonoText size="xs">{detail.startTime ? new Date(detail.startTime).toISOString() : '\u2014'}</MonoText>
</div>
</div>
</div>
{errorMsg && (
<div className={styles.panelSection}>
<div className={styles.panelSectionTitle}>Errors</div>
<div className={styles.errorBlock}>
<div className={styles.errorClass}>{errorClass}</div>
<div className={styles.errorMessage}>{errorMsg}</div>
</div>
</div>
)}
<div className={styles.panelSection}>
<div className={styles.panelSectionTitle}>Route Flow</div>
{routeFlows.length > 0 ? (
<RouteFlow flows={routeFlows} />
) : (
<div style={{ color: 'var(--text-muted)', fontSize: 12 }}>No diagram available</div>
)}
</div>
<div className={styles.panelSection}>
<div className={styles.panelSectionTitle}>
Processor Timeline
<span className={styles.panelSectionMeta}>{formatDuration(detail.durationMs)}</span>
</div>
{flatProcs.length > 0 ? (
<ProcessorTimeline
processors={flatProcs}
totalMs={detail.durationMs}
/>
) : (
<div style={{ color: 'var(--text-muted)', fontSize: 12 }}>No processor data</div>
)}
</div>
</DetailPanel>
)}
</>
)
}