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

198 lines
5.9 KiB
TypeScript
Raw Normal View History

import { useMemo, useState } from "react";
import {
useApproveUplift,
useDenyUplift,
useUpliftsForDispatch,
} from "@/domain/uplifts/use-cases/use-uplift-actions";
import type { UpliftRequest } from "@/domain/uplifts/types/uplift";
import type { DispatchDetail } from "@/domain/work-orders/types/work-order";
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";
export function useDispatchDetailController(
dispatchId: string | number,
workOrderId: string | number,
) {
const { user } = useAuthContext();
const { data: dispatch, isLoading } = useDispatchDetail(dispatchId);
const { data: uplifts = [] } = useUpliftsForDispatch(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 [upliftAction, setUpliftAction] = useState<{
type: "approve" | "deny";
uplift: UpliftRequest;
} | 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 approveUpliftMutation = useApproveUplift(dispatchId);
const denyUpliftMutation = useDenyUplift(dispatchId);
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(() => {
if (!dispatch) {
return { canVerify: false, missing: [] as string[] };
}
const checklistTotal = dispatch.checklistItems.length;
const checklistDone = dispatch.checklistItems.filter((item) => item.isCompleted).length;
const signoffCount = dispatch.signoffs.length;
const missing: string[] = [];
if (checklistTotal > 0 && checklistDone < checklistTotal) {
missing.push(`${checklistTotal - checklistDone} checklist item(s)`);
}
if (signoffCount < 2) {
const types = dispatch.signoffs.map((signoff) => signoff.signoffType);
if (!types.includes("Customer")) {
missing.push("Customer sign-off");
}
if (!types.includes("Vendor")) {
missing.push("Vendor sign-off");
}
}
return {
missing,
canVerify: missing.length === 0 && (checklistTotal > 0 || signoffCount >= 2),
};
}, [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("") },
);
};
const handleUpliftConfirm = (note: string) => {
if (!upliftAction) {
return;
}
if (upliftAction.type === "approve") {
approveUpliftMutation.mutate(
{ id: upliftAction.uplift.id, note },
{ onSuccess: () => setUpliftAction(null) },
);
return;
}
denyUpliftMutation.mutate(
{ id: upliftAction.uplift.id, note },
{ onSuccess: () => setUpliftAction(null) },
);
};
return {
dispatch,
isLoading,
uplifts,
editing,
form,
setForm,
newChecklistItem,
setNewChecklistItem,
commentText,
setCommentText,
sendEmail,
setSendEmail,
sigCaptureType,
setSigCaptureType,
upliftAction,
setUpliftAction,
isLocked,
userName,
verifyState,
beginEditing,
handleSave,
handleCancelDispatch,
handleComment,
handleUpliftConfirm,
setEditing,
updateDispatchMutation,
updateChecklistMutation,
addChecklistMutation,
addSignoffMutation,
verifyMutation,
approveUpliftMutation,
denyUpliftMutation,
addCommentMutation,
};
}