mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-09-30 02:13:13 +00:00
fix(workorders): preserve explicit no-technician choice
This commit is contained in:
parent
316cafccaa
commit
64c53e9da0
7 changed files with 126 additions and 3 deletions
|
|
@ -134,6 +134,19 @@ async function mockApi(
|
|||
});
|
||||
}
|
||||
if (pathname === "/api/workorders/board" && method === "POST") {
|
||||
const body = bodyOf(request) as { vendorId?: number; technicianAssigned?: boolean };
|
||||
const vendorId = body.vendorId ?? 0;
|
||||
currentBoardRows.push({
|
||||
...BOARD_ROW,
|
||||
id: 900,
|
||||
woNumber: "SH-900",
|
||||
lifecycleStatusLabel: "Incomplete",
|
||||
vendorId: vendorId || null,
|
||||
vendorName: vendorId > 0 ? "Vinewood LLC" : null,
|
||||
technicianAssigned: body.technicianAssigned,
|
||||
techName: body.technicianAssigned === true ? "Jordan Lee" : null,
|
||||
techPhone: body.technicianAssigned === true ? "(314) 555-0199" : null,
|
||||
});
|
||||
return fulfillJson(route, { data: { id: 900, woNumber: "SH-900" } });
|
||||
}
|
||||
if (/^\/api\/workorders\/\d+\/board$/u.test(pathname) && method === "PATCH") {
|
||||
|
|
@ -280,6 +293,44 @@ test.describe("Inline technician registration", () => {
|
|||
expect(create?.body).toEqual(expect.objectContaining({ vendorId: 42 }));
|
||||
});
|
||||
|
||||
test("wizard No technician selection overrides a staged technician through create and reopen", async ({
|
||||
page,
|
||||
}) => {
|
||||
const writes: Captured[] = [];
|
||||
await mockApi(page, writes);
|
||||
await openWizardVendorStep(page);
|
||||
|
||||
await page.getByRole("button", { name: "No technician" }).first().click();
|
||||
await page.getByRole("button", { name: "No technician" }).last().click();
|
||||
await page.getByRole("button", { name: "Create work order" }).click();
|
||||
|
||||
await expect
|
||||
.poll(() =>
|
||||
writes.find(
|
||||
(write) => write.pathname === "/api/workorders/board" && write.method === "POST",
|
||||
),
|
||||
)
|
||||
.toBeTruthy();
|
||||
const create = writes.find(
|
||||
(write) => write.pathname === "/api/workorders/board" && write.method === "POST",
|
||||
);
|
||||
expect(create?.body).toEqual(
|
||||
expect.objectContaining({ vendorId: 41, technicianAssigned: false }),
|
||||
);
|
||||
expect(vendorWrites(writes)).toEqual([
|
||||
expect.objectContaining({ pathname: "/api/vendor-company-roster/7" }),
|
||||
]);
|
||||
|
||||
await page.reload();
|
||||
const createdRow = page.locator("#wo-row-900");
|
||||
await expect(createdRow.getByRole("button", { name: /Vinewood LLC/ })).toBeVisible();
|
||||
await createdRow.getByRole("button", { name: /Vinewood LLC/ }).click();
|
||||
const vendorDialog = page.getByRole("dialog", { name: "Vendor" });
|
||||
await expect(vendorDialog.getByRole("button", { name: "No technician" })).toBeVisible();
|
||||
await expect(vendorDialog.locator('input[type="tel"]')).toHaveCount(0);
|
||||
await expect(vendorDialog.getByRole("button", { name: "Jordan Lee" })).toHaveCount(0);
|
||||
});
|
||||
|
||||
test("company-only save returns no technician and stays empty after reopen and reload", async ({
|
||||
page,
|
||||
}) => {
|
||||
|
|
|
|||
|
|
@ -172,9 +172,7 @@ export function useVendorDialogForm({
|
|||
notes,
|
||||
pendingCreateCount: Object.keys(pendingCreates).length,
|
||||
},
|
||||
baseline: {
|
||||
...buildSubmitBaseline({ ...technicianKeyArgs, techPhone }),
|
||||
},
|
||||
baseline: buildSubmitBaseline({ ...technicianKeyArgs, techPhone }),
|
||||
});
|
||||
|
||||
const { handleSave } = useVendorDialogSave({
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ export function buildCreatedTechniciansPatch(
|
|||
if (
|
||||
created.length === 1 &&
|
||||
only != null &&
|
||||
draft.technicianSelectionCleared !== true &&
|
||||
(draft.technicianAssigned === false || draft.vendorId === "")
|
||||
) {
|
||||
patch.vendorId = String(only.id);
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ export function WizardVendorCompanyFields({
|
|||
techPhone: "",
|
||||
vendorNotes: "",
|
||||
newTechnicians: [],
|
||||
technicianSelectionCleared: false,
|
||||
})
|
||||
}
|
||||
>
|
||||
|
|
@ -96,6 +97,7 @@ export function WizardVendorCompanyFields({
|
|||
vendorId: nextAnchor ? String(nextAnchor.id) : "",
|
||||
vendorName: nextAnchor ? nextCompany : "",
|
||||
technicianAssigned: false,
|
||||
technicianSelectionCleared: false,
|
||||
techPhone: "",
|
||||
vendorNotes: "",
|
||||
newTechnicians: [],
|
||||
|
|
@ -116,6 +118,7 @@ export function WizardVendorCompanyFields({
|
|||
vendorId: noTechnicianVendorId,
|
||||
vendorName: companyAnchor ? company : "",
|
||||
technicianAssigned: false,
|
||||
technicianSelectionCleared: true,
|
||||
techPhone: "",
|
||||
vendorNotes: "",
|
||||
})
|
||||
|
|
@ -147,6 +150,7 @@ export function WizardVendorCompanyFields({
|
|||
vendorId: noTechnicianVendorId,
|
||||
vendorName: companyAnchor ? company : "",
|
||||
technicianAssigned: false,
|
||||
technicianSelectionCleared: true,
|
||||
techPhone: "",
|
||||
});
|
||||
return;
|
||||
|
|
@ -157,6 +161,7 @@ export function WizardVendorCompanyFields({
|
|||
vendorId: id,
|
||||
vendorName: vendor?.companyName ?? "",
|
||||
technicianAssigned: true,
|
||||
technicianSelectionCleared: false,
|
||||
techPhone: "",
|
||||
});
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ export interface WorkOrderWizardDraft {
|
|||
vendorCompany?: string;
|
||||
/** Explicit technician choice; null/omitted retains legacy inference. */
|
||||
technicianAssigned?: boolean | null;
|
||||
/** UI intent: an explicit clear overrides staged single-technician autoassignment. */
|
||||
technicianSelectionCleared?: boolean;
|
||||
/** Technicians registered inline; persisted under `vendorCompany` only on create. */
|
||||
newTechnicians?: WizardNewTechnician[];
|
||||
techPhone: string;
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ describe("WizardStepVendorTime inline technician (SH-364)", () => {
|
|||
vendorId: "52",
|
||||
vendorName: "Gateway Plumbing",
|
||||
technicianAssigned: false,
|
||||
technicianSelectionCleared: false,
|
||||
techPhone: "",
|
||||
vendorNotes: "",
|
||||
newTechnicians: [],
|
||||
|
|
@ -141,6 +142,7 @@ describe("WizardStepVendorTime inline technician (SH-364)", () => {
|
|||
vendorId: "41",
|
||||
vendorName: "Vinewood LLC",
|
||||
technicianAssigned: false,
|
||||
technicianSelectionCleared: false,
|
||||
techPhone: "",
|
||||
vendorNotes: "",
|
||||
newTechnicians: [],
|
||||
|
|
@ -170,10 +172,41 @@ describe("WizardStepVendorTime inline technician (SH-364)", () => {
|
|||
vendorId: "41",
|
||||
vendorName: "Vinewood LLC",
|
||||
technicianAssigned: true,
|
||||
technicianSelectionCleared: false,
|
||||
techPhone: "",
|
||||
});
|
||||
});
|
||||
|
||||
it("records explicit no-technician intent while leaving a staged technician untouched", async () => {
|
||||
const onPatch = vi.fn();
|
||||
render(
|
||||
<WizardStepVendorTime
|
||||
draft={draftWith({
|
||||
vendorCompany: "Vinewood LLC",
|
||||
vendorId: "41",
|
||||
technicianAssigned: false,
|
||||
newTechnicians: [{ name: "Jordan Lee", phone: "" }],
|
||||
})}
|
||||
vendors={vendors}
|
||||
onUpdate={vi.fn()}
|
||||
onPatch={onPatch}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "No technician" }));
|
||||
const noTechnicianOptions = screen.getAllByRole("button", { name: "No technician" });
|
||||
fireEvent.click(noTechnicianOptions[noTechnicianOptions.length - 1]!);
|
||||
|
||||
expect(onPatch).toHaveBeenCalledWith({
|
||||
vendorId: "41",
|
||||
vendorName: "Vinewood LLC",
|
||||
technicianAssigned: false,
|
||||
technicianSelectionCleared: true,
|
||||
techPhone: "",
|
||||
});
|
||||
expect(screen.getByRole("group", { name: "New Technician 1" })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("retains the same company vendor id when clearing a second technician", async () => {
|
||||
const onPatch = vi.fn();
|
||||
const secondVinewoodTech = {
|
||||
|
|
@ -201,6 +234,7 @@ describe("WizardStepVendorTime inline technician (SH-364)", () => {
|
|||
vendorId: "42",
|
||||
vendorName: "Vinewood LLC",
|
||||
technicianAssigned: false,
|
||||
technicianSelectionCleared: true,
|
||||
techPhone: "",
|
||||
});
|
||||
});
|
||||
|
|
@ -327,6 +361,24 @@ describe("useWizardCreateWithTechnicians (SH-364)", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("does not autoassign a staged technician after No technician was explicitly chosen", () => {
|
||||
const created = { ...vendors[0], id: 99, contactName: "Jordan Lee" };
|
||||
expect(
|
||||
buildCreatedTechniciansPatch(
|
||||
draftWith({
|
||||
vendorCompany: "Vinewood LLC",
|
||||
vendorId: "41",
|
||||
technicianAssigned: false,
|
||||
technicianSelectionCleared: true,
|
||||
newTechnicians: [{ name: "Jordan Lee", phone: "" }],
|
||||
}),
|
||||
"Vinewood LLC",
|
||||
[{ name: "Jordan Lee", phone: "" }],
|
||||
[created],
|
||||
),
|
||||
).toEqual({ newTechnicians: [] });
|
||||
});
|
||||
|
||||
it("blocks Create and skips the roster save when a staged phone is not a valid number", async () => {
|
||||
const createMutation = { mutate: vi.fn(), isPending: false };
|
||||
const { result } = renderHook(() =>
|
||||
|
|
|
|||
|
|
@ -77,6 +77,20 @@ describe("mapWizardDraftToBoardCreatePayload", () => {
|
|||
expect(payload).not.toHaveProperty("customer");
|
||||
});
|
||||
|
||||
it("omits wizard-only technician selection intent from the create payload", () => {
|
||||
const payload = mapWizardDraftToBoardCreatePayload({
|
||||
...EMPTY_WIZARD_DRAFT,
|
||||
type: "PM",
|
||||
siteCode: "BK5",
|
||||
vendorId: "41",
|
||||
technicianAssigned: false,
|
||||
technicianSelectionCleared: true,
|
||||
});
|
||||
|
||||
expect(payload).toEqual(expect.objectContaining({ vendorId: 41, technicianAssigned: false }));
|
||||
expect(payload).not.toHaveProperty("technicianSelectionCleared");
|
||||
});
|
||||
|
||||
it("allows create mapping without a service", () => {
|
||||
const payload = mapWizardDraftToBoardCreatePayload({
|
||||
...EMPTY_WIZARD_DRAFT,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue