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 { useMemo, useState } from "react";
|
||||||
import { toast } from "react-toastify";
|
|
||||||
import { useLocationSites } from "@/domain/locations/use-cases/use-location-sites";
|
import { useLocationSites } from "@/domain/locations/use-cases/use-location-sites";
|
||||||
import type {
|
import type {
|
||||||
LocationOption,
|
LocationOption,
|
||||||
|
|
@ -10,14 +9,11 @@ import type {
|
||||||
WorkOrderTablePatch,
|
WorkOrderTablePatch,
|
||||||
WorkOrderTableRow,
|
WorkOrderTableRow,
|
||||||
} from "@/domain/work-orders/types/work-order-table-row";
|
} 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 { DuplicateWoNumberDialog } from "@/app/(protected)/workorders/_components/list/duplicate-wo-number-dialog";
|
||||||
import { WoTableSkeleton } from "@/app/(protected)/workorders/_components/loading/wo-table-skeleton";
|
import { WoTableSkeleton } from "@/app/(protected)/workorders/_components/loading/wo-table-skeleton";
|
||||||
import type { ReturnTypeOfUseWorkOrdersTableData } from "./wo-table-types";
|
import type { ReturnTypeOfUseWorkOrdersTableData } from "./wo-table-types";
|
||||||
import { buildInlineDraftCreateInput } from "./build-inline-draft-create-input";
|
|
||||||
import { buildSitePlaceLookups, resolveSitePlace } from "./site-place-lookup";
|
import { buildSitePlaceLookups, resolveSitePlace } from "./site-place-lookup";
|
||||||
|
import { useWoTableInlineCreate } from "./use-wo-table-inline-create";
|
||||||
import { useWoTableRowHandlers } from "./use-wo-table-row-handlers";
|
import { useWoTableRowHandlers } from "./use-wo-table-row-handlers";
|
||||||
import { WoTableBody } from "./wo-table-body";
|
import { WoTableBody } from "./wo-table-body";
|
||||||
import { WoTableHead } from "./wo-table-head";
|
import { WoTableHead } from "./wo-table-head";
|
||||||
|
|
@ -60,11 +56,13 @@ export function WoTable({
|
||||||
}: WoTableProps) {
|
}: WoTableProps) {
|
||||||
const [scheduleHighlightId, setScheduleHighlightId] = useState<string | number | null>(null);
|
const [scheduleHighlightId, setScheduleHighlightId] = useState<string | number | null>(null);
|
||||||
const [duplicateRow, setDuplicateRow] = useState<WorkOrderTableRow | null>(null);
|
const [duplicateRow, setDuplicateRow] = useState<WorkOrderTableRow | null>(null);
|
||||||
const [isCheckingInlineDuplicate, setIsCheckingInlineDuplicate] = useState(false);
|
|
||||||
const { data: locationSites = [] } = useLocationSites();
|
const { data: locationSites = [] } = useLocationSites();
|
||||||
const sitePlaceLookups = useMemo(() => buildSitePlaceLookups(locationSites), [locationSites]);
|
const sitePlaceLookups = useMemo(() => buildSitePlaceLookups(locationSites), [locationSites]);
|
||||||
const createMutation = useCreateWorkOrderFromWizard(() => {
|
const { handleInlineSave, saving } = useWoTableInlineCreate({
|
||||||
tableData.cancelDraft();
|
tableData,
|
||||||
|
users,
|
||||||
|
sites,
|
||||||
|
onDuplicateFound: setDuplicateRow,
|
||||||
});
|
});
|
||||||
|
|
||||||
const handlers = useWoTableRowHandlers({
|
const handlers = useWoTableRowHandlers({
|
||||||
|
|
@ -79,31 +77,6 @@ export function WoTable({
|
||||||
onDuplicateFound: setDuplicateRow,
|
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) => (
|
const renderRow = (row: WorkOrderTableRow, forceScheduleHighlight = false) => (
|
||||||
<WoTableRow
|
<WoTableRow
|
||||||
key={String(row.id)}
|
key={String(row.id)}
|
||||||
|
|
@ -138,7 +111,7 @@ export function WoTable({
|
||||||
vendors={vendors}
|
vendors={vendors}
|
||||||
pmTypes={pmTypes}
|
pmTypes={pmTypes}
|
||||||
allRows={tableData.rows}
|
allRows={tableData.rows}
|
||||||
saving={createMutation.isPending || isCheckingInlineDuplicate}
|
saving={saving}
|
||||||
onChange={tableData.setDraft}
|
onChange={tableData.setDraft}
|
||||||
onSave={handleInlineSave}
|
onSave={handleInlineSave}
|
||||||
onCancel={tableData.cancelDraft}
|
onCancel={tableData.cancelDraft}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { useEffect, useMemo, useRef, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { toast } from "react-toastify";
|
|
||||||
import { isWoWizardMockEnabled } from "@/domain/work-orders/mocks/is-wo-wizard-mock-enabled";
|
import { isWoWizardMockEnabled } from "@/domain/work-orders/mocks/is-wo-wizard-mock-enabled";
|
||||||
import {
|
import {
|
||||||
getMockWoWizardLocationDetail,
|
getMockWoWizardLocationDetail,
|
||||||
|
|
@ -21,8 +20,7 @@ import {
|
||||||
useWorkOrderVendors,
|
useWorkOrderVendors,
|
||||||
} from "@/domain/work-orders/use-cases/use-work-order-detail";
|
} 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 { 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 { useWizardDuplicateActions } from "./use-wizard-duplicate-actions";
|
||||||
import { workOrdersApi } from "@/domain/work-orders/api/work-orders-api";
|
|
||||||
|
|
||||||
type UseNewWoWizardStateArgs = {
|
type UseNewWoWizardStateArgs = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
|
|
@ -44,18 +42,6 @@ export function useNewWoWizardState({
|
||||||
status: "Incomplete",
|
status: "Incomplete",
|
||||||
});
|
});
|
||||||
const [pocAutoFilledFor, setPocAutoFilledFor] = useState("");
|
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: apiLocations = [] } = useWorkOrderLocations();
|
||||||
const { data: apiProblems = [] } = useDropdownOptionsByCategory("Problem");
|
const { data: apiProblems = [] } = useDropdownOptionsByCategory("Problem");
|
||||||
|
|
@ -72,6 +58,18 @@ export function useNewWoWizardState({
|
||||||
isMockLocation ? undefined : locationDetail?.zipCode,
|
isMockLocation ? undefined : locationDetail?.zipCode,
|
||||||
);
|
);
|
||||||
const createMutation = useCreateWorkOrderFromWizard(() => onOpenChange(false));
|
const createMutation = useCreateWorkOrderFromWizard(() => onOpenChange(false));
|
||||||
|
const {
|
||||||
|
duplicateRow,
|
||||||
|
setDuplicateRow,
|
||||||
|
handleDuplicateFound,
|
||||||
|
handleCreate,
|
||||||
|
isCreating,
|
||||||
|
} = useWizardDuplicateActions({
|
||||||
|
open,
|
||||||
|
draft,
|
||||||
|
existingRows,
|
||||||
|
createMutation,
|
||||||
|
});
|
||||||
|
|
||||||
const wizardUsers = useMemo(() => {
|
const wizardUsers = useMemo(() => {
|
||||||
if (users.length > 0) {
|
if (users.length > 0) {
|
||||||
|
|
@ -92,7 +90,7 @@ export function useNewWoWizardState({
|
||||||
setPocAutoFilledFor("");
|
setPocAutoFilledFor("");
|
||||||
setDuplicateRow(null);
|
setDuplicateRow(null);
|
||||||
}
|
}
|
||||||
}, [open]);
|
}, [open, setDuplicateRow]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!draft.locationId || !locationDetail) {
|
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 {
|
return {
|
||||||
step,
|
step,
|
||||||
setStep,
|
setStep,
|
||||||
|
|
@ -194,6 +136,6 @@ export function useNewWoWizardState({
|
||||||
patchDraft,
|
patchDraft,
|
||||||
handleDuplicateFound,
|
handleDuplicateFound,
|
||||||
handleCreate,
|
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