From 89695df0129f5e72c2bc2d410dce2066012b938c Mon Sep 17 00:00:00 2001 From: Fred KISSIE Date: Fri, 30 Jan 2026 05:39:01 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=A7=20wip:=20pagination=20and=20search?= =?UTF-8?q?=20work?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/routers/site/listSites.ts | 21 +- src/app/[orgId]/settings/sites/page.tsx | 4 +- src/components/SitesTable.tsx | 36 +- src/components/UserDevicesTable.tsx | 89 ++-- src/components/ui/data-table.tsx | 12 +- src/components/ui/manual-data-table.tsx | 567 ++++++++++++++++++++++++ 6 files changed, 667 insertions(+), 62 deletions(-) create mode 100644 src/components/ui/manual-data-table.tsx diff --git a/server/routers/site/listSites.ts b/server/routers/site/listSites.ts index cefaeaf3..e77f6333 100644 --- a/server/routers/site/listSites.ts +++ b/server/routers/site/listSites.ts @@ -222,15 +222,24 @@ export async function listSites( const accessibleSiteIds = accessibleSites.map((site) => site.siteId); const baseQuery = querySites(orgId, accessibleSiteIds, query); + let conditions = and( + inArray(sites.siteId, accessibleSiteIds), + eq(sites.orgId, orgId) + ); + if (query) { + conditions = and( + conditions, + or( + ilike(sites.name, "%" + query + "%"), + ilike(sites.niceId, "%" + query + "%") + ) + ); + } + const countQuery = db .select({ count: count() }) .from(sites) - .where( - and( - inArray(sites.siteId, accessibleSiteIds), - eq(sites.orgId, orgId) - ) - ); + .where(conditions); const sitesList = await baseQuery .limit(pageSize) diff --git a/src/app/[orgId]/settings/sites/page.tsx b/src/app/[orgId]/settings/sites/page.tsx index 69bb599c..161c757f 100644 --- a/src/app/[orgId]/settings/sites/page.tsx +++ b/src/app/[orgId]/settings/sites/page.tsx @@ -81,10 +81,8 @@ export default async function SitesPage(props: SitesPageProps) { router.push(`${pathname}?${sp.toString()}`)); }; - // const = useDebouncedCallback() - const handleSearchChange = useDebouncedCallback((query: string) => { const sp = new URLSearchParams(searchParams); sp.set("query", query); + sp.delete("page"); startTransition(() => router.push(`${pathname}?${sp.toString()}`)); }, 300); + console.log({ + pagination, + rowCount + }); + return ( <> {selectedSite && ( @@ -459,13 +464,11 @@ export default function SitesTable({ /> )} - router.push(`/${orgId}/settings/sites/create`)} @@ -474,10 +477,7 @@ export default function SitesTable({ addButtonText={t("siteAdd")} onRefresh={() => startTransition(refreshData)} isRefreshing={isRefreshing} - defaultSort={{ - id: "name", - desc: false - }} + rowCount={rowCount} columnVisibility={{ niceId: false, nice: false, diff --git a/src/components/UserDevicesTable.tsx b/src/components/UserDevicesTable.tsx index 9d1469f1..edc84088 100644 --- a/src/components/UserDevicesTable.tsx +++ b/src/components/UserDevicesTable.tsx @@ -13,7 +13,10 @@ import { useEnvContext } from "@app/hooks/useEnvContext"; import { toast } from "@app/hooks/useToast"; import { createApiClient, formatAxiosError } from "@app/lib/api"; import { getUserDisplayName } from "@app/lib/getUserDisplayName"; -import { formatFingerprintInfo, formatPlatform } from "@app/lib/formatDeviceFingerprint"; +import { + formatFingerprintInfo, + formatPlatform +} from "@app/lib/formatDeviceFingerprint"; import { ArrowRight, ArrowUpDown, @@ -188,9 +191,13 @@ export default function UserDevicesTable({ userClients }: ClientTableProps) { try { // Fetch approvalId for this client using clientId query parameter const approvalsRes = await api.get<{ - data: { approvals: Array<{ approvalId: number; clientId: number }> }; - }>(`/org/${clientRow.orgId}/approvals?approvalState=pending&clientId=${clientRow.id}`); - + data: { + approvals: Array<{ approvalId: number; clientId: number }>; + }; + }>( + `/org/${clientRow.orgId}/approvals?approvalState=pending&clientId=${clientRow.id}` + ); + const approval = approvalsRes.data.data.approvals[0]; if (!approval) { @@ -202,9 +209,12 @@ export default function UserDevicesTable({ userClients }: ClientTableProps) { return; } - await api.put(`/org/${clientRow.orgId}/approvals/${approval.approvalId}`, { - decision: "approved" - }); + await api.put( + `/org/${clientRow.orgId}/approvals/${approval.approvalId}`, + { + decision: "approved" + } + ); toast({ title: t("accessApprovalUpdated"), @@ -230,9 +240,13 @@ export default function UserDevicesTable({ userClients }: ClientTableProps) { try { // Fetch approvalId for this client using clientId query parameter const approvalsRes = await api.get<{ - data: { approvals: Array<{ approvalId: number; clientId: number }> }; - }>(`/org/${clientRow.orgId}/approvals?approvalState=pending&clientId=${clientRow.id}`); - + data: { + approvals: Array<{ approvalId: number; clientId: number }>; + }; + }>( + `/org/${clientRow.orgId}/approvals?approvalState=pending&clientId=${clientRow.id}` + ); + const approval = approvalsRes.data.data.approvals[0]; if (!approval) { @@ -244,9 +258,12 @@ export default function UserDevicesTable({ userClients }: ClientTableProps) { return; } - await api.put(`/org/${clientRow.orgId}/approvals/${approval.approvalId}`, { - decision: "denied" - }); + await api.put( + `/org/${clientRow.orgId}/approvals/${approval.approvalId}`, + { + decision: "denied" + } + ); toast({ title: t("accessApprovalUpdated"), @@ -398,7 +415,7 @@ export default function UserDevicesTable({ userClients }: ClientTableProps) { }, { accessorKey: "online", - friendlyName: t("connectivity"), + friendlyName: t("online"), header: ({ column }) => { return ( + + + + {filter.label} + + + {filter.options.map( + (option) => { + const isChecked = + selectedValues.includes( + option.value + ); + return ( + { + // handleFilterChange( + // filter.id, + // option.value, + // checked + // ) + }} + onSelect={(e) => + e.preventDefault() + } + > + {option.label} + + ); + } + )} + + + ); + })} + + )} + +
+ {onRefresh && ( +
+ +
+ )} + {onAdd && addButtonText && ( +
+ +
+ )} +
+ + +
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + const columnId = header.column.id; + const accessorKey = ( + header.column.columnDef as any + ).accessorKey as string | undefined; + const stickyClasses = + getStickyClasses( + columnId, + accessorKey + ); + const isRightSticky = + isStickyColumn( + columnId, + accessorKey, + "right" + ); + const hasHideableColumns = + enableColumnVisibility && + table + .getAllColumns() + .some((col) => + col.getCanHide() + ); + + return ( + + {header.isPlaceholder ? null : isRightSticky && + hasHideableColumns ? ( +
+ + + + + + + {t( + "toggleColumns" + ) || + "Toggle columns"} + + + {table + .getAllColumns() + .filter( + ( + column + ) => + column.getCanHide() + ) + .map( + ( + column + ) => { + const columnDef = + column.columnDef as any; + const friendlyName = + columnDef.friendlyName; + const displayName = + friendlyName || + (typeof columnDef.header === + "string" + ? columnDef.header + : column.id); + return ( + + column.toggleVisibility( + !!value + ) + } + onSelect={( + e + ) => + e.preventDefault() + } + > + { + displayName + } + + ); + } + )} + + +
+ {flexRender( + header + .column + .columnDef + .header, + header.getContext() + )} +
+
+ ) : ( + flexRender( + header.column + .columnDef + .header, + header.getContext() + ) + )} +
+ ); + })} +
+ ))} +
+ + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row + .getVisibleCells() + .map((cell) => { + const columnId = + cell.column.id; + const accessorKey = ( + cell.column + .columnDef as any + ).accessorKey as + | string + | undefined; + const stickyClasses = + getStickyClasses( + columnId, + accessorKey + ); + const isRightSticky = + isStickyColumn( + columnId, + accessorKey, + "right" + ); + return ( + + {flexRender( + cell.column + .columnDef + .cell, + cell.getContext() + )} + + ); + })} + + )) + ) : ( + + + No results found. + + + )} + +
+
+
+ {rowCount > 0 && ( + + onPaginationChange({ + ...pagination, + pageSize + }) + } + onPageChange={(pageIndex) => { + onPaginationChange({ + ...pagination, + pageIndex + }); + }} + isServerPagination + pageSize={pagination.pageSize} + pageIndex={pagination.pageIndex} + /> + )} +
+
+ + + ); +}