@evoke-platform/ui-components 1.6.0-dev.23 → 1.6.0-dev.24
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/components/AccordionSections.d.ts +4 -0
- package/dist/published/components/custom/FormV2/components/AccordionSections.js +131 -0
- package/dist/published/components/custom/FormV2/components/FieldWrapper.d.ts +1 -1
- package/dist/published/components/custom/FormV2/components/FieldWrapper.js +1 -1
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/UserProperty.d.ts +2 -2
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/UserProperty.js +5 -1
- package/dist/published/components/custom/FormV2/components/FormSections.d.ts +4 -0
- package/dist/published/components/custom/FormV2/components/FormSections.js +104 -0
- package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.d.ts +2 -0
- package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.js +209 -0
- package/dist/published/components/custom/FormV2/components/TabNav.d.ts +2 -2
- package/dist/published/components/custom/FormV2/components/TabNav.js +2 -2
- package/dist/published/components/custom/FormV2/components/types.d.ts +37 -2
- package/dist/published/components/custom/FormV2/components/utils.d.ts +5 -2
- package/dist/published/components/custom/FormV2/components/utils.js +104 -0
- package/package.json +1 -1
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { ExpandMoreOutlined } from '@mui/icons-material';
|
|
2
|
+
import { nanoid } from 'nanoid';
|
|
3
|
+
import React, { useEffect } from 'react';
|
|
4
|
+
import { useResponsive } from '../../../../theme';
|
|
5
|
+
import { Accordion, AccordionDetails, AccordionSummary, Typography } from '../../../core';
|
|
6
|
+
import { Box } from '../../../layout';
|
|
7
|
+
import { RecursiveEntryRenderer } from './RecursiveEntryRenderer';
|
|
8
|
+
import { getErrorCountForSection } from './utils';
|
|
9
|
+
function AccordionSections(props) {
|
|
10
|
+
const { entry, handleChange, fieldHeight, richTextEditor, instance, expandedSections, setExpandedSections, expandAll, setExpandAll, errors, parameters, triggerFieldReset, showSubmitError, } = props;
|
|
11
|
+
const { isMd, isLg, isXl } = useResponsive();
|
|
12
|
+
const lastSection = entry.sections.length - 1;
|
|
13
|
+
const sectionsWithIds = React.useMemo(() => assignIds(entry.sections), [entry]);
|
|
14
|
+
function collectNestedSections(entries = []) {
|
|
15
|
+
const nestedSections = [];
|
|
16
|
+
entries.forEach((entry) => {
|
|
17
|
+
if (entry.type === 'sections') {
|
|
18
|
+
nestedSections.push(entry);
|
|
19
|
+
}
|
|
20
|
+
else if (entry.type === 'columns') {
|
|
21
|
+
const columnEntry = entry;
|
|
22
|
+
columnEntry.columns.forEach((column) => {
|
|
23
|
+
const columnSections = collectNestedSections(column.entries);
|
|
24
|
+
nestedSections.push(...columnSections);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
return nestedSections;
|
|
29
|
+
}
|
|
30
|
+
// need to add ids to section so expanded sections can differentiate between sections with the same label
|
|
31
|
+
function assignIds(sections) {
|
|
32
|
+
const result = [];
|
|
33
|
+
sections.forEach((section) => {
|
|
34
|
+
const newSection = {
|
|
35
|
+
...section,
|
|
36
|
+
id: nanoid(),
|
|
37
|
+
};
|
|
38
|
+
result.push(newSection);
|
|
39
|
+
if (section.entries) {
|
|
40
|
+
const nestedSections = collectNestedSections(section.entries);
|
|
41
|
+
nestedSections.forEach((nestedSection) => {
|
|
42
|
+
nestedSection.sections = assignIds(nestedSection.sections);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
function getExpandedSections(sections, expandAll) {
|
|
49
|
+
const expandedSections = [];
|
|
50
|
+
const processSections = (sectionList) => {
|
|
51
|
+
sectionList.forEach((section, index) => {
|
|
52
|
+
expandedSections.push({
|
|
53
|
+
label: section.label,
|
|
54
|
+
expanded: expandAll ?? (index === 0 || isMd || isLg || isXl),
|
|
55
|
+
id: section?.id,
|
|
56
|
+
});
|
|
57
|
+
if (section.entries) {
|
|
58
|
+
const nestedSections = collectNestedSections(section.entries).flatMap((s) => s.sections);
|
|
59
|
+
processSections(nestedSections);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
processSections(sections);
|
|
64
|
+
return expandedSections;
|
|
65
|
+
}
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (expandAll !== null) {
|
|
68
|
+
setExpandedSections(getExpandedSections(sectionsWithIds, expandAll));
|
|
69
|
+
}
|
|
70
|
+
}, [expandAll, sectionsWithIds]);
|
|
71
|
+
const handleAccordionChange = (id) => {
|
|
72
|
+
const updatedSections = expandedSections.map((section) => section.id === id ? { ...section, expanded: !section.expanded } : section);
|
|
73
|
+
setExpandedSections(updatedSections);
|
|
74
|
+
setExpandAll(null);
|
|
75
|
+
};
|
|
76
|
+
return (React.createElement(Box, null, sectionsWithIds.map((section, sectionIndex) => {
|
|
77
|
+
const errorCount = getErrorCountForSection(section, errors);
|
|
78
|
+
return (React.createElement(Accordion, { key: section.id, expanded: expandedSections.find((expandedSection) => expandedSection.id === section.id)?.expanded ??
|
|
79
|
+
!!expandAll, onChange: () => handleAccordionChange(section.id), defaultExpanded: sectionIndex === 0, sx: {
|
|
80
|
+
border: '1px solid #dbe0e4',
|
|
81
|
+
boxShadow: 'none',
|
|
82
|
+
marginBottom: '16px',
|
|
83
|
+
borderRadius: '6px',
|
|
84
|
+
'&:before': {
|
|
85
|
+
display: 'none',
|
|
86
|
+
},
|
|
87
|
+
} },
|
|
88
|
+
React.createElement(AccordionSummary, { sx: {
|
|
89
|
+
'&.Mui-expanded': {
|
|
90
|
+
borderBottom: '1px solid #dbe0e4',
|
|
91
|
+
minHeight: '44px',
|
|
92
|
+
borderBottomLeftRadius: '0px',
|
|
93
|
+
borderBottomRightRadius: '0px',
|
|
94
|
+
},
|
|
95
|
+
minHeight: '44px',
|
|
96
|
+
maxHeight: '44px',
|
|
97
|
+
backgroundColor: '#F4F6F8',
|
|
98
|
+
borderRadius: '5px',
|
|
99
|
+
// MUI accordion summaries have different border radius for the first and last item
|
|
100
|
+
borderTopLeftRadius: sectionIndex === 0 ? '3px' : undefined,
|
|
101
|
+
borderTopRightRadius: sectionIndex === 0 ? '3px' : undefined,
|
|
102
|
+
borderBottomRightRadius: sectionIndex === lastSection ? '3px' : undefined,
|
|
103
|
+
borderBottomLeftRadius: sectionIndex === lastSection ? '3px' : undefined,
|
|
104
|
+
}, expandIcon: React.createElement(ExpandMoreOutlined, { fontSize: "medium" }) },
|
|
105
|
+
React.createElement(Box, { sx: {
|
|
106
|
+
display: 'flex',
|
|
107
|
+
alignItems: 'center',
|
|
108
|
+
width: '100%',
|
|
109
|
+
justifyContent: 'space-between',
|
|
110
|
+
} },
|
|
111
|
+
React.createElement(Typography, { sx: {
|
|
112
|
+
fontWeight: '600',
|
|
113
|
+
} }, section.label),
|
|
114
|
+
errorCount > 0 && showSubmitError && (React.createElement(Box, { sx: {
|
|
115
|
+
ml: 1,
|
|
116
|
+
bgcolor: '#FF4842',
|
|
117
|
+
color: 'white',
|
|
118
|
+
borderRadius: '50%',
|
|
119
|
+
minWidth: '20px',
|
|
120
|
+
minHeight: '20px',
|
|
121
|
+
display: 'flex',
|
|
122
|
+
justifyContent: 'center',
|
|
123
|
+
alignItems: 'center',
|
|
124
|
+
fontSize: '12px',
|
|
125
|
+
margin: '0px',
|
|
126
|
+
marginRight: '16px',
|
|
127
|
+
} }, errorCount)))),
|
|
128
|
+
React.createElement(AccordionDetails, null, section.entries?.map((sectionEntry, index) => (React.createElement(RecursiveEntryRenderer, { key: sectionEntry.type + index, entry: sectionEntry, handleChange: handleChange, errors: errors, fieldHeight: fieldHeight, richTextEditor: richTextEditor, instance: instance, expandedSections: expandedSections, setExpandedSections: setExpandedSections, expandAll: expandAll, setExpandAll: setExpandAll, parameters: parameters, triggerFieldReset: triggerFieldReset, showSubmitError: showSubmitError }))))));
|
|
129
|
+
})));
|
|
130
|
+
}
|
|
131
|
+
export default AccordionSections;
|
|
@@ -49,7 +49,7 @@ const PrefixSuffix = (props) => {
|
|
|
49
49
|
*/
|
|
50
50
|
const FieldWrapper = (props) => {
|
|
51
51
|
const { inputId, label, description, tooltip, prefix, suffix, value, maxLength, errorMessage, showCharCount, inputType, viewOnly, children, fieldHeight, required, } = props;
|
|
52
|
-
const charCount = value ? value.length : 0;
|
|
52
|
+
const charCount = typeof value === 'string' ? value.length : 0;
|
|
53
53
|
const remainingChars = maxLength ? maxLength - charCount : undefined;
|
|
54
54
|
return (React.createElement(Box, null,
|
|
55
55
|
React.createElement(Box, { sx: { padding: '10px 0' } },
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { UserAccount } from '@evoke-platform/context';
|
|
1
2
|
import React from 'react';
|
|
2
|
-
import { AutocompleteOption } from '../../../../core';
|
|
3
3
|
export type UserPropertyProps = {
|
|
4
4
|
id: string;
|
|
5
|
-
|
|
5
|
+
handleChange: (id: string, user: UserAccount | undefined) => void;
|
|
6
6
|
error?: boolean;
|
|
7
7
|
value?: {
|
|
8
8
|
id: string;
|
|
@@ -5,7 +5,7 @@ import { useFormContext } from '../../../../../theme/hooks';
|
|
|
5
5
|
import { Autocomplete, Paper, TextField, Typography } from '../../../../core';
|
|
6
6
|
import { getPrefixedUrl, isOptionEqualToValue } from '../utils';
|
|
7
7
|
const UserProperty = (props) => {
|
|
8
|
-
const { id,
|
|
8
|
+
const { id, handleChange, error, value, fieldHeight, readOnly, hasDescription } = props;
|
|
9
9
|
const { fetchedOptions, setFetchedOptions } = useFormContext();
|
|
10
10
|
const [loadingOptions, setLoadingOptions] = useState(false);
|
|
11
11
|
const apiServices = useApiServices();
|
|
@@ -40,6 +40,10 @@ const UserProperty = (props) => {
|
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
42
|
}, [id]);
|
|
43
|
+
function handleChangeUserProperty(id, value) {
|
|
44
|
+
const updatedValue = typeof value.value === 'string' ? { name: value.label, id: value.value } : undefined;
|
|
45
|
+
handleChange(id, updatedValue);
|
|
46
|
+
}
|
|
43
47
|
return (options && (React.createElement(Autocomplete, { id: id, fullWidth: true, open: openOptions, popupIcon: userValue || readOnly ? '' : React.createElement(ExpandMore, null), PaperComponent: ({ children }) => {
|
|
44
48
|
return (React.createElement(Paper, { sx: {
|
|
45
49
|
borderRadius: '12px',
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import RadioButtonCheckedIcon from '@mui/icons-material/RadioButtonChecked';
|
|
2
|
+
import { TabContext, TabPanel } from '@mui/lab';
|
|
3
|
+
import React, { useRef, useState } from 'react';
|
|
4
|
+
import { Tab, Tabs, Typography } from '../../../core';
|
|
5
|
+
import { Box } from '../../../layout';
|
|
6
|
+
import { RecursiveEntryRenderer } from './RecursiveEntryRenderer';
|
|
7
|
+
import TabNav from './TabNav';
|
|
8
|
+
import { getErrorCountForSection, scrollIntoViewWithOffset } from './utils';
|
|
9
|
+
function FormSections(props) {
|
|
10
|
+
const { entry, errors, handleChange, showSubmitError, fieldHeight, richTextEditor, instance, expandedSections, setExpandedSections, expandAll, setExpandAll, parameters, triggerFieldReset, } = props;
|
|
11
|
+
const tabPanelsRef = useRef(null);
|
|
12
|
+
const [tabValue, setTabValue] = useState(0);
|
|
13
|
+
const handleTabChange = (type, newValue) => {
|
|
14
|
+
setTabValue(Number(newValue));
|
|
15
|
+
if (tabPanelsRef.current && type === 'buttonNav') {
|
|
16
|
+
scrollIntoViewWithOffset(tabPanelsRef.current, 170);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const prevTabLabel = tabValue > 0 ? entry.sections[tabValue - 1].label : '';
|
|
20
|
+
const nextTabLabel = tabValue < entry.sections.length - 1 ? entry.sections[tabValue + 1].label : '';
|
|
21
|
+
return (React.createElement(React.Fragment, null,
|
|
22
|
+
React.createElement(TabContext, { value: tabValue?.toString() || '0' },
|
|
23
|
+
React.createElement(Box, { sx: {
|
|
24
|
+
display: 'flex',
|
|
25
|
+
minHeight: '150px',
|
|
26
|
+
} },
|
|
27
|
+
React.createElement(Tabs, { value: tabValue?.toString(), onChange: (e, value) => handleTabChange('tabNav', value), orientation: "vertical", sx: {
|
|
28
|
+
overflow: 'visible',
|
|
29
|
+
paddingTop: '10px',
|
|
30
|
+
flex: 1,
|
|
31
|
+
borderRight: '1px solid #e9ecef',
|
|
32
|
+
minWidth: '190px',
|
|
33
|
+
maxWidth: '264px',
|
|
34
|
+
}, TabIndicatorProps: {
|
|
35
|
+
sx: {
|
|
36
|
+
display: 'none',
|
|
37
|
+
},
|
|
38
|
+
} }, entry.sections.map((section, sectionIndex) => {
|
|
39
|
+
const errorCount = getErrorCountForSection(section, errors);
|
|
40
|
+
const isSelected = tabValue?.toString() === sectionIndex.toString();
|
|
41
|
+
return (React.createElement(Tab, { disableRipple: true, key: section.label + sectionIndex, label: React.createElement(Box, { sx: {
|
|
42
|
+
display: 'flex',
|
|
43
|
+
alignItems: 'center',
|
|
44
|
+
width: '100%',
|
|
45
|
+
minWidth: '168px',
|
|
46
|
+
justifyContent: 'space-between',
|
|
47
|
+
} },
|
|
48
|
+
React.createElement(Box, { sx: { display: 'flex', alignItems: 'center' } },
|
|
49
|
+
React.createElement(Box, { sx: { width: '28px', display: 'flex' } }, isSelected ? (React.createElement(RadioButtonCheckedIcon, { color: errorCount > 0 && showSubmitError ? 'error' : 'primary', sx: {
|
|
50
|
+
fontSize: 'medium',
|
|
51
|
+
} })) : (React.createElement(Box, { sx: {
|
|
52
|
+
width: '8px',
|
|
53
|
+
height: '8px',
|
|
54
|
+
borderRadius: '50%',
|
|
55
|
+
backgroundColor: '#dfe3e8',
|
|
56
|
+
marginLeft: '4.25px',
|
|
57
|
+
} }))),
|
|
58
|
+
React.createElement(Typography, { sx: {
|
|
59
|
+
fontSize: '16px',
|
|
60
|
+
fontWeight: isSelected ? 'bold' : 'normal',
|
|
61
|
+
} }, section.label)),
|
|
62
|
+
errorCount > 0 && showSubmitError && (React.createElement(Box, { sx: {
|
|
63
|
+
ml: 1,
|
|
64
|
+
bgcolor: '#FF4842',
|
|
65
|
+
color: 'white',
|
|
66
|
+
borderRadius: '50%',
|
|
67
|
+
minWidth: '20px',
|
|
68
|
+
minHeight: '20px',
|
|
69
|
+
display: 'flex',
|
|
70
|
+
justifyContent: 'center',
|
|
71
|
+
alignItems: 'center',
|
|
72
|
+
fontSize: '12px',
|
|
73
|
+
margin: '0px',
|
|
74
|
+
marginRight: '16px',
|
|
75
|
+
} }, errorCount))), value: sectionIndex.toString(), tabIndex: -1, sx: {
|
|
76
|
+
textAlign: 'left',
|
|
77
|
+
display: 'flex',
|
|
78
|
+
textTransform: 'none',
|
|
79
|
+
color: '#212B36',
|
|
80
|
+
'&.Mui-selected': {
|
|
81
|
+
fontWeight: 'bold',
|
|
82
|
+
color: '#212B36',
|
|
83
|
+
},
|
|
84
|
+
padding: '14px 10px 14px 4px',
|
|
85
|
+
} }));
|
|
86
|
+
})),
|
|
87
|
+
entry.sections.map((section, sectionIndex) => (React.createElement(TabPanel, { key: section.label + sectionIndex, value: sectionIndex.toString(), ref: tabPanelsRef, sx: {
|
|
88
|
+
padding: '10px 20px 0px 40px',
|
|
89
|
+
width: '100%',
|
|
90
|
+
flex: 4,
|
|
91
|
+
} },
|
|
92
|
+
React.createElement(Box, { sx: {
|
|
93
|
+
display: 'flex',
|
|
94
|
+
flexDirection: 'column',
|
|
95
|
+
justifyContent: section.entries && section?.entries.length > 0 ? 'space-between' : 'flex-end',
|
|
96
|
+
height: '100%',
|
|
97
|
+
} },
|
|
98
|
+
React.createElement(Box, null, section.entries &&
|
|
99
|
+
section.entries.map((sectionEntry, index) => {
|
|
100
|
+
return (React.createElement(RecursiveEntryRenderer, { key: sectionEntry.type + index, entry: sectionEntry, handleChange: handleChange, errors: errors, showSubmitError: showSubmitError, fieldHeight: fieldHeight, richTextEditor: richTextEditor, instance: instance, expandedSections: expandedSections, setExpandedSections: setExpandedSections, expandAll: expandAll, setExpandAll: setExpandAll, parameters: parameters, triggerFieldReset: triggerFieldReset }));
|
|
101
|
+
})),
|
|
102
|
+
React.createElement(TabNav, { prevTabLabel: prevTabLabel, nextTabLabel: nextTabLabel, numberOfTabs: entry.sections.length, tabValue: tabValue, handleTabChange: handleTabChange })))))))));
|
|
103
|
+
}
|
|
104
|
+
export default FormSections;
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { useApiServices, useAuthenticationContext, } from '@evoke-platform/context';
|
|
2
|
+
import { WarningRounded } from '@mui/icons-material';
|
|
3
|
+
import { cloneDeep } from 'lodash';
|
|
4
|
+
import React, { useEffect, useMemo } from 'react';
|
|
5
|
+
import { useResponsive } from '../../../../theme';
|
|
6
|
+
import { useFormContext } from '../../../../theme/hooks';
|
|
7
|
+
import { TextField, Typography } from '../../../core';
|
|
8
|
+
import { Box } from '../../../layout';
|
|
9
|
+
import FormField from '../../FormField';
|
|
10
|
+
import AccordionSections from './AccordionSections';
|
|
11
|
+
import FieldWrapper from './FieldWrapper';
|
|
12
|
+
import AddressFields from './FormFieldTypes/AddressFields';
|
|
13
|
+
import DropdownRepeatableField from './FormFieldTypes/CollectionFiles/DropdownRepeatableField';
|
|
14
|
+
import RepeatableField from './FormFieldTypes/CollectionFiles/RepeatableField';
|
|
15
|
+
import Criteria from './FormFieldTypes/Criteria';
|
|
16
|
+
import { Document } from './FormFieldTypes/DocumentFiles/Document';
|
|
17
|
+
import { Image } from './FormFieldTypes/Image';
|
|
18
|
+
import ObjectPropertyInput from './FormFieldTypes/relatedObjectFiles/ObjectPropertyInput';
|
|
19
|
+
import UserProperty from './FormFieldTypes/UserProperty';
|
|
20
|
+
import FormSections from './FormSections';
|
|
21
|
+
import { entryIsVisible, fetchCollectionData, getEntryId, isAddressProperty, isOptionEqualToValue, updateCriteriaInputs, } from './utils';
|
|
22
|
+
function getFieldWrapperProps(fieldDefinition, entry, entryId, fieldValue, display, fieldHeight, errors, validation) {
|
|
23
|
+
return {
|
|
24
|
+
inputId: entryId,
|
|
25
|
+
inputType: fieldDefinition.type,
|
|
26
|
+
label: display?.label || fieldDefinition.name || 'default',
|
|
27
|
+
description: display?.description,
|
|
28
|
+
tooltip: display?.tooltip,
|
|
29
|
+
value: fieldValue,
|
|
30
|
+
maxLength: validation && 'maxLength' in validation ? validation.maxLength : 0,
|
|
31
|
+
required: entry.display?.required || false,
|
|
32
|
+
showCharCount: display?.charCount,
|
|
33
|
+
prefix: display?.prefix,
|
|
34
|
+
suffix: display?.suffix,
|
|
35
|
+
viewOnly: entry.type === 'readonlyField',
|
|
36
|
+
fieldHeight,
|
|
37
|
+
errorMessage: errors?.[entryId]?.message,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function RecursiveEntryRenderer(props) {
|
|
41
|
+
const { handleChange, errors, showSubmitError, fieldHeight, instance, richTextEditor, expandedSections, setExpandedSections, expandAll, setExpandAll, parameters, entry, triggerFieldReset, } = props;
|
|
42
|
+
const { fetchedOptions, setFetchedOptions, object, getValues } = useFormContext();
|
|
43
|
+
// If the entry is hidden, clear its value and any nested values, and skip rendering
|
|
44
|
+
if (!entryIsVisible(entry, getValues(), instance)) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const apiServices = useApiServices();
|
|
48
|
+
const userAccount = useAuthenticationContext()?.account;
|
|
49
|
+
const { smallerThan, isXs } = useResponsive();
|
|
50
|
+
const entryId = getEntryId(entry) || 'defaultId';
|
|
51
|
+
const display = 'display' in entry ? entry.display : undefined;
|
|
52
|
+
const fieldValue = entry.type === 'readonlyField' ? instance?.[entryId] : getValues(entryId);
|
|
53
|
+
const initialMiddleObjectInstances = fetchedOptions[`${entryId}InitialMiddleObjectInstances`];
|
|
54
|
+
const middleObject = fetchedOptions[`${entryId}MiddleObject`];
|
|
55
|
+
const fieldDefinition = useMemo(() => {
|
|
56
|
+
let def;
|
|
57
|
+
if (entry.type === 'input') {
|
|
58
|
+
def = parameters?.find((param) => param.id === entry.parameterId);
|
|
59
|
+
}
|
|
60
|
+
else if (entry.type === 'readonlyField') {
|
|
61
|
+
def = isAddressProperty(entry.propertyId)
|
|
62
|
+
? object?.properties?.find((prop) => prop.id === entry.propertyId.split('.')[0])
|
|
63
|
+
: object?.properties?.find((prop) => prop.id === entry.propertyId);
|
|
64
|
+
}
|
|
65
|
+
else if (entry.type === 'inputField') {
|
|
66
|
+
def = entry.input;
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
if (def?.enum && def.type === 'string') {
|
|
72
|
+
const cloned = cloneDeep(def);
|
|
73
|
+
// single select must be made to be type choices for label and error handling
|
|
74
|
+
cloned.type = 'choices';
|
|
75
|
+
return cloned;
|
|
76
|
+
}
|
|
77
|
+
return def;
|
|
78
|
+
}, [entry, parameters, object]);
|
|
79
|
+
const validation = fieldDefinition?.validation || {};
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
if (fieldDefinition?.type === 'collection' && fieldDefinition?.manyToManyPropertyId && instance) {
|
|
82
|
+
fetchCollectionData(apiServices, fieldDefinition, setFetchedOptions, instance.id, fetchedOptions, initialMiddleObjectInstances);
|
|
83
|
+
}
|
|
84
|
+
}, [fieldDefinition, instance]);
|
|
85
|
+
if (entry.type === 'content') {
|
|
86
|
+
return (React.createElement(Box, { dangerouslySetInnerHTML: { __html: entry.html }, sx: {
|
|
87
|
+
fontFamily: 'Roboto, Helvetica, Arial, sans-serif',
|
|
88
|
+
} }));
|
|
89
|
+
}
|
|
90
|
+
else if ((entry.type === 'input' || entry.type === 'readonlyField' || entry.type === 'inputField') &&
|
|
91
|
+
fieldDefinition) {
|
|
92
|
+
if (isAddressProperty(entryId)) {
|
|
93
|
+
return (React.createElement(AddressFields, { entry: entry, errors: errors, handleChange: handleChange, fieldHeight: fieldHeight, parameters: parameters, instance: instance, entryId: entryId, fieldDefinition: fieldDefinition }));
|
|
94
|
+
}
|
|
95
|
+
else if (fieldDefinition.type === 'image') {
|
|
96
|
+
return (React.createElement(FieldWrapper, { ...getFieldWrapperProps(fieldDefinition, entry, entryId, fieldValue, display, fieldHeight, errors) },
|
|
97
|
+
React.createElement(Image, { id: entryId, handleChange: handleChange, canUpdateProperty: entry.type !== 'readonlyField', error: !!errors[entryId], value: fieldValue, hasDescription: !!display?.description })));
|
|
98
|
+
}
|
|
99
|
+
else if (fieldDefinition.type === 'object') {
|
|
100
|
+
return (React.createElement(FieldWrapper, { ...getFieldWrapperProps(fieldDefinition, entry, entryId, fieldValue, display, fieldHeight, errors) },
|
|
101
|
+
React.createElement(ObjectPropertyInput, { instance: instance, fieldDefinition: fieldDefinition, id: entryId, handleChangeObjectProperty: handleChange, mode: display?.mode || 'default', error: !!errors[entryId], displayOption: display?.relatedObjectDisplay || 'dialogBox', initialValue: fieldValue, parameters: parameters, readOnly: entry.type === 'readonlyField', filter: validation?.criteria
|
|
102
|
+
? updateCriteriaInputs(validation.criteria, getValues(), userAccount)
|
|
103
|
+
: undefined, sortBy: typeof display?.defaultValue === 'object' && 'sortBy' in display.defaultValue
|
|
104
|
+
? display?.defaultValue.sortBy
|
|
105
|
+
: undefined, orderBy: typeof display?.defaultValue === 'object' && 'orderBy' in display.defaultValue
|
|
106
|
+
? display?.defaultValue.orderBy
|
|
107
|
+
: undefined, fieldHeight: fieldHeight, richTextEditor: richTextEditor, defaultValueCriteria: typeof display?.defaultValue === 'object' && 'criteria' in display.defaultValue
|
|
108
|
+
? display?.defaultValue?.criteria
|
|
109
|
+
: undefined, viewLayout: display?.viewLayout, hasDescription: !!display?.description })));
|
|
110
|
+
}
|
|
111
|
+
else if (fieldDefinition.type === 'user') {
|
|
112
|
+
return (React.createElement(FieldWrapper, { ...getFieldWrapperProps(fieldDefinition, entry, entryId, fieldValue, display, fieldHeight, errors) },
|
|
113
|
+
React.createElement(UserProperty, { id: entryId, value: fieldValue, handleChange: handleChange, error: !!errors[entryId], readOnly: entry.type === 'readonlyField', fieldHeight: fieldHeight, hasDescription: !!display?.description })));
|
|
114
|
+
}
|
|
115
|
+
else if (fieldDefinition.type === 'collection') {
|
|
116
|
+
return fieldDefinition?.manyToManyPropertyId ? (middleObject && initialMiddleObjectInstances && (React.createElement(FieldWrapper, { ...getFieldWrapperProps(fieldDefinition, entry, entryId, fieldValue, display, fieldHeight, errors) },
|
|
117
|
+
React.createElement(DropdownRepeatableField, { initialMiddleObjectInstances: fetchedOptions[`${entryId}MiddleObjectInstances`] || initialMiddleObjectInstances, fieldDefinition: fieldDefinition, id: entryId, middleObject: middleObject, instance: instance, readOnly: entry.type === 'readonlyField', fieldHeight: fieldHeight, criteria: validation?.criteria, hasDescription: !!display?.description })))) : (React.createElement(FieldWrapper, { ...getFieldWrapperProps(fieldDefinition, entry, entryId, fieldValue, display, fieldHeight, errors) },
|
|
118
|
+
React.createElement(RepeatableField, { fieldDefinition: fieldDefinition, canUpdateProperty: entry.type !== 'readonlyField', criteria: validation?.criteria, viewLayout: display?.viewLayout, instance: instance })));
|
|
119
|
+
}
|
|
120
|
+
else if (fieldDefinition.type === 'richText') {
|
|
121
|
+
return (React.createElement(FieldWrapper, { ...getFieldWrapperProps(fieldDefinition, entry, entryId, fieldValue, display, fieldHeight, errors) }, richTextEditor ? (React.createElement(richTextEditor, {
|
|
122
|
+
id: entryId,
|
|
123
|
+
value: fieldValue,
|
|
124
|
+
handleUpdate: (value) => handleChange(entryId, value),
|
|
125
|
+
format: 'rtf',
|
|
126
|
+
disabled: entry.type === 'readonlyField',
|
|
127
|
+
rows: display?.rowCount,
|
|
128
|
+
hasError: !!errors[entryId],
|
|
129
|
+
})) : (React.createElement(FormField, { id: entryId, property: fieldDefinition, defaultValue: fieldValue || getValues(entryId), onChange: handleChange, readOnly: entry.type === 'readonlyField', placeholder: display?.placeholder, error: !!errors[entryId], errorMessage: errors[entryId]?.message, isMultiLineText: !!display?.rowCount, rows: display?.rowCount, size: fieldHeight }))));
|
|
130
|
+
}
|
|
131
|
+
else if (fieldDefinition.type === 'document') {
|
|
132
|
+
return (React.createElement(FieldWrapper, { ...getFieldWrapperProps(fieldDefinition, entry, entryId, fieldValue, display, fieldHeight, errors) },
|
|
133
|
+
React.createElement(Document, { id: entryId, handleChange: handleChange, error: !!errors[entryId], value: fieldValue, instance: instance, canUpdateProperty: !(entry.type === 'readonlyField'), hasDescription: !!display?.description })));
|
|
134
|
+
}
|
|
135
|
+
else if (fieldDefinition.type === 'criteria') {
|
|
136
|
+
return (React.createElement(FieldWrapper, { ...getFieldWrapperProps(fieldDefinition, entry, entryId, fieldValue, display, fieldHeight, errors) },
|
|
137
|
+
React.createElement(Criteria, { key: triggerFieldReset ? `${entryId}-reset-true` : `${entryId}-reset-false`, fieldDefinition: fieldDefinition, value: fieldValue, handleChange: handleChange, canUpdateProperty: !(entry.type === 'readonlyField'), error: !!errors[entryId] })));
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
// Add `aria-describedby` to ensure screen readers read the description
|
|
141
|
+
// when the input is tabbed into.
|
|
142
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
143
|
+
const additionalProps = {};
|
|
144
|
+
if (fieldDefinition.enum && display?.description) {
|
|
145
|
+
additionalProps.renderInput = (params) => (React.createElement(TextField, { ...params, inputProps: {
|
|
146
|
+
...params.inputProps,
|
|
147
|
+
'aria-describedby': `${entryId}-description`,
|
|
148
|
+
} }));
|
|
149
|
+
}
|
|
150
|
+
else if (display?.description) {
|
|
151
|
+
additionalProps.inputProps = {
|
|
152
|
+
'aria-describedby': `${entryId}-description`,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
return (React.createElement(FieldWrapper
|
|
156
|
+
/*
|
|
157
|
+
* Key remounts the field if a value is changed.
|
|
158
|
+
* Needed to clear certain fields on discard changes.
|
|
159
|
+
*/
|
|
160
|
+
, {
|
|
161
|
+
/*
|
|
162
|
+
* Key remounts the field if a value is changed.
|
|
163
|
+
* Needed to clear certain fields on discard changes.
|
|
164
|
+
*/
|
|
165
|
+
key: (fieldDefinition.type === 'choices' ||
|
|
166
|
+
fieldDefinition?.type === 'time' ||
|
|
167
|
+
fieldDefinition?.type === 'boolean') &&
|
|
168
|
+
triggerFieldReset
|
|
169
|
+
? `${entryId}-reset-true`
|
|
170
|
+
: `${entryId}-reset-false`, ...getFieldWrapperProps(fieldDefinition, entry, entryId, fieldValue, display, fieldHeight, errors), errorMessage: undefined },
|
|
171
|
+
React.createElement(FormField, { id: entryId,
|
|
172
|
+
// TODO: Ideally the FormField prop should be called parameter but can't change the name for backwards compatibility reasons
|
|
173
|
+
property: fieldDefinition, defaultValue: fieldValue || getValues(entryId), onChange: handleChange, readOnly: entry.type === 'readonlyField', placeholder: display?.placeholder, mask: validation?.mask, isOptionEqualToValue: isOptionEqualToValue, error: !!errors[entryId], errorMessage: errors[entryId]?.message, isMultiLineText: !!display?.rowCount, rows: display?.rowCount, required: entry.display?.required || false, getOptionLabel: (option) => {
|
|
174
|
+
if (typeof option === 'string') {
|
|
175
|
+
return (entry?.enumWithLabels?.find((e) => e.value === option)
|
|
176
|
+
?.label ?? option);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
return (entry?.enumWithLabels?.find((e) => e.value === option.value)?.label ?? String(option.value));
|
|
180
|
+
}
|
|
181
|
+
}, size: fieldHeight, sortBy: display?.choicesDisplay?.sortBy && display.choicesDisplay.sortBy, displayOption: fieldDefinition.type === 'boolean'
|
|
182
|
+
? display?.booleanDisplay
|
|
183
|
+
: display?.choicesDisplay?.type && display.choicesDisplay.type, label: display?.label, description: display?.description, tooltip: display?.tooltip, selectOptions: entry?.enumWithLabels &&
|
|
184
|
+
entry.enumWithLabels, additionalProps: additionalProps, isCombobox: fieldDefinition.nonStrictEnum, strictlyTrue: fieldDefinition.strictlyTrue })));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
else if (entry.type === 'columns') {
|
|
188
|
+
return (React.createElement(Box, { sx: { display: 'flex', alignItems: 'flex-start', gap: '30px', flexDirection: isXs ? 'column' : 'row' } }, entry.columns.map((column, colIndex) => (
|
|
189
|
+
// calculating the width like this rather than flex={column.width} to prevent collections from being too wide
|
|
190
|
+
React.createElement(Box, { key: colIndex, sx: { width: isXs ? '100%' : `calc(${(column.width / 12) * 100}% - 15px)` } }, column.entries?.map((columnEntry, entryIndex) => {
|
|
191
|
+
return (React.createElement(RecursiveEntryRenderer, { key: entryIndex + (columnEntry?.parameterId ?? ''), entry: columnEntry, handleChange: handleChange, errors: errors, fieldHeight: fieldHeight, richTextEditor: richTextEditor, instance: instance, expandedSections: expandedSections, setExpandedSections: setExpandedSections, expandAll: expandAll, setExpandAll: setExpandAll, parameters: parameters, triggerFieldReset: triggerFieldReset }));
|
|
192
|
+
}))))));
|
|
193
|
+
}
|
|
194
|
+
else if (entry.type === 'sections') {
|
|
195
|
+
return smallerThan('md') ? (React.createElement(AccordionSections, { entry: entry, handleChange: handleChange, fieldHeight: fieldHeight, richTextEditor: richTextEditor, instance: instance, expandedSections: expandedSections, setExpandedSections: setExpandedSections, expandAll: expandAll, setExpandAll: setExpandAll, errors: errors, parameters: parameters, triggerFieldReset: triggerFieldReset })) : (React.createElement(FormSections, { entry: entry, errors: errors, handleChange: handleChange, showSubmitError: showSubmitError, fieldHeight: fieldHeight, richTextEditor: richTextEditor, instance: instance, expandedSections: expandedSections, setExpandedSections: setExpandedSections, expandAll: expandAll, setExpandAll: setExpandAll, parameters: parameters }));
|
|
196
|
+
}
|
|
197
|
+
else if (!fieldDefinition) {
|
|
198
|
+
return (React.createElement(Box, { sx: {
|
|
199
|
+
display: 'flex',
|
|
200
|
+
backgroundColor: '#ffc1073b',
|
|
201
|
+
borderRadius: '8px',
|
|
202
|
+
padding: '16.5px 14px',
|
|
203
|
+
marginTop: '6px',
|
|
204
|
+
} },
|
|
205
|
+
React.createElement(WarningRounded, { sx: { paddingRight: '8px' }, color: "warning" }),
|
|
206
|
+
React.createElement(Typography, { variant: "body2", color: "textSecondary" }, "This field was not configured correctly")));
|
|
207
|
+
}
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react';
|
|
2
2
|
type TabNavProps = {
|
|
3
3
|
numberOfTabs: number;
|
|
4
4
|
tabValue: number;
|
|
5
|
-
handleTabChange: (
|
|
5
|
+
handleTabChange: (type: 'tabNav' | 'buttonNav', newValue: number) => void;
|
|
6
6
|
prevTabLabel?: string;
|
|
7
7
|
nextTabLabel?: string;
|
|
8
8
|
};
|
|
@@ -13,10 +13,10 @@ function TabNav(props) {
|
|
|
13
13
|
paddingTop: '10px',
|
|
14
14
|
paddingBottom: '20px',
|
|
15
15
|
} },
|
|
16
|
-
!isFirstTab && (React.createElement(Button, {
|
|
16
|
+
!isFirstTab && (React.createElement(Button, { onClick: () => handleTabChange('buttonNav', tabValue - 1), sx: { color: 'black', backgroundColor: '#dfe3e8', '&:hover': { backgroundColor: '#d0d4d9' } }, "aria-label": `Navigate to previous tab ${prevTabLabel}` },
|
|
17
17
|
React.createElement(ArrowBackIosNewIcon, { sx: { fontSize: 'medium', marginRight: 1 } }),
|
|
18
18
|
prevTabLabel ?? 'Previous tab')),
|
|
19
|
-
!isLastTab && (React.createElement(Button, {
|
|
19
|
+
!isLastTab && (React.createElement(Button, { onClick: () => handleTabChange('buttonNav', tabValue + 1), sx: { color: 'black', backgroundColor: '#dfe3e8', '&:hover': { backgroundColor: '#d0d4d9' } }, "aria-label": `Navigate to next tab ${nextTabLabel}` },
|
|
20
20
|
nextTabLabel ?? 'Next tab',
|
|
21
21
|
React.createElement(ArrowForwardIosIcon, { sx: { fontSize: 'medium', marginLeft: 1 } })))));
|
|
22
22
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { InputParameter, ObjectInstance, Property, ViewLayoutEntityReference } from '@evoke-platform/context';
|
|
1
|
+
import { FormEntry, InputParameter, ObjectInstance, Property, Section, Sections, ViewLayoutEntityReference } from '@evoke-platform/context';
|
|
2
2
|
import { GridSize } from '@mui/material';
|
|
3
3
|
import { ComponentType } from 'react';
|
|
4
|
-
import { FieldValues } from 'react-hook-form';
|
|
4
|
+
import { FieldErrors, FieldValues } from 'react-hook-form';
|
|
5
5
|
export type FieldAddress = {
|
|
6
6
|
line1?: string;
|
|
7
7
|
line2?: string;
|
|
@@ -85,3 +85,38 @@ export type BaseProps = {
|
|
|
85
85
|
getWidgetState?: () => any;
|
|
86
86
|
saveWidgetState?: (widgetState: unknown) => void;
|
|
87
87
|
};
|
|
88
|
+
export type ExpandedSection = Section & {
|
|
89
|
+
id: string;
|
|
90
|
+
expanded?: boolean;
|
|
91
|
+
};
|
|
92
|
+
export type EntryRendererProps = BaseProps & {
|
|
93
|
+
entry: FormEntry;
|
|
94
|
+
errors: FieldErrors;
|
|
95
|
+
handleChange: (name: string, value: unknown) => void;
|
|
96
|
+
showSubmitError?: boolean;
|
|
97
|
+
fieldHeight?: 'small' | 'medium';
|
|
98
|
+
instance?: FieldValues;
|
|
99
|
+
richTextEditor?: ComponentType<SimpleEditorProps>;
|
|
100
|
+
expandedSections: ExpandedSection[];
|
|
101
|
+
setExpandedSections: React.Dispatch<React.SetStateAction<ExpandedSection[]>>;
|
|
102
|
+
expandAll?: boolean | undefined | null;
|
|
103
|
+
setExpandAll: React.Dispatch<React.SetStateAction<boolean | undefined | null>>;
|
|
104
|
+
parameters?: InputParameter[];
|
|
105
|
+
readOnly?: boolean;
|
|
106
|
+
triggerFieldReset?: boolean;
|
|
107
|
+
};
|
|
108
|
+
export type SectionsProps = {
|
|
109
|
+
entry: Sections;
|
|
110
|
+
handleChange: (propertyId: string, value: unknown) => void;
|
|
111
|
+
fieldHeight?: 'small' | 'medium';
|
|
112
|
+
richTextEditor?: ComponentType<SimpleEditorProps>;
|
|
113
|
+
instance?: FieldValues;
|
|
114
|
+
expandAll?: boolean | undefined | null;
|
|
115
|
+
expandedSections: ExpandedSection[];
|
|
116
|
+
setExpandedSections: React.Dispatch<React.SetStateAction<ExpandedSection[]>>;
|
|
117
|
+
setExpandAll: React.Dispatch<React.SetStateAction<boolean | undefined | null>>;
|
|
118
|
+
errors: FieldErrors;
|
|
119
|
+
parameters?: InputParameter[];
|
|
120
|
+
triggerFieldReset?: boolean;
|
|
121
|
+
showSubmitError?: boolean;
|
|
122
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Columns, FormEntry, InputParameter, Obj, ObjectInstance, Property, Sections } from '@evoke-platform/context';
|
|
1
|
+
import { ApiServices, Column, Columns, FormEntry, InputParameter, Obj, ObjectInstance, Property, Section, Sections, UserAccount } from '@evoke-platform/context';
|
|
2
2
|
import { LocalDateTime } from '@js-joda/core';
|
|
3
|
-
import { FieldValues } from 'react-hook-form';
|
|
3
|
+
import { FieldErrors, FieldValues } from 'react-hook-form';
|
|
4
4
|
import { AutocompleteOption } from '../../../core';
|
|
5
5
|
export declare const scrollIntoViewWithOffset: (el: HTMLElement, offset: number, container?: HTMLElement) => void;
|
|
6
6
|
export declare const normalizeDateTime: (dateTime: LocalDateTime) => string;
|
|
@@ -37,3 +37,6 @@ export declare const encodePageSlug: (slug: string) => string;
|
|
|
37
37
|
export declare function getDefaultPages(parameters: InputParameter[], defaultPages: Record<string, string> | undefined, findDefaultPageSlugFor: (objectId: string) => Promise<string | undefined>): Promise<{
|
|
38
38
|
[x: string]: string;
|
|
39
39
|
}>;
|
|
40
|
+
export declare function updateCriteriaInputs(criteria: Record<string, unknown>, data: Record<string, unknown>, user?: UserAccount): Record<string, unknown>;
|
|
41
|
+
export declare function fetchCollectionData(apiServices: ApiServices, fieldDefinition: InputParameter | Property, setFetchedOptions: (newData: FieldValues) => void, instanceId?: string, fetchedOptions?: Record<string, unknown>, initialMiddleObjectInstances?: ObjectInstance[]): Promise<void>;
|
|
42
|
+
export declare const getErrorCountForSection: (section: Section | Column, errors: FieldErrors) => number;
|
|
@@ -2,6 +2,8 @@ import { LocalDateTime } from '@js-joda/core';
|
|
|
2
2
|
import jsonLogic from 'json-logic-js';
|
|
3
3
|
import { get, isArray, isObject, transform } from 'lodash';
|
|
4
4
|
import { DateTime } from 'luxon';
|
|
5
|
+
import Handlebars from 'no-eval-handlebars';
|
|
6
|
+
import { defaultRuleProcessorMongoDB, formatQuery, parseMongoDB } from 'react-querybuilder';
|
|
5
7
|
export const scrollIntoViewWithOffset = (el, offset, container) => {
|
|
6
8
|
const elementRect = el.getBoundingClientRect();
|
|
7
9
|
const containerRect = container ? container.getBoundingClientRect() : document.body.getBoundingClientRect();
|
|
@@ -267,3 +269,105 @@ export async function getDefaultPages(parameters, defaultPages, findDefaultPageS
|
|
|
267
269
|
}
|
|
268
270
|
return { ...defaultPages, ...foundDefaultPages };
|
|
269
271
|
}
|
|
272
|
+
function compileQueryValues(query, data) {
|
|
273
|
+
if ('rules' in query && Array.isArray(query.rules)) {
|
|
274
|
+
const updatedRules = query.rules
|
|
275
|
+
.map((item) => compileQueryValues(item, data))
|
|
276
|
+
.filter((item) => item !== undefined);
|
|
277
|
+
if (updatedRules?.length) {
|
|
278
|
+
return {
|
|
279
|
+
...query,
|
|
280
|
+
rules: updatedRules,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
if ('value' in query && typeof query.value === 'string') {
|
|
289
|
+
const template = Handlebars.compileAST(query.value);
|
|
290
|
+
const result = template(data);
|
|
291
|
+
if (result !== '') {
|
|
292
|
+
return {
|
|
293
|
+
...query,
|
|
294
|
+
value: result,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
return undefined;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return query;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
export function updateCriteriaInputs(criteria, data, user) {
|
|
305
|
+
const dataSet = {
|
|
306
|
+
input: {
|
|
307
|
+
...data,
|
|
308
|
+
},
|
|
309
|
+
user: user,
|
|
310
|
+
};
|
|
311
|
+
const compiledQuery = compileQueryValues(parseMongoDB(criteria), dataSet);
|
|
312
|
+
// The "compiledQueryValues" function filters out rules that have a value of "undefined".
|
|
313
|
+
// If "compiledQuery" is "undefined", that means that all rules and nested rules have a value of "undefined".
|
|
314
|
+
// Therefore, the resulting query is empty.
|
|
315
|
+
return !compiledQuery
|
|
316
|
+
? {}
|
|
317
|
+
: JSON.parse(formatQuery(compiledQuery, {
|
|
318
|
+
format: 'mongodb',
|
|
319
|
+
ruleProcessor: (rule, options) => {
|
|
320
|
+
let updatedRule = rule;
|
|
321
|
+
if (['contains', 'beginsWith', 'endsWith'].includes(rule.operator)) {
|
|
322
|
+
updatedRule = { ...rule, value: escape(rule.value) };
|
|
323
|
+
}
|
|
324
|
+
return defaultRuleProcessorMongoDB(updatedRule, options);
|
|
325
|
+
},
|
|
326
|
+
}));
|
|
327
|
+
}
|
|
328
|
+
export async function fetchCollectionData(apiServices, fieldDefinition, setFetchedOptions, instanceId, fetchedOptions, initialMiddleObjectInstances) {
|
|
329
|
+
try {
|
|
330
|
+
if ((fetchedOptions && !fetchedOptions[`${fieldDefinition.id}MiddleObject`]) || !fetchedOptions) {
|
|
331
|
+
const fetchedMiddleObject = await apiServices.get(getPrefixedUrl(`/objects/${fieldDefinition.objectId}/effective?sanitizedVersion=true`), {
|
|
332
|
+
params: {
|
|
333
|
+
filter: { fields: ['properties', 'actions', 'rootObjectId'] },
|
|
334
|
+
},
|
|
335
|
+
});
|
|
336
|
+
setFetchedOptions({ [`${fieldDefinition.id}MiddleObject`]: fetchedMiddleObject });
|
|
337
|
+
}
|
|
338
|
+
// Fetch the initial middle object instances
|
|
339
|
+
const filter = instanceId ? getMiddleObjectFilter(fieldDefinition, instanceId) : {};
|
|
340
|
+
if (!isArray(initialMiddleObjectInstances)) {
|
|
341
|
+
const fetchedInitialMiddleObjectInstances = await apiServices.get(getPrefixedUrl(`/objects/${fieldDefinition.objectId}/instances`), {
|
|
342
|
+
params: { filter: JSON.stringify(filter) },
|
|
343
|
+
});
|
|
344
|
+
setFetchedOptions({
|
|
345
|
+
[`${fieldDefinition.id}InitialMiddleObjectInstances`]: fetchedInitialMiddleObjectInstances,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
catch (error) {
|
|
350
|
+
console.error('Error fetching collection data:', error);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
export const getErrorCountForSection = (section, errors) => {
|
|
354
|
+
const entries = section.entries || [];
|
|
355
|
+
return isArray(section.entries)
|
|
356
|
+
? entries.reduce((count, entry) => {
|
|
357
|
+
if (get(errors, entry.type === 'input' ? entry.parameterId : entry.type === 'inputField' ? entry.input.id : '')) {
|
|
358
|
+
count += 1;
|
|
359
|
+
}
|
|
360
|
+
else if (entry.type === 'sections') {
|
|
361
|
+
count += entry.sections.reduce((subCount, subSection) => {
|
|
362
|
+
return subCount + getErrorCountForSection(subSection, errors);
|
|
363
|
+
}, 0);
|
|
364
|
+
}
|
|
365
|
+
else if (entry.type === 'columns') {
|
|
366
|
+
count += entry.columns.reduce((colCount, column) => {
|
|
367
|
+
return colCount + getErrorCountForSection(column, errors);
|
|
368
|
+
}, 0);
|
|
369
|
+
}
|
|
370
|
+
return count;
|
|
371
|
+
}, 0)
|
|
372
|
+
: 0;
|
|
373
|
+
};
|