---
phase: 10
slug: events
status: complete
created: 2026-05-16
---

# Phase 10 - Pattern Map

## Purpose

Map the Events implementation to existing codebase patterns so execution can add the feature without inventing a parallel architecture.

## File Map

| New / Modified File | Role | Closest Existing Analog | Pattern to Reuse |
|---------------------|------|-------------------------|------------------|
| `backend/migrations/0008_events.sql` | Tablo-owned child table | `backend/migrations/0007_etapes.sql`, `backend/migrations/0005_files.sql` | `tablo_id` FK with `ON DELETE CASCADE`, indexes by parent and display ordering |
| `backend/internal/db/queries/events.sql` | SQLC query surface | `backend/internal/db/queries/etapes.sql`, `backend/internal/db/queries/files.sql` | Parent-scoped CRUD with `id` + `tablo_id` guards; ordered list queries |
| `backend/internal/web/handlers_events.go` | Event tab and CRUD handlers | `backend/internal/web/handlers_etapes.go`, `backend/internal/web/handlers_files.go` | `loadOwnedTablo`, parent-scoped child loader, HTMX fragments, 422 validation |
| `backend/internal/web/router.go` | Protected route mounting | Existing `/tasks`, `/etapes`, `/files` blocks | Static child routes before parametric child routes |
| `backend/templates/events.templ` | Calendar tab and forms | `backend/templates/files.templ`, `backend/templates/etapes.templ`, `backend/templates/tasks.templ` | Tab fragment wrapper, `#event-form-slot`, inline forms, delete confirmation |
| `backend/templates/events_forms.go` | Form structs/helpers | `backend/templates/tasks_forms.go`, `backend/templates/etapes_forms.go`, `backend/templates/files_helpers.go` | Small value structs, explicit error structs, helper formatting |
| `backend/templates/tablos.templ` | Tab integration | Existing Overview/Tasks/Files links | Add Events nav link and active-tab branch |
| `backend/internal/web/handlers_events_test.go` | DB-backed web tests | `backend/internal/web/handlers_tasks_test.go`, `backend/internal/web/handlers_etapes_test.go` | `setupTestDB`, `preInsertUser`, session cookie, CSRF token, response body assertions |
| `backend/internal/db/sqlc/*` | Generated query/models | Existing generated files | Regenerate with `cd backend && just generate`; do not edit by hand |
| Router call sites/tests | Dependency wiring | Existing `NewRouter(... EtapesDeps, FilesDeps, ...)` updates from Phase 9 | Add `EventsDeps` to `NewRouter` signature and every test helper/cmd call |

## Handler Patterns

Use `loadOwnedTablo` for every `/tablos/{id}/events...` route. Add `loadOwnedEvent(w, r, deps)` that:

1. Calls `loadOwnedTablo`.
2. Parses `{event_id}` as UUID.
3. Calls `GetEventByID(id, tablo_id)`.
4. Returns 404 on parse failure or `pgx.ErrNoRows`.
5. Logs and returns 500 on unexpected query errors.

This mirrors `loadOwnedEtape` and `loadOwnedTabloForFile`.

Use tab handlers like `TabloFilesTabHandler` and `TabloTasksTabHandler`:

- HTMX request returns only `EventsTabFragment`.
- Non-HTMX request renders full `TabloDetailPage(..., activeTab="events")`.
- Mutations return a refreshed `EventsTabFragment` and target `#events-tab` or `#tab-content`.

## Template Patterns

Events should use local templ output and existing Tailwind utility style:

- Outer tab wrapper: `id="events-tab"`.
- Inline form slot: `id="event-form-slot"`.
- Form panel classes should resemble etape/file forms: `rounded border border-slate-200 bg-white p-3 shadow-sm space-y-3` or `bg-slate-50 p-4`.
- Inputs reuse existing classes: `block w-full rounded border border-slate-300 px-2 py-1 text-sm ...`.
- Buttons should use `@ui.Button` or existing `ui-button` classes; no new button system.

## Date and Time Patterns

Add focused helpers in the web/template layer:

- Parse month query: `YYYY-MM`, default current local month.
- Parse date field: `YYYY-MM-DD`.
- Parse time field: `HH:MM`.
- Format DB values back to input-compatible strings.

Keep query-string defaults separate from submitted form values. Query params may prefill `GET /events/new`; `POST /events` must use submitted form fields.

## Test Patterns

Follow existing DB-backed tests:

- Use `setupTestDB`.
- Insert users/tablos directly through helpers or SQLC queries.
- Use authenticated session cookies and `getCSRFToken`.
- Assert 404 for non-owner access rather than 403.
- For validation, assert 422 and visible error copy.
- For HTMX, set `HX-Request: true` and assert fragment-specific body/targets.

## Security Patterns

- Parent ownership is always through `loadOwnedTablo`.
- Event child queries always include `tablo_id`.
- CSRF is included in create/update/delete forms.
- User content renders through templ escaped expressions.
- Date/time validation happens before query execution and is reinforced by the database check constraint.

