@vendure/dashboard 3.3.8-master-202507290247 → 3.3.8-master-202507300243
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 +4 -4
- package/src/app/routes/_authenticated/_collections/components/collection-contents-preview-table.tsx +1 -1
- package/src/app/routes/_authenticated/_collections/components/collection-filters-selector.tsx +11 -78
- package/src/app/routes/_authenticated/_payment-methods/components/payment-eligibility-checker-selector.tsx +11 -81
- package/src/app/routes/_authenticated/_payment-methods/components/payment-handler-selector.tsx +10 -77
- package/src/app/routes/_authenticated/_promotions/components/promotion-actions-selector.tsx +12 -87
- package/src/app/routes/_authenticated/_promotions/components/promotion-conditions-selector.tsx +12 -87
- package/src/app/routes/_authenticated/_shipping-methods/components/shipping-calculator-selector.tsx +10 -80
- package/src/app/routes/_authenticated/_shipping-methods/components/shipping-eligibility-checker-selector.tsx +10 -79
- package/src/app/routes/_authenticated/_shipping-methods/shipping-methods_.$id.tsx +8 -6
- package/src/lib/components/data-input/combination-mode-input.tsx +52 -0
- package/src/lib/components/data-input/configurable-operation-list-input.tsx +433 -0
- package/src/lib/components/data-input/custom-field-list-input.tsx +297 -0
- package/src/lib/components/data-input/datetime-input.tsx +5 -2
- package/src/lib/components/data-input/default-relation-input.tsx +599 -0
- package/src/lib/components/data-input/index.ts +6 -0
- package/src/lib/components/data-input/product-multi-selector.tsx +426 -0
- package/src/lib/components/data-input/relation-selector.tsx +7 -6
- package/src/lib/components/data-input/select-with-options.tsx +84 -0
- package/src/lib/components/data-input/struct-form-input.tsx +324 -0
- package/src/lib/components/shared/configurable-operation-arg-input.tsx +365 -21
- package/src/lib/components/shared/configurable-operation-input.tsx +81 -41
- package/src/lib/components/shared/configurable-operation-multi-selector.tsx +260 -0
- package/src/lib/components/shared/configurable-operation-selector.tsx +156 -0
- package/src/lib/components/shared/custom-fields-form.tsx +207 -36
- package/src/lib/components/shared/multi-select.tsx +1 -1
- package/src/lib/components/ui/form.tsx +4 -4
- package/src/lib/framework/extension-api/input-component-extensions.tsx +5 -1
- package/src/lib/framework/form-engine/form-schema-tools.spec.ts +472 -0
- package/src/lib/framework/form-engine/form-schema-tools.ts +340 -5
- package/src/lib/framework/form-engine/use-generated-form.tsx +24 -8
- package/src/lib/framework/form-engine/utils.ts +3 -9
- package/src/lib/framework/layout-engine/page-layout.tsx +11 -3
- package/src/lib/framework/page/use-detail-page.ts +3 -3
- package/src/lib/lib/utils.ts +26 -24
|
@@ -1,51 +1,395 @@
|
|
|
1
1
|
import { InputComponent } from '@/vdb/framework/component-registry/dynamic-component.js';
|
|
2
2
|
import { ConfigurableOperationDefFragment } from '@/vdb/graphql/fragments.js';
|
|
3
|
+
import { RelationCustomFieldConfig } from '@vendure/common/lib/generated-types';
|
|
3
4
|
import { ConfigArgType } from '@vendure/core';
|
|
5
|
+
import { AffixedInput } from '../data-input/affixed-input.js';
|
|
6
|
+
import { ConfigurableOperationListInput } from '../data-input/configurable-operation-list-input.js';
|
|
7
|
+
import { DateTimeInput } from '../data-input/datetime-input.js';
|
|
8
|
+
import { DefaultRelationInput } from '../data-input/default-relation-input.js';
|
|
4
9
|
import { FacetValueInput } from '../data-input/facet-value-input.js';
|
|
10
|
+
import { Input } from '../ui/input.js';
|
|
11
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select.js';
|
|
12
|
+
import { Switch } from '../ui/switch.js';
|
|
13
|
+
import { Textarea } from '../ui/textarea.js';
|
|
5
14
|
|
|
6
15
|
export interface ConfigurableOperationArgInputProps {
|
|
7
16
|
definition: ConfigurableOperationDefFragment['args'][number];
|
|
8
17
|
readOnly?: boolean;
|
|
9
18
|
value: string;
|
|
10
19
|
onChange: (value: any) => void;
|
|
20
|
+
position?: number;
|
|
11
21
|
}
|
|
12
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Maps Vendure UI component names to their corresponding Dashboard input component IDs
|
|
25
|
+
*/
|
|
26
|
+
const UI_COMPONENT_MAP = {
|
|
27
|
+
'number-form-input': 'vendure:numberInput',
|
|
28
|
+
'currency-form-input': 'vendure:currencyInput',
|
|
29
|
+
'facet-value-form-input': 'facet-value-input',
|
|
30
|
+
'product-selector-form-input': 'vendure:productSelectorInput',
|
|
31
|
+
'customer-group-form-input': 'vendure:customerGroupInput',
|
|
32
|
+
'date-form-input': 'date-input',
|
|
33
|
+
'textarea-form-input': 'textarea-input',
|
|
34
|
+
'password-form-input': 'vendure:passwordInput',
|
|
35
|
+
'json-editor-form-input': 'vendure:jsonEditorInput',
|
|
36
|
+
'html-editor-form-input': 'vendure:htmlEditorInput',
|
|
37
|
+
'rich-text-form-input': 'vendure:richTextInput',
|
|
38
|
+
'boolean-form-input': 'boolean-input',
|
|
39
|
+
'select-form-input': 'select-input',
|
|
40
|
+
'text-form-input': 'vendure:textInput',
|
|
41
|
+
'product-multi-form-input': 'vendure:productMultiInput',
|
|
42
|
+
'combination-mode-form-input': 'vendure:combinationModeInput',
|
|
43
|
+
'relation-form-input': 'vendure:relationInput',
|
|
44
|
+
'struct-form-input': 'vendure:structInput',
|
|
45
|
+
} as const;
|
|
46
|
+
|
|
13
47
|
export function ConfigurableOperationArgInput({
|
|
14
48
|
definition,
|
|
15
49
|
value,
|
|
16
50
|
onChange,
|
|
17
51
|
readOnly,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
52
|
+
position,
|
|
53
|
+
}: Readonly<ConfigurableOperationArgInputProps>) {
|
|
54
|
+
const uiComponent = (definition.ui as any)?.component;
|
|
55
|
+
const argType = definition.type as ConfigArgType;
|
|
56
|
+
const isList = definition.list ?? false;
|
|
57
|
+
|
|
58
|
+
// Handle specific UI components first
|
|
59
|
+
if (uiComponent) {
|
|
60
|
+
switch (uiComponent) {
|
|
61
|
+
case 'product-selector-form-input': {
|
|
62
|
+
const entityType =
|
|
63
|
+
(definition.ui as any)?.selectionMode === 'variant' ? 'ProductVariant' : 'Product';
|
|
64
|
+
const isMultiple = (definition.ui as any)?.multiple ?? false;
|
|
65
|
+
return (
|
|
66
|
+
<DefaultRelationInput
|
|
67
|
+
fieldDef={
|
|
68
|
+
{
|
|
69
|
+
entity: entityType,
|
|
70
|
+
list: isMultiple,
|
|
71
|
+
} as RelationCustomFieldConfig
|
|
72
|
+
}
|
|
73
|
+
field={{
|
|
74
|
+
value,
|
|
75
|
+
onChange,
|
|
76
|
+
onBlur: () => {},
|
|
77
|
+
name: '',
|
|
78
|
+
ref: () => {},
|
|
79
|
+
}}
|
|
80
|
+
disabled={readOnly}
|
|
81
|
+
/>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
case 'customer-group-form-input': {
|
|
85
|
+
const isCustomerGroupMultiple = (definition.ui as any)?.multiple ?? false;
|
|
86
|
+
return (
|
|
87
|
+
<DefaultRelationInput
|
|
88
|
+
fieldDef={
|
|
89
|
+
{
|
|
90
|
+
entity: 'CustomerGroup',
|
|
91
|
+
list: isCustomerGroupMultiple,
|
|
92
|
+
} as RelationCustomFieldConfig
|
|
93
|
+
}
|
|
94
|
+
field={{
|
|
95
|
+
value,
|
|
96
|
+
onChange,
|
|
97
|
+
onBlur: () => {},
|
|
98
|
+
name: '',
|
|
99
|
+
ref: () => {},
|
|
100
|
+
}}
|
|
101
|
+
disabled={readOnly}
|
|
102
|
+
/>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
case 'facet-value-form-input': {
|
|
106
|
+
return <FacetValueInput value={value} onChange={onChange} readOnly={readOnly} />;
|
|
107
|
+
}
|
|
108
|
+
case 'select-form-input': {
|
|
109
|
+
return (
|
|
110
|
+
<SelectInput
|
|
111
|
+
definition={definition}
|
|
112
|
+
value={value}
|
|
113
|
+
onChange={onChange}
|
|
114
|
+
readOnly={readOnly}
|
|
115
|
+
/>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
case 'textarea-form-input': {
|
|
119
|
+
return (
|
|
120
|
+
<TextareaInput
|
|
121
|
+
definition={definition}
|
|
122
|
+
value={value}
|
|
123
|
+
onChange={onChange}
|
|
124
|
+
readOnly={readOnly}
|
|
125
|
+
/>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
case 'date-form-input': {
|
|
129
|
+
return <DateTimeInput value={value} onChange={onChange} disabled={readOnly} />;
|
|
130
|
+
}
|
|
131
|
+
case 'boolean-form-input': {
|
|
132
|
+
return <BooleanInput value={value} onChange={onChange} readOnly={readOnly} />;
|
|
133
|
+
}
|
|
134
|
+
case 'number-form-input': {
|
|
135
|
+
return (
|
|
136
|
+
<NumberInput
|
|
137
|
+
definition={definition}
|
|
138
|
+
value={value}
|
|
139
|
+
onChange={onChange}
|
|
140
|
+
readOnly={readOnly}
|
|
141
|
+
/>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
case 'currency-form-input': {
|
|
145
|
+
return (
|
|
146
|
+
<CurrencyInput
|
|
147
|
+
definition={definition}
|
|
148
|
+
value={value}
|
|
149
|
+
onChange={onChange}
|
|
150
|
+
readOnly={readOnly}
|
|
151
|
+
/>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
default: {
|
|
155
|
+
// Try to use the component registry for other UI components
|
|
156
|
+
const componentId = UI_COMPONENT_MAP[uiComponent as keyof typeof UI_COMPONENT_MAP];
|
|
157
|
+
if (componentId) {
|
|
158
|
+
try {
|
|
159
|
+
return (
|
|
160
|
+
<InputComponent
|
|
161
|
+
id={componentId}
|
|
162
|
+
value={value}
|
|
163
|
+
onChange={onChange}
|
|
164
|
+
readOnly={readOnly}
|
|
165
|
+
position={position}
|
|
166
|
+
definition={definition}
|
|
167
|
+
{...(definition.ui as any)}
|
|
168
|
+
/>
|
|
169
|
+
);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.warn(
|
|
172
|
+
`Failed to load UI component ${uiComponent}, falling back to type-based input`,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Handle list fields with array wrapper
|
|
181
|
+
if (isList) {
|
|
182
|
+
return (
|
|
183
|
+
<ConfigurableOperationListInput
|
|
184
|
+
definition={definition}
|
|
185
|
+
value={value}
|
|
186
|
+
onChange={onChange}
|
|
187
|
+
readOnly={readOnly}
|
|
188
|
+
/>
|
|
189
|
+
);
|
|
21
190
|
}
|
|
22
|
-
|
|
191
|
+
|
|
192
|
+
// Fall back to type-based rendering
|
|
193
|
+
switch (argType) {
|
|
23
194
|
case 'boolean':
|
|
195
|
+
return <BooleanInput value={value} onChange={onChange} readOnly={readOnly} />;
|
|
196
|
+
|
|
197
|
+
case 'int':
|
|
198
|
+
case 'float':
|
|
24
199
|
return (
|
|
25
|
-
<
|
|
26
|
-
id="vendure:checkboxInput"
|
|
27
|
-
value={value}
|
|
28
|
-
onChange={(value: any) => onChange(value)}
|
|
29
|
-
readOnly={readOnly}
|
|
30
|
-
/>
|
|
200
|
+
<NumberInput definition={definition} value={value} onChange={onChange} readOnly={readOnly} />
|
|
31
201
|
);
|
|
32
|
-
|
|
202
|
+
|
|
203
|
+
case 'datetime':
|
|
204
|
+
return <DateTimeInput value={value} onChange={onChange} disabled={readOnly} />;
|
|
205
|
+
|
|
206
|
+
case 'ID':
|
|
207
|
+
// ID fields typically need specialized selectors
|
|
33
208
|
return (
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
value={value}
|
|
37
|
-
onChange={
|
|
38
|
-
|
|
209
|
+
<Input
|
|
210
|
+
type="text"
|
|
211
|
+
value={value || ''}
|
|
212
|
+
onChange={e => onChange(e.target.value)}
|
|
213
|
+
disabled={readOnly}
|
|
214
|
+
placeholder="Enter ID..."
|
|
215
|
+
className="bg-background"
|
|
39
216
|
/>
|
|
40
217
|
);
|
|
218
|
+
|
|
219
|
+
case 'string':
|
|
41
220
|
default:
|
|
42
221
|
return (
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
value={value}
|
|
46
|
-
onChange={
|
|
47
|
-
|
|
222
|
+
<Input
|
|
223
|
+
type="text"
|
|
224
|
+
value={value || ''}
|
|
225
|
+
onChange={e => onChange(e.target.value)}
|
|
226
|
+
disabled={readOnly}
|
|
227
|
+
className="bg-background"
|
|
48
228
|
/>
|
|
49
229
|
);
|
|
50
230
|
}
|
|
51
231
|
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Boolean input component
|
|
235
|
+
*/
|
|
236
|
+
function BooleanInput({
|
|
237
|
+
value,
|
|
238
|
+
onChange,
|
|
239
|
+
readOnly,
|
|
240
|
+
}: Readonly<{
|
|
241
|
+
value: string;
|
|
242
|
+
onChange: (value: string) => void;
|
|
243
|
+
readOnly?: boolean;
|
|
244
|
+
}>) {
|
|
245
|
+
const boolValue = value === 'true';
|
|
246
|
+
|
|
247
|
+
return (
|
|
248
|
+
<Switch
|
|
249
|
+
checked={boolValue}
|
|
250
|
+
onCheckedChange={checked => onChange(checked.toString())}
|
|
251
|
+
disabled={readOnly}
|
|
252
|
+
/>
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Number input component with support for UI configuration
|
|
258
|
+
*/
|
|
259
|
+
function NumberInput({
|
|
260
|
+
definition,
|
|
261
|
+
value,
|
|
262
|
+
onChange,
|
|
263
|
+
readOnly,
|
|
264
|
+
}: Readonly<{
|
|
265
|
+
definition: ConfigurableOperationDefFragment['args'][number];
|
|
266
|
+
value: string;
|
|
267
|
+
onChange: (value: string) => void;
|
|
268
|
+
readOnly?: boolean;
|
|
269
|
+
}>) {
|
|
270
|
+
const ui = definition.ui as any;
|
|
271
|
+
const isFloat = (definition.type as ConfigArgType) === 'float';
|
|
272
|
+
const min = ui?.min;
|
|
273
|
+
const max = ui?.max;
|
|
274
|
+
const step = ui?.step || (isFloat ? 0.01 : 1);
|
|
275
|
+
const prefix = ui?.prefix;
|
|
276
|
+
const suffix = ui?.suffix;
|
|
277
|
+
|
|
278
|
+
const numericValue = value ? parseFloat(value) : '';
|
|
279
|
+
|
|
280
|
+
return (
|
|
281
|
+
<AffixedInput
|
|
282
|
+
type="number"
|
|
283
|
+
value={numericValue}
|
|
284
|
+
onChange={e => {
|
|
285
|
+
const val = e.target.valueAsNumber;
|
|
286
|
+
onChange(isNaN(val) ? '' : val.toString());
|
|
287
|
+
}}
|
|
288
|
+
disabled={readOnly}
|
|
289
|
+
min={min}
|
|
290
|
+
max={max}
|
|
291
|
+
step={step}
|
|
292
|
+
prefix={prefix}
|
|
293
|
+
suffix={suffix}
|
|
294
|
+
className="bg-background"
|
|
295
|
+
/>
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Currency input component
|
|
301
|
+
*/
|
|
302
|
+
function CurrencyInput({
|
|
303
|
+
definition,
|
|
304
|
+
value,
|
|
305
|
+
onChange,
|
|
306
|
+
readOnly,
|
|
307
|
+
}: Readonly<{
|
|
308
|
+
definition: ConfigurableOperationDefFragment['args'][number];
|
|
309
|
+
value: string;
|
|
310
|
+
onChange: (value: string) => void;
|
|
311
|
+
readOnly?: boolean;
|
|
312
|
+
}>) {
|
|
313
|
+
const numericValue = value ? parseInt(value, 10) : '';
|
|
314
|
+
|
|
315
|
+
return (
|
|
316
|
+
<AffixedInput
|
|
317
|
+
type="number"
|
|
318
|
+
value={numericValue}
|
|
319
|
+
onChange={e => {
|
|
320
|
+
const val = e.target.valueAsNumber;
|
|
321
|
+
onChange(isNaN(val) ? '0' : val.toString());
|
|
322
|
+
}}
|
|
323
|
+
disabled={readOnly}
|
|
324
|
+
min={0}
|
|
325
|
+
step={1}
|
|
326
|
+
prefix="$"
|
|
327
|
+
className="bg-background"
|
|
328
|
+
/>
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Select input component with options
|
|
334
|
+
*/
|
|
335
|
+
function SelectInput({
|
|
336
|
+
definition,
|
|
337
|
+
value,
|
|
338
|
+
onChange,
|
|
339
|
+
readOnly,
|
|
340
|
+
}: Readonly<{
|
|
341
|
+
definition: ConfigurableOperationDefFragment['args'][number];
|
|
342
|
+
value: string;
|
|
343
|
+
onChange: (value: string) => void;
|
|
344
|
+
readOnly?: boolean;
|
|
345
|
+
}>) {
|
|
346
|
+
const ui = definition.ui as any;
|
|
347
|
+
const options = ui?.options || [];
|
|
348
|
+
|
|
349
|
+
return (
|
|
350
|
+
<Select value={value} onValueChange={onChange} disabled={readOnly}>
|
|
351
|
+
<SelectTrigger className="bg-background mb-0">
|
|
352
|
+
<SelectValue placeholder="Select an option..." />
|
|
353
|
+
</SelectTrigger>
|
|
354
|
+
<SelectContent>
|
|
355
|
+
{options.map((option: any) => (
|
|
356
|
+
<SelectItem key={option.value} value={option.value}>
|
|
357
|
+
{typeof option.label === 'string'
|
|
358
|
+
? option.label
|
|
359
|
+
: option.label?.[0]?.value || option.value}
|
|
360
|
+
</SelectItem>
|
|
361
|
+
))}
|
|
362
|
+
</SelectContent>
|
|
363
|
+
</Select>
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Textarea input component
|
|
369
|
+
*/
|
|
370
|
+
function TextareaInput({
|
|
371
|
+
definition,
|
|
372
|
+
value,
|
|
373
|
+
onChange,
|
|
374
|
+
readOnly,
|
|
375
|
+
}: Readonly<{
|
|
376
|
+
definition: ConfigurableOperationDefFragment['args'][number];
|
|
377
|
+
value: string;
|
|
378
|
+
onChange: (value: string) => void;
|
|
379
|
+
readOnly?: boolean;
|
|
380
|
+
}>) {
|
|
381
|
+
const ui = definition.ui as any;
|
|
382
|
+
const spellcheck = ui?.spellcheck ?? true;
|
|
383
|
+
|
|
384
|
+
return (
|
|
385
|
+
<Textarea
|
|
386
|
+
value={value || ''}
|
|
387
|
+
onChange={e => onChange(e.target.value)}
|
|
388
|
+
disabled={readOnly}
|
|
389
|
+
spellCheck={spellcheck}
|
|
390
|
+
placeholder="Enter text..."
|
|
391
|
+
rows={4}
|
|
392
|
+
className="bg-background"
|
|
393
|
+
/>
|
|
394
|
+
);
|
|
395
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { ConfigurableOperationDefFragment } from '@/vdb/graphql/fragments.js';
|
|
2
2
|
import { ConfigurableOperationInput as ConfigurableOperationInputType } from '@vendure/common/lib/generated-types';
|
|
3
|
-
import {
|
|
3
|
+
import { X } from 'lucide-react';
|
|
4
4
|
import { useForm } from 'react-hook-form';
|
|
5
5
|
import { Button } from '../ui/button.js';
|
|
6
|
+
import { Card, CardContent, CardHeader } from '../ui/card.js';
|
|
6
7
|
import { Form, FormControl, FormField, FormItem, FormLabel } from '../ui/form.js';
|
|
7
8
|
import { ConfigurableOperationArgInput } from './configurable-operation-arg-input.js';
|
|
8
9
|
|
|
@@ -26,7 +27,7 @@ export function ConfigurableOperationInput({
|
|
|
26
27
|
value,
|
|
27
28
|
onChange,
|
|
28
29
|
onRemove,
|
|
29
|
-
}: ConfigurableOperationInputProps) {
|
|
30
|
+
}: Readonly<ConfigurableOperationInputProps>) {
|
|
30
31
|
const form = useForm({
|
|
31
32
|
defaultValues: {
|
|
32
33
|
...value,
|
|
@@ -49,46 +50,85 @@ export function ConfigurableOperationInput({
|
|
|
49
50
|
};
|
|
50
51
|
|
|
51
52
|
return (
|
|
52
|
-
<
|
|
53
|
-
<
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
<div className="
|
|
57
|
-
{
|
|
58
|
-
|
|
53
|
+
<div>
|
|
54
|
+
<Card className="bg-muted/50 shadow-none">
|
|
55
|
+
<CardHeader className="pb-3">
|
|
56
|
+
<div className="flex items-start justify-between">
|
|
57
|
+
<div className="flex-1 min-w-0">
|
|
58
|
+
{!hideDescription && (
|
|
59
|
+
<div className="font-medium text-sm text-foreground leading-relaxed">
|
|
60
|
+
{interpolateDescription(operationDefinition, value.arguments)}
|
|
61
|
+
</div>
|
|
62
|
+
)}
|
|
63
|
+
|
|
64
|
+
{operationDefinition.code && (
|
|
65
|
+
<div className="text-xs text-muted-foreground mt-1 font-mono">
|
|
66
|
+
{operationDefinition.code}
|
|
67
|
+
</div>
|
|
68
|
+
)}
|
|
59
69
|
</div>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
70
|
+
|
|
71
|
+
{removable !== false && (
|
|
72
|
+
<Button
|
|
73
|
+
variant="ghost"
|
|
74
|
+
size="sm"
|
|
75
|
+
onClick={onRemove}
|
|
76
|
+
className="h-8 w-8 p-0 hover:bg-destructive/10 hover:text-destructive"
|
|
77
|
+
disabled={readonly}
|
|
78
|
+
>
|
|
79
|
+
<X className="h-3.5 w-3.5" />
|
|
80
|
+
</Button>
|
|
81
|
+
)}
|
|
82
|
+
</div>
|
|
83
|
+
</CardHeader>
|
|
84
|
+
|
|
85
|
+
{operationDefinition.args && operationDefinition.args.length > 0 && (
|
|
86
|
+
<CardContent className="pt-0">
|
|
87
|
+
<Form {...form}>
|
|
88
|
+
<div className="space-y-4">
|
|
89
|
+
<div
|
|
90
|
+
className={`grid gap-4 ${operationDefinition.args.length === 1 ? 'grid-cols-1' : 'grid-cols-1 sm:grid-cols-2'}`}
|
|
91
|
+
>
|
|
92
|
+
{operationDefinition.args
|
|
93
|
+
.filter(
|
|
94
|
+
arg =>
|
|
95
|
+
arg.ui?.component !== 'combination-mode-form-input',
|
|
96
|
+
)
|
|
97
|
+
.map(arg => {
|
|
98
|
+
const argValue =
|
|
99
|
+
value.arguments.find(a => a.name === arg.name)?.value || '';
|
|
100
|
+
return (
|
|
101
|
+
<FormField
|
|
102
|
+
key={arg.name}
|
|
103
|
+
name={`args.${arg.name}`}
|
|
104
|
+
render={() => (
|
|
105
|
+
<FormItem className="space-y-2">
|
|
106
|
+
<FormLabel className="text-sm font-medium text-foreground">
|
|
107
|
+
{arg.label || arg.name}
|
|
108
|
+
</FormLabel>
|
|
109
|
+
<FormControl>
|
|
110
|
+
<ConfigurableOperationArgInput
|
|
111
|
+
definition={arg}
|
|
112
|
+
value={argValue}
|
|
113
|
+
onChange={value =>
|
|
114
|
+
handleInputChange(arg.name, value)
|
|
115
|
+
}
|
|
116
|
+
readOnly={readonly}
|
|
117
|
+
position={position}
|
|
118
|
+
/>
|
|
119
|
+
</FormControl>
|
|
120
|
+
</FormItem>
|
|
121
|
+
)}
|
|
122
|
+
/>
|
|
123
|
+
);
|
|
124
|
+
})}
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
</Form>
|
|
128
|
+
</CardContent>
|
|
129
|
+
)}
|
|
130
|
+
</Card>
|
|
131
|
+
</div>
|
|
92
132
|
);
|
|
93
133
|
}
|
|
94
134
|
|