From b2d44944abd6b64f6c1da178fd2d23308832d1c9 Mon Sep 17 00:00:00 2001 From: Arthur Bassi Date: Thu, 30 Jul 2026 11:09:05 -0300 Subject: [PATCH] fix(work-orders): resolve CompDoc/dev merge type and maintainability issues --- .../list/work-orders-list-overlays.tsx | 91 +++++++++++++++ src/app/(protected)/workorders/index.tsx | 109 ++++++++---------- .../mappers/work-order-table-row-mapper.ts | 1 + .../use-cases/use-work-order-detail.ts | 11 -- ...mp-doc-dialog-completion-contract.test.tsx | 7 ++ .../media-upload-modal-api.test.tsx | 7 ++ .../mappers/work-order-form-payload.test.ts | 6 +- .../utils/slide-over-draft.test.ts | 1 + 8 files changed, 157 insertions(+), 76 deletions(-) create mode 100644 src/app/(protected)/workorders/_components/list/work-orders-list-overlays.tsx diff --git a/src/app/(protected)/workorders/_components/list/work-orders-list-overlays.tsx b/src/app/(protected)/workorders/_components/list/work-orders-list-overlays.tsx new file mode 100644 index 00000000..baa789d0 --- /dev/null +++ b/src/app/(protected)/workorders/_components/list/work-orders-list-overlays.tsx @@ -0,0 +1,91 @@ +import { CompDocDialog } from "@/app/(protected)/workorders/_components/comp-doc/comp-doc-dialog"; +import { MediaUploadModal } from "@/app/(protected)/workorders/_components/media/media-upload-modal"; +import { WorkOrdersAdvancedSearchSheet } from "@/app/(protected)/workorders/_components/list/advanced-search/work-orders-advanced-search-sheet"; +import { NewWoWizard } from "@/app/(protected)/workorders/_components/wizard/new-wo-wizard"; +import type { CompDocData, CompDocSaveStatus } from "@/domain/work-orders/types/completion-doc"; +import type { WorkOrderAdvancedFilters } from "@/domain/work-orders/types/work-order-advanced-filters"; +import type { LocationOption } from "@/domain/work-orders/types/work-order-common"; +import type { WorkOrderUser } from "@/domain/work-orders/types/work-order-list"; +import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row"; + +type WorkOrdersListOverlaysProps = { + wizardOpen: boolean; + onWizardOpenChange: (open: boolean) => void; + users: WorkOrderUser[]; + sites: LocationOption[]; + pmTypes: string[]; + vendorTechs: string[]; + advOpen: boolean; + onAdvOpenChange: (open: boolean) => void; + advInitial: WorkOrderAdvancedFilters; + onApplyAdvanced: (next: WorkOrderAdvancedFilters) => void; + onClearAdvanced: () => void; + activeDocRow: WorkOrderTableRow | null; + onDocOpenChange: (open: boolean) => void; + completionDocs: Record; + onSaveCompDoc: (data: CompDocData, status: CompDocSaveStatus) => void; + onDateCompletedChange: (date: string) => void; + activeMediaRow: WorkOrderTableRow | null; + onMediaOpenChange: (open: boolean) => void; +}; + +export function WorkOrdersListOverlays({ + wizardOpen, + onWizardOpenChange, + users, + sites, + pmTypes, + vendorTechs, + advOpen, + onAdvOpenChange, + advInitial, + onApplyAdvanced, + onClearAdvanced, + activeDocRow, + onDocOpenChange, + completionDocs, + onSaveCompDoc, + onDateCompletedChange, + activeMediaRow, + onMediaOpenChange, +}: WorkOrdersListOverlaysProps) { + return ( + <> + + + + + {activeDocRow != null && ( + + )} + + {activeMediaRow != null && ( + + )} + + ); +} diff --git a/src/app/(protected)/workorders/index.tsx b/src/app/(protected)/workorders/index.tsx index 66be947b..da7c0483 100644 --- a/src/app/(protected)/workorders/index.tsx +++ b/src/app/(protected)/workorders/index.tsx @@ -2,15 +2,12 @@ import { useMemo, useState } from "react"; import { Box } from "@mui/material"; import { useNavigate } from "react-router"; import { PageHeader } from "@/components/ui/page-header"; -import { CompDocDialog } from "@/app/(protected)/workorders/_components/comp-doc/comp-doc-dialog"; import { QueryErrorPanel } from "@/components/ui/query-error-panel"; -import { MediaUploadModal } from "@/app/(protected)/workorders/_components/media/media-upload-modal"; -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 { 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 { WorkOrdersListOverlays } from "@/app/(protected)/workorders/_components/list/work-orders-list-overlays"; 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 { 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"; @@ -25,6 +22,14 @@ import { useWorkOrderTableMutations } from "@/domain/work-orders/use-cases/use-w import { hasCompletionDocService } from "@/domain/work-orders/utils/completion-doc-service-gate"; import { useAuthContext } from "@/providers/auth-context"; +function resolveActiveRow( + selected: WorkOrderTableRow | null, + rows: WorkOrderTableRow[], +): WorkOrderTableRow | null { + if (!selected) return null; + return rows.find((row) => row.id === selected.id) ?? selected; +} + export default function WorkOrdersListPage() { const navigate = useNavigate(); const { user } = useAuthContext(); @@ -39,35 +44,26 @@ export default function WorkOrdersListPage() { clearPatch: tableData.clearRowPatch, getRow: (id) => tableData.rows.find((row) => String(row.id) === String(id)), }); - const { data: users = [], isLoading: usersLoading } = useWorkOrderUsers(); const { data: locations = [] } = useWorkOrderLocations(); const { data: vendors = [] } = useWorkOrderVendors(); const { data: problemOptions = [] } = useDropdownOptionsByCategory("Problem"); - const pmTypes = useMemo( () => problemOptions.map((option) => option.value).filter(Boolean), [problemOptions], ); - const vendorTechs = useMemo( () => [...new Set(vendors.map((vendor) => vendor.companyName).filter(Boolean))], [vendors], ); - - const totalCount = tableData.totalCount; - const scopeTotalCount = tableData.scopeTotalCount; - - const activeDocRow = useMemo(() => { - if (!docRow) return null; - return tableData.rows.find((row) => row.id === docRow.id) ?? docRow; - }, [docRow, tableData.rows]); - - const activeMediaRow = useMemo(() => { - if (!mediaRow) return null; - return tableData.rows.find((row) => row.id === mediaRow.id) ?? mediaRow; - }, [mediaRow, tableData.rows]); - + const activeDocRow = useMemo( + () => resolveActiveRow(docRow, tableData.rows), + [docRow, tableData.rows], + ); + const activeMediaRow = useMemo( + () => resolveActiveRow(mediaRow, tableData.rows), + [mediaRow, tableData.rows], + ); return ( {filters.advApplied != null && ( - { + if (!open) setDocRow(null); + }} + completionDocs={completionDocs} + onSaveCompDoc={(data, status) => { + if (!activeDocRow) return; + setCompletionDocs((prev) => ({ ...prev, [String(activeDocRow.id)]: data })); + patchField(activeDocRow.id, { docStatus: status }); + }} + onDateCompletedChange={(date) => { + if (!activeDocRow) return; + patchField(activeDocRow.id, { completedDate: date }); + }} + activeMediaRow={activeMediaRow} + onMediaOpenChange={(open) => { + if (!open) setMediaRow(null); + }} /> - - - - {activeDocRow != null && ( - { - if (!open) setDocRow(null); - }} - data={completionDocs[String(activeDocRow.id)]} - // docStatus persists via board PATCH (Yes|No|NotNeeded); completedDate via - // EditWorkorder. patchField serializes same-id saves so they never race. - // Notes/generatedAt stay client-local (no content endpoint on this branch). - onSave={(data, status) => { - setCompletionDocs((prev) => ({ ...prev, [String(activeDocRow.id)]: data })); - patchField(activeDocRow.id, { docStatus: status }); - }} - onDateCompletedChange={(date) => patchField(activeDocRow.id, { completedDate: date })} - /> - )} - - {activeMediaRow != null && ( - { - if (!open) setMediaRow(null); - }} - /> - )} ); } diff --git a/src/domain/work-orders/mappers/work-order-table-row-mapper.ts b/src/domain/work-orders/mappers/work-order-table-row-mapper.ts index a12162c2..af886e9b 100644 --- a/src/domain/work-orders/mappers/work-order-table-row-mapper.ts +++ b/src/domain/work-orders/mappers/work-order-table-row-mapper.ts @@ -237,6 +237,7 @@ export function mapWorkOrderTableRow(raw: unknown): WorkOrderTableRow { dispatcherId, dispatcherName, scheduledOn, + completedDate: toDateInputValue(readString(item, "completedDate", "CompletedDate")), targetWeek: toDateInputValue(readString(item, "targetWeek", "TargetWeek")), dayGroup, dayLabel, diff --git a/src/domain/work-orders/use-cases/use-work-order-detail.ts b/src/domain/work-orders/use-cases/use-work-order-detail.ts index 7a30153c..a8b3ef3f 100644 --- a/src/domain/work-orders/use-cases/use-work-order-detail.ts +++ b/src/domain/work-orders/use-cases/use-work-order-detail.ts @@ -47,17 +47,6 @@ export function useWorkOrderMedia( }); } -export function useWorkOrderBoardDetail( - id: string | number | undefined, - enabled = true, -): UseQueryResult { - return useQuery({ - queryKey: queryKeys.workOrders.boardDetail(id ?? ""), - queryFn: () => workOrdersApi.getBoardDetail(requireQueryParam(id, "Work order id")), - enabled: enabled && id !== undefined && id !== "", - }); -} - export function useWorkOrderLocations(): UseQueryResult { return useQuery({ queryKey: queryKeys.locations.dropdown(), diff --git a/src/test/app/(protected)/workorders/comp-doc-dialog-completion-contract.test.tsx b/src/test/app/(protected)/workorders/comp-doc-dialog-completion-contract.test.tsx index 875491a1..3d9532d1 100644 --- a/src/test/app/(protected)/workorders/comp-doc-dialog-completion-contract.test.tsx +++ b/src/test/app/(protected)/workorders/comp-doc-dialog-completion-contract.test.tsx @@ -30,6 +30,9 @@ function baseRow(overrides: Partial = {}): WorkOrderTableRow location: "Seattle", locationId: "mock-loc-1", pm: "HVAC", + serviceNotes: "", + extraServices: [], + vendorId: "", company: "Vendor Co", tech: "Tech One", techPhone: "", @@ -51,6 +54,10 @@ function baseRow(overrides: Partial = {}): WorkOrderTableRow mediaCount: 0, title: "PM — SEA1", priority: "Sev 3", + primaryDispatchId: null, + rowVersion: "", + dispatchRowVersion: "", + flagColor: null, ...overrides, }; } diff --git a/src/test/app/(protected)/workorders/media-upload-modal-api.test.tsx b/src/test/app/(protected)/workorders/media-upload-modal-api.test.tsx index fe6318ed..c47e4149 100644 --- a/src/test/app/(protected)/workorders/media-upload-modal-api.test.tsx +++ b/src/test/app/(protected)/workorders/media-upload-modal-api.test.tsx @@ -49,6 +49,9 @@ function baseRow(): WorkOrderTableRow { location: "Seattle", locationId: "loc-1", pm: "", + serviceNotes: "", + extraServices: [], + vendorId: "", company: "", tech: "", techPhone: "", @@ -70,6 +73,10 @@ function baseRow(): WorkOrderTableRow { mediaCount: 1, title: "Emergency", priority: "Sev 1", + primaryDispatchId: null, + rowVersion: "", + dispatchRowVersion: "", + flagColor: null, }; } diff --git a/src/test/domain/work-orders/mappers/work-order-form-payload.test.ts b/src/test/domain/work-orders/mappers/work-order-form-payload.test.ts index f99a03bf..6f7a9378 100644 --- a/src/test/domain/work-orders/mappers/work-order-form-payload.test.ts +++ b/src/test/domain/work-orders/mappers/work-order-form-payload.test.ts @@ -144,9 +144,9 @@ describe("mapWorkOrderFormToUpdateFormData", () => { docStatus: "NN" as const, }; - const payload = mapWorkOrderFormToUpdatePayload(1, values); + const formData = mapWorkOrderFormToUpdateFormData(1, values); - expect(payload.CompletedDate).toBe("2026-07-20"); - expect(payload).not.toHaveProperty("DocStatus"); + expect(formData.get("CompletedDate")).toBe("2026-07-20"); + expect(formData.has("DocStatus")).toBe(false); }); }); diff --git a/src/test/domain/work-orders/utils/slide-over-draft.test.ts b/src/test/domain/work-orders/utils/slide-over-draft.test.ts index 25788b12..30f342b3 100644 --- a/src/test/domain/work-orders/utils/slide-over-draft.test.ts +++ b/src/test/domain/work-orders/utils/slide-over-draft.test.ts @@ -15,6 +15,7 @@ function baseRow(overrides: Partial = {}): WorkOrderTableRow dispatcherId: "10", dispatcherName: "Alex", scheduledOn: "2026-07-16", + completedDate: "", targetWeek: "", dayGroup: "2026-07-16", dayLabel: "Thu",