diff --git a/src/app/(protected)/workorders/_components/detail/tabs/slide-over-info-tab-edit-view.tsx b/src/app/(protected)/workorders/_components/detail/tabs/slide-over-info-tab-edit-view.tsx
index da8b4264..2a6afbb3 100644
--- a/src/app/(protected)/workorders/_components/detail/tabs/slide-over-info-tab-edit-view.tsx
+++ b/src/app/(protected)/workorders/_components/detail/tabs/slide-over-info-tab-edit-view.tsx
@@ -17,7 +17,7 @@ import type {
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
import type { WizardWOStatus } from "@/domain/work-orders/types/work-order-wizard";
import type { Service } from "@/domain/services/types/service";
-import { isServiceEligibleForWorkOrderType } from "@/domain/services/utils/services-for-work-order-type";
+import { serviceSupportsWorkOrderType } from "@/domain/services/utils/services-for-work-order-type";
import { serviceIdentityPatch } from "@/domain/work-orders/utils/service-identity-patch";
import { deriveDayFields } from "@/domain/work-orders/utils/table-day-utils";
import { toVendorTablePatch } from "@/domain/work-orders/utils/vendor-assignment-patch";
@@ -180,7 +180,7 @@ export function SlideOverInfoTabEditView({
onDraftChange(
draft.serviceId != null &&
selectedService != null &&
- !isServiceEligibleForWorkOrderType(selectedService, type)
+ !serviceSupportsWorkOrderType(selectedService, type)
? { type, severity, serviceId: null, pm: "" }
: { type, severity },
);
diff --git a/src/app/(protected)/workorders/_components/list/table/save-existing-row-type.ts b/src/app/(protected)/workorders/_components/list/table/save-existing-row-type.ts
index bc32d6df..6e5e0bf7 100644
--- a/src/app/(protected)/workorders/_components/list/table/save-existing-row-type.ts
+++ b/src/app/(protected)/workorders/_components/list/table/save-existing-row-type.ts
@@ -6,7 +6,7 @@ import type {
} from "@/domain/work-orders/types/work-order-table-row";
import type { WOType } from "@/domain/work-orders/types/work-order-wizard";
import type { Service } from "@/domain/services/types/service";
-import { isServiceEligibleForWorkOrderType } from "@/domain/services/utils/services-for-work-order-type";
+import { serviceSupportsWorkOrderType } from "@/domain/services/utils/services-for-work-order-type";
import {
formatSeverityEscalationToast,
shouldAnnounceSeverityEscalation,
@@ -31,7 +31,7 @@ export function saveExistingRowType(
const clearService =
row.serviceId != null &&
selectedService != null &&
- !isServiceEligibleForWorkOrderType(selectedService, type);
+ !serviceSupportsWorkOrderType(selectedService, type);
const patch = clearService ? { type, severity, serviceId: null, pm: "" } : { type, severity };
if (!shouldAnnounceSeverityEscalation(row.severity, severity)) {
onPatch(row.id, patch);
diff --git a/src/domain/services/utils/services-for-work-order-type.ts b/src/domain/services/utils/services-for-work-order-type.ts
index c11641cf..6ba6096a 100644
--- a/src/domain/services/utils/services-for-work-order-type.ts
+++ b/src/domain/services/utils/services-for-work-order-type.ts
@@ -10,5 +10,10 @@ export function servicesForWorkOrderType(services: Service[], type: WOType | "")
}
export function isServiceEligibleForWorkOrderType(service: Service, type: WOType | ""): boolean {
- return service.isActive && service.supportedWorkOrderTypes.includes(serviceWorkOrderType(type));
+ return service.isActive && serviceSupportsWorkOrderType(service, type);
+}
+
+/** Whether a service definition supports a WO type, regardless of active assignment status. */
+export function serviceSupportsWorkOrderType(service: Service, type: WOType | ""): boolean {
+ return service.supportedWorkOrderTypes.includes(serviceWorkOrderType(type));
}
diff --git a/src/test/app/(protected)/workorders/save-existing-row-type.test.ts b/src/test/app/(protected)/workorders/save-existing-row-type.test.ts
index 7754c9cf..11809966 100644
--- a/src/test/app/(protected)/workorders/save-existing-row-type.test.ts
+++ b/src/test/app/(protected)/workorders/save-existing-row-type.test.ts
@@ -78,16 +78,36 @@ describe("saveExistingRowType", () => {
},
];
- saveExistingRowType(onPatch, { ...row, serviceId: 701 }, "Reactive", null, services);
+ saveExistingRowType(onPatch, { ...row, serviceId: 701 }, "Emergency", null, services);
expect(onPatch).toHaveBeenCalledWith(7, {
- type: "Reactive",
+ type: "Emergency",
severity: null,
serviceId: null,
pm: "",
});
});
+ it("preserves a known inactive historical service that supports the new type", () => {
+ const onPatch = vi.fn();
+ const services: Service[] = [
+ {
+ id: 702,
+ name: "Retired Multi-type Service",
+ trade: "Electrical",
+ iconKey: "wrench",
+ requiresCompletionDocument: false,
+ completionDocTemplate: null,
+ isActive: false,
+ supportedWorkOrderTypes: ["PM", "Reactive"],
+ },
+ ];
+
+ saveExistingRowType(onPatch, { ...row, serviceId: 702 }, "Reactive", null, services);
+
+ expect(onPatch).toHaveBeenCalledWith(7, { type: "Reactive", severity: null });
+ });
+
it("clears a known inactive historical service before type change", () => {
const onPatch = vi.fn();
const services: Service[] = [
@@ -103,10 +123,10 @@ describe("saveExistingRowType", () => {
},
];
- saveExistingRowType(onPatch, { ...row, serviceId: 701 }, "Reactive", null, services);
+ saveExistingRowType(onPatch, { ...row, serviceId: 701 }, "Emergency", null, services);
expect(onPatch).toHaveBeenCalledWith(7, {
- type: "Reactive",
+ type: "Emergency",
severity: null,
serviceId: null,
pm: "",
diff --git a/src/test/app/(protected)/workorders/slide-over-info-tab-edit-view-additional-contacts.test.tsx b/src/test/app/(protected)/workorders/slide-over-info-tab-edit-view-additional-contacts.test.tsx
index 19b7be33..8457d0ac 100644
--- a/src/test/app/(protected)/workorders/slide-over-info-tab-edit-view-additional-contacts.test.tsx
+++ b/src/test/app/(protected)/workorders/slide-over-info-tab-edit-view-additional-contacts.test.tsx
@@ -50,10 +50,19 @@ vi.mock(
}),
);
vi.mock("@/app/(protected)/workorders/_components/list/table/cells/type-cell", () => ({
- TypeCell: ({ onSave }: { onSave: (type: "Reactive", severity: number) => void }) => (
-
+ TypeCell: ({
+ onSave,
+ }: {
+ onSave: (type: "Reactive" | "Emergency", severity: number) => void;
+ }) => (
+ <>
+
+
+ >
),
}));
vi.mock("@/app/(protected)/workorders/_components/list/table/cells/assigned-to-cell", () => ({
@@ -227,12 +236,63 @@ describe("SlideOverInfoTabEditView additional contacts", () => {
}
renderWithProviders(, { withAuth: false });
- fireEvent.click(screen.getByRole("button", { name: "Change type to Reactive" }));
+ fireEvent.click(screen.getByRole("button", { name: "Change type to Emergency" }));
fireEvent.click(screen.getByRole("button", { name: "Save changes" }));
expect(onSave).toHaveBeenCalledWith([
{ field: "severity", value: "2" },
{ field: "serviceId", value: "" },
+ { field: "workOrderType", value: "Emergency" },
+ ]);
+ });
+
+ it("preserves a compatible inactive service during a slide-over type change", () => {
+ const baseline = baseRow({ type: "PM", pm: "Historical service", serviceId: 702 });
+ const onSave = vi.fn();
+ const compatibleInactiveService = {
+ id: 702,
+ name: "Historical service",
+ trade: "Electrical",
+ iconKey: "wrench",
+ requiresCompletionDocument: false,
+ completionDocTemplate: null,
+ isActive: false,
+ supportedWorkOrderTypes: ["PM" as const, "Reactive" as const],
+ };
+
+ function SlideOverSaveHarness() {
+ const [draft, setDraft] = useState(baseline);
+ return (
+ <>
+ setDraft((current) => ({ ...current, ...patch }))}
+ />
+
+ >
+ );
+ }
+
+ renderWithProviders(, { 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: "workOrderType", value: "Reactive" },
]);
});