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:
+29
-5
@@ -42,8 +42,9 @@ src/components/AppSidebar.vue left nav: Dashboard link, project dropdown, Inbox
|
||||
src/components/CardRow.vue editable text + status chip + delete, one card
|
||||
src/components/KanbanCard.vue small draggable card for the board columns and the inbox
|
||||
src/components/PasskeyNotice.vue dismissible "add a passkey" banner across the top of the page
|
||||
src/views/ DashboardView, ProjectView, LoginView, ProfileView,
|
||||
VerifyEmailView
|
||||
src/components/ProjectManageMenu.vue "Manage" dropdown + delete-project modal, shared by ProjectView and ProjectConfigureView
|
||||
src/views/ DashboardView, ProjectView, ProjectConfigureView, LoginView,
|
||||
ProfileView, VerifyEmailView
|
||||
```
|
||||
|
||||
Signed-in "app" routes (`meta.requiresAuth`) render inside a persistent shell:
|
||||
@@ -82,9 +83,10 @@ under the list adds a card straight to the inbox.
|
||||
`/projects/:id` shows one project. It renders on a **full-width** layout (the
|
||||
route sets `meta.wide`, which widens `.app__main` in `App.vue`), so the header
|
||||
spans the full width and the **Manage** menu sits top right. The title is
|
||||
inline-editable (saved on blur via `PATCH /api/projects/:id`). Manage has a
|
||||
**Delete project** action that opens a confirmation modal; confirming calls
|
||||
`DELETE /api/projects/:id` and returns to the dashboard.
|
||||
inline-editable (saved on blur via `PATCH /api/projects/:id`). Manage
|
||||
(`ProjectManageMenu.vue`) has a **Configure** link (to the status-management
|
||||
view below) and a **Delete project** action that opens a confirmation modal;
|
||||
confirming calls `DELETE /api/projects/:id` and returns to the dashboard.
|
||||
|
||||
Below the header are two tabs (local `activeTab` state, `v-show` so both stay
|
||||
mounted). The tab order is fixed — **All tasks** first, **Kanban** second — but
|
||||
@@ -112,6 +114,28 @@ card and re-packs whatever column it left; afterwards the view always reloads
|
||||
both `inbox` and this project's `cards`, since either could have been the other
|
||||
side of the move.
|
||||
|
||||
## Project configuration
|
||||
|
||||
`/projects/:id/configure` (`ProjectConfigureView.vue`) manages a project's
|
||||
statuses. The header mirrors the project view's — title, `ProjectManageMenu`
|
||||
top right — plus a "← Back to project" link (Manage's own Configure link is
|
||||
hidden here, since it would just point at the current page).
|
||||
|
||||
The status list is a `vuedraggable` list (its own list, no shared drag group
|
||||
with the kanban board) bound directly to a local `statuses` ref; dragging
|
||||
mutates it in place, and `@change` persists the whole new order via
|
||||
`PUT /api/projects/:id/statuses/order`, reverting to the server's copy on
|
||||
failure. A small form below it adds a status
|
||||
(`POST /api/projects/:id/statuses`) at the end of the list.
|
||||
|
||||
Each row has a delete button. A status with no cards deletes immediately; one
|
||||
still holding cards gets `409` back from `DELETE .../statuses/:statusId` with
|
||||
`error.details.card_count` (surfaced as `ApiError#cardCount`) -- that opens a
|
||||
modal asking which other status to move its cards to, then resubmits the same
|
||||
delete with `{ reassign_to }`, which reassigns and deletes in one request. The
|
||||
last remaining status can't be deleted (a project card always needs one); its
|
||||
row's delete button is disabled once `statuses.length <= 1`.
|
||||
|
||||
## Auth flow
|
||||
|
||||
There is no password and no separate sign-up — `LoginView` is an email field
|
||||
|
||||
Reference in New Issue
Block a user