diff --git a/src/app/(protected)/workorders/_components/detail/use-slide-over-edit-state.ts b/src/app/(protected)/workorders/_components/detail/use-slide-over-edit-state.ts index c7e526d3..9ebf9e3e 100644 --- a/src/app/(protected)/workorders/_components/detail/use-slide-over-edit-state.ts +++ b/src/app/(protected)/workorders/_components/detail/use-slide-over-edit-state.ts @@ -1,3 +1,4 @@ +import type { Dispatch, SetStateAction } from "react"; import { useEffect, useMemo, useState } from "react"; import type { WorkOrderTablePatch, @@ -19,6 +20,8 @@ function isSlideOverInfoLocked(status: string | null | undefined): boolean { return isWorkOrderFullyLocked(status) || isWorkOrderCoreLocked(status); } +type StateSetter = Dispatch>; + type UseSlideOverEditStateArgs = { row: WorkOrderTableRow | null; editMode: boolean; @@ -33,6 +36,47 @@ type UseSlideOverEditStateArgs = { openRequestKey?: number; }; +function useSlideOverInitialization({ + row, + editMode, + initialTab, + setTab, + setDraft, + setBaseline, + setEditing, + setShowUnsaved, + setUnsavedIntent, +}: { + row: WorkOrderTableRow | null; + editMode: boolean; + initialTab: SlideOverTab; + setTab: (tab: SlideOverTab) => void; + setDraft: StateSetter; + setBaseline: StateSetter; + setEditing: StateSetter; + setShowUnsaved: StateSetter; + setUnsavedIntent: StateSetter<"close" | "cancel-edit" | null>; +}) { + useEffect(() => { + if (!row) { + setDraft(null); + setBaseline(null); + setEditing(false); + setShowUnsaved(false); + setUnsavedIntent(null); + return; + } + setDraft(row); + setBaseline(row); + setEditing(editMode && !isSlideOverInfoLocked(row.status)); + setTab(editMode ? "info" : initialTab); + setShowUnsaved(false); + setUnsavedIntent(null); + // Intentionally identity-only: depending on `row` would absorb optimistic patches. + // eslint-disable-next-line react-hooks/exhaustive-deps -- row.id / editMode gate + }, [row?.id, editMode]); +} + export function useSlideOverEditState({ row, editMode, @@ -53,24 +97,17 @@ export function useSlideOverEditState({ // Reset draft/baseline only when the opened WO identity or editMode intent changes — // not on every optimistic parent-row field update during a failed/in-flight save. - useEffect(() => { - if (!row) { - setDraft(null); - setBaseline(null); - setEditing(false); - setShowUnsaved(false); - setUnsavedIntent(null); - return; - } - setDraft(row); - setBaseline(row); - setEditing(editMode && !isSlideOverInfoLocked(row.status)); - setTab(editMode ? "info" : initialTab); - setShowUnsaved(false); - setUnsavedIntent(null); - // Intentionally identity-only: depending on `row` would absorb optimistic patches. - // eslint-disable-next-line react-hooks/exhaustive-deps -- row.id / editMode gate - }, [row?.id, editMode]); + useSlideOverInitialization({ + row, + editMode, + initialTab, + setTab, + setDraft, + setBaseline, + setEditing, + setShowUnsaved, + setUnsavedIntent, + }); useEffect(() => { if (row?.id == null || editMode) return; @@ -106,8 +143,7 @@ export function useSlideOverEditState({ }); }, [infoSource, editing, baseline]); - const activeDraft = draft ?? infoSource; - const dirtyBaseline = baseline ?? infoSource; + const [activeDraft, dirtyBaseline] = [draft ?? infoSource, baseline ?? infoSource]; const isDirty = useMemo(() => { if (!dirtyBaseline || !activeDraft || !editing) return false; return isSlideOverDraftDirty(activeDraft, dirtyBaseline); diff --git a/src/app/(protected)/workorders/_hooks/use-work-orders-list-page.ts b/src/app/(protected)/workorders/_hooks/use-work-orders-list-page.ts index 41f338fa..d912d0ec 100644 --- a/src/app/(protected)/workorders/_hooks/use-work-orders-list-page.ts +++ b/src/app/(protected)/workorders/_hooks/use-work-orders-list-page.ts @@ -18,12 +18,45 @@ import { guardDialogClose } from "@/lib/dialog-guard"; import { useWorkOrdersListFilters } from "./use-work-orders-list-filters"; import { useWorkOrdersTableData } from "./use-work-orders-table-data"; -export function useWorkOrdersListPage() { - const [wizardOpen, setWizardOpen] = useState(false); +function useSlideOverOpenState() { const [slideOverRow, setSlideOverRow] = useState(null); const [slideOverEdit, setSlideOverEdit] = useState(false); const [slideOverTab, setSlideOverTab] = useState("info"); const [slideOverOpenRequestKey, setSlideOverOpenRequestKey] = useState(0); + + const handleOpenSlideOver = ( + row: WorkOrderTableRow, + edit = false, + tab: SlideOverTab = "info", + ) => { + setSlideOverRow(row); + setSlideOverEdit(edit); + setSlideOverTab(tab); + setSlideOverOpenRequestKey((key) => key + 1); + }; + + return { + slideOverRow, + setSlideOverRow, + slideOverEdit, + setSlideOverEdit, + slideOverTab, + slideOverOpenRequestKey, + handleOpenSlideOver, + }; +} + +export function useWorkOrdersListPage() { + const [wizardOpen, setWizardOpen] = useState(false); + const { + slideOverRow, + setSlideOverRow, + slideOverEdit, + setSlideOverEdit, + slideOverTab, + slideOverOpenRequestKey, + handleOpenSlideOver, + } = useSlideOverOpenState(); const [confirmCancel, setConfirmCancel] = useState(null); const [confirmComplete, setConfirmComplete] = useState(null); const [docRow, setDocRow] = useState(null); @@ -77,17 +110,6 @@ export function useWorkOrdersListPage() { return tableData.rows.find((row) => row.id === upliftsRow.id) ?? upliftsRow; }, [upliftsRow, tableData.rows]); - const handleOpenSlideOver = ( - row: WorkOrderTableRow, - edit = false, - tab: SlideOverTab = "info", - ) => { - setSlideOverRow(row); - setSlideOverEdit(edit); - setSlideOverTab(tab); - setSlideOverOpenRequestKey((key) => key + 1); - }; - const handleCloseSlideOver = () => { setSlideOverRow(null); setSlideOverEdit(false);