Files
cameleer-server/ui/src/components/ExecutionDiagram/tabs/InfoTab.tsx

116 lines
3.9 KiB
TypeScript
Raw Normal View History

import { Badge } from '@cameleer/design-system';
import type { ProcessorNode, ExecutionDetail } from '../types';
import { attributeBadgeColor } from '../../../utils/attribute-color';
import styles from '../ExecutionDiagram.module.css';
interface InfoTabProps {
processor: ProcessorNode | null;
executionDetail: ExecutionDetail;
}
function formatTime(iso: string | undefined): string {
if (!iso) return '-';
try {
const d = new Date(iso);
const h = String(d.getHours()).padStart(2, '0');
const m = String(d.getMinutes()).padStart(2, '0');
const s = String(d.getSeconds()).padStart(2, '0');
const ms = String(d.getMilliseconds()).padStart(3, '0');
return `${h}:${m}:${s}.${ms}`;
} catch {
return iso;
}
}
function formatDuration(ms: number | undefined): string {
if (ms === undefined || ms === null) return '-';
if (ms < 1000) return `${ms}ms`;
return `${(ms / 1000).toFixed(1)}s`;
}
function statusClass(status: string): string {
const s = status?.toUpperCase();
if (s === 'COMPLETED') return styles.statusCompleted;
if (s === 'FAILED') return styles.statusFailed;
return '';
}
function Field({ label, value, mono }: { label: string; value: string; mono?: boolean }) {
return (
<div>
<div className={styles.fieldLabel}>{label}</div>
<div className={mono ? styles.fieldValueMono : styles.fieldValue}>{value || '-'}</div>
</div>
);
}
function Attributes({ attrs }: { attrs: Record<string, string> | undefined }) {
if (!attrs) return null;
const entries = Object.entries(attrs);
if (entries.length === 0) return null;
return (
<div className={styles.attributesSection}>
<div className={styles.attributesLabel}>Attributes</div>
<div className={styles.attributesList}>
{entries.map(([k, v]) => (
<Badge key={k} label={`${k}: ${v}`} color={attributeBadgeColor(String(v))} />
))}
</div>
</div>
);
}
export function InfoTab({ processor, executionDetail }: InfoTabProps) {
if (processor) {
return (
<div>
<div className={styles.infoGrid}>
<Field label="Processor ID" value={processor.processorId} mono />
<Field label="Type" value={processor.processorType} />
<div>
<div className={styles.fieldLabel}>Status</div>
<span className={`${styles.statusBadge} ${statusClass(processor.status)}`}>
{processor.status}
</span>
</div>
<Field label="Start Time" value={formatTime(processor.startTime)} mono />
<Field label="End Time" value={formatTime(processor.endTime)} mono />
<Field label="Duration" value={formatDuration(processor.durationMs)} mono />
<Field label="Endpoint URI" value={processor.processorType} />
<Field label="Resolved URI" value="-" />
<div />
</div>
<Attributes attrs={processor.attributes} />
</div>
);
}
// Exchange-level view
return (
<div>
<div className={styles.infoGrid}>
<Field label="Execution ID" value={executionDetail.executionId} mono />
<Field label="Correlation ID" value={executionDetail.correlationId} mono />
<Field label="Exchange ID" value={executionDetail.exchangeId} mono />
<Field label="Application" value={executionDetail.applicationName} />
<Field label="Route ID" value={executionDetail.routeId} />
<div>
<div className={styles.fieldLabel}>Status</div>
<span className={`${styles.statusBadge} ${statusClass(executionDetail.status)}`}>
{executionDetail.status}
</span>
</div>
<Field label="Start Time" value={formatTime(executionDetail.startTime)} mono />
<Field label="End Time" value={formatTime(executionDetail.endTime)} mono />
<Field label="Duration" value={formatDuration(executionDetail.durationMs)} mono />
</div>
<Attributes attrs={executionDetail.attributes} />
</div>
);
}