feat(ui): Page size for datatables (#7634)

This commit is contained in:
sumit-tft
2025-05-05 19:12:06 +05:30
committed by GitHub
parent 180eb61fee
commit 44f8e4c488
17 changed files with 93 additions and 10 deletions
+2
View File
@@ -15,6 +15,7 @@ export const getInvitations = async ({
query = "",
sort = "",
filters = {},
pageSize = 10,
}) => {
const headers = await getAuthHeaders({ contentType: false });
@@ -23,6 +24,7 @@ export const getInvitations = async ({
const url = new URL(`${apiBaseUrl}/tenants/invitations`);
if (page) url.searchParams.append("page[number]", page.toString());
if (pageSize) url.searchParams.append("page[size]", pageSize.toString());
if (query) url.searchParams.append("filter[search]", query);
if (sort) url.searchParams.append("sort", sort);
@@ -16,11 +16,13 @@ export const getProviderGroups = async ({
query = "",
sort = "",
filters = {},
pageSize = 10,
}: {
page?: number;
query?: string;
sort?: string;
filters?: Record<string, string | number>;
pageSize?: number;
}): Promise<ProviderGroupsResponse | undefined> => {
const headers = await getAuthHeaders({ contentType: false });
@@ -29,6 +31,7 @@ export const getProviderGroups = async ({
const url = new URL(`${apiBaseUrl}/provider-groups`);
if (page) url.searchParams.append("page[number]", page.toString());
if (pageSize) url.searchParams.append("page[size]", pageSize.toString());
if (query) url.searchParams.append("filter[search]", query);
if (sort) url.searchParams.append("sort", sort);
+2
View File
@@ -16,6 +16,7 @@ export const getProviders = async ({
query = "",
sort = "",
filters = {},
pageSize = 10,
}) => {
const headers = await getAuthHeaders({ contentType: false });
@@ -24,6 +25,7 @@ export const getProviders = async ({
const url = new URL(`${apiBaseUrl}/providers?include=provider_groups`);
if (page) url.searchParams.append("page[number]", page.toString());
if (pageSize) url.searchParams.append("page[size]", pageSize.toString());
if (query) url.searchParams.append("filter[search]", query);
if (sort) url.searchParams.append("sort", sort);
+2
View File
@@ -15,6 +15,7 @@ export const getRoles = async ({
query = "",
sort = "",
filters = {},
pageSize = 10,
}) => {
const headers = await getAuthHeaders({ contentType: false });
@@ -23,6 +24,7 @@ export const getRoles = async ({
const url = new URL(`${apiBaseUrl}/roles`);
if (page) url.searchParams.append("page[number]", page.toString());
if (pageSize) url.searchParams.append("page[size]", pageSize.toString());
if (query) url.searchParams.append("filter[search]", query);
if (sort) url.searchParams.append("sort", sort);
+2
View File
@@ -15,6 +15,7 @@ export const getScans = async ({
query = "",
sort = "",
filters = {},
pageSize = 10,
}) => {
const headers = await getAuthHeaders({ contentType: false });
@@ -23,6 +24,7 @@ export const getScans = async ({
const url = new URL(`${apiBaseUrl}/scans`);
if (page) url.searchParams.append("page[number]", page.toString());
if (pageSize) url.searchParams.append("page[size]", pageSize.toString());
if (query) url.searchParams.append("filter[search]", query);
if (sort) url.searchParams.append("sort", sort);
+2
View File
@@ -15,6 +15,7 @@ export const getUsers = async ({
query = "",
sort = "",
filters = {},
pageSize = 10,
}) => {
const headers = await getAuthHeaders({ contentType: false });
@@ -23,6 +24,7 @@ export const getUsers = async ({
const url = new URL(`${apiBaseUrl}/users?include=roles`);
if (page) url.searchParams.append("page[number]", page.toString());
if (pageSize) url.searchParams.append("page[size]", pageSize.toString());
if (query) url.searchParams.append("filter[search]", query);
if (sort) url.searchParams.append("sort", sort);
+2 -1
View File
@@ -149,6 +149,7 @@ const SSRDataTable = async ({
searchParams: SearchParamsProps;
}) => {
const page = parseInt(searchParams.page?.toString() || "1", 10);
const pageSize = parseInt(searchParams.pageSize?.toString() || "10", 10);
const defaultSort = "severity,status,-inserted_at";
const sort = searchParams.sort?.toString() || defaultSort;
@@ -186,7 +187,7 @@ const SSRDataTable = async ({
page,
sort: encodedSort,
filters,
pageSize: 10,
pageSize,
});
// Create dictionaries for resources, scans, and providers
+8 -1
View File
@@ -44,6 +44,7 @@ const SSRDataTable = async ({
}) => {
const page = parseInt(searchParams.page?.toString() || "1", 10);
const sort = searchParams.sort?.toString();
const pageSize = parseInt(searchParams.pageSize?.toString() || "10", 10);
// Extract all filter parameters
const filters = Object.fromEntries(
@@ -54,7 +55,13 @@ const SSRDataTable = async ({
const query = (filters["filter[search]"] as string) || "";
// Fetch invitations and roles
const invitationsData = await getInvitations({ query, page, sort, filters });
const invitationsData = await getInvitations({
query,
page,
sort,
filters,
pageSize,
});
const rolesData = await getRoles({});
// Create a dictionary for roles by invitation ID
+2
View File
@@ -163,6 +163,7 @@ const SSRDataTable = async ({
}) => {
const page = parseInt(searchParams.page?.toString() || "1", 10);
const sort = searchParams.sort?.toString();
const pageSize = parseInt(searchParams.pageSize?.toString() || "10", 10);
// Convert filters to the correct type
const filters: Record<string, string> = {};
@@ -178,6 +179,7 @@ const SSRDataTable = async ({
page,
sort,
filters,
pageSize,
});
return (
<DataTable
+8 -1
View File
@@ -50,6 +50,7 @@ const SSRDataTable = async ({
}) => {
const page = parseInt(searchParams.page?.toString() || "1", 10);
const sort = searchParams.sort?.toString();
const pageSize = parseInt(searchParams.pageSize?.toString() || "10", 10);
// Extract all filter parameters
const filters = Object.fromEntries(
@@ -59,7 +60,13 @@ const SSRDataTable = async ({
// Extract query from filters
const query = (filters["filter[search]"] as string) || "";
const providersData = await getProviders({ query, page, sort, filters });
const providersData = await getProviders({
query,
page,
sort,
filters,
pageSize,
});
const providerGroupDict =
providersData?.included
+2 -1
View File
@@ -41,6 +41,7 @@ const SSRDataTable = async ({
}) => {
const page = parseInt(searchParams.page?.toString() || "1", 10);
const sort = searchParams.sort?.toString();
const pageSize = parseInt(searchParams.pageSize?.toString() || "10", 10);
// Extract all filter parameters
const filters = Object.fromEntries(
@@ -50,7 +51,7 @@ const SSRDataTable = async ({
// Extract query from filters
const query = (filters["filter[search]"] as string) || "";
const rolesData = await getRoles({ query, page, sort, filters });
const rolesData = await getRoles({ query, page, sort, filters, pageSize });
return (
<DataTable
+2 -1
View File
@@ -107,6 +107,7 @@ const SSRDataTableScans = async ({
searchParams: SearchParamsProps;
}) => {
const page = parseInt(searchParams.page?.toString() || "1", 10);
const pageSize = parseInt(searchParams.pageSize?.toString() || "10", 10);
const sort = searchParams.sort?.toString();
// Extract all filter parameters, excluding scanId
@@ -120,7 +121,7 @@ const SSRDataTableScans = async ({
const query = (filters["filter[search]"] as string) || "";
// Fetch scans data
const scansData = await getScans({ query, page, sort, filters });
const scansData = await getScans({ query, page, sort, filters, pageSize });
// Handle expanded scans data
const expandedScansData = await Promise.all(
+2 -1
View File
@@ -41,6 +41,7 @@ const SSRDataTable = async ({
}) => {
const page = parseInt(searchParams.page?.toString() || "1", 10);
const sort = searchParams.sort?.toString();
const pageSize = parseInt(searchParams.pageSize?.toString() || "10", 10);
// Extract all filter parameters
const filters = Object.fromEntries(
@@ -50,7 +51,7 @@ const SSRDataTable = async ({
// Extract query from filters
const query = (filters["filter[search]"] as string) || "";
const usersData = await getUsers({ query, page, sort, filters });
const usersData = await getUsers({ query, page, sort, filters, pageSize });
const rolesData = await getRoles({});
// Create a dictionary for roles by user ID
@@ -7,23 +7,36 @@ import {
DoubleArrowRightIcon,
} from "@radix-ui/react-icons";
import Link from "next/link";
import { usePathname, useSearchParams } from "next/navigation";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useState } from "react";
import { getPaginationInfo } from "@/lib";
import { MetaDataProps } from "@/types";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "../select/Select";
interface DataTablePaginationProps {
pageSizeOptions?: number[];
metadata?: MetaDataProps;
}
export function DataTablePagination({ metadata }: DataTablePaginationProps) {
const pathname = usePathname();
const searchParams = useSearchParams();
const router = useRouter();
const initialPageSize = searchParams.get("pageSize") ?? "10";
const [selectedPageSize, setSelectedPageSize] = useState(initialPageSize);
if (!metadata) return null;
const { currentPage, totalPages, totalEntries } = getPaginationInfo(metadata);
const { currentPage, totalPages, totalEntries, itemsPerPageOptions } =
getPaginationInfo(metadata);
const createPageUrl = (pageNumber: number | string) => {
const params = new URLSearchParams(searchParams);
@@ -44,6 +57,38 @@ export function DataTablePagination({ metadata }: DataTablePaginationProps) {
{totalEntries} entries in Total.
</div>
<div className="flex flex-col-reverse items-center gap-4 sm:flex-row sm:gap-6 lg:gap-8">
{/* Rows per page selector */}
<div className="flex items-center space-x-2">
<p className="whitespace-nowrap text-sm font-medium">Rows per page</p>
<Select
value={selectedPageSize}
onValueChange={(value) => {
setSelectedPageSize(value);
const params = new URLSearchParams(searchParams);
params.set("pageSize", value);
params.set("page", "1");
// This pushes the URL without reloading the page
router.push(`${pathname}?${params.toString()}`);
}}
>
<SelectTrigger className="h-8 w-[4.5rem]">
<SelectValue />
</SelectTrigger>
<SelectContent side="top">
{itemsPerPageOptions.map((pageSize) => (
<SelectItem
key={pageSize}
value={`${pageSize}`}
className="cursor-pointer"
>
{pageSize}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex items-center justify-center text-sm font-medium">
Page {currentPage} of {totalPages}
</div>
+1
View File
@@ -48,6 +48,7 @@ export function DataTable<TData, TValue>({
getSortedRowModel: getSortedRowModel(),
onColumnFiltersChange: setColumnFilters,
getFilteredRowModel: getFilteredRowModel(),
manualPagination: true,
state: {
sorting,
columnFilters,
+4 -1
View File
@@ -161,8 +161,11 @@ export const getPaginationInfo = (metadata: MetaDataProps) => {
const currentPage = metadata?.pagination.page ?? "1";
const totalPages = metadata?.pagination.pages;
const totalEntries = metadata?.pagination.count;
const itemsPerPageOptions = metadata?.pagination.itemsPerPage ?? [
10, 20, 30, 50, 100,
];
return { currentPage, totalPages, totalEntries };
return { currentPage, totalPages, totalEntries, itemsPerPageOptions };
};
export function encryptKey(passkey: string) {
+1
View File
@@ -755,6 +755,7 @@ export interface MetaDataProps {
page: number;
pages: number;
count: number;
itemsPerPage?: Array<number>;
};
version: string;
}