2026-07-17 12:01:45 -03:00
|
|
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
|
|
|
import { toast } from "react-toastify";
|
|
|
|
|
import { workOrdersApi } from "@/domain/work-orders/api/work-orders-api";
|
|
|
|
|
import {
|
|
|
|
|
mapApiStatusToWizard,
|
|
|
|
|
mapWizardStatusToApi,
|
|
|
|
|
} from "@/domain/work-orders/mappers/wizard-draft-mapper";
|
|
|
|
|
import {
|
|
|
|
|
mapWorkOrderDetailToFormValues,
|
|
|
|
|
mapWorkOrderTableRow,
|
|
|
|
|
} from "@/domain/work-orders/mappers/work-order-mapper";
|
|
|
|
|
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
|
|
|
|
|
import type { WizardWOStatus } from "@/domain/work-orders/types/work-order-wizard";
|
|
|
|
|
import { deriveDayFields } from "@/domain/work-orders/utils/table-day-utils";
|
|
|
|
|
import { enrichTableRow } from "@/domain/work-orders/utils/table-filter-utils";
|
|
|
|
|
import { queryKeys } from "@/infra/query-key/query-key";
|
|
|
|
|
|
|
|
|
|
export type WorkOrderTablePatch = Partial<WorkOrderTableRow>;
|
|
|
|
|
|
|
|
|
|
async function loadFormAndUpdate(
|
|
|
|
|
id: string | number,
|
|
|
|
|
patch: (
|
|
|
|
|
values: ReturnType<typeof mapWorkOrderDetailToFormValues>,
|
|
|
|
|
) => ReturnType<typeof mapWorkOrderDetailToFormValues>,
|
|
|
|
|
) {
|
|
|
|
|
const detail = await workOrdersApi.getById(id);
|
|
|
|
|
const values = mapWorkOrderDetailToFormValues(detail);
|
|
|
|
|
const next = patch(values);
|
|
|
|
|
await workOrdersApi.update(id, next);
|
|
|
|
|
return detail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useWorkOrderTableMutations(
|
|
|
|
|
onPatch: (id: string | number, patch: WorkOrderTablePatch) => void,
|
|
|
|
|
) {
|
|
|
|
|
const queryClient = useQueryClient();
|
|
|
|
|
|
|
|
|
|
const invalidate = () => {
|
|
|
|
|
void queryClient.invalidateQueries({ queryKey: queryKeys.workOrders.all });
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const patchMutation = useMutation({
|
|
|
|
|
mutationFn: async ({
|
|
|
|
|
id,
|
|
|
|
|
patch,
|
|
|
|
|
useFullUpdate,
|
|
|
|
|
}: {
|
|
|
|
|
id: string | number;
|
|
|
|
|
patch: WorkOrderTablePatch;
|
|
|
|
|
useFullUpdate?: boolean;
|
|
|
|
|
}) => {
|
|
|
|
|
if (patch.status !== undefined && !useFullUpdate) {
|
|
|
|
|
const apiStatus = mapWizardStatusToApi(patch.status as WizardWOStatus);
|
|
|
|
|
await workOrdersApi.changeStatus(id, apiStatus);
|
|
|
|
|
return patch;
|
|
|
|
|
}
|
|
|
|
|
if (patch.dispatcherId !== undefined && !useFullUpdate) {
|
|
|
|
|
await workOrdersApi.changeAssignment(id, patch.dispatcherId || "");
|
|
|
|
|
return {
|
|
|
|
|
...patch,
|
|
|
|
|
dispatcherName: patch.dispatcherName ?? "",
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
await loadFormAndUpdate(id, (values) => ({
|
|
|
|
|
...values,
|
|
|
|
|
workOrderNumber: patch.woNumber ?? values.workOrderNumber,
|
|
|
|
|
internalWONumber: patch.woNumber ?? values.internalWONumber,
|
|
|
|
|
locationId: patch.locationId != null ? String(patch.locationId) : values.locationId,
|
|
|
|
|
dueDate: patch.dueDate ?? values.dueDate,
|
|
|
|
|
scheduledDate: patch.scheduledOn ?? values.scheduledDate,
|
2026-07-27 09:47:46 -03:00
|
|
|
completedDate: patch.completedDate ?? values.completedDate,
|
2026-07-17 12:01:45 -03:00
|
|
|
assignedTo: patch.dispatcherName ?? values.assignedTo,
|
|
|
|
|
problem: patch.pm ?? values.problem,
|
|
|
|
|
source: patch.type ?? values.source,
|
|
|
|
|
priority: values.priority,
|
|
|
|
|
status:
|
|
|
|
|
patch.status !== undefined
|
|
|
|
|
? mapWizardStatusToApi(patch.status as WizardWOStatus)
|
|
|
|
|
: values.status,
|
|
|
|
|
description: [
|
|
|
|
|
values.description,
|
|
|
|
|
patch.pocNotes ? `POC notes: ${patch.pocNotes}` : "",
|
|
|
|
|
patch.pocName || patch.pocPhone
|
|
|
|
|
? `POC: ${[patch.pocName, patch.pocPhone].filter(Boolean).join(" · ")}`
|
|
|
|
|
: "",
|
|
|
|
|
]
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
.join("\n"),
|
|
|
|
|
}));
|
|
|
|
|
return patch;
|
|
|
|
|
},
|
|
|
|
|
onMutate: async ({ id, patch }) => {
|
|
|
|
|
onPatch(id, patch);
|
|
|
|
|
},
|
|
|
|
|
onSuccess: () => {
|
|
|
|
|
invalidate();
|
|
|
|
|
},
|
|
|
|
|
onError: (error: Error) => {
|
|
|
|
|
toast.error(error.message || "Failed to update work order");
|
|
|
|
|
invalidate();
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const patchField = (id: string | number, patch: WorkOrderTablePatch, useFullUpdate = false) => {
|
|
|
|
|
patchMutation.mutate({ id, patch, useFullUpdate });
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
patchField,
|
|
|
|
|
isPending: patchMutation.isPending,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function tableRowFromDetail(detail: Awaited<ReturnType<typeof workOrdersApi.getById>>) {
|
|
|
|
|
return enrichTableRow(
|
|
|
|
|
mapWorkOrderTableRow({
|
|
|
|
|
...detail,
|
|
|
|
|
status: mapApiStatusToWizard(detail.status),
|
|
|
|
|
scheduledOn: detail.scheduledDate,
|
|
|
|
|
woNumber: detail.internalWONumber || detail.workOrderNumber,
|
|
|
|
|
dispatcherName: detail.assignedTo,
|
|
|
|
|
pm: detail.problem,
|
|
|
|
|
type: detail.source,
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function maybeAutoSchedulePatch(row: WorkOrderTableRow): WorkOrderTablePatch {
|
|
|
|
|
const hasDate = !!row.scheduledOn;
|
|
|
|
|
const hasDispatcher = !!row.dispatcherId;
|
|
|
|
|
if (hasDate && hasDispatcher && row.status === "Incomplete") {
|
|
|
|
|
return { status: "Scheduled" };
|
|
|
|
|
}
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function applySchedulePatch(
|
|
|
|
|
scheduledOn: string,
|
|
|
|
|
scheduleWeekOnly: boolean,
|
|
|
|
|
): WorkOrderTablePatch {
|
|
|
|
|
if (scheduleWeekOnly) {
|
|
|
|
|
return {
|
|
|
|
|
scheduledOn: "",
|
|
|
|
|
targetWeek: scheduledOn,
|
|
|
|
|
scheduleWeekOnly: true,
|
|
|
|
|
...deriveDayFields(""),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return {
|
|
|
|
|
scheduledOn,
|
|
|
|
|
scheduleWeekOnly: false,
|
|
|
|
|
targetWeek: "",
|
|
|
|
|
...deriveDayFields(scheduledOn),
|
|
|
|
|
};
|
|
|
|
|
}
|