shoc-frontend-new/src/test/app/(protected)/workorders/slide-over-info-tab-edit-view-additional-contacts.test.tsx

251 lines
7.6 KiB
TypeScript

import { useState } from "react";
import { fireEvent, screen } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import { SlideOverInfoTabEditView } from "@/app/(protected)/workorders/_components/detail/tabs/slide-over-info-tab-edit-view";
import { SlideOverInfoTabDisplayView } from "@/app/(protected)/workorders/_components/detail/tabs/slide-over-info-tab-display-view";
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
import { expandTablePatchToBoardFieldOps } from "@/domain/work-orders/mappers/work-order-board-patch-mapper";
import { buildSlideOverPatch } from "@/domain/work-orders/utils/slide-over-draft";
import { renderWithProviders } from "@/test/test-utils";
vi.mock("@/app/(protected)/workorders/_components/list/table/cells/site-cell", () => ({
SiteCell: ({
additionalContacts,
onSave,
}: {
additionalContacts?: { name: string; phone: string }[];
onSave: (patch: {
site: string;
location: string;
locationId: string;
pocName: string;
pocPhone: string;
pocNotes: string;
additionalContacts?: { name: string; phone: string }[];
}) => void;
}) => (
<button
type="button"
onClick={() =>
onSave({
site: "SEA1",
location: "Seattle",
locationId: "loc-1",
pocName: "Pat",
pocPhone: "111",
pocNotes: "",
additionalContacts: [{ name: "Backup", phone: "555-0101" }],
})
}
>
save site {additionalContacts?.length ?? 0}
</button>
),
}));
vi.mock(
"@/app/(protected)/workorders/_components/list/table/cells/editable-wo-number-cell",
() => ({
EditableWONumberCell: () => null,
}),
);
vi.mock("@/app/(protected)/workorders/_components/list/table/cells/type-cell", () => ({
TypeCell: ({ onSave }: { onSave: (type: "Reactive", severity: number) => void }) => (
<button type="button" onClick={() => onSave("Reactive", 2)}>
Change type to Reactive
</button>
),
}));
vi.mock("@/app/(protected)/workorders/_components/list/table/cells/assigned-to-cell", () => ({
AssignedToCell: () => null,
}));
vi.mock("@/app/(protected)/workorders/_components/list/table/cells/date-cell", () => ({
DateCell: () => null,
}));
vi.mock("@/app/(protected)/workorders/_components/list/table/cells/appt-time-cell", () => ({
ApptTimeCell: () => null,
}));
vi.mock("@/app/(protected)/workorders/_components/list/table/cells/pm-type-cell", () => ({
PMTypeCell: () => null,
}));
vi.mock("@/app/(protected)/workorders/_components/list/table/cells/vendor-cell", () => ({
VendorCell: () => null,
}));
vi.mock("@/app/(protected)/workorders/_components/list/table/cells/status-cell", () => ({
StatusCell: () => null,
}));
function baseRow(overrides: Partial<WorkOrderTableRow> = {}): WorkOrderTableRow {
return {
id: 42,
woNumber: "WO-42",
site: "SEA1",
type: "PM",
severity: null,
dispatcherId: "u1",
dispatcherName: "Dispatcher",
scheduledOn: "2026-07-20",
completedDate: "",
targetWeek: "",
dayGroup: "07/20",
dayLabel: "Mon",
location: "Seattle",
locationId: "loc-1",
pm: "HVAC",
serviceNotes: "",
extraServices: [],
company: "Vendor Co",
tech: "Tech",
techPhone: "",
vendorId: "v1",
apptStart: "",
apptEnd: "",
apptTime: "",
status: "Scheduled",
docStatus: "No",
pocName: "",
pocPhone: "",
pocNotes: "",
additionalContacts: [],
dueDate: "",
rescheduleCount: 0,
carriedOver: 0,
isAddOn: false,
originalWeek: "",
originalDate: "",
isPastDue: false,
scheduleWeekOnly: false,
mediaCount: 0,
title: "",
priority: "",
primaryDispatchId: null,
rowVersion: "v1",
dispatchRowVersion: "",
flagColor: null,
...overrides,
};
}
describe("SlideOverInfoTabEditView additional contacts", () => {
it("keeps the historic service label in slide-over display for an inactive service", () => {
renderWithProviders(
<SlideOverInfoTabDisplayView
source={baseRow({ pm: "Historic Service Name", serviceId: 701 })}
isCancelled={false}
sites={[]}
vendors={[]}
pmTypes={[]}
services={[
{
id: 701,
name: "Renamed Service",
trade: "Electrical",
iconKey: "wrench",
requiresCompletionDocument: false,
completionDocTemplate: null,
isActive: false,
supportedWorkOrderTypes: ["PM"],
},
]}
/>,
{ withAuth: false },
);
expect(screen.getByText("Historic Service Name")).toBeInTheDocument();
expect(screen.queryByText("Renamed Service")).not.toBeInTheDocument();
});
it("saves an incompatible type change only after clearing the registry service", () => {
const baseline = baseRow({ type: "PM", pm: "PM only", serviceId: 701 });
const onSave = vi.fn();
const pmOnlyService = {
id: 701,
name: "PM only",
trade: "Electrical",
iconKey: "wrench",
requiresCompletionDocument: false,
completionDocTemplate: null,
isActive: true,
supportedWorkOrderTypes: ["PM" as const],
};
function SlideOverSaveHarness() {
const [draft, setDraft] = useState(baseline);
return (
<>
<SlideOverInfoTabEditView
draft={draft}
users={[]}
sites={[]}
vendors={[]}
pmTypes={[]}
services={[pmOnlyService]}
allOrders={[]}
onDraftChange={(patch) => setDraft((current) => ({ ...current, ...patch }))}
/>
<button
type="button"
onClick={() =>
onSave(expandTablePatchToBoardFieldOps(buildSlideOverPatch(draft, baseline)))
}
>
Save changes
</button>
</>
);
}
renderWithProviders(<SlideOverSaveHarness />, { withAuth: false });
fireEvent.click(screen.getByRole("button", { name: "Change type to Reactive" }));
fireEvent.click(screen.getByRole("button", { name: "Save changes" }));
expect(onSave).toHaveBeenCalledWith([
{ field: "severity", value: "2" },
{ field: "serviceId", value: "" },
{ field: "workOrderType", value: "Reactive" },
]);
});
it("passes additionalContacts into SiteCell and persists them on save", () => {
const onDraftChange = vi.fn();
const contacts = [{ name: "Existing", phone: "555-0000" }];
renderWithProviders(
<SlideOverInfoTabEditView
draft={baseRow({ additionalContacts: contacts })}
users={[]}
sites={[]}
vendors={[]}
pmTypes={[]}
allOrders={[]}
onDraftChange={onDraftChange}
/>,
{ withAuth: false },
);
expect(screen.getByText("Appt Time (optional)")).toBeInTheDocument();
expect(screen.getByRole("button", { name: /save site 1/i })).toBeInTheDocument();
fireEvent.click(screen.getByRole("button", { name: /save site 1/i }));
expect(onDraftChange).toHaveBeenCalledWith(
expect.objectContaining({
additionalContacts: [{ name: "Backup", phone: "555-0101" }],
}),
);
});
it("includes additionalContacts in the slide-over board PATCH ops", () => {
const baseline = baseRow();
const draft = baseRow({
additionalContacts: [{ name: "Backup", phone: "555-0101", notes: "Gate" }],
});
const patch = buildSlideOverPatch(draft, baseline);
expect(expandTablePatchToBoardFieldOps(patch)).toEqual([
{
field: "additionalContacts",
value: JSON.stringify([{ name: "Backup", phone: "555-0101", notes: "Gate" }]),
},
]);
});
});