mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-23 13:26:41 +00:00
Merge branch 'enhancement-#906/persist-amount-of-entries' of github.com:Pallavikumarimdb/pangolin into Pallavikumarimdb-enhancement-#906/persist-amount-of-entries
This commit is contained in:
@@ -22,6 +22,7 @@ export function InvitationsDataTable<TData, TValue>({
|
|||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={data}
|
data={data}
|
||||||
|
persistPageSize="invitations-table"
|
||||||
title={t('invite')}
|
title={t('invite')}
|
||||||
searchPlaceholder={t('inviteSearch')}
|
searchPlaceholder={t('inviteSearch')}
|
||||||
searchColumn="email"
|
searchColumn="email"
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export function RolesDataTable<TData, TValue>({
|
|||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={data}
|
data={data}
|
||||||
|
persistPageSize="roles-table"
|
||||||
title={t('roles')}
|
title={t('roles')}
|
||||||
searchPlaceholder={t('accessRolesSearch')}
|
searchPlaceholder={t('accessRolesSearch')}
|
||||||
searchColumn="name"
|
searchColumn="name"
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export function UsersDataTable<TData, TValue>({
|
|||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={data}
|
data={data}
|
||||||
|
persistPageSize="users-table"
|
||||||
title={t('users')}
|
title={t('users')}
|
||||||
searchPlaceholder={t('accessUsersSearch')}
|
searchPlaceholder={t('accessUsersSearch')}
|
||||||
searchColumn="email"
|
searchColumn="email"
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export function OrgApiKeysDataTable<TData, TValue>({
|
|||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={data}
|
data={data}
|
||||||
|
persistPageSize="Org-apikeys-table"
|
||||||
title={t('apiKeys')}
|
title={t('apiKeys')}
|
||||||
searchPlaceholder={t('searchApiKeys')}
|
searchPlaceholder={t('searchApiKeys')}
|
||||||
searchColumn="name"
|
searchColumn="name"
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export function ClientsDataTable<TData, TValue>({
|
|||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={data}
|
data={data}
|
||||||
|
persistPageSize="clients-table"
|
||||||
title="Clients"
|
title="Clients"
|
||||||
searchPlaceholder="Search clients..."
|
searchPlaceholder="Search clients..."
|
||||||
searchColumn="name"
|
searchColumn="name"
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export function DomainsDataTable<TData, TValue>({
|
|||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={data}
|
data={data}
|
||||||
|
persistPageSize="domains-table"
|
||||||
title={t("domains")}
|
title={t("domains")}
|
||||||
searchPlaceholder={t("domainsSearch")}
|
searchPlaceholder={t("domainsSearch")}
|
||||||
searchColumn="baseDomain"
|
searchColumn="baseDomain"
|
||||||
|
|||||||
@@ -99,6 +99,43 @@ type ResourcesTableProps = {
|
|||||||
defaultView?: "proxy" | "internal";
|
defaultView?: "proxy" | "internal";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const STORAGE_KEYS = {
|
||||||
|
PAGE_SIZE: 'datatable-page-size',
|
||||||
|
getTablePageSize: (tableId?: string) =>
|
||||||
|
tableId ? `datatable-${tableId}-page-size` : STORAGE_KEYS.PAGE_SIZE
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStoredPageSize = (tableId?: string, defaultSize = 20): number => {
|
||||||
|
if (typeof window === 'undefined') return defaultSize;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const key = STORAGE_KEYS.getTablePageSize(tableId);
|
||||||
|
const stored = localStorage.getItem(key);
|
||||||
|
if (stored) {
|
||||||
|
const parsed = parseInt(stored, 10);
|
||||||
|
if (parsed > 0 && parsed <= 1000) {
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to read page size from localStorage:', error);
|
||||||
|
}
|
||||||
|
return defaultSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
const setStoredPageSize = (pageSize: number, tableId?: string): void => {
|
||||||
|
if (typeof window === 'undefined') return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const key = STORAGE_KEYS.getTablePageSize(tableId);
|
||||||
|
localStorage.setItem(key, pageSize.toString());
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to save page size to localStorage:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export default function ResourcesTable({
|
export default function ResourcesTable({
|
||||||
resources,
|
resources,
|
||||||
internalResources,
|
internalResources,
|
||||||
@@ -113,6 +150,13 @@ export default function ResourcesTable({
|
|||||||
|
|
||||||
const api = createApiClient({ env });
|
const api = createApiClient({ env });
|
||||||
|
|
||||||
|
const [proxyPageSize, setProxyPageSize] = useState<number>(() =>
|
||||||
|
getStoredPageSize('proxy-resources', 20)
|
||||||
|
);
|
||||||
|
const [internalPageSize, setInternalPageSize] = useState<number>(() =>
|
||||||
|
getStoredPageSize('internal-resources', 20)
|
||||||
|
);
|
||||||
|
|
||||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||||
const [selectedResource, setSelectedResource] =
|
const [selectedResource, setSelectedResource] =
|
||||||
useState<ResourceRow | null>();
|
useState<ResourceRow | null>();
|
||||||
@@ -559,7 +603,7 @@ export default function ResourcesTable({
|
|||||||
onGlobalFilterChange: setProxyGlobalFilter,
|
onGlobalFilterChange: setProxyGlobalFilter,
|
||||||
initialState: {
|
initialState: {
|
||||||
pagination: {
|
pagination: {
|
||||||
pageSize: 20,
|
pageSize: proxyPageSize,
|
||||||
pageIndex: 0
|
pageIndex: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -582,7 +626,7 @@ export default function ResourcesTable({
|
|||||||
onGlobalFilterChange: setInternalGlobalFilter,
|
onGlobalFilterChange: setInternalGlobalFilter,
|
||||||
initialState: {
|
initialState: {
|
||||||
pagination: {
|
pagination: {
|
||||||
pageSize: 20,
|
pageSize: internalPageSize,
|
||||||
pageIndex: 0
|
pageIndex: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -593,6 +637,16 @@ export default function ResourcesTable({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleProxyPageSizeChange = (newPageSize: number) => {
|
||||||
|
setProxyPageSize(newPageSize);
|
||||||
|
setStoredPageSize(newPageSize, 'proxy-resources');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleInternalPageSizeChange = (newPageSize: number) => {
|
||||||
|
setInternalPageSize(newPageSize);
|
||||||
|
setStoredPageSize(newPageSize, 'internal-resources');
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{selectedResource && (
|
{selectedResource && (
|
||||||
@@ -761,7 +815,10 @@ export default function ResourcesTable({
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<DataTablePagination table={proxyTable} />
|
<DataTablePagination
|
||||||
|
table={proxyTable}
|
||||||
|
onPageSizeChange={handleProxyPageSizeChange}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value="internal">
|
<TabsContent value="internal">
|
||||||
@@ -861,6 +918,7 @@ export default function ResourcesTable({
|
|||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<DataTablePagination
|
<DataTablePagination
|
||||||
table={internalTable}
|
table={internalTable}
|
||||||
|
onPageSizeChange={handleInternalPageSizeChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export function ShareLinksDataTable<TData, TValue>({
|
|||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={data}
|
data={data}
|
||||||
|
persistPageSize="shareLinks-table"
|
||||||
title={t('shareLinks')}
|
title={t('shareLinks')}
|
||||||
searchPlaceholder={t('shareSearch')}
|
searchPlaceholder={t('shareSearch')}
|
||||||
searchColumn="name"
|
searchColumn="name"
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export function SitesDataTable<TData, TValue>({
|
|||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={data}
|
data={data}
|
||||||
|
persistPageSize="sites-table"
|
||||||
title={t('sites')}
|
title={t('sites')}
|
||||||
searchPlaceholder={t('searchSitesProgress')}
|
searchPlaceholder={t('searchSitesProgress')}
|
||||||
searchColumn="name"
|
searchColumn="name"
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ export function ApiKeysDataTable<TData, TValue>({
|
|||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={data}
|
data={data}
|
||||||
|
persistPageSize="apiKeys-table"
|
||||||
title={t('apiKeys')}
|
title={t('apiKeys')}
|
||||||
searchPlaceholder={t('searchApiKeys')}
|
searchPlaceholder={t('searchApiKeys')}
|
||||||
searchColumn="name"
|
searchColumn="name"
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export function IdpDataTable<TData, TValue>({
|
|||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={data}
|
data={data}
|
||||||
|
persistPageSize="idp-table"
|
||||||
title={t('idp')}
|
title={t('idp')}
|
||||||
searchPlaceholder={t('idpSearch')}
|
searchPlaceholder={t('idpSearch')}
|
||||||
searchColumn="name"
|
searchColumn="name"
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export function PolicyDataTable<TData, TValue>({
|
|||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={data}
|
data={data}
|
||||||
|
persistPageSize="orgPolicies-table"
|
||||||
title={t('orgPolicies')}
|
title={t('orgPolicies')}
|
||||||
searchPlaceholder={t('orgPoliciesSearch')}
|
searchPlaceholder={t('orgPoliciesSearch')}
|
||||||
searchColumn="orgId"
|
searchColumn="orgId"
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ export function LicenseKeysDataTable({
|
|||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={licenseKeys}
|
data={licenseKeys}
|
||||||
|
persistPageSize="licenseKeys-table"
|
||||||
title={t('licenseKeys')}
|
title={t('licenseKeys')}
|
||||||
searchPlaceholder={t('licenseKeySearch')}
|
searchPlaceholder={t('licenseKeySearch')}
|
||||||
searchColumn="licenseKey"
|
searchColumn="licenseKey"
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export function UsersDataTable<TData, TValue>({
|
|||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={data}
|
data={data}
|
||||||
|
persistPageSize="userServer-table"
|
||||||
title={t('userServer')}
|
title={t('userServer')}
|
||||||
searchPlaceholder={t('userSearch')}
|
searchPlaceholder={t('userSearch')}
|
||||||
searchColumn="email"
|
searchColumn="email"
|
||||||
|
|||||||
@@ -18,28 +18,38 @@ import { useTranslations } from "next-intl";
|
|||||||
|
|
||||||
interface DataTablePaginationProps<TData> {
|
interface DataTablePaginationProps<TData> {
|
||||||
table: Table<TData>;
|
table: Table<TData>;
|
||||||
|
onPageSizeChange?: (pageSize: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DataTablePagination<TData>({
|
export function DataTablePagination<TData>({
|
||||||
table
|
table,
|
||||||
|
onPageSizeChange
|
||||||
}: DataTablePaginationProps<TData>) {
|
}: DataTablePaginationProps<TData>) {
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
|
|
||||||
|
const handlePageSizeChange = (value: string) => {
|
||||||
|
const newPageSize = Number(value);
|
||||||
|
table.setPageSize(newPageSize);
|
||||||
|
|
||||||
|
// Call the callback if provided (for persistence)
|
||||||
|
if (onPageSizeChange) {
|
||||||
|
onPageSizeChange(newPageSize);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between text-muted-foreground">
|
<div className="flex items-center justify-between text-muted-foreground">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Select
|
<Select
|
||||||
value={`${table.getState().pagination.pageSize}`}
|
value={`${table.getState().pagination.pageSize}`}
|
||||||
onValueChange={(value) => {
|
onValueChange={handlePageSizeChange}
|
||||||
table.setPageSize(Number(value));
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<SelectTrigger className="h-8 w-[70px]">
|
<SelectTrigger className="h-8 w-[70px]">
|
||||||
<SelectValue
|
<SelectValue
|
||||||
placeholder={table.getState().pagination.pageSize}
|
placeholder={table.getState().pagination.pageSize}
|
||||||
/>
|
/>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent side="top">
|
<SelectContent side="bottom">
|
||||||
{[10, 20, 30, 40, 50, 100].map((pageSize) => (
|
{[10, 20, 30, 40, 50, 100].map((pageSize) => (
|
||||||
<SelectItem key={pageSize} value={`${pageSize}`}>
|
<SelectItem key={pageSize} value={`${pageSize}`}>
|
||||||
{pageSize}
|
{pageSize}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
TableRow
|
TableRow
|
||||||
} from "@/components/ui/table";
|
} from "@/components/ui/table";
|
||||||
import { Button } from "@app/components/ui/button";
|
import { Button } from "@app/components/ui/button";
|
||||||
import { useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { Input } from "@app/components/ui/input";
|
import { Input } from "@app/components/ui/input";
|
||||||
import { DataTablePagination } from "@app/components/DataTablePagination";
|
import { DataTablePagination } from "@app/components/DataTablePagination";
|
||||||
import { Plus, Search, RefreshCw } from "lucide-react";
|
import { Plus, Search, RefreshCw } from "lucide-react";
|
||||||
@@ -32,7 +32,42 @@ import {
|
|||||||
} from "@app/components/ui/card";
|
} from "@app/components/ui/card";
|
||||||
import { Tabs, TabsList, TabsTrigger } from "@app/components/ui/tabs";
|
import { Tabs, TabsList, TabsTrigger } from "@app/components/ui/tabs";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useMemo } from "react";
|
|
||||||
|
const STORAGE_KEYS = {
|
||||||
|
PAGE_SIZE: 'datatable-page-size',
|
||||||
|
getTablePageSize: (tableId?: string) =>
|
||||||
|
tableId ? `${tableId}-size` : STORAGE_KEYS.PAGE_SIZE
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStoredPageSize = (tableId?: string, defaultSize = 20): number => {
|
||||||
|
if (typeof window === 'undefined') return defaultSize;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const key = STORAGE_KEYS.getTablePageSize(tableId);
|
||||||
|
const stored = localStorage.getItem(key);
|
||||||
|
if (stored) {
|
||||||
|
const parsed = parseInt(stored, 10);
|
||||||
|
// Validate that it's a reasonable page size
|
||||||
|
if (parsed > 0 && parsed <= 1000) {
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to read page size from localStorage:', error);
|
||||||
|
}
|
||||||
|
return defaultSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
const setStoredPageSize = (pageSize: number, tableId?: string): void => {
|
||||||
|
if (typeof window === 'undefined') return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const key = STORAGE_KEYS.getTablePageSize(tableId);
|
||||||
|
localStorage.setItem(key, pageSize.toString());
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to save page size to localStorage:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
type TabFilter = {
|
type TabFilter = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -56,6 +91,8 @@ type DataTableProps<TData, TValue> = {
|
|||||||
};
|
};
|
||||||
tabs?: TabFilter[];
|
tabs?: TabFilter[];
|
||||||
defaultTab?: string;
|
defaultTab?: string;
|
||||||
|
persistPageSize?: boolean | string;
|
||||||
|
defaultPageSize?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function DataTable<TData, TValue>({
|
export function DataTable<TData, TValue>({
|
||||||
@@ -70,8 +107,23 @@ export function DataTable<TData, TValue>({
|
|||||||
searchColumn = "name",
|
searchColumn = "name",
|
||||||
defaultSort,
|
defaultSort,
|
||||||
tabs,
|
tabs,
|
||||||
defaultTab
|
defaultTab,
|
||||||
|
persistPageSize = false,
|
||||||
|
defaultPageSize = 20
|
||||||
}: DataTableProps<TData, TValue>) {
|
}: DataTableProps<TData, TValue>) {
|
||||||
|
const t = useTranslations();
|
||||||
|
|
||||||
|
// Determine table identifier for storage
|
||||||
|
const tableId = typeof persistPageSize === 'string' ? persistPageSize : undefined;
|
||||||
|
|
||||||
|
// Initialize page size from storage or default
|
||||||
|
const [pageSize, setPageSize] = useState<number>(() => {
|
||||||
|
if (persistPageSize) {
|
||||||
|
return getStoredPageSize(tableId, defaultPageSize);
|
||||||
|
}
|
||||||
|
return defaultPageSize;
|
||||||
|
});
|
||||||
|
|
||||||
const [sorting, setSorting] = useState<SortingState>(
|
const [sorting, setSorting] = useState<SortingState>(
|
||||||
defaultSort ? [defaultSort] : []
|
defaultSort ? [defaultSort] : []
|
||||||
);
|
);
|
||||||
@@ -80,7 +132,6 @@ export function DataTable<TData, TValue>({
|
|||||||
const [activeTab, setActiveTab] = useState<string>(
|
const [activeTab, setActiveTab] = useState<string>(
|
||||||
defaultTab || tabs?.[0]?.id || ""
|
defaultTab || tabs?.[0]?.id || ""
|
||||||
);
|
);
|
||||||
const t = useTranslations();
|
|
||||||
|
|
||||||
// Apply tab filter to data
|
// Apply tab filter to data
|
||||||
const filteredData = useMemo(() => {
|
const filteredData = useMemo(() => {
|
||||||
@@ -108,7 +159,7 @@ export function DataTable<TData, TValue>({
|
|||||||
onGlobalFilterChange: setGlobalFilter,
|
onGlobalFilterChange: setGlobalFilter,
|
||||||
initialState: {
|
initialState: {
|
||||||
pagination: {
|
pagination: {
|
||||||
pageSize: 20,
|
pageSize: pageSize,
|
||||||
pageIndex: 0
|
pageIndex: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -119,12 +170,35 @@ export function DataTable<TData, TValue>({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const currentPageSize = table.getState().pagination.pageSize;
|
||||||
|
if (currentPageSize !== pageSize) {
|
||||||
|
table.setPageSize(pageSize);
|
||||||
|
|
||||||
|
// Persist to localStorage if enabled
|
||||||
|
if (persistPageSize) {
|
||||||
|
setStoredPageSize(pageSize, tableId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [pageSize, table, persistPageSize, tableId]);
|
||||||
|
|
||||||
const handleTabChange = (value: string) => {
|
const handleTabChange = (value: string) => {
|
||||||
setActiveTab(value);
|
setActiveTab(value);
|
||||||
// Reset to first page when changing tabs
|
// Reset to first page when changing tabs
|
||||||
table.setPageIndex(0);
|
table.setPageIndex(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Enhanced pagination component that updates our local state
|
||||||
|
const handlePageSizeChange = (newPageSize: number) => {
|
||||||
|
setPageSize(newPageSize);
|
||||||
|
table.setPageSize(newPageSize);
|
||||||
|
|
||||||
|
// Persist immediately when changed
|
||||||
|
if (persistPageSize) {
|
||||||
|
setStoredPageSize(newPageSize, tableId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container mx-auto max-w-12xl">
|
<div className="container mx-auto max-w-12xl">
|
||||||
<Card>
|
<Card>
|
||||||
@@ -235,7 +309,10 @@ export function DataTable<TData, TValue>({
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<DataTablePagination table={table} />
|
<DataTablePagination
|
||||||
|
table={table}
|
||||||
|
onPageSizeChange={handlePageSizeChange}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
Reference in New Issue
Block a user