@evoke-platform/ui-components 1.10.0-dev.2 → 1.10.0-dev.21
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 +116 -74
- 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 +36 -49
- 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 +433 -4
- package/dist/published/components/custom/FormV2/tests/FormRendererContainer.test.js +662 -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,10 +62,12 @@ 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
|
+
if (formId === '_auto_')
|
|
70
|
+
return;
|
|
69
71
|
if (formId || action?.defaultFormId) {
|
|
70
72
|
apiServices
|
|
71
73
|
.get(getPrefixedUrl(`/forms/${formId || action?.defaultFormId}`))
|
|
@@ -76,27 +78,6 @@ const RepeatableField = (props) => {
|
|
|
76
78
|
console.error(error);
|
|
77
79
|
});
|
|
78
80
|
}
|
|
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
81
|
}
|
|
101
82
|
const fetchRelatedInstances = useCallback(async (refetch = false) => {
|
|
102
83
|
let relatedObject;
|
|
@@ -209,11 +190,11 @@ const RepeatableField = (props) => {
|
|
|
209
190
|
}, [fetchCriteriaObjects, relatedObject]);
|
|
210
191
|
useEffect(() => {
|
|
211
192
|
if (createAction && !createForm)
|
|
212
|
-
getForm(setCreateForm, createAction
|
|
193
|
+
getForm(setCreateForm, createAction, entry.display?.createFormId);
|
|
213
194
|
if (updateAction && !updateForm)
|
|
214
|
-
getForm(setUpdateForm, updateAction
|
|
195
|
+
getForm(setUpdateForm, updateAction, entry.display?.updateFormId);
|
|
215
196
|
if (deleteAction && !deleteForm)
|
|
216
|
-
getForm(setDeleteForm, deleteAction
|
|
197
|
+
getForm(setDeleteForm, deleteAction, entry.display?.deleteFormId);
|
|
217
198
|
}, [entry.display, createAction, updateAction, deleteAction]);
|
|
218
199
|
useEffect(() => {
|
|
219
200
|
if (relatedObject?.rootObjectId) {
|
|
@@ -269,10 +250,10 @@ const RepeatableField = (props) => {
|
|
|
269
250
|
if (fieldDefinition.objectId && canUpdateProperty && !fetchedOptions[`${fieldDefinition.id}HasCreateAction`]) {
|
|
270
251
|
apiServices
|
|
271
252
|
.get(getPrefixedUrl(`/objects/${fieldDefinition.objectId}/instances/checkAccess`), {
|
|
272
|
-
params: { action: 'execute', field:
|
|
253
|
+
params: { action: 'execute', field: entry.display?.createActionId, scope: 'data' },
|
|
273
254
|
})
|
|
274
255
|
.then((checkAccess) => {
|
|
275
|
-
const action = relatedObject.actions?.find((item) => item.id ===
|
|
256
|
+
const action = relatedObject.actions?.find((item) => item.id === entry.display?.createActionId);
|
|
276
257
|
if (action && fieldDefinition.relatedPropertyId) {
|
|
277
258
|
const { relatedObjectProperty, criteria } = retrieveCriteria(fieldDefinition.relatedPropertyId, action, relatedObject);
|
|
278
259
|
if (!criteria || JSON.stringify(criteria).includes('{{{input.') || !relatedObjectProperty) {
|
|
@@ -346,17 +327,21 @@ const RepeatableField = (props) => {
|
|
|
346
327
|
}, variant: "text", onClick: () => setReloadOnErrorTrigger((prevState) => !prevState) }, "Retry")));
|
|
347
328
|
const save = async (input) => {
|
|
348
329
|
const action = relatedObject?.actions?.find((a) => a.id ===
|
|
349
|
-
(dialogType === 'create'
|
|
330
|
+
(dialogType === 'create'
|
|
331
|
+
? entry.display?.createActionId
|
|
332
|
+
: dialogType === 'update'
|
|
333
|
+
? entry.display?.updateActionId
|
|
334
|
+
: entry.display?.deleteActionId));
|
|
350
335
|
// when save is called we know that fieldDefinition is a parameter and fieldDefinition.objectId is defined
|
|
351
336
|
input = await formatSubmission(input, apiServices, fieldDefinition.objectId, selectedInstanceId, action?.type === 'update' ? updateForm : undefined);
|
|
352
|
-
if (action?.type === 'create' && createActionId) {
|
|
337
|
+
if (action?.type === 'create' && entry.display?.createActionId) {
|
|
353
338
|
const updatedInput = {
|
|
354
339
|
...input,
|
|
355
340
|
[fieldDefinition?.relatedPropertyId]: { id: instance?.id },
|
|
356
341
|
};
|
|
357
342
|
try {
|
|
358
343
|
const instance = await apiServices.post(getPrefixedUrl(`/objects/${fieldDefinition.objectId}/instances/actions`), {
|
|
359
|
-
actionId: createActionId,
|
|
344
|
+
actionId: entry.display?.createActionId,
|
|
360
345
|
input: updatedInput,
|
|
361
346
|
});
|
|
362
347
|
const hasAccess = fieldDefinition?.relatedPropertyId && fieldDefinition.relatedPropertyId in instance;
|
|
@@ -379,8 +364,8 @@ const RepeatableField = (props) => {
|
|
|
379
364
|
try {
|
|
380
365
|
const response = await apiServices.post(getPrefixedUrl(`/objects/${relatedObjectId}/instances/${selectedInstanceId}/actions`), {
|
|
381
366
|
actionId: `_${action?.type}`,
|
|
382
|
-
input:
|
|
383
|
-
?.filter((property) =>
|
|
367
|
+
input: omit(input, relatedObject?.properties
|
|
368
|
+
?.filter((property) => property.formula || property.type === 'collection')
|
|
384
369
|
.map((property) => property.id) ?? []),
|
|
385
370
|
});
|
|
386
371
|
if (response && relatedObject && instance) {
|
|
@@ -519,12 +504,12 @@ const RepeatableField = (props) => {
|
|
|
519
504
|
users?.find((user) => get(relatedInstance, `${prop.id.split('.')[0]}.id`) === user.id)?.status === 'Inactive' &&
|
|
520
505
|
' (Inactive)')))))),
|
|
521
506
|
canUpdateProperty && (React.createElement(Box, { sx: { mt: 2, display: 'flex', gap: 1 } },
|
|
522
|
-
React.createElement(IconButton, { onClick: () => editRow(relatedInstance.id) },
|
|
507
|
+
entry.display?.updateActionId && (React.createElement(IconButton, { onClick: () => editRow(relatedInstance.id) },
|
|
523
508
|
React.createElement(Tooltip, { title: "Edit" },
|
|
524
|
-
React.createElement(Edit, null))),
|
|
525
|
-
React.createElement(IconButton, { onClick: () => deleteRow(relatedInstance.id) },
|
|
509
|
+
React.createElement(Edit, null)))),
|
|
510
|
+
entry.display?.deleteActionId && (React.createElement(IconButton, { onClick: () => deleteRow(relatedInstance.id) },
|
|
526
511
|
React.createElement(Tooltip, { title: "Delete" },
|
|
527
|
-
React.createElement(TrashCan, { sx: { ':hover': { color: '#A12723' } } }))))))))))) : (React.createElement(TableContainer, { sx: {
|
|
512
|
+
React.createElement(TrashCan, { sx: { ':hover': { color: '#A12723' } } })))))))))))) : (React.createElement(TableContainer, { sx: {
|
|
528
513
|
borderRadius: '6px',
|
|
529
514
|
border: '1px solid #919EAB3D',
|
|
530
515
|
boxShadow: 'none',
|
|
@@ -544,7 +529,7 @@ const RepeatableField = (props) => {
|
|
|
544
529
|
cursor: 'pointer',
|
|
545
530
|
},
|
|
546
531
|
}
|
|
547
|
-
: {}, onClick: updateActionId &&
|
|
532
|
+
: {}, onClick: entry.display?.updateActionId &&
|
|
548
533
|
canUpdateProperty &&
|
|
549
534
|
prop.id === 'name'
|
|
550
535
|
? () => editRow(relatedInstance.id)
|
|
@@ -554,24 +539,26 @@ const RepeatableField = (props) => {
|
|
|
554
539
|
users?.find((user) => get(relatedInstance, `${prop.id.split('.')[0]}.id`) === user.id)?.status === 'Inactive' && (React.createElement("span", null, ' (Inactive)'))))));
|
|
555
540
|
}),
|
|
556
541
|
canUpdateProperty && (React.createElement(TableCell, { sx: { width: '80px' } },
|
|
557
|
-
updateActionId && (React.createElement(IconButton, { "aria-label": `edit-collection-instance-${index}`, onClick: () => editRow(relatedInstance.id) },
|
|
542
|
+
entry.display?.updateActionId && (React.createElement(IconButton, { "aria-label": `edit-collection-instance-${index}`, onClick: () => editRow(relatedInstance.id) },
|
|
558
543
|
React.createElement(Tooltip, { title: "Edit" },
|
|
559
544
|
React.createElement(Edit, null)))),
|
|
560
|
-
React.createElement(IconButton, { "aria-label": `delete-collection-instance-${index}`, onClick: () => deleteRow(relatedInstance.id) },
|
|
545
|
+
entry.display?.deleteActionId && (React.createElement(IconButton, { "aria-label": `delete-collection-instance-${index}`, onClick: () => deleteRow(relatedInstance.id) },
|
|
561
546
|
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"))),
|
|
547
|
+
React.createElement(TrashCan, { sx: { ':hover': { color: '#A12723' } } }))))))))))))),
|
|
548
|
+
hasCreateAction && entry.display?.createActionId && (React.createElement(Button, { variant: "contained", sx: styles.addButton, disabled: !createAction, onClick: addRow, "aria-label": 'Add' }, "Add"))),
|
|
564
549
|
relatedObject && openDialog && (React.createElement(ActionDialog, { object: relatedObject, open: openDialog, onClose: () => setOpenDialog(false), onSubmit: save, action: relatedObject?.actions?.find((a) => a.id ===
|
|
565
550
|
(dialogType === 'create'
|
|
566
|
-
? createActionId
|
|
551
|
+
? entry.display?.createActionId
|
|
567
552
|
: dialogType === 'update'
|
|
568
|
-
? updateActionId
|
|
569
|
-
: deleteActionId)), relatedFormId: dialogType === 'create'
|
|
553
|
+
? entry.display?.updateActionId
|
|
554
|
+
: entry.display?.deleteActionId)), relatedFormId: dialogType === 'create'
|
|
570
555
|
? createForm?.id
|
|
571
556
|
: dialogType === 'update'
|
|
572
557
|
? updateForm?.id
|
|
573
558
|
: dialogType === 'delete'
|
|
574
|
-
?
|
|
559
|
+
? entry.display?.deleteFormId === '_auto_'
|
|
560
|
+
? '_auto_'
|
|
561
|
+
: deleteForm?.id
|
|
575
562
|
: undefined, instanceId: selectedInstanceId, relatedParameter: fieldDefinition, associatedObject: instance?.id && fieldDefinition.relatedPropertyId
|
|
576
563
|
? { instanceId: instance.id, propertyId: fieldDefinition.relatedPropertyId }
|
|
577
564
|
: undefined })),
|
|
@@ -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') {
|