mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-07 16:19:01 +00:00
fix(work-orders): extract hooks to satisfy max-lines governance gate
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
c6c11b9f04
commit
647c7a7b3d
4 changed files with 181 additions and 108 deletions
|
|
@ -0,0 +1,58 @@
|
|||
import { useState } from "react";
|
||||
import { toast } from "react-toastify";
|
||||
import type { LocationOption, WorkOrderUser } from "@/domain/work-orders/types/work-order";
|
||||
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
|
||||
import { workOrdersApi } from "@/domain/work-orders/api/work-orders-api";
|
||||
import { useCreateWorkOrderFromWizard } from "@/domain/work-orders/use-cases/use-create-work-order-from-wizard";
|
||||
import { findDuplicateWoNumberRow } from "@/domain/work-orders/utils/find-duplicate-wo-number-row";
|
||||
import type { ReturnTypeOfUseWorkOrdersTableData } from "./wo-table-types";
|
||||
import { buildInlineDraftCreateInput } from "./build-inline-draft-create-input";
|
||||
|
||||
type UseWoTableInlineCreateArgs = {
|
||||
tableData: ReturnTypeOfUseWorkOrdersTableData;
|
||||
users: WorkOrderUser[];
|
||||
sites: LocationOption[];
|
||||
onDuplicateFound: (row: WorkOrderTableRow) => void;
|
||||
};
|
||||
|
||||
export function useWoTableInlineCreate({
|
||||
tableData,
|
||||
users,
|
||||
sites,
|
||||
onDuplicateFound,
|
||||
}: UseWoTableInlineCreateArgs) {
|
||||
const [isCheckingInlineDuplicate, setIsCheckingInlineDuplicate] = useState(false);
|
||||
const createMutation = useCreateWorkOrderFromWizard(() => {
|
||||
tableData.cancelDraft();
|
||||
});
|
||||
|
||||
const handleInlineSave = () => {
|
||||
void (async () => {
|
||||
const draftSnapshot = tableData.draft;
|
||||
const woNumber = draftSnapshot.woNumber.trim();
|
||||
if (woNumber) {
|
||||
setIsCheckingInlineDuplicate(true);
|
||||
try {
|
||||
const duplicate =
|
||||
findDuplicateWoNumberRow(woNumber, tableData.rows, "draft") ??
|
||||
(await workOrdersApi.findByWoNumber(woNumber));
|
||||
if (duplicate && String(duplicate.id) !== "draft") {
|
||||
onDuplicateFound(duplicate);
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
toast.error("Unable to verify the work order number. Please try again.");
|
||||
return;
|
||||
} finally {
|
||||
setIsCheckingInlineDuplicate(false);
|
||||
}
|
||||
}
|
||||
createMutation.mutate(buildInlineDraftCreateInput(tableData.draft, users, sites));
|
||||
})();
|
||||
};
|
||||
|
||||
return {
|
||||
handleInlineSave,
|
||||
saving: createMutation.isPending || isCheckingInlineDuplicate,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
import { useMemo, useState } from "react";
|
||||
import { toast } from "react-toastify";
|
||||
import { useLocationSites } from "@/domain/locations/use-cases/use-location-sites";
|
||||
import type {
|
||||
LocationOption,
|
||||
|
|
@ -10,14 +9,11 @@ import type {
|
|||
WorkOrderTablePatch,
|
||||
WorkOrderTableRow,
|
||||
} from "@/domain/work-orders/types/work-order-table-row";
|
||||
import { workOrdersApi } from "@/domain/work-orders/api/work-orders-api";
|
||||
import { useCreateWorkOrderFromWizard } from "@/domain/work-orders/use-cases/use-create-work-order-from-wizard";
|
||||
import { findDuplicateWoNumberRow } from "@/domain/work-orders/utils/find-duplicate-wo-number-row";
|
||||
import { DuplicateWoNumberDialog } from "@/app/(protected)/workorders/_components/list/duplicate-wo-number-dialog";
|
||||
import { WoTableSkeleton } from "@/app/(protected)/workorders/_components/loading/wo-table-skeleton";
|
||||
import type { ReturnTypeOfUseWorkOrdersTableData } from "./wo-table-types";
|
||||
import { buildInlineDraftCreateInput } from "./build-inline-draft-create-input";
|
||||
import { buildSitePlaceLookups, resolveSitePlace } from "./site-place-lookup";
|
||||
import { useWoTableInlineCreate } from "./use-wo-table-inline-create";
|
||||
import { useWoTableRowHandlers } from "./use-wo-table-row-handlers";
|
||||
import { WoTableBody } from "./wo-table-body";
|
||||
import { WoTableHead } from "./wo-table-head";
|
||||
|
|
@ -60,11 +56,13 @@ export function WoTable({
|
|||
}: WoTableProps) {
|
||||
const [scheduleHighlightId, setScheduleHighlightId] = useState<string | number | null>(null);
|
||||
const [duplicateRow, setDuplicateRow] = useState<WorkOrderTableRow | null>(null);
|
||||
const [isCheckingInlineDuplicate, setIsCheckingInlineDuplicate] = useState(false);
|
||||
const { data: locationSites = [] } = useLocationSites();
|
||||
const sitePlaceLookups = useMemo(() => buildSitePlaceLookups(locationSites), [locationSites]);
|
||||
const createMutation = useCreateWorkOrderFromWizard(() => {
|
||||
tableData.cancelDraft();
|
||||
const { handleInlineSave, saving } = useWoTableInlineCreate({
|
||||
tableData,
|
||||
users,
|
||||
sites,
|
||||
onDuplicateFound: setDuplicateRow,
|
||||
});
|
||||
|
||||
const handlers = useWoTableRowHandlers({
|
||||
|
|
@ -79,31 +77,6 @@ export function WoTable({
|
|||
onDuplicateFound: setDuplicateRow,
|
||||
});
|
||||
|
||||
const handleInlineSave = () => {
|
||||
void (async () => {
|
||||
const draftSnapshot = tableData.draft;
|
||||
const woNumber = draftSnapshot.woNumber.trim();
|
||||
if (woNumber) {
|
||||
setIsCheckingInlineDuplicate(true);
|
||||
try {
|
||||
const duplicate =
|
||||
findDuplicateWoNumberRow(woNumber, tableData.rows, "draft") ??
|
||||
(await workOrdersApi.findByWoNumber(woNumber));
|
||||
if (duplicate && String(duplicate.id) !== "draft") {
|
||||
setDuplicateRow(duplicate);
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
toast.error("Unable to verify the work order number. Please try again.");
|
||||
return;
|
||||
} finally {
|
||||
setIsCheckingInlineDuplicate(false);
|
||||
}
|
||||
}
|
||||
createMutation.mutate(buildInlineDraftCreateInput(tableData.draft, users, sites));
|
||||
})();
|
||||
};
|
||||
|
||||
const renderRow = (row: WorkOrderTableRow, forceScheduleHighlight = false) => (
|
||||
<WoTableRow
|
||||
key={String(row.id)}
|
||||
|
|
@ -138,7 +111,7 @@ export function WoTable({
|
|||
vendors={vendors}
|
||||
pmTypes={pmTypes}
|
||||
allRows={tableData.rows}
|
||||
saving={createMutation.isPending || isCheckingInlineDuplicate}
|
||||
saving={saving}
|
||||
onChange={tableData.setDraft}
|
||||
onSave={handleInlineSave}
|
||||
onCancel={tableData.cancelDraft}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { toast } from "react-toastify";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { isWoWizardMockEnabled } from "@/domain/work-orders/mocks/is-wo-wizard-mock-enabled";
|
||||
import {
|
||||
getMockWoWizardLocationDetail,
|
||||
|
|
@ -21,8 +20,7 @@ import {
|
|||
useWorkOrderVendors,
|
||||
} from "@/domain/work-orders/use-cases/use-work-order-detail";
|
||||
import { useCreateWorkOrderFromWizard } from "@/domain/work-orders/use-cases/use-create-work-order-from-wizard";
|
||||
import { findDuplicateWoNumberRow } from "@/domain/work-orders/utils/find-duplicate-wo-number-row";
|
||||
import { workOrdersApi } from "@/domain/work-orders/api/work-orders-api";
|
||||
import { useWizardDuplicateActions } from "./use-wizard-duplicate-actions";
|
||||
|
||||
type UseNewWoWizardStateArgs = {
|
||||
open: boolean;
|
||||
|
|
@ -44,18 +42,6 @@ export function useNewWoWizardState({
|
|||
status: "Incomplete",
|
||||
});
|
||||
const [pocAutoFilledFor, setPocAutoFilledFor] = useState("");
|
||||
const [duplicateRow, setDuplicateRow] = useState<WorkOrderTableRow | null>(null);
|
||||
const [isCheckingDuplicate, setIsCheckingDuplicate] = useState(false);
|
||||
const openRef = useRef(open);
|
||||
const draftRef = useRef(draft);
|
||||
|
||||
useEffect(() => {
|
||||
openRef.current = open;
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
draftRef.current = draft;
|
||||
}, [draft]);
|
||||
|
||||
const { data: apiLocations = [] } = useWorkOrderLocations();
|
||||
const { data: apiProblems = [] } = useDropdownOptionsByCategory("Problem");
|
||||
|
|
@ -72,6 +58,18 @@ export function useNewWoWizardState({
|
|||
isMockLocation ? undefined : locationDetail?.zipCode,
|
||||
);
|
||||
const createMutation = useCreateWorkOrderFromWizard(() => onOpenChange(false));
|
||||
const {
|
||||
duplicateRow,
|
||||
setDuplicateRow,
|
||||
handleDuplicateFound,
|
||||
handleCreate,
|
||||
isCreating,
|
||||
} = useWizardDuplicateActions({
|
||||
open,
|
||||
draft,
|
||||
existingRows,
|
||||
createMutation,
|
||||
});
|
||||
|
||||
const wizardUsers = useMemo(() => {
|
||||
if (users.length > 0) {
|
||||
|
|
@ -92,7 +90,7 @@ export function useNewWoWizardState({
|
|||
setPocAutoFilledFor("");
|
||||
setDuplicateRow(null);
|
||||
}
|
||||
}, [open]);
|
||||
}, [open, setDuplicateRow]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!draft.locationId || !locationDetail) {
|
||||
|
|
@ -123,62 +121,6 @@ export function useNewWoWizardState({
|
|||
}
|
||||
};
|
||||
|
||||
const resolveDuplicate = async (woNumber: string) => {
|
||||
const localDuplicate = findDuplicateWoNumberRow(woNumber, existingRows);
|
||||
if (localDuplicate) {
|
||||
return localDuplicate;
|
||||
}
|
||||
|
||||
// The loaded board rows are only an immediate UX hint; the unfiltered API lookup is authoritative.
|
||||
return workOrdersApi.findByWoNumber(woNumber);
|
||||
};
|
||||
|
||||
const handleDuplicateFound = (woNumber: string) => {
|
||||
void (async () => {
|
||||
try {
|
||||
const duplicate = await resolveDuplicate(woNumber);
|
||||
// Ignore stale lookups after dismiss or after the user edits the WO#.
|
||||
if (!openRef.current || woNumber !== draftRef.current.woNumber) {
|
||||
return;
|
||||
}
|
||||
if (duplicate) {
|
||||
setDuplicateRow(duplicate);
|
||||
}
|
||||
} catch {
|
||||
if (!openRef.current) {
|
||||
return;
|
||||
}
|
||||
toast.error("Unable to verify the work order number. Please try again.");
|
||||
}
|
||||
})();
|
||||
};
|
||||
|
||||
const handleCreate = () => {
|
||||
const woNumberAtSubmit = draft.woNumber;
|
||||
void (async () => {
|
||||
setIsCheckingDuplicate(true);
|
||||
try {
|
||||
const duplicate = await resolveDuplicate(woNumberAtSubmit);
|
||||
// Ignore after dismiss or if the WO# changed while the request was in flight.
|
||||
if (!openRef.current || woNumberAtSubmit !== draftRef.current.woNumber) {
|
||||
return;
|
||||
}
|
||||
if (duplicate) {
|
||||
setDuplicateRow(duplicate);
|
||||
return;
|
||||
}
|
||||
createMutation.mutate(draftRef.current);
|
||||
} catch {
|
||||
if (!openRef.current) {
|
||||
return;
|
||||
}
|
||||
toast.error("Unable to verify the work order number. Please try again.");
|
||||
} finally {
|
||||
setIsCheckingDuplicate(false);
|
||||
}
|
||||
})();
|
||||
};
|
||||
|
||||
return {
|
||||
step,
|
||||
setStep,
|
||||
|
|
@ -194,6 +136,6 @@ export function useNewWoWizardState({
|
|||
patchDraft,
|
||||
handleDuplicateFound,
|
||||
handleCreate,
|
||||
isCreating: createMutation.isPending || isCheckingDuplicate,
|
||||
isCreating,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
import { useEffect, useRef, useState } from "react";
|
||||
import { toast } from "react-toastify";
|
||||
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
|
||||
import type { WorkOrderWizardDraft } from "@/domain/work-orders/types/work-order-wizard";
|
||||
import { findDuplicateWoNumberRow } from "@/domain/work-orders/utils/find-duplicate-wo-number-row";
|
||||
import { workOrdersApi } from "@/domain/work-orders/api/work-orders-api";
|
||||
|
||||
type UseWizardDuplicateActionsArgs = {
|
||||
open: boolean;
|
||||
draft: WorkOrderWizardDraft;
|
||||
existingRows: WorkOrderTableRow[];
|
||||
createMutation: {
|
||||
mutate: (draft: WorkOrderWizardDraft) => void;
|
||||
isPending: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export function useWizardDuplicateActions({
|
||||
open,
|
||||
draft,
|
||||
existingRows,
|
||||
createMutation,
|
||||
}: UseWizardDuplicateActionsArgs) {
|
||||
const [duplicateRow, setDuplicateRow] = useState<WorkOrderTableRow | null>(null);
|
||||
const [isCheckingDuplicate, setIsCheckingDuplicate] = useState(false);
|
||||
const openRef = useRef(open);
|
||||
const draftRef = useRef(draft);
|
||||
|
||||
useEffect(() => {
|
||||
openRef.current = open;
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
draftRef.current = draft;
|
||||
}, [draft]);
|
||||
|
||||
const resolveDuplicate = async (woNumber: string) => {
|
||||
const localDuplicate = findDuplicateWoNumberRow(woNumber, existingRows);
|
||||
if (localDuplicate) {
|
||||
return localDuplicate;
|
||||
}
|
||||
|
||||
// The loaded board rows are only an immediate UX hint; the unfiltered API lookup is authoritative.
|
||||
return workOrdersApi.findByWoNumber(woNumber);
|
||||
};
|
||||
|
||||
const handleDuplicateFound = (woNumber: string) => {
|
||||
void (async () => {
|
||||
try {
|
||||
const duplicate = await resolveDuplicate(woNumber);
|
||||
// Ignore stale lookups after dismiss or after the user edits the WO#.
|
||||
if (!openRef.current || woNumber !== draftRef.current.woNumber) {
|
||||
return;
|
||||
}
|
||||
if (duplicate) {
|
||||
setDuplicateRow(duplicate);
|
||||
}
|
||||
} catch {
|
||||
if (!openRef.current) {
|
||||
return;
|
||||
}
|
||||
toast.error("Unable to verify the work order number. Please try again.");
|
||||
}
|
||||
})();
|
||||
};
|
||||
|
||||
const handleCreate = () => {
|
||||
const woNumberAtSubmit = draft.woNumber;
|
||||
void (async () => {
|
||||
setIsCheckingDuplicate(true);
|
||||
try {
|
||||
const duplicate = await resolveDuplicate(woNumberAtSubmit);
|
||||
// Ignore after dismiss or if the WO# changed while the request was in flight.
|
||||
if (!openRef.current || woNumberAtSubmit !== draftRef.current.woNumber) {
|
||||
return;
|
||||
}
|
||||
if (duplicate) {
|
||||
setDuplicateRow(duplicate);
|
||||
return;
|
||||
}
|
||||
createMutation.mutate(draftRef.current);
|
||||
} catch {
|
||||
if (!openRef.current) {
|
||||
return;
|
||||
}
|
||||
toast.error("Unable to verify the work order number. Please try again.");
|
||||
} finally {
|
||||
setIsCheckingDuplicate(false);
|
||||
}
|
||||
})();
|
||||
};
|
||||
|
||||
return {
|
||||
duplicateRow,
|
||||
setDuplicateRow,
|
||||
handleDuplicateFound,
|
||||
handleCreate,
|
||||
isCreating: createMutation.isPending || isCheckingDuplicate,
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue