@visns-studio/visns-components 5.14.11 → 5.14.13
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 +5 -5
- package/src/components/DataGrid.jsx +51 -0
- package/src/components/Field.jsx +15 -0
- package/src/components/Form.jsx +57 -8
- package/src/components/generic/GenericReport.jsx +1437 -589
- package/src/components/generic/StandardModal.jsx +62 -12
- package/src/components/styles/Field.module.scss +144 -48
|
@@ -2,7 +2,6 @@ import React, { useState, useEffect, useCallback } from 'react';
|
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import { toast } from 'react-toastify';
|
|
4
4
|
import moment from 'moment';
|
|
5
|
-
import ReactDataGrid from '@visns-studio/visns-datagrid-enterprise';
|
|
6
5
|
import GroupedReportRenderer from './GroupedReportRenderer';
|
|
7
6
|
import { formatCellContent } from './shared/formatters';
|
|
8
7
|
import {
|
|
@@ -918,7 +917,8 @@ const wizardSteps = [
|
|
|
918
917
|
icon: Data,
|
|
919
918
|
description: 'Organize data into groups (optional)',
|
|
920
919
|
guidance: {
|
|
921
|
-
summary:
|
|
920
|
+
summary:
|
|
921
|
+
'Group your data by a specific field for better organization',
|
|
922
922
|
howTo: [
|
|
923
923
|
'Enable grouping to organize data into sections',
|
|
924
924
|
'Choose which field to group by (e.g., Department, Category, Status)',
|
|
@@ -1073,13 +1073,13 @@ const GenericReportImproved = ({
|
|
|
1073
1073
|
showGroupTotals: true,
|
|
1074
1074
|
expandable: true,
|
|
1075
1075
|
groupHeaderStyle: 'primary',
|
|
1076
|
-
emptyRowStyle: 'light'
|
|
1076
|
+
emptyRowStyle: 'light',
|
|
1077
1077
|
});
|
|
1078
1078
|
|
|
1079
1079
|
// State for custom URLs
|
|
1080
1080
|
const [customUrls, setCustomUrls] = useState({
|
|
1081
1081
|
executeUrl: '',
|
|
1082
|
-
exportUrl: ''
|
|
1082
|
+
exportUrl: '',
|
|
1083
1083
|
});
|
|
1084
1084
|
|
|
1085
1085
|
// Note: Removed table search functionality to simplify user experience
|
|
@@ -2259,15 +2259,19 @@ const GenericReportImproved = ({
|
|
|
2259
2259
|
|
|
2260
2260
|
try {
|
|
2261
2261
|
// Check if this is a grouped report
|
|
2262
|
-
const isGroupedReport =
|
|
2263
|
-
|
|
2262
|
+
const isGroupedReport =
|
|
2263
|
+
grouping?.enabled || selectedTemplate?.grouping?.enabled;
|
|
2264
|
+
|
|
2264
2265
|
if (isGroupedReport) {
|
|
2265
2266
|
// Check if we're using a dynamic template (custom grouping) or predefined template
|
|
2266
2267
|
const isDynamicTemplate = !!template;
|
|
2267
|
-
|
|
2268
|
+
|
|
2268
2269
|
// Priority: custom URL > template URL > default URL
|
|
2269
|
-
const templateExecuteUrl =
|
|
2270
|
-
|
|
2270
|
+
const templateExecuteUrl =
|
|
2271
|
+
customUrls.executeUrl ||
|
|
2272
|
+
selectedTemplate?.executeUrl ||
|
|
2273
|
+
executeUrl;
|
|
2274
|
+
|
|
2271
2275
|
// Debug logging
|
|
2272
2276
|
console.log('🔍 [DEBUG] Grouped Report Detected:', {
|
|
2273
2277
|
selectedTemplate: selectedTemplate?.id,
|
|
@@ -2281,11 +2285,11 @@ const GenericReportImproved = ({
|
|
|
2281
2285
|
templateGroupingConfig: selectedTemplate?.grouping,
|
|
2282
2286
|
customGroupingConfig: groupingConfig,
|
|
2283
2287
|
usingCustomUrl: !!customUrls.executeUrl,
|
|
2284
|
-
usingTemplateExecuteUrl: !!selectedTemplate?.executeUrl
|
|
2288
|
+
usingTemplateExecuteUrl: !!selectedTemplate?.executeUrl,
|
|
2285
2289
|
});
|
|
2286
2290
|
|
|
2287
2291
|
let payload;
|
|
2288
|
-
|
|
2292
|
+
|
|
2289
2293
|
if (isDynamicTemplate) {
|
|
2290
2294
|
// For dynamic templates, send the complete template configuration
|
|
2291
2295
|
payload = {
|
|
@@ -2294,20 +2298,21 @@ const GenericReportImproved = ({
|
|
|
2294
2298
|
columns: selectedColumns,
|
|
2295
2299
|
joins: joins,
|
|
2296
2300
|
filters: flattenFilterCriteria(filterCriteria),
|
|
2297
|
-
sorting: sortCriteria
|
|
2301
|
+
sorting: sortCriteria,
|
|
2298
2302
|
};
|
|
2299
2303
|
} else {
|
|
2300
2304
|
// For predefined templates, use the existing approach
|
|
2301
2305
|
payload = {
|
|
2302
|
-
template_id:
|
|
2303
|
-
|
|
2306
|
+
template_id:
|
|
2307
|
+
selectedTemplate?.id || 'utilization_planning',
|
|
2308
|
+
filters: [],
|
|
2304
2309
|
};
|
|
2305
2310
|
}
|
|
2306
|
-
|
|
2311
|
+
|
|
2307
2312
|
console.log('🚀 [DEBUG] Sending grouped report request:', {
|
|
2308
2313
|
url: templateExecuteUrl,
|
|
2309
2314
|
method: 'POST',
|
|
2310
|
-
payload
|
|
2315
|
+
payload,
|
|
2311
2316
|
});
|
|
2312
2317
|
|
|
2313
2318
|
const result = await CustomFetch(
|
|
@@ -2319,7 +2324,10 @@ const GenericReportImproved = ({
|
|
|
2319
2324
|
console.log('📊 [DEBUG] Grouped report response:', result);
|
|
2320
2325
|
|
|
2321
2326
|
if (result.error) {
|
|
2322
|
-
console.error(
|
|
2327
|
+
console.error(
|
|
2328
|
+
'❌ Grouped report execution error:',
|
|
2329
|
+
result.error
|
|
2330
|
+
);
|
|
2323
2331
|
return { data: [], count: 0 };
|
|
2324
2332
|
}
|
|
2325
2333
|
|
|
@@ -2380,8 +2388,11 @@ const GenericReportImproved = ({
|
|
|
2380
2388
|
};
|
|
2381
2389
|
|
|
2382
2390
|
// Priority: custom URL > template URL > default URL
|
|
2383
|
-
const finalExecuteUrl =
|
|
2384
|
-
|
|
2391
|
+
const finalExecuteUrl =
|
|
2392
|
+
customUrls.executeUrl ||
|
|
2393
|
+
selectedTemplate?.executeUrl ||
|
|
2394
|
+
executeUrl;
|
|
2395
|
+
|
|
2385
2396
|
console.log('🔍 [DEBUG] Regular Report API Call:', {
|
|
2386
2397
|
selectedTemplate: selectedTemplate?.id,
|
|
2387
2398
|
customExecuteUrl: customUrls.executeUrl,
|
|
@@ -2389,7 +2400,7 @@ const GenericReportImproved = ({
|
|
|
2389
2400
|
defaultExecuteUrl: executeUrl,
|
|
2390
2401
|
finalExecuteUrl,
|
|
2391
2402
|
usingCustomUrl: !!customUrls.executeUrl,
|
|
2392
|
-
usingTemplateExecuteUrl: !!selectedTemplate?.executeUrl
|
|
2403
|
+
usingTemplateExecuteUrl: !!selectedTemplate?.executeUrl,
|
|
2393
2404
|
});
|
|
2394
2405
|
|
|
2395
2406
|
const result = await CustomFetch(
|
|
@@ -2448,7 +2459,7 @@ const GenericReportImproved = ({
|
|
|
2448
2459
|
selectedTemplate: selectedTemplate?.id,
|
|
2449
2460
|
hasGrouping: selectedTemplate?.grouping?.enabled,
|
|
2450
2461
|
executeUrl,
|
|
2451
|
-
setting
|
|
2462
|
+
setting,
|
|
2452
2463
|
});
|
|
2453
2464
|
|
|
2454
2465
|
if (!selectedTable || selectedColumns.length === 0) {
|
|
@@ -2467,22 +2478,25 @@ const GenericReportImproved = ({
|
|
|
2467
2478
|
const hasCustomGrouping = groupingConfig.enabled;
|
|
2468
2479
|
const hasGrouping = hasTemplateGrouping || hasCustomGrouping;
|
|
2469
2480
|
let fetchedDataCount = 0;
|
|
2470
|
-
|
|
2481
|
+
|
|
2471
2482
|
console.log('📋 [DEBUG] Template analysis:', {
|
|
2472
2483
|
selectedTemplate,
|
|
2473
2484
|
hasTemplateGrouping,
|
|
2474
2485
|
hasCustomGrouping,
|
|
2475
2486
|
hasGrouping,
|
|
2476
2487
|
customGroupingConfig: groupingConfig,
|
|
2477
|
-
templateGroupingConfig: selectedTemplate?.grouping
|
|
2488
|
+
templateGroupingConfig: selectedTemplate?.grouping,
|
|
2478
2489
|
});
|
|
2479
|
-
console.log(
|
|
2480
|
-
|
|
2490
|
+
console.log(
|
|
2491
|
+
'🎯 [DEBUG] Initial fetchedDataCount:',
|
|
2492
|
+
fetchedDataCount
|
|
2493
|
+
);
|
|
2494
|
+
|
|
2481
2495
|
if (hasGrouping) {
|
|
2482
2496
|
// Determine which grouping configuration to use
|
|
2483
2497
|
let effectiveGrouping;
|
|
2484
2498
|
let dynamicTemplate;
|
|
2485
|
-
|
|
2499
|
+
|
|
2486
2500
|
if (hasCustomGrouping && groupingConfig.groupByField) {
|
|
2487
2501
|
// Create dynamic template with custom grouping configuration
|
|
2488
2502
|
dynamicTemplate = {
|
|
@@ -2503,24 +2517,34 @@ const GenericReportImproved = ({
|
|
|
2503
2517
|
showGroupTotals: groupingConfig.showGroupTotals,
|
|
2504
2518
|
expandable: groupingConfig.expandable,
|
|
2505
2519
|
groupHeaderStyle: groupingConfig.groupHeaderStyle,
|
|
2506
|
-
emptyRowStyle: groupingConfig.emptyRowStyle
|
|
2507
|
-
}
|
|
2520
|
+
emptyRowStyle: groupingConfig.emptyRowStyle,
|
|
2521
|
+
},
|
|
2508
2522
|
};
|
|
2509
2523
|
effectiveGrouping = dynamicTemplate.grouping;
|
|
2510
|
-
|
|
2511
|
-
console.log(
|
|
2524
|
+
|
|
2525
|
+
console.log(
|
|
2526
|
+
'🏗️ [DEBUG] Created dynamic template for custom grouping:',
|
|
2527
|
+
dynamicTemplate
|
|
2528
|
+
);
|
|
2512
2529
|
} else if (hasCustomGrouping && !groupingConfig.groupByField) {
|
|
2513
2530
|
// Custom grouping enabled but no field selected - show error and fallback to regular report
|
|
2514
|
-
toast.error(
|
|
2515
|
-
|
|
2531
|
+
toast.error(
|
|
2532
|
+
'Please select a field to group by before executing the report.'
|
|
2533
|
+
);
|
|
2534
|
+
console.log(
|
|
2535
|
+
'⚠️ [DEBUG] Custom grouping enabled but no groupByField selected'
|
|
2536
|
+
);
|
|
2516
2537
|
setIsLoading(false);
|
|
2517
2538
|
return;
|
|
2518
2539
|
} else {
|
|
2519
2540
|
// Use existing template grouping
|
|
2520
2541
|
effectiveGrouping = selectedTemplate.grouping;
|
|
2521
|
-
console.log(
|
|
2542
|
+
console.log(
|
|
2543
|
+
'📋 [DEBUG] Using template grouping:',
|
|
2544
|
+
effectiveGrouping
|
|
2545
|
+
);
|
|
2522
2546
|
}
|
|
2523
|
-
|
|
2547
|
+
|
|
2524
2548
|
// For grouped reports, fetch data with reasonable limit
|
|
2525
2549
|
const groupedResult = await dataSource({
|
|
2526
2550
|
skip: 0,
|
|
@@ -2528,32 +2552,55 @@ const GenericReportImproved = ({
|
|
|
2528
2552
|
sortInfo: null,
|
|
2529
2553
|
filterValue: null,
|
|
2530
2554
|
grouping: effectiveGrouping,
|
|
2531
|
-
template: dynamicTemplate // Pass dynamic template if created
|
|
2555
|
+
template: dynamicTemplate, // Pass dynamic template if created
|
|
2532
2556
|
});
|
|
2533
|
-
|
|
2557
|
+
|
|
2534
2558
|
console.log('🎯 [DEBUG] Full API Response:', groupedResult);
|
|
2535
|
-
console.log(
|
|
2536
|
-
|
|
2537
|
-
|
|
2559
|
+
console.log(
|
|
2560
|
+
'🎯 [DEBUG] groupedResult.data:',
|
|
2561
|
+
groupedResult.data
|
|
2562
|
+
);
|
|
2563
|
+
console.log(
|
|
2564
|
+
'🎯 [DEBUG] groupedResult.data.grouped:',
|
|
2565
|
+
groupedResult.data?.grouped
|
|
2566
|
+
);
|
|
2567
|
+
console.log(
|
|
2568
|
+
'🎯 [DEBUG] groupedResult.data.groupedData:',
|
|
2569
|
+
groupedResult.data?.groupedData
|
|
2570
|
+
);
|
|
2538
2571
|
|
|
2539
|
-
if (
|
|
2572
|
+
if (
|
|
2573
|
+
groupedResult.data?.grouped &&
|
|
2574
|
+
groupedResult.data?.groupedData?.groups
|
|
2575
|
+
) {
|
|
2540
2576
|
console.log('✅ Processing grouped report');
|
|
2541
2577
|
setGroupedData(groupedResult.data); // Store entire response with grouped flag
|
|
2542
2578
|
setPreviewData([]); // Clear regular preview data
|
|
2543
2579
|
// Get the count from the fetched grouped data
|
|
2544
|
-
console.log(
|
|
2545
|
-
|
|
2546
|
-
|
|
2580
|
+
console.log(
|
|
2581
|
+
'🎯 [DEBUG] Before assignment - groupedResult.data.groupedData.totalRecords:',
|
|
2582
|
+
groupedResult.data.groupedData?.totalRecords
|
|
2583
|
+
);
|
|
2584
|
+
fetchedDataCount =
|
|
2585
|
+
groupedResult.data.groupedData?.totalRecords || 0;
|
|
2586
|
+
console.log(
|
|
2587
|
+
'🎯 [DEBUG] After assignment - fetchedDataCount:',
|
|
2588
|
+
fetchedDataCount
|
|
2589
|
+
);
|
|
2547
2590
|
console.log('🔢 [DEBUG] Grouped data count:', {
|
|
2548
|
-
totalRecords:
|
|
2591
|
+
totalRecords:
|
|
2592
|
+
groupedResult.data.groupedData?.totalRecords,
|
|
2549
2593
|
fetchedDataCount,
|
|
2550
|
-
hasGroups: !!groupedResult.data.groupedData?.groups
|
|
2594
|
+
hasGroups: !!groupedResult.data.groupedData?.groups,
|
|
2551
2595
|
});
|
|
2552
2596
|
} else {
|
|
2553
2597
|
// Fallback to regular display if server doesn't support grouping
|
|
2554
2598
|
console.log('⚠️ Fallback to regular display');
|
|
2555
2599
|
const fallbackData = groupedResult.data?.data || [];
|
|
2556
|
-
console.log(
|
|
2600
|
+
console.log(
|
|
2601
|
+
'🎯 [DEBUG] Fallback data length:',
|
|
2602
|
+
fallbackData.length
|
|
2603
|
+
);
|
|
2557
2604
|
setPreviewData(fallbackData);
|
|
2558
2605
|
fetchedDataCount = fallbackData.length;
|
|
2559
2606
|
}
|
|
@@ -2566,7 +2613,10 @@ const GenericReportImproved = ({
|
|
|
2566
2613
|
filterValue: null,
|
|
2567
2614
|
});
|
|
2568
2615
|
|
|
2569
|
-
if (
|
|
2616
|
+
if (
|
|
2617
|
+
currentPageResult.data &&
|
|
2618
|
+
currentPageResult.data.length > 0
|
|
2619
|
+
) {
|
|
2570
2620
|
setPreviewData(currentPageResult.data);
|
|
2571
2621
|
fetchedDataCount = currentPageResult.data.length;
|
|
2572
2622
|
}
|
|
@@ -2575,32 +2625,127 @@ const GenericReportImproved = ({
|
|
|
2575
2625
|
// Create grid columns for regular reports only
|
|
2576
2626
|
if (!hasGrouping) {
|
|
2577
2627
|
const dataToProcess = previewData.length > 0 ? previewData : [];
|
|
2578
|
-
|
|
2628
|
+
|
|
2579
2629
|
if (dataToProcess.length > 0) {
|
|
2580
2630
|
// Create grid columns dynamically from actual data with smart formatting
|
|
2581
2631
|
const firstRow = dataToProcess[0];
|
|
2582
2632
|
const dynamicColumns = Object.keys(firstRow).map((key) => {
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2633
|
+
// Find the column metadata from selected columns to get type information
|
|
2634
|
+
const columnMetadata = selectedColumns.find((col) => {
|
|
2635
|
+
// Handle both direct column names and aliased names
|
|
2636
|
+
return (
|
|
2637
|
+
col.column === key ||
|
|
2638
|
+
col.displayName === key ||
|
|
2639
|
+
key.includes(col.column) ||
|
|
2640
|
+
col.column.includes(key)
|
|
2641
|
+
);
|
|
2642
|
+
});
|
|
2593
2643
|
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2644
|
+
// Check if any value in this column is JSON
|
|
2645
|
+
const hasJsonValues = dataToProcess.some((row) => {
|
|
2646
|
+
return isJsonValue(row[key]);
|
|
2647
|
+
});
|
|
2648
|
+
|
|
2649
|
+
// Try to find column type from table columns with improved detection
|
|
2650
|
+
let columnType = 'varchar'; // default
|
|
2651
|
+
if (columnMetadata) {
|
|
2652
|
+
const tableColumn = tableColumns.find(
|
|
2653
|
+
(tc) => tc.name === columnMetadata.column
|
|
2654
|
+
);
|
|
2655
|
+
if (tableColumn) {
|
|
2656
|
+
columnType = tableColumn.type;
|
|
2657
|
+
} else {
|
|
2658
|
+
// Check in joined table columns
|
|
2659
|
+
joins.forEach((join) => {
|
|
2660
|
+
if (join.availableColumns) {
|
|
2661
|
+
const joinedColumn =
|
|
2662
|
+
join.availableColumns.find(
|
|
2663
|
+
(jc) =>
|
|
2664
|
+
jc.name ===
|
|
2665
|
+
columnMetadata.column
|
|
2666
|
+
);
|
|
2667
|
+
if (joinedColumn) {
|
|
2668
|
+
columnType = joinedColumn.type;
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2671
|
+
});
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
// Enhanced column type detection for status fields
|
|
2676
|
+
if (!columnMetadata || columnType === 'varchar') {
|
|
2677
|
+
// Check if this looks like a status field based on column name
|
|
2678
|
+
const lowerKey = key.toLowerCase();
|
|
2679
|
+
if (
|
|
2680
|
+
lowerKey.includes('status') ||
|
|
2681
|
+
lowerKey.includes('active') ||
|
|
2682
|
+
lowerKey.includes('enabled') ||
|
|
2683
|
+
lowerKey.includes('disabled')
|
|
2684
|
+
) {
|
|
2685
|
+
// Check if values look like boolean/status values
|
|
2686
|
+
const sampleValues = firstPageResult.data
|
|
2687
|
+
.slice(0, 5)
|
|
2688
|
+
.map((row) => row[key]);
|
|
2689
|
+
const hasStatusValues = sampleValues.some(
|
|
2690
|
+
(val) =>
|
|
2691
|
+
val === 0 ||
|
|
2692
|
+
val === 1 ||
|
|
2693
|
+
val === '0' ||
|
|
2694
|
+
val === '1' ||
|
|
2695
|
+
val === true ||
|
|
2696
|
+
val === false ||
|
|
2697
|
+
val === 'true' ||
|
|
2698
|
+
val === 'false'
|
|
2699
|
+
);
|
|
2700
|
+
if (hasStatusValues) {
|
|
2701
|
+
columnType = 'tinyint'; // This will trigger status formatting
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
|
|
2706
|
+
return {
|
|
2707
|
+
name: key,
|
|
2708
|
+
header: formatName(key), // Use formatted name as header
|
|
2709
|
+
defaultFlex: 1,
|
|
2710
|
+
minWidth: hasJsonValues ? 250 : 100, // Wider columns for JSON data
|
|
2711
|
+
maxWidth: hasJsonValues ? 400 : undefined, // Max width for JSON columns
|
|
2712
|
+
render: ({ value }) => {
|
|
2713
|
+
// Use smart formatting for the cell value
|
|
2714
|
+
const formattedValue = formatSmartValue(
|
|
2715
|
+
value,
|
|
2716
|
+
key,
|
|
2717
|
+
columnType
|
|
2718
|
+
);
|
|
2598
2719
|
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2720
|
+
// For JSON content, wrap in a container that allows full height expansion
|
|
2721
|
+
if (isJsonValue(value)) {
|
|
2722
|
+
return (
|
|
2723
|
+
<div
|
|
2724
|
+
style={{
|
|
2725
|
+
whiteSpace: 'normal',
|
|
2726
|
+
wordWrap: 'break-word',
|
|
2727
|
+
lineHeight: '1.4',
|
|
2728
|
+
padding: '4px 0',
|
|
2729
|
+
width: '100%',
|
|
2730
|
+
}}
|
|
2731
|
+
>
|
|
2732
|
+
{formattedValue}
|
|
2733
|
+
</div>
|
|
2734
|
+
);
|
|
2735
|
+
}
|
|
2736
|
+
|
|
2737
|
+
return formattedValue;
|
|
2738
|
+
},
|
|
2739
|
+
};
|
|
2740
|
+
});
|
|
2741
|
+
setGridColumns(dynamicColumns);
|
|
2742
|
+
} else {
|
|
2743
|
+
// Fallback to selected columns if no data
|
|
2744
|
+
const columns = selectedColumns.map((col) => {
|
|
2745
|
+
// Find column type from table columns with enhanced detection
|
|
2746
|
+
let columnType = 'varchar';
|
|
2602
2747
|
const tableColumn = tableColumns.find(
|
|
2603
|
-
(tc) => tc.name ===
|
|
2748
|
+
(tc) => tc.name === col.column
|
|
2604
2749
|
);
|
|
2605
2750
|
if (tableColumn) {
|
|
2606
2751
|
columnType = tableColumn.type;
|
|
@@ -2610,9 +2755,7 @@ const GenericReportImproved = ({
|
|
|
2610
2755
|
if (join.availableColumns) {
|
|
2611
2756
|
const joinedColumn =
|
|
2612
2757
|
join.availableColumns.find(
|
|
2613
|
-
(jc) =>
|
|
2614
|
-
jc.name ===
|
|
2615
|
-
columnMetadata.column
|
|
2758
|
+
(jc) => jc.name === col.column
|
|
2616
2759
|
);
|
|
2617
2760
|
if (joinedColumn) {
|
|
2618
2761
|
columnType = joinedColumn.type;
|
|
@@ -2620,144 +2763,53 @@ const GenericReportImproved = ({
|
|
|
2620
2763
|
}
|
|
2621
2764
|
});
|
|
2622
2765
|
}
|
|
2623
|
-
}
|
|
2624
2766
|
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
) {
|
|
2635
|
-
// Check if values look like boolean/status values
|
|
2636
|
-
const sampleValues = firstPageResult.data
|
|
2637
|
-
.slice(0, 5)
|
|
2638
|
-
.map((row) => row[key]);
|
|
2639
|
-
const hasStatusValues = sampleValues.some(
|
|
2640
|
-
(val) =>
|
|
2641
|
-
val === 0 ||
|
|
2642
|
-
val === 1 ||
|
|
2643
|
-
val === '0' ||
|
|
2644
|
-
val === '1' ||
|
|
2645
|
-
val === true ||
|
|
2646
|
-
val === false ||
|
|
2647
|
-
val === 'true' ||
|
|
2648
|
-
val === 'false'
|
|
2649
|
-
);
|
|
2650
|
-
if (hasStatusValues) {
|
|
2767
|
+
// Enhanced column type detection for status fields
|
|
2768
|
+
if (columnType === 'varchar') {
|
|
2769
|
+
const lowerColumn = col.column.toLowerCase();
|
|
2770
|
+
if (
|
|
2771
|
+
lowerColumn.includes('status') ||
|
|
2772
|
+
lowerColumn.includes('active') ||
|
|
2773
|
+
lowerColumn.includes('enabled') ||
|
|
2774
|
+
lowerColumn.includes('disabled')
|
|
2775
|
+
) {
|
|
2651
2776
|
columnType = 'tinyint'; // This will trigger status formatting
|
|
2652
2777
|
}
|
|
2653
2778
|
}
|
|
2654
|
-
}
|
|
2655
|
-
|
|
2656
|
-
return {
|
|
2657
|
-
name: key,
|
|
2658
|
-
header: formatName(key), // Use formatted name as header
|
|
2659
|
-
defaultFlex: 1,
|
|
2660
|
-
minWidth: hasJsonValues ? 250 : 100, // Wider columns for JSON data
|
|
2661
|
-
maxWidth: hasJsonValues ? 400 : undefined, // Max width for JSON columns
|
|
2662
|
-
render: ({ value }) => {
|
|
2663
|
-
// Use smart formatting for the cell value
|
|
2664
|
-
const formattedValue = formatSmartValue(
|
|
2665
|
-
value,
|
|
2666
|
-
key,
|
|
2667
|
-
columnType
|
|
2668
|
-
);
|
|
2669
2779
|
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
>
|
|
2682
|
-
{formattedValue}
|
|
2683
|
-
</div>
|
|
2780
|
+
return {
|
|
2781
|
+
name:
|
|
2782
|
+
col.displayName || `${col.table}.${col.column}`,
|
|
2783
|
+
header: col.displayName || formatName(col.column),
|
|
2784
|
+
defaultFlex: 1,
|
|
2785
|
+
minWidth: 100,
|
|
2786
|
+
render: ({ value }) => {
|
|
2787
|
+
const formattedValue = formatSmartValue(
|
|
2788
|
+
value,
|
|
2789
|
+
col.column,
|
|
2790
|
+
columnType
|
|
2684
2791
|
);
|
|
2685
|
-
}
|
|
2686
2792
|
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
} else {
|
|
2703
|
-
// Check in joined table columns
|
|
2704
|
-
joins.forEach((join) => {
|
|
2705
|
-
if (join.availableColumns) {
|
|
2706
|
-
const joinedColumn = join.availableColumns.find(
|
|
2707
|
-
(jc) => jc.name === col.column
|
|
2708
|
-
);
|
|
2709
|
-
if (joinedColumn) {
|
|
2710
|
-
columnType = joinedColumn.type;
|
|
2793
|
+
// For JSON content, wrap in a container that allows full height expansion
|
|
2794
|
+
if (isJsonValue(value)) {
|
|
2795
|
+
return (
|
|
2796
|
+
<div
|
|
2797
|
+
style={{
|
|
2798
|
+
whiteSpace: 'normal',
|
|
2799
|
+
wordWrap: 'break-word',
|
|
2800
|
+
lineHeight: '1.4',
|
|
2801
|
+
padding: '4px 0',
|
|
2802
|
+
width: '100%',
|
|
2803
|
+
}}
|
|
2804
|
+
>
|
|
2805
|
+
{formattedValue}
|
|
2806
|
+
</div>
|
|
2807
|
+
);
|
|
2711
2808
|
}
|
|
2712
|
-
}
|
|
2713
|
-
});
|
|
2714
|
-
}
|
|
2715
|
-
|
|
2716
|
-
// Enhanced column type detection for status fields
|
|
2717
|
-
if (columnType === 'varchar') {
|
|
2718
|
-
const lowerColumn = col.column.toLowerCase();
|
|
2719
|
-
if (
|
|
2720
|
-
lowerColumn.includes('status') ||
|
|
2721
|
-
lowerColumn.includes('active') ||
|
|
2722
|
-
lowerColumn.includes('enabled') ||
|
|
2723
|
-
lowerColumn.includes('disabled')
|
|
2724
|
-
) {
|
|
2725
|
-
columnType = 'tinyint'; // This will trigger status formatting
|
|
2726
|
-
}
|
|
2727
|
-
}
|
|
2728
|
-
|
|
2729
|
-
return {
|
|
2730
|
-
name: col.displayName || `${col.table}.${col.column}`,
|
|
2731
|
-
header: col.displayName || formatName(col.column),
|
|
2732
|
-
defaultFlex: 1,
|
|
2733
|
-
minWidth: 100,
|
|
2734
|
-
render: ({ value }) => {
|
|
2735
|
-
const formattedValue = formatSmartValue(
|
|
2736
|
-
value,
|
|
2737
|
-
col.column,
|
|
2738
|
-
columnType
|
|
2739
|
-
);
|
|
2740
|
-
|
|
2741
|
-
// For JSON content, wrap in a container that allows full height expansion
|
|
2742
|
-
if (isJsonValue(value)) {
|
|
2743
|
-
return (
|
|
2744
|
-
<div
|
|
2745
|
-
style={{
|
|
2746
|
-
whiteSpace: 'normal',
|
|
2747
|
-
wordWrap: 'break-word',
|
|
2748
|
-
lineHeight: '1.4',
|
|
2749
|
-
padding: '4px 0',
|
|
2750
|
-
width: '100%',
|
|
2751
|
-
}}
|
|
2752
|
-
>
|
|
2753
|
-
{formattedValue}
|
|
2754
|
-
</div>
|
|
2755
|
-
);
|
|
2756
|
-
}
|
|
2757
2809
|
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2810
|
+
return formattedValue;
|
|
2811
|
+
},
|
|
2812
|
+
};
|
|
2761
2813
|
});
|
|
2762
2814
|
setGridColumns(columns);
|
|
2763
2815
|
}
|
|
@@ -2769,7 +2821,10 @@ const GenericReportImproved = ({
|
|
|
2769
2821
|
|
|
2770
2822
|
// Report executed successfully
|
|
2771
2823
|
console.log('🎯 [DEBUG] Final fetchedDataCount:', fetchedDataCount);
|
|
2772
|
-
console.log(
|
|
2824
|
+
console.log(
|
|
2825
|
+
'🎯 [DEBUG] Type of fetchedDataCount:',
|
|
2826
|
+
typeof fetchedDataCount
|
|
2827
|
+
);
|
|
2773
2828
|
} catch (error) {
|
|
2774
2829
|
toast.error('Failed to execute report');
|
|
2775
2830
|
console.error('Error executing report:', error);
|
|
@@ -2870,8 +2925,9 @@ const GenericReportImproved = ({
|
|
|
2870
2925
|
showEmptyRows: config.grouping.showEmptyRows || false,
|
|
2871
2926
|
showGroupTotals: config.grouping.showGroupTotals || true,
|
|
2872
2927
|
expandable: config.grouping.expandable || true,
|
|
2873
|
-
groupHeaderStyle:
|
|
2874
|
-
|
|
2928
|
+
groupHeaderStyle:
|
|
2929
|
+
config.grouping.groupHeaderStyle || 'primary',
|
|
2930
|
+
emptyRowStyle: config.grouping.emptyRowStyle || 'light',
|
|
2875
2931
|
});
|
|
2876
2932
|
} else {
|
|
2877
2933
|
// Reset grouping config if not present in saved report
|
|
@@ -2886,7 +2942,7 @@ const GenericReportImproved = ({
|
|
|
2886
2942
|
showGroupTotals: true,
|
|
2887
2943
|
expandable: true,
|
|
2888
2944
|
groupHeaderStyle: 'primary',
|
|
2889
|
-
emptyRowStyle: 'light'
|
|
2945
|
+
emptyRowStyle: 'light',
|
|
2890
2946
|
});
|
|
2891
2947
|
}
|
|
2892
2948
|
|
|
@@ -2921,9 +2977,9 @@ const GenericReportImproved = ({
|
|
|
2921
2977
|
templateId: template.id,
|
|
2922
2978
|
templateName: template.name,
|
|
2923
2979
|
hasGrouping: template.grouping?.enabled,
|
|
2924
|
-
groupingConfig: template.grouping
|
|
2980
|
+
groupingConfig: template.grouping,
|
|
2925
2981
|
});
|
|
2926
|
-
|
|
2982
|
+
|
|
2927
2983
|
setSelectedTemplate(template);
|
|
2928
2984
|
|
|
2929
2985
|
// Set main table
|
|
@@ -2996,7 +3052,9 @@ const GenericReportImproved = ({
|
|
|
2996
3052
|
};
|
|
2997
3053
|
|
|
2998
3054
|
template.quickFilters.forEach((quickFilter) => {
|
|
2999
|
-
let filterValue = convertDynamicDateValue(
|
|
3055
|
+
let filterValue = convertDynamicDateValue(
|
|
3056
|
+
quickFilter.value
|
|
3057
|
+
);
|
|
3000
3058
|
|
|
3001
3059
|
// Parse the field to extract table and column
|
|
3002
3060
|
const fieldParts = quickFilter.field.split('.');
|
|
@@ -3004,7 +3062,12 @@ const GenericReportImproved = ({
|
|
|
3004
3062
|
const column = fieldParts.slice(1).join('.'); // Handle cases like "leads.user.name"
|
|
3005
3063
|
|
|
3006
3064
|
// Convert date range objects to comma-separated string for BETWEEN operator
|
|
3007
|
-
if (
|
|
3065
|
+
if (
|
|
3066
|
+
quickFilter.type === 'date_range' &&
|
|
3067
|
+
typeof filterValue === 'object' &&
|
|
3068
|
+
filterValue.start &&
|
|
3069
|
+
filterValue.end
|
|
3070
|
+
) {
|
|
3008
3071
|
filterValue = `${filterValue.start},${filterValue.end}`;
|
|
3009
3072
|
}
|
|
3010
3073
|
|
|
@@ -3015,7 +3078,9 @@ const GenericReportImproved = ({
|
|
|
3015
3078
|
column: column,
|
|
3016
3079
|
operator:
|
|
3017
3080
|
quickFilter.operator ||
|
|
3018
|
-
(quickFilter.type === 'date_range'
|
|
3081
|
+
(quickFilter.type === 'date_range'
|
|
3082
|
+
? 'BETWEEN'
|
|
3083
|
+
: '='),
|
|
3019
3084
|
value: filterValue,
|
|
3020
3085
|
label: quickFilter.label,
|
|
3021
3086
|
type: quickFilter.type,
|
|
@@ -3025,7 +3090,6 @@ const GenericReportImproved = ({
|
|
|
3025
3090
|
});
|
|
3026
3091
|
|
|
3027
3092
|
setFilterCriteria(updatedFilterCriteria);
|
|
3028
|
-
|
|
3029
3093
|
}
|
|
3030
3094
|
|
|
3031
3095
|
// Apply template grouping configuration
|
|
@@ -3040,14 +3104,15 @@ const GenericReportImproved = ({
|
|
|
3040
3104
|
showEmptyRows: template.grouping.showEmptyRows || false,
|
|
3041
3105
|
showGroupTotals: template.grouping.showGroupTotals || true,
|
|
3042
3106
|
expandable: template.grouping.expandable !== false, // Default true
|
|
3043
|
-
groupHeaderStyle:
|
|
3044
|
-
|
|
3107
|
+
groupHeaderStyle:
|
|
3108
|
+
template.grouping.groupHeaderStyle || 'primary',
|
|
3109
|
+
emptyRowStyle: template.grouping.emptyRowStyle || 'light',
|
|
3045
3110
|
});
|
|
3046
|
-
|
|
3111
|
+
|
|
3047
3112
|
console.log('🏗️ [DEBUG] Applied template grouping config:', {
|
|
3048
3113
|
enabled: template.grouping.enabled,
|
|
3049
3114
|
groupByField: template.grouping.groupByField,
|
|
3050
|
-
groupDisplayName: template.grouping.groupDisplayName
|
|
3115
|
+
groupDisplayName: template.grouping.groupDisplayName,
|
|
3051
3116
|
});
|
|
3052
3117
|
} else {
|
|
3053
3118
|
// Reset grouping config if template doesn't have grouping
|
|
@@ -3062,7 +3127,7 @@ const GenericReportImproved = ({
|
|
|
3062
3127
|
showGroupTotals: true,
|
|
3063
3128
|
expandable: true,
|
|
3064
3129
|
groupHeaderStyle: 'primary',
|
|
3065
|
-
emptyRowStyle: 'light'
|
|
3130
|
+
emptyRowStyle: 'light',
|
|
3066
3131
|
});
|
|
3067
3132
|
}
|
|
3068
3133
|
|
|
@@ -4744,7 +4809,12 @@ const GenericReportImproved = ({
|
|
|
4744
4809
|
const column = fieldParts.slice(1).join('.'); // Handle cases like "leads.user.name"
|
|
4745
4810
|
|
|
4746
4811
|
// Convert date range objects to comma-separated string for BETWEEN operator
|
|
4747
|
-
if (
|
|
4812
|
+
if (
|
|
4813
|
+
quickFilter.type === 'date_range' &&
|
|
4814
|
+
typeof filterValue === 'object' &&
|
|
4815
|
+
filterValue.start &&
|
|
4816
|
+
filterValue.end
|
|
4817
|
+
) {
|
|
4748
4818
|
filterValue = `${filterValue.start},${filterValue.end}`;
|
|
4749
4819
|
}
|
|
4750
4820
|
|
|
@@ -5569,7 +5639,6 @@ const GenericReportImproved = ({
|
|
|
5569
5639
|
suggestions: smartSuggestions,
|
|
5570
5640
|
});
|
|
5571
5641
|
|
|
5572
|
-
|
|
5573
5642
|
return (
|
|
5574
5643
|
<div className={styles.filtersSection}>
|
|
5575
5644
|
{/* Template Quick Filters */}
|
|
@@ -6472,8 +6541,11 @@ const GenericReportImproved = ({
|
|
|
6472
6541
|
console.log('📁 Generated filename:', fileName);
|
|
6473
6542
|
|
|
6474
6543
|
// Priority: custom export URL > template export URL > default export URL
|
|
6475
|
-
const finalExportUrl =
|
|
6476
|
-
|
|
6544
|
+
const finalExportUrl =
|
|
6545
|
+
customUrls.exportUrl ||
|
|
6546
|
+
selectedTemplate?.exportUrl ||
|
|
6547
|
+
exportUrl;
|
|
6548
|
+
|
|
6477
6549
|
console.log('🔍 [DEBUG] Export API Call:', {
|
|
6478
6550
|
selectedTemplate: selectedTemplate?.id,
|
|
6479
6551
|
customExportUrl: customUrls.exportUrl,
|
|
@@ -6481,7 +6553,7 @@ const GenericReportImproved = ({
|
|
|
6481
6553
|
defaultExportUrl: exportUrl,
|
|
6482
6554
|
finalExportUrl,
|
|
6483
6555
|
usingCustomExportUrl: !!customUrls.exportUrl,
|
|
6484
|
-
usingTemplateExportUrl: !!selectedTemplate?.exportUrl
|
|
6556
|
+
usingTemplateExportUrl: !!selectedTemplate?.exportUrl,
|
|
6485
6557
|
});
|
|
6486
6558
|
|
|
6487
6559
|
// Use Download component which handles error checking and blob download properly
|
|
@@ -6618,9 +6690,12 @@ const GenericReportImproved = ({
|
|
|
6618
6690
|
groupedData: groupedData,
|
|
6619
6691
|
hasGroupedData: !!groupedData,
|
|
6620
6692
|
hasGroups: !!(groupedData && groupedData.groups),
|
|
6621
|
-
groupsLength:
|
|
6693
|
+
groupsLength:
|
|
6694
|
+
groupedData && groupedData.groups
|
|
6695
|
+
? groupedData.groups.length
|
|
6696
|
+
: 'undefined',
|
|
6622
6697
|
selectedColumns: selectedColumns,
|
|
6623
|
-
selectedTemplate: selectedTemplate?.name
|
|
6698
|
+
selectedTemplate: selectedTemplate?.name,
|
|
6624
6699
|
});
|
|
6625
6700
|
|
|
6626
6701
|
// Check the correct path: groupedData.groupedData.groups
|
|
@@ -6629,9 +6704,15 @@ const GenericReportImproved = ({
|
|
|
6629
6704
|
console.error('❌ [DEBUG] Export failed - no grouped data:', {
|
|
6630
6705
|
groupedData: groupedData,
|
|
6631
6706
|
hasGroupedData: !!groupedData,
|
|
6632
|
-
hasGroupedDataProperty: !!(
|
|
6633
|
-
|
|
6634
|
-
|
|
6707
|
+
hasGroupedDataProperty: !!(
|
|
6708
|
+
groupedData && groupedData.groupedData
|
|
6709
|
+
),
|
|
6710
|
+
hasGroups: !!(
|
|
6711
|
+
groupedData &&
|
|
6712
|
+
groupedData.groupedData &&
|
|
6713
|
+
groupedData.groupedData.groups
|
|
6714
|
+
),
|
|
6715
|
+
groupsLength: actualGroups ? actualGroups.length : 'undefined',
|
|
6635
6716
|
});
|
|
6636
6717
|
toast.error('No grouped data available for export');
|
|
6637
6718
|
return;
|
|
@@ -6645,7 +6726,9 @@ const GenericReportImproved = ({
|
|
|
6645
6726
|
const worksheetData = [];
|
|
6646
6727
|
|
|
6647
6728
|
// Add headers
|
|
6648
|
-
const headers = selectedColumns.map(
|
|
6729
|
+
const headers = selectedColumns.map(
|
|
6730
|
+
(col) => col.displayName || col.column
|
|
6731
|
+
);
|
|
6649
6732
|
worksheetData.push(headers);
|
|
6650
6733
|
|
|
6651
6734
|
// Helper function to get cell value (same logic as SectionGroupedReport)
|
|
@@ -6655,13 +6738,25 @@ const GenericReportImproved = ({
|
|
|
6655
6738
|
let value;
|
|
6656
6739
|
|
|
6657
6740
|
// Try multiple field access patterns
|
|
6658
|
-
if (
|
|
6741
|
+
if (
|
|
6742
|
+
column.displayName &&
|
|
6743
|
+
row[`\`${column.displayName}\``] !== undefined
|
|
6744
|
+
) {
|
|
6659
6745
|
value = row[`\`${column.displayName}\``];
|
|
6660
|
-
} else if (
|
|
6746
|
+
} else if (
|
|
6747
|
+
column.displayName &&
|
|
6748
|
+
row[column.displayName] !== undefined
|
|
6749
|
+
) {
|
|
6661
6750
|
value = row[column.displayName];
|
|
6662
|
-
} else if (
|
|
6751
|
+
} else if (
|
|
6752
|
+
column.table &&
|
|
6753
|
+
row[`${column.table}_${column.column}`] !== undefined
|
|
6754
|
+
) {
|
|
6663
6755
|
value = row[`${column.table}_${column.column}`];
|
|
6664
|
-
} else if (
|
|
6756
|
+
} else if (
|
|
6757
|
+
column.table &&
|
|
6758
|
+
row[`\`${column.table}_${column.column}\``] !== undefined
|
|
6759
|
+
) {
|
|
6665
6760
|
value = row[`\`${column.table}_${column.column}\``];
|
|
6666
6761
|
} else if (row[column.column] !== undefined) {
|
|
6667
6762
|
value = row[column.column];
|
|
@@ -6673,11 +6768,11 @@ const GenericReportImproved = ({
|
|
|
6673
6768
|
|
|
6674
6769
|
// Format the value
|
|
6675
6770
|
if (value === null || value === undefined) return '';
|
|
6676
|
-
|
|
6771
|
+
|
|
6677
6772
|
// Handle dates
|
|
6678
6773
|
if (column.type === 'date' || column.type === 'datetime') {
|
|
6679
6774
|
if (value && moment(value).isValid()) {
|
|
6680
|
-
return column.type === 'date'
|
|
6775
|
+
return column.type === 'date'
|
|
6681
6776
|
? moment(value).format('DD/MM/YYYY')
|
|
6682
6777
|
: moment(value).format('DD/MM/YYYY HH:mm:ss');
|
|
6683
6778
|
}
|
|
@@ -6685,24 +6780,41 @@ const GenericReportImproved = ({
|
|
|
6685
6780
|
|
|
6686
6781
|
// Handle Comments field - strip HTML and formatting
|
|
6687
6782
|
if (column.displayName === 'Comments' && value) {
|
|
6688
|
-
return value
|
|
6783
|
+
return value
|
|
6784
|
+
.replace(/• /g, '')
|
|
6785
|
+
.replace(/<[^>]*>/g, '')
|
|
6786
|
+
.trim();
|
|
6689
6787
|
}
|
|
6690
6788
|
|
|
6691
6789
|
// Handle ABN (Australian Business Number) columns for export
|
|
6692
|
-
if (
|
|
6693
|
-
(column.
|
|
6790
|
+
if (
|
|
6791
|
+
(column.displayName &&
|
|
6792
|
+
column.displayName.toLowerCase().includes('abn')) ||
|
|
6793
|
+
(column.column &&
|
|
6794
|
+
column.column.toLowerCase().includes('abn'))
|
|
6795
|
+
) {
|
|
6694
6796
|
const cleanABN = String(value).replace(/\s/g, '');
|
|
6695
6797
|
if (cleanABN.length === 11 && /^\d{11}$/.test(cleanABN)) {
|
|
6696
|
-
return `${cleanABN.slice(0,
|
|
6798
|
+
return `${cleanABN.slice(0, 2)} ${cleanABN.slice(
|
|
6799
|
+
2,
|
|
6800
|
+
5
|
|
6801
|
+
)} ${cleanABN.slice(5, 8)} ${cleanABN.slice(8, 11)}`;
|
|
6697
6802
|
}
|
|
6698
6803
|
}
|
|
6699
6804
|
|
|
6700
6805
|
// Handle ACN (Australian Company Number) columns for export
|
|
6701
|
-
if (
|
|
6702
|
-
(column.
|
|
6806
|
+
if (
|
|
6807
|
+
(column.displayName &&
|
|
6808
|
+
column.displayName.toLowerCase().includes('acn')) ||
|
|
6809
|
+
(column.column &&
|
|
6810
|
+
column.column.toLowerCase().includes('acn'))
|
|
6811
|
+
) {
|
|
6703
6812
|
const cleanACN = String(value).replace(/\s/g, '');
|
|
6704
6813
|
if (cleanACN.length === 9 && /^\d{9}$/.test(cleanACN)) {
|
|
6705
|
-
return `${cleanACN.slice(0,
|
|
6814
|
+
return `${cleanACN.slice(0, 3)} ${cleanACN.slice(
|
|
6815
|
+
3,
|
|
6816
|
+
6
|
|
6817
|
+
)} ${cleanACN.slice(6, 9)}`;
|
|
6706
6818
|
}
|
|
6707
6819
|
}
|
|
6708
6820
|
|
|
@@ -6712,15 +6824,18 @@ const GenericReportImproved = ({
|
|
|
6712
6824
|
// Add data for each group
|
|
6713
6825
|
actualGroups.forEach((group, groupIndex) => {
|
|
6714
6826
|
// Add group header row
|
|
6715
|
-
const groupDisplayName =
|
|
6827
|
+
const groupDisplayName =
|
|
6828
|
+
selectedTemplate?.grouping?.groupDisplayName || 'Group';
|
|
6716
6829
|
const groupHeaderRow = Array(headers.length).fill('');
|
|
6717
6830
|
groupHeaderRow[0] = `${groupDisplayName}: ${group.groupDisplayName}`;
|
|
6718
6831
|
worksheetData.push(groupHeaderRow);
|
|
6719
6832
|
|
|
6720
6833
|
// Add data rows for this group
|
|
6721
6834
|
const allRows = [...group.rows, ...(group.emptyRows || [])];
|
|
6722
|
-
allRows.forEach(row => {
|
|
6723
|
-
const dataRow = selectedColumns.map(column =>
|
|
6835
|
+
allRows.forEach((row) => {
|
|
6836
|
+
const dataRow = selectedColumns.map((column) =>
|
|
6837
|
+
getCellValue(row, column)
|
|
6838
|
+
);
|
|
6724
6839
|
worksheetData.push(dataRow);
|
|
6725
6840
|
});
|
|
6726
6841
|
|
|
@@ -6734,34 +6849,45 @@ const GenericReportImproved = ({
|
|
|
6734
6849
|
const worksheet = XLSX.utils.aoa_to_sheet(worksheetData);
|
|
6735
6850
|
|
|
6736
6851
|
// Set column widths based on content
|
|
6737
|
-
const colWidths = selectedColumns.map(column => {
|
|
6852
|
+
const colWidths = selectedColumns.map((column) => {
|
|
6738
6853
|
switch (column.displayName) {
|
|
6739
|
-
case 'Comments':
|
|
6854
|
+
case 'Comments':
|
|
6855
|
+
return { wch: 40 };
|
|
6740
6856
|
case 'Project':
|
|
6741
|
-
case 'Project Name':
|
|
6857
|
+
case 'Project Name':
|
|
6858
|
+
return { wch: 25 };
|
|
6742
6859
|
case 'User':
|
|
6743
|
-
case 'Assigned User':
|
|
6860
|
+
case 'Assigned User':
|
|
6861
|
+
return { wch: 20 };
|
|
6744
6862
|
case 'Start Date':
|
|
6745
6863
|
case 'End Date':
|
|
6746
|
-
case 'Database Updated':
|
|
6747
|
-
|
|
6864
|
+
case 'Database Updated':
|
|
6865
|
+
return { wch: 15 };
|
|
6866
|
+
default:
|
|
6867
|
+
return { wch: 18 };
|
|
6748
6868
|
}
|
|
6749
6869
|
});
|
|
6750
6870
|
worksheet['!cols'] = colWidths;
|
|
6751
6871
|
|
|
6752
|
-
|
|
6753
6872
|
// Add worksheet to workbook
|
|
6754
6873
|
const sheetName = selectedTemplate.name || 'Grouped Report';
|
|
6755
6874
|
XLSX.utils.book_append_sheet(workbook, worksheet, sheetName);
|
|
6756
6875
|
|
|
6757
6876
|
// Generate file and download
|
|
6758
|
-
const filename = `${selectedTemplate.name.replace(
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6877
|
+
const filename = `${selectedTemplate.name.replace(
|
|
6878
|
+
/[^a-zA-Z0-9]/g,
|
|
6879
|
+
'_'
|
|
6880
|
+
)}_grouped_report_${new Date().toISOString().split('T')[0]}.xlsx`;
|
|
6881
|
+
const excelBuffer = XLSX.write(workbook, {
|
|
6882
|
+
bookType: 'xlsx',
|
|
6883
|
+
type: 'array',
|
|
6884
|
+
});
|
|
6885
|
+
const blob = new Blob([excelBuffer], {
|
|
6886
|
+
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
6887
|
+
});
|
|
6888
|
+
|
|
6762
6889
|
saveAs(blob, filename);
|
|
6763
6890
|
toast.success('Excel export completed successfully!');
|
|
6764
|
-
|
|
6765
6891
|
} catch (error) {
|
|
6766
6892
|
console.error('Export error:', error);
|
|
6767
6893
|
toast.error(`Export failed: ${error.message}`);
|
|
@@ -6845,7 +6971,9 @@ const GenericReportImproved = ({
|
|
|
6845
6971
|
joins: joins,
|
|
6846
6972
|
filters: filterCriteria,
|
|
6847
6973
|
sorting: sortCriteria,
|
|
6848
|
-
grouping: groupingConfig.enabled
|
|
6974
|
+
grouping: groupingConfig.enabled
|
|
6975
|
+
? groupingConfig
|
|
6976
|
+
: null,
|
|
6849
6977
|
isGrouped: groupingConfig.enabled,
|
|
6850
6978
|
customCalculatedFields: customCalculatedFields,
|
|
6851
6979
|
customUrls: customUrls,
|
|
@@ -6911,187 +7039,288 @@ const GenericReportImproved = ({
|
|
|
6911
7039
|
<input
|
|
6912
7040
|
type="checkbox"
|
|
6913
7041
|
checked={groupingConfig.enabled}
|
|
6914
|
-
onChange={(e) =>
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
7042
|
+
onChange={(e) =>
|
|
7043
|
+
setGroupingConfig({
|
|
7044
|
+
...groupingConfig,
|
|
7045
|
+
enabled: e.target.checked,
|
|
7046
|
+
// Reset fields when disabled
|
|
7047
|
+
...(e.target.checked
|
|
7048
|
+
? {}
|
|
7049
|
+
: {
|
|
7050
|
+
groupByField: '',
|
|
7051
|
+
groupDisplayName: '',
|
|
7052
|
+
}),
|
|
6921
7053
|
})
|
|
6922
|
-
}
|
|
7054
|
+
}
|
|
6923
7055
|
className={styles.toggleInput}
|
|
6924
7056
|
/>
|
|
6925
7057
|
<span className={styles.toggleCheckbox}></span>
|
|
6926
|
-
<span className={styles.toggleText}>
|
|
7058
|
+
<span className={styles.toggleText}>
|
|
7059
|
+
Enable Grouping
|
|
7060
|
+
</span>
|
|
6927
7061
|
</label>
|
|
6928
7062
|
<p className={styles.helpText}>
|
|
6929
|
-
Group data by a field to organize results into
|
|
7063
|
+
Group data by a field to organize results into
|
|
7064
|
+
sections (e.g., "Projects by Facility")
|
|
6930
7065
|
</p>
|
|
6931
7066
|
</div>
|
|
6932
|
-
|
|
7067
|
+
|
|
6933
7068
|
{groupingConfig.enabled && (
|
|
6934
7069
|
<div className={styles.groupingOptions}>
|
|
6935
7070
|
{/* Group By Field Selection */}
|
|
6936
7071
|
<div className={styles.formGroup}>
|
|
6937
|
-
<label className={styles.formLabel}>
|
|
7072
|
+
<label className={styles.formLabel}>
|
|
7073
|
+
Group By Field *
|
|
7074
|
+
</label>
|
|
6938
7075
|
<select
|
|
6939
7076
|
value={groupingConfig.groupByField}
|
|
6940
7077
|
onChange={(e) => {
|
|
6941
|
-
const selectedOption =
|
|
6942
|
-
|
|
7078
|
+
const selectedOption =
|
|
7079
|
+
e.target.selectedOptions[0];
|
|
7080
|
+
const fieldDisplayName =
|
|
7081
|
+
selectedOption?.text || '';
|
|
6943
7082
|
// Extract table name for display
|
|
6944
|
-
const tableMatch =
|
|
6945
|
-
|
|
6946
|
-
|
|
7083
|
+
const tableMatch =
|
|
7084
|
+
fieldDisplayName.match(
|
|
7085
|
+
/^([^.]+)\./
|
|
7086
|
+
);
|
|
7087
|
+
const tableName = tableMatch
|
|
7088
|
+
? tableMatch[1]
|
|
7089
|
+
: 'Group';
|
|
7090
|
+
|
|
6947
7091
|
setGroupingConfig({
|
|
6948
7092
|
...groupingConfig,
|
|
6949
7093
|
groupByField: e.target.value,
|
|
6950
|
-
groupDisplayName:
|
|
6951
|
-
|
|
7094
|
+
groupDisplayName:
|
|
7095
|
+
groupingConfig.groupDisplayName ||
|
|
7096
|
+
tableName,
|
|
7097
|
+
sortBy: e.target.value, // Auto-set sort field
|
|
6952
7098
|
});
|
|
6953
7099
|
}}
|
|
6954
7100
|
className={styles.formSelect}
|
|
6955
7101
|
>
|
|
6956
|
-
<option value="">
|
|
6957
|
-
|
|
6958
|
-
|
|
6959
|
-
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
|
|
6963
|
-
|
|
6964
|
-
|
|
6965
|
-
|
|
7102
|
+
<option value="">
|
|
7103
|
+
Select field to group by...
|
|
7104
|
+
</option>
|
|
7105
|
+
{getAvailableFilterColumns().map(
|
|
7106
|
+
(col, index) => {
|
|
7107
|
+
const fieldValue = `${col.table}.${col.column}`;
|
|
7108
|
+
const displayName = `${col.tableName}.${col.columnName}`;
|
|
7109
|
+
return (
|
|
7110
|
+
<option
|
|
7111
|
+
key={index}
|
|
7112
|
+
value={fieldValue}
|
|
7113
|
+
>
|
|
7114
|
+
{displayName}
|
|
7115
|
+
</option>
|
|
7116
|
+
);
|
|
7117
|
+
}
|
|
7118
|
+
)}
|
|
6966
7119
|
</select>
|
|
6967
7120
|
<p className={styles.fieldHelp}>
|
|
6968
|
-
Choose the field that contains the
|
|
7121
|
+
Choose the field that contains the
|
|
7122
|
+
categories you want to group by
|
|
6969
7123
|
</p>
|
|
6970
7124
|
</div>
|
|
6971
|
-
|
|
7125
|
+
|
|
6972
7126
|
{/* Group Display Name */}
|
|
6973
7127
|
<div className={styles.formGroup}>
|
|
6974
|
-
<label className={styles.formLabel}>
|
|
7128
|
+
<label className={styles.formLabel}>
|
|
7129
|
+
Group Display Name
|
|
7130
|
+
</label>
|
|
6975
7131
|
<input
|
|
6976
7132
|
type="text"
|
|
6977
7133
|
value={groupingConfig.groupDisplayName}
|
|
6978
|
-
onChange={(e) =>
|
|
6979
|
-
|
|
6980
|
-
|
|
6981
|
-
|
|
7134
|
+
onChange={(e) =>
|
|
7135
|
+
setGroupingConfig({
|
|
7136
|
+
...groupingConfig,
|
|
7137
|
+
groupDisplayName: e.target.value,
|
|
7138
|
+
})
|
|
7139
|
+
}
|
|
6982
7140
|
placeholder="e.g., Facility, Department, Category"
|
|
6983
7141
|
className={styles.formInput}
|
|
6984
7142
|
/>
|
|
6985
7143
|
<p className={styles.fieldHelp}>
|
|
6986
|
-
What to call each group in the report (e.g.,
|
|
7144
|
+
What to call each group in the report (e.g.,
|
|
7145
|
+
"Facility: Main Building")
|
|
6987
7146
|
</p>
|
|
6988
7147
|
</div>
|
|
6989
|
-
|
|
7148
|
+
|
|
6990
7149
|
{/* Advanced Group Options */}
|
|
6991
7150
|
<div className={styles.advancedGroupOptions}>
|
|
6992
|
-
<h4 className={styles.sectionTitle}>
|
|
6993
|
-
|
|
7151
|
+
<h4 className={styles.sectionTitle}>
|
|
7152
|
+
Advanced Options
|
|
7153
|
+
</h4>
|
|
7154
|
+
|
|
6994
7155
|
<div className={styles.optionsGrid}>
|
|
6995
7156
|
<div className={styles.formGroup}>
|
|
6996
|
-
<label className={styles.formLabel}>
|
|
7157
|
+
<label className={styles.formLabel}>
|
|
7158
|
+
Rows Per Group
|
|
7159
|
+
</label>
|
|
6997
7160
|
<input
|
|
6998
7161
|
type="number"
|
|
6999
7162
|
min="1"
|
|
7000
7163
|
max="100"
|
|
7001
7164
|
value={groupingConfig.rowsPerGroup}
|
|
7002
|
-
onChange={(e) =>
|
|
7003
|
-
|
|
7004
|
-
|
|
7005
|
-
|
|
7165
|
+
onChange={(e) =>
|
|
7166
|
+
setGroupingConfig({
|
|
7167
|
+
...groupingConfig,
|
|
7168
|
+
rowsPerGroup:
|
|
7169
|
+
parseInt(
|
|
7170
|
+
e.target.value
|
|
7171
|
+
) || 10,
|
|
7172
|
+
})
|
|
7173
|
+
}
|
|
7006
7174
|
className={styles.formInput}
|
|
7007
7175
|
/>
|
|
7008
7176
|
</div>
|
|
7009
|
-
|
|
7177
|
+
|
|
7010
7178
|
<div className={styles.formGroup}>
|
|
7011
|
-
<label className={styles.formLabel}>
|
|
7179
|
+
<label className={styles.formLabel}>
|
|
7180
|
+
Group Header Style
|
|
7181
|
+
</label>
|
|
7012
7182
|
<select
|
|
7013
|
-
value={
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
|
|
7183
|
+
value={
|
|
7184
|
+
groupingConfig.groupHeaderStyle
|
|
7185
|
+
}
|
|
7186
|
+
onChange={(e) =>
|
|
7187
|
+
setGroupingConfig({
|
|
7188
|
+
...groupingConfig,
|
|
7189
|
+
groupHeaderStyle:
|
|
7190
|
+
e.target.value,
|
|
7191
|
+
})
|
|
7192
|
+
}
|
|
7018
7193
|
className={styles.formSelect}
|
|
7019
7194
|
>
|
|
7020
|
-
<option value="primary">
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
<option value="
|
|
7195
|
+
<option value="primary">
|
|
7196
|
+
Primary (Blue)
|
|
7197
|
+
</option>
|
|
7198
|
+
<option value="secondary">
|
|
7199
|
+
Secondary (Gray)
|
|
7200
|
+
</option>
|
|
7201
|
+
<option value="success">
|
|
7202
|
+
Success (Green)
|
|
7203
|
+
</option>
|
|
7204
|
+
<option value="warning">
|
|
7205
|
+
Warning (Orange)
|
|
7206
|
+
</option>
|
|
7024
7207
|
</select>
|
|
7025
7208
|
</div>
|
|
7026
|
-
|
|
7209
|
+
|
|
7027
7210
|
<div className={styles.formGroup}>
|
|
7028
|
-
<label className={styles.formLabel}>
|
|
7211
|
+
<label className={styles.formLabel}>
|
|
7212
|
+
Sort Direction
|
|
7213
|
+
</label>
|
|
7029
7214
|
<select
|
|
7030
7215
|
value={groupingConfig.sortDirection}
|
|
7031
|
-
onChange={(e) =>
|
|
7032
|
-
|
|
7033
|
-
|
|
7034
|
-
|
|
7216
|
+
onChange={(e) =>
|
|
7217
|
+
setGroupingConfig({
|
|
7218
|
+
...groupingConfig,
|
|
7219
|
+
sortDirection:
|
|
7220
|
+
e.target.value,
|
|
7221
|
+
})
|
|
7222
|
+
}
|
|
7035
7223
|
className={styles.formSelect}
|
|
7036
7224
|
>
|
|
7037
|
-
<option value="asc">
|
|
7038
|
-
|
|
7225
|
+
<option value="asc">
|
|
7226
|
+
Ascending (A-Z)
|
|
7227
|
+
</option>
|
|
7228
|
+
<option value="desc">
|
|
7229
|
+
Descending (Z-A)
|
|
7230
|
+
</option>
|
|
7039
7231
|
</select>
|
|
7040
7232
|
</div>
|
|
7041
7233
|
</div>
|
|
7042
|
-
|
|
7234
|
+
|
|
7043
7235
|
<div className={styles.checkboxGrid}>
|
|
7044
7236
|
<label className={styles.checkboxLabel}>
|
|
7045
7237
|
<input
|
|
7046
7238
|
type="checkbox"
|
|
7047
|
-
checked={
|
|
7048
|
-
|
|
7049
|
-
|
|
7050
|
-
|
|
7051
|
-
|
|
7239
|
+
checked={
|
|
7240
|
+
groupingConfig.showEmptyRows
|
|
7241
|
+
}
|
|
7242
|
+
onChange={(e) =>
|
|
7243
|
+
setGroupingConfig({
|
|
7244
|
+
...groupingConfig,
|
|
7245
|
+
showEmptyRows:
|
|
7246
|
+
e.target.checked,
|
|
7247
|
+
})
|
|
7248
|
+
}
|
|
7052
7249
|
/>
|
|
7053
|
-
<span className={styles.checkboxText}>
|
|
7250
|
+
<span className={styles.checkboxText}>
|
|
7251
|
+
Show Empty Rows
|
|
7252
|
+
</span>
|
|
7054
7253
|
</label>
|
|
7055
|
-
|
|
7254
|
+
|
|
7056
7255
|
<label className={styles.checkboxLabel}>
|
|
7057
7256
|
<input
|
|
7058
7257
|
type="checkbox"
|
|
7059
|
-
checked={
|
|
7060
|
-
|
|
7061
|
-
|
|
7062
|
-
|
|
7063
|
-
|
|
7258
|
+
checked={
|
|
7259
|
+
groupingConfig.showGroupTotals
|
|
7260
|
+
}
|
|
7261
|
+
onChange={(e) =>
|
|
7262
|
+
setGroupingConfig({
|
|
7263
|
+
...groupingConfig,
|
|
7264
|
+
showGroupTotals:
|
|
7265
|
+
e.target.checked,
|
|
7266
|
+
})
|
|
7267
|
+
}
|
|
7064
7268
|
/>
|
|
7065
|
-
<span className={styles.checkboxText}>
|
|
7269
|
+
<span className={styles.checkboxText}>
|
|
7270
|
+
Show Group Totals
|
|
7271
|
+
</span>
|
|
7066
7272
|
</label>
|
|
7067
|
-
|
|
7273
|
+
|
|
7068
7274
|
<label className={styles.checkboxLabel}>
|
|
7069
7275
|
<input
|
|
7070
7276
|
type="checkbox"
|
|
7071
7277
|
checked={groupingConfig.expandable}
|
|
7072
|
-
onChange={(e) =>
|
|
7073
|
-
|
|
7074
|
-
|
|
7075
|
-
|
|
7278
|
+
onChange={(e) =>
|
|
7279
|
+
setGroupingConfig({
|
|
7280
|
+
...groupingConfig,
|
|
7281
|
+
expandable:
|
|
7282
|
+
e.target.checked,
|
|
7283
|
+
})
|
|
7284
|
+
}
|
|
7076
7285
|
/>
|
|
7077
|
-
<span className={styles.checkboxText}>
|
|
7286
|
+
<span className={styles.checkboxText}>
|
|
7287
|
+
Collapsible Groups
|
|
7288
|
+
</span>
|
|
7078
7289
|
</label>
|
|
7079
7290
|
</div>
|
|
7080
7291
|
</div>
|
|
7081
|
-
|
|
7292
|
+
|
|
7082
7293
|
{/* Grouping Preview */}
|
|
7083
7294
|
{groupingConfig.groupByField && (
|
|
7084
7295
|
<div className={styles.groupingPreview}>
|
|
7085
|
-
<h4 className={styles.sectionTitle}>
|
|
7296
|
+
<h4 className={styles.sectionTitle}>
|
|
7297
|
+
Configuration Preview
|
|
7298
|
+
</h4>
|
|
7086
7299
|
<div className={styles.previewBox}>
|
|
7087
|
-
<p><strong>Group By:</strong> {groupingConfig.groupByField}</p>
|
|
7088
|
-
<p><strong>Display Name:</strong> {groupingConfig.groupDisplayName || 'Group'}</p>
|
|
7089
|
-
<p><strong>Sort:</strong> {groupingConfig.sortDirection === 'asc' ? 'Ascending' : 'Descending'}</p>
|
|
7090
7300
|
<p>
|
|
7091
|
-
<strong>
|
|
7092
|
-
{groupingConfig.
|
|
7093
|
-
|
|
7094
|
-
|
|
7301
|
+
<strong>Group By:</strong>{' '}
|
|
7302
|
+
{groupingConfig.groupByField}
|
|
7303
|
+
</p>
|
|
7304
|
+
<p>
|
|
7305
|
+
<strong>Display Name:</strong>{' '}
|
|
7306
|
+
{groupingConfig.groupDisplayName ||
|
|
7307
|
+
'Group'}
|
|
7308
|
+
</p>
|
|
7309
|
+
<p>
|
|
7310
|
+
<strong>Sort:</strong>{' '}
|
|
7311
|
+
{groupingConfig.sortDirection ===
|
|
7312
|
+
'asc'
|
|
7313
|
+
? 'Ascending'
|
|
7314
|
+
: 'Descending'}
|
|
7315
|
+
</p>
|
|
7316
|
+
<p>
|
|
7317
|
+
<strong>Options:</strong>
|
|
7318
|
+
{groupingConfig.showEmptyRows &&
|
|
7319
|
+
' Empty Rows'}
|
|
7320
|
+
{groupingConfig.showGroupTotals &&
|
|
7321
|
+
' Group Totals'}
|
|
7322
|
+
{groupingConfig.expandable &&
|
|
7323
|
+
' Collapsible'}
|
|
7095
7324
|
</p>
|
|
7096
7325
|
</div>
|
|
7097
7326
|
</div>
|
|
@@ -7102,43 +7331,56 @@ const GenericReportImproved = ({
|
|
|
7102
7331
|
{/* Custom URLs Configuration - Only show when grouping is enabled */}
|
|
7103
7332
|
{groupingConfig.enabled && (
|
|
7104
7333
|
<div className={styles.customUrlsSection}>
|
|
7105
|
-
<h4 className={styles.sectionTitle}>
|
|
7334
|
+
<h4 className={styles.sectionTitle}>
|
|
7335
|
+
Custom API Endpoints (Optional)
|
|
7336
|
+
</h4>
|
|
7106
7337
|
<p className={styles.helpText}>
|
|
7107
|
-
Override default API endpoints for specialized
|
|
7338
|
+
Override default API endpoints for specialized
|
|
7339
|
+
report handling
|
|
7108
7340
|
</p>
|
|
7109
|
-
|
|
7341
|
+
|
|
7110
7342
|
<div className={styles.optionsGrid}>
|
|
7111
7343
|
<div className={styles.formGroup}>
|
|
7112
|
-
<label className={styles.formLabel}>
|
|
7344
|
+
<label className={styles.formLabel}>
|
|
7345
|
+
Custom Execute URL
|
|
7346
|
+
</label>
|
|
7113
7347
|
<input
|
|
7114
7348
|
type="text"
|
|
7115
7349
|
value={customUrls.executeUrl}
|
|
7116
|
-
onChange={(e) =>
|
|
7117
|
-
|
|
7118
|
-
|
|
7119
|
-
|
|
7350
|
+
onChange={(e) =>
|
|
7351
|
+
setCustomUrls({
|
|
7352
|
+
...customUrls,
|
|
7353
|
+
executeUrl: e.target.value,
|
|
7354
|
+
})
|
|
7355
|
+
}
|
|
7120
7356
|
placeholder="/api/custom-reports/execute"
|
|
7121
7357
|
className={styles.formInput}
|
|
7122
7358
|
/>
|
|
7123
7359
|
<p className={styles.fieldHelp}>
|
|
7124
|
-
Custom endpoint for report execution
|
|
7360
|
+
Custom endpoint for report execution
|
|
7361
|
+
(overrides template and default URLs)
|
|
7125
7362
|
</p>
|
|
7126
7363
|
</div>
|
|
7127
|
-
|
|
7364
|
+
|
|
7128
7365
|
<div className={styles.formGroup}>
|
|
7129
|
-
<label className={styles.formLabel}>
|
|
7366
|
+
<label className={styles.formLabel}>
|
|
7367
|
+
Custom Export URL
|
|
7368
|
+
</label>
|
|
7130
7369
|
<input
|
|
7131
7370
|
type="text"
|
|
7132
7371
|
value={customUrls.exportUrl}
|
|
7133
|
-
onChange={(e) =>
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7372
|
+
onChange={(e) =>
|
|
7373
|
+
setCustomUrls({
|
|
7374
|
+
...customUrls,
|
|
7375
|
+
exportUrl: e.target.value,
|
|
7376
|
+
})
|
|
7377
|
+
}
|
|
7137
7378
|
placeholder="/api/custom-reports/export"
|
|
7138
7379
|
className={styles.formInput}
|
|
7139
7380
|
/>
|
|
7140
7381
|
<p className={styles.fieldHelp}>
|
|
7141
|
-
Custom endpoint for report exports (for
|
|
7382
|
+
Custom endpoint for report exports (for
|
|
7383
|
+
server-side exports)
|
|
7142
7384
|
</p>
|
|
7143
7385
|
</div>
|
|
7144
7386
|
</div>
|
|
@@ -7165,17 +7407,21 @@ const GenericReportImproved = ({
|
|
|
7165
7407
|
>
|
|
7166
7408
|
{isLoading ? 'Loading...' : 'Generate Report'}
|
|
7167
7409
|
</button>
|
|
7168
|
-
|
|
7410
|
+
|
|
7169
7411
|
{/* View Toggle Button - only show if both grouped and regular data are available */}
|
|
7170
|
-
{(groupingConfig.enabled ||
|
|
7412
|
+
{(groupingConfig.enabled ||
|
|
7413
|
+
selectedTemplate?.grouping?.enabled) && (
|
|
7171
7414
|
<div className={styles.viewToggle}>
|
|
7172
7415
|
<span className={styles.viewLabel}>View:</span>
|
|
7173
7416
|
<button
|
|
7174
|
-
className={`${styles.btn} ${
|
|
7175
|
-
|
|
7176
|
-
}`}
|
|
7417
|
+
className={`${styles.btn} ${
|
|
7418
|
+
styles.btnSecondary
|
|
7419
|
+
} ${groupedData ? styles.active : ''}`}
|
|
7177
7420
|
onClick={() => {
|
|
7178
|
-
if (
|
|
7421
|
+
if (
|
|
7422
|
+
!groupedData &&
|
|
7423
|
+
previewData.length > 0
|
|
7424
|
+
) {
|
|
7179
7425
|
// Switch to grouped view
|
|
7180
7426
|
executeReport();
|
|
7181
7427
|
}
|
|
@@ -7186,9 +7432,9 @@ const GenericReportImproved = ({
|
|
|
7186
7432
|
📊 Grouped
|
|
7187
7433
|
</button>
|
|
7188
7434
|
<button
|
|
7189
|
-
className={`${styles.btn} ${
|
|
7190
|
-
|
|
7191
|
-
}`}
|
|
7435
|
+
className={`${styles.btn} ${
|
|
7436
|
+
styles.btnSecondary
|
|
7437
|
+
} ${!groupedData ? styles.active : ''}`}
|
|
7192
7438
|
onClick={() => {
|
|
7193
7439
|
if (groupedData) {
|
|
7194
7440
|
// Switch to regular table view
|
|
@@ -7209,20 +7455,29 @@ const GenericReportImproved = ({
|
|
|
7209
7455
|
|
|
7210
7456
|
{/* Render grouped report if available */}
|
|
7211
7457
|
{(() => {
|
|
7212
|
-
console.log(
|
|
7213
|
-
|
|
7214
|
-
|
|
7458
|
+
console.log(
|
|
7459
|
+
'🎯 [DEBUG] Render check - groupedData:',
|
|
7460
|
+
groupedData
|
|
7461
|
+
);
|
|
7462
|
+
console.log(
|
|
7463
|
+
'🎯 [DEBUG] Render check - groupedData?.grouped:',
|
|
7464
|
+
groupedData?.grouped
|
|
7465
|
+
);
|
|
7466
|
+
console.log(
|
|
7467
|
+
'🎯 [DEBUG] Render check - selectedColumns:',
|
|
7468
|
+
selectedColumns
|
|
7469
|
+
);
|
|
7215
7470
|
return groupedData && groupedData.grouped;
|
|
7216
7471
|
})() && (
|
|
7217
7472
|
<div className={styles.groupedReportContainer}>
|
|
7218
|
-
<GroupedReportRenderer
|
|
7473
|
+
<GroupedReportRenderer
|
|
7219
7474
|
data={{
|
|
7220
7475
|
...groupedData.groupedData,
|
|
7221
|
-
grouped: true
|
|
7476
|
+
grouped: true,
|
|
7222
7477
|
}}
|
|
7223
7478
|
config={
|
|
7224
|
-
groupingConfig.enabled
|
|
7225
|
-
? groupingConfig
|
|
7479
|
+
groupingConfig.enabled
|
|
7480
|
+
? groupingConfig
|
|
7226
7481
|
: selectedTemplate?.grouping || {}
|
|
7227
7482
|
}
|
|
7228
7483
|
columns={selectedColumns}
|
|
@@ -7241,14 +7496,23 @@ const GenericReportImproved = ({
|
|
|
7241
7496
|
<div className="grouped-report-section">
|
|
7242
7497
|
<div className="grouped-report-header">
|
|
7243
7498
|
<div className="grouped-report-summary">
|
|
7244
|
-
<h3 className="group-title">
|
|
7245
|
-
|
|
7499
|
+
<h3 className="group-title">
|
|
7500
|
+
Report Results
|
|
7501
|
+
</h3>
|
|
7502
|
+
<span className="group-stats">
|
|
7503
|
+
({previewData.length} records)
|
|
7504
|
+
</span>
|
|
7246
7505
|
</div>
|
|
7247
7506
|
<div className="grouped-report-actions">
|
|
7248
|
-
<button
|
|
7507
|
+
<button
|
|
7249
7508
|
className="btn btn-export"
|
|
7250
|
-
onClick={() =>
|
|
7251
|
-
|
|
7509
|
+
onClick={() =>
|
|
7510
|
+
handleRegularExport()
|
|
7511
|
+
}
|
|
7512
|
+
disabled={
|
|
7513
|
+
isLoading ||
|
|
7514
|
+
previewData.length === 0
|
|
7515
|
+
}
|
|
7252
7516
|
title="Export report data as Excel file"
|
|
7253
7517
|
>
|
|
7254
7518
|
Export
|
|
@@ -7260,285 +7524,858 @@ const GenericReportImproved = ({
|
|
|
7260
7524
|
<thead>
|
|
7261
7525
|
<tr>
|
|
7262
7526
|
{(() => {
|
|
7263
|
-
const headerColumns =
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7527
|
+
const headerColumns =
|
|
7528
|
+
previewData.length >
|
|
7529
|
+
0
|
|
7530
|
+
? Object.keys(
|
|
7531
|
+
previewData[0]
|
|
7532
|
+
).map(
|
|
7533
|
+
(
|
|
7534
|
+
key
|
|
7535
|
+
) => ({
|
|
7536
|
+
name: key,
|
|
7537
|
+
header: key.replace(
|
|
7538
|
+
/\./g,
|
|
7539
|
+
' '
|
|
7540
|
+
),
|
|
7541
|
+
})
|
|
7542
|
+
)
|
|
7543
|
+
: gridColumns;
|
|
7544
|
+
return headerColumns.map(
|
|
7545
|
+
(col, index) => (
|
|
7546
|
+
<th
|
|
7547
|
+
key={index}
|
|
7548
|
+
className="table-header"
|
|
7549
|
+
>
|
|
7550
|
+
{col.header ||
|
|
7551
|
+
col.name}
|
|
7552
|
+
</th>
|
|
7553
|
+
)
|
|
7554
|
+
);
|
|
7269
7555
|
})()}
|
|
7270
7556
|
</tr>
|
|
7271
7557
|
</thead>
|
|
7272
7558
|
<tbody>
|
|
7273
7559
|
{(() => {
|
|
7274
|
-
console.log(
|
|
7560
|
+
console.log(
|
|
7561
|
+
`🎯 [DEBUG] Rendering ${previewData.length} table rows`
|
|
7562
|
+
);
|
|
7275
7563
|
// Always use response keys as fallback instead of gridColumns that might have wrong names
|
|
7276
|
-
const actualColumns =
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
|
|
7280
|
-
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
|
|
7286
|
-
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
|
|
7296
|
-
|
|
7297
|
-
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
|
|
7302
|
-
|
|
7303
|
-
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
}
|
|
7307
|
-
|
|
7308
|
-
// Handle ABN (Australian Business Number) columns
|
|
7309
|
-
if (col.name.toLowerCase().includes('abn') && value) {
|
|
7310
|
-
const cleanABN = String(value).replace(/\s/g, ''); // Remove spaces, convert to string
|
|
7311
|
-
if (cleanABN.length === 11 && /^\d{11}$/.test(cleanABN)) {
|
|
7312
|
-
// Format as XX XXX XXX XXX
|
|
7313
|
-
const formatted = `${cleanABN.slice(0,2)} ${cleanABN.slice(2,5)} ${cleanABN.slice(5,8)} ${cleanABN.slice(8,11)}`;
|
|
7314
|
-
return <span style={{fontFamily: 'monospace', fontWeight: '500', color: '#495057'}}>{formatted}</span>;
|
|
7315
|
-
}
|
|
7316
|
-
}
|
|
7317
|
-
|
|
7318
|
-
// Handle ACN (Australian Company Number) columns
|
|
7319
|
-
if (col.name.toLowerCase().includes('acn') && value) {
|
|
7320
|
-
const cleanACN = String(value).replace(/\s/g, ''); // Remove spaces, convert to string
|
|
7321
|
-
if (cleanACN.length === 9 && /^\d{9}$/.test(cleanACN)) {
|
|
7322
|
-
// Format as XXX XXX XXX
|
|
7323
|
-
const formatted = `${cleanACN.slice(0,3)} ${cleanACN.slice(3,6)} ${cleanACN.slice(6,9)}`;
|
|
7324
|
-
return <span style={{fontFamily: 'monospace', fontWeight: '500', color: '#495057'}}>{formatted}</span>;
|
|
7325
|
-
}
|
|
7326
|
-
}
|
|
7327
|
-
|
|
7328
|
-
// Handle status/boolean-like columns
|
|
7329
|
-
if (col.name.toLowerCase().includes('status')) {
|
|
7330
|
-
if (value === 1 || value === '1' || value === true) {
|
|
7331
|
-
return <span style={{color: '#28a745', fontWeight: '500'}}>Active</span>;
|
|
7332
|
-
} else if (value === 0 || value === '0' || value === false) {
|
|
7333
|
-
return <span style={{color: '#6c757d', fontWeight: '500'}}>Inactive</span>;
|
|
7334
|
-
}
|
|
7335
|
-
}
|
|
7336
|
-
|
|
7337
|
-
// Handle other boolean values
|
|
7338
|
-
if (typeof value === 'boolean') {
|
|
7339
|
-
return value ?
|
|
7340
|
-
<span style={{color: '#28a745', fontWeight: '500'}}>Yes</span> :
|
|
7341
|
-
<span style={{color: '#6c757d', fontWeight: '500'}}>No</span>;
|
|
7342
|
-
}
|
|
7343
|
-
|
|
7344
|
-
// Handle numeric values (but exclude potential boolean columns)
|
|
7345
|
-
if (typeof value === 'number' && !col.name.toLowerCase().includes('status')) {
|
|
7346
|
-
const columnName = col.name.toLowerCase();
|
|
7347
|
-
// Don't format as numbers if they might be boolean values
|
|
7348
|
-
const mightBeBoolean = (value === 0 || value === 1) && (
|
|
7349
|
-
columnName.includes('is_') || columnName.includes('has_') || columnName.includes('confirmed') ||
|
|
7350
|
-
columnName.includes('active') || columnName.includes('enabled') || columnName.includes('completed') ||
|
|
7351
|
-
columnName.includes('stage') || columnName.includes('final') || columnName.includes('flag') ||
|
|
7352
|
-
columnName.includes('approved') || columnName.includes('verified') || columnName.includes('sent') ||
|
|
7353
|
-
columnName.endsWith('?')
|
|
7354
|
-
);
|
|
7355
|
-
|
|
7356
|
-
if (!mightBeBoolean) {
|
|
7357
|
-
return value.toLocaleString();
|
|
7564
|
+
const actualColumns =
|
|
7565
|
+
previewData.length > 0
|
|
7566
|
+
? Object.keys(
|
|
7567
|
+
previewData[0]
|
|
7568
|
+
).map((key) => ({
|
|
7569
|
+
name: key,
|
|
7570
|
+
header: key.replace(
|
|
7571
|
+
/\./g,
|
|
7572
|
+
' '
|
|
7573
|
+
),
|
|
7574
|
+
}))
|
|
7575
|
+
: gridColumns;
|
|
7576
|
+
console.log(
|
|
7577
|
+
`🎯 [DEBUG] Using columns:`,
|
|
7578
|
+
actualColumns
|
|
7579
|
+
);
|
|
7580
|
+
return previewData.map(
|
|
7581
|
+
(row, rowIndex) => (
|
|
7582
|
+
<tr
|
|
7583
|
+
key={rowIndex}
|
|
7584
|
+
className="data-row"
|
|
7585
|
+
>
|
|
7586
|
+
{actualColumns.map(
|
|
7587
|
+
(
|
|
7588
|
+
col,
|
|
7589
|
+
colIndex
|
|
7590
|
+
) => (
|
|
7591
|
+
<td
|
|
7592
|
+
key={
|
|
7593
|
+
colIndex
|
|
7358
7594
|
}
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7595
|
+
className="table-cell"
|
|
7596
|
+
>
|
|
7597
|
+
{(() => {
|
|
7598
|
+
const value =
|
|
7599
|
+
row[
|
|
7600
|
+
col
|
|
7601
|
+
.name
|
|
7602
|
+
];
|
|
7603
|
+
|
|
7604
|
+
// Handle null/undefined
|
|
7605
|
+
if (
|
|
7606
|
+
value ===
|
|
7607
|
+
null ||
|
|
7608
|
+
value ===
|
|
7609
|
+
undefined
|
|
7610
|
+
) {
|
|
7611
|
+
return (
|
|
7612
|
+
<span
|
|
7613
|
+
style={{
|
|
7614
|
+
color: '#6c757d',
|
|
7615
|
+
fontStyle:
|
|
7616
|
+
'italic',
|
|
7617
|
+
}}
|
|
7618
|
+
>
|
|
7619
|
+
—
|
|
7620
|
+
</span>
|
|
7371
7621
|
);
|
|
7372
|
-
|
|
7373
|
-
|
|
7622
|
+
}
|
|
7623
|
+
|
|
7624
|
+
// Handle JSON strings
|
|
7625
|
+
if (
|
|
7626
|
+
typeof value ===
|
|
7627
|
+
'string' &&
|
|
7628
|
+
value.startsWith(
|
|
7629
|
+
'{'
|
|
7630
|
+
) &&
|
|
7631
|
+
value.endsWith(
|
|
7632
|
+
'}'
|
|
7633
|
+
)
|
|
7634
|
+
) {
|
|
7635
|
+
try {
|
|
7636
|
+
const parsed =
|
|
7637
|
+
JSON.parse(
|
|
7638
|
+
value
|
|
7639
|
+
);
|
|
7640
|
+
return (
|
|
7641
|
+
<div
|
|
7642
|
+
style={{
|
|
7643
|
+
fontSize:
|
|
7644
|
+
'0.85em',
|
|
7645
|
+
lineHeight:
|
|
7646
|
+
'1.3',
|
|
7647
|
+
}}
|
|
7648
|
+
>
|
|
7649
|
+
{Object.entries(
|
|
7650
|
+
parsed
|
|
7651
|
+
).map(
|
|
7652
|
+
([
|
|
7653
|
+
key,
|
|
7654
|
+
val,
|
|
7655
|
+
]) => (
|
|
7656
|
+
<div
|
|
7657
|
+
key={
|
|
7658
|
+
key
|
|
7659
|
+
}
|
|
7660
|
+
style={{
|
|
7661
|
+
marginBottom:
|
|
7662
|
+
'2px',
|
|
7663
|
+
}}
|
|
7664
|
+
>
|
|
7665
|
+
<strong>
|
|
7666
|
+
{key.replace(
|
|
7667
|
+
/_/g,
|
|
7668
|
+
' '
|
|
7669
|
+
)}
|
|
7670
|
+
:
|
|
7671
|
+
</strong>{' '}
|
|
7672
|
+
{val ||
|
|
7673
|
+
'—'}
|
|
7674
|
+
</div>
|
|
7675
|
+
)
|
|
7676
|
+
)}
|
|
7677
|
+
</div>
|
|
7678
|
+
);
|
|
7679
|
+
} catch (e) {
|
|
7680
|
+
return value;
|
|
7681
|
+
}
|
|
7682
|
+
}
|
|
7683
|
+
|
|
7684
|
+
// Handle ABN (Australian Business Number) columns
|
|
7685
|
+
if (
|
|
7686
|
+
col.name
|
|
7687
|
+
.toLowerCase()
|
|
7688
|
+
.includes(
|
|
7689
|
+
'abn'
|
|
7690
|
+
) &&
|
|
7691
|
+
value
|
|
7692
|
+
) {
|
|
7693
|
+
const cleanABN =
|
|
7694
|
+
String(
|
|
7695
|
+
value
|
|
7696
|
+
).replace(
|
|
7697
|
+
/\s/g,
|
|
7698
|
+
''
|
|
7699
|
+
); // Remove spaces, convert to string
|
|
7700
|
+
if (
|
|
7701
|
+
cleanABN.length ===
|
|
7702
|
+
11 &&
|
|
7703
|
+
/^\d{11}$/.test(
|
|
7704
|
+
cleanABN
|
|
7705
|
+
)
|
|
7706
|
+
) {
|
|
7707
|
+
// Format as XX XXX XXX XXX
|
|
7708
|
+
const formatted = `${cleanABN.slice(
|
|
7709
|
+
0,
|
|
7710
|
+
2
|
|
7711
|
+
)} ${cleanABN.slice(
|
|
7712
|
+
2,
|
|
7713
|
+
5
|
|
7714
|
+
)} ${cleanABN.slice(
|
|
7715
|
+
5,
|
|
7716
|
+
8
|
|
7717
|
+
)} ${cleanABN.slice(
|
|
7718
|
+
8,
|
|
7719
|
+
11
|
|
7720
|
+
)}`;
|
|
7721
|
+
return (
|
|
7722
|
+
<span
|
|
7723
|
+
style={{
|
|
7724
|
+
fontFamily:
|
|
7725
|
+
'monospace',
|
|
7726
|
+
fontWeight:
|
|
7727
|
+
'500',
|
|
7728
|
+
color: '#495057',
|
|
7729
|
+
}}
|
|
7730
|
+
>
|
|
7731
|
+
{
|
|
7732
|
+
formatted
|
|
7733
|
+
}
|
|
7734
|
+
</span>
|
|
7735
|
+
);
|
|
7736
|
+
}
|
|
7737
|
+
}
|
|
7738
|
+
|
|
7739
|
+
// Handle ACN (Australian Company Number) columns
|
|
7740
|
+
if (
|
|
7741
|
+
col.name
|
|
7742
|
+
.toLowerCase()
|
|
7743
|
+
.includes(
|
|
7744
|
+
'acn'
|
|
7745
|
+
) &&
|
|
7746
|
+
value
|
|
7747
|
+
) {
|
|
7748
|
+
const cleanACN =
|
|
7749
|
+
String(
|
|
7750
|
+
value
|
|
7751
|
+
).replace(
|
|
7752
|
+
/\s/g,
|
|
7753
|
+
''
|
|
7754
|
+
); // Remove spaces, convert to string
|
|
7755
|
+
if (
|
|
7756
|
+
cleanACN.length ===
|
|
7757
|
+
9 &&
|
|
7758
|
+
/^\d{9}$/.test(
|
|
7759
|
+
cleanACN
|
|
7760
|
+
)
|
|
7761
|
+
) {
|
|
7762
|
+
// Format as XXX XXX XXX
|
|
7763
|
+
const formatted = `${cleanACN.slice(
|
|
7764
|
+
0,
|
|
7765
|
+
3
|
|
7766
|
+
)} ${cleanACN.slice(
|
|
7767
|
+
3,
|
|
7768
|
+
6
|
|
7769
|
+
)} ${cleanACN.slice(
|
|
7770
|
+
6,
|
|
7771
|
+
9
|
|
7772
|
+
)}`;
|
|
7773
|
+
return (
|
|
7774
|
+
<span
|
|
7775
|
+
style={{
|
|
7776
|
+
fontFamily:
|
|
7777
|
+
'monospace',
|
|
7778
|
+
fontWeight:
|
|
7779
|
+
'500',
|
|
7780
|
+
color: '#495057',
|
|
7781
|
+
}}
|
|
7782
|
+
>
|
|
7783
|
+
{
|
|
7784
|
+
formatted
|
|
7785
|
+
}
|
|
7786
|
+
</span>
|
|
7787
|
+
);
|
|
7788
|
+
}
|
|
7789
|
+
}
|
|
7790
|
+
|
|
7791
|
+
// Handle status/boolean-like columns
|
|
7792
|
+
if (
|
|
7793
|
+
col.name
|
|
7794
|
+
.toLowerCase()
|
|
7795
|
+
.includes(
|
|
7796
|
+
'status'
|
|
7797
|
+
)
|
|
7798
|
+
) {
|
|
7799
|
+
if (
|
|
7800
|
+
value ===
|
|
7801
|
+
1 ||
|
|
7802
|
+
value ===
|
|
7803
|
+
'1' ||
|
|
7804
|
+
value ===
|
|
7805
|
+
true
|
|
7806
|
+
) {
|
|
7807
|
+
return (
|
|
7808
|
+
<span
|
|
7809
|
+
style={{
|
|
7810
|
+
color: '#28a745',
|
|
7811
|
+
fontWeight:
|
|
7812
|
+
'500',
|
|
7813
|
+
}}
|
|
7814
|
+
>
|
|
7815
|
+
Active
|
|
7816
|
+
</span>
|
|
7817
|
+
);
|
|
7818
|
+
} else if (
|
|
7819
|
+
value ===
|
|
7820
|
+
0 ||
|
|
7821
|
+
value ===
|
|
7822
|
+
'0' ||
|
|
7823
|
+
value ===
|
|
7824
|
+
false
|
|
7825
|
+
) {
|
|
7826
|
+
return (
|
|
7827
|
+
<span
|
|
7828
|
+
style={{
|
|
7829
|
+
color: '#6c757d',
|
|
7830
|
+
fontWeight:
|
|
7831
|
+
'500',
|
|
7832
|
+
}}
|
|
7833
|
+
>
|
|
7834
|
+
Inactive
|
|
7835
|
+
</span>
|
|
7836
|
+
);
|
|
7374
7837
|
}
|
|
7375
7838
|
}
|
|
7376
|
-
|
|
7377
|
-
//
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
|
|
7381
|
-
|
|
7839
|
+
|
|
7840
|
+
// Handle other boolean values
|
|
7841
|
+
if (
|
|
7842
|
+
typeof value ===
|
|
7843
|
+
'boolean'
|
|
7844
|
+
) {
|
|
7845
|
+
return value ? (
|
|
7846
|
+
<span
|
|
7847
|
+
style={{
|
|
7848
|
+
color: '#28a745',
|
|
7849
|
+
fontWeight:
|
|
7850
|
+
'500',
|
|
7851
|
+
}}
|
|
7852
|
+
>
|
|
7853
|
+
Yes
|
|
7854
|
+
</span>
|
|
7855
|
+
) : (
|
|
7856
|
+
<span
|
|
7857
|
+
style={{
|
|
7858
|
+
color: '#6c757d',
|
|
7859
|
+
fontWeight:
|
|
7860
|
+
'500',
|
|
7861
|
+
}}
|
|
7862
|
+
>
|
|
7863
|
+
No
|
|
7864
|
+
</span>
|
|
7865
|
+
);
|
|
7382
7866
|
}
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
|
|
7391
|
-
|
|
7867
|
+
|
|
7868
|
+
// Handle numeric values (but exclude potential boolean columns)
|
|
7869
|
+
if (
|
|
7870
|
+
typeof value ===
|
|
7871
|
+
'number' &&
|
|
7872
|
+
!col.name
|
|
7873
|
+
.toLowerCase()
|
|
7874
|
+
.includes(
|
|
7875
|
+
'status'
|
|
7876
|
+
)
|
|
7877
|
+
) {
|
|
7878
|
+
const columnName =
|
|
7879
|
+
col.name.toLowerCase();
|
|
7880
|
+
// Don't format as numbers if they might be boolean values
|
|
7881
|
+
const mightBeBoolean =
|
|
7882
|
+
(value ===
|
|
7883
|
+
0 ||
|
|
7884
|
+
value ===
|
|
7885
|
+
1) &&
|
|
7886
|
+
(columnName.includes(
|
|
7887
|
+
'is_'
|
|
7888
|
+
) ||
|
|
7889
|
+
columnName.includes(
|
|
7890
|
+
'has_'
|
|
7891
|
+
) ||
|
|
7892
|
+
columnName.includes(
|
|
7893
|
+
'confirmed'
|
|
7894
|
+
) ||
|
|
7895
|
+
columnName.includes(
|
|
7896
|
+
'active'
|
|
7897
|
+
) ||
|
|
7898
|
+
columnName.includes(
|
|
7899
|
+
'enabled'
|
|
7900
|
+
) ||
|
|
7901
|
+
columnName.includes(
|
|
7902
|
+
'completed'
|
|
7903
|
+
) ||
|
|
7904
|
+
columnName.includes(
|
|
7905
|
+
'stage'
|
|
7906
|
+
) ||
|
|
7907
|
+
columnName.includes(
|
|
7908
|
+
'final'
|
|
7909
|
+
) ||
|
|
7910
|
+
columnName.includes(
|
|
7911
|
+
'flag'
|
|
7912
|
+
) ||
|
|
7913
|
+
columnName.includes(
|
|
7914
|
+
'approved'
|
|
7915
|
+
) ||
|
|
7916
|
+
columnName.includes(
|
|
7917
|
+
'verified'
|
|
7918
|
+
) ||
|
|
7919
|
+
columnName.includes(
|
|
7920
|
+
'sent'
|
|
7921
|
+
) ||
|
|
7922
|
+
columnName.endsWith(
|
|
7923
|
+
'?'
|
|
7924
|
+
));
|
|
7925
|
+
|
|
7926
|
+
if (
|
|
7927
|
+
!mightBeBoolean
|
|
7928
|
+
) {
|
|
7929
|
+
return value.toLocaleString();
|
|
7930
|
+
}
|
|
7392
7931
|
}
|
|
7393
|
-
|
|
7394
|
-
|
|
7395
|
-
|
|
7396
|
-
|
|
7397
|
-
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
|
|
7402
|
-
|
|
7932
|
+
|
|
7933
|
+
// Use formatCellContent for all other values including dates and smart boolean formatting
|
|
7934
|
+
const formattedValue =
|
|
7935
|
+
formatCellContent(
|
|
7936
|
+
value,
|
|
7937
|
+
{
|
|
7938
|
+
id: col.name,
|
|
7939
|
+
name: col.name,
|
|
7940
|
+
// Get type from template column definition if available, otherwise infer
|
|
7941
|
+
type: (() => {
|
|
7942
|
+
// First try to find type from template's defaultColumns
|
|
7943
|
+
if (
|
|
7944
|
+
selectedTemplate?.defaultColumns
|
|
7945
|
+
) {
|
|
7946
|
+
const templateColumn =
|
|
7947
|
+
selectedTemplate.defaultColumns.find(
|
|
7948
|
+
(
|
|
7949
|
+
tc
|
|
7950
|
+
) =>
|
|
7951
|
+
`${tc.table}.${tc.column}` ===
|
|
7952
|
+
col.name ||
|
|
7953
|
+
tc.column ===
|
|
7954
|
+
col.name
|
|
7955
|
+
);
|
|
7956
|
+
if (
|
|
7957
|
+
templateColumn?.type
|
|
7958
|
+
) {
|
|
7959
|
+
return templateColumn.type;
|
|
7960
|
+
}
|
|
7961
|
+
}
|
|
7962
|
+
|
|
7963
|
+
// Fallback to column name-based type inference
|
|
7964
|
+
const columnName =
|
|
7965
|
+
col.name.toLowerCase();
|
|
7966
|
+
// Date column detection
|
|
7967
|
+
if (
|
|
7968
|
+
columnName.includes(
|
|
7969
|
+
'date'
|
|
7970
|
+
) ||
|
|
7971
|
+
columnName.includes(
|
|
7972
|
+
'time'
|
|
7973
|
+
) ||
|
|
7974
|
+
columnName.includes(
|
|
7975
|
+
'created_at'
|
|
7976
|
+
) ||
|
|
7977
|
+
columnName.includes(
|
|
7978
|
+
'updated_at'
|
|
7979
|
+
)
|
|
7980
|
+
) {
|
|
7981
|
+
return 'date';
|
|
7982
|
+
}
|
|
7983
|
+
// Boolean column detection - enhanced logic
|
|
7984
|
+
if (
|
|
7985
|
+
columnName.includes(
|
|
7986
|
+
'is_'
|
|
7987
|
+
) ||
|
|
7988
|
+
columnName.includes(
|
|
7989
|
+
'has_'
|
|
7990
|
+
) ||
|
|
7991
|
+
columnName.includes(
|
|
7992
|
+
'confirmed'
|
|
7993
|
+
) ||
|
|
7994
|
+
columnName.includes(
|
|
7995
|
+
'active'
|
|
7996
|
+
) ||
|
|
7997
|
+
columnName.includes(
|
|
7998
|
+
'enabled'
|
|
7999
|
+
) ||
|
|
8000
|
+
columnName.includes(
|
|
8001
|
+
'completed'
|
|
8002
|
+
) ||
|
|
8003
|
+
columnName.includes(
|
|
8004
|
+
'stage'
|
|
8005
|
+
) ||
|
|
8006
|
+
columnName.includes(
|
|
8007
|
+
'final'
|
|
8008
|
+
) ||
|
|
8009
|
+
columnName.includes(
|
|
8010
|
+
'flag'
|
|
8011
|
+
) ||
|
|
8012
|
+
columnName.includes(
|
|
8013
|
+
'approved'
|
|
8014
|
+
) ||
|
|
8015
|
+
columnName.includes(
|
|
8016
|
+
'verified'
|
|
8017
|
+
) ||
|
|
8018
|
+
columnName.includes(
|
|
8019
|
+
'sent'
|
|
8020
|
+
) ||
|
|
8021
|
+
columnName.endsWith(
|
|
8022
|
+
'?'
|
|
8023
|
+
) || // Handle columns like "Confirmed?"
|
|
8024
|
+
(typeof value ===
|
|
8025
|
+
'number' &&
|
|
8026
|
+
(value ===
|
|
8027
|
+
0 ||
|
|
8028
|
+
value ===
|
|
8029
|
+
1)) ||
|
|
8030
|
+
(typeof value ===
|
|
8031
|
+
'string' &&
|
|
8032
|
+
(value ===
|
|
8033
|
+
'0' ||
|
|
8034
|
+
value ===
|
|
8035
|
+
'1'))
|
|
8036
|
+
) {
|
|
8037
|
+
return 'boolean';
|
|
8038
|
+
}
|
|
8039
|
+
return undefined; // Let formatCellContent auto-detect
|
|
8040
|
+
})(),
|
|
8041
|
+
}
|
|
8042
|
+
);
|
|
8043
|
+
|
|
8044
|
+
return formattedValue;
|
|
8045
|
+
})()}
|
|
8046
|
+
</td>
|
|
8047
|
+
)
|
|
8048
|
+
)}
|
|
8049
|
+
</tr>
|
|
8050
|
+
)
|
|
8051
|
+
);
|
|
7403
8052
|
})()}
|
|
7404
8053
|
</tbody>
|
|
7405
8054
|
</table>
|
|
7406
8055
|
</div>
|
|
7407
|
-
|
|
8056
|
+
|
|
7408
8057
|
{/* Pagination Controls */}
|
|
7409
8058
|
{previewData.length > 0 && (
|
|
7410
|
-
<div
|
|
7411
|
-
|
|
8059
|
+
<div
|
|
8060
|
+
className={
|
|
8061
|
+
styles.paginationContainer
|
|
8062
|
+
}
|
|
8063
|
+
>
|
|
8064
|
+
<div
|
|
8065
|
+
className={
|
|
8066
|
+
styles.paginationInfo
|
|
8067
|
+
}
|
|
8068
|
+
>
|
|
7412
8069
|
{(() => {
|
|
7413
|
-
const startRecord =
|
|
7414
|
-
|
|
8070
|
+
const startRecord =
|
|
8071
|
+
Math.min(
|
|
8072
|
+
(currentPage - 1) *
|
|
8073
|
+
pageSize +
|
|
8074
|
+
1,
|
|
8075
|
+
totalCount
|
|
8076
|
+
);
|
|
8077
|
+
const endRecord = Math.min(
|
|
8078
|
+
currentPage * pageSize,
|
|
8079
|
+
totalCount
|
|
8080
|
+
);
|
|
7415
8081
|
return `Showing ${startRecord}-${endRecord} of ${totalCount} records`;
|
|
7416
8082
|
})()}
|
|
7417
8083
|
</div>
|
|
7418
|
-
|
|
7419
|
-
<div
|
|
7420
|
-
|
|
8084
|
+
|
|
8085
|
+
<div
|
|
8086
|
+
className={
|
|
8087
|
+
styles.paginationControls
|
|
8088
|
+
}
|
|
8089
|
+
>
|
|
8090
|
+
<div
|
|
8091
|
+
className={
|
|
8092
|
+
styles.pageSizeSelector
|
|
8093
|
+
}
|
|
8094
|
+
>
|
|
7421
8095
|
<label>Show:</label>
|
|
7422
8096
|
<select
|
|
7423
8097
|
value={pageSize}
|
|
7424
8098
|
onChange={(e) => {
|
|
7425
|
-
const newPageSize =
|
|
7426
|
-
|
|
8099
|
+
const newPageSize =
|
|
8100
|
+
parseInt(
|
|
8101
|
+
e.target
|
|
8102
|
+
.value
|
|
8103
|
+
);
|
|
8104
|
+
setPageSize(
|
|
8105
|
+
newPageSize
|
|
8106
|
+
);
|
|
7427
8107
|
setCurrentPage(1); // Reset to first page
|
|
7428
8108
|
// Re-execute the report with new page size
|
|
7429
|
-
if (
|
|
8109
|
+
if (
|
|
8110
|
+
previewData.length >
|
|
8111
|
+
0
|
|
8112
|
+
) {
|
|
7430
8113
|
executeReport();
|
|
7431
8114
|
}
|
|
7432
8115
|
}}
|
|
7433
|
-
className={
|
|
8116
|
+
className={
|
|
8117
|
+
styles.pageSizeSelect
|
|
8118
|
+
}
|
|
7434
8119
|
>
|
|
7435
|
-
<option value={10}>
|
|
7436
|
-
|
|
7437
|
-
|
|
7438
|
-
<option value={
|
|
7439
|
-
|
|
8120
|
+
<option value={10}>
|
|
8121
|
+
10
|
|
8122
|
+
</option>
|
|
8123
|
+
<option value={25}>
|
|
8124
|
+
25
|
|
8125
|
+
</option>
|
|
8126
|
+
<option value={50}>
|
|
8127
|
+
50
|
|
8128
|
+
</option>
|
|
8129
|
+
<option value={100}>
|
|
8130
|
+
100
|
|
8131
|
+
</option>
|
|
8132
|
+
<option value={250}>
|
|
8133
|
+
250
|
|
8134
|
+
</option>
|
|
7440
8135
|
</select>
|
|
7441
8136
|
<span>records</span>
|
|
7442
8137
|
</div>
|
|
7443
|
-
|
|
7444
|
-
<div
|
|
8138
|
+
|
|
8139
|
+
<div
|
|
8140
|
+
className={
|
|
8141
|
+
styles.pageNavigation
|
|
8142
|
+
}
|
|
8143
|
+
>
|
|
7445
8144
|
<button
|
|
7446
8145
|
onClick={() => {
|
|
7447
|
-
if (
|
|
7448
|
-
|
|
7449
|
-
|
|
8146
|
+
if (
|
|
8147
|
+
currentPage > 1
|
|
8148
|
+
) {
|
|
8149
|
+
const newPage =
|
|
8150
|
+
currentPage -
|
|
8151
|
+
1;
|
|
8152
|
+
setCurrentPage(
|
|
8153
|
+
newPage
|
|
8154
|
+
);
|
|
7450
8155
|
executeReport();
|
|
7451
8156
|
}
|
|
7452
8157
|
}}
|
|
7453
|
-
disabled={
|
|
7454
|
-
|
|
8158
|
+
disabled={
|
|
8159
|
+
currentPage <= 1
|
|
8160
|
+
}
|
|
8161
|
+
className={`${
|
|
8162
|
+
styles.pageBtn
|
|
8163
|
+
} ${
|
|
8164
|
+
currentPage <= 1
|
|
8165
|
+
? styles.disabled
|
|
8166
|
+
: ''
|
|
8167
|
+
}`}
|
|
7455
8168
|
>
|
|
7456
8169
|
‹ Previous
|
|
7457
8170
|
</button>
|
|
7458
|
-
|
|
7459
|
-
<div
|
|
8171
|
+
|
|
8172
|
+
<div
|
|
8173
|
+
className={
|
|
8174
|
+
styles.pageNumbers
|
|
8175
|
+
}
|
|
8176
|
+
>
|
|
7460
8177
|
{(() => {
|
|
7461
8178
|
const maxPagesToShow = 5;
|
|
7462
|
-
const totalPages =
|
|
7463
|
-
|
|
7464
|
-
|
|
7465
|
-
|
|
8179
|
+
const totalPages =
|
|
8180
|
+
Math.ceil(
|
|
8181
|
+
totalCount /
|
|
8182
|
+
pageSize
|
|
8183
|
+
);
|
|
8184
|
+
let startPage =
|
|
8185
|
+
Math.max(
|
|
8186
|
+
1,
|
|
8187
|
+
currentPage -
|
|
8188
|
+
Math.floor(
|
|
8189
|
+
maxPagesToShow /
|
|
8190
|
+
2
|
|
8191
|
+
)
|
|
8192
|
+
);
|
|
8193
|
+
let endPage =
|
|
8194
|
+
Math.min(
|
|
8195
|
+
totalPages,
|
|
8196
|
+
startPage +
|
|
8197
|
+
maxPagesToShow -
|
|
8198
|
+
1
|
|
8199
|
+
);
|
|
8200
|
+
|
|
7466
8201
|
// Adjust start if we're near the end
|
|
7467
|
-
if (
|
|
7468
|
-
|
|
8202
|
+
if (
|
|
8203
|
+
endPage -
|
|
8204
|
+
startPage <
|
|
8205
|
+
maxPagesToShow -
|
|
8206
|
+
1
|
|
8207
|
+
) {
|
|
8208
|
+
startPage =
|
|
8209
|
+
Math.max(
|
|
8210
|
+
1,
|
|
8211
|
+
endPage -
|
|
8212
|
+
maxPagesToShow +
|
|
8213
|
+
1
|
|
8214
|
+
);
|
|
7469
8215
|
}
|
|
7470
|
-
|
|
8216
|
+
|
|
7471
8217
|
const pages = [];
|
|
7472
|
-
|
|
8218
|
+
|
|
7473
8219
|
// Add first page and ellipsis if needed
|
|
7474
8220
|
if (startPage > 1) {
|
|
7475
8221
|
pages.push(
|
|
7476
8222
|
<button
|
|
7477
8223
|
key={1}
|
|
7478
8224
|
onClick={() => {
|
|
7479
|
-
setCurrentPage(
|
|
8225
|
+
setCurrentPage(
|
|
8226
|
+
1
|
|
8227
|
+
);
|
|
7480
8228
|
executeReport();
|
|
7481
8229
|
}}
|
|
7482
|
-
className={`${
|
|
8230
|
+
className={`${
|
|
8231
|
+
styles.pageBtn
|
|
8232
|
+
} ${
|
|
8233
|
+
1 ===
|
|
8234
|
+
currentPage
|
|
8235
|
+
? styles.active
|
|
8236
|
+
: ''
|
|
8237
|
+
}`}
|
|
7483
8238
|
>
|
|
7484
8239
|
1
|
|
7485
8240
|
</button>
|
|
7486
8241
|
);
|
|
7487
|
-
if (
|
|
7488
|
-
|
|
8242
|
+
if (
|
|
8243
|
+
startPage >
|
|
8244
|
+
2
|
|
8245
|
+
) {
|
|
8246
|
+
pages.push(
|
|
8247
|
+
<span
|
|
8248
|
+
key="start-ellipsis"
|
|
8249
|
+
className={
|
|
8250
|
+
styles.ellipsis
|
|
8251
|
+
}
|
|
8252
|
+
>
|
|
8253
|
+
...
|
|
8254
|
+
</span>
|
|
8255
|
+
);
|
|
7489
8256
|
}
|
|
7490
8257
|
}
|
|
7491
|
-
|
|
8258
|
+
|
|
7492
8259
|
// Add visible page numbers
|
|
7493
|
-
for (
|
|
8260
|
+
for (
|
|
8261
|
+
let i =
|
|
8262
|
+
startPage;
|
|
8263
|
+
i <= endPage;
|
|
8264
|
+
i++
|
|
8265
|
+
) {
|
|
7494
8266
|
pages.push(
|
|
7495
8267
|
<button
|
|
7496
8268
|
key={i}
|
|
7497
8269
|
onClick={() => {
|
|
7498
|
-
setCurrentPage(
|
|
8270
|
+
setCurrentPage(
|
|
8271
|
+
i
|
|
8272
|
+
);
|
|
7499
8273
|
executeReport();
|
|
7500
8274
|
}}
|
|
7501
|
-
className={`${
|
|
8275
|
+
className={`${
|
|
8276
|
+
styles.pageBtn
|
|
8277
|
+
} ${
|
|
8278
|
+
i ===
|
|
8279
|
+
currentPage
|
|
8280
|
+
? styles.active
|
|
8281
|
+
: ''
|
|
8282
|
+
}`}
|
|
7502
8283
|
>
|
|
7503
8284
|
{i}
|
|
7504
8285
|
</button>
|
|
7505
8286
|
);
|
|
7506
8287
|
}
|
|
7507
|
-
|
|
8288
|
+
|
|
7508
8289
|
// Add ellipsis and last page if needed
|
|
7509
|
-
if (
|
|
7510
|
-
|
|
7511
|
-
|
|
8290
|
+
if (
|
|
8291
|
+
endPage <
|
|
8292
|
+
totalPages
|
|
8293
|
+
) {
|
|
8294
|
+
if (
|
|
8295
|
+
endPage <
|
|
8296
|
+
totalPages -
|
|
8297
|
+
1
|
|
8298
|
+
) {
|
|
8299
|
+
pages.push(
|
|
8300
|
+
<span
|
|
8301
|
+
key="end-ellipsis"
|
|
8302
|
+
className={
|
|
8303
|
+
styles.ellipsis
|
|
8304
|
+
}
|
|
8305
|
+
>
|
|
8306
|
+
...
|
|
8307
|
+
</span>
|
|
8308
|
+
);
|
|
7512
8309
|
}
|
|
7513
8310
|
pages.push(
|
|
7514
8311
|
<button
|
|
7515
|
-
key={
|
|
8312
|
+
key={
|
|
8313
|
+
totalPages
|
|
8314
|
+
}
|
|
7516
8315
|
onClick={() => {
|
|
7517
|
-
setCurrentPage(
|
|
8316
|
+
setCurrentPage(
|
|
8317
|
+
totalPages
|
|
8318
|
+
);
|
|
7518
8319
|
executeReport();
|
|
7519
8320
|
}}
|
|
7520
|
-
className={`${
|
|
8321
|
+
className={`${
|
|
8322
|
+
styles.pageBtn
|
|
8323
|
+
} ${
|
|
8324
|
+
totalPages ===
|
|
8325
|
+
currentPage
|
|
8326
|
+
? styles.active
|
|
8327
|
+
: ''
|
|
8328
|
+
}`}
|
|
7521
8329
|
>
|
|
7522
|
-
{
|
|
8330
|
+
{
|
|
8331
|
+
totalPages
|
|
8332
|
+
}
|
|
7523
8333
|
</button>
|
|
7524
8334
|
);
|
|
7525
8335
|
}
|
|
7526
|
-
|
|
8336
|
+
|
|
7527
8337
|
return pages;
|
|
7528
8338
|
})()}
|
|
7529
8339
|
</div>
|
|
7530
|
-
|
|
8340
|
+
|
|
7531
8341
|
<button
|
|
7532
8342
|
onClick={() => {
|
|
7533
|
-
const totalPages =
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
8343
|
+
const totalPages =
|
|
8344
|
+
Math.ceil(
|
|
8345
|
+
totalCount /
|
|
8346
|
+
pageSize
|
|
8347
|
+
);
|
|
8348
|
+
if (
|
|
8349
|
+
currentPage <
|
|
8350
|
+
totalPages
|
|
8351
|
+
) {
|
|
8352
|
+
const newPage =
|
|
8353
|
+
currentPage +
|
|
8354
|
+
1;
|
|
8355
|
+
setCurrentPage(
|
|
8356
|
+
newPage
|
|
8357
|
+
);
|
|
7537
8358
|
executeReport();
|
|
7538
8359
|
}
|
|
7539
8360
|
}}
|
|
7540
|
-
disabled={
|
|
7541
|
-
|
|
8361
|
+
disabled={
|
|
8362
|
+
currentPage >=
|
|
8363
|
+
Math.ceil(
|
|
8364
|
+
totalCount /
|
|
8365
|
+
pageSize
|
|
8366
|
+
)
|
|
8367
|
+
}
|
|
8368
|
+
className={`${
|
|
8369
|
+
styles.pageBtn
|
|
8370
|
+
} ${
|
|
8371
|
+
currentPage >=
|
|
8372
|
+
Math.ceil(
|
|
8373
|
+
totalCount /
|
|
8374
|
+
pageSize
|
|
8375
|
+
)
|
|
8376
|
+
? styles.disabled
|
|
8377
|
+
: ''
|
|
8378
|
+
}`}
|
|
7542
8379
|
>
|
|
7543
8380
|
Next ›
|
|
7544
8381
|
</button>
|
|
@@ -7579,33 +8416,44 @@ const GenericReportImproved = ({
|
|
|
7579
8416
|
)}
|
|
7580
8417
|
</div>
|
|
7581
8418
|
</div>
|
|
7582
|
-
|
|
7583
8419
|
</div>
|
|
7584
8420
|
</>
|
|
7585
8421
|
)}
|
|
7586
8422
|
|
|
7587
8423
|
{/* Show no data message when no results found */}
|
|
7588
|
-
{!groupedData &&
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
<
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
</
|
|
8424
|
+
{!groupedData &&
|
|
8425
|
+
previewData.length === 0 &&
|
|
8426
|
+
!isLoading &&
|
|
8427
|
+
selectedTable &&
|
|
8428
|
+
selectedColumns.length > 0 && (
|
|
8429
|
+
<div className={styles.noDataContainer}>
|
|
8430
|
+
<div className={styles.noDataMessage}>
|
|
8431
|
+
<div className={styles.noDataIcon}>
|
|
8432
|
+
<Database size={48} />
|
|
8433
|
+
</div>
|
|
8434
|
+
<h3 className={styles.noDataTitle}>
|
|
8435
|
+
No Data Found
|
|
8436
|
+
</h3>
|
|
8437
|
+
<p className={styles.noDataText}>
|
|
8438
|
+
No records match the selected filters and
|
|
8439
|
+
criteria.
|
|
8440
|
+
</p>
|
|
8441
|
+
<div className={styles.noDataSuggestions}>
|
|
8442
|
+
<p>
|
|
8443
|
+
<strong>Try:</strong>
|
|
8444
|
+
</p>
|
|
8445
|
+
<ul>
|
|
8446
|
+
<li>Adjusting or removing filters</li>
|
|
8447
|
+
<li>Selecting a broader date range</li>
|
|
8448
|
+
<li>
|
|
8449
|
+
Checking if data exists for the
|
|
8450
|
+
selected table
|
|
8451
|
+
</li>
|
|
8452
|
+
</ul>
|
|
8453
|
+
</div>
|
|
7605
8454
|
</div>
|
|
7606
8455
|
</div>
|
|
7607
|
-
|
|
7608
|
-
)}
|
|
8456
|
+
)}
|
|
7609
8457
|
</div>
|
|
7610
8458
|
);
|
|
7611
8459
|
};
|