mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-02 08:33:14 +00:00
fix(work-orders): sync search page when the result set shrinks
This commit is contained in:
parent
cb6d9ea00e
commit
24c09ff103
5 changed files with 117 additions and 0 deletions
|
|
@ -0,0 +1,24 @@
|
|||
import { useEffect } from "react";
|
||||
import { nextSearchPageIfOutOfRange } from "@/domain/work-orders/utils/day-pagination";
|
||||
|
||||
type UseSyncOutOfRangeSearchPageInput = {
|
||||
enabled: boolean;
|
||||
ready: boolean;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
totalCount: number;
|
||||
setPage: (page: number) => void;
|
||||
};
|
||||
|
||||
/** Keeps the requested search page inside the last valid page after the result set shrinks. */
|
||||
export function useSyncOutOfRangeSearchPage(input: UseSyncOutOfRangeSearchPageInput) {
|
||||
const { enabled, ready, page, pageSize, totalCount, setPage } = input;
|
||||
const nextPage = nextSearchPageIfOutOfRange({ page, pageSize, totalCount });
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled || !ready || nextPage == null) {
|
||||
return;
|
||||
}
|
||||
setPage(nextPage);
|
||||
}, [enabled, ready, nextPage, setPage]);
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import { useMemo } from "react";
|
||||
import type { useWorkOrdersListFilters } from "@/app/(protected)/workorders/_hooks/use-work-orders-list-filters";
|
||||
import { useSyncOutOfRangeSearchPage } from "@/app/(protected)/workorders/_hooks/use-sync-out-of-range-search-page";
|
||||
import { useWorkOrdersBoardSource } from "@/app/(protected)/workorders/_hooks/use-work-orders-board-source";
|
||||
import { useWorkOrdersDayPagination } from "@/app/(protected)/workorders/_hooks/use-work-orders-day-pagination";
|
||||
import { useWorkOrdersTableLocalState } from "@/app/(protected)/workorders/_hooks/use-work-orders-table-local-state";
|
||||
|
|
@ -104,6 +105,14 @@ export function useWorkOrdersTableData(filters: FiltersReturn) {
|
|||
);
|
||||
|
||||
const scopeTotalCount = advActive ? (data?.totalCount ?? advResults.length) : dayTotalCount;
|
||||
useSyncOutOfRangeSearchPage({
|
||||
enabled: advActive,
|
||||
ready: !isLoading,
|
||||
page: filters.page,
|
||||
pageSize: filters.pageSize,
|
||||
totalCount: scopeTotalCount,
|
||||
setPage: filters.setPage,
|
||||
});
|
||||
const listTotalCount = advActive ? advResults.length : dayVisibleCount;
|
||||
const pager = pickBoardPager({
|
||||
advActive,
|
||||
|
|
|
|||
|
|
@ -91,6 +91,21 @@ export function resolveSearchPager(input: { page: number; pageSize: number; tota
|
|||
};
|
||||
}
|
||||
|
||||
export function nextSearchPageIfOutOfRange(input: {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
totalCount: number;
|
||||
}): number | null {
|
||||
if (input.totalCount <= 0) {
|
||||
return null;
|
||||
}
|
||||
const pageCount = totalPageCount(input.totalCount, asDayPageSize(input.pageSize));
|
||||
if (input.page < pageCount) {
|
||||
return null;
|
||||
}
|
||||
return pageCount - 1;
|
||||
}
|
||||
|
||||
export function pickBoardPager(input: {
|
||||
advActive: boolean;
|
||||
isLoading: boolean;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
import { renderHook } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { useSyncOutOfRangeSearchPage } from "@/app/(protected)/workorders/_hooks/use-sync-out-of-range-search-page";
|
||||
|
||||
describe("useSyncOutOfRangeSearchPage", () => {
|
||||
it("sets the last valid page when a search shrinks to one page", () => {
|
||||
const setPage = vi.fn();
|
||||
renderHook(() =>
|
||||
useSyncOutOfRangeSearchPage({
|
||||
enabled: true,
|
||||
ready: true,
|
||||
page: 1,
|
||||
pageSize: 50,
|
||||
totalCount: 40,
|
||||
setPage,
|
||||
}),
|
||||
);
|
||||
expect(setPage).toHaveBeenCalledWith(0);
|
||||
});
|
||||
|
||||
it("does not sync while the search query is loading", () => {
|
||||
const setPage = vi.fn();
|
||||
renderHook(() =>
|
||||
useSyncOutOfRangeSearchPage({
|
||||
enabled: true,
|
||||
ready: false,
|
||||
page: 1,
|
||||
pageSize: 50,
|
||||
totalCount: 40,
|
||||
setPage,
|
||||
}),
|
||||
);
|
||||
expect(setPage).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not sync when the result set is empty", () => {
|
||||
const setPage = vi.fn();
|
||||
renderHook(() =>
|
||||
useSyncOutOfRangeSearchPage({
|
||||
enabled: true,
|
||||
ready: true,
|
||||
page: 1,
|
||||
pageSize: 50,
|
||||
totalCount: 0,
|
||||
setPage,
|
||||
}),
|
||||
);
|
||||
expect(setPage).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
@ -5,6 +5,7 @@ import {
|
|||
asDayPageSize,
|
||||
findItemPageIndex,
|
||||
formatDayPaginationSummary,
|
||||
nextSearchPageIfOutOfRange,
|
||||
pageIndexForItem,
|
||||
paginateItems,
|
||||
pickBoardPager,
|
||||
|
|
@ -137,3 +138,21 @@ describe("day pagination utils (SH-241)", () => {
|
|||
expect(formatDayPaginationSummary("Tue · 06/02", 1)).toBe("TUE · 06/02 · 1 item");
|
||||
});
|
||||
});
|
||||
|
||||
describe("nextSearchPageIfOutOfRange", () => {
|
||||
it("returns the last valid page when a search shrinks to one page", () => {
|
||||
expect(nextSearchPageIfOutOfRange({ page: 1, pageSize: 50, totalCount: 40 })).toBe(0);
|
||||
});
|
||||
|
||||
it("leaves a still-valid second page alone", () => {
|
||||
expect(nextSearchPageIfOutOfRange({ page: 1, pageSize: 50, totalCount: 60 })).toBeNull();
|
||||
});
|
||||
|
||||
it("does not pull an empty result set onto page 1", () => {
|
||||
expect(nextSearchPageIfOutOfRange({ page: 1, pageSize: 50, totalCount: 0 })).toBeNull();
|
||||
});
|
||||
|
||||
it("leaves the last valid page alone", () => {
|
||||
expect(nextSearchPageIfOutOfRange({ page: 0, pageSize: 50, totalCount: 40 })).toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue