diff --git a/src/app/(protected)/workorders/_components/list/filter-bar/dispatcher-filter.tsx b/src/app/(protected)/workorders/_components/list/filter-bar/dispatcher-filter.tsx index 9db6ca98..6bee3519 100644 --- a/src/app/(protected)/workorders/_components/list/filter-bar/dispatcher-filter.tsx +++ b/src/app/(protected)/workorders/_components/list/filter-bar/dispatcher-filter.tsx @@ -14,6 +14,7 @@ type DispatcherFilterProps = { users: WorkOrderUser[]; usersLoading?: boolean; currentUserId?: string | number; + disabled?: boolean; }; function userInitials(name: string): string { @@ -40,6 +41,7 @@ export function DispatcherFilter({ users, usersLoading = false, currentUserId, + disabled = false, }: DispatcherFilterProps) { const { anchorEl, open, setAnchor, close } = useMenuAnchor(); @@ -86,6 +88,7 @@ export function DispatcherFilter({ void; + + disabled?: boolean; }; const VISIBLE_COUNT = 3; @@ -51,7 +53,7 @@ function WeekLiveBadge() { ); } -export function WeekNav({ weekMonday, onChange }: WeekNavProps) { +export function WeekNav({ weekMonday, onChange, disabled = false }: WeekNavProps) { const weeks = useMemo( () => buildWeekWindow(weekMonday, WINDOW_BEFORE, WINDOW_AFTER), @@ -91,7 +93,7 @@ export function WeekNav({ weekMonday, onChange }: WeekNavProps) { return (
setOffset((current) => current - 1)} aria-label="Previous weeks" > @@ -109,7 +111,7 @@ export function WeekNav({ weekMonday, onChange }: WeekNavProps) { onChange(week.mondayIso)} > {week.label} @@ -120,7 +122,7 @@ export function WeekNav({ weekMonday, onChange }: WeekNavProps) { })} setOffset((current) => current + 1)} aria-label="Next weeks" > diff --git a/src/app/(protected)/workorders/_components/list/filter-bar/wo-type-segment.tsx b/src/app/(protected)/workorders/_components/list/filter-bar/wo-type-segment.tsx index 3810f8ec..03d89520 100644 --- a/src/app/(protected)/workorders/_components/list/filter-bar/wo-type-segment.tsx +++ b/src/app/(protected)/workorders/_components/list/filter-bar/wo-type-segment.tsx @@ -8,15 +8,22 @@ type WoTypeSegmentProps = { value: WorkOrderTypeFilter; onChange: (value: WorkOrderTypeFilter) => void; + + disabled?: boolean; }; const OPTIONS: WorkOrderTypeFilter[] = ["All", ...WO_TYPES]; -export function WoTypeSegment({ value, onChange }: WoTypeSegmentProps) { +export function WoTypeSegment({ value, onChange, disabled = false }: WoTypeSegmentProps) { return ( {OPTIONS.map((option) => ( - onChange(option)}> + onChange(option)} + > {option} ))} diff --git a/src/app/(protected)/workorders/_components/list/filter-bar/work-orders-filter-bar.tsx b/src/app/(protected)/workorders/_components/list/filter-bar/work-orders-filter-bar.tsx index d12819f5..3a1c114f 100644 --- a/src/app/(protected)/workorders/_components/list/filter-bar/work-orders-filter-bar.tsx +++ b/src/app/(protected)/workorders/_components/list/filter-bar/work-orders-filter-bar.tsx @@ -16,6 +16,7 @@ type WorkOrdersFilterBarProps = { currentUserId?: string | number; filteredCount: number; scopeTotalCount: number; + disabled?: boolean; }; export function WorkOrdersFilterBar({ @@ -30,20 +31,25 @@ export function WorkOrdersFilterBar({ currentUserId, filteredCount, scopeTotalCount, + disabled = false, }: WorkOrdersFilterBarProps) { const resultLabel = `${filteredCount} of ${scopeTotalCount}`; return ( -
+
- - + +
{resultLabel}
diff --git a/src/app/(protected)/workorders/index.tsx b/src/app/(protected)/workorders/index.tsx index 9036de10..066cd167 100644 --- a/src/app/(protected)/workorders/index.tsx +++ b/src/app/(protected)/workorders/index.tsx @@ -49,6 +49,7 @@ export default function WorkOrdersListPage() { currentUserId={user?.id} filteredCount={tableData.totalCount} scopeTotalCount={tableData.scopeTotalCount} + disabled={filters.advApplied != null} /> {filters.advApplied != null && ( diff --git a/src/domain/work-orders/utils/board-query-params.ts b/src/domain/work-orders/utils/board-query-params.ts index ba8d6701..36592b05 100644 --- a/src/domain/work-orders/utils/board-query-params.ts +++ b/src/domain/work-orders/utils/board-query-params.ts @@ -1,5 +1,8 @@ import { ASSIGNEE_FILTER_UNASSIGNED } from "@/domain/work-orders/types/work-order-list"; -import type { WorkOrderAdvancedFilters } from "@/domain/work-orders/types/work-order-advanced-filters"; +import { + EMPTY_ADVANCED_FILTERS, + type WorkOrderAdvancedFilters, +} from "@/domain/work-orders/types/work-order-advanced-filters"; import type { WorkOrderBarFilters } from "@/domain/work-orders/types/work-order-list-filters"; import { BOARD_DISPATCHER_UNASSIGNED, @@ -16,6 +19,10 @@ import type { WizardWOStatus, WOType } from "@/domain/work-orders/types/work-ord export const BOARD_SEARCH_MAX_PAGE_SIZE = 100; +/** Wide Custom window so applied advanced search is not pinned to ThisWeek. */ +export const ADVANCED_SEARCH_ALL_WEEKS_FROM = "2000-01-01"; +export const ADVANCED_SEARCH_ALL_WEEKS_TO = "2099-12-31"; + function toBoardDispatcherId(id: string): string { if (id === ASSIGNEE_FILTER_UNASSIGNED || id === BOARD_DISPATCHER_UNASSIGNED) { return BOARD_DISPATCHER_UNASSIGNED; @@ -136,6 +143,31 @@ export function toBoardQueryParams( }; } +function resolveAdvancedSearchDate(filters: WorkOrderAdvancedFilters): { + datePreset: WorkOrderBoardSearchQueryParams["datePreset"]; + dateFrom?: string; + dateTo?: string; +} { + if (filters.dateRange === EMPTY_ADVANCED_FILTERS.dateRange) { + return { + datePreset: "Custom", + dateFrom: ADVANCED_SEARCH_ALL_WEEKS_FROM, + dateTo: ADVANCED_SEARCH_ALL_WEEKS_TO, + }; + } + + const datePreset = DATE_RANGE_TO_PRESET[filters.dateRange]; + if (datePreset === "Custom") { + return { + datePreset, + dateFrom: filters.customFrom || undefined, + dateTo: filters.customTo || undefined, + }; + } + + return { datePreset }; +} + /** Build GET /board/search query from advanced filters (+ optional bar/search). */ export function toBoardSearchQueryParams(input: { filters: WorkOrderAdvancedFilters; @@ -148,7 +180,7 @@ export function toBoardSearchQueryParams(input: { vendorIdsByCompanyName?: Map; }): WorkOrderBoardSearchQueryParams { const { filters } = input; - const datePreset = DATE_RANGE_TO_PRESET[filters.dateRange]; + const date = resolveAdvancedSearchDate(filters); const pageSize = Math.min( input.pageSize ?? BOARD_SEARCH_MAX_PAGE_SIZE, BOARD_SEARCH_MAX_PAGE_SIZE, @@ -158,11 +190,13 @@ export function toBoardSearchQueryParams(input: { const trimmedSearch = input.search?.trim(); const search = trimmedSearch && trimmedSearch.length >= 2 ? trimmedSearch : undefined; - const params: WorkOrderBoardSearchQueryParams = { + return { page: input.page ?? 0, pageSize, search, - datePreset, + datePreset: date.datePreset, + dateFrom: date.dateFrom, + dateTo: date.dateTo, sites: filters.sites.size > 0 ? [...filters.sites] : undefined, types: typeFilters.types, overdue: typeFilters.overdue, @@ -175,13 +209,6 @@ export function toBoardSearchQueryParams(input: { sortBy: input.sortBy || "scheduledDate", sortDir: input.sortDir === "desc" ? "desc" : "asc", }; - - if (datePreset === "Custom") { - params.dateFrom = filters.customFrom || undefined; - params.dateTo = filters.customTo || undefined; - } - - return params; } /** ky/URLSearchParams-friendly object; arrays become repeated keys. */ diff --git a/src/test/app/(protected)/workorders/work-orders-advanced-search-wiring.test.tsx b/src/test/app/(protected)/workorders/work-orders-advanced-search-wiring.test.tsx index 197dd309..19bca5be 100644 --- a/src/test/app/(protected)/workorders/work-orders-advanced-search-wiring.test.tsx +++ b/src/test/app/(protected)/workorders/work-orders-advanced-search-wiring.test.tsx @@ -106,10 +106,6 @@ vi.mock("@/app/(protected)/workorders/_components/list/table/wo-table", () => ({ WoTable: () => null, })); -vi.mock("@/app/(protected)/workorders/_components/list/filter-bar/work-orders-filter-bar", () => ({ - WorkOrdersFilterBar: () => null, -})); - vi.mock("@/app/(protected)/workorders/_components/media/media-upload-modal", () => ({ MediaUploadModal: () => null, })); @@ -196,6 +192,9 @@ describe("WorkOrders Advanced Search page wiring (SH-121)", () => { expect(params.severities).toEqual([1]); expect(params.hasUplift).toBe(true); expect(params.upliftStatuses).toEqual(["pending"]); + expect(params.datePreset).toBe("Custom"); + expect(params.dateFrom).toBe("2000-01-01"); + expect(params.dateTo).toBe("2099-12-31"); }); it("applies Indicators, Flag, and Internal WO# through the public Advanced Filters sheet", () => { @@ -239,5 +238,22 @@ describe("WorkOrders Advanced Search page wiring (SH-121)", () => { expect(params.addOn).toBe(true); expect(params.flagColors).toEqual(["#EF4444"]); expect(params.internalOnly).toBe(true); + expect(params.datePreset).toBe("Custom"); + expect(params.dateFrom).toBe("2000-01-01"); + expect(params.dateTo).toBe("2099-12-31"); + }); + + it("disables dispatcher, week, and type bar while advanced filters are applied", () => { + advApplied = { ...EMPTY_ADVANCED_FILTERS, rescheduled: true }; + renderWithProviders(, { + route: "/workorders", + withAuth: false, + }); + + expect(screen.getByRole("button", { name: "All dispatchers" })).toBeDisabled(); + expect(screen.getByRole("button", { name: "Previous weeks" })).toBeDisabled(); + expect(screen.getByRole("button", { name: "Next weeks" })).toBeDisabled(); + expect(screen.getByRole("button", { name: "All" })).toBeDisabled(); + expect(screen.getByRole("button", { name: "PM" })).toBeDisabled(); }); }); diff --git a/src/test/domain/work-orders/utils/board-query-params.test.ts b/src/test/domain/work-orders/utils/board-query-params.test.ts index 31461891..53f79b3b 100644 --- a/src/test/domain/work-orders/utils/board-query-params.test.ts +++ b/src/test/domain/work-orders/utils/board-query-params.test.ts @@ -65,7 +65,9 @@ describe("toBoardSearchQueryParams", () => { }); expect(params.page).toBe(0); expect(params.pageSize).toBe(BOARD_SEARCH_MAX_PAGE_SIZE); - expect(params.datePreset).toBe("ThisWeek"); + expect(params.datePreset).toBe("Custom"); + expect(params.dateFrom).toBe("2000-01-01"); + expect(params.dateTo).toBe("2099-12-31"); }); it("maps date presets to PascalCase API values", () => { @@ -96,8 +98,9 @@ describe("toBoardSearchQueryParams", () => { customTo: "2026-06-30", }, }); - expect(thisWeek.dateFrom).toBeUndefined(); - expect(thisWeek.dateTo).toBeUndefined(); + expect(thisWeek.datePreset).toBe("Custom"); + expect(thisWeek.dateFrom).toBe("2000-01-01"); + expect(thisWeek.dateTo).toBe("2099-12-31"); }); it("maps Overdue-only advanced filter to overdue=true without types", () => {