@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.
Files changed (39) hide show
  1. package/dist/published/components/custom/FormV2/FormRenderer.d.ts +20 -6
  2. package/dist/published/components/custom/FormV2/FormRenderer.js +142 -128
  3. package/dist/published/components/custom/FormV2/FormRendererContainer.d.ts +21 -2
  4. package/dist/published/components/custom/FormV2/FormRendererContainer.js +44 -46
  5. package/dist/published/components/custom/FormV2/components/Body.d.ts +18 -0
  6. package/dist/published/components/custom/FormV2/components/Body.js +27 -0
  7. package/dist/published/components/custom/FormV2/components/Footer.d.ts +15 -0
  8. package/dist/published/components/custom/FormV2/components/Footer.js +77 -0
  9. package/dist/published/components/custom/FormV2/components/FormContext.d.ts +2 -2
  10. package/dist/published/components/custom/FormV2/components/FormContext.js +1 -1
  11. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/ActionDialog.d.ts +1 -1
  12. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/ActionDialog.js +64 -22
  13. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/RepeatableField.js +18 -16
  14. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js +7 -7
  15. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/InstanceLookup.d.ts +0 -1
  16. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/InstanceLookup.js +10 -8
  17. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/ObjectPropertyInput.js +38 -49
  18. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.d.ts +2 -1
  19. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.js +95 -51
  20. package/dist/published/components/custom/FormV2/components/Header.d.ts +29 -0
  21. package/dist/published/components/custom/FormV2/components/Header.js +63 -0
  22. package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.js +3 -3
  23. package/dist/published/components/custom/FormV2/components/ValidationFiles/ValidationErrors.d.ts +9 -0
  24. package/dist/published/components/custom/FormV2/components/ValidationFiles/{ValidationErrorDisplay.js → ValidationErrors.js} +11 -8
  25. package/dist/published/components/custom/FormV2/components/types.d.ts +1 -7
  26. package/dist/published/components/custom/FormV2/index.d.ts +3 -0
  27. package/dist/published/components/custom/FormV2/tests/FormRenderer.test.js +6 -5
  28. package/dist/published/components/custom/FormV2/tests/FormRendererContainer.test.js +4 -3
  29. package/dist/published/components/custom/index.d.ts +1 -1
  30. package/dist/published/components/custom/index.js +1 -1
  31. package/dist/published/index.d.ts +1 -1
  32. package/dist/published/stories/FormRenderer.stories.d.ts +24 -16
  33. package/dist/published/stories/FormRenderer.stories.js +2 -10
  34. package/dist/published/stories/FormRendererContainer.stories.d.ts +40 -10
  35. package/dist/published/theme/hooks.d.ts +12 -3
  36. package/package.json +1 -1
  37. package/dist/published/components/custom/FormV2/components/ActionButtons.d.ts +0 -17
  38. package/dist/published/components/custom/FormV2/components/ActionButtons.js +0 -111
  39. 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
- handleSubmit: (action: Action, input: FieldValues, instanceId?: string, setSubmitting?: (value: boolean) => void) => void;
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 React, { useEffect, useState } from 'react';
4
- import { useFormContext } from '../../../../../../theme/hooks';
5
- import { Dialog, DialogContent, DialogTitle, IconButton, Skeleton } from '../../../../../core';
6
- import { Box } from '../../../../../layout';
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
- paddingTop: '35px',
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, handleSubmit, associatedObject, } = props;
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 { stickyFooter, fieldHeight, richTextEditor } = useFormContext();
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 handleSubmit(action, data, instanceId);
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
- React.createElement(DialogTitle, { sx: { ...styles.dialogTitle, borderBottom: action.type === 'delete' ? undefined : '1px solid #e9ecef' } },
68
- React.createElement(IconButton, { sx: styles.closeIcon, onClick: onClose },
69
- React.createElement(Close, { fontSize: "small" })),
70
- action && hasAccess && !loading ? action?.name : ''),
71
- React.createElement(DialogContent, { sx: { paddingBottom: loading ? undefined : '0px' } }, hasAccess ? (React.createElement(Box, { sx: { width: '100%', marginTop: '10px' } },
72
- React.createElement(FormRendererContainer, { instanceId: instanceId, formId: relatedFormId, display: { fieldHeight: fieldHeight ?? 'medium' }, actionId: action.id, stickyFooter: stickyFooter,
73
- // relatedParameter will have an objectId here
74
- objectId: relatedParameter.objectId, onClose: onClose, onSubmit: handleFormSave, richTextEditor: richTextEditor, associatedObject: associatedObject }))) : (React.createElement(React.Fragment, null, loading ? (React.createElement(React.Fragment, null,
75
- React.createElement(Skeleton, { height: '30px', animation: 'wave' }),
76
- React.createElement(Skeleton, { height: '30px', animation: 'wave' }),
77
- React.createElement(Skeleton, { height: '30px', animation: 'wave' }))) : (React.createElement(ErrorComponent, { code: 'AccessDenied', message: 'You do not have permission to perform this action.', styles: { boxShadow: 'none' } })))))));
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 [selectedRow, setSelectedRow] = useState();
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
- setSelectedRow(id);
317
+ setSelectedInstanceId(id);
318
318
  setOpenDialog(true);
319
319
  };
320
320
  const addRow = () => {
321
321
  setDialogType('create');
322
- setSelectedRow(undefined);
322
+ setSelectedInstanceId(undefined);
323
323
  setOpenDialog(true);
324
324
  };
325
325
  const editRow = (id) => {
326
326
  setDialogType('update');
327
- setSelectedRow(id);
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 (action, input, instanceId) => {
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, instanceId, action.type === 'update' ? updateForm : undefined);
350
- if (action.type === 'create' && createActionId) {
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
- setSelectedRow(undefined);
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/${instanceId}/actions`), {
379
- actionId: `_${action.type}`,
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.type === 'delete') {
388
- setRelatedInstances((prevInstances) => prevInstances.filter((instance) => instance.id !== instanceId));
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
- setSelectedRow(undefined);
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.type === 'delete' ? ' deleting' : ' updating'} an instance`,
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), handleSubmit: save, action: relatedObject?.actions?.find((a) => a.id ===
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: selectedRow, relatedParameter: fieldDefinition, associatedObject: instance?.id && fieldDefinition.relatedPropertyId
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 })));
@@ -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
- useEffect(() => {
37
- checkPermissions();
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, nestedFieldsView, layout, } = props;
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
- function fetchObjectInstance() {
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
- debounce(() => fetchObjectInstance(), 500)();
191
+ debouncedFetchObjectInstances();
191
192
  }
192
193
  else if (searchString === '') {
193
194
  setRows([]);
194
195
  }
195
- }, [filter, searchString.length]);
196
- return (React.createElement(Grid, { container: true, sx: { padding: '30px 24px' } },
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
- !nestedFieldsView && mode !== 'existingOnly' && (React.createElement(React.Fragment, null,
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',