feat(ui): display business attributes on ExchangeDetail page
Show route-level attributes as Badge strips in the exchange header card, and per-processor attributes above the message IN/OUT panels. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -168,6 +168,27 @@
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
ATTRIBUTES STRIP
|
||||
========================================================================== */
|
||||
.attributesStrip {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
padding: 10px 14px;
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border-subtle);
|
||||
border-radius: var(--radius-lg);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.attributesLabel {
|
||||
font-size: 11px;
|
||||
color: var(--text-muted);
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
TIMELINE SECTION
|
||||
========================================================================== */
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
Badge, StatusDot, MonoText, CodeBlock, InfoCallout,
|
||||
ProcessorTimeline, Spinner, RouteFlow, useToast,
|
||||
LogViewer, ButtonGroup, SectionHeader, useBreadcrumb,
|
||||
Modal, Tabs, Button, Select, Input, Textarea,
|
||||
} from '@cameleer/design-system'
|
||||
import type { ProcessorStep, RouteNode, NodeBadge, LogEntry, ButtonGroupItem } from '@cameleer/design-system'
|
||||
import { useExecutionDetail, useProcessorSnapshot } from '../../api/queries/executions'
|
||||
@@ -11,7 +12,8 @@ import { useCorrelationChain } from '../../api/queries/correlation'
|
||||
import { useDiagramLayout } from '../../api/queries/diagrams'
|
||||
import { mapDiagramToRouteNodes, toFlowSegments } from '../../utils/diagram-mapping'
|
||||
import { useTracingStore } from '../../stores/tracing-store'
|
||||
import { useApplicationConfig, useUpdateApplicationConfig } from '../../api/queries/commands'
|
||||
import { useApplicationConfig, useUpdateApplicationConfig, useReplayExchange } from '../../api/queries/commands'
|
||||
import { useAgents } from '../../api/queries/agents'
|
||||
import { useApplicationLogs } from '../../api/queries/logs'
|
||||
import styles from './ExchangeDetail.module.css'
|
||||
|
||||
@@ -124,6 +126,17 @@ export default function ExchangeDetail() {
|
||||
return result
|
||||
}, [procList, tracedMap])
|
||||
|
||||
// Flatten processor tree into raw node objects (for attribute access)
|
||||
const flatProcNodes = useMemo(() => {
|
||||
const nodes: any[] = []
|
||||
function walk(node: any) {
|
||||
nodes.push(node)
|
||||
if (node.children) node.children.forEach(walk)
|
||||
}
|
||||
procList.forEach(walk)
|
||||
return nodes
|
||||
}, [procList])
|
||||
|
||||
// Default selected processor: first failed, or 0
|
||||
const defaultIndex = useMemo(() => {
|
||||
if (!processors.length) return 0
|
||||
@@ -359,6 +372,16 @@ export default function ExchangeDetail() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Route-level Attributes */}
|
||||
{detail.attributes && Object.keys(detail.attributes).length > 0 && (
|
||||
<div className={styles.attributesStrip}>
|
||||
<span className={styles.attributesLabel}>Attributes</span>
|
||||
{Object.entries(detail.attributes).map(([key, value]) => (
|
||||
<Badge key={key} label={`${key}: ${value}`} color="auto" variant="filled" />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Correlation Chain */}
|
||||
{correlatedExchanges.length > 1 && (
|
||||
<div className={styles.correlationChain}>
|
||||
@@ -522,6 +545,19 @@ export default function ExchangeDetail() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Processor Attributes */}
|
||||
{selectedProc && (() => {
|
||||
const procNode = flatProcNodes[activeIndex]
|
||||
return procNode?.attributes && Object.keys(procNode.attributes).length > 0 ? (
|
||||
<div className={styles.attributesStrip}>
|
||||
<span className={styles.attributesLabel}>Processor Attributes</span>
|
||||
{Object.entries(procNode.attributes).map(([key, value]) => (
|
||||
<Badge key={key} label={`${key}: ${value}`} color="auto" variant="filled" />
|
||||
))}
|
||||
</div>
|
||||
) : null
|
||||
})()}
|
||||
|
||||
{/* Processor Detail Panel (split IN / OUT) */}
|
||||
{selectedProc && snapshot && (
|
||||
<div className={styles.detailSplit}>
|
||||
|
||||
Reference in New Issue
Block a user