@evoke-platform/ui-components 1.13.0-dev.5 → 1.13.0-dev.7

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 (31) hide show
  1. package/dist/published/components/custom/Form/FormComponents/DocumentComponent/Document.js +1 -1
  2. package/dist/published/components/custom/Form/FormComponents/DocumentComponent/DocumentList.js +6 -3
  3. package/dist/published/components/custom/FormV2/FormRenderer.d.ts +1 -1
  4. package/dist/published/components/custom/FormV2/FormRenderer.js +25 -27
  5. package/dist/published/components/custom/FormV2/FormRendererContainer.js +93 -86
  6. package/dist/published/components/custom/FormV2/components/ConditionalQueryClientProvider.d.ts +5 -0
  7. package/dist/published/components/custom/FormV2/components/ConditionalQueryClientProvider.js +21 -0
  8. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableField.js +86 -143
  9. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableFieldInput.d.ts +0 -2
  10. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableFieldInput.js +1 -4
  11. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/RepeatableField.js +104 -184
  12. package/dist/published/components/custom/FormV2/components/FormFieldTypes/Criteria.js +36 -49
  13. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.d.ts +3 -2
  14. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js +51 -32
  15. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.d.ts +4 -3
  16. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.js +40 -38
  17. package/dist/published/components/custom/FormV2/components/FormFieldTypes/UserProperty.js +17 -21
  18. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/ObjectPropertyInput.js +95 -169
  19. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.d.ts +0 -2
  20. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.js +12 -6
  21. package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.d.ts +2 -1
  22. package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.js +39 -17
  23. package/dist/published/components/custom/FormV2/components/types.d.ts +6 -1
  24. package/dist/published/components/custom/FormV2/components/utils.d.ts +10 -11
  25. package/dist/published/components/custom/FormV2/components/utils.js +169 -93
  26. package/dist/published/components/custom/FormV2/tests/FormRenderer.test.js +48 -15
  27. package/dist/published/components/custom/FormV2/tests/FormRendererContainer.test.js +38 -46
  28. package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.d.ts +2 -1
  29. package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.js +38 -13
  30. package/dist/published/components/custom/ViewDetailsV2/ViewDetailsV2Container.js +7 -2
  31. package/package.json +3 -2
@@ -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) {