mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-07 16:19:01 +00:00
refactor(work-orders): extract wizard controller for maintainability gate
Move duplicate-WO wiring and lookup hooks out of NewWoWizard so the changed-file function line cap passes after merging dev.
This commit is contained in:
parent
7d57319c91
commit
33026ab927
2 changed files with 135 additions and 109 deletions
|
|
@ -1,21 +1,10 @@
|
|||
import { useEffect } from "react";
|
||||
import { Dialog } from "@mui/material";
|
||||
|
||||
import { dialogBlurBackdropSlotProps } from "@/components/ui/dialog-blur-backdrop";
|
||||
import { guardDialogClose } from "@/lib/dialog-guard";
|
||||
import { DuplicateWoNumberDialog } from "@/app/(protected)/workorders/_components/list/duplicate-wo-number-dialog";
|
||||
|
||||
import { useDropdownOptionsByCategory } from "@/domain/settings/dropdown-options/use-cases/use-dropdown-options-by-category";
|
||||
|
||||
import type { WorkOrderUser } from "@/domain/work-orders/types/work-order";
|
||||
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
|
||||
import type { WorkOrderWizardDraft } from "@/domain/work-orders/types/work-order-wizard";
|
||||
|
||||
import { useLocationDetail } from "@/domain/locations/use-cases/use-location-detail";
|
||||
import { useLocationSites } from "@/domain/locations/use-cases/use-location-sites";
|
||||
|
||||
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 { WizardFooter } from "@/app/(protected)/workorders/_components/wizard/wizard-footer";
|
||||
import { WizardSidebar } from "@/app/(protected)/workorders/_components/wizard/wizard-sidebar";
|
||||
|
|
@ -23,9 +12,7 @@ import { WizardStepHeader } from "@/app/(protected)/workorders/_components/wizar
|
|||
import { WizardStepLocationService } from "@/app/(protected)/workorders/_components/wizard/wizard-step-location-service";
|
||||
import { WizardStepTypeSchedule } from "@/app/(protected)/workorders/_components/wizard/wizard-step-type-schedule";
|
||||
import { WizardStepVendorTime } from "@/app/(protected)/workorders/_components/wizard/wizard-step-vendor-time";
|
||||
import { useNewWoWizardDraft } from "@/app/(protected)/workorders/_components/wizard/use-new-wo-wizard-draft";
|
||||
import { useWizardDuplicateActions } from "@/app/(protected)/workorders/_components/wizard/use-wizard-duplicate-actions";
|
||||
import { useWizardPocAutofill } from "@/app/(protected)/workorders/_components/wizard/use-wizard-poc-autofill";
|
||||
import { useNewWoWizardController } from "@/app/(protected)/workorders/_components/wizard/use-new-wo-wizard-controller";
|
||||
|
||||
type NewWoWizardProps = {
|
||||
open: boolean;
|
||||
|
|
@ -42,67 +29,13 @@ export function NewWoWizard({
|
|||
existingRows = [],
|
||||
onViewExistingWo,
|
||||
}: NewWoWizardProps) {
|
||||
const {
|
||||
step,
|
||||
setStep,
|
||||
draft,
|
||||
setDraft,
|
||||
setField,
|
||||
patchDraft: patchDraftOnly,
|
||||
svcOpen,
|
||||
setSvcOpen,
|
||||
svcSearch,
|
||||
setSvcSearch,
|
||||
} = useNewWoWizardDraft(open);
|
||||
|
||||
const { data: locations = [] } = useLocationSites();
|
||||
const { data: problems = [] } = useDropdownOptionsByCategory("Problem");
|
||||
const {
|
||||
data: locationDetail,
|
||||
isLoading: locationDetailLoading,
|
||||
isError: locationDetailError,
|
||||
} = useLocationDetail(draft.locationId || undefined);
|
||||
const { data: vendors = [] } = useWorkOrderVendors(
|
||||
draft.pm.trim() || undefined,
|
||||
locationDetail?.zipCode,
|
||||
);
|
||||
|
||||
const { notifyLocationChanged } = useWizardPocAutofill(
|
||||
open,
|
||||
draft.locationId,
|
||||
locationDetail,
|
||||
setDraft,
|
||||
);
|
||||
|
||||
const patchDraft = (patch: Partial<WorkOrderWizardDraft>) => {
|
||||
patchDraftOnly(patch);
|
||||
if (patch.locationId !== undefined) {
|
||||
notifyLocationChanged(patch.locationId);
|
||||
}
|
||||
};
|
||||
|
||||
const createMutation = useCreateWorkOrderFromWizard(() => onOpenChange(false));
|
||||
const { duplicateRow, setDuplicateRow, handleDuplicateFound, handleCreate, isCreating } =
|
||||
useWizardDuplicateActions({
|
||||
open,
|
||||
draft,
|
||||
existingRows,
|
||||
createMutation,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setDuplicateRow(null);
|
||||
}
|
||||
}, [open, setDuplicateRow]);
|
||||
|
||||
const handleClose = guardDialogClose(isCreating, () => onOpenChange(false));
|
||||
const wizard = useNewWoWizardController({ open, onOpenChange, existingRows });
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
onClose={wizard.handleClose}
|
||||
maxWidth={false}
|
||||
slotProps={{
|
||||
...dialogBlurBackdropSlotProps,
|
||||
|
|
@ -114,80 +47,80 @@ export function NewWoWizard({
|
|||
maxWidth: 768,
|
||||
height: 680,
|
||||
maxHeight: 680,
|
||||
pointerEvents: isCreating ? "none" : undefined,
|
||||
opacity: isCreating ? 0.85 : 1,
|
||||
pointerEvents: wizard.isCreating ? "none" : undefined,
|
||||
opacity: wizard.isCreating ? 0.85 : 1,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<span className="sr-only">Guided setup for creating a new work order</span>
|
||||
|
||||
<div className="flex min-h-0 flex-1" aria-busy={isCreating || undefined}>
|
||||
<WizardSidebar step={step} draft={draft} />
|
||||
<div className="flex min-h-0 flex-1" aria-busy={wizard.isCreating || undefined}>
|
||||
<WizardSidebar step={wizard.step} draft={wizard.draft} />
|
||||
|
||||
<div
|
||||
className="min-h-0 flex-1 overflow-y-auto"
|
||||
style={{ padding: 32, background: "var(--color-bg-surface)" }}
|
||||
>
|
||||
<WizardStepHeader step={step} />
|
||||
<WizardStepHeader step={wizard.step} />
|
||||
|
||||
{step === 1 && (
|
||||
{wizard.step === 1 && (
|
||||
<WizardStepTypeSchedule
|
||||
draft={draft}
|
||||
draft={wizard.draft}
|
||||
users={users}
|
||||
onUpdate={setField}
|
||||
onPatch={patchDraft}
|
||||
onDuplicateWoNumber={handleDuplicateFound}
|
||||
onUpdate={wizard.setField}
|
||||
onPatch={wizard.patchDraft}
|
||||
onDuplicateWoNumber={wizard.handleDuplicateFound}
|
||||
/>
|
||||
)}
|
||||
|
||||
{step === 2 && (
|
||||
{wizard.step === 2 && (
|
||||
<WizardStepLocationService
|
||||
draft={draft}
|
||||
locations={locations}
|
||||
locationDetail={locationDetail}
|
||||
locationDetailLoading={locationDetailLoading}
|
||||
locationDetailError={locationDetailError}
|
||||
services={problems.map((p) => ({ id: p.id, value: p.value }))}
|
||||
svcOpen={svcOpen}
|
||||
onSvcOpenChange={setSvcOpen}
|
||||
svcSearch={svcSearch}
|
||||
onSvcSearchChange={setSvcSearch}
|
||||
onUpdate={setField}
|
||||
onPatch={patchDraft}
|
||||
draft={wizard.draft}
|
||||
locations={wizard.locations}
|
||||
locationDetail={wizard.locationDetail}
|
||||
locationDetailLoading={wizard.locationDetailLoading}
|
||||
locationDetailError={wizard.locationDetailError}
|
||||
services={wizard.problems.map((p) => ({ id: p.id, value: p.value }))}
|
||||
svcOpen={wizard.svcOpen}
|
||||
onSvcOpenChange={wizard.setSvcOpen}
|
||||
svcSearch={wizard.svcSearch}
|
||||
onSvcSearchChange={wizard.setSvcSearch}
|
||||
onUpdate={wizard.setField}
|
||||
onPatch={wizard.patchDraft}
|
||||
/>
|
||||
)}
|
||||
|
||||
{step === 3 && (
|
||||
{wizard.step === 3 && (
|
||||
<WizardStepVendorTime
|
||||
draft={draft}
|
||||
vendors={vendors}
|
||||
onUpdate={setField}
|
||||
onPatch={patchDraft}
|
||||
draft={wizard.draft}
|
||||
vendors={wizard.vendors}
|
||||
onUpdate={wizard.setField}
|
||||
onPatch={wizard.patchDraft}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<WizardFooter
|
||||
step={step}
|
||||
draft={draft}
|
||||
step={wizard.step}
|
||||
draft={wizard.draft}
|
||||
onCancel={() => {
|
||||
if (!isCreating) onOpenChange(false);
|
||||
if (!wizard.isCreating) onOpenChange(false);
|
||||
}}
|
||||
onBack={() => setStep((s) => s - 1)}
|
||||
onContinue={() => setStep((s) => s + 1)}
|
||||
onCreate={handleCreate}
|
||||
isCreating={isCreating}
|
||||
onBack={() => wizard.setStep((s) => s - 1)}
|
||||
onContinue={() => wizard.setStep((s) => s + 1)}
|
||||
onCreate={wizard.handleCreate}
|
||||
isCreating={wizard.isCreating}
|
||||
/>
|
||||
</Dialog>
|
||||
|
||||
<DuplicateWoNumberDialog
|
||||
open={duplicateRow != null}
|
||||
existing={duplicateRow}
|
||||
onDismiss={() => setDuplicateRow(null)}
|
||||
open={wizard.duplicateRow != null}
|
||||
existing={wizard.duplicateRow}
|
||||
onDismiss={() => wizard.setDuplicateRow(null)}
|
||||
onViewExisting={(row) => {
|
||||
setDuplicateRow(null);
|
||||
wizard.setDuplicateRow(null);
|
||||
onOpenChange(false);
|
||||
onViewExistingWo?.(row);
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
import { useEffect } from "react";
|
||||
|
||||
import { guardDialogClose } from "@/lib/dialog-guard";
|
||||
|
||||
import { useDropdownOptionsByCategory } from "@/domain/settings/dropdown-options/use-cases/use-dropdown-options-by-category";
|
||||
|
||||
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
|
||||
import type { WorkOrderWizardDraft } from "@/domain/work-orders/types/work-order-wizard";
|
||||
|
||||
import { useLocationDetail } from "@/domain/locations/use-cases/use-location-detail";
|
||||
import { useLocationSites } from "@/domain/locations/use-cases/use-location-sites";
|
||||
|
||||
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 { useNewWoWizardDraft } from "@/app/(protected)/workorders/_components/wizard/use-new-wo-wizard-draft";
|
||||
import { useWizardDuplicateActions } from "@/app/(protected)/workorders/_components/wizard/use-wizard-duplicate-actions";
|
||||
import { useWizardPocAutofill } from "@/app/(protected)/workorders/_components/wizard/use-wizard-poc-autofill";
|
||||
|
||||
type UseNewWoWizardControllerArgs = {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
existingRows: WorkOrderTableRow[];
|
||||
};
|
||||
|
||||
/** Wires draft state, lookups, POC autofill, and duplicate-WO guards for the create wizard. */
|
||||
export function useNewWoWizardController({
|
||||
open,
|
||||
onOpenChange,
|
||||
existingRows,
|
||||
}: UseNewWoWizardControllerArgs) {
|
||||
const draftState = useNewWoWizardDraft(open);
|
||||
|
||||
const { data: locations = [] } = useLocationSites();
|
||||
const { data: problems = [] } = useDropdownOptionsByCategory("Problem");
|
||||
const {
|
||||
data: locationDetail,
|
||||
isLoading: locationDetailLoading,
|
||||
isError: locationDetailError,
|
||||
} = useLocationDetail(draftState.draft.locationId || undefined);
|
||||
const { data: vendors = [] } = useWorkOrderVendors(
|
||||
draftState.draft.pm.trim() || undefined,
|
||||
locationDetail?.zipCode,
|
||||
);
|
||||
|
||||
const { notifyLocationChanged } = useWizardPocAutofill(
|
||||
open,
|
||||
draftState.draft.locationId,
|
||||
locationDetail,
|
||||
draftState.setDraft,
|
||||
);
|
||||
|
||||
const patchDraft = (patch: Partial<WorkOrderWizardDraft>) => {
|
||||
draftState.patchDraft(patch);
|
||||
if (patch.locationId !== undefined) {
|
||||
notifyLocationChanged(patch.locationId);
|
||||
}
|
||||
};
|
||||
|
||||
const createMutation = useCreateWorkOrderFromWizard(() => onOpenChange(false));
|
||||
const { duplicateRow, setDuplicateRow, handleDuplicateFound, handleCreate, isCreating } =
|
||||
useWizardDuplicateActions({
|
||||
open,
|
||||
draft: draftState.draft,
|
||||
existingRows,
|
||||
createMutation,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setDuplicateRow(null);
|
||||
}
|
||||
}, [open, setDuplicateRow]);
|
||||
|
||||
const handleClose = guardDialogClose(isCreating, () => onOpenChange(false));
|
||||
|
||||
return {
|
||||
...draftState,
|
||||
patchDraft,
|
||||
locations,
|
||||
problems,
|
||||
locationDetail,
|
||||
locationDetailLoading,
|
||||
locationDetailError,
|
||||
vendors,
|
||||
duplicateRow,
|
||||
setDuplicateRow,
|
||||
handleDuplicateFound,
|
||||
handleCreate,
|
||||
isCreating,
|
||||
handleClose,
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue