mirror of
https://codeberg.org/MarkusThielker/next-ory.git
synced 2025-04-19 00:51:18 +00:00
NORY-15: implement infinite scroll for identities
This commit is contained in:
parent
2eb4b80607
commit
6db1b441f0
3 changed files with 64 additions and 6 deletions
|
@ -5,8 +5,9 @@ import { Identity } from '@ory/client';
|
||||||
import { DataTable } from '@/components/ui/data-table';
|
import { DataTable } from '@/components/ui/data-table';
|
||||||
import { CircleCheck, CircleX } from 'lucide-react';
|
import { CircleCheck, CircleX } from 'lucide-react';
|
||||||
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card';
|
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card';
|
||||||
import { useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { FetchIdentityPageProps } from '@/app/user/page';
|
import { FetchIdentityPageProps } from '@/app/user/page';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
|
|
||||||
interface IdentityDataTableProps {
|
interface IdentityDataTableProps {
|
||||||
data: Identity[];
|
data: Identity[];
|
||||||
|
@ -89,8 +90,12 @@ 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);
|
const [nextToken, setNextToken] = useState<string | undefined>(pageToken);
|
||||||
|
|
||||||
const fetchMore = async () => {
|
useEffect(() => {
|
||||||
|
setItems(data);
|
||||||
|
setNextToken(pageToken);
|
||||||
|
}, [data, pageSize, pageToken, query]);
|
||||||
|
|
||||||
|
const fetchMore = async () => {
|
||||||
if (!nextToken) return;
|
if (!nextToken) return;
|
||||||
|
|
||||||
const response = await fetchIdentityPage({
|
const response = await fetchIdentityPage({
|
||||||
|
@ -103,6 +108,39 @@ export function IdentityDataTable({ data, pageSize, pageToken, query, fetchIdent
|
||||||
setNextToken(response.tokens.get('next') ?? undefined);
|
setNextToken(response.tokens.get('next') ?? undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: fetch more when scrolling to the end of list
|
const infiniteScrollSensor = useRef(null);
|
||||||
return <DataTable columns={columns} data={items}/>;
|
|
||||||
|
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]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DataTable columns={columns} data={items}/>
|
||||||
|
{
|
||||||
|
nextToken && (
|
||||||
|
<div className="flex w-full justify-center">
|
||||||
|
<Spinner ref={infiniteScrollSensor} className="h-10"/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,10 +56,10 @@ export default async function UserPage(
|
||||||
const params = await searchParams;
|
const params = await searchParams;
|
||||||
const query = params.query ? params.query as string : '';
|
const query = params.query ? params.query as string : '';
|
||||||
|
|
||||||
let pageSize = 200;
|
let pageSize = 250;
|
||||||
let pageToken: string = '00000000-0000-0000-0000-000000000000';
|
let pageToken: string = '00000000-0000-0000-0000-000000000000';
|
||||||
|
|
||||||
const initialFetch = await fetchIdentityPage({ pageSize, pageToken, query: '' });
|
const initialFetch = await fetchIdentityPage({ pageSize, pageToken, query: query });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
|
20
dashboard/src/components/ui/spinner.tsx
Normal file
20
dashboard/src/components/ui/spinner.tsx
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import { RefObject } from 'react';
|
||||||
|
|
||||||
|
export const Spinner = (
|
||||||
|
{ className, ref }: { className?: string, ref: RefObject<any> },
|
||||||
|
) => <svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
className={cn('animate-spin', className)}
|
||||||
|
ref={ref}
|
||||||
|
>
|
||||||
|
<path d="M21 12a9 9 0 1 1-6.219-8.56"/>
|
||||||
|
</svg>;
|
Loading…
Add table
Reference in a new issue