@visns-studio/visns-components 5.9.9 → 5.9.10

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.
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect } from 'react';
1
+ 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';
@@ -13,16 +13,15 @@ import {
13
13
  Info,
14
14
  CircleCheck,
15
15
  CirclePlus,
16
- BookOpen,
17
16
  Data,
18
17
  File,
19
- SettingsVertical,
20
18
  Person,
21
19
  Calendar,
22
20
  Map,
23
21
  Tag,
24
22
  Money,
25
23
  ShippingBoxV1,
24
+ SettingsVertical,
26
25
  Cart,
27
26
  Newspaper,
28
27
  } from 'akar-icons';
@@ -30,7 +29,6 @@ import CustomFetch from '../Fetch';
30
29
  import Download from '../Download';
31
30
  import { saveAs } from 'file-saver';
32
31
  import Swal from 'sweetalert2';
33
- import GenericReport from './GenericReport';
34
32
  import styles from './styles/GenericReportImproved.module.scss';
35
33
  import './styles/SweetAlert.module.css';
36
34
 
@@ -737,21 +735,12 @@ const wizardSteps = [
737
735
  },
738
736
  ];
739
737
 
740
- // Legacy template reports (no longer used in UI but kept for backward compatibility)
741
- const reportTemplates = [];
742
-
743
738
  const GenericReportImproved = ({ setting = {}, definition = null }) => {
744
739
  // Extract settings with defaults
745
740
  const { tableUrl, columnUrl } = setting;
746
741
 
747
- // Ref for tooltip element
748
- const tooltipRef = React.useRef(null);
749
-
750
742
  // UI State
751
- const [beginnerMode, setBeginnerMode] = useState(true);
752
743
  const [currentWizardStep, setCurrentWizardStep] = useState(0);
753
- const [showAdvancedOptions, setShowAdvancedOptions] = useState(false);
754
- // Removed selectedTemplate state as we now use saved reports instead of templates
755
744
  const [showTemplates, setShowTemplates] = useState(true);
756
745
 
757
746
  // State for database schema
@@ -784,7 +773,6 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
784
773
 
785
774
  // State for saved reports
786
775
  const [savedReports, setSavedReports] = useState([]);
787
- const [selectedReport, setSelectedReport] = useState(null);
788
776
  const [loadedReportId, setLoadedReportId] = useState(null); // Track loaded report for overwrite functionality
789
777
  const [isLoadingReports, setIsLoadingReports] = useState(false);
790
778
 
@@ -792,15 +780,10 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
792
780
  const [suggestedJoins, setSuggestedJoins] = useState({});
793
781
  const [isLoadingSuggestedJoins, setIsLoadingSuggestedJoins] = useState({});
794
782
 
795
- // State for toggling visibility of sections
796
- const [showSuggestedRelationships, setShowSuggestedRelationships] =
797
- useState({});
798
- const [showJoinTables, setShowJoinTables] = useState(false);
799
- const [showSortingSection, setShowSortingSection] = useState(false);
800
- const [showFilteringSection, setShowFilteringSection] = useState(false);
801
-
802
783
  // State for sorting and filtering
803
784
  const [sortCriteria, setSortCriteria] = useState([]);
785
+ const [showSortingSection, setShowSortingSection] = useState(false);
786
+ const [showFilteringSection, setShowFilteringSection] = useState(false);
804
787
 
805
788
  // Enhanced filter criteria with visual builder support
806
789
  const [filterCriteria, setFilterCriteria] = useState({
@@ -815,30 +798,18 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
815
798
 
816
799
  // State for report execution
817
800
  const [totalResults, setTotalResults] = useState(0);
818
- const [executedSql, setExecutedSql] = useState('');
819
- const [showSqlPreview, setShowSqlPreview] = useState(false);
820
801
 
821
802
  // State for report preview
822
803
  const [previewData, setPreviewData] = useState([]);
823
804
  const [gridColumns, setGridColumns] = useState([]);
824
805
  const [isLoading, setIsLoading] = useState(false);
806
+ const [hasAutoExecuted, setHasAutoExecuted] = useState(false);
825
807
 
826
- // State for pagination - now using local pagination
808
+ // State for pagination - now using server-side pagination
827
809
  const [currentPage, setCurrentPage] = useState(1);
828
810
  const [pageSize, setPageSize] = useState(25); // Default to 25 for better UX
829
811
  const [totalPages, setTotalPages] = useState(0);
830
- const [allData, setAllData] = useState([]); // Store all fetched data for local pagination
831
-
832
- // State for JSON field keys
833
- const [jsonFieldKeys, setJsonFieldKeys] = useState({});
834
- const [loadingJsonKeys, setLoadingJsonKeys] = useState({});
835
- const [showJsonKeySelector, setShowJsonKeySelector] = useState({});
836
-
837
- // State for suggested filters
838
- const [suggestedFilters, setSuggestedFilters] = useState([]);
839
- const [isGeneratingSuggestions, setIsGeneratingSuggestions] =
840
- useState(false);
841
- const [showSuggestedFilters, setShowSuggestedFilters] = useState(false);
812
+ const [totalCount, setTotalCount] = useState(0); // Total records from server
842
813
 
843
814
  // State for showing hidden fields
844
815
  const [showHiddenFields, setShowHiddenFields] = useState(false);
@@ -850,7 +821,6 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
850
821
  reportsUrl = '/ajax/reportBuilder/reports',
851
822
  executeUrl = '/ajax/reportBuilder/execute',
852
823
  suggestedJoinsUrl = '/ajax/reportBuilder/getSuggestedJoins',
853
- jsonFieldKeysUrl = '/ajax/reportBuilder/getJsonFieldKeys',
854
824
  exportUrl = '/ajax/reportBuilder/export',
855
825
  } = setting;
856
826
 
@@ -862,21 +832,21 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
862
832
  <div style="text-align: left; font-size: 14px; color: #4b5563;">
863
833
  <div style="margin-bottom: 20px;">
864
834
  <h4 style="color: #1f2937; margin-bottom: 10px;">Choose Your Path:</h4>
865
-
835
+
866
836
  <div style="background: #f0f9ff; border: 1px solid #3b82f6; border-radius: 8px; padding: 12px; margin-bottom: 12px;">
867
837
  <h5 style="color: #1e40af; margin: 0 0 8px 0;">🚀 Quick Start (Recommended)</h5>
868
- <p style="margin: 0;">Use a pre-built template to get started immediately. Perfect for common reports like client lists or recent invoices.</p>
838
+ <p style="margin: 0;">Use a saved report to get started immediately. Perfect for common reports like client lists or recent invoices.</p>
869
839
  </div>
870
-
840
+
871
841
  <div style="background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 8px; padding: 12px;">
872
842
  <h5 style="color: #374151; margin: 0 0 8px 0;">✨ Custom Report</h5>
873
843
  <p style="margin: 0;">Build your own report from scratch with our step-by-step wizard. We'll guide you through each step!</p>
874
844
  </div>
875
845
  </div>
876
-
846
+
877
847
  <div style="background: #fef3c7; border: 1px solid #f59e0b; border-radius: 8px; padding: 12px;">
878
848
  <p style="margin: 0; color: #92400e;">
879
- <strong>💡 Tip:</strong> You can switch between Beginner and Advanced mode at any time using the toggle in the top right.
849
+ <strong>💡 Tip:</strong> Follow the wizard steps to create professional reports quickly and easily.
880
850
  </p>
881
851
  </div>
882
852
  </div>
@@ -901,7 +871,7 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
901
871
 
902
872
  // Show help on first load
903
873
  const hasSeenHelp = localStorage.getItem('reportBuilder_hasSeenHelp');
904
- if (!hasSeenHelp && beginnerMode) {
874
+ if (!hasSeenHelp) {
905
875
  setTimeout(() => {
906
876
  showGuidedHelp();
907
877
  localStorage.setItem('reportBuilder_hasSeenHelp', 'true');
@@ -920,7 +890,6 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
920
890
  // Auto-select common columns when user reaches the columns step
921
891
  useEffect(() => {
922
892
  if (
923
- beginnerMode &&
924
893
  selectedColumns.length === 0 &&
925
894
  tableColumns.length > 0 &&
926
895
  currentWizardStep === 2 // Step 3 is index 2 (columns step)
@@ -958,7 +927,6 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
958
927
  }, [
959
928
  tableColumns,
960
929
  selectedColumns.length,
961
- beginnerMode,
962
930
  selectedTable,
963
931
  currentWizardStep,
964
932
  ]);
@@ -966,22 +934,23 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
966
934
  // Auto-execute report when user reaches step 5 (preview step)
967
935
  useEffect(() => {
968
936
  if (
969
- beginnerMode &&
970
937
  currentWizardStep === 4 && // Step 5 is index 4 (preview step)
971
938
  selectedTable &&
972
939
  selectedColumns.length > 0 &&
973
940
  previewData.length === 0 && // Only if no data loaded yet
974
- !isLoading
941
+ !isLoading &&
942
+ !hasAutoExecuted // Prevent infinite loops
975
943
  ) {
944
+ setHasAutoExecuted(true);
976
945
  executeReport();
977
946
  }
978
947
  }, [
979
948
  currentWizardStep,
980
- beginnerMode,
981
949
  selectedTable,
982
950
  selectedColumns.length,
983
951
  previewData.length,
984
952
  isLoading,
953
+ hasAutoExecuted,
985
954
  ]);
986
955
 
987
956
  // Delete saved report
@@ -1041,6 +1010,8 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
1041
1010
  ],
1042
1011
  });
1043
1012
  setSortCriteria([]);
1013
+ setShowSortingSection(false);
1014
+ setShowFilteringSection(false);
1044
1015
  setPreviewData([]);
1045
1016
  setGridColumns([]);
1046
1017
  setReportName('');
@@ -1050,6 +1021,7 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
1050
1021
  setShowTemplates(true);
1051
1022
  setTableColumns([]);
1052
1023
  setAvailableTables([]);
1024
+ setHasAutoExecuted(false); // Reset auto-execute flag
1053
1025
 
1054
1026
  toast.info(
1055
1027
  'Report builder reset. You can start creating a new report.'
@@ -1459,14 +1431,36 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
1459
1431
  if (!tableName || !columnUrl) return;
1460
1432
 
1461
1433
  try {
1462
- const result = await CustomFetch(
1463
- `${columnUrl}/${tableName}`,
1464
- 'GET'
1434
+ // Use the same API format as fetchTableColumns
1435
+ const result = await CustomFetch(columnUrl, 'POST', {
1436
+ table: tableName,
1437
+ });
1438
+
1439
+ console.log(
1440
+ `Manual join columns API response for ${tableName}:`,
1441
+ result
1465
1442
  );
1466
- if (result.data?.success && result.data.data) {
1443
+
1444
+ if (result.error) {
1445
+ console.error(`Error fetching ${type} columns:`, result.error);
1446
+ } else {
1447
+ // Handle the response format similar to fetchTableColumns
1448
+ let columnsData = [];
1449
+ if (result.data?.success && result.data.data) {
1450
+ columnsData = result.data.data;
1451
+ } else if (Array.isArray(result.data)) {
1452
+ columnsData = result.data;
1453
+ } else if (Array.isArray(result)) {
1454
+ columnsData = result;
1455
+ }
1456
+
1457
+ console.log(
1458
+ `Loaded ${columnsData.length} columns for ${type} table ${tableName}`
1459
+ );
1460
+
1467
1461
  setManualJoinColumns((prev) => ({
1468
1462
  ...prev,
1469
- [type]: result.data.data,
1463
+ [type]: columnsData,
1470
1464
  }));
1471
1465
  }
1472
1466
  } catch (error) {
@@ -1521,9 +1515,7 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
1521
1515
  fetchTableColumns(tableName);
1522
1516
  fetchSuggestedJoins(tableName);
1523
1517
 
1524
- if (beginnerMode) {
1525
- goToNextStep(); // Now goes to relationships step
1526
- }
1518
+ goToNextStep(); // Go to relationships step
1527
1519
  };
1528
1520
 
1529
1521
  // Column selection handler
@@ -1551,13 +1543,101 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
1551
1543
  }
1552
1544
  };
1553
1545
 
1554
- // Execute report with local pagination - fetch all data at once
1546
+ // Create dataSource function for server-side pagination (similar to DataGrid)
1547
+ const dataSource = useCallback(
1548
+ async ({ skip, limit, sortInfo, filterValue }) => {
1549
+ if (!selectedTable || selectedColumns.length === 0) {
1550
+ return { data: [], count: 0 };
1551
+ }
1552
+
1553
+ try {
1554
+ const page = Math.floor(skip / limit) + 1;
1555
+
1556
+ // Use the same payload format as GenericReport (with query wrapper)
1557
+ const queryConfig = {
1558
+ mainTable: selectedTable,
1559
+ columns: selectedColumns.map((col) => ({
1560
+ table: col.table,
1561
+ column: col.column,
1562
+ alias: col.alias || col.displayName,
1563
+ })),
1564
+ joins: joins.map((join) => ({
1565
+ joinType: join.joinType,
1566
+ targetTable: join.targetTable,
1567
+ sourceColumn: join.sourceColumn,
1568
+ targetColumn: join.targetColumn,
1569
+ })),
1570
+ filters: flattenFilterCriteria(filterCriteria),
1571
+ sorting: sortCriteria,
1572
+ };
1573
+
1574
+ // Add sorting from DataGrid if provided
1575
+ if (sortInfo) {
1576
+ queryConfig.sorting = [
1577
+ {
1578
+ column: sortInfo.name,
1579
+ direction: sortInfo.dir === 1 ? 'asc' : 'desc',
1580
+ },
1581
+ ];
1582
+ }
1583
+
1584
+ const reportConfig = {
1585
+ query: queryConfig,
1586
+ limit: limit,
1587
+ offset: skip,
1588
+ page: page,
1589
+ };
1590
+
1591
+ const result = await CustomFetch(
1592
+ executeUrl,
1593
+ 'POST',
1594
+ reportConfig
1595
+ );
1596
+
1597
+ if (result.error) {
1598
+ console.error('Report execution error:', result.error);
1599
+ return { data: [], count: 0 };
1600
+ } else if (result.data?.success) {
1601
+ const fetchedData = result.data.data || [];
1602
+ const totalRecords =
1603
+ result.data.total || fetchedData.length;
1604
+
1605
+ // Update total count and results for display
1606
+ setTotalCount(totalRecords);
1607
+ setTotalResults(totalRecords);
1608
+
1609
+ console.info(
1610
+ 'Fetched data:',
1611
+ fetchedData.length,
1612
+ 'of',
1613
+ totalRecords
1614
+ );
1615
+
1616
+ return { data: fetchedData, count: totalRecords };
1617
+ } else {
1618
+ console.error('Unexpected response format:', result);
1619
+ return { data: [], count: 0 };
1620
+ }
1621
+ } catch (error) {
1622
+ console.error('Error in dataSource:', error);
1623
+ return { data: [], count: 0 };
1624
+ }
1625
+ },
1626
+ [
1627
+ selectedTable,
1628
+ selectedColumns,
1629
+ joins,
1630
+ filterCriteria,
1631
+ sortCriteria,
1632
+ executeUrl,
1633
+ ]
1634
+ );
1635
+
1636
+ // Execute report function for manual execution (creates grid columns)
1555
1637
  const executeReport = async () => {
1556
1638
  if (!selectedTable || selectedColumns.length === 0) {
1557
1639
  toast.warning(
1558
- beginnerMode
1559
- ? 'Please select at least one field to include in your report.'
1560
- : 'Please select a table and at least one column.'
1640
+ 'Please select at least one field to include in your report.'
1561
1641
  );
1562
1642
  return;
1563
1643
  }
@@ -1565,147 +1645,41 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
1565
1645
  setIsLoading(true);
1566
1646
 
1567
1647
  try {
1568
- // Use the same payload format as GenericReport (with query wrapper)
1569
- const queryConfig = {
1570
- mainTable: selectedTable,
1571
- columns: selectedColumns.map((col) => ({
1572
- table: col.table,
1573
- column: col.column,
1574
- alias: col.alias || col.displayName,
1575
- })),
1576
- joins: joins.map((join) => ({
1577
- joinType: join.joinType,
1578
- targetTable: join.targetTable,
1579
- sourceColumn: join.sourceColumn,
1580
- targetColumn: join.targetColumn,
1581
- })),
1582
- filters: flattenFilterCriteria(filterCriteria),
1583
- sorting: sortCriteria,
1584
- };
1585
-
1586
- const reportConfig = {
1587
- query: queryConfig,
1588
- limit: 1000, // Fetch up to 1000 records for local pagination
1589
- offset: 0, // Always start from beginning
1590
- };
1591
-
1592
- const result = await CustomFetch(executeUrl, 'POST', reportConfig);
1593
-
1594
- if (result.error) {
1595
- toast.error(result.error);
1596
- } else if (result.data?.success) {
1597
- // Store all fetched data for local pagination
1598
- const allFetchedData = result.data.data || [];
1599
- setAllData(allFetchedData);
1600
- console.info('Fetched data:', allFetchedData.length);
1601
-
1602
- setTotalResults(allFetchedData.length);
1603
- setExecutedSql(result.data.sql || '');
1604
-
1605
- // Calculate total pages for local pagination
1606
- const calculatedTotalPages = Math.ceil(
1607
- allFetchedData.length / pageSize
1608
- );
1609
-
1610
- setTotalPages(calculatedTotalPages);
1611
- console.info('Total pages:', calculatedTotalPages);
1612
-
1613
- setCurrentPage(1); // Reset to first page
1648
+ // Fetch first page to get data structure for grid columns
1649
+ const firstPageResult = await dataSource({
1650
+ skip: 0,
1651
+ limit: pageSize,
1652
+ sortInfo: null,
1653
+ filterValue: null,
1654
+ });
1614
1655
 
1615
- // Set initial page data (first page)
1616
- const startIndex = 0;
1617
- const endIndex = Math.min(pageSize, allFetchedData.length);
1618
- setPreviewData(allFetchedData.slice(startIndex, endIndex));
1656
+ if (firstPageResult.data && firstPageResult.data.length > 0) {
1657
+ setPreviewData(firstPageResult.data);
1619
1658
 
1620
1659
  // Create grid columns dynamically from actual data with smart formatting
1621
- if (result.data.data && result.data.data.length > 0) {
1622
- const firstRow = result.data.data[0];
1623
- const dynamicColumns = Object.keys(firstRow).map((key) => {
1624
- // Find the column metadata from selected columns to get type information
1625
- const columnMetadata = selectedColumns.find((col) => {
1626
- // Handle both direct column names and aliased names
1627
- return (
1628
- col.column === key ||
1629
- col.displayName === key ||
1630
- key.includes(col.column) ||
1631
- col.column.includes(key)
1632
- );
1633
- });
1634
-
1635
- // Check if any value in this column is JSON
1636
- const hasJsonValues = result.data.data.some((row) => {
1637
- return isJsonValue(row[key]);
1638
- });
1639
-
1640
- // Try to find column type from table columns
1641
- let columnType = 'varchar'; // default
1642
- if (columnMetadata) {
1643
- const tableColumn = tableColumns.find(
1644
- (tc) => tc.name === columnMetadata.column
1645
- );
1646
- if (tableColumn) {
1647
- columnType = tableColumn.type;
1648
- } else {
1649
- // Check in joined table columns
1650
- joins.forEach((join) => {
1651
- if (join.availableColumns) {
1652
- const joinedColumn =
1653
- join.availableColumns.find(
1654
- (jc) =>
1655
- jc.name ===
1656
- columnMetadata.column
1657
- );
1658
- if (joinedColumn) {
1659
- columnType = joinedColumn.type;
1660
- }
1661
- }
1662
- });
1663
- }
1664
- }
1665
-
1666
- return {
1667
- name: key,
1668
- header: formatName(key), // Use formatted name as header
1669
- defaultFlex: 1,
1670
- minWidth: hasJsonValues ? 250 : 100, // Wider columns for JSON data
1671
- maxWidth: hasJsonValues ? 400 : undefined, // Max width for JSON columns
1672
- render: ({ value }) => {
1673
- // Use smart formatting for the cell value
1674
- const formattedValue = formatSmartValue(
1675
- value,
1676
- key,
1677
- columnType
1678
- );
1679
-
1680
- // For JSON content, wrap in a container that allows full height expansion
1681
- if (isJsonValue(value)) {
1682
- return (
1683
- <div
1684
- style={{
1685
- whiteSpace: 'normal',
1686
- wordWrap: 'break-word',
1687
- lineHeight: '1.4',
1688
- padding: '4px 0',
1689
- width: '100%',
1690
- }}
1691
- >
1692
- {formattedValue}
1693
- </div>
1694
- );
1695
- }
1660
+ const firstRow = firstPageResult.data[0];
1661
+ const dynamicColumns = Object.keys(firstRow).map((key) => {
1662
+ // Find the column metadata from selected columns to get type information
1663
+ const columnMetadata = selectedColumns.find((col) => {
1664
+ // Handle both direct column names and aliased names
1665
+ return (
1666
+ col.column === key ||
1667
+ col.displayName === key ||
1668
+ key.includes(col.column) ||
1669
+ col.column.includes(key)
1670
+ );
1671
+ });
1696
1672
 
1697
- return formattedValue;
1698
- },
1699
- };
1673
+ // Check if any value in this column is JSON
1674
+ const hasJsonValues = firstPageResult.data.some((row) => {
1675
+ return isJsonValue(row[key]);
1700
1676
  });
1701
- setGridColumns(dynamicColumns);
1702
- } else {
1703
- // Fallback to selected columns if no data
1704
- const columns = selectedColumns.map((col) => {
1705
- // Find column type from table columns
1706
- let columnType = 'varchar';
1677
+
1678
+ // Try to find column type from table columns with improved detection
1679
+ let columnType = 'varchar'; // default
1680
+ if (columnMetadata) {
1707
1681
  const tableColumn = tableColumns.find(
1708
- (tc) => tc.name === col.column
1682
+ (tc) => tc.name === columnMetadata.column
1709
1683
  );
1710
1684
  if (tableColumn) {
1711
1685
  columnType = tableColumn.type;
@@ -1715,7 +1689,9 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
1715
1689
  if (join.availableColumns) {
1716
1690
  const joinedColumn =
1717
1691
  join.availableColumns.find(
1718
- (jc) => jc.name === col.column
1692
+ (jc) =>
1693
+ jc.name ===
1694
+ columnMetadata.column
1719
1695
  );
1720
1696
  if (joinedColumn) {
1721
1697
  columnType = joinedColumn.type;
@@ -1723,67 +1699,154 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
1723
1699
  }
1724
1700
  });
1725
1701
  }
1702
+ }
1726
1703
 
1727
- return {
1728
- name: col.displayName || formatName(col.column),
1729
- header: col.displayName || formatName(col.column),
1730
- defaultFlex: 1,
1731
- minWidth: 100,
1732
- render: ({ value }) => {
1733
- const formattedValue = formatSmartValue(
1734
- value,
1735
- col.column,
1736
- columnType
1704
+ // Enhanced column type detection for status fields
1705
+ if (!columnMetadata || columnType === 'varchar') {
1706
+ // Check if this looks like a status field based on column name
1707
+ const lowerKey = key.toLowerCase();
1708
+ if (
1709
+ lowerKey.includes('status') ||
1710
+ lowerKey.includes('active') ||
1711
+ lowerKey.includes('enabled') ||
1712
+ lowerKey.includes('disabled')
1713
+ ) {
1714
+ // Check if values look like boolean/status values
1715
+ const sampleValues = firstPageResult.data
1716
+ .slice(0, 5)
1717
+ .map((row) => row[key]);
1718
+ const hasStatusValues = sampleValues.some(
1719
+ (val) =>
1720
+ val === 0 ||
1721
+ val === 1 ||
1722
+ val === '0' ||
1723
+ val === '1' ||
1724
+ val === true ||
1725
+ val === false ||
1726
+ val === 'true' ||
1727
+ val === 'false'
1728
+ );
1729
+ if (hasStatusValues) {
1730
+ columnType = 'tinyint'; // This will trigger status formatting
1731
+ }
1732
+ }
1733
+ }
1734
+
1735
+ return {
1736
+ name: key,
1737
+ header: formatName(key), // Use formatted name as header
1738
+ defaultFlex: 1,
1739
+ minWidth: hasJsonValues ? 250 : 100, // Wider columns for JSON data
1740
+ maxWidth: hasJsonValues ? 400 : undefined, // Max width for JSON columns
1741
+ render: ({ value }) => {
1742
+ // Use smart formatting for the cell value
1743
+ const formattedValue = formatSmartValue(
1744
+ value,
1745
+ key,
1746
+ columnType
1747
+ );
1748
+
1749
+ // For JSON content, wrap in a container that allows full height expansion
1750
+ if (isJsonValue(value)) {
1751
+ return (
1752
+ <div
1753
+ style={{
1754
+ whiteSpace: 'normal',
1755
+ wordWrap: 'break-word',
1756
+ lineHeight: '1.4',
1757
+ padding: '4px 0',
1758
+ width: '100%',
1759
+ }}
1760
+ >
1761
+ {formattedValue}
1762
+ </div>
1737
1763
  );
1764
+ }
1738
1765
 
1739
- // For JSON content, wrap in a container that allows full height expansion
1740
- if (isJsonValue(value)) {
1741
- return (
1742
- <div
1743
- style={{
1744
- whiteSpace: 'normal',
1745
- wordWrap: 'break-word',
1746
- lineHeight: '1.4',
1747
- padding: '4px 0',
1748
- width: '100%',
1749
- }}
1750
- >
1751
- {formattedValue}
1752
- </div>
1753
- );
1766
+ return formattedValue;
1767
+ },
1768
+ };
1769
+ });
1770
+ setGridColumns(dynamicColumns);
1771
+ } else {
1772
+ // Fallback to selected columns if no data
1773
+ const columns = selectedColumns.map((col) => {
1774
+ // Find column type from table columns with enhanced detection
1775
+ let columnType = 'varchar';
1776
+ const tableColumn = tableColumns.find(
1777
+ (tc) => tc.name === col.column
1778
+ );
1779
+ if (tableColumn) {
1780
+ columnType = tableColumn.type;
1781
+ } else {
1782
+ // Check in joined table columns
1783
+ joins.forEach((join) => {
1784
+ if (join.availableColumns) {
1785
+ const joinedColumn = join.availableColumns.find(
1786
+ (jc) => jc.name === col.column
1787
+ );
1788
+ if (joinedColumn) {
1789
+ columnType = joinedColumn.type;
1754
1790
  }
1791
+ }
1792
+ });
1793
+ }
1755
1794
 
1756
- return formattedValue;
1757
- },
1758
- };
1759
- });
1760
- setGridColumns(columns);
1761
- }
1795
+ // Enhanced column type detection for status fields
1796
+ if (columnType === 'varchar') {
1797
+ const lowerColumn = col.column.toLowerCase();
1798
+ if (
1799
+ lowerColumn.includes('status') ||
1800
+ lowerColumn.includes('active') ||
1801
+ lowerColumn.includes('enabled') ||
1802
+ lowerColumn.includes('disabled')
1803
+ ) {
1804
+ columnType = 'tinyint'; // This will trigger status formatting
1805
+ }
1806
+ }
1762
1807
 
1763
- if (
1764
- beginnerMode &&
1765
- currentWizardStep < wizardSteps.length - 1
1766
- ) {
1767
- setCurrentWizardStep(wizardSteps.length - 1);
1768
- }
1808
+ return {
1809
+ name: col.displayName || formatName(col.column),
1810
+ header: col.displayName || formatName(col.column),
1811
+ defaultFlex: 1,
1812
+ minWidth: 100,
1813
+ render: ({ value }) => {
1814
+ const formattedValue = formatSmartValue(
1815
+ value,
1816
+ col.column,
1817
+ columnType
1818
+ );
1819
+
1820
+ // For JSON content, wrap in a container that allows full height expansion
1821
+ if (isJsonValue(value)) {
1822
+ return (
1823
+ <div
1824
+ style={{
1825
+ whiteSpace: 'normal',
1826
+ wordWrap: 'break-word',
1827
+ lineHeight: '1.4',
1828
+ padding: '4px 0',
1829
+ width: '100%',
1830
+ }}
1831
+ >
1832
+ {formattedValue}
1833
+ </div>
1834
+ );
1835
+ }
1769
1836
 
1770
- // Show success message with total records found
1771
- toast.success(`Found ${allFetchedData.length} records`);
1837
+ return formattedValue;
1838
+ },
1839
+ };
1840
+ });
1841
+ setGridColumns(columns);
1842
+ }
1772
1843
 
1773
- // Show warning if we hit the 1000 record limit
1774
- if (allFetchedData.length === 1000) {
1775
- toast.warning(
1776
- 'Showing first 1000 records. Consider adding filters to narrow your results.'
1777
- );
1778
- }
1779
- } else {
1780
- toast.error(
1781
- 'Failed to execute report: ' +
1782
- (result.data?.message ||
1783
- result.message ||
1784
- 'Unknown error')
1785
- );
1844
+ if (currentWizardStep < wizardSteps.length - 1) {
1845
+ setCurrentWizardStep(wizardSteps.length - 1);
1786
1846
  }
1847
+
1848
+ // Show success message with total records found
1849
+ toast.success(`Found ${firstPageResult.count} records`);
1787
1850
  } catch (error) {
1788
1851
  toast.error('Failed to execute report');
1789
1852
  console.error('Error executing report:', error);
@@ -1792,60 +1855,22 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
1792
1855
  }
1793
1856
  };
1794
1857
 
1795
- // Local pagination handlers
1796
- const handlePageChange = (newPage) => {
1797
- if (
1798
- newPage >= 1 &&
1799
- newPage <= totalPages &&
1800
- newPage !== currentPage &&
1801
- allData.length > 0
1802
- ) {
1803
- setCurrentPage(newPage);
1804
-
1805
- // Calculate slice indices for local pagination
1806
- const startIndex = (newPage - 1) * pageSize;
1807
- const endIndex = Math.min(startIndex + pageSize, allData.length);
1808
- setPreviewData(allData.slice(startIndex, endIndex));
1809
- }
1810
- };
1811
-
1812
- const handlePageSizeChange = (newPageSize) => {
1813
- if (newPageSize !== pageSize && allData.length > 0) {
1814
- setPageSize(newPageSize);
1815
- setCurrentPage(1); // Reset to first page
1816
-
1817
- // Recalculate total pages with new page size
1818
- const calculatedTotalPages = Math.ceil(
1819
- allData.length / newPageSize
1820
- );
1821
- setTotalPages(calculatedTotalPages);
1822
-
1823
- // Update preview data for first page with new page size
1824
- const endIndex = Math.min(newPageSize, allData.length);
1825
- setPreviewData(allData.slice(0, endIndex));
1826
- }
1827
- };
1828
-
1829
1858
  // Reset pagination when starting a new report
1830
1859
  const resetPagination = () => {
1831
1860
  setCurrentPage(1);
1832
1861
  setTotalPages(0);
1833
1862
  setPreviewData([]);
1834
1863
  setTotalResults(0);
1835
- setAllData([]);
1864
+ setTotalCount(0);
1836
1865
  };
1837
1866
 
1838
- // Render the appropriate content based on mode
1867
+ // Render the appropriate content
1839
1868
  const renderContent = () => {
1840
- if (beginnerMode && showTemplates) {
1869
+ if (showTemplates) {
1841
1870
  return renderTemplateSelection();
1842
1871
  }
1843
1872
 
1844
- if (beginnerMode) {
1845
- return renderWizardMode();
1846
- }
1847
-
1848
- return renderAdvancedMode();
1873
+ return renderWizardMode();
1849
1874
  };
1850
1875
 
1851
1876
  // Load saved report configuration
@@ -1869,13 +1894,16 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
1869
1894
 
1870
1895
  if (config.joins) {
1871
1896
  setJoins(config.joins);
1872
- config.joins.forEach((join) => {
1897
+ // Fetch columns for each joined table
1898
+ config.joins.forEach((join, index) => {
1873
1899
  if (!availableTables.includes(join.targetTable)) {
1874
1900
  setAvailableTables((prev) => [
1875
1901
  ...prev,
1876
1902
  join.targetTable,
1877
1903
  ]);
1878
1904
  }
1905
+ // Fetch columns for the joined table to populate availableColumns
1906
+ fetchJoinColumns(join.targetTable, index);
1879
1907
  });
1880
1908
  }
1881
1909
 
@@ -1885,13 +1913,18 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
1885
1913
 
1886
1914
  if (config.sorting) {
1887
1915
  setSortCriteria(config.sorting);
1916
+ // Show sorting section if there are sort criteria
1917
+ if (config.sorting.length > 0) {
1918
+ setShowSortingSection(true);
1919
+ }
1888
1920
  }
1889
1921
 
1890
1922
  setReportName(report.label);
1891
1923
  setIsPublic(report.is_public || false);
1892
1924
  setLoadedReportId(report.id); // Track the loaded report ID for overwrite functionality
1893
1925
  setShowTemplates(false);
1894
- setCurrentWizardStep(2); // Start from columns step since table and relationships are already selected
1926
+ // Set to the preview step (index 4) since the report is already configured
1927
+ setCurrentWizardStep(4);
1895
1928
 
1896
1929
  toast.success(`Loaded report: ${report.label}`);
1897
1930
  } catch (error) {
@@ -2142,13 +2175,21 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
2142
2175
 
2143
2176
  {/* Navigation buttons */}
2144
2177
  <div className={styles.wizardNavigation}>
2145
- <button
2146
- className={`${styles.btn} ${styles.btnSecondary}`}
2147
- onClick={goToPreviousStep}
2148
- disabled={currentWizardStep === 0}
2149
- >
2150
- Previous
2151
- </button>
2178
+ {currentWizardStep === 0 ? (
2179
+ <button
2180
+ className={`${styles.btn} ${styles.btnSecondary}`}
2181
+ onClick={startAgain}
2182
+ >
2183
+ Cancel
2184
+ </button>
2185
+ ) : (
2186
+ <button
2187
+ className={`${styles.btn} ${styles.btnSecondary}`}
2188
+ onClick={goToPreviousStep}
2189
+ >
2190
+ Previous
2191
+ </button>
2192
+ )}
2152
2193
 
2153
2194
  {currentWizardStep === wizardSteps.length - 1 ? (
2154
2195
  <button
@@ -2879,19 +2920,20 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
2879
2920
  }
2880
2921
  >
2881
2922
  <option value="">Select Table</option>
2882
- {availableTables
2923
+ {/* Use all tables, not just availableTables */}
2924
+ {tables
2883
2925
  .filter(
2884
2926
  (table) =>
2885
- table !==
2927
+ table.name !==
2886
2928
  manualJoin.sourceTable
2887
2929
  )
2888
2930
  .map((table) => (
2889
2931
  <option
2890
- key={table}
2891
- value={table}
2932
+ key={table.name}
2933
+ value={table.name}
2892
2934
  >
2893
2935
  {getTableDisplayName(
2894
- table,
2936
+ table.name,
2895
2937
  definition
2896
2938
  )}
2897
2939
  </option>
@@ -3306,14 +3348,299 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
3306
3348
 
3307
3349
  return (
3308
3350
  <div className={styles.filtersSection}>
3351
+ {/* Sorting Section */}
3352
+ <div className={styles.reportSection}>
3353
+ <div
3354
+ className={styles.sectionHeader}
3355
+ title={
3356
+ showSortingSection
3357
+ ? 'Hide sorting options'
3358
+ : 'Show sorting options'
3359
+ }
3360
+ >
3361
+ <h3
3362
+ onClick={() =>
3363
+ setShowSortingSection(!showSortingSection)
3364
+ }
3365
+ style={{ cursor: 'pointer' }}
3366
+ >
3367
+ Sorting
3368
+ </h3>
3369
+ <div className={styles.sectionHeaderActions}>
3370
+ <button
3371
+ className={styles.btn}
3372
+ onClick={handleAddSortCriterion}
3373
+ disabled={selectedColumns.length === 0}
3374
+ >
3375
+ Add Sort Criterion
3376
+ </button>
3377
+ <div
3378
+ className={`${styles.toggleBtn} ${
3379
+ showSortingSection
3380
+ ? styles.toggleBtnActive
3381
+ : ''
3382
+ }`}
3383
+ onClick={() =>
3384
+ setShowSortingSection(!showSortingSection)
3385
+ }
3386
+ style={{ cursor: 'pointer' }}
3387
+ >
3388
+ {showSortingSection ? '▲' : '▼'}
3389
+ </div>
3390
+ </div>
3391
+ </div>
3392
+
3393
+ {showSortingSection && (
3394
+ <>
3395
+ {sortCriteria.length > 0 && (
3396
+ <div className={styles.filterGroup}>
3397
+ <div className={styles.joinTablesContainer}>
3398
+ {sortCriteria.map(
3399
+ (criterion, index) => (
3400
+ <div
3401
+ key={index}
3402
+ className={
3403
+ styles.joinSection
3404
+ }
3405
+ data-sort-index={index + 1}
3406
+ >
3407
+ <div
3408
+ className={
3409
+ styles.joinHeader
3410
+ }
3411
+ >
3412
+ <h4
3413
+ data-sort-index={
3414
+ index + 1
3415
+ }
3416
+ >
3417
+ by{' '}
3418
+ {criterion.table &&
3419
+ criterion.column
3420
+ ? `${formatName(
3421
+ criterion.table
3422
+ )}.${formatName(
3423
+ criterion.column
3424
+ )}`
3425
+ : `Criterion #${
3426
+ index + 1
3427
+ }`}
3428
+ </h4>
3429
+ <button
3430
+ className={
3431
+ styles.removeJoinBtn
3432
+ }
3433
+ onClick={() =>
3434
+ handleRemoveSortCriterion(
3435
+ index
3436
+ )
3437
+ }
3438
+ title="Remove Sort Criterion"
3439
+ >
3440
+ ×
3441
+ </button>
3442
+ </div>
3443
+
3444
+ <div
3445
+ className={
3446
+ styles.joinGrid
3447
+ }
3448
+ >
3449
+ <div
3450
+ className={
3451
+ styles.joinGridColumn
3452
+ }
3453
+ >
3454
+ <div
3455
+ className={
3456
+ styles.joinField
3457
+ }
3458
+ >
3459
+ <label>
3460
+ Column:
3461
+ </label>
3462
+ <select
3463
+ className={
3464
+ styles.selectControl
3465
+ }
3466
+ value={
3467
+ criterion.table &&
3468
+ criterion.column
3469
+ ? `${criterion.table}.${criterion.column}`
3470
+ : ''
3471
+ }
3472
+ onChange={(
3473
+ e
3474
+ ) => {
3475
+ if (
3476
+ e
3477
+ .target
3478
+ .value
3479
+ ) {
3480
+ const dotIndex =
3481
+ e.target.value.indexOf(
3482
+ '.'
3483
+ );
3484
+ if (
3485
+ dotIndex !==
3486
+ -1
3487
+ ) {
3488
+ const table =
3489
+ e.target.value.substring(
3490
+ 0,
3491
+ dotIndex
3492
+ );
3493
+ const column =
3494
+ e.target.value.substring(
3495
+ dotIndex +
3496
+ 1
3497
+ );
3498
+
3499
+ handleSortCriterionChange(
3500
+ index,
3501
+ 'column',
3502
+ ''
3503
+ );
3504
+ handleSortCriterionChange(
3505
+ index,
3506
+ 'table',
3507
+ table
3508
+ );
3509
+ handleSortCriterionChange(
3510
+ index,
3511
+ 'column',
3512
+ column
3513
+ );
3514
+ }
3515
+ }
3516
+ }}
3517
+ >
3518
+ <option value="">
3519
+ Select
3520
+ Column
3521
+ </option>
3522
+ {/* Main table columns */}
3523
+ <optgroup
3524
+ label={`${formatName(
3525
+ selectedTable
3526
+ )} Columns`}
3527
+ >
3528
+ {tableColumns.map(
3529
+ (
3530
+ column
3531
+ ) => (
3532
+ <option
3533
+ key={`${selectedTable}.${column.name}`}
3534
+ value={`${selectedTable}.${column.name}`}
3535
+ >
3536
+ {formatName(
3537
+ column.name
3538
+ )}
3539
+ </option>
3540
+ )
3541
+ )}
3542
+ </optgroup>
3543
+
3544
+ {/* Joined tables columns */}
3545
+ {joins.map(
3546
+ (
3547
+ join,
3548
+ joinIndex
3549
+ ) =>
3550
+ join.targetTable &&
3551
+ join.availableColumns &&
3552
+ join
3553
+ .availableColumns
3554
+ .length >
3555
+ 0 ? (
3556
+ <optgroup
3557
+ key={`join-${joinIndex}`}
3558
+ label={`${formatName(
3559
+ join.targetTable
3560
+ )} Columns`}
3561
+ >
3562
+ {join.availableColumns.map(
3563
+ (
3564
+ column
3565
+ ) => (
3566
+ <option
3567
+ key={`${join.targetTable}.${column.name}`}
3568
+ value={`${join.targetTable}.${column.name}`}
3569
+ >
3570
+ {formatName(
3571
+ column.name
3572
+ )}
3573
+ </option>
3574
+ )
3575
+ )}
3576
+ </optgroup>
3577
+ ) : null
3578
+ )}
3579
+ </select>
3580
+ </div>
3581
+ </div>
3582
+
3583
+ <div
3584
+ className={
3585
+ styles.joinGridColumn
3586
+ }
3587
+ >
3588
+ <div
3589
+ className={
3590
+ styles.joinField
3591
+ }
3592
+ >
3593
+ <label>
3594
+ Direction:
3595
+ </label>
3596
+ <select
3597
+ className={
3598
+ styles.selectControl
3599
+ }
3600
+ value={
3601
+ criterion.direction
3602
+ }
3603
+ onChange={(
3604
+ e
3605
+ ) =>
3606
+ handleSortCriterionChange(
3607
+ index,
3608
+ 'direction',
3609
+ e
3610
+ .target
3611
+ .value
3612
+ )
3613
+ }
3614
+ >
3615
+ <option value="ASC">
3616
+ Ascending
3617
+ </option>
3618
+ <option value="DESC">
3619
+ Descending
3620
+ </option>
3621
+ </select>
3622
+ </div>
3623
+ </div>
3624
+ </div>
3625
+ </div>
3626
+ )
3627
+ )}
3628
+ </div>
3629
+ </div>
3630
+ )}
3631
+ </>
3632
+ )}
3633
+ </div>
3634
+
3635
+ {/* Filtering Section */}
3309
3636
  <div className={styles.helpBox}>
3310
3637
  <Info size={20} />
3311
3638
  <div>
3312
- <h4>Advanced Filter Builder</h4>
3639
+ <h4>Filter Builder</h4>
3313
3640
  <p>
3314
- Create sophisticated filters with multiple groups
3315
- and logical operators. Use filter groups to create
3316
- complex conditions like "(A AND B) OR (C AND D)".
3641
+ Create filters to narrow down your results. Use
3642
+ filter groups to create complex conditions like "(A
3643
+ AND B) OR (C AND D)".
3317
3644
  </p>
3318
3645
  </div>
3319
3646
  </div>
@@ -3685,6 +4012,48 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
3685
4012
  );
3686
4013
  };
3687
4014
 
4015
+ // Sorting functions (from original GenericReport)
4016
+ // Add a new sort criterion
4017
+ const handleAddSortCriterion = () => {
4018
+ // Default to the first selected column if available
4019
+ const defaultColumn =
4020
+ selectedColumns.length > 0
4021
+ ? {
4022
+ table: selectedColumns[0].table,
4023
+ column: selectedColumns[0].column,
4024
+ displayName: selectedColumns[0].displayName,
4025
+ }
4026
+ : null;
4027
+
4028
+ const newSortCriterion = {
4029
+ table: defaultColumn?.table || '',
4030
+ column: defaultColumn?.column || '',
4031
+ direction: 'ASC',
4032
+ };
4033
+
4034
+ setSortCriteria([...sortCriteria, newSortCriterion]);
4035
+ setShowSortingSection(true);
4036
+ };
4037
+
4038
+ // Update a sort criterion
4039
+ const handleSortCriterionChange = (index, field, value) => {
4040
+ const updatedSortCriteria = [...sortCriteria];
4041
+ updatedSortCriteria[index][field] = value;
4042
+ setSortCriteria(updatedSortCriteria);
4043
+ };
4044
+
4045
+ // Remove a sort criterion
4046
+ const handleRemoveSortCriterion = (index) => {
4047
+ const updatedSortCriteria = [...sortCriteria];
4048
+ updatedSortCriteria.splice(index, 1);
4049
+ setSortCriteria(updatedSortCriteria);
4050
+
4051
+ // Hide the section if no sort criteria remain
4052
+ if (updatedSortCriteria.length === 0) {
4053
+ setShowSortingSection(false);
4054
+ }
4055
+ };
4056
+
3688
4057
  // Export report in different formats (replicating GenericReport pattern)
3689
4058
  const exportReport = async (format) => {
3690
4059
  if (!selectedTable || selectedColumns.length === 0) {
@@ -3882,6 +4251,7 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
3882
4251
  className={`${styles.btn} ${styles.btnPrimary}`}
3883
4252
  onClick={() => {
3884
4253
  resetPagination();
4254
+ setHasAutoExecuted(false); // Reset flag for manual execution
3885
4255
  executeReport();
3886
4256
  }}
3887
4257
  disabled={isLoading}
@@ -3895,19 +4265,12 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
3895
4265
  <div className={styles.gridContainer}>
3896
4266
  <ReactDataGrid
3897
4267
  columns={gridColumns}
3898
- dataSource={previewData}
4268
+ dataSource={dataSource}
3899
4269
  style={{ height: 800 }}
3900
- pagination="local"
4270
+ pagination
3901
4271
  limit={pageSize}
3902
- skip={(currentPage - 1) * pageSize}
3903
- count={totalResults}
3904
4272
  onLimitChange={(newLimit) => {
3905
- handlePageSizeChange(newLimit);
3906
- }}
3907
- onSkipChange={(newSkip) => {
3908
- const newPage =
3909
- Math.floor(newSkip / pageSize) + 1;
3910
- handlePageChange(newPage);
4273
+ setPageSize(newLimit);
3911
4274
  }}
3912
4275
  pageSizes={[10, 15, 25, 50]}
3913
4276
  defaultLimit={25}
@@ -3936,6 +4299,7 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
3936
4299
  <button
3937
4300
  className={`${styles.btn} ${styles.btnSuccess}`}
3938
4301
  onClick={() => saveReport(false)}
4302
+ style={{ marginRight: '10px' }}
3939
4303
  >
3940
4304
  <Save size={16} />
3941
4305
  {loadedReportId
@@ -4021,15 +4385,6 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
4021
4385
  );
4022
4386
  };
4023
4387
 
4024
- // Render advanced mode (using original GenericReport)
4025
- const renderAdvancedMode = () => {
4026
- return (
4027
- <div className={styles.advancedMode}>
4028
- <GenericReport setting={setting} definition={definition} />
4029
- </div>
4030
- );
4031
- };
4032
-
4033
4388
  return (
4034
4389
  <div className={styles.reportBuilder}>
4035
4390
  {/* Header */}
@@ -4044,29 +4399,6 @@ const GenericReportImproved = ({ setting = {}, definition = null }) => {
4044
4399
  Help
4045
4400
  </button>
4046
4401
  </div>
4047
-
4048
- <div className={styles.headerRight}>
4049
- <div className={styles.modeToggle}>
4050
- <span className={beginnerMode ? styles.active : ''}>
4051
- <BookOpen size={16} />
4052
- Beginner
4053
- </span>
4054
- <label className={styles.switch}>
4055
- <input
4056
- type="checkbox"
4057
- checked={!beginnerMode}
4058
- onChange={(e) =>
4059
- setBeginnerMode(!e.target.checked)
4060
- }
4061
- />
4062
- <span className={styles.slider}></span>
4063
- </label>
4064
- <span className={!beginnerMode ? styles.active : ''}>
4065
- <SettingsVertical size={16} />
4066
- Advanced
4067
- </span>
4068
- </div>
4069
- </div>
4070
4402
  </div>
4071
4403
 
4072
4404
  {/* Main content */}