@evoke-platform/ui-components 1.4.0-testing.1 → 1.4.0-testing.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.js +1 -17
- package/dist/published/components/custom/CriteriaBuilder/ValueEditor.js +23 -8
- package/dist/published/components/custom/CriteriaBuilder/index.d.ts +2 -1
- package/dist/published/components/custom/CriteriaBuilder/index.js +2 -1
- package/dist/published/components/custom/CriteriaBuilder/utils.d.ts +13 -0
- package/dist/published/components/custom/CriteriaBuilder/utils.js +58 -1
- package/dist/published/components/custom/Form/Common/Form.js +20 -21
- package/dist/published/components/custom/Form/Common/FormComponentWrapper.js +2 -1
- package/dist/published/components/custom/Form/FormComponents/ObjectComponent/ObjectComponent.d.ts +2 -0
- package/dist/published/components/custom/Form/FormComponents/ObjectComponent/ObjectComponent.js +75 -26
- package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/ActionDialog.d.ts +5 -2
- package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/ActionDialog.js +4 -6
- package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/RepeatableField.js +3 -1
- package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/RepeatableFieldComponent.js +31 -13
- package/dist/published/components/custom/Form/tests/Form.test.d.ts +1 -0
- package/dist/published/components/custom/Form/tests/Form.test.js +112 -0
- package/dist/published/components/custom/Form/tests/test-data.d.ts +13 -0
- package/dist/published/components/custom/Form/tests/test-data.js +282 -0
- package/dist/published/components/custom/Form/utils.d.ts +10 -4
- package/dist/published/components/custom/Form/utils.js +133 -52
- package/dist/published/components/custom/HistoryLog/DisplayedProperty.d.ts +2 -1
- package/dist/published/components/custom/HistoryLog/DisplayedProperty.js +5 -2
- package/dist/published/components/custom/HistoryLog/HistoryData.d.ts +1 -0
- package/dist/published/components/custom/HistoryLog/HistoryData.js +9 -3
- package/dist/published/components/custom/HistoryLog/index.js +24 -2
- package/dist/published/components/custom/index.d.ts +1 -1
- package/dist/published/components/custom/index.js +1 -1
- package/dist/published/index.d.ts +1 -1
- package/dist/published/index.js +1 -1
- package/package.json +2 -3
@@ -12,23 +12,8 @@ import { Box } from '../../layout';
|
|
12
12
|
import { OverflowTextField } from '../OverflowTextField';
|
13
13
|
import { difference } from '../util';
|
14
14
|
import PropertyTree from './PropertyTree';
|
15
|
-
import { parseMongoDB, traversePropertyPath } from './utils';
|
15
|
+
import { ALL_OPERATORS, parseMongoDB, traversePropertyPath } from './utils';
|
16
16
|
import ValueEditor from './ValueEditor';
|
17
|
-
const ALL_OPERATORS = [
|
18
|
-
{ name: '=', label: 'Is' },
|
19
|
-
{ name: '!=', label: 'Is not' },
|
20
|
-
{ name: '<', label: 'Less than' },
|
21
|
-
{ name: '>', label: 'Greater than' },
|
22
|
-
{ name: '<=', label: 'Less than or equal to' },
|
23
|
-
{ name: '>=', label: 'Greater than or equal to' },
|
24
|
-
{ name: 'contains', label: 'Contains' },
|
25
|
-
{ name: 'beginsWith', label: 'Starts with' },
|
26
|
-
{ name: 'endsWith', label: 'Ends with' },
|
27
|
-
{ name: 'null', label: 'Is empty' },
|
28
|
-
{ name: 'notNull', label: 'Is not empty' },
|
29
|
-
{ name: 'in', label: 'In' },
|
30
|
-
{ name: 'notIn', label: 'Not in' },
|
31
|
-
];
|
32
17
|
const styles = {
|
33
18
|
buttons: {
|
34
19
|
padding: '6px 16px',
|
@@ -437,7 +422,6 @@ const CriteriaBuilder = (props) => {
|
|
437
422
|
'.ruleGroup:not(.ruleGroup .ruleGroup)': {
|
438
423
|
borderStyle: 'hidden',
|
439
424
|
background: '#fff',
|
440
|
-
maxWidth: '70vw',
|
441
425
|
},
|
442
426
|
'.ruleGroup-header': {
|
443
427
|
display: 'block',
|
@@ -1,10 +1,10 @@
|
|
1
1
|
import { Instant, LocalDate, LocalDateTime, LocalTime, ZoneId } from '@js-joda/core';
|
2
|
-
import { ClearRounded } from '@mui/icons-material';
|
2
|
+
import { ClearRounded, CodeRounded } from '@mui/icons-material';
|
3
3
|
import { Box, darken, lighten, styled } from '@mui/material';
|
4
4
|
import { TimePicker } from '@mui/x-date-pickers';
|
5
5
|
import React, { useEffect, useRef, useState } from 'react';
|
6
6
|
import { InvalidDate } from '../../../util';
|
7
|
-
import { Autocomplete, Chip, DatePicker, DateTimePicker, LocalizationProvider, Menu, MenuItem, TextField, Typography, } from '../../core';
|
7
|
+
import { Autocomplete, Chip, DatePicker, DateTimePicker, IconButton, LocalizationProvider, Menu, MenuItem, TextField, Typography, } from '../../core';
|
8
8
|
import { NumericFormat } from '../FormField/InputFieldComponent';
|
9
9
|
const GroupHeader = styled('div')(({ theme }) => ({
|
10
10
|
position: 'sticky',
|
@@ -54,6 +54,9 @@ const ValueEditor = (props) => {
|
|
54
54
|
width: '33%',
|
55
55
|
background: readOnly ? '#f4f6f8' : '#fff',
|
56
56
|
borderRadius: '8px',
|
57
|
+
'& .MuiAutocomplete-tag': {
|
58
|
+
backgroundColor: '#edeff1',
|
59
|
+
},
|
57
60
|
},
|
58
61
|
};
|
59
62
|
useEffect(() => {
|
@@ -274,14 +277,26 @@ const ValueEditor = (props) => {
|
|
274
277
|
}
|
275
278
|
};
|
276
279
|
return (React.createElement(React.Fragment, null,
|
277
|
-
isPresetValueSelected ? (React.createElement(
|
278
|
-
borderRadius: '8px',
|
279
|
-
fontSize: '14px',
|
280
|
+
isPresetValueSelected ? (React.createElement(Box, { ref: inputRef, sx: {
|
280
281
|
width: '33%',
|
281
|
-
|
282
|
-
padding: '0 5px',
|
282
|
+
display: 'flex',
|
283
283
|
justifyContent: 'space-between',
|
284
|
-
|
284
|
+
alignItems: 'center',
|
285
|
+
height: '40px',
|
286
|
+
border: readOnly ? undefined : '1px solid #d5d5d5',
|
287
|
+
borderRadius: '8px',
|
288
|
+
backgroundColor: readOnly ? '#edeff1' : '#ffffff',
|
289
|
+
} },
|
290
|
+
React.createElement(Chip, { label: presetDisplayValue, sx: {
|
291
|
+
fontSize: '14px',
|
292
|
+
margin: '6px',
|
293
|
+
backgroundColor: '#edeff1',
|
294
|
+
borderRadius: '6px',
|
295
|
+
color: '#212B36',
|
296
|
+
height: '28px',
|
297
|
+
}, icon: React.createElement(CodeRounded, { sx: { height: '18px' } }) }),
|
298
|
+
!readOnly && (React.createElement(IconButton, { onClick: clearValue, sx: { padding: '3px', margin: '3px' } },
|
299
|
+
React.createElement(ClearRounded, { fontSize: "small", sx: { color: 'rgba(0, 0, 0, 0.54)' } }))))) : (getEditor()),
|
285
300
|
!!presetValues?.length && (React.createElement(Menu, { open: openPresetValues, anchorEl: inputRef?.current, PaperProps: { sx: { borderRadius: '8px', width: inputRef?.current?.offsetWidth } }, onClose: onClose }, presetValues &&
|
286
301
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
287
302
|
presetValues.map((option) => (React.createElement(MenuItem, { ...props, onClick: () => setPresetValue(option.value.name), sx: { padding: '8px', minHeight: '25px' } },
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { Property } from '@evoke-platform/context';
|
1
2
|
import { RuleGroupType } from 'react-querybuilder';
|
2
3
|
import { ExpandedProperty, Obj, ObjectProperty } from '../../../types';
|
3
4
|
/**
|
@@ -61,4 +62,16 @@ export declare const truncateNamePath: (namePath: string, limit?: number) => str
|
|
61
62
|
* @returns {RuleGroupType} - Correctly formatted rule or rules for the query builder.
|
62
63
|
*/
|
63
64
|
export declare function parseMongoDB(mongoQuery: Record<string, unknown>): RuleGroupType;
|
65
|
+
export declare const ALL_OPERATORS: {
|
66
|
+
name: string;
|
67
|
+
label: string;
|
68
|
+
}[];
|
69
|
+
/**
|
70
|
+
* Gets a human readable representation of a MongoDB query.
|
71
|
+
*
|
72
|
+
* @param {Record<string, unknown>} [mongoQuery] - The MongoDB query
|
73
|
+
* @param {Property[]} [properties] - The object properties referenced in the query
|
74
|
+
* @returns {string} The resulting query string.
|
75
|
+
*/
|
76
|
+
export declare const getReadableQuery: (mongoQuery?: Record<string, unknown>, properties?: Property[]) => string;
|
64
77
|
export {};
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { isArray, isEmpty } from 'lodash';
|
1
|
+
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
|
*
|
@@ -302,3 +302,60 @@ export function parseMongoDB(mongoQuery) {
|
|
302
302
|
};
|
303
303
|
}
|
304
304
|
}
|
305
|
+
export const ALL_OPERATORS = [
|
306
|
+
{ name: '=', label: 'Is' },
|
307
|
+
{ name: '!=', label: 'Is not' },
|
308
|
+
{ name: '<', label: 'Less than' },
|
309
|
+
{ name: '>', label: 'Greater than' },
|
310
|
+
{ name: '<=', label: 'Less than or equal to' },
|
311
|
+
{ name: '>=', label: 'Greater than or equal to' },
|
312
|
+
{ name: 'contains', label: 'Contains' },
|
313
|
+
{ name: 'beginsWith', label: 'Starts with' },
|
314
|
+
{ name: 'endsWith', label: 'Ends with' },
|
315
|
+
{ name: 'null', label: 'Is empty' },
|
316
|
+
{ name: 'notNull', label: 'Is not empty' },
|
317
|
+
{ name: 'in', label: 'In' },
|
318
|
+
{ name: 'notIn', label: 'Not in' },
|
319
|
+
];
|
320
|
+
/**
|
321
|
+
* Gets a human readable representation of a MongoDB query.
|
322
|
+
*
|
323
|
+
* @param {Record<string, unknown>} [mongoQuery] - The MongoDB query
|
324
|
+
* @param {Property[]} [properties] - The object properties referenced in the query
|
325
|
+
* @returns {string} The resulting query string.
|
326
|
+
*/
|
327
|
+
export const getReadableQuery = (mongoQuery, properties) => {
|
328
|
+
function isPresetValue(value) {
|
329
|
+
return typeof value === 'string' && value.startsWith('{{{') && value.endsWith('}}}');
|
330
|
+
}
|
331
|
+
function parseValue(val) {
|
332
|
+
if (val && Array.isArray(val)) {
|
333
|
+
return val.map((v) => (isPresetValue(v) ? startCase(v.slice(3, -3)) : v)).join(', ');
|
334
|
+
}
|
335
|
+
else {
|
336
|
+
return isPresetValue(val) ? startCase(val.slice(3, -3)) : `${val}`;
|
337
|
+
}
|
338
|
+
}
|
339
|
+
function getOperatorLabel(operator) {
|
340
|
+
const operatorObj = ALL_OPERATORS.find((o) => o.name === operator);
|
341
|
+
const defaultLabel = operatorObj ? operatorObj.label.toLowerCase() : operator;
|
342
|
+
if (['<', '>', '<=', '>='].includes(operator)) {
|
343
|
+
return `is ${defaultLabel}`;
|
344
|
+
}
|
345
|
+
return defaultLabel;
|
346
|
+
}
|
347
|
+
function buildQueryString(rule) {
|
348
|
+
if ('combinator' in rule) {
|
349
|
+
return rule?.rules?.map(buildQueryString).filter(Boolean).join(` ${rule.combinator.toLowerCase()} `);
|
350
|
+
}
|
351
|
+
else {
|
352
|
+
const property = properties?.find((p) => p.id === rule.field);
|
353
|
+
return `${property?.name ?? rule.field} ${getOperatorLabel(rule.operator)} ${parseValue(rule.value)}`;
|
354
|
+
}
|
355
|
+
}
|
356
|
+
if (!mongoQuery) {
|
357
|
+
return '';
|
358
|
+
}
|
359
|
+
const parsedQuery = parseMongoDB(mongoQuery);
|
360
|
+
return buildQueryString(parsedQuery);
|
361
|
+
};
|
@@ -1,14 +1,14 @@
|
|
1
1
|
import { useApp, useAuthenticationContext, } from '@evoke-platform/context';
|
2
2
|
import { Components, Form as FormIO, Utils } from '@formio/react';
|
3
3
|
import { flatten } from 'flat';
|
4
|
-
import { isEmpty, isEqual, toPairs } from 'lodash';
|
4
|
+
import { isEmpty, isEqual, isObject, pick, toPairs } from 'lodash';
|
5
5
|
import React, { useEffect, useRef, useState } from 'react';
|
6
6
|
import '../../../../styles/form-component.css';
|
7
7
|
import { Skeleton, Snackbar } from '../../../core';
|
8
8
|
import { Box } from '../../../layout';
|
9
9
|
import { ButtonComponent, DocumentComponent, FormFieldComponent, ImageComponent, ObjectComponent, RepeatableFieldComponent, UserComponent, ViewOnlyComponent, } from '../FormComponents';
|
10
10
|
import { CriteriaComponent } from '../FormComponents/CriteriaComponent/CriteriaComponent';
|
11
|
-
import { addObjectPropertiesToComponentProps, buildComponentPropsFromDocumentProperties, buildComponentPropsFromObjectProperties, convertFormToComponents, getFlattenEntries, getPrefixedUrl, } from '../utils';
|
11
|
+
import { addObjectPropertiesToComponentProps, buildComponentPropsFromDocumentProperties, buildComponentPropsFromObjectProperties, convertFormToComponents, flattenFormComponents, getAllCriteriaInputs, getFlattenEntries, getPrefixedUrl, } from '../utils';
|
12
12
|
const usePrevious = (value) => {
|
13
13
|
const ref = useRef();
|
14
14
|
useEffect(() => {
|
@@ -101,24 +101,16 @@ export function Form(props) {
|
|
101
101
|
const buildComponents = async () => {
|
102
102
|
const action = object?.actions?.find((action) => action.id === actionId);
|
103
103
|
let input;
|
104
|
-
|
105
|
-
? action.parameters.filter((param) => param.id !== associatedObject.propertyId)
|
106
|
-
: action?.parameters;
|
107
|
-
if (parameters && object) {
|
104
|
+
if (action?.parameters && object) {
|
108
105
|
input = action?.form?.entries
|
109
|
-
? convertFormToComponents(action.form.entries, parameters, object)
|
110
|
-
: parameters.filter((param) => object.properties?.some((prop) => prop.id === param.id));
|
106
|
+
? convertFormToComponents(action.form.entries, action.parameters, object)
|
107
|
+
: action.parameters.filter((param) => object.properties?.some((prop) => prop.id === param.id));
|
111
108
|
}
|
112
109
|
else {
|
113
110
|
input = action?.inputProperties ?? (action?.type === 'delete' ? [] : undefined);
|
114
111
|
}
|
115
|
-
let visibleObjectProperties = object?.properties;
|
116
|
-
if (associatedObject) {
|
117
|
-
// Eliminates the associated object's field from the form
|
118
|
-
visibleObjectProperties = visibleObjectProperties?.filter((property) => property.id !== associatedObject.propertyId);
|
119
|
-
}
|
120
112
|
let foundDefaultPages = defaultPages;
|
121
|
-
const relatedObjectProperties =
|
113
|
+
const relatedObjectProperties = object?.properties?.filter((property) => property.type === 'object');
|
122
114
|
if (relatedObjectProperties) {
|
123
115
|
foundDefaultPages = await relatedObjectProperties.reduce(async (acc, property) => {
|
124
116
|
const result = await acc;
|
@@ -132,23 +124,25 @@ export function Form(props) {
|
|
132
124
|
}, Promise.resolve({}));
|
133
125
|
}
|
134
126
|
const allDefaultPages = { ...defaultPages, ...foundDefaultPages };
|
135
|
-
|
127
|
+
// visibleObjectProperties
|
128
|
+
if (input && object?.properties) {
|
129
|
+
const allCriteriaInputs = getAllCriteriaInputs(action?.inputProperties ? flattenFormComponents(action.inputProperties) : action?.parameters ?? []);
|
136
130
|
if (input.length || action?.type !== 'delete') {
|
137
131
|
// formIO builder-configured input properties exist
|
138
|
-
const newComponentProps = await addObjectPropertiesToComponentProps(
|
132
|
+
const newComponentProps = await addObjectPropertiesToComponentProps(object.properties, input, allCriteriaInputs, instance, {
|
139
133
|
...objectInputCommonProps,
|
140
134
|
defaultPages: allDefaultPages,
|
141
135
|
navigateTo,
|
142
136
|
apiServices,
|
143
137
|
user: userAccount,
|
144
|
-
}, undefined, isReadOnly, allDefaultPages, navigateTo, queryAddresses, apiServices, !!closeModal, fieldHeight, richTextEditor);
|
138
|
+
}, associatedObject, undefined, isReadOnly, allDefaultPages, navigateTo, queryAddresses, apiServices, !!closeModal, fieldHeight, richTextEditor);
|
145
139
|
if (!hideButtons && !isReadOnly) {
|
146
140
|
newComponentProps.push(BottomButtons);
|
147
141
|
}
|
148
142
|
setComponentProps(newComponentProps);
|
149
143
|
}
|
150
144
|
else {
|
151
|
-
const components = await addObjectPropertiesToComponentProps(
|
145
|
+
const components = await addObjectPropertiesToComponentProps(object.properties, [
|
152
146
|
{
|
153
147
|
html: `<p>${action?.type === 'delete' ? 'This action cannot be undone.' : 'Are you sure?'}</p>`,
|
154
148
|
label: 'Content',
|
@@ -216,13 +210,13 @@ export function Form(props) {
|
|
216
210
|
addons: [],
|
217
211
|
id: 'eahbwo',
|
218
212
|
},
|
219
|
-
], instance, {
|
213
|
+
], undefined, instance, {
|
220
214
|
...objectInputCommonProps,
|
221
215
|
defaultPages: allDefaultPages,
|
222
216
|
navigateTo,
|
223
217
|
apiServices,
|
224
218
|
user: userAccount,
|
225
|
-
}, undefined, undefined, undefined, undefined, undefined, undefined, !!closeModal, fieldHeight, richTextEditor);
|
219
|
+
}, undefined, undefined, undefined, undefined, undefined, undefined, undefined, !!closeModal, fieldHeight, richTextEditor);
|
226
220
|
if (!hideButtons) {
|
227
221
|
components.push(BottomButtons);
|
228
222
|
}
|
@@ -343,7 +337,12 @@ export function Form(props) {
|
|
343
337
|
submittedFields[field] = null;
|
344
338
|
}
|
345
339
|
else {
|
346
|
-
|
340
|
+
if (isObject(value) && 'id' in value && 'name' in value) {
|
341
|
+
submittedFields[field] = pick(value, 'id', 'name');
|
342
|
+
}
|
343
|
+
else {
|
344
|
+
submittedFields[field] = value;
|
345
|
+
}
|
347
346
|
}
|
348
347
|
}
|
349
348
|
//OPTIMIZATION TODO: See if type can be inferred from the event target
|
@@ -81,7 +81,8 @@ export const FormComponentWrapper = (props) => {
|
|
81
81
|
property && onChange(property.id, '');
|
82
82
|
} },
|
83
83
|
React.createElement(HighlightOffOutlined, { sx: clearBtnStyles }))))),
|
84
|
-
React.createElement(
|
84
|
+
React.createElement(Box, { sx: { ...(displayOption === 'radioButton' && { display: 'flex' }) } },
|
85
|
+
React.createElement(Typography, { variant: "caption", sx: descriptionStyles }, description)),
|
85
86
|
React.createElement(Box, { sx: { display: 'flex', flexDirection: 'row' } },
|
86
87
|
React.createElement(PrefixSuffix, { prefix: prefix, height: fieldHeight }),
|
87
88
|
React.createElement(Box, { sx: { width: '100%', paddingTop: '6px' } }, children),
|
package/dist/published/components/custom/Form/FormComponents/ObjectComponent/ObjectComponent.d.ts
CHANGED
@@ -6,6 +6,7 @@ interface ObjectComponentProps extends BaseFormComponentProps {
|
|
6
6
|
mode: 'default' | 'existingOnly';
|
7
7
|
defaultValueCriteria?: Record<string, unknown>;
|
8
8
|
initialValue?: string;
|
9
|
+
allCriteriaInputs: string[];
|
9
10
|
richTextEditor?: typeof ReactComponent;
|
10
11
|
}
|
11
12
|
export declare class ObjectComponent extends ReactComponent {
|
@@ -20,6 +21,7 @@ export declare class ObjectComponent extends ReactComponent {
|
|
20
21
|
updatedDefaultValueCriteria: Record<string, unknown>;
|
21
22
|
constructor(component: ObjectComponentProps, options: any, data: any);
|
22
23
|
init(): void;
|
24
|
+
expandInstance(): Promise<void>;
|
23
25
|
clearErrors(): void;
|
24
26
|
handleValidation(): void;
|
25
27
|
hasErrors(): boolean;
|
package/dist/published/components/custom/Form/FormComponents/ObjectComponent/ObjectComponent.js
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
import { ReactComponent } from '@formio/react';
|
2
|
-
import
|
3
|
-
import { cloneDeep, isEmpty, pick } from 'lodash';
|
2
|
+
import { cloneDeep, isEmpty, isNil, isObject, pick } from 'lodash';
|
4
3
|
import React from 'react';
|
5
4
|
import ReactDOM from 'react-dom';
|
6
5
|
import { FormComponentWrapper } from '../../Common';
|
7
|
-
import {
|
6
|
+
import { getCriteriaInputs, getPrefixedUrl, populateInstanceWithNestedData, transformToWhere, updateCriteriaInputs, } from '../../utils';
|
8
7
|
import { ObjectPropertyInput } from './ObjectPropertyInput';
|
9
8
|
export class ObjectComponent extends ReactComponent {
|
10
9
|
constructor(component, options, data) {
|
@@ -17,9 +16,9 @@ export class ObjectComponent extends ReactComponent {
|
|
17
16
|
delete this.errorDetails['api-error'];
|
18
17
|
const updatedValue = pick(value, 'id', 'name');
|
19
18
|
// set the value on the form instance at this.root.data
|
20
|
-
this.setValue(updatedValue
|
19
|
+
this.setValue(!isNil(updatedValue) ? updatedValue : '');
|
21
20
|
// update the value in the component instance
|
22
|
-
this.updateValue(updatedValue
|
21
|
+
this.updateValue(!isNil(updatedValue) ? updatedValue : {}, { modified: true });
|
23
22
|
this.handleValidation();
|
24
23
|
this.emit('changed-' + this.component.key, value);
|
25
24
|
this.attach(this.element);
|
@@ -33,25 +32,44 @@ export class ObjectComponent extends ReactComponent {
|
|
33
32
|
this.handleChangeObjectProperty = this.handleChangeObjectProperty.bind(this);
|
34
33
|
}
|
35
34
|
init() {
|
36
|
-
const data = dot.dot(this.root._data);
|
37
35
|
if (this.criteria) {
|
38
|
-
const inputProps =
|
36
|
+
const inputProps = getCriteriaInputs(this.criteria);
|
37
|
+
this.updatedCriteria = updateCriteriaInputs(this.criteria, this.root._data, this.component.user);
|
39
38
|
for (const inputProp of inputProps) {
|
40
|
-
// Parse data to update criteria when form is loaded.
|
41
|
-
updateCriteriaInputs(this.updatedCriteria, inputProp, data[inputProp], true);
|
42
39
|
// Parse data to update criteria when form field is updated
|
43
40
|
// Need to parse all fields again.
|
44
41
|
const compKeyFragments = inputProp.split('.');
|
45
42
|
let compKey = compKeyFragments[0];
|
46
|
-
|
43
|
+
const property = this.component.properties.find((c) => c.id === compKey);
|
44
|
+
if (property?.type === 'address' &&
|
45
|
+
['line1', 'line2', 'city', 'state', 'zipCode'].includes(compKeyFragments[1])) {
|
47
46
|
compKey = inputProp;
|
48
47
|
}
|
49
|
-
this.on(`changed-${compKey}`, () => {
|
50
|
-
const data =
|
51
|
-
|
52
|
-
|
53
|
-
|
48
|
+
this.on(`changed-${compKey}`, async (value) => {
|
49
|
+
const data = this.root._data;
|
50
|
+
if (property?.type === 'object' && isObject(value) && 'id' in value && 'objectId' in value) {
|
51
|
+
const paths = this.component.allCriteriaInputs
|
52
|
+
.filter((input) => input.split('.')[0] === compKey)
|
53
|
+
.map((p) => p.split('.').slice(1).join('.'));
|
54
|
+
let instance = value;
|
55
|
+
if (!isEmpty(paths)) {
|
56
|
+
instance = await populateInstanceWithNestedData(value['id'], value['objectId'], paths, this.component.apiServices);
|
57
|
+
}
|
58
|
+
data[compKey] = instance;
|
54
59
|
}
|
60
|
+
else {
|
61
|
+
if (compKey.includes('.')) {
|
62
|
+
const keyFragments = compKey.split('.');
|
63
|
+
if (!data[keyFragments[0]]) {
|
64
|
+
data[keyFragments[0]] = {};
|
65
|
+
}
|
66
|
+
data[keyFragments[0]][keyFragments[1]] = value;
|
67
|
+
}
|
68
|
+
else {
|
69
|
+
data[compKey] = value;
|
70
|
+
}
|
71
|
+
}
|
72
|
+
this.updatedCriteria = updateCriteriaInputs(this.criteria ?? {}, data, this.component.user);
|
55
73
|
if (this.visible) {
|
56
74
|
this.attachReact(this.element);
|
57
75
|
}
|
@@ -59,25 +77,43 @@ export class ObjectComponent extends ReactComponent {
|
|
59
77
|
}
|
60
78
|
}
|
61
79
|
if (this.defaultValueCriteria) {
|
62
|
-
const inputProps =
|
63
|
-
updateCriteriaInputs(this.
|
80
|
+
const inputProps = getCriteriaInputs(this.defaultValueCriteria);
|
81
|
+
this.updatedDefaultValueCriteria = updateCriteriaInputs(this.defaultValueCriteria, this.root._data, this.component.user);
|
64
82
|
for (const inputProp of inputProps) {
|
65
|
-
// Parse data to update criteria when form is loaded.
|
66
|
-
updateCriteriaInputs(this.updatedDefaultValueCriteria, inputProp, data[inputProp], true);
|
67
83
|
// Parse data to update criteria when form field is updated
|
68
84
|
// Need to parse all fields again.
|
69
85
|
const compKeyFragments = inputProp.split('.');
|
70
86
|
let compKey = compKeyFragments[0];
|
71
|
-
|
87
|
+
const property = this.component.properties.find((c) => c.id === compKey);
|
88
|
+
if (property?.type === 'address' &&
|
89
|
+
['line1', 'line2', 'city', 'state', 'zipCode'].includes(compKeyFragments[1])) {
|
72
90
|
compKey = inputProp;
|
73
91
|
}
|
74
|
-
this.on(`changed-${compKey}`, () => {
|
75
|
-
const data =
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
92
|
+
this.on(`changed-${compKey}`, async (value) => {
|
93
|
+
const data = this.root._data;
|
94
|
+
if (property?.type === 'object' && isObject(value) && 'id' in value && 'objectId' in value) {
|
95
|
+
const paths = this.component.allCriteriaInputs
|
96
|
+
.filter((input) => input.split('.')[0] === compKey)
|
97
|
+
.map((p) => p.split('.').slice(1).join('.'));
|
98
|
+
let instance = value;
|
99
|
+
if (!isEmpty(paths)) {
|
100
|
+
instance = await populateInstanceWithNestedData(value['id'], value['objectId'], paths, this.component.apiServices);
|
101
|
+
}
|
102
|
+
data[compKey] = instance;
|
103
|
+
}
|
104
|
+
else {
|
105
|
+
if (compKey.includes('.')) {
|
106
|
+
const keyFragments = compKey.split('.');
|
107
|
+
if (!data[keyFragments[0]]) {
|
108
|
+
data[keyFragments[0]] = {};
|
109
|
+
}
|
110
|
+
data[keyFragments[0]][keyFragments[1]] = value;
|
111
|
+
}
|
112
|
+
else {
|
113
|
+
data[compKey] = value;
|
114
|
+
}
|
80
115
|
}
|
116
|
+
this.updatedDefaultValueCriteria = updateCriteriaInputs(this.defaultValueCriteria ?? {}, data, this.component.user);
|
81
117
|
if (this.visible) {
|
82
118
|
this.attachReact(this.element);
|
83
119
|
}
|
@@ -124,6 +160,19 @@ export class ObjectComponent extends ReactComponent {
|
|
124
160
|
this.attach(this.element);
|
125
161
|
this.attachReact(this.element);
|
126
162
|
});
|
163
|
+
if (this.component.defaultValue) {
|
164
|
+
this.expandInstance();
|
165
|
+
}
|
166
|
+
}
|
167
|
+
async expandInstance() {
|
168
|
+
const { property, apiServices, defaultValue } = this.component;
|
169
|
+
const paths = this.component.allCriteriaInputs
|
170
|
+
.filter((input) => input.split('.')[0] === this.component.key)
|
171
|
+
.map((p) => p.split('.').slice(1).join('.'));
|
172
|
+
if (!isEmpty(paths)) {
|
173
|
+
const instance = await populateInstanceWithNestedData(defaultValue.id, property.objectId, paths, apiServices);
|
174
|
+
this.handleChangeObjectProperty(property.id, instance);
|
175
|
+
}
|
127
176
|
}
|
128
177
|
clearErrors() {
|
129
178
|
this.errorDetails = {};
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Action, ApiServices, Obj,
|
1
|
+
import { Action, ApiServices, Obj, UserAccount } from '@evoke-platform/context';
|
2
2
|
import React from 'react';
|
3
3
|
import { Address, ObjectPropertyInputProps } from '../../types';
|
4
4
|
export type ActionDialogProps = {
|
@@ -13,9 +13,12 @@ export type ActionDialogProps = {
|
|
13
13
|
object: Obj;
|
14
14
|
objectInputCommonProps: ObjectPropertyInputProps;
|
15
15
|
instanceId?: string;
|
16
|
-
relatedProperty?: Property;
|
17
16
|
apiServices: ApiServices;
|
18
17
|
queryAddresses?: (query: string) => Promise<Address[]>;
|
19
18
|
user?: UserAccount;
|
19
|
+
associatedObject?: {
|
20
|
+
instanceId: string;
|
21
|
+
propertyId: string;
|
22
|
+
};
|
20
23
|
};
|
21
24
|
export declare const ActionDialog: (props: ActionDialogProps) => React.JSX.Element;
|
@@ -36,7 +36,7 @@ const styles = {
|
|
36
36
|
},
|
37
37
|
};
|
38
38
|
export const ActionDialog = (props) => {
|
39
|
-
const { open, onClose, action, instanceInput, handleSubmit, apiServices, object, instanceId,
|
39
|
+
const { open, onClose, action, instanceInput, handleSubmit, apiServices, object, instanceId, objectInputCommonProps, queryAddresses, associatedObject, user, } = props;
|
40
40
|
const [updatedObject, setUpdatedObject] = useState();
|
41
41
|
const [hasAccess, setHasAccess] = useState(false);
|
42
42
|
const [loading, setLoading] = useState(false);
|
@@ -57,18 +57,16 @@ export const ActionDialog = (props) => {
|
|
57
57
|
}, [object, instanceId]);
|
58
58
|
useEffect(() => {
|
59
59
|
const input = (action.form?.entries && action.parameters) || action?.inputProperties
|
60
|
-
?
|
60
|
+
? action.form?.entries && action.parameters
|
61
61
|
? convertFormToComponents(action.form.entries, action.parameters, object)
|
62
|
-
: action?.inputProperties
|
62
|
+
: action?.inputProperties
|
63
63
|
: undefined;
|
64
64
|
const updatedAction = {
|
65
65
|
...action,
|
66
66
|
form: input ? { entries: convertComponentsToForm(input) } : undefined,
|
67
67
|
};
|
68
|
-
const properties = object.properties?.filter((prop) => prop.id !== relatedProperty?.relatedPropertyId);
|
69
68
|
setUpdatedObject({
|
70
69
|
...object,
|
71
|
-
properties,
|
72
70
|
actions: concat(object.actions?.filter((a) => a.id !== action.id) ?? [], [updatedAction]),
|
73
71
|
});
|
74
72
|
}, [object]);
|
@@ -78,7 +76,7 @@ export const ActionDialog = (props) => {
|
|
78
76
|
React.createElement(IconButton, { sx: styles.closeIcon, onClick: onClose },
|
79
77
|
React.createElement(Close, { fontSize: "small" })),
|
80
78
|
action && hasAccess && !loading ? action?.name : ''),
|
81
|
-
React.createElement(DialogContent, null, hasAccess ? (React.createElement(Box, { sx: { width: '100%', marginTop: '10px' } }, (updatedObject || isDeleteAction) && (React.createElement(Form, { actionId: action.id, actionType: action.type, apiServices: objectInputCommonProps.apiServices, object: !isDeleteAction ? updatedObject : object, instance: instanceInput, onSave: async (data, setSubmitting) => handleSubmit(action.type, data, instanceId, setSubmitting), objectInputCommonProps: objectInputCommonProps, closeModal: onClose, queryAddresses: queryAddresses, user: user, submitButtonLabel: isDeleteAction ? 'Delete' : undefined })))) : (React.createElement(React.Fragment, null, loading ? (React.createElement(React.Fragment, null,
|
79
|
+
React.createElement(DialogContent, null, hasAccess ? (React.createElement(Box, { sx: { width: '100%', marginTop: '10px' } }, (updatedObject || isDeleteAction) && (React.createElement(Form, { actionId: action.id, actionType: action.type, apiServices: objectInputCommonProps.apiServices, object: !isDeleteAction ? updatedObject : object, instance: instanceInput, onSave: async (data, setSubmitting) => handleSubmit(action.type, data, instanceId, setSubmitting), objectInputCommonProps: objectInputCommonProps, closeModal: onClose, queryAddresses: queryAddresses, user: user, submitButtonLabel: isDeleteAction ? 'Delete' : undefined, associatedObject: associatedObject })))) : (React.createElement(React.Fragment, null, loading ? (React.createElement(React.Fragment, null,
|
82
80
|
React.createElement(Skeleton, { height: '30px', animation: 'wave' }),
|
83
81
|
React.createElement(Skeleton, { height: '30px', animation: 'wave' }),
|
84
82
|
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' } })))))));
|
@@ -405,7 +405,9 @@ const RepeatableField = (props) => {
|
|
405
405
|
relatedObject && openDialog && (React.createElement(ActionDialog, { object: relatedObject, open: openDialog, apiServices: apiServices, onClose: () => setOpenDialog(false), instanceInput: dialogType === 'update' ? relatedInstances.find((i) => i.id === selectedRow) ?? {} : {}, handleSubmit: save,
|
406
406
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
407
407
|
objectInputCommonProps: { apiServices }, action: relatedObject?.actions?.find((a) => a.id ===
|
408
|
-
(dialogType === 'create' ? '_create' : dialogType === 'update' ? '_update' : '_delete')), instanceId: selectedRow,
|
408
|
+
(dialogType === 'create' ? '_create' : dialogType === 'update' ? '_update' : '_delete')), instanceId: selectedRow, queryAddresses: queryAddresses, user: user, associatedObject: instance.id && property.relatedPropertyId
|
409
|
+
? { instanceId: instance.id, propertyId: property.relatedPropertyId }
|
410
|
+
: undefined })),
|
409
411
|
React.createElement(Snackbar, { open: snackbarError.showAlert, handleClose: () => setSnackbarError({ isError: snackbarError.isError, showAlert: false }), message: snackbarError.message, error: snackbarError.isError })));
|
410
412
|
};
|
411
413
|
export default RepeatableField;
|
@@ -1,11 +1,10 @@
|
|
1
1
|
import { ApiBaseUrlProvider, NotificationProvider, } from '@evoke-platform/context';
|
2
2
|
import { ReactComponent } from '@formio/react';
|
3
|
-
import
|
4
|
-
import { cloneDeep } from 'lodash';
|
3
|
+
import { cloneDeep, isEmpty, isObject } from 'lodash';
|
5
4
|
import React from 'react';
|
6
5
|
import ReactDOM from 'react-dom';
|
7
6
|
import { FormComponentWrapper } from '../../Common';
|
8
|
-
import {
|
7
|
+
import { getCriteriaInputs, populateInstanceWithNestedData, updateCriteriaInputs } from '../../utils';
|
9
8
|
import { DropdownRepeatableField } from './ManyToMany/DropdownRepeatableField';
|
10
9
|
import RepeatableField from './RepeatableField';
|
11
10
|
const apiBaseUrl = process.env.REACT_APP_API_ROOT || `${window.location.origin}/api`;
|
@@ -23,24 +22,43 @@ export class RepeatableFieldComponent extends ReactComponent {
|
|
23
22
|
}
|
24
23
|
init() {
|
25
24
|
if (this.criteria) {
|
26
|
-
const inputProps =
|
27
|
-
|
25
|
+
const inputProps = getCriteriaInputs(this.criteria);
|
26
|
+
this.updatedCriteria = updateCriteriaInputs(this.updatedCriteria, this.root._data, this.component.user);
|
28
27
|
for (const inputProp of inputProps) {
|
29
|
-
// Parse data to update criteria when form is loaded.
|
30
|
-
updateCriteriaInputs(this.updatedCriteria, inputProp, data[inputProp], true);
|
31
28
|
// Parse data to update criteria when form field is updated
|
32
29
|
// Need to parse all fields again.
|
33
30
|
const compKeyFragments = inputProp.split('.');
|
34
31
|
let compKey = compKeyFragments[0];
|
35
|
-
|
32
|
+
const property = this.component.properties.find((c) => c.id === compKey);
|
33
|
+
if (property?.type === 'address' &&
|
34
|
+
['line1', 'line2', 'city', 'state', 'zipCode'].includes(compKeyFragments[1])) {
|
36
35
|
compKey = inputProp;
|
37
36
|
}
|
38
|
-
this.on(`changed-${compKey}`, () => {
|
39
|
-
const data =
|
40
|
-
|
41
|
-
|
42
|
-
|
37
|
+
this.on(`changed-${compKey}`, async (value) => {
|
38
|
+
const data = this.root._data;
|
39
|
+
if (property?.type === 'object' && isObject(value) && 'id' in value && 'objectId' in value) {
|
40
|
+
const paths = this.component.allCriteriaInputs
|
41
|
+
.filter((input) => input.split('.')[0] === compKey)
|
42
|
+
.map((p) => p.split('.').slice(1).join('.'));
|
43
|
+
let instance = value;
|
44
|
+
if (!isEmpty(paths)) {
|
45
|
+
instance = await populateInstanceWithNestedData(value['id'], value['objectId'], paths, this.component.apiServices);
|
46
|
+
}
|
47
|
+
data[compKey] = instance;
|
43
48
|
}
|
49
|
+
else {
|
50
|
+
if (compKey.includes('.')) {
|
51
|
+
const keyFragments = compKey.split('.');
|
52
|
+
if (!data[keyFragments[0]]) {
|
53
|
+
data[keyFragments[0]] = {};
|
54
|
+
}
|
55
|
+
data[keyFragments[0]][keyFragments[1]] = value;
|
56
|
+
}
|
57
|
+
else {
|
58
|
+
data[compKey] = value;
|
59
|
+
}
|
60
|
+
}
|
61
|
+
this.updatedCriteria = updateCriteriaInputs(this.criteria ?? {}, data, this.component.user);
|
44
62
|
this.attachReact(this.element);
|
45
63
|
});
|
46
64
|
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|