docs(20-01): complete tablo detail handler + view model plan summary
- TabloDetailViewModel, TabloDetailColumnView, TabloDetailEtapeView exported
- computeTabloProgress excludes etape tasks
- GetTabloDetailPage handler with IDOR mitigation
- GET /tablos/{tabloID} route registered
- Full test suite green (13 packages)
This commit is contained in:
parent
3fc8aae7a0
commit
27326f57d6
1 changed files with 140 additions and 0 deletions
140
.planning/phases/20-tablo-detail-kanban-restyle/20-01-SUMMARY.md
Normal file
140
.planning/phases/20-tablo-detail-kanban-restyle/20-01-SUMMARY.md
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
---
|
||||
phase: 20-tablo-detail-kanban-restyle
|
||||
plan: "01"
|
||||
subsystem: go-backend/handlers+views
|
||||
tags: [tablo-detail, kanban, view-model, handler, router]
|
||||
dependency_graph:
|
||||
requires: []
|
||||
provides:
|
||||
- TabloDetailViewModel (views package)
|
||||
- TabloDetailColumnView (views package)
|
||||
- TabloDetailEtapeView (views package)
|
||||
- NewTabloDetailViewModel (views package)
|
||||
- computeTabloProgress (views package)
|
||||
- GetTabloDetailPage handler
|
||||
- GET /tablos/{tabloID} route
|
||||
affects:
|
||||
- go-backend/router.go
|
||||
tech_stack:
|
||||
added: []
|
||||
patterns:
|
||||
- tabloDetailRepository interface (type assertion pattern, mirrors taskPageRepository)
|
||||
- TabloDetailPage stub templ.ComponentFunc (replaced by Plan 02 with real component)
|
||||
- TDD RED/GREEN cycle across views + handlers packages
|
||||
key_files:
|
||||
created:
|
||||
- go-backend/internal/web/views/tablo_detail_view.go
|
||||
- go-backend/internal/web/views/tablo_detail_view_test.go
|
||||
- go-backend/internal/web/handlers/tablo_detail.go
|
||||
- go-backend/internal/web/handlers/tablo_detail_test.go
|
||||
modified:
|
||||
- go-backend/router.go
|
||||
decisions:
|
||||
- computeTabloProgress lives in views package (not handlers) — needed by NewTabloDetailViewModel; handlers already have tabloStatusPresentation duplicated in views for same reason (avoid import cycle)
|
||||
- tabloStatusPresentation duplicated in views package — mirrors handlers/tablos.go to avoid import cycle; both functions are simple switch statements
|
||||
- TabloDetailPage stub uses templ.ComponentFunc — avoids needing templ generate in Plan 01; Plan 02 replaces with real .templ component
|
||||
- activePath="/tablos" in DashboardPage/DashboardContentSwap — sidebar Tablos nav item stays highlighted on detail page (Pitfall 3 from RESEARCH)
|
||||
- projectInitialFromName defined in views package — mirrors projectInitial in handlers to avoid import cycle
|
||||
metrics:
|
||||
duration: "~20min"
|
||||
completed_date: "2026-05-18"
|
||||
tasks_completed: 2
|
||||
files_created: 4
|
||||
files_modified: 1
|
||||
---
|
||||
|
||||
# Phase 20 Plan 01: Tablo Detail Handler + View Model Summary
|
||||
|
||||
GET /tablos/{tabloID} handler, view model, and route registration for the tablo detail page. Data contracts (TabloDetailViewModel, column views, etape views) are established that Plan 02 builds its templ component against.
|
||||
|
||||
## What Was Built
|
||||
|
||||
**views/tablo_detail_view.go**
|
||||
- `TabloDetailViewModel` — full page view model with Columns, Etapes, Progress, StatusTone
|
||||
- `TabloDetailColumnView` — kanban column with Tasks []TabloDetailTaskView and CreateHref
|
||||
- `TabloDetailTaskView` — task card with DeleteHref and EditHref
|
||||
- `TabloDetailEtapeView` — etape summary with TaskCount (child tasks count)
|
||||
- `computeTabloProgress(tasks)` — excludes IsEtape==true tasks; returns (doneCount*100)/total
|
||||
- `NewTabloDetailViewModel(tablo, tasks, ownerName)` — builds 4 ordered columns + etapes
|
||||
- `TabloDetailPage(vm)` stub — emits tablo name, column status IDs, `initTabloDetailSortable` function
|
||||
|
||||
**handlers/tablo_detail.go**
|
||||
- `tabloDetailRepository` interface exposing `ListTasksByTablo`
|
||||
- `GetTabloDetailPage()` handler with: auth check → UUID parse (400) → ownership-scoped ListTablos+findTabloByID (404) → type assertion to tabloDetailRepository → ListTasksByTablo → owner lookup (best-effort) → render
|
||||
|
||||
**router.go**
|
||||
- `mux.Get("/tablos/{tabloID}", authHandler.GetTabloDetailPage())` registered before `/tablos/{tabloID}/edit`
|
||||
|
||||
## Security (Threat Model)
|
||||
|
||||
| Threat | Mitigation | Status |
|
||||
|--------|------------|--------|
|
||||
| T-20-01: IDOR via guessed UUID | findTabloByID filters by OwnerID from authenticated session | Mitigated |
|
||||
| T-20-02: Session spoofing | authenticatedUser validates session cookie via GetSessionByToken | Mitigated |
|
||||
| T-20-03: Tampered OwnerID in ListTasksByTablo input | OwnerID comes from session, not request params | Mitigated |
|
||||
|
||||
## Test Coverage
|
||||
|
||||
**View tests (go-backend/internal/web/views/):**
|
||||
- `TestComputeTabloProgress_Empty` — returns 0 for empty input
|
||||
- `TestComputeTabloProgress_AllDone` — returns 100
|
||||
- `TestComputeTabloProgress_Half` — returns 50
|
||||
- `TestComputeTabloProgress_EtapesIgnored` — etape tasks excluded
|
||||
- `TestNewTabloDetailViewModel_GroupsTasksByStatus` — 4 columns populated correctly
|
||||
- `TestNewTabloDetailViewModel_EtapesExcludedFromColumns` — etapes not in columns
|
||||
- `TestNewTabloDetailViewModel_EtapesPopulated` — etapes with correct TaskCount
|
||||
|
||||
**Handler tests (go-backend/internal/web/handlers/):**
|
||||
- `TestGetTabloDetailPage_Returns200` — authenticated + owned tablo → 200 + name in body
|
||||
- `TestGetTabloDetailPage_Returns404` — authenticated + unknown tablo → 404
|
||||
- `TestGetTabloDetailPage_Returns400` — invalid UUID → 400
|
||||
- `TestGetTabloDetailPage_Unauthenticated` — no cookie → 302 /login
|
||||
- `TestTabloDetailKanbanColumns` — body contains todo/in_progress/in_review/done
|
||||
- `TestGetTabloDetailPage_ContainsSortableScript` — body contains "initTabloDetailSortable"
|
||||
|
||||
## Commits
|
||||
|
||||
| Hash | Type | Description |
|
||||
|------|------|-------------|
|
||||
| f24e1c4 | test | RED: failing tests for TabloDetailViewModel + GetTabloDetailPage |
|
||||
| 9713cbd | feat | GREEN: TabloDetailViewModel + computeTabloProgress + TabloDetailPage stub |
|
||||
| 3fc8aae | feat | GetTabloDetailPage handler + GET /tablos/{tabloID} route registration |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
**1. [Rule 2 - Critical] tabloStatusPresentation duplicated in views package**
|
||||
- **Found during:** Task 1
|
||||
- **Issue:** The plan specifies calling `tabloStatusPresentation(tablo.Status)` from the handler, but view model construction (NewTabloDetailViewModel) lives in the views package and cannot import from handlers (import cycle)
|
||||
- **Fix:** Duplicated the small switch statement as `tabloStatusPresentation` in `tablo_detail_view.go` (views package). Same approach used elsewhere in the codebase (e.g. `projectInitialFromName` mirrors `projectInitial`).
|
||||
- **Files modified:** go-backend/internal/web/views/tablo_detail_view.go
|
||||
- **Impact:** Cosmetic code duplication only; no behavior divergence
|
||||
|
||||
**2. [Rule 2 - Critical] projectInitialFromName implemented in views package**
|
||||
- **Found during:** Task 1
|
||||
- **Issue:** `projectInitial` is a handler-package function; calling it from views would create import cycle
|
||||
- **Fix:** `projectInitialFromName` added to views package with identical logic (uppercase first rune of name)
|
||||
- **Files modified:** go-backend/internal/web/views/tablo_detail_view.go
|
||||
|
||||
## Known Stubs
|
||||
|
||||
| Stub | File | Line | Reason |
|
||||
|------|------|------|--------|
|
||||
| `TabloDetailPage(vm)` stub | go-backend/internal/web/views/tablo_detail_view.go | ~155 | Plan 02 replaces with real templ component; stub emits tablo name + column IDs + initTabloDetailSortable |
|
||||
|
||||
## Self-Check
|
||||
|
||||
Files created:
|
||||
- go-backend/internal/web/views/tablo_detail_view.go — FOUND
|
||||
- go-backend/internal/web/views/tablo_detail_view_test.go — FOUND
|
||||
- go-backend/internal/web/handlers/tablo_detail.go — FOUND
|
||||
- go-backend/internal/web/handlers/tablo_detail_test.go — FOUND
|
||||
|
||||
Commits:
|
||||
- f24e1c4 — FOUND
|
||||
- 9713cbd — FOUND
|
||||
- 3fc8aae — FOUND
|
||||
|
||||
go build ./... — PASSES
|
||||
go test ./... -count=1 — ALL PASS (13 packages, 0 failures)
|
||||
|
||||
## Self-Check: PASSED
|
||||
Loading…
Reference in a new issue