mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-09-30 06:53:12 +00:00
Merge pull request #228 from Sea-Haven-Industries/fix/ab/sh-379-poc-persist
Persist manual POC edits via dedicated POC endpoint (SH-379)
This commit is contained in:
commit
f25a3dd628
12 changed files with 329 additions and 67 deletions
|
|
@ -30,6 +30,7 @@ export const API_PATHS = {
|
|||
boardCreate: "workorders/board",
|
||||
boardSearch: "workorders/board/search",
|
||||
boardPatch: (id: string | number) => `workorders/${id}/board`,
|
||||
pocPatch: (id: string | number) => `workorders/${id}/poc`,
|
||||
boardDetail: (id: string | number) => `workorders/${id}/detail`,
|
||||
completionTemplates: "workorders/completion-templates",
|
||||
comments: (id: string | number) => `workorders/${id}/comments`,
|
||||
|
|
|
|||
81
src/domain/work-orders/api/work-order-board-patch-api.ts
Normal file
81
src/domain/work-orders/api/work-order-board-patch-api.ts
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import { API_PATHS } from "@/api/api-paths";
|
||||
import { ApiError, mapHttpStatusToMessage } from "@/api/api-error";
|
||||
import { apiRequestRaw } from "@/api/api";
|
||||
import {
|
||||
BoardPatchConflictError,
|
||||
BoardPatchValidationError,
|
||||
} from "@/domain/work-orders/errors/board-patch-errors";
|
||||
import { mapWorkOrderTableRow } from "@/domain/work-orders/mappers/work-order-mapper";
|
||||
import type {
|
||||
WorkOrderBoardPatchPayload,
|
||||
WorkOrderPocPatchPayload,
|
||||
} from "@/domain/work-orders/types/work-order-board";
|
||||
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
|
||||
|
||||
/** Shared request/error handling for board row PATCH endpoints (board + poc). */
|
||||
async function patchBoardRow(
|
||||
operationName: string,
|
||||
method: "patch",
|
||||
url: string,
|
||||
payload: WorkOrderBoardPatchPayload | WorkOrderPocPatchPayload,
|
||||
): Promise<WorkOrderTableRow> {
|
||||
const response = await apiRequestRaw(
|
||||
method,
|
||||
url,
|
||||
{ json: payload, throwHttpErrors: false },
|
||||
operationName,
|
||||
);
|
||||
|
||||
let data: unknown;
|
||||
try {
|
||||
data = await response.json();
|
||||
} catch {
|
||||
data = undefined;
|
||||
}
|
||||
|
||||
if (response.ok) {
|
||||
return mapWorkOrderTableRow(data);
|
||||
}
|
||||
|
||||
const record = typeof data === "object" && data !== null ? (data as Record<string, unknown>) : {};
|
||||
|
||||
if (response.status === 409) {
|
||||
const currentStateRaw = record.currentState ?? record.CurrentState;
|
||||
const currentState = currentStateRaw != null ? mapWorkOrderTableRow(currentStateRaw) : null;
|
||||
throw new BoardPatchConflictError(
|
||||
typeof record.message === "string"
|
||||
? record.message
|
||||
: "The record was modified by another user. Refresh and retry.",
|
||||
currentState,
|
||||
);
|
||||
}
|
||||
|
||||
if (response.status === 422) {
|
||||
throw new BoardPatchValidationError(
|
||||
typeof record.code === "string" ? record.code : "ValidationError",
|
||||
typeof record.message === "string" ? record.message : mapHttpStatusToMessage(422, data),
|
||||
);
|
||||
}
|
||||
|
||||
throw new ApiError(mapHttpStatusToMessage(response.status, data), response.status);
|
||||
}
|
||||
|
||||
export const workOrderBoardPatchApi = {
|
||||
patchBoardField: async (
|
||||
id: string | number,
|
||||
payload: WorkOrderBoardPatchPayload,
|
||||
): Promise<WorkOrderTableRow> =>
|
||||
patchBoardRow(
|
||||
"workOrdersApi.patchBoardField",
|
||||
"patch",
|
||||
API_PATHS.workOrder.boardPatch(id),
|
||||
payload,
|
||||
),
|
||||
|
||||
/** Manual POC override — same row/error contract as the board patch. */
|
||||
updatePoc: async (
|
||||
id: string | number,
|
||||
payload: WorkOrderPocPatchPayload,
|
||||
): Promise<WorkOrderTableRow> =>
|
||||
patchBoardRow("workOrdersApi.updatePoc", "patch", API_PATHS.workOrder.pocPatch(id), payload),
|
||||
};
|
||||
|
|
@ -1,12 +1,8 @@
|
|||
import { API_PATHS } from "@/api/api-paths";
|
||||
import { ApiError, mapHttpStatusToMessage } from "@/api/api-error";
|
||||
import { apiDelete, apiGet, apiPost, apiPostForm, apiPut, apiRequestRaw } from "@/api/api";
|
||||
import { apiDelete, apiGet, apiPost, apiPostForm, apiPut } from "@/api/api";
|
||||
import { handleApiResponse, handlePaginatedResponse } from "@/api/handle-api-response";
|
||||
import {
|
||||
BoardPatchConflictError,
|
||||
BoardPatchValidationError,
|
||||
} from "@/domain/work-orders/errors/board-patch-errors";
|
||||
import { workOrderBoardDocumentsApi } from "@/domain/work-orders/api/work-order-board-documents-api";
|
||||
import { workOrderBoardPatchApi } from "@/domain/work-orders/api/work-order-board-patch-api";
|
||||
import {
|
||||
mapDispatchComment,
|
||||
mapDispatchDetail,
|
||||
|
|
@ -20,7 +16,6 @@ import {
|
|||
mapWorkOrderDetail,
|
||||
mapWorkOrderFormToCreatePayload,
|
||||
mapWorkOrderFormToUpdateFormData,
|
||||
mapWorkOrderTableRow,
|
||||
mapWorkOrderTableRows,
|
||||
} from "@/domain/work-orders/mappers/work-order-mapper";
|
||||
import { mapWizardDraftToBoardCreatePayload } from "@/domain/work-orders/mappers/work-order-board-mapper";
|
||||
|
|
@ -42,7 +37,6 @@ import type {
|
|||
import type { WorkOrderWizardDraft } from "@/domain/work-orders/types/work-order-wizard";
|
||||
import type {
|
||||
WorkOrderBoardListResult,
|
||||
WorkOrderBoardPatchPayload,
|
||||
WorkOrderBoardQueryParams,
|
||||
WorkOrderBoardResponseApi,
|
||||
WorkOrderBoardSearchQueryParams,
|
||||
|
|
@ -310,51 +304,10 @@ export const workOrdersApi = {
|
|||
return mapWorkOrderDetail(handleApiResponse(data));
|
||||
},
|
||||
|
||||
patchBoardField: async (
|
||||
id: string | number,
|
||||
payload: WorkOrderBoardPatchPayload,
|
||||
): Promise<WorkOrderTableRow> => {
|
||||
const response = await apiRequestRaw(
|
||||
"patch",
|
||||
API_PATHS.workOrder.boardPatch(id),
|
||||
{ json: payload, throwHttpErrors: false },
|
||||
"workOrdersApi.patchBoardField",
|
||||
);
|
||||
patchBoardField: workOrderBoardPatchApi.patchBoardField,
|
||||
|
||||
let data: unknown;
|
||||
try {
|
||||
data = await response.json();
|
||||
} catch {
|
||||
data = undefined;
|
||||
}
|
||||
|
||||
if (response.ok) {
|
||||
return mapWorkOrderTableRow(data);
|
||||
}
|
||||
|
||||
const record =
|
||||
typeof data === "object" && data !== null ? (data as Record<string, unknown>) : {};
|
||||
|
||||
if (response.status === 409) {
|
||||
const currentStateRaw = record.currentState ?? record.CurrentState;
|
||||
const currentState = currentStateRaw != null ? mapWorkOrderTableRow(currentStateRaw) : null;
|
||||
throw new BoardPatchConflictError(
|
||||
typeof record.message === "string"
|
||||
? record.message
|
||||
: "The record was modified by another user. Refresh and retry.",
|
||||
currentState,
|
||||
);
|
||||
}
|
||||
|
||||
if (response.status === 422) {
|
||||
throw new BoardPatchValidationError(
|
||||
typeof record.code === "string" ? record.code : "ValidationError",
|
||||
typeof record.message === "string" ? record.message : mapHttpStatusToMessage(422, data),
|
||||
);
|
||||
}
|
||||
|
||||
throw new ApiError(mapHttpStatusToMessage(response.status, data), response.status);
|
||||
},
|
||||
/** Manual POC override — same row/error contract as the board patch. */
|
||||
updatePoc: workOrderBoardPatchApi.updatePoc,
|
||||
|
||||
update: async (
|
||||
id: string | number,
|
||||
|
|
|
|||
|
|
@ -28,9 +28,6 @@ const LOCAL_ONLY_KEYS = new Set<keyof WorkOrderTablePatch>([
|
|||
"techPhone",
|
||||
"location",
|
||||
"locationId",
|
||||
"pocName",
|
||||
"pocPhone",
|
||||
"pocNotes",
|
||||
"title",
|
||||
"priority",
|
||||
"apptStart",
|
||||
|
|
@ -53,16 +50,44 @@ export function isLocalOnlyTablePatch(patch: WorkOrderTablePatch): boolean {
|
|||
return keys.length > 0 && keys.every((key) => LOCAL_ONLY_KEYS.has(key));
|
||||
}
|
||||
|
||||
/** Composite manual POC override op routed to PATCH …/poc. */
|
||||
export type BoardPocOp = {
|
||||
kind: "poc";
|
||||
pocName?: string;
|
||||
pocPhone?: string;
|
||||
pocNotes?: string;
|
||||
};
|
||||
|
||||
export type BoardOp = BoardFieldOp | BoardPocOp;
|
||||
|
||||
function expandPocOp(patch: WorkOrderTablePatch): BoardPocOp[] {
|
||||
const hasPocFields =
|
||||
patch.pocName !== undefined || patch.pocPhone !== undefined || patch.pocNotes !== undefined;
|
||||
if (!hasPocFields) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
kind: "poc",
|
||||
pocName: patch.pocName,
|
||||
pocPhone: patch.pocPhone,
|
||||
pocNotes: patch.pocNotes,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands a UI table patch into ordered board field ops (one PATCH each).
|
||||
* Schedule/week-only fields are emitted first in a stable order.
|
||||
* Expands a UI table patch into ordered board ops (one request each).
|
||||
* Schedule/week-only fields are emitted first in a stable order; the composite
|
||||
* POC op follows, then scalar fields.
|
||||
*/
|
||||
export function expandTablePatchToBoardFieldOps(patch: WorkOrderTablePatch): BoardFieldOp[] {
|
||||
export function expandTablePatchToBoardFieldOps(patch: WorkOrderTablePatch): BoardOp[] {
|
||||
if (isLocalOnlyTablePatch(patch)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [...expandScheduleBoardOps(patch), ...expandScalarBoardOps(patch)];
|
||||
return [...expandScheduleBoardOps(patch), ...expandPocOp(patch), ...expandScalarBoardOps(patch)];
|
||||
}
|
||||
|
||||
export function isDispatchBoardField(field: WorkOrderBoardEditableField): boolean {
|
||||
|
|
|
|||
|
|
@ -134,6 +134,14 @@ export interface WorkOrderBoardPatchPayload {
|
|||
primaryDispatchId?: number | null;
|
||||
}
|
||||
|
||||
/** Payload for PATCH /workorders/{id}/poc (manual POC override). */
|
||||
export interface WorkOrderPocPatchPayload {
|
||||
pocName: string;
|
||||
pocPhone: string;
|
||||
pocNotes: string;
|
||||
workOrderVersion: string;
|
||||
}
|
||||
|
||||
export type WorkOrderBoardEditableField =
|
||||
| "woNumber"
|
||||
| "workOrderType"
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
buildBoardPatchPayload,
|
||||
expandTablePatchToBoardFieldOps,
|
||||
isLocalOnlyTablePatch,
|
||||
type BoardOp,
|
||||
} from "@/domain/work-orders/mappers/work-order-board-patch-mapper";
|
||||
import { mapWorkOrderTableRow } from "@/domain/work-orders/mappers/work-order-mapper";
|
||||
import type {
|
||||
|
|
@ -53,6 +54,30 @@ export type WorkOrderTablePatchHandlers = {
|
|||
type PatchResult =
|
||||
{ kind: "local"; patch: WorkOrderTablePatch } | { kind: "board"; row: WorkOrderTableRow };
|
||||
|
||||
function isPocOp(op: BoardOp): op is Extract<BoardOp, { kind: "poc" }> {
|
||||
return "kind" in op && op.kind === "poc";
|
||||
}
|
||||
|
||||
async function applyBoardOp(
|
||||
id: string | number,
|
||||
op: BoardOp,
|
||||
row: WorkOrderTableRow,
|
||||
): Promise<WorkOrderTableRow> {
|
||||
if (isPocOp(op)) {
|
||||
if (!row.rowVersion) {
|
||||
throw new Error("workOrderVersion is required for board patch");
|
||||
}
|
||||
return workOrdersApi.updatePoc(id, {
|
||||
pocName: op.pocName ?? row.pocName,
|
||||
pocPhone: op.pocPhone ?? row.pocPhone,
|
||||
pocNotes: op.pocNotes ?? row.pocNotes,
|
||||
workOrderVersion: row.rowVersion,
|
||||
});
|
||||
}
|
||||
|
||||
return workOrdersApi.patchBoardField(id, buildBoardPatchPayload(op, row));
|
||||
}
|
||||
|
||||
async function applyBoardFieldOps(
|
||||
id: string | number,
|
||||
patch: WorkOrderTablePatch,
|
||||
|
|
@ -72,7 +97,7 @@ async function applyBoardFieldOps(
|
|||
let completedOps = 0;
|
||||
for (const op of ops) {
|
||||
try {
|
||||
latest = await workOrdersApi.patchBoardField(id, buildBoardPatchPayload(op, latest));
|
||||
latest = await applyBoardOp(id, op, latest);
|
||||
completedOps += 1;
|
||||
} catch (error) {
|
||||
if (completedOps > 0) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ import type {
|
|||
/**
|
||||
* Fields compared for dirty state and included in Save patch.
|
||||
* Only keys that map to board PATCH (or required companions like dispatcherName).
|
||||
* Primary POC scalars and tech phone are not board-editable — exclude so Save cannot no-op.
|
||||
* Primary POC scalars persist via the dedicated POC endpoint;
|
||||
* tech phone is not board-editable — exclude so Save cannot no-op.
|
||||
* additionalContacts is board-editable (SH-218 PATCH field).
|
||||
*/
|
||||
export const SLIDE_OVER_EDIT_KEYS = [
|
||||
|
|
@ -32,6 +33,9 @@ export const SLIDE_OVER_EDIT_KEYS = [
|
|||
"vendorId",
|
||||
// company/tech are display companions; board persists vendor via vendorId only
|
||||
"status",
|
||||
"pocName",
|
||||
"pocPhone",
|
||||
"pocNotes",
|
||||
] as const satisfies ReadonlyArray<keyof WorkOrderTableRow>;
|
||||
|
||||
type EditKey = (typeof SLIDE_OVER_EDIT_KEYS)[number];
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ function DayAssignHarness({
|
|||
dispatcherId: id,
|
||||
dispatcherName: name,
|
||||
});
|
||||
const patched = await patchBoardField(item.id, ops[0]!);
|
||||
const patched = await patchBoardField(item.id, ops[0] as { field: string });
|
||||
setRow({ ...patched, dispatcherId: id, dispatcherName: name });
|
||||
}}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -658,6 +658,100 @@ describe("workOrdersApi.patchBoardField", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("workOrdersApi.updatePoc", () => {
|
||||
beforeEach(() => {
|
||||
apiPatchFn.mockReset();
|
||||
});
|
||||
|
||||
it("PATCHes poc payload to workorders/{id}/poc and maps row", async () => {
|
||||
apiPatchFn.mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
id: 10,
|
||||
siteCode: "BK5",
|
||||
pocName: "Manual POC",
|
||||
pocPhone: "312-555-0001",
|
||||
pocNotes: "Call first",
|
||||
lifecycleStatusLabel: "Scheduled",
|
||||
rowVersion: "AAAAAAAB",
|
||||
dispatchRowVersion: null,
|
||||
primaryDispatchId: null,
|
||||
vendorId: null,
|
||||
}),
|
||||
});
|
||||
|
||||
const row = await workOrdersApi.updatePoc(10, {
|
||||
pocName: "Manual POC",
|
||||
pocPhone: "312-555-0001",
|
||||
pocNotes: "Call first",
|
||||
workOrderVersion: "AAAAAAA=",
|
||||
});
|
||||
|
||||
expect(apiPatchFn).toHaveBeenCalledWith(API_PATHS.workOrder.pocPatch(10), {
|
||||
json: {
|
||||
pocName: "Manual POC",
|
||||
pocPhone: "312-555-0001",
|
||||
pocNotes: "Call first",
|
||||
workOrderVersion: "AAAAAAA=",
|
||||
},
|
||||
throwHttpErrors: false,
|
||||
});
|
||||
expect(row.pocName).toBe("Manual POC");
|
||||
expect(row.rowVersion).toBe("AAAAAAAB");
|
||||
});
|
||||
|
||||
it("throws BoardPatchConflictError with currentState on 409", async () => {
|
||||
apiPatchFn.mockResolvedValue({
|
||||
ok: false,
|
||||
status: 409,
|
||||
json: async () => ({
|
||||
message: "The record was modified by another user. Refresh and retry.",
|
||||
currentState: {
|
||||
id: 10,
|
||||
siteCode: "BK5",
|
||||
rowVersion: "NEWVER==",
|
||||
lifecycleStatusLabel: "Scheduled",
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const { BoardPatchConflictError } =
|
||||
await import("@/domain/work-orders/errors/board-patch-errors");
|
||||
|
||||
await expect(
|
||||
workOrdersApi.updatePoc(10, {
|
||||
pocName: "Manual POC",
|
||||
pocPhone: "312-555-0001",
|
||||
pocNotes: "",
|
||||
workOrderVersion: "STALE==",
|
||||
}),
|
||||
).rejects.toBeInstanceOf(BoardPatchConflictError);
|
||||
});
|
||||
|
||||
it("throws BoardPatchValidationError on 422 ReadOnly", async () => {
|
||||
apiPatchFn.mockResolvedValue({
|
||||
ok: false,
|
||||
status: 422,
|
||||
json: async () => ({
|
||||
code: "ReadOnly",
|
||||
message: "Work order is read-only in its current status.",
|
||||
}),
|
||||
});
|
||||
|
||||
await expect(
|
||||
workOrdersApi.updatePoc(10, {
|
||||
pocName: "Manual POC",
|
||||
pocPhone: "312-555-0001",
|
||||
pocNotes: "",
|
||||
workOrderVersion: "AAAAAAA=",
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
name: "BoardPatchValidationError",
|
||||
code: "ReadOnly",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("workOrdersApi.createComment", () => {
|
||||
beforeEach(() => {
|
||||
apiPost.mockReset();
|
||||
|
|
|
|||
|
|
@ -34,6 +34,12 @@ describe("isLocalOnlyTablePatch", () => {
|
|||
it("treats scheduleEnd and schedMode as local-only overlays", () => {
|
||||
expect(isLocalOnlyTablePatch({ scheduleEnd: "2026-06-12", schedMode: "range" })).toBe(true);
|
||||
});
|
||||
|
||||
it("does not treat POC edits as local-only", () => {
|
||||
expect(isLocalOnlyTablePatch({ pocName: "Pat", pocPhone: "111", pocNotes: "call" })).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("expandTablePatchToBoardFieldOps", () => {
|
||||
|
|
@ -185,7 +191,33 @@ describe("expandTablePatchToBoardFieldOps", () => {
|
|||
pocPhone: "111",
|
||||
pocNotes: "call",
|
||||
}),
|
||||
).toEqual([{ field: "siteCode", value: "BK5" }]);
|
||||
).toEqual([
|
||||
{
|
||||
kind: "poc",
|
||||
pocName: "Pat",
|
||||
pocPhone: "111",
|
||||
pocNotes: "call",
|
||||
},
|
||||
{ field: "siteCode", value: "BK5" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("emits one composite POC op before scalar ops", () => {
|
||||
expect(
|
||||
expandTablePatchToBoardFieldOps({
|
||||
pocName: "Pat",
|
||||
pocNotes: "call first",
|
||||
docStatus: "Yes",
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
kind: "poc",
|
||||
pocName: "Pat",
|
||||
pocPhone: undefined,
|
||||
pocNotes: "call first",
|
||||
},
|
||||
{ field: "docStatus", value: "Yes" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps dueDate and scheduledDate patches independent", () => {
|
||||
|
|
|
|||
|
|
@ -9,12 +9,14 @@ import type {
|
|||
|
||||
const getById = vi.fn();
|
||||
const update = vi.fn();
|
||||
const updatePoc = vi.fn();
|
||||
const patchBoardField = vi.fn();
|
||||
|
||||
vi.mock("@/domain/work-orders/api/work-orders-api", () => ({
|
||||
workOrdersApi: {
|
||||
getById: (...args: unknown[]) => getById(...args),
|
||||
update: (...args: unknown[]) => update(...args),
|
||||
updatePoc: (...args: unknown[]) => updatePoc(...args),
|
||||
patchBoardField: (...args: unknown[]) => patchBoardField(...args),
|
||||
},
|
||||
}));
|
||||
|
|
@ -121,6 +123,7 @@ describe("useWorkOrderTableMutations completion-doc persistence", () => {
|
|||
beforeEach(() => {
|
||||
getById.mockReset();
|
||||
update.mockReset();
|
||||
updatePoc.mockReset();
|
||||
patchBoardField.mockReset();
|
||||
serverState = { ...BASE_SERVER };
|
||||
rowState = { ...BASE_ROW };
|
||||
|
|
@ -829,6 +832,40 @@ describe("useWorkOrderTableMutations type and severity", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("useWorkOrderTableMutations POC persistence", () => {
|
||||
it("fills omitted POC fields from the current row before the full replacement", async () => {
|
||||
const currentRow = {
|
||||
...BASE_ROW,
|
||||
pocName: "Alex",
|
||||
pocPhone: "555-0000",
|
||||
pocNotes: "Gate",
|
||||
};
|
||||
updatePoc.mockResolvedValue({ ...currentRow, pocName: "Jordan", rowVersion: "wo-v2" });
|
||||
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useWorkOrderTableMutations({
|
||||
onPatch: vi.fn(),
|
||||
clearPatch: () => undefined,
|
||||
getRow: () => currentRow,
|
||||
}),
|
||||
{ wrapper: makeWrapper() },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.patchField(42, { pocName: "Jordan" });
|
||||
});
|
||||
|
||||
await waitFor(() => expect(updatePoc).toHaveBeenCalled());
|
||||
expect(updatePoc).toHaveBeenCalledWith(42, {
|
||||
pocName: "Jordan",
|
||||
pocPhone: "555-0000",
|
||||
pocNotes: "Gate",
|
||||
workOrderVersion: "AAAAAAA=",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("useWorkOrderTableMutations audit cache refresh", () => {
|
||||
it("invalidates work-order queries after a successful status transition", async () => {
|
||||
const client = new QueryClient({
|
||||
|
|
|
|||
|
|
@ -67,7 +67,9 @@ describe("slide-over-draft", () => {
|
|||
expect(SLIDE_OVER_EDIT_KEYS).toContain("vendorId");
|
||||
expect(SLIDE_OVER_EDIT_KEYS).toContain("additionalContacts");
|
||||
expect(SLIDE_OVER_EDIT_KEYS).toContain("avetaRequired");
|
||||
expect(SLIDE_OVER_EDIT_KEYS).not.toContain("pocName");
|
||||
expect(SLIDE_OVER_EDIT_KEYS).toContain("pocName");
|
||||
expect(SLIDE_OVER_EDIT_KEYS).toContain("pocPhone");
|
||||
expect(SLIDE_OVER_EDIT_KEYS).toContain("pocNotes");
|
||||
expect(SLIDE_OVER_EDIT_KEYS).not.toContain("locationId");
|
||||
expect(SLIDE_OVER_EDIT_KEYS).not.toContain("techPhone");
|
||||
expect(SLIDE_OVER_EDIT_KEYS).not.toContain("company");
|
||||
|
|
@ -85,11 +87,11 @@ describe("slide-over-draft", () => {
|
|||
expect(buildSlideOverPatch(vendorDraft, row)).toEqual({ vendorId: "v2" });
|
||||
});
|
||||
|
||||
it("ignores POC-only edits for dirty state", () => {
|
||||
it("tracks POC edits for dirty state and patch", () => {
|
||||
const row = baseRow();
|
||||
const draft = baseRow({ pocName: "Jordan", pocPhone: "555-0100" });
|
||||
expect(isSlideOverDraftDirty(draft, row)).toBe(false);
|
||||
expect(buildSlideOverPatch(draft, row)).toEqual({});
|
||||
expect(isSlideOverDraftDirty(draft, row)).toBe(true);
|
||||
expect(buildSlideOverPatch(draft, row)).toEqual({ pocName: "Jordan", pocPhone: "555-0100" });
|
||||
});
|
||||
|
||||
it("detects dirty draft when a tracked field changes", () => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue