@evoke-platform/ui-components 1.13.0-dev.4 → 1.13.0-dev.6

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 (32) hide show
  1. package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.d.ts +4 -4
  2. package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.js +145 -72
  3. package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.test.js +189 -67
  4. package/dist/published/components/custom/CriteriaBuilder/PropertyTree.d.ts +6 -6
  5. package/dist/published/components/custom/CriteriaBuilder/PropertyTree.js +12 -25
  6. package/dist/published/components/custom/CriteriaBuilder/PropertyTreeItem.d.ts +4 -5
  7. package/dist/published/components/custom/CriteriaBuilder/PropertyTreeItem.js +34 -22
  8. package/dist/published/components/custom/CriteriaBuilder/types.d.ts +2 -11
  9. package/dist/published/components/custom/CriteriaBuilder/utils.d.ts +6 -34
  10. package/dist/published/components/custom/CriteriaBuilder/utils.js +18 -89
  11. package/dist/published/components/custom/Form/FormComponents/DocumentComponent/Document.js +1 -1
  12. package/dist/published/components/custom/Form/FormComponents/DocumentComponent/DocumentList.js +6 -3
  13. package/dist/published/components/custom/Form/utils.d.ts +1 -0
  14. package/dist/published/components/custom/FormV2/FormRenderer.d.ts +1 -0
  15. package/dist/published/components/custom/FormV2/FormRendererContainer.d.ts +1 -0
  16. package/dist/published/components/custom/FormV2/FormRendererContainer.js +25 -22
  17. package/dist/published/components/custom/FormV2/components/FormContext.d.ts +1 -0
  18. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/ActionDialog.d.ts +1 -0
  19. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.d.ts +3 -2
  20. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js +46 -10
  21. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.d.ts +4 -3
  22. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.js +29 -23
  23. package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.js +4 -2
  24. package/dist/published/components/custom/FormV2/components/types.d.ts +6 -1
  25. package/dist/published/components/custom/FormV2/components/utils.d.ts +5 -7
  26. package/dist/published/components/custom/FormV2/components/utils.js +146 -69
  27. package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.js +20 -8
  28. package/dist/published/stories/CriteriaBuilder.stories.js +70 -22
  29. package/dist/published/stories/FormRenderer.stories.d.ts +3 -0
  30. package/dist/published/stories/FormRendererContainer.stories.d.ts +5 -0
  31. package/dist/published/theme/hooks.d.ts +2 -1
  32. package/package.json +1 -1
@@ -4,38 +4,25 @@ import { Autocomplete, RichTreeView } from '../../core';
4
4
  import { Box } from '../../layout';
5
5
  import { OverflowTextField } from '../OverflowTextField';
6
6
  import { PropertyTreeItem } from './PropertyTreeItem';
7
- import { findTreeItemById, truncateNamePath, updateTreeNode } from './utils';
7
+ import { convertTreeViewPropertyToTreeItem, findTreeItemById, truncateNamePath, updateTreeViewProperty } from './utils';
8
8
  const NAME_PATH_LIMIT = 35;
9
9
  const PropertyTree = (props) => {
10
- const { fetchObject, handleTreePropertySelect, rootObject, value, propertyTreeMap } = props;
10
+ const { fetchObject, handleTreePropertySelect, rootObject, setRootObject, value, propertyTreeMap } = props;
11
11
  const [options, setOptions] = useState([]);
12
12
  const [expandedItems, setExpandedItems] = useState([]);
13
13
  const [openDropdown, setOpenDropdown] = useState(false);
14
14
  useEffect(() => {
15
15
  // Transform rootObject properties to TreeItem format
16
- setOptions(rootObject.properties.map((property) => ({
17
- id: property.id,
18
- label: property.name,
19
- value: property.id,
20
- type: property.type,
21
- objectId: property.objectId,
22
- children: property.children
23
- ? property.children.map((child) => ({
24
- id: child.id,
25
- label: child.name,
26
- value: child.id,
27
- type: child.type,
28
- objectId: child.objectId,
29
- }))
30
- : undefined,
31
- })));
32
- setExpandedItems([]);
16
+ setOptions(rootObject.properties.map(convertTreeViewPropertyToTreeItem));
33
17
  }, [rootObject.properties]);
34
18
  const handleExpandedItemsChange = (e, itemIds) => {
35
19
  setExpandedItems(itemIds);
36
20
  };
37
21
  const handleUpdateNodeChildren = (nodeId, children) => {
38
- setOptions((prevOptions) => updateTreeNode(prevOptions, nodeId, (node) => ({ ...node, children })));
22
+ setRootObject((prevObject) => ({
23
+ ...prevObject,
24
+ properties: updateTreeViewProperty(prevObject.properties, nodeId, (node) => ({ ...node, children })),
25
+ }));
39
26
  };
40
27
  return (React.createElement(Autocomplete, { "aria-label": "Property Selector", value: value, fullWidth: true, sx: { width: '37%' }, size: "small", disableClearable: true, options: options, open: openDropdown, onBlur: (e) => {
41
28
  const targetComponents = e.relatedTarget?.getAttribute('id')?.split('-');
@@ -58,11 +45,11 @@ const PropertyTree = (props) => {
58
45
  }, onFocus: () => setOpenDropdown(true), getOptionLabel: (option) => {
59
46
  // Retrieve the full name path from the map
60
47
  const namePath = typeof option === 'string'
61
- ? ((propertyTreeMap[option] ?? '')?.name ?? option)
62
- : (propertyTreeMap[option.value]?.name ?? option.value);
48
+ ? (propertyTreeMap[option] ?? '') || option
49
+ : propertyTreeMap[option.value] || option.value;
63
50
  return truncateNamePath(namePath, NAME_PATH_LIMIT);
64
51
  }, renderInput: (params) => {
65
- const fullDisplayName = value && propertyTreeMap[value]?.name;
52
+ const fullDisplayName = value && propertyTreeMap[value];
66
53
  return (React.createElement(OverflowTextField, { ...params, "aria-label": fullDisplayName, value: fullDisplayName, placeholder: "Select a property" }));
67
54
  }, ListboxComponent: (props) => {
68
55
  return (React.createElement(ClickAwayListener, { onClickAway: (e) => {
@@ -73,8 +60,8 @@ const PropertyTree = (props) => {
73
60
  } },
74
61
  React.createElement(Box, { ...props },
75
62
  React.createElement(RichTreeView, { sx: { width: '100%', paddingLeft: 0 }, role: "menu", "aria-label": "Property Tree", expandedItems: expandedItems, onExpandedItemsChange: handleExpandedItemsChange, items: options, slots: {
76
- item: (itemProps) => (React.createElement(PropertyTreeItem, { ...itemProps, items: options, expanded: expandedItems, setExpanded: setExpandedItems, fetchObject: fetchObject, updateNodeChildren: handleUpdateNodeChildren, handleTreePropertySelect: async (propertyId) => {
77
- await handleTreePropertySelect(propertyId);
63
+ item: (itemProps) => (React.createElement(PropertyTreeItem, { ...itemProps, items: options, expanded: expandedItems, setExpanded: setExpandedItems, fetchObject: fetchObject, updateNodeChildren: handleUpdateNodeChildren, handleTreePropertySelect: (propertyId) => {
64
+ handleTreePropertySelect(propertyId);
78
65
  setOpenDropdown(false);
79
66
  } })),
80
67
  } }))));
@@ -1,14 +1,13 @@
1
1
  import { TreeItemProps } from '@mui/x-tree-view';
2
2
  import React from 'react';
3
- import { Obj } from '../../../types';
4
- import { TreeItem } from './types';
3
+ import { TreeItem, TreeViewObject, TreeViewProperty } from './types';
5
4
  type PropertyTreeItemProps = TreeItemProps & {
6
5
  items: TreeItem[];
7
6
  expanded: string[];
8
7
  setExpanded: (expanded: string[]) => void;
9
- updateNodeChildren: (nodeId: string, children: TreeItem[]) => void;
10
- fetchObject: (id: string) => Promise<Obj | undefined>;
11
- handleTreePropertySelect: (propertyId: string) => Promise<void>;
8
+ updateNodeChildren: (nodeId: string, children: TreeViewProperty[]) => void;
9
+ fetchObject: (id: string) => Promise<TreeViewObject | undefined>;
10
+ handleTreePropertySelect: (propertyId: string) => void;
12
11
  };
13
12
  export declare const PropertyTreeItem: (props: PropertyTreeItemProps) => React.JSX.Element;
14
13
  export {};
@@ -11,35 +11,47 @@ export const PropertyTreeItem = (props) => {
11
11
  return;
12
12
  }
13
13
  const item = findTreeItemById(items, itemId);
14
- if (item?.type === 'object') {
14
+ if (item?.type === 'loadingFailed') {
15
+ e.stopPropagation();
16
+ return;
17
+ }
18
+ if (item?.children) {
15
19
  e.stopPropagation();
16
20
  // If the item has an associated "objectId", fetch the properties of the object and expand the item.
17
- if (item.objectId && item.children?.length === 1 && item.children[0].type === 'loading') {
18
- const object = item.objectId ? await fetchObject(item.objectId) : undefined;
19
- if (object) {
20
- updateNodeChildren(itemId, (object.properties ?? []).map((prop) => ({
21
- id: `${itemId}.${prop.id}`,
22
- label: prop.name,
23
- value: `${itemId}.${prop.id}`,
24
- type: prop.type,
25
- objectId: prop.objectId,
26
- children: prop.type === 'object'
27
- ? [
28
- {
29
- id: `${itemId}.${prop.id}-loading`,
30
- label: 'Loading...',
31
- value: `${itemId}.${prop.id}-loading`,
32
- type: 'loading',
33
- },
34
- ]
35
- : undefined,
36
- })));
21
+ if (item?.type === 'object' &&
22
+ item.objectId &&
23
+ item.children?.length === 1 &&
24
+ item.children[0].type === 'loading') {
25
+ try {
26
+ const object = await fetchObject(item.objectId);
27
+ if (object) {
28
+ updateNodeChildren(itemId, object.properties
29
+ .filter((prop) => prop.type !== 'collection')
30
+ .map((prop) => ({
31
+ ...prop,
32
+ id: `${itemId}.${prop.id}`,
33
+ children: prop.children?.map((child) => ({
34
+ ...child,
35
+ id: `${itemId}.${prop.id}.${child.id}`,
36
+ })),
37
+ })));
38
+ }
39
+ }
40
+ catch (error) {
41
+ updateNodeChildren(itemId, [
42
+ {
43
+ id: `${itemId}-failed`,
44
+ name: 'Loading Failed',
45
+ type: 'loadingFailed',
46
+ },
47
+ ]);
48
+ console.error('Error fetching object:', error);
37
49
  }
38
50
  }
39
51
  setExpanded([...expanded, itemId]);
40
52
  return;
41
53
  }
42
- await handleTreePropertySelect(itemId);
54
+ handleTreePropertySelect(itemId);
43
55
  };
44
56
  return React.createElement(RichTreeItem, { itemId: itemId, label: label, children: children, onClick: onClick });
45
57
  };
@@ -1,17 +1,8 @@
1
1
  /// <reference types="react" />
2
+ import { Property } from '@evoke-platform/context';
2
3
  import { BaseSelectorProps } from 'react-querybuilder';
3
4
  import { ExpandedProperty } from '../../../types';
4
5
  import { AutocompleteOption, TreeViewBaseItem } from '../../core';
5
- export type ObjectProperty = {
6
- id: string;
7
- name: string;
8
- type: string;
9
- enum?: string[];
10
- required?: boolean;
11
- searchable?: boolean;
12
- objectId?: string;
13
- formula?: string;
14
- };
15
6
  export type CustomSelectorProps = BaseSelectorProps & {
16
7
  options: AutocompleteOption[] | any[];
17
8
  fieldData?: Record<string, any>;
@@ -28,7 +19,7 @@ export type PresetValue = {
28
19
  };
29
20
  type?: string;
30
21
  };
31
- export type TreeViewProperty = ObjectProperty & {
22
+ export type TreeViewProperty = Property & {
32
23
  children?: TreeViewProperty[];
33
24
  };
34
25
  export type TreeViewObject = {
@@ -1,43 +1,16 @@
1
1
  import { Property } from '@evoke-platform/context';
2
2
  import { RuleGroupType } from 'react-querybuilder';
3
- import { Obj, ObjectProperty } from '../../../types';
4
- import { TreeItem } from './types';
3
+ import { TreeItem, TreeViewProperty } from './types';
5
4
  /**
6
5
  * Recursively updates a node in a tree structure by applying an updater function to the node with the specified ID.
7
6
  *
8
- * @param {TreeItem[]} tree - The tree structure to update.
7
+ * @param {TreeViewProperty[]} tree - The tree structure to update.
9
8
  * @param {string} nodeId - The ID of the node to update.
10
- * @param {(node: TreeItem) => TreeItem} updater - The function to apply to the node.
11
- * @returns {TreeItem[]} - The updated tree structure.
9
+ * @param {(node: TreeViewProperty) => TreeViewProperty} updater - The function to apply to the node.
10
+ * @returns {TreeViewProperty[]} - The updated tree structure.
12
11
  */
13
- export declare const updateTreeNode: (tree: TreeItem[], nodeId: string, updater: (node: TreeItem) => TreeItem) => TreeItem[];
14
- type FetchObjectFunction = (id: string) => Promise<Obj | undefined>;
15
- /**
16
- * Fetches the display name path for a given property ID within an object hierarchy.
17
- *
18
- * @param {string} propertyId - The property ID to find the display name for.
19
- * @param {Obj} rootObject - The root object to start the search from.
20
- * @param {FetchObjectFunction} fetchObject - Function to fetch an object by its ID.
21
- * @returns {Promise<string>} - A promise that resolves to the display name path.
22
- */
23
- export declare const fetchDisplayNamePath: (propertyId: string, rootObject: Obj, fetchObject: FetchObjectFunction) => Promise<string>;
24
- /**
25
- * stores full dot-notation path to each property ID in the given array of properties.
26
- *
27
- * @param {ObjectProperty[]} properties - The array of properties to update.
28
- * @param {string} parentPath - The parent path to attach to each property ID.
29
- * @returns {ObjectProperty[]} The updated array of properties with the parent path attached to each property ID.
30
- */
31
- export declare const setIdPaths: (properties: ObjectProperty[], parentPath: string) => ObjectProperty[];
32
- /**
33
- * Traverses a property path within an object hierarchy to retrieve detailed property information.
34
- *
35
- * @param {string} propertyPath - The dot-separated path of the property to traverse.
36
- * @param {Obj} rootObject - The root object from which to start the traversal.
37
- * @param {FetchObjectFunction} fetchObject - A function to fetch an object by its ID.
38
- * @returns {Promise<ObjectProperty | null>} A promise that resolves to an ObjectProperty if found, or null otherwise.
39
- */
40
- export declare const traversePropertyPath: (propertyPath: string, rootObject: Obj, fetchObject: (objectId: string) => Promise<Obj | undefined>) => Promise<ObjectProperty | null>;
12
+ export declare const updateTreeViewProperty: (tree: TreeViewProperty[], nodeId: string, updater: (node: TreeViewProperty) => TreeViewProperty) => TreeViewProperty[];
13
+ export declare const convertTreeViewPropertyToTreeItem: (property: TreeViewProperty) => TreeItem;
41
14
  /**
42
15
  * Truncates the name path if it exceeds the specified character limit.
43
16
  *
@@ -68,4 +41,3 @@ export declare const ALL_OPERATORS: {
68
41
  */
69
42
  export declare const getReadableQuery: (mongoQuery?: Record<string, unknown>, properties?: Property[]) => string;
70
43
  export declare const findTreeItemById: (nodes: TreeItem[], nodeId: string) => TreeItem | null;
71
- export {};
@@ -2,105 +2,32 @@ import { isArray, isEmpty, startCase } from 'lodash';
2
2
  /**
3
3
  * Recursively updates a node in a tree structure by applying an updater function to the node with the specified ID.
4
4
  *
5
- * @param {TreeItem[]} tree - The tree structure to update.
5
+ * @param {TreeViewProperty[]} tree - The tree structure to update.
6
6
  * @param {string} nodeId - The ID of the node to update.
7
- * @param {(node: TreeItem) => TreeItem} updater - The function to apply to the node.
8
- * @returns {TreeItem[]} - The updated tree structure.
7
+ * @param {(node: TreeViewProperty) => TreeViewProperty} updater - The function to apply to the node.
8
+ * @returns {TreeViewProperty[]} - The updated tree structure.
9
9
  */
10
- export const updateTreeNode = (tree, nodeId, updater) => {
10
+ export const updateTreeViewProperty = (tree, nodeId, updater) => {
11
11
  return tree.map((node) => {
12
12
  if (node.id === nodeId) {
13
13
  return updater(node);
14
14
  }
15
15
  else if (node.children) {
16
- return { ...node, children: updateTreeNode(node.children, nodeId, updater) };
16
+ return { ...node, children: updateTreeViewProperty(node.children, nodeId, updater) };
17
17
  }
18
18
  else {
19
19
  return node;
20
20
  }
21
21
  });
22
22
  };
23
- /**
24
- * Fetches the display name path for a given property ID within an object hierarchy.
25
- *
26
- * @param {string} propertyId - The property ID to find the display name for.
27
- * @param {Obj} rootObject - The root object to start the search from.
28
- * @param {FetchObjectFunction} fetchObject - Function to fetch an object by its ID.
29
- * @returns {Promise<string>} - A promise that resolves to the display name path.
30
- */
31
- export const fetchDisplayNamePath = async (propertyId, rootObject, fetchObject) => {
32
- const propertyInfo = await traversePropertyPath(propertyId, rootObject, fetchObject);
33
- return propertyInfo ? propertyInfo.name : '';
34
- };
35
- /**
36
- * stores full dot-notation path to each property ID in the given array of properties.
37
- *
38
- * @param {ObjectProperty[]} properties - The array of properties to update.
39
- * @param {string} parentPath - The parent path to attach to each property ID.
40
- * @returns {ObjectProperty[]} The updated array of properties with the parent path attached to each property ID.
41
- */
42
- export const setIdPaths = (properties, parentPath) => {
43
- return properties.map((prop) => {
44
- const fullPath = parentPath ? `${parentPath}.${prop.id}` : prop.id;
45
- return {
46
- ...prop,
47
- id: fullPath,
48
- };
49
- });
50
- };
51
- /**
52
- * Traverses a property path within an object hierarchy to retrieve detailed property information.
53
- *
54
- * @param {string} propertyPath - The dot-separated path of the property to traverse.
55
- * @param {Obj} rootObject - The root object from which to start the traversal.
56
- * @param {FetchObjectFunction} fetchObject - A function to fetch an object by its ID.
57
- * @returns {Promise<ObjectProperty | null>} A promise that resolves to an ObjectProperty if found, or null otherwise.
58
- */
59
- export const traversePropertyPath = async (propertyPath, rootObject, fetchObject) => {
60
- const segments = propertyPath.split('.');
61
- let currentObject = rootObject;
62
- let fullPath = '';
63
- let namePath = '';
64
- for (let i = 0; i < segments.length; i++) {
65
- const remainingPath = segments.slice(i).join('.');
66
- let prop = currentObject.properties?.find((p) => p.id === remainingPath);
67
- if (prop) {
68
- // flattened address or user properties
69
- fullPath = fullPath ? `${fullPath}.${remainingPath}` : remainingPath;
70
- namePath = namePath ? `${namePath} / ${prop.name}` : prop.name;
71
- return {
72
- ...prop,
73
- id: fullPath,
74
- name: namePath,
75
- };
76
- }
77
- else {
78
- prop = currentObject.properties?.find((p) => p.id === segments[i]);
79
- if (!prop) {
80
- return null;
81
- }
82
- fullPath = fullPath ? `${fullPath}.${prop.id}` : prop.id;
83
- namePath = namePath ? `${namePath} / ${prop.name}` : prop.name;
84
- if (i === segments.length - 1) {
85
- return {
86
- ...prop,
87
- id: fullPath,
88
- name: namePath,
89
- };
90
- }
91
- if (prop.type === 'object' && prop.objectId) {
92
- const fetchedObject = await fetchObject(prop.objectId);
93
- if (fetchedObject) {
94
- currentObject = fetchedObject;
95
- }
96
- else {
97
- return null;
98
- }
99
- }
100
- }
101
- }
102
- return null;
103
- };
23
+ export const convertTreeViewPropertyToTreeItem = (property) => ({
24
+ id: property.id,
25
+ label: property.name,
26
+ value: property.id,
27
+ type: property.type,
28
+ objectId: property.objectId,
29
+ children: property.children?.map(convertTreeViewPropertyToTreeItem),
30
+ });
104
31
  /**
105
32
  * Truncates the name path if it exceeds the specified character limit.
106
33
  *
@@ -343,9 +270,11 @@ export const findTreeItemById = (nodes, nodeId) => {
343
270
  for (const node of nodes) {
344
271
  if (node.id === nodeId)
345
272
  return node;
346
- const found = node.children && findTreeItemById(node.children, nodeId);
347
- if (found)
348
- return found;
273
+ if (nodeId.startsWith(node.id)) {
274
+ const found = node.children && findTreeItemById(node.children, nodeId);
275
+ if (found)
276
+ return found;
277
+ }
349
278
  }
350
279
  return null;
351
280
  };
@@ -43,7 +43,7 @@ export const Document = (props) => {
43
43
  // For 'file' type properties, check permissions on the sys__file object
44
44
  // For 'document' type properties, check document attachment permissions
45
45
  const endpoint = property.type === 'file'
46
- ? getPrefixedUrl(`/objects/sys__file/instances/${instance.id}/checkAccess?action=update`)
46
+ ? getPrefixedUrl('/objects/sys__file/instances/checkAccess?action=execute&field=_create')
47
47
  : getPrefixedUrl(`/objects/${objectId}/instances/${instance.id}/documents/checkAccess?action=update`);
48
48
  apiServices
49
49
  .get(endpoint)
@@ -64,12 +64,15 @@ export const DocumentList = (props) => {
64
64
  }, []);
65
65
  const checkPermissions = () => {
66
66
  if (instance?.[property.id]?.length) {
67
- // For 'file' type properties, check permissions on the sys__file object
67
+ // For 'file' type properties, check regular object instance permissions
68
68
  // For 'document' type properties, check document attachment permissions
69
69
  const endpoint = property.type === 'file'
70
- ? getPrefixedUrl(`/objects/sys__file/instances/${instance.id}/checkAccess?action=view`)
70
+ ? getPrefixedUrl(`/objects/sys__file/instances/checkAccess?action=read&field=content`)
71
71
  : getPrefixedUrl(`/objects/${objectId}/instances/${instance.id}/documents/checkAccess?action=view`);
72
- apiServices.get(endpoint).then((accessCheck) => setHasViewPermission(accessCheck.result));
72
+ apiServices
73
+ .get(endpoint)
74
+ .then((accessCheck) => setHasViewPermission(accessCheck.result))
75
+ .catch(() => setHasViewPermission(false));
73
76
  }
74
77
  };
75
78
  const isFile = (doc) => doc instanceof File;
@@ -25,6 +25,7 @@ export declare function flattenFormComponents(components?: ActionInput[]): Actio
25
25
  export declare function addObjectPropertiesToComponentProps(properties: Property[], formComponents: any[], allCriteriaInputs?: string[], instance?: ObjectInstance, objectPropertyInputProps?: ObjectPropertyInputProps, associatedObject?: {
26
26
  instanceId?: string;
27
27
  propertyId?: string;
28
+ objectId?: string;
28
29
  }, autoSave?: (data: Record<string, unknown>) => void, readOnly?: boolean, defaultPages?: Record<string, string>, navigateTo?: (path: string) => void, queryAddresses?: (query: string) => Promise<Address[]>, apiServices?: ApiServices, isModal?: boolean, fieldHeight?: 'small' | 'medium', richTextEditor?: typeof ReactComponent): Promise<ActionInput[]>;
29
30
  export declare function getDefaultValue(initialValue: unknown, selectOptions?: AutocompleteOption[]): unknown;
30
31
  export declare const buildComponentPropsFromObjectProperties: (properties: Property[], objectId: string, instance?: ObjectInstance, objectPropertyInputProps?: ObjectPropertyInputProps, hasActionPermissions?: boolean, autoSave?: ((data: Record<string, unknown>) => void) | undefined, readOnly?: boolean, queryAddresses?: ((query: string) => Promise<Address[]>) | undefined, isModal?: boolean, fieldHeight?: 'small' | 'medium', richTextEditor?: typeof ReactComponent) => unknown[];
@@ -21,6 +21,7 @@ export type FormRendererProps = BaseProps & {
21
21
  associatedObject?: {
22
22
  instanceId: string;
23
23
  propertyId: string;
24
+ objectId?: string;
24
25
  };
25
26
  renderHeader?: (props: HeaderProps) => React.ReactNode;
26
27
  renderBody?: (props: BodyProps) => React.ReactNode;
@@ -31,6 +31,7 @@ export type FormRendererContainerProps = BaseProps & {
31
31
  associatedObject?: {
32
32
  instanceId: string;
33
33
  propertyId: string;
34
+ objectId?: string;
34
35
  };
35
36
  renderContainer?: (state: FormRendererState) => React.ReactNode;
36
37
  renderHeader?: FormRendererProps['renderHeader'];
@@ -158,7 +158,7 @@ function FormRendererContainer(props) {
158
158
  });
159
159
  if (navigationSlug) {
160
160
  if (navigationSlug.includes(':instanceId')) {
161
- const navigateInstanceId = action?.type === 'create' ? updatedInstance?.id : instanceId;
161
+ const navigateInstanceId = action?.type === 'create' ? updatedInstance.id : instanceId;
162
162
  navigateTo(`/${appId}/${navigationSlug.replace(':instanceId', navigateInstanceId ?? ':instanceId')}`);
163
163
  }
164
164
  else {
@@ -167,18 +167,17 @@ function FormRendererContainer(props) {
167
167
  }
168
168
  setInstance(updatedInstance);
169
169
  };
170
+ /**
171
+ * Manually links any newly uploaded files in the submission to the specified instance.
172
+ * @param submission The form submission data
173
+ * @param linkTo The instance to link the files to
174
+ */
170
175
  const linkFiles = async (submission, linkTo) => {
171
- // Create file links for any uploaded files after instance creation
176
+ // Create file links for any uploaded files that haven't been linked yet
172
177
  for (const property of sanitizedObject?.properties?.filter((property) => property.type === 'file') ?? []) {
173
178
  const files = submission[property.id];
174
179
  if (files?.length) {
175
- try {
176
- await createFileLinks(files, linkTo, apiServices);
177
- }
178
- catch (error) {
179
- console.error('Failed to create file links:', error);
180
- // Don't fail the entire submission if file linking fails
181
- }
180
+ await createFileLinks(files, linkTo, apiServices);
182
181
  }
183
182
  }
184
183
  };
@@ -195,11 +194,9 @@ function FormRendererContainer(props) {
195
194
  ?.filter((property) => property.formula || property.type === 'collection')
196
195
  .map((property) => property.id) ?? []),
197
196
  });
198
- if (response) {
199
- // Manually link files to created instance.
200
- await linkFiles(submission, { id: response.id, objectId: form.objectId });
201
- onSubmissionSuccess(response);
202
- }
197
+ // Manually link files to created instance.
198
+ await linkFiles(submission, { id: response.id, objectId: form.objectId });
199
+ onSubmissionSuccess(response);
203
200
  }
204
201
  else if (instanceId && action) {
205
202
  const response = await objectStore.instanceAction(instanceId, {
@@ -209,21 +206,26 @@ function FormRendererContainer(props) {
209
206
  .map((property) => property.id) ?? []),
210
207
  });
211
208
  if (sanitizedObject && instance) {
209
+ if (!onAutosave) {
210
+ // For non-autosave updates, link any uploaded files to the instance.
211
+ await linkFiles(submission, { id: instanceId, objectId: objectId });
212
+ }
212
213
  onSubmissionSuccess(response);
213
- deleteDocuments(submission, true, apiServices, sanitizedObject, instance, action, setSnackbarError);
214
+ // Only delete the necessary files after submission succeeds to avoid deleting a file prematurely.
215
+ await deleteDocuments(submission, true, apiServices, sanitizedObject, instance, action, setSnackbarError);
214
216
  }
215
217
  }
216
218
  }
217
219
  catch (error) {
218
- // Handle deleteDocuments for uploaded documents if the main submission fails
219
- if (instanceId && action && sanitizedObject && instance) {
220
- deleteDocuments(submission, false, apiServices, sanitizedObject, instance, action, setSnackbarError);
221
- }
222
220
  setSnackbarError({
223
221
  isError: true,
224
222
  showAlert: true,
225
223
  message: error.response?.data?.error?.message ?? 'An error occurred',
226
224
  });
225
+ if (instanceId && action && sanitizedObject && instance) {
226
+ // For an update, uploaded documents have been linked to the instance and need to be deleted.
227
+ await deleteDocuments(submission, false, apiServices, sanitizedObject, instance, action, setSnackbarError);
228
+ }
227
229
  throw error; // Throw error so caller knows submission failed
228
230
  }
229
231
  };
@@ -255,10 +257,10 @@ function FormRendererContainer(props) {
255
257
  const parameter = parameters?.find((param) => param.id === fieldId);
256
258
  if (associatedObject?.propertyId === fieldId &&
257
259
  associatedObject?.instanceId &&
258
- parameter &&
260
+ (parameter || associatedObject.objectId) &&
259
261
  action?.type === 'create') {
260
262
  try {
261
- const instance = await apiServices.get(getPrefixedUrl(`/objects/${parameter.objectId}/instances/${associatedObject.instanceId}`));
263
+ const instance = await apiServices.get(getPrefixedUrl(`/objects/${parameter?.objectId || associatedObject.objectId}/instances/${associatedObject.instanceId}`));
262
264
  result[associatedObject.propertyId] = instance;
263
265
  }
264
266
  catch (error) {
@@ -327,7 +329,7 @@ function FormRendererContainer(props) {
327
329
  try {
328
330
  setIsSaving(true);
329
331
  const cleanedData = removeUneditedProtectedValues(formDataRef.current);
330
- const submission = await formatSubmission(cleanedData, apiServices, objectId, instanceId, form, setSnackbarError);
332
+ const submission = await formatSubmission(cleanedData, apiServices, objectId, instanceId, form, setSnackbarError, undefined, parameters);
331
333
  // Handle object instance autosave
332
334
  if (instanceId && action?.type === 'update') {
333
335
  await apiServices.post(getPrefixedUrl(`/objects/${objectId}/instances/${instanceId}/actions`), {
@@ -336,6 +338,7 @@ function FormRendererContainer(props) {
336
338
  ?.filter((property) => !property.formula && property.type !== 'collection')
337
339
  .map((property) => property.id) ?? []),
338
340
  });
341
+ await linkFiles(submission, { id: instanceId, objectId });
339
342
  }
340
343
  setLastSavedData(cloneDeep(formDataRef.current));
341
344
  setIsSaving(false);
@@ -23,6 +23,7 @@ type FormContextType = {
23
23
  associatedObject?: {
24
24
  instanceId: string;
25
25
  propertyId: string;
26
+ objectId?: string;
26
27
  };
27
28
  form?: EvokeForm;
28
29
  width: number;
@@ -13,6 +13,7 @@ export type ActionDialogProps = {
13
13
  associatedObject?: {
14
14
  instanceId: string;
15
15
  propertyId: string;
16
+ objectId?: string;
16
17
  };
17
18
  };
18
19
  export declare const ActionDialog: (props: ActionDialogProps) => React.JSX.Element;
@@ -1,12 +1,13 @@
1
1
  import { DocumentParameterValidation } from '@evoke-platform/context';
2
2
  import React from 'react';
3
- import { SavedDocumentReference } from '../../types';
3
+ import { DocumentReference } from '../../types';
4
4
  type DocumentProps = {
5
5
  id: string;
6
+ fieldType?: 'file' | 'document';
6
7
  canUpdateProperty: boolean;
7
8
  error: boolean;
8
9
  validate?: DocumentParameterValidation;
9
- value: (File | SavedDocumentReference)[] | undefined;
10
+ value: (File | DocumentReference)[] | undefined;
10
11
  hasDescription?: boolean;
11
12
  };
12
13
  export declare const Document: (props: DocumentProps) => React.JSX.Element;