feat(approvals): My Approvals page with inline approve/reject (PMS-481) #329

Merged
YousifShkara merged 1 commit from feat/PMS-481-approvals-page into main 2026-06-24 10:01:59 +02:00
Owner

PMS-451 shipped the ticket-approvals server surface; PMS-470 widened it to polymorphic approvals across tickets / time_entries / change_requests / quotes. The SPA had no consumer for /approvals/pending - every approver had to read the column directly. PMS-481 lands the highest-leverage slice: a new /approvals page that lists every pending decision the signed-in user can decide and accepts approve / reject inline.

What landed:

  • New pages/approvals.rs::ApprovalsPage, registered at the /approvals route. Fetches GET /api/v1/approvals/pending via use_resource with a version signal that bumps after each successful decision so the queue refreshes without a page reload.

  • Each row carries a per-target badge (Ticket / Time entry / Change request / Quote, with unknown targets falling through to the raw string so a future server-side surface still renders), a clickable entity chip that links to the parent (only for tickets today - non-ticket entity routes don't exist client-side yet, so those render the id verbatim), the requester name, the approver scope ("To: name" or "Role: role"), the requested-at timestamp, and the optional notes.

  • Approve / Reject buttons POST { "decision": "approve" | "reject" } to /api/v1/approvals/{id}/decision. On success a toast confirms the action and the queue refetches; on failure the inline error chip surfaces the message without dropping the rest of the queue.

  • The DTO tolerates both the PMS-451-phase-1 ticket-only payload and the PMS-470 polymorphic shape: target defaults to "ticket", entity_id falls back to ticket_id when absent, so a tenant running an older server still gets a functional page.

Follow-up filed as PMS-486 for the ticket-detail "Approvals" tab + "Request approval" modal + top-bar pending-decisions badge. Each of those touches existing high-traffic surfaces (the ticket-detail tabbed section + the AppLayout chrome) so they land separately; the standalone /approvals page is the surface every other approval flow eventually links to.

#PMS-481

PMS-451 shipped the ticket-approvals server surface; PMS-470 widened it to polymorphic approvals across tickets / time_entries / change_requests / quotes. The SPA had no consumer for `/approvals/pending` - every approver had to read the column directly. PMS-481 lands the highest-leverage slice: a new `/approvals` page that lists every pending decision the signed-in user can decide and accepts approve / reject inline. What landed: * New `pages/approvals.rs::ApprovalsPage`, registered at the `/approvals` route. Fetches `GET /api/v1/approvals/pending` via `use_resource` with a `version` signal that bumps after each successful decision so the queue refreshes without a page reload. * Each row carries a per-target badge (`Ticket` / `Time entry` / `Change request` / `Quote`, with unknown targets falling through to the raw string so a future server-side surface still renders), a clickable entity chip that links to the parent (only for tickets today - non-ticket entity routes don't exist client-side yet, so those render the id verbatim), the requester name, the approver scope ("To: name" or "Role: role"), the requested-at timestamp, and the optional notes. * Approve / Reject buttons POST `{ "decision": "approve" | "reject" }` to `/api/v1/approvals/{id}/decision`. On success a toast confirms the action and the queue refetches; on failure the inline error chip surfaces the message without dropping the rest of the queue. * The DTO tolerates both the PMS-451-phase-1 ticket-only payload and the PMS-470 polymorphic shape: `target` defaults to `"ticket"`, `entity_id` falls back to `ticket_id` when absent, so a tenant running an older server still gets a functional page. Follow-up filed as PMS-486 for the ticket-detail "Approvals" tab + "Request approval" modal + top-bar pending-decisions badge. Each of those touches existing high-traffic surfaces (the ticket-detail tabbed section + the AppLayout chrome) so they land separately; the standalone `/approvals` page is the surface every other approval flow eventually links to. #PMS-481
feat(approvals): My Approvals page with inline approve/reject (PMS-481)
All checks were successful
Check / fmt + clippy + tests (pull_request) Successful in 28m17s
Create release / Create release from merged PR (pull_request) Has been skipped
58f17edd31
PMS-451 shipped the ticket-approvals server surface; PMS-470 widened it to polymorphic approvals across tickets / time_entries / change_requests / quotes. The SPA had no consumer for `/approvals/pending` - every approver had to read the column directly. PMS-481 lands the highest-leverage slice: a new `/approvals` page that lists every pending decision the signed-in user can decide and accepts approve / reject inline.

What landed:

* New `pages/approvals.rs::ApprovalsPage`, registered at the `/approvals` route. Fetches `GET /api/v1/approvals/pending` via `use_resource` with a `version` signal that bumps after each successful decision so the queue refreshes without a page reload.

* Each row carries a per-target badge (`Ticket` / `Time entry` / `Change request` / `Quote`, with unknown targets falling through to the raw string so a future server-side surface still renders), a clickable entity chip that links to the parent (only for tickets today - non-ticket entity routes don't exist client-side yet, so those render the id verbatim), the requester name, the approver scope ("To: name" or "Role: role"), the requested-at timestamp, and the optional notes.

* Approve / Reject buttons POST `{ "decision": "approve" | "reject" }` to `/api/v1/approvals/{id}/decision`. On success a toast confirms the action and the queue refetches; on failure the inline error chip surfaces the message without dropping the rest of the queue.

* The DTO tolerates both the PMS-451-phase-1 ticket-only payload and the PMS-470 polymorphic shape: `target` defaults to `"ticket"`, `entity_id` falls back to `ticket_id` when absent, so a tenant running an older server still gets a functional page.

Follow-up filed as PMS-486 for the ticket-detail "Approvals" tab + "Request approval" modal + top-bar pending-decisions badge. Each of those touches existing high-traffic surfaces (the ticket-detail tabbed section + the AppLayout chrome) so they land separately; the standalone `/approvals` page is the surface every other approval flow eventually links to.

#PMS-481
YousifShkara deleted branch feat/PMS-481-approvals-page 2026-06-24 10:01:59 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/mokosh-apps!329
No description provided.