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:
Owen
2025-09-01 17:39:16 -07:00
16 changed files with 172 additions and 14 deletions

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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>

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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}

View File

@@ -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>