@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.
@@ -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: 'Group your data by a specific field for better organization',
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 = grouping?.enabled || selectedTemplate?.grouping?.enabled;
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 = customUrls.executeUrl || selectedTemplate?.executeUrl || executeUrl;
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: selectedTemplate?.id || 'utilization_planning',
2303
- filters: []
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('❌ Grouped report execution error:', result.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 = customUrls.executeUrl || selectedTemplate?.executeUrl || executeUrl;
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('🎯 [DEBUG] Initial fetchedDataCount:', fetchedDataCount);
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('🏗️ [DEBUG] Created dynamic template for custom grouping:', dynamicTemplate);
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('Please select a field to group by before executing the report.');
2515
- console.log('⚠️ [DEBUG] Custom grouping enabled but no groupByField selected');
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('📋 [DEBUG] Using template grouping:', effectiveGrouping);
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('🎯 [DEBUG] groupedResult.data:', groupedResult.data);
2536
- console.log('🎯 [DEBUG] groupedResult.data.grouped:', groupedResult.data?.grouped);
2537
- console.log('🎯 [DEBUG] groupedResult.data.groupedData:', groupedResult.data?.groupedData);
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 (groupedResult.data?.grouped && groupedResult.data?.groupedData?.groups) {
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('🎯 [DEBUG] Before assignment - groupedResult.data.groupedData.totalRecords:', groupedResult.data.groupedData?.totalRecords);
2545
- fetchedDataCount = groupedResult.data.groupedData?.totalRecords || 0;
2546
- console.log('🎯 [DEBUG] After assignment - fetchedDataCount:', fetchedDataCount);
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: groupedResult.data.groupedData?.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('🎯 [DEBUG] Fallback data length:', fallbackData.length);
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 (currentPageResult.data && currentPageResult.data.length > 0) {
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
- // Find the column metadata from selected columns to get type information
2584
- const columnMetadata = selectedColumns.find((col) => {
2585
- // Handle both direct column names and aliased names
2586
- return (
2587
- col.column === key ||
2588
- col.displayName === key ||
2589
- key.includes(col.column) ||
2590
- col.column.includes(key)
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
- // Check if any value in this column is JSON
2595
- const hasJsonValues = dataToProcess.some((row) => {
2596
- return isJsonValue(row[key]);
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
- // Try to find column type from table columns with improved detection
2600
- let columnType = 'varchar'; // default
2601
- if (columnMetadata) {
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 === columnMetadata.column
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
- // Enhanced column type detection for status fields
2626
- if (!columnMetadata || columnType === 'varchar') {
2627
- // Check if this looks like a status field based on column name
2628
- const lowerKey = key.toLowerCase();
2629
- if (
2630
- lowerKey.includes('status') ||
2631
- lowerKey.includes('active') ||
2632
- lowerKey.includes('enabled') ||
2633
- lowerKey.includes('disabled')
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
- // For JSON content, wrap in a container that allows full height expansion
2671
- if (isJsonValue(value)) {
2672
- return (
2673
- <div
2674
- style={{
2675
- whiteSpace: 'normal',
2676
- wordWrap: 'break-word',
2677
- lineHeight: '1.4',
2678
- padding: '4px 0',
2679
- width: '100%',
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
- return formattedValue;
2688
- },
2689
- };
2690
- });
2691
- setGridColumns(dynamicColumns);
2692
- } else {
2693
- // Fallback to selected columns if no data
2694
- const columns = selectedColumns.map((col) => {
2695
- // Find column type from table columns with enhanced detection
2696
- let columnType = 'varchar';
2697
- const tableColumn = tableColumns.find(
2698
- (tc) => tc.name === col.column
2699
- );
2700
- if (tableColumn) {
2701
- columnType = tableColumn.type;
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
- return formattedValue;
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('🎯 [DEBUG] Type of fetchedDataCount:', typeof fetchedDataCount);
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: config.grouping.groupHeaderStyle || 'primary',
2874
- emptyRowStyle: config.grouping.emptyRowStyle || 'light'
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(quickFilter.value);
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 (quickFilter.type === 'date_range' && typeof filterValue === 'object' && filterValue.start && filterValue.end) {
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' ? 'BETWEEN' : '='),
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: template.grouping.groupHeaderStyle || 'primary',
3044
- emptyRowStyle: template.grouping.emptyRowStyle || 'light'
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 (quickFilter.type === 'date_range' && typeof filterValue === 'object' && filterValue.start && filterValue.end) {
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 = customUrls.exportUrl || selectedTemplate?.exportUrl || exportUrl;
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: groupedData && groupedData.groups ? groupedData.groups.length : 'undefined',
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: !!(groupedData && groupedData.groupedData),
6633
- hasGroups: !!(groupedData && groupedData.groupedData && groupedData.groupedData.groups),
6634
- groupsLength: actualGroups ? actualGroups.length : 'undefined'
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(col => col.displayName || col.column);
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 (column.displayName && row[`\`${column.displayName}\``] !== undefined) {
6741
+ if (
6742
+ column.displayName &&
6743
+ row[`\`${column.displayName}\``] !== undefined
6744
+ ) {
6659
6745
  value = row[`\`${column.displayName}\``];
6660
- } else if (column.displayName && row[column.displayName] !== undefined) {
6746
+ } else if (
6747
+ column.displayName &&
6748
+ row[column.displayName] !== undefined
6749
+ ) {
6661
6750
  value = row[column.displayName];
6662
- } else if (column.table && row[`${column.table}_${column.column}`] !== undefined) {
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 (column.table && row[`\`${column.table}_${column.column}\``] !== undefined) {
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.replace(/• /g, '').replace(/<[^>]*>/g, '').trim();
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 ((column.displayName && column.displayName.toLowerCase().includes('abn')) ||
6693
- (column.column && column.column.toLowerCase().includes('abn'))) {
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,2)} ${cleanABN.slice(2,5)} ${cleanABN.slice(5,8)} ${cleanABN.slice(8,11)}`;
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 ((column.displayName && column.displayName.toLowerCase().includes('acn')) ||
6702
- (column.column && column.column.toLowerCase().includes('acn'))) {
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,3)} ${cleanACN.slice(3,6)} ${cleanACN.slice(6,9)}`;
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 = selectedTemplate?.grouping?.groupDisplayName || 'Group';
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 => getCellValue(row, 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': return { wch: 40 };
6854
+ case 'Comments':
6855
+ return { wch: 40 };
6740
6856
  case 'Project':
6741
- case 'Project Name': return { wch: 25 };
6857
+ case 'Project Name':
6858
+ return { wch: 25 };
6742
6859
  case 'User':
6743
- case 'Assigned User': return { wch: 20 };
6860
+ case 'Assigned User':
6861
+ return { wch: 20 };
6744
6862
  case 'Start Date':
6745
6863
  case 'End Date':
6746
- case 'Database Updated': return { wch: 15 };
6747
- default: return { wch: 18 };
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(/[^a-zA-Z0-9]/g, '_')}_grouped_report_${new Date().toISOString().split('T')[0]}.xlsx`;
6759
- const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
6760
- const blob = new Blob([excelBuffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
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 ? groupingConfig : null,
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) => setGroupingConfig({
6915
- ...groupingConfig,
6916
- enabled: e.target.checked,
6917
- // Reset fields when disabled
6918
- ...(e.target.checked ? {} : {
6919
- groupByField: '',
6920
- groupDisplayName: ''
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}>Enable Grouping</span>
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 sections (e.g., "Projects by Facility")
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}>Group By Field *</label>
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 = e.target.selectedOptions[0];
6942
- const fieldDisplayName = selectedOption?.text || '';
7078
+ const selectedOption =
7079
+ e.target.selectedOptions[0];
7080
+ const fieldDisplayName =
7081
+ selectedOption?.text || '';
6943
7082
  // Extract table name for display
6944
- const tableMatch = fieldDisplayName.match(/^([^.]+)\./);
6945
- const tableName = tableMatch ? tableMatch[1] : 'Group';
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: groupingConfig.groupDisplayName || tableName,
6951
- sortBy: e.target.value // Auto-set sort field
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="">Select field to group by...</option>
6957
- {getAvailableFilterColumns().map((col, index) => {
6958
- const fieldValue = `${col.table}.${col.column}`;
6959
- const displayName = `${col.tableName}.${col.columnName}`;
6960
- return (
6961
- <option key={index} value={fieldValue}>
6962
- {displayName}
6963
- </option>
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 categories you want to group by
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}>Group Display Name</label>
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) => setGroupingConfig({
6979
- ...groupingConfig,
6980
- groupDisplayName: e.target.value
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., "Facility: Main Building")
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}>Advanced Options</h4>
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}>Rows Per Group</label>
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) => setGroupingConfig({
7003
- ...groupingConfig,
7004
- rowsPerGroup: parseInt(e.target.value) || 10
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}>Group Header Style</label>
7179
+ <label className={styles.formLabel}>
7180
+ Group Header Style
7181
+ </label>
7012
7182
  <select
7013
- value={groupingConfig.groupHeaderStyle}
7014
- onChange={(e) => setGroupingConfig({
7015
- ...groupingConfig,
7016
- groupHeaderStyle: e.target.value
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">Primary (Blue)</option>
7021
- <option value="secondary">Secondary (Gray)</option>
7022
- <option value="success">Success (Green)</option>
7023
- <option value="warning">Warning (Orange)</option>
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}>Sort Direction</label>
7211
+ <label className={styles.formLabel}>
7212
+ Sort Direction
7213
+ </label>
7029
7214
  <select
7030
7215
  value={groupingConfig.sortDirection}
7031
- onChange={(e) => setGroupingConfig({
7032
- ...groupingConfig,
7033
- sortDirection: e.target.value
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">Ascending (A-Z)</option>
7038
- <option value="desc">Descending (Z-A)</option>
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={groupingConfig.showEmptyRows}
7048
- onChange={(e) => setGroupingConfig({
7049
- ...groupingConfig,
7050
- showEmptyRows: e.target.checked
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}>Show Empty Rows</span>
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={groupingConfig.showGroupTotals}
7060
- onChange={(e) => setGroupingConfig({
7061
- ...groupingConfig,
7062
- showGroupTotals: e.target.checked
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}>Show Group Totals</span>
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) => setGroupingConfig({
7073
- ...groupingConfig,
7074
- expandable: e.target.checked
7075
- })}
7278
+ onChange={(e) =>
7279
+ setGroupingConfig({
7280
+ ...groupingConfig,
7281
+ expandable:
7282
+ e.target.checked,
7283
+ })
7284
+ }
7076
7285
  />
7077
- <span className={styles.checkboxText}>Collapsible Groups</span>
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}>Configuration Preview</h4>
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>Options:</strong>
7092
- {groupingConfig.showEmptyRows && ' Empty Rows'}
7093
- {groupingConfig.showGroupTotals && ' Group Totals'}
7094
- {groupingConfig.expandable && ' Collapsible'}
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}>Custom API Endpoints (Optional)</h4>
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 report handling
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}>Custom Execute URL</label>
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) => setCustomUrls({
7117
- ...customUrls,
7118
- executeUrl: e.target.value
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 (overrides template and default URLs)
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}>Custom Export URL</label>
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) => setCustomUrls({
7134
- ...customUrls,
7135
- exportUrl: e.target.value
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 server-side exports)
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 || selectedTemplate?.grouping?.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} ${styles.btnSecondary} ${
7175
- groupedData ? styles.active : ''
7176
- }`}
7417
+ className={`${styles.btn} ${
7418
+ styles.btnSecondary
7419
+ } ${groupedData ? styles.active : ''}`}
7177
7420
  onClick={() => {
7178
- if (!groupedData && previewData.length > 0) {
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} ${styles.btnSecondary} ${
7190
- !groupedData ? styles.active : ''
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('🎯 [DEBUG] Render check - groupedData:', groupedData);
7213
- console.log('🎯 [DEBUG] Render check - groupedData?.grouped:', groupedData?.grouped);
7214
- console.log('🎯 [DEBUG] Render check - selectedColumns:', selectedColumns);
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">Report Results</h3>
7245
- <span className="group-stats">({previewData.length} records)</span>
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={() => handleRegularExport()}
7251
- disabled={isLoading || previewData.length === 0}
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 = previewData.length > 0 ? Object.keys(previewData[0]).map(key => ({name: key, header: key.replace(/\./g, ' ')})) : gridColumns;
7264
- return headerColumns.map((col, index) => (
7265
- <th key={index} className="table-header">
7266
- {col.header || col.name}
7267
- </th>
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(`🎯 [DEBUG] Rendering ${previewData.length} table rows`);
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 = previewData.length > 0 ? Object.keys(previewData[0]).map(key => ({name: key, header: key.replace(/\./g, ' ')})) : gridColumns;
7277
- console.log(`🎯 [DEBUG] Using columns:`, actualColumns);
7278
- return previewData.map((row, rowIndex) => (
7279
- <tr key={rowIndex} className="data-row">
7280
- {actualColumns.map((col, colIndex) => (
7281
- <td key={colIndex} className="table-cell">
7282
- {(() => {
7283
- const value = row[col.name];
7284
-
7285
- // Handle null/undefined
7286
- if (value === null || value === undefined) {
7287
- return <span style={{color: '#6c757d', fontStyle: 'italic'}}>—</span>;
7288
- }
7289
-
7290
- // Handle JSON strings
7291
- if (typeof value === 'string' && value.startsWith('{') && value.endsWith('}')) {
7292
- try {
7293
- const parsed = JSON.parse(value);
7294
- return (
7295
- <div style={{fontSize: '0.85em', lineHeight: '1.3'}}>
7296
- {Object.entries(parsed).map(([key, val]) => (
7297
- <div key={key} style={{marginBottom: '2px'}}>
7298
- <strong>{key.replace(/_/g, ' ')}:</strong> {val || '—'}
7299
- </div>
7300
- ))}
7301
- </div>
7302
- );
7303
- } catch (e) {
7304
- return value;
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
- // Use formatCellContent for all other values including dates and smart boolean formatting
7362
- const formattedValue = formatCellContent(value, {
7363
- id: col.name,
7364
- name: col.name,
7365
- // Get type from template column definition if available, otherwise infer
7366
- type: (() => {
7367
- // First try to find type from template's defaultColumns
7368
- if (selectedTemplate?.defaultColumns) {
7369
- const templateColumn = selectedTemplate.defaultColumns.find(
7370
- tc => `${tc.table}.${tc.column}` === col.name || tc.column === col.name
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
- if (templateColumn?.type) {
7373
- return templateColumn.type;
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
- // Fallback to column name-based type inference
7378
- const columnName = col.name.toLowerCase();
7379
- // Date column detection
7380
- if (columnName.includes('date') || columnName.includes('time') || columnName.includes('created_at') || columnName.includes('updated_at')) {
7381
- return 'date';
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
- // Boolean column detection - enhanced logic
7384
- if (columnName.includes('is_') || columnName.includes('has_') || columnName.includes('confirmed') ||
7385
- columnName.includes('active') || columnName.includes('enabled') || columnName.includes('completed') ||
7386
- columnName.includes('stage') || columnName.includes('final') || columnName.includes('flag') ||
7387
- columnName.includes('approved') || columnName.includes('verified') || columnName.includes('sent') ||
7388
- columnName.endsWith('?') || // Handle columns like "Confirmed?"
7389
- (typeof value === 'number' && (value === 0 || value === 1)) ||
7390
- (typeof value === 'string' && (value === '0' || value === '1'))) {
7391
- return 'boolean';
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
- return undefined; // Let formatCellContent auto-detect
7394
- })()
7395
- });
7396
-
7397
- return formattedValue;
7398
- })()}
7399
- </td>
7400
- ))}
7401
- </tr>
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 className={styles.paginationContainer}>
7411
- <div className={styles.paginationInfo}>
8059
+ <div
8060
+ className={
8061
+ styles.paginationContainer
8062
+ }
8063
+ >
8064
+ <div
8065
+ className={
8066
+ styles.paginationInfo
8067
+ }
8068
+ >
7412
8069
  {(() => {
7413
- const startRecord = Math.min((currentPage - 1) * pageSize + 1, totalCount);
7414
- const endRecord = Math.min(currentPage * pageSize, totalCount);
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 className={styles.paginationControls}>
7420
- <div className={styles.pageSizeSelector}>
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 = parseInt(e.target.value);
7426
- setPageSize(newPageSize);
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 (previewData.length > 0) {
8109
+ if (
8110
+ previewData.length >
8111
+ 0
8112
+ ) {
7430
8113
  executeReport();
7431
8114
  }
7432
8115
  }}
7433
- className={styles.pageSizeSelect}
8116
+ className={
8117
+ styles.pageSizeSelect
8118
+ }
7434
8119
  >
7435
- <option value={10}>10</option>
7436
- <option value={25}>25</option>
7437
- <option value={50}>50</option>
7438
- <option value={100}>100</option>
7439
- <option value={250}>250</option>
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 className={styles.pageNavigation}>
8138
+
8139
+ <div
8140
+ className={
8141
+ styles.pageNavigation
8142
+ }
8143
+ >
7445
8144
  <button
7446
8145
  onClick={() => {
7447
- if (currentPage > 1) {
7448
- const newPage = currentPage - 1;
7449
- setCurrentPage(newPage);
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={currentPage <= 1}
7454
- className={`${styles.pageBtn} ${currentPage <= 1 ? styles.disabled : ''}`}
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 className={styles.pageNumbers}>
8171
+
8172
+ <div
8173
+ className={
8174
+ styles.pageNumbers
8175
+ }
8176
+ >
7460
8177
  {(() => {
7461
8178
  const maxPagesToShow = 5;
7462
- const totalPages = Math.ceil(totalCount / pageSize);
7463
- let startPage = Math.max(1, currentPage - Math.floor(maxPagesToShow / 2));
7464
- let endPage = Math.min(totalPages, startPage + maxPagesToShow - 1);
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 (endPage - startPage < maxPagesToShow - 1) {
7468
- startPage = Math.max(1, endPage - maxPagesToShow + 1);
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(1);
8225
+ setCurrentPage(
8226
+ 1
8227
+ );
7480
8228
  executeReport();
7481
8229
  }}
7482
- className={`${styles.pageBtn} ${1 === currentPage ? styles.active : ''}`}
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 (startPage > 2) {
7488
- pages.push(<span key="start-ellipsis" className={styles.ellipsis}>...</span>);
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 (let i = startPage; i <= endPage; i++) {
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(i);
8270
+ setCurrentPage(
8271
+ i
8272
+ );
7499
8273
  executeReport();
7500
8274
  }}
7501
- className={`${styles.pageBtn} ${i === currentPage ? styles.active : ''}`}
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 (endPage < totalPages) {
7510
- if (endPage < totalPages - 1) {
7511
- pages.push(<span key="end-ellipsis" className={styles.ellipsis}>...</span>);
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={totalPages}
8312
+ key={
8313
+ totalPages
8314
+ }
7516
8315
  onClick={() => {
7517
- setCurrentPage(totalPages);
8316
+ setCurrentPage(
8317
+ totalPages
8318
+ );
7518
8319
  executeReport();
7519
8320
  }}
7520
- className={`${styles.pageBtn} ${totalPages === currentPage ? styles.active : ''}`}
8321
+ className={`${
8322
+ styles.pageBtn
8323
+ } ${
8324
+ totalPages ===
8325
+ currentPage
8326
+ ? styles.active
8327
+ : ''
8328
+ }`}
7521
8329
  >
7522
- {totalPages}
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 = Math.ceil(totalCount / pageSize);
7534
- if (currentPage < totalPages) {
7535
- const newPage = currentPage + 1;
7536
- setCurrentPage(newPage);
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={currentPage >= Math.ceil(totalCount / pageSize)}
7541
- className={`${styles.pageBtn} ${currentPage >= Math.ceil(totalCount / pageSize) ? styles.disabled : ''}`}
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 && previewData.length === 0 && !isLoading && selectedTable && selectedColumns.length > 0 && (
7589
- <div className={styles.noDataContainer}>
7590
- <div className={styles.noDataMessage}>
7591
- <div className={styles.noDataIcon}>
7592
- <Database size={48} />
7593
- </div>
7594
- <h3 className={styles.noDataTitle}>No Data Found</h3>
7595
- <p className={styles.noDataText}>
7596
- No records match the selected filters and criteria.
7597
- </p>
7598
- <div className={styles.noDataSuggestions}>
7599
- <p><strong>Try:</strong></p>
7600
- <ul>
7601
- <li>Adjusting or removing filters</li>
7602
- <li>Selecting a broader date range</li>
7603
- <li>Checking if data exists for the selected table</li>
7604
- </ul>
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
- </div>
7608
- )}
8456
+ )}
7609
8457
  </div>
7610
8458
  );
7611
8459
  };