Add tasks multi-view dashboard design spec
This commit is contained in:
parent
dd6e5b7d64
commit
1a00f84364
1 changed files with 374 additions and 0 deletions
|
|
@ -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=<tablo_id>` optional, repeatable
|
||||||
|
- `assignee=<user_id>` 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
|
||||||
Loading…
Reference in a new issue