Project configuration view: manage a project's statuses
New /projects/:id/configure view, linked from a new 'Configure' item on
the project view's Manage menu.
Backend:
- CardStatusRepository/CardStatusController gain full CRUD: create
(appended at the end), reorder (dense positions, like card
ordering), and delete.
- Deleting a status with cards attached is rejected with 409 and
error.details.card_count, rather than hitting the existing FK
RESTRICT constraint -- retrying with { reassign_to: <status id> }
moves those cards to that status first (CardRepository::
reassignStatus, appended after the destination's existing cards)
and deletes in one transaction (CardStatusRepository::transaction,
shared PDO connection across repositories).
- The last status in a project can't be deleted, since a project card
is required to have one.
- Routes: POST/DELETE .../statuses(/:id), PUT .../statuses/order.
- 14 new CardStatusTest cases covering all of the above.
Frontend:
- ProjectConfigureView.vue: header (title, back-to-project link, the
shared Manage menu) + a vuedraggable status list (reorder persists
the whole new order) with a delete button per row and an add-status
form. A row's plain delete either succeeds immediately or, on 409,
opens a modal to choose a different status before retrying the
delete with reassign_to.
- Extracted ProjectManageMenu.vue (the Manage dropdown + delete-project
modal) out of ProjectView so both views share it; it now also has a
Configure link (hidden on the configure page itself).
- ApiError gains a cardCount getter (details.card_count), mirroring
the existing retryAfter getter.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,7 @@ Each user owns **projects**, and each project holds ordered **cards**.
|
||||
| 13 | Global inbox — cards can have no project; moved into the sidebar, drag in/out of any project's kanban columns | ✅ done |
|
||||
| 14 | New-project form moved to the dashboard; sidebar project list is now a switcher dropdown; Kanban is a project's default tab | ✅ done |
|
||||
| 15 | Passkeys (WebAuthn) — register from the profile page, sign in with one instead of a magic link; a dismissible notice nudges users with none | ✅ done |
|
||||
| 16 | Project configuration view — manage a project's statuses: add, drag to reorder, delete (reassigning any cards on it first) | ✅ done |
|
||||
|
||||
There is no password. Signing in is entering an email address and opening the
|
||||
magic link sent to it — the same step creates the account the first time. See
|
||||
@@ -379,13 +380,16 @@ returns `{ "cards": [ … ] }`.
|
||||
|
||||
Every project has an ordered set of card statuses, created with the project:
|
||||
"To do", "Doing", "Done". They are project-specific — each project owns its own
|
||||
rows. There is no create/update/delete for the statuses themselves yet, and (as
|
||||
a project card must always have one) a referenced status can't be deleted at
|
||||
the database level either.
|
||||
rows, managed from the project's **configuration** view (create, reorder,
|
||||
delete). A project always keeps at least one status, since a project card must
|
||||
have one; deleting the last one is rejected (`409`).
|
||||
|
||||
| Method | Path | Purpose |
|
||||
|--------|------|---------|
|
||||
| `GET` | `/api/projects/{id}/statuses` | the project's statuses, ordered by `position` |
|
||||
| `POST` | `/api/projects/{id}/statuses` | add one at the end — `{ "name": "Blocked" }` |
|
||||
| `PUT` | `/api/projects/{id}/statuses/order` | reorder — `{ "status_ids": [3, 1, 2] }`, every status once |
|
||||
| `DELETE` | `/api/projects/{id}/statuses/{statusId}` | delete (see below) |
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -400,6 +404,13 @@ the database level either.
|
||||
Requires `Authorization: Bearer <jwt>`; a project that is missing or not owned by
|
||||
the caller responds `404`.
|
||||
|
||||
**Deleting a status that still has cards** fails with `409` and
|
||||
`error.details.card_count` set, rather than silently orphaning them (a
|
||||
referenced status can't be deleted at the database level either — the FK is
|
||||
`ON DELETE RESTRICT`). Retry with `{ "reassign_to": <another status id> }` in
|
||||
the same project; those cards are moved there and the status deleted, in one
|
||||
transaction.
|
||||
|
||||
### Error shape
|
||||
|
||||
Every error response looks like:
|
||||
|
||||
Reference in New Issue
Block a user