diff --git a/dashboard/src/app/user/data-table.tsx b/dashboard/src/app/user/data-table.tsx index f68d10e..e40e332 100644 --- a/dashboard/src/app/user/data-table.tsx +++ b/dashboard/src/app/user/data-table.tsx @@ -5,12 +5,18 @@ import { Identity } from '@ory/client'; import { DataTable } from '@/components/ui/data-table'; import { CircleCheck, CircleX } from 'lucide-react'; import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card'; +import { useState } from 'react'; +import { FetchIdentityPageProps } from '@/app/user/page'; interface IdentityDataTableProps { data: Identity[]; + pageSize: number; + pageToken: string | undefined; + query: string; + fetchIdentityPage: (props: FetchIdentityPageProps) => Promise<{ data: Identity[], tokens: Map }>; } -export function IdentityDataTable({ data }: IdentityDataTableProps) { +export function IdentityDataTable({ data, pageSize, pageToken, query, fetchIdentityPage }: IdentityDataTableProps) { const columns: ColumnDef[] = [ { @@ -80,5 +86,23 @@ export function IdentityDataTable({ data }: IdentityDataTableProps) { }, ]; - return ; + const [items, setItems] = useState(data); + const [nextToken, setNextToken] = useState(pageToken); + + const fetchMore = async () => { + + if (!nextToken) return; + + const response = await fetchIdentityPage({ + pageSize: pageSize, + pageToken: nextToken, + query: query, + }); + + setItems([...items, ...response.data]); + setNextToken(response.tokens.get('next') ?? undefined); + }; + + // TODO: fetch more when scrolling to the end of list + return ; } diff --git a/dashboard/src/app/user/page.tsx b/dashboard/src/app/user/page.tsx index ff0eecc..3938d42 100644 --- a/dashboard/src/app/user/page.tsx +++ b/dashboard/src/app/user/page.tsx @@ -3,6 +3,48 @@ import { IdentityDataTable } from '@/app/user/data-table'; import { getIdentityApi } from '@/ory/sdk/server'; import { SearchInput } from '@/components/search-input'; +export interface FetchIdentityPageProps { + pageSize: number; + pageToken: string; + query: string; +} + +async function fetchIdentityPage({ pageSize, pageToken, query }: FetchIdentityPageProps) { + 'use server'; + + const identityApi = await getIdentityApi(); + const response = await identityApi.listIdentities({ + pageSize: pageSize, + pageToken: pageToken, + previewCredentialsIdentifierSimilar: query, + }); + + return { + data: response.data, + tokens: parseTokens(response.headers.link), + }; +} + +function parseTokens(link: string) { + + const parsed = link.split(',').map((it) => { + const startRel = it.lastIndexOf('rel="'); + const endRel = it.lastIndexOf('"'); + const rel = it.slice(startRel, endRel); + + const startToken = it.lastIndexOf('page_token='); + const endToken = it.lastIndexOf('&'); + const token = it.slice(startToken, endToken); + + return [rel, token]; + }); + + return new Map(parsed.map(obj => [ + obj[0].replace('rel="', ''), + obj[1].replace('page_token=', ''), + ])); +} + export default async function UserPage( { searchParams, @@ -11,15 +53,13 @@ export default async function UserPage( }, ) { - const identityApi = await getIdentityApi(); - const params = await searchParams; const query = params.query ? params.query as string : ''; - const data = await identityApi.listIdentities({ - pageSize: 100, - previewCredentialsIdentifierSimilar: query, - }).then(response => response.data); + let pageSize = 200; + let pageToken: string = '00000000-0000-0000-0000-000000000000'; + + const initialFetch = await fetchIdentityPage({ pageSize, pageToken, query: '' }); return (
@@ -31,7 +71,12 @@ export default async function UserPage(
- +
);