import { useState, useMemo } from 'react'; import type { ExecutionSummary } from '../../api/schema-types'; import { StatusPill } from '../../components/shared/StatusPill'; import { DurationBar } from '../../components/shared/DurationBar'; import { AppBadge } from '../../components/shared/AppBadge'; import { ProcessorTree } from './ProcessorTree'; import { ExchangeDetail } from './ExchangeDetail'; import styles from './ResultsTable.module.css'; interface ResultsTableProps { results: ExecutionSummary[]; loading: boolean; } type SortColumn = 'startTime' | 'status' | 'agentId' | 'routeId' | 'correlationId' | 'durationMs'; type SortDir = 'asc' | 'desc'; function formatTime(iso: string) { return new Date(iso).toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', second: '2-digit', fractionalSecondDigits: 3, }); } function compareFn(a: ExecutionSummary, b: ExecutionSummary, col: SortColumn, dir: SortDir): number { let cmp = 0; switch (col) { case 'startTime': cmp = a.startTime.localeCompare(b.startTime); break; case 'status': cmp = a.status.localeCompare(b.status); break; case 'agentId': cmp = a.agentId.localeCompare(b.agentId); break; case 'routeId': cmp = a.routeId.localeCompare(b.routeId); break; case 'correlationId': cmp = (a.correlationId ?? '').localeCompare(b.correlationId ?? ''); break; case 'durationMs': cmp = a.durationMs - b.durationMs; break; } return dir === 'asc' ? cmp : -cmp; } interface SortableThProps { label: string; column: SortColumn; activeColumn: SortColumn | null; direction: SortDir; onSort: (col: SortColumn) => void; style?: React.CSSProperties; } function SortableTh({ label, column, activeColumn, direction, onSort, style }: SortableThProps) { const isActive = activeColumn === column; return ( onSort(column)} > {label} {isActive ? (direction === 'asc' ? '\u25B2' : '\u25BC') : '\u25B4'} ); } export function ResultsTable({ results, loading }: ResultsTableProps) { const [expandedId, setExpandedId] = useState(null); const [sortColumn, setSortColumn] = useState(null); const [sortDir, setSortDir] = useState('desc'); const sortedResults = useMemo(() => { if (!sortColumn) return results; return [...results].sort((a, b) => compareFn(a, b, sortColumn, sortDir)); }, [results, sortColumn, sortDir]); function handleSort(col: SortColumn) { if (sortColumn === col) { setSortDir((d) => (d === 'asc' ? 'desc' : 'asc')); } else { setSortColumn(col); setSortDir('desc'); } } if (loading && results.length === 0) { return (
Loading executions...
); } if (results.length === 0) { return (
No executions found matching your filters.
); } return (
{sortedResults.map((exec) => { const isExpanded = expandedId === exec.executionId; return ( setExpandedId(isExpanded ? null : exec.executionId)} /> ); })}
); } function ResultRow({ exec, isExpanded, onToggle, }: { exec: ExecutionSummary; isExpanded: boolean; onToggle: () => void; }) { return ( <> › {formatTime(exec.startTime)} {exec.routeId} {exec.correlationId ?? '-'} {isExpanded && (
)} ); }