xtablo-source/.planning/phases/11-individual-planning/11-01-PLAN.md
2026-05-16 07:21:34 +02:00

10 KiB

phase plan type wave depends_on files_modified autonomous requirements must_haves
11-individual-planning 01 execute 1
backend/internal/web/handlers_planning_test.go
backend/internal/web/handlers_planning.go
backend/internal/web/router.go
backend/cmd/web/main.go
backend/templates/planning.templ
backend/templates/planning_forms.go
true
PLAN-01
PLAN-02
PLAN-03
PLAN-04
truths artifacts
D-01/D-02: /planning defaults to an upcoming 14-day agenda starting today
D-03/D-04: navigation uses fixed 14-day windows with Today, previous-window, and next-window controls; past events are reachable through previous-window navigation
D-05/D-06: planning is a single continuous agenda list, not a month/week calendar and not grouped by day or tablo
D-07/D-08/D-10: each row shows time, title, tablo context, and location when present; no description snippets; no-end events display start time only
D-09: tablo context shows title plus a color dot when tablo_color is present
UI-SPEC: page uses existing Layout, compact header, toolbar, semantic agenda list, empty state, and no JS framework
PLAN-03: every event links back to /tablos/{id}/events?month=YYYY-MM for its event date
Security: planning aggregation uses authenticated user ID with ListUserEventsRange; no client-supplied user/tablo ID drives results
path provides
backend/internal/web/handlers_planning.go protected /planning route handler and 14-day range query
path provides
backend/templates/planning.templ server-rendered planning agenda page
path provides
backend/templates/planning_forms.go planning view models and date/link helpers
path provides
backend/internal/web/handlers_planning_test.go DB-backed planning behavior and authorization coverage
Vertical slice 1: add the protected `/planning` page that renders the authenticated user's accessible events across tablos as a fixed 14-day chronological agenda with working navigation, empty state, and source tablo links. @/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/11-individual-planning/11-CONTEXT.md @/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/11-individual-planning/11-RESEARCH.md @/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/11-individual-planning/11-UI-SPEC.md @/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/11-individual-planning/11-PATTERNS.md @/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/10-events/10-03-SUMMARY.md

<threat_model> T-11-01 Unauthenticated access: /planning must be mounted inside the protected auth.RequireAuth route group so unauthenticated users are redirected to /login. T-11-02 Cross-user event leak: handler must call ListUserEventsRange with auth.UserFromContext(r.Context()).ID; no query parameter may select user or tablo scope. T-11-03 XSS: event titles, tablo titles, locations, and color strings must render through templ escaped expressions; do not use templ.Raw. T-11-04 Invalid date input: malformed start query values must fall back to today's local date instead of returning 500 or generating unsafe SQL. T-11-05 Source-link drift: event deep links must derive month=YYYY-MM from each event's own event_date, not from the current planning window. </threat_model>

Task 1: Add RED coverage for protected planning agenda behavior - backend/internal/web/handlers_planning_test.go - backend/internal/web/router.go - backend/internal/web/handlers_events_test.go - backend/internal/web/handlers_tablos_test.go - backend/internal/web/router.go - backend/internal/db/queries/events.sql - .planning/phases/11-individual-planning/11-CONTEXT.md - .planning/phases/11-individual-planning/11-UI-SPEC.md - .planning/phases/11-individual-planning/11-RESEARCH.md Create `backend/internal/web/handlers_planning_test.go` with DB-backed tests that initially fail: 1. `TestPlanningRequiresAuth` sends unauthenticated `GET /planning` and expects a redirect to `/login`. 2. `TestPlanningDefaultsToUpcomingFourteenDays` injects `Now` as `2026-05-16`, creates owned events on `2026-05-16`, `2026-05-29`, and `2026-05-30`, then expects the first two titles and not the third. 3. `TestPlanningListsOwnedEventsChronologically` creates owned events across two tablos in non-chronological insert order and expects response order by date, start time, then title; it also expects visible event title, time, tablo title, color value or dot marker, location, and `/tablos/{id}/events?month=YYYY-MM`. 4. `TestPlanningDoesNotLeakOtherUsersEvents` creates an event for another user in the same range and expects the foreign title to be absent. 5. `TestPlanningNavigationLinks` expects visible `Previous 14 days`, `Today`, and `Next 14 days` links with `start=2026-05-02`, `/planning` or `start=2026-05-16`, and `start=2026-05-30`. 6. `TestPlanningEmptyState` expects `No events in this range` and `Use the navigation controls to browse another 14-day window.` while the navigation controls remain visible. 7. `TestPlanningInvalidStartFallsBackToToday` requests `/planning?start=not-a-date` with injected `Now=2026-05-16` and expects the same default range label as `/planning`. Add a local `newPlanningTestRouter(q, store, now)` helper that wires `PlanningDeps{Queries: q, Now: now}` through `NewRouter`. cd backend && go test ./internal/web -run 'TestPlanning' -count=1 - `backend/internal/web/handlers_planning_test.go` contains all seven named `TestPlanning...` tests. - Tests use full router requests with authenticated session cookies where needed. - The ownership test proves another user's event title is not present in `/planning`. - The default-window test proves `2026-05-16` and `2026-05-29` are included while `2026-05-30` is excluded. Task 2: Implement the protected planning route, handler, view models, and agenda template - backend/internal/web/handlers_planning.go - backend/internal/web/router.go - backend/cmd/web/main.go - backend/templates/planning.templ - backend/templates/planning_forms.go - backend/internal/web/handlers_events.go - backend/internal/web/router.go - backend/cmd/web/main.go - backend/templates/layout.templ - backend/templates/events_forms.go - backend/templates/events.templ - backend/templates/tablos.templ - backend/internal/db/queries/events.sql - .planning/phases/11-individual-planning/11-UI-SPEC.md - .planning/phases/11-individual-planning/11-PATTERNS.md Add `PlanningDeps{Queries *sqlc.Queries, Now func() time.Time}` and `PlanningPageHandler(deps PlanningDeps)` in `handlers_planning.go`. The handler must default `Now` to `time.Now`, parse `start` with layout `2006-01-02`, fall back to today's local date when blank or invalid, compute `end := start.AddDate(0, 0, 13)`, and call `ListUserEventsRange` with authenticated `user.ID`, `pgDateFromTime(start)`, and `pgDateFromTime(end)`. Extend `NewRouter` to accept `planningDeps PlanningDeps` after `eventDeps EventsDeps` and before `fileDeps FilesDeps`; mount `r.Get("/planning", PlanningPageHandler(planningDeps))` inside the protected route group before `/tablos/new` and `/tablos/{id}` routes. Update `backend/cmd/web/main.go` to construct `planningDeps := web.PlanningDeps{Queries: q}` and pass it to `NewRouter`; update all `NewRouter` test helper call sites with `PlanningDeps{Queries: q}` or `PlanningDeps{}` where the route is not exercised. Add `PlanningAgenda` and `PlanningEventRow` helpers in `planning_forms.go` with exact URL helper behavior: planning navigation URLs use `start=YYYY-MM-DD`, today can use `/planning`, and event links use `/tablos/{tablo_id}/events?month=YYYY-MM` derived from each event date. Add `PlanningPage` in `planning.templ` using existing `Layout`, `ui-button` classes, one `h1` with `Planning`, a concise date range label, previous/today/next links, a semantic continuous agenda list, compact rows, optional location, decorative tablo color dot, and the exact empty-state copy from `11-UI-SPEC.md`. Do not add migrations, JavaScript framework code, calendar grids, description snippets, grouping headings, create/edit/delete controls, registry components, or custom SVG icons. Run `cd backend && just generate`. cd backend && just generate && go test ./internal/web -run 'TestPlanning' -count=1 - `backend/internal/web/router.go` contains `r.Get("/planning", PlanningPageHandler(planningDeps))` inside the protected group. - `PlanningPageHandler` calls `ListUserEventsRange` with the authenticated user's ID. - `planning.templ` contains `Planning`, `Previous 14 days`, `Today`, `Next 14 days`, `No events in this range`, and no description rendering. - Event links contain `/tablos/` and `/events?month=`. - `cd backend && just generate && go test ./internal/web -run 'TestPlanning' -count=1` exits 0. Run: - `cd backend && just generate` - `cd backend && go test ./internal/web -run 'TestPlanning' -count=1` - `git diff --check`

<success_criteria>

  • PLAN-01: authenticated users can open /planning; unauthenticated users are redirected.
  • PLAN-02: planning lists owned accessible events across tablos in chronological order.
  • PLAN-03: each listed event links back to its tablo Events tab with the event month.
  • PLAN-04: empty state and fixed 14-day navigation work without a JS framework. </success_criteria>