10 KiB
spec, created, status
| spec | created | status |
|---|---|---|
| docs/specs/ui/task-listing-display-preferences.md | 2026-07-25 | done |
Implementation Plan: Task Listing Display Preferences
Overview
First correct workflow-filter resolution and introduce a device-local view preference with a compatibility fallback from the existing portable Kanban mode. In parallel, extend backend user settings with the portable richer-row boolean. Then wire that setting through frontend state and render optional Kanban-like metadata in list rows before proving the integrated desktop and mobile flows.
Root cause
resolveDesiredWorkflowId in
apps/web/lib/kanban/resolve-workflow.ts treats a single visible workflow as a
reason to replace an explicit null selection with that workflow ID.
KanbanBoard.useWorkflowSelection runs the resolver after the display dropdown
stores All Workflows as null, so Kanban immediately selects the sole
workflow again. List does not run that effect, which is why the same selector
appears stable there.
Backend
Portable richer-row user setting
apps/backend/internal/user/models/models.go: addTasksListShowDetails booltoUserSettings.apps/backend/internal/user/dto/dto.go: exposetasks_list_show_detailsin read and PATCH DTOs and map it inFromUserSettings.apps/backend/internal/user/service/service.go: apply the optional boolean patch and include it inuser.settings.updatedevents.apps/backend/internal/user/store/sqlite.go: serialize and deserialize the boolean in the existing JSON-backed user settings record. No SQL migration is required.apps/backend/internal/backendapp/boot_state_routes.go: includetasksListShowDetailsin the SPA boot payload.
Backend tests
apps/backend/internal/user/dto/dto_test.go: prove read mapping and PATCH omission/false semantics.apps/backend/internal/user/service/service_test.go: prove applying and publishing both true and false values without changing omitted values.- Add or update the closest
apps/backend/internal/user/store/*_test.goandapps/backend/internal/backendapp/*_test.gocoverage if the existing round-trip/boot-payload suites enumerate every field.
Frontend
Workflow-filter correction
apps/web/lib/kanban/resolve-workflow.ts: stop interpreting a single visible workflow as stronger than an explicit empty filter.apps/web/lib/kanban/resolve-workflow.test.ts: replace the one-workflow auto-selection expectation with a regression that preservesnull; retain valid active/saved workflow fallback coverage.apps/web/app/page.tsx,apps/web/app/tasks/page.tsx, andapps/web/src/spa-routes.tsx: update stale comments only where needed; all call sites continue using the shared resolver.
Device-local task-listing view
- Add
apps/web/lib/task-listing/view-preference.tsandview-preference.test.tswith the versioned localStorage key, strict enum parsing, legacykanbanViewModefallback, mobile effective-view resolution, and same-document change notification. - Add
apps/web/hooks/use-task-listing-view.tsto expose the stored preference, synchronize the in-memory Kanban/Pipeline mode used by the board, and perform route changes without backend writes. apps/web/app/page-client.tsx: when Home mounts, restore List by navigating to/tasks; otherwise synchronize the saved Kanban/Pipeline mode before the board becomes authoritative.apps/web/app/tasks/tasks-page-client.tsx: record List when/tasksis explicitly shown.apps/web/components/kanban/kanban-header.tsxandapps/web/components/kanban/mobile-menu-sheet.tsx: use the device-local hook for selected state and view changes. Phone renders the effective Kanban fallback for a saved Pipeline preference without writing over Pipeline.apps/web/hooks/use-kanban-display-settings.tsandapps/web/hooks/use-user-display-settings.ts: stop view-toggle interactions from PATCHingkanban_view_mode; retain the legacy backend field only as a first-use compatibility input.
Portable richer-row frontend setting
apps/web/lib/types/http-user-settings.ts,apps/web/lib/types/backend.ts,apps/web/lib/state/slices/settings/types.ts,apps/web/lib/state/slices/settings/settings-slice.ts, andapps/web/lib/ssr/user-settings.ts: maptasks_list_show_detailstotasksListShowDetails, defaulting tofalse.apps/web/lib/ssr/user-settings.test.tsand the closest settings-slice tests: prove true mapping and the false default.apps/web/hooks/use-user-display-settings.tsandapps/web/hooks/use-kanban-display-settings.ts: carry, optimistically update, and PATCH the richer-row preference without dropping unrelated settings.apps/web/components/kanban-display-dropdown.tsx: accept the current page and show a self-documenting Show task details checkbox only for List.apps/web/components/kanban/mobile-menu-sheet.tsx: show the same checkbox only for List in the existing phone drawer/tablet sheet.
Rich list rows
apps/web/app/tasks/tasks-page-client.tsx: read the new setting, fetch workspace PR summaries while details are enabled, and pass the option into the list.apps/web/app/tasks/tasks-list-view.tsx: keep the compact row unchanged when disabled; when enabled, render a taller but still single-column row hierarchy with repository slug chips, truncated description,PRTaskIcon, session count, parent context, and review-attention state. Extract a focused sibling component/helper if needed to stay within file/function limits.- Reuse
repositorySlug, shared PR status UI, existing badges, and existing task navigation/action handlers. Missing metadata renders nothing.
Mobile design contract
- Desktop outcome: existing topbar toggles restore the device's last Kanban/Pipeline/List view; List's display dropdown controls optional rich rows.
- Mobile entry point: the shipped topbar menu opens
MobileMenuSheet; its existing View controls and Display Options section own both choices. - Nearest shipped exemplars:
mobile-menu-sheet.tsxsupplies the safe-area-aware inset drawer and internal scroll owner;mobile-task-list-search.spec.tssupplies the compact list interaction; Kanban card content supplies metadata hierarchy. - Hierarchy and primary action: task state and title remain first, repo/PR context second, description third, attention badges last. The row body opens the task; archive/delete remain explicit secondary controls.
- Presentation: temporary settings remain in the existing inset bottom drawer. Rich task data stays inline in the primary List surface rather than opening another overlay.
- Geometry: the drawer retains its
100dvh, safe-area padding, and internal scroll. The task-list<main>remains the only page-content scroll owner; metadata truncates/wraps within the row and must not create document horizontal overflow. - Shared versus responsive logic: preference, filtering, metadata resolution, and row actions are shared. Only spacing, truncation, and the Pipeline effective fallback vary by viewport.
- Mobile proof: Playwright selects List from the drawer, reloads Home to prove local restoration, enables details, verifies rich metadata and row navigation, and asserts no horizontal document overflow.
Tests
- One-workflow All Workflows: unit regression in
resolve-workflow.test.ts; browser regression ine2e/tests/kanban/workflow-filter.spec.ts. - Device preference parsing/fallback: table-driven Vitest cases in
view-preference.test.tsfor valid, missing, malformed, legacy, and mobile Pipeline values. - Portable details setting: Go DTO/service/store tests plus frontend SSR and settings-store mapping tests.
- Rich metadata: pure helper tests for repository/attention derivation where logic is extracted; rendered behavior is covered through Playwright rather than new React component tests.
E2E Tests
apps/web/e2e/tests/kanban/workflow-filter.spec.ts: with only the seeded workflow, choose All Workflows, close/reopen display options, reload, and verify it remains selected.apps/web/e2e/tests/task/task-listing-view-preferences.spec.ts: select Pipeline and List through desktop controls, reload Home, and verify the last device-local view; enable rich rows and verify repo, description, PR, and portable-setting reload behavior.apps/web/e2e/tests/task/mobile-task-listing-display.spec.ts: use the phone drawer to choose List, reopen Home to prove restoration, enable rich rows, assert metadata/row navigation, and assert no horizontal overflow. Also preseed Pipeline and prove the phone fallback does not overwrite it.
Implementation waves
Wave 1 (parallel):
Wave 2:
Wave 3:
Verification
make -C apps/backend fmtcd apps/backend && go test ./internal/user/... ./internal/backendapp/...cd apps && pnpm --filter @kandev/web test -- --run lib/kanban/resolve-workflow.test.ts lib/task-listing/view-preference.test.ts lib/ssr/user-settings.test.tscd apps/web && pnpm run typecheckcd apps && pnpm --filter @kandev/web lintcd apps/web && pnpm e2e:run tests/kanban/workflow-filter.spec.ts tests/task/task-listing-view-preferences.spec.ts tests/task/mobile-task-listing-display.spec.ts- After the implementation commit, delegate final change-aware
verifywith the non-bypassed hook receipt.
Risks
- Home restoration must avoid a
/↔/tasksredirect loop and must not hijack task-detail or explicitly opened external routes. - Hydrated backend
kanban_view_modecan briefly disagree with the local preference; the local preference must become authoritative before the board chooses Pipeline/Kanban. - User-settings payload builders carry many unrelated fields; the new boolean must not be dropped by optimistic updates or WS fallback writes.
- PR summary fetching should be enabled only when rich rows need it and must reuse the existing workspace-level cache/store.