mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-02 01:33:12 +00:00
fix(work-orders): resolve CompDoc/dev merge type and maintainability issues
This commit is contained in:
parent
e3aa69e577
commit
b2d44944ab
8 changed files with 157 additions and 76 deletions
|
|
@ -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<string, CompDocData>;
|
||||
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 (
|
||||
<>
|
||||
<WorkOrdersAdvancedSearchSheet
|
||||
open={advOpen}
|
||||
onOpenChange={onAdvOpenChange}
|
||||
initial={advInitial}
|
||||
onApply={onApplyAdvanced}
|
||||
onClearAll={onClearAdvanced}
|
||||
sites={sites}
|
||||
users={users}
|
||||
pmTypes={pmTypes}
|
||||
vendorTechs={vendorTechs}
|
||||
/>
|
||||
|
||||
<NewWoWizard open={wizardOpen} onOpenChange={onWizardOpenChange} users={users} />
|
||||
|
||||
{activeDocRow != null && (
|
||||
<CompDocDialog
|
||||
row={activeDocRow}
|
||||
open={!!activeDocRow}
|
||||
onOpenChange={onDocOpenChange}
|
||||
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={onSaveCompDoc}
|
||||
onDateCompletedChange={onDateCompletedChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
{activeMediaRow != null && (
|
||||
<MediaUploadModal
|
||||
row={activeMediaRow}
|
||||
open={!!activeMediaRow}
|
||||
onOpenChange={onMediaOpenChange}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -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 (
|
||||
<Box className="flex h-full flex-col overflow-hidden p-2.5">
|
||||
<PageHeader
|
||||
|
|
@ -96,14 +92,14 @@ export default function WorkOrdersListPage() {
|
|||
users={users}
|
||||
usersLoading={usersLoading}
|
||||
currentUserId={user?.id}
|
||||
filteredCount={totalCount}
|
||||
scopeTotalCount={scopeTotalCount}
|
||||
filteredCount={tableData.totalCount}
|
||||
scopeTotalCount={tableData.scopeTotalCount}
|
||||
/>
|
||||
|
||||
{filters.advApplied != null && (
|
||||
<WorkOrdersAdvancedFiltersBanner
|
||||
applied={filters.advApplied}
|
||||
totalCount={totalCount}
|
||||
totalCount={tableData.totalCount}
|
||||
sites={locations}
|
||||
users={users}
|
||||
onClearChip={filters.clearAdvancedFilterChip}
|
||||
|
|
@ -146,48 +142,37 @@ export default function WorkOrdersListPage() {
|
|||
onPatch={patchField}
|
||||
/>
|
||||
|
||||
<WorkOrdersAdvancedSearchSheet
|
||||
open={filters.advOpen}
|
||||
onOpenChange={filters.setAdvOpen}
|
||||
initial={filters.advInitial}
|
||||
onApply={filters.applyAdvancedFilters}
|
||||
onClearAll={filters.clearAdvancedFilters}
|
||||
sites={locations}
|
||||
<WorkOrdersListOverlays
|
||||
wizardOpen={wizardOpen}
|
||||
onWizardOpenChange={setWizardOpen}
|
||||
users={users}
|
||||
sites={locations}
|
||||
pmTypes={pmTypes}
|
||||
vendorTechs={vendorTechs}
|
||||
advOpen={filters.advOpen}
|
||||
onAdvOpenChange={filters.setAdvOpen}
|
||||
advInitial={filters.advInitial}
|
||||
onApplyAdvanced={filters.applyAdvancedFilters}
|
||||
onClearAdvanced={filters.clearAdvancedFilters}
|
||||
activeDocRow={activeDocRow}
|
||||
onDocOpenChange={(open) => {
|
||||
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);
|
||||
}}
|
||||
/>
|
||||
|
||||
<NewWoWizard open={wizardOpen} onOpenChange={setWizardOpen} users={users} />
|
||||
|
||||
{activeDocRow != null && (
|
||||
<CompDocDialog
|
||||
row={activeDocRow}
|
||||
open={!!activeDocRow}
|
||||
onOpenChange={(open) => {
|
||||
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 && (
|
||||
<MediaUploadModal
|
||||
row={activeMediaRow}
|
||||
open={!!activeMediaRow}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) setMediaRow(null);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -47,17 +47,6 @@ export function useWorkOrderMedia(
|
|||
});
|
||||
}
|
||||
|
||||
export function useWorkOrderBoardDetail(
|
||||
id: string | number | undefined,
|
||||
enabled = true,
|
||||
): UseQueryResult<WorkOrderBoardDetail, Error> {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.workOrders.boardDetail(id ?? ""),
|
||||
queryFn: () => workOrdersApi.getBoardDetail(requireQueryParam(id, "Work order id")),
|
||||
enabled: enabled && id !== undefined && id !== "",
|
||||
});
|
||||
}
|
||||
|
||||
export function useWorkOrderLocations(): UseQueryResult<LocationOption[], Error> {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.locations.dropdown(),
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ function baseRow(overrides: Partial<WorkOrderTableRow> = {}): 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> = {}): WorkOrderTableRow
|
|||
mediaCount: 0,
|
||||
title: "PM — SEA1",
|
||||
priority: "Sev 3",
|
||||
primaryDispatchId: null,
|
||||
rowVersion: "",
|
||||
dispatchRowVersion: "",
|
||||
flagColor: null,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ function baseRow(overrides: Partial<WorkOrderTableRow> = {}): WorkOrderTableRow
|
|||
dispatcherId: "10",
|
||||
dispatcherName: "Alex",
|
||||
scheduledOn: "2026-07-16",
|
||||
completedDate: "",
|
||||
targetWeek: "",
|
||||
dayGroup: "2026-07-16",
|
||||
dayLabel: "Thu",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue