@vendure/dashboard 3.5.1-master-202510310232 → 3.5.1-master-202511040232

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vendure/dashboard",
3
3
  "private": false,
4
- "version": "3.5.1-master-202510310232",
4
+ "version": "3.5.1-master-202511040232",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",
@@ -154,8 +154,8 @@
154
154
  "@storybook/addon-vitest": "^10.0.0-beta.9",
155
155
  "@storybook/react-vite": "^10.0.0-beta.9",
156
156
  "@types/node": "^22.13.4",
157
- "@vendure/common": "^3.5.1-master-202510310232",
158
- "@vendure/core": "^3.5.1-master-202510310232",
157
+ "@vendure/common": "^3.5.1-master-202511040232",
158
+ "@vendure/core": "^3.5.1-master-202511040232",
159
159
  "@vitest/browser": "^3.2.4",
160
160
  "@vitest/coverage-v8": "^3.2.4",
161
161
  "eslint": "^9.19.0",
@@ -172,5 +172,5 @@
172
172
  "lightningcss-linux-arm64-musl": "^1.29.3",
173
173
  "lightningcss-linux-x64-musl": "^1.29.1"
174
174
  },
175
- "gitHead": "8c7ae8868ccdb0ebccfbbe8a867028e6971ce602"
175
+ "gitHead": "fe1c9639d991ff7ebfb1188c4d9a62f1b7c74a04"
176
176
  }
@@ -85,7 +85,7 @@ function OrderListPage() {
85
85
  header: () => <Trans>Shipping</Trans>,
86
86
  cell: ({ row }) => {
87
87
  const value = row.original.shippingLines;
88
- return <div>{value.map(line => line.shippingMethod.name).join(', ')}</div>;
88
+ return <div>{value?.map(line => line.shippingMethod.name).join(', ')}</div>;
89
89
  },
90
90
  },
91
91
  }}
@@ -18,3 +18,6 @@ export * from './relation-selector.js';
18
18
 
19
19
  // Slug input component
20
20
  export * from './slug-input.js';
21
+
22
+ // String list input component
23
+ export * from './string-list-input.js';
@@ -0,0 +1,106 @@
1
+ import { X } from 'lucide-react';
2
+ import { KeyboardEvent, useId, useRef, useState } from 'react';
3
+
4
+ import { Badge } from '@/vdb/components/ui/badge.js';
5
+ import { Input } from '@/vdb/components/ui/input.js';
6
+ import type { DashboardFormComponentProps } from '@/vdb/framework/form-engine/form-engine-types.js';
7
+ import { isReadonlyField } from '@/vdb/framework/form-engine/utils.js';
8
+ import { cn } from '@/vdb/lib/utils.js';
9
+ import { useLingui } from '@lingui/react';
10
+
11
+ export function StringListInput({
12
+ value,
13
+ onChange,
14
+ onBlur,
15
+ disabled,
16
+ name,
17
+ fieldDef,
18
+ }: DashboardFormComponentProps) {
19
+ const [inputValue, setInputValue] = useState('');
20
+ const inputRef = useRef<HTMLInputElement>(null);
21
+ const { i18n } = useLingui();
22
+ const isDisabled = isReadonlyField(fieldDef) || disabled;
23
+ const id = useId();
24
+
25
+ const items = Array.isArray(value) ? value : [];
26
+
27
+ const addItem = (item: string) => {
28
+ const trimmedItem = item.trim();
29
+ if (trimmedItem) {
30
+ onChange([...items, trimmedItem]);
31
+ setInputValue('');
32
+ }
33
+ };
34
+
35
+ const removeItem = (indexToRemove: number) => {
36
+ onChange(items.filter((_, index) => index !== indexToRemove));
37
+ };
38
+
39
+ const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
40
+ if (e.key === 'Enter' || e.key === ',') {
41
+ e.preventDefault();
42
+ addItem(inputValue);
43
+ } else if (e.key === 'Backspace' && !inputValue && items.length > 0) {
44
+ // Remove last item when backspace is pressed on empty input
45
+ removeItem(items.length - 1);
46
+ }
47
+ };
48
+
49
+ const handleInputBlur = () => {
50
+ // Add current input value as item on blur if there's any
51
+ if (inputValue.trim()) {
52
+ addItem(inputValue);
53
+ }
54
+ onBlur?.();
55
+ };
56
+
57
+ return (
58
+ <div
59
+ className={cn(
60
+ 'flex min-h-10 w-full flex-wrap gap-2',
61
+ isDisabled && 'cursor-not-allowed opacity-50',
62
+ )}
63
+ >
64
+ {!isDisabled && (
65
+ <Input
66
+ ref={inputRef}
67
+ type="text"
68
+ value={inputValue}
69
+ onChange={e => setInputValue(e.target.value)}
70
+ onKeyDown={handleKeyDown}
71
+ onBlur={handleInputBlur}
72
+ name={name}
73
+ placeholder={i18n.t('Type and press Enter or comma to add...')}
74
+ className="min-w-[120px]"
75
+ />
76
+ )}
77
+ <div className="flex flex-wrap gap-1 items-start justify-start">
78
+ {items.map((item, index) => (
79
+ <Badge key={id + index} variant="secondary">
80
+ <span>{item}</span>
81
+ {!isDisabled && (
82
+ <button
83
+ type="button"
84
+ onClick={e => {
85
+ e.stopPropagation();
86
+ removeItem(index);
87
+ }}
88
+ className={cn(
89
+ 'ml-1 rounded-full outline-none ring-offset-background',
90
+ 'hover:bg-muted focus:ring-2 focus:ring-ring focus:ring-offset-2',
91
+ )}
92
+ aria-label={`Remove ${item}`}
93
+ >
94
+ <X className="h-3 w-3" />
95
+ </button>
96
+ )}
97
+ </Badge>
98
+ ))}
99
+ </div>
100
+ </div>
101
+ );
102
+ }
103
+
104
+ StringListInput.metadata = {
105
+ isListInput: true,
106
+ };
@@ -36,7 +36,7 @@ function TooltipContent({
36
36
  data-slot="tooltip-content"
37
37
  sideOffset={sideOffset}
38
38
  className={cn(
39
- 'bg-secondary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-w-sm rounded-md px-3 py-1.5 text-xs',
39
+ 'bg-secondary text-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-w-sm rounded-md px-3 py-1.5 text-xs',
40
40
  className,
41
41
  )}
42
42
  {...props}
@@ -26,6 +26,9 @@ export function registerNavigationExtensions(
26
26
  id: route.navMenuItem.id ?? route.path,
27
27
  title: route.navMenuItem.title ?? route.path,
28
28
  order: route.navMenuItem.order,
29
+ requiresPermission: route.navMenuItem.requiresPermission,
30
+ icon: route.navMenuItem.icon,
31
+ placement: route.navMenuItem.placement,
29
32
  };
30
33
  addNavMenuItem(item, route.navMenuItem.sectionId);
31
34
  }
@@ -2,6 +2,7 @@ import { JSX, useMemo } from 'react';
2
2
  import { ControllerRenderProps } from 'react-hook-form';
3
3
 
4
4
  import { CustomFieldListInput } from '@/vdb/components/data-input/custom-field-list-input.js';
5
+ import { StringListInput } from '@/vdb/components/data-input/string-list-input.js';
5
6
  import { StructFormInput } from '@/vdb/components/data-input/struct-form-input.js';
6
7
  import { ConfigurableOperationListInput } from '../../components/data-input/configurable-operation-list-input.js';
7
8
 
@@ -124,7 +125,7 @@ function renderListField(
124
125
  }
125
126
 
126
127
  if (fieldDef.type === 'string') {
127
- return <DefaultInputForType {...fieldWithTransform} fieldDef={fieldDef} />;
128
+ return <StringListInput {...fieldWithTransform} fieldDef={fieldDef} />;
128
129
  }
129
130
 
130
131
  return (
@@ -60,7 +60,7 @@ const awesomeClient = new AwesomeGraphQLClient({
60
60
  credentials: 'include',
61
61
  mode: 'cors',
62
62
  }).then(res => {
63
- const authToken = res.headers.get('vendure-auth-token');
63
+ const authToken = res.headers.get(uiConfig.api.authTokenHeaderKey);
64
64
  if (authToken) {
65
65
  localStorage.setItem(LS_KEY_SESSION_TOKEN, authToken);
66
66
  }