From ccdad96170cd48868cfc1f77499239d616a96740 Mon Sep 17 00:00:00 2001 From: Arthur Bassi Date: Mon, 7 Sep 2026 11:39:12 -0300 Subject: [PATCH] fix(work-orders): persist type via PATCH and lock completed rows --- .../list/table/cells/assigned-to-cell.tsx | 6 +- .../list/table/cells/type-cell.tsx | 28 +++++--- .../list/table/save-existing-row-type.ts | 25 +++++-- .../list/table/wo-table-row-cells.tsx | 4 +- .../workorders/save-existing-row-type.test.ts | 32 ++++++--- .../(protected)/workorders/type-cell.test.tsx | 17 +++++ .../wo-table-row-cells-type-toast.test.tsx | 69 +++++++++++++++++++ .../wo-table-row-completed-lock.test.tsx | 52 ++++++++++++++ 8 files changed, 205 insertions(+), 28 deletions(-) create mode 100644 src/test/app/(protected)/workorders/wo-table-row-cells-type-toast.test.tsx diff --git a/src/app/(protected)/workorders/_components/list/table/cells/assigned-to-cell.tsx b/src/app/(protected)/workorders/_components/list/table/cells/assigned-to-cell.tsx index cc4cf518..bd0db367 100644 --- a/src/app/(protected)/workorders/_components/list/table/cells/assigned-to-cell.tsx +++ b/src/app/(protected)/workorders/_components/list/table/cells/assigned-to-cell.tsx @@ -28,7 +28,7 @@ export function AssignedToCell({ dispatcherId, users, highlight, onSave }: Assig e.stopPropagation(); setAnchorEl(e.currentTarget); }} - className="group/cell flex w-full items-center gap-1.5 rounded transition-opacity hover:opacity-80" + className="group/cell flex w-full min-w-0 items-center gap-1.5 rounded transition-opacity hover:opacity-80" style={ highlight ? { @@ -43,13 +43,13 @@ export function AssignedToCell({ dispatcherId, users, highlight, onSave }: Assig {selected ? ( ) : ( - + - Select dispatcher + Select dispatcher )} diff --git a/src/app/(protected)/workorders/_components/list/table/cells/type-cell.tsx b/src/app/(protected)/workorders/_components/list/table/cells/type-cell.tsx index 4cf14290..949722d6 100644 --- a/src/app/(protected)/workorders/_components/list/table/cells/type-cell.tsx +++ b/src/app/(protected)/workorders/_components/list/table/cells/type-cell.tsx @@ -18,14 +18,32 @@ type TypeCellProps = { value: WOType | ""; severity?: WorkOrderSeverity | null; q?: string; + viewOnly?: boolean; onSave: (type: WOType, severity: WorkOrderSeverity | null) => void; }; -export function TypeCell({ value, severity = null, q, onSave }: TypeCellProps) { +function TypeCellValue({ value, q }: { value: WOType | ""; q?: string }) { + if (value) { + return ; + } + return Select type of WO; +} + +export function TypeCell({ value, severity = null, q, viewOnly = false, onSave }: TypeCellProps) { const [anchorEl, setAnchorEl] = useState(null); const [modalOpen, setModalOpen] = useState(false); const [modalSeed, setModalSeed] = useState(value); const [resetSeverity, setResetSeverity] = useState(false); + const showSeverityBadge = Boolean(value !== "" && typeNeedsSeverity(value) && severity != null); + + if (viewOnly) { + return ( +
+ + {Boolean(showSeverityBadge && severity != null) && } +
+ ); + } function pick(t: WOType) { setAnchorEl(null); @@ -43,8 +61,6 @@ export function TypeCell({ value, severity = null, q, onSave }: TypeCellProps) { onSave(t, needsSeverity ? severity : null); } - const showSeverityBadge = Boolean(value !== "" && typeNeedsSeverity(value) && severity != null); - return ( <> + ), +})); +vi.mock("@/app/(protected)/workorders/_components/list/table/cells/date-cell", () => ({ + DateCell: () => null, +})); +vi.mock("@/app/(protected)/workorders/_components/list/table/cells/due-date-cell", () => ({ + DueDateCell: () => null, +})); +vi.mock("@/app/(protected)/workorders/_components/list/table/cells/assigned-to-cell", () => ({ + AssignedToCell: () => null, +})); + +const row = { id: 7, woNumber: "00000024965", severity: 3 } as WorkOrderTableRow; + +describe("WoTableRowCells type persist", () => { + it("forwards type save through patch onSuccess instead of onPatchRow", () => { + const onPatch = vi.fn(); + const onPatchRow = vi.fn(); + const handlers = { onPatch } as unknown as WoTableRowHandlers; + + renderWithProviders( + + + + + + +
, + { withAuth: false }, + ); + + fireEvent.click(screen.getByRole("button", { name: "persist-type" })); + + expect(onPatchRow).not.toHaveBeenCalled(); + expect(onPatch).toHaveBeenCalledWith( + 7, + { type: "Emergency", severity: 1 }, + expect.objectContaining({ onSuccess: expect.any(Function) }), + ); + }); +}); diff --git a/src/test/app/(protected)/workorders/wo-table-row-completed-lock.test.tsx b/src/test/app/(protected)/workorders/wo-table-row-completed-lock.test.tsx index 2c02e58c..7ced9b99 100644 --- a/src/test/app/(protected)/workorders/wo-table-row-completed-lock.test.tsx +++ b/src/test/app/(protected)/workorders/wo-table-row-completed-lock.test.tsx @@ -133,4 +133,56 @@ describe("WoTableRow completed selective lock", () => { expect(screen.queryByRole("button", { name: /in progress/i })).not.toBeInTheDocument(); expect(handlers.onPatch).not.toHaveBeenCalled(); }); + + it("does not expose the type picker on a Completed row", () => { + const row = makeRow(); + renderWithProviders( + + + + +
, + { withAuth: false }, + ); + + expect(screen.queryByRole("button", { name: "Type of WO, PM" })).not.toBeInTheDocument(); + expect(screen.getByText("PM")).toBeInTheDocument(); + }); + + it("does not expose the type picker on a Canceled row", () => { + const row = makeRow({ status: "Canceled" }); + renderWithProviders( + + + + +
, + { withAuth: false }, + ); + + expect(screen.queryByRole("button", { name: "Type of WO, PM" })).not.toBeInTheDocument(); + expect(screen.getByText("PM")).toBeInTheDocument(); + }); });