Skip to content

Commit 09f7df5

Browse files
committed
feat: add Datasets route with data fetching, pagination, and table columns
1 parent d8d0e99 commit 09f7df5

File tree

4 files changed

+178
-5
lines changed

4 files changed

+178
-5
lines changed

src/modules/datasets/DatasetsPreviewTable.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { PREVIEW_TABLE_LENGTH, PREVIEW_TABLE_REFETCH_INTERVAL } from '@/config';
22
import { execute } from '@/graphql/execute';
33
import { useQuery } from '@tanstack/react-query';
4+
import { Link } from '@tanstack/react-router';
45
import { Box, LoaderCircle, Terminal } from 'lucide-react';
56
import { CircularLoader } from '@/components/CircularLoader';
67
import CopyButton from '@/components/CopyButton';
@@ -41,8 +42,8 @@ export function DatasetsPreviewTable() {
4142
<LoaderCircle className="animate-spin" />
4243
)}
4344
</h2>
44-
<Button variant="link" className="-mr-4">
45-
View all
45+
<Button variant="link" className="-mr-4" asChild>
46+
<Link to="/datasets">View all</Link>
4647
</Button>
4748
</div>
4849
<Table>
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
'use client';
2+
3+
import { DatasetsQuery } from '@/graphql/graphql';
4+
import { ColumnDef } from '@tanstack/react-table';
5+
import CopyButton from '@/components/CopyButton';
6+
import { formatElapsedTime } from '@/utils/formatElapsedTime';
7+
import { truncateAddress } from '@/utils/truncateAddress';
8+
9+
type Dataset = DatasetsQuery['datasets'][number];
10+
11+
export const columns: ColumnDef<Dataset>[] = [
12+
{
13+
accessorKey: 'datasetAddress',
14+
header: 'Dataset',
15+
cell: ({ row }) => {
16+
const datasetAddress = row.original.address;
17+
return (
18+
<CopyButton
19+
displayText={truncateAddress(datasetAddress, {
20+
startLen: 8,
21+
})}
22+
textToCopy={datasetAddress}
23+
/>
24+
);
25+
},
26+
},
27+
{
28+
accessorKey: 'datasetName',
29+
header: 'Name',
30+
cell: ({ row }) => {
31+
const datasetName = row.original.name;
32+
return datasetName ? (
33+
<div className="w-36 overflow-hidden overflow-ellipsis">
34+
{datasetName}
35+
</div>
36+
) : (
37+
<span className="text-muted-foreground">No dataset name</span>
38+
);
39+
},
40+
},
41+
{
42+
accessorKey: 'owner.address',
43+
header: 'Owner',
44+
cell: ({ row }) => {
45+
const ownerAddress = row.original.owner.address;
46+
return (
47+
<CopyButton
48+
displayText={truncateAddress(ownerAddress, {
49+
startLen: 8,
50+
})}
51+
textToCopy={ownerAddress}
52+
/>
53+
);
54+
},
55+
},
56+
{
57+
accessorKey: 'tx_hash',
58+
header: 'TxHash',
59+
cell: ({ row }) => {
60+
const txHash = row.original.transfers[0].transaction.txHash;
61+
return (
62+
<CopyButton
63+
displayText={truncateAddress(txHash, {
64+
startLen: 8,
65+
})}
66+
textToCopy={txHash}
67+
/>
68+
);
69+
},
70+
},
71+
{
72+
accessorKey: 'time',
73+
header: 'Time',
74+
cell: ({ row }) => {
75+
const timestamp = row.original.timestamp;
76+
return <div className="min-w-32">{formatElapsedTime(timestamp)}</div>;
77+
},
78+
},
79+
];

src/routeTree.gen.ts

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { Route as IndexImport } from './routes/index'
1515
import { Route as WorkerpoolsIndexImport } from './routes/workerpools/index'
1616
import { Route as TasksIndexImport } from './routes/tasks/index'
1717
import { Route as DealsIndexImport } from './routes/deals/index'
18+
import { Route as DatasetsIndexImport } from './routes/datasets/index'
1819
import { Route as AppsIndexImport } from './routes/apps/index'
1920

2021
// Create/Update Routes
@@ -43,6 +44,12 @@ const DealsIndexRoute = DealsIndexImport.update({
4344
getParentRoute: () => rootRoute,
4445
} as any)
4546

47+
const DatasetsIndexRoute = DatasetsIndexImport.update({
48+
id: '/datasets/',
49+
path: '/datasets/',
50+
getParentRoute: () => rootRoute,
51+
} as any)
52+
4653
const AppsIndexRoute = AppsIndexImport.update({
4754
id: '/apps/',
4855
path: '/apps/',
@@ -67,6 +74,13 @@ declare module '@tanstack/react-router' {
6774
preLoaderRoute: typeof AppsIndexImport
6875
parentRoute: typeof rootRoute
6976
}
77+
'/datasets/': {
78+
id: '/datasets/'
79+
path: '/datasets'
80+
fullPath: '/datasets'
81+
preLoaderRoute: typeof DatasetsIndexImport
82+
parentRoute: typeof rootRoute
83+
}
7084
'/deals/': {
7185
id: '/deals/'
7286
path: '/deals'
@@ -96,6 +110,7 @@ declare module '@tanstack/react-router' {
96110
export interface FileRoutesByFullPath {
97111
'/': typeof IndexRoute
98112
'/apps': typeof AppsIndexRoute
113+
'/datasets': typeof DatasetsIndexRoute
99114
'/deals': typeof DealsIndexRoute
100115
'/tasks': typeof TasksIndexRoute
101116
'/workerpools': typeof WorkerpoolsIndexRoute
@@ -104,6 +119,7 @@ export interface FileRoutesByFullPath {
104119
export interface FileRoutesByTo {
105120
'/': typeof IndexRoute
106121
'/apps': typeof AppsIndexRoute
122+
'/datasets': typeof DatasetsIndexRoute
107123
'/deals': typeof DealsIndexRoute
108124
'/tasks': typeof TasksIndexRoute
109125
'/workerpools': typeof WorkerpoolsIndexRoute
@@ -113,23 +129,32 @@ export interface FileRoutesById {
113129
__root__: typeof rootRoute
114130
'/': typeof IndexRoute
115131
'/apps/': typeof AppsIndexRoute
132+
'/datasets/': typeof DatasetsIndexRoute
116133
'/deals/': typeof DealsIndexRoute
117134
'/tasks/': typeof TasksIndexRoute
118135
'/workerpools/': typeof WorkerpoolsIndexRoute
119136
}
120137

121138
export interface FileRouteTypes {
122139
fileRoutesByFullPath: FileRoutesByFullPath
123-
fullPaths: '/' | '/apps' | '/deals' | '/tasks' | '/workerpools'
140+
fullPaths: '/' | '/apps' | '/datasets' | '/deals' | '/tasks' | '/workerpools'
124141
fileRoutesByTo: FileRoutesByTo
125-
to: '/' | '/apps' | '/deals' | '/tasks' | '/workerpools'
126-
id: '__root__' | '/' | '/apps/' | '/deals/' | '/tasks/' | '/workerpools/'
142+
to: '/' | '/apps' | '/datasets' | '/deals' | '/tasks' | '/workerpools'
143+
id:
144+
| '__root__'
145+
| '/'
146+
| '/apps/'
147+
| '/datasets/'
148+
| '/deals/'
149+
| '/tasks/'
150+
| '/workerpools/'
127151
fileRoutesById: FileRoutesById
128152
}
129153

130154
export interface RootRouteChildren {
131155
IndexRoute: typeof IndexRoute
132156
AppsIndexRoute: typeof AppsIndexRoute
157+
DatasetsIndexRoute: typeof DatasetsIndexRoute
133158
DealsIndexRoute: typeof DealsIndexRoute
134159
TasksIndexRoute: typeof TasksIndexRoute
135160
WorkerpoolsIndexRoute: typeof WorkerpoolsIndexRoute
@@ -138,6 +163,7 @@ export interface RootRouteChildren {
138163
const rootRouteChildren: RootRouteChildren = {
139164
IndexRoute: IndexRoute,
140165
AppsIndexRoute: AppsIndexRoute,
166+
DatasetsIndexRoute: DatasetsIndexRoute,
141167
DealsIndexRoute: DealsIndexRoute,
142168
TasksIndexRoute: TasksIndexRoute,
143169
WorkerpoolsIndexRoute: WorkerpoolsIndexRoute,
@@ -155,6 +181,7 @@ export const routeTree = rootRoute
155181
"children": [
156182
"/",
157183
"/apps/",
184+
"/datasets/",
158185
"/deals/",
159186
"/tasks/",
160187
"/workerpools/"
@@ -166,6 +193,9 @@ export const routeTree = rootRoute
166193
"/apps/": {
167194
"filePath": "apps/index.tsx"
168195
},
196+
"/datasets/": {
197+
"filePath": "datasets/index.tsx"
198+
},
169199
"/deals/": {
170200
"filePath": "deals/index.tsx"
171201
},

src/routes/datasets/index.tsx

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { TABLE_LENGTH, TABLE_REFETCH_INTERVAL } from '@/config';
2+
import { execute } from '@/graphql/execute';
3+
import { keepPreviousData, useQuery } from '@tanstack/react-query';
4+
import { createFileRoute } from '@tanstack/react-router';
5+
import { Box, LoaderCircle } from 'lucide-react';
6+
import { useState } from 'react';
7+
import { PaginatedNavigation } from '@/components/PaginatedNavigation.tsx';
8+
import { DataTable } from '@/components/data-table';
9+
import { SearcherBar } from '@/modules/SearcherBar';
10+
import { datasetsQuery } from '@/modules/datasets/datasetsQuery';
11+
import { columns } from '@/modules/datasets/datasetsTable/columns';
12+
13+
export const Route = createFileRoute('/datasets/')({
14+
component: DatasetsRoute,
15+
});
16+
17+
function useDatasetsData(currentPage: number) {
18+
const skip = currentPage * TABLE_LENGTH;
19+
20+
const { data, isFetching, isPending, isError } = useQuery({
21+
queryKey: ['datasets', currentPage],
22+
queryFn: () => execute(datasetsQuery, { length: TABLE_LENGTH, skip }),
23+
refetchInterval: TABLE_REFETCH_INTERVAL,
24+
placeholderData: keepPreviousData,
25+
});
26+
27+
const formattedData =
28+
data?.datasets.map((dataset) => ({
29+
...dataset,
30+
destination: `/datasets/${dataset.address}`,
31+
})) ?? [];
32+
33+
return { data: formattedData, isFetching, isPending, isError };
34+
}
35+
36+
function DatasetsRoute() {
37+
const [currentPage, setCurrentPage] = useState(0);
38+
const { data, isFetching, isPending, isError } = useDatasetsData(currentPage);
39+
40+
return (
41+
<div className="mt-8 grid gap-6">
42+
<SearcherBar className="py-16" />
43+
44+
<h1 className="flex items-center gap-2 font-sans text-2xl font-extrabold">
45+
<Box size="20" />
46+
Datasets
47+
{data.length > 0 && isError && (
48+
<span className="text-muted-foreground text-sm font-light">
49+
(outdated)
50+
</span>
51+
)}
52+
{isFetching && isPending && <LoaderCircle className="animate-spin" />}
53+
</h1>
54+
55+
<DataTable columns={columns} data={data} />
56+
<PaginatedNavigation
57+
currentPage={currentPage}
58+
totalPages={currentPage + 2}
59+
onPageChange={setCurrentPage}
60+
/>
61+
</div>
62+
);
63+
}

0 commit comments

Comments
 (0)