@vendure/dashboard 3.5.2-master-202512170238 → 3.5.2-master-202512190240

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/plugin/constants.js +21 -2
  2. package/dist/plugin/dashboard.plugin.js +1 -1
  3. package/package.json +3 -3
  4. package/src/app/routeTree.gen.ts +1135 -1072
  5. package/src/app/routes/_authenticated/_collections/collections.graphql.ts +1 -0
  6. package/src/app/routes/_authenticated/_collections/collections.tsx +249 -167
  7. package/src/app/routes/_authenticated/_collections/components/collection-bulk-actions.tsx +8 -0
  8. package/src/app/routes/_authenticated/_collections/components/move-collections-dialog.tsx +4 -0
  9. package/src/app/routes/_authenticated/_facets/components/facet-values-sheet.tsx +4 -1
  10. package/src/app/routes/_authenticated/_facets/components/facet-values-table.tsx +1 -1
  11. package/src/app/routes/_authenticated/_facets/facets.tsx +22 -38
  12. package/src/app/routes/_authenticated/_orders/components/order-table-totals.tsx +16 -1
  13. package/src/app/routes/_authenticated/_product-variants/product-variants.graphql.ts +0 -1
  14. package/src/app/routes/_authenticated/_product-variants/product-variants_.$id.tsx +2 -2
  15. package/src/app/routes/_authenticated/_products/products.graphql.ts +5 -0
  16. package/src/app/routes/_authenticated/_products/products_.$id.tsx +24 -1
  17. package/src/app/routes/_authenticated/_system/components/payload-dialog.tsx +9 -2
  18. package/src/app/routes/_authenticated/_system/job-queue.tsx +11 -2
  19. package/src/app/routes/_authenticated/_tax-rates/tax-rates_.$id.tsx +2 -9
  20. package/src/app/routes/_authenticated/_zones/zones.tsx +1 -0
  21. package/src/i18n/locales/ar.po +177 -141
  22. package/src/i18n/locales/cs.po +177 -141
  23. package/src/i18n/locales/de.po +177 -141
  24. package/src/i18n/locales/en.po +177 -141
  25. package/src/i18n/locales/es.po +177 -141
  26. package/src/i18n/locales/fa.po +177 -141
  27. package/src/i18n/locales/fr.po +177 -141
  28. package/src/i18n/locales/he.po +177 -141
  29. package/src/i18n/locales/hr.po +177 -141
  30. package/src/i18n/locales/it.po +177 -141
  31. package/src/i18n/locales/ja.po +177 -141
  32. package/src/i18n/locales/nb.po +177 -141
  33. package/src/i18n/locales/ne.po +177 -141
  34. package/src/i18n/locales/pl.po +177 -141
  35. package/src/i18n/locales/pt_BR.po +177 -141
  36. package/src/i18n/locales/pt_PT.po +177 -141
  37. package/src/i18n/locales/ru.po +177 -141
  38. package/src/i18n/locales/sv.po +177 -141
  39. package/src/i18n/locales/tr.po +177 -141
  40. package/src/i18n/locales/uk.po +177 -141
  41. package/src/i18n/locales/zh_Hans.po +177 -141
  42. package/src/i18n/locales/zh_Hant.po +177 -141
  43. package/src/lib/components/data-input/number-input.tsx +24 -5
  44. package/src/lib/components/data-table/data-table-context.tsx +18 -3
  45. package/src/lib/components/data-table/data-table-utils.ts +241 -1
  46. package/src/lib/components/data-table/data-table.tsx +189 -60
  47. package/src/lib/components/data-table/global-views-bar.tsx +1 -1
  48. package/src/lib/components/data-table/save-view-dialog.tsx +21 -0
  49. package/src/lib/components/data-table/use-generated-columns.tsx +56 -24
  50. package/src/lib/components/data-table/views-sheet.tsx +1 -1
  51. package/src/lib/components/layout/channel-switcher.tsx +7 -5
  52. package/src/lib/components/layout/nav-main.tsx +2 -2
  53. package/src/lib/components/shared/alerts.tsx +3 -1
  54. package/src/lib/components/shared/assign-to-channel-bulk-action.tsx +10 -10
  55. package/src/lib/components/shared/assign-to-channel-dialog.tsx +1 -1
  56. package/src/lib/components/shared/assigned-channels.tsx +108 -0
  57. package/src/lib/components/shared/assigned-facet-values.tsx +5 -7
  58. package/src/lib/components/shared/channel-chip.tsx +43 -0
  59. package/src/lib/components/shared/paginated-list-data-table.tsx +19 -0
  60. package/src/lib/components/ui/alert.tsx +1 -1
  61. package/src/lib/components/ui/dropdown-menu.tsx +4 -1
  62. package/src/lib/components/ui/sidebar.tsx +2 -1
  63. package/src/lib/framework/page/list-page.tsx +62 -38
  64. package/src/lib/hooks/use-drag-and-drop.ts +86 -0
  65. package/src/lib/hooks/use-saved-views.ts +1 -0
  66. package/src/lib/providers/channel-provider.tsx +7 -1
  67. package/src/lib/types/saved-views.ts +3 -0
@@ -163,6 +163,7 @@ export const moveCollectionDocument = graphql(`
163
163
  mutation MoveCollection($input: MoveCollectionInput!) {
164
164
  moveCollection(input: $input) {
165
165
  id
166
+ position
166
167
  }
167
168
  }
168
169
  `);
@@ -4,18 +4,25 @@ import { Button } from '@/vdb/components/ui/button.js';
4
4
  import { PageActionBarRight } from '@/vdb/framework/layout-engine/page-layout.js';
5
5
  import { ListPage } from '@/vdb/framework/page/list-page.js';
6
6
  import { api } from '@/vdb/graphql/api.js';
7
- import { Trans } from '@lingui/react/macro';
8
- import { FetchQueryOptions, useQueries } from '@tanstack/react-query';
7
+ import { Trans, useLingui } from '@lingui/react/macro';
8
+ import { FetchQueryOptions, useQueries, useQueryClient } from '@tanstack/react-query';
9
9
  import { createFileRoute, Link } from '@tanstack/react-router';
10
10
  import { ExpandedState, getExpandedRowModel } from '@tanstack/react-table';
11
11
  import { TableOptions } from '@tanstack/table-core';
12
12
  import { ResultOf } from 'gql.tada';
13
13
  import { Folder, FolderOpen, PlusIcon } from 'lucide-react';
14
14
  import { useState } from 'react';
15
+ import { toast } from 'sonner';
15
16
 
16
17
  import { RichTextDescriptionCell } from '@/vdb/components/shared/table-cell/order-table-cell-components.js';
17
18
  import { Badge } from '@/vdb/components/ui/badge.js';
18
- import { collectionListDocument } from './collections.graphql.js';
19
+ import {
20
+ calculateDragTargetPosition,
21
+ calculateSiblingIndex,
22
+ getItemParentId,
23
+ isCircularReference,
24
+ } from '@/vdb/components/data-table/data-table-utils.js';
25
+ import { collectionListDocument, moveCollectionDocument } from './collections.graphql.js';
19
26
  import {
20
27
  AssignCollectionsToChannelBulkAction,
21
28
  DeleteCollectionsBulkAction,
@@ -25,15 +32,21 @@ import {
25
32
  } from './components/collection-bulk-actions.js';
26
33
  import { CollectionContentsSheet } from './components/collection-contents-sheet.js';
27
34
 
35
+
28
36
  export const Route = createFileRoute('/_authenticated/_collections/collections')({
29
37
  component: CollectionListPage,
30
38
  loader: () => ({ breadcrumb: () => <Trans>Collections</Trans> }),
31
39
  });
32
40
 
41
+
33
42
  type Collection = ResultOf<typeof collectionListDocument>['collections']['items'][number];
34
43
 
35
44
  function CollectionListPage() {
45
+ const { t } = useLingui();
46
+ const queryClient = useQueryClient();
36
47
  const [expanded, setExpanded] = useState<ExpandedState>({});
48
+ const [searchTerm, setSearchTerm] = useState<string>('');
49
+
37
50
  const childrenQueries = useQueries({
38
51
  queries: Object.entries(expanded).map(([collectionId, isExpanded]) => {
39
52
  return {
@@ -50,6 +63,7 @@ function CollectionListPage() {
50
63
  } satisfies FetchQueryOptions;
51
64
  }),
52
65
  });
66
+
53
67
  const childCollectionsByParentId = childrenQueries.reduce(
54
68
  (acc, query, index) => {
55
69
  const collectionId = Object.keys(expanded)[index];
@@ -79,177 +93,245 @@ function CollectionListPage() {
79
93
  return allRows;
80
94
  };
81
95
 
96
+ const handleReorder = async (oldIndex: number, newIndex: number, item: Collection, allItems?: Collection[]) => {
97
+ try {
98
+ const items = allItems || [];
99
+ const sourceParentId = getItemParentId(item);
100
+
101
+ if (!sourceParentId) {
102
+ throw new Error('Unable to determine parent collection ID');
103
+ }
104
+
105
+ // Calculate target position (parent and index)
106
+ const { targetParentId, adjustedIndex: initialIndex } = calculateDragTargetPosition({
107
+ item,
108
+ oldIndex,
109
+ newIndex,
110
+ items,
111
+ sourceParentId,
112
+ expanded,
113
+ });
114
+
115
+ // Validate no circular references when moving to different parent
116
+ if (targetParentId !== sourceParentId && isCircularReference(item, targetParentId, items)) {
117
+ toast.error(t`Cannot move a collection into its own descendant`);
118
+ throw new Error('Circular reference detected');
119
+ }
120
+
121
+ // Calculate final index (adjust for same-parent moves)
122
+ const adjustedIndex = targetParentId === sourceParentId
123
+ ? calculateSiblingIndex({ item, oldIndex, newIndex, items, parentId: sourceParentId })
124
+ : initialIndex;
125
+
126
+ // Perform the move
127
+ await api.mutate(moveCollectionDocument, {
128
+ input: {
129
+ collectionId: item.id,
130
+ parentId: targetParentId,
131
+ index: adjustedIndex,
132
+ },
133
+ });
134
+
135
+ // Invalidate queries and show success message
136
+ const queriesToInvalidate = [
137
+ queryClient.invalidateQueries({ queryKey: ['childCollections', sourceParentId] }),
138
+ queryClient.invalidateQueries({ queryKey: ['PaginatedListDataTable'] }),
139
+ ];
140
+
141
+ if (targetParentId === sourceParentId) {
142
+ await Promise.all(queriesToInvalidate);
143
+ toast.success(t`Collection position updated`);
144
+ } else {
145
+ queriesToInvalidate.push(
146
+ queryClient.invalidateQueries({ queryKey: ['childCollections', targetParentId] })
147
+ );
148
+ await Promise.all(queriesToInvalidate);
149
+ toast.success(t`Collection moved to new parent`);
150
+ }
151
+ } catch (error) {
152
+ console.error('Failed to reorder collection:', error);
153
+ if (error instanceof Error && error.message !== 'Circular reference detected') {
154
+ toast.error(t`Failed to update collection position`);
155
+ }
156
+ throw error;
157
+ }
158
+ };
159
+
82
160
  return (
83
- <>
84
- <ListPage
85
- pageId="collection-list"
86
- title={<Trans>Collections</Trans>}
87
- listQuery={collectionListDocument}
88
- transformVariables={input => {
89
- const filterTerm = input.options?.filter?.name?.contains;
90
- const isFiltering = !!filterTerm;
91
- return {
92
- options: {
93
- ...input.options,
94
- topLevelOnly: !isFiltering,
95
- },
96
- };
97
- }}
98
- customizeColumns={{
99
- name: {
100
- meta: {
101
- // This column needs the following fields to always be available
102
- // in order to correctly render.
103
- dependencies: ['children', 'breadcrumbs'],
104
- },
105
- cell: ({ row }) => {
106
- const isExpanded = row.getIsExpanded();
107
- const hasChildren = !!row.original.children?.length;
108
- return (
109
- <div
110
- style={{ marginLeft: (row.original.breadcrumbs?.length - 2) * 20 + 'px' }}
111
- className="flex gap-2 items-center"
112
- >
113
- <Button
114
- size="icon"
115
- variant="secondary"
116
- onClick={row.getToggleExpandedHandler()}
117
- disabled={!hasChildren}
118
- className={!hasChildren ? 'opacity-20' : ''}
119
- >
120
- {isExpanded ? <FolderOpen /> : <Folder />}
121
- </Button>
122
- <DetailPageButton id={row.original.id} label={row.original.name} />
123
- </div>
124
- );
125
- },
161
+ <ListPage
162
+ pageId="collection-list"
163
+ title={<Trans>Collections</Trans>}
164
+ listQuery={collectionListDocument}
165
+ transformVariables={input => {
166
+ const filterTerm = input.options?.filter?.name?.contains;
167
+ const isFiltering = !!filterTerm;
168
+ return {
169
+ options: {
170
+ ...input.options,
171
+ topLevelOnly: !isFiltering,
126
172
  },
127
- description: {
128
- cell: RichTextDescriptionCell,
173
+ };
174
+ }}
175
+ customizeColumns={{
176
+ name: {
177
+ meta: {
178
+ dependencies: ['children', 'breadcrumbs'],
129
179
  },
130
- breadcrumbs: {
131
- cell: ({ cell }) => {
132
- const value = cell.getValue();
133
- if (!Array.isArray(value)) {
134
- return null;
135
- }
136
- return (
137
- <div>
138
- {value
139
- .slice(1)
140
- .map(breadcrumb => breadcrumb.name)
141
- .join(' / ')}
142
- </div>
143
- );
144
- },
145
- },
146
- productVariants: {
147
- header: () => <Trans>Contents</Trans>,
148
- cell: ({ row }) => {
149
- return (
150
- <CollectionContentsSheet
151
- collectionId={row.original.id}
152
- collectionName={row.original.name}
180
+ cell: ({ row }) => {
181
+ const isExpanded = row.getIsExpanded();
182
+ const hasChildren = !!row.original.children?.length;
183
+ return (
184
+ <div
185
+ style={{ marginLeft: (row.original.breadcrumbs?.length - 2) * 20 + 'px' }}
186
+ className="flex gap-2 items-center"
187
+ >
188
+ <Button
189
+ size="icon"
190
+ variant="secondary"
191
+ onClick={row.getToggleExpandedHandler()}
192
+ disabled={!hasChildren}
193
+ className={!hasChildren ? 'opacity-20' : ''}
153
194
  >
154
- <Trans>{row.original.productVariants?.totalItems} variants</Trans>
155
- </CollectionContentsSheet>
156
- );
157
- },
158
- },
159
- children: {
160
- cell: ({ row }) => {
161
- const children = row.original.children ?? [];
162
- const count = children.length;
163
- const maxDisplay = 5;
164
- const leftOver = Math.max(count - maxDisplay, 0);
165
- return (
166
- <div className="flex flex-wrap gap-2">
167
- {children.slice(0, maxDisplay).map(child => (
168
- <Badge variant="outline">{child.name}</Badge>
169
- ))}
170
- {leftOver > 0 ? (
171
- <Badge variant="outline">
172
- <Trans>+ {leftOver} more</Trans>
173
- </Badge>
174
- ) : null}
175
- </div>
176
- );
177
- },
195
+ {isExpanded ? <FolderOpen /> : <Folder />}
196
+ </Button>
197
+ <DetailPageButton id={row.original.id} label={row.original.name} />
198
+ </div>
199
+ );
178
200
  },
179
- }}
180
- defaultColumnOrder={[
181
- 'featuredAsset',
182
- 'children',
183
- 'name',
184
- 'slug',
185
- 'breadcrumbs',
186
- 'productVariants',
187
- ]}
188
- transformData={data => {
189
- return addSubCollections(data);
190
- }}
191
- setTableOptions={(options: TableOptions<any>) => {
192
- options.state = {
193
- ...options.state,
194
- expanded: expanded,
195
- };
196
- options.onExpandedChange = setExpanded;
197
- options.getExpandedRowModel = getExpandedRowModel();
198
- options.getRowCanExpand = () => true;
199
- options.getRowId = row => {
200
- return row.id;
201
- };
202
- return options;
203
- }}
204
- defaultVisibility={{
205
- id: false,
206
- createdAt: false,
207
- updatedAt: false,
208
- position: false,
209
- parentId: false,
210
- children: false,
211
- description: false,
212
- }}
213
- onSearchTermChange={searchTerm => {
214
- return {
215
- name: { contains: searchTerm },
216
- };
217
- }}
218
- route={Route}
219
- bulkActions={[
220
- {
221
- component: AssignCollectionsToChannelBulkAction,
222
- order: 100,
201
+ },
202
+ description: {
203
+ cell: RichTextDescriptionCell,
204
+ },
205
+ breadcrumbs: {
206
+ cell: ({ cell }) => {
207
+ const value = cell.getValue();
208
+ if (!Array.isArray(value)) {
209
+ return null;
210
+ }
211
+ return (
212
+ <div>
213
+ {value
214
+ .slice(1)
215
+ .map(breadcrumb => breadcrumb.name)
216
+ .join(' / ')}
217
+ </div>
218
+ );
223
219
  },
224
- {
225
- component: RemoveCollectionsFromChannelBulkAction,
226
- order: 200,
220
+ },
221
+ productVariants: {
222
+ header: () => <Trans>Contents</Trans>,
223
+ cell: ({ row }) => {
224
+ return (
225
+ <CollectionContentsSheet
226
+ collectionId={row.original.id}
227
+ collectionName={row.original.name}
228
+ >
229
+ <Trans>{row.original.productVariants?.totalItems} variants</Trans>
230
+ </CollectionContentsSheet>
231
+ );
227
232
  },
228
- {
229
- component: DuplicateCollectionsBulkAction,
230
- order: 300,
233
+ },
234
+ children: {
235
+ cell: ({ row }) => {
236
+ const children = row.original.children ?? [];
237
+ const count = children.length;
238
+ const maxDisplay = 5;
239
+ const leftOver = Math.max(count - maxDisplay, 0);
240
+ return (
241
+ <div className="flex flex-wrap gap-2">
242
+ {children.slice(0, maxDisplay).map(child => (
243
+ <Badge key={child.id} variant="outline">{child.name}</Badge>
244
+ ))}
245
+ {leftOver > 0 ? (
246
+ <Badge variant="outline">
247
+ <Trans>+ {leftOver} more</Trans>
248
+ </Badge>
249
+ ) : null}
250
+ </div>
251
+ );
231
252
  },
232
- {
233
- component: MoveCollectionsBulkAction,
234
- order: 400,
235
- },
236
- {
237
- component: DeleteCollectionsBulkAction,
238
- order: 500,
239
- },
240
- ]}
241
- >
242
- <PageActionBarRight>
243
- <PermissionGuard requires={['CreateCollection', 'CreateCatalog']}>
244
- <Button asChild>
245
- <Link to="./new">
246
- <PlusIcon className="mr-2 h-4 w-4" />
247
- <Trans>New Collection</Trans>
248
- </Link>
249
- </Button>
250
- </PermissionGuard>
251
- </PageActionBarRight>
252
- </ListPage>
253
- </>
253
+ },
254
+ }}
255
+ defaultColumnOrder={[
256
+ 'featuredAsset',
257
+ 'name',
258
+ 'slug',
259
+ 'breadcrumbs',
260
+ 'productVariants',
261
+ ]}
262
+ transformData={data => {
263
+ return addSubCollections(data);
264
+ }}
265
+ setTableOptions={(options: TableOptions<any>) => {
266
+ options.state = {
267
+ ...options.state,
268
+ expanded: expanded,
269
+ };
270
+ options.onExpandedChange = setExpanded;
271
+ options.getExpandedRowModel = getExpandedRowModel();
272
+ options.getRowCanExpand = () => true;
273
+ options.getRowId = row => {
274
+ return row.id;
275
+ };
276
+ options.meta = {
277
+ ...options.meta,
278
+ resetExpanded: () => setExpanded({}),
279
+ };
280
+ return options;
281
+ }}
282
+ defaultVisibility={{
283
+ id: false,
284
+ createdAt: false,
285
+ updatedAt: false,
286
+ position: false,
287
+ parentId: false,
288
+ children: false,
289
+ description: false,
290
+ isPrivate: false,
291
+ }}
292
+ onSearchTermChange={searchTerm => {
293
+ setSearchTerm(searchTerm);
294
+ return {
295
+ name: { contains: searchTerm },
296
+ };
297
+ }}
298
+ route={Route}
299
+ bulkActions={[
300
+ {
301
+ component: AssignCollectionsToChannelBulkAction,
302
+ order: 100,
303
+ },
304
+ {
305
+ component: RemoveCollectionsFromChannelBulkAction,
306
+ order: 200,
307
+ },
308
+ {
309
+ component: DuplicateCollectionsBulkAction,
310
+ order: 300,
311
+ },
312
+ {
313
+ component: MoveCollectionsBulkAction,
314
+ order: 400,
315
+ },
316
+ {
317
+ component: DeleteCollectionsBulkAction,
318
+ order: 500,
319
+ },
320
+ ]}
321
+ onReorder={handleReorder}
322
+ disableDragAndDrop={!!searchTerm} // Disable dragging while searching
323
+ >
324
+ <PageActionBarRight>
325
+ <PermissionGuard requires={['CreateCollection', 'CreateCatalog']}>
326
+ <Button asChild>
327
+ <Link to="./new">
328
+ <PlusIcon className="mr-2 h-4 w-4" />
329
+ <Trans>New Collection</Trans>
330
+ </Link>
331
+ </Button>
332
+ </PermissionGuard>
333
+ </PageActionBarRight>
334
+ </ListPage>
254
335
  );
255
336
  }
337
+
@@ -103,6 +103,13 @@ export const MoveCollectionsBulkAction: BulkActionComponent<any> = ({ selection,
103
103
  table.resetRowSelection();
104
104
  };
105
105
 
106
+ const handleResetExpanded = () => {
107
+ const resetExpanded = (table.options.meta as { resetExpanded: () => void })?.resetExpanded;
108
+ if (resetExpanded) {
109
+ resetExpanded();
110
+ }
111
+ };
112
+
106
113
  return (
107
114
  <>
108
115
  <DataTableBulkActionItem
@@ -116,6 +123,7 @@ export const MoveCollectionsBulkAction: BulkActionComponent<any> = ({ selection,
116
123
  onOpenChange={setDialogOpen}
117
124
  collectionsToMove={selection}
118
125
  onSuccess={handleSuccess}
126
+ onResetExpanded={handleResetExpanded}
119
127
  />
120
128
  </>
121
129
  );
@@ -34,6 +34,7 @@ interface MoveCollectionsDialogProps {
34
34
  onOpenChange: (open: boolean) => void;
35
35
  collectionsToMove: Collection[];
36
36
  onSuccess?: () => void;
37
+ onResetExpanded?: () => void;
37
38
  }
38
39
 
39
40
  interface CollectionTreeNodeProps {
@@ -209,6 +210,7 @@ export function MoveCollectionsDialog({
209
210
  onOpenChange,
210
211
  collectionsToMove,
211
212
  onSuccess,
213
+ onResetExpanded,
212
214
  }: Readonly<MoveCollectionsDialogProps>) {
213
215
  const [expanded, setExpanded] = useState<Record<string, boolean>>({});
214
216
  const [selectedCollectionId, setSelectedCollectionId] = useState<string>();
@@ -282,6 +284,8 @@ export function MoveCollectionsDialog({
282
284
  toast.success(t`Collections moved successfully`);
283
285
  queryClient.invalidateQueries({ queryKey: collectionForMoveKey });
284
286
  queryClient.invalidateQueries({ queryKey: childCollectionsForMoveKey() });
287
+ queryClient.invalidateQueries({ queryKey: ['PaginatedListDataTable'] });
288
+ onResetExpanded?.();
285
289
  onSuccess?.();
286
290
  onOpenChange(false);
287
291
  },
@@ -7,6 +7,7 @@ import {
7
7
  SheetTitle,
8
8
  SheetTrigger,
9
9
  } from '@/vdb/components/ui/sheet.js';
10
+ import { FullWidthPageBlock } from '@/vdb/framework/layout-engine/page-layout.js';
10
11
  import { Trans } from '@lingui/react/macro';
11
12
  import { PanelLeftOpen } from 'lucide-react';
12
13
  import { FacetValuesTable } from './facet-values-table.js';
@@ -38,7 +39,9 @@ export function FacetValuesSheet({ facetName, facetId, children }: Readonly<Face
38
39
  </SheetDescription>
39
40
  </SheetHeader>
40
41
  <div className="px-4">
41
- <FacetValuesTable facetId={facetId} />
42
+ <FullWidthPageBlock blockId="facet-values-sheet-table">
43
+ <FacetValuesTable facetId={facetId} />
44
+ </FullWidthPageBlock>
42
45
  </div>
43
46
  </SheetContent>
44
47
  </Sheet>
@@ -128,7 +128,7 @@ export function FacetValuesTable({ facetId, registerRefresher }: Readonly<FacetV
128
128
  />
129
129
  <div className="mt-4">
130
130
  <Button asChild variant="outline">
131
- <Link to="./values/new">
131
+ <Link to={`/facets/${facetId}/values/new`}>
132
132
  <PlusIcon />
133
133
  <Trans>Add facet value</Trans>
134
134
  </Link>
@@ -1,4 +1,3 @@
1
- import { DataTableCellComponent } from '@/vdb/components/data-table/types.js';
2
1
  import { DetailPageButton } from '@/vdb/components/shared/detail-page-button.js';
3
2
  import { FacetValueChip } from '@/vdb/components/shared/facet-value-chip.js';
4
3
  import { PermissionGuard } from '@/vdb/components/shared/permission-guard.js';
@@ -8,7 +7,6 @@ import { PageActionBarRight } from '@/vdb/framework/layout-engine/page-layout.js
8
7
  import { ListPage } from '@/vdb/framework/page/list-page.js';
9
8
  import { Trans } from '@lingui/react/macro';
10
9
  import { createFileRoute, Link } from '@tanstack/react-router';
11
- import { ResultOf } from 'gql.tada';
12
10
  import { PlusIcon } from 'lucide-react';
13
11
  import {
14
12
  AssignFacetsToChannelBulkAction,
@@ -24,39 +22,6 @@ export const Route = createFileRoute('/_authenticated/_facets/facets')({
24
22
  loader: () => ({ breadcrumb: () => <Trans>Facets</Trans> }),
25
23
  });
26
24
 
27
- const FacetValuesCell: DataTableCellComponent<ResultOf<typeof facetListDocument>['facets']['items'][0]> = ({
28
- row,
29
- }) => {
30
- const value = row.original.valueList;
31
- if (!value) {
32
- return null;
33
- }
34
- const list = value;
35
- return (
36
- <div className="flex flex-wrap gap-2 items-center">
37
- {list.items.map(item => {
38
- return (
39
- <FacetValueChip
40
- key={item.id}
41
- facetValue={item}
42
- removable={false}
43
- displayFacetName={false}
44
- />
45
- );
46
- })}
47
- <FacetValuesSheet facetId={row.original.id} facetName={row.original.name}>
48
- {list.totalItems > 3 ? (
49
- <div>
50
- <Trans>+ {list.totalItems - 3} more</Trans>
51
- </div>
52
- ) : (
53
- <Trans>View values</Trans>
54
- )}
55
- </FacetValuesSheet>
56
- </div>
57
- );
58
- };
59
-
60
25
  function FacetListPage() {
61
26
  return (
62
27
  <ListPage
@@ -66,11 +31,9 @@ function FacetListPage() {
66
31
  defaultVisibility={{
67
32
  name: true,
68
33
  isPrivate: true,
69
- valueList: true,
70
34
  }}
71
35
  customizeColumns={{
72
36
  name: {
73
- id: 'name',
74
37
  cell: ({ row }) => <DetailPageButton id={row.original.id} label={row.original.name} />,
75
38
  },
76
39
  isPrivate: {
@@ -86,7 +49,28 @@ function FacetListPage() {
86
49
  },
87
50
  valueList: {
88
51
  header: () => <Trans>Values</Trans>,
89
- cell: FacetValuesCell,
52
+ cell: ({ row }) => {
53
+ const list = row.original.valueList;
54
+ return (
55
+ <div className="flex flex-wrap gap-2 items-center">
56
+ {list?.items.map(item => (
57
+ <FacetValueChip
58
+ key={item.id}
59
+ facetValue={item}
60
+ removable={false}
61
+ displayFacetName={false}
62
+ />
63
+ ))}
64
+ <FacetValuesSheet facetId={row.original.id} facetName={row.original.name}>
65
+ {list && list.totalItems > 3 ? (
66
+ <Trans>+ {list.totalItems - 3} more</Trans>
67
+ ) : (
68
+ <Trans>View values</Trans>
69
+ )}
70
+ </FacetValuesSheet>
71
+ </div>
72
+ );
73
+ },
90
74
  },
91
75
  }}
92
76
  onSearchTermChange={searchTerm => {