@evoke-platform/ui-components 1.0.0-dev.243 → 1.0.0-dev.245
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 +79 -4
- package/dist/published/components/custom/CriteriaBuilder/PropertyTree.d.ts +2 -2
- package/dist/published/components/custom/CriteriaBuilder/PropertyTree.js +2 -2
- package/dist/published/components/custom/CriteriaBuilder/ValueEditor.js +16 -3
- package/dist/published/components/custom/CriteriaBuilder/utils.d.ts +1 -1
- package/dist/published/components/custom/CriteriaBuilder/utils.js +1 -1
- package/dist/published/components/custom/Form/FormComponents/FormFieldComponent.js +22 -59
- package/dist/published/components/custom/Form/FormComponents/ObjectComponent/ObjectPropertyInput.js +2 -2
- package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/ManyToMany/DropdownRepeatableFieldInput.js +3 -6
- package/dist/published/stories/CriteriaBuilder.stories.d.ts +1 -0
- package/dist/published/stories/CriteriaBuilder.stories.js +139 -3
- package/package.json +2 -2
@@ -10,6 +10,7 @@ import { Autocomplete, Button, IconButton, TextField } from '../../core';
|
|
10
10
|
import { Box } from '../../layout';
|
11
11
|
import { difference } from '../util';
|
12
12
|
import PropertyTree from './PropertyTree';
|
13
|
+
import { traversePropertyPath } from './utils';
|
13
14
|
import ValueEditor from './ValueEditor';
|
14
15
|
const ALL_OPERATORS = [
|
15
16
|
{ name: '=', label: 'Is' },
|
@@ -107,7 +108,11 @@ const customSelector = (props) => {
|
|
107
108
|
let width = '90px';
|
108
109
|
let val = value;
|
109
110
|
let opts = options;
|
110
|
-
|
111
|
+
let inputType = props.fieldData?.inputType;
|
112
|
+
// for tree view / related object properties, the properties are stored in the propertyTreeMap upon selection
|
113
|
+
if (!!context.treeViewOpts && !!context.propertyTreeMap[rule.field]) {
|
114
|
+
inputType = context.propertyTreeMap[rule.field].type;
|
115
|
+
}
|
111
116
|
let readOnly = false;
|
112
117
|
const isTreeViewEnabled = context.treeViewOpts && title === 'Fields';
|
113
118
|
const fetchObject = context.treeViewOpts?.fetchObject;
|
@@ -120,9 +125,11 @@ const customSelector = (props) => {
|
|
120
125
|
opts = opts.filter((o) => readOnly || !keys.includes(o.name));
|
121
126
|
}
|
122
127
|
}
|
128
|
+
let placeholder = '';
|
123
129
|
switch (title) {
|
124
130
|
case 'Operators':
|
125
131
|
width = '25%';
|
132
|
+
placeholder = 'Select Operator';
|
126
133
|
if (inputType === 'array') {
|
127
134
|
opts = options
|
128
135
|
.filter((option) => ['null', 'notNull', 'in', 'notIn'].includes(option.name))
|
@@ -161,11 +168,19 @@ const customSelector = (props) => {
|
|
161
168
|
}
|
162
169
|
break;
|
163
170
|
case 'Fields':
|
171
|
+
placeholder = 'Select Property';
|
164
172
|
width = '33%';
|
165
173
|
val = options.find((option) => option.name === val)?.name;
|
166
174
|
break;
|
167
175
|
}
|
168
|
-
|
176
|
+
const handleTreePropertySelect = async (propertyId) => {
|
177
|
+
const propertyInfo = await traversePropertyPath(propertyId, object, fetchObject);
|
178
|
+
context.setPropertyTreeMap((prev) => {
|
179
|
+
return { ...prev, [propertyId]: propertyInfo };
|
180
|
+
});
|
181
|
+
handleOnChange(propertyId);
|
182
|
+
};
|
183
|
+
return (React.createElement(React.Fragment, null, isTreeViewEnabled ? (React.createElement(PropertyTree, { value: val ?? value, rootObject: object, fetchObject: fetchObject, handleTreePropertySelect: handleTreePropertySelect })) : (React.createElement(Autocomplete, { options: opts, value: val ?? null, getOptionLabel: (option) => {
|
169
184
|
if (typeof option === 'string') {
|
170
185
|
return opts.find((o) => option === o.name)?.label || '';
|
171
186
|
}
|
@@ -176,7 +191,7 @@ const customSelector = (props) => {
|
|
176
191
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
177
192
|
onChange: (event, newValue) => {
|
178
193
|
handleOnChange(newValue?.value.name);
|
179
|
-
}, renderInput: (params) => React.createElement(TextField, { ...params, size: "small" }), sx: { width: width, background: '#fff' }, disableClearable: true, readOnly: readOnly }))));
|
194
|
+
}, renderInput: (params) => React.createElement(TextField, { ...params, placeholder: placeholder, size: "small" }), sx: { width: width, background: '#fff' }, disableClearable: true, readOnly: readOnly }))));
|
180
195
|
};
|
181
196
|
const customCombinator = (props) => {
|
182
197
|
const { value, handleOnChange, context, level, path } = props;
|
@@ -243,10 +258,12 @@ export const valueEditor = (props) => {
|
|
243
258
|
const CriteriaBuilder = (props) => {
|
244
259
|
const { properties, criteria, setCriteria, originalCriteria, enablePresetValues, presetValues, operators, dynamicContentInput, disabled, disabledCriteria, hideBorder, presetGroupLabel, treeViewOpts, } = props;
|
245
260
|
const [query, setQuery] = useState(undefined);
|
261
|
+
const [propertyTreeMap, setPropertyTreeMap] = useState({});
|
246
262
|
useEffect(() => {
|
247
263
|
if (criteria || originalCriteria) {
|
248
264
|
const criteriaToParse = criteria || originalCriteria || {};
|
249
265
|
const updatedQuery = parseMongoDB(criteriaToParse);
|
266
|
+
!isEmpty(treeViewOpts) && updatePropertyTreeMap(updatedQuery);
|
250
267
|
setQuery({
|
251
268
|
...updatedQuery,
|
252
269
|
rules: processRules(updatedQuery.rules),
|
@@ -276,10 +293,40 @@ const CriteriaBuilder = (props) => {
|
|
276
293
|
}
|
277
294
|
});
|
278
295
|
}
|
296
|
+
// this retrieves the properties from a treeview for each property in the query
|
297
|
+
// they are then used in the custom query builder components to determine the input type etc
|
298
|
+
const updatePropertyTreeMap = (q) => {
|
299
|
+
const ids = [];
|
300
|
+
const traverseRulesForIds = (rules) => {
|
301
|
+
rules.forEach((rule) => {
|
302
|
+
if ('rules' in rule) {
|
303
|
+
traverseRulesForIds(rule.rules);
|
304
|
+
}
|
305
|
+
else {
|
306
|
+
ids.push(rule.field);
|
307
|
+
}
|
308
|
+
});
|
309
|
+
};
|
310
|
+
traverseRulesForIds(q.rules);
|
311
|
+
const tempPropertyMap = { ...propertyTreeMap };
|
312
|
+
ids.forEach(async (id) => {
|
313
|
+
if (!propertyTreeMap[id] && treeViewOpts?.object && treeViewOpts?.fetchObject) {
|
314
|
+
const prop = await traversePropertyPath(id, treeViewOpts?.object, treeViewOpts?.fetchObject);
|
315
|
+
if (prop)
|
316
|
+
tempPropertyMap[id] = prop;
|
317
|
+
}
|
318
|
+
setPropertyTreeMap(tempPropertyMap);
|
319
|
+
});
|
320
|
+
};
|
279
321
|
const handleQueryChange = (q) => {
|
280
322
|
setQuery(q);
|
281
323
|
const newCriteria = JSON.parse(formatQuery(q, 'mongodb'));
|
282
|
-
|
324
|
+
//when q has no rules, it formats and parses to { $and: [{ $expr: true }] }
|
325
|
+
const allRulesDeleted = isEmpty(difference(newCriteria, { $and: [{ $expr: true }] }));
|
326
|
+
// since the Add Condition / Add Group buttons add rules with all the fields empty,
|
327
|
+
// we need to check if the first rule was added because q will still parse to { $and: [{ $expr: true }] }
|
328
|
+
const firstRuleAdded = isEmpty(criteria) && q.rules.length > 0;
|
329
|
+
if (allRulesDeleted && !firstRuleAdded) {
|
283
330
|
setCriteria(undefined);
|
284
331
|
}
|
285
332
|
else {
|
@@ -346,6 +393,32 @@ const CriteriaBuilder = (props) => {
|
|
346
393
|
React.createElement(QueryBuilderMaterial, null,
|
347
394
|
React.createElement(QueryBuilder, { query: !criteria && !originalCriteria ? { combinator: 'and', rules: [] } : query, fields: fields, onQueryChange: (q) => {
|
348
395
|
handleQueryChange(q);
|
396
|
+
}, onAddRule: (rule) => {
|
397
|
+
// overrides new rule and sets up an empty rule with all three fields empty
|
398
|
+
return {
|
399
|
+
...rule,
|
400
|
+
field: '',
|
401
|
+
operator: '',
|
402
|
+
};
|
403
|
+
}, onAddGroup: (group) => {
|
404
|
+
// overrides new group and sets up a new group with an empty rule with all three fields showing
|
405
|
+
const emptyRules = group.rules.map((rule) => ({
|
406
|
+
...rule,
|
407
|
+
field: '',
|
408
|
+
operator: '',
|
409
|
+
}));
|
410
|
+
return {
|
411
|
+
...group,
|
412
|
+
rules: emptyRules,
|
413
|
+
combinator: 'and',
|
414
|
+
};
|
415
|
+
}, translations: {
|
416
|
+
fields: {
|
417
|
+
placeholderLabel: '',
|
418
|
+
},
|
419
|
+
operators: {
|
420
|
+
placeholderLabel: '',
|
421
|
+
},
|
349
422
|
}, showCombinatorsBetweenRules: true, listsAsArrays: true, disabled: disabled, addRuleToNewGroups: true, controlElements: {
|
350
423
|
combinatorSelector: customCombinator,
|
351
424
|
fieldSelector: customSelector,
|
@@ -363,6 +436,8 @@ const CriteriaBuilder = (props) => {
|
|
363
436
|
presetGroupLabel,
|
364
437
|
disabledCriteria,
|
365
438
|
treeViewOpts,
|
439
|
+
propertyTreeMap,
|
440
|
+
setPropertyTreeMap,
|
366
441
|
}, controlClassnames: {
|
367
442
|
ruleGroup: 'container',
|
368
443
|
}, operators: operators
|
@@ -3,8 +3,8 @@ import { EvokeObject } from '../../../types';
|
|
3
3
|
type PropertyTreeProps = {
|
4
4
|
fetchObject: (id: string) => Promise<EvokeObject | undefined>;
|
5
5
|
rootObject: EvokeObject;
|
6
|
-
|
6
|
+
handleTreePropertySelect: (propertyId: string) => void;
|
7
7
|
value: string | undefined;
|
8
8
|
};
|
9
|
-
declare const PropertyTree: ({ fetchObject,
|
9
|
+
declare const PropertyTree: ({ fetchObject, handleTreePropertySelect, rootObject, value }: PropertyTreeProps) => JSX.Element;
|
10
10
|
export default PropertyTree;
|
@@ -3,7 +3,7 @@ import React, { useEffect, useState } from 'react';
|
|
3
3
|
import { Autocomplete, MenuItem, TextField, Tooltip, TreeView } from '../../core';
|
4
4
|
import PropertyTreeItem from './PropertyTreeItem';
|
5
5
|
import { fetchDisplayNamePath, findPropertyById, setIdPaths, truncateNamePath, updateTreeNode } from './utils';
|
6
|
-
const PropertyTree = ({ fetchObject,
|
6
|
+
const PropertyTree = ({ fetchObject, handleTreePropertySelect, rootObject, value }) => {
|
7
7
|
const [expandedNodes, setExpandedNodes] = useState([]);
|
8
8
|
const [objectPropertyNamePathMap, setObjectPropertyNamePathMap] = useState({});
|
9
9
|
const [objectProperties, setObjectProperties] = useState(rootObject.properties ?? []);
|
@@ -99,7 +99,7 @@ const PropertyTree = ({ fetchObject, handleOnChange, rootObject, value }) => {
|
|
99
99
|
const property = findPropertyById(objectProperties, propertyId);
|
100
100
|
// this prevents the selection of a parent node
|
101
101
|
if (property && !property.children) {
|
102
|
-
|
102
|
+
handleTreePropertySelect(property.id);
|
103
103
|
}
|
104
104
|
};
|
105
105
|
return (React.createElement(Autocomplete, { "aria-label": "Property Selector", value: value, fullWidth: true, sx: {
|
@@ -17,7 +17,20 @@ const GroupHeader = styled('div')(({ theme }) => ({
|
|
17
17
|
}));
|
18
18
|
const GroupItems = styled('ul')({ padding: 0 });
|
19
19
|
const ValueEditor = (props) => {
|
20
|
-
const { handleOnChange, value,
|
20
|
+
const { handleOnChange, value, operator, context, level, rule } = props;
|
21
|
+
let inputType = props.inputType;
|
22
|
+
let values = props.values;
|
23
|
+
const property = context.propertyTreeMap[rule.field];
|
24
|
+
// for tree view / related object properties, the properties are stored in the propertyTreeMap upon selection
|
25
|
+
if (!!context.treeViewOpts && !!property) {
|
26
|
+
inputType = property.type;
|
27
|
+
if (property.enum) {
|
28
|
+
values = property.enum.map((item) => ({
|
29
|
+
name: item,
|
30
|
+
label: item,
|
31
|
+
}));
|
32
|
+
}
|
33
|
+
}
|
21
34
|
const inputRef = useRef(null);
|
22
35
|
const [openPresetValues, setOpenPresetValues] = useState(false);
|
23
36
|
const disabled = ['null', 'notNull'].includes(operator);
|
@@ -79,7 +92,7 @@ const ValueEditor = (props) => {
|
|
79
92
|
handleOnChange(uniqueSelections.length ? uniqueSelections : '');
|
80
93
|
},
|
81
94
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
82
|
-
isOptionEqualToValue: (option, value) => option === value, renderInput: (params) => (React.createElement(TextField, { label: params.label, ...params, size: "small" })), groupBy: (option) => isPresetValue(option.value?.name) ? context.presetGroupLabel || 'Preset Values' : 'Options', renderGroup: groupRenderGroup, sx: { width: '33%', background: '#fff' }, readOnly: readOnly }));
|
95
|
+
isOptionEqualToValue: (option, value) => option === value, renderInput: (params) => (React.createElement(TextField, { label: params.label, ...params, size: "small", sx: { backgroundColor: '#fff' } })), groupBy: (option) => isPresetValue(option.value?.name) ? context.presetGroupLabel || 'Preset Values' : 'Options', renderGroup: groupRenderGroup, sx: { width: '33%', background: '#fff' }, readOnly: readOnly }));
|
83
96
|
}
|
84
97
|
else {
|
85
98
|
return (React.createElement(TextField, { inputRef: inputRef, value: ['null', 'notNull'].includes(operator) ? '' : value, disabled: ['null', 'notNull'].includes(operator), onChange: (e) => {
|
@@ -114,7 +127,7 @@ const ValueEditor = (props) => {
|
|
114
127
|
value = newValue?.name ?? newValue?.value?.name ?? '';
|
115
128
|
}
|
116
129
|
handleOnChange(value);
|
117
|
-
}, renderInput: (params) => (React.createElement(TextField, { inputRef: inputRef, label: params?.label, ...params, size: "small" })),
|
130
|
+
}, renderInput: (params) => (React.createElement(TextField, { inputRef: inputRef, label: params?.label, ...params, size: "small", sx: { backgroundColor: '#fff' } })),
|
118
131
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
119
132
|
getOptionLabel: (option) => {
|
120
133
|
if (typeof option === 'string') {
|
@@ -39,7 +39,7 @@ export declare const setIdPaths: (properties: ObjectProperty[], parentPath: stri
|
|
39
39
|
*
|
40
40
|
* @param {string} propertyPath - The dot-separated path of the property to traverse.
|
41
41
|
* @param {Obj} rootObject - The root object from which to start the traversal.
|
42
|
-
* @param {
|
42
|
+
* @param {FetchObjectFunction} fetchObject - A function to fetch an object by its ID.
|
43
43
|
* @returns {Promise<ObjectProperty | null>} A promise that resolves to an ObjectProperty if found, or null otherwise.
|
44
44
|
*/
|
45
45
|
export declare const traversePropertyPath: (propertyPath: string, rootObject: Obj, fetchObject: (objectId: string) => Promise<Obj | undefined>) => Promise<ObjectProperty | null>;
|
@@ -72,7 +72,7 @@ export const setIdPaths = (properties, parentPath) => {
|
|
72
72
|
*
|
73
73
|
* @param {string} propertyPath - The dot-separated path of the property to traverse.
|
74
74
|
* @param {Obj} rootObject - The root object from which to start the traversal.
|
75
|
-
* @param {
|
75
|
+
* @param {FetchObjectFunction} fetchObject - A function to fetch an object by its ID.
|
76
76
|
* @returns {Promise<ObjectProperty | null>} A promise that resolves to an ObjectProperty if found, or null otherwise.
|
77
77
|
*/
|
78
78
|
export const traversePropertyPath = async (propertyPath, rootObject, fetchObject) => {
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import { ReactComponent } from '@formio/react';
|
2
|
+
import Handlebars from 'handlebars';
|
2
3
|
import { isArray, isEmpty, isNil, uniq } from 'lodash';
|
3
4
|
import { DateTime } from 'luxon';
|
4
5
|
import React from 'react';
|
@@ -9,20 +10,20 @@ import { isPropertyVisible } from '../utils';
|
|
9
10
|
function isAddressProperties(key) {
|
10
11
|
return /\.line1|\.line2|\.city|\.county|\.state|\.zipCode$/.test(key);
|
11
12
|
}
|
12
|
-
function
|
13
|
+
Handlebars.registerHelper('addDays', function (addend1, addend2) {
|
13
14
|
const dateAddend1 = DateTime.fromISO(addend1);
|
14
15
|
if (dateAddend1.isValid) {
|
15
16
|
return dateAddend1.plus({ days: addend2 }).toISODate();
|
16
17
|
}
|
17
18
|
return undefined;
|
18
|
-
}
|
19
|
-
function
|
19
|
+
});
|
20
|
+
Handlebars.registerHelper('subDays', function (minuend, subtrahend) {
|
20
21
|
const dateMinuend = DateTime.fromISO(minuend);
|
21
22
|
if (dateMinuend.isValid) {
|
22
23
|
return dateMinuend.minus({ days: subtrahend }).toISODate();
|
23
24
|
}
|
24
25
|
return undefined;
|
25
|
-
}
|
26
|
+
});
|
26
27
|
export class FormFieldComponent extends ReactComponent {
|
27
28
|
/**
|
28
29
|
* Called when the component has been instantiated. This is useful to define
|
@@ -344,63 +345,29 @@ export class FormFieldComponent extends ReactComponent {
|
|
344
345
|
else {
|
345
346
|
delete this.errorDetails['regexes'];
|
346
347
|
}
|
347
|
-
|
348
|
+
if (this.component.validate.minDate || this.component.validate.maxDate) {
|
348
349
|
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
|
349
|
-
const
|
350
|
-
|
351
|
-
|
352
|
-
|
350
|
+
const data = {
|
351
|
+
input: { ...this.root.data },
|
352
|
+
__today__: DateTime.now().toISODate(),
|
353
|
+
};
|
354
|
+
let { minDate, maxDate } = validate;
|
355
|
+
if (minDate && !dateRegex.test(minDate)) {
|
353
356
|
try {
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
if (propertyId in this.root.data) {
|
364
|
-
return this.root.data[propertyId];
|
365
|
-
}
|
366
|
-
}
|
367
|
-
}
|
368
|
-
else if (relativeDateRegex.test(dateValidation)) {
|
369
|
-
const { groups = {} } = relativeDateRegex.exec(dateValidation) ?? {};
|
370
|
-
// eslint-disable-next-line prefer-const
|
371
|
-
let { operation, operand1, operand2 } = groups;
|
372
|
-
if (operation && operand1 && operand2) {
|
373
|
-
if (operand1 === '__today__') {
|
374
|
-
operand1 = today;
|
375
|
-
}
|
376
|
-
else if (operand1.split('.').length > 1) {
|
377
|
-
const propertyId = operand1.split('.')[1];
|
378
|
-
if (propertyId in this.root.data) {
|
379
|
-
operand1 = this.root.data[propertyId];
|
380
|
-
}
|
381
|
-
}
|
382
|
-
else {
|
383
|
-
throw `Invalid operand: ${operand1}`;
|
384
|
-
}
|
385
|
-
if (operation === 'addDays') {
|
386
|
-
return addDays(operand1, parseInt(operand2));
|
387
|
-
}
|
388
|
-
else {
|
389
|
-
return subDays(operand1, parseInt(operand2));
|
390
|
-
}
|
391
|
-
}
|
392
|
-
}
|
393
|
-
throw `Invalid date validation: ${dateValidation}`;
|
357
|
+
minDate = Handlebars.compile(minDate)(data);
|
358
|
+
}
|
359
|
+
catch (err) {
|
360
|
+
console.log(err);
|
361
|
+
}
|
362
|
+
}
|
363
|
+
if (maxDate && !dateRegex.test(maxDate)) {
|
364
|
+
try {
|
365
|
+
maxDate = Handlebars.compile(maxDate)(data);
|
394
366
|
}
|
395
367
|
catch (err) {
|
396
368
|
console.log(err);
|
397
|
-
return undefined;
|
398
369
|
}
|
399
370
|
}
|
400
|
-
};
|
401
|
-
if (this.component.validate.minDate || this.component.validate.maxDate) {
|
402
|
-
const minDate = validate.minDate ? evaluateDateValidation(validate.minDate) : undefined;
|
403
|
-
const maxDate = validate.maxDate ? evaluateDateValidation(validate.maxDate) : undefined;
|
404
371
|
if ((minDate && value < minDate) || (maxDate && value > maxDate)) {
|
405
372
|
this.errorDetails['date'] = validate.customMessage;
|
406
373
|
}
|
@@ -456,11 +423,7 @@ export class FormFieldComponent extends ReactComponent {
|
|
456
423
|
!this.root.customErrors.some((err) => err.message === error)) {
|
457
424
|
this.root.customErrors = [
|
458
425
|
...this.root.customErrors,
|
459
|
-
{
|
460
|
-
component: this.component,
|
461
|
-
message: error,
|
462
|
-
formattedKeyOrPath: this.component.key,
|
463
|
-
},
|
426
|
+
{ component: this.component, message: error, formattedKeyOrPath: this.component.key },
|
464
427
|
];
|
465
428
|
}
|
466
429
|
});
|
package/dist/published/components/custom/Form/FormComponents/ObjectComponent/ObjectPropertyInput.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import cleanDeep from 'clean-deep';
|
2
|
-
import Handlebars from '
|
2
|
+
import Handlebars from 'handlebars';
|
3
3
|
import { cloneDeep, debounce, isEmpty, isNil } from 'lodash';
|
4
4
|
import React, { useCallback, useEffect, useState } from 'react';
|
5
5
|
import { Close } from '../../../../../icons';
|
@@ -132,7 +132,7 @@ export const ObjectPropertyInput = (props) => {
|
|
132
132
|
setOpenCreateDialog(false);
|
133
133
|
};
|
134
134
|
const compileExpression = (expression, instance) => {
|
135
|
-
const template = Handlebars.
|
135
|
+
const template = Handlebars.compile(expression);
|
136
136
|
return instance ? template(instance) : undefined;
|
137
137
|
};
|
138
138
|
const navigationSlug = defaultPages && property.objectId && defaultPages[property.objectId];
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import Handlebars from '
|
1
|
+
import Handlebars from 'handlebars';
|
2
2
|
import { difference, isEmpty, isObject } from 'lodash';
|
3
3
|
import React, { useEffect, useState } from 'react';
|
4
4
|
import { FormField } from '../../../..';
|
@@ -60,7 +60,7 @@ export const DropdownRepeatableFieldInput = (props) => {
|
|
60
60
|
}
|
61
61
|
};
|
62
62
|
const compileExpression = (instance, expression) => {
|
63
|
-
const template = Handlebars.
|
63
|
+
const template = Handlebars.compile(expression);
|
64
64
|
return template(instance);
|
65
65
|
};
|
66
66
|
return (React.createElement(React.Fragment, null, !readOnly ? (property && (React.createElement(React.Fragment, null,
|
@@ -87,8 +87,5 @@ export const DropdownRepeatableFieldInput = (props) => {
|
|
87
87
|
} })),
|
88
88
|
loading: loading,
|
89
89
|
} }),
|
90
|
-
React.createElement(Snackbar, { open: snackbarError.showAlert, handleClose: () => setSnackbarError({
|
91
|
-
isError: snackbarError.isError,
|
92
|
-
showAlert: false,
|
93
|
-
}), message: snackbarError.message, error: snackbarError.isError })))) : (React.createElement(Typography, null, selectedOptions && selectedOptions.map((option) => option.label).join(', ')))));
|
90
|
+
React.createElement(Snackbar, { open: snackbarError.showAlert, handleClose: () => setSnackbarError({ isError: snackbarError.isError, showAlert: false }), message: snackbarError.message, error: snackbarError.isError })))) : (React.createElement(Typography, null, selectedOptions && selectedOptions.map((option) => option.label).join(', ')))));
|
94
91
|
};
|
@@ -3,6 +3,7 @@ import { ComponentMeta, ComponentStory } from '@storybook/react';
|
|
3
3
|
import { CriteriaInputProps } from '../components/custom/CriteriaBuilder/CriteriaBuilder';
|
4
4
|
declare const _default: ComponentMeta<(props: CriteriaInputProps) => JSX.Element>;
|
5
5
|
export default _default;
|
6
|
+
export declare const CriteriaBuilderEmpty: ComponentStory<(props: CriteriaInputProps) => JSX.Element>;
|
6
7
|
export declare const CriteriaBuilder: ComponentStory<(props: CriteriaInputProps) => JSX.Element>;
|
7
8
|
export declare const CriteriaBuilderPresetUserID: ComponentStory<(props: CriteriaInputProps) => JSX.Element>;
|
8
9
|
export declare const CriteriaBuilderGroupedPresetValues: ComponentStory<(props: CriteriaInputProps) => JSX.Element>;
|
@@ -4,7 +4,101 @@ export default {
|
|
4
4
|
title: 'Input/CriteriaBuilder',
|
5
5
|
component: BuildCriteria,
|
6
6
|
};
|
7
|
-
const CriteriaBuilderTemplate = (args) =>
|
7
|
+
const CriteriaBuilderTemplate = (args) => {
|
8
|
+
const [criteria, setCriteria] = React.useState(args.criteria);
|
9
|
+
args.setCriteria = setCriteria;
|
10
|
+
args.criteria = criteria;
|
11
|
+
console.log('reset criteria= ', criteria);
|
12
|
+
return React.createElement(BuildCriteria, { ...args });
|
13
|
+
};
|
14
|
+
export const CriteriaBuilderEmpty = CriteriaBuilderTemplate.bind({});
|
15
|
+
CriteriaBuilderEmpty.args = {
|
16
|
+
properties: [
|
17
|
+
{
|
18
|
+
id: 'name',
|
19
|
+
name: 'name',
|
20
|
+
type: 'string',
|
21
|
+
required: true,
|
22
|
+
},
|
23
|
+
{
|
24
|
+
id: 'licenseNumber',
|
25
|
+
name: 'license number',
|
26
|
+
type: 'string',
|
27
|
+
required: true,
|
28
|
+
},
|
29
|
+
{
|
30
|
+
id: 'issueDate',
|
31
|
+
name: 'issue date',
|
32
|
+
type: 'date',
|
33
|
+
required: false,
|
34
|
+
objectId: '',
|
35
|
+
},
|
36
|
+
{
|
37
|
+
id: 'status',
|
38
|
+
name: 'status',
|
39
|
+
type: 'string',
|
40
|
+
enum: ['active', 'expired', 'pending approval'],
|
41
|
+
required: false,
|
42
|
+
objectId: '',
|
43
|
+
},
|
44
|
+
{
|
45
|
+
id: 'licenseeName',
|
46
|
+
name: 'Licensee Name',
|
47
|
+
type: 'string',
|
48
|
+
required: false,
|
49
|
+
formula: '{{person.firstName}} {{person.middleName}} {{person.lastName}}',
|
50
|
+
},
|
51
|
+
{
|
52
|
+
id: 'calculated',
|
53
|
+
name: 'calculated',
|
54
|
+
type: 'string',
|
55
|
+
required: false,
|
56
|
+
objectId: '',
|
57
|
+
formula: '{{status}} - {{licensee.firstName}}',
|
58
|
+
},
|
59
|
+
{
|
60
|
+
id: 'application',
|
61
|
+
name: 'applicant',
|
62
|
+
type: 'object',
|
63
|
+
required: false,
|
64
|
+
objectId: 'application',
|
65
|
+
},
|
66
|
+
{
|
67
|
+
id: 'applicantName',
|
68
|
+
name: 'Application Info',
|
69
|
+
type: 'string',
|
70
|
+
required: false,
|
71
|
+
formula: '{{application.name}} - {{status}}',
|
72
|
+
},
|
73
|
+
{
|
74
|
+
id: 'licensedFor',
|
75
|
+
name: 'licensed for',
|
76
|
+
type: 'array',
|
77
|
+
enum: [
|
78
|
+
'amusements',
|
79
|
+
'food and beverage',
|
80
|
+
'entertainment',
|
81
|
+
'transportation',
|
82
|
+
'hot dogs',
|
83
|
+
'swimming pools',
|
84
|
+
'ferris wheels',
|
85
|
+
'bungee jumping',
|
86
|
+
],
|
87
|
+
required: true,
|
88
|
+
searchable: false,
|
89
|
+
},
|
90
|
+
{
|
91
|
+
id: 'inState',
|
92
|
+
name: 'In State',
|
93
|
+
type: 'string',
|
94
|
+
enum: ['Yes', 'No'],
|
95
|
+
required: false,
|
96
|
+
searchable: false,
|
97
|
+
},
|
98
|
+
],
|
99
|
+
criteria: {},
|
100
|
+
setCriteria: (criteria) => console.log('criteria= ', criteria),
|
101
|
+
};
|
8
102
|
export const CriteriaBuilder = CriteriaBuilderTemplate.bind({});
|
9
103
|
CriteriaBuilder.args = {
|
10
104
|
properties: [
|
@@ -418,6 +512,13 @@ CriteriaBuilderRelatedObject.args = {
|
|
418
512
|
type: 'date',
|
419
513
|
required: false,
|
420
514
|
},
|
515
|
+
{
|
516
|
+
id: 'approvalStatus',
|
517
|
+
name: 'Approval Status',
|
518
|
+
type: 'string',
|
519
|
+
enum: ['Approved', 'Denied', 'InProgress', 'Received'],
|
520
|
+
required: false,
|
521
|
+
},
|
421
522
|
{
|
422
523
|
id: 'licenseNumber',
|
423
524
|
name: 'LicenseNumber',
|
@@ -464,6 +565,13 @@ CriteriaBuilderRelatedObject.args = {
|
|
464
565
|
type: 'string',
|
465
566
|
required: true,
|
466
567
|
},
|
568
|
+
{
|
569
|
+
id: 'status',
|
570
|
+
name: 'Status',
|
571
|
+
type: 'string',
|
572
|
+
enum: ['RN', 'NP', 'DNP', 'MA'],
|
573
|
+
required: false,
|
574
|
+
},
|
467
575
|
{
|
468
576
|
id: 'description',
|
469
577
|
name: 'Description',
|
@@ -703,10 +811,38 @@ CriteriaBuilderRelatedObject.args = {
|
|
703
811
|
criteria: {
|
704
812
|
$and: [
|
705
813
|
{
|
706
|
-
|
814
|
+
'applicant.vehicleCount': {
|
815
|
+
$gt: 2,
|
816
|
+
},
|
817
|
+
},
|
818
|
+
{
|
819
|
+
'applicant.license.expirationDate': {
|
820
|
+
$gt: '2025-01-16',
|
821
|
+
},
|
822
|
+
},
|
823
|
+
{
|
824
|
+
'applicant.license.person.phone': {
|
825
|
+
$regex: '410',
|
826
|
+
},
|
827
|
+
},
|
828
|
+
{
|
829
|
+
$or: [
|
830
|
+
{
|
831
|
+
'applicant.license.approvalStatus': 'InProgress',
|
832
|
+
},
|
833
|
+
{
|
834
|
+
'applicant.license.approvalStatus': 'Approved',
|
835
|
+
},
|
836
|
+
{
|
837
|
+
'applicant.license.issueDate': '2025-01-09',
|
838
|
+
},
|
839
|
+
{
|
840
|
+
'applicant.avgVehicleRating': 1,
|
841
|
+
},
|
842
|
+
],
|
707
843
|
},
|
708
844
|
{
|
709
|
-
'
|
845
|
+
'applicationType.status': 'DNP',
|
710
846
|
},
|
711
847
|
],
|
712
848
|
},
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@evoke-platform/ui-components",
|
3
|
-
"version": "1.0.0-dev.
|
3
|
+
"version": "1.0.0-dev.245",
|
4
4
|
"description": "",
|
5
5
|
"main": "dist/published/index.js",
|
6
6
|
"module": "dist/published/index.js",
|
@@ -114,8 +114,8 @@
|
|
114
114
|
"eslint-plugin-no-inline-styles": "^1.0.5",
|
115
115
|
"flat": "^6.0.1",
|
116
116
|
"formiojs": "^4.15.0-rc.23",
|
117
|
+
"handlebars": "^4.7.8",
|
117
118
|
"html-react-parser": "^5.1.18",
|
118
|
-
"kbn-handlebars": "^1.0.0",
|
119
119
|
"luxon": "^2.5.2",
|
120
120
|
"nanoid": "^5.0.8",
|
121
121
|
"nanoid-dictionary": "^4.3.0",
|