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:
@@ -0,0 +1,69 @@
|
||||
.root {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: var(--font-size-xs);
|
||||
font-family: var(--font-family);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* ── Dot ──────────────────────────────────────────────────── */
|
||||
|
||||
.dot {
|
||||
position: relative;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.dot::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 50%;
|
||||
transition: background var(--transition);
|
||||
}
|
||||
|
||||
/* ── Pulse ring ───────────────────────────────────────────── */
|
||||
|
||||
.ring {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 50%;
|
||||
animation: pulse 0.8s ease-out 1;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 color-mix(in srgb, var(--color-primary) 70%, transparent);
|
||||
}
|
||||
70% {
|
||||
box-shadow: 0 0 0 7px color-mix(in srgb, var(--color-primary) 0%, transparent);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 color-mix(in srgb, var(--color-primary) 0%, transparent);
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Active state ─────────────────────────────────────────── */
|
||||
|
||||
.active .dot::before {
|
||||
background: var(--color-primary);
|
||||
}
|
||||
|
||||
.active .label {
|
||||
color: var(--color-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* ── Inactive / disabled state ────────────────────────────── */
|
||||
|
||||
.inactive .dot::before {
|
||||
background: var(--color-text-muted);
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.inactive .label {
|
||||
color: var(--color-text-muted);
|
||||
opacity: 0.5;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import styles from './AutoRefreshIndicator.module.css';
|
||||
|
||||
export interface AutoRefreshIndicatorProps {
|
||||
active: boolean;
|
||||
pulseKey?: number;
|
||||
label?: string;
|
||||
}
|
||||
|
||||
export function AutoRefreshIndicator({ active, pulseKey, label = 'Live' }: AutoRefreshIndicatorProps) {
|
||||
return (
|
||||
<div
|
||||
className={[styles.root, active ? styles.active : styles.inactive].join(' ')}
|
||||
title={active ? 'Auto-refreshing' : 'Auto-refresh stopped'}
|
||||
aria-label={active ? 'Auto-refreshing' : 'Auto-refresh stopped'}
|
||||
aria-live="polite"
|
||||
>
|
||||
<span className={styles.dot}>
|
||||
{pulseKey !== undefined && pulseKey > 0 && (
|
||||
<span key={pulseKey} className={styles.ring} />
|
||||
)}
|
||||
</span>
|
||||
<span className={styles.label}>{label}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -19,12 +19,17 @@
|
||||
padding: var(--space-3) var(--space-4);
|
||||
font-weight: 700;
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--color-secondary-fg);
|
||||
color: #ffffff;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
border-bottom: var(--border-width) solid var(--color-secondary-border);
|
||||
white-space: nowrap;
|
||||
background: var(--color-secondary-fg);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .th {
|
||||
background: var(--color-secondary);
|
||||
color: var(--color-secondary-fg);
|
||||
}
|
||||
|
||||
/* Rounded top corners on first/last header cells */
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
export { AutoRefreshIndicator } from './AutoRefreshIndicator/AutoRefreshIndicator';
|
||||
export type { AutoRefreshIndicatorProps } from './AutoRefreshIndicator/AutoRefreshIndicator';
|
||||
|
||||
export { Button } from './Button/Button';
|
||||
export type { ButtonProps } from './Button/Button';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user