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:
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 { 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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Add table
Reference in a new issue