) => void;
+ onDuplicateFound?: (existing: WorkOrderTableRow) => void;
};
function InlineMutedDashCell() {
@@ -56,6 +60,7 @@ export function WoTableInlineRowCells({
siteError,
woShowError,
onPatch,
+ onDuplicateFound,
}: WoTableInlineRowCellsProps) {
const draftRow = toInlineDraftRow(draft);
const hasSchedule = Boolean(draft.scheduledOn) || Boolean(draft.targetWeek);
@@ -73,6 +78,7 @@ export function WoTableInlineRowCells({
siteError={siteError}
woShowError={woShowError}
onPatch={onPatch}
+ onDuplicateFound={onDuplicateFound}
/>
|
diff --git a/src/app/(protected)/workorders/_components/list/table/wo-table-inline-row.tsx b/src/app/(protected)/workorders/_components/list/table/wo-table-inline-row.tsx
index 816ed9e3..679a0c3b 100644
--- a/src/app/(protected)/workorders/_components/list/table/wo-table-inline-row.tsx
+++ b/src/app/(protected)/workorders/_components/list/table/wo-table-inline-row.tsx
@@ -6,7 +6,10 @@ import type {
VendorDropdownItem,
WorkOrderUser,
} from "@/domain/work-orders/types/work-order";
-import type { WorkOrderTableDraft } from "@/domain/work-orders/types/work-order-table-row";
+import type {
+ WorkOrderTableDraft,
+ WorkOrderTableRow,
+} from "@/domain/work-orders/types/work-order-table-row";
import { UNSCHEDULED_DAY_KEY } from "@/domain/work-orders/types/work-order-table-row";
import {
draftHasScheduleDate,
@@ -32,6 +35,7 @@ type WoTableInlineRowProps = {
onChange: (draft: WorkOrderTableDraft) => void;
onSave: () => void;
onCancel: () => void;
+ onDuplicateFound?: (existing: WorkOrderTableRow) => void;
};
const PRE_SCHEDULE = new Set(["Incomplete", "Pending", ""]);
@@ -60,6 +64,7 @@ export function WoTableInlineRow({
onChange,
onSave,
onCancel,
+ onDuplicateFound,
}: WoTableInlineRowProps) {
const weekDefault = draftDayKey === UNSCHEDULED_DAY_KEY;
const missing = getInlineCreateMissing(draft);
@@ -127,6 +132,7 @@ export function WoTableInlineRow({
siteError={inlineCreateSiteError(missing)}
woShowError={woShowError}
onPatch={patch}
+ onDuplicateFound={onDuplicateFound}
/>
|
diff --git a/src/app/(protected)/workorders/_components/list/table/wo-table.tsx b/src/app/(protected)/workorders/_components/list/table/wo-table.tsx
index 94e019a7..c733ce89 100644
--- a/src/app/(protected)/workorders/_components/list/table/wo-table.tsx
+++ b/src/app/(protected)/workorders/_components/list/table/wo-table.tsx
@@ -9,11 +9,11 @@ import type {
WorkOrderTablePatch,
WorkOrderTableRow,
} from "@/domain/work-orders/types/work-order-table-row";
-import { useCreateWorkOrderFromWizard } from "@/domain/work-orders/use-cases/use-create-work-order-from-wizard";
+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";
@@ -55,10 +55,14 @@ export function WoTable({
onPatch,
}: WoTableProps) {
const [dueDateHighlightId, setDueDateHighlightId] = useState(null);
+ const [duplicateRow, setDuplicateRow] = useState(null);
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({
@@ -70,6 +74,7 @@ export function WoTable({
onOpenMedia,
onPatch,
onScheduleHighlight: setDueDateHighlightId,
+ onDuplicateFound: setDuplicateRow,
});
const renderRow = (row: WorkOrderTableRow, forceScheduleHighlight = false) => (
@@ -107,12 +112,11 @@ export function WoTable({
vendors={vendors}
pmTypes={pmTypes}
allRows={tableData.rows}
- saving={createMutation.isPending}
+ saving={saving}
onChange={tableData.setDraft}
- onSave={() =>
- createMutation.mutate(buildInlineDraftCreateInput(tableData.draft, users, sites))
- }
+ onSave={handleInlineSave}
onCancel={tableData.cancelDraft}
+ onDuplicateFound={setDuplicateRow}
/>
);
@@ -151,21 +155,33 @@ export function WoTable({
}
return (
-
-
+
+
+
+
+ setDuplicateRow(null)}
+ onViewExisting={(row) => {
+ setDuplicateRow(null);
+ onOpenSlideOver(row, false);
}}
- >
-
-
-
-
+ />
+ >
);
}
diff --git a/src/app/(protected)/workorders/_components/list/work-orders-list-wizard.tsx b/src/app/(protected)/workorders/_components/list/work-orders-list-wizard.tsx
new file mode 100644
index 00000000..c9ab1c6c
--- /dev/null
+++ b/src/app/(protected)/workorders/_components/list/work-orders-list-wizard.tsx
@@ -0,0 +1,32 @@
+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[];
+ onViewExisting: (row: WorkOrderTableRow) => void;
+};
+
+export function WorkOrdersListWizard({
+ open,
+ onOpenChange,
+ users,
+ rows,
+ onViewExisting,
+}: WorkOrdersListWizardProps) {
+ return (
+ {
+ onOpenChange(false);
+ onViewExisting(row);
+ }}
+ />
+ );
+}
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 6643256d..7b7d5326 100644
--- a/src/app/(protected)/workorders/_components/wizard/new-wo-wizard.tsx
+++ b/src/app/(protected)/workorders/_components/wizard/new-wo-wizard.tsx
@@ -1,38 +1,23 @@
-import { useEffect, useMemo, useState } from "react";
import { Dialog } from "@mui/material";
import { dialogBlurBackdropSlotProps } from "@/components/ui/dialog-blur-backdrop";
import { Text } from "@/components/ui/text";
-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 { DuplicateWoNumberDialog } from "@/app/(protected)/workorders/_components/list/duplicate-wo-number-dialog";
import type { WorkOrderUser } from "@/domain/work-orders/types/work-order";
-import {
- 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 type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
+import { WIZARD_STEPS } from "@/domain/work-orders/types/work-order-wizard";
import { WizardFooter } from "@/app/(protected)/workorders/_components/wizard/wizard-footer";
import { WizardSidebar } from "@/app/(protected)/workorders/_components/wizard/wizard-sidebar";
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 { useNewWoWizardState } from "@/app/(protected)/workorders/_components/wizard/use-new-wo-wizard-state";
type NewWoWizardProps = {
open: boolean;
onOpenChange: (open: boolean) => void;
users: WorkOrderUser[];
+ existingRows?: WorkOrderTableRow[];
+ onViewExistingWo?: (row: WorkOrderTableRow) => void;
};
function WizardStepHeader({ step }: { step: number }) {
@@ -75,152 +60,99 @@ function WizardStepHeader({ step }: { step: number }) {
);
}
-export function NewWoWizard({ open, onOpenChange, users }: NewWoWizardProps) {
- const mockFallback = isWoWizardMockEnabled();
- const [step, setStep] = useState(1);
- const [draft, setDraft] = useState({
- ...EMPTY_WIZARD_DRAFT,
- status: "Incomplete",
- });
- const [pocAutoFilledFor, setPocAutoFilledFor] = useState("");
-
- 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("");
- }
- }, [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 = (
- key: K,
- value: WorkOrderWizardDraft[K],
- ) => {
- setDraft((current) => ({ ...current, [key]: value }));
- };
-
- const patchDraft = (patch: Partial) => {
- setDraft((current) => ({ ...current, ...patch }));
- if (patch.locationId !== undefined && patch.locationId !== pocAutoFilledFor) {
- setPocAutoFilledFor("");
- }
- };
-
- const handleCreate = () => createMutation.mutate(draft);
+export function NewWoWizard({
+ open,
+ onOpenChange,
+ users,
+ existingRows = [],
+ onViewExistingWo,
+}: NewWoWizardProps) {
+ const wizard = useNewWoWizardState({ open, users, existingRows, onOpenChange });
return (
- |