@vendure/dashboard 3.4.1-master-202508210231 → 3.4.1
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 +152 -152
- package/src/app/routes/_authenticated/_orders/components/order-modification-preview-dialog.tsx +2 -1
- package/src/app/routes/_authenticated/_products/components/add-product-variant-dialog.tsx +1 -0
- package/src/lib/components/data-input/affixed-input.tsx +19 -5
- package/src/lib/components/data-input/boolean-input.tsx +9 -0
- package/src/lib/components/data-input/checkbox-input.tsx +8 -0
- package/src/lib/components/data-input/combination-mode-input.tsx +11 -2
- package/src/lib/components/data-input/configurable-operation-list-input.tsx +26 -401
- package/src/lib/components/data-input/custom-field-list-input.tsx +18 -25
- package/src/lib/components/data-input/customer-group-input.tsx +7 -11
- package/src/lib/components/data-input/datetime-input.tsx +9 -13
- package/src/lib/components/data-input/default-relation-input.tsx +29 -9
- package/src/lib/components/data-input/facet-value-input.tsx +15 -13
- package/src/lib/components/data-input/index.ts +2 -2
- package/src/lib/components/data-input/money-input.tsx +27 -20
- package/src/lib/components/data-input/number-input.tsx +48 -0
- package/src/lib/components/data-input/password-input.tsx +16 -0
- package/src/lib/components/data-input/{product-multi-selector.tsx → product-multi-selector-input.tsx} +8 -15
- package/src/lib/components/data-input/relation-input.tsx +7 -6
- package/src/lib/components/data-input/rich-text-input.tsx +10 -13
- package/src/lib/components/data-input/select-with-options.tsx +29 -17
- package/src/lib/components/data-input/struct-form-input.tsx +54 -59
- package/src/lib/components/data-input/text-input.tsx +9 -0
- package/src/lib/components/data-input/textarea-input.tsx +16 -0
- package/src/lib/components/data-table/filters/data-table-number-filter.tsx +3 -0
- package/src/lib/components/data-table/use-generated-columns.tsx +16 -5
- package/src/lib/components/shared/configurable-operation-arg-input.tsx +3 -10
- package/src/lib/components/shared/configurable-operation-input.tsx +1 -6
- package/src/lib/components/shared/configurable-operation-multi-selector.tsx +8 -5
- package/src/lib/components/shared/configurable-operation-selector.tsx +5 -5
- package/src/lib/components/shared/custom-fields-form.tsx +20 -49
- package/src/lib/components/shared/multi-select.tsx +1 -1
- package/src/lib/framework/component-registry/component-registry.tsx +9 -32
- package/src/lib/framework/component-registry/display-component.tsx +28 -0
- package/src/lib/framework/extension-api/display-component-extensions.tsx +0 -14
- package/src/lib/framework/extension-api/input-component-extensions.tsx +52 -34
- package/src/lib/framework/extension-api/logic/data-table.ts +4 -27
- package/src/lib/framework/extension-api/logic/form-components.ts +3 -2
- package/src/lib/framework/extension-api/types/detail-forms.ts +2 -38
- package/src/lib/framework/extension-api/types/form-components.ts +2 -4
- package/src/lib/framework/form-engine/custom-form-component-extensions.ts +0 -23
- package/src/lib/framework/form-engine/custom-form-component.tsx +8 -25
- package/src/lib/framework/form-engine/default-input-for-type.tsx +35 -0
- package/src/lib/framework/form-engine/form-control-adapter.tsx +192 -0
- package/src/lib/framework/form-engine/form-engine-types.ts +163 -0
- package/src/lib/framework/form-engine/form-schema-tools.ts +55 -71
- package/src/lib/framework/form-engine/overridden-form-component.tsx +2 -2
- package/src/lib/framework/form-engine/utils.ts +223 -0
- package/src/lib/{components/shared → framework/form-engine}/value-transformers.ts +9 -9
- package/src/lib/framework/registry/registry-types.ts +3 -5
- package/src/lib/graphql/graphql-env.d.ts +11 -7
- package/src/lib/index.ts +28 -1
- package/src/lib/providers/server-config.tsx +1 -0
- package/src/lib/components/shared/direct-form-component-map.tsx +0 -393
- package/src/lib/components/shared/universal-field-definition.ts +0 -118
- package/src/lib/components/shared/universal-form-input.tsx +0 -175
- package/src/lib/components/shared/universal-input-components.tsx +0 -291
- package/src/lib/framework/component-registry/dynamic-component.tsx +0 -58
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { configurableOperationDefFragment } from '@/vdb/graphql/fragments.js';
|
|
2
|
+
import {
|
|
3
|
+
allCustomFieldsFragment,
|
|
4
|
+
booleanCustomFieldFragment,
|
|
5
|
+
customFieldConfigFragment,
|
|
6
|
+
dateTimeCustomFieldFragment,
|
|
7
|
+
floatCustomFieldFragment,
|
|
8
|
+
intCustomFieldFragment,
|
|
9
|
+
localeStringCustomFieldFragment,
|
|
10
|
+
localeTextCustomFieldFragment,
|
|
11
|
+
relationCustomFieldFragment,
|
|
12
|
+
stringCustomFieldFragment,
|
|
13
|
+
structCustomFieldFragment,
|
|
14
|
+
textCustomFieldFragment,
|
|
15
|
+
} from '@/vdb/providers/server-config.js';
|
|
16
|
+
import { ResultOf } from 'gql.tada';
|
|
17
|
+
import React from 'react';
|
|
18
|
+
import { ControllerRenderProps, FieldPath, FieldValues } from 'react-hook-form';
|
|
19
|
+
|
|
20
|
+
// Base custom field config
|
|
21
|
+
export type CustomFieldConfig = ResultOf<typeof customFieldConfigFragment>;
|
|
22
|
+
|
|
23
|
+
// Individual custom field type configurations
|
|
24
|
+
export type StringCustomFieldConfig = ResultOf<typeof stringCustomFieldFragment>;
|
|
25
|
+
export type LocaleStringCustomFieldConfig = ResultOf<typeof localeStringCustomFieldFragment>;
|
|
26
|
+
export type TextCustomFieldConfig = ResultOf<typeof textCustomFieldFragment>;
|
|
27
|
+
export type LocaleTextCustomFieldConfig = ResultOf<typeof localeTextCustomFieldFragment>;
|
|
28
|
+
export type BooleanCustomFieldConfig = ResultOf<typeof booleanCustomFieldFragment>;
|
|
29
|
+
export type IntCustomFieldConfig = ResultOf<typeof intCustomFieldFragment>;
|
|
30
|
+
export type FloatCustomFieldConfig = ResultOf<typeof floatCustomFieldFragment>;
|
|
31
|
+
export type DateTimeCustomFieldConfig = ResultOf<typeof dateTimeCustomFieldFragment>;
|
|
32
|
+
export type RelationCustomFieldConfig = ResultOf<typeof relationCustomFieldFragment>;
|
|
33
|
+
export type StructCustomFieldConfig = ResultOf<typeof structCustomFieldFragment>;
|
|
34
|
+
|
|
35
|
+
// Union type of all custom field configs
|
|
36
|
+
export type AllCustomFieldConfigs = ResultOf<typeof allCustomFieldsFragment>;
|
|
37
|
+
|
|
38
|
+
// Configurable operation argument definition
|
|
39
|
+
export type ConfigurableArgDef = ResultOf<typeof configurableOperationDefFragment>['args'][number];
|
|
40
|
+
|
|
41
|
+
// Union type for all field definitions
|
|
42
|
+
export type ConfigurableFieldDef = AllCustomFieldConfigs | ConfigurableArgDef;
|
|
43
|
+
|
|
44
|
+
// Struct field types (used within struct custom fields)
|
|
45
|
+
export type StructField = StructCustomFieldConfig['fields'][number];
|
|
46
|
+
|
|
47
|
+
// Individual struct field type configurations (for type guards)
|
|
48
|
+
export type StringStructField = Extract<StructField, { type: 'string' }>;
|
|
49
|
+
export type IntStructField = Extract<StructField, { type: 'int' }>;
|
|
50
|
+
export type FloatStructField = Extract<StructField, { type: 'float' }>;
|
|
51
|
+
export type BooleanStructField = Extract<StructField, { type: 'boolean' }>;
|
|
52
|
+
export type DateTimeStructField = Extract<StructField, { type: 'datetime' }>;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @description
|
|
56
|
+
* Props that get passed to all form input components. They are based on the
|
|
57
|
+
* controller props used by the underlying `react-hook-form`, i.e.:
|
|
58
|
+
*
|
|
59
|
+
* ```ts
|
|
60
|
+
* export type ControllerRenderProps = {
|
|
61
|
+
* onChange: (event: any) => void;
|
|
62
|
+
* onBlur: () => void;
|
|
63
|
+
* value: any;
|
|
64
|
+
* disabled?: boolean;
|
|
65
|
+
* name: string;
|
|
66
|
+
* ref: RefCallBack;
|
|
67
|
+
* };
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* in addition, they can optionally be passed a `fieldDef` prop if the
|
|
71
|
+
* component is used in the context of a custom field or configurable operation arg.
|
|
72
|
+
*
|
|
73
|
+
* The `fieldDef` arg, when present, has the following shape:
|
|
74
|
+
*
|
|
75
|
+
* ```ts
|
|
76
|
+
* export type ConfigurableArgDef = {
|
|
77
|
+
* defaultValue: any
|
|
78
|
+
* description: string | null
|
|
79
|
+
* label: string | null
|
|
80
|
+
* list: boolean
|
|
81
|
+
* name: string
|
|
82
|
+
* required: boolean
|
|
83
|
+
* type: string
|
|
84
|
+
* ui: any
|
|
85
|
+
* }
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* @docsCategory forms
|
|
89
|
+
* @docsPage DashboardFormComponent
|
|
90
|
+
*/
|
|
91
|
+
export type DashboardFormComponentProps<
|
|
92
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
93
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
94
|
+
> = ControllerRenderProps<TFieldValues, TName> & {
|
|
95
|
+
fieldDef?: ConfigurableFieldDef;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @description
|
|
100
|
+
* Metadata which can be defined on a {@link DashboardFormComponent} which
|
|
101
|
+
* provides additional information about how the dashboard should render the
|
|
102
|
+
* component.
|
|
103
|
+
*
|
|
104
|
+
* The metadata is defined by adding the static property on the component:
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```ts
|
|
108
|
+
* export const MyCustomInput: DashboardFormComponent = props => {
|
|
109
|
+
* // implementation omitted
|
|
110
|
+
* }
|
|
111
|
+
*
|
|
112
|
+
* // highlight-start
|
|
113
|
+
* MyCustomInput.metadata = {
|
|
114
|
+
* isListInput: true
|
|
115
|
+
* }
|
|
116
|
+
* // highlight-end
|
|
117
|
+
* ```
|
|
118
|
+
*
|
|
119
|
+
* @docsCategory forms
|
|
120
|
+
* @docsPage DashboardFormComponent
|
|
121
|
+
*/
|
|
122
|
+
export type DashboardFormComponentMetadata = {
|
|
123
|
+
/**
|
|
124
|
+
* @description
|
|
125
|
+
* Defines whether this form component is designed to handle list inputs.
|
|
126
|
+
* If set to `'dynamic'`, it means the component has internal logic that can
|
|
127
|
+
* handle both lists and single values.
|
|
128
|
+
*/
|
|
129
|
+
isListInput?: boolean | 'dynamic';
|
|
130
|
+
isFullWidth?: boolean;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @description
|
|
135
|
+
* This is the common type for all custom form components registered for:
|
|
136
|
+
*
|
|
137
|
+
* - custom fields
|
|
138
|
+
* - configurable operation args
|
|
139
|
+
* - detail page fields
|
|
140
|
+
*
|
|
141
|
+
* Here's a simple example:
|
|
142
|
+
*
|
|
143
|
+
* ```ts
|
|
144
|
+
* import { DashboardFormComponent, Input } from '\@vendure/dashboard';
|
|
145
|
+
*
|
|
146
|
+
* const MyComponent: DashboardFormComponent = (props) => {
|
|
147
|
+
* return <Input value={props.value}
|
|
148
|
+
* onChange={props.onChange}
|
|
149
|
+
* onBlur={props.onBlur}
|
|
150
|
+
* name={props.name}
|
|
151
|
+
* disabled={props.disabled}
|
|
152
|
+
* ref={props.ref}
|
|
153
|
+
* />;
|
|
154
|
+
* };
|
|
155
|
+
* ```
|
|
156
|
+
*
|
|
157
|
+
* @docsCategory forms
|
|
158
|
+
* @docsPage DashboardFormComponent
|
|
159
|
+
* @docsWeight 0
|
|
160
|
+
*/
|
|
161
|
+
export type DashboardFormComponent = React.ComponentType<DashboardFormComponentProps> & {
|
|
162
|
+
metadata?: DashboardFormComponentMetadata;
|
|
163
|
+
};
|
|
@@ -3,33 +3,24 @@ import {
|
|
|
3
3
|
isEnumType,
|
|
4
4
|
isScalarType,
|
|
5
5
|
} from '@/vdb/framework/document-introspection/get-document-structure.js';
|
|
6
|
-
import {
|
|
7
|
-
|
|
6
|
+
import {
|
|
7
|
+
CustomFieldConfig,
|
|
8
|
+
DateTimeCustomFieldConfig,
|
|
9
|
+
FloatCustomFieldConfig,
|
|
10
|
+
IntCustomFieldConfig,
|
|
11
|
+
StringCustomFieldConfig,
|
|
12
|
+
StructCustomFieldConfig,
|
|
13
|
+
StructField,
|
|
14
|
+
} from '@/vdb/framework/form-engine/form-engine-types.js';
|
|
8
15
|
import { z, ZodRawShape, ZodType, ZodTypeAny } from 'zod';
|
|
9
16
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
type: string;
|
|
15
|
-
pattern?: string;
|
|
16
|
-
intMin?: number;
|
|
17
|
-
intMax?: number;
|
|
18
|
-
floatMin?: number;
|
|
19
|
-
floatMax?: number;
|
|
20
|
-
datetimeMin?: string;
|
|
21
|
-
datetimeMax?: string;
|
|
22
|
-
list?: boolean;
|
|
23
|
-
nullable?: boolean;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
type StructFieldConfig = ResultOf<typeof structCustomFieldFragment>['fields'][number];
|
|
27
|
-
|
|
28
|
-
function mapGraphQLCustomFieldToConfig(field: StructFieldConfig): CustomFieldConfig {
|
|
29
|
-
const baseConfig = {
|
|
30
|
-
name: field.name,
|
|
31
|
-
type: field.type,
|
|
17
|
+
function mapGraphQLCustomFieldToConfig(field: StructField) {
|
|
18
|
+
const { __typename, ...rest } = field;
|
|
19
|
+
const baseConfig: CustomFieldConfig = {
|
|
20
|
+
...rest,
|
|
32
21
|
list: field.list ?? false,
|
|
22
|
+
readonly: false,
|
|
23
|
+
requiresPermission: [],
|
|
33
24
|
nullable: true, // Default to true since GraphQL fields are nullable by default
|
|
34
25
|
};
|
|
35
26
|
|
|
@@ -37,26 +28,34 @@ function mapGraphQLCustomFieldToConfig(field: StructFieldConfig): CustomFieldCon
|
|
|
37
28
|
case 'StringStructFieldConfig':
|
|
38
29
|
return {
|
|
39
30
|
...baseConfig,
|
|
40
|
-
|
|
41
|
-
|
|
31
|
+
__typename: 'StringCustomFieldConfig',
|
|
32
|
+
pattern: field.pattern ?? null,
|
|
33
|
+
options: [],
|
|
34
|
+
} satisfies StringCustomFieldConfig;
|
|
42
35
|
case 'IntStructFieldConfig':
|
|
43
36
|
return {
|
|
44
37
|
...baseConfig,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
__typename: 'IntCustomFieldConfig',
|
|
39
|
+
intMin: field.intMin ?? null,
|
|
40
|
+
intMax: field.intMax ?? null,
|
|
41
|
+
intStep: field.intStep ?? null,
|
|
42
|
+
} satisfies IntCustomFieldConfig;
|
|
48
43
|
case 'FloatStructFieldConfig':
|
|
49
44
|
return {
|
|
50
45
|
...baseConfig,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
__typename: 'FloatCustomFieldConfig',
|
|
47
|
+
floatMin: field.floatMin ?? null,
|
|
48
|
+
floatMax: field.floatMax ?? null,
|
|
49
|
+
floatStep: field.floatStep ?? null,
|
|
50
|
+
} satisfies FloatCustomFieldConfig;
|
|
54
51
|
case 'DateTimeStructFieldConfig':
|
|
55
52
|
return {
|
|
56
53
|
...baseConfig,
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
__typename: 'DateTimeCustomFieldConfig',
|
|
55
|
+
datetimeMin: field.datetimeMin ?? null,
|
|
56
|
+
datetimeMax: field.datetimeMax ?? null,
|
|
57
|
+
datetimeStep: field.datetimeStep ?? null,
|
|
58
|
+
} satisfies DateTimeCustomFieldConfig;
|
|
60
59
|
default:
|
|
61
60
|
return baseConfig;
|
|
62
61
|
}
|
|
@@ -116,7 +115,7 @@ function createDateValidationSchema(minDate: Date | undefined, maxDate: Date | u
|
|
|
116
115
|
* @param pattern - Optional regex pattern string for validation
|
|
117
116
|
* @returns Zod string schema with optional pattern validation
|
|
118
117
|
*/
|
|
119
|
-
function createStringValidationSchema(pattern?: string): ZodType {
|
|
118
|
+
function createStringValidationSchema(pattern?: string | null): ZodType {
|
|
120
119
|
let schema = z.string();
|
|
121
120
|
if (pattern) {
|
|
122
121
|
schema = schema.regex(new RegExp(pattern), {
|
|
@@ -134,30 +133,7 @@ function createStringValidationSchema(pattern?: string): ZodType {
|
|
|
134
133
|
* @param max - Optional maximum value constraint
|
|
135
134
|
* @returns Zod number schema with optional range validation
|
|
136
135
|
*/
|
|
137
|
-
function
|
|
138
|
-
let schema = z.number();
|
|
139
|
-
if (min != null) {
|
|
140
|
-
schema = schema.min(min, {
|
|
141
|
-
message: `Value must be at least ${min}`,
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
if (max != null) {
|
|
145
|
-
schema = schema.max(max, {
|
|
146
|
-
message: `Value must be at most ${max}`,
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
return schema;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Creates a Zod validation schema for float fields with optional min/max constraints.
|
|
154
|
-
* Used for float-type custom fields that may have numeric range limits.
|
|
155
|
-
*
|
|
156
|
-
* @param min - Optional minimum value constraint
|
|
157
|
-
* @param max - Optional maximum value constraint
|
|
158
|
-
* @returns Zod number schema with optional range validation
|
|
159
|
-
*/
|
|
160
|
-
function createFloatValidationSchema(min?: number, max?: number): ZodType {
|
|
136
|
+
function createNumberValidationSchema(min?: number | null, max?: number | null): ZodType {
|
|
161
137
|
let schema = z.number();
|
|
162
138
|
if (min != null) {
|
|
163
139
|
schema = schema.min(min, {
|
|
@@ -187,17 +163,23 @@ function createCustomFieldValidationSchema(customField: CustomFieldConfig): ZodT
|
|
|
187
163
|
case 'localeString':
|
|
188
164
|
case 'localeText':
|
|
189
165
|
case 'string':
|
|
190
|
-
zodType = createStringValidationSchema(customField.pattern);
|
|
166
|
+
zodType = createStringValidationSchema((customField as StringCustomFieldConfig).pattern);
|
|
191
167
|
break;
|
|
192
168
|
case 'int':
|
|
193
|
-
zodType =
|
|
169
|
+
zodType = createNumberValidationSchema(
|
|
170
|
+
(customField as IntCustomFieldConfig).intMin,
|
|
171
|
+
(customField as IntCustomFieldConfig).intMax,
|
|
172
|
+
);
|
|
194
173
|
break;
|
|
195
174
|
case 'float':
|
|
196
|
-
zodType =
|
|
175
|
+
zodType = createNumberValidationSchema(
|
|
176
|
+
(customField as FloatCustomFieldConfig).floatMin,
|
|
177
|
+
(customField as FloatCustomFieldConfig).floatMax,
|
|
178
|
+
);
|
|
197
179
|
break;
|
|
198
180
|
case 'datetime': {
|
|
199
|
-
const minDate = parseDate(customField.datetimeMin);
|
|
200
|
-
const maxDate = parseDate(customField.datetimeMax);
|
|
181
|
+
const minDate = parseDate((customField as DateTimeCustomFieldConfig).datetimeMin);
|
|
182
|
+
const maxDate = parseDate((customField as DateTimeCustomFieldConfig).datetimeMax);
|
|
201
183
|
zodType = createDateValidationSchema(minDate, maxDate);
|
|
202
184
|
break;
|
|
203
185
|
}
|
|
@@ -227,7 +209,7 @@ function createStructFieldSchema(structFieldConfig: StructCustomFieldConfig): Zo
|
|
|
227
209
|
|
|
228
210
|
const nestedSchema: ZodRawShape = {};
|
|
229
211
|
for (const structSubField of structFieldConfig.fields) {
|
|
230
|
-
const config = mapGraphQLCustomFieldToConfig(structSubField
|
|
212
|
+
const config = mapGraphQLCustomFieldToConfig(structSubField);
|
|
231
213
|
let subFieldType = createCustomFieldValidationSchema(config);
|
|
232
214
|
|
|
233
215
|
// Handle list and nullable for struct sub-fields
|
|
@@ -253,7 +235,7 @@ function createStructFieldSchema(structFieldConfig: StructCustomFieldConfig): Zo
|
|
|
253
235
|
* @param customField - Custom field config containing list/nullable flags
|
|
254
236
|
* @returns Modified Zod schema with list/nullable modifiers applied
|
|
255
237
|
*/
|
|
256
|
-
function
|
|
238
|
+
function applyCustomFieldModifiers(zodType: ZodType, customField: CustomFieldConfig): ZodType {
|
|
257
239
|
let modifiedType = zodType;
|
|
258
240
|
|
|
259
241
|
if (customField.list) {
|
|
@@ -262,7 +244,9 @@ function applyListAndNullableModifiers(zodType: ZodType, customField: CustomFiel
|
|
|
262
244
|
if (customField.nullable !== false) {
|
|
263
245
|
modifiedType = modifiedType.optional().nullable();
|
|
264
246
|
}
|
|
265
|
-
|
|
247
|
+
if (customField.readonly) {
|
|
248
|
+
modifiedType = modifiedType.readonly();
|
|
249
|
+
}
|
|
266
250
|
return modifiedType;
|
|
267
251
|
}
|
|
268
252
|
|
|
@@ -299,7 +283,7 @@ function processCustomFieldsSchema(
|
|
|
299
283
|
zodType = createCustomFieldValidationSchema(customField);
|
|
300
284
|
}
|
|
301
285
|
|
|
302
|
-
zodType =
|
|
286
|
+
zodType = applyCustomFieldModifiers(zodType, customField);
|
|
303
287
|
const schemaPropertyName = getGraphQlInputName(customField);
|
|
304
288
|
customFieldsSchema[schemaPropertyName] = zodType;
|
|
305
289
|
}
|
|
@@ -319,7 +303,7 @@ export function createFormSchemaFromFields(
|
|
|
319
303
|
const isEnum = isEnumType(field.type);
|
|
320
304
|
|
|
321
305
|
if ((isScalar || isEnum) && field.name !== 'customFields') {
|
|
322
|
-
schemaConfig[field.name] = getZodTypeFromField(field
|
|
306
|
+
schemaConfig[field.name] = getZodTypeFromField(field);
|
|
323
307
|
} else if (field.name === 'customFields') {
|
|
324
308
|
const customFieldsSchema =
|
|
325
309
|
customFieldConfigs && customFieldConfigs.length > 0
|
|
@@ -391,7 +375,7 @@ export function getDefaultValueFromField(field: FieldInfo, defaultLanguageCode?:
|
|
|
391
375
|
}
|
|
392
376
|
}
|
|
393
377
|
|
|
394
|
-
export function getZodTypeFromField(field: FieldInfo
|
|
378
|
+
export function getZodTypeFromField(field: FieldInfo): ZodTypeAny {
|
|
395
379
|
let zodType: ZodType;
|
|
396
380
|
|
|
397
381
|
// This function is only used for non-custom fields, so we don't need custom field logic here
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DataDisplayComponent,
|
|
3
|
-
DataInputComponent,
|
|
4
3
|
useComponentRegistry,
|
|
5
4
|
} from '@/vdb/framework/component-registry/component-registry.js';
|
|
6
5
|
import { generateInputComponentKey } from '@/vdb/framework/extension-api/input-component-extensions.js';
|
|
6
|
+
import { DashboardFormComponent } from '@/vdb/framework/form-engine/form-engine-types.js';
|
|
7
7
|
import { usePageBlock } from '@/vdb/hooks/use-page-block.js';
|
|
8
8
|
import { usePage } from '@/vdb/hooks/use-page.js';
|
|
9
9
|
import { ControllerRenderProps, FieldPath, FieldValues } from 'react-hook-form';
|
|
@@ -34,7 +34,7 @@ export function OverriddenFormComponent({ fieldName, field, children }: Readonly
|
|
|
34
34
|
const pageBlock = usePageBlock({ optional: true });
|
|
35
35
|
const componentRegistry = useComponentRegistry();
|
|
36
36
|
let DisplayComponent: DataDisplayComponent | undefined;
|
|
37
|
-
let InputComponent:
|
|
37
|
+
let InputComponent: DashboardFormComponent | undefined;
|
|
38
38
|
if (page.pageId && pageBlock?.blockId) {
|
|
39
39
|
const customInputComponentKey = generateInputComponentKey(page.pageId, pageBlock.blockId, fieldName);
|
|
40
40
|
DisplayComponent = componentRegistry.getDisplayComponent(customInputComponentKey);
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AllCustomFieldConfigs,
|
|
3
|
+
BooleanCustomFieldConfig,
|
|
4
|
+
BooleanStructField,
|
|
5
|
+
ConfigurableArgDef,
|
|
6
|
+
ConfigurableFieldDef,
|
|
7
|
+
DateTimeCustomFieldConfig,
|
|
8
|
+
DateTimeStructField,
|
|
9
|
+
FloatCustomFieldConfig,
|
|
10
|
+
FloatStructField,
|
|
11
|
+
IntCustomFieldConfig,
|
|
12
|
+
IntStructField,
|
|
13
|
+
LocaleStringCustomFieldConfig,
|
|
14
|
+
LocaleTextCustomFieldConfig,
|
|
15
|
+
RelationCustomFieldConfig,
|
|
16
|
+
StringCustomFieldConfig,
|
|
17
|
+
StringStructField,
|
|
18
|
+
StructCustomFieldConfig,
|
|
19
|
+
StructField,
|
|
20
|
+
TextCustomFieldConfig,
|
|
21
|
+
} from '@/vdb/framework/form-engine/form-engine-types.js';
|
|
22
|
+
|
|
1
23
|
import { FieldInfo } from '../document-introspection/get-document-structure.js';
|
|
2
24
|
|
|
3
25
|
/**
|
|
@@ -97,3 +119,204 @@ export function removeEmptyIdFields<T extends Record<string, any>>(values: T, fi
|
|
|
97
119
|
recursiveRemove(result, fields);
|
|
98
120
|
return result;
|
|
99
121
|
}
|
|
122
|
+
|
|
123
|
+
// =============================================================================
|
|
124
|
+
// TYPE GUARDS FOR CONFIGURABLE FIELD DEFINITIONS
|
|
125
|
+
// =============================================================================
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Determines if a field definition is a custom field config (vs configurable operation arg)
|
|
129
|
+
*/
|
|
130
|
+
export function isCustomFieldConfig(input: ConfigurableFieldDef): input is AllCustomFieldConfigs {
|
|
131
|
+
return input.hasOwnProperty('readonly');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Determines if a field definition is a configurable operation argument
|
|
136
|
+
*/
|
|
137
|
+
export function isConfigurableArgDef(input: ConfigurableFieldDef): input is ConfigurableArgDef {
|
|
138
|
+
return !input.hasOwnProperty('readonly');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// =============================================================================
|
|
142
|
+
// TYPE GUARDS FOR SPECIFIC CUSTOM FIELD TYPES
|
|
143
|
+
// =============================================================================
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* String custom field with optional pattern and options
|
|
147
|
+
*/
|
|
148
|
+
export function isStringCustomFieldConfig(input: ConfigurableFieldDef): input is StringCustomFieldConfig {
|
|
149
|
+
return input.type === 'string' && isCustomFieldConfig(input);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* String custom field that has options (select dropdown)
|
|
154
|
+
*/
|
|
155
|
+
export function isStringFieldWithOptions(input: ConfigurableFieldDef): input is StringCustomFieldConfig {
|
|
156
|
+
const isCustomFieldWithOptions =
|
|
157
|
+
input.type === 'string' &&
|
|
158
|
+
isCustomFieldConfig(input) &&
|
|
159
|
+
input.hasOwnProperty('options') &&
|
|
160
|
+
Array.isArray((input as any).options);
|
|
161
|
+
if (isCustomFieldWithOptions) {
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
const isConfigArgWithOptions =
|
|
165
|
+
input.type === 'string' && isConfigurableArgDef(input) && Array.isArray(input.ui?.options);
|
|
166
|
+
if (isConfigArgWithOptions) {
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Locale string custom field
|
|
174
|
+
*/
|
|
175
|
+
export function isLocaleStringCustomFieldConfig(
|
|
176
|
+
input: ConfigurableFieldDef,
|
|
177
|
+
): input is LocaleStringCustomFieldConfig {
|
|
178
|
+
return input.type === 'localeString' && isCustomFieldConfig(input);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Text custom field (textarea)
|
|
183
|
+
*/
|
|
184
|
+
export function isTextCustomFieldConfig(input: ConfigurableFieldDef): input is TextCustomFieldConfig {
|
|
185
|
+
return input.type === 'text' && isCustomFieldConfig(input);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Locale text custom field (localized textarea)
|
|
190
|
+
*/
|
|
191
|
+
export function isLocaleTextCustomFieldConfig(
|
|
192
|
+
input: ConfigurableFieldDef,
|
|
193
|
+
): input is LocaleTextCustomFieldConfig {
|
|
194
|
+
return input.type === 'localeText' && isCustomFieldConfig(input);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Boolean custom field
|
|
199
|
+
*/
|
|
200
|
+
export function isBooleanCustomFieldConfig(input: ConfigurableFieldDef): input is BooleanCustomFieldConfig {
|
|
201
|
+
return input.type === 'boolean' && isCustomFieldConfig(input);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Integer custom field with optional min/max/step
|
|
206
|
+
*/
|
|
207
|
+
export function isIntCustomFieldConfig(input: ConfigurableFieldDef): input is IntCustomFieldConfig {
|
|
208
|
+
return input.type === 'int' && isCustomFieldConfig(input);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Float custom field with optional min/max/step
|
|
213
|
+
*/
|
|
214
|
+
export function isFloatCustomFieldConfig(input: ConfigurableFieldDef): input is FloatCustomFieldConfig {
|
|
215
|
+
return input.type === 'float' && isCustomFieldConfig(input);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* DateTime custom field with optional min/max/step
|
|
220
|
+
*/
|
|
221
|
+
export function isDateTimeCustomFieldConfig(input: ConfigurableFieldDef): input is DateTimeCustomFieldConfig {
|
|
222
|
+
return input.type === 'datetime' && isCustomFieldConfig(input);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Relation custom field (references another entity)
|
|
227
|
+
*/
|
|
228
|
+
export function isRelationCustomFieldConfig(input: ConfigurableFieldDef): input is RelationCustomFieldConfig {
|
|
229
|
+
return input.type === 'relation' && isCustomFieldConfig(input);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Struct custom field (nested object with sub-fields)
|
|
234
|
+
*/
|
|
235
|
+
export function isStructCustomFieldConfig(input: ConfigurableFieldDef): input is StructCustomFieldConfig {
|
|
236
|
+
return input.type === 'struct' && isCustomFieldConfig(input);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Legacy alias for backward compatibility
|
|
240
|
+
export const isStructFieldConfig = isStructCustomFieldConfig;
|
|
241
|
+
|
|
242
|
+
// =============================================================================
|
|
243
|
+
// TYPE GUARDS FOR STRUCT FIELD TYPES (fields within struct custom fields)
|
|
244
|
+
// =============================================================================
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* String field within a struct custom field
|
|
248
|
+
*/
|
|
249
|
+
export function isStringStructField(input: StructField): input is StringStructField {
|
|
250
|
+
return input.type === 'string';
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* String struct field that has options (select dropdown)
|
|
255
|
+
*/
|
|
256
|
+
export function isStringStructFieldWithOptions(
|
|
257
|
+
input: StructField,
|
|
258
|
+
): input is StringStructField & { options: any[] } {
|
|
259
|
+
return (
|
|
260
|
+
input.type === 'string' && input.hasOwnProperty('options') && Array.isArray((input as any).options)
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Integer field within a struct custom field
|
|
266
|
+
*/
|
|
267
|
+
export function isIntStructField(input: StructField): input is IntStructField {
|
|
268
|
+
return input.type === 'int';
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Float field within a struct custom field
|
|
273
|
+
*/
|
|
274
|
+
export function isFloatStructField(input: StructField): input is FloatStructField {
|
|
275
|
+
return input.type === 'float';
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Boolean field within a struct custom field
|
|
280
|
+
*/
|
|
281
|
+
export function isBooleanStructField(input: StructField): input is BooleanStructField {
|
|
282
|
+
return input.type === 'boolean';
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* DateTime field within a struct custom field
|
|
287
|
+
*/
|
|
288
|
+
export function isDateTimeStructField(input: StructField): input is DateTimeStructField {
|
|
289
|
+
return input.type === 'datetime';
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// =============================================================================
|
|
293
|
+
// UTILITY TYPE GUARDS
|
|
294
|
+
// =============================================================================
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Determines if a field is a list/array field
|
|
298
|
+
*/
|
|
299
|
+
export function isListField(input?: ConfigurableFieldDef): boolean {
|
|
300
|
+
return input && isCustomFieldConfig(input) ? Boolean(input.list) : false;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Determines if a field is readonly
|
|
305
|
+
*/
|
|
306
|
+
export function isReadonlyField(input?: ConfigurableFieldDef): boolean {
|
|
307
|
+
return input && isCustomFieldConfig(input) ? Boolean(input.readonly) : false;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Determines if a field requires special permissions
|
|
312
|
+
*/
|
|
313
|
+
export function hasPermissionRequirement(input: ConfigurableFieldDef): boolean {
|
|
314
|
+
return isCustomFieldConfig(input) && Boolean(input.requiresPermission);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Determines if a field is nullable
|
|
319
|
+
*/
|
|
320
|
+
export function isNullableField(input: ConfigurableFieldDef): boolean {
|
|
321
|
+
return isCustomFieldConfig(input) && Boolean(input.nullable);
|
|
322
|
+
}
|