@evoke-platform/ui-components 1.9.1-testing.1 → 1.10.0-testing.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/FormV2/FormRenderer.d.ts +20 -6
- package/dist/published/components/custom/FormV2/FormRenderer.js +142 -128
- package/dist/published/components/custom/FormV2/FormRendererContainer.d.ts +21 -2
- package/dist/published/components/custom/FormV2/FormRendererContainer.js +44 -46
- package/dist/published/components/custom/FormV2/components/Body.d.ts +18 -0
- package/dist/published/components/custom/FormV2/components/Body.js +27 -0
- package/dist/published/components/custom/FormV2/components/Footer.d.ts +15 -0
- package/dist/published/components/custom/FormV2/components/Footer.js +77 -0
- package/dist/published/components/custom/FormV2/components/FormContext.d.ts +2 -2
- package/dist/published/components/custom/FormV2/components/FormContext.js +1 -1
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/ActionDialog.d.ts +1 -1
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/ActionDialog.js +64 -22
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/RepeatableField.js +18 -16
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js +7 -7
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/InstanceLookup.d.ts +0 -1
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/InstanceLookup.js +10 -8
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/ObjectPropertyInput.js +38 -49
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.d.ts +2 -1
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.js +95 -51
- package/dist/published/components/custom/FormV2/components/Header.d.ts +29 -0
- package/dist/published/components/custom/FormV2/components/Header.js +63 -0
- package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.js +3 -3
- package/dist/published/components/custom/FormV2/components/ValidationFiles/ValidationErrors.d.ts +9 -0
- package/dist/published/components/custom/FormV2/components/ValidationFiles/{ValidationErrorDisplay.js → ValidationErrors.js} +11 -8
- package/dist/published/components/custom/FormV2/components/types.d.ts +1 -7
- package/dist/published/components/custom/FormV2/index.d.ts +3 -0
- package/dist/published/components/custom/FormV2/tests/FormRenderer.test.js +6 -5
- package/dist/published/components/custom/FormV2/tests/FormRendererContainer.test.js +4 -3
- package/dist/published/components/custom/index.d.ts +1 -1
- package/dist/published/components/custom/index.js +1 -1
- package/dist/published/index.d.ts +1 -1
- package/dist/published/stories/FormRenderer.stories.d.ts +24 -16
- package/dist/published/stories/FormRenderer.stories.js +2 -10
- package/dist/published/stories/FormRendererContainer.stories.d.ts +40 -10
- package/dist/published/theme/hooks.d.ts +12 -3
- package/package.json +1 -1
- package/dist/published/components/custom/FormV2/components/ActionButtons.d.ts +0 -17
- package/dist/published/components/custom/FormV2/components/ActionButtons.js +0 -111
- package/dist/published/components/custom/FormV2/components/ValidationFiles/ValidationErrorDisplay.d.ts +0 -11
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Action } from '@evoke-platform/context';
|
|
2
|
+
import { SxProps } from '@mui/material/styles';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
export type FooterProps = {
|
|
5
|
+
onSubmit: () => Promise<void>;
|
|
6
|
+
onDiscardChanges: () => void;
|
|
7
|
+
action?: Action;
|
|
8
|
+
submitButtonLabel?: string;
|
|
9
|
+
discardChangesButtonLabel?: string;
|
|
10
|
+
sx?: SxProps;
|
|
11
|
+
};
|
|
12
|
+
export declare const Footer: React.FC<FooterProps>;
|
|
13
|
+
export type FooterActionsProps = Omit<FooterProps, 'sx'>;
|
|
14
|
+
export declare const FooterActions: (props: FooterActionsProps) => React.JSX.Element;
|
|
15
|
+
export default Footer;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
|
+
import { useWidgetSize } from '../../../../theme';
|
|
3
|
+
import Button from '../../../core/Button/Button';
|
|
4
|
+
import LoadingButton from '../../../core/LoadingButton/LoadingButton';
|
|
5
|
+
import { Box } from '../../../layout';
|
|
6
|
+
import { FormContext } from './FormContext';
|
|
7
|
+
/* Default FormRenderer Footer. Displays a submit button and cancel changes button. */
|
|
8
|
+
export const Footer = (props) => {
|
|
9
|
+
const { action, sx } = props;
|
|
10
|
+
const { width } = useContext(FormContext);
|
|
11
|
+
const { isBelow, breakpoints } = useWidgetSize({
|
|
12
|
+
scroll: false,
|
|
13
|
+
defaultWidth: width,
|
|
14
|
+
});
|
|
15
|
+
const { isXs } = breakpoints;
|
|
16
|
+
const isSmallerThanMd = isBelow('md');
|
|
17
|
+
return (React.createElement(Box, { sx: {
|
|
18
|
+
display: 'flex',
|
|
19
|
+
flexWrap: 'wrap-reverse',
|
|
20
|
+
padding: isSmallerThanMd ? '16px' : '20px',
|
|
21
|
+
justifyContent: isXs ? 'center' : 'flex-end',
|
|
22
|
+
alignItems: 'center',
|
|
23
|
+
borderTop: action?.type !== 'delete' ? '1px solid #f4f6f8' : 'none',
|
|
24
|
+
borderRadius: '0px 0px 6px 6px',
|
|
25
|
+
...sx,
|
|
26
|
+
} },
|
|
27
|
+
React.createElement(FooterActions, { ...props })));
|
|
28
|
+
};
|
|
29
|
+
export const FooterActions = (props) => {
|
|
30
|
+
const { action, onDiscardChanges, onSubmit, submitButtonLabel, discardChangesButtonLabel } = props;
|
|
31
|
+
const { width } = useContext(FormContext);
|
|
32
|
+
const [loading, setLoading] = React.useState(false);
|
|
33
|
+
const handleSubmit = async () => {
|
|
34
|
+
setLoading(true);
|
|
35
|
+
try {
|
|
36
|
+
await onSubmit();
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
setLoading(false);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const { breakpoints } = useWidgetSize({
|
|
43
|
+
scroll: false,
|
|
44
|
+
defaultWidth: width,
|
|
45
|
+
});
|
|
46
|
+
const { isXs } = breakpoints;
|
|
47
|
+
return (React.createElement(React.Fragment, null,
|
|
48
|
+
React.createElement(Button, { onClick: onDiscardChanges, variant: "outlined", sx: {
|
|
49
|
+
margin: '5px',
|
|
50
|
+
marginX: isXs ? '0px' : undefined,
|
|
51
|
+
color: 'black',
|
|
52
|
+
border: '1px solid rgb(206, 212, 218)',
|
|
53
|
+
width: isXs ? '100%' : 'auto',
|
|
54
|
+
'&:hover': {
|
|
55
|
+
backgroundColor: '#f2f4f7',
|
|
56
|
+
border: '1px solid rgb(206, 212, 218)',
|
|
57
|
+
},
|
|
58
|
+
} }, discardChangesButtonLabel),
|
|
59
|
+
React.createElement(LoadingButton, { onClick: handleSubmit, variant: "contained", sx: {
|
|
60
|
+
lineHeight: '2.75',
|
|
61
|
+
margin: '5px 0 5px 5px',
|
|
62
|
+
marginX: isXs ? '0px' : undefined,
|
|
63
|
+
padding: '0 23px',
|
|
64
|
+
backgroundColor: action?.type === 'delete' ? '#A12723' : 'primary',
|
|
65
|
+
borderRadius: '8px',
|
|
66
|
+
boxShadow: 'none',
|
|
67
|
+
whiteSpace: 'nowrap',
|
|
68
|
+
width: isXs ? '100%' : 'auto',
|
|
69
|
+
'& .MuiCircularProgress-root': {
|
|
70
|
+
color: 'white',
|
|
71
|
+
},
|
|
72
|
+
'&:hover': {
|
|
73
|
+
backgroundColor: action?.type === 'delete' ? ' #8C2421' : '#014E7B',
|
|
74
|
+
},
|
|
75
|
+
}, loading: loading }, submitButtonLabel)));
|
|
76
|
+
};
|
|
77
|
+
export default Footer;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { InputParameter, Obj } from '@evoke-platform/context';
|
|
1
|
+
import { EvokeForm, InputParameter, Obj } from '@evoke-platform/context';
|
|
2
2
|
import { ComponentType } from 'react';
|
|
3
3
|
import { FieldValues, UseFormGetValues } from 'react-hook-form';
|
|
4
4
|
import { ExpandedSection, SimpleEditorProps } from './types';
|
|
@@ -6,7 +6,6 @@ type FormContextType = {
|
|
|
6
6
|
fetchedOptions: FieldValues;
|
|
7
7
|
setFetchedOptions: (newData: FieldValues) => void;
|
|
8
8
|
getValues: UseFormGetValues<FieldValues>;
|
|
9
|
-
stickyFooter?: boolean;
|
|
10
9
|
object?: Obj;
|
|
11
10
|
errors?: FieldValues;
|
|
12
11
|
instance?: FieldValues;
|
|
@@ -24,6 +23,7 @@ type FormContextType = {
|
|
|
24
23
|
instanceId?: string;
|
|
25
24
|
propertyId?: string;
|
|
26
25
|
};
|
|
26
|
+
form?: EvokeForm;
|
|
27
27
|
width: number;
|
|
28
28
|
};
|
|
29
29
|
export declare const FormContext: import("react").Context<FormContextType>;
|
|
@@ -3,7 +3,6 @@ export const FormContext = createContext({
|
|
|
3
3
|
fetchedOptions: {},
|
|
4
4
|
setFetchedOptions: () => { },
|
|
5
5
|
getValues: (() => ({})),
|
|
6
|
-
stickyFooter: false,
|
|
7
6
|
object: undefined,
|
|
8
7
|
errors: {},
|
|
9
8
|
instance: {},
|
|
@@ -14,6 +13,7 @@ export const FormContext = createContext({
|
|
|
14
13
|
triggerFieldReset: false,
|
|
15
14
|
handleChange: () => { },
|
|
16
15
|
fieldHeight: 'medium',
|
|
16
|
+
form: {},
|
|
17
17
|
// Default width 1200 to match common 'lg' render size and avoid style changes on first render
|
|
18
18
|
width: 1200,
|
|
19
19
|
});
|
|
@@ -5,7 +5,7 @@ export type ActionDialogProps = {
|
|
|
5
5
|
open: boolean;
|
|
6
6
|
onClose: () => void;
|
|
7
7
|
action: Action;
|
|
8
|
-
|
|
8
|
+
onSubmit: (input: FieldValues) => void;
|
|
9
9
|
object: Obj;
|
|
10
10
|
instanceId?: string;
|
|
11
11
|
relatedParameter: InputParameter;
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { useApiServices } from '@evoke-platform/context';
|
|
2
2
|
import { Close } from '@mui/icons-material';
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
3
|
+
import { isEmpty } from 'lodash';
|
|
4
|
+
import React, { useContext, useEffect, useRef, useState } from 'react';
|
|
5
|
+
import useWidgetSize, { useFormContext } from '../../../../../../theme/hooks';
|
|
6
|
+
import { Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Skeleton } from '../../../../../core';
|
|
7
7
|
import ErrorComponent from '../../../../ErrorComponent';
|
|
8
8
|
import FormRendererContainer from '../../../FormRendererContainer';
|
|
9
|
+
import Body from '../../Body';
|
|
10
|
+
import { Footer } from '../../Footer';
|
|
11
|
+
import { FormContext } from '../../FormContext';
|
|
12
|
+
import { AccordionActions } from '../../Header';
|
|
9
13
|
import { getPrefixedUrl } from '../../utils';
|
|
14
|
+
import ValidationErrors from '../../ValidationFiles/ValidationErrors';
|
|
10
15
|
const styles = {
|
|
11
16
|
button: {
|
|
12
17
|
textTransform: 'initial',
|
|
@@ -17,8 +22,7 @@ const styles = {
|
|
|
17
22
|
dialogTitle: {
|
|
18
23
|
fontSize: '18px',
|
|
19
24
|
fontWeight: 700,
|
|
20
|
-
|
|
21
|
-
paddingBottom: '20px',
|
|
25
|
+
padding: 3,
|
|
22
26
|
},
|
|
23
27
|
closeIcon: {
|
|
24
28
|
position: 'absolute',
|
|
@@ -38,11 +42,18 @@ const styles = {
|
|
|
38
42
|
},
|
|
39
43
|
};
|
|
40
44
|
export const ActionDialog = (props) => {
|
|
41
|
-
const { open, onClose, action, object, instanceId, relatedFormId, relatedParameter,
|
|
45
|
+
const { open, onClose, action, object, instanceId, relatedFormId, relatedParameter, onSubmit, associatedObject } = props;
|
|
42
46
|
const [loading, setLoading] = useState(false);
|
|
43
|
-
const [hasAccess, setHasAccess] = useState();
|
|
44
|
-
const {
|
|
47
|
+
const [hasAccess, setHasAccess] = useState(true);
|
|
48
|
+
const { fieldHeight, richTextEditor } = useFormContext();
|
|
49
|
+
const validationErrorsRef = useRef(null);
|
|
45
50
|
const apiServices = useApiServices();
|
|
51
|
+
const { width } = useContext(FormContext);
|
|
52
|
+
const { breakpoints } = useWidgetSize({
|
|
53
|
+
scroll: false,
|
|
54
|
+
defaultWidth: width,
|
|
55
|
+
});
|
|
56
|
+
const { isXs, isSm } = breakpoints;
|
|
46
57
|
useEffect(() => {
|
|
47
58
|
if (instanceId) {
|
|
48
59
|
setLoading(true);
|
|
@@ -59,20 +70,51 @@ export const ActionDialog = (props) => {
|
|
|
59
70
|
}
|
|
60
71
|
}, [object, instanceId]);
|
|
61
72
|
const handleFormSave = async (data) => {
|
|
62
|
-
return
|
|
73
|
+
return onSubmit(data);
|
|
74
|
+
};
|
|
75
|
+
const handleSaveError = () => {
|
|
76
|
+
if (validationErrorsRef.current) {
|
|
77
|
+
validationErrorsRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
78
|
+
}
|
|
63
79
|
};
|
|
64
80
|
return (React.createElement(Dialog, { PaperProps: {
|
|
65
81
|
sx: { maxWidth: '950px', width: '100%' },
|
|
66
|
-
}, fullWidth: true, open: open, onClose: (e, reason) => reason !== 'backdropClick' && onClose() },
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
82
|
+
}, fullWidth: true, open: open, onClose: (e, reason) => reason !== 'backdropClick' && onClose() }, hasAccess ? (React.createElement(FormRendererContainer, { instanceId: instanceId, formId: relatedFormId, display: { fieldHeight: fieldHeight ?? 'medium' }, actionId: action.id,
|
|
83
|
+
// relatedParameter will have an objectId here
|
|
84
|
+
objectId: relatedParameter.objectId, onSubmit: handleFormSave, onDiscardChanges: onClose, onSubmitError: handleSaveError, richTextEditor: richTextEditor, associatedObject: associatedObject, renderHeader: (formHeaderProps) => {
|
|
85
|
+
return (React.createElement(DialogTitle, { sx: {
|
|
86
|
+
...styles.dialogTitle,
|
|
87
|
+
borderBottom: action.type === 'delete' ? undefined : '1px solid #e9ecef',
|
|
88
|
+
} },
|
|
89
|
+
action && hasAccess && !loading ? action?.name : '',
|
|
90
|
+
React.createElement(IconButton, { sx: styles.closeIcon, onClick: onClose },
|
|
91
|
+
React.createElement(Close, { fontSize: "small" })),
|
|
92
|
+
formHeaderProps.hasAccordions && React.createElement(AccordionActions, { ...formHeaderProps })));
|
|
93
|
+
}, renderBody: (bodyProps) => (React.createElement(DialogContent, { sx: {
|
|
94
|
+
padding: '20px 24px 0px 24px',
|
|
95
|
+
paddingBottom: loading ? undefined : '0px',
|
|
96
|
+
maxHeight: 'calc(100vh - 340px)',
|
|
97
|
+
overflowY: 'auto',
|
|
98
|
+
} },
|
|
99
|
+
React.createElement("div", { ref: validationErrorsRef }, !isEmpty(bodyProps.errors) && bodyProps.shouldShowValidationErrors ? (React.createElement(ValidationErrors, { errors: bodyProps.errors, sx: {
|
|
100
|
+
my: isSm || isXs ? 2 : 3,
|
|
101
|
+
} })) : null),
|
|
102
|
+
React.createElement(Body, { ...bodyProps, sx: { padding: 0 } }))), renderFooter: (footerProps) => (React.createElement(DialogActions, { sx: { padding: 0 } },
|
|
103
|
+
React.createElement(Footer, { ...footerProps, discardChangesButtonLabel: "Cancel" }))), renderContainer: ({ status, error, defaultContainer }) => (React.createElement(React.Fragment, null,
|
|
104
|
+
status === 'ready' && defaultContainer,
|
|
105
|
+
status === 'loading' && (React.createElement(DialogContent, null,
|
|
106
|
+
React.createElement(IconButton, { sx: styles.closeIcon, onClick: onClose },
|
|
107
|
+
React.createElement(Close, { fontSize: "small" })),
|
|
108
|
+
defaultContainer)),
|
|
109
|
+
status === 'error' && (React.createElement(DialogContent, null,
|
|
110
|
+
React.createElement(IconButton, { sx: styles.closeIcon, onClick: onClose },
|
|
111
|
+
React.createElement(Close, { fontSize: "small" })),
|
|
112
|
+
React.createElement(ErrorComponent, { code: error === 403
|
|
113
|
+
? 'AccessDenied'
|
|
114
|
+
: error === 404
|
|
115
|
+
? 'NotFound'
|
|
116
|
+
: 'Misconfigured', styles: { boxShadow: 'none' } }))))) })) : (React.createElement(React.Fragment, null, loading ? (React.createElement(React.Fragment, null,
|
|
117
|
+
React.createElement(Skeleton, { height: '30px', animation: 'wave' }),
|
|
118
|
+
React.createElement(Skeleton, { height: '30px', animation: 'wave' }),
|
|
119
|
+
React.createElement(Skeleton, { height: '30px', animation: 'wave' }))) : (!hasAccess && (React.createElement(ErrorComponent, { code: 'AccessDenied', message: 'You do not have permission to perform this action.', styles: { boxShadow: 'none' } })))))));
|
|
78
120
|
};
|
|
@@ -45,7 +45,7 @@ const RepeatableField = (props) => {
|
|
|
45
45
|
const apiServices = useApiServices();
|
|
46
46
|
const [reloadOnErrorTrigger, setReloadOnErrorTrigger] = useState(true);
|
|
47
47
|
const [criteriaObjects, setCriteriaObjects] = useState([]);
|
|
48
|
-
const [
|
|
48
|
+
const [selectedInstanceId, setSelectedInstanceId] = useState();
|
|
49
49
|
const [dialogType, setDialogType] = useState();
|
|
50
50
|
const [openDialog, setOpenDialog] = useState(false);
|
|
51
51
|
const [users, setUsers] = useState(fetchedOptions[`${fieldDefinition.id}Users`] || []);
|
|
@@ -314,17 +314,17 @@ const RepeatableField = (props) => {
|
|
|
314
314
|
}, [relatedObject, relatedInstances, hasCreateAction, tableViewLayout]);
|
|
315
315
|
const deleteRow = (id) => {
|
|
316
316
|
setDialogType('delete');
|
|
317
|
-
|
|
317
|
+
setSelectedInstanceId(id);
|
|
318
318
|
setOpenDialog(true);
|
|
319
319
|
};
|
|
320
320
|
const addRow = () => {
|
|
321
321
|
setDialogType('create');
|
|
322
|
-
|
|
322
|
+
setSelectedInstanceId(undefined);
|
|
323
323
|
setOpenDialog(true);
|
|
324
324
|
};
|
|
325
325
|
const editRow = (id) => {
|
|
326
326
|
setDialogType('update');
|
|
327
|
-
|
|
327
|
+
setSelectedInstanceId(id);
|
|
328
328
|
setOpenDialog(true);
|
|
329
329
|
};
|
|
330
330
|
const ErrorComponent = () => loading ? (React.createElement("div", null,
|
|
@@ -344,10 +344,12 @@ const RepeatableField = (props) => {
|
|
|
344
344
|
},
|
|
345
345
|
'min-width': '44px',
|
|
346
346
|
}, variant: "text", onClick: () => setReloadOnErrorTrigger((prevState) => !prevState) }, "Retry")));
|
|
347
|
-
const save = async (
|
|
347
|
+
const save = async (input) => {
|
|
348
|
+
const action = relatedObject?.actions?.find((a) => a.id ===
|
|
349
|
+
(dialogType === 'create' ? createActionId : dialogType === 'update' ? updateActionId : deleteActionId));
|
|
348
350
|
// when save is called we know that fieldDefinition is a parameter and fieldDefinition.objectId is defined
|
|
349
|
-
input = await formatSubmission(input, apiServices, fieldDefinition.objectId,
|
|
350
|
-
if (action
|
|
351
|
+
input = await formatSubmission(input, apiServices, fieldDefinition.objectId, selectedInstanceId, action?.type === 'update' ? updateForm : undefined);
|
|
352
|
+
if (action?.type === 'create' && createActionId) {
|
|
351
353
|
const updatedInput = {
|
|
352
354
|
...input,
|
|
353
355
|
[fieldDefinition?.relatedPropertyId]: { id: instance?.id },
|
|
@@ -361,7 +363,7 @@ const RepeatableField = (props) => {
|
|
|
361
363
|
hasAccess && setRelatedInstances([...relatedInstances, instance]);
|
|
362
364
|
setOpenDialog(false);
|
|
363
365
|
setDialogType(undefined);
|
|
364
|
-
|
|
366
|
+
setSelectedInstanceId(undefined);
|
|
365
367
|
}
|
|
366
368
|
catch (err) {
|
|
367
369
|
setSnackbarError({
|
|
@@ -375,8 +377,8 @@ const RepeatableField = (props) => {
|
|
|
375
377
|
else {
|
|
376
378
|
const relatedObjectId = relatedObject?.id;
|
|
377
379
|
try {
|
|
378
|
-
const response = await apiServices.post(getPrefixedUrl(`/objects/${relatedObjectId}/instances/${
|
|
379
|
-
actionId: `_${action
|
|
380
|
+
const response = await apiServices.post(getPrefixedUrl(`/objects/${relatedObjectId}/instances/${selectedInstanceId}/actions`), {
|
|
381
|
+
actionId: `_${action?.type}`,
|
|
380
382
|
input: pick(input, relatedObject?.properties
|
|
381
383
|
?.filter((property) => !property.formula && property.type !== 'collection')
|
|
382
384
|
.map((property) => property.id) ?? []),
|
|
@@ -384,21 +386,21 @@ const RepeatableField = (props) => {
|
|
|
384
386
|
if (response && relatedObject && instance) {
|
|
385
387
|
deleteDocuments(input, !!response, apiServices, relatedObject, instance, action);
|
|
386
388
|
}
|
|
387
|
-
if (action
|
|
388
|
-
setRelatedInstances((prevInstances) => prevInstances.filter((instance) => instance.id !==
|
|
389
|
+
if (action?.type === 'delete') {
|
|
390
|
+
setRelatedInstances((prevInstances) => prevInstances.filter((instance) => instance.id !== selectedInstanceId));
|
|
389
391
|
}
|
|
390
392
|
else {
|
|
391
393
|
setRelatedInstances((prevInstances) => prevInstances.map((i) => (i.id === instance?.id ? instance : i)));
|
|
392
394
|
}
|
|
393
395
|
setOpenDialog(false);
|
|
394
396
|
setDialogType(undefined);
|
|
395
|
-
|
|
397
|
+
setSelectedInstanceId(undefined);
|
|
396
398
|
}
|
|
397
399
|
catch (err) {
|
|
398
400
|
setSnackbarError({
|
|
399
401
|
showAlert: true,
|
|
400
402
|
message: retrieveCustomErrorMessage(err) ??
|
|
401
|
-
`An error occurred while ${action
|
|
403
|
+
`An error occurred while ${action?.type === 'delete' ? ' deleting' : ' updating'} an instance`,
|
|
402
404
|
isError: true,
|
|
403
405
|
});
|
|
404
406
|
}
|
|
@@ -559,7 +561,7 @@ const RepeatableField = (props) => {
|
|
|
559
561
|
React.createElement(Tooltip, { title: "Delete" },
|
|
560
562
|
React.createElement(TrashCan, { sx: { ':hover': { color: '#A12723' } } })))))))))))),
|
|
561
563
|
hasCreateAction && createActionId && (React.createElement(Button, { variant: "contained", sx: styles.addButton, onClick: addRow, "aria-label": 'Add' }, "Add"))),
|
|
562
|
-
relatedObject && openDialog && (React.createElement(ActionDialog, { object: relatedObject, open: openDialog, onClose: () => setOpenDialog(false),
|
|
564
|
+
relatedObject && openDialog && (React.createElement(ActionDialog, { object: relatedObject, open: openDialog, onClose: () => setOpenDialog(false), onSubmit: save, action: relatedObject?.actions?.find((a) => a.id ===
|
|
563
565
|
(dialogType === 'create'
|
|
564
566
|
? createActionId
|
|
565
567
|
: dialogType === 'update'
|
|
@@ -570,7 +572,7 @@ const RepeatableField = (props) => {
|
|
|
570
572
|
? updateForm?.id
|
|
571
573
|
: dialogType === 'delete'
|
|
572
574
|
? deleteForm?.id
|
|
573
|
-
: undefined, instanceId:
|
|
575
|
+
: undefined, instanceId: selectedInstanceId, relatedParameter: fieldDefinition, associatedObject: instance?.id && fieldDefinition.relatedPropertyId
|
|
574
576
|
? { instanceId: instance.id, propertyId: fieldDefinition.relatedPropertyId }
|
|
575
577
|
: undefined })),
|
|
576
578
|
React.createElement(Snackbar, { open: snackbarError.showAlert, handleClose: () => setSnackbarError({ isError: snackbarError.isError, showAlert: false }), message: snackbarError.message, error: snackbarError.isError })));
|
package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useApiServices } from '@evoke-platform/context';
|
|
2
2
|
import { isNil } from 'lodash';
|
|
3
3
|
import prettyBytes from 'pretty-bytes';
|
|
4
|
-
import React, { useEffect, useState } from 'react';
|
|
4
|
+
import React, { useCallback, useEffect, useState } from 'react';
|
|
5
5
|
import { useDropzone } from 'react-dropzone';
|
|
6
6
|
import { InfoRounded, UploadCloud } from '../../../../../../icons';
|
|
7
7
|
import { useFormContext } from '../../../../../../theme/hooks';
|
|
@@ -33,11 +33,8 @@ export const Document = (props) => {
|
|
|
33
33
|
useEffect(() => {
|
|
34
34
|
setDocuments(value);
|
|
35
35
|
}, [value]);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}, [object]);
|
|
39
|
-
const checkPermissions = () => {
|
|
40
|
-
if (canUpdateProperty && !fetchedOptions[`${id}UpdatePermission`]) {
|
|
36
|
+
const checkPermissions = useCallback(() => {
|
|
37
|
+
if (canUpdateProperty && !fetchedOptions[`${id}UpdatePermission`] && instance?.id) {
|
|
41
38
|
apiServices
|
|
42
39
|
.get(getPrefixedUrl(`/objects/${object?.id}/instances/${instance?.id}/documents/checkAccess?action=update`))
|
|
43
40
|
.then((accessCheck) => {
|
|
@@ -47,7 +44,10 @@ export const Document = (props) => {
|
|
|
47
44
|
setHasUpdatePermission(accessCheck.result);
|
|
48
45
|
});
|
|
49
46
|
}
|
|
50
|
-
};
|
|
47
|
+
}, [canUpdateProperty, fetchedOptions, instance, object]);
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
checkPermissions();
|
|
50
|
+
}, [checkPermissions]);
|
|
51
51
|
const handleUpload = async (files) => {
|
|
52
52
|
const newDocuments = [...(documents ?? []), ...(files ?? [])];
|
|
53
53
|
setDocuments(newDocuments);
|
|
@@ -7,7 +7,6 @@ export type InstanceLookUpProps = BaseProps & {
|
|
|
7
7
|
setSelectedInstance: (selectedInstance: ObjectInstance) => void;
|
|
8
8
|
setRelationType: (relationType: 'new' | 'existing') => void;
|
|
9
9
|
mode: 'default' | 'existingOnly';
|
|
10
|
-
nestedFieldsView?: boolean;
|
|
11
10
|
filter?: Record<string, unknown>;
|
|
12
11
|
layout?: TableViewLayout;
|
|
13
12
|
};
|
|
@@ -2,7 +2,7 @@ import { useApiServices } from '@evoke-platform/context';
|
|
|
2
2
|
import { Clear, Search } from '@mui/icons-material';
|
|
3
3
|
import { debounce, get, startCase } from 'lodash';
|
|
4
4
|
import { DateTime } from 'luxon';
|
|
5
|
-
import React, { useEffect, useState } from 'react';
|
|
5
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
6
6
|
import { Button, IconButton, InputAdornment, TextField, Typography, } from '../../../../../core';
|
|
7
7
|
import { Box, Grid } from '../../../../../layout';
|
|
8
8
|
import BuilderGrid from '../../../../BuilderGrid';
|
|
@@ -50,7 +50,7 @@ const SearchField = (props) => {
|
|
|
50
50
|
} }));
|
|
51
51
|
};
|
|
52
52
|
const InstanceLookup = (props) => {
|
|
53
|
-
const { object, setSelectedInstance, setRelationType, filter: criteriaFilter, mode,
|
|
53
|
+
const { object, setSelectedInstance, setRelationType, filter: criteriaFilter, mode, layout } = props;
|
|
54
54
|
const [rows, setRows] = useState([]);
|
|
55
55
|
const [loading, setLoading] = useState(false);
|
|
56
56
|
const [filter, setFilter] = useState();
|
|
@@ -164,7 +164,7 @@ const InstanceLookup = (props) => {
|
|
|
164
164
|
}
|
|
165
165
|
return columns;
|
|
166
166
|
};
|
|
167
|
-
|
|
167
|
+
const fetchObjectInstance = useCallback(() => {
|
|
168
168
|
setLoading(true);
|
|
169
169
|
const combinedFilter = {
|
|
170
170
|
...criteriaFilter,
|
|
@@ -184,16 +184,18 @@ const InstanceLookup = (props) => {
|
|
|
184
184
|
}
|
|
185
185
|
setLoading(false);
|
|
186
186
|
});
|
|
187
|
-
}
|
|
187
|
+
}, [object.id, criteriaFilter, filter]);
|
|
188
|
+
const debouncedFetchObjectInstances = useMemo(() => debounce(fetchObjectInstance, 500), [fetchObjectInstance]);
|
|
188
189
|
useEffect(() => {
|
|
189
190
|
if (filter || searchString.length) {
|
|
190
|
-
|
|
191
|
+
debouncedFetchObjectInstances();
|
|
191
192
|
}
|
|
192
193
|
else if (searchString === '') {
|
|
193
194
|
setRows([]);
|
|
194
195
|
}
|
|
195
|
-
|
|
196
|
-
|
|
196
|
+
return () => debouncedFetchObjectInstances.cancel();
|
|
197
|
+
}, [filter, searchString.length, debouncedFetchObjectInstances]);
|
|
198
|
+
return (React.createElement(Grid, { container: true },
|
|
197
199
|
React.createElement(Grid, { item: true, xs: 12 }, searchableColumns.length ? (React.createElement(SearchField, { searchString: searchString, setSearchString: setSearchString, filter: filter, setFilter: setFilter, searchableColumns: searchableColumns })) : (React.createElement(Typography, { sx: { fontSize: '16px', fontWeight: '700' } }, "There are no searchable properties configured for this object"))),
|
|
198
200
|
React.createElement(BuilderGrid, { item: 'instances', rows: rows, columns: retrieveColumns(layout), onRowClick: (params) => setSelectedInstance(params.row), initialSort: {
|
|
199
201
|
field: object.viewLayout?.table?.sort?.colId ?? 'name',
|
|
@@ -217,7 +219,7 @@ const InstanceLookup = (props) => {
|
|
|
217
219
|
textAlign: 'center',
|
|
218
220
|
} },
|
|
219
221
|
"Refine your search",
|
|
220
|
-
|
|
222
|
+
mode !== 'existingOnly' && (React.createElement(React.Fragment, null,
|
|
221
223
|
' or ',
|
|
222
224
|
React.createElement(Button, { variant: "text", onClick: () => setRelationType('new'), sx: {
|
|
223
225
|
padding: '3px 5px',
|