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
This commit is contained in:
@@ -391,6 +391,8 @@ export type UpdateServiceOrderStatusBodyStatus =
|
||||
(typeof UpdateServiceOrderStatusBodyStatus)[keyof typeof UpdateServiceOrderStatusBodyStatus];
|
||||
|
||||
export const UpdateServiceOrderStatusBodyStatus = {
|
||||
pending: "pending",
|
||||
received: "received",
|
||||
preparing: "preparing",
|
||||
completed: "completed",
|
||||
cancelled: "cancelled",
|
||||
|
||||
@@ -2546,7 +2546,7 @@ export function useListIncomingServiceOrders<
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Update order status to preparing, completed, or cancelled. Preparing/completed restricted to assigned receiver or admin; cancelled allowed for owner (while pending|received) or admin (any time).
|
||||
* @summary Update order status. Preparing/completed restricted to assigned receiver or admin; cancelled allowed for owner (while pending|received) or admin (any time); pending/received are restore-from-cancelled transitions allowed for owner or admin.
|
||||
*/
|
||||
export const getUpdateServiceOrderStatusUrl = (id: number) => {
|
||||
return `/api/orders/${id}/status`;
|
||||
@@ -2611,7 +2611,7 @@ export type UpdateServiceOrderStatusMutationBody =
|
||||
export type UpdateServiceOrderStatusMutationError = ErrorType<ErrorResponse>;
|
||||
|
||||
/**
|
||||
* @summary Update order status to preparing, completed, or cancelled. Preparing/completed restricted to assigned receiver or admin; cancelled allowed for owner (while pending|received) or admin (any time).
|
||||
* @summary Update order status. Preparing/completed restricted to assigned receiver or admin; cancelled allowed for owner (while pending|received) or admin (any time); pending/received are restore-from-cancelled transitions allowed for owner or admin.
|
||||
*/
|
||||
export const useUpdateServiceOrderStatus = <
|
||||
TError = ErrorType<ErrorResponse>,
|
||||
|
||||
@@ -657,7 +657,7 @@ paths:
|
||||
patch:
|
||||
operationId: updateServiceOrderStatus
|
||||
tags: [orders]
|
||||
summary: Update order status to preparing, completed, or cancelled. Preparing/completed restricted to assigned receiver or admin; cancelled allowed for owner (while pending|received) or admin (any time).
|
||||
summary: Update order status. Preparing/completed restricted to assigned receiver or admin; cancelled allowed for owner (while pending|received) or admin (any time); pending/received are restore-from-cancelled transitions allowed for owner or admin.
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
@@ -2261,7 +2261,7 @@ components:
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
enum: [preparing, completed, cancelled]
|
||||
enum: [pending, received, preparing, completed, cancelled]
|
||||
required:
|
||||
- status
|
||||
|
||||
|
||||
@@ -708,14 +708,20 @@ export const ListIncomingServiceOrdersResponse = zod.array(
|
||||
);
|
||||
|
||||
/**
|
||||
* @summary Update order status to preparing, completed, or cancelled. Preparing/completed restricted to assigned receiver or admin; cancelled allowed for owner (while pending|received) or admin (any time).
|
||||
* @summary Update order status. Preparing/completed restricted to assigned receiver or admin; cancelled allowed for owner (while pending|received) or admin (any time); pending/received are restore-from-cancelled transitions allowed for owner or admin.
|
||||
*/
|
||||
export const UpdateServiceOrderStatusParams = zod.object({
|
||||
id: zod.coerce.number(),
|
||||
});
|
||||
|
||||
export const UpdateServiceOrderStatusBody = zod.object({
|
||||
status: zod.enum(["preparing", "completed", "cancelled"]),
|
||||
status: zod.enum([
|
||||
"pending",
|
||||
"received",
|
||||
"preparing",
|
||||
"completed",
|
||||
"cancelled",
|
||||
]),
|
||||
});
|
||||
|
||||
export const UpdateServiceOrderStatusResponse = zod.object({
|
||||
|
||||
Reference in New Issue
Block a user