fix(work-orders): extract docStatus mapper for governance ratchet

Keep work-order-mapper under the frozen line cap and cap addDispatchSignoff params.
This commit is contained in:
Arthur Bassi 2026-07-29 09:31:15 -03:00
parent 6869ef70ee
commit 2d64c34898
5 changed files with 76 additions and 54 deletions

View file

@ -396,19 +396,19 @@ export const workOrdersApi = {
});
},
addDispatchSignoff: async (
dispatchId: string | number,
signoffType: string,
name: string,
signature: string,
signatureMethod: string,
): Promise<DispatchDetail["signoffs"][number]> => {
addDispatchSignoff: async (params: {
dispatchId: string | number;
signoffType: string;
name: string;
signature: string;
signatureMethod: string;
}): Promise<DispatchDetail["signoffs"][number]> => {
const data = await apiPost<unknown>(API_PATHS.workOrder.addDispatchSignoff, {
DispatchId: dispatchId,
SignoffType: signoffType,
Name: name,
Signature: signature,
SignatureMethod: signatureMethod,
DispatchId: params.dispatchId,
SignoffType: params.signoffType,
Name: params.name,
Signature: params.signature,
SignatureMethod: params.signatureMethod,
});
return mapDispatchSignoff(handleApiResponse(data));
},

View file

@ -0,0 +1,28 @@
import type { CompletionDocStatus } from "@/domain/work-orders/types/work-order-table-row";
/** UI uses `NN`; board/API enum uses `NotNeeded` (also numeric 3). */
export function mapDocStatusToApi(value: string): string {
if (value === "NN") {
return "NotNeeded";
}
return value;
}
export function readDocStatusFromRecord(record: Record<string, unknown>): CompletionDocStatus {
const rawValue =
record.docStatus ?? record.DocStatus ?? record.completionDoc ?? record.CompletionDoc;
if (typeof rawValue === "number") {
if (rawValue === 1) return "Yes";
if (rawValue === 2) return "No";
if (rawValue === 3) return "NN";
}
if (typeof rawValue === "string") {
if (rawValue === "Yes" || rawValue === "No" || rawValue === "NN") {
return rawValue;
}
if (rawValue === "NotNeeded") {
return "NN";
}
}
return "";
}

View file

@ -1,4 +1,9 @@
import { mapApiStatusToWizard } from "@/domain/work-orders/mappers/wizard-draft-mapper";
import {
mapDocStatusToApi,
readDocStatusFromRecord,
} from "@/domain/work-orders/mappers/doc-status-mapper";
import type { UpliftRequest } from "@/domain/uplifts/types/uplift";
import type {
DispatchChecklistItem,
DispatchComment,
@ -17,6 +22,8 @@ import type {
WorkOrderFormValues,
WorkOrderListItem,
} from "@/domain/work-orders/types/work-order";
import type { WorkOrderStatus } from "@/domain/work-orders/types/work-order-common";
import { WORK_ORDER_STATUS_OPTIONS } from "@/domain/work-orders/types/work-order-common";
import type {
CompletionDocStatus,
WorkOrderTableRow,
@ -24,11 +31,10 @@ import type {
import type { WOType } from "@/domain/work-orders/types/work-order-wizard";
import { deriveDayFields } from "@/domain/work-orders/utils/table-day-utils";
import { formatApptRange } from "@/domain/work-orders/utils/wizard-date-utils";
import type { UpliftRequest } from "@/domain/uplifts/types/uplift";
import type { WorkOrderStatus } from "@/domain/work-orders/types/work-order-common";
import { WORK_ORDER_STATUS_OPTIONS } from "@/domain/work-orders/types/work-order-common";
import { toDateInputValue } from "@/lib/time-utils";
export { mapDocStatusToApi };
function asRecord(raw: unknown): Record<string, unknown> {
return typeof raw === "object" && raw !== null ? (raw as Record<string, unknown>) : {};
}
@ -118,30 +124,8 @@ function readWoType(record: Record<string, unknown>): WOType | "" {
return "";
}
/** UI uses `NN`; board/API enum uses `NotNeeded` (also numeric 3). */
export function mapDocStatusToApi(value: string): string {
if (value === "NN") {
return "NotNeeded";
}
return value;
}
function readDocStatus(record: Record<string, unknown>): CompletionDocStatus {
const rawValue =
record.docStatus ?? record.DocStatus ?? record.completionDoc ?? record.CompletionDoc;
if (typeof rawValue === "number") {
if (rawValue === 1) return "Yes";
if (rawValue === 2) return "No";
if (rawValue === 3) return "NN";
}
const raw = readString(record, "docStatus", "DocStatus", "completionDoc", "CompletionDoc");
if (raw === "Yes" || raw === "No" || raw === "NN") {
return raw;
}
if (raw === "NotNeeded") {
return "NN";
}
return "";
return readDocStatusFromRecord(record);
}
export function mapWorkOrderTableRow(raw: unknown): WorkOrderTableRow {

View file

@ -263,7 +263,13 @@ export function useAddDispatchSignoff(
return useMutation({
mutationFn: ({ signoffType, name, signature, signatureMethod }: AddDispatchSignoffInput) =>
workOrdersApi.addDispatchSignoff(dispatchId, signoffType, name, signature, signatureMethod),
workOrdersApi.addDispatchSignoff({
dispatchId,
signoffType,
name,
signature,
signatureMethod,
}),
onSuccess: (_data, variables) => {
void queryClient.invalidateQueries({ queryKey: queryKeys.workOrders.dispatch(dispatchId) });
toast.success(`${variables.signoffType} sign-off collected`);

View file

@ -72,7 +72,6 @@ const BASE_DETAIL: WorkOrderDetail = {
describe("useWorkOrderTableMutations completion-doc persistence", () => {
let serverState: WorkOrderDetail;
let events: string[];
let lastBoardPatch: { field: string; value?: string | null; workOrderVersion: string } | null;
beforeEach(() => {
getById.mockReset();
@ -83,7 +82,6 @@ describe("useWorkOrderTableMutations completion-doc persistence", () => {
patchBoardField.mockReset();
serverState = { ...BASE_DETAIL };
events = [];
lastBoardPatch = null;
resolveBoardWorkOrderVersion.mockImplementation(async () => {
events.push("resolveVersion:start");
@ -92,19 +90,25 @@ describe("useWorkOrderTableMutations completion-doc persistence", () => {
return "AAAAAAA=";
});
patchBoardField.mockImplementation(async (_id: unknown, payload: typeof lastBoardPatch) => {
events.push("boardPatch:start");
await delay(5);
lastBoardPatch = payload;
if (payload?.field === "docStatus" && typeof payload.value === "string") {
serverState = {
...serverState,
docStatus:
payload.value === "NotNeeded" ? "NN" : (payload.value as WorkOrderDetail["docStatus"]),
};
}
events.push("boardPatch:end");
});
patchBoardField.mockImplementation(
async (
_id: unknown,
payload: { field: string; value?: string | null; workOrderVersion: string },
) => {
events.push("boardPatch:start");
await delay(5);
if (payload.field === "docStatus" && typeof payload.value === "string") {
serverState = {
...serverState,
docStatus:
payload.value === "NotNeeded"
? "NN"
: (payload.value as WorkOrderDetail["docStatus"]),
};
}
events.push("boardPatch:end");
},
);
getById.mockImplementation(async () => {
events.push("getById:start");