feat(runs): add runs pages, polling, autorefresh indicator, and scheduler fix

- add global runs page and per-scenario runs/run-detail pages
- run detail page polls every 1s until pass/fail, cleans up on unmount
- runs list pages poll every 10s with AutoRefreshIndicator (pulse on data load)
- fix scheduler: set run to pass after empty step loop to prevent stuck in_progress
- fix table header colors and link cell color for readability
- fix play button to navigate to the new run after creation
- fix package.json import paths in server for Docker build context
This commit is contained in:
2026-04-09 21:58:27 +03:00
parent ebcb8b8ff4
commit 1f3a604940
20 changed files with 745 additions and 22 deletions
+21 -3
View File
@@ -5,6 +5,8 @@ import type {
Session,
Scenario,
ScenarioRun,
ScenarioRunDetail,
ScenarioRunStep,
ScenarioStep,
} from './types';
@@ -103,17 +105,33 @@ export const scenarios = {
run(id: number): Promise<ScenarioRun> {
return request(`/scenarios/${id}/run`, { method: 'POST' });
},
getRun(scenarioId: number, runId: number): Promise<ScenarioRun> {
getRun(scenarioId: number, runId: number): Promise<ScenarioRunDetail> {
return request(`/scenarios/${scenarioId}/run/${runId}`);
},
waitForRun(scenarioId: number, runId: number): Promise<ScenarioRun> {
waitForRun(scenarioId: number, runId: number): Promise<ScenarioRunDetail> {
return request(`/scenarios/${scenarioId}/run/${runId}/wait`, { method: 'POST' });
},
listRuns(scenarioId: number, page = 1, limit = 20): Promise<PaginatedResponse<ScenarioRun>> {
listRuns(scenarioId: number, page = 1, limit = 20): Promise<PaginatedResponse<ScenarioRun & { stepRuns: ScenarioRunStep[] }>> {
return request(`/scenarios/${scenarioId}/runs?page=${page}&limit=${limit}`);
},
};
// ── Runs ─────────────────────────────────────────────────────────────────────
export const runs = {
listAll(page = 1, limit = 20, status?: string): Promise<PaginatedResponse<ScenarioRun & { stepRuns: ScenarioRunStep[]; scenario: { id: number; name: string } }>> {
const q = new URLSearchParams({ page: String(page), limit: String(limit) });
if (status) q.set('status', status);
return request(`/scenarios/runs?${q}`);
},
list(scenarioId: number, page = 1, limit = 20): Promise<PaginatedResponse<ScenarioRun & { stepRuns: ScenarioRunStep[] }>> {
return request(`/scenarios/${scenarioId}/runs?page=${page}&limit=${limit}`);
},
get(scenarioId: number, runId: number): Promise<ScenarioRunDetail> {
return request(`/scenarios/${scenarioId}/run/${runId}`);
},
};
// ── Scenario Steps ────────────────────────────────────────────────────────────
export interface CreateStepPayload {