@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.
- package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.d.ts +4 -4
- package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.js +72 -145
- package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.test.js +67 -189
- package/dist/published/components/custom/CriteriaBuilder/PropertyTree.d.ts +6 -6
- package/dist/published/components/custom/CriteriaBuilder/PropertyTree.js +25 -12
- package/dist/published/components/custom/CriteriaBuilder/PropertyTreeItem.d.ts +5 -4
- package/dist/published/components/custom/CriteriaBuilder/PropertyTreeItem.js +22 -34
- package/dist/published/components/custom/CriteriaBuilder/types.d.ts +11 -2
- package/dist/published/components/custom/CriteriaBuilder/utils.d.ts +34 -6
- package/dist/published/components/custom/CriteriaBuilder/utils.js +89 -18
- package/dist/published/components/custom/Form/FormComponents/DocumentComponent/Document.js +1 -1
- package/dist/published/components/custom/Form/FormComponents/DocumentComponent/DocumentList.js +3 -6
- package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/RepeatableField.js +1 -1
- package/dist/published/components/custom/Form/utils.d.ts +0 -1
- package/dist/published/components/custom/FormField/DateTimePickerSelect/DateTimePickerSelect.js +1 -2
- package/dist/published/components/custom/FormV2/FormRenderer.d.ts +2 -2
- package/dist/published/components/custom/FormV2/FormRenderer.js +29 -26
- package/dist/published/components/custom/FormV2/FormRendererContainer.d.ts +3 -1
- package/dist/published/components/custom/FormV2/FormRendererContainer.js +88 -95
- package/dist/published/components/custom/FormV2/components/Body.js +1 -1
- package/dist/published/components/custom/FormV2/components/Footer.js +1 -1
- package/dist/published/components/custom/FormV2/components/FormContext.d.ts +0 -1
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/ActionDialog.d.ts +0 -1
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableField.js +143 -86
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableFieldInput.d.ts +2 -0
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableFieldInput.js +4 -1
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/RepeatableField.js +186 -106
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/Criteria.js +49 -36
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.d.ts +2 -3
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js +32 -51
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.d.ts +3 -4
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.js +38 -40
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/UserProperty.js +21 -17
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/InstanceLookup.js +1 -1
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/ObjectPropertyInput.js +169 -95
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.d.ts +2 -0
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.js +6 -12
- package/dist/published/components/custom/FormV2/components/FormSections.js +0 -1
- package/dist/published/components/custom/FormV2/components/Header.d.ts +1 -0
- package/dist/published/components/custom/FormV2/components/Header.js +19 -8
- package/dist/published/components/custom/FormV2/components/HtmlView.d.ts +9 -0
- package/dist/published/components/custom/FormV2/components/HtmlView.js +46 -0
- package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.d.ts +1 -2
- package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.js +20 -46
- package/dist/published/components/custom/FormV2/components/types.d.ts +1 -6
- package/dist/published/components/custom/FormV2/components/utils.d.ts +11 -11
- package/dist/published/components/custom/FormV2/components/utils.js +104 -181
- package/dist/published/components/custom/FormV2/tests/FormRenderer.test.js +17 -50
- package/dist/published/components/custom/FormV2/tests/FormRendererContainer.test.js +131 -40
- package/dist/published/components/custom/HistoryLog/HistoryData.js +1 -2
- package/dist/published/components/custom/HistoryLog/index.js +1 -2
- package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.d.ts +1 -2
- package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.js +22 -61
- package/dist/published/components/custom/ViewDetailsV2/ViewDetailsV2Container.d.ts +3 -0
- package/dist/published/components/custom/ViewDetailsV2/ViewDetailsV2Container.js +5 -8
- package/dist/published/stories/Backdrop.stories.d.ts +2 -2
- package/dist/published/stories/CriteriaBuilder.stories.js +22 -70
- package/dist/published/stories/FormLabel.stories.d.ts +2 -2
- package/dist/published/stories/FormRenderer.stories.d.ts +3 -3
- package/dist/published/stories/FormRendererContainer.stories.d.ts +15 -5
- package/dist/published/stories/ViewDetailsV2Container.stories.d.ts +9 -0
- package/dist/published/theme/hooks.d.ts +1 -2
- package/package.json +11 -17
- package/dist/published/components/custom/FormV2/components/ConditionalQueryClientProvider.d.ts +0 -5
- 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 {
|
|
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,
|
|
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 {
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
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
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
|
|
212
|
-
return validate(instance) && checkAccess.result;
|
|
273
|
+
setHasCreateAction(false);
|
|
213
274
|
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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 = () =>
|
|
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: () =>
|
|
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
|
|
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
|
-
|
|
276
|
-
|
|
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
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
|
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 ? (!
|
|
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
|
-
|
|
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 {
|
|
3
|
-
import React from 'react';
|
|
2
|
+
import React, { useCallback, useEffect, useState } from 'react';
|
|
4
3
|
import { useFormContext } from '../../../../../theme/hooks';
|
|
5
|
-
import { Button, CircularProgress,
|
|
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
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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 (
|
|
33
|
-
|
|
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
|
-
|
|
53
|
+
setLoading(false);
|
|
36
54
|
});
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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:
|
|
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 {
|
|
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 |
|
|
9
|
+
value: (File | SavedDocumentReference)[] | undefined;
|
|
11
10
|
hasDescription?: boolean;
|
|
12
11
|
};
|
|
13
12
|
export declare const Document: (props: DocumentProps) => React.JSX.Element;
|