NORY-41: apply new query and temporarily remove pagination
This commit is contained in:
parent
21dbd4cb05
commit
7df37e4001
2 changed files with 29 additions and 125 deletions
|
@ -4,9 +4,7 @@ import { ColumnDef } from '@tanstack/react-table';
|
|||
import { Identity } from '@ory/client';
|
||||
import { DataTable } from '@/components/ui/data-table';
|
||||
import { CircleCheck, CircleX, Copy, MoreHorizontal, Trash, UserCheck, UserMinus, UserPen, UserX } from 'lucide-react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { FetchIdentityPageProps } from '@/app/(inside)/user/page';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
@ -33,13 +31,11 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip
|
|||
|
||||
interface IdentityDataTableProps {
|
||||
data: Identity[];
|
||||
pageSize: number;
|
||||
pageToken: string | undefined;
|
||||
page: number;
|
||||
query: string;
|
||||
fetchIdentityPage: (props: FetchIdentityPageProps) => Promise<{ data: Identity[], tokens: Map<string, string> }>;
|
||||
}
|
||||
|
||||
export function IdentityDataTable({ data, pageSize, pageToken, query, fetchIdentityPage }: IdentityDataTableProps) {
|
||||
export function IdentityDataTable({ data, page, query }: IdentityDataTableProps) {
|
||||
|
||||
const columns: ColumnDef<Identity>[] = [
|
||||
{
|
||||
|
@ -81,27 +77,24 @@ export function IdentityDataTable({ data, pageSize, pageToken, query, fetchIdent
|
|||
return (
|
||||
<div className="flex flex-row space-x-2 items-center">
|
||||
<span>{email.value}</span>
|
||||
{
|
||||
email.verified ?
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<CircleCheck/>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<span>This email was confirmed at </span>
|
||||
{new Date(email.verified_at!!).toLocaleString()}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
:
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<CircleX/>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
This email is not confirmed yet
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
}
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
{
|
||||
email.verified ? <CircleCheck/> : <CircleX/>
|
||||
}
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
{
|
||||
email.verified ?
|
||||
<>
|
||||
<span>This email was confirmed at </span>
|
||||
{new Date(email.verified_at!!).toLocaleString()}
|
||||
</>
|
||||
:
|
||||
<p>This email is not confirmed yet</p>
|
||||
}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -189,49 +182,10 @@ export function IdentityDataTable({ data, pageSize, pageToken, query, fetchIdent
|
|||
];
|
||||
|
||||
const [items, setItems] = useState<Identity[]>(data);
|
||||
const [nextToken, setNextToken] = useState<string | undefined>(pageToken);
|
||||
|
||||
// react on changes from ssr (query params)
|
||||
useEffect(() => {
|
||||
setItems(data);
|
||||
setNextToken(pageToken);
|
||||
}, [data, pageSize, pageToken, query]);
|
||||
|
||||
// infinite scroll handling
|
||||
const infiniteScrollSensor = useRef(null);
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
if (entries[0].isIntersecting) {
|
||||
fetchMore();
|
||||
}
|
||||
},
|
||||
{ threshold: 0.5 }, // Adjust threshold as needed
|
||||
);
|
||||
|
||||
if (infiniteScrollSensor.current) {
|
||||
observer.observe(infiniteScrollSensor.current);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (infiniteScrollSensor.current) {
|
||||
observer.unobserve(infiniteScrollSensor.current);
|
||||
}
|
||||
};
|
||||
}, [items]);
|
||||
|
||||
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);
|
||||
};
|
||||
}, [data]);
|
||||
|
||||
// quick actions
|
||||
const [currentIdentity, setCurrentIdentity] = useState<Identity | undefined>(undefined);
|
||||
|
@ -341,13 +295,6 @@ export function IdentityDataTable({ data, pageSize, pageToken, query, fetchIdent
|
|||
</>
|
||||
)
|
||||
}
|
||||
{
|
||||
nextToken && (
|
||||
<div className="flex w-full justify-center">
|
||||
<Spinner ref={infiniteScrollSensor} className="h-10"/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,49 +1,7 @@
|
|||
import React from 'react';
|
||||
import { IdentityDataTable } from '@/app/(inside)/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=', ''),
|
||||
]));
|
||||
}
|
||||
import { queryIdentities } from '@/lib/action/identity';
|
||||
|
||||
export default async function UserPage(
|
||||
{
|
||||
|
@ -54,12 +12,13 @@ export default async function UserPage(
|
|||
) {
|
||||
|
||||
const params = await searchParams;
|
||||
|
||||
const page = params.page ? Number(params.page) : 1;
|
||||
const query = params.query ? params.query as string : '';
|
||||
|
||||
let pageSize = 250;
|
||||
let pageToken: string = '00000000-0000-0000-0000-000000000000';
|
||||
|
||||
const initialFetch = await fetchIdentityPage({ pageSize, pageToken, query: query });
|
||||
const initialData = await queryIdentities({ page, pageSize, query });
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
|
@ -72,11 +31,9 @@ export default async function UserPage(
|
|||
<div className="space-y-2">
|
||||
<SearchInput queryParamKey="query" placeholder="Search for identifiers (Email, Username...)"/>
|
||||
<IdentityDataTable
|
||||
data={initialFetch.data}
|
||||
pageSize={pageSize}
|
||||
pageToken={initialFetch.tokens.get('next')}
|
||||
query={query}
|
||||
fetchIdentityPage={fetchIdentityPage}/>
|
||||
data={initialData}
|
||||
page={page}
|
||||
query={query}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
Loading…
Add table
Reference in a new issue