c43a28e143acfbda1a30750079fca26a9cded9e2
11 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4e657a2d4d |
Task #397: Per-card and bulk select+delete on Incoming Orders
Backend (artifacts/api-server):
- Added hasReceivePermission() and refactored DELETE /orders/:id into a
shared authorizeAndDeleteOrder() helper so single and bulk paths use
the same authorization rules.
- Added POST /orders/bulk-delete returning { deletedIds, failedIds } with
per-id authorization, dedup, and best-effort partial success.
- Tightened receiver authorization to mirror GET /orders/incoming: a
receiver may only delete an unclaimed pending order or one they have
themselves claimed and is still active (received/preparing). Another
receiver's claimed order, and any terminal order, return 403 even at
the API layer. Code, comments and OpenAPI description now agree.
API spec (lib/api-spec/openapi.yaml):
- New /orders/bulk-delete operation with BulkDeleteServiceOrdersBody and
BulkDeleteServiceOrdersResponse schemas. Updated DELETE /orders/{id}
description. Regenerated react-query hooks via codegen — both
useDeleteServiceOrder and useBulkDeleteServiceOrders are used by the UI.
UI (artifacts/tx-os/src/pages/orders-incoming.tsx):
- Per-card checkbox visible at all times (RTL-safe leading edge).
- Per-card trash icon for single delete.
- Section-scoped Select-all (مين / unclaimed) with tri-state
all/some(indeterminate)/none and shadcn Checkbox.
- Sticky bottom bulk action bar shows selected count + Clear selection +
destructive Delete.
- Single AlertDialog used by both per-card and bulk paths, with
pluralized confirmation copy. Single delete calls DELETE /orders/:id;
multi delete calls POST /orders/bulk-delete; partial-failure surfaces
via toast.
i18n: New incomingOrders keys in ar.json and en.json (select, selectAll,
clearSelection, selectedCount, delete, deleteConfirmTitle/Body, deleted,
deleteFailed, deletePartial) with pluralization in both languages.
Tests:
- artifacts/api-server/tests/service-orders.test.mjs: receivers can
delete pending/received/preparing; receivers cannot delete terminal
(completed/cancelled); bulk-delete with mixed authorized / unknown /
dedup / unauthenticated cases.
- artifacts/tx-os/tests/order-incoming-delete.spec.mjs (new Playwright):
bulk-delete two unclaimed orders shrinks the list by 2; per-card
trash on a claimed order deletes one. Both pass.
Pre-existing executive-meetings PDF/font test failures are unrelated.
Follow-ups proposed: #398 (Undo for incoming-order deletes), #399
(safer notification handling in bulk-delete).
|
||
|
|
d62e67af96 |
Task #397: Per-card and bulk select+delete on Incoming Orders
Backend (artifacts/api-server):
- Added hasReceivePermission() helper and refactored DELETE /orders/:id
into shared authorizeAndDeleteOrder() so single and bulk paths use the
same authorization rules.
- Added POST /orders/bulk-delete returning { deletedIds, failedIds } with
per-id authorization, dedup, and best-effort partial success.
- Tightened receiver authorization (per code review): receivers may only
delete pending/received/preparing orders; terminal (completed/cancelled)
orders remain the owner's cleanup responsibility. OpenAPI description
and the implementation now agree.
API spec (lib/api-spec/openapi.yaml):
- New /orders/bulk-delete operation with BulkDeleteServiceOrdersBody and
BulkDeleteServiceOrdersResponse schemas; updated DELETE /orders/{id}
description. Regenerated react-query hooks via codegen — useBulkDelete
ServiceOrders is now available.
UI (artifacts/tx-os/src/pages/orders-incoming.tsx):
- Selection mode toggle in the page header (RTL-safe).
- Per-card Checkbox plus a Select-all control.
- Sticky bottom action bar with destructive Delete and selected count.
- AlertDialog confirmation using pluralized i18n keys; toast surfaces
partial-failure results when some ids couldn't be deleted.
i18n: New incomingOrders keys in ar.json and en.json (select, selectAll,
clearSelection, selectedCount, delete, deleteConfirmTitle/Body, deleted,
deleteFailed, deletePartial) with pluralization.
Tests: Added two new tests in service-orders.test.mjs covering receiver
delete on incoming queue, receiver forbidden on terminal orders, and
bulk-delete with mixed authorized / unknown / dedup / unauthenticated
cases. Both pass. Pre-existing executive-meetings PDF/font test failures
are unrelated.
|
||
|
|
b865e69f64 |
Add test coverage for the order-unavailable error responses (Task #86)
Task #80 introduced a new 409 `order_unavailable` error code returned by: - PATCH /api/orders/:id/confirm-receipt - PATCH /api/orders/:id/status …to distinguish "already_claimed" (lost a race) from "no longer claimable" (target order is cancelled or completed). The integration test suite did not cover these paths. Changes ------- artifacts/api-server/tests/service-orders.test.mjs + Added test "confirm-receipt returns 409 order_unavailable when the order is cancelled or completed": - Owner cancels a pending order → another receiver's confirm-receipt returns 409 with body.error === "order_unavailable" (not "already_claimed"). - Receiver claims, prepares, completes an order → another receiver's confirm-receipt returns 409 "order_unavailable". + Added test "PATCH /orders/:id/status returns 409 order_unavailable when targeting a terminal order": - Completed order: assignee → preparing → 409 "order_unavailable" - Completed order: assignee → completed → 409 "order_unavailable" - Completed order: owner → cancelled → 409 "order_unavailable" - Cancelled order: owner → cancelled → 409 "order_unavailable" Verification ------------ All 11 service-orders integration tests pass (including both new tests): pnpm --filter @workspace/api-server test → 11 passed, 0 failed. No production code changes; tests only. Replit-Task-Id: 229ed991-1c32-4281-8e57-00abeacd80b4 |
||
|
|
c05ae786fc |
Fix order cancellation logic and update tests to reflect new behavior
Adjusts the order cancellation endpoint to correctly handle admin overrides for completed/cancelled orders and updates associated tests to expect 409 status codes reflecting Task #80's changes. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: d358e7e1-8a16-4746-86aa-4f1739233f2e Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/3E2mNig Replit-Helium-Checkpoint-Created: true |
||
|
|
5de821fac8 |
Add Undo window for cancelled orders on My Orders
Original task (#72): Mirror the recently-added delete Undo toast for the cancel action so accidental cancellations can be reverted within ~7s. Changes - Frontend (artifacts/teaboy-os/src/pages/my-orders.tsx): OrderCard.handleCancel captures the order's previous status, and on successful cancel shows a toast with an "Undo" action (duration matches the existing UNDO_WINDOW_MS = 7000ms). Clicking Undo issues a PATCH to restore the order to its previous status (pending or received) and emits a "Restored" toast. Errors surface a "Could not restore the order" toast. - Cancel confirmation copy was softened (no longer says "can't be reopened") and a new restoreFailed string was added in both en.json and ar.json. - Backend (artifacts/api-server/src/routes/service-orders.ts): PATCH /orders/:id/status accepts pending and received as targets for the owner (or admin) when the existing status is cancelled, serving as the restore-from-cancelled transition. We require assignedTo to be null for pending and non-null for received so we always restore to the actually-prior state. The existing logic already broadcasts order_incoming_changed to receivers and emits order_updated to the owner, so receivers' incoming queues refresh automatically when an order is restored. notifyUser titleMap was extended with "Your order was restored" entries. - Time-bound restore: server enforces a RESTORE_WINDOW_MS of 15s (slightly larger than the 7s client window to absorb network/clock skew). After it expires, restore is rejected with `undo_window_expired`, so cancellation truly becomes permanent — this applies to admin too, so restore is strictly Undo and not an arbitrary reopen. - API spec (lib/api-spec/openapi.yaml): UpdateServiceOrderStatusBody enum extended to include pending and received; endpoint summary updated. Regenerated api-zod and api-client-react. Tests - Added a new test case in artifacts/api-server/tests/service-orders.test.mjs that covers: restore from pending, restore from received, pending<->received validation against assignedTo, stranger forbidden, and undo window expiry (by backdating updated_at). Verification - pnpm typecheck passes across libs and artifacts. - New api-server test "owner can restore (undo) a freshly cancelled order..." passes; all pre-existing service-order tests still pass. - e2e tested: login -> place order -> cancel -> Undo restores to Pending; cancel again -> let window expire -> order stays Cancelled with no Undo available. Notes / unrelated - The unrelated `admin-app-opens-pagination` test (`by-app: paginating with limit returns nextOffset until exhausted`) fails in the dev environment because the dev DB has > 100 app_opens rows for the chosen app and the test only walks up to 50 pages of size 2. This is a pre-existing flaky test unrelated to this task and was not modified. Replit-Task-Id: beca78bc-32f3-4cdc-8440-9a661b48363b |
||
|
|
1cea719b80 |
Task #69: Delete past orders from My Orders
- Add DELETE /api/orders/:id endpoint: owner can delete completed/cancelled orders; admin can delete any. 401/403/404/204 responses. Emits order_deleted to owner and assignee for live invalidation. - Update OpenAPI spec with deleteServiceOrder operation; regenerate api-client-react + api-zod. - Frontend: per-order trash button on completed/cancelled cards with confirm dialog; bulk "Clear finished (N)" button at top of list with confirm dialog and toast summary (handles partial failures). - Add ar/en locale keys under myOrders for delete + clear flows (singular/plural variants). - Backend tests cover all paths: pending owner=403, stranger=403, owner-cancelled=204, owner-completed=204, admin-pending=204, 404, 401. All 25 service-order tests pass; the 1 failing test (admin-app-opens- pagination) is unrelated/pre-existing. Follow-ups proposed: #70 undo for deleted orders, #71 audit self-notification on receiver-cancel-own-order. |
||
|
|
58553d3d36 |
Task #69: Delete past orders from My Orders
- Add DELETE /api/orders/:id endpoint: owner can delete completed/cancelled orders; admin can delete any. 401/403/404/204 responses. Emits order_deleted to owner and assignee for live invalidation. - Update OpenAPI spec with deleteServiceOrder operation; regenerate api-client-react + api-zod. - Frontend: per-order trash button on completed/cancelled cards with confirm dialog; bulk "Clear finished (N)" button at top of list with confirm dialog and toast summary (handles partial failures). - Add ar/en locale keys under myOrders for delete + clear flows (singular/plural variants). - Backend tests cover all paths: pending owner=403, stranger=403, owner-cancelled=204, owner-completed=204, admin-pending=204, 404, 401. All 25 service-order tests pass; the 1 failing test (admin-app-opens- pagination) is unrelated/pre-existing. Follow-ups proposed: #70 undo for deleted orders, #71 audit self-notification on receiver-cancel-own-order. |
||
|
|
6c4c5600de |
Notify requester distinctly when receiver cancels their claimed order (Task #67)
Context - After Task #64 receivers can cancel an order they have already claimed. - Previously the requester's cancellation notification body was the same regardless of who cancelled (admin vs receiver), giving them no signal that their order had been claimed and then dropped. Changes - artifacts/api-server/src/routes/service-orders.ts (PATCH /orders/:id/status, cancel branch): - When the cancellation is initiated by the order's currently assigned receiver (existing.assignedTo === userId && next === "cancelled"), the requester's notification body now includes the bilingual phrase "استلم طلبك ولكن تم إلغاؤه لاحقاً" / "Your order was claimed but later cancelled by the receiver", appended to the service name for context. - Admin / non-assignee cancellations keep the existing service-name body so the two cases remain distinguishable in the requester's notifications list. - The notification is created via the existing notifyUser() helper, which persists to notifications and emits notification_created over Socket.IO, so the bell badge updates in real time. - artifacts/api-server/tests/service-orders.test.mjs: - Added "requester gets a distinct notification when the receiver cancels a claimed order" covering the full flow: place → confirm-receipt → cancel, then asserts the owner has a cancellation notification whose body matches the new bilingual receiver-cancel copy. Verification - All 7 tests in service-orders.test.mjs pass locally against the running API. Notes / deviations - Locale JSON files (ar.json/en.json) were inspected but not modified: notification copy is stored server-side as titleAr/titleEn/bodyAr/bodyEn on the notifications row; the client just renders those strings directly. Replit-Task-Id: 5aa480a9-c6e0-4a06-8919-b2b6784d8a98 |
||
|
|
7ebf59d4b7 |
Task #64: Service Orders — receiver page + admin role toggle
Backend (artifacts/api-server)
- Add admin-only role-toggle endpoints:
- POST /users/:id/roles { roleName } — idempotent, returns UserProfile
- DELETE /users/:id/roles/:roleName — idempotent, returns UserProfile
- Allow assigned receiver to cancel their own claimed order
(received/preparing) in PATCH /orders/:id/status. Owner & admin
rules unchanged.
- New backend test cases: receiver can cancel own assigned order;
another receiver gets 403.
API spec / codegen
- Add AddUserRoleBody schema and the two new role endpoints
under a new "roles" tag in lib/api-spec/openapi.yaml.
- Regenerated api-zod and api-client-react.
Frontend (artifacts/teaboy-os)
- New page src/pages/orders-incoming.tsx at /orders/incoming:
RBAC-gated (admin || order_receiver), shows "My active orders"
and "Awaiting receiver" sections, with claim, mark preparing,
mark completed and cancel buttons. Handles 409 already_claimed.
- Add Inbox button to home top bar, conditional on the same roles.
- Admin users table now has an "Order Receiver" Switch wired to
the new role-toggle hooks.
- Extend use-notifications-socket to invalidate the incoming-orders
query on order_incoming_changed and on notification_created with
type === "order".
- Bilingual locale keys (ar/en) for the new page and admin label.
Tests
- All 25 api-server tests pass (24 existing + 1 new receiver-cancel
case). All 3 teaboy-os e2e tests pass.
Follow-up filed: #67 (notify requester when receiver cancels).
|
||
|
|
2602edaca0 |
Task #62: Service Orders backend foundation (corrected)
After prior code review rejection, refactored to match spec exactly: - Permission renamed orders:receive → orders.receive (dot form), seeded with order_receiver role - service_orders table: user_id, service_id, notes, status (pending/received/preparing/completed/cancelled with CHECK), assigned_to, created_at, updated_at - /confirm-receipt is now the receiver atomic claim (UPDATE WHERE pending+unassigned), 409 already_claimed on miss - /status accepts preparing|completed|cancelled with permission matrix: * preparing/completed: assigned receiver or admin * cancelled: owner (pending|received) OR admin (any non-cancelled status) - requirePermission middleware no longer auto-bypasses admin; admin gets the permission via explicit seed grant - Notifications use type='order', relatedType='order' - Realtime emits notification_created (per receiver) + order_incoming_changed (broadcast) + order_updated (owner) - OpenAPI Order schema rewritten (no quantity, no per-status timestamps), endpoint summaries updated, codegen run - Tests cover: client place+list, non-receiver 403, no-role 403, parallel claim race (200/409), status matrix, owner-cancel rules, admin-cancel-completed, descriptions excluded from service summary All 24 api-server tests pass. Ready for code review re-check. |
||
|
|
cdf5bf4d33 |
Service Orders — backend foundation (Task #62)
- New `service_orders` table (status pending|claimed|delivered|received|cancelled) - New `orders:receive` permission + `order_receiver` role; admins implicitly allowed - Added `requirePermission(name)` and `userHasPermission(userId, name)` middleware helpers - New routes: - POST /api/orders place order (authenticated) - GET /api/orders/my list current user's orders - GET /api/orders/incoming receivers see pending+active visible orders - PATCH /api/orders/:id/status claim (atomic), deliver, cancel - PATCH /api/orders/:id/confirm-receipt requester confirms a delivered order - Atomic claim via UPDATE ... WHERE status='pending' AND assigned_to IS NULL (returns 409 already_claimed on race) - Realtime: emits `notification_created` to receivers/requester, `order_incoming_changed` to all receivers, `order_updated` to requester - Service shape in order responses limited to id/nameAr/nameEn/imageUrl (no description fields), per spec - OpenAPI updated with new paths and schemas; codegen run - Seed updated idempotently (permission, role, role_permissions) - New tests in artifacts/api-server/tests/service-orders.test.mjs (full lifecycle, atomic claim race, unauth rejection) — all 21 api tests pass No deviations from the planned scope. Tasks #63 (client UI) and #64 (receiver page + admin role toggle) remain blocked-by #62 and are next. |