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.
This commit is contained in:
Arthur Bassi 2026-09-09 10:23:23 -03:00
parent 793230f0c2
commit dee1578efe
9 changed files with 125 additions and 27 deletions

View file

@ -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<WorkOrderTableRow>) => void;
}) {
const hasSchedule =
Boolean(draft.scheduledOn) || Boolean(draft.targetWeek) || Boolean(draft.scheduleEnd);
return (
<DateCell
value={draft.scheduledOn}
@ -48,37 +52,59 @@ function SlideOverScheduleOnField({
scheduleEnd={draft.scheduleEnd}
schedMode={draft.schedMode}
onSave={(scheduledOn) =>
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
}
/>
);

View file

@ -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);
},

View file

@ -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
}
/>
</td>
<td

View file

@ -39,6 +39,7 @@ export type WoTableRowHandlers = {
onScheduleDate: (id: string | number, date: string) => 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);
},
};
}

View file

@ -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(
<DateCell
value="2026-06-01"
allowWeekMode
onSave={vi.fn()}
onSaveWeek={vi.fn()}
onSaveRange={vi.fn()}
onClear={onClear}
/>,
);
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", () => {

View file

@ -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();
});
});

View file

@ -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(),

View file

@ -66,6 +66,7 @@ function handlers(overrides: Partial<WoTableRowHandlers> = {}): WoTableRowHandle
onScheduleDate: vi.fn(),
onScheduleWeek: vi.fn(),
onScheduleRange: vi.fn(),
onScheduleClear: vi.fn(),
onOpenSlideOver: vi.fn(),
onReorder: vi.fn(),
onDragStart: vi.fn(),

View file

@ -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(),