1
0
Fork 0
mirror of https://codeberg.org/MarkusThielker/next-ory.git synced 2025-04-10 11:58:41 +00:00

NORY-41: apply new query and temporarily remove pagination

This commit is contained in:
Markus Thielker 2025-01-04 10:53:08 +01:00
parent 21dbd4cb05
commit 7df37e4001
No known key found for this signature in database
2 changed files with 29 additions and 125 deletions

View file

@ -4,9 +4,7 @@ import { ColumnDef } from '@tanstack/react-table';
import { Identity } from '@ory/client'; import { Identity } from '@ory/client';
import { DataTable } from '@/components/ui/data-table'; import { DataTable } from '@/components/ui/data-table';
import { CircleCheck, CircleX, Copy, MoreHorizontal, Trash, UserCheck, UserMinus, UserPen, UserX } from 'lucide-react'; import { CircleCheck, CircleX, Copy, MoreHorizontal, Trash, UserCheck, UserMinus, UserPen, UserX } from 'lucide-react';
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { FetchIdentityPageProps } from '@/app/(inside)/user/page';
import { Spinner } from '@/components/ui/spinner';
import { import {
DropdownMenu, DropdownMenu,
DropdownMenuContent, DropdownMenuContent,
@ -33,13 +31,11 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip
interface IdentityDataTableProps { interface IdentityDataTableProps {
data: Identity[]; data: Identity[];
pageSize: number; page: number;
pageToken: string | undefined;
query: string; 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>[] = [ const columns: ColumnDef<Identity>[] = [
{ {
@ -81,27 +77,24 @@ export function IdentityDataTable({ data, pageSize, pageToken, query, fetchIdent
return ( return (
<div className="flex flex-row space-x-2 items-center"> <div className="flex flex-row space-x-2 items-center">
<span>{email.value}</span> <span>{email.value}</span>
{ <Tooltip>
email.verified ? <TooltipTrigger>
<Tooltip> {
<TooltipTrigger> email.verified ? <CircleCheck/> : <CircleX/>
<CircleCheck/> }
</TooltipTrigger> </TooltipTrigger>
<TooltipContent> <TooltipContent>
<span>This email was confirmed at </span> {
{new Date(email.verified_at!!).toLocaleString()} email.verified ?
</TooltipContent> <>
</Tooltip> <span>This email was confirmed at </span>
: {new Date(email.verified_at!!).toLocaleString()}
<Tooltip> </>
<TooltipTrigger> :
<CircleX/> <p>This email is not confirmed yet</p>
</TooltipTrigger> }
<TooltipContent> </TooltipContent>
This email is not confirmed yet </Tooltip>
</TooltipContent>
</Tooltip>
}
</div> </div>
); );
} }
@ -189,49 +182,10 @@ export function IdentityDataTable({ data, pageSize, pageToken, query, fetchIdent
]; ];
const [items, setItems] = useState<Identity[]>(data); const [items, setItems] = useState<Identity[]>(data);
const [nextToken, setNextToken] = useState<string | undefined>(pageToken);
// react on changes from ssr (query params)
useEffect(() => { useEffect(() => {
setItems(data); setItems(data);
setNextToken(pageToken); }, [data]);
}, [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);
};
// quick actions // quick actions
const [currentIdentity, setCurrentIdentity] = useState<Identity | undefined>(undefined); 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>
)
}
</> </>
); );
} }

View file

@ -1,49 +1,7 @@
import React from 'react'; import React from 'react';
import { IdentityDataTable } from '@/app/(inside)/user/data-table'; import { IdentityDataTable } from '@/app/(inside)/user/data-table';
import { getIdentityApi } from '@/ory/sdk/server';
import { SearchInput } from '@/components/search-input'; import { SearchInput } from '@/components/search-input';
import { queryIdentities } from '@/lib/action/identity';
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( export default async function UserPage(
{ {
@ -54,12 +12,13 @@ export default async function UserPage(
) { ) {
const params = await searchParams; const params = await searchParams;
const page = params.page ? Number(params.page) : 1;
const query = params.query ? params.query as string : ''; const query = params.query ? params.query as string : '';
let pageSize = 250; 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 ( return (
<div className="space-y-4"> <div className="space-y-4">
@ -72,11 +31,9 @@ export default async function UserPage(
<div className="space-y-2"> <div className="space-y-2">
<SearchInput queryParamKey="query" placeholder="Search for identifiers (Email, Username...)"/> <SearchInput queryParamKey="query" placeholder="Search for identifiers (Email, Username...)"/>
<IdentityDataTable <IdentityDataTable
data={initialFetch.data} data={initialData}
pageSize={pageSize} page={page}
pageToken={initialFetch.tokens.get('next')} query={query}/>
query={query}
fetchIdentityPage={fetchIdentityPage}/>
</div> </div>
</div> </div>
); );