Add sortable table columns, pre-populate date filter, inline command palette
- Table headers are now clickable to sort by column (client-side) - From date picker defaults to today 00:00 instead of empty - Command palette expands inline from search bar instead of modal dialog Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import { useState, useMemo } from 'react';
|
||||
import type { ExecutionSummary } from '../../api/schema';
|
||||
import { StatusPill } from '../../components/shared/StatusPill';
|
||||
import { DurationBar } from '../../components/shared/DurationBar';
|
||||
@@ -12,6 +12,9 @@ interface ResultsTableProps {
|
||||
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',
|
||||
@@ -21,8 +24,74 @@ function formatTime(iso: string) {
|
||||
});
|
||||
}
|
||||
|
||||
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 (
|
||||
<th
|
||||
className={`${styles.th} ${styles.thSortable} ${isActive ? styles.thActive : ''}`}
|
||||
style={style}
|
||||
onClick={() => onSort(column)}
|
||||
>
|
||||
{label}
|
||||
<span className={styles.sortArrow}>
|
||||
{isActive ? (direction === 'asc' ? '\u25B2' : '\u25BC') : '\u25B4'}
|
||||
</span>
|
||||
</th>
|
||||
);
|
||||
}
|
||||
|
||||
export function ResultsTable({ results, loading }: ResultsTableProps) {
|
||||
const [expandedId, setExpandedId] = useState<string | null>(null);
|
||||
const [sortColumn, setSortColumn] = useState<SortColumn | null>(null);
|
||||
const [sortDir, setSortDir] = useState<SortDir>('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 (
|
||||
@@ -46,16 +115,16 @@ export function ResultsTable({ results, loading }: ResultsTableProps) {
|
||||
<thead className={styles.thead}>
|
||||
<tr>
|
||||
<th className={styles.th} style={{ width: 32 }} />
|
||||
<th className={styles.th}>Timestamp</th>
|
||||
<th className={styles.th}>Status</th>
|
||||
<th className={styles.th}>Application</th>
|
||||
<th className={styles.th}>Route</th>
|
||||
<th className={styles.th}>Correlation ID</th>
|
||||
<th className={styles.th}>Duration</th>
|
||||
<SortableTh label="Timestamp" column="startTime" activeColumn={sortColumn} direction={sortDir} onSort={handleSort} />
|
||||
<SortableTh label="Status" column="status" activeColumn={sortColumn} direction={sortDir} onSort={handleSort} />
|
||||
<SortableTh label="Application" column="agentId" activeColumn={sortColumn} direction={sortDir} onSort={handleSort} />
|
||||
<SortableTh label="Route" column="routeId" activeColumn={sortColumn} direction={sortDir} onSort={handleSort} />
|
||||
<SortableTh label="Correlation ID" column="correlationId" activeColumn={sortColumn} direction={sortDir} onSort={handleSort} />
|
||||
<SortableTh label="Duration" column="durationMs" activeColumn={sortColumn} direction={sortDir} onSort={handleSort} />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{results.map((exec) => {
|
||||
{sortedResults.map((exec) => {
|
||||
const isExpanded = expandedId === exec.executionId;
|
||||
return (
|
||||
<ResultRow
|
||||
|
||||
Reference in New Issue
Block a user