From 3a5a26c5c832e384a17910676b7a0bb636072d75 Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Sat, 16 May 2026 23:43:44 +0200 Subject: [PATCH] docs(16-03): complete kanban restyle + etape grouping plan summary Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .../phases/16-tablo-detail/16-03-SUMMARY.md | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 .planning/phases/16-tablo-detail/16-03-SUMMARY.md diff --git a/.planning/phases/16-tablo-detail/16-03-SUMMARY.md b/.planning/phases/16-tablo-detail/16-03-SUMMARY.md new file mode 100644 index 0000000..9547924 --- /dev/null +++ b/.planning/phases/16-tablo-detail/16-03-SUMMARY.md @@ -0,0 +1,118 @@ +--- +phase: 16-tablo-detail +plan: "03" +subsystem: templates +tags: [htmx, templ, kanban, etape-grouping, design-tokens] +dependency_graph: + requires: [16-01, 16-02] + provides: [kanban-etape-grouping, tasks-section-layout, task-row-layout, etape-strip-removed] + affects: [16-04-files-templ] +tech_stack: + added: [] + patterns: [groupTasksByEtape helper, EtapeGroup type, EtapeGroupHeader component, tasks-section kanban layout, task-row card pattern] +key_files: + created: [] + modified: + - backend/templates/tasks.templ + - backend/templates/tablos.templ + - backend/internal/web/handlers_tasks.go +decisions: + - "EtapeGroup.EtapeColor always set to empty string — sqlc.Etape struct has no Color field in this schema (no etape color in DB migration)" + - "uuid.UUID(t.EtapeID.Bytes).String() used for pgtype.UUID → string conversion — matches existing taskEtapeIDString helper in tasks_forms.go" + - "tablos.templ KanbanBoard call updated in Task 2 (not Task 1) — build would fail if tasks.templ committed alone; treated as atomic with handlers_tasks.go updates" +metrics: + duration: ~20min + completed: 2026-05-16T22:30:00Z + tasks_completed: 2 + files_modified: 3 +--- + +# Phase 16 Plan 03: Kanban Restyle + Etape Grouping Summary + +**One-liner:** Added `groupTasksByEtape` helper and `EtapeGroupHeader` component to `tasks.templ`; restyled `KanbanColumn` to `tasks-section` layout, `TaskCard` to `task-row` layout, `AddTaskTrigger` to `tasks-add-button`; removed `@EtapeStrip` OOB calls from `TaskCardGone`/`TaskCardOOB`; updated all three `KanbanBoard` call sites to accept `etapes []sqlc.Etape` as 5th parameter. + +## Tasks Completed + +| Task | Name | Commit | Files | +|------|------|--------|-------| +| 1 | Add groupTasksByEtape helper, EtapeGroup type, EtapeGroupHeader component; update KanbanBoard/Column signatures and restyled column/card/trigger | 084fc0e | `backend/templates/tasks.templ` | +| 2 | Update all three KanbanBoard call sites (tablos.templ + handlers_tasks.go) and verify full test suite | e2ee434 | `backend/internal/web/handlers_tasks.go`, `backend/templates/tablos.templ` | + +## What Was Built + +**Task 1 — tasks.templ:** + +- **EtapeGroup type:** Struct with `EtapeID string`, `EtapeTitle string`, `EtapeColor string`, `Tasks []sqlc.Task`. EtapeColor always empty (schema has no color field). + +- **groupTasksByEtape helper:** Iterates tasks, uses `!t.EtapeID.Valid` to detect unassigned (pgtype.UUID nil check), converts valid UUIDs via `uuid.UUID(t.EtapeID.Bytes).String()` (same pattern as `taskEtapeIDString` in `tasks_forms.go`). Iterates etapes in declaration order, only adds groups with tasks. Appends unassigned group last only if any unassigned tasks exist. + +- **EtapeGroupHeader component:** Renders `div class="etape-group-header"`. If `EtapeColor != ""` renders color dot span with inline style. Renders `span class="etape-group-label is-unassigned"` for unassigned group (`EtapeID == ""`), plain `etape-group-label` otherwise. + +- **KanbanBoard signature:** Added `etapes []sqlc.Etape` as 5th parameter. Passes `etapes` through to each `@KanbanColumn(...)` call. + +- **KanbanColumn restyled:** Outer `div class="kanban-column"` wraps `div class="tasks-section"`. Header: `div class="tasks-section-header"` with left side (h3 + badge) and right side (`div id="add-task-slot-{status}"` containing `@AddTaskTrigger`). Body: `div class="task-list sortable-column"` — empty state uses `p class="task-list-empty"`, else computes `groups := groupTasksByEtape(tasks, etapes)` and renders `@EtapeGroupHeader(group)` + task loop. + +- **TaskCard restyled:** Inner div changed from `task-card bg-white rounded border...` to `task-row task-card`. Drag handle removed. HTMX edit attributes moved to the outer task-row div (making the whole row clickable). Children: `div class="task-check"` (round checkbox), `div class="task-body"` (task title p), `@ui.IconButton` with trash/danger/ghost for delete confirm. + +- **AddTaskTrigger restyled:** Button class changed from `ui-button ui-button-soft ui-button-neutral ui-button-md w-full text-left text-sm mt-2` to `tasks-add-button`. All HTMX attributes preserved unchanged. + +- **EtapeStrip OOB removal:** Deleted `@EtapeStrip(tabloID, etapes, counts, filter, csrfToken, true)` from both `TaskCardGone` and `TaskCardOOB`. Kept `etapes []sqlc.Etape` and `counts EtapeTaskCounts` params on both signatures (avoids handler signature changes that would break tests). Added `// TODO: remove etapes and counts params after Phase 16 cleanup` comments. + +**Task 2 — call site updates:** + +- `backend/templates/tablos.templ` `TasksTabFragment`: `@KanbanBoard(tablo.ID, csrfToken, tasks, filter)` → `@KanbanBoard(tablo.ID, csrfToken, tasks, filter, etapes)`. `etapes` is already a parameter of `TasksTabFragment`. + +- `backend/internal/web/handlers_tasks.go` line 589 (single reorder path): `tasks, _, _, filter, ok` → `tasks, etapes, _, filter, ok`; KanbanBoard call updated to pass `etapes`. + +- `backend/internal/web/handlers_tasks.go` line 639 (batch reorder path): same change. + +## Verification + +``` +grep -c "groupTasksByEtape" tasks.templ → 3 (func decl + call inside KanbanColumn) +grep -c "EtapeGroupHeader" tasks.templ → 3 (templ decl + usage in KanbanColumn) +grep -c "kanban-column" tasks.templ → 1 +grep -c "tasks-section" tasks.templ → 2 (tasks-section + tasks-section-header) +grep -c "task-row" tasks.templ → 1 +grep -c "EtapeStrip" tasks.templ → 0 +grep -c "KanbanBoard.*etapes" handlers_tasks.go → 2 +grep -c "KanbanBoard.*etapes" tablos.templ → 1 +go build ./backend/... → exit 0 +go test ./backend/internal/web/... -count=1 → ok (all tests pass) +``` + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 1 - Bug] sqlc.Etape has no Color field** +- **Found during:** Task 1 — first build attempt +- **Issue:** Plan specified `etape.Color.Valid` and `etape.Color.String` field access on `sqlc.Etape`. The actual schema `type Etape struct` has no `Color` field — only `ID, TabloID, Title, Description, Position, CreatedAt, UpdatedAt`. +- **Fix:** Set `EtapeGroup.EtapeColor` to `""` unconditionally in `groupTasksByEtape`. `EtapeGroupHeader` will not render a color dot for any group (since `EtapeColor` is always empty). The `is-unassigned` label logic still works correctly. +- **Files modified:** `backend/templates/tasks.templ` +- **Commit:** 084fc0e + +**2. [Rule 3 - Blocking] tablos.templ KanbanBoard call updated in Task 2 (not deferred)** +- **Found during:** Task 1 — `go build ./backend/...` fails after tasks_templ.go regeneration because `tablos_templ.go` still calls old 4-arg KanbanBoard +- **Issue:** Plan intended tablos.templ to be updated in Task 2, but the templ compiler regenerates `tablos_templ.go` from `tablos.templ` immediately, causing a compile-time argument count mismatch. +- **Fix:** Updated `@KanbanBoard(tablo.ID, csrfToken, tasks, filter)` → `@KanbanBoard(tablo.ID, csrfToken, tasks, filter, etapes)` in tablos.templ during the Task 1/Task 2 boundary (before the first commit), then committed tasks.templ alone and tablos.templ + handlers_tasks.go together. The work was still split into two commits as planned. +- **Files modified:** `backend/templates/tablos.templ` +- **Commit:** e2ee434 + +## Known Stubs + +- **EtapeGroup color dot:** `EtapeColor` is always `""` because `sqlc.Etape` has no `Color` field. The `etape-group-color-dot` CSS and `EtapeGroupHeader` color dot rendering are wired but will never fire unless a `Color` field is added to the schema in a future phase. + +## Threat Flags + +No new trust boundaries introduced. All changes are HTML/CSS restructuring of existing authenticated routes. T-16-03-01 through T-16-03-04 dispositions unchanged (accept). + +## Self-Check: PASSED + +- `backend/templates/tasks.templ` — FOUND, contains `groupTasksByEtape`, `EtapeGroupHeader`, `kanban-column`, `tasks-section`, `task-row task-card`, `tasks-add-button`, zero `EtapeStrip` +- `backend/templates/tablos.templ` — FOUND, contains `KanbanBoard.*etapes` +- `backend/internal/web/handlers_tasks.go` — FOUND, contains 2x `KanbanBoard.*etapes` +- Commit 084fc0e — FOUND (Task 1) +- Commit e2ee434 — FOUND (Task 2) +- `go build ./backend/...` — exit 0 +- `go test ./backend/internal/web/... -count=1` — PASSED (ok backend/internal/web + ok backend/internal/web/ui)