mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-02 15:33:12 +00:00
fix(work-orders): search all weeks when advanced filters are applied
This commit is contained in:
parent
7902476e2a
commit
bea2858c81
8 changed files with 92 additions and 27 deletions
|
|
@ -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({
|
|||
<MenuAnchorButton
|
||||
onAnchor={setAnchor}
|
||||
open={open}
|
||||
disabled={disabled}
|
||||
className="flex w-auto shrink-0 items-center gap-2 border whitespace-nowrap transition-colors"
|
||||
style={
|
||||
!allSelected
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ type WeekNavProps = {
|
|||
weekMonday: string;
|
||||
|
||||
onChange: (mondayIso: string) => 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 (
|
||||
<div className="flex shrink-0 items-center gap-1.5">
|
||||
<NavArrowButton
|
||||
disabled={!canGoBack}
|
||||
disabled={disabled || !canGoBack}
|
||||
onClick={() => setOffset((current) => current - 1)}
|
||||
aria-label="Previous weeks"
|
||||
>
|
||||
|
|
@ -109,7 +111,7 @@ export function WeekNav({ weekMonday, onChange }: WeekNavProps) {
|
|||
<NavPillButton
|
||||
key={week.mondayIso}
|
||||
active={isActive}
|
||||
disabled={isDisabled}
|
||||
disabled={disabled || isDisabled}
|
||||
onClick={() => onChange(week.mondayIso)}
|
||||
>
|
||||
{week.label}
|
||||
|
|
@ -120,7 +122,7 @@ export function WeekNav({ weekMonday, onChange }: WeekNavProps) {
|
|||
})}
|
||||
|
||||
<NavArrowButton
|
||||
disabled={!canGoForward}
|
||||
disabled={disabled || !canGoForward}
|
||||
onClick={() => setOffset((current) => current + 1)}
|
||||
aria-label="Next weeks"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<SegmentGroup>
|
||||
{OPTIONS.map((option) => (
|
||||
<SegmentButton key={option} active={value === option} onClick={() => onChange(option)}>
|
||||
<SegmentButton
|
||||
key={option}
|
||||
active={value === option}
|
||||
disabled={disabled}
|
||||
onClick={() => onChange(option)}
|
||||
>
|
||||
{option}
|
||||
</SegmentButton>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div className="-mx-2.5 flex h-14 shrink-0 items-center gap-3 border-b border-(--color-border) bg-(--color-bg-surface) px-4">
|
||||
<div
|
||||
className="-mx-2.5 flex h-14 shrink-0 items-center gap-3 border-b border-(--color-border) bg-(--color-bg-surface) px-4"
|
||||
aria-disabled={disabled}
|
||||
>
|
||||
<DispatcherFilter
|
||||
selectedIds={dispatcherIds}
|
||||
onChange={onDispatcherChange}
|
||||
users={users}
|
||||
usersLoading={usersLoading}
|
||||
currentUserId={currentUserId}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<WeekNav weekMonday={weekMonday} onChange={onWeekChange} />
|
||||
<WoTypeSegment value={typeFilter} onChange={onTypeChange} />
|
||||
<WeekNav weekMonday={weekMonday} onChange={onWeekChange} disabled={disabled} />
|
||||
<WoTypeSegment value={typeFilter} onChange={onTypeChange} disabled={disabled} />
|
||||
<div className="flex-1" />
|
||||
<span className="shrink-0 text-xs font-medium text-muted-foreground">{resultLabel}</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ export default function WorkOrdersListPage() {
|
|||
currentUserId={user?.id}
|
||||
filteredCount={tableData.totalCount}
|
||||
scopeTotalCount={tableData.scopeTotalCount}
|
||||
disabled={filters.advApplied != null}
|
||||
/>
|
||||
|
||||
{filters.advApplied != null && (
|
||||
|
|
|
|||
|
|
@ -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<string, number>;
|
||||
}): 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. */
|
||||
|
|
|
|||
|
|
@ -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(<WorkOrdersListPage />, {
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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", () => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue