@evoke-platform/ui-components 1.10.0-testing.2 → 1.10.0-testing.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.js +1 -1
- package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.test.d.ts +1 -0
- package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.test.js +430 -0
- package/dist/published/components/custom/CriteriaBuilder/ValueEditor.js +19 -6
- package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/RepeatableField.js +1 -1
- package/dist/published/components/custom/Form/utils.js +1 -0
- package/dist/published/components/custom/FormField/DatePickerSelect/DatePickerSelect.js +14 -1
- package/dist/published/components/custom/FormField/DateTimePickerSelect/DateTimePickerSelect.js +14 -1
- package/dist/published/components/custom/FormField/TimePickerSelect/TimePickerSelect.js +14 -1
- package/dist/published/components/custom/FormV2/FormRenderer.d.ts +2 -1
- package/dist/published/components/custom/FormV2/FormRenderer.js +17 -4
- package/dist/published/components/custom/FormV2/FormRendererContainer.js +112 -73
- package/dist/published/components/custom/FormV2/components/AccordionSections.js +7 -2
- package/dist/published/components/custom/FormV2/components/Body.d.ts +1 -1
- package/dist/published/components/custom/FormV2/components/FieldWrapper.js +1 -1
- package/dist/published/components/custom/FormV2/components/Footer.d.ts +1 -0
- package/dist/published/components/custom/FormV2/components/Footer.js +3 -3
- package/dist/published/components/custom/FormV2/components/FormContext.d.ts +3 -2
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/AddressFields.d.ts +9 -0
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/AddressFields.js +32 -15
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/ActionDialog.js +1 -1
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/RepeatableField.d.ts +0 -3
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/RepeatableField.js +31 -48
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/Criteria.js +16 -3
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js +16 -4
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.d.ts +2 -1
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.js +16 -3
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/Image.js +31 -5
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/UserProperty.js +15 -3
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/ObjectPropertyInput.js +109 -81
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.js +38 -16
- package/dist/published/components/custom/FormV2/components/Header.d.ts +13 -3
- package/dist/published/components/custom/FormV2/components/Header.js +47 -8
- package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.js +44 -35
- package/dist/published/components/custom/FormV2/components/ValidationFiles/ValidationErrors.js +1 -1
- package/dist/published/components/custom/FormV2/components/types.d.ts +1 -0
- package/dist/published/components/custom/FormV2/components/utils.d.ts +2 -2
- package/dist/published/components/custom/FormV2/components/utils.js +11 -14
- package/dist/published/components/custom/FormV2/tests/FormRenderer.test.js +432 -4
- package/dist/published/components/custom/FormV2/tests/FormRendererContainer.test.js +642 -13
- package/dist/published/components/custom/FormV2/tests/test-data.d.ts +1 -0
- package/dist/published/components/custom/FormV2/tests/test-data.js +140 -0
- package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.d.ts +3 -0
- package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.js +155 -0
- package/dist/published/components/custom/ViewDetailsV2/ViewDetailsV2Container.d.ts +13 -0
- package/dist/published/components/custom/ViewDetailsV2/ViewDetailsV2Container.js +140 -0
- package/dist/published/components/custom/ViewDetailsV2/index.d.ts +3 -0
- package/dist/published/components/custom/ViewDetailsV2/index.js +2 -0
- package/dist/published/components/custom/index.d.ts +2 -0
- package/dist/published/components/custom/index.js +1 -0
- package/dist/published/index.d.ts +6 -6
- package/dist/published/index.js +1 -1
- package/dist/published/stories/FormRenderer.stories.d.ts +8 -4
- package/dist/published/stories/FormRendererContainer.stories.d.ts +26 -0
- package/dist/published/stories/FormRendererContainer.stories.js +5 -0
- package/dist/published/stories/FormRendererData.d.ts +12 -0
- package/dist/published/stories/FormRendererData.js +27 -44
- package/dist/published/stories/ViewDetailsV2Container.stories.d.ts +26 -0
- package/dist/published/stories/ViewDetailsV2Container.stories.js +37 -0
- package/dist/published/stories/ViewDetailsV2Data.d.ts +4 -0
- package/dist/published/stories/ViewDetailsV2Data.js +203 -0
- package/dist/published/stories/sharedMswHandlers.js +49 -10
- package/dist/published/theme/hooks.d.ts +4 -3
- package/package.json +4 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useApiServices, useNotification, } from '@evoke-platform/context';
|
|
2
|
-
import { get, isEqual,
|
|
2
|
+
import { get, isEqual, omit, startCase } from 'lodash';
|
|
3
3
|
import { DateTime } from 'luxon';
|
|
4
4
|
import React, { useCallback, useEffect, useState } from 'react';
|
|
5
5
|
import sift from 'sift';
|
|
@@ -34,7 +34,7 @@ const styles = {
|
|
|
34
34
|
},
|
|
35
35
|
};
|
|
36
36
|
const RepeatableField = (props) => {
|
|
37
|
-
const { fieldDefinition, canUpdateProperty, criteria, viewLayout, entry
|
|
37
|
+
const { fieldDefinition, canUpdateProperty, criteria, viewLayout, entry } = props;
|
|
38
38
|
const { fetchedOptions, setFetchedOptions, instance, width } = useFormContext();
|
|
39
39
|
const { isBelow } = useWidgetSize({
|
|
40
40
|
scroll: false,
|
|
@@ -62,9 +62,9 @@ const RepeatableField = (props) => {
|
|
|
62
62
|
showAlert: false,
|
|
63
63
|
isError: false,
|
|
64
64
|
});
|
|
65
|
-
const createAction = relatedObject?.actions?.find((item) => item.id === createActionId);
|
|
66
|
-
const updateAction = relatedObject?.actions?.find((item) => item.id === updateActionId);
|
|
67
|
-
const deleteAction = relatedObject?.actions?.find((item) => item.id === deleteActionId);
|
|
65
|
+
const createAction = relatedObject?.actions?.find((item) => item.id === entry.display?.createActionId);
|
|
66
|
+
const updateAction = relatedObject?.actions?.find((item) => item.id === entry.display?.updateActionId);
|
|
67
|
+
const deleteAction = relatedObject?.actions?.find((item) => item.id === entry.display?.deleteActionId);
|
|
68
68
|
function getForm(setForm, action, formId) {
|
|
69
69
|
if (formId || action?.defaultFormId) {
|
|
70
70
|
apiServices
|
|
@@ -76,27 +76,6 @@ const RepeatableField = (props) => {
|
|
|
76
76
|
console.error(error);
|
|
77
77
|
});
|
|
78
78
|
}
|
|
79
|
-
else if (action) {
|
|
80
|
-
apiServices
|
|
81
|
-
.get(getPrefixedUrl('/forms'), {
|
|
82
|
-
params: {
|
|
83
|
-
filter: {
|
|
84
|
-
where: {
|
|
85
|
-
actionId: action.id,
|
|
86
|
-
objectId: fieldDefinition.objectId,
|
|
87
|
-
},
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
})
|
|
91
|
-
.then((matchingForms) => {
|
|
92
|
-
if (matchingForms.length === 1) {
|
|
93
|
-
setForm(matchingForms[0]);
|
|
94
|
-
}
|
|
95
|
-
})
|
|
96
|
-
.catch((error) => {
|
|
97
|
-
console.error(error);
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
79
|
}
|
|
101
80
|
const fetchRelatedInstances = useCallback(async (refetch = false) => {
|
|
102
81
|
let relatedObject;
|
|
@@ -209,11 +188,11 @@ const RepeatableField = (props) => {
|
|
|
209
188
|
}, [fetchCriteriaObjects, relatedObject]);
|
|
210
189
|
useEffect(() => {
|
|
211
190
|
if (createAction && !createForm)
|
|
212
|
-
getForm(setCreateForm, createAction
|
|
191
|
+
getForm(setCreateForm, createAction, entry.display?.createFormId);
|
|
213
192
|
if (updateAction && !updateForm)
|
|
214
|
-
getForm(setUpdateForm, updateAction
|
|
193
|
+
getForm(setUpdateForm, updateAction, entry.display?.updateFormId);
|
|
215
194
|
if (deleteAction && !deleteForm)
|
|
216
|
-
getForm(setDeleteForm, deleteAction
|
|
195
|
+
getForm(setDeleteForm, deleteAction, entry.display?.deleteFormId);
|
|
217
196
|
}, [entry.display, createAction, updateAction, deleteAction]);
|
|
218
197
|
useEffect(() => {
|
|
219
198
|
if (relatedObject?.rootObjectId) {
|
|
@@ -269,10 +248,10 @@ const RepeatableField = (props) => {
|
|
|
269
248
|
if (fieldDefinition.objectId && canUpdateProperty && !fetchedOptions[`${fieldDefinition.id}HasCreateAction`]) {
|
|
270
249
|
apiServices
|
|
271
250
|
.get(getPrefixedUrl(`/objects/${fieldDefinition.objectId}/instances/checkAccess`), {
|
|
272
|
-
params: { action: 'execute', field:
|
|
251
|
+
params: { action: 'execute', field: entry.display?.createActionId, scope: 'data' },
|
|
273
252
|
})
|
|
274
253
|
.then((checkAccess) => {
|
|
275
|
-
const action = relatedObject.actions?.find((item) => item.id ===
|
|
254
|
+
const action = relatedObject.actions?.find((item) => item.id === entry.display?.createActionId);
|
|
276
255
|
if (action && fieldDefinition.relatedPropertyId) {
|
|
277
256
|
const { relatedObjectProperty, criteria } = retrieveCriteria(fieldDefinition.relatedPropertyId, action, relatedObject);
|
|
278
257
|
if (!criteria || JSON.stringify(criteria).includes('{{{input.') || !relatedObjectProperty) {
|
|
@@ -346,17 +325,21 @@ const RepeatableField = (props) => {
|
|
|
346
325
|
}, variant: "text", onClick: () => setReloadOnErrorTrigger((prevState) => !prevState) }, "Retry")));
|
|
347
326
|
const save = async (input) => {
|
|
348
327
|
const action = relatedObject?.actions?.find((a) => a.id ===
|
|
349
|
-
(dialogType === 'create'
|
|
328
|
+
(dialogType === 'create'
|
|
329
|
+
? entry.display?.createActionId
|
|
330
|
+
: dialogType === 'update'
|
|
331
|
+
? entry.display?.updateActionId
|
|
332
|
+
: entry.display?.deleteActionId));
|
|
350
333
|
// when save is called we know that fieldDefinition is a parameter and fieldDefinition.objectId is defined
|
|
351
334
|
input = await formatSubmission(input, apiServices, fieldDefinition.objectId, selectedInstanceId, action?.type === 'update' ? updateForm : undefined);
|
|
352
|
-
if (action?.type === 'create' && createActionId) {
|
|
335
|
+
if (action?.type === 'create' && entry.display?.createActionId) {
|
|
353
336
|
const updatedInput = {
|
|
354
337
|
...input,
|
|
355
338
|
[fieldDefinition?.relatedPropertyId]: { id: instance?.id },
|
|
356
339
|
};
|
|
357
340
|
try {
|
|
358
341
|
const instance = await apiServices.post(getPrefixedUrl(`/objects/${fieldDefinition.objectId}/instances/actions`), {
|
|
359
|
-
actionId: createActionId,
|
|
342
|
+
actionId: entry.display?.createActionId,
|
|
360
343
|
input: updatedInput,
|
|
361
344
|
});
|
|
362
345
|
const hasAccess = fieldDefinition?.relatedPropertyId && fieldDefinition.relatedPropertyId in instance;
|
|
@@ -379,8 +362,8 @@ const RepeatableField = (props) => {
|
|
|
379
362
|
try {
|
|
380
363
|
const response = await apiServices.post(getPrefixedUrl(`/objects/${relatedObjectId}/instances/${selectedInstanceId}/actions`), {
|
|
381
364
|
actionId: `_${action?.type}`,
|
|
382
|
-
input:
|
|
383
|
-
?.filter((property) =>
|
|
365
|
+
input: omit(input, relatedObject?.properties
|
|
366
|
+
?.filter((property) => property.formula || property.type === 'collection')
|
|
384
367
|
.map((property) => property.id) ?? []),
|
|
385
368
|
});
|
|
386
369
|
if (response && relatedObject && instance) {
|
|
@@ -519,12 +502,12 @@ const RepeatableField = (props) => {
|
|
|
519
502
|
users?.find((user) => get(relatedInstance, `${prop.id.split('.')[0]}.id`) === user.id)?.status === 'Inactive' &&
|
|
520
503
|
' (Inactive)')))))),
|
|
521
504
|
canUpdateProperty && (React.createElement(Box, { sx: { mt: 2, display: 'flex', gap: 1 } },
|
|
522
|
-
React.createElement(IconButton, { onClick: () => editRow(relatedInstance.id) },
|
|
505
|
+
entry.display?.updateActionId && (React.createElement(IconButton, { onClick: () => editRow(relatedInstance.id) },
|
|
523
506
|
React.createElement(Tooltip, { title: "Edit" },
|
|
524
|
-
React.createElement(Edit, null))),
|
|
525
|
-
React.createElement(IconButton, { onClick: () => deleteRow(relatedInstance.id) },
|
|
507
|
+
React.createElement(Edit, null)))),
|
|
508
|
+
entry.display?.deleteActionId && (React.createElement(IconButton, { onClick: () => deleteRow(relatedInstance.id) },
|
|
526
509
|
React.createElement(Tooltip, { title: "Delete" },
|
|
527
|
-
React.createElement(TrashCan, { sx: { ':hover': { color: '#A12723' } } }))))))))))) : (React.createElement(TableContainer, { sx: {
|
|
510
|
+
React.createElement(TrashCan, { sx: { ':hover': { color: '#A12723' } } })))))))))))) : (React.createElement(TableContainer, { sx: {
|
|
528
511
|
borderRadius: '6px',
|
|
529
512
|
border: '1px solid #919EAB3D',
|
|
530
513
|
boxShadow: 'none',
|
|
@@ -544,7 +527,7 @@ const RepeatableField = (props) => {
|
|
|
544
527
|
cursor: 'pointer',
|
|
545
528
|
},
|
|
546
529
|
}
|
|
547
|
-
: {}, onClick: updateActionId &&
|
|
530
|
+
: {}, onClick: entry.display?.updateActionId &&
|
|
548
531
|
canUpdateProperty &&
|
|
549
532
|
prop.id === 'name'
|
|
550
533
|
? () => editRow(relatedInstance.id)
|
|
@@ -554,19 +537,19 @@ const RepeatableField = (props) => {
|
|
|
554
537
|
users?.find((user) => get(relatedInstance, `${prop.id.split('.')[0]}.id`) === user.id)?.status === 'Inactive' && (React.createElement("span", null, ' (Inactive)'))))));
|
|
555
538
|
}),
|
|
556
539
|
canUpdateProperty && (React.createElement(TableCell, { sx: { width: '80px' } },
|
|
557
|
-
updateActionId && (React.createElement(IconButton, { "aria-label": `edit-collection-instance-${index}`, onClick: () => editRow(relatedInstance.id) },
|
|
540
|
+
entry.display?.updateActionId && (React.createElement(IconButton, { "aria-label": `edit-collection-instance-${index}`, onClick: () => editRow(relatedInstance.id) },
|
|
558
541
|
React.createElement(Tooltip, { title: "Edit" },
|
|
559
542
|
React.createElement(Edit, null)))),
|
|
560
|
-
React.createElement(IconButton, { "aria-label": `delete-collection-instance-${index}`, onClick: () => deleteRow(relatedInstance.id) },
|
|
543
|
+
entry.display?.deleteActionId && (React.createElement(IconButton, { "aria-label": `delete-collection-instance-${index}`, onClick: () => deleteRow(relatedInstance.id) },
|
|
561
544
|
React.createElement(Tooltip, { title: "Delete" },
|
|
562
|
-
React.createElement(TrashCan, { sx: { ':hover': { color: '#A12723' } } })))))))))))),
|
|
563
|
-
hasCreateAction && createActionId && (React.createElement(Button, { variant: "contained", sx: styles.addButton, onClick: addRow, "aria-label": 'Add' }, "Add"))),
|
|
545
|
+
React.createElement(TrashCan, { sx: { ':hover': { color: '#A12723' } } }))))))))))))),
|
|
546
|
+
hasCreateAction && entry.display?.createActionId && (React.createElement(Button, { variant: "contained", sx: styles.addButton, disabled: !createAction, onClick: addRow, "aria-label": 'Add' }, "Add"))),
|
|
564
547
|
relatedObject && openDialog && (React.createElement(ActionDialog, { object: relatedObject, open: openDialog, onClose: () => setOpenDialog(false), onSubmit: save, action: relatedObject?.actions?.find((a) => a.id ===
|
|
565
548
|
(dialogType === 'create'
|
|
566
|
-
? createActionId
|
|
549
|
+
? entry.display?.createActionId
|
|
567
550
|
: dialogType === 'update'
|
|
568
|
-
? updateActionId
|
|
569
|
-
: deleteActionId)), relatedFormId: dialogType === 'create'
|
|
551
|
+
? entry.display?.updateActionId
|
|
552
|
+
: entry.display?.deleteActionId)), relatedFormId: dialogType === 'create'
|
|
570
553
|
? createForm?.id
|
|
571
554
|
: dialogType === 'update'
|
|
572
555
|
? updateForm?.id
|
|
@@ -8,7 +8,7 @@ import { addressProperties, getPrefixedUrl } from '../utils';
|
|
|
8
8
|
export default function Criteria(props) {
|
|
9
9
|
const { value, canUpdateProperty, fieldDefinition, error } = props;
|
|
10
10
|
const apiServices = useApiServices();
|
|
11
|
-
const { fetchedOptions, setFetchedOptions, handleChange } = useFormContext();
|
|
11
|
+
const { fetchedOptions, setFetchedOptions, handleChange, onAutosave } = useFormContext();
|
|
12
12
|
const [loadingError, setLoadingError] = useState(false);
|
|
13
13
|
const [loading, setLoading] = useState(false);
|
|
14
14
|
const [properties, setProperties] = useState(fetchedOptions[`${fieldDefinition.id}Options`] || []);
|
|
@@ -57,9 +57,22 @@ export default function Criteria(props) {
|
|
|
57
57
|
useEffect(() => {
|
|
58
58
|
fetchProperties();
|
|
59
59
|
}, [fetchProperties]);
|
|
60
|
-
const handleUpdate = (criteria) => {
|
|
60
|
+
const handleUpdate = async (criteria) => {
|
|
61
61
|
if (criteria || value) {
|
|
62
|
-
|
|
62
|
+
const newValue = criteria ?? null;
|
|
63
|
+
try {
|
|
64
|
+
handleChange && (await handleChange(fieldDefinition.id, newValue));
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
console.error('Failed to update field:', error);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
await onAutosave?.(fieldDefinition.id);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
console.error('Autosave failed:', error);
|
|
75
|
+
}
|
|
63
76
|
}
|
|
64
77
|
};
|
|
65
78
|
if (loadingError) {
|
package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js
CHANGED
|
@@ -12,7 +12,7 @@ import { DocumentList } from './DocumentList';
|
|
|
12
12
|
export const Document = (props) => {
|
|
13
13
|
const { id, canUpdateProperty, error, value, validate, hasDescription } = props;
|
|
14
14
|
const apiServices = useApiServices();
|
|
15
|
-
const { fetchedOptions, setFetchedOptions, object, handleChange, instance } = useFormContext();
|
|
15
|
+
const { fetchedOptions, setFetchedOptions, object, handleChange, onAutosave: onAutosave, instance, } = useFormContext();
|
|
16
16
|
const [snackbarError, setSnackbarError] = useState();
|
|
17
17
|
const [documents, setDocuments] = useState();
|
|
18
18
|
const [hasUpdatePermission, setHasUpdatePermission] = useState(fetchedOptions[`${id}UpdatePermission`]);
|
|
@@ -49,13 +49,25 @@ export const Document = (props) => {
|
|
|
49
49
|
checkPermissions();
|
|
50
50
|
}, [checkPermissions]);
|
|
51
51
|
const handleUpload = async (files) => {
|
|
52
|
+
// Store File objects in form state - they will be uploaded during autosave via formatSubmission()
|
|
52
53
|
const newDocuments = [...(documents ?? []), ...(files ?? [])];
|
|
53
54
|
setDocuments(newDocuments);
|
|
54
|
-
|
|
55
|
+
try {
|
|
56
|
+
handleChange && (await handleChange(id, newDocuments));
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
console.error('Failed to update field:', error);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
await onAutosave?.(id);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error('Autosave failed:', error);
|
|
67
|
+
}
|
|
55
68
|
};
|
|
56
69
|
const uploadDisabled = !!validate?.maxDocuments && (documents?.length ?? 0) >= validate.maxDocuments;
|
|
57
70
|
const { getRootProps, getInputProps, open, fileRejections } = useDropzone({
|
|
58
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
59
71
|
onDrop: (files) => handleUpload(files),
|
|
60
72
|
disabled: uploadDisabled,
|
|
61
73
|
accept: validate?.allowedFileExtensions
|
|
@@ -109,7 +121,7 @@ export const Document = (props) => {
|
|
|
109
121
|
} }, validate?.maxDocuments === 1
|
|
110
122
|
? `Maximum size is ${formattedMaxSize}.`
|
|
111
123
|
: `The maximum size of each document is ${formattedMaxSize}.`)))))),
|
|
112
|
-
canUpdateProperty && isNil(hasUpdatePermission) ? (React.createElement(Skeleton, { variant: "rectangular", height: formattedMaxSize || allowedTypesMessage ? '136px' : '115px', sx: { margin: '5px 0', borderRadius: '8px' } })) : (React.createElement(DocumentList, { id: id, handleChange: handleChange, value: value, setSnackbarError: (type, message) => setSnackbarError({ message, type }), canUpdateProperty: canUpdateProperty && !!hasUpdatePermission })),
|
|
124
|
+
canUpdateProperty && isNil(hasUpdatePermission) ? (React.createElement(Skeleton, { variant: "rectangular", height: formattedMaxSize || allowedTypesMessage ? '136px' : '115px', sx: { margin: '5px 0', borderRadius: '8px' } })) : (React.createElement(DocumentList, { id: id, handleChange: handleChange, onAutosave: onAutosave, value: value, setSnackbarError: (type, message) => setSnackbarError({ message, type }), canUpdateProperty: canUpdateProperty && !!hasUpdatePermission })),
|
|
113
125
|
React.createElement(Snackbar, { open: !!snackbarError?.message, handleClose: () => setSnackbarError(null), message: snackbarError?.message, error: snackbarError?.type === 'error' }),
|
|
114
126
|
errors.length > 0 && (React.createElement(Box, { display: 'flex', alignItems: 'center' },
|
|
115
127
|
React.createElement(InfoRounded, { sx: { fontSize: '.75rem', marginRight: '3px', color: '#D3271B' } }),
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { SavedDocumentReference } from '../../types';
|
|
3
3
|
type DocumentListProps = {
|
|
4
|
-
handleChange
|
|
4
|
+
handleChange?: (propertyId: string, value: (File | SavedDocumentReference)[] | undefined) => void;
|
|
5
|
+
onAutosave?: (fieldId: string) => void | Promise<void>;
|
|
5
6
|
id: string;
|
|
6
7
|
canUpdateProperty: boolean;
|
|
7
8
|
value: (File | SavedDocumentReference)[] | undefined;
|
|
@@ -24,7 +24,7 @@ const viewableFileTypes = [
|
|
|
24
24
|
'text/plain',
|
|
25
25
|
];
|
|
26
26
|
export const DocumentList = (props) => {
|
|
27
|
-
const { handleChange, id, canUpdateProperty, value: documents, setSnackbarError } = props;
|
|
27
|
+
const { handleChange, onAutosave, id, canUpdateProperty, value: documents, setSnackbarError } = props;
|
|
28
28
|
const apiServices = useApiServices();
|
|
29
29
|
const { fetchedOptions, setFetchedOptions, object, instance } = useFormContext();
|
|
30
30
|
const [hasViewPermission, setHasViewPermission] = useState(fetchedOptions[`${id}ViewPermission`] ?? true);
|
|
@@ -88,9 +88,22 @@ export const DocumentList = (props) => {
|
|
|
88
88
|
};
|
|
89
89
|
const isFile = (doc) => doc instanceof File;
|
|
90
90
|
const fileExists = (doc) => savedDocuments?.find((d) => d.id === doc.id);
|
|
91
|
-
const handleRemove = (index) => {
|
|
91
|
+
const handleRemove = async (index) => {
|
|
92
92
|
const updatedDocuments = documents?.filter((_, i) => i !== index) ?? [];
|
|
93
|
-
|
|
93
|
+
const newValue = updatedDocuments.length === 0 ? undefined : updatedDocuments;
|
|
94
|
+
try {
|
|
95
|
+
handleChange && (await handleChange(id, newValue));
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
console.error('Failed to update field:', error);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
await onAutosave?.(id);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
console.error('Autosave failed:', error);
|
|
106
|
+
}
|
|
94
107
|
};
|
|
95
108
|
const openDocument = async (index) => {
|
|
96
109
|
const doc = documents?.[index];
|
|
@@ -55,7 +55,7 @@ const styles = {
|
|
|
55
55
|
};
|
|
56
56
|
export const Image = (props) => {
|
|
57
57
|
const { id, canUpdateProperty, error, value, hasDescription } = props;
|
|
58
|
-
const { handleChange } = useFormContext();
|
|
58
|
+
const { handleChange, onAutosave: onAutosave } = useFormContext();
|
|
59
59
|
const [image, setImage] = useState();
|
|
60
60
|
useEffect(() => {
|
|
61
61
|
if (typeof value === 'string') {
|
|
@@ -63,15 +63,41 @@ export const Image = (props) => {
|
|
|
63
63
|
}
|
|
64
64
|
}, [value]);
|
|
65
65
|
const handleUpload = async (file) => {
|
|
66
|
+
// max file size 300KB
|
|
66
67
|
if (file?.size && file.size <= 300000) {
|
|
67
|
-
const dataUrl =
|
|
68
|
+
const dataUrl = await blobToDataUrl(file);
|
|
68
69
|
setImage(dataUrl);
|
|
69
|
-
|
|
70
|
+
try {
|
|
71
|
+
handleChange && (await handleChange(id, dataUrl));
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
console.error('Failed to update field:', error);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
await onAutosave?.(id);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
console.error('Autosave failed:', error);
|
|
82
|
+
}
|
|
70
83
|
}
|
|
71
84
|
};
|
|
72
|
-
const handleRemove = (e) => {
|
|
85
|
+
const handleRemove = async (e) => {
|
|
73
86
|
setImage(null);
|
|
74
|
-
|
|
87
|
+
try {
|
|
88
|
+
handleChange && (await handleChange(id, ''));
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
console.error('Failed to update field:', error);
|
|
92
|
+
e.stopPropagation();
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
await onAutosave?.(id);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
console.error('Autosave failed:', error);
|
|
100
|
+
}
|
|
75
101
|
e.stopPropagation();
|
|
76
102
|
};
|
|
77
103
|
const { getRootProps, getInputProps, open } = useDropzone({
|
|
@@ -6,7 +6,7 @@ import { Autocomplete, IconButton, Paper, TextField, Typography } from '../../..
|
|
|
6
6
|
import { getPrefixedUrl, isOptionEqualToValue } from '../utils';
|
|
7
7
|
const UserProperty = (props) => {
|
|
8
8
|
const { id, error, value, readOnly, hasDescription } = props;
|
|
9
|
-
const { fetchedOptions, setFetchedOptions, handleChange, fieldHeight } = useFormContext();
|
|
9
|
+
const { fetchedOptions, setFetchedOptions, handleChange, onAutosave: onAutosave, fieldHeight } = useFormContext();
|
|
10
10
|
const [loadingOptions, setLoadingOptions] = useState(false);
|
|
11
11
|
const apiServices = useApiServices();
|
|
12
12
|
const [options, setOptions] = useState(fetchedOptions[`${id}Options`] || []);
|
|
@@ -40,9 +40,21 @@ const UserProperty = (props) => {
|
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
42
|
}, [id]);
|
|
43
|
-
function handleChangeUserProperty(id, value) {
|
|
43
|
+
async function handleChangeUserProperty(id, value) {
|
|
44
44
|
const updatedValue = typeof value?.value === 'string' ? { name: value.label, id: value.value } : null;
|
|
45
|
-
|
|
45
|
+
try {
|
|
46
|
+
handleChange && (await handleChange(id, updatedValue));
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
console.error('Failed to update field:', error);
|
|
50
|
+
return; // Exit early if handleChange fails
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
await onAutosave?.(id);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
console.error('Autosave failed:', error);
|
|
57
|
+
}
|
|
46
58
|
}
|
|
47
59
|
return (options && (React.createElement(Autocomplete, { id: id, fullWidth: true, open: openOptions, popupIcon: userValue || readOnly ? '' : React.createElement(ExpandMore, null), clearIcon: !loadingOptions && userValue ? (React.createElement(IconButton, { size: "small", disableRipple: true, onKeyDown: (e) => {
|
|
48
60
|
if (e.key === 'Enter') {
|