diff --git a/.planning/STATE.md b/.planning/STATE.md index 76b45c1..30c7c71 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -2,15 +2,15 @@ gsd_state_version: 1.0 milestone: v2.0 milestone_name: Collaboration, planning, and social sign-in -status: Phase 09 UAT passed; security review pending -last_updated: "2026-05-15T22:12:20.689Z" -last_activity: 2026-05-15 -- Phase 09 UAT gap resolved and Plan 09-04 closed +status: ready_to_execute +last_updated: "2026-05-15T22:18:38.208Z" +last_activity: 2026-05-15 -- Phase 10 planning complete progress: total_phases: 5 completed_phases: 2 - total_plans: 9 + total_plans: 13 completed_plans: 9 - percent: 100 + percent: 69 --- # STATE @@ -24,15 +24,15 @@ progress: See: `.planning/PROJECT.md` (updated 2026-05-15) **Core value:** A user can sign in and run the Tablos workflow — organize work, attach files, discuss, and plan scheduled events — without a JS framework or managed chat provider. -**Current focus:** Phase 09 — etapes +**Current focus:** Phase 10 — events ## Current Position -Phase: 09 (etapes) — EXECUTING -Plan: 4 of 4 -Status: Phase 09 UAT passed; security review pending -Last activity: 2026-05-15 -- Phase 09 UAT gap resolved and Plan 09-04 closed -Resume file: .planning/phases/10-events/10-UI-SPEC.md +Phase: 10 (events) — PLANNED +Plan: 0 of 4 +Status: Ready to execute +Last activity: 2026-05-15 -- Phase 10 planning complete +Resume file: .planning/phases/10-events/10-01-PLAN.md ## Phase Status @@ -40,7 +40,7 @@ Resume file: .planning/phases/10-events/10-UI-SPEC.md |---|-------|--------| | 8 | Social Sign-in | ✓ Complete | | 9 | Etapes | ◆ UAT passed; security pending | -| 10 | Events | ○ Pending | +| 10 | Events | ◆ Ready to execute | | 11 | Individual Planning | ○ Pending | | 12 | Native Tablo Chat | ○ Pending | diff --git a/.planning/phases/10-events/10-01-PLAN.md b/.planning/phases/10-events/10-01-PLAN.md new file mode 100644 index 0000000..67decaf --- /dev/null +++ b/.planning/phases/10-events/10-01-PLAN.md @@ -0,0 +1,156 @@ +--- +phase: 10-events +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - backend/migrations/0008_events.sql + - backend/internal/db/queries/events.sql + - backend/internal/web/handlers_events_test.go + - backend/internal/web/handlers_events.go + - backend/internal/web/router.go + - backend/cmd/web/main.go + - backend/templates/events.templ + - backend/templates/events_forms.go + - backend/templates/tablos.templ +autonomous: true +requirements: [EVENT-01, EVENT-03, EVENT-04, EVENT-05] + +must_haves: + truths: + - "GOAL: A user can schedule events that belong to tablos with the same authorization expectations as tasks and files" + - "EVENT-01: User can create a scheduled event attached to a tablo with title, start time, optional end time, optional description, and optional location" + - "EVENT-03: Tablo detail page includes an Events view listing that tablo's scheduled events" + - "EVENT-04: Event validation requires end_time to be empty or after start_time" + - "EVENT-05: Event create/list authorization follows tablo ownership via loadOwnedTablo" + - "D-01/D-05: Store local event_date, start_time, and nullable end_time columns, not timestamptz" + - "D-02/D-03/D-04: Date/start time are required; end time is optional and display omits end when blank" + - "D-06/D-07/D-10/D-17/D-18/D-19: Events tab renders a month grid showing past/future events as titles ordered by start_time then title" + - "D-12/D-13/D-15: Inline create form fields refresh the whole Events tab after create" + - "UI-SPEC: Events tab uses #events-tab, #event-form-slot, New event, Create event, Close form, and slate/blue/red local UI styling" + artifacts: + - path: "backend/migrations/0008_events.sql" + provides: "events table with local date/time columns, tablo FK, end-after-start check, and ordering indexes" + - path: "backend/internal/db/queries/events.sql" + provides: "CreateEvent, GetEventByID, and ListEventsByTabloRange" + - path: "backend/internal/web/handlers_events.go" + provides: "Events tab, create form, create validation, month data loading, and ownership checks" + - path: "backend/templates/events.templ" + provides: "Events tab month grid and inline create form rendering" +--- + + +Vertical slice 1: a signed-in user can open a tablo's Events tab, create an event with local date/time fields, and see the event title in that tablo's month calendar. This plan establishes the event schema, query surface, route wiring, and first working tab UI. + + + +@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/10-events/10-CONTEXT.md +@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/10-events/10-RESEARCH.md +@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/10-events/10-UI-SPEC.md +@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/10-events/10-VALIDATION.md +@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/10-events/10-PATTERNS.md + + + +T-10-01 Cross-tablo event access: every list/create path must load the parent through `loadOwnedTablo`; child event queries must include `tablo_id`. +T-10-02 CSRF: create form must include `@ui.CSRFField(csrfToken)` and use the existing POST route behind CSRF middleware. +T-10-03 XSS: event title, description, and location must render through templ escaped expressions. +T-10-04 Time validation bypass: create handler and DB check must reject `end_time <= start_time`. +T-10-05 Route ambiguity: events static routes must be mounted before event parametric routes. + + + + + + Task 1: Add RED coverage for create-and-list calendar slice + + - backend/internal/web/handlers_events_test.go + + + - backend/internal/web/handlers_tasks_test.go + - backend/internal/web/handlers_etapes_test.go + - backend/internal/web/router.go + - .planning/phases/10-events/10-VALIDATION.md + - .planning/phases/10-events/10-UI-SPEC.md + + + Add DB-backed handler tests in `handlers_events_test.go` using `setupTestDB`, `preInsertUser`, authenticated cookies, and `getCSRFToken`. + Add `TestEventsTabRendersMonthGrid` for `GET /tablos/{id}/events?month=2026-05` expecting `Events`, `May 2026`, weekday labels, and `New event`. + Add `TestEventCreateRendersTitleInCalendar` that POSTs `/tablos/{id}/events` with `title=Kickoff`, `event_date=2026-05-20`, `start_time=09:30`, empty `end_time`, `location=Office`, `description=Planning`, then expects the refreshed fragment to contain `Kickoff`. + Add `TestEventCreateRejectsEndTimeBeforeOrEqualStart` with `start_time=10:00` and `end_time=10:00`, expecting HTTP 422 and copy `End time must be after the start time.`. + Add `TestEventsTabOwnershipReturns404` proving another signed-in user cannot GET the first user's `/tablos/{id}/events`. + + + cd backend && go test ./internal/web -run 'TestEventsTabRendersMonthGrid|TestEventCreateRendersTitleInCalendar|TestEventCreateRejectsEndTimeBeforeOrEqualStart|TestEventsTabOwnershipReturns404' -count=1 + + + - `backend/internal/web/handlers_events_test.go` contains all four named tests. + - Tests fail before implementation because the events route/schema/templates do not exist. + - The validation test asserts exact copy `End time must be after the start time.`. + + + + + Task 2: Implement event creation and month listing + + - backend/migrations/0008_events.sql + - backend/internal/db/queries/events.sql + - backend/internal/web/handlers_events.go + - backend/internal/web/router.go + - backend/cmd/web/main.go + - backend/templates/events.templ + - backend/templates/events_forms.go + - backend/templates/tablos.templ + + + - backend/migrations/0003_tablos.sql + - backend/migrations/0007_etapes.sql + - backend/internal/db/queries/tablos.sql + - backend/internal/web/handlers_files.go + - backend/internal/web/handlers_etapes.go + - backend/internal/web/router.go + - backend/templates/files.templ + - backend/templates/etapes.templ + - backend/templates/tablos.templ + - .planning/phases/10-events/10-RESEARCH.md + - .planning/phases/10-events/10-UI-SPEC.md + - .planning/phases/10-events/10-PATTERNS.md + + + Add `0008_events.sql` with `events(id uuid primary key default gen_random_uuid(), tablo_id uuid not null references tablos(id) on delete cascade, title text not null, event_date date not null, start_time time not null, end_time time, description text, location text, created_at timestamptz not null default now(), updated_at timestamptz not null default now())`, `events_title_not_blank`, `events_end_after_start`, `events_tablo_month_idx`, and `events_date_idx`. + Add `events.sql` queries: `CreateEvent`, `GetEventByID`, and `ListEventsByTabloRange`, all parent-scoped by `tablo_id` where applicable and ordered by `event_date`, `start_time`, `title`. + Add `EventsDeps{Queries *sqlc.Queries}` and wire it through `NewRouter` plus `backend/cmd/web/main.go` and test helpers. Mount `GET /tablos/{id}/events`, `GET /tablos/{id}/events/new`, and `POST /tablos/{id}/events` under authenticated routes with static event routes before future parametric routes. + Add `handlers_events.go` with month parsing for `YYYY-MM`, local current-month default, date/time parsing helpers for `YYYY-MM-DD` and `HH:MM`, create validation, and a `renderEventsTab` helper that returns `EventsTabFragment`. + Extend `TabloDetailPage` to include the `Events` nav link and render `EventsTabFragment` when `activeTab == "events"`. + Add `events.templ` and `events_forms.go` with `#events-tab`, `#event-form-slot`, month grid, weekday labels, `New event`, `Create event`, `Close form`, and escaped event title rows. Day cells show event titles only; no special empty-month copy. + Run `cd backend && just generate` after migration/query/template changes. + + + cd backend && just generate && go test ./internal/web -run 'TestEventsTabRendersMonthGrid|TestEventCreateRendersTitleInCalendar|TestEventCreateRejectsEndTimeBeforeOrEqualStart|TestEventsTabOwnershipReturns404' -count=1 + + + - `backend/migrations/0008_events.sql` contains `CREATE TABLE events`, `event_date DATE NOT NULL`, `start_time TIME NOT NULL`, `end_time TIME`, and `events_end_after_start`. + - `backend/internal/db/queries/events.sql` contains `CreateEvent`, `GetEventByID`, and `ListEventsByTabloRange`. + - `backend/internal/web/router.go` mounts `GET /tablos/{id}/events` and `POST /tablos/{id}/events`. + - `backend/templates/tablos.templ` contains tab label `Events` and active branch for `activeTab == "events"`. + - Targeted tests listed in verify pass. + + + + + + +Run: +- `cd backend && just generate` +- `cd backend && go test ./internal/web -run 'TestEventsTabRendersMonthGrid|TestEventCreateRendersTitleInCalendar|TestEventCreateRejectsEndTimeBeforeOrEqualStart|TestEventsTabOwnershipReturns404' -count=1` +- `git diff --check` + + + +- EVENT-01 create path is delivered for all required fields. +- EVENT-03 Events tab displays created event titles in a month grid. +- EVENT-04 create validation and DB check reject invalid end times. +- EVENT-05 create/list access is scoped to the owned tablo. + + diff --git a/.planning/phases/10-events/10-02-PLAN.md b/.planning/phases/10-events/10-02-PLAN.md new file mode 100644 index 0000000..789a5db --- /dev/null +++ b/.planning/phases/10-events/10-02-PLAN.md @@ -0,0 +1,138 @@ +--- +phase: 10-events +plan: 02 +type: execute +wave: 2 +depends_on: + - 10-01-PLAN.md +files_modified: + - backend/internal/db/queries/events.sql + - backend/internal/web/handlers_events_test.go + - backend/internal/web/handlers_events.go + - backend/internal/web/router.go + - backend/templates/events.templ + - backend/templates/events_forms.go +autonomous: true +requirements: [EVENT-01, EVENT-02, EVENT-04, EVENT-05] + +must_haves: + truths: + - "EVENT-02: User can edit and delete tablo events" + - "EVENT-04: Update path also rejects end_time before or equal to start_time" + - "EVENT-05: Event edit/update/delete authorization follows parent tablo ownership and returns 404 for inaccessible events" + - "D-12/D-14/D-15: Clicking an event title opens inline edit; create/edit/delete refresh the whole Events tab" + - "D-13: Edit form exposes required title/date/start time and optional end time/description/location" + - "D-16: Delete is hard delete after confirmation; no deleted_at or restore behavior" + - "UI-SPEC: Edit submit copy is Save event changes; delete copy uses Delete event?, Delete event, Keep event" + artifacts: + - path: "backend/internal/web/handlers_events.go" + provides: "loadOwnedEvent plus edit, update, delete-confirm, and hard delete handlers" + - path: "backend/templates/events.templ" + provides: "event title edit triggers, edit form, and delete confirmation fragments" + - path: "backend/internal/web/handlers_events_test.go" + provides: "edit/delete/ownership and update validation coverage" +--- + + +Vertical slice 2: a user can click an event title in the Events tab, edit all event fields inline, and hard-delete the event after confirmation without leaking or mutating inaccessible events. + + + +@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/10-events/10-CONTEXT.md +@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/10-events/10-RESEARCH.md +@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/10-events/10-UI-SPEC.md +@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/10-events/10-PATTERNS.md +@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/10-events/10-01-PLAN.md + + + +T-10-01 Cross-tablo event access: edit/update/delete must bind `event_id` to `tablo_id` through `GetEventByID`. +T-10-02 CSRF: update and delete forms must include `@ui.CSRFField(csrfToken)`. +T-10-03 XSS: edited title, description, and location remain escaped on re-render. +T-10-04 Time validation bypass: update handler must reject invalid end time and DB check remains in place. +T-10-06 Data retention mismatch: delete must be hard delete only; do not add `deleted_at`. + + + + + + Task 1: Add RED coverage for edit, update, delete, and ownership + + - backend/internal/web/handlers_events_test.go + + + - backend/internal/web/handlers_events_test.go + - backend/internal/web/handlers_etapes_test.go + - backend/templates/events.templ + - .planning/phases/10-events/10-UI-SPEC.md + + + Add failing tests: + 1. `TestEventEditRendersInlineForm` creates an event, GETs `/tablos/{id}/events/{event_id}/edit`, and expects `Save event changes`, current title, date, start time, location, and description. + 2. `TestEventUpdateChangesCalendarPlacement` posts `/tablos/{id}/events/{event_id}` with changed title/date/time and verifies the refreshed calendar contains the new title and not the old title. + 3. `TestEventUpdateRejectsInvalidEndTime` posts equal start/end times and expects 422 plus `End time must be after the start time.`. + 4. `TestEventDeleteRemovesFromCalendarAndDatabase` confirms delete, verifies refreshed calendar omits the title, and verifies `GetEventByID` returns no row. + 5. `TestEventMutationOwnershipReturns404` verifies non-owner edit/update/delete-confirm/delete attempts return 404. + + + cd backend && go test ./internal/web -run 'TestEventEditRendersInlineForm|TestEventUpdateChangesCalendarPlacement|TestEventUpdateRejectsInvalidEndTime|TestEventDeleteRemovesFromCalendarAndDatabase|TestEventMutationOwnershipReturns404' -count=1 + + + - `handlers_events_test.go` contains all five named tests. + - Tests fail before implementation because edit/update/delete routes or behavior are missing. + + + + + Task 2: Implement inline edit and hard delete + + - backend/internal/db/queries/events.sql + - backend/internal/web/handlers_events.go + - backend/internal/web/router.go + - backend/templates/events.templ + - backend/templates/events_forms.go + + + - backend/internal/db/queries/events.sql + - backend/internal/web/handlers_events.go + - backend/internal/web/handlers_etapes.go + - backend/internal/web/router.go + - backend/templates/events.templ + - backend/templates/etapes.templ + - .planning/phases/10-events/10-UI-SPEC.md + - .planning/phases/10-events/10-PATTERNS.md + + + Add `UpdateEvent` and `DeleteEvent` SQLC queries, each scoped by `id` and `tablo_id`, with `UpdateEvent` setting `updated_at = now()`. + Add `loadOwnedEvent` in `handlers_events.go` using `loadOwnedTablo`, `chi.URLParam(r, "event_id")`, `uuid.Parse`, `GetEventByID`, 404 on missing/foreign rows, and 500 on unexpected errors. + Mount `GET /tablos/{id}/events/{event_id}/edit`, `POST /tablos/{id}/events/{event_id}`, `GET /tablos/{id}/events/{event_id}/delete-confirm`, and `POST /tablos/{id}/events/{event_id}/delete` after static events routes. + Render event titles as HTMX controls targeting `#event-form-slot` with accessible label `Edit event: {title}`. Add edit form and delete confirmation fragments using UI-SPEC copy: `Save event changes`, `Delete event?`, `This removes the event from this tablo. This cannot be undone.`, `Delete event`, and `Keep event`. + Reuse create validation helpers for update. On update validation errors, return 422 and re-render the edit form with submitted values. After successful update/delete, refresh `#events-tab` with the currently selected `month` context. + Run `cd backend && just generate`. + + + cd backend && just generate && go test ./internal/web -run 'TestEventEditRendersInlineForm|TestEventUpdateChangesCalendarPlacement|TestEventUpdateRejectsInvalidEndTime|TestEventDeleteRemovesFromCalendarAndDatabase|TestEventMutationOwnershipReturns404' -count=1 + + + - `backend/internal/db/queries/events.sql` contains `UpdateEvent` and `DeleteEvent`. + - `backend/internal/web/handlers_events.go` contains `loadOwnedEvent`. + - `backend/templates/events.templ` contains `Save event changes`, `Delete event?`, `Delete event`, and `Keep event`. + - Delete test proves the row is removed from the database rather than marked with `deleted_at`. + - Targeted tests listed in verify pass. + + + + + + +Run: +- `cd backend && just generate` +- `cd backend && go test ./internal/web -run 'TestEventEditRendersInlineForm|TestEventUpdateChangesCalendarPlacement|TestEventUpdateRejectsInvalidEndTime|TestEventDeleteRemovesFromCalendarAndDatabase|TestEventMutationOwnershipReturns404' -count=1` +- `git diff --check` + + + +- EVENT-02 is delivered for edit and hard delete. +- EVENT-04 is enforced on update. +- EVENT-05 is enforced on all event mutation routes. + diff --git a/.planning/phases/10-events/10-03-PLAN.md b/.planning/phases/10-events/10-03-PLAN.md new file mode 100644 index 0000000..baeaa5a --- /dev/null +++ b/.planning/phases/10-events/10-03-PLAN.md @@ -0,0 +1,134 @@ +--- +phase: 10-events +plan: 03 +type: execute +wave: 3 +depends_on: + - 10-02-PLAN.md +files_modified: + - backend/internal/db/queries/events.sql + - backend/internal/web/handlers_events_test.go + - backend/internal/web/handlers_events.go + - backend/templates/events.templ + - backend/templates/events_forms.go +autonomous: true +requirements: [EVENT-01, EVENT-03, EVENT-05] + +must_haves: + truths: + - "D-08/D-21: Events tab supports previous/next month navigation with /tablos/{id}/events?month=YYYY-MM" + - "D-09: New event can be opened generally or from a day cell with the date prefilled" + - "D-10/D-11: Day cells show event titles only, first few titles, and +N more" + - "D-20/D-22/D-23: Phase 10 includes user-scoped date-range query surface returning event fields plus tablo title/color" + - "EVENT-05: User-scoped aggregation must filter through tablos.user_id and not leak other users' events" + - "Research pitfall: submitted form fields must win over date/month query defaults" + artifacts: + - path: "backend/internal/db/queries/events.sql" + provides: "ListUserEventsRange for Phase 11 planning handoff" + - path: "backend/internal/web/handlers_events.go" + provides: "month navigation, day prefill, and form/query precedence helpers" + - path: "backend/templates/events.templ" + provides: "previous/next controls, day create controls, and +N more rendering" +--- + + +Vertical slice 3: the Events tab supports month navigation and day-click date prefill, while the database query layer exposes the user-scoped date-range aggregation that Phase 11 needs for individual planning. + + + +@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/10-events/10-CONTEXT.md +@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/10-events/10-RESEARCH.md +@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/10-events/10-UI-SPEC.md +@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/10-events/10-PATTERNS.md +@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/10-events/10-02-PLAN.md + + + +T-10-01 Cross-tablo event access: day prefill and month links must never bypass `loadOwnedTablo`. +T-10-07 Cross-user aggregation leak: `ListUserEventsRange` must join `tablos` and filter by `tablos.user_id = $1`. +T-10-08 Query-default confusion: date query params are defaults only; POST body fields must be authoritative. +T-10-03 XSS: `+N more`, titles, and month/day labels must render as escaped text. + + + + + + Task 1: Add RED coverage for navigation, prefill, overflow, and aggregation + + - backend/internal/web/handlers_events_test.go + + + - backend/internal/web/handlers_events_test.go + - backend/internal/web/handlers_tasks_test.go + - backend/templates/events.templ + - .planning/phases/10-events/10-RESEARCH.md + - .planning/phases/10-events/10-UI-SPEC.md + + + Add failing tests: + 1. `TestEventsMonthNavigationPushesMonthParam` GETs `/tablos/{id}/events?month=2026-05` and expects links for `month=2026-04` and `month=2026-06` plus accessible labels naming those months. + 2. `TestEventNewFromDayPrefillsDate` GETs `/tablos/{id}/events/new?date=2026-05-20&month=2026-05` and expects the date input value `2026-05-20`. + 3. `TestEventCreateSubmittedDateOverridesQueryDefault` POSTs `/tablos/{id}/events?date=2026-05-20&month=2026-05` with form `event_date=2026-05-21` and verifies the event is stored on `2026-05-21`. + 4. `TestEventsCalendarShowsMoreIndicator` creates four events on one date and expects three titles plus `+1 more`. + 5. `TestListUserEventsRangeReturnsOnlyOwnedTablos` creates events for two users in the same date range and verifies the user-scoped query returns only the authenticated user's events with `tablo_title` and `tablo_color`. + + + cd backend && go test ./internal/web -run 'TestEventsMonthNavigationPushesMonthParam|TestEventNewFromDayPrefillsDate|TestEventCreateSubmittedDateOverridesQueryDefault|TestEventsCalendarShowsMoreIndicator|TestListUserEventsRangeReturnsOnlyOwnedTablos' -count=1 + + + - `handlers_events_test.go` contains all five named tests. + - The submitted-date test fails before implementation if query defaults override form data. + - The user-scoped query test proves foreign-user events are not returned. + + + + + Task 2: Implement navigation, day prefill, overflow display, and planning query + + - backend/internal/db/queries/events.sql + - backend/internal/web/handlers_events.go + - backend/templates/events.templ + - backend/templates/events_forms.go + + + - backend/internal/db/queries/events.sql + - backend/internal/web/handlers_events.go + - backend/templates/events.templ + - backend/templates/events_forms.go + - .planning/phases/10-events/10-CONTEXT.md + - .planning/phases/10-events/10-UI-SPEC.md + + + Add `ListUserEventsRange` to `events.sql`, joining `events` to `tablos`, filtering by `tablos.user_id`, `events.event_date >= $2`, and `events.event_date <= $3`, ordering by `event_date`, `start_time`, `title`, and returning event fields plus `tablo_title` and `tablo_color`. + Extend month model generation to calculate previous and next `YYYY-MM` links, in-month day metadata, and per-day event truncation at three titles with `+N more`. + Add day-cell create controls targeting `/tablos/{id}/events/new?date=YYYY-MM-DD&month=YYYY-MM` with `aria-label="Create event on YYYY-MM-DD"`. + Implement `EventNewFormHandler` so query `date` pre-populates the create form date and query `month` is carried as calendar context. Ensure POST create/update reads only submitted form fields for `event_date`, `start_time`, and `end_time`; month query controls the refresh view only. + Run `cd backend && just generate`. + + + cd backend && just generate && go test ./internal/web -run 'TestEventsMonthNavigationPushesMonthParam|TestEventNewFromDayPrefillsDate|TestEventCreateSubmittedDateOverridesQueryDefault|TestEventsCalendarShowsMoreIndicator|TestListUserEventsRangeReturnsOnlyOwnedTablos' -count=1 + + + - `backend/internal/db/queries/events.sql` contains `ListUserEventsRange` and `tablos.user_id`. + - `backend/templates/events.templ` contains `+` more rendering logic and day create labels. + - `EventNewFormHandler` pre-fills date from query only on GET. + - POST create/update handlers use submitted `event_date`, not query `date`, for persistence. + - Targeted tests listed in verify pass. + + + + + + +Run: +- `cd backend && just generate` +- `cd backend && go test ./internal/web -run 'TestEventsMonthNavigationPushesMonthParam|TestEventNewFromDayPrefillsDate|TestEventCreateSubmittedDateOverridesQueryDefault|TestEventsCalendarShowsMoreIndicator|TestListUserEventsRangeReturnsOnlyOwnedTablos' -count=1` +- `git diff --check` + + + +- Month navigation and day-click create prefill are delivered. +- Calendar cells show titles only and handle overflow with `+N more`. +- Phase 11 can query the authenticated user's events across owned tablos by date range with tablo title/color. + + diff --git a/.planning/phases/10-events/10-04-PLAN.md b/.planning/phases/10-events/10-04-PLAN.md new file mode 100644 index 0000000..351ad3e --- /dev/null +++ b/.planning/phases/10-events/10-04-PLAN.md @@ -0,0 +1,185 @@ +--- +phase: 10-events +plan: 04 +type: execute +wave: 4 +depends_on: + - 10-03-PLAN.md +files_modified: + - backend/internal/web/handlers_events_test.go + - backend/internal/web/handlers_tablos_test.go + - backend/internal/web/handlers_tasks_test.go + - backend/templates/events.templ + - backend/templates/tablos.templ +autonomous: false +requirements: [EVENT-01, EVENT-02, EVENT-03, EVENT-04, EVENT-05] + +must_haves: + truths: + - "All EVENT-01..05 requirements are covered by automated tests or the final browser checkpoint" + - "D-01 through D-23 are represented by source behavior, tests, or the final browser checkpoint" + - "UI-SPEC: Events tab, toolbar, month grid, inline forms, copy, colors, typography, spacing, and accessibility labels are visible" + - "Validation Architecture: final full suite and browser UAT run before verify-work" + artifacts: + - path: "backend/internal/web/handlers_events_test.go" + provides: "final regression coverage for events" + - path: "backend/templates/events.templ" + provides: "final UI contract adjustments" + - path: "backend/templates/tablos.templ" + provides: "final tab integration adjustments" +--- + + +Vertical slice 4: harden the completed Events feature with regression tests, full generated-code verification, and browser UAT against all event requirements, context decisions, UI contract, and security expectations. + + + +@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/10-events/10-CONTEXT.md +@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/10-events/10-RESEARCH.md +@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/10-events/10-UI-SPEC.md +@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/10-events/10-VALIDATION.md +@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.planning/phases/10-events/10-03-PLAN.md + + + +T-10-01 Cross-tablo event access remains covered by owner-only handler tests. +T-10-02 CSRF remains covered by mutation tests using real CSRF tokens. +T-10-03 XSS remains covered by templ escaped rendering and should be spot-checked with HTML-looking event content. +T-10-04 Time validation remains covered on create and update plus DB check. +T-10-07 Cross-user aggregation leak remains covered by `ListUserEventsRange` tests. +T-10-09 Regression risk: NewRouter signature changes can silently break existing tablo/task/file/auth tests if not run together. + + + +SOURCE | ID | Feature/Requirement | Plan | Status | Notes +GOAL | - | Tablo-owned scheduled events with task/file-level authorization | 10-01,10-02,10-03,10-04 | COVERED | Schema, handlers, ownership tests, final UAT +REQ | EVENT-01 | Create event with title, start time, optional end time, description, location | 10-01,10-03,10-04 | COVERED | Create form, validation, submitted-value precedence +REQ | EVENT-02 | Edit and delete events | 10-02,10-04 | COVERED | Inline edit and hard delete +REQ | EVENT-03 | Tablo detail events view listing scheduled events | 10-01,10-03,10-04 | COVERED | Events tab month grid +REQ | EVENT-04 | End time empty or after start time | 10-01,10-02,10-04 | COVERED | Handler validation and DB check +REQ | EVENT-05 | Authorization follows tablo access rules | 10-01,10-02,10-03,10-04 | COVERED | Parent ownership and aggregation filtering +CONTEXT | D-01..D-05 | Local date/time storage and display assumptions | 10-01,10-02,10-04 | COVERED | date/time columns and helpers +CONTEXT | D-06..D-11 | Events tab month grid, navigation, day create, title-only cells, +N more | 10-01,10-03,10-04 | COVERED | Calendar UI tasks +CONTEXT | D-12..D-15 | Inline create/edit flow and full tab refresh | 10-01,10-02,10-04 | COVERED | `#event-form-slot`, `#events-tab` +CONTEXT | D-16..D-19 | Hard delete, past visibility, ordering, no special empty state | 10-01,10-02,10-04 | COVERED | Query/order/delete tasks +CONTEXT | D-20..D-23 | Phase 11 user-scoped date-range query with tablo metadata | 10-03,10-04 | COVERED | `ListUserEventsRange` +RESEARCH | Data model | `events` table with local date/time columns and indexes | 10-01 | COVERED | Migration task +RESEARCH | Security | Parent-scoped queries, CSRF, escaping, DB check | 10-01,10-02,10-03,10-04 | COVERED | Threat models and tests +RESEARCH | Testing | DB-backed create/list/update/delete/ownership/aggregation tests | 10-01,10-02,10-03,10-04 | COVERED | Per-plan RED tests +UI-SPEC | Contract | Events tab, calendar, forms, copy, a11y, registry safety | 10-01,10-02,10-03,10-04 | COVERED | Implemented and UAT checked + + + + + + Task 1: Add final regression coverage for tab integration and escaping + + - backend/internal/web/handlers_events_test.go + - backend/internal/web/handlers_tablos_test.go + + + - backend/internal/web/handlers_events_test.go + - backend/internal/web/handlers_tablos_test.go + - backend/templates/events.templ + - backend/templates/tablos.templ + - .planning/phases/10-events/10-UI-SPEC.md + - .planning/phases/10-events/10-VALIDATION.md + + + Add final regression tests: + 1. `TestTabloDetailEventsTabFullPageFallback` verifies non-HTMX `/tablos/{id}/events?month=2026-05` renders the full detail shell with Events active and includes Overview, Tasks, Files, and Events tabs. + 2. `TestEventContentIsEscaped` creates an event with title `` and verifies rendered HTML does not contain a raw script tag while still displaying escaped title text. + 3. `TestEventWithoutEndTimeDisplaysOnlyStartTime` verifies an event with blank end time displays `09:30` and does not render an inferred range. + 4. `TestEventsOrderedByStartTimeThenTitle` creates same-day events and verifies calendar title order follows start time ascending, then title. + + + cd backend && go test ./internal/web -run 'TestTabloDetailEventsTabFullPageFallback|TestEventContentIsEscaped|TestEventWithoutEndTimeDisplaysOnlyStartTime|TestEventsOrderedByStartTimeThenTitle' -count=1 + + + - `handlers_events_test.go` contains escaping, no-end-time display, and ordering tests. + - `handlers_tablos_test.go` or `handlers_events_test.go` contains the full-page fallback test. + - Targeted tests listed in verify pass. + + + + + Task 2: Run full backend verification and fix regressions + + - backend/internal/web/handlers_events.go + - backend/internal/web/router.go + - backend/templates/events.templ + - backend/templates/tablos.templ + - backend/internal/web/handlers_events_test.go + - backend/internal/web/handlers_tasks_test.go + - backend/internal/web/handlers_tablos_test.go + + + - backend/internal/web/router.go + - backend/internal/web/handlers_events.go + - backend/templates/events.templ + - backend/templates/tablos.templ + - .planning/phases/10-events/10-RESEARCH.md + - .planning/phases/10-events/10-UI-SPEC.md + - .planning/phases/10-events/10-VALIDATION.md + + + Run generated code and full backend tests. Fix any regressions caused by the new `EventsDeps` router signature, generated SQLC types, templ signatures, route ordering, or tab rendering. + Keep fixes scoped to Phase 10 files and affected test helpers. Do not alter Phase 8 social sign-in behavior, Phase 9 etape behavior, or existing task/file route semantics except where router call sites require the new dependency parameter. + + + cd backend && just generate && go test ./... -count=1 + + + - `cd backend && just generate` exits 0. + - `cd backend && go test ./... -count=1` exits 0. + - `git diff --check` exits 0. + - Existing task, etape, file, auth, and tablo tests still pass with the new router signature. + + + + + Task 3: Browser UAT checkpoint for Events tab + + + - .planning/phases/10-events/10-UI-SPEC.md + - .planning/phases/10-events/10-VALIDATION.md + - backend/templates/events.templ + + + Start the app with `just dev` from the repository root or `cd backend && just dev`, sign in, open an existing tablo, and verify the Events tab manually: + 1. Open Events and confirm the month grid, `Previous month`, `Next month`, and `New event` controls render without overlap. + 2. Create an event from `New event` with title, date, start time, optional end time, description, and location. + 3. Create another event from a day cell and confirm the date is prefilled. + 4. Edit an event title/date/time and confirm the calendar refreshes to the correct month. + 5. Try equal start/end times and confirm the visible error copy is `End time must be after the start time.`. + 6. Delete an event and confirm it disappears after `Delete event`. + 7. Navigate to a past month with events and confirm past events remain visible. + + + cd backend && go test ./... -count=1 + Browser confirms all EVENT-01..05 success criteria and the 10-UI-SPEC interaction contract. + + + - Full backend tests pass before the browser checkpoint. + - Browser verification covers create, edit, delete, invalid end time, month navigation, day-cell prefill, and past-month visibility. + - User approves the checkpoint before Phase 10 is marked complete. + + + + + + +Run: +- `cd backend && just generate` +- `cd backend && go test ./... -count=1` +- `git diff --check` +- Browser UAT from Task 3 + + + +- All EVENT-01..05 requirements are complete. +- All D-01..D-23 context decisions are implemented or verified. +- UI-SPEC is satisfied by the first functional Events tab UI. +- Security expectations are covered by parent-scoped queries, CSRF forms, escaped rendering, and owner-only tests. + +