mirror of
https://codeberg.org/MarkusThielker/next-ory.git
synced 2025-04-10 11:58:41 +00:00
NORY-34: show oauth2 clients in a data table
This commit is contained in:
parent
502098d8ef
commit
6335036a04
2 changed files with 141 additions and 1 deletions
105
dashboard/src/app/(inside)/client/data-table.tsx
Normal file
105
dashboard/src/app/(inside)/client/data-table.tsx
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { ColumnDef } from '@tanstack/react-table';
|
||||||
|
import { OAuth2Client } from '@ory/client';
|
||||||
|
import { DataTable } from '@/components/ui/data-table';
|
||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
|
import { FetchClientPageProps } from '@/app/(inside)/client/page';
|
||||||
|
|
||||||
|
interface ClientDataTableProps {
|
||||||
|
data: OAuth2Client[];
|
||||||
|
pageSize: number;
|
||||||
|
pageToken: string | undefined;
|
||||||
|
fetchClientPage: (props: FetchClientPageProps) => Promise<{
|
||||||
|
data: OAuth2Client[],
|
||||||
|
tokens: Map<string, string>
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ClientDataTable(
|
||||||
|
{
|
||||||
|
data,
|
||||||
|
pageSize,
|
||||||
|
pageToken,
|
||||||
|
fetchClientPage,
|
||||||
|
}: ClientDataTableProps,
|
||||||
|
) {
|
||||||
|
|
||||||
|
console.log('OAuth2 client', data);
|
||||||
|
|
||||||
|
const columns: ColumnDef<OAuth2Client>[] = [
|
||||||
|
{
|
||||||
|
id: 'client_id',
|
||||||
|
accessorKey: 'client_id',
|
||||||
|
header: 'Client ID',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'client_name',
|
||||||
|
accessorKey: 'client_name',
|
||||||
|
header: 'Client Name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'owner',
|
||||||
|
accessorKey: 'owner',
|
||||||
|
header: 'Owner',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const [items, setItems] = useState<OAuth2Client[]>(data);
|
||||||
|
const [nextToken, setNextToken] = useState<string | undefined>(pageToken);
|
||||||
|
|
||||||
|
// react on changes from ssr (query params)
|
||||||
|
useEffect(() => {
|
||||||
|
setItems(data);
|
||||||
|
setNextToken(pageToken);
|
||||||
|
}, [data, pageSize, pageToken]);
|
||||||
|
|
||||||
|
// 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 fetchClientPage({
|
||||||
|
pageSize: pageSize,
|
||||||
|
pageToken: nextToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
setItems([...items, ...response.data]);
|
||||||
|
setNextToken(response.tokens.get('next') ?? undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DataTable columns={columns} data={items}/>
|
||||||
|
{
|
||||||
|
nextToken && (
|
||||||
|
<div className="flex w-full justify-center">
|
||||||
|
<Spinner ref={infiniteScrollSensor} className="h-10"/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,12 +1,47 @@
|
||||||
|
import { getOAuth2Api } from '@/ory/sdk/server';
|
||||||
|
import { parseTokens } from '@/app/(inside)/user/page';
|
||||||
|
import { ClientDataTable } from '@/app/(inside)/client/data-table';
|
||||||
|
|
||||||
|
export interface FetchClientPageProps {
|
||||||
|
pageSize: number;
|
||||||
|
pageToken: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchClientPage({ pageSize, pageToken }: FetchClientPageProps) {
|
||||||
|
'use server';
|
||||||
|
|
||||||
|
const oAuth2Api = await getOAuth2Api();
|
||||||
|
const response = await oAuth2Api.listOAuth2Clients({
|
||||||
|
pageSize: pageSize,
|
||||||
|
pageToken: pageToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: response.data,
|
||||||
|
tokens: parseTokens(response.headers.link),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default async function ListClientPage() {
|
export default async function ListClientPage() {
|
||||||
|
|
||||||
|
let pageSize = 100;
|
||||||
|
let pageToken: string = '00000000-0000-0000-0000-000000000000';
|
||||||
|
|
||||||
|
const initialFetch = await fetchClientPage({ pageSize, pageToken });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-3xl font-bold leading-tight tracking-tight">OAuth2 Clients</p>
|
<p className="text-3xl font-bold leading-tight tracking-tight">OAuth2 Clients</p>
|
||||||
<p className="text-lg font-light">
|
<p className="text-lg font-light">
|
||||||
Part of milestone v0.2.0
|
See and manage all OAuth2 clients registered with your Ory Hydra instance
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<ClientDataTable
|
||||||
|
data={initialFetch.data}
|
||||||
|
pageSize={pageSize}
|
||||||
|
pageToken={initialFetch.tokens.get('next')}
|
||||||
|
fetchClientPage={fetchClientPage}/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue