Skip to content

Commit 12b5b4a

Browse files
committed
feat: implement DatasetTypes component and integrate schema handling in DatasetsPreviewTable
1 parent ac399d3 commit 12b5b4a

File tree

8 files changed

+257
-63
lines changed

8 files changed

+257
-63
lines changed

src/components/DatasetTypes.tsx

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { processSchemaPathsToTypes } from '@/modules/datasets/utils/datasetTypeUtils';
2+
3+
interface DatasetTypesProps {
4+
schemaPaths?: Array<{ path?: string | null }>;
5+
isLoading?: boolean;
6+
layout?: 'horizontal' | 'vertical';
7+
maxDisplay?: number;
8+
showMoreCount?: boolean;
9+
}
10+
11+
export function DatasetTypes({
12+
schemaPaths,
13+
isLoading,
14+
layout = 'horizontal',
15+
maxDisplay,
16+
showMoreCount = false,
17+
}: DatasetTypesProps) {
18+
const displayTypes = processSchemaPathsToTypes(schemaPaths);
19+
20+
if (isLoading) {
21+
const skeletonCount = 5;
22+
23+
return (
24+
<div
25+
className={`flex ${layout === 'vertical' ? 'flex-col' : 'flex-wrap'} gap-1`}
26+
>
27+
{Array.from({ length: skeletonCount }, (_, i) => (
28+
<div
29+
key={i}
30+
className="bg-muted w-40 flex-shrink-0 animate-pulse rounded-lg border p-3"
31+
>
32+
<div className="flex-1">
33+
<div className="bg-muted-foreground/20 mb-2 h-4 rounded"></div>
34+
<div className="bg-muted-foreground/10 h-3 w-3/4 rounded"></div>
35+
</div>
36+
</div>
37+
))}
38+
</div>
39+
);
40+
}
41+
42+
if (!schemaPaths || schemaPaths.length === 0 || displayTypes.length === 0) {
43+
return <span className="text-muted-foreground text-sm">No type</span>;
44+
}
45+
46+
const typesToShow = maxDisplay
47+
? displayTypes.slice(0, maxDisplay)
48+
: displayTypes;
49+
const remainingCount =
50+
maxDisplay && displayTypes.length > maxDisplay
51+
? displayTypes.length - maxDisplay
52+
: 0;
53+
54+
return (
55+
<div
56+
className={`flex ${layout === 'vertical' ? 'flex-col' : 'flex-wrap'} gap-1 ${layout === 'vertical' ? 'max-w-40' : ''}`}
57+
>
58+
{typesToShow.map((type) => (
59+
<span
60+
key={type.name}
61+
className="inline-block w-fit cursor-pointer rounded-lg border px-3 py-2 text-sm font-medium whitespace-nowrap transition-transform duration-200 hover:scale-105"
62+
style={type.style}
63+
title={type.name}
64+
>
65+
{type.name}
66+
</span>
67+
))}
68+
{showMoreCount && remainingCount > 0 && (
69+
<span
70+
className="text-muted-foreground py-1 text-xs"
71+
title={`+${remainingCount} more types`}
72+
>
73+
+{remainingCount} more
74+
</span>
75+
)}
76+
</div>
77+
);
78+
}

src/modules/datasets/DatasetsPreviewTable.tsx

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { createPlaceholderDataFnForQueryKey } from '@/utils/createPlaceholderDat
1212
import { ErrorAlert } from '../ErrorAlert';
1313
import { datasetsQuery } from './datasetsQuery';
1414
import { columns } from './datasetsTable/columns';
15+
import { useDatasetsSchemas } from './hooks/useDatasetsSchemas';
1516

1617
export function DatasetsPreviewTable({ className }: { className?: string }) {
1718
const { chainId } = useUserStore();
@@ -29,10 +30,21 @@ export function DatasetsPreviewTable({ className }: { className?: string }) {
2930
placeholderData: createPlaceholderDataFnForQueryKey(queryKey),
3031
});
3132

33+
const datasetsArray = datasets.data?.datasets ?? [];
34+
35+
// Get schema data for each dataset using optimized hook
36+
const datasetAddresses = datasetsArray.map((dataset) => dataset.address);
37+
const { schemasMap, isLoading: isSchemasLoading } = useDatasetsSchemas(
38+
datasetAddresses,
39+
chainId!
40+
);
41+
3242
const formattedData =
33-
datasets.data?.datasets.map((dataset) => ({
43+
datasetsArray.map((dataset) => ({
3444
...dataset,
3545
destination: `/dataset/${dataset.address}`,
46+
schemaPaths: schemasMap.get(dataset.address) || [],
47+
isSchemaLoading: isSchemasLoading,
3648
})) ?? [];
3749

3850
return (
@@ -60,12 +72,14 @@ export function DatasetsPreviewTable({ className }: { className?: string }) {
6072
{(datasets.isError || datasets.errorUpdateCount > 0) && !datasets.data ? (
6173
<ErrorAlert message="A error occurred during datasets loading." />
6274
) : (
63-
<DataTable
64-
columns={columns}
65-
data={formattedData}
66-
tableLength={PREVIEW_TABLE_LENGTH}
67-
isLoading={datasets.isLoading || datasets.isRefetching}
68-
/>
75+
<div className="h-[400px] overflow-auto">
76+
<DataTable
77+
columns={columns}
78+
data={formattedData}
79+
tableLength={PREVIEW_TABLE_LENGTH}
80+
isLoading={datasets.isLoading || datasets.isRefetching}
81+
/>
82+
</div>
6983
)}
7084
</div>
7185
);

src/modules/datasets/dataset/DatasetTypes.tsx

Lines changed: 0 additions & 51 deletions
This file was deleted.

src/modules/datasets/dataset/buildDatasetDetails.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { DatasetQuery } from '@/graphql/graphql';
22
import CopyButton from '@/components/CopyButton';
3+
import { DatasetTypes } from '@/components/DatasetTypes';
34
import SmartLinkGroup from '@/components/SmartLinkGroup';
45
import TransferEvent from '@/modules/events/TransferEvent';
56
import { multiaddrHexToHuman } from '@/utils/format';
6-
import { formatDateCompact, formatElapsedTime } from '@/utils/formatElapsedTime';
7-
import { DatasetTypes } from './DatasetTypes';
7+
import {
8+
formatDateCompact,
9+
formatElapsedTime,
10+
} from '@/utils/formatElapsedTime';
811

912
export function buildDatasetDetails({
1013
dataset,
@@ -44,7 +47,11 @@ export function buildDatasetDetails({
4447
),
4548
}),
4649
Types: (
47-
<DatasetTypes schemaPaths={schemaPaths} isLoading={isSchemaLoading} />
50+
<DatasetTypes
51+
schemaPaths={schemaPaths}
52+
isLoading={isSchemaLoading}
53+
layout="horizontal"
54+
/>
4855
),
4956
...(firstTimestamp && {
5057
Created: (

src/modules/datasets/dataset/protectedDataQuery.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,16 @@ export const datasetSchemaQuery = graphql(`
88
}
99
}
1010
}
11-
`);
11+
`);
12+
13+
// Query optimisée en string pour utiliser avec executeDataprotector
14+
export const optimizedProtectedDatasQueryString = graphql(`
15+
query OptimizedProtectedDatasWithSchemas($datasetIds: [Bytes!]!) {
16+
protectedDatas(where: { id_in: $datasetIds }) {
17+
id
18+
schema {
19+
path
20+
}
21+
}
22+
}
23+
`);

src/modules/datasets/datasetsTable/columns.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import { DatasetsQuery } from '@/graphql/graphql';
22
import { ColumnDef } from '@tanstack/react-table';
33
import CopyButton from '@/components/CopyButton';
4+
import { DatasetTypes } from '@/components/DatasetTypes';
45
import { formatElapsedTime } from '@/utils/formatElapsedTime';
56
import { truncateAddress } from '@/utils/truncateAddress';
67

7-
type Dataset = DatasetsQuery['datasets'][number];
8+
type Dataset = DatasetsQuery['datasets'][number] & {
9+
schemaPaths?: Array<{ path?: string | null }>;
10+
isSchemaLoading?: boolean;
11+
};
812

913
export const columns: ColumnDef<Dataset>[] = [
1014
{
@@ -40,6 +44,23 @@ export const columns: ColumnDef<Dataset>[] = [
4044
);
4145
},
4246
},
47+
{
48+
accessorKey: 'type',
49+
header: 'Type',
50+
cell: ({ row }) => {
51+
const schemaPaths = row.original.schemaPaths;
52+
const isSchemaLoading = row.original.isSchemaLoading;
53+
return (
54+
<DatasetTypes
55+
schemaPaths={schemaPaths}
56+
isLoading={isSchemaLoading}
57+
layout="vertical"
58+
maxDisplay={3}
59+
showMoreCount={true}
60+
/>
61+
);
62+
},
63+
},
4364
{
4465
accessorKey: 'owner.address',
4566
header: 'Owner',
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { executeDataprotector } from '@/graphql/executeDataprotector';
2+
import { useQuery } from '@tanstack/react-query';
3+
import { createPlaceholderDataFnForQueryKey } from '@/utils/createPlaceholderDataFnForQueryKey';
4+
import { datasetSchemaQuery, optimizedProtectedDatasQueryString } from '@/modules/datasets/dataset/protectedDataQuery';
5+
6+
export function useDatasetsSchemas(datasetAddresses: string[], chainId: number) {
7+
const queryKey = [chainId, 'datasetsSchemas', datasetAddresses.sort()];
8+
9+
const { data, isLoading, isError } = useQuery({
10+
queryKey,
11+
enabled: !!chainId && datasetAddresses.length > 0,
12+
queryFn: async () => {
13+
try {
14+
// Utilisation directe d'executeDataprotector avec la query optimisée
15+
// Plus efficace qu'un Promise.all de n requêtes individuelles
16+
const result = await executeDataprotector(
17+
optimizedProtectedDatasQueryString,
18+
chainId,
19+
{ datasetIds: datasetAddresses }
20+
);
21+
22+
// Transform data into a map for easy lookup
23+
const schemasMap = new Map<string, Array<{ path?: string | null }>>();
24+
25+
if (result?.protectedDatas) {
26+
result.protectedDatas.forEach((protectedData: any) => {
27+
if (protectedData.id) {
28+
schemasMap.set(protectedData.id, protectedData.schema || []);
29+
}
30+
});
31+
}
32+
33+
// Ensure all requested datasets have an entry (empty array if no schema)
34+
datasetAddresses.forEach(address => {
35+
if (!schemasMap.has(address)) {
36+
schemasMap.set(address, []);
37+
}
38+
});
39+
40+
return schemasMap;
41+
} catch (error) {
42+
console.warn('Failed to fetch schemas with optimized query, falling back to individual queries:', error);
43+
44+
// Fallback: Use individual queries in parallel if the optimized query fails
45+
const schemaPromises = datasetAddresses.map(async (address) => {
46+
try {
47+
const result = await executeDataprotector(datasetSchemaQuery, chainId, {
48+
datasetAddress: address,
49+
});
50+
return { address, schema: result?.protectedData?.schema || [] };
51+
} catch {
52+
return { address, schema: [] };
53+
}
54+
});
55+
56+
const results = await Promise.allSettled(schemaPromises);
57+
const schemasMap = new Map();
58+
59+
results.forEach((result, index) => {
60+
if (result.status === 'fulfilled') {
61+
schemasMap.set(datasetAddresses[index], result.value.schema);
62+
} else {
63+
schemasMap.set(datasetAddresses[index], []);
64+
}
65+
});
66+
67+
return schemasMap;
68+
}
69+
},
70+
placeholderData: createPlaceholderDataFnForQueryKey(queryKey),
71+
// Cache longer since schemas rarely change
72+
staleTime: 5 * 60 * 1000, // 5 minutes
73+
});
74+
75+
return {
76+
schemasMap: data || new Map(),
77+
isLoading,
78+
isError,
79+
};
80+
}
81+
82+
export function useDatasetSchema(datasetAddress: string, chainId: number) {
83+
const queryKey = [chainId, 'datasetSchema', datasetAddress];
84+
85+
const { data, isLoading, isError } = useQuery({
86+
queryKey,
87+
enabled: !!chainId && !!datasetAddress,
88+
queryFn: async () => {
89+
const result = await executeDataprotector(datasetSchemaQuery, chainId, {
90+
datasetAddress,
91+
});
92+
return result;
93+
},
94+
placeholderData: createPlaceholderDataFnForQueryKey(queryKey),
95+
});
96+
97+
return {
98+
schema: data?.protectedData?.schema || [],
99+
isLoading,
100+
isError,
101+
};
102+
}

0 commit comments

Comments
 (0)