#91b — Priorisierte Liste — 2D-Karten-Ansicht mit Drag-to-Reposition #91
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#91
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: Priorisierte Liste — 2D-Karten-Ansicht mit Drag-to-Reposition
Folge-Story zu
#91.#91's V1 lieferte die klassische Listenansicht mit Formel-Sortierung + einemdisabled-"Karte (kommt bald)"-Toggle-Placeholder. Diese Story tauscht den Placeholder gegen die
tatsächliche interaktive 2D-Ansicht aus.
As a Nutzer einer Priorisierten Liste,
I want to meine Todos in einer echten 2D-Karte (Achsen Dringend x / Wichtig y) sehen und einen
bestehenden Punkt direkt auf der Karte ziehen, um seine Urgency/Importance nachträglich zu ändern,
so that ich die Priorisierung visuell und mit einer einzigen Geste anpassen kann, statt jeden
Todo einzeln über das Ändern-Modal aufzurufen.
Voraussetzungen (bereits durch
#91V1 vorhanden — keine Backend-Arbeit nötig):TodoListType.Priority-Diskriminator +PriorityMatrixFactoranTodoListEntity.TodoEntity.Urgency/Importance(nullable, 1–100).SetTodoUrgencyImportanceCommand-Handler mit Type=Priority-Gate.PriorityMatrixPagemit Ansichts-Toggle "Liste / Karte (kommt bald)" — der Karten-Button istbereits im DOM und muss nur enabled + der Panel-Inhalt gerendert werden.
Acceptance criteria:
PriorityMatrixPageist enabled; der Klick tauscht den Listen-Panelgegen einen 2D-Scatter-Panel (Achsen Dringend x=1–100 / Wichtig y=1–100).
Urgency/Importance-Position gerendert, mit sichtbarem Titel bei Hover/Focus.
Loslassen wird
SetTodoUrgencyImportanceCommandmit den neuen Achsen-Werten aufgerufen unddie Kartenposition bestätigt sich anhand der DTO-Antwort.
geklemmt (kein 400 vom Vogen-Validator, kein Punkt außerhalb der Karte).
Ansicht filtert die sichtbaren Punkte).
ausgeblendet — analog zur Sortierung-ans-Ende in der Listen-Ansicht.
sodass ein Nutzer der die Karte per Zeigegerät nicht bedienen kann, weiterhin über die
Listen-Ansicht + das bestehende Modal (aus
#91V1) editieren kann.Out of scope for this story:
Command-Ausführung wie jede andere; Undo ist ein eigenständiges Cross-cutting-Thema).
Hinweis zur Priorität: Could — wie
#91selbst. Sinnvoll aufzunehmen, sobald V1 in echterNutzung ist und der genaue Bedarf klar wird (z. B. "wird die Karte auf Mobile überhaupt praktisch
genutzt?").
#91's Design-Doc hält die Rationale für den Deferral fest.design (
91b_priority_matrix_2d_view_design.md)Design:
#91b— Priorisierte Liste, 2D-Karten-Ansicht mit Drag-to-RepositionContext
#91V1 shipped the Priority Matrix list type with a formula-sorted classic list view and adisabled "Karte (kommt bald)" toggle, deliberately deferring the full 2D scatter/drag view to this
follow-up story. No backend, data-model, or command-handler changes are needed —
TodoListType,TodoUrgency/TodoImportance, andSetTodoUrgencyImportanceCommand(with its Type=Priority gate)already exist and are already tested. This is a frontend-only story.
Approach
New
PriorityMatrixCanvas.tsx: a plain SVG scatter plot,viewBox="0 0 100 100", mapping directlyonto the 1–100 Urgency (x) / Importance (y) range from the AC. Importance is flipped
(
cy = 100 - importance) so "more important" renders higher, matching the existing mini-card'sbottom-origin convention in
CreatePriorityTodoDialog.tsx(kept consistent rather thanreinventing the axis direction for the full-size view).
PriorityMatrixPage.tsxgained aview: 'list' | 'map'state; the previously-disabled "Karte"button now toggles it, and the map panel renders
PriorityMatrixCanvaswith one point per todocurrently visible in
filteredSorted(so the label filter chip stays live in both views, per AC).Done todos render dimmed (
opacity: 0.35vs0.9) rather than being unconditionally excluded —in practice they are usually already absent, since
PriorityMatrixPagereuses the samegetTodosOfSelectedTodoListstore selector the Standard list page uses, which defaultsfilterStatusto'Open'. The dimming only becomes visible if a user has widened that sharedfilter to
'All'/'Done'from the Standard list's own filter bar before navigating here — the ACallows either "dimmed" or "fully hidden," and this satisfies both readings without adding a
second, Priority-page-local filter control.
Dragging:
onPointerDownon a circle callssetPointerCaptureand records the dragged id;onPointerMove/onPointerUpon the<svg>compute the new axis values from the pointer'sposition relative to the SVG's bounding rect, clamped to 1..100 client-side before the drop is
reported. This clamp is not cosmetic:
TodoUrgency/TodoImportance's Vogen validators rejectout-of-range values with a 400 rather than clamping them, and the AC explicitly requires "kein 400
vom Vogen-Validator, kein Punkt außerhalb der Karte" — so an out-of-bounds drag must never reach
the wire as an out-of-bounds value. On release,
PriorityMatrixPage.handlePointDragcallsSetTodoUrgencyImportanceCommand(the same command the "Ändern" modal already uses) and appliesthe returned
TodoDtoto the store directly viaupdateTodo, rather than waiting on the WSbroadcast round-trip for the dragged point to visually settle.
Screen-reader alternative (AC's last bullet): unchanged — the classic list view's "Ändern" button
and modal from V1 remain the only editing path required, so a user who can't perform a pointer
drag still has full access to the same Urgency/Importance/label editing via that route.
Concurrent-session conflict note
This story exists because of a real merge conflict: an earlier session in parallel had already
implemented Feature
#91asTodoListType-based (this design) while a second, independent localsession simultaneously implemented an incompatible schema (
TodoListEntity.PriorityMatrixFactor-as-sole-switch, no
Typecolumn,SetTodoPriorityMatrixPositionCommand) and built the full 2Ddrag view directly into
#91rather than deferring it. Both were pushed/committed around the sametime. Rather than force-overwrite either side's tested work, the
TodoListType-based V1 (alreadyon
origin/master, already covered by its own backend+frontend+E2E tests) was kept as the singlesource of truth, and the other session's already-built 2D-drag UI was re-derived against this
schema as this story — the deferred
#91bthe V1 author had already drafted for exactly thisscope. See the git history around 2026-08-09 for the two divergent commits if reconstructing this
is ever needed; the abandoned local attempt is preserved on the
backup-91-standalone-attemptbranch, not merged.
Testing
PriorityMatrixCanvas.test.tsx(new): point rendering/positioning, dimmed-opacity rendering,drag-to-report with correct axis math, clamping on out-of-bounds drag, no-op when no
onPointDraghandler is given.PriorityMatrixPage.test.tsx: replaced the now-obsolete "disabled Karte button" test withcoverage for switching to the map view, dimmed rendering of a done todo (with the shared
filterStatuswidened to'All'first, matching the real precondition), the label filterapplying to the map, and a full drag round-trip asserting the exact
SetTodoUrgencyImportanceCommandpayload.Verification
dotnet build/dotnet test(Release): 748 tests green, no backend files touched by this story.npx tsc -b,npm run build,npx vitest run: clean; 845 frontend tests green (9 new — 6 inPriorityMatrixCanvas.test.tsx, net +3 replacing the removed placeholder test inPriorityMatrixPage.test.tsx).two todos via direct API calls (UI drawer clicks were unreliable in this sandbox's headless
browser pane — see the Team Coach memory note this cycle), switched to the map view, dragged a
point via a timed pointer-event sequence (see the Learned Pattern on synthetic-event timing),
confirmed the position updated live and persisted across a full page reload, and confirmed the
classic list view re-sorts correctly after the drag. Cleaned up the verification list afterward.