diff --git a/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-filters.png b/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-filters.png index f9ab4659..7af374c4 100644 Binary files a/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-filters.png and b/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-filters.png differ diff --git a/src/app/(protected)/workorders/_components/list/advanced-search/adv-filter-sections.tsx b/src/app/(protected)/workorders/_components/list/advanced-search/adv-filter-sections.tsx index 812b98b6..b21120b7 100644 --- a/src/app/(protected)/workorders/_components/list/advanced-search/adv-filter-sections.tsx +++ b/src/app/(protected)/workorders/_components/list/advanced-search/adv-filter-sections.tsx @@ -13,11 +13,7 @@ import { import { AdvDocPill, AdvStatusPill } from "./adv-filter-pill"; import { AdvDateRangeSection } from "./adv-date-range-section"; import { AdvMultiSelect } from "./adv-multi-select"; -import { - AdvAvetaSection, - AdvFlagInternalUpliftSections, - AdvIndicatorsSection, -} from "./adv-parity-filter-sections"; +import { AdvFlagInternalUpliftSections, AdvIndicatorsSection } from "./adv-parity-filter-sections"; import { AdvSection } from "./adv-section"; import { AdvSeverityList } from "./adv-severity-list"; import { AdvTypeGrid } from "./adv-type-grid"; @@ -131,8 +127,6 @@ export function AdvancedSearchFilterSections({ - - ({ value: pmType, label: pmType }))} diff --git a/src/app/(protected)/workorders/_components/list/advanced-search/adv-parity-filter-sections.tsx b/src/app/(protected)/workorders/_components/list/advanced-search/adv-parity-filter-sections.tsx index c5189fab..8c6b1446 100644 --- a/src/app/(protected)/workorders/_components/list/advanced-search/adv-parity-filter-sections.tsx +++ b/src/app/(protected)/workorders/_components/list/advanced-search/adv-parity-filter-sections.tsx @@ -125,6 +125,12 @@ export function AdvIndicatorsSection({ draft, setDraft }: DraftProps) { ); } +const FILTER_TOGGLE_CHECKBOX_SX = { + padding: 0, + color: "var(--color-border-hover)", + "&.Mui-checked": { color: "var(--color-primary)" }, +} as const; + export function AdvAvetaSection({ draft, setDraft }: DraftProps) { return ( @@ -133,11 +139,7 @@ export function AdvAvetaSection({ draft, setDraft }: DraftProps) { size="small" checked={draft.avetaOnly} onChange={() => setDraft((prev) => ({ ...prev, avetaOnly: !prev.avetaOnly }))} - sx={{ - padding: 0, - color: "var(--color-border-hover)", - "&.Mui-checked": { color: "var(--warning)" }, - }} + sx={FILTER_TOGGLE_CHECKBOX_SX} /> Aveta required only @@ -195,6 +197,8 @@ export function AdvFlagInternalUpliftSections({ + + diff --git a/src/app/(protected)/workorders/_components/list/advanced-search/use-advanced-search-draft.ts b/src/app/(protected)/workorders/_components/list/advanced-search/use-advanced-search-draft.ts index 4f367f18..e1061f0e 100644 --- a/src/app/(protected)/workorders/_components/list/advanced-search/use-advanced-search-draft.ts +++ b/src/app/(protected)/workorders/_components/list/advanced-search/use-advanced-search-draft.ts @@ -55,11 +55,17 @@ export function useAdvancedSearchDraft({ }; const handleClearAll = () => { + if (activeCount === 0) { + return; + } setDraft(cloneAdvancedFilters(EMPTY_ADVANCED_FILTERS)); onClearAll(); }; const handleApply = () => { + if (activeCount === 0) { + return; + } onApply(cloneAdvancedFilters(draft)); onOpenChange(false); }; diff --git a/src/app/(protected)/workorders/_components/list/advanced-search/work-orders-advanced-search-sheet.tsx b/src/app/(protected)/workorders/_components/list/advanced-search/work-orders-advanced-search-sheet.tsx index c42e1276..b18415f5 100644 --- a/src/app/(protected)/workorders/_components/list/advanced-search/work-orders-advanced-search-sheet.tsx +++ b/src/app/(protected)/workorders/_components/list/advanced-search/work-orders-advanced-search-sheet.tsx @@ -64,7 +64,12 @@ export function WorkOrdersAdvancedSearchSheet({ />
-
@@ -74,7 +79,7 @@ export function WorkOrdersAdvancedSearchSheet({ )}
-
diff --git a/src/components/ui/collapsible-section.tsx b/src/components/ui/collapsible-section.tsx index 8a870576..1d18f8f6 100644 --- a/src/components/ui/collapsible-section.tsx +++ b/src/components/ui/collapsible-section.tsx @@ -33,7 +33,9 @@ export function CollapsibleSection({ >
{Icon != null && } - {label} + + {label} +
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 5b1e50c8..2bcf1cf6 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 @@ -152,6 +152,8 @@ describe("WorkOrders Advanced Search page wiring (SH-121)", () => { expect(within(sheet).getByText("HVAC")).toBeInTheDocument(); expect(within(sheet).getByText("Vendor Co")).toBeInTheDocument(); + fireEvent.click(within(sheet).getByRole("button", { name: /Aveta Compliance/i })); + fireEvent.click(within(sheet).getByRole("checkbox", { name: /Aveta required only/i })); fireEvent.click(within(sheet).getByRole("button", { name: /apply filters/i })); expect(applyAdvancedFilters).toHaveBeenCalled(); expect(setAdvOpen).toHaveBeenCalledWith(false); @@ -341,6 +343,45 @@ describe("WorkOrders Advanced Search page wiring (SH-121)", () => { expect(screen.getByRole("button", { name: /work order types, all types/i })).toBeEnabled(); }); + it("places Aveta Compliance immediately above Uplift", () => { + const view = renderWithProviders(, { + route: "/workorders", + withAuth: false, + }); + + fireEvent.click(screen.getByRole("button", { name: "Advanced Filters" })); + advOpen = true; + view.rerender(); + + const sheet = screen.getByRole("dialog", { name: "Advanced Filters" }); + const aveta = within(sheet).getByRole("button", { name: /Aveta Compliance/i }); + const uplift = within(sheet).getByRole("button", { name: /^Uplift/i }); + expect(aveta.closest(".border-b")?.nextElementSibling).toBe(uplift.closest(".border-b")); + }); + + it("keeps Clear all and Apply filters disabled until a filter is selected", () => { + const view = renderWithProviders(, { + route: "/workorders", + withAuth: false, + }); + + fireEvent.click(screen.getByRole("button", { name: "Advanced Filters" })); + advOpen = true; + view.rerender(); + + const sheet = screen.getByRole("dialog", { name: "Advanced Filters" }); + const clearAll = within(sheet).getByRole("button", { name: /clear all/i }); + const apply = within(sheet).getByRole("button", { name: /apply filters/i }); + expect(clearAll).toBeDisabled(); + expect(apply).toBeDisabled(); + + fireEvent.click(within(sheet).getByRole("button", { name: /Aveta Compliance/i })); + fireEvent.click(within(sheet).getByRole("checkbox", { name: /Aveta required only/i })); + + expect(clearAll).toBeEnabled(); + expect(apply).toBeEnabled(); + }); + it("applies Aveta required only through Advanced Filters", () => { const view = renderWithProviders(, { route: "/workorders", diff --git a/src/test/app/(protected)/workorders/work-orders-board-core-affordances.test.tsx b/src/test/app/(protected)/workorders/work-orders-board-core-affordances.test.tsx index c466867d..c3fb07b9 100644 --- a/src/test/app/(protected)/workorders/work-orders-board-core-affordances.test.tsx +++ b/src/test/app/(protected)/workorders/work-orders-board-core-affordances.test.tsx @@ -211,7 +211,9 @@ describe("WorkOrdersListHeader slice affordances", () => { ); expect(screen.getByRole("button", { name: /new wo/i })).toBeInTheDocument(); - expect(screen.getByLabelText("Advanced Filters")).toBeInTheDocument(); + const filters = screen.getByRole("button", { name: "Advanced Filters" }); + expect(filters).toBeInTheDocument(); + expect(filters).toHaveTextContent("Filters"); }); }); diff --git a/src/test/components/ui/collapsible-section.test.tsx b/src/test/components/ui/collapsible-section.test.tsx new file mode 100644 index 00000000..0d1d1994 --- /dev/null +++ b/src/test/components/ui/collapsible-section.test.tsx @@ -0,0 +1,16 @@ +import { screen } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; + +import { CollapsibleSection } from "@/components/ui/collapsible-section"; +import { renderWithProviders } from "@/test/test-utils"; + +describe("CollapsibleSection", () => { + it("renders the section title at the 14px body size token", () => { + renderWithProviders(child, { + withAuth: false, + }); + + const title = screen.getByRole("button", { name: /Aveta Compliance/i }).querySelector("span"); + expect(title).toHaveClass("text-[length:var(--text-base)]"); + }); +});