fix(work-orders): show fixed copy for edit conflict toasts (SH-375)

This commit is contained in:
Codex Review Integration 2026-09-16 21:22:07 -03:00
parent afc98a9daa
commit 2d54ff12f8
3 changed files with 33 additions and 1 deletions

View file

@ -44,6 +44,9 @@ export function boardPatchValidationMessage(error: BoardPatchValidationError): s
return KNOWN_VALIDATION_COPY.get(error.code) ?? BOARD_PATCH_FAILED_MESSAGE;
}
export const BOARD_PATCH_CONFLICT_MESSAGE =
"This work order was changed by someone else. Refresh to see the latest before saving again.";
export const PARTIAL_SAVE_PREFIX = "Some changes were saved, but the rest were not.";
/** Statuses whose mapped copy is fixed product text, never the response body. */
@ -51,6 +54,9 @@ const FIXED_COPY_STATUSES = new Set([401, 403, 404, 500]);
/** why: any board edit failure toast must use product copy, never backend text or field keys. */
export function boardPatchErrorMessage(error: unknown): string {
if (error instanceof BoardPatchConflictError) {
return BOARD_PATCH_CONFLICT_MESSAGE;
}
if (error instanceof BoardPatchValidationError) {
return boardPatchValidationMessage(error);
}

View file

@ -156,7 +156,7 @@ export function useWorkOrderTableMutations(
if (error instanceof BoardPatchConflictError && error.currentState) {
onPatch(id, error.currentState);
toast.error(error.message || "Conflict — refreshed with latest data");
toast.error(boardPatchErrorMessage(error));
} else {
clearPatch(id);
toast.error(boardPatchErrorMessage(error));

View file

@ -4,6 +4,7 @@ import type { ReactNode } from "react";
import { toast } from "react-toastify";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
BOARD_PATCH_CONFLICT_MESSAGE,
BOARD_PATCH_FAILED_MESSAGE,
DISPATCH_REQUIRED_MESSAGE,
PARTIAL_SAVE_PREFIX,
@ -142,4 +143,29 @@ describe("useWorkOrderTableMutations edit error copy", () => {
expect(message).toBe(`${PARTIAL_SAVE_PREFIX} ${DISPATCH_REQUIRED_MESSAGE}`);
});
it("shows fixed conflict copy and still applies the latest row on a 409", async () => {
const onPatch = vi.fn();
respondWith({
status: 409,
body: {
message: "RowVersion mismatch for workOrderId 7 (expected v1, found v3).",
currentState: { id: 7, workOrderNumber: "WO-099", rowVersion: "v3" },
},
});
const message = await patchAndGetToast({ woNumber: "WO-100" }, 1, onPatch);
expect(message).toBe(BOARD_PATCH_CONFLICT_MESSAGE);
expect(message).not.toMatch(/RowVersion|workOrderId|v3/);
expect(onPatch).toHaveBeenLastCalledWith(7, expect.objectContaining({ rowVersion: "v3" }));
});
it("shows fixed conflict copy when a 409 carries no latest row", async () => {
respondWith({ status: 409, body: { message: "RowVersion mismatch for workOrderId 7." } });
const message = await patchAndGetToast();
expect(message).toBe(BOARD_PATCH_CONFLICT_MESSAGE);
});
});