import { describe, expect, it } from "vitest"; import { ASSIGNEE_FILTER_UNASSIGNED } from "@/domain/work-orders/types/work-order-list"; import { EMPTY_ADVANCED_FILTERS } from "@/domain/work-orders/types/work-order-advanced-filters"; import { DOC_STATUS_API, LIFECYCLE_STATUS_API, WORK_ORDER_TYPE_API, } from "@/domain/work-orders/types/work-order-board"; import type { WOType } from "@/domain/work-orders/types/work-order-wizard"; import { queryKeys } from "@/infra/query-key/query-key"; import { BOARD_SEARCH_MAX_PAGE_SIZE, buildBoardSearchParamsRecord, toBoardQueryParams, toBoardSearchQueryParams, toUnassignedQueueQueryParams, UNASSIGNED_QUEUE_DATE_FROM, UNASSIGNED_QUEUE_DATE_TO, } from "@/domain/work-orders/utils/board-query-params"; describe("toBoardQueryParams", () => { const baseBar = { weekMonday: "2026-07-13", dispatcherIds: new Set(), types: new Set(), }; it("omits search when shorter than 2 characters", () => { const params = toBoardQueryParams(baseBar, "B"); expect(params.search).toBeUndefined(); expect(params.weekStart).toBe("2026-07-13"); expect(params.weekEnd).toBe("2026-07-17"); }); it("includes trimmed search when length >= 2", () => { const params = toBoardQueryParams(baseBar, " BK5 "); expect(params.search).toBe("BK5"); }); it("maps unassigned dispatcher to __unassigned__", () => { const params = toBoardQueryParams( { ...baseBar, dispatcherIds: new Set([ASSIGNEE_FILTER_UNASSIGNED, "user-guid"]), }, "", ); expect(params.dispatchers).toEqual(["__unassigned__", "user-guid"]); }); it("maps PM type filter to API types", () => { const params = toBoardQueryParams({ ...baseBar, types: new Set(["PM"]) }, ""); expect(params.types).toEqual([WORK_ORDER_TYPE_API.PM]); expect(params.overdue).toBeUndefined(); }); it("maps multiple types as union query params", () => { const params = toBoardQueryParams({ ...baseBar, types: new Set(["PM", "Emergency"]) }, ""); expect(params.types).toEqual([WORK_ORDER_TYPE_API.PM, WORK_ORDER_TYPE_API.Emergency]); expect(params.overdue).toBeUndefined(); }); it("maps Overdue bar filter to overdue=true without types", () => { const params = toBoardQueryParams({ ...baseBar, types: new Set(["Overdue"]) }, ""); expect(params.overdue).toBe(true); expect(params.types).toBeUndefined(); }); it("maps Overdue with real types as OR (types + overdue)", () => { const params = toBoardQueryParams({ ...baseBar, types: new Set(["PM", "Overdue"]) }, ""); expect(params.types).toEqual([WORK_ORDER_TYPE_API.PM]); expect(params.overdue).toBe(true); }); it("omits types when the set is empty", () => { const params = toBoardQueryParams(baseBar, ""); expect(params.types).toBeUndefined(); expect(params.overdue).toBeUndefined(); }); }); describe("toBoardSearchQueryParams", () => { it("defaults page to 0 (0-based pagination)", () => { const params = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS }, }); expect(params.page).toBe(0); expect(params.pageSize).toBe(BOARD_SEARCH_MAX_PAGE_SIZE); expect(params.datePreset).toBe("Custom"); expect(params.dateFrom).toBe("2000-01-01"); expect(params.dateTo).toBe("2099-12-31"); expect(params.unscheduledOnly).toBeUndefined(); }); it("forwards chrome pageSize 50 instead of the search max", () => { const params = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS }, page: 1, pageSize: 50, }); expect(params.page).toBe(1); expect(params.pageSize).toBe(50); }); it("maps date presets to PascalCase API values", () => { const params = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, dateRange: "last-3-months" }, }); expect(params.datePreset).toBe("Last3Months"); }); it("includes dateFrom/dateTo only for Custom preset", () => { const custom = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, dateRange: "custom", customFrom: "2026-06-01", customTo: "2026-06-30", }, }); expect(custom.datePreset).toBe("Custom"); expect(custom.dateFrom).toBe("2026-06-01"); expect(custom.dateTo).toBe("2026-06-30"); const thisWeek = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, dateRange: "this-week", customFrom: "2026-06-01", customTo: "2026-06-30", }, }); expect(thisWeek.datePreset).toBe("Custom"); expect(thisWeek.dateFrom).toBe("2000-01-01"); expect(thisWeek.dateTo).toBe("2099-12-31"); }); it("sends unscheduledOnly=true with no faked Custom date window (SH-338)", () => { const withDefaultRange = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, unscheduledOnly: true }, }); expect(withDefaultRange.unscheduledOnly).toBe(true); expect(withDefaultRange.datePreset).toBeUndefined(); expect(withDefaultRange.dateFrom).toBeUndefined(); expect(withDefaultRange.dateTo).toBeUndefined(); const overridingLastWeek = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, dateRange: "last-week", unscheduledOnly: true, }, }); expect(overridingLastWeek.unscheduledOnly).toBe(true); expect(overridingLastWeek.datePreset).toBeUndefined(); expect(overridingLastWeek.dateFrom).toBeUndefined(); expect(overridingLastWeek.dateTo).toBeUndefined(); const record = buildBoardSearchParamsRecord(overridingLastWeek); expect(record.unscheduledOnly).toBe(true); expect(record).not.toHaveProperty("datePreset"); expect(record).not.toHaveProperty("dateFrom"); expect(record).not.toHaveProperty("dateTo"); }); it("keeps applying every other facet alongside unscheduledOnly", () => { const params = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, unscheduledOnly: true, statuses: new Set(["Scheduled"]), severities: new Set([4]), pmTypes: new Set(["HVAC"]), }, }); expect(params.unscheduledOnly).toBe(true); expect(params.statuses).toEqual([LIFECYCLE_STATUS_API.Scheduled]); expect(params.severities).toEqual([4]); expect(params.pmTypes).toEqual(["HVAC"]); }); it("omits unscheduledOnly and keeps the wide Custom default when the toggle is off", () => { const params = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, dateRange: "last-week" }, }); expect(params.unscheduledOnly).toBeUndefined(); expect(params.datePreset).toBe("LastWeek"); expect(buildBoardSearchParamsRecord(params)).not.toHaveProperty("unscheduledOnly"); }); it("maps Overdue-only advanced filter to overdue=true without types", () => { const params = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, types: new Set(["Overdue"]), }, }); expect(params.overdue).toBe(true); expect(params.types).toBeUndefined(); }); it("maps Overdue with real types as OR (types + overdue)", () => { const params = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, types: new Set(["PM", "Overdue"]), }, }); expect(params.types).toEqual([WORK_ORDER_TYPE_API.PM]); expect(params.overdue).toBe(true); }); it("resolves vendor company names to vendorIds", () => { const params = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, vendorTechs: new Set(["Acme HVAC", "Unknown"]), }, vendorIdsByCompanyName: new Map([["Acme HVAC", 12]]), }); expect(params.vendorIds).toEqual([12]); }); it("omits empty filter arrays", () => { const params = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS }, search: "x", }); expect(params.search).toBeUndefined(); expect(params.sites).toBeUndefined(); expect(params.types).toBeUndefined(); expect(params.overdue).toBeUndefined(); expect(params.dispatchers).toBeUndefined(); expect(params.statuses).toBeUndefined(); expect(params.pmTypes).toBeUndefined(); expect(params.vendorIds).toBeUndefined(); expect(params.docStatuses).toBeUndefined(); expect(params.severities).toBeUndefined(); expect(params.rescheduled).toBeUndefined(); expect(params.carriedOver).toBeUndefined(); expect(params.addOn).toBeUndefined(); expect(params.flagColors).toBeUndefined(); expect(params.internalOnly).toBeUndefined(); expect(params.hasUplift).toBeUndefined(); expect(params.upliftStatuses).toBeUndefined(); expect(params.unscheduledOnly).toBeUndefined(); }); it("maps service types and completion docs onto board search params", () => { const params = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, pmTypes: new Set(["HVAC", "Electrical"]), docs: new Set(["No", "Yes", "NN"]), }, }); expect(params.pmTypes).toEqual(["HVAC", "Electrical"]); expect(params.docStatuses).toEqual([ DOC_STATUS_API.No, DOC_STATUS_API.Yes, DOC_STATUS_API.NotNeeded, ]); }); it("maps each extra advanced-filter facet alone onto board search params", () => { const severity = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, severities: new Set([4]) }, }); expect(severity.severities).toEqual([4]); expect(severity.carriedOver).toBeUndefined(); expect(severity.addOn).toBeUndefined(); expect(severity.pmTypes).toBeUndefined(); expect(severity.docStatuses).toBeUndefined(); const carried = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, carriedOver: true }, }); expect(carried.carriedOver).toBe(true); expect(carried.severities).toBeUndefined(); const addOn = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, addOn: true }, }); expect(addOn.addOn).toBe(true); expect(addOn.carriedOver).toBeUndefined(); const aveta = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, avetaOnly: true }, }); expect(aveta.avetaOnly).toBe(true); const service = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, pmTypes: new Set(["HVAC"]) }, }); expect(service.pmTypes).toEqual(["HVAC"]); const docs = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, docs: new Set(["NN"]) }, }); expect(docs.docStatuses).toEqual([DOC_STATUS_API.NotNeeded]); }); it("combines extra advanced-filter facets with a status facet on the same request", () => { const params = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, severities: new Set([1]), carriedOver: true, addOn: true, pmTypes: new Set(["HVAC"]), docs: new Set(["Yes"]), statuses: new Set(["Scheduled"]), }, }); expect(params.severities).toEqual([1]); expect(params.carriedOver).toBe(true); expect(params.addOn).toBe(true); expect(params.pmTypes).toEqual(["HVAC"]); expect(params.docStatuses).toEqual([DOC_STATUS_API.Yes]); expect(params.statuses).toEqual([LIFECYCLE_STATUS_API.Scheduled]); const record = buildBoardSearchParamsRecord(params); expect(record.severities).toEqual([1]); expect(record.carriedOver).toBe(true); expect(record.addOn).toBe(true); expect(record.pmTypes).toEqual(["HVAC"]); expect(record.docStatuses).toEqual([DOC_STATUS_API.Yes]); expect(record.statuses).toEqual([LIFECYCLE_STATUS_API.Scheduled]); }); it("maps severity and uplift facets onto board search params", () => { const params = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, severities: new Set([1, 3, 5]), rescheduled: true, carriedOver: true, addOn: true, flagColors: new Set(["#EF4444", "#3B82F6"]), internalOnly: true, hasUplift: true, upliftStatuses: new Set(["pending", "approved"]), }, }); expect(params.severities).toEqual([1, 3, 5]); expect(params.rescheduled).toBe(true); expect(params.carriedOver).toBe(true); expect(params.addOn).toBe(true); expect(params.flagColors).toEqual(["#EF4444", "#3B82F6"]); expect(params.internalOnly).toBe(true); expect(params.hasUplift).toBe(true); expect(params.upliftStatuses).toEqual(["pending", "approved"]); }); it("omits upliftStatuses when hasUplift is false", () => { const params = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, hasUplift: false, upliftStatuses: new Set(["pending"]), }, }); expect(params.hasUplift).toBeUndefined(); expect(params.upliftStatuses).toBeUndefined(); }); it("caps pageSize at BOARD_SEARCH_MAX_PAGE_SIZE", () => { const params = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS }, pageSize: 500, }); expect(params.pageSize).toBe(200); }); }); describe("buildBoardSearchParamsRecord", () => { it("keeps arrays as repeated-key friendly values (not types[])", () => { const record = buildBoardSearchParamsRecord({ weekStart: "2026-07-13", types: [2, 6], overdue: true, dispatchers: ["guid-a", "__unassigned__"], search: "", }); expect(record.types).toEqual([2, 6]); expect(record.overdue).toBe(true); expect(record.dispatchers).toEqual(["guid-a", "__unassigned__"]); expect(record.search).toBeUndefined(); expect(Object.keys(record)).not.toContain("types[]"); }); }); describe("toUnassignedQueueQueryParams", () => { it("requests unassigned dispatchers with a Custom range, not the weekly board window", () => { const params = toUnassignedQueueQueryParams(2); const record = buildBoardSearchParamsRecord(params); expect(params.page).toBe(2); expect(params.pageSize).toBe(BOARD_SEARCH_MAX_PAGE_SIZE); expect(params.dispatchers).toEqual(["__unassigned__"]); expect(params.statuses).not.toContain(7); expect(params.statuses).not.toContain(9); expect(params.datePreset).toBe("Custom"); expect(params.dateFrom).toBe(UNASSIGNED_QUEUE_DATE_FROM); expect(params.dateTo).toBe(UNASSIGNED_QUEUE_DATE_TO); expect(record).not.toHaveProperty("weekStart"); expect(record).not.toHaveProperty("weekEnd"); }); }); describe("boardSearch query-key isolation", () => { it("separates unscheduledOnly=true from the same facets without it", () => { const without = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, rescheduled: true }, }); const withUnscheduled = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, rescheduled: true, unscheduledOnly: true }, }); expect(queryKeys.workOrders.boardSearch(without)).not.toEqual( queryKeys.workOrders.boardSearch(withUnscheduled), ); }); it("separates pages under the same unscheduledOnly filter", () => { const base = toBoardSearchQueryParams({ filters: { ...EMPTY_ADVANCED_FILTERS, unscheduledOnly: true }, page: 0, }); const pageOne = { ...base, page: 1 }; expect(queryKeys.workOrders.boardSearch(base)).not.toEqual( queryKeys.workOrders.boardSearch(pageOne), ); }); });