feat(M16 fase 2): full jobs-UI port — split-pane, SSE, admin actions #4

Merged
janpeter merged 23 commits from feat/M16-fase2-jobs-ui into main 2026-05-26 22:47:16 +02:00
Owner

M16 platform-split fase 2 — port van de full split-pane /jobs-UI van Scrum4Me naar workers.jp-visser.nl met admin-variant.

Wat is er gebouwd

Batch A — UI primitives + deps (6 commits)

  • Deps: zustand@5, immer@10, clsx, tailwind-merge, cva, sonner, @testing-library, @base-ui/react
  • lib/utils.ts (cn helper) + lib/debug.ts
  • shadcn primitives: button, badge, table, skeleton
  • split-pane component (localStorage-persisted ipv server-synced store; minor adaptation)
  • Smoke tests (3/3)

Batch B — Data layer (6 commits, incl. spec-fix)

  • lib/jobs-mapper.ts met admin-variant JOB_INCLUDE (incl. user.username)
  • lib/job-config-snapshot.ts
  • actions/jobs-page.tsfetchJobsPageData admin-variant (geen user_id filter)
  • actions/claude-jobs.ts — cancel + restart met requireWorkersAdmin
  • Spec-fix: plan miste pg_notify in beide actions + create-new restart breekt sprint_task_execution rows. Aligned met Scrum4Me's pattern: update-in-place + tx-scoped sprint_task_execution reset + pg_notify emit.

Batch C — Realtime (3 commits)

  • lib/realtime/pg-client-cleanup.ts
  • /api/realtime/queue SSE endpoint (admin-only, met pg.on('error') brought in van Scrum4Me)
  • stores/jobs-store.ts (Zustand + immer) + hooks/use-jobs-realtime.ts

Batch D — UI components (5 commits)

  • job-card, jobs-column, jobs-board + time-filter, detail-pane, usage-pane, sprint-sub-tasks-pane
  • Helpers ported: components/shared/job-status.ts, components/shared/filter-pills.tsx, components/ui/popover.tsx, lib/jobs-time-filter.ts
  • demo-tooltip.tsx stubbed als no-op (workers heeft geen demo)
  • useUserSettingsStore vervangen door local useState (geen settings-store in workers)
  • Integration test JobsBoard (2 tests)

Batch E — Page wiring (2 commits + 1 build-fix)

  • app/jobs/page.tsx met admin-gate (requireWorkersAdmin → 403 redirect /login)
  • app/jobs/loading.tsx skeleton grid
  • /api/jobs/[id] + /api/jobs/[id]/sub-tasks (admin)
  • Build-fix: lib/prisma.ts lazy connection-string voor next build page-data collection (DATABASE_URL is build-time niet beschikbaar in Docker).

Verificatie

  • npm run verify groen: lint + typecheck + 67 tests / 13 files passing
  • npm run build slaagt: alle 8 routes correct gemarkeerd dynamic (ƒ) waar DB-toegang nodig is

Architectuur-keuzes (gedocumenteerd in plan)

  • Workers /jobs: full split-pane port (~1500 LOC inclusief stores/hooks)
  • Scrum4Me /jobs: action-buttons verborgen + deeplink-banner (separate PR op Scrum4Me)
  • SSE endpoint: /api/realtime/queue (per design-spec)
  • Cross-user: workers admin sees all jobs (geen user_id filter, includes username)

Companion PR (Scrum4Me)

feat(M16 fase 2): jobs-deeplink-banner + hide action buttons — to be merged in tandem

Server deploy (na merge)

Zie docs/runbooks/platform-split-server-tasks.md sectie 2 fase 2 — pull + rebuild scrum4me-workers container.

**M16 platform-split fase 2** — port van de full split-pane `/jobs`-UI van Scrum4Me naar workers.jp-visser.nl met admin-variant. ## Wat is er gebouwd ### Batch A — UI primitives + deps (6 commits) - Deps: zustand@5, immer@10, clsx, tailwind-merge, cva, sonner, @testing-library, @base-ui/react - `lib/utils.ts` (cn helper) + `lib/debug.ts` - shadcn primitives: button, badge, table, skeleton - split-pane component (localStorage-persisted ipv server-synced store; minor adaptation) - Smoke tests (3/3) ### Batch B — Data layer (6 commits, incl. spec-fix) - `lib/jobs-mapper.ts` met admin-variant `JOB_INCLUDE` (incl. user.username) - `lib/job-config-snapshot.ts` - `actions/jobs-page.ts` — `fetchJobsPageData` admin-variant (geen user_id filter) - `actions/claude-jobs.ts` — cancel + restart met `requireWorkersAdmin` - **Spec-fix:** plan miste pg_notify in beide actions + create-new restart breekt `sprint_task_execution` rows. Aligned met Scrum4Me's pattern: update-in-place + tx-scoped sprint_task_execution reset + pg_notify emit. ### Batch C — Realtime (3 commits) - `lib/realtime/pg-client-cleanup.ts` - `/api/realtime/queue` SSE endpoint (admin-only, met pg.on('error') brought in van Scrum4Me) - `stores/jobs-store.ts` (Zustand + immer) + `hooks/use-jobs-realtime.ts` ### Batch D — UI components (5 commits) - job-card, jobs-column, jobs-board + time-filter, detail-pane, usage-pane, sprint-sub-tasks-pane - Helpers ported: `components/shared/job-status.ts`, `components/shared/filter-pills.tsx`, `components/ui/popover.tsx`, `lib/jobs-time-filter.ts` - `demo-tooltip.tsx` stubbed als no-op (workers heeft geen demo) - `useUserSettingsStore` vervangen door local useState (geen settings-store in workers) - Integration test JobsBoard (2 tests) ### Batch E — Page wiring (2 commits + 1 build-fix) - `app/jobs/page.tsx` met admin-gate (`requireWorkersAdmin` → 403 redirect /login) - `app/jobs/loading.tsx` skeleton grid - `/api/jobs/[id]` + `/api/jobs/[id]/sub-tasks` (admin) - **Build-fix:** `lib/prisma.ts` lazy connection-string voor `next build` page-data collection (DATABASE_URL is build-time niet beschikbaar in Docker). ## Verificatie - `npm run verify` groen: lint + typecheck + **67 tests / 13 files** passing - `npm run build` slaagt: alle 8 routes correct gemarkeerd dynamic (ƒ) waar DB-toegang nodig is ## Architectuur-keuzes (gedocumenteerd in plan) - **Workers /jobs:** full split-pane port (~1500 LOC inclusief stores/hooks) - **Scrum4Me /jobs:** action-buttons verborgen + deeplink-banner (separate PR op Scrum4Me) - **SSE endpoint:** `/api/realtime/queue` (per design-spec) - **Cross-user:** workers admin sees all jobs (geen user_id filter, includes username) ## Companion PR (Scrum4Me) feat(M16 fase 2): jobs-deeplink-banner + hide action buttons — to be merged in tandem ## Server deploy (na merge) Zie `docs/runbooks/platform-split-server-tasks.md` sectie 2 fase 2 — pull + rebuild scrum4me-workers container.
Plan-spec deviation: workers' restart created a new job + neither action emitted
pg_notify. Without trigger on claude_jobs, SSE listeners would never see the
status-change events. Also: create-new orphaned sprint_task_execution rows
which FK to claude_job.id.

Now mirrors Scrum4Me's pattern: requireWorkersAdmin (admin variant), CUID
validation, terminal/restartable checks unchanged. Cancel + restart both emit
pg_notify on scrum4me_changes. Restart resets sprint_task_execution children
in-transaction for SPRINT_IMPLEMENTATION jobs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`next build`'s page-data collection evaluates route modules, including those
that import `lib/prisma.ts` (e.g. /jobs, /api/jobs/[id]). At that point
DATABASE_URL is not yet available — env_file values land at runtime via
docker-compose.

Default to empty connection string so Pool/PrismaPg/PrismaClient init
lazily without connecting. First real query at runtime fails clearly if
DATABASE_URL is still missing. Mirrors the SKIP_ENV_VALIDATION escape
hatch in lib/env.ts.

Verified: `npm run build` now succeeds, all 8 routes correctly marked
dynamic (ƒ) where DB access is needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
janpeter/scrum4me-workers!4
No description provided.