@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.
Files changed (46) hide show
  1. package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.js +6 -3
  2. package/dist/published/components/custom/CriteriaBuilder/types.d.ts +0 -15
  3. package/dist/published/components/custom/CriteriaBuilder/utils.d.ts +0 -10
  4. package/dist/published/components/custom/CriteriaBuilder/utils.js +2 -161
  5. package/dist/published/components/custom/Form/utils.js +3 -2
  6. package/dist/published/components/custom/FormV2/FormRenderer.d.ts +1 -1
  7. package/dist/published/components/custom/FormV2/FormRenderer.js +25 -33
  8. package/dist/published/components/custom/FormV2/FormRendererContainer.js +246 -164
  9. package/dist/published/components/custom/FormV2/components/ConditionalQueryClientProvider.d.ts +5 -0
  10. package/dist/published/components/custom/FormV2/components/ConditionalQueryClientProvider.js +21 -0
  11. package/dist/published/components/custom/FormV2/components/DefaultValues.d.ts +1 -1
  12. package/dist/published/components/custom/FormV2/components/DefaultValues.js +60 -89
  13. package/dist/published/components/custom/FormV2/components/FormContext.d.ts +0 -1
  14. package/dist/published/components/custom/FormV2/components/FormContext.js +0 -1
  15. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableField.js +86 -143
  16. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableFieldInput.d.ts +0 -2
  17. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableFieldInput.js +1 -4
  18. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/RepeatableField.js +105 -185
  19. package/dist/published/components/custom/FormV2/components/FormFieldTypes/Criteria.d.ts +2 -2
  20. package/dist/published/components/custom/FormV2/components/FormFieldTypes/Criteria.js +39 -51
  21. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js +18 -26
  22. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.js +18 -18
  23. package/dist/published/components/custom/FormV2/components/FormFieldTypes/Image.js +2 -4
  24. package/dist/published/components/custom/FormV2/components/FormFieldTypes/UserProperty.js +17 -21
  25. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/ObjectPropertyInput.js +96 -169
  26. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.d.ts +0 -2
  27. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.js +63 -15
  28. package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.d.ts +2 -1
  29. package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.js +67 -36
  30. package/dist/published/components/custom/FormV2/components/utils.d.ts +23 -4
  31. package/dist/published/components/custom/FormV2/components/utils.js +139 -29
  32. package/dist/published/components/custom/FormV2/tests/FormRenderer.test.js +28 -14
  33. package/dist/published/components/custom/FormV2/tests/FormRendererContainer.test.js +40 -46
  34. package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.d.ts +2 -1
  35. package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.js +56 -19
  36. package/dist/published/components/custom/ViewDetailsV2/ViewDetailsV2Container.js +7 -2
  37. package/dist/published/components/custom/index.d.ts +2 -0
  38. package/dist/published/components/custom/index.js +1 -0
  39. package/dist/published/components/custom/types.d.ts +15 -0
  40. package/dist/published/components/custom/types.js +1 -0
  41. package/dist/published/components/custom/util.d.ts +10 -0
  42. package/dist/published/components/custom/util.js +161 -1
  43. package/dist/published/index.d.ts +2 -2
  44. package/dist/published/index.js +1 -1
  45. package/dist/published/theme/hooks.d.ts +2 -3
  46. 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, unnestedEntries, entry, fieldValue, fieldId, apiServices, userAccount, formValues, updatedRelatedObjectValue) {
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?.id && relatedObjectParameter) {
32
- let relatedObjectId = relatedObjectParameter.objectId;
33
- if (!relatedObjectId) {
34
- const relatedObjectParamEntry = unnestedEntries.find((e) => getEntryId(e) === relatedObjectParameter.id);
35
- relatedObjectId = relatedObjectParamEntry?.display?.relatedObjectId;
36
- }
37
- const instance = await new Promise((resolve) => {
38
- apiServices.get(getPrefixedUrl(`/objects/${relatedObjectId}/instances/${relatedObjectInstance?.id}`), (error, instance) => {
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?.[groups.addressProperty]?.[groups.nestedAddressProperty]) {
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?.id && relatedObjectParameter) {
81
- let relatedObjectId = relatedObjectParameter.objectId;
82
- if (!relatedObjectId) {
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?.[groups.nestedProperty]) {
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?.id && relatedObjectParameter) {
121
- let relatedObjectId = relatedObjectParameter.objectId;
122
- if (!relatedObjectId) {
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, unnestedEntries, entry, formValues?.[addressObject]?.[addressField], parameterId, apiServices, userAccount, formValues, updatedRelatedObjectValue);
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(entry.display.defaultValue) &&
191
- entry.display.defaultValue.some((item) => /^{{.*}}$/.test(item))) {
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, unnestedEntries, entry, entry.display.defaultValue, parameterId, apiServices, userAccount, formValues, updatedRelatedObjectValue);
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(entry.display.defaultValue)?.groups;
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, unnestedEntries, entry, formValues?.[parameterId], parameterId, apiServices, userAccount, formValues, updatedRelatedObjectValue);
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;
@@ -10,7 +10,6 @@ export const FormContext = createContext({
10
10
  expandedSections: [],
11
11
  parameters: [],
12
12
  showSubmitError: false,
13
- triggerFieldReset: false,
14
13
  handleChange: () => { },
15
14
  fieldHeight: 'medium',
16
15
  form: {},
@@ -1,24 +1,21 @@
1
1
  import { useApiServices, useNotification, } from '@evoke-platform/context';
2
- import { debounce, isArray, isEmpty, isEqual } from 'lodash';
3
- import React, { useCallback, useEffect, useState } from 'react';
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, initialMiddleObjectInstances, middleObject, hasDescription, viewLayout, } = props;
11
- const { fetchedOptions, setFetchedOptions, instance } = useFormContext();
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 [endObject, setEndObject] = useState(fetchedOptions[`${fieldDefinition.id}EndObject`]);
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
- const endObjectProperty = middleObject?.properties?.find((currProperty) => fieldDefinition.manyToManyPropertyId === currProperty.id);
56
- if (endObjectProperty && endObjectProperty.objectId && !fetchedOptions[`${fieldDefinition.id}EndObject`]) {
57
- setLayoutLoaded(false);
58
- apiServices.get(getPrefixedUrl(`/objects/${endObjectProperty.objectId}/effective`), { params: { filter: { fields: ['id', 'name', 'properties', 'viewLayout'] } } }, (error, effectiveObject) => {
59
- if (error) {
60
- console.error(error);
61
- }
62
- else {
63
- // If there's no error then the effective object is defined.
64
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
65
- const endObject = effectiveObject;
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 (!fetchedOptions[`${fieldDefinition.id}EndObjectInstances`] ||
146
- (isArray(fetchedOptions[`${fieldDefinition.id}EndObjectInstances`]) &&
147
- fetchedOptions[`${fieldDefinition.id}EndObjectInstances`].length === 0)) {
148
- setFetchedOptions({
149
- [`${fieldDefinition.id}EndObjectInstances`]: endObjectInstances,
150
- [`${fieldDefinition.id}EndObjectInstancesHaveFetched`]: hasFetched,
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 (!fetchedOptions[`${fieldDefinition.id}EndObject`]) {
154
- setFetchedOptions({
155
- [`${fieldDefinition.id}EndObject`]: endObject,
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
- if (!isEqual(middleObjectInstances, initialMiddleObjectInstances)) {
159
- setFetchedOptions({
160
- [`${fieldDefinition.id}MiddleObjectInstances`]: middleObjectInstances,
161
- });
88
+ else {
89
+ setLayout(defaultLayout);
90
+ setLayoutLoaded(true);
162
91
  }
163
- }, [endObjectInstances, endObject, middleObjectInstances]);
92
+ }, [endObject, viewLayout]);
93
+ const debouncedSetSearchValue = useMemo(() => debounce((value) => {
94
+ setDebouncedSearchValue(value);
95
+ }, 200), []);
164
96
  useEffect(() => {
165
- const updateFetchedOptions = (key, value) => {
166
- if (!fetchedOptions[key]) {
167
- setFetchedOptions({ [key]: value });
168
- }
97
+ debouncedSetSearchValue(searchValue);
98
+ return () => {
99
+ debouncedSetSearchValue.cancel();
169
100
  };
170
- updateFetchedOptions(`${fieldDefinition.id}EndObjectInstances`, endObjectInstances);
171
- updateFetchedOptions(`${fieldDefinition.id}EndObjectInstancesHaveFetched`, hasFetched);
172
- updateFetchedOptions(`${fieldDefinition.id}EndObject`, endObject);
173
- if (!isEqual(middleObjectInstances, initialMiddleObjectInstances)) {
174
- setFetchedOptions({ [`${fieldDefinition.id}MiddleObjectInstances`]: middleObjectInstances });
175
- }
176
- }, [
177
- endObjectInstances,
178
- endObject,
179
- middleObjectInstances,
180
- fetchedOptions,
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
- }, [fetchEndObjectInstances, layoutLoaded]);
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, middleObjectInstances: middleObjectInstances, endObjectInstances: endObjectInstances ?? [], endObject: endObject, searchValue: searchValue, loading: loading, handleSaveMiddleInstance: saveMiddleInstance, handleRemoveMiddleInstance: removeMiddleInstance, setSearchValue: setSearchValue, setSnackbarError: setSnackbarError, snackbarError: snackbarError, selectedOptions: selectedOptions, setSelectedOptions: setSelectedOptions, setDropdownSelections: setDropDownSelections, hasDescription: hasDescription }))));
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, middleObjectInstances, endObjectInstances, endObject, searchValue, loading, handleSaveMiddleInstance, handleRemoveMiddleInstance, setSearchValue, selectedOptions, setSnackbarError, snackbarError, setDropdownSelections, hasDescription, } = props;
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) {