diff --git a/src/app/(protected)/workorders/_hooks/use-work-orders-table-local-state.ts b/src/app/(protected)/workorders/_hooks/use-work-orders-table-local-state.ts index 9401ba1f..fff37a87 100644 --- a/src/app/(protected)/workorders/_hooks/use-work-orders-table-local-state.ts +++ b/src/app/(protected)/workorders/_hooks/use-work-orders-table-local-state.ts @@ -1,5 +1,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row"; +import { applyCarryOverDisplayDayFields } from "@/domain/work-orders/utils/carry-over-display"; import { deriveDayFields } from "@/domain/work-orders/utils/table-day-utils"; import { enrichTableRow } from "@/domain/work-orders/utils/table-filter-utils"; import { @@ -62,8 +63,11 @@ export function useWorkOrdersTableLocalState({ const displayRows = useMemo(() => { const apiIds = new Set(apiRows.map((r) => String(r.id))); const extra = localRows.filter((r) => !apiIds.has(String(r.id))); - return applyRowOrderByDay([...apiRows, ...extra.map(enrichTableRow)], rowOrderByDay); - }, [apiRows, localRows, rowOrderByDay]); + const merged = [...apiRows, ...extra.map(enrichTableRow)].map((row) => + applyCarryOverDisplayDayFields(row, weekMonday), + ); + return applyRowOrderByDay(merged, rowOrderByDay); + }, [apiRows, localRows, rowOrderByDay, weekMonday]); const clearRowPatch = useCallback((id: string | number) => { const key = String(id); diff --git a/src/domain/work-orders/utils/carry-over-display.ts b/src/domain/work-orders/utils/carry-over-display.ts index ec31b3d8..dd92b2d3 100644 --- a/src/domain/work-orders/utils/carry-over-display.ts +++ b/src/domain/work-orders/utils/carry-over-display.ts @@ -1,5 +1,6 @@ import { compactWeekLabel } from "@/domain/work-orders/utils/list-week-utils"; import { formatRescheduleCountLabel } from "@/domain/work-orders/utils/reschedule-indicator"; +import { deriveDayFields } from "@/domain/work-orders/utils/table-day-utils"; import { addDaysIso, mondayOf } from "@/domain/work-orders/utils/wizard-date-utils"; const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/; @@ -92,3 +93,17 @@ export function rowMatchesCarryOverDay( } return mapCarryOverDisplayDay(row.scheduledOn, weekMonday) === selectedDayIso; } + +/** Aligns UI day keys with the mapped weekday so reorder stays on the displayed tab. */ +export function applyCarryOverDisplayDayFields< + T extends CarryOverRow & { dayGroup: string; dayLabel: string }, +>(row: T, weekMonday: string): T { + if (!isCarryOverDisplayRow(row, weekMonday)) { + return row; + } + const displayDay = mapCarryOverDisplayDay(row.scheduledOn, weekMonday); + if (!displayDay) { + return row; + } + return { ...row, ...deriveDayFields(displayDay) }; +} diff --git a/src/domain/work-orders/utils/table-filter-utils.ts b/src/domain/work-orders/utils/table-filter-utils.ts index dcda43c5..4f2a045b 100644 --- a/src/domain/work-orders/utils/table-filter-utils.ts +++ b/src/domain/work-orders/utils/table-filter-utils.ts @@ -7,6 +7,7 @@ import type { WorkOrderTableRow, } from "@/domain/work-orders/types/work-order-table-row"; import { ADVANCED_FILTER_PREDICATES } from "@/domain/work-orders/utils/advanced-filter-predicates"; +import { isCarryOverDisplayRow } from "@/domain/work-orders/utils/carry-over-display"; import { isDateInWeek } from "@/domain/work-orders/utils/table-day-utils"; import { weekRange } from "@/domain/work-orders/utils/list-week-utils"; @@ -96,7 +97,7 @@ export function filterByBarAndSearch( if (row.scheduledOn) { if (row.scheduledOn < weekFrom || row.scheduledOn > weekTo) { - return false; + return isCarryOverDisplayRow(row, bar.weekMonday); } } else if (row.targetWeek) { if (!isDateInWeek(row.targetWeek, bar.weekMonday)) { diff --git a/src/test/domain/work-orders/utils/carry-over-display.test.ts b/src/test/domain/work-orders/utils/carry-over-display.test.ts index a52e7be3..00b54dfe 100644 --- a/src/test/domain/work-orders/utils/carry-over-display.test.ts +++ b/src/test/domain/work-orders/utils/carry-over-display.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from "vitest"; import { + applyCarryOverDisplayDayFields, formatCarriedOverHover, isCarryOverDisplayRow, mapCarryOverDisplayDay, @@ -60,4 +61,34 @@ describe("carry-over-display", () => { expect(mapCarryOverDisplayDay("2026-05-16", "2026-05-18")).toBe("2026-05-18"); expect(mapCarryOverDisplayDay("2026-05-17", "2026-05-18")).toBe("2026-05-18"); }); + + it("remaps dayGroup to the viewed weekday without changing scheduledOn", () => { + const row = { + scheduledOn: "2026-05-13", + status: "Scheduled", + dayGroup: "05/13", + dayLabel: "WED · 05/13", + }; + const next = applyCarryOverDisplayDayFields(row, "2026-05-18"); + expect(next.scheduledOn).toBe("2026-05-13"); + expect(next.dayGroup).toBe("05/20"); + expect(next.dayLabel).toBe("WED · 05/20"); + }); + + it("leaves in-week and terminal rows' dayGroup unchanged", () => { + const inWeek = { + scheduledOn: "2026-05-20", + status: "Scheduled", + dayGroup: "05/20", + dayLabel: "WED · 05/20", + }; + const completed = { + scheduledOn: "2026-05-13", + status: "Completed", + dayGroup: "05/13", + dayLabel: "WED · 05/13", + }; + expect(applyCarryOverDisplayDayFields(inWeek, "2026-05-18")).toEqual(inWeek); + expect(applyCarryOverDisplayDayFields(completed, "2026-05-18")).toEqual(completed); + }); }); diff --git a/src/test/domain/work-orders/utils/table-filter-utils.test.ts b/src/test/domain/work-orders/utils/table-filter-utils.test.ts index fb68c6f4..85651d2c 100644 --- a/src/test/domain/work-orders/utils/table-filter-utils.test.ts +++ b/src/test/domain/work-orders/utils/table-filter-utils.test.ts @@ -137,6 +137,28 @@ describe("filterByBarAndSearch", () => { expect(ids).toEqual(["in"]); }); + it("keeps prior-week unresolved carry-over rows that match a one-character search", () => { + const carried = makeRow({ + id: "co", + woNumber: "WO-CARRY", + scheduledOn: "2026-05-13", + status: "Scheduled", + ...deriveDayFields("2026-05-13"), + }); + const completed = makeRow({ + id: "done", + woNumber: "WO-DONE", + scheduledOn: "2026-05-13", + status: "Completed", + ...deriveDayFields("2026-05-13"), + }); + + const ids = filterByBarAndSearch([carried, completed], bar("2026-05-18"), "W").map( + (row) => row.id, + ); + expect(ids).toEqual(["co"]); + }); + it("keeps the union of selected types (OR)", () => { const pm = makeRow({ id: "pm", type: "PM", scheduledOn: "2026-06-03" }); const emergency = makeRow({ id: "em", type: "Emergency", scheduledOn: "2026-06-03" });