"use client"; import { ChevronFirst, ChevronLast, ChevronLeft, ChevronRight, } from "lucide-react"; import Link from "next/link"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/shadcn/select/select"; import { getPaginationInfo } from "@/lib"; import { cn } from "@/lib/utils"; import { MetaDataProps } from "@/types"; interface DataTablePaginationProps { metadata?: MetaDataProps; disableScroll?: boolean; /** Prefix for URL params to avoid conflicts (e.g., "findings" -> "findingsPage") */ paramPrefix?: string; /* * Controlled mode: Use these props to manage pagination via React state * instead of URL params. Useful for tables in drawers/modals to avoid * triggering page re-renders when paginating. */ controlledPage?: number; controlledPageSize?: number; onPageChange?: (page: number) => void; onPageSizeChange?: (pageSize: number) => void; } const NAV_BUTTON_STYLES = { base: "flex items-center justify-center rounded-full p-3 transition-colors", enabled: "text-text-neutral-secondary hover:bg-bg-neutral-tertiary hover:text-text-neutral-primary", disabled: "text-text-neutral-tertiary cursor-not-allowed pointer-events-none", } as const; export function DataTablePagination({ metadata, disableScroll = false, paramPrefix = "", controlledPage, controlledPageSize, onPageChange, onPageSizeChange, }: DataTablePaginationProps) { const pathname = usePathname(); const searchParams = useSearchParams(); const router = useRouter(); // Determine if we're in controlled mode const isControlled = controlledPage !== undefined && onPageChange; // Determine param names based on prefix const pageParam = paramPrefix ? `${paramPrefix}Page` : "page"; const pageSizeParam = paramPrefix ? `${paramPrefix}PageSize` : "pageSize"; // Derived, not stored, so it survives back/forward and filter resets const selectedPageSize = isControlled ? String(controlledPageSize ?? 10) : (searchParams.get(pageSizeParam) ?? "10"); if (!metadata) return null; const { currentPage: metaCurrentPage, totalPages, totalEntries, itemsPerPageOptions, } = getPaginationInfo(metadata); // Gated separately: a large page size collapses the table to one page, and the // selector is the only way back to a smaller one. const smallestPageSize = itemsPerPageOptions.length ? Math.min(...itemsPerPageOptions) : 10; const showPageSizeSelector = totalEntries > smallestPageSize; const showPageNavigation = totalPages > 1; // No wrapper when empty: it would add a stray gap to DataTable's flex column if (!showPageSizeSelector && !showPageNavigation) return null; // For controlled mode, use controlled values; for prefixed, read from URL; otherwise use metadata const currentPage = isControlled ? controlledPage : paramPrefix ? parseInt(searchParams.get(pageParam) || "1", 10) : metaCurrentPage; const createPageUrl = (pageNumber: number | string) => { const params = new URLSearchParams(searchParams); // Preserve all important parameters const scanId = searchParams.get("scanId"); const id = searchParams.get("id"); const version = searchParams.get("version"); if (+pageNumber > totalPages) { return `${pathname}?${params.toString()}`; } params.set(pageParam, pageNumber.toString()); // Ensure that scanId, id and version are preserved if (scanId) params.set("scanId", scanId); if (id) params.set("id", id); if (version) params.set("version", version); return `${pathname}?${params.toString()}`; }; // Handle page navigation for controlled mode const handlePageChange = (pageNumber: number) => { if (isControlled) { onPageChange(pageNumber); } else { const url = createPageUrl(pageNumber); if (disableScroll) { router.push(url, { scroll: false }); } else { router.push(url); } } }; const isFirstPage = currentPage === 1; const isLastPage = currentPage === totalPages; return (