@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.
- package/dist/plugin/constants.js +2 -2
- package/dist/plugin/dashboard.plugin.js +1 -1
- 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/_collections/collections.graphql.ts +1 -0
- package/src/app/routes/_authenticated/_collections/collections.tsx +249 -167
- package/src/app/routes/_authenticated/_collections/components/collection-bulk-actions.tsx +8 -0
- package/src/app/routes/_authenticated/_collections/components/move-collections-dialog.tsx +4 -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/app/routes/_authenticated/_tax-rates/tax-rates_.$id.tsx +2 -9
- 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/number-input.tsx +24 -5
- 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-utils.ts +241 -1
- package/src/lib/components/data-table/data-table.tsx +190 -60
- 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/shared/paginated-list-data-table.tsx +19 -0
- package/src/lib/components/ui/alert.tsx +1 -1
- 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/list-page.tsx +62 -38
- 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/hooks/use-drag-and-drop.ts +86 -0
- 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
|
@@ -3,11 +3,14 @@ import { Input } from '@/vdb/components/ui/input.js';
|
|
|
3
3
|
|
|
4
4
|
import { DashboardFormComponentProps } from '@/vdb/framework/form-engine/form-engine-types.js';
|
|
5
5
|
import { isReadonlyField } from '@/vdb/framework/form-engine/utils.js';
|
|
6
|
+
import { ReactNode } from 'react';
|
|
6
7
|
|
|
7
8
|
export type NumberInputProps = DashboardFormComponentProps & {
|
|
8
9
|
min?: number;
|
|
9
10
|
max?: number;
|
|
10
11
|
step?: number;
|
|
12
|
+
prefix?: ReactNode;
|
|
13
|
+
suffix?: ReactNode;
|
|
11
14
|
};
|
|
12
15
|
|
|
13
16
|
/**
|
|
@@ -17,28 +20,43 @@ export type NumberInputProps = DashboardFormComponentProps & {
|
|
|
17
20
|
* @docsCategory form-components
|
|
18
21
|
* @docsPage NumberInput
|
|
19
22
|
*/
|
|
20
|
-
export function NumberInput({
|
|
23
|
+
export function NumberInput({
|
|
24
|
+
fieldDef,
|
|
25
|
+
onChange,
|
|
26
|
+
prefix: overridePrefix,
|
|
27
|
+
suffix: overrideSuffix,
|
|
28
|
+
...fieldProps
|
|
29
|
+
}: Readonly<NumberInputProps>) {
|
|
21
30
|
const readOnly = fieldProps.disabled || isReadonlyField(fieldDef);
|
|
22
31
|
const isFloat = fieldDef ? fieldDef.type === 'float' : false;
|
|
23
32
|
const min = fieldProps.min ?? fieldDef?.ui?.min;
|
|
24
33
|
const max = fieldProps.max ?? fieldDef?.ui?.max;
|
|
25
34
|
const step = fieldProps.step ?? (fieldDef?.ui?.step || (isFloat ? 0.01 : 1));
|
|
26
|
-
const prefix = fieldDef?.ui?.prefix;
|
|
27
|
-
const suffix = fieldDef?.ui?.suffix;
|
|
35
|
+
const prefix = overridePrefix ?? fieldDef?.ui?.prefix;
|
|
36
|
+
const suffix = overrideSuffix ?? fieldDef?.ui?.suffix;
|
|
28
37
|
const shouldUseAffixedInput = prefix || suffix;
|
|
38
|
+
const value = fieldProps.value ?? '';
|
|
29
39
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
30
40
|
if (readOnly) return;
|
|
31
|
-
|
|
41
|
+
|
|
42
|
+
let numValue = e.target.valueAsNumber;
|
|
43
|
+
|
|
44
|
+
if (Number.isNaN(numValue) && e.target.value) {
|
|
45
|
+
const normalized = e.target.value.replace(',', '.');
|
|
46
|
+
numValue = Number(normalized);
|
|
47
|
+
}
|
|
48
|
+
|
|
32
49
|
if (Number.isNaN(numValue)) {
|
|
33
50
|
onChange(null);
|
|
34
51
|
} else {
|
|
35
|
-
onChange(
|
|
52
|
+
onChange(numValue);
|
|
36
53
|
}
|
|
37
54
|
};
|
|
38
55
|
if (shouldUseAffixedInput) {
|
|
39
56
|
return (
|
|
40
57
|
<AffixedInput
|
|
41
58
|
{...fieldProps}
|
|
59
|
+
value={value}
|
|
42
60
|
type="number"
|
|
43
61
|
onChange={handleChange}
|
|
44
62
|
min={min}
|
|
@@ -57,6 +75,7 @@ export function NumberInput({ fieldDef, onChange, ...fieldProps }: Readonly<Numb
|
|
|
57
75
|
type="number"
|
|
58
76
|
onChange={handleChange}
|
|
59
77
|
{...fieldProps}
|
|
78
|
+
value={value}
|
|
60
79
|
min={min}
|
|
61
80
|
max={max}
|
|
62
81
|
step={step}
|
|
@@ -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
|
+
};
|