Validated against live OpenAPI spec at /api/v1/api-docs. Fixes: - duration → durationMs (all models) - Remove processorCount (not in ExecutionSummary) - Remove ProcessorNode.index and .uri (not in backend) - ProcessorSnapshot is Record<string,string>, not structured object - Add missing fields: endTime, diagramContentHash, exchangeId, etc. - Save openapi.json from live server Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
70 lines
2.7 KiB
TypeScript
70 lines
2.7 KiB
TypeScript
import { useExecutionDetail } from '../../api/queries/executions';
|
|
import type { ProcessorNode as ProcessorNodeType } from '../../api/schema';
|
|
import styles from './ProcessorTree.module.css';
|
|
|
|
const ICON_MAP: Record<string, { label: string; className: string }> = {
|
|
from: { label: 'EP', className: styles.iconEndpoint },
|
|
to: { label: 'EP', className: styles.iconEndpoint },
|
|
toD: { label: 'EP', className: styles.iconEndpoint },
|
|
choice: { label: 'CB', className: styles.iconEip },
|
|
when: { label: 'CB', className: styles.iconEip },
|
|
otherwise: { label: 'CB', className: styles.iconEip },
|
|
split: { label: 'CB', className: styles.iconEip },
|
|
aggregate: { label: 'CB', className: styles.iconEip },
|
|
filter: { label: 'CB', className: styles.iconEip },
|
|
multicast: { label: 'CB', className: styles.iconEip },
|
|
recipientList: { label: 'CB', className: styles.iconEip },
|
|
routingSlip: { label: 'CB', className: styles.iconEip },
|
|
dynamicRouter: { label: 'CB', className: styles.iconEip },
|
|
exception: { label: '!!', className: styles.iconError },
|
|
onException: { label: '!!', className: styles.iconError },
|
|
};
|
|
|
|
function getIcon(type: string, status: string) {
|
|
if (status === 'FAILED') return { label: '!!', className: styles.iconError };
|
|
const key = type.toLowerCase();
|
|
return ICON_MAP[key] ?? { label: 'PR', className: styles.iconProcessor };
|
|
}
|
|
|
|
export function ProcessorTree({ executionId }: { executionId: string }) {
|
|
const { data, isLoading } = useExecutionDetail(executionId);
|
|
|
|
if (isLoading) return <div className={styles.tree}><div className={styles.loading}>Loading processor tree...</div></div>;
|
|
if (!data) return null;
|
|
|
|
return (
|
|
<div className={styles.tree}>
|
|
<h4 className={styles.title}>Processor Execution Tree</h4>
|
|
{data.processors.map((proc, i) => (
|
|
<ProcessorNodeView key={proc.processorId ?? i} node={proc} />
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ProcessorNodeView({ node }: { node: ProcessorNodeType }) {
|
|
const icon = getIcon(node.processorType, node.status);
|
|
|
|
return (
|
|
<div>
|
|
<div className={styles.procNode}>
|
|
<div className={styles.procConnector} />
|
|
<div className={`${styles.procIcon} ${icon.className}`}>{icon.label}</div>
|
|
<div className={styles.procInfo}>
|
|
<div className={styles.procType}>{node.processorType}</div>
|
|
</div>
|
|
<div className={styles.procTiming}>
|
|
<span className={styles.procDuration}>{node.durationMs}ms</span>
|
|
</div>
|
|
</div>
|
|
{node.children.length > 0 && (
|
|
<div className={styles.nested}>
|
|
{node.children.map((child, i) => (
|
|
<ProcessorNodeView key={child.processorId ?? i} node={child} />
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|