mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 04:51:51 +00:00
feat(ui): Page size for datatables (#7634)
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -48,6 +48,7 @@ export function DataTable<TData, TValue>({
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
manualPagination: true,
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
|
||||
+4
-1
@@ -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) {
|
||||
|
||||
@@ -755,6 +755,7 @@ export interface MetaDataProps {
|
||||
page: number;
|
||||
pages: number;
|
||||
count: number;
|
||||
itemsPerPage?: Array<number>;
|
||||
};
|
||||
version: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user