diff --git a/apps/web/__tests__/unit/pagination-items.test.ts b/apps/web/__tests__/unit/pagination-items.test.ts new file mode 100644 index 0000000000..43f2e19e16 --- /dev/null +++ b/apps/web/__tests__/unit/pagination-items.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from "vitest"; +import { getPaginationItems } from "@/app/(org)/dashboard/caps/components/pagination-items"; + +describe("getPaginationItems", () => { + it("always links the first and last pages", () => { + expect(getPaginationItems(1, 4)).toEqual([1, 2, 3, 4]); + expect(getPaginationItems(1, 10)).toEqual([1, 2, "ellipsis", 10]); + expect(getPaginationItems(10, 10)).toEqual([1, "ellipsis", 9, 10]); + }); + + it("shows neighbours of the current page with gaps marked once", () => { + expect(getPaginationItems(3, 4)).toEqual([1, 2, 3, 4]); + expect(getPaginationItems(5, 10)).toEqual([ + 1, + "ellipsis", + 4, + 5, + 6, + "ellipsis", + 10, + ]); + }); + + it("fills a one-page gap with the page instead of an ellipsis", () => { + expect(getPaginationItems(4, 6)).toEqual([1, 2, 3, 4, 5, 6]); + }); + + it("handles small and out-of-range page counts", () => { + expect(getPaginationItems(1, 1)).toEqual([1]); + expect(getPaginationItems(1, 2)).toEqual([1, 2]); + expect(getPaginationItems(99, 4)).toEqual([1, "ellipsis", 4]); + expect(getPaginationItems(1, 0)).toEqual([]); + }); +}); diff --git a/apps/web/app/(org)/dashboard/caps/components/CapPagination.tsx b/apps/web/app/(org)/dashboard/caps/components/CapPagination.tsx index 10ccca102c..474f33a752 100644 --- a/apps/web/app/(org)/dashboard/caps/components/CapPagination.tsx +++ b/apps/web/app/(org)/dashboard/caps/components/CapPagination.tsx @@ -7,6 +7,7 @@ import { PaginationNext, PaginationPrevious, } from "@cap/ui"; +import { getPaginationItems } from "./pagination-items"; interface CapPaginationProps { currentPage: number; @@ -21,7 +22,7 @@ export const CapPagination: React.FC = ({ }) => { return ( - + {currentPage > 1 && ( = ({ /> )} - - - 1 - - - {currentPage !== 1 && ( - - - {currentPage} - - + {getPaginationItems(currentPage, totalPages).map((item, index) => + item === "ellipsis" ? ( + + ) : ( + + + {item} + + + ), )} - {totalPages > currentPage + 1 && ( + {currentPage < totalPages && ( - - {currentPage + 1} - + /> )} - {currentPage > 2 && } - - - ); diff --git a/apps/web/app/(org)/dashboard/caps/components/pagination-items.ts b/apps/web/app/(org)/dashboard/caps/components/pagination-items.ts new file mode 100644 index 0000000000..97a016c5af --- /dev/null +++ b/apps/web/app/(org)/dashboard/caps/components/pagination-items.ts @@ -0,0 +1,22 @@ +export type PaginationItemValue = number | "ellipsis"; + +export function getPaginationItems( + currentPage: number, + totalPages: number, +): PaginationItemValue[] { + const pages = [ + ...new Set([1, currentPage - 1, currentPage, currentPage + 1, totalPages]), + ] + .filter((page) => page >= 1 && page <= totalPages) + .sort((a, b) => a - b); + + const items: PaginationItemValue[] = []; + let previous = 0; + for (const page of pages) { + if (page - previous === 2) items.push(previous + 1); + else if (page - previous > 2) items.push("ellipsis"); + items.push(page); + previous = page; + } + return items; +}