mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-03 02:13:22 +00:00
Keep the slide-over uplift tab and the evidence upload while taking the current board, dashboard, and vendor changes from main.
183 lines
5.9 KiB
TypeScript
183 lines
5.9 KiB
TypeScript
import type { QueryClient } from "@tanstack/react-query";
|
|
import { toast } from "react-toastify";
|
|
import { showCompDocDraftSavedToast } from "@/app/(protected)/workorders/_components/comp-doc/comp-doc-draft-toast";
|
|
import { locationsApi } from "@/domain/locations/api/locations-api";
|
|
import type { Location } from "@/domain/locations/types/location";
|
|
import type {
|
|
CompDocData,
|
|
CompDocSaveStatus,
|
|
DocFields,
|
|
DocState,
|
|
} from "@/domain/work-orders/types/completion-doc";
|
|
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
|
|
import { buildDocHtml, printDoc } from "@/domain/work-orders/utils/completion-doc-html";
|
|
import { fmtDocNow } from "@/domain/work-orders/utils/completion-doc-state";
|
|
import { queryKeys } from "@/infra/query-key/query-key";
|
|
|
|
type CompDocSite = { street: string; fullAddress: string };
|
|
|
|
export function buildCompDocAddressLine(site: CompDocSite): string {
|
|
if (site.street.trim()) return site.street.trim();
|
|
return site.fullAddress !== "—" ? site.fullAddress : "";
|
|
}
|
|
|
|
function locationUpdatePayload(location: Location, address: string): Location {
|
|
return {
|
|
...location,
|
|
address,
|
|
};
|
|
}
|
|
|
|
type CreateCompDocDialogActionsParams = {
|
|
row: WorkOrderTableRow;
|
|
notes: string;
|
|
generatedAt: string;
|
|
editDateCompleted: string;
|
|
editAddress: string;
|
|
docState: DocState;
|
|
docFields: DocFields;
|
|
site: CompDocSite;
|
|
locId: string;
|
|
isMockLocation: boolean;
|
|
isSiteFrozen: boolean;
|
|
locationDetail: Location | undefined;
|
|
queryClient: QueryClient;
|
|
onSave: (data: CompDocData, status: CompDocSaveStatus) => void | Promise<void>;
|
|
onDateCompletedChange?: (date: string) => void | Promise<void>;
|
|
setDocState: (state: DocState) => void;
|
|
setGeneratedAt: (value: string) => void;
|
|
};
|
|
|
|
export function createCompDocDialogActions({
|
|
row,
|
|
notes,
|
|
generatedAt,
|
|
editDateCompleted,
|
|
editAddress,
|
|
docState,
|
|
docFields,
|
|
site,
|
|
locId,
|
|
isMockLocation,
|
|
isSiteFrozen,
|
|
locationDetail,
|
|
queryClient,
|
|
onSave,
|
|
onDateCompletedChange,
|
|
setDocState,
|
|
setGeneratedAt,
|
|
}: CreateCompDocDialogActionsParams) {
|
|
async function persist(extra: Partial<CompDocData>, status: CompDocSaveStatus): Promise<void> {
|
|
await onSave({ notes, generatedAt, ...extra }, status);
|
|
}
|
|
|
|
async function syncCompletedDateIfChanged(): Promise<void> {
|
|
if (editDateCompleted !== (row.completedDate || "")) {
|
|
await onDateCompletedChange?.(editDateCompleted);
|
|
}
|
|
}
|
|
|
|
async function syncAddressIfChanged(): Promise<void> {
|
|
const next = editAddress.trim();
|
|
const current = buildCompDocAddressLine(site);
|
|
if (next === current) return;
|
|
if (isSiteFrozen || isMockLocation || !locId) return;
|
|
if (!locationDetail) {
|
|
throw new Error("Location details are still loading. Try saving again.");
|
|
}
|
|
|
|
await locationsApi.update(locId, locationUpdatePayload(locationDetail, next));
|
|
void queryClient.invalidateQueries({ queryKey: queryKeys.locations.detail(locId) });
|
|
void queryClient.invalidateQueries({ queryKey: queryKeys.locations.all });
|
|
void queryClient.invalidateQueries({ queryKey: queryKeys.workOrders.all });
|
|
}
|
|
|
|
function refreshAuthoritativeState() {
|
|
void queryClient.invalidateQueries({ queryKey: queryKeys.workOrders.all });
|
|
if (locId && !isMockLocation) {
|
|
void queryClient.invalidateQueries({ queryKey: queryKeys.locations.detail(locId) });
|
|
void queryClient.invalidateQueries({ queryKey: queryKeys.locations.all });
|
|
}
|
|
}
|
|
|
|
async function handleMarkNotRequired() {
|
|
const previous = docState;
|
|
setDocState("not-required");
|
|
try {
|
|
await persist({}, "NN");
|
|
} catch (error) {
|
|
setDocState(previous);
|
|
refreshAuthoritativeState();
|
|
toast.error(
|
|
error instanceof Error ? error.message : "Failed to mark document as not required",
|
|
);
|
|
}
|
|
}
|
|
|
|
async function handleUndo() {
|
|
const previous = docState;
|
|
setDocState("draft");
|
|
try {
|
|
await persist({}, "No");
|
|
} catch (error) {
|
|
setDocState(previous);
|
|
refreshAuthoritativeState();
|
|
toast.error(error instanceof Error ? error.message : "Failed to undo not-required");
|
|
}
|
|
}
|
|
|
|
async function handleSaveDraft() {
|
|
try {
|
|
await syncCompletedDateIfChanged();
|
|
await syncAddressIfChanged();
|
|
await persist({}, "No");
|
|
showCompDocDraftSavedToast();
|
|
} catch (error) {
|
|
refreshAuthoritativeState();
|
|
toast.error(error instanceof Error ? error.message : "Failed to save draft");
|
|
}
|
|
}
|
|
|
|
async function handleGenerate() {
|
|
if (!editDateCompleted.trim()) {
|
|
toast.error("Date Work Completed is required to generate the document.");
|
|
return;
|
|
}
|
|
const ts = fmtDocNow();
|
|
try {
|
|
// Prerequisites first; only mark Generated after completedDate/address persist.
|
|
await syncCompletedDateIfChanged();
|
|
await syncAddressIfChanged();
|
|
await persist({ generatedAt: ts }, "Yes");
|
|
setGeneratedAt(ts);
|
|
setDocState("generated");
|
|
} catch (error) {
|
|
// Keep Draft UI; refresh so any partially persisted date/address is authoritative.
|
|
refreshAuthoritativeState();
|
|
toast.error(error instanceof Error ? error.message : "Failed to generate document");
|
|
}
|
|
}
|
|
|
|
function handleDownloadPdf() {
|
|
printDoc(buildDocHtml(row, { notes, generatedAt }, docFields));
|
|
}
|
|
|
|
// why: Uploading a signed PDF finalizes the document server-side: the endpoint sets DocStatus=Yes and
|
|
// bumps the work-order row version in one transaction. Reflect that in the UI and refresh
|
|
// authoritative state, but do not re-run the Generate persist — that patch would carry the
|
|
// pre-upload row version and 409, surfacing an error right after a successful upload.
|
|
function markSignedPdfUploaded() {
|
|
setGeneratedAt(fmtDocNow());
|
|
setDocState("generated");
|
|
refreshAuthoritativeState();
|
|
}
|
|
|
|
return {
|
|
handleMarkNotRequired,
|
|
handleUndo,
|
|
handleSaveDraft,
|
|
handleGenerate,
|
|
handleDownloadPdf,
|
|
markSignedPdfUploaded,
|
|
};
|
|
}
|