From 9fc67516cacccf8c35358491db6910d694b80a94 Mon Sep 17 00:00:00 2001 From: Arthur Bassi Date: Thu, 3 Sep 2026 13:35:26 -0300 Subject: [PATCH] fix(work-orders): derive lifecycle status only when the schedule patch changes --- .../table/build-inline-draft-create-input.ts | 16 +++++-------- .../wizard/wizard-step-vendor-time.tsx | 3 ++- .../use-work-order-table-mutations.ts | 23 +++++++++++++------ .../utils/wo-lifecycle-transitions.ts | 15 ++++++++---- .../utils/wo-lifecycle-transitions.test.ts | 6 ++--- 5 files changed, 37 insertions(+), 26 deletions(-) diff --git a/src/app/(protected)/workorders/_components/list/table/build-inline-draft-create-input.ts b/src/app/(protected)/workorders/_components/list/table/build-inline-draft-create-input.ts index 20def0b6..c4bc18c9 100644 --- a/src/app/(protected)/workorders/_components/list/table/build-inline-draft-create-input.ts +++ b/src/app/(protected)/workorders/_components/list/table/build-inline-draft-create-input.ts @@ -2,7 +2,7 @@ import type { LocationOption, WorkOrderUser } from "@/domain/work-orders/types/w import type { WorkOrderTableDraft } from "@/domain/work-orders/types/work-order-table-row"; import type { WorkOrderWizardDraft } from "@/domain/work-orders/types/work-order-wizard"; import { resolveSiteCode } from "@/domain/work-orders/mappers/work-order-board-mapper"; -import { maybeAutoSchedulePatch } from "@/domain/work-orders/utils/wo-lifecycle-transitions"; +import { resolveCreationStatus } from "@/domain/work-orders/utils/wo-lifecycle-transitions"; export function buildInlineDraftCreateInput( draft: WorkOrderTableDraft, @@ -15,14 +15,6 @@ export function buildInlineDraftCreateInput( const hasWeekTarget = Boolean(draft.targetWeek?.trim()); const scheduleWeekOnly = draft.scheduleWeekOnly && (hasWeekTarget || Boolean(draft.scheduledOn)); - const status = draft.status || "Incomplete"; - const autoStatus = maybeAutoSchedulePatch({ - status, - scheduledOn: draft.scheduledOn, - scheduleWeekOnly, - }).status; - const resolvedStatus = - autoStatus === "Scheduled" || autoStatus === "Pending" ? autoStatus : status; return { locationId: String(draft.locationId || ""), @@ -45,7 +37,11 @@ export function buildInlineDraftCreateInput( vendorNotes: "", apptStart: draft.apptStart, apptEnd: draft.apptEnd, - status: resolvedStatus, + status: resolveCreationStatus({ + status: draft.status || "Incomplete", + scheduledOn: draft.scheduledOn, + scheduleWeekOnly, + }), pocName: draft.pocName, pocPhone: draft.pocPhone, pocNotes: draft.pocNotes, diff --git a/src/app/(protected)/workorders/_components/wizard/wizard-step-vendor-time.tsx b/src/app/(protected)/workorders/_components/wizard/wizard-step-vendor-time.tsx index 8e39022d..1fbd6142 100644 --- a/src/app/(protected)/workorders/_components/wizard/wizard-step-vendor-time.tsx +++ b/src/app/(protected)/workorders/_components/wizard/wizard-step-vendor-time.tsx @@ -19,6 +19,7 @@ import { import { CREATION_INLINE_STATUSES, getStatusOptionState, + withAutoScheduleStatus, } from "@/domain/work-orders/utils/wo-lifecycle-transitions"; import { WizardFieldSelect } from "@/app/(protected)/workorders/_components/wizard/wizard-field-select"; import { @@ -277,7 +278,7 @@ export function WizardStepVendorTime({ if (option.disabled) { return; } - onUpdate("status", status as WizardWOStatus); + onPatch(withAutoScheduleStatus(draft, { status: status as WizardWOStatus })); }} /> diff --git a/src/domain/work-orders/use-cases/use-work-order-table-mutations.ts b/src/domain/work-orders/use-cases/use-work-order-table-mutations.ts index adb2017a..f77d44a9 100644 --- a/src/domain/work-orders/use-cases/use-work-order-table-mutations.ts +++ b/src/domain/work-orders/use-cases/use-work-order-table-mutations.ts @@ -130,10 +130,16 @@ export function useWorkOrderTableMutations( const patchMutation = useMutation({ meta: { suppressErrorToast: true }, - mutationFn: ({ id, patch }: { id: string | number; patch: WorkOrderTablePatch }) => - enqueuePatch(id, () => runWorkOrderTablePatch(id, patch, getRow)), - onMutate: async ({ id, patch }) => { - onPatch(id, patch); + mutationFn: ({ + id, + persistPatch, + }: { + id: string | number; + patch: WorkOrderTablePatch; + persistPatch: WorkOrderTablePatch; + }) => enqueuePatch(id, () => runWorkOrderTablePatch(id, persistPatch, getRow)), + onMutate: async ({ id, persistPatch }) => { + onPatch(id, persistPatch); }, onSuccess: (result, { id }) => { if (result.kind === "board") { @@ -168,14 +174,17 @@ export function useWorkOrderTableMutations( mergeAutoSchedulePatch(getRow?.(id), patch); const patchField: WorkOrderTablePatchFn = (id, patch, options) => { - void patchMutation.mutateAsync({ id, patch: resolvePatch(id, patch) }).then( + const persistPatch = resolvePatch(id, patch); + void patchMutation.mutateAsync({ id, patch, persistPatch }).then( () => options?.onSuccess?.(), (error: Error) => options?.onError?.(error), ); }; - const patchFieldAsync = (id: string | number, patch: WorkOrderTablePatch) => - patchMutation.mutateAsync({ id, patch: resolvePatch(id, patch) }); + const patchFieldAsync = (id: string | number, patch: WorkOrderTablePatch) => { + const persistPatch = resolvePatch(id, patch); + return patchMutation.mutateAsync({ id, patch, persistPatch }); + }; return { patchField, diff --git a/src/domain/work-orders/utils/wo-lifecycle-transitions.ts b/src/domain/work-orders/utils/wo-lifecycle-transitions.ts index daea62f1..43a746d3 100644 --- a/src/domain/work-orders/utils/wo-lifecycle-transitions.ts +++ b/src/domain/work-orders/utils/wo-lifecycle-transitions.ts @@ -151,15 +151,11 @@ function patchTouchesSchedule(patch: WorkOrderTablePatch): boolean { ); } -function patchTouchesScheduleOrStatus(patch: WorkOrderTablePatch): boolean { - return patch.status !== undefined || patchTouchesSchedule(patch); -} - export function mergeAutoSchedulePatch( row: WorkOrderTableRow | undefined, patch: WorkOrderTablePatch, ): WorkOrderTablePatch { - if (!row || !patchTouchesScheduleOrStatus(patch)) { + if (!row || !patchTouchesSchedule(patch)) { return patch; } const merged = { @@ -187,3 +183,12 @@ export function withAutoScheduleStatus { expect(mergeAutoSchedulePatch(row, { vendorId: "9" })).toEqual({ vendorId: "9" }); }); - it("reapplies auto-schedule after a Pending or Incomplete pick on a dated row", () => { + it("keeps explicit status picks on dated rows", () => { const row = { status: "Scheduled", scheduledOn: "2026-06-01", scheduleWeekOnly: false, } as WorkOrderTableRow; - expect(mergeAutoSchedulePatch(row, { status: "Pending" })).toEqual({ status: "Scheduled" }); + expect(mergeAutoSchedulePatch(row, { status: "Pending" })).toEqual({ status: "Pending" }); expect(mergeAutoSchedulePatch(row, { status: "Incomplete" })).toEqual({ - status: "Scheduled", + status: "Incomplete", }); });