@visns-studio/visns-components 5.5.1 → 5.5.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/package.json +6 -5
- package/src/components/crm/DataGrid.jsx +357 -15
- package/src/components/crm/MultiSelect.jsx +19 -2
- package/src/components/crm/generic/GenericReport.jsx +1426 -330
- package/src/components/crm/generic/styles/GenericReport.module.scss +140 -23
- package/src/components/crm/generic/styles/SweetAlertCustom.css +85 -0
- package/src/components/crm/styles/DataGrid.module.scss +468 -13
- package/src/components/styles/global.css +138 -0
|
@@ -2,11 +2,13 @@ import React, { useState, useEffect } from 'react';
|
|
|
2
2
|
import { toast } from 'react-toastify';
|
|
3
3
|
import moment from 'moment';
|
|
4
4
|
import ReactDataGrid from '@inovua/reactdatagrid-community';
|
|
5
|
-
import { CircleX } from 'akar-icons';
|
|
5
|
+
import { CircleX, Question } from 'akar-icons';
|
|
6
6
|
import CustomFetch from '../Fetch';
|
|
7
7
|
import Download from '../Download';
|
|
8
8
|
import { saveAs } from 'file-saver';
|
|
9
|
+
import Swal from 'sweetalert2';
|
|
9
10
|
import styles from './styles/GenericReport.module.scss';
|
|
11
|
+
import './styles/SweetAlertCustom.css';
|
|
10
12
|
|
|
11
13
|
// Utility function to format names from camelCase or snake_case to Title Case with spaces
|
|
12
14
|
const formatName = (name) => {
|
|
@@ -179,6 +181,142 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
179
181
|
// Extract settings with defaults
|
|
180
182
|
const { tableUrl, columnUrl } = setting;
|
|
181
183
|
|
|
184
|
+
// Help functions for showing popups
|
|
185
|
+
const showReportBuilderHelp = () => {
|
|
186
|
+
Swal.fire({
|
|
187
|
+
title: 'How to Use the Report Builder',
|
|
188
|
+
html: `
|
|
189
|
+
<div style="text-align: left; margin-bottom: 20px; font-size: 14px; color: #4b5563;">
|
|
190
|
+
<h4 style="font-size: 16px; color: #1f2937; margin-bottom: 10px; font-weight: 600;">Getting Started:</h4>
|
|
191
|
+
<ol style="padding-left: 18px; margin-bottom: 14px; line-height: 1.4;">
|
|
192
|
+
<li>Select a table from the left panel</li>
|
|
193
|
+
<li>Choose columns you want to include in your report</li>
|
|
194
|
+
<li>Optionally, add relationships to connect data from different tables</li>
|
|
195
|
+
<li>Click "Execute Query" to preview your report</li>
|
|
196
|
+
<li>Save or export your report when ready</li>
|
|
197
|
+
</ol>
|
|
198
|
+
|
|
199
|
+
<h4 style="font-size: 16px; color: #1f2937; margin-bottom: 10px; margin-top: 16px; font-weight: 600;">Understanding Relationships:</h4>
|
|
200
|
+
<p style="margin-bottom: 8px; line-height: 1.4;">Tables in a database are connected through relationships. For example:</p>
|
|
201
|
+
<ul style="padding-left: 18px; margin-bottom: 14px; line-height: 1.4; font-size: 14px !important;">
|
|
202
|
+
<li style="font-size: 14px !important; margin-bottom: 4px;"><span style="font-weight: 500;">Clients → Sites</span>: Each client can have multiple sites</li>
|
|
203
|
+
<li style="font-size: 14px !important; margin-bottom: 4px;"><span style="font-weight: 500;">Sites → Contacts</span>: Each site can have multiple contacts</li>
|
|
204
|
+
</ul>
|
|
205
|
+
<p style="margin-bottom: 8px; line-height: 1.4;">Adding these relationships lets you combine data from multiple tables.</p>
|
|
206
|
+
|
|
207
|
+
<h4 style="font-size: 16px; color: #1f2937; margin-bottom: 10px; margin-top: 16px; font-weight: 600;">Suggested Relationships:</h4>
|
|
208
|
+
<p style="margin-bottom: 8px; line-height: 1.4;">The system automatically suggests relationships based on the database structure:</p>
|
|
209
|
+
<ul style="padding-left: 18px; line-height: 1.4; font-size: 14px !important; margin-bottom: 14px;">
|
|
210
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="color: #f59e0b;">⭐⭐⭐</span> = High confidence (very likely correct)</li>
|
|
211
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="color: #f59e0b;">⭐⭐</span> = Medium confidence</li>
|
|
212
|
+
<li style="font-size: 14px !important;"><span style="color: #f59e0b;">⭐</span> = Low confidence (might need verification)</li>
|
|
213
|
+
</ul>
|
|
214
|
+
|
|
215
|
+
<h4 style="font-size: 16px; color: #1f2937; margin-bottom: 10px; margin-top: 16px; font-weight: 600;">Using Sorting and Filtering:</h4>
|
|
216
|
+
<p style="margin-bottom: 8px; line-height: 1.4;">Customize your reports with powerful sorting and filtering options:</p>
|
|
217
|
+
<ul style="padding-left: 18px; line-height: 1.4; font-size: 14px !important; margin-bottom: 14px;">
|
|
218
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><strong>Sorting</strong>: Click the "Sorting" header to expand the section, then add sort criteria to order your results</li>
|
|
219
|
+
<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
|
+
<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
|
+
<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 equals, contains, greater than, etc. to narrow down results</li>
|
|
223
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;">Combine multiple filters for precise data selection (all filters are combined with AND logic)</li>
|
|
224
|
+
<li style="font-size: 14px !important;">Sorting and filtering settings are saved with your report for future use</li>
|
|
225
|
+
</ul>
|
|
226
|
+
|
|
227
|
+
<h4 style="font-size: 16px; color: #1f2937; margin-bottom: 10px; margin-top: 16px; font-weight: 600;">Tips for Effective Reports:</h4>
|
|
228
|
+
<p style="margin-bottom: 8px; line-height: 1.4;">Follow these best practices for creating useful reports:</p>
|
|
229
|
+
<ul style="padding-left: 18px; line-height: 1.4; font-size: 14px !important; margin-bottom: 14px;">
|
|
230
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;">Start with the main table that contains your primary data</li>
|
|
231
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;">Select only the columns you need to avoid cluttered reports</li>
|
|
232
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;">Use joins to bring in related data rather than creating separate reports</li>
|
|
233
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;">Consider adding date fields to enable time-based filtering</li>
|
|
234
|
+
<li style="font-size: 14px !important;">Save frequently used reports for quick access</li>
|
|
235
|
+
</ul>
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
</div>
|
|
239
|
+
`,
|
|
240
|
+
width: 550,
|
|
241
|
+
confirmButtonText: 'Got it!',
|
|
242
|
+
confirmButtonColor: '#2563eb',
|
|
243
|
+
customClass: {
|
|
244
|
+
title: 'swal-title',
|
|
245
|
+
htmlContainer: 'swal-html-container',
|
|
246
|
+
confirmButton: 'swal-confirm-button',
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const showDataTypesHelp = () => {
|
|
252
|
+
Swal.fire({
|
|
253
|
+
title: 'Understanding Data Types',
|
|
254
|
+
html: `
|
|
255
|
+
<div style="text-align: left; margin-bottom: 20px; font-size: 14px; color: #4b5563;">
|
|
256
|
+
<p style="margin-bottom: 12px; line-height: 1.4;">When selecting columns, you'll see different data types. Here's what they mean:</p>
|
|
257
|
+
|
|
258
|
+
<h4 style="font-size: 16px; color: #1f2937; margin-bottom: 10px; font-weight: 600;">String Data Types:</h4>
|
|
259
|
+
<ul style="padding-left: 18px; margin-bottom: 14px; line-height: 1.4; font-size: 14px !important;">
|
|
260
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">CHAR</span>: Fixed-length text (codes, abbreviations)</li>
|
|
261
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">VARCHAR</span>: Variable-length text (names, descriptions)</li>
|
|
262
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">TINYTEXT</span>: Very small text (up to 255 characters)</li>
|
|
263
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">TEXT</span>: Standard text (up to 65,535 characters)</li>
|
|
264
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">MEDIUMTEXT</span>: Medium-sized text (up to 16.7 million characters)</li>
|
|
265
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">LONGTEXT</span>: Large text (up to 4.2 billion characters)</li>
|
|
266
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">ENUM</span>: Field with predefined options</li>
|
|
267
|
+
<li style="font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">SET</span>: Field that can contain multiple predefined options</li>
|
|
268
|
+
</ul>
|
|
269
|
+
|
|
270
|
+
<h4 style="font-size: 16px; color: #1f2937; margin-bottom: 10px; margin-top: 16px; font-weight: 600;">Numeric Data Types:</h4>
|
|
271
|
+
<ul style="padding-left: 18px; margin-bottom: 14px; line-height: 1.4; font-size: 14px !important;">
|
|
272
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">TINYINT</span>: Very small integers (-128 to 127)</li>
|
|
273
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">SMALLINT</span>: Small integers (-32,768 to 32,767)</li>
|
|
274
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">MEDIUMINT</span>: Medium-sized integers (-8.3M to 8.3M)</li>
|
|
275
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">INT/INTEGER</span>: Standard integers (-2.1B to 2.1B)</li>
|
|
276
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">BIGINT</span>: Large integers (very large range)</li>
|
|
277
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">DECIMAL/NUMERIC</span>: Fixed-point numbers (precise values)</li>
|
|
278
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">FLOAT</span>: Single-precision floating point</li>
|
|
279
|
+
<li style="font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">DOUBLE</span>: Double-precision floating point</li>
|
|
280
|
+
</ul>
|
|
281
|
+
|
|
282
|
+
<h4 style="font-size: 16px; color: #1f2937; margin-bottom: 10px; margin-top: 16px; font-weight: 600;">Date and Time Types:</h4>
|
|
283
|
+
<ul style="padding-left: 18px; margin-bottom: 14px; line-height: 1.4; font-size: 14px !important;">
|
|
284
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">DATE</span>: Date values (YYYY-MM-DD)</li>
|
|
285
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">TIME</span>: Time values (HH:MM:SS)</li>
|
|
286
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">DATETIME</span>: Date and time (YYYY-MM-DD HH:MM:SS)</li>
|
|
287
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">TIMESTAMP</span>: Date and time with timezone awareness</li>
|
|
288
|
+
<li style="font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">YEAR</span>: Year values (YYYY)</li>
|
|
289
|
+
</ul>
|
|
290
|
+
|
|
291
|
+
<h4 style="font-size: 16px; color: #1f2937; margin-bottom: 10px; margin-top: 16px; font-weight: 600;">Binary and Specialized Types:</h4>
|
|
292
|
+
<ul style="padding-left: 18px; margin-bottom: 14px; line-height: 1.4; font-size: 14px !important;">
|
|
293
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">BINARY/VARBINARY</span>: Binary strings</li>
|
|
294
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">TINYBLOB</span>: Very small binary objects (up to 255 bytes)</li>
|
|
295
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">BLOB</span>: Binary objects (up to 65,535 bytes)</li>
|
|
296
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">MEDIUMBLOB</span>: Medium-sized binary objects</li>
|
|
297
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">LONGBLOB</span>: Large binary objects</li>
|
|
298
|
+
<li style="margin-bottom: 4px; font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">JSON</span>: Structured data in JSON format</li>
|
|
299
|
+
<li style="font-size: 14px !important;"><span style="font-weight: 500; color: #2563eb;">BOOLEAN/BOOL</span>: True/false values (stored as TINYINT)</li>
|
|
300
|
+
</ul>
|
|
301
|
+
|
|
302
|
+
<h4 style="font-size: 16px; color: #1f2937; margin-bottom: 10px; margin-top: 16px; font-weight: 600;">Primary Keys:</h4>
|
|
303
|
+
<p style="line-height: 1.4; margin-bottom: 8px;">Columns marked as "Primary Key" uniquely identify each record in a table. These are often used to create relationships between tables.</p>
|
|
304
|
+
|
|
305
|
+
<h4 style="font-size: 16px; color: #1f2937; margin-bottom: 10px; margin-top: 16px; font-weight: 600;">Foreign Keys:</h4>
|
|
306
|
+
<p style="line-height: 1.4;">Foreign keys connect tables by referencing the primary key of another table. They enable the creation of relationships between tables.</p>
|
|
307
|
+
</div>
|
|
308
|
+
`,
|
|
309
|
+
width: 650,
|
|
310
|
+
confirmButtonText: 'Got it!',
|
|
311
|
+
confirmButtonColor: '#2563eb',
|
|
312
|
+
customClass: {
|
|
313
|
+
title: 'swal-title',
|
|
314
|
+
htmlContainer: 'swal-html-container',
|
|
315
|
+
confirmButton: 'swal-confirm-button',
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
};
|
|
319
|
+
|
|
182
320
|
// No mock data - component will rely on API responses
|
|
183
321
|
|
|
184
322
|
// State for database schema
|
|
@@ -204,6 +342,17 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
204
342
|
const [suggestedJoins, setSuggestedJoins] = useState({});
|
|
205
343
|
const [isLoadingSuggestedJoins, setIsLoadingSuggestedJoins] = useState({});
|
|
206
344
|
|
|
345
|
+
// State for toggling visibility of sections
|
|
346
|
+
const [showSuggestedRelationships, setShowSuggestedRelationships] =
|
|
347
|
+
useState({});
|
|
348
|
+
const [showJoinTables, setShowJoinTables] = useState(false);
|
|
349
|
+
const [showSortingSection, setShowSortingSection] = useState(false);
|
|
350
|
+
const [showFilteringSection, setShowFilteringSection] = useState(false);
|
|
351
|
+
|
|
352
|
+
// State for sorting and filtering
|
|
353
|
+
const [sortCriteria, setSortCriteria] = useState([]);
|
|
354
|
+
const [filterCriteria, setFilterCriteria] = useState([]);
|
|
355
|
+
|
|
207
356
|
// State for report execution
|
|
208
357
|
const [totalResults, setTotalResults] = useState(0);
|
|
209
358
|
const [executedSql, setExecutedSql] = useState('');
|
|
@@ -306,6 +455,11 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
306
455
|
|
|
307
456
|
// Fetch suggested joins for a table
|
|
308
457
|
const fetchSuggestedJoins = async (tableName, tableKey) => {
|
|
458
|
+
// Initialize visibility state for this table's relationships (default: hidden)
|
|
459
|
+
setShowSuggestedRelationships((prev) => ({
|
|
460
|
+
...prev,
|
|
461
|
+
[tableKey]: false,
|
|
462
|
+
}));
|
|
309
463
|
// Update loading state for this specific table
|
|
310
464
|
setIsLoadingSuggestedJoins((prev) => ({
|
|
311
465
|
...prev,
|
|
@@ -430,6 +584,11 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
430
584
|
setPreviewData([]);
|
|
431
585
|
setExecutedSql('');
|
|
432
586
|
setTotalResults(0);
|
|
587
|
+
setShowJoinTables(false);
|
|
588
|
+
setShowSortingSection(false);
|
|
589
|
+
setShowFilteringSection(false);
|
|
590
|
+
setSortCriteria([]);
|
|
591
|
+
setFilterCriteria([]);
|
|
433
592
|
};
|
|
434
593
|
|
|
435
594
|
// Handle table selection
|
|
@@ -533,6 +692,8 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
533
692
|
};
|
|
534
693
|
|
|
535
694
|
setJoins([...joins, newJoin]);
|
|
695
|
+
// Show the join tables section when adding a new join
|
|
696
|
+
setShowJoinTables(true);
|
|
536
697
|
};
|
|
537
698
|
|
|
538
699
|
// Add a suggested join
|
|
@@ -561,6 +722,9 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
561
722
|
setJoins([...joins, firstJoin, secondJoin]);
|
|
562
723
|
toast.success('Added many-to-many relationship with pivot table');
|
|
563
724
|
|
|
725
|
+
// Show the join tables section when adding a suggested join
|
|
726
|
+
setShowJoinTables(true);
|
|
727
|
+
|
|
564
728
|
// Fetch columns for both target tables
|
|
565
729
|
fetchJoinTableColumns(suggestedJoin.targetTable, joins.length);
|
|
566
730
|
if (suggestedJoin.secondJoin.targetTable) {
|
|
@@ -583,6 +747,9 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
583
747
|
setJoins([...joins, newJoin]);
|
|
584
748
|
toast.success('Added suggested join');
|
|
585
749
|
|
|
750
|
+
// Show the join tables section when adding a suggested join
|
|
751
|
+
setShowJoinTables(true);
|
|
752
|
+
|
|
586
753
|
// Fetch columns for the target table
|
|
587
754
|
if (suggestedJoin.targetTable) {
|
|
588
755
|
fetchJoinTableColumns(suggestedJoin.targetTable, joins.length);
|
|
@@ -681,6 +848,89 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
681
848
|
setJoins(updatedJoins);
|
|
682
849
|
};
|
|
683
850
|
|
|
851
|
+
// Add a new sort criterion
|
|
852
|
+
const handleAddSortCriterion = () => {
|
|
853
|
+
// Default to the first selected column if available
|
|
854
|
+
const defaultColumn =
|
|
855
|
+
selectedColumns.length > 0
|
|
856
|
+
? {
|
|
857
|
+
table: selectedColumns[0].table,
|
|
858
|
+
column: selectedColumns[0].column,
|
|
859
|
+
displayName: selectedColumns[0].displayName,
|
|
860
|
+
}
|
|
861
|
+
: null;
|
|
862
|
+
|
|
863
|
+
const newSortCriterion = {
|
|
864
|
+
table: defaultColumn?.table || '',
|
|
865
|
+
column: defaultColumn?.column || '',
|
|
866
|
+
direction: 'ASC',
|
|
867
|
+
};
|
|
868
|
+
|
|
869
|
+
setSortCriteria([...sortCriteria, newSortCriterion]);
|
|
870
|
+
setShowSortingSection(true);
|
|
871
|
+
};
|
|
872
|
+
|
|
873
|
+
// Update a sort criterion
|
|
874
|
+
const handleSortCriterionChange = (index, field, value) => {
|
|
875
|
+
const updatedSortCriteria = [...sortCriteria];
|
|
876
|
+
updatedSortCriteria[index][field] = value;
|
|
877
|
+
setSortCriteria(updatedSortCriteria);
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
// Remove a sort criterion
|
|
881
|
+
const handleRemoveSortCriterion = (index) => {
|
|
882
|
+
const updatedSortCriteria = [...sortCriteria];
|
|
883
|
+
updatedSortCriteria.splice(index, 1);
|
|
884
|
+
setSortCriteria(updatedSortCriteria);
|
|
885
|
+
|
|
886
|
+
// Hide the section if no sort criteria remain
|
|
887
|
+
if (updatedSortCriteria.length === 0) {
|
|
888
|
+
setShowSortingSection(false);
|
|
889
|
+
}
|
|
890
|
+
};
|
|
891
|
+
|
|
892
|
+
// Add a new filter criterion
|
|
893
|
+
const handleAddFilterCriterion = () => {
|
|
894
|
+
// Default to the first selected column if available
|
|
895
|
+
const defaultColumn =
|
|
896
|
+
selectedColumns.length > 0
|
|
897
|
+
? {
|
|
898
|
+
table: selectedColumns[0].table,
|
|
899
|
+
column: selectedColumns[0].column,
|
|
900
|
+
displayName: selectedColumns[0].displayName,
|
|
901
|
+
}
|
|
902
|
+
: null;
|
|
903
|
+
|
|
904
|
+
const newFilterCriterion = {
|
|
905
|
+
table: defaultColumn?.table || '',
|
|
906
|
+
column: defaultColumn?.column || '',
|
|
907
|
+
operator: '=',
|
|
908
|
+
value: '',
|
|
909
|
+
};
|
|
910
|
+
|
|
911
|
+
setFilterCriteria([...filterCriteria, newFilterCriterion]);
|
|
912
|
+
setShowFilteringSection(true);
|
|
913
|
+
};
|
|
914
|
+
|
|
915
|
+
// Update a filter criterion
|
|
916
|
+
const handleFilterCriterionChange = (index, field, value) => {
|
|
917
|
+
const updatedFilterCriteria = [...filterCriteria];
|
|
918
|
+
updatedFilterCriteria[index][field] = value;
|
|
919
|
+
setFilterCriteria(updatedFilterCriteria);
|
|
920
|
+
};
|
|
921
|
+
|
|
922
|
+
// Remove a filter criterion
|
|
923
|
+
const handleRemoveFilterCriterion = (index) => {
|
|
924
|
+
const updatedFilterCriteria = [...filterCriteria];
|
|
925
|
+
updatedFilterCriteria.splice(index, 1);
|
|
926
|
+
setFilterCriteria(updatedFilterCriteria);
|
|
927
|
+
|
|
928
|
+
// Hide the section if no filter criteria remain
|
|
929
|
+
if (updatedFilterCriteria.length === 0) {
|
|
930
|
+
setShowFilteringSection(false);
|
|
931
|
+
}
|
|
932
|
+
};
|
|
933
|
+
|
|
684
934
|
// Delete a saved report
|
|
685
935
|
const handleDeleteReport = async (reportId) => {
|
|
686
936
|
if (!reportId) return;
|
|
@@ -868,6 +1118,28 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
868
1118
|
// Set joins
|
|
869
1119
|
if (detail.joins && Array.isArray(detail.joins)) {
|
|
870
1120
|
setJoins(detail.joins);
|
|
1121
|
+
// Show join tables section if there are joins
|
|
1122
|
+
if (detail.joins.length > 0) {
|
|
1123
|
+
setShowJoinTables(true);
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
// Set sorting criteria
|
|
1128
|
+
if (detail.sorting && Array.isArray(detail.sorting)) {
|
|
1129
|
+
setSortCriteria(detail.sorting);
|
|
1130
|
+
// Show sorting section if there are sort criteria
|
|
1131
|
+
if (detail.sorting.length > 0) {
|
|
1132
|
+
setShowSortingSection(true);
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
// Set filtering criteria
|
|
1137
|
+
if (detail.filters && Array.isArray(detail.filters)) {
|
|
1138
|
+
setFilterCriteria(detail.filters);
|
|
1139
|
+
// Show filtering section if there are filter criteria
|
|
1140
|
+
if (detail.filters.length > 0) {
|
|
1141
|
+
setShowFilteringSection(true);
|
|
1142
|
+
}
|
|
871
1143
|
}
|
|
872
1144
|
|
|
873
1145
|
toast.success(
|
|
@@ -934,6 +1206,28 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
934
1206
|
// Set joins
|
|
935
1207
|
if (detail.joins && Array.isArray(detail.joins)) {
|
|
936
1208
|
setJoins(detail.joins);
|
|
1209
|
+
// Show join tables section if there are joins
|
|
1210
|
+
if (detail.joins.length > 0) {
|
|
1211
|
+
setShowJoinTables(true);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
// Set sorting criteria
|
|
1216
|
+
if (detail.sorting && Array.isArray(detail.sorting)) {
|
|
1217
|
+
setSortCriteria(detail.sorting);
|
|
1218
|
+
// Show sorting section if there are sort criteria
|
|
1219
|
+
if (detail.sorting.length > 0) {
|
|
1220
|
+
setShowSortingSection(true);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
// Set filtering criteria
|
|
1225
|
+
if (detail.filters && Array.isArray(detail.filters)) {
|
|
1226
|
+
setFilterCriteria(detail.filters);
|
|
1227
|
+
// Show filtering section if there are filter criteria
|
|
1228
|
+
if (detail.filters.length > 0) {
|
|
1229
|
+
setShowFilteringSection(true);
|
|
1230
|
+
}
|
|
937
1231
|
}
|
|
938
1232
|
|
|
939
1233
|
// Wait a moment for state to update
|
|
@@ -974,8 +1268,8 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
974
1268
|
sourceColumn: join.sourceColumn,
|
|
975
1269
|
targetColumn: join.targetColumn,
|
|
976
1270
|
})),
|
|
977
|
-
filters:
|
|
978
|
-
sorting:
|
|
1271
|
+
filters: filterCriteria,
|
|
1272
|
+
sorting: sortCriteria,
|
|
979
1273
|
};
|
|
980
1274
|
|
|
981
1275
|
// Execute query
|
|
@@ -1151,8 +1445,8 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
1151
1445
|
sourceColumn: join.sourceColumn,
|
|
1152
1446
|
targetColumn: join.targetColumn,
|
|
1153
1447
|
})),
|
|
1154
|
-
filters:
|
|
1155
|
-
sorting:
|
|
1448
|
+
filters: filterCriteria,
|
|
1449
|
+
sorting: sortCriteria,
|
|
1156
1450
|
};
|
|
1157
1451
|
|
|
1158
1452
|
// Determine if we're updating or creating a new report
|
|
@@ -1204,7 +1498,27 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
1204
1498
|
<>
|
|
1205
1499
|
<div className={styles.grid__full}>
|
|
1206
1500
|
<div className={styles.crmtitle}>
|
|
1207
|
-
<
|
|
1501
|
+
<div className={styles.titleContainer}>
|
|
1502
|
+
<h1>Report Builder</h1>
|
|
1503
|
+
<div className={styles.helpButtons}>
|
|
1504
|
+
<button
|
|
1505
|
+
className={styles.helpButton}
|
|
1506
|
+
onClick={showReportBuilderHelp}
|
|
1507
|
+
title="How to use the Report Builder"
|
|
1508
|
+
>
|
|
1509
|
+
<Question size={18} />
|
|
1510
|
+
<span>How to use</span>
|
|
1511
|
+
</button>
|
|
1512
|
+
<button
|
|
1513
|
+
className={styles.helpButton}
|
|
1514
|
+
onClick={showDataTypesHelp}
|
|
1515
|
+
title="Understanding data types"
|
|
1516
|
+
>
|
|
1517
|
+
<Question size={18} />
|
|
1518
|
+
<span>Data types</span>
|
|
1519
|
+
</button>
|
|
1520
|
+
</div>
|
|
1521
|
+
</div>
|
|
1208
1522
|
<div className={styles.titleInfo}>
|
|
1209
1523
|
<span>
|
|
1210
1524
|
Create custom reports by selecting tables, columns,
|
|
@@ -1564,213 +1878,177 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
1564
1878
|
)}
|
|
1565
1879
|
</div>
|
|
1566
1880
|
|
|
1567
|
-
{
|
|
1568
|
-
<div
|
|
1881
|
+
<div className={styles.reportSection}>
|
|
1882
|
+
<div
|
|
1883
|
+
className={styles.sectionHeader}
|
|
1884
|
+
onClick={() =>
|
|
1885
|
+
setShowJoinTables(!showJoinTables)
|
|
1886
|
+
}
|
|
1887
|
+
style={{ cursor: 'pointer' }}
|
|
1888
|
+
title={
|
|
1889
|
+
showJoinTables
|
|
1890
|
+
? 'Hide join tables'
|
|
1891
|
+
: 'Show join tables'
|
|
1892
|
+
}
|
|
1893
|
+
>
|
|
1569
1894
|
<h3>Join Tables</h3>
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1895
|
+
<div
|
|
1896
|
+
className={`${styles.toggleBtn} ${
|
|
1897
|
+
showJoinTables
|
|
1898
|
+
? styles.toggleBtnActive
|
|
1899
|
+
: ''
|
|
1900
|
+
}`}
|
|
1901
|
+
>
|
|
1902
|
+
{showJoinTables ? '▲' : '▼'}
|
|
1903
|
+
</div>
|
|
1904
|
+
</div>
|
|
1905
|
+
|
|
1906
|
+
{showJoinTables && selectedTable && (
|
|
1907
|
+
<>
|
|
1908
|
+
<div
|
|
1909
|
+
className={
|
|
1910
|
+
styles.addManualJoinContainer
|
|
1911
|
+
}
|
|
1912
|
+
>
|
|
1913
|
+
<button
|
|
1914
|
+
className={styles.btn}
|
|
1915
|
+
onClick={handleAddJoin}
|
|
1575
1916
|
>
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
? `${formatName(
|
|
1580
|
-
join.sourceTable
|
|
1581
|
-
)} ${join.joinType.replace(
|
|
1582
|
-
' JOIN',
|
|
1583
|
-
''
|
|
1584
|
-
)} ${formatName(
|
|
1585
|
-
join.targetTable
|
|
1586
|
-
)}`
|
|
1587
|
-
: `Join #${index + 1}`}
|
|
1588
|
-
</h4>
|
|
1589
|
-
<button
|
|
1590
|
-
className={
|
|
1591
|
-
styles.removeJoinBtn
|
|
1592
|
-
}
|
|
1593
|
-
onClick={() =>
|
|
1594
|
-
handleRemoveJoin(index)
|
|
1595
|
-
}
|
|
1596
|
-
title="Remove Join"
|
|
1597
|
-
>
|
|
1598
|
-
×
|
|
1599
|
-
</button>
|
|
1600
|
-
</div>
|
|
1917
|
+
Add Manual Relationship
|
|
1918
|
+
</button>
|
|
1919
|
+
</div>
|
|
1601
1920
|
|
|
1602
|
-
|
|
1921
|
+
{joins.length > 0 && (
|
|
1922
|
+
<div
|
|
1923
|
+
className={
|
|
1924
|
+
styles.joinTablesContainer
|
|
1925
|
+
}
|
|
1926
|
+
>
|
|
1927
|
+
{joins.map((join, index) => (
|
|
1603
1928
|
<div
|
|
1929
|
+
key={index}
|
|
1604
1930
|
className={
|
|
1605
|
-
styles.
|
|
1931
|
+
styles.joinSection
|
|
1606
1932
|
}
|
|
1607
1933
|
>
|
|
1608
1934
|
<div
|
|
1609
1935
|
className={
|
|
1610
|
-
styles.
|
|
1936
|
+
styles.joinHeader
|
|
1611
1937
|
}
|
|
1612
1938
|
>
|
|
1613
|
-
<
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1939
|
+
<h4>
|
|
1940
|
+
{join.targetTable
|
|
1941
|
+
? `${formatName(
|
|
1942
|
+
join.sourceTable
|
|
1943
|
+
)} ${join.joinType.replace(
|
|
1944
|
+
' JOIN',
|
|
1945
|
+
''
|
|
1946
|
+
)} ${formatName(
|
|
1947
|
+
join.targetTable
|
|
1948
|
+
)}`
|
|
1949
|
+
: `Join #${
|
|
1950
|
+
index + 1
|
|
1951
|
+
}`}
|
|
1952
|
+
</h4>
|
|
1953
|
+
<button
|
|
1617
1954
|
className={
|
|
1618
|
-
styles.
|
|
1619
|
-
}
|
|
1620
|
-
value={
|
|
1621
|
-
join.joinType
|
|
1955
|
+
styles.removeJoinBtn
|
|
1622
1956
|
}
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
index
|
|
1626
|
-
'joinType',
|
|
1627
|
-
e.target
|
|
1628
|
-
.value
|
|
1957
|
+
onClick={() =>
|
|
1958
|
+
handleRemoveJoin(
|
|
1959
|
+
index
|
|
1629
1960
|
)
|
|
1630
1961
|
}
|
|
1962
|
+
title="Remove Join"
|
|
1631
1963
|
>
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
</option>
|
|
1635
|
-
<option value="LEFT JOIN">
|
|
1636
|
-
LEFT JOIN
|
|
1637
|
-
</option>
|
|
1638
|
-
<option value="RIGHT JOIN">
|
|
1639
|
-
RIGHT JOIN
|
|
1640
|
-
</option>
|
|
1641
|
-
<option value="FULL JOIN">
|
|
1642
|
-
FULL JOIN
|
|
1643
|
-
</option>
|
|
1644
|
-
</select>
|
|
1645
|
-
</div>
|
|
1646
|
-
|
|
1647
|
-
<div
|
|
1648
|
-
className={
|
|
1649
|
-
styles.joinField
|
|
1650
|
-
}
|
|
1651
|
-
>
|
|
1652
|
-
<label>
|
|
1653
|
-
Source Table:
|
|
1654
|
-
</label>
|
|
1655
|
-
<input
|
|
1656
|
-
type="text"
|
|
1657
|
-
className={
|
|
1658
|
-
styles.formControl
|
|
1659
|
-
}
|
|
1660
|
-
value={formatName(
|
|
1661
|
-
join.sourceTable
|
|
1662
|
-
)}
|
|
1663
|
-
readOnly
|
|
1664
|
-
/>
|
|
1964
|
+
×
|
|
1965
|
+
</button>
|
|
1665
1966
|
</div>
|
|
1666
1967
|
|
|
1667
1968
|
<div
|
|
1668
1969
|
className={
|
|
1669
|
-
styles.
|
|
1970
|
+
styles.joinGrid
|
|
1670
1971
|
}
|
|
1671
1972
|
>
|
|
1672
|
-
<
|
|
1673
|
-
Source Column:
|
|
1674
|
-
</label>
|
|
1675
|
-
<select
|
|
1973
|
+
<div
|
|
1676
1974
|
className={
|
|
1677
|
-
styles.
|
|
1678
|
-
}
|
|
1679
|
-
value={
|
|
1680
|
-
join.sourceColumn
|
|
1681
|
-
}
|
|
1682
|
-
onChange={(e) =>
|
|
1683
|
-
handleJoinChange(
|
|
1684
|
-
index,
|
|
1685
|
-
'sourceColumn',
|
|
1686
|
-
e.target
|
|
1687
|
-
.value
|
|
1688
|
-
)
|
|
1975
|
+
styles.joinGridColumn
|
|
1689
1976
|
}
|
|
1690
1977
|
>
|
|
1691
|
-
<
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1978
|
+
<div
|
|
1979
|
+
className={
|
|
1980
|
+
styles.joinField
|
|
1981
|
+
}
|
|
1982
|
+
>
|
|
1983
|
+
<label>
|
|
1984
|
+
Join Type:
|
|
1985
|
+
</label>
|
|
1986
|
+
<select
|
|
1987
|
+
className={
|
|
1988
|
+
styles.selectControl
|
|
1989
|
+
}
|
|
1990
|
+
value={
|
|
1991
|
+
join.joinType
|
|
1992
|
+
}
|
|
1993
|
+
onChange={(
|
|
1994
|
+
e
|
|
1995
|
+
) =>
|
|
1996
|
+
handleJoinChange(
|
|
1997
|
+
index,
|
|
1998
|
+
'joinType',
|
|
1999
|
+
e
|
|
2000
|
+
.target
|
|
2001
|
+
.value
|
|
2002
|
+
)
|
|
2003
|
+
}
|
|
2004
|
+
>
|
|
2005
|
+
<option value="INNER JOIN">
|
|
2006
|
+
INNER
|
|
2007
|
+
JOIN
|
|
1707
2008
|
</option>
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
</div>
|
|
1712
|
-
</div>
|
|
1713
|
-
|
|
1714
|
-
<div
|
|
1715
|
-
className={
|
|
1716
|
-
styles.joinGridColumn
|
|
1717
|
-
}
|
|
1718
|
-
>
|
|
1719
|
-
<div
|
|
1720
|
-
className={
|
|
1721
|
-
styles.joinField
|
|
1722
|
-
}
|
|
1723
|
-
>
|
|
1724
|
-
<label>
|
|
1725
|
-
Target Table:
|
|
1726
|
-
</label>
|
|
1727
|
-
<select
|
|
1728
|
-
className={
|
|
1729
|
-
styles.selectControl
|
|
1730
|
-
}
|
|
1731
|
-
value={
|
|
1732
|
-
join.targetTable
|
|
1733
|
-
}
|
|
1734
|
-
onChange={(e) =>
|
|
1735
|
-
handleJoinChange(
|
|
1736
|
-
index,
|
|
1737
|
-
'targetTable',
|
|
1738
|
-
e.target
|
|
1739
|
-
.value
|
|
1740
|
-
)
|
|
1741
|
-
}
|
|
1742
|
-
>
|
|
1743
|
-
<option value="">
|
|
1744
|
-
Select Table
|
|
1745
|
-
</option>
|
|
1746
|
-
{availableTables.map(
|
|
1747
|
-
(table) => (
|
|
1748
|
-
<option
|
|
1749
|
-
key={
|
|
1750
|
-
table.name
|
|
1751
|
-
}
|
|
1752
|
-
value={
|
|
1753
|
-
table.name
|
|
1754
|
-
}
|
|
1755
|
-
>
|
|
1756
|
-
{formatName(
|
|
1757
|
-
table.name
|
|
1758
|
-
)}
|
|
2009
|
+
<option value="LEFT JOIN">
|
|
2010
|
+
LEFT
|
|
2011
|
+
JOIN
|
|
1759
2012
|
</option>
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
2013
|
+
<option value="RIGHT JOIN">
|
|
2014
|
+
RIGHT
|
|
2015
|
+
JOIN
|
|
2016
|
+
</option>
|
|
2017
|
+
<option value="FULL JOIN">
|
|
2018
|
+
FULL
|
|
2019
|
+
JOIN
|
|
2020
|
+
</option>
|
|
2021
|
+
</select>
|
|
2022
|
+
</div>
|
|
1764
2023
|
|
|
1765
|
-
{join.targetTable &&
|
|
1766
|
-
join.availableColumns && (
|
|
1767
2024
|
<div
|
|
1768
2025
|
className={
|
|
1769
2026
|
styles.joinField
|
|
1770
2027
|
}
|
|
1771
2028
|
>
|
|
1772
2029
|
<label>
|
|
1773
|
-
|
|
2030
|
+
Source
|
|
2031
|
+
Table:
|
|
2032
|
+
</label>
|
|
2033
|
+
<input
|
|
2034
|
+
type="text"
|
|
2035
|
+
className={
|
|
2036
|
+
styles.formControl
|
|
2037
|
+
}
|
|
2038
|
+
value={formatName(
|
|
2039
|
+
join.sourceTable
|
|
2040
|
+
)}
|
|
2041
|
+
readOnly
|
|
2042
|
+
/>
|
|
2043
|
+
</div>
|
|
2044
|
+
|
|
2045
|
+
<div
|
|
2046
|
+
className={
|
|
2047
|
+
styles.joinField
|
|
2048
|
+
}
|
|
2049
|
+
>
|
|
2050
|
+
<label>
|
|
2051
|
+
Source
|
|
1774
2052
|
Column:
|
|
1775
2053
|
</label>
|
|
1776
2054
|
<select
|
|
@@ -1778,14 +2056,14 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
1778
2056
|
styles.selectControl
|
|
1779
2057
|
}
|
|
1780
2058
|
value={
|
|
1781
|
-
join.
|
|
2059
|
+
join.sourceColumn
|
|
1782
2060
|
}
|
|
1783
2061
|
onChange={(
|
|
1784
2062
|
e
|
|
1785
2063
|
) =>
|
|
1786
2064
|
handleJoinChange(
|
|
1787
2065
|
index,
|
|
1788
|
-
'
|
|
2066
|
+
'sourceColumn',
|
|
1789
2067
|
e
|
|
1790
2068
|
.target
|
|
1791
2069
|
.value
|
|
@@ -1796,7 +2074,7 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
1796
2074
|
Select
|
|
1797
2075
|
Column
|
|
1798
2076
|
</option>
|
|
1799
|
-
{
|
|
2077
|
+
{tableColumns.map(
|
|
1800
2078
|
(
|
|
1801
2079
|
column
|
|
1802
2080
|
) => (
|
|
@@ -1816,24 +2094,134 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
1816
2094
|
)}
|
|
1817
2095
|
</select>
|
|
1818
2096
|
</div>
|
|
1819
|
-
|
|
2097
|
+
</div>
|
|
2098
|
+
|
|
2099
|
+
<div
|
|
2100
|
+
className={
|
|
2101
|
+
styles.joinGridColumn
|
|
2102
|
+
}
|
|
2103
|
+
>
|
|
2104
|
+
<div
|
|
2105
|
+
className={
|
|
2106
|
+
styles.joinField
|
|
2107
|
+
}
|
|
2108
|
+
>
|
|
2109
|
+
<label>
|
|
2110
|
+
Target
|
|
2111
|
+
Table:
|
|
2112
|
+
</label>
|
|
2113
|
+
<select
|
|
2114
|
+
className={
|
|
2115
|
+
styles.selectControl
|
|
2116
|
+
}
|
|
2117
|
+
value={
|
|
2118
|
+
join.targetTable
|
|
2119
|
+
}
|
|
2120
|
+
onChange={(
|
|
2121
|
+
e
|
|
2122
|
+
) =>
|
|
2123
|
+
handleJoinChange(
|
|
2124
|
+
index,
|
|
2125
|
+
'targetTable',
|
|
2126
|
+
e
|
|
2127
|
+
.target
|
|
2128
|
+
.value
|
|
2129
|
+
)
|
|
2130
|
+
}
|
|
2131
|
+
>
|
|
2132
|
+
<option value="">
|
|
2133
|
+
Select
|
|
2134
|
+
Table
|
|
2135
|
+
</option>
|
|
2136
|
+
{availableTables.map(
|
|
2137
|
+
(
|
|
2138
|
+
table
|
|
2139
|
+
) => (
|
|
2140
|
+
<option
|
|
2141
|
+
key={
|
|
2142
|
+
table.name
|
|
2143
|
+
}
|
|
2144
|
+
value={
|
|
2145
|
+
table.name
|
|
2146
|
+
}
|
|
2147
|
+
>
|
|
2148
|
+
{formatName(
|
|
2149
|
+
table.name
|
|
2150
|
+
)}
|
|
2151
|
+
</option>
|
|
2152
|
+
)
|
|
2153
|
+
)}
|
|
2154
|
+
</select>
|
|
2155
|
+
</div>
|
|
2156
|
+
|
|
2157
|
+
{join.targetTable &&
|
|
2158
|
+
join.availableColumns && (
|
|
2159
|
+
<div
|
|
2160
|
+
className={
|
|
2161
|
+
styles.joinField
|
|
2162
|
+
}
|
|
2163
|
+
>
|
|
2164
|
+
<label>
|
|
2165
|
+
Target
|
|
2166
|
+
Column:
|
|
2167
|
+
</label>
|
|
2168
|
+
<select
|
|
2169
|
+
className={
|
|
2170
|
+
styles.selectControl
|
|
2171
|
+
}
|
|
2172
|
+
value={
|
|
2173
|
+
join.targetColumn
|
|
2174
|
+
}
|
|
2175
|
+
onChange={(
|
|
2176
|
+
e
|
|
2177
|
+
) =>
|
|
2178
|
+
handleJoinChange(
|
|
2179
|
+
index,
|
|
2180
|
+
'targetColumn',
|
|
2181
|
+
e
|
|
2182
|
+
.target
|
|
2183
|
+
.value
|
|
2184
|
+
)
|
|
2185
|
+
}
|
|
2186
|
+
>
|
|
2187
|
+
<option value="">
|
|
2188
|
+
Select
|
|
2189
|
+
Column
|
|
2190
|
+
</option>
|
|
2191
|
+
{join.availableColumns.map(
|
|
2192
|
+
(
|
|
2193
|
+
column
|
|
2194
|
+
) => (
|
|
2195
|
+
<option
|
|
2196
|
+
key={
|
|
2197
|
+
column.name
|
|
2198
|
+
}
|
|
2199
|
+
value={
|
|
2200
|
+
column.name
|
|
2201
|
+
}
|
|
2202
|
+
>
|
|
2203
|
+
{formatName(
|
|
2204
|
+
column.name
|
|
2205
|
+
)}
|
|
2206
|
+
</option>
|
|
2207
|
+
)
|
|
2208
|
+
)}
|
|
2209
|
+
</select>
|
|
2210
|
+
</div>
|
|
2211
|
+
)}
|
|
2212
|
+
</div>
|
|
2213
|
+
</div>
|
|
1820
2214
|
</div>
|
|
1821
|
-
|
|
2215
|
+
))}
|
|
1822
2216
|
</div>
|
|
1823
|
-
)
|
|
1824
|
-
|
|
1825
|
-
|
|
2217
|
+
)}
|
|
2218
|
+
</>
|
|
2219
|
+
)}
|
|
2220
|
+
</div>
|
|
1826
2221
|
|
|
1827
2222
|
<div className={styles.addJoinButtonContainer}>
|
|
1828
2223
|
{selectedTable && (
|
|
1829
2224
|
<>
|
|
1830
|
-
<button
|
|
1831
|
-
className={styles.btn}
|
|
1832
|
-
onClick={handleAddJoin}
|
|
1833
|
-
>
|
|
1834
|
-
Add Manual Relationship
|
|
1835
|
-
</button>
|
|
1836
|
-
|
|
1837
2225
|
{/* Main table suggested relationships */}
|
|
1838
2226
|
{suggestedJoins['main'] &&
|
|
1839
2227
|
suggestedJoins['main'].length > 0 && (
|
|
@@ -1842,89 +2230,151 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
1842
2230
|
styles.suggestedJoinsSection
|
|
1843
2231
|
}
|
|
1844
2232
|
>
|
|
1845
|
-
<h4>
|
|
1846
|
-
Suggested Relationships for{' '}
|
|
1847
|
-
{formatName(selectedTable)}
|
|
1848
|
-
</h4>
|
|
1849
|
-
<p className={styles.helpText}>
|
|
1850
|
-
Auto-detected based on
|
|
1851
|
-
database structure.
|
|
1852
|
-
<span
|
|
1853
|
-
className={
|
|
1854
|
-
styles.ratingInfo
|
|
1855
|
-
}
|
|
1856
|
-
>
|
|
1857
|
-
⭐⭐⭐ = High confidence
|
|
1858
|
-
| ⭐⭐ = Medium
|
|
1859
|
-
confidence | ⭐ = Low
|
|
1860
|
-
confidence
|
|
1861
|
-
</span>
|
|
1862
|
-
</p>
|
|
1863
2233
|
<div
|
|
1864
2234
|
className={
|
|
1865
|
-
styles.
|
|
2235
|
+
styles.sectionHeader
|
|
1866
2236
|
}
|
|
1867
2237
|
>
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
2238
|
+
<h4>
|
|
2239
|
+
Suggested Relationships
|
|
2240
|
+
for{' '}
|
|
2241
|
+
{formatName(
|
|
2242
|
+
selectedTable
|
|
2243
|
+
)}
|
|
2244
|
+
</h4>
|
|
2245
|
+
<button
|
|
2246
|
+
className={`${
|
|
2247
|
+
styles.toggleBtn
|
|
2248
|
+
} ${
|
|
2249
|
+
showSuggestedRelationships[
|
|
2250
|
+
'main'
|
|
2251
|
+
]
|
|
2252
|
+
? styles.toggleBtnActive
|
|
2253
|
+
: ''
|
|
2254
|
+
}`}
|
|
2255
|
+
onClick={() =>
|
|
2256
|
+
setShowSuggestedRelationships(
|
|
2257
|
+
(prev) => ({
|
|
2258
|
+
...prev,
|
|
2259
|
+
main: !prev[
|
|
2260
|
+
'main'
|
|
2261
|
+
],
|
|
2262
|
+
})
|
|
2263
|
+
)
|
|
2264
|
+
}
|
|
2265
|
+
title={
|
|
2266
|
+
showSuggestedRelationships[
|
|
2267
|
+
'main'
|
|
2268
|
+
]
|
|
2269
|
+
? 'Hide relationships'
|
|
2270
|
+
: 'Show relationships'
|
|
2271
|
+
}
|
|
2272
|
+
>
|
|
2273
|
+
{showSuggestedRelationships[
|
|
2274
|
+
'main'
|
|
2275
|
+
]
|
|
2276
|
+
? '▲'
|
|
2277
|
+
: '▼'}
|
|
2278
|
+
</button>
|
|
2279
|
+
</div>
|
|
2280
|
+
{showSuggestedRelationships[
|
|
2281
|
+
'main'
|
|
2282
|
+
] && (
|
|
2283
|
+
<>
|
|
2284
|
+
<p
|
|
2285
|
+
className={
|
|
2286
|
+
styles.helpText
|
|
2287
|
+
}
|
|
2288
|
+
>
|
|
2289
|
+
Auto-detected based
|
|
2290
|
+
on database
|
|
2291
|
+
structure.
|
|
2292
|
+
<span
|
|
1872
2293
|
className={
|
|
1873
|
-
styles.
|
|
2294
|
+
styles.ratingInfo
|
|
1874
2295
|
}
|
|
1875
|
-
title={formatRelationshipDescription(
|
|
1876
|
-
join.description,
|
|
1877
|
-
false,
|
|
1878
|
-
join
|
|
1879
|
-
)}
|
|
1880
2296
|
>
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
2297
|
+
⭐⭐⭐ = High
|
|
2298
|
+
confidence |
|
|
2299
|
+
⭐⭐ = Medium
|
|
2300
|
+
confidence | ⭐
|
|
2301
|
+
= Low confidence
|
|
2302
|
+
</span>
|
|
2303
|
+
</p>
|
|
2304
|
+
<div
|
|
2305
|
+
className={
|
|
2306
|
+
styles.suggestedJoinsList
|
|
2307
|
+
}
|
|
2308
|
+
>
|
|
2309
|
+
{suggestedJoins[
|
|
2310
|
+
'main'
|
|
2311
|
+
].map(
|
|
2312
|
+
(
|
|
2313
|
+
join,
|
|
2314
|
+
index
|
|
2315
|
+
) => (
|
|
2316
|
+
<div
|
|
2317
|
+
key={
|
|
2318
|
+
index
|
|
2319
|
+
}
|
|
1887
2320
|
className={
|
|
1888
|
-
styles.
|
|
2321
|
+
styles.suggestedJoinItem
|
|
1889
2322
|
}
|
|
1890
|
-
|
|
1891
|
-
{formatRelationshipDescription(
|
|
2323
|
+
title={formatRelationshipDescription(
|
|
1892
2324
|
join.description,
|
|
1893
|
-
|
|
2325
|
+
false,
|
|
1894
2326
|
join
|
|
1895
2327
|
)}
|
|
1896
|
-
</span>
|
|
1897
|
-
<span
|
|
1898
|
-
className={
|
|
1899
|
-
styles.confidenceBadge
|
|
1900
|
-
}
|
|
1901
2328
|
>
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
2329
|
+
<div
|
|
2330
|
+
className={
|
|
2331
|
+
styles.suggestedJoinInfo
|
|
2332
|
+
}
|
|
2333
|
+
>
|
|
2334
|
+
<span
|
|
2335
|
+
className={
|
|
2336
|
+
styles.suggestedJoinDescription
|
|
2337
|
+
}
|
|
2338
|
+
>
|
|
2339
|
+
{formatRelationshipDescription(
|
|
2340
|
+
join.description,
|
|
2341
|
+
true,
|
|
2342
|
+
join
|
|
2343
|
+
)}
|
|
2344
|
+
</span>
|
|
2345
|
+
<span
|
|
2346
|
+
className={
|
|
2347
|
+
styles.confidenceBadge
|
|
2348
|
+
}
|
|
2349
|
+
>
|
|
2350
|
+
{join.confidence ===
|
|
2351
|
+
'high'
|
|
2352
|
+
? '⭐⭐⭐'
|
|
2353
|
+
: join.confidence ===
|
|
2354
|
+
'medium'
|
|
2355
|
+
? '⭐⭐'
|
|
2356
|
+
: '⭐'}
|
|
2357
|
+
</span>
|
|
2358
|
+
</div>
|
|
2359
|
+
<button
|
|
2360
|
+
className={
|
|
2361
|
+
styles.addSuggestedJoinBtn
|
|
2362
|
+
}
|
|
2363
|
+
onClick={() =>
|
|
2364
|
+
handleAddSuggestedJoin(
|
|
2365
|
+
join
|
|
2366
|
+
)
|
|
2367
|
+
}
|
|
2368
|
+
title="Add this relationship"
|
|
2369
|
+
>
|
|
2370
|
+
Add
|
|
2371
|
+
</button>
|
|
2372
|
+
</div>
|
|
2373
|
+
)
|
|
2374
|
+
)}
|
|
2375
|
+
</div>
|
|
2376
|
+
</>
|
|
2377
|
+
)}
|
|
1928
2378
|
</div>
|
|
1929
2379
|
)}
|
|
1930
2380
|
|
|
@@ -1953,87 +2403,139 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
1953
2403
|
styles.suggestedJoinsSection
|
|
1954
2404
|
}
|
|
1955
2405
|
>
|
|
1956
|
-
<h4>
|
|
1957
|
-
Suggested
|
|
1958
|
-
Relationships
|
|
1959
|
-
for{' '}
|
|
1960
|
-
{formatName(
|
|
1961
|
-
join.targetTable
|
|
1962
|
-
)}
|
|
1963
|
-
</h4>
|
|
1964
2406
|
<div
|
|
1965
2407
|
className={
|
|
1966
|
-
styles.
|
|
2408
|
+
styles.sectionHeader
|
|
1967
2409
|
}
|
|
1968
2410
|
>
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
(
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
2411
|
+
<h4>
|
|
2412
|
+
Suggested
|
|
2413
|
+
Relationships
|
|
2414
|
+
for{' '}
|
|
2415
|
+
{formatName(
|
|
2416
|
+
join.targetTable
|
|
2417
|
+
)}
|
|
2418
|
+
</h4>
|
|
2419
|
+
<button
|
|
2420
|
+
className={`${
|
|
2421
|
+
styles.toggleBtn
|
|
2422
|
+
} ${
|
|
2423
|
+
showSuggestedRelationships[
|
|
2424
|
+
join
|
|
2425
|
+
.targetTable
|
|
2426
|
+
]
|
|
2427
|
+
? styles.toggleBtnActive
|
|
2428
|
+
: ''
|
|
2429
|
+
}`}
|
|
2430
|
+
onClick={() =>
|
|
2431
|
+
setShowSuggestedRelationships(
|
|
2432
|
+
(
|
|
2433
|
+
prev
|
|
2434
|
+
) => ({
|
|
2435
|
+
...prev,
|
|
2436
|
+
[join.targetTable]:
|
|
2437
|
+
!prev[
|
|
2438
|
+
join
|
|
2439
|
+
.targetTable
|
|
2440
|
+
],
|
|
2441
|
+
})
|
|
2442
|
+
)
|
|
2443
|
+
}
|
|
2444
|
+
title={
|
|
2445
|
+
showSuggestedRelationships[
|
|
2446
|
+
join
|
|
2447
|
+
.targetTable
|
|
2448
|
+
]
|
|
2449
|
+
? 'Hide relationships'
|
|
2450
|
+
: 'Show relationships'
|
|
2451
|
+
}
|
|
2452
|
+
>
|
|
2453
|
+
{showSuggestedRelationships[
|
|
2454
|
+
join
|
|
2455
|
+
.targetTable
|
|
2456
|
+
]
|
|
2457
|
+
? '▲'
|
|
2458
|
+
: '▼'}
|
|
2459
|
+
</button>
|
|
2460
|
+
</div>
|
|
2461
|
+
{showSuggestedRelationships[
|
|
2462
|
+
join
|
|
2463
|
+
.targetTable
|
|
2464
|
+
] && (
|
|
2465
|
+
<div
|
|
2466
|
+
className={
|
|
2467
|
+
styles.suggestedJoinsList
|
|
2468
|
+
}
|
|
2469
|
+
>
|
|
2470
|
+
{suggestedJoins[
|
|
2471
|
+
join
|
|
2472
|
+
.targetTable
|
|
2473
|
+
].map(
|
|
2474
|
+
(
|
|
2475
|
+
suggestedJoin,
|
|
2476
|
+
index
|
|
2477
|
+
) => (
|
|
1990
2478
|
<div
|
|
2479
|
+
key={
|
|
2480
|
+
index
|
|
2481
|
+
}
|
|
1991
2482
|
className={
|
|
1992
|
-
styles.
|
|
2483
|
+
styles.suggestedJoinItem
|
|
1993
2484
|
}
|
|
2485
|
+
title={formatRelationshipDescription(
|
|
2486
|
+
suggestedJoin.description,
|
|
2487
|
+
false,
|
|
2488
|
+
suggestedJoin
|
|
2489
|
+
)}
|
|
1994
2490
|
>
|
|
1995
|
-
<
|
|
2491
|
+
<div
|
|
1996
2492
|
className={
|
|
1997
|
-
styles.
|
|
2493
|
+
styles.suggestedJoinInfo
|
|
1998
2494
|
}
|
|
1999
2495
|
>
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2496
|
+
<span
|
|
2497
|
+
className={
|
|
2498
|
+
styles.suggestedJoinDescription
|
|
2499
|
+
}
|
|
2500
|
+
>
|
|
2501
|
+
{formatRelationshipDescription(
|
|
2502
|
+
suggestedJoin.description,
|
|
2503
|
+
true,
|
|
2504
|
+
suggestedJoin
|
|
2505
|
+
)}
|
|
2506
|
+
</span>
|
|
2507
|
+
<span
|
|
2508
|
+
className={
|
|
2509
|
+
styles.confidenceBadge
|
|
2510
|
+
}
|
|
2511
|
+
>
|
|
2512
|
+
{suggestedJoin.confidence ===
|
|
2513
|
+
'high'
|
|
2514
|
+
? '⭐⭐⭐'
|
|
2515
|
+
: suggestedJoin.confidence ===
|
|
2516
|
+
'medium'
|
|
2517
|
+
? '⭐⭐'
|
|
2518
|
+
: '⭐'}
|
|
2519
|
+
</span>
|
|
2520
|
+
</div>
|
|
2521
|
+
<button
|
|
2007
2522
|
className={
|
|
2008
|
-
styles.
|
|
2523
|
+
styles.addSuggestedJoinBtn
|
|
2009
2524
|
}
|
|
2525
|
+
onClick={() =>
|
|
2526
|
+
handleAddSuggestedJoin(
|
|
2527
|
+
suggestedJoin
|
|
2528
|
+
)
|
|
2529
|
+
}
|
|
2530
|
+
title="Add this relationship"
|
|
2010
2531
|
>
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
? '⭐⭐⭐'
|
|
2014
|
-
: suggestedJoin.confidence ===
|
|
2015
|
-
'medium'
|
|
2016
|
-
? '⭐⭐'
|
|
2017
|
-
: '⭐'}
|
|
2018
|
-
</span>
|
|
2532
|
+
Add
|
|
2533
|
+
</button>
|
|
2019
2534
|
</div>
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
onClick={() =>
|
|
2025
|
-
handleAddSuggestedJoin(
|
|
2026
|
-
suggestedJoin
|
|
2027
|
-
)
|
|
2028
|
-
}
|
|
2029
|
-
title="Add this relationship"
|
|
2030
|
-
>
|
|
2031
|
-
Add
|
|
2032
|
-
</button>
|
|
2033
|
-
</div>
|
|
2034
|
-
)
|
|
2035
|
-
)}
|
|
2036
|
-
</div>
|
|
2535
|
+
)
|
|
2536
|
+
)}
|
|
2537
|
+
</div>
|
|
2538
|
+
)}
|
|
2037
2539
|
</div>
|
|
2038
2540
|
)}
|
|
2039
2541
|
|
|
@@ -2060,6 +2562,600 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
2060
2562
|
)}
|
|
2061
2563
|
</div>
|
|
2062
2564
|
|
|
2565
|
+
{/* Sorting Section */}
|
|
2566
|
+
<div className={styles.reportSection}>
|
|
2567
|
+
<div
|
|
2568
|
+
className={styles.sectionHeader}
|
|
2569
|
+
onClick={() =>
|
|
2570
|
+
setShowSortingSection(!showSortingSection)
|
|
2571
|
+
}
|
|
2572
|
+
style={{ cursor: 'pointer' }}
|
|
2573
|
+
title={
|
|
2574
|
+
showSortingSection
|
|
2575
|
+
? 'Hide sorting options'
|
|
2576
|
+
: 'Show sorting options'
|
|
2577
|
+
}
|
|
2578
|
+
>
|
|
2579
|
+
<h3>Sorting</h3>
|
|
2580
|
+
<div
|
|
2581
|
+
className={`${styles.toggleBtn} ${
|
|
2582
|
+
showSortingSection
|
|
2583
|
+
? styles.toggleBtnActive
|
|
2584
|
+
: ''
|
|
2585
|
+
}`}
|
|
2586
|
+
>
|
|
2587
|
+
{showSortingSection ? '▲' : '▼'}
|
|
2588
|
+
</div>
|
|
2589
|
+
</div>
|
|
2590
|
+
|
|
2591
|
+
{showSortingSection && (
|
|
2592
|
+
<>
|
|
2593
|
+
<div
|
|
2594
|
+
className={
|
|
2595
|
+
styles.addManualJoinContainer
|
|
2596
|
+
}
|
|
2597
|
+
>
|
|
2598
|
+
<button
|
|
2599
|
+
className={styles.btn}
|
|
2600
|
+
onClick={handleAddSortCriterion}
|
|
2601
|
+
disabled={
|
|
2602
|
+
selectedColumns.length === 0
|
|
2603
|
+
}
|
|
2604
|
+
>
|
|
2605
|
+
Add Sort Criterion
|
|
2606
|
+
</button>
|
|
2607
|
+
</div>
|
|
2608
|
+
|
|
2609
|
+
{sortCriteria.length > 0 && (
|
|
2610
|
+
<div
|
|
2611
|
+
className={
|
|
2612
|
+
styles.joinTablesContainer
|
|
2613
|
+
}
|
|
2614
|
+
>
|
|
2615
|
+
{sortCriteria.map(
|
|
2616
|
+
(criterion, index) => (
|
|
2617
|
+
<div
|
|
2618
|
+
key={index}
|
|
2619
|
+
className={
|
|
2620
|
+
styles.joinSection
|
|
2621
|
+
}
|
|
2622
|
+
>
|
|
2623
|
+
<div
|
|
2624
|
+
className={
|
|
2625
|
+
styles.joinHeader
|
|
2626
|
+
}
|
|
2627
|
+
>
|
|
2628
|
+
<h4>
|
|
2629
|
+
Sort by{' '}
|
|
2630
|
+
{criterion.table &&
|
|
2631
|
+
criterion.column
|
|
2632
|
+
? `${formatName(
|
|
2633
|
+
criterion.table
|
|
2634
|
+
)}.${formatName(
|
|
2635
|
+
criterion.column
|
|
2636
|
+
)}`
|
|
2637
|
+
: `Criterion #${
|
|
2638
|
+
index +
|
|
2639
|
+
1
|
|
2640
|
+
}`}
|
|
2641
|
+
</h4>
|
|
2642
|
+
<button
|
|
2643
|
+
className={
|
|
2644
|
+
styles.removeJoinBtn
|
|
2645
|
+
}
|
|
2646
|
+
onClick={() =>
|
|
2647
|
+
handleRemoveSortCriterion(
|
|
2648
|
+
index
|
|
2649
|
+
)
|
|
2650
|
+
}
|
|
2651
|
+
title="Remove Sort Criterion"
|
|
2652
|
+
>
|
|
2653
|
+
×
|
|
2654
|
+
</button>
|
|
2655
|
+
</div>
|
|
2656
|
+
|
|
2657
|
+
<div
|
|
2658
|
+
className={
|
|
2659
|
+
styles.joinGrid
|
|
2660
|
+
}
|
|
2661
|
+
>
|
|
2662
|
+
<div
|
|
2663
|
+
className={
|
|
2664
|
+
styles.joinGridColumn
|
|
2665
|
+
}
|
|
2666
|
+
>
|
|
2667
|
+
<div
|
|
2668
|
+
className={
|
|
2669
|
+
styles.joinField
|
|
2670
|
+
}
|
|
2671
|
+
>
|
|
2672
|
+
<label>
|
|
2673
|
+
Column:
|
|
2674
|
+
</label>
|
|
2675
|
+
<select
|
|
2676
|
+
className={
|
|
2677
|
+
styles.selectControl
|
|
2678
|
+
}
|
|
2679
|
+
value={`${criterion.table}.${criterion.column}`}
|
|
2680
|
+
onChange={(
|
|
2681
|
+
e
|
|
2682
|
+
) => {
|
|
2683
|
+
const [
|
|
2684
|
+
table,
|
|
2685
|
+
column,
|
|
2686
|
+
] =
|
|
2687
|
+
e.target.value.split(
|
|
2688
|
+
'.'
|
|
2689
|
+
);
|
|
2690
|
+
handleSortCriterionChange(
|
|
2691
|
+
index,
|
|
2692
|
+
'table',
|
|
2693
|
+
table
|
|
2694
|
+
);
|
|
2695
|
+
handleSortCriterionChange(
|
|
2696
|
+
index,
|
|
2697
|
+
'column',
|
|
2698
|
+
column
|
|
2699
|
+
);
|
|
2700
|
+
}}
|
|
2701
|
+
>
|
|
2702
|
+
<option value="">
|
|
2703
|
+
Select
|
|
2704
|
+
Column
|
|
2705
|
+
</option>
|
|
2706
|
+
{/* Main table columns */}
|
|
2707
|
+
<optgroup
|
|
2708
|
+
label={`${formatName(
|
|
2709
|
+
selectedTable
|
|
2710
|
+
)} Columns`}
|
|
2711
|
+
>
|
|
2712
|
+
{tableColumns.map(
|
|
2713
|
+
(
|
|
2714
|
+
column
|
|
2715
|
+
) => (
|
|
2716
|
+
<option
|
|
2717
|
+
key={`${selectedTable}.${column.name}`}
|
|
2718
|
+
value={`${selectedTable}.${column.name}`}
|
|
2719
|
+
>
|
|
2720
|
+
{formatName(
|
|
2721
|
+
column.name
|
|
2722
|
+
)}
|
|
2723
|
+
</option>
|
|
2724
|
+
)
|
|
2725
|
+
)}
|
|
2726
|
+
</optgroup>
|
|
2727
|
+
|
|
2728
|
+
{/* Joined tables columns */}
|
|
2729
|
+
{joins.map(
|
|
2730
|
+
(
|
|
2731
|
+
join,
|
|
2732
|
+
joinIndex
|
|
2733
|
+
) =>
|
|
2734
|
+
join.targetTable &&
|
|
2735
|
+
join.availableColumns &&
|
|
2736
|
+
join
|
|
2737
|
+
.availableColumns
|
|
2738
|
+
.length >
|
|
2739
|
+
0 ? (
|
|
2740
|
+
<optgroup
|
|
2741
|
+
key={`join-${joinIndex}`}
|
|
2742
|
+
label={`${formatName(
|
|
2743
|
+
join.targetTable
|
|
2744
|
+
)} Columns`}
|
|
2745
|
+
>
|
|
2746
|
+
{join.availableColumns.map(
|
|
2747
|
+
(
|
|
2748
|
+
column
|
|
2749
|
+
) => (
|
|
2750
|
+
<option
|
|
2751
|
+
key={`${join.targetTable}.${column.name}`}
|
|
2752
|
+
value={`${join.targetTable}.${column.name}`}
|
|
2753
|
+
>
|
|
2754
|
+
{formatName(
|
|
2755
|
+
column.name
|
|
2756
|
+
)}
|
|
2757
|
+
</option>
|
|
2758
|
+
)
|
|
2759
|
+
)}
|
|
2760
|
+
</optgroup>
|
|
2761
|
+
) : null
|
|
2762
|
+
)}
|
|
2763
|
+
</select>
|
|
2764
|
+
</div>
|
|
2765
|
+
</div>
|
|
2766
|
+
|
|
2767
|
+
<div
|
|
2768
|
+
className={
|
|
2769
|
+
styles.joinGridColumn
|
|
2770
|
+
}
|
|
2771
|
+
>
|
|
2772
|
+
<div
|
|
2773
|
+
className={
|
|
2774
|
+
styles.joinField
|
|
2775
|
+
}
|
|
2776
|
+
>
|
|
2777
|
+
<label>
|
|
2778
|
+
Direction:
|
|
2779
|
+
</label>
|
|
2780
|
+
<select
|
|
2781
|
+
className={
|
|
2782
|
+
styles.selectControl
|
|
2783
|
+
}
|
|
2784
|
+
value={
|
|
2785
|
+
criterion.direction
|
|
2786
|
+
}
|
|
2787
|
+
onChange={(
|
|
2788
|
+
e
|
|
2789
|
+
) =>
|
|
2790
|
+
handleSortCriterionChange(
|
|
2791
|
+
index,
|
|
2792
|
+
'direction',
|
|
2793
|
+
e
|
|
2794
|
+
.target
|
|
2795
|
+
.value
|
|
2796
|
+
)
|
|
2797
|
+
}
|
|
2798
|
+
>
|
|
2799
|
+
<option value="ASC">
|
|
2800
|
+
Ascending
|
|
2801
|
+
</option>
|
|
2802
|
+
<option value="DESC">
|
|
2803
|
+
Descending
|
|
2804
|
+
</option>
|
|
2805
|
+
</select>
|
|
2806
|
+
</div>
|
|
2807
|
+
</div>
|
|
2808
|
+
</div>
|
|
2809
|
+
</div>
|
|
2810
|
+
)
|
|
2811
|
+
)}
|
|
2812
|
+
</div>
|
|
2813
|
+
)}
|
|
2814
|
+
</>
|
|
2815
|
+
)}
|
|
2816
|
+
</div>
|
|
2817
|
+
|
|
2818
|
+
{/* Filtering Section */}
|
|
2819
|
+
<div className={styles.reportSection}>
|
|
2820
|
+
<div
|
|
2821
|
+
className={styles.sectionHeader}
|
|
2822
|
+
onClick={() =>
|
|
2823
|
+
setShowFilteringSection(
|
|
2824
|
+
!showFilteringSection
|
|
2825
|
+
)
|
|
2826
|
+
}
|
|
2827
|
+
style={{ cursor: 'pointer' }}
|
|
2828
|
+
title={
|
|
2829
|
+
showFilteringSection
|
|
2830
|
+
? 'Hide filtering options'
|
|
2831
|
+
: 'Show filtering options'
|
|
2832
|
+
}
|
|
2833
|
+
>
|
|
2834
|
+
<h3>Filtering</h3>
|
|
2835
|
+
<div
|
|
2836
|
+
className={`${styles.toggleBtn} ${
|
|
2837
|
+
showFilteringSection
|
|
2838
|
+
? styles.toggleBtnActive
|
|
2839
|
+
: ''
|
|
2840
|
+
}`}
|
|
2841
|
+
>
|
|
2842
|
+
{showFilteringSection ? '▲' : '▼'}
|
|
2843
|
+
</div>
|
|
2844
|
+
</div>
|
|
2845
|
+
|
|
2846
|
+
{showFilteringSection && (
|
|
2847
|
+
<>
|
|
2848
|
+
<div
|
|
2849
|
+
className={
|
|
2850
|
+
styles.addManualJoinContainer
|
|
2851
|
+
}
|
|
2852
|
+
>
|
|
2853
|
+
<button
|
|
2854
|
+
className={styles.btn}
|
|
2855
|
+
onClick={handleAddFilterCriterion}
|
|
2856
|
+
disabled={
|
|
2857
|
+
selectedColumns.length === 0
|
|
2858
|
+
}
|
|
2859
|
+
>
|
|
2860
|
+
Add Filter Criterion
|
|
2861
|
+
</button>
|
|
2862
|
+
</div>
|
|
2863
|
+
|
|
2864
|
+
{filterCriteria.length > 0 && (
|
|
2865
|
+
<div
|
|
2866
|
+
className={
|
|
2867
|
+
styles.joinTablesContainer
|
|
2868
|
+
}
|
|
2869
|
+
>
|
|
2870
|
+
{filterCriteria.map(
|
|
2871
|
+
(criterion, index) => (
|
|
2872
|
+
<div
|
|
2873
|
+
key={index}
|
|
2874
|
+
className={
|
|
2875
|
+
styles.joinSection
|
|
2876
|
+
}
|
|
2877
|
+
>
|
|
2878
|
+
<div
|
|
2879
|
+
className={
|
|
2880
|
+
styles.joinHeader
|
|
2881
|
+
}
|
|
2882
|
+
>
|
|
2883
|
+
<h4>
|
|
2884
|
+
Filter on{' '}
|
|
2885
|
+
{criterion.table &&
|
|
2886
|
+
criterion.column
|
|
2887
|
+
? `${formatName(
|
|
2888
|
+
criterion.table
|
|
2889
|
+
)}.${formatName(
|
|
2890
|
+
criterion.column
|
|
2891
|
+
)}`
|
|
2892
|
+
: `Criterion #${
|
|
2893
|
+
index +
|
|
2894
|
+
1
|
|
2895
|
+
}`}
|
|
2896
|
+
</h4>
|
|
2897
|
+
<button
|
|
2898
|
+
className={
|
|
2899
|
+
styles.removeJoinBtn
|
|
2900
|
+
}
|
|
2901
|
+
onClick={() =>
|
|
2902
|
+
handleRemoveFilterCriterion(
|
|
2903
|
+
index
|
|
2904
|
+
)
|
|
2905
|
+
}
|
|
2906
|
+
title="Remove Filter Criterion"
|
|
2907
|
+
>
|
|
2908
|
+
×
|
|
2909
|
+
</button>
|
|
2910
|
+
</div>
|
|
2911
|
+
|
|
2912
|
+
<div
|
|
2913
|
+
className={
|
|
2914
|
+
styles.joinGrid
|
|
2915
|
+
}
|
|
2916
|
+
>
|
|
2917
|
+
<div
|
|
2918
|
+
className={
|
|
2919
|
+
styles.joinGridColumn
|
|
2920
|
+
}
|
|
2921
|
+
>
|
|
2922
|
+
<div
|
|
2923
|
+
className={
|
|
2924
|
+
styles.joinField
|
|
2925
|
+
}
|
|
2926
|
+
>
|
|
2927
|
+
<label>
|
|
2928
|
+
Column:
|
|
2929
|
+
</label>
|
|
2930
|
+
<select
|
|
2931
|
+
className={
|
|
2932
|
+
styles.selectControl
|
|
2933
|
+
}
|
|
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
|
+
>
|
|
2957
|
+
<option value="">
|
|
2958
|
+
Select
|
|
2959
|
+
Column
|
|
2960
|
+
</option>
|
|
2961
|
+
{/* Main table columns */}
|
|
2962
|
+
<optgroup
|
|
2963
|
+
label={`${formatName(
|
|
2964
|
+
selectedTable
|
|
2965
|
+
)} Columns`}
|
|
2966
|
+
>
|
|
2967
|
+
{tableColumns.map(
|
|
2968
|
+
(
|
|
2969
|
+
column
|
|
2970
|
+
) => (
|
|
2971
|
+
<option
|
|
2972
|
+
key={`${selectedTable}.${column.name}`}
|
|
2973
|
+
value={`${selectedTable}.${column.name}`}
|
|
2974
|
+
>
|
|
2975
|
+
{formatName(
|
|
2976
|
+
column.name
|
|
2977
|
+
)}
|
|
2978
|
+
</option>
|
|
2979
|
+
)
|
|
2980
|
+
)}
|
|
2981
|
+
</optgroup>
|
|
2982
|
+
|
|
2983
|
+
{/* Joined tables columns */}
|
|
2984
|
+
{joins.map(
|
|
2985
|
+
(
|
|
2986
|
+
join,
|
|
2987
|
+
joinIndex
|
|
2988
|
+
) =>
|
|
2989
|
+
join.targetTable &&
|
|
2990
|
+
join.availableColumns &&
|
|
2991
|
+
join
|
|
2992
|
+
.availableColumns
|
|
2993
|
+
.length >
|
|
2994
|
+
0 ? (
|
|
2995
|
+
<optgroup
|
|
2996
|
+
key={`join-${joinIndex}`}
|
|
2997
|
+
label={`${formatName(
|
|
2998
|
+
join.targetTable
|
|
2999
|
+
)} Columns`}
|
|
3000
|
+
>
|
|
3001
|
+
{join.availableColumns.map(
|
|
3002
|
+
(
|
|
3003
|
+
column
|
|
3004
|
+
) => (
|
|
3005
|
+
<option
|
|
3006
|
+
key={`${join.targetTable}.${column.name}`}
|
|
3007
|
+
value={`${join.targetTable}.${column.name}`}
|
|
3008
|
+
>
|
|
3009
|
+
{formatName(
|
|
3010
|
+
column.name
|
|
3011
|
+
)}
|
|
3012
|
+
</option>
|
|
3013
|
+
)
|
|
3014
|
+
)}
|
|
3015
|
+
</optgroup>
|
|
3016
|
+
) : null
|
|
3017
|
+
)}
|
|
3018
|
+
</select>
|
|
3019
|
+
</div>
|
|
3020
|
+
</div>
|
|
3021
|
+
|
|
3022
|
+
<div
|
|
3023
|
+
className={
|
|
3024
|
+
styles.joinGridColumn
|
|
3025
|
+
}
|
|
3026
|
+
>
|
|
3027
|
+
<div
|
|
3028
|
+
className={
|
|
3029
|
+
styles.joinField
|
|
3030
|
+
}
|
|
3031
|
+
>
|
|
3032
|
+
<label>
|
|
3033
|
+
Operator:
|
|
3034
|
+
</label>
|
|
3035
|
+
<select
|
|
3036
|
+
className={
|
|
3037
|
+
styles.selectControl
|
|
3038
|
+
}
|
|
3039
|
+
value={
|
|
3040
|
+
criterion.operator
|
|
3041
|
+
}
|
|
3042
|
+
onChange={(
|
|
3043
|
+
e
|
|
3044
|
+
) =>
|
|
3045
|
+
handleFilterCriterionChange(
|
|
3046
|
+
index,
|
|
3047
|
+
'operator',
|
|
3048
|
+
e
|
|
3049
|
+
.target
|
|
3050
|
+
.value
|
|
3051
|
+
)
|
|
3052
|
+
}
|
|
3053
|
+
>
|
|
3054
|
+
<option value="=">
|
|
3055
|
+
Equals
|
|
3056
|
+
(=)
|
|
3057
|
+
</option>
|
|
3058
|
+
<option value="!=">
|
|
3059
|
+
Not
|
|
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>
|
|
3112
|
+
|
|
3113
|
+
{criterion.operator !==
|
|
3114
|
+
'IS NULL' &&
|
|
3115
|
+
criterion.operator !==
|
|
3116
|
+
'IS NOT NULL' && (
|
|
3117
|
+
<div
|
|
3118
|
+
className={
|
|
3119
|
+
styles.joinField
|
|
3120
|
+
}
|
|
3121
|
+
>
|
|
3122
|
+
<label>
|
|
3123
|
+
Value:
|
|
3124
|
+
</label>
|
|
3125
|
+
<input
|
|
3126
|
+
type="text"
|
|
3127
|
+
className={
|
|
3128
|
+
styles.formControl
|
|
3129
|
+
}
|
|
3130
|
+
value={
|
|
3131
|
+
criterion.value
|
|
3132
|
+
}
|
|
3133
|
+
onChange={(
|
|
3134
|
+
e
|
|
3135
|
+
) =>
|
|
3136
|
+
handleFilterCriterionChange(
|
|
3137
|
+
index,
|
|
3138
|
+
'value',
|
|
3139
|
+
e
|
|
3140
|
+
.target
|
|
3141
|
+
.value
|
|
3142
|
+
)
|
|
3143
|
+
}
|
|
3144
|
+
placeholder="Enter filter value"
|
|
3145
|
+
/>
|
|
3146
|
+
</div>
|
|
3147
|
+
)}
|
|
3148
|
+
</div>
|
|
3149
|
+
</div>
|
|
3150
|
+
</div>
|
|
3151
|
+
)
|
|
3152
|
+
)}
|
|
3153
|
+
</div>
|
|
3154
|
+
)}
|
|
3155
|
+
</>
|
|
3156
|
+
)}
|
|
3157
|
+
</div>
|
|
3158
|
+
|
|
2063
3159
|
<div className={styles.actionButtons}>
|
|
2064
3160
|
<button
|
|
2065
3161
|
className={styles.btn}
|