feat(workflows): M6 — dashboard run-history + DAG view
A per-app "Workflows" tab: list definitions, browse run history, and inspect a run's per-step progress with a static layered DAG. - API: the run-detail endpoint now returns each step's `depends_on` (loaded from the definition via a new `get_workflow_by_id` reader) so the dashboard can draw the graph edges — the run's step rows don't carry them. - dashboard `$lib/api`: a `workflows` namespace (list / runs / start / run) + the matching types. - `apps/[slug]/workflows/+page.svelte`: workflow table → drill into runs → drill into a run. The run view renders a step table plus an SVG DAG (nodes = steps colored by status, laid out by longest-path level; edges = depends_on, arrowed) and a "Start run" button. Nested/parked steps show their child run. - AppTabBar + the per-app layout gain the Workflows tab (admin-only). Tests: `npm run check` clean (0 errors); two Playwright smoke tests (navigation tabs — workflows loads cleanly + renders its empty state) pass against the full stack. With M6 the v1.2 Workflows track (M1 schema/validation → M2 durable orchestrator → M3 when/templating → M4 nested → M5 SDK/API/CLI → M6 dashboard) is COMPLETE. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -105,6 +105,7 @@ const TABS: Array<{ subpath: string; label: string }> = [
|
||||
{ subpath: '', label: 'main' },
|
||||
{ subpath: '/queues', label: 'queues' },
|
||||
{ subpath: '/queues/never-enqueued', label: 'queue drilldown' },
|
||||
{ subpath: '/workflows', label: 'workflows' },
|
||||
{ subpath: '/files', label: 'files' },
|
||||
{ subpath: '/dead-letters', label: 'dead-letters' },
|
||||
{ subpath: '/users', label: 'app users' },
|
||||
@@ -154,4 +155,21 @@ test.describe('per-app tab navigation accepts slug URLs', () => {
|
||||
// DOM element.
|
||||
await expect(page.getByTestId('queues-empty-state')).toBeVisible();
|
||||
});
|
||||
|
||||
test('workflows tab renders its empty state (v1.2)', async ({ page, uniqueSlug }) => {
|
||||
const slug = uniqueSlug('wf');
|
||||
cleanup.app(slug);
|
||||
const api = await adminApi();
|
||||
try {
|
||||
const res = await api.post('/api/v1/admin/apps', {
|
||||
data: { slug, name: slug }
|
||||
});
|
||||
expect(res.ok()).toBe(true);
|
||||
} finally {
|
||||
await api.dispose();
|
||||
}
|
||||
await expectTabLoadsCleanly(page, `/admin/apps/${slug}/workflows`);
|
||||
// A fresh app has no workflows → the empty-state anchors the smoke test.
|
||||
await expect(page.getByTestId('workflows-empty-state')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user