feat: trace data indicators, inline tap config, and detail tab gating
Trace data visibility: - ProcessorNode now includes hasTraceData flag computed from captured body/headers during tree conversion - ConfigBadge shows teal for tracing configured, green when data captured - Search results show green footprints icon for exchanges with trace data - New has_trace_data column on executions table (V11 migration with backfill) - OpenSearch documents and ExecutionSummary include the flag Inline tap configuration: - Extracted reusable TapConfigModal component from RouteDetail - Diagram context menu opens tap modal inline instead of navigating away - Toggle-trace action works immediately with toast feedback - Modal closes only on ESC, Cancel, Save, or Delete (not backdrop click) Detail panel tab gating: - Headers, Input, Output tabs disabled when no data is available - Works at both exchange and processor level - Falls back to Info tab when active tab becomes empty Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -63,7 +63,7 @@ export function DetailPanel({
|
||||
? !!selectedProcessor.errorMessage
|
||||
: !!executionDetail.errorMessage;
|
||||
|
||||
// Fetch snapshot for body tabs when a processor is selected
|
||||
// Fetch snapshot for body/headers tabs when a processor is selected
|
||||
const snapshotQuery = useProcessorSnapshotById(
|
||||
selectedProcessor ? executionId : null,
|
||||
selectedProcessor?.processorId ?? null,
|
||||
@@ -72,15 +72,33 @@ export function DetailPanel({
|
||||
// Determine body content for Input/Output tabs
|
||||
let inputBody: string | undefined;
|
||||
let outputBody: string | undefined;
|
||||
let hasHeaders = false;
|
||||
|
||||
if (selectedProcessor && snapshotQuery.data) {
|
||||
inputBody = snapshotQuery.data.inputBody;
|
||||
outputBody = snapshotQuery.data.outputBody;
|
||||
hasHeaders = !!(snapshotQuery.data.inputHeaders || snapshotQuery.data.outputHeaders);
|
||||
} else if (selectedProcessor && snapshotQuery.isLoading) {
|
||||
// Still loading — keep tabs enabled
|
||||
hasHeaders = true;
|
||||
inputBody = undefined;
|
||||
outputBody = undefined;
|
||||
} else if (!selectedProcessor) {
|
||||
inputBody = executionDetail.inputBody;
|
||||
outputBody = executionDetail.outputBody;
|
||||
hasHeaders = !!(executionDetail.inputHeaders || executionDetail.outputHeaders);
|
||||
}
|
||||
|
||||
const hasInput = !!inputBody;
|
||||
const hasOutput = !!outputBody;
|
||||
|
||||
// If active tab becomes disabled, fall back to info
|
||||
useEffect(() => {
|
||||
if (activeTab === 'headers' && !hasHeaders) setActiveTab('info');
|
||||
if (activeTab === 'input' && !hasInput) setActiveTab('info');
|
||||
if (activeTab === 'output' && !hasOutput) setActiveTab('info');
|
||||
}, [hasHeaders, hasInput, hasOutput, activeTab]);
|
||||
|
||||
// Header display
|
||||
const headerName = selectedProcessor ? selectedProcessor.processorType : 'Exchange';
|
||||
const headerStatus = selectedProcessor ? selectedProcessor.status : executionDetail.status;
|
||||
@@ -103,7 +121,10 @@ export function DetailPanel({
|
||||
<div className={styles.tabBar}>
|
||||
{TABS.map((tab) => {
|
||||
const isActive = activeTab === tab.key;
|
||||
const isDisabled = tab.key === 'config';
|
||||
const isDisabled = tab.key === 'config'
|
||||
|| (tab.key === 'headers' && !hasHeaders)
|
||||
|| (tab.key === 'input' && !hasInput)
|
||||
|| (tab.key === 'output' && !hasOutput);
|
||||
const isError = tab.key === 'error' && hasError;
|
||||
const isErrorGrayed = tab.key === 'error' && !hasError;
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ function buildOverlay(
|
||||
status: proc.status as 'COMPLETED' | 'FAILED',
|
||||
durationMs: proc.durationMs ?? 0,
|
||||
subRouteFailed: subRouteFailed || undefined,
|
||||
hasTraceData: true,
|
||||
hasTraceData: !!proc.hasTraceData,
|
||||
});
|
||||
|
||||
// Recurse into children
|
||||
|
||||
Reference in New Issue
Block a user