Files
liqa/client/src/pages/run/AllRunsPage.tsx
T
ars9 e66e53c817 chore: apply code formatting and linting
- format long import statements with consistent line wrapping
- apply consistent indentation across client and server modules
- remove generated tsconfig.tsbuildinfo file
2026-04-14 22:54:03 +03:00

119 lines
2.8 KiB
TypeScript

import { useQuery } from '@tanstack/react-query';
import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Activity } from 'lucide-react';
import { runs } from '../../api';
import type { ScenarioRun, ScenarioRunStep, ScenarioRunStatus } from '../../api';
import {
AutoRefreshIndicator,
Badge,
Breadcrumbs,
Table,
Timestamp,
UuidBadge,
type TableColumn,
} from '../../ui';
import type { BadgeVariant } from '../../ui';
import styles from '../Page.module.css';
const STATUS_VARIANT: Record<ScenarioRunStatus, BadgeVariant> = {
pending: 'neutral',
in_progress: 'info',
pass: 'success',
fail: 'error',
};
const STATUS_LABEL: Record<ScenarioRunStatus, string> = {
pending: 'Pending',
in_progress: 'Running',
pass: 'Pass',
fail: 'Fail',
};
type AllRunRow = ScenarioRun & {
stepRuns: ScenarioRunStep[];
scenario: { id: number; name: string };
};
export function AllRunsPage() {
const { t } = useTranslation();
const navigate = useNavigate();
const {
data: items = [],
isLoading,
error,
refetch,
} = useQuery<AllRunRow[]>({
queryKey: ['runs'],
queryFn: async () => {
const res = await runs.listAll();
return res.data as AllRunRow[];
},
refetchInterval: 10_000,
staleTime: 0,
});
const columns: TableColumn<AllRunRow>[] = [
{ key: 'id', header: t('runs.col_id'), render: (r) => <UuidBadge id={r.id} />, width: 60 },
{
key: 'scenario',
header: t('runs.col_scenario'),
render: (r) => (
<span
className={styles.linkCell}
onClick={(e) => {
e.stopPropagation();
navigate(`/scenarios/${r.scenario.id}`);
}}
>
{r.scenario.name}
</span>
),
},
{
key: 'status',
header: t('runs.col_status'),
width: 110,
render: (r) => <Badge variant={STATUS_VARIANT[r.status]}>{STATUS_LABEL[r.status]}</Badge>,
},
{
key: 'steps',
header: t('runs.col_steps'),
width: 80,
render: (r) => r.stepRuns.length,
},
{
key: 'created',
header: t('runs.col_created'),
width: 160,
render: (r) => <Timestamp value={r.createdAt} />,
},
];
return (
<div>
<div className={styles.pageToolbar}>
<Breadcrumbs items={[{ label: t('runs.title'), icon: <Activity size={14} /> }]} />
<AutoRefreshIndicator
active
pulseKey={items.length}
error={!!error}
onClick={() => refetch()}
/>
</div>
<Table
columns={columns}
data={items}
rowKey={(r) => r.id}
loading={isLoading}
emptyMessage={t('runs.empty')}
pageSize={20}
pageSizeOptions={[10, 20, 50]}
onRowClick={(r) => navigate(`/scenarios/${r.scenarioId}/runs/${r.id}`)}
/>
</div>
);
}