shoc-frontend-new/src/app/(protected)/workorders/_hooks/use-dispatch-detail-controller.ts

182 lines
5.8 KiB
TypeScript
Raw Normal View History

import { useMemo, useState } from "react";
import type { DispatchDetail } from "@/domain/work-orders/types/work-order";
2026-08-14 11:52:48 -03:00
import { useDispatchUpliftController } from "@/app/(protected)/workorders/_hooks/use-dispatch-uplift-controller";
import { useDispatchDetail } from "@/domain/work-orders/use-cases/use-work-order-detail";
import {
useAddChecklistItem,
useAddDispatchComment,
useAddDispatchSignoff,
useUpdateChecklistItem,
useUpdateDispatch,
useVerifyDispatch,
} from "@/domain/work-orders/use-cases/use-work-order-mutations";
import { isDispatchLocked } from "@/domain/work-orders/utils/is-dispatch-locked";
import { useAuthContext } from "@/providers/auth-context";
import { toDateInputValue } from "@/lib/time-utils";
import { buildDispatchEditForm } from "@/app/(protected)/workorders/_components/dispatch/dispatch-detail/build-dispatch-edit-form";
import type { DispatchEditFormState } from "@/app/(protected)/workorders/_components/dispatch/dispatch-detail/dispatch-edit-form";
function buildVerifyState(dispatch?: DispatchDetail) {
if (!dispatch) {
return { canVerify: false, missing: [] as string[] };
}
const checklistTotal = dispatch.checklistItems.length;
const checklistDone = dispatch.checklistItems.filter((item) => item.isCompleted).length;
const signoffTypes = new Set(dispatch.signoffs.map((signoff) => signoff.signoffType));
const missing: string[] = [];
if (checklistDone < checklistTotal) {
missing.push(`${checklistTotal - checklistDone} checklist item(s)`);
}
if (!signoffTypes.has("Customer")) {
missing.push("Customer sign-off");
}
if (!signoffTypes.has("Vendor")) {
missing.push("Vendor sign-off");
}
return {
missing,
canVerify: missing.length === 0 && (checklistTotal > 0 || dispatch.signoffs.length >= 2),
};
}
export function useDispatchDetailController(
dispatchId: string | number,
workOrderId: string | number,
) {
const { user } = useAuthContext();
2026-08-14 11:52:48 -03:00
const { data: dispatch, isLoading, isFetching, refetch } = useDispatchDetail(dispatchId);
const uplift = useDispatchUpliftController(dispatchId);
const [editing, setEditing] = useState(false);
const [form, setForm] = useState<DispatchEditFormState>({
status: "",
nteAmount: "",
scheduledDate: "",
completedDate: "",
description: "",
});
const [newChecklistItem, setNewChecklistItem] = useState("");
const [commentText, setCommentText] = useState("");
const [sendEmail, setSendEmail] = useState(true);
const [sigCaptureType, setSigCaptureType] = useState<string | null>(null);
const updateDispatchMutation = useUpdateDispatch(dispatchId, workOrderId);
const addCommentMutation = useAddDispatchComment(dispatchId);
const updateChecklistMutation = useUpdateChecklistItem(dispatchId);
const addChecklistMutation = useAddChecklistItem(dispatchId);
const addSignoffMutation = useAddDispatchSignoff(dispatchId);
const verifyMutation = useVerifyDispatch(dispatchId, workOrderId);
const userName =
(typeof user?.name === "string" ? user.name : undefined) ??
(typeof user?.fullname === "string" ? user.fullname : undefined) ??
"User";
const isLocked = dispatch ? isDispatchLocked(dispatch) : false;
const verifyState = useMemo(() => buildVerifyState(dispatch), [dispatch]);
const beginEditing = () => {
if (dispatch) {
setForm(buildDispatchEditForm(dispatch));
}
setEditing(true);
};
const handleSave = () => {
updateDispatchMutation.mutate(
{
id: dispatchId,
status: form.status,
nteAmount: form.nteAmount ? parseFloat(form.nteAmount) : null,
scheduledDate: form.scheduledDate || null,
completedDate: form.completedDate || null,
description: form.description,
},
{ onSuccess: () => setEditing(false) },
);
};
const handleCancelDispatch = (current: DispatchDetail) => {
if (!window.confirm("Are you sure you want to cancel this dispatch?")) {
return;
}
updateDispatchMutation.mutate({
id: dispatchId,
status: "Cancelled",
nteAmount: current.nteAmount,
scheduledDate: toDateInputValue(current.scheduledDate) || null,
completedDate: toDateInputValue(current.completedDate) || null,
description: current.description,
});
};
const handleComment = (event: React.FormEvent) => {
event.preventDefault();
if (!commentText.trim()) {
return;
}
addCommentMutation.mutate(
{ text: commentText.trim(), sendEmail },
{ onSuccess: () => setCommentText("") },
);
};
2026-08-14 11:52:48 -03:00
const isMutating =
updateDispatchMutation.isPending ||
addCommentMutation.isPending ||
updateChecklistMutation.isPending ||
addChecklistMutation.isPending ||
addSignoffMutation.isPending ||
verifyMutation.isPending ||
uplift.isMutating;
return {
dispatch,
isLoading,
2026-08-14 11:52:48 -03:00
isFetching,
refetch,
uplifts: uplift.uplifts,
isLoadingUplifts: uplift.isLoadingUplifts,
editing,
form,
setForm,
newChecklistItem,
setNewChecklistItem,
commentText,
setCommentText,
sendEmail,
setSendEmail,
sigCaptureType,
setSigCaptureType,
2026-08-14 11:52:48 -03:00
upliftAction: uplift.upliftAction,
setUpliftAction: uplift.setUpliftAction,
isLocked,
2026-08-14 11:52:48 -03:00
isMutating,
userName,
verifyState,
beginEditing,
handleSave,
handleCancelDispatch,
handleComment,
2026-08-14 11:52:48 -03:00
handleUpliftConfirm: uplift.handleUpliftConfirm,
setEditing,
updateDispatchMutation,
updateChecklistMutation,
addChecklistMutation,
addSignoffMutation,
verifyMutation,
2026-08-14 11:52:48 -03:00
approveUpliftMutation: uplift.approveUpliftMutation,
rejectUpliftMutation: uplift.rejectUpliftMutation,
requestChangesUpliftMutation: uplift.requestChangesUpliftMutation,
addCommentMutation,
};
}
2026-08-14 11:52:48 -03:00
export type DispatchDetailController = ReturnType<typeof useDispatchDetailController>;