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 5f911bd8..20def0b6 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,6 +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";
export function buildInlineDraftCreateInput(
draft: WorkOrderTableDraft,
@@ -14,6 +15,14 @@ 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 || ""),
@@ -36,7 +45,7 @@ export function buildInlineDraftCreateInput(
vendorNotes: "",
apptStart: draft.apptStart,
apptEnd: draft.apptEnd,
- status: draft.status || "Incomplete",
+ status: resolvedStatus,
pocName: draft.pocName,
pocPhone: draft.pocPhone,
pocNotes: draft.pocNotes,
diff --git a/src/app/(protected)/workorders/_components/list/table/cells/status-cell.tsx b/src/app/(protected)/workorders/_components/list/table/cells/status-cell.tsx
index a9ee2647..b9331330 100644
--- a/src/app/(protected)/workorders/_components/list/table/cells/status-cell.tsx
+++ b/src/app/(protected)/workorders/_components/list/table/cells/status-cell.tsx
@@ -167,7 +167,7 @@ export function StatusCell({
onClose={() => setAnchorEl(null)}
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
onClick={(e) => e.stopPropagation()}
- slotProps={{ paper: { className: "min-w-[168px] rounded-lg border border-border p-1" } }}
+ slotProps={{ paper: { className: "min-w-[190px] rounded-lg border border-border p-1" } }}
>
{statusOptions.map((status) => {
const option = getStatusOptionState(wo, status, closabilityGaps);
@@ -192,8 +192,8 @@ export function StatusCell({
}}
className={
option.disabled
- ? "flex w-full cursor-not-allowed items-center gap-2 rounded px-2 py-1.5 opacity-50"
- : "flex w-full items-center gap-2 rounded px-2 py-1.5 hover:bg-[#F9FAFB]"
+ ? "flex w-full cursor-not-allowed items-center justify-between gap-2 rounded px-2 py-1.5 opacity-50"
+ : "flex w-full items-center justify-between gap-2 rounded px-2 py-1.5 hover:bg-[#F9FAFB]"
}
>
diff --git a/src/app/(protected)/workorders/_components/list/table/wo-table-inline-row-cells.tsx b/src/app/(protected)/workorders/_components/list/table/wo-table-inline-row-cells.tsx
index 4f4c9f51..cde640c8 100644
--- a/src/app/(protected)/workorders/_components/list/table/wo-table-inline-row-cells.tsx
+++ b/src/app/(protected)/workorders/_components/list/table/wo-table-inline-row-cells.tsx
@@ -13,7 +13,10 @@ import { ApptTimeCell } from "./cells/appt-time-cell";
import { DateCell } from "./cells/date-cell";
import { DueDateCell } from "./cells/due-date-cell";
import { PMTypeCell } from "./cells/pm-type-cell";
-import { CREATION_INLINE_STATUSES } from "@/domain/work-orders/utils/wo-lifecycle-transitions";
+import {
+ CREATION_INLINE_STATUSES,
+ withAutoScheduleStatus,
+} from "@/domain/work-orders/utils/wo-lifecycle-transitions";
import { StatusCell } from "./cells/status-cell";
import { TypeCell } from "./cells/type-cell";
import { VendorCell } from "./cells/vendor-cell";
@@ -85,30 +88,36 @@ function InlineScheduleOnCell({
defaultMode={weekDefault ? "week" : "date"}
highlight={!draft.scheduledOn && !draft.targetWeek}
onSave={(date) =>
- onPatch({
- scheduledOn: date,
- scheduleWeekOnly: false,
- targetWeek: "",
- isAddOnOverride: false,
- })
+ onPatch(
+ withAutoScheduleStatus(draft, {
+ scheduledOn: date,
+ scheduleWeekOnly: false,
+ targetWeek: "",
+ isAddOnOverride: false,
+ }),
+ )
}
onSaveWeek={(week) =>
- onPatch({
- scheduledOn: "",
- targetWeek: week,
- scheduleWeekOnly: true,
- isAddOnOverride: false,
- })
+ onPatch(
+ withAutoScheduleStatus(draft, {
+ scheduledOn: "",
+ targetWeek: week,
+ scheduleWeekOnly: true,
+ isAddOnOverride: false,
+ }),
+ )
}
onClear={
hasSchedule
? () =>
- onPatch({
- scheduledOn: "",
- targetWeek: "",
- scheduleWeekOnly: false,
- isAddOnOverride: false,
- })
+ onPatch(
+ withAutoScheduleStatus(draft, {
+ scheduledOn: "",
+ targetWeek: "",
+ scheduleWeekOnly: false,
+ isAddOnOverride: false,
+ }),
+ )
: undefined
}
/>
diff --git a/src/app/(protected)/workorders/_components/wizard/wizard-step-type-schedule.tsx b/src/app/(protected)/workorders/_components/wizard/wizard-step-type-schedule.tsx
index 565c7de1..f4df71a3 100644
--- a/src/app/(protected)/workorders/_components/wizard/wizard-step-type-schedule.tsx
+++ b/src/app/(protected)/workorders/_components/wizard/wizard-step-type-schedule.tsx
@@ -13,6 +13,7 @@ import {
type WorkOrderSeverity,
} from "@/domain/work-orders/types/work-order-severity";
import { getDispatcherAvatarColor } from "@/domain/work-orders/utils/dispatcher-avatar-color";
+import { withAutoScheduleStatus } from "@/domain/work-orders/utils/wo-lifecycle-transitions";
import { WizardDateField } from "@/app/(protected)/workorders/_components/wizard/wizard-date-field";
import { WizardDueDateField } from "@/app/(protected)/workorders/_components/wizard/wizard-due-date-field";
import { WizardFieldSelect } from "@/app/(protected)/workorders/_components/wizard/wizard-field-select";
@@ -146,7 +147,11 @@ function WizardTypeScheduleDates({ draft, onUpdate, onPatch }: WizardTypeSchedul
onPatch(clearSchedulePatch()) : undefined}
+ onClear={
+ hasWizardSchedule(draft)
+ ? () => onPatch(withAutoScheduleStatus(draft, clearSchedulePatch()))
+ : undefined
+ }
>
Schedule On
@@ -158,34 +163,40 @@ function WizardTypeScheduleDates({ draft, onUpdate, onPatch }: WizardTypeSchedul
scheduleEnd={draft.scheduleEnd}
schedMode={draft.schedMode}
onChange={(iso) =>
- onPatch({
- scheduledOn: iso,
- scheduleWeekOnly: false,
- scheduleEnd: "",
- schedMode: "date",
- targetWeek: "",
- isAddOnOverride: false,
- })
+ onPatch(
+ withAutoScheduleStatus(draft, {
+ scheduledOn: iso,
+ scheduleWeekOnly: false,
+ scheduleEnd: "",
+ schedMode: "date",
+ targetWeek: "",
+ isAddOnOverride: false,
+ }),
+ )
}
onChangeWeek={(w) =>
- onPatch({
- scheduledOn: w,
- scheduleWeekOnly: Boolean(w),
- scheduleEnd: "",
- schedMode: "week",
- targetWeek: w,
- isAddOnOverride: false,
- })
+ onPatch(
+ withAutoScheduleStatus(draft, {
+ scheduledOn: w,
+ scheduleWeekOnly: Boolean(w),
+ scheduleEnd: "",
+ schedMode: "week",
+ targetWeek: w,
+ isAddOnOverride: false,
+ }),
+ )
}
onChangeRange={(s, e) =>
- onPatch({
- scheduledOn: s,
- scheduleEnd: e,
- schedMode: "range",
- scheduleWeekOnly: false,
- targetWeek: "",
- isAddOnOverride: false,
- })
+ onPatch(
+ withAutoScheduleStatus(draft, {
+ scheduledOn: s,
+ scheduleEnd: e,
+ schedMode: "range",
+ scheduleWeekOnly: false,
+ targetWeek: "",
+ isAddOnOverride: false,
+ }),
+ )
}
/>
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 e0a68614..adb2017a 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
@@ -131,12 +131,9 @@ export function useWorkOrderTableMutations(
const patchMutation = useMutation({
meta: { suppressErrorToast: true },
mutationFn: ({ id, patch }: { id: string | number; patch: WorkOrderTablePatch }) =>
- enqueuePatch(id, () => {
- const next = mergeAutoSchedulePatch(getRow?.(id), patch);
- return runWorkOrderTablePatch(id, next, getRow);
- }),
+ enqueuePatch(id, () => runWorkOrderTablePatch(id, patch, getRow)),
onMutate: async ({ id, patch }) => {
- onPatch(id, mergeAutoSchedulePatch(getRow?.(id), patch));
+ onPatch(id, patch);
},
onSuccess: (result, { id }) => {
if (result.kind === "board") {
@@ -167,15 +164,18 @@ export function useWorkOrderTableMutations(
},
});
+ const resolvePatch = (id: string | number, patch: WorkOrderTablePatch) =>
+ mergeAutoSchedulePatch(getRow?.(id), patch);
+
const patchField: WorkOrderTablePatchFn = (id, patch, options) => {
- void patchMutation.mutateAsync({ id, patch }).then(
+ void patchMutation.mutateAsync({ id, patch: resolvePatch(id, patch) }).then(
() => options?.onSuccess?.(),
(error: Error) => options?.onError?.(error),
);
};
const patchFieldAsync = (id: string | number, patch: WorkOrderTablePatch) =>
- patchMutation.mutateAsync({ id, patch });
+ patchMutation.mutateAsync({ id, patch: resolvePatch(id, patch) });
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 0ff1b9f7..daea62f1 100644
--- a/src/domain/work-orders/utils/wo-lifecycle-transitions.ts
+++ b/src/domain/work-orders/utils/wo-lifecycle-transitions.ts
@@ -131,10 +131,10 @@ export function getStatusOptionState(
/** AAP `maybeAutoSchedule` — no dispatcher requirement. */
export function maybeAutoSchedulePatch(
- row: Pick,
+ row: ScheduleDateInput & { status?: string },
): WorkOrderTablePatch {
const hasDate = hasConcreteScheduleDate(row);
- if (hasDate && isPreScheduleStatus(row.status)) {
+ if (hasDate && isPreScheduleStatus(row.status ?? "")) {
return { status: "Scheduled" };
}
if (!hasDate && row.status === "Scheduled") {
@@ -143,15 +143,18 @@ export function maybeAutoSchedulePatch(
return {};
}
-function patchTouchesScheduleOrStatus(patch: WorkOrderTablePatch): boolean {
+function patchTouchesSchedule(patch: WorkOrderTablePatch): boolean {
return (
- patch.status !== undefined ||
patch.scheduledOn !== undefined ||
patch.scheduleWeekOnly !== undefined ||
patch.targetWeek !== undefined
);
}
+function patchTouchesScheduleOrStatus(patch: WorkOrderTablePatch): boolean {
+ return patch.status !== undefined || patchTouchesSchedule(patch);
+}
+
export function mergeAutoSchedulePatch(
row: WorkOrderTableRow | undefined,
patch: WorkOrderTablePatch,
@@ -167,3 +170,20 @@ export function mergeAutoSchedulePatch(
};
return { ...patch, ...maybeAutoSchedulePatch(merged) };
}
+
+/** Apply schedule fields and the AAP auto-status for that next schedule. */
+export function withAutoScheduleStatus(
+ current: T,
+ schedulePatch: Partial & ScheduleDateInput,
+): Partial {
+ const next = {
+ status: current.status ?? "",
+ scheduledOn:
+ schedulePatch.scheduledOn !== undefined ? schedulePatch.scheduledOn : current.scheduledOn,
+ scheduleWeekOnly:
+ schedulePatch.scheduleWeekOnly !== undefined
+ ? schedulePatch.scheduleWeekOnly
+ : current.scheduleWeekOnly,
+ };
+ return { ...schedulePatch, ...maybeAutoSchedulePatch(next) };
+}
diff --git a/src/test/app/(protected)/workorders/build-inline-draft-create-input.test.ts b/src/test/app/(protected)/workorders/build-inline-draft-create-input.test.ts
index 441f39e2..d4cc9a1f 100644
--- a/src/test/app/(protected)/workorders/build-inline-draft-create-input.test.ts
+++ b/src/test/app/(protected)/workorders/build-inline-draft-create-input.test.ts
@@ -92,4 +92,22 @@ describe("buildInlineDraftCreateInput", () => {
expect(input.assigneeId).toBe("");
expect(payload).not.toHaveProperty("assignTo");
});
+
+ it("demotes Scheduled to Pending when create has no concrete date", () => {
+ const draft = {
+ ...EMPTY_TABLE_DRAFT,
+ site: "BK5",
+ locationId: "9",
+ woNumber: "25005",
+ pocName: "Jane",
+ pocPhone: "555-0100",
+ status: "Scheduled" as const,
+ scheduledOn: "",
+ scheduleWeekOnly: true,
+ targetWeek: "",
+ };
+
+ const input = buildInlineDraftCreateInput(draft, [], [{ id: 9, name: "BK5 · Dallas, TX" }]);
+ expect(input.status).toBe("Pending");
+ });
});
diff --git a/src/test/domain/work-orders/use-cases/use-work-order-table-mutations.test.tsx b/src/test/domain/work-orders/use-cases/use-work-order-table-mutations.test.tsx
index 6015695b..ed614089 100644
--- a/src/test/domain/work-orders/use-cases/use-work-order-table-mutations.test.tsx
+++ b/src/test/domain/work-orders/use-cases/use-work-order-table-mutations.test.tsx
@@ -254,6 +254,10 @@ describe("maybeAutoSchedule on board patch", () => {
expect.objectContaining({ scheduledOn: "2026-07-15", status: "Scheduled" }),
);
await waitFor(() => expect(patchBoardField).toHaveBeenCalled());
+ expect(patchBoardField).toHaveBeenCalledWith(
+ 42,
+ expect.objectContaining({ field: "lifecycleStatus", value: "Scheduled" }),
+ );
});
});
diff --git a/src/test/domain/work-orders/utils/wo-lifecycle-transitions.test.ts b/src/test/domain/work-orders/utils/wo-lifecycle-transitions.test.ts
index f8150706..838595b9 100644
--- a/src/test/domain/work-orders/utils/wo-lifecycle-transitions.test.ts
+++ b/src/test/domain/work-orders/utils/wo-lifecycle-transitions.test.ts
@@ -103,6 +103,31 @@ describe("wo-lifecycle-transitions", () => {
expect(mergeAutoSchedulePatch(row, { vendorId: "9" })).toEqual({ vendorId: "9" });
});
+ it("reapplies auto-schedule after a Pending or Incomplete pick on a dated row", () => {
+ const row = {
+ status: "Scheduled",
+ scheduledOn: "2026-06-01",
+ scheduleWeekOnly: false,
+ } as WorkOrderTableRow;
+ expect(mergeAutoSchedulePatch(row, { status: "Pending" })).toEqual({ status: "Scheduled" });
+ expect(mergeAutoSchedulePatch(row, { status: "Incomplete" })).toEqual({
+ status: "Scheduled",
+ });
+ });
+
+ it("still demotes Scheduled when the schedule is cleared", () => {
+ const row = {
+ status: "Scheduled",
+ scheduledOn: "2026-06-01",
+ scheduleWeekOnly: false,
+ } as WorkOrderTableRow;
+ expect(mergeAutoSchedulePatch(row, { scheduledOn: "", scheduleWeekOnly: false })).toEqual({
+ scheduledOn: "",
+ scheduleWeekOnly: false,
+ status: "Pending",
+ });
+ });
+
it("uses AAP completed tooltip hierarchy", () => {
expect(formatAapCompletedBlockTitle(["Dispatcher", "Company"])).toBe(
"Fill in Assigned To, Vendor before completing this work order.",