@vendure/dashboard 3.3.5-master-202506251305 → 3.3.5-master-202506260234

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.3.5-master-202506251305",
4
+ "version": "3.3.5-master-202506260234",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",
@@ -86,8 +86,8 @@
86
86
  "@types/react-dom": "^19.0.4",
87
87
  "@types/react-grid-layout": "^1.3.5",
88
88
  "@uidotdev/usehooks": "^2.4.1",
89
- "@vendure/common": "^3.3.5-master-202506251305",
90
- "@vendure/core": "^3.3.5-master-202506251305",
89
+ "@vendure/common": "^3.3.5-master-202506260234",
90
+ "@vendure/core": "^3.3.5-master-202506260234",
91
91
  "@vitejs/plugin-react": "^4.3.4",
92
92
  "awesome-graphql-client": "^2.1.0",
93
93
  "class-variance-authority": "^0.7.1",
@@ -130,5 +130,5 @@
130
130
  "lightningcss-linux-arm64-musl": "^1.29.3",
131
131
  "lightningcss-linux-x64-musl": "^1.29.1"
132
132
  },
133
- "gitHead": "6781f6697a37aa8c7b7e76e9fc53b4dd0300a94b"
133
+ "gitHead": "e69b67636ecee5332319d38d031f6cc0f70517eb"
134
134
  }
@@ -7,12 +7,15 @@ import {
7
7
  FormMessage,
8
8
  } from '@/components/ui/form.js';
9
9
  import { Input } from '@/components/ui/input.js';
10
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs.js';
10
11
  import { CustomFormComponent } from '@/framework/form-engine/custom-form-component.js';
11
12
  import { useCustomFieldConfig } from '@/hooks/use-custom-field-config.js';
12
13
  import { useUserSettings } from '@/hooks/use-user-settings.js';
14
+ import { useLingui } from '@/lib/trans.js';
13
15
  import { customFieldConfigFragment } from '@/providers/server-config.js';
14
16
  import { CustomFieldType } from '@vendure/common/lib/shared-types';
15
17
  import { ResultOf } from 'gql.tada';
18
+ import React, { useMemo } from 'react';
16
19
  import { Control, ControllerRenderProps } from 'react-hook-form';
17
20
  import { Switch } from '../ui/switch.js';
18
21
  import { TranslatableFormField } from './translatable-form-field.js';
@@ -29,6 +32,7 @@ export function CustomFieldsForm({ entityType, control, formPathPrefix }: Custom
29
32
  const {
30
33
  settings: { displayLanguage },
31
34
  } = useUserSettings();
35
+ const { i18n } = useLingui();
32
36
 
33
37
  const getTranslation = (input: Array<{ languageCode: string; value: string }> | null | undefined) => {
34
38
  return input?.find(t => t.languageCode === displayLanguage)?.value;
@@ -42,18 +46,80 @@ export function CustomFieldsForm({ entityType, control, formPathPrefix }: Custom
42
46
  : `customFields.${fieldDefName}`;
43
47
  };
44
48
 
49
+ // Group custom fields by tabs
50
+ const groupedFields = useMemo(() => {
51
+ if (!customFields) return [];
52
+
53
+ const tabMap = new Map<string, CustomFieldConfig[]>();
54
+ const defaultTabName = '__default_tab__';
55
+
56
+ for (const field of customFields) {
57
+ const tabName = field.ui?.tab ?? defaultTabName;
58
+ if (tabMap.has(tabName)) {
59
+ tabMap.get(tabName)?.push(field);
60
+ } else {
61
+ tabMap.set(tabName, [field]);
62
+ }
63
+ }
64
+
65
+ return Array.from(tabMap.entries())
66
+ .sort((a, b) => (a[0] === defaultTabName ? -1 : 1))
67
+ .map(([tabName, customFields]) => ({
68
+ tabName: tabName === defaultTabName ? 'general' : tabName,
69
+ customFields,
70
+ }));
71
+ }, [customFields]);
72
+
73
+ // Check if we should show tabs (more than one tab or at least one field has a tab)
74
+ const shouldShowTabs = useMemo(() => {
75
+ if (!customFields) return false;
76
+ const hasTabbedFields = customFields.some(field => field.ui?.tab);
77
+ return hasTabbedFields || groupedFields.length > 1;
78
+ }, [customFields, groupedFields.length]);
79
+
80
+ if (!shouldShowTabs) {
81
+ // Single tab view - use the original grid layout
82
+ return (
83
+ <div className="grid grid-cols-2 gap-4">
84
+ {customFields?.map(fieldDef => (
85
+ <CustomFieldItem
86
+ key={fieldDef.name}
87
+ fieldDef={fieldDef}
88
+ control={control}
89
+ fieldName={getFieldName(fieldDef.name)}
90
+ getTranslation={getTranslation}
91
+ />
92
+ ))}
93
+ </div>
94
+ );
95
+ }
96
+
97
+ // Tabbed view
45
98
  return (
46
- <div className="grid grid-cols-2 gap-4">
47
- {customFields?.map(fieldDef => (
48
- <CustomFieldItem
49
- key={fieldDef.name}
50
- fieldDef={fieldDef}
51
- control={control}
52
- fieldName={getFieldName(fieldDef.name)}
53
- getTranslation={getTranslation}
54
- />
99
+ <Tabs defaultValue={groupedFields[0]?.tabName} className="w-full">
100
+ <TabsList>
101
+ {groupedFields.map(group => (
102
+ <TabsTrigger key={group.tabName} value={group.tabName}>
103
+ {group.tabName === 'general' ? i18n.t('General') : group.tabName}
104
+ </TabsTrigger>
105
+ ))}
106
+ </TabsList>
107
+ {groupedFields.map(group => (
108
+ <TabsContent key={group.tabName} value={group.tabName} className="mt-4">
109
+ <div className="grid grid-cols-2 gap-4">
110
+ {group.customFields.map(fieldDef => (
111
+ <CustomFieldItem
112
+ key={fieldDef.name}
113
+ fieldDef={fieldDef}
114
+ control={control}
115
+ fieldName={getFieldName(fieldDef.name)}
116
+ getTranslation={getTranslation}
117
+ />
118
+ ))}
119
+ </div>
120
+ </TabsContent>
55
121
  ))}
56
- </div>
122
+ </Tabs>
57
123
  );
58
124
  }
59
125
 
@@ -69,83 +135,91 @@ interface CustomFieldItemProps {
69
135
  function CustomFieldItem({ fieldDef, control, fieldName, getTranslation }: CustomFieldItemProps) {
70
136
  const hasCustomFormComponent = fieldDef.ui && fieldDef.ui.component;
71
137
  const isLocaleField = fieldDef.type === 'localeString' || fieldDef.type === 'localeText';
138
+ const shouldBeFullWidth = fieldDef.ui?.fullWidth === true;
139
+ const containerClassName = shouldBeFullWidth ? 'col-span-2' : '';
72
140
 
73
141
  // For locale fields, always use TranslatableFormField regardless of custom components
74
142
  if (isLocaleField) {
75
143
  return (
76
- <TranslatableFormField
77
- control={control}
78
- name={fieldName}
79
- render={({ field, ...props }) => (
80
- <FormItem>
81
- <FormLabel>{getTranslation(fieldDef.label) ?? field.name}</FormLabel>
82
- <FormControl>
83
- {hasCustomFormComponent ? (
84
- <CustomFormComponent
85
- fieldDef={fieldDef}
86
- fieldProps={{
87
- ...props,
88
- field: {
89
- ...field,
90
- disabled: fieldDef.readonly ?? false,
91
- },
92
- }}
93
- />
94
- ) : (
95
- <FormInputForType fieldDef={fieldDef} field={field} />
96
- )}
97
- </FormControl>
98
- <FormDescription>{getTranslation(fieldDef.description)}</FormDescription>
99
- <FormMessage />
100
- </FormItem>
101
- )}
102
- />
144
+ <div className={containerClassName}>
145
+ <TranslatableFormField
146
+ control={control}
147
+ name={fieldName}
148
+ render={({ field, ...props }) => (
149
+ <FormItem>
150
+ <FormLabel>{getTranslation(fieldDef.label) ?? field.name}</FormLabel>
151
+ <FormControl>
152
+ {hasCustomFormComponent ? (
153
+ <CustomFormComponent
154
+ fieldDef={fieldDef}
155
+ fieldProps={{
156
+ ...props,
157
+ field: {
158
+ ...field,
159
+ disabled: fieldDef.readonly ?? false,
160
+ },
161
+ }}
162
+ />
163
+ ) : (
164
+ <FormInputForType fieldDef={fieldDef} field={field} />
165
+ )}
166
+ </FormControl>
167
+ <FormDescription>{getTranslation(fieldDef.description)}</FormDescription>
168
+ <FormMessage />
169
+ </FormItem>
170
+ )}
171
+ />
172
+ </div>
103
173
  );
104
174
  }
105
175
 
106
176
  // For non-locale fields with custom components
107
177
  if (hasCustomFormComponent) {
108
178
  return (
179
+ <div className={containerClassName}>
180
+ <FormField
181
+ control={control}
182
+ name={fieldName}
183
+ render={fieldProps => (
184
+ <CustomFieldFormItem
185
+ fieldDef={fieldDef}
186
+ getTranslation={getTranslation}
187
+ fieldName={fieldProps.field.name}
188
+ >
189
+ <CustomFormComponent
190
+ fieldDef={fieldDef}
191
+ fieldProps={{
192
+ ...fieldProps,
193
+ field: {
194
+ ...fieldProps.field,
195
+ disabled: fieldDef.readonly ?? false,
196
+ },
197
+ }}
198
+ />
199
+ </CustomFieldFormItem>
200
+ )}
201
+ />
202
+ </div>
203
+ );
204
+ }
205
+
206
+ // For regular fields without custom components
207
+ return (
208
+ <div className={containerClassName}>
109
209
  <FormField
110
210
  control={control}
111
211
  name={fieldName}
112
- render={fieldProps => (
212
+ render={({ field }) => (
113
213
  <CustomFieldFormItem
114
214
  fieldDef={fieldDef}
115
215
  getTranslation={getTranslation}
116
- fieldName={fieldProps.field.name}
216
+ fieldName={field.name}
117
217
  >
118
- <CustomFormComponent
119
- fieldDef={fieldDef}
120
- fieldProps={{
121
- ...fieldProps,
122
- field: {
123
- ...fieldProps.field,
124
- disabled: fieldDef.readonly ?? false,
125
- },
126
- }}
127
- />
218
+ <FormInputForType fieldDef={fieldDef} field={field} />
128
219
  </CustomFieldFormItem>
129
220
  )}
130
221
  />
131
- );
132
- }
133
-
134
- // For regular fields without custom components
135
- return (
136
- <FormField
137
- control={control}
138
- name={fieldName}
139
- render={({ field }) => (
140
- <CustomFieldFormItem
141
- fieldDef={fieldDef}
142
- getTranslation={getTranslation}
143
- fieldName={field.name}
144
- >
145
- <FormInputForType fieldDef={fieldDef} field={field} />
146
- </CustomFieldFormItem>
147
- )}
148
- />
222
+ </div>
149
223
  );
150
224
  }
151
225