@vendure/dashboard 3.5.2-master-202512020233 → 3.5.2-master-202512170238
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/dist/plugin/constants.js +2 -2
- package/dist/vite/constants.js +1 -0
- package/lingui.config.js +1 -0
- package/package.json +7 -7
- package/src/app/routeTree.gen.ts +1221 -0
- package/src/app/routes/_authenticated/_customers/components/customer-history/index.ts +0 -1
- package/src/app/routes/_authenticated/_global-settings/global-settings.tsx +1 -1
- package/src/app/routes/_authenticated/_orders/components/add-surcharge-form.tsx +139 -0
- package/src/app/routes/_authenticated/_orders/components/edit-order-table.tsx +3 -0
- package/src/app/routes/_authenticated/_orders/components/order-address.tsx +3 -3
- package/src/app/routes/_authenticated/_orders/components/order-modification-summary.tsx +49 -11
- package/src/app/routes/_authenticated/_orders/orders_.$id_.modify.tsx +9 -0
- package/src/app/routes/_authenticated/_orders/utils/use-modify-order.ts +23 -0
- package/src/app/routes/_authenticated/_product-variants/components/add-currency-dropdown.tsx +3 -3
- package/src/app/routes/_authenticated/_product-variants/components/add-stock-location-dropdown.tsx +2 -2
- package/src/app/routes/_authenticated/_products/products.graphql.ts +1 -0
- package/src/i18n/locales/bg.po +3436 -0
- package/src/lib/components/data-input/datetime-input.tsx +1 -1
- package/src/lib/components/data-input/relation-selector.tsx +1 -1
- package/src/lib/components/data-input/struct-form-input.tsx +175 -174
- package/src/lib/components/data-table/data-table.tsx +1 -0
- package/src/lib/components/layout/manage-languages-dialog.tsx +2 -25
- package/src/lib/components/shared/custom-fields-form.tsx +13 -8
- package/src/lib/components/ui/carousel.tsx +2 -2
- package/src/lib/components/ui/chart.tsx +1 -1
- package/src/lib/components/ui/context-menu.tsx +1 -1
- package/src/lib/components/ui/drawer.tsx +1 -1
- package/src/lib/components/ui/grid-layout.tsx +1 -1
- package/src/lib/components/ui/input-group.tsx +1 -0
- package/src/lib/components/ui/input-otp.tsx +1 -1
- package/src/lib/components/ui/menubar.tsx +1 -1
- package/src/lib/components/ui/navigation-menu.tsx +1 -1
- package/src/lib/components/ui/progress.tsx +1 -1
- package/src/lib/components/ui/radio-group.tsx +1 -1
- package/src/lib/components/ui/resizable.tsx +1 -1
- package/src/lib/components/ui/select.tsx +1 -1
- package/src/lib/components/ui/slider.tsx +1 -1
- package/src/lib/components/ui/toggle-group.tsx +2 -2
- package/src/lib/components/ui/toggle.tsx +1 -1
- package/src/lib/framework/component-registry/component-registry.tsx +2 -6
- package/src/lib/framework/extension-api/display-component-extensions.tsx +4 -3
- package/src/lib/framework/extension-api/logic/detail-forms.ts +0 -13
- package/src/lib/framework/extension-api/types/data-table.ts +4 -2
- package/src/lib/framework/extension-api/types/navigation.ts +2 -2
- package/src/lib/framework/form-engine/use-generated-form.tsx +7 -1
- package/src/lib/framework/layout-engine/page-layout.tsx +1 -1
- package/src/lib/framework/nav-menu/nav-menu-extensions.ts +1 -1
- package/src/lib/framework/page/detail-page-route-loader.tsx +1 -1
- package/src/lib/framework/page/page-api.ts +1 -1
- package/src/lib/framework/page/use-detail-page.ts +4 -2
- package/src/lib/framework/page/use-extended-router.tsx +20 -16
- package/src/lib/framework/registry/registry-types.ts +2 -1
- package/src/lib/graphql/graphql-env.d.ts +8 -12
- package/src/lib/providers/channel-provider.tsx +11 -7
- package/LICENSE.md +0 -42
- package/src/app/routes/_authenticated/_facets/components/edit-facet-value.tsx +0 -129
- /package/src/{app/routes/_authenticated/_global-settings → lib}/utils/global-languages.ts +0 -0
|
@@ -28,7 +28,7 @@ export interface RelationSelectorConfig<T = any> {
|
|
|
28
28
|
/** Number of items to load per page */
|
|
29
29
|
pageSize?: number;
|
|
30
30
|
/** Placeholder text for the search input */
|
|
31
|
-
placeholder?:
|
|
31
|
+
placeholder?: string;
|
|
32
32
|
/** Whether to enable multi-select mode */
|
|
33
33
|
multiple?: boolean;
|
|
34
34
|
/** Custom filter function for search */
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
StructCustomFieldConfig,
|
|
21
21
|
StructField,
|
|
22
22
|
} from '@/vdb/framework/form-engine/form-engine-types.js';
|
|
23
|
-
import { isStructFieldConfig } from '@/vdb/framework/form-engine/utils.js';
|
|
23
|
+
import { isReadonlyField, isStructFieldConfig } from '@/vdb/framework/form-engine/utils.js';
|
|
24
24
|
import { useUserSettings } from '@/vdb/hooks/use-user-settings.js';
|
|
25
25
|
import { CustomFieldListInput } from './custom-field-list-input.js';
|
|
26
26
|
import { DateTimeInput } from './datetime-input.js';
|
|
@@ -98,185 +98,61 @@ export function StructFormInput({ fieldDef, ...field }: Readonly<DashboardFormCo
|
|
|
98
98
|
return input?.find(t => t.languageCode === displayLanguage)?.value;
|
|
99
99
|
};
|
|
100
100
|
|
|
101
|
-
const isReadonly = fieldDef
|
|
102
|
-
|
|
103
|
-
// Helper function to render individual struct field inputs
|
|
104
|
-
const renderStructFieldInput = (
|
|
105
|
-
structField: StructField,
|
|
106
|
-
inputField: ControllerRenderProps<any, any>,
|
|
107
|
-
) => {
|
|
108
|
-
const isList = structField.list ?? false;
|
|
109
|
-
|
|
110
|
-
// Helper function to render single input for a struct field
|
|
111
|
-
const renderSingleStructInput = (singleField: ControllerRenderProps<any, any>) => {
|
|
112
|
-
switch (structField.type) {
|
|
113
|
-
case 'string': {
|
|
114
|
-
// Check if the field has options (dropdown)
|
|
115
|
-
const stringField = structField as any; // GraphQL union types need casting
|
|
116
|
-
if (stringField.options && stringField.options.length > 0) {
|
|
117
|
-
return (
|
|
118
|
-
<SelectWithOptions {...singleField} fieldDef={stringField} isListField={false} />
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
return (
|
|
122
|
-
<Input
|
|
123
|
-
value={singleField.value ?? ''}
|
|
124
|
-
onChange={e => singleField.onChange(e.target.value)}
|
|
125
|
-
onBlur={singleField.onBlur}
|
|
126
|
-
name={singleField.name}
|
|
127
|
-
disabled={isReadonly}
|
|
128
|
-
/>
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
case 'int':
|
|
132
|
-
case 'float': {
|
|
133
|
-
const isFloat = structField.type === 'float';
|
|
134
|
-
const numericField = structField as any; // GraphQL union types need casting
|
|
135
|
-
const min = isFloat ? numericField.floatMin : numericField.intMin;
|
|
136
|
-
const max = isFloat ? numericField.floatMax : numericField.intMax;
|
|
137
|
-
const step = isFloat ? numericField.floatStep : numericField.intStep;
|
|
138
|
-
|
|
139
|
-
return (
|
|
140
|
-
<Input
|
|
141
|
-
type="number"
|
|
142
|
-
value={singleField.value ?? ''}
|
|
143
|
-
onChange={e => {
|
|
144
|
-
const value = e.target.valueAsNumber;
|
|
145
|
-
singleField.onChange(isNaN(value) ? undefined : value);
|
|
146
|
-
}}
|
|
147
|
-
onBlur={singleField.onBlur}
|
|
148
|
-
name={singleField.name}
|
|
149
|
-
disabled={isReadonly}
|
|
150
|
-
min={min}
|
|
151
|
-
max={max}
|
|
152
|
-
step={step}
|
|
153
|
-
/>
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
case 'boolean':
|
|
157
|
-
return (
|
|
158
|
-
<Switch
|
|
159
|
-
checked={singleField.value}
|
|
160
|
-
onCheckedChange={singleField.onChange}
|
|
161
|
-
disabled={isReadonly}
|
|
162
|
-
/>
|
|
163
|
-
);
|
|
164
|
-
case 'datetime':
|
|
165
|
-
return <DateTimeInput {...singleField} />;
|
|
166
|
-
default:
|
|
167
|
-
return (
|
|
168
|
-
<Input
|
|
169
|
-
value={singleField.value ?? ''}
|
|
170
|
-
onChange={e => singleField.onChange(e.target.value)}
|
|
171
|
-
onBlur={singleField.onBlur}
|
|
172
|
-
name={singleField.name}
|
|
173
|
-
disabled={isReadonly}
|
|
174
|
-
/>
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
// Handle string fields with options (dropdown) - already handles list case with multi-select
|
|
180
|
-
if (structField.type === 'string') {
|
|
181
|
-
const stringField = structField as any; // GraphQL union types need casting
|
|
182
|
-
if (stringField.options && stringField.options.length > 0) {
|
|
183
|
-
return (
|
|
184
|
-
<SelectWithOptions
|
|
185
|
-
{...inputField}
|
|
186
|
-
fieldDef={stringField}
|
|
187
|
-
disabled={isReadonly}
|
|
188
|
-
isListField={isList}
|
|
189
|
-
/>
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// For list struct fields, wrap with list input
|
|
195
|
-
if (isList) {
|
|
196
|
-
const getDefaultValue = () => {
|
|
197
|
-
switch (structField.type) {
|
|
198
|
-
case 'string':
|
|
199
|
-
return '';
|
|
200
|
-
case 'int':
|
|
201
|
-
case 'float':
|
|
202
|
-
return 0;
|
|
203
|
-
case 'boolean':
|
|
204
|
-
return false;
|
|
205
|
-
case 'datetime':
|
|
206
|
-
return '';
|
|
207
|
-
default:
|
|
208
|
-
return '';
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
// Determine if the field type needs full width
|
|
213
|
-
const needsFullWidth = structField.type === 'text' || structField.type === 'localeText';
|
|
214
|
-
|
|
215
|
-
return (
|
|
216
|
-
<CustomFieldListInput
|
|
217
|
-
{...inputField}
|
|
218
|
-
disabled={isReadonly}
|
|
219
|
-
renderInput={(index, listItemField) => renderSingleStructInput(listItemField)}
|
|
220
|
-
defaultValue={getDefaultValue()}
|
|
221
|
-
/>
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// For non-list fields, render directly
|
|
226
|
-
return renderSingleStructInput(inputField);
|
|
227
|
-
};
|
|
101
|
+
const isReadonly = isReadonlyField(fieldDef);
|
|
228
102
|
|
|
229
103
|
// Edit mode - memoized to prevent focus loss from re-renders
|
|
230
104
|
const EditMode = useMemo(
|
|
231
|
-
() =>
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
<
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
<
|
|
254
|
-
<div className="flex-
|
|
255
|
-
<
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
105
|
+
() =>
|
|
106
|
+
fieldDef && isStructFieldConfig(fieldDef) ? (
|
|
107
|
+
<div className="space-y-4 border rounded-md p-4">
|
|
108
|
+
{!isReadonly && (
|
|
109
|
+
<div className="flex justify-end">
|
|
110
|
+
<Button
|
|
111
|
+
variant="ghost"
|
|
112
|
+
size="sm"
|
|
113
|
+
onClick={() => setIsEditing(false)}
|
|
114
|
+
className="h-8 w-8 p-0 text-muted-foreground hover:text-foreground"
|
|
115
|
+
>
|
|
116
|
+
<CheckIcon className="h-4 w-4" />
|
|
117
|
+
<span className="sr-only">Done</span>
|
|
118
|
+
</Button>
|
|
119
|
+
</div>
|
|
120
|
+
)}
|
|
121
|
+
{fieldDef?.fields.map(structField => (
|
|
122
|
+
<FormField
|
|
123
|
+
key={structField.name}
|
|
124
|
+
control={control}
|
|
125
|
+
name={`${field.name}.${structField.name}`}
|
|
126
|
+
render={({ field: structInputField }) => (
|
|
127
|
+
<FormItem>
|
|
128
|
+
<div className="flex items-baseline gap-4">
|
|
129
|
+
<div className="flex-1">
|
|
130
|
+
<FormLabel>
|
|
131
|
+
{getTranslation(structField.label) ?? structField.name}
|
|
132
|
+
</FormLabel>
|
|
133
|
+
{getTranslation(structField.description) && (
|
|
134
|
+
<FormDescription>
|
|
135
|
+
{getTranslation(structField.description)}
|
|
136
|
+
</FormDescription>
|
|
137
|
+
)}
|
|
138
|
+
</div>
|
|
139
|
+
<div className="flex-[2]">
|
|
140
|
+
<FormControl>
|
|
141
|
+
{renderStructFieldInput(structField, structInputField)}
|
|
142
|
+
</FormControl>
|
|
143
|
+
<FormMessage />
|
|
144
|
+
</div>
|
|
263
145
|
</div>
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
</FormItem>
|
|
272
|
-
)}
|
|
273
|
-
/>
|
|
274
|
-
))}
|
|
275
|
-
</div>
|
|
276
|
-
),
|
|
277
|
-
[fieldDef, control, field.name, getTranslation, renderStructFieldInput, isReadonly],
|
|
146
|
+
</FormItem>
|
|
147
|
+
)}
|
|
148
|
+
/>
|
|
149
|
+
))}
|
|
150
|
+
</div>
|
|
151
|
+
) : null,
|
|
152
|
+
[fieldDef, control, field.name, getTranslation, isReadonly],
|
|
278
153
|
);
|
|
279
154
|
|
|
155
|
+
// Early return if not a struct field config
|
|
280
156
|
if (!fieldDef || !isStructFieldConfig(fieldDef)) {
|
|
281
157
|
return null;
|
|
282
158
|
}
|
|
@@ -317,3 +193,128 @@ export function StructFormInput({ fieldDef, ...field }: Readonly<DashboardFormCo
|
|
|
317
193
|
/>
|
|
318
194
|
);
|
|
319
195
|
}
|
|
196
|
+
|
|
197
|
+
// Helper function to render individual struct field inputs
|
|
198
|
+
const renderStructFieldInput = (
|
|
199
|
+
structField: StructField,
|
|
200
|
+
inputField: ControllerRenderProps<any, any>,
|
|
201
|
+
isReadonly: boolean = false,
|
|
202
|
+
) => {
|
|
203
|
+
const isList = structField.list ?? false;
|
|
204
|
+
|
|
205
|
+
// Helper function to render single input for a struct field
|
|
206
|
+
const renderSingleStructInput = (singleField: ControllerRenderProps<any, any>) => {
|
|
207
|
+
switch (structField.type) {
|
|
208
|
+
case 'string': {
|
|
209
|
+
// Check if the field has options (dropdown)
|
|
210
|
+
const stringField = structField as any; // GraphQL union types need casting
|
|
211
|
+
if (stringField.options && stringField.options.length > 0) {
|
|
212
|
+
return <SelectWithOptions {...singleField} fieldDef={stringField} isListField={false} />;
|
|
213
|
+
}
|
|
214
|
+
return (
|
|
215
|
+
<Input
|
|
216
|
+
value={singleField.value ?? ''}
|
|
217
|
+
onChange={e => singleField.onChange(e.target.value)}
|
|
218
|
+
onBlur={singleField.onBlur}
|
|
219
|
+
name={singleField.name}
|
|
220
|
+
disabled={isReadonly}
|
|
221
|
+
/>
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
case 'int':
|
|
225
|
+
case 'float': {
|
|
226
|
+
const isFloat = structField.type === 'float';
|
|
227
|
+
const numericField = structField as any; // GraphQL union types need casting
|
|
228
|
+
const min = isFloat ? numericField.floatMin : numericField.intMin;
|
|
229
|
+
const max = isFloat ? numericField.floatMax : numericField.intMax;
|
|
230
|
+
const step = isFloat ? numericField.floatStep : numericField.intStep;
|
|
231
|
+
|
|
232
|
+
return (
|
|
233
|
+
<Input
|
|
234
|
+
type="number"
|
|
235
|
+
value={singleField.value ?? ''}
|
|
236
|
+
onChange={e => {
|
|
237
|
+
const value = e.target.valueAsNumber;
|
|
238
|
+
singleField.onChange(isNaN(value) ? undefined : value);
|
|
239
|
+
}}
|
|
240
|
+
onBlur={singleField.onBlur}
|
|
241
|
+
name={singleField.name}
|
|
242
|
+
disabled={isReadonly}
|
|
243
|
+
min={min}
|
|
244
|
+
max={max}
|
|
245
|
+
step={step}
|
|
246
|
+
/>
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
case 'boolean':
|
|
250
|
+
return (
|
|
251
|
+
<Switch
|
|
252
|
+
checked={singleField.value}
|
|
253
|
+
onCheckedChange={singleField.onChange}
|
|
254
|
+
disabled={isReadonly}
|
|
255
|
+
/>
|
|
256
|
+
);
|
|
257
|
+
case 'datetime':
|
|
258
|
+
return <DateTimeInput {...singleField} />;
|
|
259
|
+
default:
|
|
260
|
+
return (
|
|
261
|
+
<Input
|
|
262
|
+
value={singleField.value ?? ''}
|
|
263
|
+
onChange={e => singleField.onChange(e.target.value)}
|
|
264
|
+
onBlur={singleField.onBlur}
|
|
265
|
+
name={singleField.name}
|
|
266
|
+
disabled={isReadonly}
|
|
267
|
+
/>
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
// Handle string fields with options (dropdown) - already handles list case with multi-select
|
|
273
|
+
if (structField.type === 'string') {
|
|
274
|
+
const stringField = structField as any; // GraphQL union types need casting
|
|
275
|
+
if (stringField.options && stringField.options.length > 0) {
|
|
276
|
+
return (
|
|
277
|
+
<SelectWithOptions
|
|
278
|
+
{...inputField}
|
|
279
|
+
fieldDef={stringField}
|
|
280
|
+
disabled={isReadonly}
|
|
281
|
+
isListField={isList}
|
|
282
|
+
/>
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// For list struct fields, wrap with list input
|
|
288
|
+
if (isList) {
|
|
289
|
+
const getDefaultValue = () => {
|
|
290
|
+
switch (structField.type) {
|
|
291
|
+
case 'string':
|
|
292
|
+
return '';
|
|
293
|
+
case 'int':
|
|
294
|
+
case 'float':
|
|
295
|
+
return 0;
|
|
296
|
+
case 'boolean':
|
|
297
|
+
return false;
|
|
298
|
+
case 'datetime':
|
|
299
|
+
return '';
|
|
300
|
+
default:
|
|
301
|
+
return '';
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
// Determine if the field type needs full width
|
|
306
|
+
const needsFullWidth = structField.type === 'text' || structField.type === 'localeText';
|
|
307
|
+
|
|
308
|
+
return (
|
|
309
|
+
<CustomFieldListInput
|
|
310
|
+
{...inputField}
|
|
311
|
+
disabled={isReadonly}
|
|
312
|
+
renderInput={(index, listItemField) => renderSingleStructInput(listItemField)}
|
|
313
|
+
defaultValue={getDefaultValue()}
|
|
314
|
+
/>
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// For non-list fields, render directly
|
|
319
|
+
return renderSingleStructInput(inputField);
|
|
320
|
+
};
|
|
@@ -18,6 +18,7 @@ import { useChannel } from '@/vdb/hooks/use-channel.js';
|
|
|
18
18
|
import { useLocalFormat } from '@/vdb/hooks/use-local-format.js';
|
|
19
19
|
import { usePermissions } from '@/vdb/hooks/use-permissions.js';
|
|
20
20
|
import { useSortedLanguages } from '@/vdb/hooks/use-sorted-languages.js';
|
|
21
|
+
import { globalLanguageCodes } from '@/vdb/utils/global-languages.js';
|
|
21
22
|
import { Trans } from '@lingui/react/macro';
|
|
22
23
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
23
24
|
import { AlertCircle, Lock } from 'lucide-react';
|
|
@@ -68,36 +69,12 @@ const updateChannelDocument = graphql(`
|
|
|
68
69
|
}
|
|
69
70
|
`);
|
|
70
71
|
|
|
71
|
-
// All possible language codes for global settings - includes more than what might be globally enabled
|
|
72
|
-
const ALL_LANGUAGE_CODES = [
|
|
73
|
-
'en',
|
|
74
|
-
'es',
|
|
75
|
-
'fr',
|
|
76
|
-
'de',
|
|
77
|
-
'it',
|
|
78
|
-
'pt',
|
|
79
|
-
'nl',
|
|
80
|
-
'pl',
|
|
81
|
-
'ru',
|
|
82
|
-
'ja',
|
|
83
|
-
'zh',
|
|
84
|
-
'ko',
|
|
85
|
-
'ar',
|
|
86
|
-
'hi',
|
|
87
|
-
'sv',
|
|
88
|
-
'da',
|
|
89
|
-
'nb',
|
|
90
|
-
'nn',
|
|
91
|
-
'fi',
|
|
92
|
-
];
|
|
93
|
-
|
|
94
72
|
interface ManageLanguagesDialogProps {
|
|
95
73
|
open: boolean;
|
|
96
74
|
onClose: () => void;
|
|
97
75
|
}
|
|
98
76
|
|
|
99
77
|
export function ManageLanguagesDialog({ open, onClose }: ManageLanguagesDialogProps) {
|
|
100
|
-
const { formatLanguageName } = useLocalFormat();
|
|
101
78
|
const { activeChannel } = useChannel();
|
|
102
79
|
const { hasPermissions } = usePermissions();
|
|
103
80
|
const queryClient = useQueryClient();
|
|
@@ -309,7 +286,7 @@ export function ManageLanguagesDialog({ open, onClose }: ManageLanguagesDialogPr
|
|
|
309
286
|
value={globalLanguages}
|
|
310
287
|
onChange={handleGlobalLanguagesChange}
|
|
311
288
|
multiple={true}
|
|
312
|
-
availableLanguageCodes={
|
|
289
|
+
availableLanguageCodes={globalLanguageCodes}
|
|
313
290
|
/>
|
|
314
291
|
</div>
|
|
315
292
|
<p className="text-xs text-muted-foreground">
|
|
@@ -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:
|
|
124
|
-
control: Control<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 = (
|
|
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:
|
|
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;
|
|
@@ -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>
|
|
@@ -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
|
|
@@ -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
|
);
|
|
@@ -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,
|
|
@@ -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
|