@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
@@ -15,6 +15,17 @@ import { useChannel } from '@/vdb/hooks/use-channel.js';
15
15
  import { usePage } from '@/vdb/hooks/use-page.js';
16
16
  import { useSavedViews } from '@/vdb/hooks/use-saved-views.js';
17
17
  import { Trans, useLingui } from '@lingui/react/macro';
18
+ import {
19
+ closestCenter,
20
+ DndContext,
21
+ } from '@dnd-kit/core';
22
+ import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
23
+ import {
24
+ SortableContext,
25
+ useSortable,
26
+ verticalListSortingStrategy,
27
+ } from '@dnd-kit/sortable';
28
+ import { CSS } from '@dnd-kit/utilities';
18
29
  import {
19
30
  ColumnDef,
20
31
  ColumnFilter,
@@ -23,18 +34,66 @@ import {
23
34
  getCoreRowModel,
24
35
  getPaginationRowModel,
25
36
  PaginationState,
37
+ Row,
26
38
  SortingState,
27
39
  Table as TableType,
28
40
  useReactTable,
29
41
  VisibilityState,
30
42
  } from '@tanstack/react-table';
31
43
  import { RowSelectionState, TableOptions } from '@tanstack/table-core';
32
- import React, { Suspense, useEffect, useRef } from 'react';
44
+ import { GripVertical } from 'lucide-react';
45
+ import React, { Suspense, useEffect, useId, useMemo, useRef } from 'react';
33
46
  import { AddFilterMenu } from './add-filter-menu.js';
34
47
  import { DataTableBulkActions } from './data-table-bulk-actions.js';
35
48
  import { DataTableProvider } from './data-table-context.js';
36
49
  import { DataTableFacetedFilter, DataTableFacetedFilterOption } from './data-table-faceted-filter.js';
37
50
  import { DataTableFilterBadgeEditable } from './data-table-filter-badge-editable.js';
51
+ import { useDragAndDrop } from '@/vdb/hooks/use-drag-and-drop.js';
52
+ import { toast } from 'sonner';
53
+
54
+ interface DraggableRowProps<TData> {
55
+ row: Row<TData>;
56
+ isDragDisabled: boolean;
57
+ }
58
+
59
+ function DraggableRow<TData>({ row, isDragDisabled }: Readonly<DraggableRowProps<TData>>) {
60
+ const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
61
+ id: row.id,
62
+ disabled: isDragDisabled,
63
+ });
64
+
65
+ const style = {
66
+ transform: CSS.Transform.toString(transform),
67
+ transition,
68
+ opacity: isDragging ? 0.5 : 1,
69
+ };
70
+
71
+ return (
72
+ <TableRow
73
+ ref={setNodeRef}
74
+ style={style}
75
+ data-state={row.getIsSelected() && 'selected'}
76
+ className="animate-in fade-in duration-100"
77
+ >
78
+ {!isDragDisabled && (
79
+ <TableCell className="w-[40px] h-12">
80
+ <div
81
+ {...attributes}
82
+ {...listeners}
83
+ className="cursor-move text-muted-foreground hover:text-foreground transition-colors"
84
+ >
85
+ <GripVertical className="h-4 w-4" />
86
+ </div>
87
+ </TableCell>
88
+ )}
89
+ {row.getVisibleCells().filter(cell => cell.column.id !== '__drag_handle__').map(cell => (
90
+ <TableCell key={cell.id} className="h-12">
91
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
92
+ </TableCell>
93
+ ))}
94
+ </TableRow>
95
+ );
96
+ }
38
97
 
39
98
  export interface FacetedFilter {
40
99
  title: string;
@@ -77,6 +136,18 @@ interface DataTableProps<TData> {
77
136
  */
78
137
  setTableOptions?: (table: TableOptions<TData>) => TableOptions<TData>;
79
138
  onRefresh?: () => void;
139
+ /**
140
+ * @description
141
+ * Callback when items are reordered via drag and drop.
142
+ * When provided, enables drag-and-drop functionality.
143
+ * The fourth parameter provides all items for context-aware reordering.
144
+ */
145
+ onReorder?: (oldIndex: number, newIndex: number, item: TData, allItems?: TData[]) => void | Promise<void>;
146
+ /**
147
+ * @description
148
+ * When true, drag and drop will be disabled. This will only have an effect if the onReorder prop is also set
149
+ */
150
+ disableDragAndDrop?: boolean;
80
151
  }
81
152
 
82
153
  /**
@@ -111,6 +182,8 @@ export function DataTable<TData>({
111
182
  bulkActions,
112
183
  setTableOptions,
113
184
  onRefresh,
185
+ onReorder,
186
+ disableDragAndDrop = false,
114
187
  }: Readonly<DataTableProps<TData>>) {
115
188
  const [sorting, setSorting] = React.useState<SortingState>(sortingInitialState || []);
116
189
  const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(filtersInitialState || []);
@@ -131,6 +204,16 @@ export function DataTable<TData>({
131
204
  const prevSearchTermRef = useRef(searchTerm);
132
205
  const prevColumnFiltersRef = useRef(columnFilters);
133
206
 
207
+ const componentId = useId();
208
+ const { sensors, localData, handleDragEnd, itemIds } = useDragAndDrop({
209
+ data,
210
+ onReorder,
211
+ disabled: disableDragAndDrop,
212
+ onError: error => {
213
+ toast.error(t`Failed to reorder items`);
214
+ },
215
+ });
216
+
134
217
  useEffect(() => {
135
218
  // If the defaultColumnVisibility changes externally (e.g. the user reset the table settings),
136
219
  // we want to reset the column visibility to the default.
@@ -143,9 +226,25 @@ export function DataTable<TData>({
143
226
  // We intentionally do not include `columnVisibility` in the dependency array
144
227
  }, [defaultColumnVisibility]);
145
228
 
229
+ // Add drag handle column if drag and drop is enabled
230
+ const columnsWithOptionalDragHandle = useMemo(() => {
231
+ if (!disableDragAndDrop && onReorder) {
232
+ const dragHandleColumn: ColumnDef<TData, any> = {
233
+ id: '__drag_handle__',
234
+ header: '',
235
+ cell: () => null, // Rendered by DraggableRow
236
+ size: 40,
237
+ enableSorting: false,
238
+ enableHiding: false,
239
+ };
240
+ return [dragHandleColumn, ...columns];
241
+ }
242
+ return columns;
243
+ }, [columns, disableDragAndDrop, onReorder]);
244
+
146
245
  let tableOptions: TableOptions<TData> = {
147
- data,
148
- columns,
246
+ data: localData,
247
+ columns: columnsWithOptionalDragHandle,
149
248
  getRowId: row => (row as { id: string }).id,
150
249
  getCoreRowModel: getCoreRowModel(),
151
250
  getPaginationRowModel: getPaginationRowModel(),
@@ -220,6 +319,8 @@ export function DataTable<TData>({
220
319
 
221
320
  const visibleColumnCount = Object.values(columnVisibility).filter(Boolean).length;
222
321
 
322
+ const isDragDisabled = disableDragAndDrop || !onReorder;
323
+
223
324
  return (
224
325
  <DataTableProvider
225
326
  columnFilters={columnFilters}
@@ -310,66 +411,94 @@ export function DataTable<TData>({
310
411
  ) : null}
311
412
 
312
413
  <div className="rounded-md border my-2 relative shadow-sm">
313
- <Table>
314
- <TableHeader className="bg-muted/50">
315
- {table.getHeaderGroups().map(headerGroup => (
316
- <TableRow key={headerGroup.id}>
317
- {headerGroup.headers.map(header => {
318
- return (
319
- <TableHead key={header.id}>
320
- {header.isPlaceholder
321
- ? null
322
- : flexRender(
323
- header.column.columnDef.header,
324
- header.getContext(),
325
- )}
326
- </TableHead>
327
- );
328
- })}
329
- </TableRow>
330
- ))}
331
- </TableHeader>
332
- <TableBody>
333
- {isLoading && !data?.length ? (
334
- Array.from({ length: Math.min(pagination.pageSize, 100) }).map((_, index) => (
335
- <TableRow
336
- key={`skeleton-${index}`}
337
- className="animate-in fade-in duration-100"
338
- >
339
- {Array.from({ length: visibleColumnCount }).map((_, cellIndex) => (
414
+ <DndContext
415
+ sensors={sensors}
416
+ collisionDetection={closestCenter}
417
+ onDragEnd={handleDragEnd}
418
+ modifiers={[restrictToVerticalAxis]}
419
+ >
420
+ <Table>
421
+ <TableHeader className="bg-muted/50">
422
+ {table.getHeaderGroups().map(headerGroup => (
423
+ <TableRow key={headerGroup.id}>
424
+ {headerGroup.headers.map(header => {
425
+ return (
426
+ <TableHead key={header.id}>
427
+ {header.isPlaceholder
428
+ ? null
429
+ : flexRender(
430
+ header.column.columnDef.header,
431
+ header.getContext(),
432
+ )}
433
+ </TableHead>
434
+ );
435
+ })}
436
+ </TableRow>
437
+ ))}
438
+ </TableHeader>
439
+ <SortableContext items={itemIds} strategy={verticalListSortingStrategy}>
440
+ <TableBody>
441
+ {isLoading && !localData?.length ? (
442
+ Array.from({ length: Math.min(pagination.pageSize, 100) }).map((_, index) => (
443
+ <TableRow
444
+ key={`skeleton-${index}`}
445
+ className="animate-in fade-in duration-100"
446
+ >
447
+ {!isDragDisabled && (
448
+ <TableCell className="w-[40px] h-12">
449
+ <Skeleton className="h-4 w-4" />
450
+ </TableCell>
451
+ )}
452
+ {Array.from({ length: visibleColumnCount }).map((_, cellIndex) => (
453
+ <TableCell
454
+ key={`skeleton-cell-${index}-${cellIndex}`}
455
+ className="h-12"
456
+ >
457
+ <Skeleton className="h-4 my-2 w-full" />
458
+ </TableCell>
459
+ ))}
460
+ </TableRow>
461
+ ))
462
+ ) : table.getRowModel().rows?.length ? (
463
+ (() => {
464
+ const isDraggableEnabled = onReorder && !isDragDisabled;
465
+ const rows = table.getRowModel().rows;
466
+
467
+ if (isDraggableEnabled) {
468
+ return rows.map(row => (
469
+ <DraggableRow key={`${row.id}-${componentId}`} row={row} isDragDisabled={isDragDisabled} />
470
+ ));
471
+ }
472
+
473
+ return rows.map(row => (
474
+ <TableRow
475
+ key={row.id}
476
+ data-state={row.getIsSelected() && 'selected'}
477
+ className="animate-in fade-in duration-100"
478
+ >
479
+ {row.getVisibleCells().map(cell => (
480
+ <TableCell key={cell.id} className="h-12">
481
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
482
+ </TableCell>
483
+ ))}
484
+ </TableRow>
485
+ ));
486
+ })()
487
+ ) : (
488
+ <TableRow className="animate-in fade-in duration-100">
340
489
  <TableCell
341
- key={`skeleton-cell-${index}-${cellIndex}`}
342
- className="h-12"
490
+ colSpan={columnsWithOptionalDragHandle.length + (isDragDisabled ? 0 : 1)}
491
+ className="h-24 text-center"
343
492
  >
344
- <Skeleton className="h-4 my-2 w-full" />
493
+ <Trans>No results</Trans>
345
494
  </TableCell>
346
- ))}
347
- </TableRow>
348
- ))
349
- ) : table.getRowModel().rows?.length ? (
350
- table.getRowModel().rows.map(row => (
351
- <TableRow
352
- key={row.id}
353
- data-state={row.getIsSelected() && 'selected'}
354
- className="animate-in fade-in duration-100"
355
- >
356
- {row.getVisibleCells().map(cell => (
357
- <TableCell key={cell.id} className="h-12">
358
- {flexRender(cell.column.columnDef.cell, cell.getContext())}
359
- </TableCell>
360
- ))}
361
- </TableRow>
362
- ))
363
- ) : (
364
- <TableRow className="animate-in fade-in duration-100">
365
- <TableCell colSpan={columns.length} className="h-24 text-center">
366
- <Trans>No results</Trans>
367
- </TableCell>
368
- </TableRow>
369
- )}
370
- {children}
371
- </TableBody>
372
- </Table>
495
+ </TableRow>
496
+ )}
497
+ {children}
498
+ </TableBody>
499
+ </SortableContext>
500
+ </Table>
501
+ </DndContext>
373
502
  <DataTableBulkActions bulkActions={bulkActions ?? []} table={table} />
374
503
  </div>
375
504
  {onPageChange && totalItems != null && <DataTablePagination table={table} />}
@@ -28,7 +28,7 @@ export const GlobalViewsBar: React.FC = () => {
28
28
  );
29
29
 
30
30
  const handleViewClick = (view: SavedView) => {
31
- handleApplyView(view.filters, view.searchTerm);
31
+ handleApplyView(view.filters, view.columnConfig, view.searchTerm);
32
32
  };
33
33
 
34
34
  const isViewActive = (view: SavedView) => {
@@ -7,6 +7,8 @@ import { Input } from '../ui/input.js';
7
7
  import { Label } from '../ui/label.js';
8
8
  import { RadioGroup, RadioGroupItem } from '../ui/radio-group.js';
9
9
  import { toast } from 'sonner';
10
+ import { usePage } from '@/vdb/hooks/use-page.js';
11
+ import { useUserSettings } from '@/vdb/hooks/use-user-settings.js';
10
12
 
11
13
  interface SaveViewDialogProps {
12
14
  open: boolean;
@@ -25,6 +27,21 @@ export const SaveViewDialog: React.FC<SaveViewDialogProps> = ({
25
27
  const [scope, setScope] = useState<'user' | 'global'>('user');
26
28
  const [saving, setSaving] = useState(false);
27
29
  const { saveView, userViews, globalViews, canManageGlobalViews } = useSavedViews();
30
+ const { pageId } = usePage();
31
+ const { settings } = useUserSettings();
32
+
33
+ const defaultVisibility = {
34
+ id: false,
35
+ createdAt: false,
36
+ updatedAt: false,
37
+ type: false,
38
+ currencyCode: false,
39
+ }
40
+ const tableSettings = pageId ? settings.tableSettings?.[pageId] : undefined;
41
+ const columnVisibility = pageId
42
+ ? (tableSettings?.columnVisibility ?? defaultVisibility)
43
+ : defaultVisibility;
44
+ const columnOrder = pageId ? (tableSettings?.columnOrder ?? []) : [];
28
45
 
29
46
  const handleSave = async () => {
30
47
  if (!name.trim()) {
@@ -45,6 +62,10 @@ export const SaveViewDialog: React.FC<SaveViewDialogProps> = ({
45
62
  name: name.trim(),
46
63
  scope,
47
64
  filters,
65
+ columnConfig : {
66
+ columnVisibility,
67
+ columnOrder,
68
+ },
48
69
  searchTerm,
49
70
  });
50
71
  toast.success(`View "${name}" saved successfully`);
@@ -16,9 +16,15 @@ import { usePage } from '@/vdb/hooks/use-page.js';
16
16
  import { TypedDocumentNode } from '@graphql-typed-document-node/core';
17
17
  import { Trans, useLingui } from '@lingui/react/macro';
18
18
  import { useMutation } from '@tanstack/react-query';
19
- import { AccessorFnColumnDef, AccessorKeyColumnDef, createColumnHelper, Row } from '@tanstack/react-table';
19
+ import {
20
+ AccessorFnColumnDef,
21
+ AccessorKeyColumnDef,
22
+ CellContext,
23
+ createColumnHelper,
24
+ Row,
25
+ } from '@tanstack/react-table';
20
26
  import { EllipsisIcon, TrashIcon } from 'lucide-react';
21
- import { useMemo } from 'react';
27
+ import { memo, useMemo } from 'react';
22
28
  import { toast } from 'sonner';
23
29
  import {
24
30
  AdditionalColumns,
@@ -116,6 +122,25 @@ export function useGeneratedColumns<T extends TypedDocumentNode<any, any>>({
116
122
  const customConfig = customizeColumns?.[fieldInfo.name as unknown as AllItemFieldKeys<T>] ?? {};
117
123
  const { header, meta, cell: customCell, ...customConfigRest } = customConfig;
118
124
  const enableColumnFilter = fieldInfo.isScalar && !facetedFilters?.[fieldInfo.name];
125
+ const displayComponentId =
126
+ pageId && pageBlock?.blockId
127
+ ? generateDisplayComponentKey(pageId, pageBlock.blockId, fieldInfo.name)
128
+ : undefined;
129
+
130
+ // If a custom cell function is provided, use it directly (like additionalColumns does).
131
+ // This preserves the same behavior and prevents cell unmounting issues.
132
+ // Only use CellWrapper for columns without custom cells.
133
+ const cellFn =
134
+ typeof customCell === 'function'
135
+ ? customCell
136
+ : (cellContext: CellContext<any, any>) => (
137
+ <CellWrapper
138
+ cellContext={cellContext}
139
+ fieldInfo={fieldInfo}
140
+ isCustomField={isCustomField}
141
+ displayComponentId={displayComponentId}
142
+ />
143
+ );
119
144
 
120
145
  return columnHelper.accessor(fieldInfo.name as any, {
121
146
  id: fieldInfo.name,
@@ -126,28 +151,7 @@ export function useGeneratedColumns<T extends TypedDocumentNode<any, any>>({
126
151
  // otherwise the TanStack Table with apply an "auto" function which somehow
127
152
  // prevents certain filters from working.
128
153
  filterFn: 'equalsString',
129
- cell: cellContext => {
130
- const { cell, row } = cellContext;
131
- const cellValue = cell.getValue();
132
- const value =
133
- cellValue ??
134
- (isCustomField ? row.original?.customFields?.[fieldInfo.name] : undefined);
135
- const displayComponentId =
136
- pageId && pageBlock?.blockId
137
- ? generateDisplayComponentKey(pageId, pageBlock.blockId, fieldInfo.name)
138
- : undefined;
139
-
140
- const CustomDisplayComponent =
141
- displayComponentId && getDisplayComponent(displayComponentId);
142
-
143
- if (CustomDisplayComponent) {
144
- return <CustomDisplayComponent value={value} {...cellContext} />;
145
- }
146
- if (typeof customCell === 'function') {
147
- return customCell(cellContext);
148
- }
149
- return <DefaultDisplayComponent value={value} fieldInfo={fieldInfo} />;
150
- },
154
+ cell: cellFn,
151
155
  header: headerContext => {
152
156
  return (
153
157
  <DataTableColumnHeader headerContext={headerContext} customConfig={customConfig} />
@@ -291,6 +295,34 @@ function DefaultDisplayComponent({ value, fieldInfo }: { value: any; fieldInfo:
291
295
  return value;
292
296
  }
293
297
 
298
+ /**
299
+ * A cell wrapper component for columns without custom cell functions.
300
+ * Handles default display logic including custom display components and field-type-based rendering.
301
+ */
302
+ const CellWrapper = memo(function CellWrapper({
303
+ cellContext,
304
+ fieldInfo,
305
+ isCustomField,
306
+ displayComponentId,
307
+ }: {
308
+ cellContext: CellContext<any, any>;
309
+ fieldInfo: FieldInfo;
310
+ isCustomField: boolean;
311
+ displayComponentId?: string;
312
+ }) {
313
+ const { cell, row } = cellContext;
314
+ const cellValue = cell.getValue();
315
+ const value =
316
+ cellValue ?? (isCustomField ? (row.original as any)?.customFields?.[fieldInfo.name] : undefined);
317
+
318
+ const CustomDisplayComponent = displayComponentId && getDisplayComponent(displayComponentId);
319
+
320
+ if (CustomDisplayComponent) {
321
+ return <CustomDisplayComponent value={value} {...cellContext} />;
322
+ }
323
+ return <DefaultDisplayComponent value={value} fieldInfo={fieldInfo} />;
324
+ });
325
+
294
326
  function DeleteMutationRowAction({
295
327
  deleteMutation,
296
328
  row,
@@ -44,7 +44,7 @@ export const ViewsSheet: React.FC<ViewsSheetProps> = ({ open, onOpenChange, type
44
44
  const isGlobal = type === 'global';
45
45
 
46
46
  const handleViewApply = (view: SavedView) => {
47
- handleApplyView(view.filters, view.searchTerm);
47
+ handleApplyView(view.filters,view.columnConfig, view.searchTerm);
48
48
  const viewName = view.name;
49
49
  const message = isGlobal ? t`Applied global view "${viewName}"` : t`Applied view "${viewName}"`;
50
50
  toast.success(message);
@@ -138,7 +138,7 @@ export function ChannelSwitcher() {
138
138
  </div>
139
139
  <ChannelCodeLabel code={channel.code} />
140
140
  {channel.id === displayChannel?.id && (
141
- <span className="ml-auto text-xs text-muted-foreground">
141
+ <span className="ms-auto text-xs text-muted-foreground">
142
142
  <Trans context="current channel">Current</Trans>
143
143
  </span>
144
144
  )}
@@ -146,9 +146,9 @@ export function ChannelSwitcher() {
146
146
  {/* Show language sub-menu for the current channel */}
147
147
  {channel.id === displayChannel?.id && (
148
148
  <DropdownMenuSub>
149
- <DropdownMenuSubTrigger className="gap-2 p-2 pl-4">
149
+ <DropdownMenuSubTrigger className="gap-2 p-2 ps-4">
150
150
  <Languages className="w-4 h-4" />
151
- <div className="flex gap-1 ml-2">
151
+ <div className="flex gap-1 ms-2">
152
152
  <span className="text-muted-foreground">Content: </span>
153
153
  {formatLanguageName(contentLanguage)}
154
154
  </div>
@@ -167,7 +167,7 @@ export function ChannelSwitcher() {
167
167
  </div>
168
168
  <span>{label}</span>
169
169
  {contentLanguage === languageCode && (
170
- <span className="ml-auto text-xs text-muted-foreground">
170
+ <span className="ms-auto text-xs text-muted-foreground">
171
171
  <Trans context="active language">
172
172
  Active
173
173
  </Trans>
@@ -200,7 +200,9 @@ export function ChannelSwitcher() {
200
200
  <div className="bg-background flex size-6 items-center justify-center rounded-md border">
201
201
  <Plus className="size-4" />
202
202
  </div>
203
- <div className="text-muted-foreground font-medium">Add channel</div>
203
+ <div className="text-muted-foreground font-medium">
204
+ <Trans>Add channel</Trans>
205
+ </div>
204
206
  </Link>
205
207
  </DropdownMenuItem>
206
208
  </DropdownMenuContent>
@@ -215,7 +215,7 @@ export function NavMain({ items }: Readonly<{ items: Array<NavMenuSection | NavM
215
215
  <SidebarMenuButton tooltip={item.title}>
216
216
  {item.icon && <item.icon />}
217
217
  <span>{i18n.t(item.title)}</span>
218
- <ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
218
+ <ChevronRight className="ms-auto transition-transform duration-200 rtl:rotate-180 group-data-[state=open]/collapsible:rotate-90" />
219
219
  </SidebarMenuButton>
220
220
  </CollapsibleTrigger>
221
221
  <CollapsibleContent>
@@ -280,7 +280,7 @@ export function NavMain({ items }: Readonly<{ items: Array<NavMenuSection | NavM
280
280
  <SidebarMenuButton tooltip={i18n.t(item.title)}>
281
281
  {item.icon && <item.icon />}
282
282
  <span>{i18n.t(item.title)}</span>
283
- <ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
283
+ <ChevronRight className="ms-auto transition-transform duration-200 rtl:rotate-180 group-data-[state=open]/collapsible:rotate-90" />
284
284
  </SidebarMenuButton>
285
285
  </CollapsibleTrigger>
286
286
  <CollapsibleContent>
@@ -29,7 +29,9 @@ export function Alerts() {
29
29
  </Button>
30
30
  </DropdownMenuTrigger>
31
31
  <DropdownMenuContent align="end" className="max-w-[800px] min-w-96">
32
- <DropdownMenuLabel>Alerts</DropdownMenuLabel>
32
+ <DropdownMenuLabel>
33
+ <Trans>Alerts</Trans>
34
+ </DropdownMenuLabel>
33
35
  <DropdownMenuSeparator />
34
36
  <ScrollArea className="max-h-[500px]">
35
37
  {activeCount > 0 ? (
@@ -23,16 +23,16 @@ interface AssignToChannelBulkActionProps {
23
23
  }
24
24
 
25
25
  export function AssignToChannelBulkAction({
26
- selection,
27
- table,
28
- entityType,
29
- mutationFn,
30
- requiredPermissions,
31
- buildInput,
32
- additionalFields,
33
- additionalData = {},
34
- onSuccess,
35
- }: Readonly<AssignToChannelBulkActionProps>) {
26
+ selection,
27
+ table,
28
+ entityType,
29
+ mutationFn,
30
+ requiredPermissions,
31
+ buildInput,
32
+ additionalFields,
33
+ additionalData = {},
34
+ onSuccess,
35
+ }: Readonly<AssignToChannelBulkActionProps>) {
36
36
  const { refetchPaginatedList } = usePaginatedList();
37
37
  const { channels } = useChannel();
38
38
  const [dialogOpen, setDialogOpen] = useState(false);
@@ -70,7 +70,7 @@ export function AssignToChannelDialog({
70
70
  onOpenChange(false);
71
71
  },
72
72
  onError: () => {
73
- toast.error(`Failed to assign ${entityIdsLength} ${entityType} to channel`);
73
+ toast.error(t`Failed to assign ${entityIdsLength} ${entityType} to channel`);
74
74
  },
75
75
  });
76
76