mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-07 16:19:01 +00:00
refactor(workorders): extract wizard state hook and list overlays
This commit is contained in:
parent
1ba7ce0003
commit
41dd7d0957
5 changed files with 264 additions and 167 deletions
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { MediaUploadModal } from "@/app/(protected)/workorders/_components/media/media-upload-modal";
|
||||||
|
import { EMPTY_MEDIA, type MediaFile } from "@/domain/work-orders/types/work-order-media";
|
||||||
|
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
|
||||||
|
|
||||||
|
type WorkOrdersListMediaOverlayProps = {
|
||||||
|
activeMediaRow: WorkOrderTableRow | null;
|
||||||
|
onCloseMedia: () => void;
|
||||||
|
mediaFiles: Record<string, MediaFile[]>;
|
||||||
|
onMediaChange: (rowId: string | number, files: MediaFile[]) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function WorkOrdersListMediaOverlay({
|
||||||
|
activeMediaRow,
|
||||||
|
onCloseMedia,
|
||||||
|
mediaFiles,
|
||||||
|
onMediaChange,
|
||||||
|
}: WorkOrdersListMediaOverlayProps) {
|
||||||
|
if (activeMediaRow == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MediaUploadModal
|
||||||
|
row={activeMediaRow}
|
||||||
|
open={Boolean(activeMediaRow)}
|
||||||
|
onOpenChange={(open) => {
|
||||||
|
if (!open) onCloseMedia();
|
||||||
|
}}
|
||||||
|
uploads={mediaFiles[String(activeMediaRow.id)] ?? EMPTY_MEDIA}
|
||||||
|
onChange={(files) => onMediaChange(activeMediaRow.id, files)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { useNavigate } from "react-router";
|
||||||
|
import { NewWoWizard } from "@/app/(protected)/workorders/_components/wizard/new-wo-wizard";
|
||||||
|
import type { WorkOrderUser } from "@/domain/work-orders/types/work-order";
|
||||||
|
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
|
||||||
|
|
||||||
|
type WorkOrdersListWizardProps = {
|
||||||
|
open: boolean;
|
||||||
|
onOpenChange: (open: boolean) => void;
|
||||||
|
users: WorkOrderUser[];
|
||||||
|
rows: WorkOrderTableRow[];
|
||||||
|
onSelectRow: (rowId: string | number) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function WorkOrdersListWizard({
|
||||||
|
open,
|
||||||
|
onOpenChange,
|
||||||
|
users,
|
||||||
|
rows,
|
||||||
|
onSelectRow,
|
||||||
|
}: WorkOrdersListWizardProps) {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NewWoWizard
|
||||||
|
open={open}
|
||||||
|
onOpenChange={onOpenChange}
|
||||||
|
users={users}
|
||||||
|
existingRows={rows}
|
||||||
|
onViewExistingWo={(row) => {
|
||||||
|
onSelectRow(row.id);
|
||||||
|
navigate(`/workorders/${row.id}`);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,36 +1,16 @@
|
||||||
import { useEffect, useMemo, useState } from "react";
|
|
||||||
import { Dialog } from "@mui/material";
|
import { Dialog } from "@mui/material";
|
||||||
import { dialogBlurBackdropSlotProps } from "@/components/ui/dialog-blur-backdrop";
|
import { dialogBlurBackdropSlotProps } from "@/components/ui/dialog-blur-backdrop";
|
||||||
import { Text } from "@/components/ui/text";
|
import { Text } from "@/components/ui/text";
|
||||||
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 { useDropdownOptionsByCategory } from "@/domain/settings/dropdown-options/use-cases/use-dropdown-options-by-category";
|
|
||||||
import { isWoWizardMockEnabled } from "@/domain/work-orders/mocks/is-wo-wizard-mock-enabled";
|
|
||||||
import {
|
|
||||||
getMockWoWizardLocationDetail,
|
|
||||||
MOCK_WO_WIZARD_LOCATIONS,
|
|
||||||
MOCK_WO_WIZARD_SERVICES,
|
|
||||||
MOCK_WO_WIZARD_USERS,
|
|
||||||
MOCK_WO_WIZARD_VENDORS,
|
|
||||||
} from "@/domain/work-orders/mocks/wo-wizard-mock-data";
|
|
||||||
import type { WorkOrderUser } from "@/domain/work-orders/types/work-order";
|
import type { WorkOrderUser } from "@/domain/work-orders/types/work-order";
|
||||||
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
|
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
|
||||||
import {
|
import { WIZARD_STEPS } from "@/domain/work-orders/types/work-order-wizard";
|
||||||
EMPTY_WIZARD_DRAFT,
|
|
||||||
WIZARD_STEPS,
|
|
||||||
type WorkOrderWizardDraft,
|
|
||||||
} from "@/domain/work-orders/types/work-order-wizard";
|
|
||||||
import { useLocationDetail } from "@/domain/locations/use-cases/use-location-detail";
|
|
||||||
import {
|
|
||||||
useWorkOrderLocations,
|
|
||||||
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 { WizardFooter } from "@/app/(protected)/workorders/_components/wizard/wizard-footer";
|
import { WizardFooter } from "@/app/(protected)/workorders/_components/wizard/wizard-footer";
|
||||||
import { WizardSidebar } from "@/app/(protected)/workorders/_components/wizard/wizard-sidebar";
|
import { WizardSidebar } from "@/app/(protected)/workorders/_components/wizard/wizard-sidebar";
|
||||||
import { WizardStepLocationService } from "@/app/(protected)/workorders/_components/wizard/wizard-step-location-service";
|
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 { WizardStepTypeSchedule } from "@/app/(protected)/workorders/_components/wizard/wizard-step-type-schedule";
|
||||||
import { WizardStepVendorTime } from "@/app/(protected)/workorders/_components/wizard/wizard-step-vendor-time";
|
import { WizardStepVendorTime } from "@/app/(protected)/workorders/_components/wizard/wizard-step-vendor-time";
|
||||||
|
import { useNewWoWizardState } from "@/app/(protected)/workorders/_components/wizard/use-new-wo-wizard-state";
|
||||||
|
|
||||||
type NewWoWizardProps = {
|
type NewWoWizardProps = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
|
|
@ -87,98 +67,7 @@ export function NewWoWizard({
|
||||||
existingRows = [],
|
existingRows = [],
|
||||||
onViewExistingWo,
|
onViewExistingWo,
|
||||||
}: NewWoWizardProps) {
|
}: NewWoWizardProps) {
|
||||||
const mockFallback = isWoWizardMockEnabled();
|
const wizard = useNewWoWizardState({ open, users, existingRows, onOpenChange });
|
||||||
const [step, setStep] = useState(1);
|
|
||||||
const [draft, setDraft] = useState<WorkOrderWizardDraft>({
|
|
||||||
...EMPTY_WIZARD_DRAFT,
|
|
||||||
status: "Incomplete",
|
|
||||||
});
|
|
||||||
const [pocAutoFilledFor, setPocAutoFilledFor] = useState("");
|
|
||||||
const [duplicateRow, setDuplicateRow] = useState<WorkOrderTableRow | null>(null);
|
|
||||||
|
|
||||||
const { data: apiLocations = [] } = useWorkOrderLocations();
|
|
||||||
const { data: apiProblems = [] } = useDropdownOptionsByCategory("Problem");
|
|
||||||
const isMockLocation = draft.locationId.startsWith("mock-");
|
|
||||||
const { data: apiLocationDetail } = useLocationDetail(
|
|
||||||
isMockLocation ? undefined : draft.locationId || undefined,
|
|
||||||
);
|
|
||||||
const mockLocationDetail = isMockLocation
|
|
||||||
? getMockWoWizardLocationDetail(draft.locationId)
|
|
||||||
: undefined;
|
|
||||||
const locationDetail = mockLocationDetail ?? apiLocationDetail;
|
|
||||||
const { data: apiVendors = [] } = useWorkOrderVendors(
|
|
||||||
undefined,
|
|
||||||
isMockLocation ? undefined : locationDetail?.zipCode,
|
|
||||||
);
|
|
||||||
const createMutation = useCreateWorkOrderFromWizard(() => onOpenChange(false));
|
|
||||||
|
|
||||||
const wizardUsers = useMemo(() => {
|
|
||||||
if (users.length > 0) {
|
|
||||||
return users;
|
|
||||||
}
|
|
||||||
return mockFallback ? MOCK_WO_WIZARD_USERS : [];
|
|
||||||
}, [mockFallback, users]);
|
|
||||||
const locations =
|
|
||||||
apiLocations.length > 0 ? apiLocations : mockFallback ? MOCK_WO_WIZARD_LOCATIONS : [];
|
|
||||||
const problems =
|
|
||||||
apiProblems.length > 0 ? apiProblems : mockFallback ? MOCK_WO_WIZARD_SERVICES : [];
|
|
||||||
const vendors = apiVendors.length > 0 ? apiVendors : mockFallback ? MOCK_WO_WIZARD_VENDORS : [];
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (open) {
|
|
||||||
setStep(1);
|
|
||||||
setDraft({ ...EMPTY_WIZARD_DRAFT, status: "Incomplete" });
|
|
||||||
setPocAutoFilledFor("");
|
|
||||||
setDuplicateRow(null);
|
|
||||||
}
|
|
||||||
}, [open]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!draft.locationId || !locationDetail) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (pocAutoFilledFor === draft.locationId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setDraft((current) => ({
|
|
||||||
...current,
|
|
||||||
pocName: locationDetail.contact ?? current.pocName,
|
|
||||||
pocPhone: locationDetail.phone ?? current.pocPhone,
|
|
||||||
}));
|
|
||||||
setPocAutoFilledFor(draft.locationId);
|
|
||||||
}, [draft.locationId, locationDetail, pocAutoFilledFor]);
|
|
||||||
|
|
||||||
const setField = <K extends keyof WorkOrderWizardDraft>(
|
|
||||||
key: K,
|
|
||||||
value: WorkOrderWizardDraft[K],
|
|
||||||
) => {
|
|
||||||
setDraft((current) => ({ ...current, [key]: value }));
|
|
||||||
};
|
|
||||||
|
|
||||||
const patchDraft = (patch: Partial<WorkOrderWizardDraft>) => {
|
|
||||||
setDraft((current) => ({ ...current, ...patch }));
|
|
||||||
if (patch.locationId !== undefined && patch.locationId !== pocAutoFilledFor) {
|
|
||||||
setPocAutoFilledFor("");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const resolveDuplicate = (woNumber: string) => findDuplicateWoNumberRow(woNumber, existingRows);
|
|
||||||
|
|
||||||
const handleDuplicateFound = (woNumber: string) => {
|
|
||||||
const duplicate = resolveDuplicate(woNumber);
|
|
||||||
if (duplicate) {
|
|
||||||
setDuplicateRow(duplicate);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCreate = () => {
|
|
||||||
const duplicate = resolveDuplicate(draft.woNumber);
|
|
||||||
if (duplicate) {
|
|
||||||
setDuplicateRow(duplicate);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
createMutation.mutate(draft);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -203,63 +92,63 @@ export function NewWoWizard({
|
||||||
<span className="sr-only">Guided setup for creating a new work order</span>
|
<span className="sr-only">Guided setup for creating a new work order</span>
|
||||||
|
|
||||||
<div className="flex min-h-0 flex-1">
|
<div className="flex min-h-0 flex-1">
|
||||||
<WizardSidebar step={step} draft={draft} />
|
<WizardSidebar step={wizard.step} draft={wizard.draft} />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="min-h-0 flex-1 overflow-y-auto"
|
className="min-h-0 flex-1 overflow-y-auto"
|
||||||
style={{ padding: 32, background: "var(--color-bg-surface)" }}
|
style={{ padding: 32, background: "var(--color-bg-surface)" }}
|
||||||
>
|
>
|
||||||
<WizardStepHeader step={step} />
|
<WizardStepHeader step={wizard.step} />
|
||||||
|
|
||||||
{step === 1 && (
|
{wizard.step === 1 && (
|
||||||
<WizardStepTypeSchedule
|
<WizardStepTypeSchedule
|
||||||
draft={draft}
|
draft={wizard.draft}
|
||||||
users={wizardUsers}
|
users={wizard.wizardUsers}
|
||||||
onUpdate={setField}
|
onUpdate={wizard.setField}
|
||||||
onPatch={patchDraft}
|
onPatch={wizard.patchDraft}
|
||||||
onDuplicateWoNumber={handleDuplicateFound}
|
onDuplicateWoNumber={wizard.handleDuplicateFound}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{step === 2 && (
|
{wizard.step === 2 && (
|
||||||
<WizardStepLocationService
|
<WizardStepLocationService
|
||||||
draft={draft}
|
draft={wizard.draft}
|
||||||
locations={locations}
|
locations={wizard.locations}
|
||||||
locationDetail={locationDetail}
|
locationDetail={wizard.locationDetail}
|
||||||
services={problems.map((p) => ({ id: p.id, value: p.value }))}
|
services={wizard.problems.map((p) => ({ id: p.id, value: p.value }))}
|
||||||
onUpdate={setField}
|
onUpdate={wizard.setField}
|
||||||
onPatch={patchDraft}
|
onPatch={wizard.patchDraft}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{step === 3 && (
|
{wizard.step === 3 && (
|
||||||
<WizardStepVendorTime
|
<WizardStepVendorTime
|
||||||
draft={draft}
|
draft={wizard.draft}
|
||||||
vendors={vendors}
|
vendors={wizard.vendors}
|
||||||
onUpdate={setField}
|
onUpdate={wizard.setField}
|
||||||
onPatch={patchDraft}
|
onPatch={wizard.patchDraft}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<WizardFooter
|
<WizardFooter
|
||||||
step={step}
|
step={wizard.step}
|
||||||
draft={draft}
|
draft={wizard.draft}
|
||||||
onCancel={() => onOpenChange(false)}
|
onCancel={() => onOpenChange(false)}
|
||||||
onBack={() => setStep((s) => s - 1)}
|
onBack={() => wizard.setStep((s) => s - 1)}
|
||||||
onContinue={() => setStep((s) => s + 1)}
|
onContinue={() => wizard.setStep((s) => s + 1)}
|
||||||
onCreate={handleCreate}
|
onCreate={wizard.handleCreate}
|
||||||
isCreating={createMutation.isPending}
|
isCreating={wizard.isCreating}
|
||||||
/>
|
/>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
<DuplicateWoNumberDialog
|
<DuplicateWoNumberDialog
|
||||||
open={duplicateRow != null}
|
open={wizard.duplicateRow != null}
|
||||||
existing={duplicateRow}
|
existing={wizard.duplicateRow}
|
||||||
onDismiss={() => setDuplicateRow(null)}
|
onDismiss={() => wizard.setDuplicateRow(null)}
|
||||||
onViewExisting={(row) => {
|
onViewExisting={(row) => {
|
||||||
setDuplicateRow(null);
|
wizard.setDuplicateRow(null);
|
||||||
onOpenChange(false);
|
onOpenChange(false);
|
||||||
onViewExistingWo?.(row);
|
onViewExistingWo?.(row);
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,148 @@
|
||||||
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
import { isWoWizardMockEnabled } from "@/domain/work-orders/mocks/is-wo-wizard-mock-enabled";
|
||||||
|
import {
|
||||||
|
getMockWoWizardLocationDetail,
|
||||||
|
MOCK_WO_WIZARD_LOCATIONS,
|
||||||
|
MOCK_WO_WIZARD_SERVICES,
|
||||||
|
MOCK_WO_WIZARD_USERS,
|
||||||
|
MOCK_WO_WIZARD_VENDORS,
|
||||||
|
} from "@/domain/work-orders/mocks/wo-wizard-mock-data";
|
||||||
|
import type { WorkOrderUser } from "@/domain/work-orders/types/work-order";
|
||||||
|
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
|
||||||
|
import {
|
||||||
|
EMPTY_WIZARD_DRAFT,
|
||||||
|
type WorkOrderWizardDraft,
|
||||||
|
} from "@/domain/work-orders/types/work-order-wizard";
|
||||||
|
import { useLocationDetail } from "@/domain/locations/use-cases/use-location-detail";
|
||||||
|
import { useDropdownOptionsByCategory } from "@/domain/settings/dropdown-options/use-cases/use-dropdown-options-by-category";
|
||||||
|
import {
|
||||||
|
useWorkOrderLocations,
|
||||||
|
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";
|
||||||
|
|
||||||
|
type UseNewWoWizardStateArgs = {
|
||||||
|
open: boolean;
|
||||||
|
users: WorkOrderUser[];
|
||||||
|
existingRows: WorkOrderTableRow[];
|
||||||
|
onOpenChange: (open: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useNewWoWizardState({
|
||||||
|
open,
|
||||||
|
users,
|
||||||
|
existingRows,
|
||||||
|
onOpenChange,
|
||||||
|
}: UseNewWoWizardStateArgs) {
|
||||||
|
const mockFallback = isWoWizardMockEnabled();
|
||||||
|
const [step, setStep] = useState(1);
|
||||||
|
const [draft, setDraft] = useState<WorkOrderWizardDraft>({
|
||||||
|
...EMPTY_WIZARD_DRAFT,
|
||||||
|
status: "Incomplete",
|
||||||
|
});
|
||||||
|
const [pocAutoFilledFor, setPocAutoFilledFor] = useState("");
|
||||||
|
const [duplicateRow, setDuplicateRow] = useState<WorkOrderTableRow | null>(null);
|
||||||
|
|
||||||
|
const { data: apiLocations = [] } = useWorkOrderLocations();
|
||||||
|
const { data: apiProblems = [] } = useDropdownOptionsByCategory("Problem");
|
||||||
|
const isMockLocation = draft.locationId.startsWith("mock-");
|
||||||
|
const { data: apiLocationDetail } = useLocationDetail(
|
||||||
|
isMockLocation ? undefined : draft.locationId || undefined,
|
||||||
|
);
|
||||||
|
const mockLocationDetail = isMockLocation
|
||||||
|
? getMockWoWizardLocationDetail(draft.locationId)
|
||||||
|
: undefined;
|
||||||
|
const locationDetail = mockLocationDetail ?? apiLocationDetail;
|
||||||
|
const { data: apiVendors = [] } = useWorkOrderVendors(
|
||||||
|
undefined,
|
||||||
|
isMockLocation ? undefined : locationDetail?.zipCode,
|
||||||
|
);
|
||||||
|
const createMutation = useCreateWorkOrderFromWizard(() => onOpenChange(false));
|
||||||
|
|
||||||
|
const wizardUsers = useMemo(() => {
|
||||||
|
if (users.length > 0) {
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
return mockFallback ? MOCK_WO_WIZARD_USERS : [];
|
||||||
|
}, [mockFallback, users]);
|
||||||
|
const locations =
|
||||||
|
apiLocations.length > 0 ? apiLocations : mockFallback ? MOCK_WO_WIZARD_LOCATIONS : [];
|
||||||
|
const problems =
|
||||||
|
apiProblems.length > 0 ? apiProblems : mockFallback ? MOCK_WO_WIZARD_SERVICES : [];
|
||||||
|
const vendors = apiVendors.length > 0 ? apiVendors : mockFallback ? MOCK_WO_WIZARD_VENDORS : [];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open) {
|
||||||
|
setStep(1);
|
||||||
|
setDraft({ ...EMPTY_WIZARD_DRAFT, status: "Incomplete" });
|
||||||
|
setPocAutoFilledFor("");
|
||||||
|
setDuplicateRow(null);
|
||||||
|
}
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!draft.locationId || !locationDetail) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (pocAutoFilledFor === draft.locationId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setDraft((current) => ({
|
||||||
|
...current,
|
||||||
|
pocName: locationDetail.contact ?? current.pocName,
|
||||||
|
pocPhone: locationDetail.phone ?? current.pocPhone,
|
||||||
|
}));
|
||||||
|
setPocAutoFilledFor(draft.locationId);
|
||||||
|
}, [draft.locationId, locationDetail, pocAutoFilledFor]);
|
||||||
|
|
||||||
|
const setField = <K extends keyof WorkOrderWizardDraft>(
|
||||||
|
key: K,
|
||||||
|
value: WorkOrderWizardDraft[K],
|
||||||
|
) => {
|
||||||
|
setDraft((current) => ({ ...current, [key]: value }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const patchDraft = (patch: Partial<WorkOrderWizardDraft>) => {
|
||||||
|
setDraft((current) => ({ ...current, ...patch }));
|
||||||
|
if (patch.locationId !== undefined && patch.locationId !== pocAutoFilledFor) {
|
||||||
|
setPocAutoFilledFor("");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveDuplicate = (woNumber: string) => findDuplicateWoNumberRow(woNumber, existingRows);
|
||||||
|
|
||||||
|
const handleDuplicateFound = (woNumber: string) => {
|
||||||
|
const duplicate = resolveDuplicate(woNumber);
|
||||||
|
if (duplicate) {
|
||||||
|
setDuplicateRow(duplicate);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCreate = () => {
|
||||||
|
const duplicate = resolveDuplicate(draft.woNumber);
|
||||||
|
if (duplicate) {
|
||||||
|
setDuplicateRow(duplicate);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
createMutation.mutate(draft);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
step,
|
||||||
|
setStep,
|
||||||
|
draft,
|
||||||
|
duplicateRow,
|
||||||
|
setDuplicateRow,
|
||||||
|
wizardUsers,
|
||||||
|
locations,
|
||||||
|
problems,
|
||||||
|
vendors,
|
||||||
|
locationDetail,
|
||||||
|
setField,
|
||||||
|
patchDraft,
|
||||||
|
handleDuplicateFound,
|
||||||
|
handleCreate,
|
||||||
|
isCreating: createMutation.isPending,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -3,13 +3,13 @@ import { Box } from "@mui/material";
|
||||||
import { useNavigate } from "react-router";
|
import { useNavigate } from "react-router";
|
||||||
import { PageHeader } from "@/components/ui/page-header";
|
import { PageHeader } from "@/components/ui/page-header";
|
||||||
import { QueryErrorPanel } from "@/components/ui/query-error-panel";
|
import { QueryErrorPanel } from "@/components/ui/query-error-panel";
|
||||||
import { MediaUploadModal } from "@/app/(protected)/workorders/_components/media/media-upload-modal";
|
import { WorkOrdersListMediaOverlay } from "@/app/(protected)/workorders/_components/list/work-orders-list-media-overlay";
|
||||||
|
import { WorkOrdersListWizard } from "@/app/(protected)/workorders/_components/list/work-orders-list-wizard";
|
||||||
import { WorkOrdersAdvancedSearchSheet } from "@/app/(protected)/workorders/_components/list/advanced-search/work-orders-advanced-search-sheet";
|
import { WorkOrdersAdvancedSearchSheet } from "@/app/(protected)/workorders/_components/list/advanced-search/work-orders-advanced-search-sheet";
|
||||||
import { WorkOrdersAdvancedFiltersBanner } from "@/app/(protected)/workorders/_components/list/work-orders-advanced-filters-banner";
|
import { WorkOrdersAdvancedFiltersBanner } from "@/app/(protected)/workorders/_components/list/work-orders-advanced-filters-banner";
|
||||||
import { WorkOrdersFilterBar } from "@/app/(protected)/workorders/_components/list/filter-bar/work-orders-filter-bar";
|
import { WorkOrdersFilterBar } from "@/app/(protected)/workorders/_components/list/filter-bar/work-orders-filter-bar";
|
||||||
import { WorkOrdersListHeader } from "@/app/(protected)/workorders/_components/list/work-orders-list-header";
|
import { WorkOrdersListHeader } from "@/app/(protected)/workorders/_components/list/work-orders-list-header";
|
||||||
import { WoTable } from "@/app/(protected)/workorders/_components/list/table/wo-table";
|
import { WoTable } from "@/app/(protected)/workorders/_components/list/table/wo-table";
|
||||||
import { NewWoWizard } from "@/app/(protected)/workorders/_components/wizard/new-wo-wizard";
|
|
||||||
import { useWorkOrdersListFilters } from "@/app/(protected)/workorders/_hooks/use-work-orders-list-filters";
|
import { useWorkOrdersListFilters } from "@/app/(protected)/workorders/_hooks/use-work-orders-list-filters";
|
||||||
import { useWorkOrdersTableData } from "@/app/(protected)/workorders/_hooks/use-work-orders-table-data";
|
import { useWorkOrdersTableData } from "@/app/(protected)/workorders/_hooks/use-work-orders-table-data";
|
||||||
import { useDropdownOptionsByCategory } from "@/domain/settings/dropdown-options/use-cases/use-dropdown-options-by-category";
|
import { useDropdownOptionsByCategory } from "@/domain/settings/dropdown-options/use-cases/use-dropdown-options-by-category";
|
||||||
|
|
@ -18,7 +18,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 { useWorkOrderUsers } from "@/domain/work-orders/use-cases/use-work-orders-list";
|
import { useWorkOrderUsers } from "@/domain/work-orders/use-cases/use-work-orders-list";
|
||||||
import { EMPTY_MEDIA, type MediaFile } from "@/domain/work-orders/types/work-order-media";
|
import type { MediaFile } from "@/domain/work-orders/types/work-order-media";
|
||||||
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
|
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
|
||||||
import { useWorkOrderTableMutations } from "@/domain/work-orders/use-cases/use-work-order-table-mutations";
|
import { useWorkOrderTableMutations } from "@/domain/work-orders/use-cases/use-work-order-table-mutations";
|
||||||
import { useAuthContext } from "@/providers/auth-context";
|
import { useAuthContext } from "@/providers/auth-context";
|
||||||
|
|
@ -146,31 +146,23 @@ export default function WorkOrdersListPage() {
|
||||||
vendorTechs={vendorTechs}
|
vendorTechs={vendorTechs}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<NewWoWizard
|
<WorkOrdersListWizard
|
||||||
open={wizardOpen}
|
open={wizardOpen}
|
||||||
onOpenChange={setWizardOpen}
|
onOpenChange={setWizardOpen}
|
||||||
users={users}
|
users={users}
|
||||||
existingRows={tableData.rows}
|
rows={tableData.rows}
|
||||||
onViewExistingWo={(row) => {
|
onSelectRow={tableData.setSelectedId}
|
||||||
tableData.setSelectedId(row.id);
|
|
||||||
navigate(`/workorders/${row.id}`);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{activeMediaRow != null && (
|
<WorkOrdersListMediaOverlay
|
||||||
<MediaUploadModal
|
activeMediaRow={activeMediaRow}
|
||||||
row={activeMediaRow}
|
onCloseMedia={() => setMediaRow(null)}
|
||||||
open={!!activeMediaRow}
|
mediaFiles={mediaFiles}
|
||||||
onOpenChange={(open) => {
|
onMediaChange={(rowId, files) => {
|
||||||
if (!open) setMediaRow(null);
|
setMediaFiles((prev) => ({ ...prev, [String(rowId)]: files }));
|
||||||
}}
|
patchField(rowId, { mediaCount: files.length });
|
||||||
uploads={mediaFiles[String(activeMediaRow.id)] ?? EMPTY_MEDIA}
|
}}
|
||||||
onChange={(files) => {
|
/>
|
||||||
setMediaFiles((prev) => ({ ...prev, [String(activeMediaRow.id)]: files }));
|
|
||||||
patchField(activeMediaRow.id, { mediaCount: files.length });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue