fix(SH-357): disable Incomplete without a schedule date

This commit is contained in:
Alexandre Brandizzi 2026-10-01 16:10:46 -03:00
parent 88307764c5
commit cbb2edf2a6
13 changed files with 27 additions and 24 deletions

View file

@ -19,7 +19,7 @@ function resolveInlineDraftSchedule(draft: WorkOrderTableDraft) {
return {
scheduleWeekOnly,
status: resolveCreationStatus({
status: draft.status || "Incomplete",
status: draft.status || "Pending",
scheduledOn: draft.scheduledOn,
scheduleWeekOnly,
}),

View file

@ -230,7 +230,7 @@ export function WoTableInlineRowCells({
dispatcherId={draft.dispatcherId}
users={users}
onSave={(id) => {
onPatch({ dispatcherId: id, status: draft.status || "Incomplete" });
onPatch({ dispatcherId: id, status: draft.status || "Pending" });
}}
/>
</InlineFieldCell>

View file

@ -9,7 +9,6 @@ export function useNewWoWizardDraft(open: boolean) {
const [step, setStep] = useState(1);
const [draft, setDraft] = useState<WorkOrderWizardDraft>({
...EMPTY_WIZARD_DRAFT,
status: "Incomplete",
});
const [svcOpen, setSvcOpen] = useState(false);
const [svcSearch, setSvcSearch] = useState("");
@ -18,7 +17,7 @@ export function useNewWoWizardDraft(open: boolean) {
useEffect(() => {
if (open) {
setStep(1);
setDraft({ ...EMPTY_WIZARD_DRAFT, status: "Incomplete" });
setDraft({ ...EMPTY_WIZARD_DRAFT });
setSvcOpen(false);
setSvcSearch("");
setShowErrors(false);

View file

@ -40,7 +40,6 @@ export function useNewWoWizardState({
const [showErrors, setShowErrors] = useState(false);
const [draft, setDraft] = useState<WorkOrderWizardDraft>({
...EMPTY_WIZARD_DRAFT,
status: "Incomplete",
});
const [pocAutoFilledFor, setPocAutoFilledFor] = useState("");
@ -83,7 +82,7 @@ export function useNewWoWizardState({
if (open) {
setStep(1);
setShowErrors(false);
setDraft({ ...EMPTY_WIZARD_DRAFT, status: "Incomplete" });
setDraft({ ...EMPTY_WIZARD_DRAFT });
setPocAutoFilledFor("");
setDuplicateRow(null);
}

View file

@ -53,6 +53,6 @@ export function buildWizardSidebarSummaryRows(draft: WorkOrderWizardDraft): [str
["Technician", draft.vendorName || "—"],
["Company", draft.vendorName || "—"],
["Appt time", draft.apptStart ? formatApptRange(draft.apptStart, draft.apptEnd) : "—"],
["Status", draft.status || "Incomplete"],
["Status", draft.status || "Pending"],
];
}

View file

@ -8,9 +8,9 @@ import { ColorDot } from "@/app/(protected)/workorders/_components/wizard/wizard
export function WizardSidebarSummary({ draft }: { draft: WorkOrderWizardDraft }) {
const summary = buildWizardSidebarSummaryRows(draft);
const statusLabel = draft.status || "Incomplete";
const statusLabel = draft.status || "Pending";
const statusValue = statusLabel as WizardWOStatus;
const statusDotColor = WIZARD_STATUS_DOTS[statusValue] ?? WIZARD_STATUS_DOTS.Incomplete;
const statusDotColor = WIZARD_STATUS_DOTS[statusValue] ?? WIZARD_STATUS_DOTS.Pending;
return (
<div style={{ marginTop: "auto", paddingTop: 44 }}>

View file

@ -194,8 +194,8 @@ export function WizardStepVendorTime({
draft.technicianAssigned === false
? undefined
: vendors.find((v) => String(v.id) === draft.vendorId);
const statusValue = (draft.status || "Incomplete") as WizardWOStatus;
const statusDotColor = WIZARD_STATUS_DOTS[statusValue] ?? WIZARD_STATUS_DOTS.Incomplete;
const statusValue = (draft.status || "Pending") as WizardWOStatus;
const statusDotColor = WIZARD_STATUS_DOTS[statusValue] ?? WIZARD_STATUS_DOTS.Pending;
const selectedDistance = formatVendorDistance(selectedVendor?.distanceMiles);
const closabilityGaps = getClosabilityGaps(wizardDraftToClosabilityInput(draft));

View file

@ -60,7 +60,7 @@ export function useWorkOrdersTableUiState(weekMonday: string) {
setDraft({
...EMPTY_TABLE_DRAFT,
scheduledOn,
status: scheduledOn ? "Scheduled" : "Incomplete",
status: scheduledOn ? "Scheduled" : "Pending",
});
},
[weekMonday],

View file

@ -8,11 +8,7 @@ import {
WORK_ORDER_TYPE_API,
} from "@/domain/work-orders/types/work-order-board";
import type {
WizardWOStatus,
WOType,
WorkOrderWizardDraft,
} from "@/domain/work-orders/types/work-order-wizard";
import type { WOType, WorkOrderWizardDraft } from "@/domain/work-orders/types/work-order-wizard";
import { fmtDateRange } from "@/domain/work-orders/utils/wizard-date-utils";
@ -20,6 +16,7 @@ import { assignWizardScheduleFields } from "@/domain/work-orders/mappers/assign-
import { assignWizardVendorApptFields } from "@/domain/work-orders/mappers/assign-wizard-vendor-appt-fields";
import { assignWizardServicePocFields } from "@/domain/work-orders/mappers/assign-wizard-service-poc-fields";
import { resolveIsAddOnForCreate } from "@/domain/work-orders/utils/wo-addon-cutoff";
import { resolveCreationStatus } from "@/domain/work-orders/utils/wo-lifecycle-transitions";
const WO_TYPE_TO_API: Record<WOType, WorkOrderTypeApi> = {
PM: WORK_ORDER_TYPE_API.PM,
@ -114,7 +111,7 @@ export function mapWizardDraftToBoardCreatePayload(
};
if (draft.status) {
payload.lifecycleStatus = LIFECYCLE_STATUS_API[draft.status as WizardWOStatus];
payload.lifecycleStatus = LIFECYCLE_STATUS_API[resolveCreationStatus(draft)];
}
if (draft.woNumber.trim()) {

View file

@ -175,7 +175,7 @@ export const EMPTY_TABLE_DRAFT: WorkOrderTableDraft = {
techPhone: "",
apptStart: "",
apptEnd: "",
status: "Incomplete",
status: "Pending",
pocName: "",
pocPhone: "",
pocNotes: "",

View file

@ -109,7 +109,7 @@ export const EMPTY_WIZARD_DRAFT: WorkOrderWizardDraft = {
vendorNotes: "",
apptStart: "",
apptEnd: "",
status: "Incomplete",
status: "Pending",
pocName: "",
pocPhone: "",
pocNotes: "",

View file

@ -38,6 +38,8 @@ export const CREATION_INLINE_STATUSES: readonly WizardWOStatus[] = [
export const SCHEDULED_NEEDS_DATE_MSG =
"Set a Schedule On date before marking this work order as Scheduled.";
export const INCOMPLETE_NEEDS_DATE_MSG =
"Set a Schedule On date before marking this work order as Incomplete.";
export const PENDING_NEEDS_NO_DATE_MSG =
"Clear the Schedule On date before marking this work order as Pending.";
@ -134,6 +136,9 @@ export function getStatusOptionState(
status: WizardWOStatus,
closabilityGaps: string[],
): StatusOptionState {
if (status === "Incomplete" && !hasConcreteScheduleDate(row)) {
return { disabled: true, title: INCOMPLETE_NEEDS_DATE_MSG };
}
if (status === "Pending" && hasConcreteScheduleDate(row)) {
return { disabled: true, title: PENDING_NEEDS_NO_DATE_MSG };
}
@ -211,5 +216,8 @@ export function resolveCreationStatus(row: ScheduleDateInput & { status: string
if (autoStatus === "Scheduled" || autoStatus === "Pending") {
return autoStatus;
}
return (row.status || "Incomplete") as WizardWOStatus;
if (!row.status || (row.status === "Incomplete" && !hasConcreteScheduleDate(row))) {
return "Pending";
}
return row.status as WizardWOStatus;
}

View file

@ -61,7 +61,7 @@ describe("mapWizardDraftToBoardCreatePayload", () => {
isAddOn: false,
lifecycleStatus: 1,
lifecycleStatus: 2,
});
});
@ -283,7 +283,7 @@ describe("mapWizardDraftToBoardCreatePayload", () => {
expect(payload.scheduledDate).toBeUndefined();
});
it("keeps Incomplete on week-only create and omits scheduledDate", () => {
it("normalizes Incomplete to Pending on week-only create and omits scheduledDate", () => {
const payload = mapWizardDraftToBoardCreatePayload({
...EMPTY_WIZARD_DRAFT,
type: "PM",
@ -293,7 +293,7 @@ describe("mapWizardDraftToBoardCreatePayload", () => {
targetWeek: "2026-06-22",
});
expect(payload.lifecycleStatus).toBe(LIFECYCLE_STATUS_API.Incomplete);
expect(payload.lifecycleStatus).toBe(LIFECYCLE_STATUS_API.Pending);
expect(payload.scheduleWeekOnly).toBe(true);
expect(payload.targetWeek).toBe("2026-06-22");
expect(payload.scheduledDate).toBeUndefined();