diff --git a/docs/superpowers/specs/2026-05-10-go-backend-tasks-multi-view-dashboard-design.md b/docs/superpowers/specs/2026-05-10-go-backend-tasks-multi-view-dashboard-design.md new file mode 100644 index 0000000..fcab2e7 --- /dev/null +++ b/docs/superpowers/specs/2026-05-10-go-backend-tasks-multi-view-dashboard-design.md @@ -0,0 +1,374 @@ +# Go Backend Tasks Multi-View Dashboard Design + +**Date:** 2026-05-10 + +**Goal** + +Replace the current simple `/tasks` CRUD page in `go-backend` with a richer owner-level tasks dashboard that aggregates tasks across every tablo owned by the signed-in user and renders three server-side views: + +- `Tableau` +- `Liste` +- `Roadmap` + +The page should use query params as the source of truth for view state and filtering, while keeping task creation, editing, and deletion inside the existing server-rendered and HTMX-friendly backend architecture. + +**Context** + +The current tasks vertical slice already exists in `go-backend`: + +- owner-scoped task and etape persistence is implemented +- `/tasks` already supports create, edit, update, and delete +- the page is currently rendered as a simple grouped CRUD view + +This work does not redesign the persistence model. It redesigns the `/tasks` page contract, its server-side view shaping, and the related create/edit flows so the route behaves like a personal cross-tablo dashboard. + +**Scope** + +- Work exclusively in `go-backend` +- Replace the current `/tasks` page with a server-rendered dashboard +- Aggregate tasks across all tablos owned by the current user +- Support three server-rendered views on the same route: + - `kanban` + - `list` + - `roadmap` +- Support URL-driven state with query params +- Implement functional filtering for: + - `tablo` + - `assignee` + - `status` +- Extend create and edit forms to include: + - `tablo_id` + - `parent_task_id` + - `assignee_id` + - `due_date` + - `status` + - `title` + - `description` +- Constrain selectable etapes to the chosen tablo during create and edit +- Implement two roadmap sub-modes: + - `week` + - `month` + +**Out Of Scope** + +- Drag and drop +- Client-only tab switching +- Calendar view +- Reordering tasks or etapes +- Persisted synthetic "Sans étape" rows +- New JSON API endpoints +- Collaborator or RBAC expansion beyond owner-only access +- Comments, attachments, activity feeds, or task detail pages + +**Architecture** + +The feature should stay inside the current Go server-rendered stack: + +- HTTP handlers in `go-backend/internal/web/handlers/` +- page and component rendering in `go-backend/internal/web/views/` +- task shaping and task-related helpers in `go-backend/internal/tasks/` or closely related view-model code +- existing repository methods reused or extended only as needed + +The core architectural move is to split `/tasks` rendering into: + +1. one shared page-state parser +2. one shared filtered task dataset +3. three pure view-specific shaping functions + +Recommended shaping functions: + +- `buildKanbanView(...)` +- `buildListView(...)` +- `buildRoadmapView(...)` + +This keeps filtering and ownership logic centralized while isolating presentation-specific grouping rules. + +**Route Contract** + +The route remains `GET /tasks`, but the page state is now controlled by query params. + +Supported query params: + +- `view=kanban|list|roadmap` +- `roadmap_mode=week|month` +- `tablo=` optional, repeatable +- `assignee=` optional, repeatable +- `status=todo|in_progress|in_review|done` optional, repeatable + +Default behavior: + +- missing or invalid `view` falls back to `kanban` +- missing or invalid `roadmap_mode` falls back to `week` +- invalid filter values are ignored rather than breaking the page +- `roadmap_mode` is ignored when `view` is not `roadmap` + +The page should remain shareable and refresh-safe: + +- filter changes submit with `GET /tasks` +- tabs are links that preserve the current applicable filters +- roadmap mode toggles are links or GET forms that preserve filters + +**Data Scope** + +All three views consume the same owner-scoped task set: + +- every active task across every tablo owned by the signed-in user +- every active etape required to label or group those tasks + +This is intentionally not a single-tablo page. `/tasks` is a personal dashboard over the user’s full owned workload. + +**Shared Page Shell** + +The page shell should render: + +- page title +- `Nouvelle tâche` action +- view tabs: + - `Tableau` + - `Liste` + - `Roadmap` +- roadmap sub-mode toggle when `view=roadmap` +- filter controls for: + - tablo + - assignee + - status + +The shell also needs the option data required to render forms and filters: + +- all owner tablos +- all relevant assignee options +- status options +- tablo-scoped etape options for create and edit forms + +**Kanban View Design** + +The kanban view is status-driven and cross-tablo. + +Columns: + +- `À faire` +- `En cours` +- `Vérification` +- `Terminé` + +Each column contains task cards from all owned tablos matching that status. + +Each card should show enough metadata to make a cross-tablo dashboard usable: + +- title +- tablo name +- étape label, or `Sans étape` +- due date if present +- assignee if present + +First pass interaction is explicit-action based: + +- no drag and drop +- status changes happen through edit actions or other explicit server-backed controls + +**List View Design** + +The list view is grouped by status across all owned tablos. + +Groups: + +- `À faire` +- `En cours` +- `Vérification` +- `Terminé` + +Inside each group, tasks are rendered as flat rows rather than nested by tablo or étape. + +Each row should show: + +- title +- tablo +- étape or `Sans étape` +- assignee if present +- due date if present +- status actions and edit/delete access as appropriate + +This view is intentionally a personal workload list rather than a single-tablo breakdown. + +**Roadmap View Design** + +The roadmap view is grouped horizontally by étape lanes and bucketed by due date. + +Lane rules: + +- lanes represent `tablo + étape` +- the lane identity must include the tablo to avoid collisions between same-named etapes in different tablos +- tasks without a parent etape go into a synthetic per-tablo lane: + - `Tablo name / Sans étape` + +Bucket rules: + +- tasks are grouped inside each lane by due date +- tasks without a due date go into a final `Sans date` bucket + +Roadmap sub-modes: + +- `week` + - weekly buckets starting from today, May 10, 2026 +- `month` + - monthly buckets starting from the current month containing May 10, 2026 + +Past-due tasks: + +- tasks with due dates before the current starting point should still remain visible +- they should be folded into the first visible bucket rather than disappearing into an unrendered past section + +This preserves visibility without introducing a separate "past" axis in the first pass. + +**Filter Behavior** + +Filters must apply consistently across all three views. + +Required filters: + +- `tablo` +- `assignee` +- `status` + +Behavior: + +- filters are applied once to the shared task set before view-specific grouping +- switching between `kanban`, `list`, and `roadmap` preserves active filters where possible +- switching roadmap sub-modes preserves active filters + +Invalid filters: + +- unknown tablo IDs are dropped +- unknown assignee IDs are dropped +- unknown status values are dropped + +The page should always normalize back to a valid state instead of failing. + +**Create And Edit Flow** + +Because `/tasks` is now cross-tablo, create and edit forms must include tablo selection. + +Create form fields: + +- `tablo_id` +- `title` +- `description` +- `status` +- `due_date` +- `assignee_id` +- `parent_task_id` + +Edit form fields: + +- `tablo_id` +- `title` +- `description` +- `status` +- `due_date` +- `assignee_id` +- `parent_task_id` + +Tablo and étape coupling: + +- once a tablo is chosen, selectable etapes must be limited to active etapes from that tablo only +- `Sans étape` remains a runtime concept, represented by `parent_task_id = null` + +The chosen tablo and étape combination must be enforced on the server even if the browser submits an invalid combination. + +**Validation Rules** + +The dashboard should reuse the current task invariants and add page-specific validation. + +Required server-side rules: + +- `parent_task_id` must be null or point to an active etape +- `parent_task_id` must belong to the selected `tablo_id` +- a task cannot point to an etape from another tablo +- `assignee_id` must resolve to a valid selectable user +- invalid `view` and `roadmap_mode` values fall back to defaults + +When editing a task: + +- if the selected tablo changes, the selected parent etape must be revalidated against the new tablo +- if the current parent no longer belongs to the selected tablo, the mutation must be rejected + +Delete behavior remains unchanged functionally: + +- delete the task or etape +- re-render the page in the current normalized query state + +**Rendering Strategy** + +The page should keep the current server-driven model: + +- full-page render for normal requests +- content fragment render for HTMX requests where appropriate +- create and edit forms can continue to use modal or inline fragment responses + +The important change is not the transport mechanism. It is the introduction of explicit page-state modeling and isolated per-view presenters. + +Recommended internal page-state model: + +- selected `view` +- selected `roadmap_mode` +- selected tablo filters +- selected assignee filters +- selected status filters + +Recommended shared view-model layers: + +- page shell view model +- create/edit form view model +- `kanban` view model +- `list` view model +- `roadmap` view model + +**Testing** + +Testing should cover both state parsing and view grouping behavior. + +Handler tests: + +- default `/tasks` render +- `view=kanban` +- `view=list` +- `view=roadmap` +- `roadmap_mode=week` +- `roadmap_mode=month` +- filter combinations for: + - tablo + - assignee + - status +- create flow with `tablo_id` +- edit flow with `tablo_id` +- invalid cross-tablo parent étape rejection + +View-model tests where practical: + +- kanban grouping by status across multiple tablos +- list grouping by status across multiple tablos +- roadmap lane identity uses `tablo + étape` +- synthetic `Sans étape` lanes are created per tablo +- `Sans date` bucket is rendered +- overdue tasks are placed in the first visible roadmap bucket + +**Implementation Slices** + +1. Extend `/tasks` query parsing and normalized page-state handling. +2. Build shared filter-option loading for tablos, assignees, and statuses. +3. Introduce a shared owner-level filtered task dataset. +4. Split rendering into `kanban`, `list`, and `roadmap` view-model builders. +5. Replace the current simple `/tasks` view with a page shell and mode-specific renderers. +6. Extend create and edit forms with `tablo_id` and tablo-scoped etape options. +7. Add tests for filtering, grouping, roadmap bucketing, and invalid parent validation. + +**Recommendation** + +Use one `/tasks` route with shared filtering and separate presenter functions for each mode. + +This approach fits the current Go backend architecture best because it: + +- keeps the URL as the source of truth +- avoids duplicated repository and filter logic +- keeps each view’s grouping logic isolated +- preserves server-rendered behavior without introducing a browser-side state machine