@evoke-platform/ui-components 1.0.0-dev.246 → 1.0.0-dev.248
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 +2 -2
- package/dist/published/components/custom/CriteriaBuilder/ValueEditor.js +3 -2
- package/dist/published/components/custom/CriteriaBuilder/types.d.ts +14 -0
- package/dist/published/components/custom/CriteriaBuilder/utils.d.ts +10 -0
- package/dist/published/components/custom/CriteriaBuilder/utils.js +151 -0
- package/dist/published/stories/CriteriaBuilder.stories.js +19 -1
- package/package.json +1 -1
@@ -3,14 +3,14 @@ import { Typography } from '@mui/material';
|
|
3
3
|
import { QueryBuilderMaterial } from '@react-querybuilder/material';
|
4
4
|
import { isArray, isEmpty, startCase } from 'lodash';
|
5
5
|
import React, { useEffect, useMemo, useState } from 'react';
|
6
|
-
import { QueryBuilder, RuleGroupBodyComponents, RuleGroupHeaderComponents, TestID, formatQuery,
|
6
|
+
import { QueryBuilder, RuleGroupBodyComponents, RuleGroupHeaderComponents, TestID, formatQuery, useRuleGroup, } from 'react-querybuilder';
|
7
7
|
import 'react-querybuilder/dist/query-builder.css';
|
8
8
|
import { TrashCan } from '../../../icons/custom';
|
9
9
|
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
|
+
import { parseMongoDB, traversePropertyPath } from './utils';
|
14
14
|
import ValueEditor from './ValueEditor';
|
15
15
|
const ALL_OPERATORS = [
|
16
16
|
{ name: '=', label: 'Is' },
|
@@ -84,7 +84,7 @@ const ValueEditor = (props) => {
|
|
84
84
|
const isMultiple = ['in', 'notIn'].includes(operator);
|
85
85
|
const options = presetValues;
|
86
86
|
if (isMultiple) {
|
87
|
-
return (React.createElement(Autocomplete, { freeSolo: true, multiple: true, options: options, getOptionLabel: (option) => typeof option === 'object' && option?.label ? option.label : option, value: Array.isArray(value) ? (disabled ? [] : value) : [], disabled: disabled,
|
87
|
+
return (React.createElement(Autocomplete, { freeSolo: true, multiple: true, options: options, getOptionLabel: (option) => typeof option === 'object' && option?.label ? option.label : String(option), value: Array.isArray(value) ? (disabled ? [] : value) : [], disabled: disabled,
|
88
88
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
89
89
|
onChange: (event, newValue) => {
|
90
90
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
@@ -131,7 +131,8 @@ const ValueEditor = (props) => {
|
|
131
131
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
132
132
|
getOptionLabel: (option) => {
|
133
133
|
if (typeof option === 'string') {
|
134
|
-
|
134
|
+
const found = options.find((o) => option === o.name || option === o.value?.name);
|
135
|
+
return found?.label || option;
|
135
136
|
}
|
136
137
|
return option?.label;
|
137
138
|
},
|
@@ -35,3 +35,17 @@ export type TreeViewObject = {
|
|
35
35
|
name: string;
|
36
36
|
properties: TreeViewProperty[];
|
37
37
|
};
|
38
|
+
export type MongoDBQueryValue = null | string | {
|
39
|
+
$not?: {
|
40
|
+
$regex?: string;
|
41
|
+
};
|
42
|
+
$regex?: string;
|
43
|
+
$eq?: unknown;
|
44
|
+
$ne?: unknown;
|
45
|
+
$lt?: unknown;
|
46
|
+
$lte?: unknown;
|
47
|
+
$gt?: unknown;
|
48
|
+
$gte?: unknown;
|
49
|
+
$in?: unknown[];
|
50
|
+
$nin?: unknown[];
|
51
|
+
};
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { RuleGroupType } from 'react-querybuilder';
|
1
2
|
import { ExpandedProperty, Obj, ObjectProperty } from '../../../types';
|
2
3
|
/**
|
3
4
|
* Recursively updates a node in a tree structure by applying an updater function to the node with the specified ID.
|
@@ -51,4 +52,13 @@ export declare const traversePropertyPath: (propertyPath: string, rootObject: Ob
|
|
51
52
|
* @returns {string} - The truncated name path if it exceeds the limit, otherwise the original name path.
|
52
53
|
*/
|
53
54
|
export declare const truncateNamePath: (namePath: string, limit?: number) => string;
|
55
|
+
/**
|
56
|
+
* Parses a MongoDB query into a RuleGroupType or a single RuleType.
|
57
|
+
* This function recursively processes the MongoDB query and transforms it into
|
58
|
+
* a structured format that can be used in the CriteriaBuilder.
|
59
|
+
*
|
60
|
+
* @param {Record<string, unknown>} mongoQuery - The MongoDB query to be parsed.
|
61
|
+
* @returns {RuleGroupType} - Correctly formatted rule or rules for the query builder.
|
62
|
+
*/
|
63
|
+
export declare function parseMongoDB(mongoQuery: Record<string, unknown>): RuleGroupType;
|
54
64
|
export {};
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { isArray, isEmpty } from 'lodash';
|
1
2
|
/**
|
2
3
|
* Recursively updates a node in a tree structure by applying an updater function to the node with the specified ID.
|
3
4
|
*
|
@@ -141,3 +142,153 @@ export const truncateNamePath = (namePath, limit = 20) => {
|
|
141
142
|
return `${namePath.substring(0, limit - 3)}...`;
|
142
143
|
}
|
143
144
|
};
|
145
|
+
/**
|
146
|
+
* Parses a MongoDB query into a RuleGroupType or a single RuleType.
|
147
|
+
* This function recursively processes the MongoDB query and transforms it into
|
148
|
+
* a structured format that can be used in the CriteriaBuilder.
|
149
|
+
*
|
150
|
+
* @param {Record<string, unknown>} mongoQuery - The MongoDB query to be parsed.
|
151
|
+
* @returns {RuleGroupType} - Correctly formatted rule or rules for the query builder.
|
152
|
+
*/
|
153
|
+
export function parseMongoDB(mongoQuery) {
|
154
|
+
/**
|
155
|
+
* Parses a single rule from the MongoDB query.
|
156
|
+
*
|
157
|
+
* @param {string} key - The field name for the rule.
|
158
|
+
* @param {MongoDBQueryValue} value - The value associated with the key to parse.
|
159
|
+
* @returns {RuleType | undefined} - A RuleType if the value is valid, otherwise undefined.
|
160
|
+
*/
|
161
|
+
const parseRule = (key, value) => {
|
162
|
+
if (value === null) {
|
163
|
+
return {
|
164
|
+
field: key,
|
165
|
+
operator: 'null',
|
166
|
+
value: null,
|
167
|
+
};
|
168
|
+
}
|
169
|
+
else if (typeof value === 'string' || typeof value === 'number' || '$eq' in value) {
|
170
|
+
return {
|
171
|
+
field: key,
|
172
|
+
operator: '=',
|
173
|
+
value: typeof value === 'object' && '$eq' in value ? value.$eq : value,
|
174
|
+
};
|
175
|
+
}
|
176
|
+
else if (value.$not && typeof value.$not.$regex === 'string') {
|
177
|
+
return {
|
178
|
+
field: key,
|
179
|
+
operator: 'doesNotContain',
|
180
|
+
value: value.$not.$regex,
|
181
|
+
};
|
182
|
+
}
|
183
|
+
else if (typeof value.$regex === 'string') {
|
184
|
+
let operator = 'contains';
|
185
|
+
const regexValue = value.$regex;
|
186
|
+
if (regexValue.startsWith('^')) {
|
187
|
+
operator = 'beginsWith';
|
188
|
+
regexValue.slice(1);
|
189
|
+
}
|
190
|
+
else if (regexValue.endsWith('$')) {
|
191
|
+
operator = 'endsWith';
|
192
|
+
regexValue.slice(0, -1);
|
193
|
+
}
|
194
|
+
return {
|
195
|
+
field: key,
|
196
|
+
operator: operator,
|
197
|
+
value: regexValue,
|
198
|
+
};
|
199
|
+
}
|
200
|
+
else if ('$ne' in value) {
|
201
|
+
return {
|
202
|
+
field: key,
|
203
|
+
operator: value.$ne === null ? 'notNull' : '!=',
|
204
|
+
value: value.$ne,
|
205
|
+
};
|
206
|
+
}
|
207
|
+
else if ('$lt' in value) {
|
208
|
+
return {
|
209
|
+
field: key,
|
210
|
+
operator: '<',
|
211
|
+
value: value.$lt,
|
212
|
+
};
|
213
|
+
}
|
214
|
+
else if ('$lte' in value) {
|
215
|
+
return {
|
216
|
+
field: key,
|
217
|
+
operator: '<=',
|
218
|
+
value: value.$lte,
|
219
|
+
};
|
220
|
+
}
|
221
|
+
else if ('$gt' in value) {
|
222
|
+
return {
|
223
|
+
field: key,
|
224
|
+
operator: '>',
|
225
|
+
value: value.$gt,
|
226
|
+
};
|
227
|
+
}
|
228
|
+
else if ('$gte' in value) {
|
229
|
+
return {
|
230
|
+
field: key,
|
231
|
+
operator: '>=',
|
232
|
+
value: value.$gte,
|
233
|
+
};
|
234
|
+
}
|
235
|
+
else if ('$in' in value) {
|
236
|
+
return {
|
237
|
+
field: key,
|
238
|
+
operator: 'in',
|
239
|
+
value: (value.$in || []).join(','),
|
240
|
+
};
|
241
|
+
}
|
242
|
+
else if ('$nin' in value) {
|
243
|
+
return {
|
244
|
+
field: key,
|
245
|
+
operator: 'notIn',
|
246
|
+
value: (value.$nin || []).join(','),
|
247
|
+
};
|
248
|
+
}
|
249
|
+
else {
|
250
|
+
return undefined;
|
251
|
+
}
|
252
|
+
};
|
253
|
+
/**
|
254
|
+
* Recursively parses a MongoDB query into a RuleGroupType or RuleType.
|
255
|
+
*
|
256
|
+
* @param {Record<string, unknown>} query - The MongoDB query object to be parsed.
|
257
|
+
* @returns {RuleGroupType | RuleType} - A RuleGroupType with combinator and rules, or a single RuleType.
|
258
|
+
*/
|
259
|
+
const parseGroup = (query) => {
|
260
|
+
if ('$and' in query && isArray(query.$and)) {
|
261
|
+
return {
|
262
|
+
combinator: 'and',
|
263
|
+
rules: query.$and.map(parseGroup),
|
264
|
+
};
|
265
|
+
}
|
266
|
+
else if ('$or' in query && isArray(query.$or)) {
|
267
|
+
return {
|
268
|
+
combinator: 'or',
|
269
|
+
rules: query.$or.map(parseGroup),
|
270
|
+
};
|
271
|
+
}
|
272
|
+
else if (isEmpty(query)) {
|
273
|
+
return { combinator: 'and', rules: [] };
|
274
|
+
}
|
275
|
+
else {
|
276
|
+
const rules = Object.entries(query)
|
277
|
+
.map(([key, value]) => parseRule(key, value))
|
278
|
+
.filter((rule) => rule !== null);
|
279
|
+
return rules[0];
|
280
|
+
}
|
281
|
+
};
|
282
|
+
const result = parseGroup(mongoQuery);
|
283
|
+
// Check if the result is a RuleGroupType (i.e., has a combinator)
|
284
|
+
if (result && 'combinator' in result) {
|
285
|
+
return result;
|
286
|
+
}
|
287
|
+
else {
|
288
|
+
// If there are no condition groups configured so it's not a RuleGroupType, wrap it in a default 'and' combinator
|
289
|
+
return {
|
290
|
+
combinator: 'and',
|
291
|
+
rules: result ? [result] : [],
|
292
|
+
};
|
293
|
+
}
|
294
|
+
}
|
@@ -183,12 +183,18 @@ CriteriaBuilder.args = {
|
|
183
183
|
required: false,
|
184
184
|
searchable: false,
|
185
185
|
},
|
186
|
+
{
|
187
|
+
id: 'amountOfInspections',
|
188
|
+
name: 'Inspection Count',
|
189
|
+
type: 'integer',
|
190
|
+
required: false,
|
191
|
+
},
|
186
192
|
],
|
187
193
|
setCriteria: (criteria) => console.log('criteria= ', criteria),
|
188
194
|
criteria: {
|
189
195
|
$or: [
|
190
196
|
{
|
191
|
-
name: '
|
197
|
+
name: { $in: ['Jane', 'John', 'Jack'] },
|
192
198
|
},
|
193
199
|
{
|
194
200
|
licensedFor: { $in: ['hot dogs', 'amusements', 'entertainment'] },
|
@@ -287,6 +293,12 @@ CriteriaBuilderPresetUserID.args = {
|
|
287
293
|
required: false,
|
288
294
|
searchable: false,
|
289
295
|
},
|
296
|
+
{
|
297
|
+
id: 'amountOfInspections',
|
298
|
+
name: 'Inspection Count',
|
299
|
+
type: 'integer',
|
300
|
+
required: false,
|
301
|
+
},
|
290
302
|
],
|
291
303
|
setCriteria: (criteria) => console.log('criteria= ', criteria),
|
292
304
|
criteria: {
|
@@ -325,6 +337,12 @@ CriteriaBuilderGroupedPresetValues.args = {
|
|
325
337
|
required: false,
|
326
338
|
objectId: '',
|
327
339
|
},
|
340
|
+
{
|
341
|
+
id: 'amountOfInspections',
|
342
|
+
name: 'Inspection Count',
|
343
|
+
type: 'integer',
|
344
|
+
required: false,
|
345
|
+
},
|
328
346
|
{
|
329
347
|
id: 'licensedFor',
|
330
348
|
name: 'licensed for',
|