feat(housekeeping): complete content events workflow

This commit is contained in:
Simo committed 2026-09-01 21:08:26 +02:00
1 parent f2e67ebace
commit d6235c37a1
42 files changed
+2868 -307

No files matched your search

@@ -0,0 +1,237 @@
# Housekeeping Content Events Vertical Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Deliver a complete ASE event and event-type operator workflow backed by real database data and existing audited mutations.
**Architecture:** Keep the generic Content query/command foundation, add strict route-specific event payloads, and render them through a focused `ContentEventWorkflow`. Use the existing event tables and mutation runtime; introduce dedicated delete command IDs only to enforce reason confirmation.
**Tech Stack:** Next.js 16, React 19, TypeScript, Drizzle/MySQL, Zod, Vitest, React server actions
**Spec:** `docs/superpowers/specs/2026-09-01-housekeeping-content-events-vertical-design.md`
## Global Constraints
- Work directly on `codex/housekeeping-rebuild-stepwise`; do not create a worktree.
- Preserve `/admin/events` and all unrelated local/untracked files.
- Do not add dependencies or change the database schema.
- Keep Polls and Prefixes behavior unchanged.
- Write and run a failing test before each production behavior change.
- Keep every destructive event or event-type action reason-protected and auditable.
---
### Task 1: Typed event query payloads and production loading
**Files:**
- Modify: `src/features/housekeeping/domains/content/queries/content-queries.ts`
- Modify: `src/features/housekeeping/domains/content/queries/content-queries-production.ts`
- Test: `src/features/housekeeping/domains/content/queries/content-queries.test.ts`
**Interfaces:**
- Produces: `ContentEventTypePayload`, `ContentEventSummaryPayload`, `ContentEventDetailPayload`, and their public type guards.
- Produces: validated private payloads for list, create, detail, and type routes.
- Consumes: `ContentQueryItem.privatePayload`, normalized `params.id`, `list.search`, `list.status`, `list.pageSize`, and `list.offset`.
- [x] **Step 1: Write failing contract tests**
Add literal fixtures proving that malformed event payloads fail closed, event list input normalizes a bounded status, and a detail adapter returning more than one item is rejected.
- [x] **Step 2: Run the query test and confirm RED**
Run: `yarn.cmd vitest run src/features/housekeeping/domains/content/queries/content-queries.test.ts`
Expected: FAIL because event payload guards and status normalization do not exist.
- [x] **Step 3: Implement the minimal event contracts**
Add route-specific interfaces with JSON-safe primitive fields and type guards. Extend list input with `status`, normalized to lowercase and at most 32 characters. Extend `isValidData` so each event route accepts only its corresponding payload and detail accepts at most one selected item.
- [x] **Step 4: Run the query test and confirm GREEN**
Run the command from Step 2. Expected: PASS.
- [x] **Step 5: Write failing production-loader tests**
Add tests proving:
```ts
expect(list.items[0]?.privatePayload).toMatchObject({
typeName: "Tournament",
registrationCount: 12,
});
expect(detail.items).toHaveLength(1);
expect(detail.items[0]?.privatePayload).toMatchObject({
description: "Complete event",
eventTypes: [{ id: "2", name: "Tournament" }],
prizes: [{ id: "4", prizeType: "badge" }],
winners: [{ userId: "9", username: "Alice" }],
registrations: [{ userId: "10", username: "Bob" }],
});
```
Also assert that the serialized detail SQL binds the requested ID and that status filtering is bound, not interpolated.
- [x] **Step 6: Run the loader tests and confirm RED**
Run the command from Step 2. Expected: FAIL because production definitions only expose generic summaries.
- [x] **Step 7: Implement production event loaders**
Update event list and type definitions to project full safe summaries. Load event-create options from active event types. Add a dedicated detail loader that performs bounded, parameterized reads for the selected event, event types, prizes, winners with usernames, and registrations with usernames. Return not-found as an empty successful result.
- [x] **Step 8: Run the loader tests and confirm GREEN**
Run the command from Step 2. Expected: PASS.
### Task 2: Event command safety and form field semantics
**Files:**
- Modify: `src/features/housekeeping/domains/content/commands/content-commands.ts`
- Modify: `src/features/housekeeping/domains/content/pages/content-command-form.tsx`
- Test: `src/features/housekeeping/domains/content/commands/content-commands.test.ts`
- Test: `src/features/housekeeping/domains/content/pages/content-pages.test.tsx`
**Interfaces:**
- Produces: `content.engagement.event.delete` and `content.engagement.event-type.delete`, both mapped to existing mutations with `requiresReason: true`.
- Produces: `ContentCommandField.type === "datetime-local"`, submitted as the normalized browser value.
- [x] **Step 1: Write failing command-policy tests**
Assert the two delete command IDs exist, use `event.change` and `event-type.change`, retain `PERMS.EVENTS_EDIT`, and require reasons while non-destructive change commands do not.
- [x] **Step 2: Run command tests and confirm RED**
Run: `yarn.cmd vitest run src/features/housekeeping/domains/content/commands/content-commands.test.ts`
Expected: FAIL because the dedicated delete commands are absent.
- [x] **Step 3: Implement command metadata**
Extend the command definition tuple with an optional `requiresReason` flag and register both dedicated delete command IDs without adding mutation operations.
- [x] **Step 4: Run command tests and confirm GREEN**
Run the command from Step 2. Expected: PASS.
- [x] **Step 5: Write a failing date/time form test**
Render a field with `type: "datetime-local"` and assert the real input type and default value. Submit it and assert the existing server action receives the exact normalized date/time string.
- [x] **Step 6: Run page tests and confirm RED**
Run: `yarn.cmd vitest run src/features/housekeeping/domains/content/pages/content-pages.test.tsx`
Expected: FAIL because `datetime-local` is not supported.
- [x] **Step 7: Implement the minimal field support**
Add `datetime-local` to the field union and map it to `<input type="datetime-local">`; keep the existing bounded string parser.
- [x] **Step 8: Run page tests and confirm GREEN**
Run the command from Step 6. Expected: PASS.
### Task 3: Complete Event workflow UI
**Files:**
- Create: `src/features/housekeeping/domains/content/pages/event-workflow.tsx`
- Modify: `src/features/housekeeping/domains/content/pages/engagement.tsx`
- Create: `src/features/housekeeping/domains/content/pages/event-workflow.test.tsx`
- Modify: `src/features/housekeeping/domains/content/pages/content-pages.test.tsx`
**Interfaces:**
- Produces: `ContentEventWorkflow(props)` for the four event routes.
- Consumes: typed payload guards from Task 1 and command IDs/field semantics from Task 2.
- [x] **Step 1: Write failing list and state tests**
Render real `HousekeepingResult` fixtures and assert loading, forbidden, dependency-error, empty, and ready states. The ready list must expose search, status filtering, result count, type, schedule, capacity, registrations, create/type links, and bounded previous/next links.
- [x] **Step 2: Run workflow tests and confirm RED**
Run: `yarn.cmd vitest run src/features/housekeeping/domains/content/pages/event-workflow.test.tsx`
Expected: FAIL because the component does not exist.
- [x] **Step 3: Implement the list/state slice**
Create the focused workflow component and route the four event route IDs to it from `ContentEngagementPage`, leaving Polls and Prefixes on the existing generic implementation.
- [x] **Step 4: Run workflow tests and confirm GREEN**
Run the command from Step 2. Expected: PASS for list/state tests.
- [x] **Step 5: Write failing create/detail tests**
Assert create uses real type options and date/time inputs. Assert detail prepopulates title, description, selected type, schedule, capacity, room, status, recurrence, and image; automatically binds event IDs for prizes/winners; renders usernames for registrations; and exposes a reason-required delete form using `content.engagement.event.delete`.
- [x] **Step 6: Run workflow tests and confirm RED**
Run the command from Step 2. Expected: FAIL because create/detail workflow sections are incomplete.
- [x] **Step 7: Implement create/detail**
Build field factories from the validated payload. Render not-found separately from dependency failure. Keep related forms and lists inside the selected event detail; never expose manual event-ID inputs.
- [x] **Step 8: Run workflow tests and confirm GREEN**
Run the command from Step 2. Expected: PASS for create/detail tests.
- [x] **Step 9: Write failing event-type tests**
Assert a create form and one prefilled update form per type, plus a reason-required delete form using `content.engagement.event-type.delete`; no operator-entered type ID field is allowed.
- [x] **Step 10: Run workflow tests and confirm RED**
Run the command from Step 2. Expected: FAIL until type management is implemented.
- [x] **Step 11: Implement event-type management and refactor**
Add create/update/delete sections using typed payloads. Extract small field and formatting helpers while all tests stay green.
- [x] **Step 12: Run focused Content tests and confirm GREEN**
Run:
`yarn.cmd vitest run src/features/housekeeping/domains/content/queries/content-queries.test.ts src/features/housekeeping/domains/content/commands/content-commands.test.ts src/features/housekeeping/domains/content/pages/content-pages.test.tsx src/features/housekeeping/domains/content/pages/event-workflow.test.tsx`
Expected: all focused tests PASS.
### Task 4: Full verification and draft PR update
**Files:**
- Modify: `docs/superpowers/plans/2026-09-01-housekeeping-content-events-vertical.md`
- Modify: draft PR 53 body in English and Dutch
**Interfaces:**
- Consumes: all deliverables from Tasks 1–3.
- Produces: verified commit(s), pushed branch, and current bilingual PR evidence.
- [ ] **Step 1: Run static and targeted checks**
Run the repository TypeScript, Biome, Knip, focused test, and Housekeeping matrix commands from `package.json` and the existing Housekeeping evidence workflow. Fix only failures caused by this vertical.
- [ ] **Step 2: Run the full test suite with coverage**
Run: `yarn.cmd test`
Expected: zero failing test files and zero failing tests.
- [ ] **Step 3: Run the production build**
Run: `yarn.cmd build`
Expected: exit code 0 with canonical `/ase-next` routes generated.
- [ ] **Step 4: Review the exact diff**
Run: `git diff --check`, `git status --short`, and `git diff --stat origin/main...HEAD` after committing. Confirm `.remember/` and `.superpowers/brainstorm/` remain untouched and untracked.
- [ ] **Step 5: Commit and push exact paths**
Commit query/command/UI/test/plan files with a scoped message, then push `codex/housekeeping-rebuild-stepwise`.
- [ ] **Step 6: Update and verify the draft PR**
Add the Events vertical and fresh verification counts to PR 53 in English and Dutch. Confirm the remote head matches local HEAD and inspect CI status without claiming deployment.