@evoke-platform/ui-components 1.14.0 → 1.15.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/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.js +6 -3
- package/dist/published/components/custom/CriteriaBuilder/types.d.ts +0 -15
- package/dist/published/components/custom/CriteriaBuilder/utils.d.ts +0 -10
- package/dist/published/components/custom/CriteriaBuilder/utils.js +2 -161
- package/dist/published/components/custom/Form/utils.js +3 -2
- package/dist/published/components/custom/FormV2/FormRenderer.d.ts +1 -1
- package/dist/published/components/custom/FormV2/FormRenderer.js +25 -33
- package/dist/published/components/custom/FormV2/FormRendererContainer.js +246 -164
- package/dist/published/components/custom/FormV2/components/ConditionalQueryClientProvider.d.ts +5 -0
- package/dist/published/components/custom/FormV2/components/ConditionalQueryClientProvider.js +21 -0
- 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 +0 -1
- package/dist/published/components/custom/FormV2/components/FormContext.js +0 -1
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableField.js +86 -143
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableFieldInput.d.ts +0 -2
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableFieldInput.js +1 -4
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/RepeatableField.js +105 -185
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/Criteria.d.ts +2 -2
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/Criteria.js +39 -51
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js +18 -26
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.js +18 -18
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/Image.js +2 -4
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/UserProperty.js +17 -21
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/ObjectPropertyInput.js +96 -169
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.d.ts +0 -2
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.js +63 -15
- package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.d.ts +2 -1
- package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.js +67 -36
- package/dist/published/components/custom/FormV2/components/utils.d.ts +23 -4
- package/dist/published/components/custom/FormV2/components/utils.js +139 -29
- package/dist/published/components/custom/FormV2/tests/FormRenderer.test.js +28 -14
- package/dist/published/components/custom/FormV2/tests/FormRendererContainer.test.js +40 -46
- package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.d.ts +2 -1
- package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.js +56 -19
- package/dist/published/components/custom/ViewDetailsV2/ViewDetailsV2Container.js +7 -2
- package/dist/published/components/custom/index.d.ts +2 -0
- package/dist/published/components/custom/index.js +1 -0
- package/dist/published/components/custom/types.d.ts +15 -0
- package/dist/published/components/custom/types.js +1 -0
- package/dist/published/components/custom/util.d.ts +10 -0
- package/dist/published/components/custom/util.js +161 -1
- package/dist/published/index.d.ts +2 -2
- package/dist/published/index.js +1 -1
- package/dist/published/theme/hooks.d.ts +2 -3
- package/package.json +4 -4
|
@@ -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,7 +18,6 @@ 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;
|
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
import { useApiServices, useNotification, } from '@evoke-platform/context';
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import { useQuery } from '@tanstack/react-query';
|
|
3
|
+
import { debounce, isEmpty } from 'lodash';
|
|
4
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
4
5
|
import { useFormContext } from '../../../../../../theme/hooks';
|
|
5
6
|
import { Skeleton } from '../../../../../core';
|
|
6
7
|
import { retrieveCustomErrorMessage } from '../../../../Form/utils';
|
|
7
8
|
import { getMiddleObject, getMiddleObjectFilter, getPrefixedUrl, transformToWhere } from '../../utils';
|
|
8
9
|
import { DropdownRepeatableFieldInput } from './DropdownRepeatableFieldInput';
|
|
9
10
|
const DropdownRepeatableField = (props) => {
|
|
10
|
-
const { id, fieldDefinition, criteria, readOnly,
|
|
11
|
-
const {
|
|
11
|
+
const { id, fieldDefinition, criteria, readOnly, middleObject, hasDescription, viewLayout, initialMiddleObjectInstances, } = props;
|
|
12
|
+
const { instance } = useFormContext();
|
|
12
13
|
const [layout, setLayout] = useState();
|
|
13
|
-
const [loading, setLoading] = useState(false);
|
|
14
14
|
const [layoutLoaded, setLayoutLoaded] = useState(false);
|
|
15
15
|
const [searchValue, setSearchValue] = useState('');
|
|
16
16
|
const [middleObjectInstances, setMiddleObjectInstances] = useState(initialMiddleObjectInstances);
|
|
17
|
-
const [
|
|
18
|
-
const [endObjectInstances, setEndObjectInstances] = useState(fetchedOptions[`${fieldDefinition.id}EndObjectInstances`] || []);
|
|
19
|
-
const [initialLoading, setInitialLoading] = useState(endObjectInstances ? false : true);
|
|
17
|
+
const [debouncedSearchValue, setDebouncedSearchValue] = useState();
|
|
20
18
|
const [selectedOptions, setSelectedOptions] = useState([]);
|
|
21
|
-
const [hasFetched, setHasFetched] = useState(!!fetchedOptions[`${fieldDefinition.id}EndObjectInstancesHaveFetched`] || false);
|
|
22
19
|
const [snackbarError, setSnackbarError] = useState({
|
|
23
20
|
showAlert: false,
|
|
24
21
|
isError: true,
|
|
@@ -41,6 +38,12 @@ const DropdownRepeatableField = (props) => {
|
|
|
41
38
|
const newInstances = await getMiddleObjectInstances();
|
|
42
39
|
setMiddleObjectInstances(newInstances);
|
|
43
40
|
};
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
instanceChanges?.subscribe(middleObject.rootObjectId, () => {
|
|
43
|
+
fetchMiddleObjectInstances();
|
|
44
|
+
});
|
|
45
|
+
return () => instanceChanges?.unsubscribe(middleObject.rootObjectId);
|
|
46
|
+
}, [instanceChanges, fetchMiddleObjectInstances, middleObject]);
|
|
44
47
|
const setDropDownSelections = (instances) => {
|
|
45
48
|
setSelectedOptions(instances
|
|
46
49
|
.filter((currInstance) => fieldDefinition.manyToManyPropertyId in currInstance)
|
|
@@ -52,146 +55,86 @@ const DropdownRepeatableField = (props) => {
|
|
|
52
55
|
.sort((instanceA, instanceB) => instanceA.label.localeCompare(instanceB.label)));
|
|
53
56
|
};
|
|
54
57
|
useEffect(() => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
setEndObject(endObject);
|
|
67
|
-
let defaultLayout;
|
|
68
|
-
if (endObject.viewLayout?.dropdown) {
|
|
69
|
-
defaultLayout = {
|
|
70
|
-
id: 'default',
|
|
71
|
-
name: 'Default',
|
|
72
|
-
objectId: endObject.id,
|
|
73
|
-
...endObject.viewLayout.dropdown,
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
if (viewLayout) {
|
|
77
|
-
apiServices
|
|
78
|
-
.get(getPrefixedUrl(`/objects/${viewLayout.objectId}/dropdownLayouts/${viewLayout.id}`))
|
|
79
|
-
.then(setLayout)
|
|
80
|
-
.catch(() => setLayout(defaultLayout))
|
|
81
|
-
.finally(() => setLayoutLoaded(true));
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
setLayout(defaultLayout);
|
|
85
|
-
setLayoutLoaded(true);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
}, [middleObject, viewLayout]);
|
|
91
|
-
useEffect(() => {
|
|
92
|
-
instanceChanges?.subscribe(middleObject.rootObjectId, () => {
|
|
93
|
-
fetchMiddleObjectInstances();
|
|
94
|
-
});
|
|
95
|
-
return () => instanceChanges?.unsubscribe(middleObject.rootObjectId);
|
|
96
|
-
}, [instanceChanges, fetchMiddleObjectInstances]);
|
|
97
|
-
const fetchEndObjectInstances = useCallback((searchedName) => {
|
|
98
|
-
if ((fieldDefinition.objectId &&
|
|
99
|
-
fieldDefinition.manyToManyPropertyId &&
|
|
100
|
-
endObjectInstances.length === 0 &&
|
|
101
|
-
!hasFetched) ||
|
|
102
|
-
(searchedName !== undefined && searchedName !== '')) {
|
|
103
|
-
setLoading(true);
|
|
104
|
-
const endObjectProperty = middleObject.properties?.find((currProperty) => fieldDefinition.manyToManyPropertyId === currProperty.id);
|
|
105
|
-
if (endObjectProperty?.objectId) {
|
|
106
|
-
const { propertyId, direction } = layout?.sort ?? {
|
|
107
|
-
propertyId: 'name',
|
|
108
|
-
direction: 'asc',
|
|
109
|
-
};
|
|
110
|
-
const filter = {
|
|
111
|
-
limit: 100,
|
|
112
|
-
order: `${propertyId} ${direction}`,
|
|
113
|
-
};
|
|
114
|
-
let searchCriteria = criteria && !isEmpty(criteria) ? transformToWhere(criteria) : {};
|
|
115
|
-
if (searchedName?.length) {
|
|
116
|
-
const nameCriteria = transformToWhere({
|
|
117
|
-
name: {
|
|
118
|
-
like: searchedName,
|
|
119
|
-
options: 'i',
|
|
120
|
-
},
|
|
121
|
-
});
|
|
122
|
-
searchCriteria = !isEmpty(criteria)
|
|
123
|
-
? {
|
|
124
|
-
and: [searchCriteria, nameCriteria],
|
|
125
|
-
}
|
|
126
|
-
: nameCriteria;
|
|
127
|
-
}
|
|
128
|
-
filter.where = searchCriteria;
|
|
129
|
-
apiServices.get(getPrefixedUrl(`/objects/${endObjectProperty.objectId}/instances`), { params: { filter: JSON.stringify(filter) } }, (error, instances) => {
|
|
130
|
-
if (!error && instances) {
|
|
131
|
-
setEndObjectInstances(instances);
|
|
132
|
-
setHasFetched(true);
|
|
133
|
-
}
|
|
134
|
-
setInitialLoading(false);
|
|
135
|
-
setLoading(false);
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
else if (endObjectInstances.length !== 0) {
|
|
140
|
-
setInitialLoading(false);
|
|
141
|
-
}
|
|
142
|
-
}, [fieldDefinition.objectId, fieldDefinition.manyToManyPropertyId, middleObject]);
|
|
143
|
-
const debouncedEndObjectSearch = useCallback(debounce(fetchEndObjectInstances, 500), [fetchEndObjectInstances]);
|
|
58
|
+
setDropDownSelections(middleObjectInstances);
|
|
59
|
+
}, [middleObjectInstances]);
|
|
60
|
+
const endObjectProperty = useMemo(() => middleObject.properties?.find((currProperty) => fieldDefinition.manyToManyPropertyId === currProperty.id), [middleObject.properties, fieldDefinition.manyToManyPropertyId]);
|
|
61
|
+
const { data: endObject } = useQuery({
|
|
62
|
+
queryKey: [endObjectProperty?.objectId, 'endObject', 'effective'],
|
|
63
|
+
queryFn: () => apiServices.get(getPrefixedUrl(`/objects/${endObjectProperty?.objectId}/effective`), {
|
|
64
|
+
params: { filter: { fields: ['id', 'name', 'properties', 'viewLayout'] } },
|
|
65
|
+
}),
|
|
66
|
+
staleTime: Infinity,
|
|
67
|
+
enabled: !!endObjectProperty?.objectId,
|
|
68
|
+
});
|
|
144
69
|
useEffect(() => {
|
|
145
|
-
if (!
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
70
|
+
if (!endObject)
|
|
71
|
+
return;
|
|
72
|
+
let defaultLayout;
|
|
73
|
+
if (endObject.viewLayout?.dropdown) {
|
|
74
|
+
defaultLayout = {
|
|
75
|
+
id: 'default',
|
|
76
|
+
name: 'Default',
|
|
77
|
+
objectId: endObject.id,
|
|
78
|
+
...endObject.viewLayout.dropdown,
|
|
79
|
+
};
|
|
152
80
|
}
|
|
153
|
-
if (
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
81
|
+
if (viewLayout) {
|
|
82
|
+
apiServices
|
|
83
|
+
.get(getPrefixedUrl(`/objects/${viewLayout.objectId}/dropdownLayouts/${viewLayout.id}`))
|
|
84
|
+
.then(setLayout)
|
|
85
|
+
.catch(() => setLayout(defaultLayout))
|
|
86
|
+
.finally(() => setLayoutLoaded(true));
|
|
157
87
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
});
|
|
88
|
+
else {
|
|
89
|
+
setLayout(defaultLayout);
|
|
90
|
+
setLayoutLoaded(true);
|
|
162
91
|
}
|
|
163
|
-
}, [
|
|
92
|
+
}, [endObject, viewLayout]);
|
|
93
|
+
const debouncedSetSearchValue = useMemo(() => debounce((value) => {
|
|
94
|
+
setDebouncedSearchValue(value);
|
|
95
|
+
}, 200), []);
|
|
164
96
|
useEffect(() => {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
97
|
+
debouncedSetSearchValue(searchValue);
|
|
98
|
+
return () => {
|
|
99
|
+
debouncedSetSearchValue.cancel();
|
|
169
100
|
};
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
fieldDefinition.id,
|
|
182
|
-
hasFetched,
|
|
183
|
-
initialMiddleObjectInstances,
|
|
184
|
-
setFetchedOptions,
|
|
185
|
-
]);
|
|
186
|
-
useEffect(() => {
|
|
187
|
-
debouncedEndObjectSearch(searchValue);
|
|
188
|
-
return () => debouncedEndObjectSearch.cancel();
|
|
189
|
-
}, [searchValue, debouncedEndObjectSearch]);
|
|
190
|
-
useEffect(() => {
|
|
191
|
-
if (layoutLoaded) {
|
|
192
|
-
fetchEndObjectInstances();
|
|
101
|
+
}, [searchValue, debouncedSetSearchValue, layoutLoaded]);
|
|
102
|
+
const searchCriteria = useMemo(() => {
|
|
103
|
+
let searchCriteria = criteria && !isEmpty(criteria) ? transformToWhere(criteria) : {};
|
|
104
|
+
if (debouncedSearchValue?.length) {
|
|
105
|
+
const nameCriteria = transformToWhere({
|
|
106
|
+
name: {
|
|
107
|
+
like: debouncedSearchValue,
|
|
108
|
+
options: 'i',
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
searchCriteria = !isEmpty(criteria) ? { and: [searchCriteria, nameCriteria] } : nameCriteria;
|
|
193
112
|
}
|
|
194
|
-
|
|
113
|
+
return searchCriteria;
|
|
114
|
+
}, [criteria, debouncedSearchValue]);
|
|
115
|
+
// Construct filter from debounced criteria
|
|
116
|
+
const updatedFilter = useMemo(() => {
|
|
117
|
+
const { propertyId, direction } = layout?.sort ?? {
|
|
118
|
+
propertyId: 'name',
|
|
119
|
+
direction: 'asc',
|
|
120
|
+
};
|
|
121
|
+
const filter = {
|
|
122
|
+
limit: 100,
|
|
123
|
+
order: `${propertyId} ${direction}`,
|
|
124
|
+
where: searchCriteria,
|
|
125
|
+
};
|
|
126
|
+
return filter;
|
|
127
|
+
}, [searchCriteria, layout]);
|
|
128
|
+
const { data: endObjectInstances = [], isLoading: initialLoading } = useQuery({
|
|
129
|
+
queryKey: ['endObjectInstances', endObjectProperty.objectId, updatedFilter],
|
|
130
|
+
queryFn: async () => {
|
|
131
|
+
return apiServices.get(getPrefixedUrl(`/objects/${endObjectProperty.objectId}/instances`), { params: { filter: JSON.stringify(updatedFilter) } });
|
|
132
|
+
},
|
|
133
|
+
enabled: !!fieldDefinition.objectId && !!fieldDefinition.manyToManyPropertyId && !!endObjectProperty?.objectId,
|
|
134
|
+
staleTime: Infinity,
|
|
135
|
+
// Keep old instances while filtering
|
|
136
|
+
placeholderData: (previousData) => previousData,
|
|
137
|
+
});
|
|
195
138
|
const saveMiddleInstance = async (endObjectId, endObjectName) => {
|
|
196
139
|
if (fieldDefinition.objectId) {
|
|
197
140
|
const middleObject = getMiddleObject(fieldDefinition, endObjectId, endObjectName, instance);
|
|
@@ -228,6 +171,6 @@ const DropdownRepeatableField = (props) => {
|
|
|
228
171
|
});
|
|
229
172
|
}
|
|
230
173
|
};
|
|
231
|
-
return initialLoading || !middleObject || !middleObjectInstances || !endObjectInstances || !endObject ? (React.createElement(Skeleton, null)) : (React.createElement(React.Fragment, null, middleObjectInstances && endObject && (React.createElement(DropdownRepeatableFieldInput, { id: id, fieldDefinition: fieldDefinition, readOnly: readOnly || !middleObject.actions?.some((action) => action.id === '_create'), layout: layout,
|
|
174
|
+
return initialLoading || !middleObject || !middleObjectInstances || !endObjectInstances || !endObject ? (React.createElement(Skeleton, null)) : (React.createElement(React.Fragment, null, middleObjectInstances && endObject && (React.createElement(DropdownRepeatableFieldInput, { id: id, fieldDefinition: fieldDefinition, readOnly: readOnly || !middleObject.actions?.some((action) => action.id === '_create'), layout: layout, endObjectInstances: endObjectInstances ?? [], endObject: endObject, searchValue: searchValue, loading: initialLoading, handleSaveMiddleInstance: saveMiddleInstance, handleRemoveMiddleInstance: removeMiddleInstance, setSearchValue: setSearchValue, setSnackbarError: setSnackbarError, snackbarError: snackbarError, selectedOptions: selectedOptions, setSelectedOptions: setSelectedOptions, hasDescription: hasDescription }))));
|
|
232
175
|
};
|
|
233
176
|
export default DropdownRepeatableField;
|
|
@@ -6,7 +6,6 @@ type DropdownRepeatableFieldInputProps = {
|
|
|
6
6
|
fieldDefinition: InputParameter | Property;
|
|
7
7
|
readOnly: boolean;
|
|
8
8
|
layout?: DropdownViewLayout;
|
|
9
|
-
middleObjectInstances: ObjectInstance[];
|
|
10
9
|
endObjectInstances: ObjectInstance[];
|
|
11
10
|
endObject: Pick<Obj, 'id' | 'name' | 'properties'>;
|
|
12
11
|
searchValue: string;
|
|
@@ -26,7 +25,6 @@ type DropdownRepeatableFieldInputProps = {
|
|
|
26
25
|
message?: string;
|
|
27
26
|
isError: boolean;
|
|
28
27
|
};
|
|
29
|
-
setDropdownSelections?: (middleObjectInstances: ObjectInstance[]) => void;
|
|
30
28
|
hasDescription?: boolean;
|
|
31
29
|
};
|
|
32
30
|
export type DropdownRepeatableFieldInputOption = AutocompleteOption & {
|
|
@@ -7,12 +7,9 @@ import FormField from '../../../../FormField';
|
|
|
7
7
|
import { normalizeDates } from '../../utils';
|
|
8
8
|
const isDropdownRepeatableFieldInputOption = (option) => isObject(option) && 'label' in option && 'endObjectId' in option;
|
|
9
9
|
export const DropdownRepeatableFieldInput = (props) => {
|
|
10
|
-
const { id, fieldDefinition, readOnly, layout,
|
|
10
|
+
const { id, fieldDefinition, readOnly, layout, endObjectInstances, endObject, searchValue, loading, handleSaveMiddleInstance, handleRemoveMiddleInstance, setSearchValue, selectedOptions, setSnackbarError, snackbarError, hasDescription, } = props;
|
|
11
11
|
const { fieldHeight } = useFormContext();
|
|
12
12
|
const [selectOptions, setSelectOptions] = useState([]);
|
|
13
|
-
useEffect(() => {
|
|
14
|
-
setDropdownSelections && setDropdownSelections(middleObjectInstances);
|
|
15
|
-
}, [middleObjectInstances]);
|
|
16
13
|
useEffect(() => {
|
|
17
14
|
const manyToManyPropertyId = fieldDefinition.manyToManyPropertyId;
|
|
18
15
|
if (manyToManyPropertyId) {
|