@evoke-platform/ui-components 1.13.0-dev.7 → 1.14.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.
Files changed (65) hide show
  1. package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.d.ts +4 -4
  2. package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.js +72 -145
  3. package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.test.js +67 -189
  4. package/dist/published/components/custom/CriteriaBuilder/PropertyTree.d.ts +6 -6
  5. package/dist/published/components/custom/CriteriaBuilder/PropertyTree.js +25 -12
  6. package/dist/published/components/custom/CriteriaBuilder/PropertyTreeItem.d.ts +5 -4
  7. package/dist/published/components/custom/CriteriaBuilder/PropertyTreeItem.js +22 -34
  8. package/dist/published/components/custom/CriteriaBuilder/types.d.ts +11 -2
  9. package/dist/published/components/custom/CriteriaBuilder/utils.d.ts +34 -6
  10. package/dist/published/components/custom/CriteriaBuilder/utils.js +89 -18
  11. package/dist/published/components/custom/Form/FormComponents/DocumentComponent/Document.js +1 -1
  12. package/dist/published/components/custom/Form/FormComponents/DocumentComponent/DocumentList.js +3 -6
  13. package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/RepeatableField.js +1 -1
  14. package/dist/published/components/custom/Form/utils.d.ts +0 -1
  15. package/dist/published/components/custom/FormField/DateTimePickerSelect/DateTimePickerSelect.js +1 -2
  16. package/dist/published/components/custom/FormV2/FormRenderer.d.ts +2 -2
  17. package/dist/published/components/custom/FormV2/FormRenderer.js +29 -26
  18. package/dist/published/components/custom/FormV2/FormRendererContainer.d.ts +3 -1
  19. package/dist/published/components/custom/FormV2/FormRendererContainer.js +88 -95
  20. package/dist/published/components/custom/FormV2/components/Body.js +1 -1
  21. package/dist/published/components/custom/FormV2/components/Footer.js +1 -1
  22. package/dist/published/components/custom/FormV2/components/FormContext.d.ts +0 -1
  23. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/ActionDialog.d.ts +0 -1
  24. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableField.js +143 -86
  25. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableFieldInput.d.ts +2 -0
  26. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableFieldInput.js +4 -1
  27. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/RepeatableField.js +186 -106
  28. package/dist/published/components/custom/FormV2/components/FormFieldTypes/Criteria.js +49 -36
  29. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.d.ts +2 -3
  30. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js +32 -51
  31. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.d.ts +3 -4
  32. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.js +38 -40
  33. package/dist/published/components/custom/FormV2/components/FormFieldTypes/UserProperty.js +21 -17
  34. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/InstanceLookup.js +1 -1
  35. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/ObjectPropertyInput.js +169 -95
  36. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.d.ts +2 -0
  37. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.js +6 -12
  38. package/dist/published/components/custom/FormV2/components/FormSections.js +0 -1
  39. package/dist/published/components/custom/FormV2/components/Header.d.ts +1 -0
  40. package/dist/published/components/custom/FormV2/components/Header.js +19 -8
  41. package/dist/published/components/custom/FormV2/components/HtmlView.d.ts +9 -0
  42. package/dist/published/components/custom/FormV2/components/HtmlView.js +46 -0
  43. package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.d.ts +1 -2
  44. package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.js +20 -46
  45. package/dist/published/components/custom/FormV2/components/types.d.ts +1 -6
  46. package/dist/published/components/custom/FormV2/components/utils.d.ts +11 -11
  47. package/dist/published/components/custom/FormV2/components/utils.js +104 -181
  48. package/dist/published/components/custom/FormV2/tests/FormRenderer.test.js +17 -50
  49. package/dist/published/components/custom/FormV2/tests/FormRendererContainer.test.js +131 -40
  50. package/dist/published/components/custom/HistoryLog/HistoryData.js +1 -2
  51. package/dist/published/components/custom/HistoryLog/index.js +1 -2
  52. package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.d.ts +1 -2
  53. package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.js +22 -61
  54. package/dist/published/components/custom/ViewDetailsV2/ViewDetailsV2Container.d.ts +3 -0
  55. package/dist/published/components/custom/ViewDetailsV2/ViewDetailsV2Container.js +5 -8
  56. package/dist/published/stories/Backdrop.stories.d.ts +2 -2
  57. package/dist/published/stories/CriteriaBuilder.stories.js +22 -70
  58. package/dist/published/stories/FormLabel.stories.d.ts +2 -2
  59. package/dist/published/stories/FormRenderer.stories.d.ts +3 -3
  60. package/dist/published/stories/FormRendererContainer.stories.d.ts +15 -5
  61. package/dist/published/stories/ViewDetailsV2Container.stories.d.ts +9 -0
  62. package/dist/published/theme/hooks.d.ts +1 -2
  63. package/package.json +11 -17
  64. package/dist/published/components/custom/FormV2/components/ConditionalQueryClientProvider.d.ts +0 -5
  65. package/dist/published/components/custom/FormV2/components/ConditionalQueryClientProvider.js +0 -21
@@ -7,9 +7,12 @@ 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, endObjectInstances, endObject, searchValue, loading, handleSaveMiddleInstance, handleRemoveMiddleInstance, setSearchValue, selectedOptions, setSnackbarError, snackbarError, hasDescription, } = props;
10
+ const { id, fieldDefinition, readOnly, layout, middleObjectInstances, endObjectInstances, endObject, searchValue, loading, handleSaveMiddleInstance, handleRemoveMiddleInstance, setSearchValue, selectedOptions, setSnackbarError, snackbarError, setDropdownSelections, hasDescription, } = props;
11
11
  const { fieldHeight } = useFormContext();
12
12
  const [selectOptions, setSelectOptions] = useState([]);
13
+ useEffect(() => {
14
+ setDropdownSelections && setDropdownSelections(middleObjectInstances);
15
+ }, [middleObjectInstances]);
13
16
  useEffect(() => {
14
17
  const manyToManyPropertyId = fieldDefinition.manyToManyPropertyId;
15
18
  if (manyToManyPropertyId) {
@@ -1,8 +1,7 @@
1
1
  import { useApiServices, useNotification, } from '@evoke-platform/context';
2
- import { useQuery, useQueryClient } from '@tanstack/react-query';
3
- import { get, omit, startCase } from 'lodash';
2
+ import { get, isEqual, omit, startCase } from 'lodash';
4
3
  import { DateTime } from 'luxon';
5
- import React, { useCallback, useEffect, useMemo, useState } from 'react';
4
+ import React, { useCallback, useEffect, useState } from 'react';
6
5
  import sift from 'sift';
7
6
  import { Edit, ExpandMoreOutlined, TrashCan } from '../../../../../../icons';
8
7
  import useWidgetSize, { useFormContext } from '../../../../../../theme/hooks';
@@ -10,7 +9,7 @@ import { Accordion, AccordionDetails, AccordionSummary, Button, IconButton, Skel
10
9
  import { Box } from '../../../../../layout';
11
10
  import { getReadableQuery } from '../../../../CriteriaBuilder';
12
11
  import { retrieveCustomErrorMessage } from '../../../../Form/utils';
13
- import { convertPropertiesToParams, deleteDocuments, formatSubmission, getPrefixedUrl, transformToWhere, useFormById, } from '../../utils';
12
+ import { deleteDocuments, formatSubmission, getPrefixedUrl, transformToWhere } from '../../utils';
14
13
  import { ActionDialog } from './ActionDialog';
15
14
  import { DocumentViewerCell } from './DocumentViewerCell';
16
15
  const styles = {
@@ -36,7 +35,7 @@ const styles = {
36
35
  };
37
36
  const RepeatableField = (props) => {
38
37
  const { fieldDefinition, canUpdateProperty, criteria, viewLayout, entry } = props;
39
- const { instance, width } = useFormContext();
38
+ const { fetchedOptions, setFetchedOptions, instance, width } = useFormContext();
40
39
  const { isBelow } = useWidgetSize({
41
40
  scroll: false,
42
41
  defaultWidth: width,
@@ -44,71 +43,106 @@ const RepeatableField = (props) => {
44
43
  const smallerThanMd = isBelow('md');
45
44
  const { instanceChanges } = useNotification();
46
45
  const apiServices = useApiServices();
46
+ const [reloadOnErrorTrigger, setReloadOnErrorTrigger] = useState(true);
47
47
  const [criteriaObjects, setCriteriaObjects] = useState([]);
48
48
  const [selectedInstanceId, setSelectedInstanceId] = useState();
49
49
  const [dialogType, setDialogType] = useState();
50
50
  const [openDialog, setOpenDialog] = useState(false);
51
+ const [users, setUsers] = useState(fetchedOptions[`${fieldDefinition.id}Users`] || []);
52
+ const [error, setError] = useState(false);
53
+ const [relatedInstances, setRelatedInstances] = useState(fetchedOptions[`${fieldDefinition.id}Options`] || []);
54
+ const [relatedObject, setRelatedObject] = useState(fetchedOptions[`${fieldDefinition.id}RelatedObject`]);
55
+ const [hasCreateAction, setHasCreateAction] = useState(fetchedOptions[`${fieldDefinition.id}HasCreateAction`] || false);
56
+ const [loading, setLoading] = useState((relatedObject && relatedInstances) || !fieldDefinition ? false : true);
57
+ const [tableViewLayout, setTableViewLayout] = useState(fetchedOptions[`${fieldDefinition.id}TableViewLayout`]);
58
+ const [createForm, setCreateForm] = useState(fetchedOptions[`${fieldDefinition.id}-createForm`]);
59
+ const [updateForm, setUpdateForm] = useState(fetchedOptions[`${fieldDefinition.id}-updateForm`]);
60
+ const [deleteForm, setDeleteForm] = useState(fetchedOptions[`${fieldDefinition.id}-deleteForm`]);
51
61
  const [snackbarError, setSnackbarError] = useState({
52
62
  showAlert: false,
53
63
  isError: false,
54
64
  });
55
- const queryClient = useQueryClient();
56
- const { data: relatedObject, isLoading: loadingRelatedObject } = useQuery({
57
- queryKey: [fieldDefinition?.objectId, 'effective'],
58
- queryFn: () => apiServices.get(getPrefixedUrl(`/objects/${fieldDefinition.objectId}/effective`)),
59
- staleTime: Infinity,
60
- enabled: !!fieldDefinition.objectId,
61
- });
62
- const { data: tableViewLayout, isFetching: isLayoutFetching } = useQuery({
63
- queryKey: ['tableViewLayout', viewLayout?.id],
64
- enabled: !!viewLayout?.id && !!relatedObject,
65
- staleTime: Infinity,
66
- placeholderData: () => {
67
- return relatedObject?.viewLayout?.table
68
- ? {
69
- id: 'default',
70
- name: 'Default',
71
- objectId: relatedObject.id,
72
- ...relatedObject.viewLayout.table,
73
- }
74
- : undefined;
75
- },
76
- queryFn: () => apiServices.get(getPrefixedUrl(`/objects/${viewLayout.objectId}/tableLayouts/${viewLayout.id}`)),
77
- });
78
- const transformedCriteria = useMemo(() => (criteria ? transformToWhere(criteria) : {}), [criteria]);
79
- const relatedInstancesQueryKey = [
80
- instance?.id,
81
- 'object',
82
- fieldDefinition?.objectId,
83
- 'instances',
84
- fieldDefinition?.relatedPropertyId,
85
- transformedCriteria,
86
- ];
87
- const { data: relatedInstances = [], refetch: refetchRelatedInstances, isLoading: loadingRelatedInstances, isError: relatedInstancesError, } = useQuery({
88
- queryKey: relatedInstancesQueryKey,
89
- queryFn: async () => {
90
- const filterProperty = `${fieldDefinition.relatedPropertyId}.id`;
91
- const filter = {
92
- where: { [filterProperty]: instance?.id, ...transformedCriteria },
93
- limit: 100,
94
- };
95
- const instances = await apiServices.get(getPrefixedUrl(`/objects/${fieldDefinition.objectId}/instances`), {
96
- params: { filter: JSON.stringify(filter) },
97
- });
98
- return instances;
99
- },
100
- enabled: !!fieldDefinition.relatedPropertyId && !!fieldDefinition.objectId && !!instance?.id,
101
- staleTime: Infinity,
102
- });
103
65
  const createAction = relatedObject?.actions?.find((item) => item.id === entry.display?.createActionId);
104
66
  const updateAction = relatedObject?.actions?.find((item) => item.id === entry.display?.updateActionId);
105
67
  const deleteAction = relatedObject?.actions?.find((item) => item.id === entry.display?.deleteActionId);
106
- const createFormId = entry.display?.createFormId || createAction?.defaultFormId;
107
- const updateFormId = entry.display?.updateFormId || updateAction?.defaultFormId;
108
- const deleteFormId = entry.display?.deleteFormId || deleteAction?.defaultFormId;
109
- const { data: createForm } = useFormById(createFormId ?? '', apiServices);
110
- const { data: updateForm } = useFormById(updateFormId ?? '', apiServices);
111
- const { data: deleteForm } = useFormById(deleteFormId ?? '', apiServices);
68
+ function getForm(setForm, action, formId) {
69
+ if (formId === '_auto_')
70
+ return;
71
+ if (formId || action?.defaultFormId) {
72
+ apiServices
73
+ .get(getPrefixedUrl(`/forms/${formId || action?.defaultFormId}`))
74
+ .then((evokeForm) => {
75
+ setForm(evokeForm);
76
+ setFetchedOptions({
77
+ [`${fieldDefinition.id}${action?.type === 'delete' ? '-deleteForm' : action?.type === 'create' ? '-createForm' : '-updateForm'}`]: evokeForm,
78
+ });
79
+ })
80
+ .catch((error) => {
81
+ console.error(error);
82
+ });
83
+ }
84
+ }
85
+ const fetchRelatedInstances = useCallback(async (refetch = false) => {
86
+ let relatedObject;
87
+ if (fieldDefinition.objectId) {
88
+ if (!fetchedOptions[`${fieldDefinition.id}RelatedObject`]) {
89
+ try {
90
+ relatedObject = await apiServices.get(getPrefixedUrl(`/objects/${fieldDefinition.objectId}/effective`));
91
+ let defaultTableViewLayout;
92
+ if (relatedObject.viewLayout?.table) {
93
+ defaultTableViewLayout = {
94
+ id: 'default',
95
+ name: 'Default',
96
+ objectId: relatedObject.id,
97
+ ...relatedObject?.viewLayout.table,
98
+ };
99
+ }
100
+ if (viewLayout) {
101
+ apiServices
102
+ .get(getPrefixedUrl(`/objects/${viewLayout.objectId}/tableLayouts/${viewLayout.id}`))
103
+ .then(setTableViewLayout)
104
+ .catch((err) => setTableViewLayout(defaultTableViewLayout));
105
+ }
106
+ else {
107
+ setTableViewLayout(defaultTableViewLayout);
108
+ }
109
+ setRelatedObject(relatedObject);
110
+ }
111
+ catch (err) {
112
+ console.error(err);
113
+ }
114
+ }
115
+ if (fieldDefinition.relatedPropertyId &&
116
+ fieldDefinition.objectId &&
117
+ instance?.id &&
118
+ (!fetchedOptions[`${fieldDefinition.id}Options`] || refetch)) {
119
+ const filterProperty = `${fieldDefinition.relatedPropertyId}.id`;
120
+ const transformedCriteria = criteria ? transformToWhere(criteria) : {};
121
+ const filter = {
122
+ where: { [filterProperty]: instance?.id, ...transformedCriteria },
123
+ limit: 100,
124
+ };
125
+ try {
126
+ const timeout = setTimeout(() => {
127
+ setLoading(false);
128
+ }, 300);
129
+ setLoading(true);
130
+ const instances = await apiServices.get(getPrefixedUrl(`/objects/${fieldDefinition.objectId}/instances`), {
131
+ params: { filter: JSON.stringify(filter) },
132
+ });
133
+ clearTimeout(timeout);
134
+ if (instances) {
135
+ setRelatedInstances(instances);
136
+ }
137
+ }
138
+ catch (error) {
139
+ setError(true);
140
+ }
141
+ }
142
+ setLoading(false);
143
+ }
144
+ relatedObject && checkCreateAccess(relatedObject);
145
+ }, [fieldDefinition]);
112
146
  const fetchCriteriaObjects = useCallback(async () => {
113
147
  let objectIds = [];
114
148
  const criteriaProperties = relatedObject?.properties?.filter((property) => property.type === 'criteria' && property.objectId) ?? [];
@@ -134,21 +168,41 @@ const RepeatableField = (props) => {
134
168
  }
135
169
  setCriteriaObjects(objects);
136
170
  }, [apiServices, relatedObject, tableViewLayout]);
137
- const { data: users } = useQuery({
138
- queryKey: ['users'],
139
- queryFn: () => apiServices.get(getPrefixedUrl(`/users`)),
140
- staleTime: Infinity,
141
- meta: {
142
- errorMessage: 'Error fetching users: ',
143
- },
144
- });
171
+ useEffect(() => {
172
+ if (!fetchedOptions[`${fieldDefinition.id}Users`]) {
173
+ (async () => {
174
+ try {
175
+ const users = await apiServices.get(getPrefixedUrl(`/users`));
176
+ setFetchedOptions({
177
+ [`${fieldDefinition.id}Users`]: users,
178
+ });
179
+ setUsers(users);
180
+ }
181
+ catch (error) {
182
+ console.error(error);
183
+ }
184
+ })();
185
+ }
186
+ }, [apiServices]);
187
+ useEffect(() => {
188
+ fetchRelatedInstances();
189
+ }, [fetchRelatedInstances, reloadOnErrorTrigger, instance]);
145
190
  useEffect(() => {
146
191
  if (relatedObject)
147
192
  fetchCriteriaObjects();
148
193
  }, [fetchCriteriaObjects, relatedObject]);
194
+ useEffect(() => {
195
+ if (createAction && !createForm)
196
+ getForm(setCreateForm, createAction, entry.display?.createFormId);
197
+ if (updateAction && !updateForm)
198
+ getForm(setUpdateForm, updateAction, entry.display?.updateFormId);
199
+ if (deleteAction && !deleteForm)
200
+ getForm(setDeleteForm, deleteAction, entry.display?.deleteFormId);
201
+ }, [entry.display, createAction, updateAction, deleteAction]);
149
202
  useEffect(() => {
150
203
  if (relatedObject?.rootObjectId) {
151
- const callback = () => refetchRelatedInstances();
204
+ // pass true here so while it doesn't refetch on every tab change it does refetch on changes made
205
+ const callback = () => fetchRelatedInstances(true);
152
206
  instanceChanges?.subscribe(relatedObject?.rootObjectId, callback);
153
207
  return () => instanceChanges?.unsubscribe(relatedObject?.rootObjectId, callback);
154
208
  }
@@ -195,30 +249,59 @@ const RepeatableField = (props) => {
195
249
  };
196
250
  }
197
251
  };
198
- const { data: hasCreatePermission } = useQuery({
199
- queryKey: [fieldDefinition.objectId, entry.display?.createActionId, 'hasCreatePermission '],
200
- queryFn: async () => {
201
- const checkAccess = await apiServices.get(getPrefixedUrl(`/objects/${fieldDefinition.objectId}/instances/checkAccess`), {
252
+ const checkCreateAccess = useCallback((relatedObject) => {
253
+ if (fieldDefinition.objectId &&
254
+ canUpdateProperty &&
255
+ !fetchedOptions[`${fieldDefinition.id}HasCreateAction`]) {
256
+ apiServices
257
+ .get(getPrefixedUrl(`/objects/${fieldDefinition.objectId}/instances/checkAccess`), {
202
258
  params: { action: 'execute', field: entry.display?.createActionId, scope: 'data' },
203
- });
204
- const action = relatedObject.actions?.find((item) => item.id === entry.display?.createActionId);
205
- if (action && fieldDefinition.relatedPropertyId) {
206
- const { relatedObjectProperty, criteria } = retrieveCriteria(fieldDefinition.relatedPropertyId, action, relatedObject);
207
- if (!criteria || JSON.stringify(criteria).includes('{{{input.') || !relatedObjectProperty) {
208
- return checkAccess.result;
259
+ })
260
+ .then((checkAccess) => {
261
+ const action = relatedObject.actions?.find((item) => item.id === entry.display?.createActionId);
262
+ if (action && fieldDefinition.relatedPropertyId) {
263
+ const { relatedObjectProperty, criteria } = retrieveCriteria(fieldDefinition.relatedPropertyId, action, relatedObject);
264
+ if (!criteria || JSON.stringify(criteria).includes('{{{input.') || !relatedObjectProperty) {
265
+ setHasCreateAction(checkAccess.result);
266
+ }
267
+ else {
268
+ const validate = sift(criteria);
269
+ setHasCreateAction(validate(instance) && checkAccess.result);
270
+ }
209
271
  }
210
272
  else {
211
- const validate = sift(criteria);
212
- return validate(instance) && checkAccess.result;
273
+ setHasCreateAction(false);
213
274
  }
214
- }
215
- else {
216
- return false;
217
- }
218
- },
219
- staleTime: Infinity,
220
- enabled: !!fieldDefinition.objectId && canUpdateProperty && !!relatedObject,
221
- });
275
+ });
276
+ }
277
+ }, [fieldDefinition, canUpdateProperty, fetchedOptions, entry.display?.createActionId, instance, apiServices]);
278
+ useEffect(() => {
279
+ // Re-check create access when instance changes to re-evaluate the criteria
280
+ relatedObject && checkCreateAccess(relatedObject);
281
+ }, [relatedObject, checkCreateAccess]);
282
+ useEffect(() => {
283
+ const updatedOptions = {};
284
+ if ((relatedInstances && !fetchedOptions[`${fieldDefinition.id}Options`]) ||
285
+ fetchedOptions[`${fieldDefinition.id}Options`].length === 0 ||
286
+ !isEqual(relatedInstances, fetchedOptions[`${fieldDefinition.id}Options`])) {
287
+ updatedOptions[`${fieldDefinition.id}Options`] = relatedInstances;
288
+ }
289
+ if (relatedObject && !fetchedOptions[`${fieldDefinition.id}RelatedObject`]) {
290
+ updatedOptions[`${fieldDefinition.id}RelatedObject`] = relatedObject;
291
+ }
292
+ if (tableViewLayout && !fetchedOptions[`${fieldDefinition.id}TableViewLayout`]) {
293
+ updatedOptions[`${fieldDefinition.id}TableViewLayout`] = tableViewLayout;
294
+ }
295
+ if ((hasCreateAction || hasCreateAction === false) && !fetchedOptions[`${fieldDefinition.id}HasCreateAction`]) {
296
+ updatedOptions[`${fieldDefinition.id}HasCreateAction`] = hasCreateAction;
297
+ }
298
+ else if (!hasCreateAction && relatedObject) {
299
+ checkCreateAccess(relatedObject);
300
+ }
301
+ if (Object.keys(updatedOptions).length > 0) {
302
+ setFetchedOptions(updatedOptions);
303
+ }
304
+ }, [relatedObject, relatedInstances, hasCreateAction, tableViewLayout]);
222
305
  const deleteRow = (id) => {
223
306
  setDialogType('delete');
224
307
  setSelectedInstanceId(id);
@@ -234,7 +317,7 @@ const RepeatableField = (props) => {
234
317
  setSelectedInstanceId(id);
235
318
  setOpenDialog(true);
236
319
  };
237
- const ErrorComponent = () => loadingRelatedObject || loadingRelatedInstances ? (React.createElement("div", null,
320
+ const ErrorComponent = () => loading ? (React.createElement("div", null,
238
321
  React.createElement(Typography, { sx: {
239
322
  fontSize: '14px',
240
323
  color: '#727c84',
@@ -250,7 +333,7 @@ const RepeatableField = (props) => {
250
333
  backgroundColor: 'transparent',
251
334
  },
252
335
  'min-width': '44px',
253
- }, variant: "text", onClick: () => refetchRelatedInstances() }, "Retry")));
336
+ }, variant: "text", onClick: () => setReloadOnErrorTrigger((prevState) => !prevState) }, "Retry")));
254
337
  const save = async (input) => {
255
338
  const action = relatedObject?.actions?.find((a) => a.id ===
256
339
  (dialogType === 'create'
@@ -261,7 +344,7 @@ const RepeatableField = (props) => {
261
344
  // when save is called we know that fieldDefinition is a parameter and fieldDefinition.objectId is defined
262
345
  input = await formatSubmission(input, apiServices, fieldDefinition.objectId, selectedInstanceId, action?.type === 'update' ? updateForm : undefined, undefined, instance?.id && fieldDefinition.relatedPropertyId
263
346
  ? { instanceId: instance.id, propertyId: fieldDefinition.relatedPropertyId }
264
- : undefined, action?.parameters ?? (relatedObject && convertPropertiesToParams(relatedObject)));
347
+ : undefined);
265
348
  if (action?.type === 'create' && entry.display?.createActionId) {
266
349
  const updatedInput = {
267
350
  ...input,
@@ -272,11 +355,8 @@ const RepeatableField = (props) => {
272
355
  actionId: entry.display?.createActionId,
273
356
  input: updatedInput,
274
357
  });
275
- queryClient.setQueryData(relatedInstancesQueryKey, (oldData) => {
276
- if (!oldData)
277
- return [instance];
278
- return [...oldData, instance];
279
- });
358
+ const hasAccess = fieldDefinition?.relatedPropertyId && fieldDefinition.relatedPropertyId in instance;
359
+ hasAccess && setRelatedInstances([...relatedInstances, instance]);
280
360
  setOpenDialog(false);
281
361
  setDialogType(undefined);
282
362
  setSelectedInstanceId(undefined);
@@ -302,12 +382,12 @@ const RepeatableField = (props) => {
302
382
  if (response && relatedObject && instance) {
303
383
  deleteDocuments(input, !!response, apiServices, relatedObject, instance, action);
304
384
  }
305
- queryClient.setQueryData(relatedInstancesQueryKey, (oldData = []) => {
306
- if (action?.type === 'delete') {
307
- return oldData.filter((i) => i.id !== selectedInstanceId);
308
- }
309
- return oldData.map((i) => (i.id === instance?.id ? instance : i));
310
- });
385
+ if (action?.type === 'delete') {
386
+ setRelatedInstances((prevInstances) => prevInstances.filter((instance) => instance.id !== selectedInstanceId));
387
+ }
388
+ else {
389
+ setRelatedInstances((prevInstances) => prevInstances.map((i) => (i.id === instance?.id ? instance : i)));
390
+ }
311
391
  setOpenDialog(false);
312
392
  setDialogType(undefined);
313
393
  setSelectedInstanceId(undefined);
@@ -381,12 +461,12 @@ const RepeatableField = (props) => {
381
461
  }
382
462
  return value;
383
463
  };
384
- return loadingRelatedObject || loadingRelatedInstances || isLayoutFetching ? (React.createElement(React.Fragment, null,
464
+ return loading ? (React.createElement(React.Fragment, null,
385
465
  React.createElement(Skeleton, null),
386
466
  React.createElement(Skeleton, null),
387
467
  React.createElement(Skeleton, null))) : (React.createElement(React.Fragment, null,
388
468
  React.createElement(Box, { sx: { padding: '10px 0' } },
389
- !relatedInstances?.length ? (!relatedInstancesError ? (React.createElement(Typography, { sx: { margin: '-10px 0', color: 'rgb(114 124 132)', fontSize: '14px' } }, "No items added")) : (React.createElement(ErrorComponent, null))) : smallerThanMd ? (React.createElement(React.Fragment, null, relatedInstances?.map((relatedInstance, index) => (React.createElement(Accordion, { key: relatedInstance.id, sx: {
469
+ !relatedInstances?.length ? (!error ? (React.createElement(Typography, { sx: { margin: '-10px 0', color: 'rgb(114 124 132)', fontSize: '14px' } }, "No items added")) : (React.createElement(ErrorComponent, null))) : smallerThanMd ? (React.createElement(React.Fragment, null, relatedInstances?.map((relatedInstance, index) => (React.createElement(Accordion, { key: relatedInstance.id, sx: {
390
470
  border: '1px solid #dbe0e4',
391
471
  borderTop: index === 0 ? undefined : 'none',
392
472
  boxShadow: 'none',
@@ -449,7 +529,7 @@ const RepeatableField = (props) => {
449
529
  React.createElement(Table, { stickyHeader: true, sx: { minWidth: 650 } },
450
530
  React.createElement(TableHead, { sx: { backgroundColor: '#F4F6F8' } },
451
531
  React.createElement(TableRow, null,
452
- columns?.map((prop) => React.createElement(TableCell, { sx: styles.tableCell }, prop.name)),
532
+ columns?.map((prop) => (React.createElement(TableCell, { sx: styles.tableCell }, prop.name))),
453
533
  canUpdateProperty && React.createElement(TableCell, { sx: { ...styles.tableCell, width: '80px' } }))),
454
534
  React.createElement(TableBody, null, relatedInstances?.map((relatedInstance, index) => (React.createElement(TableRow, { key: relatedInstance.id },
455
535
  columns?.map((prop) => {
@@ -476,7 +556,7 @@ const RepeatableField = (props) => {
476
556
  entry.display?.deleteActionId && (React.createElement(IconButton, { "aria-label": `delete-collection-instance-${index}`, onClick: () => deleteRow(relatedInstance.id) },
477
557
  React.createElement(Tooltip, { title: "Delete" },
478
558
  React.createElement(TrashCan, { sx: { ':hover': { color: '#A12723' } } }))))))))))))),
479
- hasCreatePermission && entry.display?.createActionId && (React.createElement(Button, { variant: "contained", sx: styles.addButton, disabled: !createAction, onClick: addRow, "aria-label": 'Add' }, "Add"))),
559
+ hasCreateAction && entry.display?.createActionId && (React.createElement(Button, { variant: "contained", sx: styles.addButton, disabled: !createAction, onClick: addRow, "aria-label": 'Add' }, "Add"))),
480
560
  relatedObject && openDialog && (React.createElement(ActionDialog, { object: relatedObject, open: openDialog, onClose: () => setOpenDialog(false), onSubmit: save, action: relatedObject?.actions?.find((a) => a.id ===
481
561
  (dialogType === 'create'
482
562
  ? entry.display?.createActionId
@@ -1,46 +1,62 @@
1
1
  import { useApiServices } from '@evoke-platform/context';
2
- import { useQuery } from '@tanstack/react-query';
3
- import React from 'react';
2
+ import React, { useCallback, useEffect, useState } from 'react';
4
3
  import { useFormContext } from '../../../../../theme/hooks';
5
- import { Button, CircularProgress, Skeleton, Typography } from '../../../../core';
4
+ import { Button, CircularProgress, Typography } from '../../../../core';
6
5
  import { Box } from '../../../../layout';
7
6
  import CriteriaBuilder from '../../../CriteriaBuilder';
8
7
  import { addressProperties, getPrefixedUrl } from '../utils';
9
8
  export default function Criteria(props) {
10
9
  const { value, canUpdateProperty, fieldDefinition, error } = props;
11
10
  const apiServices = useApiServices();
12
- const { handleChange, onAutosave } = useFormContext();
13
- const { data: properties = [], error: loadingError, refetch: fetchProperties, isFetching: loading, } = useQuery({
14
- queryKey: [fieldDefinition?.objectId, 'effective properties'],
15
- queryFn: async () => {
16
- const properties = await apiServices.get(getPrefixedUrl(`/objects/${fieldDefinition.objectId}/effective/properties`));
17
- return properties.flatMap((prop) => {
18
- if (prop.type === 'object' || prop.type === 'user') {
19
- return [
20
- {
21
- id: `${prop.id}.id`,
22
- name: `${prop.name} Id`,
23
- type: 'string',
24
- },
25
- {
26
- id: `${prop.id}.name`,
27
- name: `${prop.name} Name`,
28
- type: 'string',
29
- },
30
- ];
11
+ const { fetchedOptions, setFetchedOptions, handleChange, onAutosave } = useFormContext();
12
+ const [loadingError, setLoadingError] = useState(false);
13
+ const [loading, setLoading] = useState(false);
14
+ const [properties, setProperties] = useState(fetchedOptions[`${fieldDefinition.id}Options`] || []);
15
+ const fetchProperties = useCallback(async () => {
16
+ if (fieldDefinition.objectId && !fetchedOptions[`${fieldDefinition.id}Options`]) {
17
+ setLoading(true);
18
+ apiServices.get(getPrefixedUrl(`/objects/${fieldDefinition.objectId}/effective/properties`), { params: { fields: ['properties'] } }, (error, properties) => {
19
+ if (error) {
20
+ console.error('Error fetching object properties', error);
21
+ setLoadingError(true);
31
22
  }
32
- if (prop.type === 'address') {
33
- return addressProperties(prop);
23
+ if (properties) {
24
+ const flattenProperties = properties.flatMap((prop) => {
25
+ if (prop.type === 'object' || prop.type === 'user') {
26
+ return [
27
+ {
28
+ id: `${prop.id}.id`,
29
+ name: `${prop.name} Id`,
30
+ type: 'string',
31
+ },
32
+ {
33
+ id: `${prop.id}.name`,
34
+ name: `${prop.name} Name`,
35
+ type: 'string',
36
+ },
37
+ ];
38
+ }
39
+ else if (prop.type === 'address') {
40
+ return addressProperties(prop);
41
+ }
42
+ return prop;
43
+ });
44
+ setProperties(flattenProperties);
45
+ setFetchedOptions({
46
+ [`${fieldDefinition.id}Options`]: flattenProperties.map((prop) => ({
47
+ id: prop.id,
48
+ name: prop.name,
49
+ })),
50
+ });
51
+ setLoadingError(false);
34
52
  }
35
- return prop;
53
+ setLoading(false);
36
54
  });
37
- },
38
- staleTime: Infinity,
39
- enabled: !!fieldDefinition?.objectId,
40
- meta: {
41
- errorMessage: 'Error fetching object properties: ',
42
- },
43
- });
55
+ }
56
+ }, [fieldDefinition.objectId, apiServices]);
57
+ useEffect(() => {
58
+ fetchProperties();
59
+ }, [fetchProperties]);
44
60
  const handleUpdate = async (criteria) => {
45
61
  if (criteria || value) {
46
62
  const newValue = criteria ?? null;
@@ -66,12 +82,9 @@ export default function Criteria(props) {
66
82
  padding: 0,
67
83
  '&:hover': { backgroundColor: 'transparent' },
68
84
  minWidth: '44px',
69
- }, variant: "text", onClick: () => fetchProperties(), disabled: loading }, "Retry"),
85
+ }, variant: "text", onClick: fetchProperties, disabled: loading }, "Retry"),
70
86
  loading && React.createElement(CircularProgress, { size: 20, sx: { paddingLeft: '10px' } })));
71
87
  }
72
- if (loading) {
73
- return React.createElement(Skeleton, null);
74
- }
75
88
  return !!value || canUpdateProperty ? (React.createElement(Box, { sx: { borderRadius: '8px', border: error ? '1px solid #FF0000' : '1px solid #ddd' } },
76
89
  React.createElement(CriteriaBuilder, { criteria: value ?? undefined, properties: properties, setCriteria: handleUpdate, disabled: !canUpdateProperty, hideBorder: true, presetValues: [
77
90
  {
@@ -1,13 +1,12 @@
1
1
  import { DocumentParameterValidation } from '@evoke-platform/context';
2
2
  import React from 'react';
3
- import { DocumentReference } from '../../types';
3
+ import { SavedDocumentReference } from '../../types';
4
4
  type DocumentProps = {
5
5
  id: string;
6
- fieldType?: 'file' | 'document';
7
6
  canUpdateProperty: boolean;
8
7
  error: boolean;
9
8
  validate?: DocumentParameterValidation;
10
- value: (File | DocumentReference)[] | undefined;
9
+ value: (File | SavedDocumentReference)[] | undefined;
11
10
  hasDescription?: boolean;
12
11
  };
13
12
  export declare const Document: (props: DocumentProps) => React.JSX.Element;