Add stage 5: list detail page with items UI and drag reorder
API: new PUT /api/lists/{id}/items/order takes the full ordered id set and
rewrites positions 0..n-1 in a transaction (422 unless the set matches the
list exactly). TodoItemRepository gains idsForList() and reorder().
Frontend: lists on the home page are now links to /lists/:id (ListView).
ListView shows the list title, a "M of N done" summary, and each item as a
drag handle + checkbox + inline-editable text (saved on blur) + delete
button, with a create-item form at the bottom. Drag-and-drop uses
vuedraggable; on drop the whole order is persisted via the new endpoint and
the response replaces local state, with a resync-on-error fallback. New
items store; items store is also reset on logout.
Tests: reorder happy path, incomplete-set rejection, owner scoping. Backend
suite: 23 passing.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,7 @@ SQLite file, plus a Vue 3 + TypeScript PWA frontend in [web/](web/).
|
||||
| 2 | Frontend shell — Vite PWA, auth-gated routing, register/login pages | ✅ done |
|
||||
| 3 | Todo list + item CRUD API | ✅ done |
|
||||
| 4 | Frontend lists view — list index + create form | ✅ done |
|
||||
| 5 | Frontend list detail — items UI | planned |
|
||||
| 5 | Frontend list detail — items UI with drag-and-drop reorder | ✅ done |
|
||||
|
||||
Registration signs the user in immediately, with the account's email marked
|
||||
unverified (`user.email_verified` is `false` until a future stage adds a
|
||||
@@ -221,6 +221,7 @@ Scoped to a list; the parent list's ownership is checked first (`404` otherwise)
|
||||
|--------|------|---------|
|
||||
| `GET` | `/api/lists/{id}/items` | items, ordered by `position` then `id` |
|
||||
| `POST` | `/api/lists/{id}/items` | add an item |
|
||||
| `PUT` | `/api/lists/{id}/items/order` | reorder all items in one shot |
|
||||
| `GET` | `/api/lists/{id}/items/{itemId}` | one item |
|
||||
| `PATCH` | `/api/lists/{id}/items/{itemId}` | update `text`, `complete`, and/or `position` |
|
||||
| `DELETE` | `/api/lists/{id}/items/{itemId}` | delete the item (`204`) |
|
||||
@@ -231,6 +232,11 @@ appended after the current highest position). `PATCH` needs at least one field.
|
||||
`position` is a plain sort key the client manages — updating one item never
|
||||
renumbers its siblings.
|
||||
|
||||
`PUT …/items/order` takes `{ "item_ids": [3, 1, 2] }` — every item in the list,
|
||||
each exactly once (`422` otherwise). It rewrites positions to `0..n-1` in one
|
||||
transaction and returns `{ "items": [ … ] }` in the new order. This is what the
|
||||
drag-and-drop reorder in the UI calls.
|
||||
|
||||
Item representation:
|
||||
|
||||
```json
|
||||
|
||||
Reference in New Issue
Block a user