diff --git a/src/app/(protected)/workorders/_hooks/use-comp-doc-dialog.ts b/src/app/(protected)/workorders/_hooks/use-comp-doc-dialog.ts index 83cf6e69..fd7d139c 100644 --- a/src/app/(protected)/workorders/_hooks/use-comp-doc-dialog.ts +++ b/src/app/(protected)/workorders/_hooks/use-comp-doc-dialog.ts @@ -137,6 +137,14 @@ export function useCompDocDialog({ void persist({}, "No"); } + function refreshAuthoritativeState() { + void queryClient.invalidateQueries({ queryKey: queryKeys.workOrders.all }); + if (locId && !isMockLocation) { + void queryClient.invalidateQueries({ queryKey: queryKeys.locations.detail(locId) }); + void queryClient.invalidateQueries({ queryKey: queryKeys.locations.all }); + } + } + async function handleSaveDraft() { try { await syncCompletedDateIfChanged(); @@ -144,6 +152,7 @@ export function useCompDocDialog({ await persist({}, "No"); showCompDocDraftSavedToast(); } catch (error) { + refreshAuthoritativeState(); toast.error(error instanceof Error ? error.message : "Failed to save draft"); } } @@ -158,6 +167,8 @@ export function useCompDocDialog({ setGeneratedAt(ts); setDocState("generated"); } catch (error) { + // Keep Draft UI; refresh so any partially persisted date/address is authoritative. + refreshAuthoritativeState(); toast.error(error instanceof Error ? error.message : "Failed to generate document"); } } diff --git a/src/domain/work-orders/mappers/work-order-mapper.ts b/src/domain/work-orders/mappers/work-order-mapper.ts index ba3a3691..941494c2 100644 --- a/src/domain/work-orders/mappers/work-order-mapper.ts +++ b/src/domain/work-orders/mappers/work-order-mapper.ts @@ -405,17 +405,6 @@ export function mapWorkOrderFormToUpdateFormData( return formData; } -/** Partial EditWorkorder payload — only Id + CompletedDate (avoids legacy GetWorkorderById). */ -export function mapCompletedDateUpdateFormData( - id: string | number, - completedDate: string, -): FormData { - const formData = new FormData(); - formData.append("Id", String(id)); - appendIfPresent(formData, "CompletedDate", completedDate); - return formData; -} - export function mapLocationOption(raw: unknown): LocationOption { const item = asRecord(raw); const name = readString(item, "name", "Name", "locationName", "LocationName"); diff --git a/src/test/app/(protected)/workorders/comp-doc-dialog-completion-contract.test.tsx b/src/test/app/(protected)/workorders/comp-doc-dialog-completion-contract.test.tsx index c99e6d3a..5ee9134a 100644 --- a/src/test/app/(protected)/workorders/comp-doc-dialog-completion-contract.test.tsx +++ b/src/test/app/(protected)/workorders/comp-doc-dialog-completion-contract.test.tsx @@ -148,11 +148,12 @@ describe("CompDocDialog Save Draft / Generate completion contract", () => { expect(row.status).toBe("Scheduled"); }); - it("Generate does not write docStatus Yes when completedDate persistence fails", async () => { + it("Generate keeps Draft and skips docStatus Yes when completedDate patch fails", async () => { const row = baseRow({ completedDate: "2026-07-01" }); const client = new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false } }, }); + const invalidateSpy = vi.spyOn(client, "invalidateQueries"); onDateCompletedChange.mockRejectedValueOnce(new Error("conflict")); render( @@ -174,5 +175,9 @@ describe("CompDocDialog Save Draft / Generate completion contract", () => { expect(onDateCompletedChange).toHaveBeenCalledWith("2026-07-20"); }); expect(onSave).not.toHaveBeenCalled(); + expect(screen.getByRole("button", { name: /^generate$/i })).toBeInTheDocument(); + await waitFor(() => { + expect(invalidateSpy).toHaveBeenCalled(); + }); }); }); diff --git a/src/test/domain/work-orders/mappers/work-order-form-payload.test.ts b/src/test/domain/work-orders/mappers/work-order-form-payload.test.ts index c7f5936f..ed1be20e 100644 --- a/src/test/domain/work-orders/mappers/work-order-form-payload.test.ts +++ b/src/test/domain/work-orders/mappers/work-order-form-payload.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from "vitest"; import { - mapCompletedDateUpdateFormData, mapWorkOrderFormToCreatePayload, mapWorkOrderFormToUpdateFormData, } from "@/domain/work-orders/mappers/work-order-mapper"; @@ -137,7 +136,7 @@ describe("mapWorkOrderFormToUpdateFormData", () => { expect(formData.getAll("CategoryIds")).toEqual([]); }); - it("keeps completedDate on EditWorkorder and omits DocStatus (board field)", () => { + it("keeps completedDate on full EditWorkorder form and omits DocStatus (board field)", () => { const values = { ...createDefaultWorkOrderFormValues(), createdDate: "2026-01-15", @@ -151,24 +150,3 @@ describe("mapWorkOrderFormToUpdateFormData", () => { expect(formData.has("DocStatus")).toBe(false); }); }); - -describe("mapCompletedDateUpdateFormData", () => { - it("builds FormData with only Id and CompletedDate", () => { - const formData = mapCompletedDateUpdateFormData(42, "2026-07-30"); - - expect(formData.get("Id")).toBe("42"); - expect(formData.get("CompletedDate")).toBe("2026-07-30"); - expect(formData.get("Status")).toBeNull(); - expect(formData.get("WorkerOrderTitle")).toBeNull(); - expect(formData.get("Description")).toBeNull(); - expect(formData.getAll("ContactIds")).toEqual([]); - expect(formData.getAll("CategoryIds")).toEqual([]); - }); - - it("omits empty CompletedDate", () => { - const formData = mapCompletedDateUpdateFormData(1, " "); - - expect(formData.get("Id")).toBe("1"); - expect(formData.get("CompletedDate")).toBeNull(); - }); -});