From 5e5754fa6260eb20dcf18c6632472eb091a2ba9e Mon Sep 17 00:00:00 2001 From: Pallavi Date: Mon, 1 Sep 2025 21:22:18 +0530 Subject: [PATCH 01/21] preserve port and method on host change --- .../resources/[resourceId]/proxy/page.tsx | 49 +++++++++++++------ 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx index ae831277..5ec16618 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx @@ -649,18 +649,29 @@ export default function ReverseProxyTargets(props: { defaultValue={row.original.ip} className="min-w-[150px]" onBlur={(e) => { - const parsed = parseHostTarget(e.target.value); - if (parsed) { - updateTarget(row.original.targetId, { - ...row.original, - method: parsed.protocol, - ip: parsed.host, - port: parsed.port - }); + const input = e.target.value.trim(); + const hasProtocol = /^https?:\/\//.test(input); + const hasPort = /:\d+/.test(input); + + if (hasProtocol || hasPort) { + const parsed = parseHostTarget(input); + if (parsed) { + updateTarget(row.original.targetId, { + ...row.original, + method: hasProtocol ? parsed.protocol : row.original.method, + ip: parsed.host, + port: hasPort ? parsed.port : row.original.port + }); + } else { + updateTarget(row.original.targetId, { + ...row.original, + ip: input + }); + } } else { updateTarget(row.original.targetId, { ...row.original, - ip: e.target.value + ip: input }); } }} @@ -961,11 +972,21 @@ export default function ReverseProxyTargets(props: { id="ip" {...field} onBlur={(e) => { - const parsed = parseHostTarget(e.target.value); - if (parsed) { - addTargetForm.setValue("method", parsed.protocol); - addTargetForm.setValue("ip", parsed.host); - addTargetForm.setValue("port", parsed.port); + const input = e.target.value.trim(); + const hasProtocol = /^https?:\/\//.test(input); + const hasPort = /:\d+/.test(input); + + if (hasProtocol || hasPort) { + const parsed = parseHostTarget(input); + if (parsed) { + if (hasProtocol || !addTargetForm.getValues("method")) { + addTargetForm.setValue("method", parsed.protocol); + } + addTargetForm.setValue("ip", parsed.host); + if (hasPort || !addTargetForm.getValues("port")) { + addTargetForm.setValue("port", parsed.port); + } + } } else { field.onBlur(); } From b670e6e3dcdf81e2ee92fb7fe9e494a29e291fed Mon Sep 17 00:00:00 2001 From: Pallavi Date: Mon, 1 Sep 2025 21:47:50 +0530 Subject: [PATCH 02/21] update parser to handle h2c --- .../resources/[resourceId]/proxy/page.tsx | 10 ++++----- src/lib/parseHostTarget.ts | 22 +++++++++++++++---- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx index 5ec16618..87c3dd13 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx @@ -650,8 +650,8 @@ export default function ReverseProxyTargets(props: { className="min-w-[150px]" onBlur={(e) => { const input = e.target.value.trim(); - const hasProtocol = /^https?:\/\//.test(input); - const hasPort = /:\d+/.test(input); + const hasProtocol = /^(https?|h2c):\/\//.test(input); + const hasPort = /:\d+(?:\/|$)/.test(input); if (hasProtocol || hasPort) { const parsed = parseHostTarget(input); @@ -675,9 +675,7 @@ export default function ReverseProxyTargets(props: { }); } }} - /> - ) }, { @@ -973,8 +971,8 @@ export default function ReverseProxyTargets(props: { {...field} onBlur={(e) => { const input = e.target.value.trim(); - const hasProtocol = /^https?:\/\//.test(input); - const hasPort = /:\d+/.test(input); + const hasProtocol = /^(https?|h2c):\/\//.test(input); + const hasPort = /:\d+(?:\/|$)/.test(input); if (hasProtocol || hasPort) { const parsed = parseHostTarget(input); diff --git a/src/lib/parseHostTarget.ts b/src/lib/parseHostTarget.ts index c79c7aa3..b860f410 100644 --- a/src/lib/parseHostTarget.ts +++ b/src/lib/parseHostTarget.ts @@ -1,15 +1,29 @@ + export function parseHostTarget(input: string) { try { - const normalized = input.match(/^https?:\/\//) ? input : `http://${input}`; + const normalized = input.match(/^(https?|h2c):\/\//) ? input : `http://${input}`; const url = new URL(normalized); - const protocol = url.protocol.replace(":", ""); // http | https + const protocol = url.protocol.replace(":", ""); // http | https | h2c const host = url.hostname; - const port = url.port ? parseInt(url.port, 10) : protocol === "https" ? 443 : 80; + + let defaultPort: number; + switch (protocol) { + case "https": + defaultPort = 443; + break; + case "h2c": + defaultPort = 80; + break; + default: // http + defaultPort = 80; + break; + } + + const port = url.port ? parseInt(url.port, 10) : defaultPort; return { protocol, host, port }; } catch { return null; } } - From ee9101e738cbdf064cc1c028d13472b0b60fe089 Mon Sep 17 00:00:00 2001 From: Pallavi Date: Mon, 1 Sep 2025 22:26:12 +0530 Subject: [PATCH 03/21] Save Amount of Entries --- .../invitations/InvitationsDataTable.tsx | 1 + .../settings/access/roles/RolesDataTable.tsx | 1 + .../settings/access/users/UsersDataTable.tsx | 1 + .../settings/api-keys/OrgApiKeysDataTable.tsx | 1 + .../settings/clients/ClientsDataTable.tsx | 1 + .../settings/domains/DomainsDataTable.tsx | 1 + .../settings/resources/ResourcesTable.tsx | 64 ++++++++++++- .../share-links/ShareLinksDataTable.tsx | 1 + .../[orgId]/settings/sites/SitesDataTable.tsx | 1 + src/app/admin/api-keys/ApiKeysDataTable.tsx | 1 + src/app/admin/idp/AdminIdpDataTable.tsx | 1 + .../idp/[idpId]/policies/PolicyDataTable.tsx | 1 + .../admin/license/LicenseKeysDataTable.tsx | 1 + src/app/admin/users/AdminUsersDataTable.tsx | 1 + src/components/DataTablePagination.tsx | 20 +++-- src/components/ui/data-table.tsx | 89 +++++++++++++++++-- 16 files changed, 172 insertions(+), 14 deletions(-) diff --git a/src/app/[orgId]/settings/access/invitations/InvitationsDataTable.tsx b/src/app/[orgId]/settings/access/invitations/InvitationsDataTable.tsx index 57b1d746..396a3c20 100644 --- a/src/app/[orgId]/settings/access/invitations/InvitationsDataTable.tsx +++ b/src/app/[orgId]/settings/access/invitations/InvitationsDataTable.tsx @@ -22,6 +22,7 @@ export function InvitationsDataTable({ ({ ({ ({ ({ ({ + 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({ resources, internalResources, @@ -113,6 +150,13 @@ export default function ResourcesTable({ const api = createApiClient({ env }); + const [proxyPageSize, setProxyPageSize] = useState(() => + getStoredPageSize('proxy-resources', 20) + ); + const [internalPageSize, setInternalPageSize] = useState(() => + getStoredPageSize('internal-resources', 20) + ); + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const [selectedResource, setSelectedResource] = useState(); @@ -559,7 +603,7 @@ export default function ResourcesTable({ onGlobalFilterChange: setProxyGlobalFilter, initialState: { pagination: { - pageSize: 20, + pageSize: proxyPageSize, pageIndex: 0 } }, @@ -582,7 +626,7 @@ export default function ResourcesTable({ onGlobalFilterChange: setInternalGlobalFilter, initialState: { pagination: { - pageSize: 20, + pageSize: proxyPageSize, 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 ( <> {selectedResource && ( @@ -761,7 +815,10 @@ export default function ResourcesTable({
- +
@@ -861,6 +918,7 @@ export default function ResourcesTable({
diff --git a/src/app/[orgId]/settings/share-links/ShareLinksDataTable.tsx b/src/app/[orgId]/settings/share-links/ShareLinksDataTable.tsx index e9fc4c6a..dd266bcf 100644 --- a/src/app/[orgId]/settings/share-links/ShareLinksDataTable.tsx +++ b/src/app/[orgId]/settings/share-links/ShareLinksDataTable.tsx @@ -24,6 +24,7 @@ export function ShareLinksDataTable({ ({ ({ ({ ({ ({ { table: Table; + onPageSizeChange?: (pageSize: number) => void; } export function DataTablePagination({ - table + table, + onPageSizeChange }: DataTablePaginationProps) { 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 (