From dee1578efedc9658b4ef5c60ec5600e4c14de7f7 Mon Sep 17 00:00:00 2001 From: Arthur Bassi Date: Wed, 9 Sep 2026 10:23:23 -0300 Subject: [PATCH] feat(work-orders): persist Schedule On clear from board and slide-over Clearing a concrete date must go through the shared schedule patch so status can revert to Pending. --- .../tabs/slide-over-info-tab-edit-view.tsx | 80 ++++++++++++------- .../list/table/use-wo-table-row-handlers.ts | 9 +++ .../list/table/wo-table-row-cells.tsx | 5 ++ .../_components/list/table/wo-table-row.tsx | 5 ++ .../date-cell-schedule-range.test.tsx | 18 +++++ ...use-wo-table-row-handlers-schedule.test.ts | 32 ++++++++ .../wo-table-row-completed-lock.test.tsx | 1 + .../wo-table-row-reorder-keyboard.test.tsx | 1 + .../wo-table-unscheduled-highlight.test.tsx | 1 + 9 files changed, 125 insertions(+), 27 deletions(-) diff --git a/src/app/(protected)/workorders/_components/detail/tabs/slide-over-info-tab-edit-view.tsx b/src/app/(protected)/workorders/_components/detail/tabs/slide-over-info-tab-edit-view.tsx index 1bc70fae..7bfb16a3 100644 --- a/src/app/(protected)/workorders/_components/detail/tabs/slide-over-info-tab-edit-view.tsx +++ b/src/app/(protected)/workorders/_components/detail/tabs/slide-over-info-tab-edit-view.tsx @@ -18,6 +18,7 @@ import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-ta import type { WizardWOStatus } from "@/domain/work-orders/types/work-order-wizard"; import { deriveDayFields } from "@/domain/work-orders/utils/table-day-utils"; import { toVendorTablePatch } from "@/domain/work-orders/utils/vendor-assignment-patch"; +import { withAutoScheduleStatus } from "@/domain/work-orders/utils/wo-lifecycle-transitions"; import { DocBadge } from "./slide-over-doc-badge"; type SlideOverInfoTabEditViewProps = { @@ -39,6 +40,9 @@ function SlideOverScheduleOnField({ draft: WorkOrderTableRow; onDraftChange: (patch: Partial) => void; }) { + const hasSchedule = + Boolean(draft.scheduledOn) || Boolean(draft.targetWeek) || Boolean(draft.scheduleEnd); + return ( - onDraftChange({ - scheduledOn, - scheduleWeekOnly: false, - targetWeek: scheduledOn ? "" : draft.targetWeek, - scheduleEnd: "", - schedMode: "date", - isPastDue: false, - ...deriveDayFields(scheduledOn), - }) + onDraftChange( + withAutoScheduleStatus(draft, { + scheduledOn, + scheduleWeekOnly: false, + targetWeek: scheduledOn ? "" : draft.targetWeek, + scheduleEnd: "", + schedMode: "date", + isPastDue: false, + ...deriveDayFields(scheduledOn), + }), + ) } onSaveWeek={(weekStart) => - onDraftChange({ - scheduledOn: "", - targetWeek: weekStart, - scheduleWeekOnly: true, - scheduleEnd: "", - schedMode: "week", - isPastDue: false, - ...deriveDayFields(""), - }) + onDraftChange( + withAutoScheduleStatus(draft, { + scheduledOn: "", + targetWeek: weekStart, + scheduleWeekOnly: true, + scheduleEnd: "", + schedMode: "week", + isPastDue: false, + ...deriveDayFields(""), + }), + ) } onSaveRange={(start, end) => - onDraftChange({ - scheduledOn: start, - scheduleEnd: end, - schedMode: "range", - scheduleWeekOnly: false, - targetWeek: "", - isPastDue: false, - ...deriveDayFields(start), - }) + onDraftChange( + withAutoScheduleStatus(draft, { + scheduledOn: start, + scheduleEnd: end, + schedMode: "range", + scheduleWeekOnly: false, + targetWeek: "", + isPastDue: false, + ...deriveDayFields(start), + }), + ) + } + onClear={ + hasSchedule + ? () => + onDraftChange( + withAutoScheduleStatus(draft, { + scheduledOn: "", + targetWeek: "", + scheduleWeekOnly: false, + scheduleEnd: "", + schedMode: undefined, + isPastDue: false, + ...deriveDayFields(""), + }), + ) + : undefined } /> ); diff --git a/src/app/(protected)/workorders/_components/list/table/use-wo-table-row-handlers.ts b/src/app/(protected)/workorders/_components/list/table/use-wo-table-row-handlers.ts index 20172baa..08e594e6 100644 --- a/src/app/(protected)/workorders/_components/list/table/use-wo-table-row-handlers.ts +++ b/src/app/(protected)/workorders/_components/list/table/use-wo-table-row-handlers.ts @@ -138,6 +138,15 @@ export function useWoTableRowHandlers({ }, }); }, + onScheduleClear: (id: string | number) => { + onPatch(id, applySchedulePatch("", false)); + weekMoveNotify(weekMonday, { + onNavigate: () => { + onNavigateToDay(weekMonday); + onOpenAdvancedFilters?.(); + }, + }); + }, onDuplicateFound: (existing: WorkOrderTableRow) => { onDuplicateFound?.(existing); }, diff --git a/src/app/(protected)/workorders/_components/list/table/wo-table-row-cells.tsx b/src/app/(protected)/workorders/_components/list/table/wo-table-row-cells.tsx index 90b16533..e6f69f55 100644 --- a/src/app/(protected)/workorders/_components/list/table/wo-table-row-cells.tsx +++ b/src/app/(protected)/workorders/_components/list/table/wo-table-row-cells.tsx @@ -137,6 +137,11 @@ export function WoTableRowCells({ onSave={(date) => handlers.onScheduleDate(row.id, date)} onSaveWeek={(week) => handlers.onScheduleWeek(row.id, week)} onSaveRange={(start, end) => handlers.onScheduleRange(row.id, start, end)} + onClear={ + Boolean(row.scheduledOn) || Boolean(row.targetWeek) || Boolean(row.scheduleEnd) + ? () => handlers.onScheduleClear(row.id) + : undefined + } /> void; onScheduleWeek: (id: string | number, week: string) => void; onScheduleRange: (id: string | number, start: string, end: string) => void; + onScheduleClear: (id: string | number) => void; onDuplicateFound?: (existing: WorkOrderTableRow) => void; onOpenSlideOver: (row: WorkOrderTableRow) => void; onRequestComplete?: (row: WorkOrderTableRow) => void; @@ -89,6 +90,10 @@ function guardScheduleHandlers( if (!canApplyWorkOrderTablePatch(row.status, { scheduledOn: start })) return; handlers.onScheduleRange(id, start, end); }, + onScheduleClear: (id) => { + if (!canApplyWorkOrderTablePatch(row.status, { scheduledOn: "" })) return; + handlers.onScheduleClear(id); + }, }; } diff --git a/src/test/app/(protected)/workorders/date-cell-schedule-range.test.tsx b/src/test/app/(protected)/workorders/date-cell-schedule-range.test.tsx index 698918c4..3344a87a 100644 --- a/src/test/app/(protected)/workorders/date-cell-schedule-range.test.tsx +++ b/src/test/app/(protected)/workorders/date-cell-schedule-range.test.tsx @@ -105,6 +105,24 @@ describe("DateCell — Schedule On range", () => { expect(onSave).not.toHaveBeenCalled(); expect(onSaveRange).not.toHaveBeenCalled(); }); + it("shows Clear when a concrete schedule exists", async () => { + const onClear = vi.fn(); + renderWithProviders( + , + ); + + fireEvent.click(screen.getByRole("button", { name: /06-01/ })); + fireEvent.click(await screen.findByRole("button", { name: /clear/i })); + + expect(onClear).toHaveBeenCalledTimes(1); + }); }); describe("DueDateCell — no Schedule On range tabs", () => { diff --git a/src/test/app/(protected)/workorders/use-wo-table-row-handlers-schedule.test.ts b/src/test/app/(protected)/workorders/use-wo-table-row-handlers-schedule.test.ts index c4cc0867..2b9fca2b 100644 --- a/src/test/app/(protected)/workorders/use-wo-table-row-handlers-schedule.test.ts +++ b/src/test/app/(protected)/workorders/use-wo-table-row-handlers-schedule.test.ts @@ -151,4 +151,36 @@ describe("useWoTableRowHandlers schedule navigation (SH-240)", () => { expect(onNavigateToDay).toHaveBeenCalledWith("2026-05-25"); expect(onOpenAdvancedFilters).toHaveBeenCalled(); }); + + it("clears Schedule On and offers Advanced Filters navigation", () => { + const onNavigateToDay = vi.fn(); + const onOpenAdvancedFilters = vi.fn(); + const onPatch = vi.fn(); + + const { result } = renderHook(() => + useWoTableRowHandlers({ + tableData: buildTableData(), + weekMonday: "2026-05-18", + onNavigateToDay, + onOpenAdvancedFilters, + onOpenSlideOver: vi.fn(), + onOpenMedia: vi.fn(), + onPatch, + onScheduleHighlight: vi.fn(), + }), + ); + + result.current.onScheduleClear("wo-1"); + + expect(onPatch).toHaveBeenCalledWith("wo-1", applySchedulePatch("", false)); + expect(weekMoveNotify).toHaveBeenCalledWith( + "2026-05-18", + expect.objectContaining({ onNavigate: expect.any(Function) }), + ); + + const { onNavigate } = weekMoveNotify.mock.calls[0][1] as { onNavigate: () => void }; + onNavigate(); + expect(onNavigateToDay).toHaveBeenCalledWith("2026-05-18"); + expect(onOpenAdvancedFilters).toHaveBeenCalled(); + }); }); 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 1ce5a477..3e8ea166 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 @@ -66,6 +66,7 @@ const handlers: WoTableRowHandlers = { onScheduleDate: vi.fn(), onScheduleWeek: vi.fn(), onScheduleRange: vi.fn(), + onScheduleClear: vi.fn(), onOpenSlideOver: vi.fn(), onOpenCompDoc: vi.fn(), onOpenUplifts: vi.fn(), diff --git a/src/test/app/(protected)/workorders/wo-table-row-reorder-keyboard.test.tsx b/src/test/app/(protected)/workorders/wo-table-row-reorder-keyboard.test.tsx index 868d7412..89c0241e 100644 --- a/src/test/app/(protected)/workorders/wo-table-row-reorder-keyboard.test.tsx +++ b/src/test/app/(protected)/workorders/wo-table-row-reorder-keyboard.test.tsx @@ -66,6 +66,7 @@ function handlers(overrides: Partial = {}): WoTableRowHandle onScheduleDate: vi.fn(), onScheduleWeek: vi.fn(), onScheduleRange: vi.fn(), + onScheduleClear: vi.fn(), onOpenSlideOver: vi.fn(), onReorder: vi.fn(), onDragStart: vi.fn(), diff --git a/src/test/app/(protected)/workorders/wo-table-unscheduled-highlight.test.tsx b/src/test/app/(protected)/workorders/wo-table-unscheduled-highlight.test.tsx index 645154c8..048deb3b 100644 --- a/src/test/app/(protected)/workorders/wo-table-unscheduled-highlight.test.tsx +++ b/src/test/app/(protected)/workorders/wo-table-unscheduled-highlight.test.tsx @@ -65,6 +65,7 @@ const handlers: WoTableRowHandlers = { onScheduleDate: vi.fn(), onScheduleWeek: vi.fn(), onScheduleRange: vi.fn(), + onScheduleClear: vi.fn(), onOpenSlideOver: vi.fn(), onOpenCompDoc: vi.fn(), onOpenUplifts: vi.fn(),