From 33026ab9274f273635151c94b9a3784720f3cba8 Mon Sep 17 00:00:00 2001 From: "arthur.bassi" Date: Thu, 13 Aug 2026 11:38:38 -0300 Subject: [PATCH] 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. --- .../_components/wizard/new-wo-wizard.tsx | 151 +++++------------- .../wizard/use-new-wo-wizard-controller.ts | 93 +++++++++++ 2 files changed, 135 insertions(+), 109 deletions(-) create mode 100644 src/app/(protected)/workorders/_components/wizard/use-new-wo-wizard-controller.ts diff --git a/src/app/(protected)/workorders/_components/wizard/new-wo-wizard.tsx b/src/app/(protected)/workorders/_components/wizard/new-wo-wizard.tsx index e7038421..b214e761 100644 --- a/src/app/(protected)/workorders/_components/wizard/new-wo-wizard.tsx +++ b/src/app/(protected)/workorders/_components/wizard/new-wo-wizard.tsx @@ -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) => { - 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 ( <> Guided setup for creating a new work order -
- +
+
- + - {step === 1 && ( + {wizard.step === 1 && ( )} - {step === 2 && ( + {wizard.step === 2 && ( ({ 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 && ( )}
{ - 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} />
setDuplicateRow(null)} + open={wizard.duplicateRow != null} + existing={wizard.duplicateRow} + onDismiss={() => wizard.setDuplicateRow(null)} onViewExisting={(row) => { - setDuplicateRow(null); + wizard.setDuplicateRow(null); onOpenChange(false); onViewExistingWo?.(row); }} diff --git a/src/app/(protected)/workorders/_components/wizard/use-new-wo-wizard-controller.ts b/src/app/(protected)/workorders/_components/wizard/use-new-wo-wizard-controller.ts new file mode 100644 index 00000000..908c6d1a --- /dev/null +++ b/src/app/(protected)/workorders/_components/wizard/use-new-wo-wizard-controller.ts @@ -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) => { + 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, + }; +}