@visns-studio/visns-components 5.5.5 → 5.5.7
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/README.md +132 -0
- package/package.json +1 -1
- package/src/components/crm/auth/ClientLogin.jsx +126 -0
- package/src/components/crm/auth/ClientOTPVerify.jsx +257 -0
- package/src/components/crm/auth/styles/ClientAuth.module.scss +167 -0
- package/src/components/crm/client/ClientDashboard.jsx +62 -0
- package/src/components/crm/client/ClientPortal.jsx +158 -0
- package/src/components/crm/client/styles/ClientDashboard.module.scss +133 -0
- package/src/components/crm/client/styles/ClientPortal.module.scss +240 -0
- package/src/components/crm/generic/GenericAuth.jsx +175 -0
- package/src/components/crm/generic/GenericClientPortal.jsx +165 -0
- package/src/components/crm/generic/GenericReport.jsx +787 -275
- package/src/components/crm/generic/styles/GenericClientPortal.module.scss +154 -0
- package/src/components/crm/generic/styles/GenericReport.module.scss +200 -0
- package/src/index.js +10 -0
|
@@ -177,6 +177,47 @@ const isJsonValue = (value) => {
|
|
|
177
177
|
return typeof value === 'object' && value !== null;
|
|
178
178
|
};
|
|
179
179
|
|
|
180
|
+
// Helper function to convert the enhanced filter structure to a flat array for the backend
|
|
181
|
+
const flattenFilterCriteria = (filterCriteria) => {
|
|
182
|
+
// If it's already a flat array (legacy format), return it as is
|
|
183
|
+
if (Array.isArray(filterCriteria)) {
|
|
184
|
+
return filterCriteria;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// If it's the new structure with groups and operators
|
|
188
|
+
if (filterCriteria.groups && Array.isArray(filterCriteria.groups)) {
|
|
189
|
+
const flattenedFilters = [];
|
|
190
|
+
|
|
191
|
+
// Process each group
|
|
192
|
+
filterCriteria.groups.forEach((group, groupIndex) => {
|
|
193
|
+
// Only add filters from non-empty groups
|
|
194
|
+
if (group.filters && group.filters.length > 0) {
|
|
195
|
+
// Add all filters from this group
|
|
196
|
+
group.filters.forEach((filter) => {
|
|
197
|
+
// Add a special property to indicate which group this filter belongs to
|
|
198
|
+
// and the operator to use within this group
|
|
199
|
+
const enhancedFilter = {
|
|
200
|
+
...filter,
|
|
201
|
+
_group: groupIndex,
|
|
202
|
+
_group_operator: group.operator,
|
|
203
|
+
};
|
|
204
|
+
flattenedFilters.push(enhancedFilter);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// If we have multiple groups, add the main operator as a property to the first filter
|
|
210
|
+
if (filterCriteria.groups.length > 1 && flattenedFilters.length > 0) {
|
|
211
|
+
flattenedFilters[0]._main_operator = filterCriteria.operator;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return flattenedFilters;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// If it's neither a flat array nor the new structure, return an empty array
|
|
218
|
+
return [];
|
|
219
|
+
};
|
|
220
|
+
|
|
180
221
|
const GenericReport = ({ setting = {} }) => {
|
|
181
222
|
// Extract settings with defaults
|
|
182
223
|
const { tableUrl, columnUrl } = setting;
|
|
@@ -219,8 +260,10 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
219
260
|
<li style="margin-bottom: 4px; font-size: 14px !important;">You can sort by multiple columns (e.g., sort by last name, then by first name)</li>
|
|
220
261
|
<li style="margin-bottom: 4px; font-size: 14px !important;">Choose between ascending (A-Z, 0-9) or descending (Z-A, 9-0) order</li>
|
|
221
262
|
<li style="margin-bottom: 4px; font-size: 14px !important;"><strong>Filtering</strong>: Click the "Filtering" header to expand the section, then add filter criteria</li>
|
|
222
|
-
<li style="margin-bottom: 4px; font-size: 14px !important;">Use operators like
|
|
223
|
-
<li style="margin-bottom: 4px; font-size: 14px !important;">
|
|
263
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;">Use operators like "contains" or "is exactly equal to" to narrow down results</li>
|
|
264
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;">Create multiple filter groups and choose whether to join them with ALL or ANY logic</li>
|
|
265
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;">Within each filter group, choose whether conditions are joined with ALL (all must match) or ANY (any can match)</li>
|
|
266
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;">This powerful combination allows for complex queries like "(A AND B) OR (C AND D)"</li>
|
|
224
267
|
<li style="font-size: 14px !important;">Sorting and filtering settings are saved with your report for future use</li>
|
|
225
268
|
</ul>
|
|
226
269
|
|
|
@@ -233,11 +276,69 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
233
276
|
<li style="margin-bottom: 4px; font-size: 14px !important;">Consider adding date fields to enable time-based filtering</li>
|
|
234
277
|
<li style="font-size: 14px !important;">Save frequently used reports for quick access</li>
|
|
235
278
|
</ul>
|
|
279
|
+
</div>
|
|
280
|
+
`,
|
|
281
|
+
width: 550,
|
|
282
|
+
confirmButtonText: 'Got it!',
|
|
283
|
+
confirmButtonColor: '#2563eb',
|
|
284
|
+
customClass: {
|
|
285
|
+
title: 'swal-title',
|
|
286
|
+
htmlContainer: 'swal-html-container',
|
|
287
|
+
confirmButton: 'swal-confirm-button',
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
};
|
|
236
291
|
|
|
292
|
+
// Help function specifically for filtering
|
|
293
|
+
const showFilteringHelp = () => {
|
|
294
|
+
Swal.fire({
|
|
295
|
+
title: 'Understanding Filtering',
|
|
296
|
+
html: `
|
|
297
|
+
<div style="text-align: left; margin-bottom: 20px; font-size: 14px; color: #4b5563;">
|
|
298
|
+
<h4 style="font-size: 16px; color: #1f2937; margin-bottom: 10px; font-weight: 600;">Filtering Basics:</h4>
|
|
299
|
+
<p style="margin-bottom: 8px; line-height: 1.4;">Filtering allows you to narrow down your report results to show only the data you're interested in.</p>
|
|
237
300
|
|
|
301
|
+
<h4 style="font-size: 16px; color: #1f2937; margin-bottom: 10px; margin-top: 16px; font-weight: 600;">Filter Groups:</h4>
|
|
302
|
+
<p style="margin-bottom: 8px; line-height: 1.4;">You can create multiple filter groups to build complex conditions:</p>
|
|
303
|
+
<ul style="padding-left: 18px; line-height: 1.4; font-size: 14px !important; margin-bottom: 14px;">
|
|
304
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;">Each filter group can contain one or more conditions</li>
|
|
305
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;">Within a group, you can choose whether ALL conditions must match or ANY condition can match</li>
|
|
306
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;">Between groups, you can also choose ALL groups must match or ANY group can match</li>
|
|
307
|
+
</ul>
|
|
308
|
+
|
|
309
|
+
<h4 style="font-size: 16px; color: #1f2937; margin-bottom: 10px; margin-top: 16px; font-weight: 600;">Filter Operators:</h4>
|
|
310
|
+
<p style="margin-bottom: 8px; line-height: 1.4;">Each filter condition uses an operator to compare values:</p>
|
|
311
|
+
<ul style="padding-left: 18px; line-height: 1.4; font-size: 14px !important; margin-bottom: 14px;">
|
|
312
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><strong>Is exactly equal to</strong>: Matches records where the field exactly matches your value</li>
|
|
313
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><strong>Is not equal to</strong>: Matches records where the field does not match your value</li>
|
|
314
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><strong>Contains</strong>: Matches records where the field contains your value anywhere within it</li>
|
|
315
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><strong>Does not contain</strong>: Matches records where the field does not contain your value</li>
|
|
316
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><strong>Is greater than</strong>: Matches records where the field is greater than your value (for numbers and dates)</li>
|
|
317
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><strong>Is less than</strong>: Matches records where the field is less than your value (for numbers and dates)</li>
|
|
318
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><strong>Is empty</strong>: Matches records where the field has no value</li>
|
|
319
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><strong>Is not empty</strong>: Matches records where the field has any value</li>
|
|
320
|
+
</ul>
|
|
321
|
+
|
|
322
|
+
<h4 style="font-size: 16px; color: #1f2937; margin-bottom: 10px; margin-top: 16px; font-weight: 600;">Examples:</h4>
|
|
323
|
+
<p style="margin-bottom: 8px; line-height: 1.4;">Here are some examples of how to use filtering:</p>
|
|
324
|
+
<ul style="padding-left: 18px; line-height: 1.4; font-size: 14px !important; margin-bottom: 14px;">
|
|
325
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><strong>Simple filter</strong>: Find all clients where Name contains "ABC"</li>
|
|
326
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><strong>Multiple conditions (ALL)</strong>: Find clients where Name contains "ABC" AND State is "NSW"</li>
|
|
327
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><strong>Multiple conditions (ANY)</strong>: Find clients where Name contains "ABC" OR Name contains "XYZ"</li>
|
|
328
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><strong>Complex filter</strong>: Find clients where (Name contains "ABC" AND State is "NSW") OR (Name contains "XYZ" AND State is "VIC")</li>
|
|
329
|
+
</ul>
|
|
330
|
+
|
|
331
|
+
<h4 style="font-size: 16px; color: #1f2937; margin-bottom: 10px; margin-top: 16px; font-weight: 600;">Tips for Effective Filtering:</h4>
|
|
332
|
+
<ul style="padding-left: 18px; line-height: 1.4; font-size: 14px !important; margin-bottom: 14px;">
|
|
333
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;">Start with simple filters and add complexity as needed</li>
|
|
334
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;">Use "Contains" for partial text matching (e.g., searching for part of a name)</li>
|
|
335
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;">Use date filters to find records within specific time periods</li>
|
|
336
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;">Group related conditions together in the same filter group</li>
|
|
337
|
+
<li style="font-size: 14px !important;">Test your filters by executing the query to make sure they work as expected</li>
|
|
338
|
+
</ul>
|
|
238
339
|
</div>
|
|
239
340
|
`,
|
|
240
|
-
width:
|
|
341
|
+
width: 650,
|
|
241
342
|
confirmButtonText: 'Got it!',
|
|
242
343
|
confirmButtonColor: '#2563eb',
|
|
243
344
|
customClass: {
|
|
@@ -351,7 +452,18 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
351
452
|
|
|
352
453
|
// State for sorting and filtering
|
|
353
454
|
const [sortCriteria, setSortCriteria] = useState([]);
|
|
354
|
-
|
|
455
|
+
|
|
456
|
+
// Enhanced filter criteria with support for AND/OR logic
|
|
457
|
+
// Structure: { operator: 'AND'|'OR', groups: [{ operator: 'AND'|'OR', filters: [...] }] }
|
|
458
|
+
const [filterCriteria, setFilterCriteria] = useState({
|
|
459
|
+
operator: 'AND',
|
|
460
|
+
groups: [
|
|
461
|
+
{
|
|
462
|
+
operator: 'AND',
|
|
463
|
+
filters: [],
|
|
464
|
+
},
|
|
465
|
+
],
|
|
466
|
+
});
|
|
355
467
|
|
|
356
468
|
// State for report execution
|
|
357
469
|
const [totalResults, setTotalResults] = useState(0);
|
|
@@ -889,8 +1001,8 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
889
1001
|
}
|
|
890
1002
|
};
|
|
891
1003
|
|
|
892
|
-
// Add a new filter criterion
|
|
893
|
-
const handleAddFilterCriterion = () => {
|
|
1004
|
+
// Add a new filter criterion to a specific group
|
|
1005
|
+
const handleAddFilterCriterion = (groupIndex = 0) => {
|
|
894
1006
|
// Default to the first selected column if available
|
|
895
1007
|
const defaultColumn =
|
|
896
1008
|
selectedColumns.length > 0
|
|
@@ -908,25 +1020,132 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
908
1020
|
value: '',
|
|
909
1021
|
};
|
|
910
1022
|
|
|
911
|
-
|
|
1023
|
+
// Create a deep copy of the current filter criteria
|
|
1024
|
+
const updatedFilterCriteria = JSON.parse(
|
|
1025
|
+
JSON.stringify(filterCriteria)
|
|
1026
|
+
);
|
|
1027
|
+
|
|
1028
|
+
// Add the new filter to the specified group
|
|
1029
|
+
updatedFilterCriteria.groups[groupIndex].filters.push(
|
|
1030
|
+
newFilterCriterion
|
|
1031
|
+
);
|
|
1032
|
+
|
|
1033
|
+
setFilterCriteria(updatedFilterCriteria);
|
|
1034
|
+
setShowFilteringSection(true);
|
|
1035
|
+
};
|
|
1036
|
+
|
|
1037
|
+
// Add a new filter group
|
|
1038
|
+
const handleAddFilterGroup = () => {
|
|
1039
|
+
// Create a deep copy of the current filter criteria
|
|
1040
|
+
const updatedFilterCriteria = JSON.parse(
|
|
1041
|
+
JSON.stringify(filterCriteria)
|
|
1042
|
+
);
|
|
1043
|
+
|
|
1044
|
+
// Add a new empty filter group
|
|
1045
|
+
updatedFilterCriteria.groups.push({
|
|
1046
|
+
operator: 'AND',
|
|
1047
|
+
filters: [],
|
|
1048
|
+
});
|
|
1049
|
+
|
|
1050
|
+
setFilterCriteria(updatedFilterCriteria);
|
|
912
1051
|
setShowFilteringSection(true);
|
|
913
1052
|
};
|
|
914
1053
|
|
|
915
1054
|
// Update a filter criterion
|
|
916
|
-
const handleFilterCriterionChange = (
|
|
917
|
-
|
|
918
|
-
|
|
1055
|
+
const handleFilterCriterionChange = (
|
|
1056
|
+
groupIndex,
|
|
1057
|
+
filterIndex,
|
|
1058
|
+
field,
|
|
1059
|
+
value
|
|
1060
|
+
) => {
|
|
1061
|
+
// Create a deep copy of the current filter criteria
|
|
1062
|
+
const updatedFilterCriteria = JSON.parse(
|
|
1063
|
+
JSON.stringify(filterCriteria)
|
|
1064
|
+
);
|
|
1065
|
+
|
|
1066
|
+
// Update the specified field of the filter
|
|
1067
|
+
updatedFilterCriteria.groups[groupIndex].filters[filterIndex][field] =
|
|
1068
|
+
value;
|
|
1069
|
+
|
|
919
1070
|
setFilterCriteria(updatedFilterCriteria);
|
|
920
1071
|
};
|
|
921
1072
|
|
|
922
1073
|
// Remove a filter criterion
|
|
923
|
-
const handleRemoveFilterCriterion = (
|
|
924
|
-
|
|
925
|
-
updatedFilterCriteria.
|
|
1074
|
+
const handleRemoveFilterCriterion = (groupIndex, filterIndex) => {
|
|
1075
|
+
// Create a deep copy of the current filter criteria
|
|
1076
|
+
const updatedFilterCriteria = JSON.parse(
|
|
1077
|
+
JSON.stringify(filterCriteria)
|
|
1078
|
+
);
|
|
1079
|
+
|
|
1080
|
+
// Remove the filter from the specified group
|
|
1081
|
+
updatedFilterCriteria.groups[groupIndex].filters.splice(filterIndex, 1);
|
|
1082
|
+
|
|
926
1083
|
setFilterCriteria(updatedFilterCriteria);
|
|
927
1084
|
|
|
928
|
-
//
|
|
929
|
-
|
|
1085
|
+
// Check if all groups are empty
|
|
1086
|
+
const allGroupsEmpty = updatedFilterCriteria.groups.every(
|
|
1087
|
+
(group) => group.filters.length === 0
|
|
1088
|
+
);
|
|
1089
|
+
|
|
1090
|
+
// Hide the section if all groups are empty
|
|
1091
|
+
if (allGroupsEmpty) {
|
|
1092
|
+
setShowFilteringSection(false);
|
|
1093
|
+
}
|
|
1094
|
+
};
|
|
1095
|
+
|
|
1096
|
+
// Update a filter group's operator (AND/OR)
|
|
1097
|
+
const handleFilterGroupOperatorChange = (groupIndex, newOperator) => {
|
|
1098
|
+
// Create a deep copy of the current filter criteria
|
|
1099
|
+
const updatedFilterCriteria = JSON.parse(
|
|
1100
|
+
JSON.stringify(filterCriteria)
|
|
1101
|
+
);
|
|
1102
|
+
|
|
1103
|
+
// Update the operator for the specified group
|
|
1104
|
+
updatedFilterCriteria.groups[groupIndex].operator = newOperator;
|
|
1105
|
+
|
|
1106
|
+
setFilterCriteria(updatedFilterCriteria);
|
|
1107
|
+
};
|
|
1108
|
+
|
|
1109
|
+
// Update the main filter operator (AND/OR between groups)
|
|
1110
|
+
const handleMainFilterOperatorChange = (newOperator) => {
|
|
1111
|
+
// Create a deep copy of the current filter criteria
|
|
1112
|
+
const updatedFilterCriteria = JSON.parse(
|
|
1113
|
+
JSON.stringify(filterCriteria)
|
|
1114
|
+
);
|
|
1115
|
+
|
|
1116
|
+
// Update the main operator
|
|
1117
|
+
updatedFilterCriteria.operator = newOperator;
|
|
1118
|
+
|
|
1119
|
+
setFilterCriteria(updatedFilterCriteria);
|
|
1120
|
+
};
|
|
1121
|
+
|
|
1122
|
+
// Remove a filter group
|
|
1123
|
+
const handleRemoveFilterGroup = (groupIndex) => {
|
|
1124
|
+
// Create a deep copy of the current filter criteria
|
|
1125
|
+
const updatedFilterCriteria = JSON.parse(
|
|
1126
|
+
JSON.stringify(filterCriteria)
|
|
1127
|
+
);
|
|
1128
|
+
|
|
1129
|
+
// Remove the group
|
|
1130
|
+
updatedFilterCriteria.groups.splice(groupIndex, 1);
|
|
1131
|
+
|
|
1132
|
+
// If no groups remain, add an empty one
|
|
1133
|
+
if (updatedFilterCriteria.groups.length === 0) {
|
|
1134
|
+
updatedFilterCriteria.groups.push({
|
|
1135
|
+
operator: 'AND',
|
|
1136
|
+
filters: [],
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
setFilterCriteria(updatedFilterCriteria);
|
|
1141
|
+
|
|
1142
|
+
// Check if all groups are empty
|
|
1143
|
+
const allGroupsEmpty = updatedFilterCriteria.groups.every(
|
|
1144
|
+
(group) => group.filters.length === 0
|
|
1145
|
+
);
|
|
1146
|
+
|
|
1147
|
+
// Hide the section if all groups are empty
|
|
1148
|
+
if (allGroupsEmpty) {
|
|
930
1149
|
setShowFilteringSection(false);
|
|
931
1150
|
}
|
|
932
1151
|
};
|
|
@@ -1134,11 +1353,36 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
1134
1353
|
}
|
|
1135
1354
|
|
|
1136
1355
|
// Set filtering criteria
|
|
1137
|
-
if (detail.filters
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1356
|
+
if (detail.filters) {
|
|
1357
|
+
// Check if it's the new structure with groups and operators
|
|
1358
|
+
if (
|
|
1359
|
+
detail.filters.groups &&
|
|
1360
|
+
Array.isArray(detail.filters.groups)
|
|
1361
|
+
) {
|
|
1362
|
+
setFilterCriteria(detail.filters);
|
|
1363
|
+
// Show filtering section if there are filter criteria in any group
|
|
1364
|
+
const hasFilters = detail.filters.groups.some(
|
|
1365
|
+
(group) => group.filters && group.filters.length > 0
|
|
1366
|
+
);
|
|
1367
|
+
if (hasFilters) {
|
|
1368
|
+
setShowFilteringSection(true);
|
|
1369
|
+
}
|
|
1370
|
+
} else if (Array.isArray(detail.filters)) {
|
|
1371
|
+
// Handle legacy format - convert to new structure
|
|
1372
|
+
const newFilterCriteria = {
|
|
1373
|
+
operator: 'AND',
|
|
1374
|
+
groups: [
|
|
1375
|
+
{
|
|
1376
|
+
operator: 'AND',
|
|
1377
|
+
filters: detail.filters,
|
|
1378
|
+
},
|
|
1379
|
+
],
|
|
1380
|
+
};
|
|
1381
|
+
setFilterCriteria(newFilterCriteria);
|
|
1382
|
+
// Show filtering section if there are filter criteria
|
|
1383
|
+
if (detail.filters.length > 0) {
|
|
1384
|
+
setShowFilteringSection(true);
|
|
1385
|
+
}
|
|
1142
1386
|
}
|
|
1143
1387
|
}
|
|
1144
1388
|
|
|
@@ -1222,11 +1466,37 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
1222
1466
|
}
|
|
1223
1467
|
|
|
1224
1468
|
// Set filtering criteria
|
|
1225
|
-
if (detail.filters
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1469
|
+
if (detail.filters) {
|
|
1470
|
+
// Check if it's the new structure with groups and operators
|
|
1471
|
+
if (
|
|
1472
|
+
detail.filters.groups &&
|
|
1473
|
+
Array.isArray(detail.filters.groups)
|
|
1474
|
+
) {
|
|
1475
|
+
setFilterCriteria(detail.filters);
|
|
1476
|
+
// Show filtering section if there are filter criteria in any group
|
|
1477
|
+
const hasFilters = detail.filters.groups.some(
|
|
1478
|
+
(group) =>
|
|
1479
|
+
group.filters && group.filters.length > 0
|
|
1480
|
+
);
|
|
1481
|
+
if (hasFilters) {
|
|
1482
|
+
setShowFilteringSection(true);
|
|
1483
|
+
}
|
|
1484
|
+
} else if (Array.isArray(detail.filters)) {
|
|
1485
|
+
// Handle legacy format - convert to new structure
|
|
1486
|
+
const newFilterCriteria = {
|
|
1487
|
+
operator: 'AND',
|
|
1488
|
+
groups: [
|
|
1489
|
+
{
|
|
1490
|
+
operator: 'AND',
|
|
1491
|
+
filters: detail.filters,
|
|
1492
|
+
},
|
|
1493
|
+
],
|
|
1494
|
+
};
|
|
1495
|
+
setFilterCriteria(newFilterCriteria);
|
|
1496
|
+
// Show filtering section if there are filter criteria
|
|
1497
|
+
if (detail.filters.length > 0) {
|
|
1498
|
+
setShowFilteringSection(true);
|
|
1499
|
+
}
|
|
1230
1500
|
}
|
|
1231
1501
|
}
|
|
1232
1502
|
|
|
@@ -1268,7 +1538,8 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
1268
1538
|
sourceColumn: join.sourceColumn,
|
|
1269
1539
|
targetColumn: join.targetColumn,
|
|
1270
1540
|
})),
|
|
1271
|
-
|
|
1541
|
+
// Convert the enhanced filter structure to a flat array that the backend expects
|
|
1542
|
+
filters: flattenFilterCriteria(filterCriteria),
|
|
1272
1543
|
sorting: sortCriteria,
|
|
1273
1544
|
};
|
|
1274
1545
|
|
|
@@ -1445,7 +1716,8 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
1445
1716
|
sourceColumn: join.sourceColumn,
|
|
1446
1717
|
targetColumn: join.targetColumn,
|
|
1447
1718
|
})),
|
|
1448
|
-
filters: filterCriteria,
|
|
1719
|
+
filters: filterCriteria, // Save the enhanced filter structure with groups and operators
|
|
1720
|
+
// Note: We're saving the full structure here, not flattening it, so we can load it properly later
|
|
1449
1721
|
sorting: sortCriteria,
|
|
1450
1722
|
};
|
|
1451
1723
|
|
|
@@ -1517,6 +1789,14 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
1517
1789
|
<Question size={18} />
|
|
1518
1790
|
<span>Data types</span>
|
|
1519
1791
|
</button>
|
|
1792
|
+
<button
|
|
1793
|
+
className={styles.helpButton}
|
|
1794
|
+
onClick={showFilteringHelp}
|
|
1795
|
+
title="Learn about filtering"
|
|
1796
|
+
>
|
|
1797
|
+
<Question size={18} />
|
|
1798
|
+
<span>Filtering</span>
|
|
1799
|
+
</button>
|
|
1520
1800
|
</div>
|
|
1521
1801
|
</div>
|
|
1522
1802
|
<div className={styles.titleInfo}>
|
|
@@ -2832,14 +3112,26 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
2832
3112
|
}
|
|
2833
3113
|
>
|
|
2834
3114
|
<h3>Filtering</h3>
|
|
2835
|
-
<div
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
3115
|
+
<div className={styles.sectionHeaderActions}>
|
|
3116
|
+
<button
|
|
3117
|
+
className={styles.helpButton}
|
|
3118
|
+
onClick={(e) => {
|
|
3119
|
+
e.stopPropagation();
|
|
3120
|
+
showFilteringHelp();
|
|
3121
|
+
}}
|
|
3122
|
+
title="Learn about filtering"
|
|
3123
|
+
>
|
|
3124
|
+
<Question size={16} />
|
|
3125
|
+
</button>
|
|
3126
|
+
<div
|
|
3127
|
+
className={`${styles.toggleBtn} ${
|
|
3128
|
+
showFilteringSection
|
|
3129
|
+
? styles.toggleBtnActive
|
|
3130
|
+
: ''
|
|
3131
|
+
}`}
|
|
3132
|
+
>
|
|
3133
|
+
{showFilteringSection ? '▲' : '▼'}
|
|
3134
|
+
</div>
|
|
2843
3135
|
</div>
|
|
2844
3136
|
</div>
|
|
2845
3137
|
|
|
@@ -2847,164 +3139,334 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
2847
3139
|
<>
|
|
2848
3140
|
<div
|
|
2849
3141
|
className={
|
|
2850
|
-
styles.
|
|
3142
|
+
styles.filterOperatorContainer
|
|
2851
3143
|
}
|
|
2852
3144
|
>
|
|
2853
|
-
<
|
|
2854
|
-
className={
|
|
2855
|
-
|
|
2856
|
-
disabled={
|
|
2857
|
-
selectedColumns.length === 0
|
|
3145
|
+
<div
|
|
3146
|
+
className={
|
|
3147
|
+
styles.mainFilterOperator
|
|
2858
3148
|
}
|
|
2859
3149
|
>
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
3150
|
+
<label>Match:</label>
|
|
3151
|
+
<div
|
|
3152
|
+
className={
|
|
3153
|
+
styles.operatorOptions
|
|
3154
|
+
}
|
|
3155
|
+
>
|
|
3156
|
+
<div
|
|
3157
|
+
className={`${
|
|
3158
|
+
styles.operatorOption
|
|
3159
|
+
} ${
|
|
3160
|
+
filterCriteria.operator ===
|
|
3161
|
+
'AND'
|
|
3162
|
+
? styles.operatorOptionSelected
|
|
3163
|
+
: ''
|
|
3164
|
+
}`}
|
|
3165
|
+
onClick={() =>
|
|
3166
|
+
handleMainFilterOperatorChange(
|
|
3167
|
+
'AND'
|
|
3168
|
+
)
|
|
3169
|
+
}
|
|
3170
|
+
>
|
|
3171
|
+
<div
|
|
3172
|
+
className={
|
|
3173
|
+
styles.operatorName
|
|
3174
|
+
}
|
|
3175
|
+
>
|
|
3176
|
+
ALL
|
|
3177
|
+
</div>
|
|
3178
|
+
<div
|
|
3179
|
+
className={
|
|
3180
|
+
styles.operatorDescription
|
|
3181
|
+
}
|
|
3182
|
+
>
|
|
3183
|
+
All filter groups must
|
|
3184
|
+
match
|
|
3185
|
+
</div>
|
|
3186
|
+
</div>
|
|
3187
|
+
<div
|
|
3188
|
+
className={`${
|
|
3189
|
+
styles.operatorOption
|
|
3190
|
+
} ${
|
|
3191
|
+
filterCriteria.operator ===
|
|
3192
|
+
'OR'
|
|
3193
|
+
? styles.operatorOptionSelected
|
|
3194
|
+
: ''
|
|
3195
|
+
}`}
|
|
3196
|
+
onClick={() =>
|
|
3197
|
+
handleMainFilterOperatorChange(
|
|
3198
|
+
'OR'
|
|
3199
|
+
)
|
|
3200
|
+
}
|
|
3201
|
+
>
|
|
3202
|
+
<div
|
|
3203
|
+
className={
|
|
3204
|
+
styles.operatorName
|
|
3205
|
+
}
|
|
3206
|
+
>
|
|
3207
|
+
ANY
|
|
3208
|
+
</div>
|
|
3209
|
+
<div
|
|
3210
|
+
className={
|
|
3211
|
+
styles.operatorDescription
|
|
3212
|
+
}
|
|
3213
|
+
>
|
|
3214
|
+
Any filter group can
|
|
3215
|
+
match
|
|
3216
|
+
</div>
|
|
3217
|
+
</div>
|
|
3218
|
+
</div>
|
|
3219
|
+
</div>
|
|
2863
3220
|
|
|
2864
|
-
{filterCriteria.length > 0 && (
|
|
2865
3221
|
<div
|
|
2866
3222
|
className={
|
|
2867
|
-
styles.
|
|
3223
|
+
styles.filterActionButtons
|
|
2868
3224
|
}
|
|
2869
3225
|
>
|
|
2870
|
-
|
|
2871
|
-
|
|
3226
|
+
<button
|
|
3227
|
+
className={styles.btn}
|
|
3228
|
+
onClick={handleAddFilterGroup}
|
|
3229
|
+
disabled={
|
|
3230
|
+
selectedColumns.length === 0
|
|
3231
|
+
}
|
|
3232
|
+
>
|
|
3233
|
+
Add Filter Group
|
|
3234
|
+
</button>
|
|
3235
|
+
</div>
|
|
3236
|
+
</div>
|
|
3237
|
+
|
|
3238
|
+
{filterCriteria.groups.map(
|
|
3239
|
+
(group, groupIndex) => (
|
|
3240
|
+
<div
|
|
3241
|
+
key={`group-${groupIndex}`}
|
|
3242
|
+
className={styles.filterGroup}
|
|
3243
|
+
>
|
|
3244
|
+
<div
|
|
3245
|
+
className={
|
|
3246
|
+
styles.filterGroupHeader
|
|
3247
|
+
}
|
|
3248
|
+
>
|
|
2872
3249
|
<div
|
|
2873
|
-
key={index}
|
|
2874
3250
|
className={
|
|
2875
|
-
styles.
|
|
3251
|
+
styles.filterGroupTitle
|
|
2876
3252
|
}
|
|
2877
3253
|
>
|
|
3254
|
+
<h4>
|
|
3255
|
+
Filter Group{' '}
|
|
3256
|
+
{groupIndex + 1}
|
|
3257
|
+
</h4>
|
|
2878
3258
|
<div
|
|
2879
3259
|
className={
|
|
2880
|
-
styles.
|
|
3260
|
+
styles.filterGroupOperator
|
|
2881
3261
|
}
|
|
2882
3262
|
>
|
|
2883
|
-
<
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
? `${formatName(
|
|
2888
|
-
criterion.table
|
|
2889
|
-
)}.${formatName(
|
|
2890
|
-
criterion.column
|
|
2891
|
-
)}`
|
|
2892
|
-
: `Criterion #${
|
|
2893
|
-
index +
|
|
2894
|
-
1
|
|
2895
|
-
}`}
|
|
2896
|
-
</h4>
|
|
2897
|
-
<button
|
|
3263
|
+
<label>
|
|
3264
|
+
Match:
|
|
3265
|
+
</label>
|
|
3266
|
+
<div
|
|
2898
3267
|
className={
|
|
2899
|
-
styles.
|
|
3268
|
+
styles.operatorButtonGroup
|
|
2900
3269
|
}
|
|
2901
|
-
onClick={() =>
|
|
2902
|
-
handleRemoveFilterCriterion(
|
|
2903
|
-
index
|
|
2904
|
-
)
|
|
2905
|
-
}
|
|
2906
|
-
title="Remove Filter Criterion"
|
|
2907
3270
|
>
|
|
2908
|
-
|
|
2909
|
-
|
|
3271
|
+
<button
|
|
3272
|
+
type="button"
|
|
3273
|
+
className={`${
|
|
3274
|
+
styles.operatorButton
|
|
3275
|
+
} ${
|
|
3276
|
+
group.operator ===
|
|
3277
|
+
'AND'
|
|
3278
|
+
? styles.operatorButtonSelected
|
|
3279
|
+
: ''
|
|
3280
|
+
}`}
|
|
3281
|
+
onClick={() =>
|
|
3282
|
+
handleFilterGroupOperatorChange(
|
|
3283
|
+
groupIndex,
|
|
3284
|
+
'AND'
|
|
3285
|
+
)
|
|
3286
|
+
}
|
|
3287
|
+
>
|
|
3288
|
+
ALL
|
|
3289
|
+
</button>
|
|
3290
|
+
<button
|
|
3291
|
+
type="button"
|
|
3292
|
+
className={`${
|
|
3293
|
+
styles.operatorButton
|
|
3294
|
+
} ${
|
|
3295
|
+
group.operator ===
|
|
3296
|
+
'OR'
|
|
3297
|
+
? styles.operatorButtonSelected
|
|
3298
|
+
: ''
|
|
3299
|
+
}`}
|
|
3300
|
+
onClick={() =>
|
|
3301
|
+
handleFilterGroupOperatorChange(
|
|
3302
|
+
groupIndex,
|
|
3303
|
+
'OR'
|
|
3304
|
+
)
|
|
3305
|
+
}
|
|
3306
|
+
>
|
|
3307
|
+
ANY
|
|
3308
|
+
</button>
|
|
3309
|
+
</div>
|
|
2910
3310
|
</div>
|
|
3311
|
+
</div>
|
|
2911
3312
|
|
|
2912
|
-
|
|
3313
|
+
<div
|
|
3314
|
+
className={
|
|
3315
|
+
styles.filterGroupActions
|
|
3316
|
+
}
|
|
3317
|
+
>
|
|
3318
|
+
<button
|
|
2913
3319
|
className={
|
|
2914
|
-
styles.
|
|
3320
|
+
styles.btn
|
|
3321
|
+
}
|
|
3322
|
+
onClick={() =>
|
|
3323
|
+
handleAddFilterCriterion(
|
|
3324
|
+
groupIndex
|
|
3325
|
+
)
|
|
3326
|
+
}
|
|
3327
|
+
disabled={
|
|
3328
|
+
selectedColumns.length ===
|
|
3329
|
+
0
|
|
2915
3330
|
}
|
|
2916
3331
|
>
|
|
2917
|
-
|
|
3332
|
+
Add Filter
|
|
3333
|
+
</button>
|
|
3334
|
+
{filterCriteria.groups
|
|
3335
|
+
.length > 1 && (
|
|
3336
|
+
<button
|
|
2918
3337
|
className={
|
|
2919
|
-
styles.
|
|
3338
|
+
styles.removeJoinBtn
|
|
2920
3339
|
}
|
|
3340
|
+
onClick={() =>
|
|
3341
|
+
handleRemoveFilterGroup(
|
|
3342
|
+
groupIndex
|
|
3343
|
+
)
|
|
3344
|
+
}
|
|
3345
|
+
title="Remove Filter Group"
|
|
2921
3346
|
>
|
|
3347
|
+
×
|
|
3348
|
+
</button>
|
|
3349
|
+
)}
|
|
3350
|
+
</div>
|
|
3351
|
+
</div>
|
|
3352
|
+
|
|
3353
|
+
{group.filters.length > 0 ? (
|
|
3354
|
+
<div
|
|
3355
|
+
className={
|
|
3356
|
+
styles.joinTablesContainer
|
|
3357
|
+
}
|
|
3358
|
+
>
|
|
3359
|
+
{group.filters.map(
|
|
3360
|
+
(
|
|
3361
|
+
criterion,
|
|
3362
|
+
filterIndex
|
|
3363
|
+
) => (
|
|
2922
3364
|
<div
|
|
3365
|
+
key={`filter-${groupIndex}-${filterIndex}`}
|
|
2923
3366
|
className={
|
|
2924
|
-
styles.
|
|
3367
|
+
styles.joinSection
|
|
2925
3368
|
}
|
|
2926
3369
|
>
|
|
2927
|
-
<
|
|
2928
|
-
Column:
|
|
2929
|
-
</label>
|
|
2930
|
-
<select
|
|
3370
|
+
<div
|
|
2931
3371
|
className={
|
|
2932
|
-
styles.
|
|
3372
|
+
styles.joinHeader
|
|
2933
3373
|
}
|
|
2934
|
-
value={`${criterion.table}.${criterion.column}`}
|
|
2935
|
-
onChange={(
|
|
2936
|
-
e
|
|
2937
|
-
) => {
|
|
2938
|
-
const [
|
|
2939
|
-
table,
|
|
2940
|
-
column,
|
|
2941
|
-
] =
|
|
2942
|
-
e.target.value.split(
|
|
2943
|
-
'.'
|
|
2944
|
-
);
|
|
2945
|
-
handleFilterCriterionChange(
|
|
2946
|
-
index,
|
|
2947
|
-
'table',
|
|
2948
|
-
table
|
|
2949
|
-
);
|
|
2950
|
-
handleFilterCriterionChange(
|
|
2951
|
-
index,
|
|
2952
|
-
'column',
|
|
2953
|
-
column
|
|
2954
|
-
);
|
|
2955
|
-
}}
|
|
2956
3374
|
>
|
|
2957
|
-
<
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
3375
|
+
<h4>
|
|
3376
|
+
Filter
|
|
3377
|
+
on{' '}
|
|
3378
|
+
{criterion.table &&
|
|
3379
|
+
criterion.column
|
|
3380
|
+
? `${formatName(
|
|
3381
|
+
criterion.table
|
|
3382
|
+
)}.${formatName(
|
|
3383
|
+
criterion.column
|
|
3384
|
+
)}`
|
|
3385
|
+
: `Criterion #${
|
|
3386
|
+
filterIndex +
|
|
3387
|
+
1
|
|
3388
|
+
}`}
|
|
3389
|
+
</h4>
|
|
3390
|
+
<button
|
|
3391
|
+
className={
|
|
3392
|
+
styles.removeJoinBtn
|
|
3393
|
+
}
|
|
3394
|
+
onClick={() =>
|
|
3395
|
+
handleRemoveFilterCriterion(
|
|
3396
|
+
groupIndex,
|
|
3397
|
+
filterIndex
|
|
2979
3398
|
)
|
|
2980
|
-
|
|
2981
|
-
|
|
3399
|
+
}
|
|
3400
|
+
title="Remove Filter Criterion"
|
|
3401
|
+
>
|
|
3402
|
+
×
|
|
3403
|
+
</button>
|
|
3404
|
+
</div>
|
|
2982
3405
|
|
|
2983
|
-
|
|
2984
|
-
{
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
3406
|
+
<div
|
|
3407
|
+
className={
|
|
3408
|
+
styles.joinGrid
|
|
3409
|
+
}
|
|
3410
|
+
>
|
|
3411
|
+
<div
|
|
3412
|
+
className={
|
|
3413
|
+
styles.joinGridColumn
|
|
3414
|
+
}
|
|
3415
|
+
>
|
|
3416
|
+
<div
|
|
3417
|
+
className={
|
|
3418
|
+
styles.joinField
|
|
3419
|
+
}
|
|
3420
|
+
>
|
|
3421
|
+
<label>
|
|
3422
|
+
Column:
|
|
3423
|
+
</label>
|
|
3424
|
+
<select
|
|
3425
|
+
className={
|
|
3426
|
+
styles.selectControl
|
|
3427
|
+
}
|
|
3428
|
+
value={`${criterion.table}.${criterion.column}`}
|
|
3429
|
+
onChange={(
|
|
3430
|
+
e
|
|
3431
|
+
) => {
|
|
3432
|
+
const [
|
|
3433
|
+
table,
|
|
3434
|
+
column,
|
|
3435
|
+
] =
|
|
3436
|
+
e.target.value.split(
|
|
3437
|
+
'.'
|
|
3438
|
+
);
|
|
3439
|
+
handleFilterCriterionChange(
|
|
3440
|
+
groupIndex,
|
|
3441
|
+
filterIndex,
|
|
3442
|
+
'table',
|
|
3443
|
+
table
|
|
3444
|
+
);
|
|
3445
|
+
handleFilterCriterionChange(
|
|
3446
|
+
groupIndex,
|
|
3447
|
+
filterIndex,
|
|
3448
|
+
'column',
|
|
3449
|
+
column
|
|
3450
|
+
);
|
|
3451
|
+
}}
|
|
3452
|
+
>
|
|
3453
|
+
<option value="">
|
|
3454
|
+
Select
|
|
3455
|
+
Column
|
|
3456
|
+
</option>
|
|
3457
|
+
{/* Main table columns */}
|
|
2995
3458
|
<optgroup
|
|
2996
|
-
key={`join-${joinIndex}`}
|
|
2997
3459
|
label={`${formatName(
|
|
2998
|
-
|
|
3460
|
+
selectedTable
|
|
2999
3461
|
)} Columns`}
|
|
3000
3462
|
>
|
|
3001
|
-
{
|
|
3463
|
+
{tableColumns.map(
|
|
3002
3464
|
(
|
|
3003
3465
|
column
|
|
3004
3466
|
) => (
|
|
3005
3467
|
<option
|
|
3006
|
-
key={`${
|
|
3007
|
-
value={`${
|
|
3468
|
+
key={`${selectedTable}.${column.name}`}
|
|
3469
|
+
value={`${selectedTable}.${column.name}`}
|
|
3008
3470
|
>
|
|
3009
3471
|
{formatName(
|
|
3010
3472
|
column.name
|
|
@@ -3013,144 +3475,194 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
3013
3475
|
)
|
|
3014
3476
|
)}
|
|
3015
3477
|
</optgroup>
|
|
3016
|
-
) : null
|
|
3017
|
-
)}
|
|
3018
|
-
</select>
|
|
3019
|
-
</div>
|
|
3020
|
-
</div>
|
|
3021
3478
|
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
Equals
|
|
3061
|
-
(!=)
|
|
3062
|
-
</option>
|
|
3063
|
-
<option value=">">
|
|
3064
|
-
Greater
|
|
3065
|
-
Than
|
|
3066
|
-
(>)
|
|
3067
|
-
</option>
|
|
3068
|
-
<option value="<">
|
|
3069
|
-
Less
|
|
3070
|
-
Than
|
|
3071
|
-
(<)
|
|
3072
|
-
</option>
|
|
3073
|
-
<option value=">=">
|
|
3074
|
-
Greater
|
|
3075
|
-
Than
|
|
3076
|
-
or
|
|
3077
|
-
Equal
|
|
3078
|
-
(>=)
|
|
3079
|
-
</option>
|
|
3080
|
-
<option value="<=">
|
|
3081
|
-
Less
|
|
3082
|
-
Than
|
|
3083
|
-
or
|
|
3084
|
-
Equal
|
|
3085
|
-
(<=)
|
|
3086
|
-
</option>
|
|
3087
|
-
<option value="LIKE">
|
|
3088
|
-
Contains
|
|
3089
|
-
(LIKE)
|
|
3090
|
-
</option>
|
|
3091
|
-
<option value="NOT LIKE">
|
|
3092
|
-
Does
|
|
3093
|
-
Not
|
|
3094
|
-
Contain
|
|
3095
|
-
(NOT
|
|
3096
|
-
LIKE)
|
|
3097
|
-
</option>
|
|
3098
|
-
<option value="IS NULL">
|
|
3099
|
-
Is
|
|
3100
|
-
Empty
|
|
3101
|
-
(NULL)
|
|
3102
|
-
</option>
|
|
3103
|
-
<option value="IS NOT NULL">
|
|
3104
|
-
Is
|
|
3105
|
-
Not
|
|
3106
|
-
Empty
|
|
3107
|
-
(NOT
|
|
3108
|
-
NULL)
|
|
3109
|
-
</option>
|
|
3110
|
-
</select>
|
|
3111
|
-
</div>
|
|
3479
|
+
{/* Joined tables columns */}
|
|
3480
|
+
{joins.map(
|
|
3481
|
+
(
|
|
3482
|
+
join,
|
|
3483
|
+
joinIndex
|
|
3484
|
+
) =>
|
|
3485
|
+
join.targetTable &&
|
|
3486
|
+
join.availableColumns &&
|
|
3487
|
+
join
|
|
3488
|
+
.availableColumns
|
|
3489
|
+
.length >
|
|
3490
|
+
0 ? (
|
|
3491
|
+
<optgroup
|
|
3492
|
+
key={`join-${joinIndex}`}
|
|
3493
|
+
label={`${formatName(
|
|
3494
|
+
join.targetTable
|
|
3495
|
+
)} Columns`}
|
|
3496
|
+
>
|
|
3497
|
+
{join.availableColumns.map(
|
|
3498
|
+
(
|
|
3499
|
+
column
|
|
3500
|
+
) => (
|
|
3501
|
+
<option
|
|
3502
|
+
key={`${join.targetTable}.${column.name}`}
|
|
3503
|
+
value={`${join.targetTable}.${column.name}`}
|
|
3504
|
+
>
|
|
3505
|
+
{formatName(
|
|
3506
|
+
column.name
|
|
3507
|
+
)}
|
|
3508
|
+
</option>
|
|
3509
|
+
)
|
|
3510
|
+
)}
|
|
3511
|
+
</optgroup>
|
|
3512
|
+
) : null
|
|
3513
|
+
)}
|
|
3514
|
+
</select>
|
|
3515
|
+
</div>
|
|
3516
|
+
</div>
|
|
3112
3517
|
|
|
3113
|
-
{criterion.operator !==
|
|
3114
|
-
'IS NULL' &&
|
|
3115
|
-
criterion.operator !==
|
|
3116
|
-
'IS NOT NULL' && (
|
|
3117
3518
|
<div
|
|
3118
3519
|
className={
|
|
3119
|
-
styles.
|
|
3520
|
+
styles.joinGridColumn
|
|
3120
3521
|
}
|
|
3121
3522
|
>
|
|
3122
|
-
<
|
|
3123
|
-
Value:
|
|
3124
|
-
</label>
|
|
3125
|
-
<input
|
|
3126
|
-
type="text"
|
|
3523
|
+
<div
|
|
3127
3524
|
className={
|
|
3128
|
-
styles.
|
|
3525
|
+
styles.joinField
|
|
3129
3526
|
}
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3527
|
+
>
|
|
3528
|
+
<label>
|
|
3529
|
+
Operator:
|
|
3530
|
+
</label>
|
|
3531
|
+
<select
|
|
3532
|
+
className={
|
|
3533
|
+
styles.selectControl
|
|
3534
|
+
}
|
|
3535
|
+
value={
|
|
3536
|
+
criterion.operator
|
|
3537
|
+
}
|
|
3538
|
+
onChange={(
|
|
3139
3539
|
e
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3540
|
+
) =>
|
|
3541
|
+
handleFilterCriterionChange(
|
|
3542
|
+
groupIndex,
|
|
3543
|
+
filterIndex,
|
|
3544
|
+
'operator',
|
|
3545
|
+
e
|
|
3546
|
+
.target
|
|
3547
|
+
.value
|
|
3548
|
+
)
|
|
3549
|
+
}
|
|
3550
|
+
>
|
|
3551
|
+
<option value="=">
|
|
3552
|
+
Is
|
|
3553
|
+
exactly
|
|
3554
|
+
equal
|
|
3555
|
+
to
|
|
3556
|
+
</option>
|
|
3557
|
+
<option value="!=">
|
|
3558
|
+
Is
|
|
3559
|
+
not
|
|
3560
|
+
equal
|
|
3561
|
+
to
|
|
3562
|
+
</option>
|
|
3563
|
+
<option value=">">
|
|
3564
|
+
Is
|
|
3565
|
+
greater
|
|
3566
|
+
than
|
|
3567
|
+
</option>
|
|
3568
|
+
<option value="<">
|
|
3569
|
+
Is
|
|
3570
|
+
less
|
|
3571
|
+
than
|
|
3572
|
+
</option>
|
|
3573
|
+
<option value=">=">
|
|
3574
|
+
Is
|
|
3575
|
+
greater
|
|
3576
|
+
than
|
|
3577
|
+
or
|
|
3578
|
+
equal
|
|
3579
|
+
to
|
|
3580
|
+
</option>
|
|
3581
|
+
<option value="<=">
|
|
3582
|
+
Is
|
|
3583
|
+
less
|
|
3584
|
+
than
|
|
3585
|
+
or
|
|
3586
|
+
equal
|
|
3587
|
+
to
|
|
3588
|
+
</option>
|
|
3589
|
+
<option value="LIKE">
|
|
3590
|
+
Contains
|
|
3591
|
+
</option>
|
|
3592
|
+
<option value="NOT LIKE">
|
|
3593
|
+
Does
|
|
3594
|
+
not
|
|
3595
|
+
contain
|
|
3596
|
+
</option>
|
|
3597
|
+
<option value="IS NULL">
|
|
3598
|
+
Is
|
|
3599
|
+
empty
|
|
3600
|
+
</option>
|
|
3601
|
+
<option value="IS NOT NULL">
|
|
3602
|
+
Is
|
|
3603
|
+
not
|
|
3604
|
+
empty
|
|
3605
|
+
</option>
|
|
3606
|
+
</select>
|
|
3607
|
+
</div>
|
|
3608
|
+
|
|
3609
|
+
{criterion.operator !==
|
|
3610
|
+
'IS NULL' &&
|
|
3611
|
+
criterion.operator !==
|
|
3612
|
+
'IS NOT NULL' && (
|
|
3613
|
+
<div
|
|
3614
|
+
className={
|
|
3615
|
+
styles.joinField
|
|
3616
|
+
}
|
|
3617
|
+
>
|
|
3618
|
+
<label>
|
|
3619
|
+
Value:
|
|
3620
|
+
</label>
|
|
3621
|
+
<input
|
|
3622
|
+
type="text"
|
|
3623
|
+
className={
|
|
3624
|
+
styles.formControl
|
|
3625
|
+
}
|
|
3626
|
+
value={
|
|
3627
|
+
criterion.value
|
|
3628
|
+
}
|
|
3629
|
+
onChange={(
|
|
3630
|
+
e
|
|
3631
|
+
) =>
|
|
3632
|
+
handleFilterCriterionChange(
|
|
3633
|
+
groupIndex,
|
|
3634
|
+
filterIndex,
|
|
3635
|
+
'value',
|
|
3636
|
+
e
|
|
3637
|
+
.target
|
|
3638
|
+
.value
|
|
3639
|
+
)
|
|
3640
|
+
}
|
|
3641
|
+
placeholder="Enter filter value"
|
|
3642
|
+
/>
|
|
3643
|
+
</div>
|
|
3644
|
+
)}
|
|
3146
3645
|
</div>
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3646
|
+
</div>
|
|
3647
|
+
</div>
|
|
3648
|
+
)
|
|
3649
|
+
)}
|
|
3150
3650
|
</div>
|
|
3151
|
-
)
|
|
3152
|
-
|
|
3153
|
-
|
|
3651
|
+
) : (
|
|
3652
|
+
<div
|
|
3653
|
+
className={
|
|
3654
|
+
styles.emptyFilterGroup
|
|
3655
|
+
}
|
|
3656
|
+
>
|
|
3657
|
+
<p>
|
|
3658
|
+
No filters in this
|
|
3659
|
+
group. Click "Add
|
|
3660
|
+
Filter" to add one.
|
|
3661
|
+
</p>
|
|
3662
|
+
</div>
|
|
3663
|
+
)}
|
|
3664
|
+
</div>
|
|
3665
|
+
)
|
|
3154
3666
|
)}
|
|
3155
3667
|
</>
|
|
3156
3668
|
)}
|