@evoke-platform/ui-components 1.15.0 → 1.16.0
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/published/components/custom/CriteriaBuilder/CriteriaBuilder.d.ts +8 -4
- package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.js +242 -142
- package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.test.js +189 -67
- package/dist/published/components/custom/CriteriaBuilder/PropertyTree.d.ts +6 -6
- package/dist/published/components/custom/CriteriaBuilder/PropertyTree.js +12 -25
- package/dist/published/components/custom/CriteriaBuilder/PropertyTreeItem.d.ts +4 -5
- package/dist/published/components/custom/CriteriaBuilder/PropertyTreeItem.js +34 -22
- package/dist/published/components/custom/CriteriaBuilder/types.d.ts +2 -11
- package/dist/published/components/custom/CriteriaBuilder/utils.d.ts +6 -34
- package/dist/published/components/custom/CriteriaBuilder/utils.js +18 -89
- package/dist/published/components/custom/Form/FormComponents/DocumentComponent/Document.js +1 -1
- package/dist/published/components/custom/Form/FormComponents/DocumentComponent/DocumentList.js +6 -3
- package/dist/published/components/custom/Form/utils.d.ts +1 -0
- package/dist/published/components/custom/FormV2/FormRenderer.d.ts +2 -1
- package/dist/published/components/custom/FormV2/FormRenderer.js +2 -8
- package/dist/published/components/custom/FormV2/FormRendererContainer.d.ts +4 -0
- package/dist/published/components/custom/FormV2/FormRendererContainer.js +229 -126
- package/dist/published/components/custom/FormV2/components/DefaultValues.d.ts +1 -1
- package/dist/published/components/custom/FormV2/components/DefaultValues.js +60 -89
- package/dist/published/components/custom/FormV2/components/FormContext.d.ts +1 -1
- package/dist/published/components/custom/FormV2/components/FormContext.js +0 -1
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/ActionDialog.d.ts +1 -0
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/RepeatableField.js +43 -16
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/Criteria.d.ts +2 -2
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/Criteria.js +3 -2
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.d.ts +3 -2
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js +44 -11
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.d.ts +4 -3
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.js +41 -29
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/FileContent.d.ts +12 -0
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/FileContent.js +197 -0
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/Image.js +2 -4
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/InstanceLookup.js +14 -0
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.js +6 -2
- package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.js +16 -13
- package/dist/published/components/custom/FormV2/components/types.d.ts +6 -1
- package/dist/published/components/custom/FormV2/components/utils.d.ts +10 -8
- package/dist/published/components/custom/FormV2/components/utils.js +168 -82
- package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.js +6 -1
- package/dist/published/components/custom/index.d.ts +1 -0
- package/dist/published/index.d.ts +1 -1
- package/dist/published/stories/CriteriaBuilder.stories.js +70 -22
- package/dist/published/stories/FormRenderer.stories.d.ts +6 -3
- package/dist/published/stories/FormRendererContainer.stories.d.ts +20 -0
- package/dist/published/theme/hooks.d.ts +3 -3
- package/package.json +3 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { isArray, isEmpty, uniq } from 'lodash';
|
|
1
|
+
import { isArray, isEmpty, isObject, uniq } from 'lodash';
|
|
2
2
|
import { DateTime } from 'luxon';
|
|
3
|
-
import { getEntryId, getPrefixedUrl, isAddressProperty } from './utils';
|
|
4
|
-
export async function evalDefaultVals(parameters,
|
|
3
|
+
import { getEntryId, getPrefixedUrl, isAddressProperty, transformToWhere, updateCriteriaInputs } from './utils';
|
|
4
|
+
export async function evalDefaultVals(parameters, entry, fieldValue, fieldId, apiServices, userAccount, formValues, updatedRelatedObjectValue, updatedRelatedObjectParamId) {
|
|
5
5
|
const updates = [];
|
|
6
6
|
const parameter = parameters.find((param) => param.id === fieldId);
|
|
7
7
|
const defaultValue = entry.display?.defaultValue;
|
|
@@ -14,7 +14,6 @@ export async function evalDefaultVals(parameters, unnestedEntries, entry, fieldV
|
|
|
14
14
|
const regex = /^{{input\.(?<relatedObjectProperty>[a-zA-Z][a-zA-Z0-9_]*)\.(?<nestedProperty>[a-zA-Z][a-zA-Z0-9_]*)}}$/;
|
|
15
15
|
const groups = regex.exec(item)?.groups;
|
|
16
16
|
if (groups?.relatedObjectProperty && groups?.nestedProperty) {
|
|
17
|
-
const relatedObjectParameter = parameters.find((param) => param.id === groups?.relatedObjectProperty);
|
|
18
17
|
let relatedObjectInstance = updatedRelatedObjectValue;
|
|
19
18
|
if (!relatedObjectInstance && !isEmpty(formValues)) {
|
|
20
19
|
relatedObjectInstance = formValues[groups.relatedObjectProperty];
|
|
@@ -28,30 +27,14 @@ export async function evalDefaultVals(parameters, unnestedEntries, entry, fieldV
|
|
|
28
27
|
]);
|
|
29
28
|
updates.push({ fieldId, fieldValue });
|
|
30
29
|
}
|
|
31
|
-
else if (relatedObjectInstance
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (error) {
|
|
40
|
-
console.error(error);
|
|
41
|
-
return resolve(undefined);
|
|
42
|
-
}
|
|
43
|
-
resolve(instance);
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
if (instance) {
|
|
47
|
-
fieldValue = uniq([
|
|
48
|
-
...staticValues,
|
|
49
|
-
...(isArray(instance[groups.nestedProperty])
|
|
50
|
-
? instance[groups.nestedProperty]
|
|
51
|
-
: []),
|
|
52
|
-
]);
|
|
53
|
-
updates.push({ fieldId, fieldValue });
|
|
54
|
-
}
|
|
30
|
+
else if (relatedObjectInstance) {
|
|
31
|
+
fieldValue = uniq([
|
|
32
|
+
...staticValues,
|
|
33
|
+
...(isArray(relatedObjectInstance[groups.nestedProperty])
|
|
34
|
+
? relatedObjectInstance[groups.nestedProperty]
|
|
35
|
+
: []),
|
|
36
|
+
]);
|
|
37
|
+
updates.push({ fieldId, fieldValue });
|
|
55
38
|
}
|
|
56
39
|
else {
|
|
57
40
|
updates.push({ fieldId, fieldValue: staticValues });
|
|
@@ -65,42 +48,23 @@ export async function evalDefaultVals(parameters, unnestedEntries, entry, fieldV
|
|
|
65
48
|
}
|
|
66
49
|
else if (typeof defaultValue === 'string' && /^{{.*}}$/.test(defaultValue)) {
|
|
67
50
|
if (isAddressProperty(fieldId)) {
|
|
68
|
-
const regex = /^{{input\.(?<relatedObjectProperty>[a-zA-Z][a-zA-Z0-9_]*)\.(?<addressProperty>[a-zA-Z][a-zA-Z0-9_]*)\.(?<nestedAddressProperty>line1|line2|city|county|state|zipCode)}}$/;
|
|
51
|
+
const regex = /^{{input\.(?<relatedObjectProperty>[a-zA-Z][a-zA-Z0-9_]*)\.(?<addressProperty>[a-zA-Z][a-zA-Z0-9_]*)\.(?<nestedAddressProperty>line1|line2|city|county|state|zipCode|country)}}$/;
|
|
69
52
|
const groups = regex.exec(defaultValue)?.groups;
|
|
70
53
|
if (groups?.relatedObjectProperty && groups?.addressProperty && groups?.nestedAddressProperty) {
|
|
71
|
-
const relatedObjectParameter = parameters.find((param) => param.id === groups?.relatedObjectProperty);
|
|
72
54
|
let relatedObjectInstance = updatedRelatedObjectValue;
|
|
73
55
|
if (!relatedObjectInstance && !isEmpty(formValues)) {
|
|
74
56
|
relatedObjectInstance = formValues[groups.relatedObjectProperty];
|
|
75
57
|
}
|
|
76
|
-
if (updatedRelatedObjectValue
|
|
58
|
+
if (updatedRelatedObjectValue === null) {
|
|
59
|
+
updates.push({ fieldId, fieldValue: '' });
|
|
60
|
+
}
|
|
61
|
+
else if (updatedRelatedObjectValue?.[groups.addressProperty]?.[groups.nestedAddressProperty]) {
|
|
77
62
|
fieldValue = updatedRelatedObjectValue?.[groups.addressProperty]?.[groups.nestedAddressProperty];
|
|
78
63
|
updates.push({ fieldId, fieldValue });
|
|
79
64
|
}
|
|
80
|
-
else if (relatedObjectInstance
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const relatedObjectParamEntry = unnestedEntries.find((e) => getEntryId(e) === relatedObjectParameter.id);
|
|
84
|
-
relatedObjectId = relatedObjectParamEntry?.display?.relatedObjectId;
|
|
85
|
-
}
|
|
86
|
-
const instance = await new Promise((resolve) => {
|
|
87
|
-
apiServices.get(getPrefixedUrl(`/objects/${relatedObjectId}/instances/${relatedObjectInstance?.id}`), (error, instance) => {
|
|
88
|
-
if (error) {
|
|
89
|
-
console.error(error);
|
|
90
|
-
return resolve(undefined);
|
|
91
|
-
}
|
|
92
|
-
resolve(instance);
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
// Clear dependent fields only if value is explicitly null (user cleared it).
|
|
96
|
-
// If updatedRelatedObjectValue is undefined (not triggered by onChange), use the value from the instance.
|
|
97
|
-
if (updatedRelatedObjectValue === null) {
|
|
98
|
-
updates.push({ fieldId, fieldValue: '' });
|
|
99
|
-
}
|
|
100
|
-
else if (instance) {
|
|
101
|
-
fieldValue = instance?.[groups.addressProperty]?.[groups.nestedAddressProperty];
|
|
102
|
-
updates.push({ fieldId, fieldValue });
|
|
103
|
-
}
|
|
65
|
+
else if (relatedObjectInstance) {
|
|
66
|
+
fieldValue = relatedObjectInstance?.[groups.addressProperty]?.[groups.nestedAddressProperty];
|
|
67
|
+
updates.push({ fieldId, fieldValue });
|
|
104
68
|
}
|
|
105
69
|
}
|
|
106
70
|
}
|
|
@@ -108,44 +72,44 @@ export async function evalDefaultVals(parameters, unnestedEntries, entry, fieldV
|
|
|
108
72
|
const regex = /^{{input\.(?<relatedObjectProperty>[a-zA-Z][a-zA-Z0-9_]*)\.(?<nestedProperty>[a-zA-Z][a-zA-Z0-9_]*)}}$/;
|
|
109
73
|
const groups = regex.exec(defaultValue)?.groups;
|
|
110
74
|
if (groups?.relatedObjectProperty && groups?.nestedProperty) {
|
|
111
|
-
const relatedObjectParameter = parameters.find((param) => param.id === groups?.relatedObjectProperty);
|
|
112
75
|
let relatedObjectInstance = updatedRelatedObjectValue;
|
|
113
76
|
if (!relatedObjectInstance && !isEmpty(formValues)) {
|
|
114
77
|
relatedObjectInstance = formValues[groups.relatedObjectProperty];
|
|
115
78
|
}
|
|
116
|
-
if (updatedRelatedObjectValue
|
|
79
|
+
if (updatedRelatedObjectValue === null) {
|
|
80
|
+
updates.push({ fieldId, fieldValue: null });
|
|
81
|
+
}
|
|
82
|
+
else if (updatedRelatedObjectValue?.[groups.nestedProperty]) {
|
|
117
83
|
fieldValue = updatedRelatedObjectValue[groups.nestedProperty];
|
|
118
84
|
updates.push({ fieldId, fieldValue });
|
|
119
85
|
}
|
|
120
|
-
else if (relatedObjectInstance
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const relatedObjectParamEntry = unnestedEntries.find((e) => getEntryId(e) === relatedObjectParameter.id);
|
|
124
|
-
relatedObjectId = relatedObjectParamEntry?.display?.relatedObjectId;
|
|
125
|
-
}
|
|
126
|
-
const instance = await new Promise((resolve) => {
|
|
127
|
-
apiServices.get(getPrefixedUrl(`/objects/${relatedObjectId}/instances/${relatedObjectInstance?.id}`), (error, instance) => {
|
|
128
|
-
if (error) {
|
|
129
|
-
console.error(error);
|
|
130
|
-
return resolve(undefined);
|
|
131
|
-
}
|
|
132
|
-
resolve(instance);
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
// Clear dependent fields only if value is explicitly null (user cleared it).
|
|
136
|
-
// If updatedRelatedObjectValue is undefined (not triggered by onChange), use the value from the instance.
|
|
137
|
-
if (updatedRelatedObjectValue === null) {
|
|
138
|
-
updates.push({ fieldId, fieldValue: null });
|
|
139
|
-
}
|
|
140
|
-
else if (instance) {
|
|
141
|
-
fieldValue = instance?.[groups.nestedProperty] || null;
|
|
142
|
-
updates.push({ fieldId, fieldValue });
|
|
143
|
-
}
|
|
86
|
+
else if (relatedObjectInstance) {
|
|
87
|
+
fieldValue = relatedObjectInstance[groups.nestedProperty] || null;
|
|
88
|
+
updates.push({ fieldId, fieldValue });
|
|
144
89
|
}
|
|
145
90
|
}
|
|
146
91
|
}
|
|
147
92
|
// all other default values set here
|
|
148
93
|
}
|
|
94
|
+
else if (isObject(defaultValue) && 'criteria' in defaultValue && !isEmpty(defaultValue.criteria)) {
|
|
95
|
+
const criteria = defaultValue.criteria;
|
|
96
|
+
const evaluatedCriteria = updateCriteriaInputs(criteria, { ...formValues, [updatedRelatedObjectParamId || '']: updatedRelatedObjectValue }, userAccount);
|
|
97
|
+
try {
|
|
98
|
+
const instances = await apiServices.get(getPrefixedUrl(`/objects/${parameter?.objectId || entry.display?.relatedObjectId}/instances`), {
|
|
99
|
+
params: {
|
|
100
|
+
filter: {
|
|
101
|
+
where: transformToWhere(evaluatedCriteria),
|
|
102
|
+
limit: 1,
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
updates.push({ fieldId, fieldValue: instances[0] ?? null });
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
updates.push({ fieldId, fieldValue: null });
|
|
110
|
+
console.error(error);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
149
113
|
else if (parameter?.type !== 'object') {
|
|
150
114
|
let updatedValue = defaultValue;
|
|
151
115
|
// handles current default values ie: "Current logged in user", "Today" etc.
|
|
@@ -175,38 +139,45 @@ export async function processValueUpdate(unnestedEntries, parameters, updatedRel
|
|
|
175
139
|
const parameterId = getEntryId(entry);
|
|
176
140
|
if (!parameterId)
|
|
177
141
|
return [];
|
|
142
|
+
if (parameterId === changedEntryId) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const defaultValue = entry.display.defaultValue;
|
|
178
146
|
if (isAddressProperty(parameterId)) {
|
|
179
|
-
const regex = /^{{input\.(?<relatedObjectProperty>[a-zA-Z][a-zA-Z0-9_]*)\.(?<addressProperty>[a-zA-Z][a-zA-Z0-9_]*)\.(?<nestedAddressProperty>line1|line2|city|county|state|zipCode)}}$/;
|
|
147
|
+
const regex = /^{{input\.(?<relatedObjectProperty>[a-zA-Z][a-zA-Z0-9_]*)\.(?<addressProperty>[a-zA-Z][a-zA-Z0-9_]*)\.(?<nestedAddressProperty>line1|line2|city|county|state|zipCode|country)}}$/;
|
|
180
148
|
const groups = regex.exec(entry.display.defaultValue)?.groups;
|
|
181
149
|
const [addressObject, addressField] = parameterId.split('.');
|
|
182
150
|
if (groups?.relatedObjectProperty &&
|
|
183
151
|
groups?.addressProperty &&
|
|
184
152
|
groups?.nestedAddressProperty &&
|
|
185
153
|
changedEntryId === groups.relatedObjectProperty) {
|
|
186
|
-
const result = await evalDefaultVals(parameters,
|
|
154
|
+
const result = await evalDefaultVals(parameters, entry, formValues?.[addressObject]?.[addressField], parameterId, apiServices, userAccount, formValues, updatedRelatedObjectValue, changedEntryId);
|
|
187
155
|
updates.push(...result);
|
|
188
156
|
}
|
|
189
157
|
}
|
|
190
|
-
else if (isArray(
|
|
191
|
-
|
|
192
|
-
for (const item of entry.display.defaultValue.filter((item) => /^{{.*}}$/.test(item))) {
|
|
158
|
+
else if (isArray(defaultValue) && defaultValue.some((item) => /^{{.*}}$/.test(item))) {
|
|
159
|
+
for (const item of defaultValue.filter((item) => /^{{.*}}$/.test(item))) {
|
|
193
160
|
const regex = /^{{input\.(?<relatedObjectProperty>[a-zA-Z][a-zA-Z0-9_]*)\.(?<nestedProperty>[a-zA-Z][a-zA-Z0-9_]*)}}$/;
|
|
194
161
|
const groups = regex.exec(item)?.groups;
|
|
195
162
|
if (groups?.relatedObjectProperty &&
|
|
196
163
|
groups?.nestedProperty &&
|
|
197
164
|
changedEntryId === groups.relatedObjectProperty) {
|
|
198
|
-
const result = await evalDefaultVals(parameters,
|
|
165
|
+
const result = await evalDefaultVals(parameters, entry, defaultValue, parameterId, apiServices, userAccount, formValues, updatedRelatedObjectValue, changedEntryId);
|
|
199
166
|
updates.push(...result);
|
|
200
167
|
}
|
|
201
168
|
}
|
|
202
169
|
}
|
|
170
|
+
else if (isObject(defaultValue) && 'criteria' in defaultValue && !isEmpty(defaultValue.criteria)) {
|
|
171
|
+
const result = await evalDefaultVals(parameters, entry, formValues?.[parameterId], parameterId, apiServices, userAccount, formValues, updatedRelatedObjectValue, changedEntryId);
|
|
172
|
+
updates.push(...result);
|
|
173
|
+
}
|
|
203
174
|
else {
|
|
204
175
|
const regex = /^{{input\.(?<relatedObjectProperty>[a-zA-Z][a-zA-Z0-9_]*)\.(?<nestedProperty>[a-zA-Z][a-zA-Z0-9_]*)}}$/;
|
|
205
|
-
const groups = regex.exec(
|
|
176
|
+
const groups = regex.exec(defaultValue)?.groups;
|
|
206
177
|
if (groups?.relatedObjectProperty &&
|
|
207
178
|
groups?.nestedProperty &&
|
|
208
179
|
changedEntryId === groups.relatedObjectProperty) {
|
|
209
|
-
const result = await evalDefaultVals(parameters,
|
|
180
|
+
const result = await evalDefaultVals(parameters, entry, formValues?.[parameterId], parameterId, apiServices, userAccount, formValues, updatedRelatedObjectValue, changedEntryId);
|
|
210
181
|
updates.push(...result);
|
|
211
182
|
}
|
|
212
183
|
}
|
|
@@ -18,11 +18,11 @@ type FormContextType = {
|
|
|
18
18
|
handleChange?: (name: string, value: unknown) => void | Promise<void>;
|
|
19
19
|
onAutosave?: (fieldId: string) => void | Promise<void>;
|
|
20
20
|
fieldHeight?: 'small' | 'medium';
|
|
21
|
-
triggerFieldReset?: boolean;
|
|
22
21
|
showSubmitError?: boolean;
|
|
23
22
|
associatedObject?: {
|
|
24
23
|
instanceId: string;
|
|
25
24
|
propertyId: string;
|
|
25
|
+
objectId?: string;
|
|
26
26
|
};
|
|
27
27
|
form?: EvokeForm;
|
|
28
28
|
width: number;
|
|
@@ -10,7 +10,7 @@ import { Accordion, AccordionDetails, AccordionSummary, Button, IconButton, Skel
|
|
|
10
10
|
import { Box } from '../../../../../layout';
|
|
11
11
|
import { getReadableQuery } from '../../../../CriteriaBuilder';
|
|
12
12
|
import { retrieveCustomErrorMessage } from '../../../../Form/utils';
|
|
13
|
-
import { convertPropertiesToParams, deleteDocuments, formatSubmission, getPrefixedUrl, transformToWhere, useFormById, } from '../../utils';
|
|
13
|
+
import { convertPropertiesToParams, deleteDocuments, formatSubmission, getPrefixedUrl, handleFileUpload, transformToWhere, useFormById, } from '../../utils';
|
|
14
14
|
import { ActionDialog } from './ActionDialog';
|
|
15
15
|
import { DocumentViewerCell } from './DocumentViewerCell';
|
|
16
16
|
const styles = {
|
|
@@ -258,20 +258,34 @@ const RepeatableField = (props) => {
|
|
|
258
258
|
: dialogType === 'update'
|
|
259
259
|
? entry.display?.updateActionId
|
|
260
260
|
: entry.display?.deleteActionId));
|
|
261
|
+
const relatedProperty = relatedObject?.properties?.find((p) => p.id === fieldDefinition.relatedPropertyId);
|
|
261
262
|
// when save is called we know that fieldDefinition is a parameter and fieldDefinition.objectId is defined
|
|
262
|
-
input = await formatSubmission(input, apiServices, fieldDefinition.objectId, selectedInstanceId, action?.type === 'update' ? updateForm :
|
|
263
|
-
? {
|
|
263
|
+
input = await formatSubmission(input, apiServices, fieldDefinition.objectId, selectedInstanceId, action?.type === 'update' ? updateForm : action?.type === 'delete' ? deleteForm : createForm, undefined, instance?.id && fieldDefinition.relatedPropertyId
|
|
264
|
+
? {
|
|
265
|
+
instanceId: instance.id,
|
|
266
|
+
propertyId: fieldDefinition.relatedPropertyId,
|
|
267
|
+
objectId: !relatedProperty?.objectId ? instance.objectId : undefined,
|
|
268
|
+
}
|
|
264
269
|
: undefined, action?.parameters ?? (relatedObject && convertPropertiesToParams(relatedObject)));
|
|
265
|
-
if (action?.type === 'create' &&
|
|
270
|
+
if (action?.type === 'create' && action.id) {
|
|
266
271
|
const updatedInput = {
|
|
267
272
|
...input,
|
|
268
|
-
[fieldDefinition?.relatedPropertyId]: {
|
|
273
|
+
[fieldDefinition?.relatedPropertyId]: {
|
|
274
|
+
id: instance?.id,
|
|
275
|
+
objectId: !relatedProperty?.objectId ? instance?.objectId : undefined,
|
|
276
|
+
},
|
|
269
277
|
};
|
|
270
278
|
try {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
279
|
+
let instance = undefined;
|
|
280
|
+
if (relatedObject?.rootObjectId === 'sys__file') {
|
|
281
|
+
instance = await handleFileUpload(apiServices, updatedInput, action.id, fieldDefinition.objectId);
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
instance = await apiServices.post(getPrefixedUrl(`/objects/${fieldDefinition.objectId}/instances/actions`), {
|
|
285
|
+
actionId: action.id,
|
|
286
|
+
input: updatedInput,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
275
289
|
queryClient.setQueryData(relatedInstancesQueryKey, (oldData) => {
|
|
276
290
|
if (!oldData)
|
|
277
291
|
return [instance];
|
|
@@ -293,12 +307,19 @@ const RepeatableField = (props) => {
|
|
|
293
307
|
else {
|
|
294
308
|
const relatedObjectId = relatedObject?.id;
|
|
295
309
|
try {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
310
|
+
let response = undefined;
|
|
311
|
+
const submission = omit(input, relatedObject?.properties
|
|
312
|
+
?.filter((property) => property.formula || property.type === 'collection')
|
|
313
|
+
.map((property) => property.id) ?? []);
|
|
314
|
+
if (relatedObject?.rootObjectId === 'sys__file' && action?.id) {
|
|
315
|
+
response = await handleFileUpload(apiServices, submission, action.id, relatedObjectId, selectedInstanceId);
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
response = await apiServices.post(getPrefixedUrl(`/objects/${relatedObjectId}/instances/${selectedInstanceId}/actions`), {
|
|
319
|
+
actionId: action?.id ?? `_${action?.type}`,
|
|
320
|
+
input: submission,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
302
323
|
if (response && relatedObject && instance) {
|
|
303
324
|
deleteDocuments(input, !!response, apiServices, relatedObject, instance, action);
|
|
304
325
|
}
|
|
@@ -491,7 +512,13 @@ const RepeatableField = (props) => {
|
|
|
491
512
|
? '_auto_'
|
|
492
513
|
: deleteForm?.id
|
|
493
514
|
: undefined, instanceId: selectedInstanceId, relatedParameter: fieldDefinition, associatedObject: instance?.id && fieldDefinition.relatedPropertyId
|
|
494
|
-
? {
|
|
515
|
+
? {
|
|
516
|
+
instanceId: instance.id,
|
|
517
|
+
propertyId: fieldDefinition.relatedPropertyId,
|
|
518
|
+
objectId: !relatedObject?.properties?.find((p) => p.id === fieldDefinition.relatedPropertyId)?.objectId
|
|
519
|
+
? instance.objectId
|
|
520
|
+
: undefined,
|
|
521
|
+
}
|
|
495
522
|
: undefined })),
|
|
496
523
|
React.createElement(Snackbar, { open: snackbarError.showAlert, handleClose: () => setSnackbarError({ isError: snackbarError.isError, showAlert: false }), message: snackbarError.message, error: snackbarError.isError })));
|
|
497
524
|
};
|
|
@@ -7,5 +7,5 @@ type CriteriaProps = {
|
|
|
7
7
|
error?: boolean;
|
|
8
8
|
};
|
|
9
9
|
type CriteriaValue = Record<string, unknown>;
|
|
10
|
-
|
|
11
|
-
export
|
|
10
|
+
declare const Criteria: (props: CriteriaProps) => React.JSX.Element;
|
|
11
|
+
export default Criteria;
|
|
@@ -6,7 +6,7 @@ import { Button, CircularProgress, Skeleton, Typography } from '../../../../core
|
|
|
6
6
|
import { Box } from '../../../../layout';
|
|
7
7
|
import CriteriaBuilder from '../../../CriteriaBuilder';
|
|
8
8
|
import { addressProperties, getPrefixedUrl } from '../utils';
|
|
9
|
-
|
|
9
|
+
const Criteria = (props) => {
|
|
10
10
|
const { value, canUpdateProperty, fieldDefinition, error } = props;
|
|
11
11
|
const apiServices = useApiServices();
|
|
12
12
|
const { handleChange, onAutosave } = useFormContext();
|
|
@@ -90,4 +90,5 @@ export default function Criteria(props) {
|
|
|
90
90
|
type: 'date-time',
|
|
91
91
|
},
|
|
92
92
|
], enablePresetValues: true }))) : (React.createElement(Typography, { variant: "body2", sx: { color: '#637381' } }, "No criteria"));
|
|
93
|
-
}
|
|
93
|
+
};
|
|
94
|
+
export default Criteria;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { DocumentParameterValidation } from '@evoke-platform/context';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { DocumentReference } from '../../types';
|
|
4
4
|
type DocumentProps = {
|
|
5
5
|
id: string;
|
|
6
|
+
fieldType?: 'file' | 'document';
|
|
6
7
|
canUpdateProperty: boolean;
|
|
7
8
|
error: boolean;
|
|
8
9
|
validate?: DocumentParameterValidation;
|
|
9
|
-
value: (File |
|
|
10
|
+
value: (File | DocumentReference)[] | undefined;
|
|
10
11
|
hasDescription?: boolean;
|
|
11
12
|
};
|
|
12
13
|
export declare const Document: (props: DocumentProps) => React.JSX.Element;
|
package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useApiServices } from '@evoke-platform/context';
|
|
1
|
+
import { useApiServices, } from '@evoke-platform/context';
|
|
2
2
|
import { useQuery } from '@tanstack/react-query';
|
|
3
3
|
import prettyBytes from 'pretty-bytes';
|
|
4
4
|
import React, { useEffect, useState } from 'react';
|
|
@@ -7,12 +7,12 @@ import { InfoRounded, UploadCloud } from '../../../../../../icons';
|
|
|
7
7
|
import { useFormContext } from '../../../../../../theme/hooks';
|
|
8
8
|
import { Skeleton, Snackbar, Typography } from '../../../../../core';
|
|
9
9
|
import { Box, Grid } from '../../../../../layout';
|
|
10
|
-
import { getPrefixedUrl } from '../../utils';
|
|
10
|
+
import { getEntryId, getPrefixedUrl, getUnnestedEntries, uploadFiles } from '../../utils';
|
|
11
11
|
import { DocumentList } from './DocumentList';
|
|
12
12
|
export const Document = (props) => {
|
|
13
|
-
const { id, canUpdateProperty, error, value, validate, hasDescription } = props;
|
|
13
|
+
const { id, fieldType = 'document', canUpdateProperty, error, value, validate, hasDescription } = props;
|
|
14
14
|
const apiServices = useApiServices();
|
|
15
|
-
const { object, handleChange, onAutosave: onAutosave, instance } = useFormContext();
|
|
15
|
+
const { object, handleChange, onAutosave: onAutosave, instance, form } = useFormContext();
|
|
16
16
|
const [snackbarError, setSnackbarError] = useState();
|
|
17
17
|
const [documents, setDocuments] = useState();
|
|
18
18
|
let allowedTypesMessage = '';
|
|
@@ -32,11 +32,22 @@ export const Document = (props) => {
|
|
|
32
32
|
useEffect(() => {
|
|
33
33
|
setDocuments(value);
|
|
34
34
|
}, [value]);
|
|
35
|
+
// Find the entry to get the configured createActionId and fileObjectId
|
|
36
|
+
const allEntries = getUnnestedEntries(form?.entries ?? []) ?? [];
|
|
37
|
+
const entry = allEntries?.find((entry) => getEntryId(entry) === id);
|
|
38
|
+
const createActionId = entry?.display?.createActionId ?? '_create';
|
|
39
|
+
// Get the configured objectId from display.fileObjectId, defaulting to sys__file
|
|
40
|
+
const fileObjectId = entry?.display?.fileObjectId ?? 'sys__file';
|
|
41
|
+
// For 'file' type properties, check regular object instance permissions
|
|
42
|
+
// For 'document' type properties, check document attachment permissions
|
|
43
|
+
const endpoint = fieldType === 'file'
|
|
44
|
+
? getPrefixedUrl(`/objects/${fileObjectId}/instances/checkAccess?action=execute&field=${createActionId}`)
|
|
45
|
+
: getPrefixedUrl(`/objects/${object?.id}/instances/${instance?.id}/documents/checkAccess?action=update`);
|
|
35
46
|
const { data: hasUpdatePermission = false, isLoading } = useQuery({
|
|
36
|
-
queryKey: ['
|
|
47
|
+
queryKey: ['hasUpdatePermission', object?.id, instance?.id, fieldType, id],
|
|
37
48
|
queryFn: async () => {
|
|
38
49
|
try {
|
|
39
|
-
const accessCheck = await apiServices.get(
|
|
50
|
+
const accessCheck = await apiServices.get(endpoint);
|
|
40
51
|
return accessCheck.result;
|
|
41
52
|
}
|
|
42
53
|
catch {
|
|
@@ -44,14 +55,36 @@ export const Document = (props) => {
|
|
|
44
55
|
}
|
|
45
56
|
},
|
|
46
57
|
staleTime: Infinity,
|
|
47
|
-
|
|
58
|
+
// For 'file' type fields the permission endpoint only requires the object ID, so the
|
|
59
|
+
// query can run on create actions where no instance exists yet. For 'document' type
|
|
60
|
+
// fields the endpoint is instance-scoped, so instance ID is still required.
|
|
61
|
+
enabled: canUpdateProperty && !!object?.id && (fieldType === 'file' || !!instance?.id),
|
|
48
62
|
});
|
|
49
63
|
const handleUpload = async (files) => {
|
|
50
|
-
|
|
51
|
-
|
|
64
|
+
if (!files?.length) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
let uploadedFiles = files;
|
|
68
|
+
// Get the createActionId and fileObjectId from form entry, default to '_create' and 'sys__file'
|
|
69
|
+
const allEntries = getUnnestedEntries(form?.entries ?? []);
|
|
70
|
+
const entry = allEntries?.find((entry) => getEntryId(entry) === id);
|
|
71
|
+
const createActionId = entry?.display?.createActionId ?? '_create';
|
|
72
|
+
const fileObjectId = entry?.display?.fileObjectId ?? 'sys__file';
|
|
73
|
+
// Immediately upload files for 'file' type properties when autosave is not enabled.
|
|
74
|
+
// Linking will happen upon final submission.
|
|
75
|
+
// If autosave is enabled, upload and linking will happen in the autosave handler.
|
|
76
|
+
if (fieldType === 'file' && !onAutosave) {
|
|
77
|
+
const { successfulUploads, errorMessage } = await uploadFiles(files, apiServices, createActionId, fileObjectId, undefined, false);
|
|
78
|
+
uploadedFiles = successfulUploads;
|
|
79
|
+
if (errorMessage) {
|
|
80
|
+
setSnackbarError({ message: errorMessage, type: 'error' });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Store uploaded file references (or File objects) in form state
|
|
84
|
+
const newDocuments = [...(documents ?? []), ...uploadedFiles];
|
|
52
85
|
setDocuments(newDocuments);
|
|
53
86
|
try {
|
|
54
|
-
|
|
87
|
+
await handleChange?.(id, newDocuments);
|
|
55
88
|
}
|
|
56
89
|
catch (error) {
|
|
57
90
|
console.error('Failed to update field:', error);
|
|
@@ -119,7 +152,7 @@ export const Document = (props) => {
|
|
|
119
152
|
} }, validate?.maxDocuments === 1
|
|
120
153
|
? `Maximum size is ${formattedMaxSize}.`
|
|
121
154
|
: `The maximum size of each document is ${formattedMaxSize}.`)))))),
|
|
122
|
-
canUpdateProperty && isLoading ? (React.createElement(Skeleton, { variant: "rectangular", height: formattedMaxSize || allowedTypesMessage ? '136px' : '115px', sx: { margin: '5px 0', borderRadius: '8px' } })) : (React.createElement(DocumentList, { id: id, handleChange: handleChange, onAutosave: onAutosave, value: documents, setSnackbarError: (type, message) => setSnackbarError({ message, type }), canUpdateProperty: canUpdateProperty && !!hasUpdatePermission })),
|
|
155
|
+
canUpdateProperty && isLoading ? (React.createElement(Skeleton, { variant: "rectangular", height: formattedMaxSize || allowedTypesMessage ? '136px' : '115px', sx: { margin: '5px 0', borderRadius: '8px' } })) : (React.createElement(DocumentList, { id: id, fieldType: fieldType, handleChange: handleChange, onAutosave: onAutosave, value: documents, setSnackbarError: (type, message) => setSnackbarError({ message, type }), canUpdateProperty: canUpdateProperty && !!hasUpdatePermission })),
|
|
123
156
|
React.createElement(Snackbar, { open: !!snackbarError?.message, handleClose: () => setSnackbarError(null), message: snackbarError?.message, error: snackbarError?.type === 'error' }),
|
|
124
157
|
errors.length > 0 && (React.createElement(Box, { display: 'flex', alignItems: 'center' },
|
|
125
158
|
React.createElement(InfoRounded, { sx: { fontSize: '.75rem', marginRight: '3px', color: '#D3271B' } }),
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { DocumentReference } from '../../types';
|
|
3
3
|
type DocumentListProps = {
|
|
4
|
-
handleChange?: (propertyId: string, value: (File |
|
|
4
|
+
handleChange?: (propertyId: string, value: (File | DocumentReference)[] | undefined) => void;
|
|
5
5
|
onAutosave?: (fieldId: string) => void | Promise<void>;
|
|
6
6
|
id: string;
|
|
7
|
+
fieldType?: 'document' | 'file';
|
|
7
8
|
canUpdateProperty: boolean;
|
|
8
|
-
value: (File |
|
|
9
|
+
value: (File | DocumentReference)[] | undefined;
|
|
9
10
|
setSnackbarError: (type: 'error' | 'success', message: string) => void;
|
|
10
11
|
};
|
|
11
12
|
export declare const DocumentList: (props: DocumentListProps) => React.JSX.Element;
|