#24 — List Templates #24
Labels
No labels
priority/could
priority/must
priority/should
priority/wont
status/blocked
status/claimed
status/done-migrated
type/bug
type/feature
type/infra
type/tech-debt
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
robert/todo#24
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Story: List Templates
As a user creating a new list,
I want to choose from a set of predefined templates,
so that common household lists are ready to use in seconds.
Acceptance criteria:
Out of scope for this story:
Blockers: None
Priority: Could — good onboarding aid; lowers the barrier to creating a first useful list.
Design note — Software Architect
Backend:
Common/Types/TodoListTemplate.cs— a plain closed enum (Blank, Groceries, WeeklyChores, MovieNight), same precedent asTodoListColor/TodoListIcon(#23): no validationlogic needed, the type system + JSON deserialization already reject out-of-range values. Per the
AC ("maintained in a static configuration file, not in the database"), the enum carries no seed
data itself — a new static class
CqsTodo/Features/TodoLists/TodoListTemplateCatalog.csmaps eachTodoListTemplateto itsIReadOnlyList<TodoTitle>seed titles. Nothing is persisted onTodoListEntity/TodoListDto— the template only affects the moment of creation; the resultingtodos are indistinguishable from any other todo afterward (per AC).
CreateTodoListCommandgains a second parameter:TodoListTemplate Template = TodoListTemplate.Blank(default preserves every existing call site/test unchanged). After the list + owner membership are
persisted (existing code),
CreateTodoListCommandHandlerloops the catalog's titles for the chosentemplate and calls a newly constructor-injected
IHandler<CreateTodoCommand, TodoDto>once pertitle — reusing the existing single-todo-creation pipeline (trigger-assigned
Nr/SortOrder,per-todo WS broadcast, activity-feed event) rather than hand-rolling a batch insert. Confirmed via
code research that constructor-injecting a handler resolves the fully decorated chain (DI
decorates at the
IHandler<TRequest,TResult>service-registration level, not per call site), soCreateTodoCommand'sAuthorizeTodoListAccessForCurrentUserQuerystill runs — it passes becausethe owner membership row was already committed earlier in the same handler. This also sidesteps a
real risk: batch-inserting multiple
TodoEntityrows in oneSaveChangesAsyncinteracts with thetrg_set_todo_nr_per_listPostgres trigger and EF's triggered-table batching-boundary behavior ina way this repo has never had a test for — going through the existing single-insert path avoids
relying on that untested interaction.
Frontend: the current "New list" flow is a bare
window.prompt()inTodoListMenu.tsx— nodialog component exists to extend. Replaces it with a new
CreateListDialog.tsx(content-only,mirroring
ListAppearanceDialog.tsx's shape), wrapped in the sharedDialog/DialogContentprimitive directly in
TodoListMenu.tsx(matching howOwnerActionsMenu.tsxwrapsListAppearanceDialog). Title input + a template picker grid (role="group",aria-pressed,same idiom as the colour/icon pickers) with "Blank" pre-selected. Submits
{todoList: {...}, template}toCreateTodoListCommand.E2E impact: 12 Playwright specs currently do
page.once('dialog', d => d.accept(title))thenpage.click('#add-todolist-button'), intercepting the nativeprompt(). All 12 need updating tofill the new dialog's title input and click its Create button instead — this sandbox has no
Docker/Playwright stack to run them (same limitation noted in
docs/roadmap.md's#46/#52entries), so these are updated by careful reading, not verified by a real run. Flagged here rather
than silently assumed correct.
Out of scope kept: no DB schema change at all — templates are pure command-time input, never
stored.
QA / Code review — closing notes (2026-07-22)
Arrived at this cycle fully implemented (backend, frontend, e2e migration) but never committed;
verified every AC against the code, confirmed
dotnet build,npm run build, and the full frontendvitest suite (357 tests) are green, and that the Testcontainers-backed backend handler tests fail
only with the known Docker-unavailable-sandbox shape (not a real regression) — see
ai/roles/memory/06_qa_agent_memory.md.8-angle code review found and fixed two issues same-cycle:
TodoListTemplateCatalog.SeedTitlesconverted from aDictionaryliteral to aswitchexpression, so the compiler (CS8509) now forces a decision the next time
TodoListTemplategainsa member instead of a runtime
KeyNotFoundException.WeeklyChoresandMovieNighttemplates (previously onlyGroceries/Blankwere tested).Two lower-severity findings were drafted as follow-up stories rather than expanding this cycle's
scope:
docs/features/new/63_template_seeding_atomicity_and_batching.md(the seed loop isn'ttransactional and costs N sequential per-item round trips) and
docs/features/new/64_shared_picker_grid_component.md(the template picker duplicates an existingselectable-grid pattern from
ListAppearanceDialog/LabelPicker).