Rename tasks plan dashboard state types
This commit is contained in:
parent
71a56b72f1
commit
faf3199b71
1 changed files with 49 additions and 49 deletions
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Replace the simple `go-backend` `/tasks` CRUD page with a server-rendered cross-tablo dashboard that supports `kanban`, `list`, and `roadmap` views, URL-driven filtering, and tablo-aware create/edit flows.
|
||||
**Goal:** Replace the simple `go-backend` `/tasks` CRUD page with a server-rendered cross-tablo tasks page that supports `kanban`, `list`, and `roadmap` views, URL-driven filtering, and tablo-aware create/edit flows.
|
||||
|
||||
**Architecture:** Keep `/tasks` as a single owner-scoped route whose state is driven by query params. Parse page state once in the handler, load one shared task dataset across all owned tablos, then shape that dataset into dedicated `kanban`, `list`, and `roadmap` view models. Reuse the existing task persistence layer, and extend the create/edit flow so `tablo_id` and tablo-scoped étape selection are enforced on the server.
|
||||
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
**Existing files to modify**
|
||||
|
||||
- `go-backend/internal/tasks/model.go`
|
||||
- Add owner-level dashboard page-state types, filter types, and shared dashboard record shapes.
|
||||
- Add owner-level task page-state types, filter types, and shared task page record shapes.
|
||||
- `go-backend/internal/web/handlers/tasks.go`
|
||||
- Parse query params, normalize page state, load shared option data, preserve query state through mutations, and validate `tablo_id` plus tablo-scoped `parent_task_id`.
|
||||
- `go-backend/internal/web/handlers/tasks_test.go`
|
||||
|
|
@ -118,24 +118,24 @@ Expected: FAIL because the current `/tasks` page has no query-state model, no `d
|
|||
Extend `go-backend/internal/tasks/model.go` with a dedicated page-state model:
|
||||
|
||||
```go
|
||||
type DashboardView string
|
||||
type TaskView string
|
||||
|
||||
const (
|
||||
DashboardViewKanban DashboardView = "kanban"
|
||||
DashboardViewList DashboardView = "list"
|
||||
DashboardViewRoadmap DashboardView = "roadmap"
|
||||
TaskViewKanban TaskView = "kanban"
|
||||
TaskViewList TaskView = "list"
|
||||
TaskViewRoadmap TaskView = "roadmap"
|
||||
)
|
||||
|
||||
type RoadmapMode string
|
||||
type TaskRoadmapMode string
|
||||
|
||||
const (
|
||||
RoadmapModeWeek RoadmapMode = "week"
|
||||
RoadmapModeMonth RoadmapMode = "month"
|
||||
TaskRoadmapModeWeek TaskRoadmapMode = "week"
|
||||
TaskRoadmapModeMonth TaskRoadmapMode = "month"
|
||||
)
|
||||
|
||||
type DashboardPageState struct {
|
||||
View DashboardView
|
||||
RoadmapMode RoadmapMode
|
||||
type TaskPageState struct {
|
||||
View TaskView
|
||||
RoadmapMode TaskRoadmapMode
|
||||
TabloIDs []uuid.UUID
|
||||
AssigneeIDs []uuid.UUID
|
||||
Statuses []Status
|
||||
|
|
@ -145,22 +145,22 @@ type DashboardPageState struct {
|
|||
Add parsing helpers with explicit normalization:
|
||||
|
||||
```go
|
||||
func NormalizeDashboardView(raw string) DashboardView {
|
||||
switch DashboardView(raw) {
|
||||
case DashboardViewList:
|
||||
return DashboardViewList
|
||||
case DashboardViewRoadmap:
|
||||
return DashboardViewRoadmap
|
||||
func NormalizeTaskView(raw string) TaskView {
|
||||
switch TaskView(raw) {
|
||||
case TaskViewList:
|
||||
return TaskViewList
|
||||
case TaskViewRoadmap:
|
||||
return TaskViewRoadmap
|
||||
default:
|
||||
return DashboardViewKanban
|
||||
return TaskViewKanban
|
||||
}
|
||||
}
|
||||
|
||||
func NormalizeRoadmapMode(raw string) RoadmapMode {
|
||||
if RoadmapMode(raw) == RoadmapModeMonth {
|
||||
return RoadmapModeMonth
|
||||
func NormalizeTaskRoadmapMode(raw string) TaskRoadmapMode {
|
||||
if TaskRoadmapMode(raw) == TaskRoadmapModeMonth {
|
||||
return TaskRoadmapModeMonth
|
||||
}
|
||||
return RoadmapModeWeek
|
||||
return TaskRoadmapModeWeek
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -169,17 +169,17 @@ func NormalizeRoadmapMode(raw string) RoadmapMode {
|
|||
In `go-backend/internal/web/handlers/tasks.go`, add a helper used by `renderTasksPage`:
|
||||
|
||||
```go
|
||||
func parseDashboardPageState(r *http.Request) taskmodel.DashboardPageState {
|
||||
func parseTaskPageState(r *http.Request) taskmodel.TaskPageState {
|
||||
query := r.URL.Query()
|
||||
state := taskmodel.DashboardPageState{
|
||||
View: taskmodel.NormalizeDashboardView(query.Get("view")),
|
||||
RoadmapMode: taskmodel.NormalizeRoadmapMode(query.Get("roadmap_mode")),
|
||||
state := taskmodel.TaskPageState{
|
||||
View: taskmodel.NormalizeTaskView(query.Get("view")),
|
||||
RoadmapMode: taskmodel.NormalizeTaskRoadmapMode(query.Get("roadmap_mode")),
|
||||
TabloIDs: parseUUIDList(query["tablo"]),
|
||||
AssigneeIDs: parseUUIDList(query["assignee"]),
|
||||
Statuses: parseStatusList(query["status"]),
|
||||
}
|
||||
if state.View != taskmodel.DashboardViewRoadmap {
|
||||
state.RoadmapMode = taskmodel.RoadmapModeWeek
|
||||
if state.View != taskmodel.TaskViewRoadmap {
|
||||
state.RoadmapMode = taskmodel.TaskRoadmapModeWeek
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
|
@ -224,7 +224,7 @@ git add go-backend/internal/tasks/model.go go-backend/internal/web/handlers/task
|
|||
git commit -m "feat: add tasks dashboard query state parsing"
|
||||
```
|
||||
|
||||
## Task 2: Build Shared Dashboard Dataset And Grouping Logic
|
||||
## Task 2: Build Shared Task Dataset And Grouping Logic
|
||||
|
||||
**Files:**
|
||||
- Modify: `go-backend/internal/tasks/model.go`
|
||||
|
|
@ -236,7 +236,7 @@ git commit -m "feat: add tasks dashboard query state parsing"
|
|||
Add tests to `go-backend/internal/web/handlers/tasks_test.go` or a new focused view-model test section in the same file:
|
||||
|
||||
```go
|
||||
func TestTasksDashboardListGroupsByStatusAcrossTablos(t *testing.T) {
|
||||
func TestTasksListGroupsByStatusAcrossTablos(t *testing.T) {
|
||||
now := time.Date(2026, 5, 10, 12, 0, 0, 0, time.UTC)
|
||||
tabloA := tablomodel.Record{ID: uuid.New(), Name: "Alpha", Color: "#111111"}
|
||||
tabloB := tablomodel.Record{ID: uuid.New(), Name: "Beta", Color: "#222222"}
|
||||
|
|
@ -247,11 +247,11 @@ func TestTasksDashboardListGroupsByStatusAcrossTablos(t *testing.T) {
|
|||
{ID: uuid.New(), TabloID: tabloB.ID, Title: "Review launch", Status: taskmodel.StatusInReview, CreatedAt: now.Add(2 * time.Minute)},
|
||||
}
|
||||
|
||||
vm := views.NewTasksDashboardPageViewModel(
|
||||
vm := views.NewTasksPageViewModel(
|
||||
[]tablomodel.Record{tabloA, tabloB},
|
||||
records,
|
||||
nil,
|
||||
taskmodel.DashboardPageState{View: taskmodel.DashboardViewList, RoadmapMode: taskmodel.RoadmapModeWeek},
|
||||
taskmodel.TaskPageState{View: taskmodel.TaskViewList, RoadmapMode: taskmodel.TaskRoadmapModeWeek},
|
||||
now,
|
||||
)
|
||||
|
||||
|
|
@ -267,7 +267,7 @@ func TestTasksDashboardListGroupsByStatusAcrossTablos(t *testing.T) {
|
|||
Add a roadmap test:
|
||||
|
||||
```go
|
||||
func TestTasksDashboardRoadmapCreatesPerTabloSansEtapeLanes(t *testing.T) {
|
||||
func TestTasksRoadmapCreatesPerTabloSansEtapeLanes(t *testing.T) {
|
||||
now := time.Date(2026, 5, 10, 12, 0, 0, 0, time.UTC)
|
||||
tabloA := tablomodel.Record{ID: uuid.New(), Name: "Alpha"}
|
||||
tabloB := tablomodel.Record{ID: uuid.New(), Name: "Beta"}
|
||||
|
|
@ -278,11 +278,11 @@ func TestTasksDashboardRoadmapCreatesPerTabloSansEtapeLanes(t *testing.T) {
|
|||
{ID: uuid.New(), TabloID: tabloB.ID, Title: "B task", Status: taskmodel.StatusTodo, DueDate: &due, CreatedAt: now},
|
||||
}
|
||||
|
||||
vm := views.NewTasksDashboardPageViewModel(
|
||||
vm := views.NewTasksPageViewModel(
|
||||
[]tablomodel.Record{tabloA, tabloB},
|
||||
records,
|
||||
nil,
|
||||
taskmodel.DashboardPageState{View: taskmodel.DashboardViewRoadmap, RoadmapMode: taskmodel.RoadmapModeWeek},
|
||||
taskmodel.TaskPageState{View: taskmodel.TaskViewRoadmap, RoadmapMode: taskmodel.TaskRoadmapModeWeek},
|
||||
now,
|
||||
)
|
||||
|
||||
|
|
@ -303,9 +303,9 @@ Expected: FAIL because the current view model has no `List` or `Roadmap` structu
|
|||
Reshape `go-backend/internal/web/views/tasks_view.go` around a new top-level model:
|
||||
|
||||
```go
|
||||
type TasksDashboardPageViewModel struct {
|
||||
State taskmodel.DashboardPageState
|
||||
Filters TasksDashboardFiltersView
|
||||
type TasksPageViewModel struct {
|
||||
State taskmodel.TaskPageState
|
||||
Filters TasksFiltersView
|
||||
Kanban TasksKanbanView
|
||||
List TasksListView
|
||||
Roadmap TasksRoadmapView
|
||||
|
|
@ -339,7 +339,7 @@ Implement these builders in `go-backend/internal/web/views/tasks_view.go`:
|
|||
```go
|
||||
func buildKanbanView(records []taskmodel.Record, tabloByID map[uuid.UUID]tablomodel.Record, assigneeLabels map[uuid.UUID]string) TasksKanbanView
|
||||
func buildListView(records []taskmodel.Record, tabloByID map[uuid.UUID]tablomodel.Record, assigneeLabels map[uuid.UUID]string) TasksListView
|
||||
func buildRoadmapView(records []taskmodel.Record, tabloByID map[uuid.UUID]tablomodel.Record, assigneeLabels map[uuid.UUID]string, mode taskmodel.RoadmapMode, now time.Time) TasksRoadmapView
|
||||
func buildRoadmapView(records []taskmodel.Record, tabloByID map[uuid.UUID]tablomodel.Record, assigneeLabels map[uuid.UUID]string, mode taskmodel.TaskRoadmapMode, now time.Time) TasksRoadmapView
|
||||
```
|
||||
|
||||
Use deterministic ordering:
|
||||
|
|
@ -543,7 +543,7 @@ git add go-backend/internal/web/handlers/tasks.go go-backend/internal/web/handle
|
|||
git commit -m "feat: add tablo-aware task form validation"
|
||||
```
|
||||
|
||||
## Task 4: Replace The `/tasks` Page Rendering With Multi-View Dashboard UI
|
||||
## Task 4: Replace The `/tasks` Page Rendering With Multi-View Task UI
|
||||
|
||||
**Files:**
|
||||
- Modify: `go-backend/internal/web/views/tasks_view.go`
|
||||
|
|
@ -633,7 +633,7 @@ Expected: FAIL because the current page still renders simple tablo sections and
|
|||
Replace `TasksPageContent` in `go-backend/internal/web/views/tasks_view.go` with a richer shell:
|
||||
|
||||
```go
|
||||
func TasksPageContent(vm TasksDashboardPageViewModel) templ.Component {
|
||||
func TasksPageContent(vm TasksPageViewModel) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
|
||||
_, _ = io.WriteString(w, `<div class="app-section-page tasks-page" data-current-view="`+html.EscapeString(string(vm.State.View))+`">`)
|
||||
_, _ = io.WriteString(w, `<div class="app-section-surface"><div class="tasks-page-header">`)
|
||||
|
|
@ -651,9 +651,9 @@ func TasksPageContent(vm TasksDashboardPageViewModel) templ.Component {
|
|||
Add explicit renderers:
|
||||
|
||||
```go
|
||||
func renderCurrentTasksView(w io.Writer, vm TasksDashboardPageViewModel) error
|
||||
func renderTasksViewTabs(state taskmodel.DashboardPageState) string
|
||||
func renderTasksFilters(filters TasksDashboardFiltersView, state taskmodel.DashboardPageState) string
|
||||
func renderCurrentTasksView(w io.Writer, vm TasksPageViewModel) error
|
||||
func renderTasksViewTabs(state taskmodel.TaskPageState) string
|
||||
func renderTasksFilters(filters TasksFiltersView, state taskmodel.TaskPageState) string
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Implement the view-specific renderers**
|
||||
|
|
@ -734,7 +734,7 @@ If your existing router tests already log in first, use that pattern instead and
|
|||
Add a mutation-state preservation test in handler tests:
|
||||
|
||||
```go
|
||||
func TestPatchTaskRendersCurrentDashboardQueryState(t *testing.T) {
|
||||
func TestPatchTaskRendersCurrentTaskQueryState(t *testing.T) {
|
||||
repo := NewInMemoryAuthRepository()
|
||||
handler := NewAuthHandler(repo)
|
||||
sessionCookie := loginTestUser(t, handler, "demo@xtablo.com", "xtablo-demo")
|
||||
|
|
@ -781,9 +781,9 @@ Make `renderTasksPage` fully query-driven so post-mutation handlers reuse the re
|
|||
|
||||
```go
|
||||
func (h *AuthHandler) renderTasksPage(w http.ResponseWriter, r *http.Request) {
|
||||
state := parseDashboardPageState(r)
|
||||
state := parseTaskPageState(r)
|
||||
// load tablos, tasks, assignees
|
||||
vm := views.NewTasksDashboardPageViewModel(tablos, filteredTasks, assigneeLabels, state, time.Now().UTC())
|
||||
vm := views.NewTasksPageViewModel(tablos, filteredTasks, assigneeLabels, state, time.Now().UTC())
|
||||
// existing DashboardPage / DashboardContentSwap render path
|
||||
}
|
||||
```
|
||||
|
|
@ -827,6 +827,6 @@ git commit -m "test: verify tasks dashboard routing and state preservation"
|
|||
|
||||
**Type consistency**
|
||||
|
||||
- View-state types use `DashboardView`, `RoadmapMode`, and `DashboardPageState` consistently.
|
||||
- View-state types use `TaskView`, `TaskRoadmapMode`, and `TaskPageState` consistently.
|
||||
- Renderer naming is consistent across `buildKanbanView`, `buildListView`, `buildRoadmapView`, and `renderKanbanView`, `renderListView`, `renderRoadmapView`.
|
||||
- Mutation validation consistently uses `tablo_id` and `parent_task_id`.
|
||||
|
|
|
|||
Loading…
Reference in a new issue