@vendure/dashboard 3.5.2-master-202512040233 → 3.5.2-master-202512180239

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 (69) hide show
  1. package/dist/plugin/constants.js +2 -2
  2. package/dist/plugin/dashboard.plugin.js +1 -1
  3. package/dist/vite/constants.js +1 -0
  4. package/lingui.config.js +1 -0
  5. package/package.json +7 -7
  6. package/src/app/routeTree.gen.ts +1221 -0
  7. package/src/app/routes/_authenticated/_collections/collections.graphql.ts +1 -0
  8. package/src/app/routes/_authenticated/_collections/collections.tsx +249 -167
  9. package/src/app/routes/_authenticated/_collections/components/collection-bulk-actions.tsx +8 -0
  10. package/src/app/routes/_authenticated/_collections/components/move-collections-dialog.tsx +4 -0
  11. package/src/app/routes/_authenticated/_customers/components/customer-history/index.ts +0 -1
  12. package/src/app/routes/_authenticated/_global-settings/global-settings.tsx +1 -1
  13. package/src/app/routes/_authenticated/_orders/components/add-surcharge-form.tsx +139 -0
  14. package/src/app/routes/_authenticated/_orders/components/edit-order-table.tsx +3 -0
  15. package/src/app/routes/_authenticated/_orders/components/order-address.tsx +3 -3
  16. package/src/app/routes/_authenticated/_orders/components/order-modification-summary.tsx +49 -11
  17. package/src/app/routes/_authenticated/_orders/orders_.$id_.modify.tsx +9 -0
  18. package/src/app/routes/_authenticated/_orders/utils/use-modify-order.ts +23 -0
  19. package/src/app/routes/_authenticated/_product-variants/components/add-currency-dropdown.tsx +3 -3
  20. package/src/app/routes/_authenticated/_product-variants/components/add-stock-location-dropdown.tsx +2 -2
  21. package/src/app/routes/_authenticated/_products/products.graphql.ts +1 -0
  22. package/src/app/routes/_authenticated/_tax-rates/tax-rates_.$id.tsx +2 -9
  23. package/src/i18n/locales/bg.po +3436 -0
  24. package/src/lib/components/data-input/datetime-input.tsx +1 -1
  25. package/src/lib/components/data-input/number-input.tsx +24 -5
  26. package/src/lib/components/data-input/relation-selector.tsx +1 -1
  27. package/src/lib/components/data-input/struct-form-input.tsx +175 -174
  28. package/src/lib/components/data-table/data-table-utils.ts +241 -1
  29. package/src/lib/components/data-table/data-table.tsx +190 -60
  30. package/src/lib/components/layout/manage-languages-dialog.tsx +2 -25
  31. package/src/lib/components/shared/custom-fields-form.tsx +13 -8
  32. package/src/lib/components/shared/paginated-list-data-table.tsx +19 -0
  33. package/src/lib/components/ui/alert.tsx +1 -1
  34. package/src/lib/components/ui/carousel.tsx +2 -2
  35. package/src/lib/components/ui/chart.tsx +1 -1
  36. package/src/lib/components/ui/context-menu.tsx +1 -1
  37. package/src/lib/components/ui/drawer.tsx +1 -1
  38. package/src/lib/components/ui/grid-layout.tsx +1 -1
  39. package/src/lib/components/ui/input-group.tsx +1 -0
  40. package/src/lib/components/ui/input-otp.tsx +1 -1
  41. package/src/lib/components/ui/menubar.tsx +1 -1
  42. package/src/lib/components/ui/navigation-menu.tsx +1 -1
  43. package/src/lib/components/ui/progress.tsx +1 -1
  44. package/src/lib/components/ui/radio-group.tsx +1 -1
  45. package/src/lib/components/ui/resizable.tsx +1 -1
  46. package/src/lib/components/ui/select.tsx +1 -1
  47. package/src/lib/components/ui/slider.tsx +1 -1
  48. package/src/lib/components/ui/toggle-group.tsx +2 -2
  49. package/src/lib/components/ui/toggle.tsx +1 -1
  50. package/src/lib/framework/component-registry/component-registry.tsx +2 -6
  51. package/src/lib/framework/extension-api/display-component-extensions.tsx +4 -3
  52. package/src/lib/framework/extension-api/logic/detail-forms.ts +0 -13
  53. package/src/lib/framework/extension-api/types/data-table.ts +4 -2
  54. package/src/lib/framework/extension-api/types/navigation.ts +2 -2
  55. package/src/lib/framework/form-engine/use-generated-form.tsx +7 -1
  56. package/src/lib/framework/layout-engine/page-layout.tsx +1 -1
  57. package/src/lib/framework/nav-menu/nav-menu-extensions.ts +1 -1
  58. package/src/lib/framework/page/detail-page-route-loader.tsx +1 -1
  59. package/src/lib/framework/page/list-page.tsx +62 -38
  60. package/src/lib/framework/page/page-api.ts +1 -1
  61. package/src/lib/framework/page/use-detail-page.ts +4 -2
  62. package/src/lib/framework/page/use-extended-router.tsx +20 -16
  63. package/src/lib/framework/registry/registry-types.ts +2 -1
  64. package/src/lib/graphql/graphql-env.d.ts +8 -12
  65. package/src/lib/hooks/use-drag-and-drop.ts +86 -0
  66. package/src/lib/providers/channel-provider.tsx +11 -7
  67. package/LICENSE.md +0 -42
  68. package/src/app/routes/_authenticated/_facets/components/edit-facet-value.tsx +0 -129
  69. /package/src/{app/routes/_authenticated/_global-settings → lib}/utils/global-languages.ts +0 -0
@@ -10,6 +10,7 @@ import {
10
10
  } from '@/vdb/components/ui/form.js';
11
11
  import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/vdb/components/ui/tabs.js';
12
12
  import { CustomFormComponent } from '@/vdb/framework/form-engine/custom-form-component.js';
13
+ import { ConfigurableFieldDef } from '@/vdb/framework/form-engine/form-engine-types.js';
13
14
  import { useCustomFieldConfig } from '@/vdb/hooks/use-custom-field-config.js';
14
15
  import { useUserSettings } from '@/vdb/hooks/use-user-settings.js';
15
16
  import { customFieldConfigFragment } from '@/vdb/providers/server-config.js';
@@ -20,7 +21,7 @@ import { Control } from 'react-hook-form';
20
21
  import { FormControlAdapter } from '../../framework/form-engine/form-control-adapter.js';
21
22
  import { TranslatableFormField } from './translatable-form-field.js';
22
23
 
23
- type CustomFieldConfig = ResultOf<typeof customFieldConfigFragment>;
24
+ type CustomFieldConfig = Omit<ResultOf<typeof customFieldConfigFragment>, '__typename'>;
24
25
 
25
26
  interface CustomFieldsFormProps {
26
27
  entityType: string;
@@ -120,8 +121,8 @@ export function CustomFieldsForm({ entityType, control, formPathPrefix }: Readon
120
121
  }
121
122
 
122
123
  interface CustomFieldItemProps {
123
- fieldDef: CustomFieldConfig;
124
- control: Control<any, any>;
124
+ fieldDef: ConfigurableFieldDef;
125
+ control: Control<any>;
125
126
  fieldName: string;
126
127
  }
127
128
 
@@ -130,14 +131,19 @@ function CustomFieldItem({ fieldDef, control, fieldName }: Readonly<CustomFieldI
130
131
  settings: { displayLanguage },
131
132
  } = useUserSettings();
132
133
 
133
- const getTranslation = (input: Array<{ languageCode: string; value: string }> | null | undefined) => {
134
+ const getTranslation = (
135
+ input: string | Array<{ languageCode: string; value: string }> | null | undefined,
136
+ ) => {
137
+ if (typeof input === 'string') {
138
+ return input;
139
+ }
134
140
  return input?.find(t => t.languageCode === displayLanguage)?.value;
135
141
  };
136
142
  const hasCustomFormComponent = fieldDef.ui?.component;
137
143
  const isLocaleField = fieldDef.type === 'localeString' || fieldDef.type === 'localeText';
138
144
  const shouldBeFullWidth = fieldDef.ui?.fullWidth === true;
139
145
  const containerClassName = shouldBeFullWidth ? 'col-span-2' : '';
140
- const isReadonly = fieldDef.readonly ?? false;
146
+ const isReadonly = (fieldDef as CustomFieldConfig).readonly ?? false;
141
147
 
142
148
  // For locale fields, always use TranslatableFormField regardless of custom components
143
149
  if (isLocaleField) {
@@ -212,7 +218,6 @@ function CustomFieldItem({ fieldDef, control, fieldName }: Readonly<CustomFieldI
212
218
  <StructFormInput {...inputField} fieldDef={fieldDef} />
213
219
  )}
214
220
  defaultValue={{}} // Empty struct object as default
215
- isFullWidth={true} // Structs should always be full-width
216
221
  />
217
222
  </FormControl>
218
223
  <FormDescription>{getTranslation(fieldDef.description)}</FormDescription>
@@ -266,9 +271,9 @@ function CustomFieldItem({ fieldDef, control, fieldName }: Readonly<CustomFieldI
266
271
  }
267
272
 
268
273
  interface CustomFieldFormItemProps {
269
- fieldDef: CustomFieldConfig;
274
+ fieldDef: ConfigurableFieldDef;
270
275
  getTranslation: (
271
- input: Array<{ languageCode: string; value: string }> | null | undefined,
276
+ input: string | Array<{ languageCode: string; value: string }> | null | undefined,
272
277
  ) => string | undefined;
273
278
  fieldName: string;
274
279
  children: React.ReactNode;
@@ -234,6 +234,21 @@ export interface PaginatedListDataTableProps<
234
234
  * the list needs to be refreshed.
235
235
  */
236
236
  registerRefresher?: PaginatedListRefresherRegisterFn;
237
+ /**
238
+ * @description
239
+ * Callback when items are reordered via drag and drop.
240
+ * When provided, enables drag-and-drop functionality.
241
+ */
242
+ onReorder?: (
243
+ oldIndex: number,
244
+ newIndex: number,
245
+ item: PaginatedListItemFields<T>,
246
+ ) => void | Promise<void>;
247
+ /**
248
+ * @description
249
+ * When true, drag and drop will be disabled. This will only have an effect if the onReorder prop is also set
250
+ */
251
+ disableDragAndDrop?: boolean;
237
252
  }
238
253
 
239
254
  export const PaginatedListDataTableKey = 'PaginatedListDataTable';
@@ -378,6 +393,8 @@ export function PaginatedListDataTable<
378
393
  setTableOptions,
379
394
  transformData,
380
395
  registerRefresher,
396
+ onReorder,
397
+ disableDragAndDrop = false,
381
398
  }: Readonly<PaginatedListDataTableProps<T, U, V, AC>>) {
382
399
  const [searchTerm, setSearchTerm] = React.useState<string>('');
383
400
  const debouncedSearchTerm = useDebounce(searchTerm, 500);
@@ -498,6 +515,8 @@ export function PaginatedListDataTable<
498
515
  bulkActions={bulkActions}
499
516
  setTableOptions={setTableOptions}
500
517
  onRefresh={refetchPaginatedList}
518
+ onReorder={onReorder}
519
+ disableDragAndDrop={disableDragAndDrop}
501
520
  />
502
521
  </PaginatedListContext.Provider>
503
522
  );
@@ -8,7 +8,7 @@ const alertVariants = cva(
8
8
  {
9
9
  variants: {
10
10
  variant: {
11
- default: 'bg-background text-foreground',
11
+ default: 'bg-background text-primary/80',
12
12
  destructive:
13
13
  'border-destructive/50 text-destructive dark:text-destructive-foreground/80 dark:border-destructive [&>svg]:text-current dark:bg-destructive/50',
14
14
  },
@@ -6,8 +6,8 @@ import useEmblaCarousel, {
6
6
  } from "embla-carousel-react"
7
7
  import { ArrowLeft, ArrowRight } from "lucide-react"
8
8
 
9
- import { cn } from "@/vdb/lib/utils"
10
- import { Button } from "@/vdb/components/ui/button"
9
+ import { cn } from "@/vdb/lib/utils.js"
10
+ import { Button } from "@/vdb/components/ui/button.js"
11
11
 
12
12
  type CarouselApi = UseEmblaCarouselType[1]
13
13
  type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
@@ -1,7 +1,7 @@
1
1
  import * as React from "react"
2
2
  import * as RechartsPrimitive from "recharts"
3
3
 
4
- import { cn } from "@/vdb/lib/utils"
4
+ import { cn } from "@/vdb/lib/utils.js"
5
5
 
6
6
  // Format: { THEME_NAME: CSS_SELECTOR }
7
7
  const THEMES = { light: "", dark: ".dark" } as const
@@ -4,7 +4,7 @@ import * as React from "react"
4
4
  import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"
5
5
  import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
6
6
 
7
- import { cn } from "@/vdb/lib/utils"
7
+ import { cn } from "@/vdb/lib/utils.js"
8
8
 
9
9
  function ContextMenu({
10
10
  ...props
@@ -1,7 +1,7 @@
1
1
  import * as React from "react"
2
2
  import { Drawer as DrawerPrimitive } from "vaul"
3
3
 
4
- import { cn } from "@/vdb/lib/utils"
4
+ import { cn } from "@/vdb/lib/utils.js"
5
5
 
6
6
  function Drawer({
7
7
  ...props
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { useState, useRef, useCallback, useEffect } from "react";
3
- import { cn } from "@/vdb/lib/utils";
3
+ import { cn } from "@/vdb/lib/utils.js";
4
4
 
5
5
  export interface GridLayout {
6
6
  x: number;
@@ -127,6 +127,7 @@ function InputGroupInput({ className, ...props }: React.ComponentProps<'input'>)
127
127
  'flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent',
128
128
  className,
129
129
  )}
130
+ value={props.value}
130
131
  {...props}
131
132
  />
132
133
  );
@@ -4,7 +4,7 @@ import * as React from "react"
4
4
  import { OTPInput, OTPInputContext } from "input-otp"
5
5
  import { MinusIcon } from "lucide-react"
6
6
 
7
- import { cn } from "@/vdb/lib/utils"
7
+ import { cn } from "@/vdb/lib/utils.js"
8
8
 
9
9
  function InputOTP({
10
10
  className,
@@ -2,7 +2,7 @@ import * as React from "react"
2
2
  import * as MenubarPrimitive from "@radix-ui/react-menubar"
3
3
  import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
4
4
 
5
- import { cn } from "@/vdb/lib/utils"
5
+ import { cn } from "@/vdb/lib/utils.js"
6
6
 
7
7
  function Menubar({
8
8
  className,
@@ -3,7 +3,7 @@ import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
3
3
  import { cva } from "class-variance-authority"
4
4
  import { ChevronDownIcon } from "lucide-react"
5
5
 
6
- import { cn } from "@/vdb/lib/utils"
6
+ import { cn } from "@/vdb/lib/utils.js"
7
7
 
8
8
  function NavigationMenu({
9
9
  className,
@@ -1,7 +1,7 @@
1
1
  import * as React from "react"
2
2
  import * as ProgressPrimitive from "@radix-ui/react-progress"
3
3
 
4
- import { cn } from "@/vdb/lib/utils"
4
+ import { cn } from "@/vdb/lib/utils.js"
5
5
 
6
6
  function Progress({
7
7
  className,
@@ -4,7 +4,7 @@ import * as React from "react"
4
4
  import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
5
5
  import { CircleIcon } from "lucide-react"
6
6
 
7
- import { cn } from "@/vdb/lib/utils"
7
+ import { cn } from "@/vdb/lib/utils.js"
8
8
 
9
9
  function RadioGroup({
10
10
  className,
@@ -2,7 +2,7 @@ import * as React from "react"
2
2
  import { GripVerticalIcon } from "lucide-react"
3
3
  import * as ResizablePrimitive from "react-resizable-panels"
4
4
 
5
- import { cn } from "@/vdb/lib/utils"
5
+ import { cn } from "@/vdb/lib/utils.js"
6
6
 
7
7
  function ResizablePanelGroup({
8
8
  className,
@@ -2,7 +2,7 @@ import * as React from "react"
2
2
  import * as SelectPrimitive from "@radix-ui/react-select"
3
3
  import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
4
4
 
5
- import { cn } from "@/vdb/lib/utils"
5
+ import { cn } from "@/vdb/lib/utils.js"
6
6
 
7
7
  function Select({
8
8
  ...props
@@ -3,7 +3,7 @@
3
3
  import * as React from "react"
4
4
  import * as SliderPrimitive from "@radix-ui/react-slider"
5
5
 
6
- import { cn } from "@/vdb/lib/utils"
6
+ import { cn } from "@/vdb/lib/utils.js"
7
7
 
8
8
  function Slider({
9
9
  className,
@@ -4,8 +4,8 @@ import * as React from "react"
4
4
  import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group"
5
5
  import { type VariantProps } from "class-variance-authority"
6
6
 
7
- import { cn } from "@/vdb/lib/utils"
8
- import { toggleVariants } from "@/vdb/components/ui/toggle"
7
+ import { cn } from "@/vdb/lib/utils.js"
8
+ import { toggleVariants } from "@/vdb/components/ui/toggle.js"
9
9
 
10
10
  const ToggleGroupContext = React.createContext<
11
11
  VariantProps<typeof toggleVariants>
@@ -2,7 +2,7 @@ import * as React from "react"
2
2
  import * as TogglePrimitive from "@radix-ui/react-toggle"
3
3
  import { cva, type VariantProps } from "class-variance-authority"
4
4
 
5
- import { cn } from "@/vdb/lib/utils"
5
+ import { cn } from "@/vdb/lib/utils.js"
6
6
 
7
7
  const toggleVariants = cva(
8
8
  "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap",
@@ -1,13 +1,9 @@
1
1
  import { DashboardFormComponent } from '@/vdb/framework/form-engine/form-engine-types.js';
2
- import * as React from 'react';
2
+ import React from 'react';
3
3
  import { getDisplayComponent } from '../extension-api/display-component-extensions.js';
4
4
  import { getInputComponent } from '../extension-api/input-component-extensions.js';
5
5
 
6
- export type DataDisplayComponentProps<T extends Record<string, any> = Record<string, any>> = {
7
- value: any;
8
- } & T;
9
-
10
- export type DataDisplayComponent = React.ComponentType<DataDisplayComponentProps>;
6
+ export type DataDisplayComponent<T extends Record<string, any> = Record<string, any>> = React.ComponentType<T & { value: any}>;
11
7
 
12
8
  // Component registry hook that uses the global registry
13
9
  export function useComponentRegistry() {
@@ -5,6 +5,7 @@ import { Money } from '@/vdb/components/data-display/money.js';
5
5
  import { VendureImage } from '@/vdb/components/shared/vendure-image.js';
6
6
  import { DataDisplayComponent } from '../component-registry/component-registry.js';
7
7
  import { globalRegistry } from '../registry/global-registry.js';
8
+ import { DataTableDisplayComponent } from './types/data-table.js';
8
9
 
9
10
  globalRegistry.register('displayComponents', new Map<string, DataDisplayComponent>());
10
11
 
@@ -21,7 +22,7 @@ displayComponents.set('vendure:money', Money);
21
22
  displayComponents.set('vendure:json', Json);
22
23
 
23
24
  export function getDisplayComponent(id: string): DataDisplayComponent | undefined {
24
- return globalRegistry.get('displayComponents').get(id);
25
+ return globalRegistry.get('displayComponents').get(id) as DataDisplayComponent | undefined;
25
26
  }
26
27
 
27
28
  /**
@@ -42,10 +43,10 @@ export function addDisplayComponent({
42
43
  pageId: string;
43
44
  blockId: string;
44
45
  field: string;
45
- component: React.ComponentType<{ value: any; [key: string]: any }>;
46
+ component: DataDisplayComponent | DataTableDisplayComponent;
46
47
  }) {
47
48
  const displayComponents = globalRegistry.get('displayComponents');
48
-
49
+
49
50
  // Generate the key using the helper function
50
51
  const key = generateDisplayComponentKey(pageId, blockId, field);
51
52
 
@@ -1,7 +1,6 @@
1
1
  import { addDetailQueryDocument } from '@/vdb/framework/form-engine/custom-form-component-extensions.js';
2
2
  import { parse } from 'graphql';
3
3
 
4
- import { addDisplayComponent } from '../display-component-extensions.js';
5
4
  import { addInputComponent } from '../input-component-extensions.js';
6
5
  import { DashboardDetailFormExtensionDefinition } from '../types/detail-forms.js';
7
6
 
@@ -31,18 +30,6 @@ export function registerDetailFormExtensions(detailForms?: DashboardDetailFormEx
31
30
  });
32
31
  }
33
32
  }
34
-
35
- // Register display components for this detail form
36
- if (detailForm.displays) {
37
- for (const displayComponent of detailForm.displays) {
38
- addDisplayComponent({
39
- pageId: detailForm.pageId,
40
- blockId: displayComponent.blockId,
41
- field: displayComponent.field,
42
- component: displayComponent.component,
43
- });
44
- }
45
- }
46
33
  }
47
34
  }
48
35
  }
@@ -1,9 +1,11 @@
1
- import { DataDisplayComponentProps } from '@/vdb/framework/component-registry/component-registry.js';
1
+ import { DataDisplayComponent } from '@/vdb/framework/component-registry/component-registry.js';
2
2
  import { Table } from '@tanstack/react-table';
3
3
  import { CellContext } from '@tanstack/table-core';
4
4
  import { DocumentNode } from 'graphql';
5
5
  import React from 'react';
6
6
 
7
+ export type DataTableDisplayComponent = DataDisplayComponent<CellContext<any, any>>;
8
+
7
9
  /**
8
10
  * @description
9
11
  * Allows you to define custom display components for specific columns in data tables.
@@ -24,7 +26,7 @@ export interface DashboardDataTableDisplayComponent {
24
26
  * The React component that will be rendered as the display.
25
27
  * It should accept `value` and other standard display props.
26
28
  */
27
- component: React.ComponentType<DataDisplayComponentProps<CellContext<any, any>>>;
29
+ component: DataTableDisplayComponent;
28
30
  }
29
31
 
30
32
  export type BulkActionContext<Item extends { id: string } & Record<string, any>> = {
@@ -39,7 +39,7 @@ export interface DashboardRouteDefinition {
39
39
  * The value is a Tanstack Router
40
40
  * [loader function](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#route-loaders)
41
41
  */
42
- loader?: RouteOptions['loader'];
42
+ loader?: RouteOptions<any>['loader'];
43
43
 
44
44
  /**
45
45
  * @description
@@ -47,7 +47,7 @@ export interface DashboardRouteDefinition {
47
47
  * The value is a Tanstack Router
48
48
  * [validateSearch function](https://tanstack.com/router/latest/docs/framework/react/guide/search-params#search-param-validation)
49
49
  */
50
- validateSearch?: RouteOptions['validateSearch'];
50
+ validateSearch?: RouteOptions<any>['validateSearch'];
51
51
 
52
52
  /**
53
53
  * @description
@@ -9,6 +9,10 @@ import { getOperationVariablesFields } from '../document-introspection/get-docum
9
9
  import { createFormSchemaFromFields, getDefaultValuesFromFields } from './form-schema-tools.js';
10
10
  import { removeEmptyIdFields, transformRelationFields } from './utils.js';
11
11
 
12
+ export type WithLooseCustomFields<T> = T extends { customFields?: any }
13
+ ? Omit<T, 'customFields'> & { customFields?: T['customFields'] | unknown }
14
+ : T;
15
+
12
16
  /**
13
17
  * @description
14
18
  * Options for the useGeneratedForm hook.
@@ -40,7 +44,9 @@ export interface GeneratedFormOptions<
40
44
  customFieldConfig?: any[]; // Add custom field config for validation
41
45
  setValues: (
42
46
  entity: NonNullable<E>,
43
- ) => VarName extends keyof VariablesOf<T> ? VariablesOf<T>[VarName] : VariablesOf<T>;
47
+ ) => WithLooseCustomFields<
48
+ VarName extends keyof VariablesOf<T> ? VariablesOf<T>[VarName] : VariablesOf<T>
49
+ >;
44
50
  onSubmit?: (
45
51
  values: VarName extends keyof VariablesOf<T> ? VariablesOf<T>[VarName] : VariablesOf<T>,
46
52
  ) => void;
@@ -547,7 +547,7 @@ export type PageBlockProps = {
547
547
  * @description
548
548
  * Which column this block should appear in
549
549
  */
550
- column: 'main' | 'side';
550
+ column: 'main' | 'side' | 'full';
551
551
  /**
552
552
  * @description
553
553
  * The ID of the block, e.g. "gift-cards" or "related-products".
@@ -13,7 +13,7 @@ export type NavMenuSectionPlacement = 'top' | 'bottom';
13
13
  * @docsPage Navigation
14
14
  * @since 3.4.0
15
15
  */
16
- interface NavMenuItem {
16
+ export interface NavMenuItem {
17
17
  /**
18
18
  * @description
19
19
  * A unique ID for this nav menu item
@@ -29,7 +29,7 @@ export function detailPageRouteLoader<T extends TypedDocumentNode<any, any>>({
29
29
  queryDocument,
30
30
  breadcrumb,
31
31
  }: DetailPageRouteLoaderConfig<T>) {
32
- const loader: FileBaseRouteOptions<any, any>['loader'] = async ({
32
+ const loader: FileBaseRouteOptions<any, any, any>['loader'] = async ({
33
33
  context,
34
34
  params,
35
35
  location,
@@ -361,6 +361,22 @@ export interface ListPageProps<
361
361
  * the list needs to be refreshed.
362
362
  */
363
363
  registerRefresher?: PaginatedListRefresherRegisterFn;
364
+ /**
365
+ * @description
366
+ * Callback when items are reordered via drag and drop.
367
+ * Only applies to top-level items. When provided, enables drag-and-drop functionality.
368
+ *
369
+ * @param oldIndex - The original index of the dragged item
370
+ * @param newIndex - The new index where the item was dropped
371
+ * @param item - The data of the item that was moved
372
+ */
373
+ onReorder?: (oldIndex: number, newIndex: number, item: any) => void | Promise<void>;
374
+ /**
375
+ * @description
376
+ * When true, drag and drop will be disabled. This will only have an effect if the onReorder prop is also set Useful when filtering or searching.
377
+ * Defaults to false. Only relevant when `onReorder` is provided.
378
+ */
379
+ disableDragAndDrop?: boolean;
364
380
  }
365
381
 
366
382
  /**
@@ -481,6 +497,8 @@ export function ListPage<
481
497
  setTableOptions,
482
498
  bulkActions,
483
499
  registerRefresher,
500
+ onReorder,
501
+ disableDragAndDrop = false,
484
502
  }: Readonly<ListPageProps<T, U, V, AC>>) {
485
503
  const route = typeof routeOrFn === 'function' ? routeOrFn() : routeOrFn;
486
504
  const routeSearch = route.useSearch();
@@ -536,6 +554,47 @@ export function ListPage<
536
554
  });
537
555
  }
538
556
 
557
+ const commonTableProps = {
558
+ listQuery,
559
+ deleteMutation,
560
+ transformVariables,
561
+ customizeColumns: customizeColumns as any,
562
+ additionalColumns: additionalColumns as any,
563
+ defaultColumnOrder: columnOrder as any,
564
+ defaultVisibility: columnVisibility as any,
565
+ onSearchTermChange,
566
+ page: pagination.page,
567
+ itemsPerPage: pagination.itemsPerPage,
568
+ sorting,
569
+ columnFilters,
570
+ onPageChange: (table: Table<any>, page: number, perPage: number) => {
571
+ persistListStateToUrl(table, { page, perPage });
572
+ if (pageId) {
573
+ setTableSettings(pageId, 'pageSize', perPage);
574
+ }
575
+ },
576
+ onSortChange: (table: Table<any>, sorting: SortingState) => {
577
+ persistListStateToUrl(table, { sort: sorting });
578
+ },
579
+ onFilterChange: (table: Table<any>, filters: ColumnFiltersState) => {
580
+ persistListStateToUrl(table, { filters });
581
+ if (pageId) {
582
+ setTableSettings(pageId, 'columnFilters', filters);
583
+ }
584
+ },
585
+ onColumnVisibilityChange: (table: Table<any>, columnVisibility: any) => {
586
+ if (pageId) {
587
+ setTableSettings(pageId, 'columnVisibility', columnVisibility);
588
+ }
589
+ },
590
+ facetedFilters,
591
+ rowActions,
592
+ bulkActions,
593
+ setTableOptions,
594
+ transformData,
595
+ registerRefresher,
596
+ };
597
+
539
598
  return (
540
599
  <Page pageId={pageId}>
541
600
  <PageTitle>{title}</PageTitle>
@@ -543,44 +602,9 @@ export function ListPage<
543
602
  <PageLayout>
544
603
  <FullWidthPageBlock blockId="list-table">
545
604
  <PaginatedListDataTable
546
- listQuery={listQuery}
547
- deleteMutation={deleteMutation}
548
- transformVariables={transformVariables}
549
- customizeColumns={customizeColumns as any}
550
- additionalColumns={additionalColumns as any}
551
- defaultColumnOrder={columnOrder as any}
552
- defaultVisibility={columnVisibility as any}
553
- onSearchTermChange={onSearchTermChange}
554
- page={pagination.page}
555
- itemsPerPage={pagination.itemsPerPage}
556
- sorting={sorting}
557
- columnFilters={columnFilters}
558
- onPageChange={(table, page, perPage) => {
559
- persistListStateToUrl(table, { page, perPage });
560
- if (pageId) {
561
- setTableSettings(pageId, 'pageSize', perPage);
562
- }
563
- }}
564
- onSortChange={(table, sorting) => {
565
- persistListStateToUrl(table, { sort: sorting });
566
- }}
567
- onFilterChange={(table, filters) => {
568
- persistListStateToUrl(table, { filters });
569
- if (pageId) {
570
- setTableSettings(pageId, 'columnFilters', filters);
571
- }
572
- }}
573
- onColumnVisibilityChange={(table, columnVisibility) => {
574
- if (pageId) {
575
- setTableSettings(pageId, 'columnVisibility', columnVisibility);
576
- }
577
- }}
578
- facetedFilters={facetedFilters}
579
- rowActions={rowActions}
580
- bulkActions={bulkActions}
581
- setTableOptions={setTableOptions}
582
- transformData={transformData}
583
- registerRefresher={registerRefresher}
605
+ {...commonTableProps}
606
+ onReorder={onReorder}
607
+ disableDragAndDrop={disableDragAndDrop}
584
608
  />
585
609
  </FullWidthPageBlock>
586
610
  </PageLayout>
@@ -1,4 +1,4 @@
1
- import { DashboardRouteDefinition } from '../extension-api/extension-api-types.js';
1
+ import { DashboardRouteDefinition } from '../extension-api/types/navigation.js';
2
2
 
3
3
  export const extensionRoutes = new Map<string, DashboardRouteDefinition>();
4
4
 
@@ -22,7 +22,7 @@ import {
22
22
  getMutationName,
23
23
  getQueryName,
24
24
  } from '../document-introspection/get-document-structure.js';
25
- import { useGeneratedForm } from '../form-engine/use-generated-form.js';
25
+ import { useGeneratedForm, WithLooseCustomFields } from '../form-engine/use-generated-form.js';
26
26
 
27
27
  import { DetailEntityPath } from './page-types.js';
28
28
 
@@ -95,7 +95,9 @@ export interface DetailPageOptions<
95
95
  * @description
96
96
  * The function to set the values for the update document.
97
97
  */
98
- setValuesForUpdate: (entity: NonNullable<ResultOf<T>[EntityField]>) => VariablesOf<U>[VarNameUpdate];
98
+ setValuesForUpdate: (
99
+ entity: NonNullable<ResultOf<T>[EntityField]>,
100
+ ) => WithLooseCustomFields<VariablesOf<U>[VarNameUpdate]>;
99
101
  transformCreateInput?: (input: VariablesOf<C>[VarNameCreate]) => VariablesOf<C>[VarNameCreate];
100
102
  transformUpdateInput?: (input: VariablesOf<U>[VarNameUpdate]) => VariablesOf<U>[VarNameUpdate];
101
103
  /**