@quillsql/react 2.13.42 → 2.13.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Chart.d.ts.map +1 -1
- package/dist/cjs/Chart.js +2 -2
- package/dist/cjs/ChartBuilder.d.ts +1 -0
- package/dist/cjs/ChartBuilder.d.ts.map +1 -1
- package/dist/cjs/ChartBuilder.js +48 -14
- package/dist/cjs/ChartEditor.d.ts +1 -1
- package/dist/cjs/ChartEditor.d.ts.map +1 -1
- package/dist/cjs/ChartEditor.js +2 -2
- package/dist/cjs/Context.d.ts.map +1 -1
- package/dist/cjs/Context.js +37 -26
- package/dist/cjs/Dashboard.d.ts +2 -2
- package/dist/cjs/Dashboard.d.ts.map +1 -1
- package/dist/cjs/Dashboard.js +115 -99
- package/dist/cjs/ReportBuilder.d.ts.map +1 -1
- package/dist/cjs/ReportBuilder.js +871 -1574
- package/dist/cjs/SQLEditor.d.ts.map +1 -1
- package/dist/cjs/SQLEditor.js +23 -4
- package/dist/cjs/Table.d.ts.map +1 -1
- package/dist/cjs/components/Chart/CustomReferenceLine.js +1 -1
- package/dist/cjs/components/Chart/GaugeChart.d.ts.map +1 -1
- package/dist/cjs/components/Chart/GaugeChart.js +64 -12
- package/dist/cjs/components/Chart/InternalChart.js +1 -1
- package/dist/cjs/components/Chart/MapChart.d.ts.map +1 -1
- package/dist/cjs/components/Chart/MapChart.js +65 -7
- package/dist/cjs/components/Dashboard/DashboardFilter.d.ts.map +1 -1
- package/dist/cjs/components/Dashboard/DashboardFilter.js +3 -3
- package/dist/cjs/components/Dashboard/DashboardSection.d.ts +1 -1
- package/dist/cjs/components/Dashboard/DashboardSection.d.ts.map +1 -1
- package/dist/cjs/components/Dashboard/DashboardSection.js +20 -19
- package/dist/cjs/components/Dashboard/MetricComponent.d.ts.map +1 -1
- package/dist/cjs/components/Dashboard/MetricComponent.js +1 -0
- package/dist/cjs/components/Dashboard/TableComponent.d.ts.map +1 -1
- package/dist/cjs/components/Dashboard/TableComponent.js +5 -1
- package/dist/cjs/components/QuillMultiSelectWithCombo.d.ts.map +1 -1
- package/dist/cjs/components/QuillMultiSelectWithCombo.js +58 -8
- package/dist/cjs/components/QuillSelect.d.ts.map +1 -1
- package/dist/cjs/components/QuillSelect.js +91 -14
- package/dist/cjs/components/QuillSelectWithCombo.js +6 -6
- package/dist/cjs/components/ReportBuilder/AddColumnModal.d.ts +6 -16
- package/dist/cjs/components/ReportBuilder/AddColumnModal.d.ts.map +1 -1
- package/dist/cjs/components/ReportBuilder/AddColumnModal.js +172 -75
- package/dist/cjs/components/ReportBuilder/AddSortPopover.d.ts +1 -5
- package/dist/cjs/components/ReportBuilder/AddSortPopover.d.ts.map +1 -1
- package/dist/cjs/components/ReportBuilder/AddSortPopover.js +2 -9
- package/dist/cjs/components/ReportBuilder/DraggableColumns.d.ts +17 -0
- package/dist/cjs/components/ReportBuilder/DraggableColumns.d.ts.map +1 -0
- package/dist/cjs/components/ReportBuilder/DraggableColumns.js +52 -0
- package/dist/cjs/components/ReportBuilder/DraggableItem.d.ts +17 -0
- package/dist/cjs/components/ReportBuilder/DraggableItem.d.ts.map +1 -0
- package/dist/cjs/components/ReportBuilder/DraggableItem.js +17 -0
- package/dist/cjs/components/ReportBuilder/FilterModal.d.ts +6 -12
- package/dist/cjs/components/ReportBuilder/FilterModal.d.ts.map +1 -1
- package/dist/cjs/components/ReportBuilder/FilterModal.js +29 -20
- package/dist/cjs/components/ReportBuilder/FilterStack.d.ts +8 -17
- package/dist/cjs/components/ReportBuilder/FilterStack.d.ts.map +1 -1
- package/dist/cjs/components/ReportBuilder/FilterStack.js +20 -86
- package/dist/cjs/components/ReportBuilder/convert.js +6 -6
- package/dist/cjs/components/ReportBuilder/ui.d.ts +8 -9
- package/dist/cjs/components/ReportBuilder/ui.d.ts.map +1 -1
- package/dist/cjs/components/ReportBuilder/ui.js +30 -8
- package/dist/cjs/components/ReportBuilder/util.d.ts +2 -5
- package/dist/cjs/components/ReportBuilder/util.d.ts.map +1 -1
- package/dist/cjs/components/ReportBuilder/util.js +1 -1
- package/dist/cjs/components/UiComponents.d.ts +3 -2
- package/dist/cjs/components/UiComponents.d.ts.map +1 -1
- package/dist/cjs/components/UiComponents.js +13 -13
- package/dist/cjs/hooks/useAskQuill.d.ts +1 -1
- package/dist/cjs/hooks/useAskQuill.d.ts.map +1 -1
- package/dist/cjs/hooks/useAskQuill.js +14 -12
- package/dist/cjs/hooks/useDashboard.d.ts +5 -3
- package/dist/cjs/hooks/useDashboard.d.ts.map +1 -1
- package/dist/cjs/hooks/useDashboard.js +8 -6
- package/dist/cjs/hooks/useOnClickOutside.d.ts +1 -0
- package/dist/cjs/hooks/useOnClickOutside.d.ts.map +1 -1
- package/dist/cjs/hooks/useOnClickOutside.js +33 -0
- package/dist/cjs/internals/ReportBuilder/PivotForm.d.ts +2 -1
- package/dist/cjs/internals/ReportBuilder/PivotForm.d.ts.map +1 -1
- package/dist/cjs/internals/ReportBuilder/PivotForm.js +22 -15
- package/dist/cjs/internals/ReportBuilder/PivotModal.d.ts +7 -5
- package/dist/cjs/internals/ReportBuilder/PivotModal.d.ts.map +1 -1
- package/dist/cjs/internals/ReportBuilder/PivotModal.js +100 -48
- package/dist/cjs/models/Client.d.ts +4 -0
- package/dist/cjs/models/Client.d.ts.map +1 -1
- package/dist/cjs/models/Dashboard.d.ts +1 -0
- package/dist/cjs/models/Dashboard.d.ts.map +1 -1
- package/dist/cjs/models/Report.d.ts +2 -0
- package/dist/cjs/models/Report.d.ts.map +1 -1
- package/dist/cjs/models/ReportBuilder.d.ts +46 -0
- package/dist/cjs/models/ReportBuilder.d.ts.map +1 -0
- package/dist/cjs/models/ReportBuilder.js +19 -0
- package/dist/cjs/models/Tables.d.ts +5 -5
- package/dist/cjs/models/Tables.d.ts.map +1 -1
- package/dist/cjs/utils/astFilterProcessing.d.ts +4 -0
- package/dist/cjs/utils/astFilterProcessing.d.ts.map +1 -1
- package/dist/cjs/utils/astFilterProcessing.js +301 -97
- package/dist/cjs/utils/astProcessing.d.ts +14 -4
- package/dist/cjs/utils/astProcessing.d.ts.map +1 -1
- package/dist/cjs/utils/astProcessing.js +38 -4
- package/dist/cjs/utils/dashboard.d.ts.map +1 -1
- package/dist/cjs/utils/dashboard.js +18 -3
- package/dist/cjs/utils/dataFetcher.d.ts.map +1 -1
- package/dist/cjs/utils/dataFetcher.js +4 -3
- package/dist/cjs/utils/filterProcessing.d.ts +2 -11
- package/dist/cjs/utils/filterProcessing.d.ts.map +1 -1
- package/dist/cjs/utils/filterProcessing.js +4 -16
- package/dist/cjs/utils/pivotConstructor.d.ts +2 -1
- package/dist/cjs/utils/pivotConstructor.d.ts.map +1 -1
- package/dist/cjs/utils/pivotConstructor.js +4 -2
- package/dist/cjs/utils/pivotProcessing.d.ts +17 -7
- package/dist/cjs/utils/pivotProcessing.d.ts.map +1 -1
- package/dist/cjs/utils/pivotProcessing.js +60 -51
- package/dist/cjs/utils/queryConstructor.d.ts.map +1 -1
- package/dist/cjs/utils/queryConstructor.js +15 -2
- package/dist/cjs/utils/report.d.ts +12 -9
- package/dist/cjs/utils/report.d.ts.map +1 -1
- package/dist/cjs/utils/report.js +114 -47
- package/dist/cjs/utils/reportBuilder.d.ts +88 -0
- package/dist/cjs/utils/reportBuilder.d.ts.map +1 -0
- package/dist/cjs/utils/reportBuilder.js +395 -0
- package/dist/cjs/utils/schema.d.ts +7 -0
- package/dist/cjs/utils/schema.d.ts.map +1 -1
- package/dist/cjs/utils/schema.js +36 -2
- package/dist/cjs/utils/tableProcessing.d.ts +59 -29
- package/dist/cjs/utils/tableProcessing.d.ts.map +1 -1
- package/dist/cjs/utils/tableProcessing.js +75 -90
- package/dist/cjs/utils/ui.d.ts +2 -0
- package/dist/cjs/utils/ui.d.ts.map +1 -0
- package/dist/cjs/utils/ui.js +18 -0
- package/dist/esm/Chart.d.ts.map +1 -1
- package/dist/esm/Chart.js +2 -2
- package/dist/esm/ChartBuilder.d.ts +1 -0
- package/dist/esm/ChartBuilder.d.ts.map +1 -1
- package/dist/esm/ChartBuilder.js +49 -15
- package/dist/esm/ChartEditor.d.ts +1 -1
- package/dist/esm/ChartEditor.d.ts.map +1 -1
- package/dist/esm/ChartEditor.js +2 -2
- package/dist/esm/Context.d.ts.map +1 -1
- package/dist/esm/Context.js +37 -26
- package/dist/esm/Dashboard.d.ts +2 -2
- package/dist/esm/Dashboard.d.ts.map +1 -1
- package/dist/esm/Dashboard.js +119 -103
- package/dist/esm/ReportBuilder.d.ts.map +1 -1
- package/dist/esm/ReportBuilder.js +878 -1581
- package/dist/esm/SQLEditor.d.ts.map +1 -1
- package/dist/esm/SQLEditor.js +23 -4
- package/dist/esm/Table.d.ts.map +1 -1
- package/dist/esm/components/Chart/CustomReferenceLine.js +1 -1
- package/dist/esm/components/Chart/GaugeChart.d.ts.map +1 -1
- package/dist/esm/components/Chart/GaugeChart.js +27 -8
- package/dist/esm/components/Chart/InternalChart.js +1 -1
- package/dist/esm/components/Chart/MapChart.d.ts.map +1 -1
- package/dist/esm/components/Chart/MapChart.js +30 -5
- package/dist/esm/components/Dashboard/DashboardFilter.d.ts.map +1 -1
- package/dist/esm/components/Dashboard/DashboardFilter.js +3 -3
- package/dist/esm/components/Dashboard/DashboardSection.d.ts +1 -1
- package/dist/esm/components/Dashboard/DashboardSection.d.ts.map +1 -1
- package/dist/esm/components/Dashboard/DashboardSection.js +21 -20
- package/dist/esm/components/Dashboard/MetricComponent.d.ts.map +1 -1
- package/dist/esm/components/Dashboard/MetricComponent.js +1 -0
- package/dist/esm/components/Dashboard/TableComponent.d.ts.map +1 -1
- package/dist/esm/components/Dashboard/TableComponent.js +5 -1
- package/dist/esm/components/QuillMultiSelectWithCombo.d.ts.map +1 -1
- package/dist/esm/components/QuillMultiSelectWithCombo.js +57 -7
- package/dist/esm/components/QuillSelect.d.ts.map +1 -1
- package/dist/esm/components/QuillSelect.js +58 -14
- package/dist/esm/components/QuillSelectWithCombo.js +6 -6
- package/dist/esm/components/ReportBuilder/AddColumnModal.d.ts +6 -16
- package/dist/esm/components/ReportBuilder/AddColumnModal.d.ts.map +1 -1
- package/dist/esm/components/ReportBuilder/AddColumnModal.js +173 -76
- package/dist/esm/components/ReportBuilder/AddSortPopover.d.ts +1 -5
- package/dist/esm/components/ReportBuilder/AddSortPopover.d.ts.map +1 -1
- package/dist/esm/components/ReportBuilder/AddSortPopover.js +2 -9
- package/dist/esm/components/ReportBuilder/DraggableColumns.d.ts +17 -0
- package/dist/esm/components/ReportBuilder/DraggableColumns.d.ts.map +1 -0
- package/dist/esm/components/ReportBuilder/DraggableColumns.js +46 -0
- package/dist/esm/components/ReportBuilder/DraggableItem.d.ts +17 -0
- package/dist/esm/components/ReportBuilder/DraggableItem.d.ts.map +1 -0
- package/dist/esm/components/ReportBuilder/DraggableItem.js +14 -0
- package/dist/esm/components/ReportBuilder/FilterModal.d.ts +6 -12
- package/dist/esm/components/ReportBuilder/FilterModal.d.ts.map +1 -1
- package/dist/esm/components/ReportBuilder/FilterModal.js +29 -20
- package/dist/esm/components/ReportBuilder/FilterStack.d.ts +8 -17
- package/dist/esm/components/ReportBuilder/FilterStack.d.ts.map +1 -1
- package/dist/esm/components/ReportBuilder/FilterStack.js +21 -87
- package/dist/esm/components/ReportBuilder/convert.js +6 -6
- package/dist/esm/components/ReportBuilder/ui.d.ts +8 -9
- package/dist/esm/components/ReportBuilder/ui.d.ts.map +1 -1
- package/dist/esm/components/ReportBuilder/ui.js +33 -11
- package/dist/esm/components/ReportBuilder/util.d.ts +2 -5
- package/dist/esm/components/ReportBuilder/util.d.ts.map +1 -1
- package/dist/esm/components/ReportBuilder/util.js +1 -1
- package/dist/esm/components/UiComponents.d.ts +3 -2
- package/dist/esm/components/UiComponents.d.ts.map +1 -1
- package/dist/esm/components/UiComponents.js +13 -13
- package/dist/esm/hooks/useAskQuill.d.ts +1 -1
- package/dist/esm/hooks/useAskQuill.d.ts.map +1 -1
- package/dist/esm/hooks/useAskQuill.js +14 -12
- package/dist/esm/hooks/useDashboard.d.ts +5 -3
- package/dist/esm/hooks/useDashboard.d.ts.map +1 -1
- package/dist/esm/hooks/useDashboard.js +8 -6
- package/dist/esm/hooks/useOnClickOutside.d.ts +1 -0
- package/dist/esm/hooks/useOnClickOutside.d.ts.map +1 -1
- package/dist/esm/hooks/useOnClickOutside.js +31 -0
- package/dist/esm/internals/ReportBuilder/PivotForm.d.ts +2 -1
- package/dist/esm/internals/ReportBuilder/PivotForm.d.ts.map +1 -1
- package/dist/esm/internals/ReportBuilder/PivotForm.js +23 -16
- package/dist/esm/internals/ReportBuilder/PivotModal.d.ts +7 -5
- package/dist/esm/internals/ReportBuilder/PivotModal.d.ts.map +1 -1
- package/dist/esm/internals/ReportBuilder/PivotModal.js +101 -49
- package/dist/esm/models/Client.d.ts +4 -0
- package/dist/esm/models/Client.d.ts.map +1 -1
- package/dist/esm/models/Dashboard.d.ts +1 -0
- package/dist/esm/models/Dashboard.d.ts.map +1 -1
- package/dist/esm/models/Report.d.ts +2 -0
- package/dist/esm/models/Report.d.ts.map +1 -1
- package/dist/esm/models/ReportBuilder.d.ts +46 -0
- package/dist/esm/models/ReportBuilder.d.ts.map +1 -0
- package/dist/esm/models/ReportBuilder.js +16 -0
- package/dist/esm/models/Tables.d.ts +5 -5
- package/dist/esm/models/Tables.d.ts.map +1 -1
- package/dist/esm/utils/astFilterProcessing.d.ts +4 -0
- package/dist/esm/utils/astFilterProcessing.d.ts.map +1 -1
- package/dist/esm/utils/astFilterProcessing.js +300 -97
- package/dist/esm/utils/astProcessing.d.ts +14 -4
- package/dist/esm/utils/astProcessing.d.ts.map +1 -1
- package/dist/esm/utils/astProcessing.js +38 -5
- package/dist/esm/utils/dashboard.d.ts.map +1 -1
- package/dist/esm/utils/dashboard.js +18 -3
- package/dist/esm/utils/dataFetcher.d.ts.map +1 -1
- package/dist/esm/utils/dataFetcher.js +4 -3
- package/dist/esm/utils/filterProcessing.d.ts +2 -11
- package/dist/esm/utils/filterProcessing.d.ts.map +1 -1
- package/dist/esm/utils/filterProcessing.js +4 -15
- package/dist/esm/utils/pivotConstructor.d.ts +2 -1
- package/dist/esm/utils/pivotConstructor.d.ts.map +1 -1
- package/dist/esm/utils/pivotConstructor.js +4 -2
- package/dist/esm/utils/pivotProcessing.d.ts +17 -7
- package/dist/esm/utils/pivotProcessing.d.ts.map +1 -1
- package/dist/esm/utils/pivotProcessing.js +58 -49
- package/dist/esm/utils/queryConstructor.d.ts.map +1 -1
- package/dist/esm/utils/queryConstructor.js +15 -2
- package/dist/esm/utils/report.d.ts +12 -9
- package/dist/esm/utils/report.d.ts.map +1 -1
- package/dist/esm/utils/report.js +116 -46
- package/dist/esm/utils/reportBuilder.d.ts +88 -0
- package/dist/esm/utils/reportBuilder.d.ts.map +1 -0
- package/dist/esm/utils/reportBuilder.js +386 -0
- package/dist/esm/utils/schema.d.ts +7 -0
- package/dist/esm/utils/schema.d.ts.map +1 -1
- package/dist/esm/utils/schema.js +34 -1
- package/dist/esm/utils/tableProcessing.d.ts +59 -29
- package/dist/esm/utils/tableProcessing.d.ts.map +1 -1
- package/dist/esm/utils/tableProcessing.js +71 -86
- package/dist/esm/utils/ui.d.ts +2 -0
- package/dist/esm/utils/ui.d.ts.map +1 -0
- package/dist/esm/utils/ui.js +14 -0
- package/package.json +5 -2
|
@@ -1,46 +1,36 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import { MemoizedButton, MemoizedDeleteButton, MemoizedHeader, MemoizedLabel, MemoizedSecondaryButton, MemoizedText, MemoizedPopover, QuillTabs, MemoizedModal, QuillChartBuilderInputRowContainer, QuillChartBuilderInputColumnContainer, MemoizedSubHeader, QuillErrorMessageComponent, QuillPivotRowContainer, QuillPivotColumnContainer, QuillColumnSearchEmptyState, QuillChartBuilderFormContainer, QuillLoadingComponent, QuillTableReportBuilderComponent, QuillChartBuilderCheckboxComponent, } from './components/UiComponents';
|
|
4
|
-
import {
|
|
5
|
-
import { arrayMove, SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy, useSortable, } from '@dnd-kit/sortable';
|
|
6
|
-
import { CSS as DND_CSS } from '@dnd-kit/utilities';
|
|
7
|
-
import { ClientContext, DashboardContext, DashboardFiltersContext, SchemaDataContext, TenantContext, ThemeContext, } from './Context';
|
|
8
|
-
import { getTableNames, isNumericColumnType, } from './components/ReportBuilder/ast';
|
|
2
|
+
import { useContext, useEffect, useMemo, useRef, useState, } from 'react';
|
|
3
|
+
import { MemoizedButton, MemoizedDeleteButton, MemoizedHeader, MemoizedLabel, MemoizedSecondaryButton, MemoizedText, MemoizedPopover, QuillTabs, MemoizedModal, QuillChartBuilderInputRowContainer, QuillChartBuilderInputColumnContainer, MemoizedSubHeader, QuillErrorMessageComponent, QuillPivotRowContainer, QuillPivotColumnContainer, QuillColumnSearchEmptyState, QuillChartBuilderFormContainer, QuillLoadingComponent, QuillTableReportBuilderComponent, QuillChartBuilderCheckboxComponent, QuillToolTip, } from './components/UiComponents';
|
|
4
|
+
import { ClientContext, DashboardContext, SchemaDataContext, TenantContext, ThemeContext, } from './Context';
|
|
9
5
|
import { ChartBuilderWithModal } from './ChartBuilder';
|
|
10
6
|
import { QuillTextInput } from './components/UiComponents';
|
|
11
7
|
import { QuillSidebar, CustomContainer, QuillSelectColumn, QuillDraggableColumn, QuillSidebarHeading, QuillFilterPopover, QuillSortPopover, QuillLimitPopover, } from './components/ReportBuilder/ui';
|
|
12
|
-
import { deepCopy } from './components/ReportBuilder/util';
|
|
13
|
-
import { hashCode } from './utils/crypto';
|
|
14
|
-
import { defaultAST, defaultColumn, defaultEntry, defaultNumericComparison, defaultTable, } from './components/ReportBuilder/constants';
|
|
15
|
-
import AddColumnModal from './components/ReportBuilder/AddColumnModal';
|
|
16
8
|
import { AddSortPopover, SortSentence, } from './components/ReportBuilder/AddSortPopover';
|
|
17
|
-
import { PivotModal
|
|
9
|
+
import { PivotModal } from './internals/ReportBuilder/PivotModal';
|
|
18
10
|
import { snakeAndCamelCaseToTitleCase } from './utils/textProcessing';
|
|
19
11
|
import { AddLimitPopover, LimitSentence, } from './components/ReportBuilder/AddLimitPopover';
|
|
20
12
|
import { updateFirstChildWidth } from './utils/width';
|
|
21
13
|
import { QuillSelectComponent } from './components/QuillSelect';
|
|
22
14
|
import { QuillCard } from './components/QuillCard';
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import { createBasicSelectASTFromColumns, fetchAndProcessASTFromPrompt, fetchASTFromQuillReport, } from './utils/astProcessing';
|
|
15
|
+
import { isValidPivot, pivotFormData, } from './utils/pivotProcessing';
|
|
16
|
+
import { fetchResultsByQuery, fetchTableByAST, getUniqueStringValuesByTable, } from './utils/tableProcessing';
|
|
17
|
+
import { createSelectStarFromAst, fetchAndProcessASTFromPrompt, fetchASTFromQuillReport, } from './utils/astProcessing';
|
|
27
18
|
import PivotForm from './internals/ReportBuilder/PivotForm';
|
|
28
|
-
import { getDateBucketFromRange, getDateFormatFromBucket } from './utils/dates';
|
|
29
19
|
import FilterModal from './components/ReportBuilder/FilterModal';
|
|
30
|
-
import { astToFilterTree, filterTreeToAst, getFieldFromExpression, } from './utils/astFilterProcessing';
|
|
31
|
-
import useAstToFilterTree from './hooks/useAstToFilterTree';
|
|
32
|
-
import { filterStackToFilterTree, uniqueValuesToStringMap, } from './utils/filterProcessing';
|
|
33
20
|
import { QuillMultiSelectComponentWithCombo } from './components/QuillMultiSelectWithCombo';
|
|
34
21
|
import { DEFAULT_PAGINATION, shouldFetchMore, } from './utils/paginationProcessing';
|
|
35
22
|
import { EMPTY_INTERNAL_REPORT, fetchReportBuilderDataFromAST, formatRowsFromReport, } from './utils/report';
|
|
36
23
|
import { TEMP_REPORT_ID, } from './models/Report';
|
|
37
|
-
import equal from 'fast-deep-equal';
|
|
38
24
|
import FilterStack from './components/ReportBuilder/FilterStack';
|
|
39
|
-
import {
|
|
25
|
+
import { SINGLE_TENANT } from './utils/constants';
|
|
40
26
|
import { convertQueryToSelectStar } from './components/ReportBuilder/convert';
|
|
41
|
-
import { disambiguatedValueField } from './utils/pivotConstructor';
|
|
42
|
-
import { isDateType, isNumberType } from './utils/columnProcessing';
|
|
43
27
|
import { cleanDashboardItem } from './utils/dashboard';
|
|
28
|
+
import { useDashboards } from './hooks/useDashboard';
|
|
29
|
+
import { EMPTY_REPORT_BUILDER_STATE, } from './models/ReportBuilder';
|
|
30
|
+
import DraggableColumns from './components/ReportBuilder/DraggableColumns';
|
|
31
|
+
import NewAddColumnModal from './components/ReportBuilder/AddColumnModal';
|
|
32
|
+
import { astToReportBuilderState, formatRows, isValidPivotForReport, reportBuilderStateToAst, setTypesOnPivot, } from './utils/reportBuilder';
|
|
33
|
+
import { fetchSqlQuery } from './utils/dataFetcher';
|
|
44
34
|
/**
|
|
45
35
|
* Quill Report Builder
|
|
46
36
|
*
|
|
@@ -73,173 +63,369 @@ import { cleanDashboardItem } from './utils/dashboard';
|
|
|
73
63
|
* @see https://docs.quillsql.com/components/report-builder
|
|
74
64
|
*/
|
|
75
65
|
export default function ReportBuilder({ initialTableName = '', onSubmitEditReport = () => void null, onSubmitCreateReport = () => void null, onSubmitSaveQuery = () => void null, onDiscardChanges = undefined, onSaveChanges = undefined, onCloseChartBuilder = undefined, destinationDashboard, chartBuilderTitle = undefined, organizationName = '', ButtonComponent = MemoizedButton, SecondaryButtonComponent = MemoizedSecondaryButton, DeleteButtonComponent = MemoizedDeleteButton, ModalComponent = MemoizedModal, TextInputComponent = QuillTextInput, SelectComponent = QuillSelectComponent, MultiSelectComponent = QuillMultiSelectComponentWithCombo, TableComponent = QuillTableReportBuilderComponent, PopoverComponent = MemoizedPopover, TabsComponent = QuillTabs, CheckboxComponent = QuillChartBuilderCheckboxComponent, SidebarComponent = QuillSidebar, ContainerComponent = CustomContainer, SelectColumnComponent = QuillSelectColumn, DraggableColumnComponent = QuillDraggableColumn, SidebarHeadingComponent = QuillSidebarHeading, FilterPopoverComponent = QuillFilterPopover, SortPopoverComponent = QuillSortPopover, LimitPopoverComponent = QuillLimitPopover, CardComponent = QuillCard, LabelComponent = MemoizedLabel, HeaderComponent = MemoizedHeader, SubHeaderComponent = MemoizedSubHeader, TextComponent = MemoizedText, ErrorMessageComponent = QuillErrorMessageComponent, ChartBuilderInputRowContainer = QuillChartBuilderInputRowContainer, ChartBuilderInputColumnContainer = QuillChartBuilderInputColumnContainer, PivotRowContainer = QuillPivotRowContainer, PivotColumnContainer = QuillPivotColumnContainer, LoadingComponent = QuillLoadingComponent, ColumnSearchEmptyState = QuillColumnSearchEmptyState, ChartBuilderFormContainer = QuillChartBuilderFormContainer, ChartBuilderModalComponent = MemoizedModal, isAdminEnabled = false, isAIEnabled = true, containerStyle, className, pivotRecommendationsEnabled = true, reportId, hideCopySQL = true, isChartBuilderHorizontalView = true, onClickChartElement, }) {
|
|
66
|
+
/**
|
|
67
|
+
* The state of the ReportBuilder is based off of an AST, which is a representation of a sql query
|
|
68
|
+
* ASTs for the same query can vary between database types, so we create a layer of abstraction on top of it
|
|
69
|
+
* An AST consists of these main parts:
|
|
70
|
+
* - from: the table(s) that the query is pulling data from
|
|
71
|
+
* - columns: the columns that the query is selecting
|
|
72
|
+
* - where: the conditions that the query is filtering by (filters)
|
|
73
|
+
* - groupby: the columns that the query is grouping by (these are represented by pivots)
|
|
74
|
+
* - orderby: the columns that the query is ordering by (sort)
|
|
75
|
+
* - limit: the number of rows that the query is limiting to (limit)
|
|
76
|
+
*
|
|
77
|
+
* The ReportBuilder maintains the state of these parts of the AST and assembles the AST when required
|
|
78
|
+
*
|
|
79
|
+
* User Interactions
|
|
80
|
+
* - Add Tables
|
|
81
|
+
* - Add Columns
|
|
82
|
+
* - Add Filters
|
|
83
|
+
* - Add Pivot
|
|
84
|
+
* - Add Sort
|
|
85
|
+
* - Add Limit
|
|
86
|
+
*
|
|
87
|
+
* - Two modes
|
|
88
|
+
* - Automatic: Run Query after every change
|
|
89
|
+
* - Manual: Run Query after user clicks "Run Query"
|
|
90
|
+
* */
|
|
91
|
+
// Notable ReportBuilder props
|
|
92
|
+
// intitialTableName: the table that the ReportBuilder will start with
|
|
93
|
+
// isAdminEnabled: whether the ReportBuilder is in admin mode
|
|
94
|
+
// isAIEnabled: whether the ReportBuilder's AI features are enabled
|
|
95
|
+
// pivotRecommendationsEnabled: whether the PivotModal's AI features are enabled
|
|
96
|
+
// reportId: a report id that the Report Builder will query from and modify
|
|
97
|
+
// onSubmitCreateReport: a callback function that will trigger when a new chart is saved
|
|
98
|
+
// onSubmitEditReport: a callback function that will trigger when a chart is edited
|
|
99
|
+
// onDiscardChanges: a callback function that will trigger when changes are discarded
|
|
100
|
+
// onCloseChartBuilder: a callback function that will trigger when the chart builder is closed
|
|
101
|
+
// hideCopySQL: hide the copy SQL button
|
|
102
|
+
// isChartBuilderHorizontalView: whether the chart builder is in horizontal view mode
|
|
103
|
+
// onClickChartElement: a callback function triggered when a chart element is clicked
|
|
104
|
+
// Contexts
|
|
76
105
|
const [dashboard] = useContext(DashboardContext);
|
|
77
106
|
const [schemaData] = useContext(SchemaDataContext);
|
|
78
|
-
const {
|
|
107
|
+
const { dashboards } = useDashboards();
|
|
108
|
+
const destinationDashboardConfig = useMemo(() => {
|
|
109
|
+
return dashboards?.find((d) => d.name === destinationDashboard);
|
|
110
|
+
}, [dashboards, destinationDashboard]);
|
|
111
|
+
const filteredSchema = useMemo(() => {
|
|
112
|
+
return schemaData.schemaWithCustomFields?.filter((table) => {
|
|
113
|
+
return (destinationDashboardConfig?.tenantKeys?.[0] === SINGLE_TENANT ||
|
|
114
|
+
table.ownerTenantFields?.length === 0 ||
|
|
115
|
+
table.ownerTenantFields?.includes(destinationDashboardConfig?.tenantKeys?.[0] ?? ''));
|
|
116
|
+
});
|
|
117
|
+
}, [schemaData.schemaWithCustomFields, destinationDashboardConfig?.tenantKeys]);
|
|
79
118
|
const { tenants } = useContext(TenantContext);
|
|
80
|
-
const
|
|
81
|
-
if (!reportId)
|
|
82
|
-
return [];
|
|
83
|
-
const dashboardName = dashboard[reportId]?.dashboardName;
|
|
84
|
-
if (!dashboardName)
|
|
85
|
-
return [];
|
|
86
|
-
return Object.values(dashboardFilters[dashboardName] ?? {}).map((f) => f.filter);
|
|
87
|
-
}, [dashboardFilters, reportId, dashboard]);
|
|
119
|
+
const [theme] = useContext(ThemeContext);
|
|
88
120
|
const [client] = useContext(ClientContext);
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
const [baseAst, setBaseAst] = useState(null);
|
|
93
|
-
const [formData, setFormData] = useState(null);
|
|
94
|
-
const [orderedColumnNames, setOrderedColumnNames] = useState([]);
|
|
95
|
-
const [selectedColumns, setSelectedColumns] = useState([]);
|
|
96
|
-
const [selectedOrderedColumns, setSelectedOrderedColumns] = useState([]);
|
|
97
|
-
const [isSaveQueryModalOpen, setIsSaveQueryModalOpen] = useState(false);
|
|
98
|
-
const [activeQuery, setActiveQuery] = useState('');
|
|
99
|
-
const [, setActiveEditItem] = useState(null);
|
|
100
|
-
const [openPopover, setOpenPopover] = useState(null);
|
|
101
|
-
const [loading, setLoading] = useState(!!initialTableName);
|
|
102
|
-
const [isChartBuilderOpen, setIsChartBuilderOpen] = useState(false);
|
|
103
|
-
const [isCopying, setIsCopying] = useState(false);
|
|
104
|
-
const [dataDisplayed, setDataDisplayed] = useState(false);
|
|
105
|
-
const [rows, setRows] = useState([]);
|
|
106
|
-
const [formattedRows, setFormattedRows] = useState([]);
|
|
107
|
-
const [columns, setColumns] = useState([]);
|
|
121
|
+
// Refs
|
|
122
|
+
const parentRef = useRef(null);
|
|
123
|
+
// Consts
|
|
108
124
|
const REPORT_BUILDER_PAGINATION = {
|
|
109
125
|
page: 0,
|
|
110
126
|
rowsPerPage: 20,
|
|
111
127
|
rowsPerRequest: 100,
|
|
112
128
|
};
|
|
129
|
+
// ReportBuilder UI States
|
|
130
|
+
const [isChartBuilderOpen, setIsChartBuilderOpen] = useState(false);
|
|
131
|
+
const [filtersEnabled, setFiltersEnabled] = useState(!!reportId);
|
|
132
|
+
const [isSaveQueryModalOpen, setIsSaveQueryModalOpen] = useState(false);
|
|
133
|
+
const [openPopover, setOpenPopover] = useState(null);
|
|
134
|
+
const [aiPrompt, setAiPrompt] = useState('');
|
|
135
|
+
const [reportBuilderLoading, setReportBuilderLoading] = useState(false);
|
|
136
|
+
const [tableLoading, setTableLoading] = useState(false);
|
|
137
|
+
const [errorMessage, setErrorMessage] = useState('');
|
|
138
|
+
const [unresolvedReportMessage, setUnresolvedReportMessage] = useState('');
|
|
139
|
+
// Core Report states
|
|
140
|
+
const [tables, setTables] = useState([]);
|
|
141
|
+
const [columns, setColumns] = useState([]);
|
|
142
|
+
const [filterStack, setFilterStack] = useState([]);
|
|
143
|
+
const [pivot, setPivot] = useState(null);
|
|
144
|
+
const [sort, setSort] = useState([]);
|
|
145
|
+
const [limit, setLimit] = useState(null);
|
|
146
|
+
const reportBuilderState = useMemo(() => {
|
|
147
|
+
return {
|
|
148
|
+
tables,
|
|
149
|
+
columns,
|
|
150
|
+
filterStack,
|
|
151
|
+
pivot,
|
|
152
|
+
sort,
|
|
153
|
+
limit,
|
|
154
|
+
};
|
|
155
|
+
}, [columns, filterStack, limit, pivot, sort, tables]);
|
|
156
|
+
const undoButtonEnabled = false; // TODO: enable
|
|
157
|
+
const [stateStack, setStateStack] = useState([]);
|
|
158
|
+
// Other Report states
|
|
159
|
+
const [activeQuery, setActiveQuery] = useState('');
|
|
160
|
+
const [unfilteredUniqueValues, setUnfilteredUniqueValues] = useState({}); // unique values before filtering
|
|
161
|
+
const [unfilteredUniqueValuesIsLoading, setUnfilteredUniqueValuesIsLoading] = useState(false);
|
|
162
|
+
const [filteredUniqueValues, setFilteredUniqueValues] = useState(null); // unique values after filtering
|
|
163
|
+
const [filteredUniqueValuesIsLoading, setFilteredUniqueValuesIsLoading] = useState(false);
|
|
164
|
+
const [columnUniqueValues, setColumnUniqueValues] = useState({});
|
|
165
|
+
const [dateRanges, setDateRanges] = useState(null);
|
|
113
166
|
const [tempReport, setTempReport] = useState({
|
|
114
167
|
...EMPTY_INTERNAL_REPORT,
|
|
115
168
|
pagination: REPORT_BUILDER_PAGINATION,
|
|
116
169
|
});
|
|
117
|
-
const [
|
|
118
|
-
|
|
119
|
-
|
|
170
|
+
const [currentProcessing, setCurrentProcessing] = useState({
|
|
171
|
+
page: REPORT_BUILDER_PAGINATION,
|
|
172
|
+
});
|
|
173
|
+
const [previousPage, setPreviousPage] = useState(0);
|
|
174
|
+
const [isCopying, setIsCopying] = useState(false);
|
|
175
|
+
const [sortOrLimitWasReset, setSortOrLimitWasReset] = useState(false);
|
|
176
|
+
// Table display states
|
|
177
|
+
const [reportColumns, setReportColumns] = useState([]);
|
|
178
|
+
const [reportRows, setReportRows] = useState([]);
|
|
179
|
+
const [formattedRows, setFormattedRows] = useState([]);
|
|
120
180
|
const [pivotData, setPivotData] = useState(null);
|
|
181
|
+
const [numberOfRows, setNumberOfRows] = useState(0);
|
|
182
|
+
const [rowCountIsLoading, setRowCountIsLoading] = useState(false);
|
|
183
|
+
const reportColumnsToStateColumns = (() => {
|
|
184
|
+
const positionMap = {};
|
|
185
|
+
columns.forEach((column, index) => {
|
|
186
|
+
positionMap[column.field] = index;
|
|
187
|
+
if (column.alias) {
|
|
188
|
+
positionMap[column.alias] = index;
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
// Sort reportColumns based on the position in columns
|
|
192
|
+
return [...reportColumns]
|
|
193
|
+
.filter((reportColumn) => positionMap[reportColumn.field] !== undefined)
|
|
194
|
+
.sort((a, b) => {
|
|
195
|
+
const posA = positionMap[a.field];
|
|
196
|
+
const posB = positionMap[b.field];
|
|
197
|
+
return posA && posB ? posA - posB : 0;
|
|
198
|
+
});
|
|
199
|
+
})();
|
|
200
|
+
// Pivot form states
|
|
201
|
+
const [pivotRowField, setPivotRowField] = useState(undefined);
|
|
202
|
+
const [pivotColumnField, setPivotColumnField] = useState(undefined);
|
|
203
|
+
const [pivotAggregations, setPivotAggregations] = useState([]);
|
|
204
|
+
const [pivotHint, setPivotHint] = useState('');
|
|
205
|
+
const [pivotError, setPivotError] = useState('');
|
|
121
206
|
const [createdPivots, setCreatedPivots] = useState([]);
|
|
122
207
|
const [recommendedPivots, setRecommendedPivots] = useState([]);
|
|
123
208
|
const [pivotPopUpTitle, setPivotPopUpTitle] = useState('Add pivot');
|
|
124
209
|
const [showPivotPopover, setShowPivotPopover] = useState(false);
|
|
125
210
|
const [isEditingPivot, setIsEditingPivot] = useState(false);
|
|
126
|
-
const [askedAQuestion, setAskedAQuestion] = useState(false);
|
|
127
|
-
const [askAILoading, setAskAILoading] = useState(false);
|
|
128
211
|
const [selectedPivotIndex, setSelectedPivotIndex] = useState(-1);
|
|
129
|
-
const [
|
|
130
|
-
|
|
131
|
-
const
|
|
212
|
+
const [pivotRecommendationsEnabledState, setPivotRecommendationsEnabledState,] = useState(pivotRecommendationsEnabled);
|
|
213
|
+
// Ask AI
|
|
214
|
+
const [askAILoading, setAskAILoading] = useState(false);
|
|
132
215
|
const askAIContainerRef = useRef(null);
|
|
133
|
-
const askAILoadingContainerRef = useRef(null);
|
|
134
216
|
const [askAIInputWidth, setAskAIInputWidth] = useState(-1);
|
|
135
|
-
const
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
]);
|
|
146
|
-
const [pivotHint, setPivotHint] = useState('');
|
|
147
|
-
/* eslint-disable-next-line */
|
|
148
|
-
const [pivotError, setPivotError] = useState(undefined);
|
|
149
|
-
const [dateRanges, setDateRanges] = useState(null);
|
|
150
|
-
const [fieldValuesMap, setFieldValuesMap] = useState({}); // Mapping of unique values per field, used in string filter 'in' and 'not in'
|
|
151
|
-
const [globalUniqueValues, setGlobalUniqueValues] = useState({});
|
|
152
|
-
const [globalUniqueValuesIsLoading, setGlobalUniqueValuesIsLoading] = useState(false);
|
|
153
|
-
const { filterTree, filterStack } = useAstToFilterTree(formData, client, columns); // Stores the state of filters
|
|
154
|
-
const [removingFilter, setRemovingFilter] = useState(false);
|
|
155
|
-
const [pivotRecommendationsEnabledState, setPivotRecommendationsEnabledState,] = useState(pivotRecommendationsEnabled);
|
|
156
|
-
const [unresolvedReportMessage, setUnresolvedReportMessage] = useState('');
|
|
157
|
-
const dashboardName = useMemo(() => {
|
|
158
|
-
if (destinationDashboard) {
|
|
159
|
-
return destinationDashboard;
|
|
217
|
+
const loading = reportBuilderLoading || tableLoading;
|
|
218
|
+
const isSelectStar = useMemo(() => {
|
|
219
|
+
if (tables.length === 1) {
|
|
220
|
+
// Check if all columns are selected
|
|
221
|
+
const totalColumnLength = tables.reduce((acc, table) => {
|
|
222
|
+
const tableColumns = filteredSchema.find((t) => t.name === table.name)
|
|
223
|
+
?.columns.length ?? 0;
|
|
224
|
+
return acc + tableColumns;
|
|
225
|
+
}, 0);
|
|
226
|
+
return totalColumnLength === columns.length;
|
|
160
227
|
}
|
|
161
|
-
|
|
162
|
-
|
|
228
|
+
else {
|
|
229
|
+
// TODO: Implement this to work with joins
|
|
230
|
+
// SELECT * won't work if joined table has shared column name
|
|
231
|
+
return false;
|
|
163
232
|
}
|
|
164
|
-
|
|
165
|
-
}, [reportId, dashboard, destinationDashboard]);
|
|
166
|
-
const [filtersEnabled, setFiltersEnabled] = useState(!!reportId);
|
|
233
|
+
}, [tables, columns, filteredSchema]);
|
|
167
234
|
const mssqlSortWarning = useMemo(() => {
|
|
168
235
|
if (!client || client?.databaseType !== 'mssql') {
|
|
169
236
|
return undefined;
|
|
170
237
|
}
|
|
171
|
-
else if (!pivot && !
|
|
238
|
+
else if (!pivot && !limit) {
|
|
172
239
|
return 'Please add a limit.';
|
|
173
240
|
}
|
|
174
|
-
}, [client,
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
241
|
+
}, [client, limit, pivot]);
|
|
242
|
+
// State changing functions
|
|
243
|
+
const clearAllState = () => {
|
|
244
|
+
setActiveQuery('');
|
|
245
|
+
handleMultiStateChange(EMPTY_REPORT_BUILDER_STATE);
|
|
246
|
+
setStateStack([]);
|
|
247
|
+
setFilteredUniqueValues(null);
|
|
248
|
+
setUnfilteredUniqueValues({});
|
|
249
|
+
setColumnUniqueValues({});
|
|
250
|
+
setDateRanges(null);
|
|
251
|
+
setTempReport({
|
|
252
|
+
...EMPTY_INTERNAL_REPORT,
|
|
253
|
+
pagination: REPORT_BUILDER_PAGINATION,
|
|
254
|
+
});
|
|
255
|
+
resetProcessing();
|
|
256
|
+
setReportColumns([]);
|
|
257
|
+
setReportRows([]);
|
|
258
|
+
setFormattedRows([]);
|
|
259
|
+
setPivotData(null);
|
|
260
|
+
setNumberOfRows(0);
|
|
261
|
+
setRowCountIsLoading(false);
|
|
262
|
+
setPivotRowField(undefined);
|
|
263
|
+
setPivotColumnField(undefined);
|
|
264
|
+
setPivotAggregations([]);
|
|
265
|
+
setCreatedPivots([]);
|
|
266
|
+
setRecommendedPivots([]);
|
|
267
|
+
setSelectedPivotIndex(-1);
|
|
268
|
+
setAskAILoading(false);
|
|
269
|
+
setReportBuilderLoading(false);
|
|
270
|
+
setTableLoading(false);
|
|
271
|
+
setPivotError('');
|
|
272
|
+
setErrorMessage('');
|
|
273
|
+
setUnresolvedReportMessage('');
|
|
274
|
+
setPivotHint('');
|
|
275
|
+
};
|
|
276
|
+
const copySQLToClipboard = () => {
|
|
277
|
+
const query = pivot && pivotData ? pivotData.pivotQuery : activeQuery;
|
|
278
|
+
setIsCopying(true);
|
|
279
|
+
navigator.clipboard.writeText(query);
|
|
280
|
+
setTimeout(() => setIsCopying(false), 800);
|
|
281
|
+
};
|
|
282
|
+
const handleTablesChange = (newTables, updateStateStack = true) => {
|
|
283
|
+
setTables(newTables);
|
|
284
|
+
if (updateStateStack) {
|
|
285
|
+
setStateStack((prevStack) => [
|
|
286
|
+
...prevStack,
|
|
287
|
+
{ ...reportBuilderState, tables: newTables },
|
|
288
|
+
]);
|
|
178
289
|
}
|
|
179
|
-
|
|
180
|
-
|
|
290
|
+
};
|
|
291
|
+
const handleColumnsChange = (newColumns, fetchData, updateStateStack = true) => {
|
|
292
|
+
// If pivot is using removed columns, remove pivot
|
|
293
|
+
let removePivot = false;
|
|
294
|
+
if (pivot) {
|
|
295
|
+
const referencedColumns = [];
|
|
296
|
+
if (pivot.rowField) {
|
|
297
|
+
referencedColumns.push(pivot.rowField);
|
|
298
|
+
}
|
|
299
|
+
if (pivot.columnField) {
|
|
300
|
+
referencedColumns.push(pivot.columnField);
|
|
301
|
+
}
|
|
302
|
+
if (pivot.aggregations) {
|
|
303
|
+
pivot.aggregations.forEach((agg) => {
|
|
304
|
+
if (agg.valueField) {
|
|
305
|
+
referencedColumns.push(agg.valueField);
|
|
306
|
+
}
|
|
307
|
+
if (agg.valueField2) {
|
|
308
|
+
referencedColumns.push(agg.valueField2);
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
const columnMapping = referencedColumns
|
|
313
|
+
.map((col) => {
|
|
314
|
+
const column = columns.find((c) => (c.alias || c.field) === col);
|
|
315
|
+
return column;
|
|
316
|
+
})
|
|
317
|
+
.filter((c) => c !== undefined);
|
|
318
|
+
removePivot = !columnMapping.every((col) => newColumns.some((c) => (c.alias || c.field) === (col.alias || col.field) &&
|
|
319
|
+
c.table === col.table));
|
|
320
|
+
if (removePivot) {
|
|
321
|
+
handlePivotChange(null);
|
|
322
|
+
}
|
|
181
323
|
}
|
|
182
|
-
|
|
183
|
-
|
|
324
|
+
setColumns(newColumns);
|
|
325
|
+
if (updateStateStack) {
|
|
326
|
+
setStateStack((prevStack) => [
|
|
327
|
+
...prevStack,
|
|
328
|
+
{ ...reportBuilderState, columns: newColumns },
|
|
329
|
+
]);
|
|
184
330
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
if (isChartBuilderOpen === false) {
|
|
192
|
-
onCloseChartBuilder && onCloseChartBuilder();
|
|
331
|
+
if (fetchData) {
|
|
332
|
+
fetchDataFromReportBuilderState({
|
|
333
|
+
...reportBuilderState,
|
|
334
|
+
columns: newColumns,
|
|
335
|
+
pivot: removePivot ? null : pivot,
|
|
336
|
+
});
|
|
193
337
|
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
338
|
+
};
|
|
339
|
+
const handleFilterInsertion = (newFilter) => {
|
|
340
|
+
const newFilterStack = [...filterStack];
|
|
341
|
+
if (newFilterStack.length > 0) {
|
|
342
|
+
const tabNode = {
|
|
343
|
+
leaf: false,
|
|
344
|
+
operator: 'and',
|
|
345
|
+
leftNode: null,
|
|
346
|
+
rightNode: null,
|
|
347
|
+
};
|
|
348
|
+
newFilterStack.push(tabNode);
|
|
349
|
+
}
|
|
350
|
+
const newItem = {
|
|
351
|
+
leaf: true,
|
|
352
|
+
operator: null,
|
|
353
|
+
leftNode: null,
|
|
354
|
+
rightNode: null,
|
|
355
|
+
value: newFilter,
|
|
356
|
+
};
|
|
357
|
+
newFilterStack.push(newItem);
|
|
358
|
+
handleFilterStackChange(newFilterStack, true);
|
|
359
|
+
};
|
|
360
|
+
const handleFilterStackChange = (newFilterStack, fetchData, updateStateStack = true) => {
|
|
361
|
+
setFilterStack(newFilterStack);
|
|
362
|
+
if (newFilterStack.length === 0) {
|
|
363
|
+
setFilteredUniqueValues(null);
|
|
364
|
+
}
|
|
365
|
+
if (updateStateStack) {
|
|
366
|
+
setStateStack((prevStack) => [
|
|
367
|
+
...prevStack,
|
|
368
|
+
{ ...reportBuilderState, filterStack: newFilterStack },
|
|
369
|
+
]);
|
|
370
|
+
}
|
|
371
|
+
if (fetchData) {
|
|
372
|
+
fetchDataFromReportBuilderState({
|
|
373
|
+
...reportBuilderState,
|
|
374
|
+
filterStack: newFilterStack,
|
|
375
|
+
}, true);
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
const handlePivotChange = (newPivot, fetchData, updateStateStack = true) => {
|
|
379
|
+
setPivot(newPivot ? setTypesOnPivot(newPivot, reportColumns) : null);
|
|
380
|
+
setPivotHint('');
|
|
381
|
+
setPivotError('');
|
|
382
|
+
if (!newPivot) {
|
|
383
|
+
setPivotData(null);
|
|
384
|
+
if (!fetchData) {
|
|
385
|
+
const formattedRows = formatRows(reportRows, reportColumns, false);
|
|
386
|
+
setFormattedRows(formattedRows);
|
|
204
387
|
}
|
|
205
388
|
}
|
|
206
|
-
|
|
389
|
+
let resetSortAndLimit = false;
|
|
390
|
+
if (!pivot && newPivot && (sort.length > 0 || limit)) {
|
|
391
|
+
setSort([]);
|
|
392
|
+
setLimit(null);
|
|
393
|
+
resetSortAndLimit = true;
|
|
394
|
+
}
|
|
395
|
+
setSortOrLimitWasReset(resetSortAndLimit);
|
|
396
|
+
if (updateStateStack) {
|
|
397
|
+
setStateStack((prevStack) => [
|
|
398
|
+
...prevStack,
|
|
399
|
+
{
|
|
400
|
+
...reportBuilderState,
|
|
401
|
+
pivot: newPivot,
|
|
402
|
+
sort: resetSortAndLimit ? [] : sort,
|
|
403
|
+
limit: resetSortAndLimit ? null : limit,
|
|
404
|
+
},
|
|
405
|
+
]);
|
|
406
|
+
}
|
|
407
|
+
if (fetchData) {
|
|
408
|
+
fetchDataFromReportBuilderState({
|
|
409
|
+
...reportBuilderState,
|
|
410
|
+
pivot: newPivot,
|
|
411
|
+
sort: resetSortAndLimit ? [] : sort,
|
|
412
|
+
limit: resetSortAndLimit ? null : limit,
|
|
413
|
+
});
|
|
414
|
+
}
|
|
207
415
|
};
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
function handleResize() {
|
|
212
|
-
updateFirstChildWidth(askAIContainerRef, setAskAIInputWidth, { gap: 12 });
|
|
213
|
-
updateFirstChildWidth(askAILoadingContainerRef, setAskAILoadingContainerWidth, { gap: 12 });
|
|
416
|
+
const updatePivot = async (changeField, fieldKey) => {
|
|
417
|
+
if (!client || !pivot) {
|
|
418
|
+
return;
|
|
214
419
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
return () => {
|
|
218
|
-
window.removeEventListener('resize', handleResize);
|
|
219
|
-
};
|
|
220
|
-
}, []);
|
|
221
|
-
const setTypesOnPivot = (newPivot, searchColumns) => ({
|
|
222
|
-
...newPivot,
|
|
223
|
-
aggregations: newPivot.aggregations?.map((agg) => ({
|
|
224
|
-
...agg,
|
|
225
|
-
valueFieldType: searchColumns.find((c) => c.field === agg.valueField)
|
|
226
|
-
?.jsType,
|
|
227
|
-
valueField2Type: searchColumns.find((c) => c.field === agg.valueField2)
|
|
228
|
-
?.jsType,
|
|
229
|
-
})),
|
|
230
|
-
valueField: newPivot.aggregations?.[0]?.valueField,
|
|
231
|
-
valueField2: newPivot.aggregations?.[0]?.valueField2,
|
|
232
|
-
aggregationType: newPivot.aggregations?.[0]?.aggregationType,
|
|
233
|
-
valueFieldType: searchColumns.find((c) => c.field === newPivot.aggregations?.[0]?.valueField)?.jsType,
|
|
234
|
-
valueField2Type: searchColumns.find((c) => c.field === newPivot.aggregations?.[0]?.valueField2)?.jsType,
|
|
235
|
-
});
|
|
236
|
-
const updatePivot = async (changeField, fieldKey, prevValue) => {
|
|
237
|
-
let newPivot = deepCopy(pivot);
|
|
238
|
-
setPivotError(undefined);
|
|
420
|
+
let newPivot = { ...pivot };
|
|
421
|
+
setPivotError('');
|
|
239
422
|
// @ts-ignore
|
|
240
423
|
newPivot[fieldKey] = changeField;
|
|
241
424
|
newPivot.rowLimit = undefined;
|
|
242
425
|
newPivot.sort = undefined;
|
|
426
|
+
newPivot.sortDirection = undefined;
|
|
427
|
+
newPivot.sortField = undefined;
|
|
428
|
+
newPivot.sortFieldType = undefined;
|
|
243
429
|
if (fieldKey === 'rowField') {
|
|
244
430
|
if (changeField === '' || changeField === undefined) {
|
|
245
431
|
setPivotColumnField(undefined);
|
|
@@ -251,22 +437,8 @@ export default function ReportBuilder({ initialTableName = '', onSubmitEditRepor
|
|
|
251
437
|
: agg.aggregationType,
|
|
252
438
|
})));
|
|
253
439
|
}
|
|
254
|
-
// check to see if the new rowField value is a date field
|
|
255
|
-
const column = columns.find((c) => c.field === changeField);
|
|
256
|
-
if (column?.jsType === 'date') {
|
|
257
|
-
newPivot.rowFieldType = 'date';
|
|
258
|
-
newPivot.sort = true;
|
|
259
|
-
newPivot.sortField = changeField;
|
|
260
|
-
newPivot.sortFieldType = column.format;
|
|
261
|
-
newPivot.sortDirection = 'ASC';
|
|
262
|
-
}
|
|
263
|
-
else {
|
|
264
|
-
newPivot.rowFieldType = 'string';
|
|
265
|
-
newPivot.sort = undefined;
|
|
266
|
-
}
|
|
267
440
|
}
|
|
268
|
-
newPivot = setTypesOnPivot(newPivot,
|
|
269
|
-
setPivot(setTypesOnPivot(newPivot, columns));
|
|
441
|
+
newPivot = setTypesOnPivot(newPivot, reportColumns);
|
|
270
442
|
if (newPivot.aggregations?.length === 0 ||
|
|
271
443
|
newPivot.aggregations?.some((agg) => !agg.aggregationType) ||
|
|
272
444
|
newPivot.aggregations?.some((agg) => !agg.valueField && agg.aggregationType !== 'count')) {
|
|
@@ -277,793 +449,116 @@ export default function ReportBuilder({ initialTableName = '', onSubmitEditRepor
|
|
|
277
449
|
setPivotError(reason);
|
|
278
450
|
return;
|
|
279
451
|
}
|
|
280
|
-
|
|
281
|
-
resetSort();
|
|
282
|
-
setPreviousPage(0);
|
|
283
|
-
setTableLoading(true);
|
|
284
|
-
let dateBucket = undefined;
|
|
285
|
-
const tempDateRange = dateRanges && dateRanges[newPivot.rowField || ''];
|
|
286
|
-
if (tempDateRange) {
|
|
287
|
-
dateBucket = getDateBucketFromRange(tempDateRange.dateRange);
|
|
288
|
-
}
|
|
289
|
-
let distinctValuesForQuery = {};
|
|
290
|
-
const prevPivot = pivot;
|
|
291
|
-
const prevPivotData = pivotData;
|
|
292
|
-
setPivotHint('');
|
|
293
|
-
if (newPivot.columnField) {
|
|
294
|
-
if (uniqueValues?.[newPivot.columnField]) {
|
|
295
|
-
distinctValuesForQuery = uniqueValues;
|
|
296
|
-
}
|
|
297
|
-
else {
|
|
298
|
-
distinctValuesForQuery = await getUniqueValuesByColumns([
|
|
299
|
-
{
|
|
300
|
-
field: newPivot.columnField,
|
|
301
|
-
label: newPivot.columnField,
|
|
302
|
-
format: 'string',
|
|
303
|
-
},
|
|
304
|
-
], activeQuery, [], client, tenants, schemaData.customFields ?? {}, undefined, dashboardName);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
try {
|
|
308
|
-
const pivotedData = await generatePivotTable({
|
|
309
|
-
pivot: newPivot,
|
|
310
|
-
dateBucket,
|
|
311
|
-
report: tempReport,
|
|
312
|
-
client,
|
|
313
|
-
uniqueValues: distinctValuesForQuery,
|
|
314
|
-
dashboardName,
|
|
315
|
-
tenants,
|
|
316
|
-
additionalProcessing: { page: REPORT_BUILDER_PAGINATION },
|
|
317
|
-
});
|
|
318
|
-
resetProcessing();
|
|
319
|
-
setPivotData(pivotedData || []);
|
|
320
|
-
const formattedRows = formatRows(pivotedData.rows, columns, true, newPivot.aggregationType);
|
|
321
|
-
setFormattedRows(formattedRows);
|
|
322
|
-
}
|
|
323
|
-
catch (e) {
|
|
324
|
-
switch (fieldKey) {
|
|
325
|
-
case 'rowField':
|
|
326
|
-
setPivotRowField(prevValue);
|
|
327
|
-
break;
|
|
328
|
-
case 'columnField':
|
|
329
|
-
setPivotColumnField(prevValue);
|
|
330
|
-
break;
|
|
331
|
-
case 'aggregations':
|
|
332
|
-
setPivotAggregations(prevValue);
|
|
333
|
-
break;
|
|
334
|
-
}
|
|
335
|
-
setPivot(prevPivot);
|
|
336
|
-
setPivotData(prevPivotData);
|
|
337
|
-
if (e instanceof Error)
|
|
338
|
-
setPivotError(e.message);
|
|
339
|
-
}
|
|
340
|
-
finally {
|
|
341
|
-
setTableLoading(false);
|
|
342
|
-
}
|
|
343
|
-
};
|
|
344
|
-
const resetLimit = (fetchData) => {
|
|
345
|
-
const newAst = {
|
|
346
|
-
...baseAst,
|
|
347
|
-
limit: null,
|
|
348
|
-
top: null,
|
|
349
|
-
orderby: client?.databaseType === 'mssql' ? null : baseAst.orderby,
|
|
350
|
-
};
|
|
351
|
-
setBaseAst((prevAst) => ({
|
|
352
|
-
...prevAst,
|
|
353
|
-
limit: null,
|
|
354
|
-
top: null,
|
|
355
|
-
orderby: client?.databaseType === 'mssql' ? null : prevAst.orderby,
|
|
356
|
-
}));
|
|
357
|
-
setPivot((oldPivot) => {
|
|
358
|
-
if (!oldPivot)
|
|
359
|
-
return null;
|
|
360
|
-
return {
|
|
361
|
-
...oldPivot,
|
|
362
|
-
rowLimit: undefined,
|
|
363
|
-
};
|
|
364
|
-
});
|
|
365
|
-
if (fetchData) {
|
|
366
|
-
fetchReportFromASTHelper({
|
|
367
|
-
baseAst: newAst,
|
|
368
|
-
curPivot: pivot ? { ...pivot, rowLimit: undefined } : undefined,
|
|
369
|
-
});
|
|
370
|
-
}
|
|
452
|
+
handlePivotChange(newPivot, true);
|
|
371
453
|
};
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
sort: undefined,
|
|
380
|
-
sortField: undefined,
|
|
381
|
-
sortDirection: undefined,
|
|
382
|
-
sortFieldType: undefined,
|
|
383
|
-
};
|
|
384
|
-
});
|
|
454
|
+
const handleSortChange = (newSort, fetchData, updateStateStack = true) => {
|
|
455
|
+
setSort(newSort);
|
|
456
|
+
if (updateStateStack) {
|
|
457
|
+
setStateStack((prevStack) => [
|
|
458
|
+
...prevStack,
|
|
459
|
+
{ ...reportBuilderState, sort: newSort },
|
|
460
|
+
]);
|
|
385
461
|
}
|
|
386
|
-
const newAst = { ...baseAst, orderby: null };
|
|
387
|
-
setBaseAst((prevAst) => ({ ...prevAst, orderby: null }));
|
|
388
462
|
if (fetchData) {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
? {
|
|
393
|
-
...pivot,
|
|
394
|
-
sort: undefined,
|
|
395
|
-
sortField: undefined,
|
|
396
|
-
sortDirection: undefined,
|
|
397
|
-
sortFieldType: undefined,
|
|
398
|
-
}
|
|
399
|
-
: undefined,
|
|
463
|
+
fetchDataFromReportBuilderState({
|
|
464
|
+
...reportBuilderState,
|
|
465
|
+
sort: newSort,
|
|
400
466
|
});
|
|
401
467
|
}
|
|
402
468
|
};
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
.filter((c) => columnsInPivot.includes(c));
|
|
411
|
-
}
|
|
412
|
-
// make the columnNames match the order of the selectedOrderedColumns
|
|
413
|
-
return columnNames.sort((a, b) => selectedOrderedColumns.indexOf(a) - selectedOrderedColumns.indexOf(b));
|
|
414
|
-
// return columnNames;
|
|
415
|
-
};
|
|
416
|
-
const clearAllState = () => {
|
|
417
|
-
// We're trying to not block the main thread while resetting all the state.
|
|
418
|
-
// This shouldn't be an issue since the dispatches shouldn't block, but
|
|
419
|
-
// this seems to work for now. ¯\_(ツ)_/¯
|
|
420
|
-
setTimeout(() => {
|
|
421
|
-
setAskedAQuestion(false);
|
|
422
|
-
setAiPrompt('');
|
|
423
|
-
setBaseAst(null);
|
|
424
|
-
setFormData(null);
|
|
425
|
-
setSelectedColumns([]);
|
|
426
|
-
setActiveQuery('');
|
|
427
|
-
setActiveEditItem(null);
|
|
428
|
-
setOpenPopover(null);
|
|
429
|
-
setLoading(false);
|
|
430
|
-
setDataDisplayed(false);
|
|
431
|
-
setRows([]);
|
|
432
|
-
setColumns([]);
|
|
433
|
-
setSelectedOrderedColumns([]);
|
|
434
|
-
setErrorMessage('');
|
|
435
|
-
setPivotError(undefined);
|
|
436
|
-
setFormattedRows([]);
|
|
437
|
-
setCurrentTable('');
|
|
438
|
-
setUniqueValues({});
|
|
439
|
-
setPivot(null);
|
|
440
|
-
setPivotHint('');
|
|
441
|
-
setPivotData(null);
|
|
442
|
-
setRecommendedPivots([]);
|
|
443
|
-
}, 0);
|
|
444
|
-
};
|
|
445
|
-
const fetchGlobalUniqueValues = async (columns, currentTable, client) => {
|
|
446
|
-
setGlobalUniqueValuesIsLoading(true);
|
|
447
|
-
const global = await getUniqueStringValues(columns, currentTable, client, tenants, schemaData.customFields, undefined, true, undefined, dashboardName);
|
|
448
|
-
setGlobalUniqueValues(uniqueValuesToStringMap(global));
|
|
449
|
-
setGlobalUniqueValuesIsLoading(false);
|
|
450
|
-
};
|
|
451
|
-
const uniqueValuesDependenciesRef = useRef({ client, columns, currentTable });
|
|
452
|
-
const memoizedFetchGlobalUniqueValues = useCallback(fetchGlobalUniqueValues, []);
|
|
453
|
-
useEffect(() => {
|
|
454
|
-
const hasChanged = !equal({ client, columns, currentTable }, uniqueValuesDependenciesRef.current);
|
|
455
|
-
if (hasChanged && client && columns && currentTable) {
|
|
456
|
-
memoizedFetchGlobalUniqueValues(columns, currentTable, client);
|
|
457
|
-
uniqueValuesDependenciesRef.current = { client, columns, currentTable };
|
|
458
|
-
}
|
|
459
|
-
}, [client, columns, currentTable, memoizedFetchGlobalUniqueValues]);
|
|
460
|
-
const formatRows = (rows, columns, pivot, aggregationType, dateBucket) => {
|
|
461
|
-
const copiedRows = deepCopy(rows);
|
|
462
|
-
if (pivot) {
|
|
463
|
-
const formattedRows = copiedRows.map((row) => {
|
|
464
|
-
const formattedRow = row;
|
|
465
|
-
Object.keys(row).forEach((key) => {
|
|
466
|
-
const column = columns.find((c) => c.field === key);
|
|
467
|
-
let format = 'string';
|
|
468
|
-
if (!column) {
|
|
469
|
-
format =
|
|
470
|
-
aggregationType === 'count'
|
|
471
|
-
? 'whole_number'
|
|
472
|
-
: 'two_decimal_places';
|
|
473
|
-
}
|
|
474
|
-
else {
|
|
475
|
-
format = DATE_FORMAT_TYPES.includes(column.format)
|
|
476
|
-
? dateBucket
|
|
477
|
-
? getDateFormatFromBucket(dateBucket)
|
|
478
|
-
: 'MMM_yyyy'
|
|
479
|
-
: 'string';
|
|
480
|
-
}
|
|
481
|
-
const formattedValue = quillFormat({
|
|
482
|
-
value: row[key],
|
|
483
|
-
format,
|
|
484
|
-
});
|
|
485
|
-
formattedRow[key] = formattedValue;
|
|
486
|
-
});
|
|
487
|
-
return formattedRow;
|
|
488
|
-
});
|
|
489
|
-
return formattedRows;
|
|
490
|
-
}
|
|
491
|
-
else {
|
|
492
|
-
const formattedRows = copiedRows.map((row) => {
|
|
493
|
-
return columns.reduce((formattedRow, column) => {
|
|
494
|
-
// Apply the format function to each field in the row
|
|
495
|
-
const formattedValue = quillFormat({
|
|
496
|
-
value: row[column.field],
|
|
497
|
-
format: column.format,
|
|
498
|
-
});
|
|
499
|
-
formattedRow[column.field] = formattedValue;
|
|
500
|
-
return formattedRow;
|
|
501
|
-
}, {});
|
|
502
|
-
});
|
|
503
|
-
return formattedRows;
|
|
504
|
-
}
|
|
505
|
-
};
|
|
506
|
-
const copySQLToClipboard = () => {
|
|
507
|
-
let query = activeQuery;
|
|
508
|
-
if (pivot) {
|
|
509
|
-
query = pivotToSql(pivot, activeQuery, columns);
|
|
510
|
-
}
|
|
511
|
-
setIsCopying(true);
|
|
512
|
-
navigator.clipboard.writeText(query);
|
|
513
|
-
setTimeout(() => setIsCopying(false), 800);
|
|
514
|
-
};
|
|
515
|
-
const clearCheckboxes = () => {
|
|
516
|
-
const checkboxes = uniqueValues;
|
|
517
|
-
const newValues = {};
|
|
518
|
-
for (const table of Object.keys(checkboxes)) {
|
|
519
|
-
newValues[table] = {};
|
|
520
|
-
for (const column of Object.keys(checkboxes[table])) {
|
|
521
|
-
newValues[table][column] = {};
|
|
522
|
-
for (const variant of Object.keys(checkboxes[table][column])) {
|
|
523
|
-
newValues[table][column][variant] = false;
|
|
524
|
-
}
|
|
525
|
-
}
|
|
469
|
+
const handleLimitChange = (newLimit, fetchData, updateStateStack = true) => {
|
|
470
|
+
setLimit(newLimit);
|
|
471
|
+
if (updateStateStack) {
|
|
472
|
+
setStateStack((prevStack) => [
|
|
473
|
+
...prevStack,
|
|
474
|
+
{ ...reportBuilderState, limit: newLimit },
|
|
475
|
+
]);
|
|
526
476
|
}
|
|
527
|
-
setUniqueValues(newValues);
|
|
528
|
-
updateFieldValuesMap(newValues, currentTable);
|
|
529
|
-
};
|
|
530
|
-
const fetchSqlQuery = async (ast, formData, fetchData = true) => {
|
|
531
477
|
if (fetchData) {
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
try {
|
|
536
|
-
const where = formData ? formData : ast?.where || null;
|
|
537
|
-
const response = await fetch(`${QUILL_SERVER}/sqlify`, {
|
|
538
|
-
method: 'POST',
|
|
539
|
-
headers: {
|
|
540
|
-
'Content-Type': 'application/json',
|
|
541
|
-
},
|
|
542
|
-
body: JSON.stringify({
|
|
543
|
-
ast: { ...ast, where },
|
|
544
|
-
publicKey: client.publicKey,
|
|
545
|
-
useNewNodeSql: true, // new flag
|
|
546
|
-
}),
|
|
478
|
+
fetchDataFromReportBuilderState({
|
|
479
|
+
...reportBuilderState,
|
|
480
|
+
limit: newLimit,
|
|
547
481
|
});
|
|
548
|
-
const data = await response.json();
|
|
549
|
-
setActiveQuery(data.query);
|
|
550
|
-
if (fetchData) {
|
|
551
|
-
fetchReportFromASTHelper({
|
|
552
|
-
baseAst: ast,
|
|
553
|
-
newFormData: formData,
|
|
554
|
-
});
|
|
555
|
-
}
|
|
556
|
-
return data.query;
|
|
557
|
-
}
|
|
558
|
-
catch (error) {
|
|
559
|
-
setLoading(false);
|
|
560
|
-
console.error(error);
|
|
561
|
-
}
|
|
562
|
-
};
|
|
563
|
-
const getDateRanges = async (columns, tableName) => {
|
|
564
|
-
const dateColumns = columns.filter((column) => {
|
|
565
|
-
return column.fieldType === 'date';
|
|
566
|
-
});
|
|
567
|
-
if (dateColumns.length === 0) {
|
|
568
|
-
return {};
|
|
569
|
-
}
|
|
570
|
-
const dateColumnNames = dateColumns.map((column) => {
|
|
571
|
-
return column.field;
|
|
572
|
-
});
|
|
573
|
-
const dateRanges = await getQueryDateRangeByColumns(dateColumns, `Select ${dateColumnNames.join(', ')} from ${tableName}`, client, tenants, schemaData.customFields ?? {}, dashboardName);
|
|
574
|
-
return dateRanges;
|
|
575
|
-
};
|
|
576
|
-
// It's just like getColumnsInPivot but we expand the columnField
|
|
577
|
-
// if there is one to include all the variants just like it would
|
|
578
|
-
// show up in the table. (eg. category -> ...[Fuel, Food, Other])
|
|
579
|
-
const getColumnsInPivotExpanded = () => {
|
|
580
|
-
if (!pivot)
|
|
581
|
-
return [];
|
|
582
|
-
const tables = getTableNames(baseAst);
|
|
583
|
-
if (tables.length !== 1)
|
|
584
|
-
return [];
|
|
585
|
-
const result = [];
|
|
586
|
-
const table = tables[0];
|
|
587
|
-
const { valueField, rowField, columnField } = pivot;
|
|
588
|
-
if (columnField &&
|
|
589
|
-
uniqueValues[table] &&
|
|
590
|
-
uniqueValues[table][columnField]) {
|
|
591
|
-
result.push(...Object.keys(uniqueValues[table][columnField]));
|
|
592
|
-
}
|
|
593
|
-
result.push(valueField, rowField);
|
|
594
|
-
return result.filter(Boolean);
|
|
595
|
-
};
|
|
596
|
-
const loadTable = async (tables) => {
|
|
597
|
-
if (!tables?.length || !schemaData.schema?.length)
|
|
598
|
-
return;
|
|
599
|
-
setLoading(true);
|
|
600
|
-
const tableInfo = tables.find((tableInfo) => tableInfo.name === initialTableName);
|
|
601
|
-
if (tableInfo) {
|
|
602
|
-
setUniqueValuesIsLoading(true);
|
|
603
|
-
const newUniqueValues = await getUniqueStringValues(tableInfo.columns, initialTableName, client, tenants, schemaData.customFields, uniqueValues, true, undefined, dashboardName);
|
|
604
|
-
if (hashCode(uniqueValues) !== hashCode(newUniqueValues)) {
|
|
605
|
-
setUniqueValues(newUniqueValues);
|
|
606
|
-
updateFieldValuesMap(newUniqueValues, initialTableName);
|
|
607
|
-
}
|
|
608
|
-
setUniqueValuesIsLoading(false);
|
|
609
|
-
const dateRangesTemp = await getDateRanges(tableInfo.columns, initialTableName);
|
|
610
|
-
setDateRanges(dateRangesTemp);
|
|
611
|
-
}
|
|
612
|
-
const columnsForTable = tables
|
|
613
|
-
.find((t) => t.name === initialTableName)
|
|
614
|
-
?.columns.map((c) => c.field)
|
|
615
|
-
.sort((a, b) => {
|
|
616
|
-
const aIsId = a.endsWith('.id') ||
|
|
617
|
-
a.endsWith('_id') ||
|
|
618
|
-
a.endsWith('Id') ||
|
|
619
|
-
a === 'id';
|
|
620
|
-
const bIsId = b.endsWith('.id') ||
|
|
621
|
-
b.endsWith('_id') ||
|
|
622
|
-
b.endsWith('Id') ||
|
|
623
|
-
b === 'id';
|
|
624
|
-
if (aIsId && !bIsId)
|
|
625
|
-
return 1;
|
|
626
|
-
if (bIsId && !aIsId)
|
|
627
|
-
return -1;
|
|
628
|
-
return 0;
|
|
629
|
-
});
|
|
630
|
-
await fetchAstFromPromptHelper(`get ${columnsForTable} from ${initialTableName}`);
|
|
631
|
-
setInitialLoad(false);
|
|
632
|
-
};
|
|
633
|
-
const onSchemaChange = async () => {
|
|
634
|
-
try {
|
|
635
|
-
setOrderedColumnNames((schemaData.schemaWithCustomFields ?? []).flatMap((table) => table.columns
|
|
636
|
-
.map((c) => `${table.name}.${c.field}`)
|
|
637
|
-
.sort((a, b) => {
|
|
638
|
-
const aIsId = a.endsWith('.id') ||
|
|
639
|
-
a.endsWith('_id') ||
|
|
640
|
-
a.endsWith('Id') ||
|
|
641
|
-
a === 'id';
|
|
642
|
-
const bIsId = b.endsWith('.id') ||
|
|
643
|
-
b.endsWith('_id') ||
|
|
644
|
-
b.endsWith('Id') ||
|
|
645
|
-
b === 'id';
|
|
646
|
-
if (aIsId && !bIsId)
|
|
647
|
-
return 1;
|
|
648
|
-
if (bIsId && !aIsId)
|
|
649
|
-
return -1;
|
|
650
|
-
return 0;
|
|
651
|
-
})));
|
|
652
|
-
if (initialTableName) {
|
|
653
|
-
await loadTable(schemaData.schemaWithCustomFields);
|
|
654
|
-
}
|
|
655
|
-
setInitialLoad(false);
|
|
656
|
-
}
|
|
657
|
-
catch (error) {
|
|
658
|
-
console.error(error);
|
|
659
|
-
}
|
|
660
|
-
};
|
|
661
|
-
useEffect(() => {
|
|
662
|
-
const loadChart = async () => {
|
|
663
|
-
let report;
|
|
664
|
-
try {
|
|
665
|
-
if (!reportId) {
|
|
666
|
-
throw new Error('Report ID is required');
|
|
667
|
-
}
|
|
668
|
-
report = dashboard[reportId];
|
|
669
|
-
if (!report || report.referencedTables?.length !== 1) {
|
|
670
|
-
throw new Error('Report not found');
|
|
671
|
-
}
|
|
672
|
-
const { ast: newAst, pivot: newPivot, schema: curSchema, } = await fetchASTFromQuillReport(report, client, schemaData.schemaWithCustomFields);
|
|
673
|
-
setBaseAst({ ...newAst, where: null });
|
|
674
|
-
await fetchReportFromASTHelper({
|
|
675
|
-
baseAst: { ...newAst, where: null },
|
|
676
|
-
newFormData: newAst.where,
|
|
677
|
-
curPivot: newPivot,
|
|
678
|
-
curSchema,
|
|
679
|
-
previousReport: report,
|
|
680
|
-
});
|
|
681
|
-
await onSchemaChange();
|
|
682
|
-
setReportInfo(report);
|
|
683
|
-
const query = await fetchSqlQuery(newAst, null, false);
|
|
684
|
-
setActiveQuery(query);
|
|
685
|
-
}
|
|
686
|
-
catch (err) {
|
|
687
|
-
console.error(err);
|
|
688
|
-
setErrorMessage('Error when loading chart');
|
|
689
|
-
}
|
|
690
|
-
};
|
|
691
|
-
if (reportId) {
|
|
692
|
-
loadChart();
|
|
693
|
-
}
|
|
694
|
-
}, [dashboard[reportId || '']]);
|
|
695
|
-
useEffect(() => {
|
|
696
|
-
onSchemaChange();
|
|
697
|
-
}, [schemaData.schemaWithCustomFields, initialTableName, reportId]);
|
|
698
|
-
// Function to handle the insertion of expressions
|
|
699
|
-
const handleInsertion = (value) => {
|
|
700
|
-
const newFilterStack = [...filterStack];
|
|
701
|
-
if (newFilterStack.length > 0) {
|
|
702
|
-
const tabNode = {
|
|
703
|
-
leaf: false,
|
|
704
|
-
operator: 'and',
|
|
705
|
-
leftNode: null,
|
|
706
|
-
rightNode: null,
|
|
707
|
-
};
|
|
708
|
-
newFilterStack.push(tabNode);
|
|
709
|
-
}
|
|
710
|
-
const newItem = {
|
|
711
|
-
leaf: true,
|
|
712
|
-
operator: null,
|
|
713
|
-
leftNode: null,
|
|
714
|
-
rightNode: null,
|
|
715
|
-
value: value,
|
|
716
|
-
};
|
|
717
|
-
newFilterStack.push(newItem);
|
|
718
|
-
const tree = filterStackToFilterTree(newFilterStack);
|
|
719
|
-
if (tree) {
|
|
720
|
-
const newFormData = filterTreeToAst(tree, client.databaseType.toLowerCase());
|
|
721
|
-
setFormData(newFormData);
|
|
722
|
-
const newAst = { ...baseAst };
|
|
723
|
-
newAst.where = newFormData;
|
|
724
|
-
setBaseAst(newAst);
|
|
725
|
-
fetchSqlQuery(newAst, newFormData);
|
|
726
482
|
}
|
|
727
483
|
};
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
* Searches the known schema and returns the fieldType of the first column
|
|
732
|
-
* it can find with the given name. Will first search through the current
|
|
733
|
-
* list of fields in the current query if any, then will default to searching
|
|
734
|
-
* through the whole schema.
|
|
735
|
-
*
|
|
736
|
-
* If more than one column exist with the given name, it will return the first
|
|
737
|
-
* one that it finds. This might not be the one that you intended.
|
|
738
|
-
*
|
|
739
|
-
* TODO: pass an optional table param to limit the search to a given table.
|
|
740
|
-
*
|
|
741
|
-
* @param columnName the name to search for.
|
|
742
|
-
* @returns the fieldType string or undefined if not found.
|
|
743
|
-
*/
|
|
744
|
-
const getColumnTypeByName = (columnName) => {
|
|
745
|
-
const column = columns.find((col) => col.field === columnName);
|
|
746
|
-
return column?.fieldType;
|
|
747
|
-
};
|
|
748
|
-
const emptyPivotColumns = () => {
|
|
749
|
-
if (pivot && pivot.rowField && pivot.columnField) {
|
|
750
|
-
return [
|
|
751
|
-
{ label: snakeAndCamelCaseToTitleCase(pivot.rowField) },
|
|
752
|
-
{ label: snakeAndCamelCaseToTitleCase(pivot.columnField) },
|
|
753
|
-
];
|
|
754
|
-
}
|
|
755
|
-
else if (pivot && pivot.rowField) {
|
|
756
|
-
return [
|
|
757
|
-
{ label: snakeAndCamelCaseToTitleCase(pivot.rowField) },
|
|
758
|
-
...(pivot.aggregations ?? [])
|
|
759
|
-
.filter((a) => !!a.valueField)
|
|
760
|
-
.map((agg) => ({
|
|
761
|
-
label: snakeAndCamelCaseToTitleCase(agg.valueField),
|
|
762
|
-
})),
|
|
763
|
-
];
|
|
484
|
+
const handleMultiStateChange = (state, fetchData, updateStateStack = true) => {
|
|
485
|
+
if (state.tables !== undefined) {
|
|
486
|
+
handleTablesChange(state.tables, false);
|
|
764
487
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
.filter((a) => !!a.valueField)
|
|
768
|
-
.map((agg) => ({
|
|
769
|
-
label: snakeAndCamelCaseToTitleCase(agg.valueField),
|
|
770
|
-
}));
|
|
488
|
+
if (state.columns !== undefined) {
|
|
489
|
+
handleColumnsChange(state.columns, false, false);
|
|
771
490
|
}
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
const [currentProcessing, setCurrentProcessing] = useState({
|
|
775
|
-
page: REPORT_BUILDER_PAGINATION,
|
|
776
|
-
});
|
|
777
|
-
const [numberOfRows, setNumberOfRows] = useState(0);
|
|
778
|
-
const [rowCountIsLoading, setRowCountIsLoading] = useState(false);
|
|
779
|
-
const [tableLoading, setTableLoading] = useState(false);
|
|
780
|
-
const resetProcessing = () => {
|
|
781
|
-
setCurrentProcessing({ page: REPORT_BUILDER_PAGINATION });
|
|
782
|
-
};
|
|
783
|
-
const onPageChange = (page, initiator = 'ReportBuilder') => {
|
|
784
|
-
const pagination = initiator === 'ReportBuilder'
|
|
785
|
-
? REPORT_BUILDER_PAGINATION
|
|
786
|
-
: DEFAULT_PAGINATION;
|
|
787
|
-
if (currentProcessing.page &&
|
|
788
|
-
shouldFetchMore(pagination, page, previousPage, pivotData ? pivotData.rows.length : rows.length)) {
|
|
789
|
-
const newPagination = { ...currentProcessing.page, page };
|
|
790
|
-
const updatedProcessing = { ...currentProcessing, page: newPagination };
|
|
791
|
-
setCurrentProcessing(updatedProcessing);
|
|
792
|
-
handleRunQuery(updatedProcessing);
|
|
491
|
+
if (state.filterStack !== undefined) {
|
|
492
|
+
handleFilterStackChange(state.filterStack, false, false);
|
|
793
493
|
}
|
|
794
|
-
if (
|
|
795
|
-
|
|
494
|
+
if (state.pivot !== undefined) {
|
|
495
|
+
handlePivotChange(state.pivot, false, false);
|
|
796
496
|
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
if (pivot) {
|
|
800
|
-
let newPivot = null;
|
|
801
|
-
if (isDelete) {
|
|
802
|
-
setPivot((oldPivot) => {
|
|
803
|
-
if (!oldPivot)
|
|
804
|
-
return null;
|
|
805
|
-
newPivot = {
|
|
806
|
-
...oldPivot,
|
|
807
|
-
sort: undefined,
|
|
808
|
-
sortField: undefined,
|
|
809
|
-
sortDirection: undefined,
|
|
810
|
-
sortFieldType: undefined,
|
|
811
|
-
};
|
|
812
|
-
return newPivot;
|
|
813
|
-
});
|
|
814
|
-
}
|
|
815
|
-
else {
|
|
816
|
-
setPivot((oldPivot) => {
|
|
817
|
-
if (!oldPivot)
|
|
818
|
-
return null;
|
|
819
|
-
newPivot = {
|
|
820
|
-
...oldPivot,
|
|
821
|
-
sort: true,
|
|
822
|
-
sortField: sort.field,
|
|
823
|
-
sortDirection: sort.direction,
|
|
824
|
-
sortFieldType: getColumnTypeByName(sort.field),
|
|
825
|
-
};
|
|
826
|
-
return newPivot;
|
|
827
|
-
});
|
|
828
|
-
}
|
|
829
|
-
fetchReportFromASTHelper({
|
|
830
|
-
baseAst: baseAst,
|
|
831
|
-
curPivot: newPivot,
|
|
832
|
-
});
|
|
497
|
+
if (state.sort !== undefined) {
|
|
498
|
+
handleSortChange(state.sort, false, false);
|
|
833
499
|
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
if (!newAst.orderby) {
|
|
837
|
-
newAst.orderby = [];
|
|
838
|
-
}
|
|
839
|
-
const existingSortIndex = newAst.orderby.findIndex((item) => getFieldFromExpression(item.expr) === sort.field);
|
|
840
|
-
if (isDelete) {
|
|
841
|
-
if (existingSortIndex !== -1) {
|
|
842
|
-
newAst.orderby.splice(existingSortIndex, 1);
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
else if (existingSortIndex !== -1) {
|
|
846
|
-
newAst.orderby[existingSortIndex] = {
|
|
847
|
-
expr: { type: 'column_ref', column: sort.field },
|
|
848
|
-
type: sort.direction.toUpperCase(),
|
|
849
|
-
};
|
|
850
|
-
}
|
|
851
|
-
else {
|
|
852
|
-
newAst.orderby.push({
|
|
853
|
-
expr: { type: 'column_ref', column: sort.field },
|
|
854
|
-
type: sort.direction.toUpperCase(),
|
|
855
|
-
});
|
|
856
|
-
}
|
|
857
|
-
setBaseAst(deepCopy(newAst));
|
|
858
|
-
fetchReportFromASTHelper({
|
|
859
|
-
baseAst: newAst,
|
|
860
|
-
});
|
|
861
|
-
setPreviousPage(0);
|
|
500
|
+
if (state.limit !== undefined) {
|
|
501
|
+
handleLimitChange(state.limit, false, false);
|
|
862
502
|
}
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
if (!oldPivot)
|
|
869
|
-
return null;
|
|
870
|
-
const newPivot = { ...oldPivot, rowLimit: limit };
|
|
871
|
-
fetchReportFromASTHelper({ baseAst, curPivot: newPivot });
|
|
872
|
-
return newPivot;
|
|
873
|
-
});
|
|
874
|
-
}
|
|
875
|
-
else {
|
|
876
|
-
const newAst = { ...baseAst };
|
|
877
|
-
if (client.databaseType.toLowerCase() === 'mssql') {
|
|
878
|
-
newAst.top = {
|
|
879
|
-
value: limit,
|
|
880
|
-
};
|
|
881
|
-
}
|
|
882
|
-
else {
|
|
883
|
-
newAst.limit = {
|
|
884
|
-
seperator: '',
|
|
885
|
-
value: [
|
|
886
|
-
{
|
|
887
|
-
type: 'number',
|
|
888
|
-
value: limit,
|
|
889
|
-
},
|
|
890
|
-
],
|
|
891
|
-
};
|
|
892
|
-
}
|
|
893
|
-
setBaseAst(deepCopy(newAst));
|
|
894
|
-
fetchSqlQuery(deepCopy(newAst), undefined, true);
|
|
895
|
-
}
|
|
503
|
+
if (updateStateStack) {
|
|
504
|
+
setStateStack((prevStack) => [
|
|
505
|
+
...prevStack,
|
|
506
|
+
{ ...reportBuilderState, ...state },
|
|
507
|
+
]);
|
|
896
508
|
}
|
|
897
|
-
|
|
898
|
-
|
|
509
|
+
if (fetchData) {
|
|
510
|
+
fetchDataFromReportBuilderState({ ...reportBuilderState, ...state }, !!state.filterStack, !!state.tables);
|
|
899
511
|
}
|
|
900
|
-
setOpenPopover(null);
|
|
901
512
|
};
|
|
902
|
-
const
|
|
903
|
-
if (
|
|
513
|
+
const handleUndo = () => {
|
|
514
|
+
if (stateStack.length <= 1) {
|
|
904
515
|
return;
|
|
905
516
|
}
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
client,
|
|
910
|
-
tenants,
|
|
911
|
-
processing,
|
|
912
|
-
customFields: schemaData.customFields,
|
|
913
|
-
filters: includeFilters ? specificDashboardFilters : undefined,
|
|
914
|
-
dateField: includeFilters
|
|
915
|
-
? (tempReport.dateField ?? reportInfo?.dateField)
|
|
916
|
-
: undefined,
|
|
917
|
-
rowsOnly: false,
|
|
918
|
-
rowCountOnly: true,
|
|
919
|
-
filterMap: undefined,
|
|
920
|
-
dashboardName,
|
|
921
|
-
});
|
|
922
|
-
if (tableInfo.rowCount) {
|
|
923
|
-
setNumberOfRows(tableInfo.rowCount);
|
|
924
|
-
// @ts-ignore
|
|
925
|
-
setTempReport((tempReport) => ({
|
|
926
|
-
...tempReport,
|
|
927
|
-
rowCount: tableInfo.rowCount,
|
|
928
|
-
}));
|
|
929
|
-
}
|
|
930
|
-
setRowCountIsLoading(false);
|
|
517
|
+
const previousState = stateStack[stateStack.length - 2];
|
|
518
|
+
setStateStack((prevStack) => prevStack.slice(0, -1));
|
|
519
|
+
handleMultiStateChange(previousState, true, false);
|
|
931
520
|
};
|
|
932
|
-
const
|
|
933
|
-
|
|
934
|
-
const tableData = await fetchTableByAST({ ...ast, where }, client, tenants, { page: REPORT_BUILDER_PAGINATION }, schemaData.customFields, false, true, dashboardName);
|
|
935
|
-
if (tableData.rowCount) {
|
|
936
|
-
setNumberOfRows(tableData.rowCount);
|
|
937
|
-
// @ts-ignore
|
|
938
|
-
setTempReport((tempReport) => ({
|
|
939
|
-
...tempReport,
|
|
940
|
-
rowCount: tableData.rowCount,
|
|
941
|
-
}));
|
|
942
|
-
}
|
|
943
|
-
setRowCountIsLoading(false);
|
|
944
|
-
};
|
|
945
|
-
const handleRunQuery = async (processing, resetRows = false, includeFilters = false) => {
|
|
946
|
-
try {
|
|
947
|
-
const isPivotPagination = !!(pivot && pivotData);
|
|
948
|
-
setErrorMessage('');
|
|
949
|
-
setTableLoading(true);
|
|
950
|
-
const tableInfo = await fetchResultsByQuery({
|
|
951
|
-
query: isPivotPagination ? pivotData.pivotQuery : activeQuery,
|
|
952
|
-
comparisonQuery: pivot && pivotData ? pivotData.comparisonPivotQuery : undefined,
|
|
953
|
-
client,
|
|
954
|
-
tenants,
|
|
955
|
-
processing,
|
|
956
|
-
customFields: schemaData.customFields,
|
|
957
|
-
rowsOnly: true,
|
|
958
|
-
dashboardName,
|
|
959
|
-
pivot: pivot,
|
|
960
|
-
getPivotRowCount: false,
|
|
961
|
-
});
|
|
962
|
-
if (tableInfo.error) {
|
|
963
|
-
throw new Error(tableInfo.error);
|
|
964
|
-
}
|
|
965
|
-
else if (tableInfo.rows.length === 0) {
|
|
966
|
-
throw new Error('No data found');
|
|
967
|
-
}
|
|
968
|
-
if (!isPivotPagination) {
|
|
969
|
-
// fetching row count for non-pivot query
|
|
970
|
-
fetchRowCount(processing, includeFilters);
|
|
971
|
-
let tempRows = [...rows, ...tableInfo.rows];
|
|
972
|
-
if (resetRows) {
|
|
973
|
-
tempRows = tableInfo.rows;
|
|
974
|
-
setPreviousPage(0);
|
|
975
|
-
}
|
|
976
|
-
setRows(tempRows);
|
|
977
|
-
setFormattedRows(formatRowsFromReport({ rows: tempRows, columns: tableInfo.columns }));
|
|
978
|
-
setTempReport((tempReport) => ({
|
|
979
|
-
...tempReport,
|
|
980
|
-
rows: tempRows,
|
|
981
|
-
rowCount: tableInfo.rowCount ?? tempReport.rowCount,
|
|
982
|
-
}));
|
|
983
|
-
setColumns(tableInfo.columns);
|
|
984
|
-
}
|
|
985
|
-
else {
|
|
986
|
-
let tempRows = [...pivotData.rows, ...tableInfo.rows];
|
|
987
|
-
if (resetRows) {
|
|
988
|
-
tempRows = tableInfo.rows;
|
|
989
|
-
setPreviousPage(0);
|
|
990
|
-
}
|
|
991
|
-
setPivotData((oldPivotData) => {
|
|
992
|
-
if (oldPivotData) {
|
|
993
|
-
return {
|
|
994
|
-
...oldPivotData,
|
|
995
|
-
rows: tempRows,
|
|
996
|
-
columns: tableInfo.columns,
|
|
997
|
-
};
|
|
998
|
-
}
|
|
999
|
-
return null;
|
|
1000
|
-
});
|
|
1001
|
-
setFormattedRows(formatRowsFromReport({ rows: tempRows, columns: tableInfo.columns }));
|
|
1002
|
-
}
|
|
1003
|
-
setTableLoading(false);
|
|
1004
|
-
}
|
|
1005
|
-
catch (e) {
|
|
1006
|
-
setTableLoading(false);
|
|
1007
|
-
setErrorMessage('Failed to run SQL query: ' + e.message);
|
|
1008
|
-
setRows([]);
|
|
1009
|
-
setColumns([]);
|
|
521
|
+
const fetchDataFromReportBuilderState = (state, filtersChanged, tablesChanged) => {
|
|
522
|
+
if (!client) {
|
|
1010
523
|
return;
|
|
1011
524
|
}
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
if (selectedColumns.length < 1)
|
|
1019
|
-
return false;
|
|
1020
|
-
const allColumns = orderedColumnNames.filter((row) => {
|
|
1021
|
-
const [table] = row.split('.');
|
|
1022
|
-
const selectedTable = selectedColumns[0].split('.')[0];
|
|
1023
|
-
return selectedTable === table;
|
|
525
|
+
const ast = reportBuilderStateToAst(state, client.databaseType?.toLowerCase() || 'postgresql');
|
|
526
|
+
fetchReportFromASTHelper({
|
|
527
|
+
ast,
|
|
528
|
+
pivot: state.pivot,
|
|
529
|
+
requiresNewFilteredUniqueValues: filtersChanged,
|
|
530
|
+
requiresNewUnfilteredUniqueValues: tablesChanged,
|
|
1024
531
|
});
|
|
1025
|
-
return selectedColumns.length === allColumns.length;
|
|
1026
532
|
};
|
|
1027
|
-
const
|
|
1028
|
-
type: 'expr',
|
|
1029
|
-
expr: {
|
|
1030
|
-
type: 'column_ref',
|
|
1031
|
-
table: null,
|
|
1032
|
-
column: name,
|
|
1033
|
-
},
|
|
1034
|
-
as: null,
|
|
1035
|
-
});
|
|
1036
|
-
const updateUniqueValue = (uniqueStrings, table) => {
|
|
1037
|
-
const uniqueStringsObj = uniqueValues ?? {};
|
|
1038
|
-
uniqueStringsObj[table] = uniqueStrings ?? {};
|
|
1039
|
-
setUniqueValues(uniqueStringsObj);
|
|
1040
|
-
updateFieldValuesMap(uniqueStringsObj, table);
|
|
1041
|
-
return uniqueStringsObj;
|
|
1042
|
-
};
|
|
1043
|
-
const fetchReportFromASTHelper = async ({ baseAst, newFormData, curPivot, curSchema, previousReport, keepPivotHint = false, }) => {
|
|
1044
|
-
const curFormData = newFormData !== undefined ? newFormData : formData;
|
|
533
|
+
const fetchReportFromASTHelper = async ({ ast, pivot, previousReport, requiresNewFilteredUniqueValues = false, requiresNewUnfilteredUniqueValues = false, }) => {
|
|
1045
534
|
let reportBuilderInfo = undefined;
|
|
535
|
+
setErrorMessage('');
|
|
536
|
+
const schema = filteredSchema;
|
|
1046
537
|
try {
|
|
1047
|
-
|
|
538
|
+
if (!client || reportBuilderLoading) {
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
setReportBuilderLoading(true);
|
|
1048
542
|
reportBuilderInfo = await fetchReportBuilderDataFromAST({
|
|
1049
|
-
baseAst,
|
|
1050
|
-
|
|
1051
|
-
schema: curSchema ?? schemaData.schemaWithCustomFields,
|
|
543
|
+
baseAst: ast,
|
|
544
|
+
schema,
|
|
1052
545
|
client,
|
|
1053
546
|
tenants,
|
|
1054
|
-
pivot:
|
|
1055
|
-
previousFormData: formData,
|
|
1056
|
-
currentTable,
|
|
547
|
+
pivot: pivot ?? undefined,
|
|
1057
548
|
previousRelevant: {
|
|
1058
|
-
|
|
549
|
+
uniqueStringsByTable: unfilteredUniqueValues,
|
|
1059
550
|
dateRanges: dateRanges ?? {},
|
|
551
|
+
uniqueStringsByColumn: requiresNewFilteredUniqueValues
|
|
552
|
+
? {}
|
|
553
|
+
: columnUniqueValues,
|
|
1060
554
|
},
|
|
1061
|
-
|
|
555
|
+
requiresNewFilteredUniqueValues,
|
|
556
|
+
report: previousReport,
|
|
1062
557
|
customFields: schemaData.customFields,
|
|
1063
558
|
skipUniqueValues: true,
|
|
1064
559
|
skipRowCount: true,
|
|
1065
560
|
processing: { page: REPORT_BUILDER_PAGINATION },
|
|
1066
|
-
dashboardName,
|
|
561
|
+
dashboardName: destinationDashboard,
|
|
1067
562
|
});
|
|
1068
563
|
if (reportBuilderInfo.error) {
|
|
1069
564
|
throw new Error(reportBuilderInfo.error);
|
|
@@ -1072,58 +567,41 @@ export default function ReportBuilder({ initialTableName = '', onSubmitEditRepor
|
|
|
1072
567
|
catch (err) {
|
|
1073
568
|
if (err instanceof Error) {
|
|
1074
569
|
setErrorMessage(err.message);
|
|
1075
|
-
|
|
570
|
+
setReportBuilderLoading(false);
|
|
1076
571
|
return { error: true, message: err.message, rows: [] };
|
|
1077
572
|
}
|
|
1078
|
-
|
|
573
|
+
setReportBuilderLoading(false);
|
|
1079
574
|
setErrorMessage('Failed to fetch');
|
|
1080
575
|
return { error: true, message: 'Failed to fetch', rows: [] };
|
|
1081
576
|
}
|
|
1082
577
|
if (!reportBuilderInfo) {
|
|
1083
|
-
|
|
578
|
+
setReportBuilderLoading(false);
|
|
1084
579
|
setErrorMessage('Failed to fetch');
|
|
1085
580
|
return;
|
|
1086
581
|
}
|
|
1087
582
|
const cleanedReport = await cleanDashboardItem({
|
|
1088
583
|
item: reportBuilderInfo.report,
|
|
1089
|
-
dashboardFilters:
|
|
584
|
+
dashboardFilters: [],
|
|
1090
585
|
client,
|
|
1091
586
|
customFields: schemaData.customFields,
|
|
1092
587
|
skipPivotFetch: true,
|
|
1093
588
|
});
|
|
589
|
+
// set tempReport
|
|
1094
590
|
setTempReport({
|
|
1095
591
|
...cleanedReport,
|
|
1096
592
|
pagination: REPORT_BUILDER_PAGINATION,
|
|
1097
593
|
});
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
setRows(reportBuilderInfo.rows);
|
|
1106
|
-
setPreviousPage(0);
|
|
1107
|
-
if (!(client.databaseType.toLowerCase() === 'bigquery') ||
|
|
594
|
+
setActiveQuery(reportBuilderInfo.query);
|
|
595
|
+
// table data
|
|
596
|
+
fetchRowCountFromAST(ast, ast.where);
|
|
597
|
+
setReportRows(reportBuilderInfo.rows);
|
|
598
|
+
setFormattedRows(reportBuilderInfo.formattedRows);
|
|
599
|
+
resetProcessing();
|
|
600
|
+
if (!(client.databaseType?.toLowerCase() === 'bigquery') ||
|
|
1108
601
|
(reportBuilderInfo.rows && reportBuilderInfo.rows.length > 0)) {
|
|
1109
|
-
|
|
1110
|
-
setSelectedColumns(reportBuilderInfoColumns);
|
|
1111
|
-
}
|
|
1112
|
-
if (prevTable !== reportBuilderInfo.table) {
|
|
1113
|
-
setSelectedOrderedColumns([]); // reset selected ordered columns
|
|
1114
|
-
}
|
|
1115
|
-
setPivot(reportBuilderInfo.pivot
|
|
1116
|
-
? setTypesOnPivot(reportBuilderInfo.pivot, reportBuilderInfo.columns)
|
|
1117
|
-
: null);
|
|
1118
|
-
if (!keepPivotHint) {
|
|
1119
|
-
setPivotHint('');
|
|
602
|
+
setReportColumns(reportBuilderInfo.columns);
|
|
1120
603
|
}
|
|
1121
604
|
setPivotData(reportBuilderInfo.pivotData);
|
|
1122
|
-
setDateRanges(reportBuilderInfo.dateRanges);
|
|
1123
|
-
setFormattedRows(reportBuilderInfo.formattedRows);
|
|
1124
|
-
setDataDisplayed(true);
|
|
1125
|
-
setCurrentTable(reportBuilderInfo.table);
|
|
1126
|
-
setFormData(curFormData);
|
|
1127
605
|
if (reportBuilderInfo.pivot) {
|
|
1128
606
|
setPivotRowField(reportBuilderInfo.pivot.rowField);
|
|
1129
607
|
setPivotColumnField(reportBuilderInfo.pivot.columnField);
|
|
@@ -1132,107 +610,109 @@ export default function ReportBuilder({ initialTableName = '', onSubmitEditRepor
|
|
|
1132
610
|
valueField: reportBuilderInfo.pivot.valueField,
|
|
1133
611
|
valueField2: reportBuilderInfo.pivot.valueField2,
|
|
1134
612
|
aggregationType: reportBuilderInfo.pivot.aggregationType,
|
|
1135
|
-
},
|
|
1136
|
-
]);
|
|
1137
|
-
}
|
|
1138
|
-
else {
|
|
1139
|
-
setLoading(false);
|
|
1140
|
-
}
|
|
1141
|
-
setReportInfo(reportBuilderInfo.report);
|
|
1142
|
-
const schema = curSchema ?? schemaData.schemaWithCustomFields;
|
|
1143
|
-
const tableInfo = schema.find((tableInfo) => tableInfo.name === reportBuilderInfo?.table);
|
|
1144
|
-
let query = reportBuilderInfo.query;
|
|
1145
|
-
if (!query && tableInfo) {
|
|
1146
|
-
const ast = baseAst ??
|
|
1147
|
-
createBasicSelectASTFromColumns(tableInfo.columns, reportBuilderInfo.table);
|
|
1148
|
-
const queryResult = await fetchSqlQuery({
|
|
1149
|
-
...ast,
|
|
1150
|
-
where: curFormData,
|
|
1151
|
-
}, curFormData, false);
|
|
1152
|
-
if (queryResult.error) {
|
|
1153
|
-
console.error(queryResult.error);
|
|
1154
|
-
}
|
|
1155
|
-
else {
|
|
1156
|
-
query = queryResult;
|
|
1157
|
-
setActiveQuery(queryResult);
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
else if (query) {
|
|
1161
|
-
setActiveQuery(query);
|
|
613
|
+
},
|
|
614
|
+
]);
|
|
1162
615
|
}
|
|
616
|
+
setPivot(reportBuilderInfo.pivot);
|
|
617
|
+
setPivotHint(reportBuilderInfo.pivotHint);
|
|
618
|
+
setDateRanges(reportBuilderInfo.dateRanges);
|
|
619
|
+
const tableNames = reportBuilderInfo.tables;
|
|
620
|
+
const columnInfo = tableNames.flatMap((table) => {
|
|
621
|
+
const tableInfo = schema.find((tableInfo) => tableInfo.name === table);
|
|
622
|
+
if (!tableInfo) {
|
|
623
|
+
return [];
|
|
624
|
+
}
|
|
625
|
+
return tableInfo.columns.map((col) => ({ ...col, table }));
|
|
626
|
+
});
|
|
1163
627
|
// fetch unique values after everything else since it is the most expensive
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
628
|
+
try {
|
|
629
|
+
let uniqueStrings = filteredUniqueValues ?? unfilteredUniqueValues;
|
|
630
|
+
let uniqueValuesByColumn = columnUniqueValues;
|
|
631
|
+
if (requiresNewUnfilteredUniqueValues) {
|
|
632
|
+
fetchGlobalUniqueValues(columnInfo, tableNames);
|
|
633
|
+
}
|
|
634
|
+
if (requiresNewFilteredUniqueValues) {
|
|
1168
635
|
if (reportBuilderInfo.pivot) {
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
throw new Error('Table info not found');
|
|
1173
|
-
}
|
|
1174
|
-
const uniqueStrings = await getUniqueStringValues(tableInfo.columns, reportBuilderInfo.table, client, tenants, schemaData.customFields, undefined, true, query, dashboardName);
|
|
1175
|
-
const newUnique = updateUniqueValue(uniqueStrings, reportBuilderInfo.table);
|
|
1176
|
-
let pivotChanged = false;
|
|
1177
|
-
let newPivot;
|
|
1178
|
-
if (reportBuilderInfo.pivot &&
|
|
1179
|
-
!isValidPivotForReport(reportBuilderInfo.pivot, newUnique, reportBuilderInfo.table, reportBuilderInfo.columns)) {
|
|
1180
|
-
const { pivot, hint } = makePivotValid(reportBuilderInfo.pivot, newUnique, reportBuilderInfo.table, reportBuilderInfo.columns);
|
|
1181
|
-
if (pivot) {
|
|
1182
|
-
setPivot(setTypesOnPivot(pivot, reportBuilderInfo.columns));
|
|
1183
|
-
newPivot = pivot;
|
|
1184
|
-
pivotChanged = true;
|
|
1185
|
-
setPivotHint(hint);
|
|
1186
|
-
}
|
|
1187
|
-
else {
|
|
1188
|
-
setUnresolvedReportMessage('Not a valid pivot');
|
|
1189
|
-
}
|
|
636
|
+
// if there's a pivot, these values would have had to been fetched
|
|
637
|
+
uniqueStrings = reportBuilderInfo.uniqueStringsByTable;
|
|
638
|
+
uniqueValuesByColumn = reportBuilderInfo.uniqueStringsByColumn;
|
|
1190
639
|
}
|
|
1191
640
|
else {
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
641
|
+
setFilteredUniqueValuesIsLoading(true);
|
|
642
|
+
const starQuery = await fetchSqlQuery(createSelectStarFromAst(ast), client);
|
|
643
|
+
uniqueStrings = await getUniqueStringValuesByTable({
|
|
644
|
+
columns: columnInfo,
|
|
645
|
+
tables: tableNames,
|
|
646
|
+
client,
|
|
647
|
+
tenants,
|
|
648
|
+
customFields: schemaData.customFields ?? undefined,
|
|
649
|
+
withExceededColumns: true,
|
|
650
|
+
dashboardName: destinationDashboard,
|
|
651
|
+
queryTemplate: starQuery.query,
|
|
652
|
+
});
|
|
653
|
+
uniqueValuesByColumn = {};
|
|
654
|
+
reportBuilderInfo.reportBuilderColumns.forEach((col) => {
|
|
655
|
+
uniqueValuesByColumn[col.alias || col.field] =
|
|
656
|
+
uniqueStrings[col.table || '']?.[col.field] ?? [];
|
|
1203
657
|
});
|
|
1204
|
-
}
|
|
1205
|
-
else {
|
|
1206
|
-
setLoading(false);
|
|
1207
658
|
}
|
|
1208
659
|
}
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
setErrorMessage(
|
|
1217
|
-
|
|
660
|
+
setFilteredUniqueValues(uniqueStrings);
|
|
661
|
+
setFilteredUniqueValuesIsLoading(false);
|
|
662
|
+
setColumnUniqueValues(uniqueValuesByColumn);
|
|
663
|
+
setReportBuilderLoading(false);
|
|
664
|
+
}
|
|
665
|
+
catch (err) {
|
|
666
|
+
if (err instanceof Error) {
|
|
667
|
+
setErrorMessage(err.message);
|
|
668
|
+
setReportBuilderLoading(false);
|
|
669
|
+
return { error: true, message: err.message, rows: [] };
|
|
1218
670
|
}
|
|
671
|
+
setReportBuilderLoading(false);
|
|
672
|
+
setErrorMessage('Failed to fetch');
|
|
673
|
+
return { error: true, message: 'Failed to fetch', rows: [] };
|
|
1219
674
|
}
|
|
1220
|
-
|
|
1221
|
-
|
|
675
|
+
};
|
|
676
|
+
const fetchRowCountFromAST = async (ast, where) => {
|
|
677
|
+
setRowCountIsLoading(true);
|
|
678
|
+
if (!client) {
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
const tableData = await fetchTableByAST({ ...ast, where }, client, tenants, { page: REPORT_BUILDER_PAGINATION }, schemaData.customFields, false, true, destinationDashboard);
|
|
682
|
+
if (tableData.rowCount) {
|
|
683
|
+
setNumberOfRows(tableData.rowCount);
|
|
684
|
+
// @ts-ignore
|
|
685
|
+
setTempReport((tempReport) => ({
|
|
686
|
+
...tempReport,
|
|
687
|
+
rowCount: tableData.rowCount,
|
|
688
|
+
}));
|
|
1222
689
|
}
|
|
690
|
+
setRowCountIsLoading(false);
|
|
1223
691
|
};
|
|
1224
692
|
const fetchAstFromPromptHelper = async (overridePrompt) => {
|
|
1225
693
|
let astInfo = {};
|
|
1226
694
|
const prompt = overridePrompt || aiPrompt;
|
|
695
|
+
if (!client) {
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
1227
698
|
if (!prompt) {
|
|
1228
699
|
setErrorMessage('Please supply a prompt.');
|
|
1229
700
|
return;
|
|
1230
701
|
}
|
|
1231
702
|
try {
|
|
1232
|
-
|
|
703
|
+
setReportBuilderLoading(true);
|
|
1233
704
|
setAskAILoading(true);
|
|
1234
705
|
setErrorMessage('');
|
|
1235
|
-
astInfo = await fetchAndProcessASTFromPrompt(
|
|
706
|
+
astInfo = await fetchAndProcessASTFromPrompt({
|
|
707
|
+
aiPrompt: prompt,
|
|
708
|
+
schema: filteredSchema,
|
|
709
|
+
client,
|
|
710
|
+
prevPivot: pivot ?? undefined,
|
|
711
|
+
currentQuery: activeQuery,
|
|
712
|
+
prevTable: tables?.[0]?.name,
|
|
713
|
+
dashboardName: destinationDashboard,
|
|
714
|
+
tenants,
|
|
715
|
+
});
|
|
1236
716
|
if (astInfo.error) {
|
|
1237
717
|
throw new Error(astInfo.error);
|
|
1238
718
|
}
|
|
@@ -1241,246 +721,263 @@ export default function ReportBuilder({ initialTableName = '', onSubmitEditRepor
|
|
|
1241
721
|
if (err instanceof Error) {
|
|
1242
722
|
setErrorMessage(err.message);
|
|
1243
723
|
}
|
|
1244
|
-
|
|
724
|
+
setReportBuilderLoading(false);
|
|
1245
725
|
setAskAILoading(false);
|
|
1246
726
|
return;
|
|
1247
727
|
}
|
|
1248
728
|
// check if pivot works with ReportBuilder constraints
|
|
1249
|
-
if (
|
|
729
|
+
if (Object.keys(columnUniqueValues).length > 0 &&
|
|
1250
730
|
astInfo.pivot &&
|
|
1251
|
-
!isValidPivotForReport(astInfo.pivot)) {
|
|
731
|
+
!isValidPivotForReport(astInfo.pivot, columnUniqueValues, reportColumns)) {
|
|
1252
732
|
astInfo.pivot = null;
|
|
1253
733
|
astInfo.ast.groupby = null;
|
|
1254
734
|
}
|
|
1255
|
-
const _tables = getTableNames(astInfo.ast);
|
|
1256
|
-
const _table = _tables[0] ? _tables[0] : '';
|
|
1257
|
-
const _columns = schemaData.schemaWithCustomFields.find((table) => {
|
|
1258
|
-
return table.name === _table;
|
|
1259
|
-
})?.columns;
|
|
1260
|
-
// parse the whereAst first
|
|
1261
|
-
const filterTree = astToFilterTree(astInfo.whereAST, client.databaseType?.toLowerCase(), _columns);
|
|
1262
|
-
const cleanAst = filterTree
|
|
1263
|
-
? filterTreeToAst(filterTree, client.databaseType?.toLowerCase())
|
|
1264
|
-
: null;
|
|
1265
|
-
setFormData(cleanAst);
|
|
1266
|
-
astInfo.ast.where = cleanAst;
|
|
1267
|
-
setBaseAst(astInfo.ast);
|
|
1268
|
-
fetchSqlQuery(astInfo.ast, cleanAst, false);
|
|
1269
735
|
setAskAILoading(false);
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
736
|
+
const newState = astToReportBuilderState(astInfo.ast, client.databaseType || 'postgresql', filteredSchema);
|
|
737
|
+
handleMultiStateChange({ ...newState, pivot: astInfo.pivot }, true);
|
|
738
|
+
};
|
|
739
|
+
const fetchGlobalUniqueValues = async (columns, tables) => {
|
|
740
|
+
if (!client) {
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
setUnfilteredUniqueValuesIsLoading(true);
|
|
744
|
+
const uniqueStrings = await getUniqueStringValuesByTable({
|
|
745
|
+
columns,
|
|
746
|
+
tables,
|
|
747
|
+
client,
|
|
748
|
+
tenants,
|
|
749
|
+
customFields: schemaData.customFields ?? undefined,
|
|
750
|
+
withExceededColumns: true,
|
|
751
|
+
dashboardName: destinationDashboard,
|
|
1274
752
|
});
|
|
753
|
+
setUnfilteredUniqueValues(uniqueStrings);
|
|
754
|
+
setUnfilteredUniqueValuesIsLoading(false);
|
|
1275
755
|
};
|
|
1276
|
-
const
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
? (tempReport.yAxisFields[0]?.label ?? '')
|
|
1280
|
-
: (disambiguatedValueField(pivot) ?? '');
|
|
1281
|
-
// date labels for pivots should be treated like strings since they are
|
|
1282
|
-
const yAxisIsDate = pivot.columnField
|
|
1283
|
-
? isDateField(pivot.columnFieldType ?? '')
|
|
1284
|
-
: false;
|
|
1285
|
-
const chartType = tempReport?.chartType ?? (pivot.rowField ? 'column' : 'metric');
|
|
1286
|
-
const result = {
|
|
1287
|
-
pivot,
|
|
1288
|
-
chartType: chartType,
|
|
1289
|
-
xAxisField: pivot.rowField
|
|
1290
|
-
? pivot.rowField
|
|
1291
|
-
: disambiguatedValueField(pivot),
|
|
1292
|
-
xAxisFormat: isDateType(pivot.rowFieldType ?? '')
|
|
1293
|
-
? 'string'
|
|
1294
|
-
: isNumberType(pivot.rowFieldType ?? '')
|
|
1295
|
-
? 'whole_number'
|
|
1296
|
-
: 'string',
|
|
1297
|
-
xAxisLabel: tempReport?.xAxisLabel || pivot.rowField,
|
|
1298
|
-
yAxisFields: [
|
|
1299
|
-
{
|
|
1300
|
-
field: yAxisField,
|
|
1301
|
-
label: yAxisLabel,
|
|
1302
|
-
format: yAxisIsDate
|
|
1303
|
-
? 'string'
|
|
1304
|
-
: tempReport?.yAxisFields && tempReport?.yAxisFields?.length > 0
|
|
1305
|
-
? (tempReport?.yAxisFields[0]?.format ?? 'whole_number')
|
|
1306
|
-
: (tempReport?.columns.find((col) => col.field === disambiguatedValueField(pivot))?.format ?? 'whole_number'),
|
|
1307
|
-
},
|
|
1308
|
-
],
|
|
1309
|
-
};
|
|
1310
|
-
return result;
|
|
756
|
+
const resetProcessing = () => {
|
|
757
|
+
setCurrentProcessing({ page: REPORT_BUILDER_PAGINATION });
|
|
758
|
+
setPreviousPage(0);
|
|
1311
759
|
};
|
|
1312
|
-
const
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
760
|
+
const handlePagination = async (processing) => {
|
|
761
|
+
try {
|
|
762
|
+
if (!client) {
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
const isPivotPagination = !!(pivot && pivotData);
|
|
766
|
+
setErrorMessage('');
|
|
767
|
+
setTableLoading(true);
|
|
768
|
+
const tableInfo = await fetchResultsByQuery({
|
|
769
|
+
query: isPivotPagination ? pivotData.pivotQuery : activeQuery,
|
|
770
|
+
comparisonQuery: pivot && pivotData ? pivotData.comparisonPivotQuery : undefined,
|
|
771
|
+
client,
|
|
772
|
+
tenants,
|
|
773
|
+
processing,
|
|
774
|
+
customFields: schemaData.customFields,
|
|
775
|
+
rowsOnly: true,
|
|
776
|
+
dashboardName: destinationDashboard,
|
|
777
|
+
pivot: pivot,
|
|
778
|
+
getPivotRowCount: false,
|
|
779
|
+
});
|
|
780
|
+
if (tableInfo.error) {
|
|
781
|
+
throw new Error(tableInfo.error);
|
|
782
|
+
}
|
|
783
|
+
else if (tableInfo.rows.length === 0) {
|
|
784
|
+
throw new Error('No data found');
|
|
785
|
+
}
|
|
786
|
+
if (!isPivotPagination) {
|
|
787
|
+
const tempRows = [...reportRows, ...tableInfo.rows];
|
|
788
|
+
setReportRows(tempRows);
|
|
789
|
+
setFormattedRows(formatRowsFromReport({ rows: tempRows, columns: tableInfo.columns }));
|
|
790
|
+
setTempReport((tempReport) => ({
|
|
791
|
+
...tempReport,
|
|
792
|
+
rows: tempRows,
|
|
793
|
+
rowCount: tableInfo.rowCount ?? tempReport.rowCount,
|
|
794
|
+
}));
|
|
795
|
+
}
|
|
796
|
+
else {
|
|
797
|
+
const tempRows = [...pivotData.rows, ...tableInfo.rows];
|
|
798
|
+
setPivotData((oldPivotData) => {
|
|
799
|
+
if (oldPivotData) {
|
|
800
|
+
return {
|
|
801
|
+
...oldPivotData,
|
|
802
|
+
rows: tempRows,
|
|
803
|
+
columns: tableInfo.columns,
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
return null;
|
|
807
|
+
});
|
|
808
|
+
setFormattedRows(formatRowsFromReport({ rows: tempRows, columns: tableInfo.columns }));
|
|
809
|
+
}
|
|
810
|
+
setTableLoading(false);
|
|
1327
811
|
}
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
if (isValidPivotForReport(newPivot, uniqueValuesForPivot, reportTable, reportColumns)) {
|
|
1335
|
-
return {
|
|
1336
|
-
pivot: newPivot,
|
|
1337
|
-
hint: 'Removing column field to maintain validity',
|
|
1338
|
-
};
|
|
812
|
+
catch (e) {
|
|
813
|
+
setTableLoading(false);
|
|
814
|
+
setErrorMessage('Failed to run SQL query: ' + e.message);
|
|
815
|
+
setReportRows([]);
|
|
816
|
+
setReportColumns([]);
|
|
817
|
+
return;
|
|
1339
818
|
}
|
|
1340
|
-
return { pivot: null, hint: '' };
|
|
1341
819
|
};
|
|
1342
|
-
const
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
pivot.columnField &&
|
|
1353
|
-
pivot.rowField === pivot.columnField) {
|
|
1354
|
-
return false;
|
|
820
|
+
const onPageChange = (page, initiator = 'ReportBuilder') => {
|
|
821
|
+
const pagination = initiator === 'ReportBuilder'
|
|
822
|
+
? REPORT_BUILDER_PAGINATION
|
|
823
|
+
: DEFAULT_PAGINATION;
|
|
824
|
+
if (currentProcessing.page &&
|
|
825
|
+
shouldFetchMore(pagination, page, previousPage, pivotData ? pivotData.rows.length : reportRows.length)) {
|
|
826
|
+
const newPagination = { ...currentProcessing.page, page };
|
|
827
|
+
const updatedProcessing = { ...currentProcessing, page: newPagination };
|
|
828
|
+
setCurrentProcessing(updatedProcessing);
|
|
829
|
+
handlePagination(updatedProcessing);
|
|
1355
830
|
}
|
|
1356
|
-
if (
|
|
1357
|
-
|
|
831
|
+
if (page > previousPage) {
|
|
832
|
+
setPreviousPage(page);
|
|
1358
833
|
}
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
834
|
+
};
|
|
835
|
+
const onSortChange = (newSort, isDelete) => {
|
|
836
|
+
if (!newSort.field) {
|
|
837
|
+
return;
|
|
1362
838
|
}
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
839
|
+
if (pivot) {
|
|
840
|
+
let newPivot = null;
|
|
841
|
+
if (isDelete) {
|
|
842
|
+
newPivot = {
|
|
843
|
+
...pivot,
|
|
844
|
+
sort: undefined,
|
|
845
|
+
sortField: undefined,
|
|
846
|
+
sortDirection: undefined,
|
|
847
|
+
sortFieldType: undefined,
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
else {
|
|
851
|
+
newPivot = {
|
|
852
|
+
...pivot,
|
|
853
|
+
sort: true,
|
|
854
|
+
sortField: newSort.field,
|
|
855
|
+
sortDirection: newSort.direction,
|
|
856
|
+
sortFieldType: reportColumns.find((col) => col.field === newSort.field)?.fieldType,
|
|
857
|
+
};
|
|
1367
858
|
}
|
|
859
|
+
handlePivotChange(newPivot, true);
|
|
1368
860
|
}
|
|
1369
|
-
|
|
1370
|
-
|
|
861
|
+
else {
|
|
862
|
+
const updatedSort = [...sort];
|
|
863
|
+
const existingSortIndex = updatedSort.findIndex((item) => item.field === newSort.field);
|
|
864
|
+
if (isDelete) {
|
|
865
|
+
if (existingSortIndex !== -1) {
|
|
866
|
+
updatedSort.splice(existingSortIndex, 1);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
else if (existingSortIndex !== -1) {
|
|
870
|
+
updatedSort[existingSortIndex] = {
|
|
871
|
+
field: newSort.field,
|
|
872
|
+
direction: newSort.direction,
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
else {
|
|
876
|
+
updatedSort.push({
|
|
877
|
+
field: newSort.field,
|
|
878
|
+
direction: newSort.direction,
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
handleSortChange(updatedSort, true);
|
|
1371
882
|
}
|
|
1372
|
-
return true;
|
|
1373
883
|
};
|
|
1374
|
-
const
|
|
1375
|
-
if (
|
|
1376
|
-
|
|
1377
|
-
|
|
884
|
+
const onLimitChange = (limit) => {
|
|
885
|
+
if (limit) {
|
|
886
|
+
if (pivot) {
|
|
887
|
+
const newPivot = { ...pivot, rowLimit: limit };
|
|
888
|
+
handlePivotChange(newPivot, true);
|
|
889
|
+
}
|
|
890
|
+
else {
|
|
891
|
+
handleLimitChange({ value: limit }, true);
|
|
892
|
+
}
|
|
1378
893
|
}
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
(!col.expr.column.expr || col.expr.column.expr.value !== name));
|
|
894
|
+
else {
|
|
895
|
+
if (pivot) {
|
|
896
|
+
const newPivot = { ...pivot, rowLimit: undefined };
|
|
897
|
+
handlePivotChange(newPivot, true);
|
|
1384
898
|
}
|
|
1385
|
-
else
|
|
1386
|
-
|
|
899
|
+
else {
|
|
900
|
+
handleLimitChange(null, true);
|
|
1387
901
|
}
|
|
1388
|
-
return col.expr.value !== name;
|
|
1389
|
-
});
|
|
1390
|
-
if (columns.length === 0) {
|
|
1391
|
-
clearAllState();
|
|
1392
|
-
return;
|
|
1393
902
|
}
|
|
1394
|
-
const newAst = deepCopy({ ...baseAst, columns });
|
|
1395
|
-
setBaseAst(newAst);
|
|
1396
|
-
fetchSqlQuery(newAst);
|
|
1397
903
|
};
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
904
|
+
useEffect(() => {
|
|
905
|
+
// Since the TextInput component takes a required numeric width parameter,
|
|
906
|
+
// we dynamically calculate the width of this component here.
|
|
907
|
+
function handleResize() {
|
|
908
|
+
updateFirstChildWidth(askAIContainerRef, setAskAIInputWidth, { gap: 12 });
|
|
909
|
+
}
|
|
910
|
+
handleResize();
|
|
911
|
+
window.addEventListener('resize', handleResize);
|
|
912
|
+
return () => {
|
|
913
|
+
window.removeEventListener('resize', handleResize);
|
|
1403
914
|
};
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
}
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
915
|
+
}, []);
|
|
916
|
+
useEffect(() => {
|
|
917
|
+
if (!client) {
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
if (client.featureFlags?.['recommendedPivotsDisabled'] !== undefined) {
|
|
921
|
+
setPivotRecommendationsEnabledState(!client.featureFlags?.['recommendedPivotsDisabled']);
|
|
922
|
+
}
|
|
923
|
+
if (!initialTableName && !reportId && client.publicKey) {
|
|
924
|
+
clearAllState();
|
|
925
|
+
}
|
|
926
|
+
}, [client]);
|
|
927
|
+
// Initialize ReportBuilder with a report
|
|
928
|
+
useEffect(() => {
|
|
929
|
+
const loadChart = async () => {
|
|
930
|
+
let report;
|
|
931
|
+
if (!client) {
|
|
1416
932
|
return;
|
|
1417
|
-
if (active.id !== over.id) {
|
|
1418
|
-
const oldIndex = orderedColumnNames.findIndex((c) => c.endsWith(`${currentTable}.${active.id}`));
|
|
1419
|
-
const newIndex = orderedColumnNames.findIndex((c) => c.endsWith(`${currentTable}.${over.id}`));
|
|
1420
|
-
const newOrder = arrayMove(orderedColumnNames, oldIndex, newIndex);
|
|
1421
|
-
setOrderedColumnNames(newOrder);
|
|
1422
|
-
const orderedSelectedColumns = [];
|
|
1423
|
-
for (const value of newOrder) {
|
|
1424
|
-
const [, ...rest] = value.split('.'); // table.field
|
|
1425
|
-
const column = rest.join('.');
|
|
1426
|
-
if (selectedColumns.includes(value)) {
|
|
1427
|
-
orderedSelectedColumns.push(column);
|
|
1428
|
-
}
|
|
1429
|
-
}
|
|
1430
|
-
setSelectedOrderedColumns(orderedSelectedColumns);
|
|
1431
|
-
// If there is already an AST saved in state, only update the columns
|
|
1432
|
-
// otherwise fill in the defaultAST shape and also update columns.
|
|
1433
|
-
const fallbackAST = {
|
|
1434
|
-
...defaultAST,
|
|
1435
|
-
from: [{ ...defaultTable }],
|
|
1436
|
-
columns: orderedSelectedColumns.map((name) => nameToColumn(name)),
|
|
1437
|
-
};
|
|
1438
|
-
const newBaseAst = {
|
|
1439
|
-
...baseAst,
|
|
1440
|
-
columns: baseAst?.columns.length
|
|
1441
|
-
? orderedSelectedColumns.map((name) => nameToColumn(name))
|
|
1442
|
-
: baseAst?.columns,
|
|
1443
|
-
};
|
|
1444
|
-
const newAst = baseAst ? newBaseAst : fallbackAST;
|
|
1445
|
-
setBaseAst(newAst);
|
|
1446
|
-
fetchSqlQuery(newAst, undefined, false);
|
|
1447
933
|
}
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
if (col.expr.type === 'column_ref' && col.expr.column) {
|
|
1452
|
-
if (typeof col.expr.column === 'string') {
|
|
1453
|
-
return col.expr.column;
|
|
934
|
+
try {
|
|
935
|
+
if (!reportId) {
|
|
936
|
+
throw new Error('Report ID is required');
|
|
1454
937
|
}
|
|
1455
|
-
|
|
1456
|
-
|
|
938
|
+
report = dashboard[reportId];
|
|
939
|
+
if (!report) {
|
|
940
|
+
throw new Error('Report not found');
|
|
1457
941
|
}
|
|
942
|
+
const { ast: newAst, pivot: newPivot } = await fetchASTFromQuillReport(report, client, filteredSchema);
|
|
943
|
+
const initialState = astToReportBuilderState(newAst, client.databaseType || 'postgresql', filteredSchema);
|
|
944
|
+
setTempReport(report);
|
|
945
|
+
handleMultiStateChange({
|
|
946
|
+
...initialState,
|
|
947
|
+
pivot: newPivot ?? null,
|
|
948
|
+
}, true);
|
|
1458
949
|
}
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
}
|
|
1463
|
-
else {
|
|
1464
|
-
return col.as.expr?.value;
|
|
1465
|
-
}
|
|
950
|
+
catch (err) {
|
|
951
|
+
console.error(err);
|
|
952
|
+
setErrorMessage('Error when loading chart');
|
|
1466
953
|
}
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
954
|
+
};
|
|
955
|
+
if (reportId && client) {
|
|
956
|
+
loadChart();
|
|
957
|
+
}
|
|
958
|
+
}, [dashboard[reportId || ''], client]);
|
|
959
|
+
useEffect(() => {
|
|
960
|
+
if (initialTableName) {
|
|
961
|
+
const tableColumns = filteredSchema.find((table) => {
|
|
962
|
+
return table.name === initialTableName;
|
|
963
|
+
})?.columns ?? [];
|
|
964
|
+
if (tableColumns.length > 0) {
|
|
965
|
+
handleMultiStateChange({
|
|
966
|
+
...EMPTY_REPORT_BUILDER_STATE,
|
|
967
|
+
tables: [{ name: initialTableName }],
|
|
968
|
+
columns: tableColumns.map((col) => ({
|
|
969
|
+
field: col.field,
|
|
970
|
+
table: initialTableName,
|
|
971
|
+
})),
|
|
972
|
+
}, true);
|
|
1472
973
|
}
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
flexDirection: 'column',
|
|
1481
|
-
gap: 8,
|
|
1482
|
-
}, children: [columnNamesInAst.map((name) => (_jsx(DraggableItem, { id: name, label: name, onDelete: () => !loading && handleDeleteColumn(name) }, name))), columnNamesInAst?.length > 0 && _jsx("div", { style: { height: 6 } })] }) }) }));
|
|
1483
|
-
}
|
|
974
|
+
}
|
|
975
|
+
}, [filteredSchema, initialTableName]);
|
|
976
|
+
useEffect(() => {
|
|
977
|
+
if (isChartBuilderOpen === false) {
|
|
978
|
+
onCloseChartBuilder && onCloseChartBuilder();
|
|
979
|
+
}
|
|
980
|
+
}, [isChartBuilderOpen]);
|
|
1484
981
|
return (_jsxs("div", { style: { backgroundColor: theme?.backgroundColor, ...containerStyle }, className: className, children: [(!isChartBuilderHorizontalView ||
|
|
1485
982
|
(isChartBuilderHorizontalView && !isChartBuilderOpen)) && (_jsxs("div", { ref: parentRef, style: {
|
|
1486
983
|
display: 'flex',
|
|
@@ -1489,72 +986,37 @@ export default function ReportBuilder({ initialTableName = '', onSubmitEditRepor
|
|
|
1489
986
|
overflowY: 'auto',
|
|
1490
987
|
boxSizing: 'border-box',
|
|
1491
988
|
...containerStyle,
|
|
1492
|
-
}, className: className, children: [_jsxs(SidebarComponent, { children: [_jsxs("div", { style: { width: '100%' }, children: [_jsx(SidebarHeadingComponent, { label: "Columns" }), _jsx(DraggableColumns, {}), _jsx(SecondaryButtonComponent, { onClick: () => {
|
|
1493
|
-
if (!orderedColumnNames) {
|
|
1494
|
-
return;
|
|
1495
|
-
}
|
|
989
|
+
}, className: className, children: [_jsxs(SidebarComponent, { children: [_jsxs("div", { style: { width: '100%' }, children: [_jsx(SidebarHeadingComponent, { label: "Columns" }), _jsx(DraggableColumns, { columns: columns, DraggableColumnComponent: DraggableColumnComponent, onColumnOrderChange: handleColumnsChange, loading: loading }), _jsx(SecondaryButtonComponent, { onClick: () => {
|
|
1496
990
|
if (!openPopover) {
|
|
1497
991
|
setOpenPopover('AddColumnModal');
|
|
1498
992
|
}
|
|
1499
|
-
}, label: "Select columns", disabled:
|
|
993
|
+
}, label: "Select columns", disabled: loading }), _jsx(ModalComponent, { isOpen: openPopover === 'AddColumnModal', setIsOpen: (isOpen) => {
|
|
1500
994
|
if (!isOpen) {
|
|
1501
995
|
// delay onClose callback so onClick no-ops
|
|
1502
996
|
setTimeout(() => {
|
|
1503
|
-
setActiveEditItem(null);
|
|
1504
997
|
setOpenPopover(null);
|
|
1505
998
|
}, 100);
|
|
1506
999
|
}
|
|
1507
|
-
}, title: "Select columns", children: _jsx(
|
|
1508
|
-
|
|
1000
|
+
}, title: "Select columns", children: _jsx(NewAddColumnModal, { onSave: (tables, columns) => {
|
|
1001
|
+
handleMultiStateChange({
|
|
1002
|
+
...EMPTY_REPORT_BUILDER_STATE,
|
|
1003
|
+
tables,
|
|
1004
|
+
columns,
|
|
1005
|
+
}, true);
|
|
1509
1006
|
setOpenPopover(null);
|
|
1510
|
-
},
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
// table changed, past presets no longer valid
|
|
1514
|
-
setFormData(null);
|
|
1515
|
-
setRecommendedPivots([]);
|
|
1516
|
-
setCreatedPivots([]);
|
|
1517
|
-
ast.where = null;
|
|
1518
|
-
ast.orderby = null;
|
|
1519
|
-
ast.limit = null;
|
|
1520
|
-
ast.top = null;
|
|
1521
|
-
setBaseAst(ast);
|
|
1522
|
-
fetchSqlQuery(ast, null);
|
|
1523
|
-
}
|
|
1524
|
-
else {
|
|
1525
|
-
setBaseAst(ast);
|
|
1526
|
-
fetchSqlQuery(ast);
|
|
1527
|
-
}
|
|
1528
|
-
}, pivot: pivot, initialTableName: initialTableName, defaultAST: defaultAST, defaultTable: defaultTable, schemaLoading: schemaData.isSchemaLoading, setPivot: setPivot, TextInputComponent: TextInputComponent, SelectColumn: SelectColumnComponent, SecondaryButton: SecondaryButtonComponent, Button: ButtonComponent, ColumnSearchEmptyState: ColumnSearchEmptyState, LoadingComponent: LoadingComponent }) })] }), _jsxs("div", { style: { width: '100%' }, children: [_jsx(SidebarHeadingComponent, { label: "Filters" }), formData && (_jsx("div", { style: {
|
|
1007
|
+
},
|
|
1008
|
+
// selectedTables={tables} // Bring back after select table join UI
|
|
1009
|
+
selectedColumns: columns, schema: filteredSchema, schemaLoading: schemaData.isSchemaLoading, TextInputComponent: TextInputComponent, SelectColumn: SelectColumnComponent, SecondaryButton: SecondaryButtonComponent, Button: ButtonComponent, ColumnSearchEmptyState: ColumnSearchEmptyState, LoadingComponent: LoadingComponent }) })] }), _jsxs("div", { style: { width: '100%' }, children: [_jsx(SidebarHeadingComponent, { label: "Filters" }), filterStack.length > 0 && (_jsx("div", { style: {
|
|
1529
1010
|
display: 'flex',
|
|
1530
1011
|
flexDirection: 'column',
|
|
1531
1012
|
gap: 8,
|
|
1532
1013
|
marginBottom: 12,
|
|
1533
|
-
}, children: _jsx(FilterStack, { client: client, filterStack: filterStack,
|
|
1014
|
+
}, children: _jsx(FilterStack, { client: client, filterStack: filterStack, handleFilterStackChange: handleFilterStackChange, schemaData: schemaData, uniqueValues: unfilteredUniqueValues, uniqueValuesIsLoading: unfilteredUniqueValuesIsLoading, tables: tables, TabsComponent: TabsComponent, FilterPopoverComponent: FilterPopoverComponent, FilterModal: FilterModal, ButtonComponent: ButtonComponent, SecondaryButtonComponent: SecondaryButtonComponent, SelectComponent: SelectComponent, TextInputComponent: TextInputComponent, MultiSelectComponent: MultiSelectComponent, actionsEnabled: !loading, dashboardName: destinationDashboard }) })), _jsxs("div", { style: {
|
|
1534
1015
|
display: 'flex',
|
|
1535
1016
|
flexDirection: 'column',
|
|
1536
1017
|
alignItems: 'flex-start',
|
|
1537
|
-
}, children: [_jsx(SecondaryButtonComponent, { disabled:
|
|
1538
|
-
if (!selectedColumns ||
|
|
1539
|
-
selectedColumns.length === 0 ||
|
|
1540
|
-
loading) {
|
|
1541
|
-
return;
|
|
1542
|
-
}
|
|
1018
|
+
}, children: [_jsx(SecondaryButtonComponent, { disabled: columns.length === 0 || loading, onClick: () => {
|
|
1543
1019
|
if (!openPopover) {
|
|
1544
|
-
const value = orderedColumnNames[0];
|
|
1545
|
-
const [, ...rest] = value.split('.'); // table.field
|
|
1546
|
-
const column = rest.join('.');
|
|
1547
|
-
const columnType = getColumnTypeByName(column);
|
|
1548
|
-
if (isNumericColumnType(columnType)) {
|
|
1549
|
-
const newSubtree = deepCopy(defaultNumericComparison);
|
|
1550
|
-
newSubtree.left.column = column;
|
|
1551
|
-
setActiveEditItem(newSubtree);
|
|
1552
|
-
}
|
|
1553
|
-
else {
|
|
1554
|
-
const newSubtree = deepCopy(defaultEntry);
|
|
1555
|
-
newSubtree.left.args.value[0].column = column;
|
|
1556
|
-
setActiveEditItem(newSubtree);
|
|
1557
|
-
}
|
|
1558
1020
|
setOpenPopover('AddFilterPopover');
|
|
1559
1021
|
}
|
|
1560
1022
|
}, label: 'Add filter' }), _jsx("div", { style: {
|
|
@@ -1562,134 +1024,56 @@ export default function ReportBuilder({ initialTableName = '', onSubmitEditRepor
|
|
|
1562
1024
|
...(openPopover === 'AddFilterPopover' && { top: 12 }),
|
|
1563
1025
|
}, children: _jsx(PopoverComponent, { isOpen: openPopover === 'AddFilterPopover', setIsOpen: (isOpen) => {
|
|
1564
1026
|
if (!isOpen) {
|
|
1565
|
-
// delay onClose callback so onClick no-ops
|
|
1566
1027
|
setOpenPopover(null);
|
|
1567
|
-
setTimeout(() => {
|
|
1568
|
-
clearCheckboxes();
|
|
1569
|
-
setActiveEditItem(null);
|
|
1570
|
-
}, 300);
|
|
1571
1028
|
}
|
|
1572
|
-
}, popoverTitle: "Add filter", popoverChildren: _jsx(FilterModal, { schema:
|
|
1573
|
-
|
|
1029
|
+
}, popoverTitle: "Add filter", popoverChildren: _jsx(FilterModal, { schema: filteredSchema, tables: tables.map((table) => table.name), fieldValuesMap: filteredUniqueValues ?? unfilteredUniqueValues, fieldValuesMapIsLoading: filteredUniqueValuesIsLoading ||
|
|
1030
|
+
unfilteredUniqueValuesIsLoading, onSubmitFilter: (filter) => {
|
|
1574
1031
|
setOpenPopover(null);
|
|
1575
|
-
|
|
1576
|
-
}, onDeleteFilter: () => { }, ButtonComponent: ButtonComponent, SelectComponent: SelectComponent, TextInputComponent: TextInputComponent, MultiSelectComponent: MultiSelectComponent }) }) })] })] }), _jsxs("div", { style: { width: '100%' }, children: [_jsx(SidebarHeadingComponent, { label: "Pivot" }), _jsx(PivotModal, { pivotRowField: pivotRowField, setPivotRowField: setPivotRowField, pivotColumnField: pivotColumnField, setPivotColumnField: setPivotColumnField, pivotAggregations: pivotAggregations, setPivotAggregations: setPivotAggregations, createdPivots: createdPivots, setCreatedPivots: setCreatedPivots, recommendedPivots: recommendedPivots, setRecommendedPivots: setRecommendedPivots, popUpTitle: pivotPopUpTitle, setPopUpTitle: setPivotPopUpTitle, selectedTable: initialTableName, SubheaderComponent: SubHeaderComponent, DeleteButtonComponent: DeleteButtonComponent, SelectComponent: SelectComponent, ButtonComponent: ButtonComponent, CardComponent: CardComponent, SecondaryButtonComponent: SecondaryButtonComponent, PopoverComponent: PopoverComponent, TextComponent: TextComponent, ErrorMessageComponent: ErrorMessageComponent, PivotRowContainer: PivotRowContainer, PivotColumnContainer: PivotColumnContainer, LoadingComponent: LoadingComponent, isOpen: showPivotPopover, setIsOpen: setShowPivotPopover, showUpdatePivot: isEditingPivot, setShowUpdatePivot: setIsEditingPivot, parentRef: parentRef, data:
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
setPivotData(null);
|
|
1580
|
-
resetLimit();
|
|
1581
|
-
resetSort(true);
|
|
1582
|
-
setPreviousPage(0);
|
|
1583
|
-
const formattedRows = formatRows(rows, columns, false);
|
|
1584
|
-
setFormattedRows(formattedRows);
|
|
1585
|
-
}, selectPivot: async (selectedPivot, uniqueValues, dateRange, pivotTable) => {
|
|
1032
|
+
handleFilterInsertion(filter);
|
|
1033
|
+
}, hideTableName: tables.length === 1, onDeleteFilter: () => { }, ButtonComponent: ButtonComponent, SelectComponent: SelectComponent, TextInputComponent: TextInputComponent, MultiSelectComponent: MultiSelectComponent }) }) })] })] }), _jsxs("div", { style: { width: '100%' }, children: [_jsx(SidebarHeadingComponent, { label: "Pivot" }), _jsx(PivotModal, { pivotRowField: pivotRowField, setPivotRowField: setPivotRowField, pivotColumnField: pivotColumnField, setPivotColumnField: setPivotColumnField, pivotAggregations: pivotAggregations, setPivotAggregations: setPivotAggregations, createdPivots: createdPivots, setCreatedPivots: setCreatedPivots, recommendedPivots: recommendedPivots, setRecommendedPivots: setRecommendedPivots, popUpTitle: pivotPopUpTitle, setPopUpTitle: setPivotPopUpTitle, selectedTable: initialTableName, SubheaderComponent: SubHeaderComponent, DeleteButtonComponent: DeleteButtonComponent, SelectComponent: SelectComponent, ButtonComponent: ButtonComponent, CardComponent: CardComponent, SecondaryButtonComponent: SecondaryButtonComponent, PopoverComponent: PopoverComponent, TextComponent: TextComponent, ErrorMessageComponent: ErrorMessageComponent, PivotRowContainer: PivotRowContainer, PivotColumnContainer: PivotColumnContainer, LoadingComponent: LoadingComponent, isOpen: showPivotPopover, setIsOpen: setShowPivotPopover, showUpdatePivot: isEditingPivot, setShowUpdatePivot: setIsEditingPivot, parentRef: parentRef, data: reportRows, columns: reportColumnsToStateColumns, triggerButtonText: 'Add pivot', selectedPivotIndex: selectedPivotIndex, setSelectedPivotIndex: setSelectedPivotIndex, removePivot: () => {
|
|
1034
|
+
handlePivotChange(null, sortOrLimitWasReset);
|
|
1035
|
+
}, selectPivot: async (selectedPivot) => {
|
|
1586
1036
|
if (!selectedPivot)
|
|
1587
1037
|
return;
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
selectedPivot['sortDirection'] = 'ASC';
|
|
1593
|
-
}
|
|
1594
|
-
// setBaseAst(newAst); // trigger refetch
|
|
1595
|
-
let dateBucket = undefined;
|
|
1596
|
-
if (dateRange) {
|
|
1597
|
-
dateBucket = getDateBucketFromRange(dateRange);
|
|
1598
|
-
}
|
|
1599
|
-
const processedPivot = setTypesOnPivot(selectedPivot, columns);
|
|
1600
|
-
setPivot(processedPivot);
|
|
1601
|
-
resetLimit();
|
|
1602
|
-
resetSort();
|
|
1603
|
-
setPreviousPage(0);
|
|
1604
|
-
setPivotHint('');
|
|
1605
|
-
try {
|
|
1606
|
-
if (!pivotTable) {
|
|
1607
|
-
setTableLoading(true);
|
|
1608
|
-
pivotTable = await generatePivotTable({
|
|
1609
|
-
pivot: processedPivot,
|
|
1610
|
-
dateBucket,
|
|
1611
|
-
report: tempReport,
|
|
1612
|
-
client,
|
|
1613
|
-
uniqueValues,
|
|
1614
|
-
dashboardName,
|
|
1615
|
-
tenants,
|
|
1616
|
-
additionalProcessing: {
|
|
1617
|
-
page: REPORT_BUILDER_PAGINATION,
|
|
1618
|
-
},
|
|
1619
|
-
});
|
|
1620
|
-
}
|
|
1621
|
-
resetProcessing();
|
|
1622
|
-
setPivotData(pivotTable || []);
|
|
1623
|
-
setTempReport({
|
|
1624
|
-
...tempReport,
|
|
1625
|
-
...pivotFormData(processedPivot),
|
|
1626
|
-
});
|
|
1627
|
-
const formattedRows = formatRows(pivotTable.rows, columns, true, selectedPivot.aggregationType, dateBucket);
|
|
1628
|
-
setFormattedRows(formattedRows);
|
|
1629
|
-
setErrorMessage('');
|
|
1630
|
-
setPivotError(undefined);
|
|
1631
|
-
}
|
|
1632
|
-
catch (e) {
|
|
1633
|
-
if (e instanceof Error)
|
|
1634
|
-
setPivotError(e.message);
|
|
1635
|
-
}
|
|
1636
|
-
finally {
|
|
1637
|
-
setTableLoading(false);
|
|
1638
|
-
}
|
|
1639
|
-
}, selectPivotOnEdit: true, showTrigger: !pivot, theme: theme, LabelComponent: LabelComponent, HeaderComponent: HeaderComponent, dateRange: undefined, pivotCountRequest: 4, query: activeQuery, initialUniqueValues: uniqueValues[currentTable], uniqueValuesIsLoading: uniqueValuesIsLoading, disabled: !baseAst || !dataDisplayed || tableLoading || loading, pivotRecommendationsEnabled: pivotRecommendationsEnabledState, report: tempReport, dashboardName: dashboardName }), pivot && (_jsx(PivotForm, { columns: columns, uniqueValues: uniqueValues[currentTable], uniqueValuesIsLoading: uniqueValuesIsLoading, setPivotRowField: (value) => {
|
|
1640
|
-
const prev = pivotRowField;
|
|
1038
|
+
handlePivotChange(selectedPivot, true);
|
|
1039
|
+
}, selectPivotOnEdit: true, showTrigger: !pivot, theme: theme, LabelComponent: LabelComponent, HeaderComponent: HeaderComponent, dateRange: undefined, pivotCountRequest: 4, query: activeQuery, initialUniqueValues: columnUniqueValues, uniqueValuesIsLoading: filteredUniqueValuesIsLoading ||
|
|
1040
|
+
unfilteredUniqueValuesIsLoading, disabled: !columns.length || tableLoading || loading, pivotRecommendationsEnabled: pivotRecommendationsEnabledState, report: tempReport, dashboardName: destinationDashboard }), pivot && (_jsx(PivotForm, { columns: reportColumnsToStateColumns, uniqueValues: columnUniqueValues, uniqueValuesIsLoading: filteredUniqueValuesIsLoading ||
|
|
1041
|
+
unfilteredUniqueValuesIsLoading, setPivotRowField: (value) => {
|
|
1641
1042
|
setPivotRowField(value);
|
|
1642
|
-
updatePivot(value, 'rowField'
|
|
1043
|
+
updatePivot(value, 'rowField');
|
|
1643
1044
|
}, setPivotColumnField: (value) => {
|
|
1644
|
-
const prev = pivotColumnField;
|
|
1645
1045
|
setPivotColumnField(value);
|
|
1646
|
-
updatePivot(value, 'columnField'
|
|
1046
|
+
updatePivot(value, 'columnField');
|
|
1647
1047
|
}, setPivotAggregations: (value) => {
|
|
1648
|
-
const prev = pivotAggregations;
|
|
1649
1048
|
setPivotAggregations(value);
|
|
1650
|
-
updatePivot(value, 'aggregations'
|
|
1049
|
+
updatePivot(value, 'aggregations');
|
|
1651
1050
|
}, onDelete: () => {
|
|
1652
|
-
|
|
1653
|
-
setPivotError(undefined);
|
|
1654
|
-
setPivotHint('');
|
|
1655
|
-
setPivotData(null);
|
|
1656
|
-
resetLimit();
|
|
1657
|
-
resetSort();
|
|
1658
|
-
setPreviousPage(0);
|
|
1659
|
-
const formattedRows = formatRows(rows, columns, false);
|
|
1660
|
-
setFormattedRows(formattedRows);
|
|
1051
|
+
handlePivotChange(null, sortOrLimitWasReset);
|
|
1661
1052
|
}, isLoading: tableLoading || loading, pivotRowField: pivotRowField, pivotColumnField: pivotColumnField, pivotAggregations: pivotAggregations, SecondaryButtonComponent: SecondaryButtonComponent, SelectComponent: SelectComponent, PivotColumnContainer: PivotColumnContainer, DeleteButtonComponent: DeleteButtonComponent, pivotHint: pivotHint }))] }), _jsxs("div", { style: { width: '100%' }, children: [_jsx(SidebarHeadingComponent, { label: "Sort" }), pivot && pivot.sort && pivot.sortField && (_jsx("div", { style: {
|
|
1662
1053
|
display: 'flex',
|
|
1663
1054
|
flexDirection: 'column',
|
|
1664
1055
|
gap: 8,
|
|
1665
1056
|
marginBottom: 12,
|
|
1666
|
-
}, children: _jsx(SortSentence, { sortField: pivot.sortField, sortDirection: pivot.sortDirection || 'ASC', columns: pivotData?.columns ?? [],
|
|
1057
|
+
}, children: _jsx(SortSentence, { sortField: pivot.sortField, sortDirection: pivot.sortDirection || 'ASC', columns: pivotData?.columns ?? [], setOpenPopover: setOpenPopover, SortPopover: SortPopoverComponent, EditPopover: AddSortPopover, handleDelete: async () => {
|
|
1667
1058
|
onSortChange({
|
|
1668
1059
|
field: pivot.sortField ?? '',
|
|
1669
1060
|
direction: pivot.sortDirection ?? 'ASC',
|
|
1670
1061
|
}, true);
|
|
1671
1062
|
}, onSave: async (column, direction) => {
|
|
1672
1063
|
onSortChange({ field: column, direction });
|
|
1673
|
-
}, Select: SelectComponent, Button: ButtonComponent, SecondaryButton: SecondaryButtonComponent, disabled: tableLoading || loading }, `sort-sentence-pivot`) })),
|
|
1064
|
+
}, Select: SelectComponent, Button: ButtonComponent, SecondaryButton: SecondaryButtonComponent, disabled: tableLoading || loading }, `sort-sentence-pivot`) })), sort && sort.length > 0 && (_jsx("div", { style: {
|
|
1674
1065
|
display: 'flex',
|
|
1675
1066
|
flexDirection: 'column',
|
|
1676
1067
|
gap: 8,
|
|
1677
1068
|
marginBottom: 12,
|
|
1678
|
-
}, children:
|
|
1679
|
-
onSortChange(
|
|
1680
|
-
field: sortData.expr?.column || sortData.expr?.value,
|
|
1681
|
-
direction: sortData.type,
|
|
1682
|
-
}, true);
|
|
1069
|
+
}, children: sort.map((sortData, id) => (_jsx(SortSentence, { sortField: sortData.field, sortDirection: sortData.direction, columns: reportColumnsToStateColumns, setOpenPopover: setOpenPopover, SortPopover: SortPopoverComponent, EditPopover: AddSortPopover, handleDelete: () => {
|
|
1070
|
+
onSortChange(sortData, true);
|
|
1683
1071
|
}, onSave: (column, direction) => {
|
|
1684
1072
|
onSortChange({ field: column, direction });
|
|
1685
|
-
}, Select: SelectComponent, Button: ButtonComponent, SecondaryButton: SecondaryButtonComponent, disabled: tableLoading || loading }, `sort-sentence-${id}`))) })), _jsx(SecondaryButtonComponent, { disabled:
|
|
1686
|
-
!dataDisplayed ||
|
|
1073
|
+
}, Select: SelectComponent, Button: ButtonComponent, SecondaryButton: SecondaryButtonComponent, disabled: tableLoading || loading }, `sort-sentence-${id}`))) })), _jsx(SecondaryButtonComponent, { disabled: columns.length === 0 ||
|
|
1687
1074
|
loading ||
|
|
1688
1075
|
tableLoading ||
|
|
1689
|
-
mssqlSortWarning, onClick: () => {
|
|
1690
|
-
if (!selectedColumns || selectedColumns.length === 0) {
|
|
1691
|
-
return;
|
|
1692
|
-
}
|
|
1076
|
+
!!mssqlSortWarning, onClick: () => {
|
|
1693
1077
|
if (!openPopover) {
|
|
1694
1078
|
setOpenPopover('AddSortPopover');
|
|
1695
1079
|
}
|
|
@@ -1698,103 +1082,25 @@ export default function ReportBuilder({ initialTableName = '', onSubmitEditRepor
|
|
|
1698
1082
|
...(openPopover === 'AddSortPopover' && { top: 12 }),
|
|
1699
1083
|
}, children: _jsx(PopoverComponent, { isOpen: openPopover === 'AddSortPopover', setIsOpen: (isOpen) => {
|
|
1700
1084
|
if (!isOpen) {
|
|
1701
|
-
setActiveEditItem(null);
|
|
1702
1085
|
setOpenPopover(null);
|
|
1703
1086
|
}
|
|
1704
|
-
}, popoverTitle: "Sort by", popoverChildren: _jsx(AddSortPopover, { columns: pivotData
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
: pivot.rowFieldType;
|
|
1711
|
-
const tempPivot = setTypesOnPivot({
|
|
1712
|
-
...pivot,
|
|
1713
|
-
sort: true,
|
|
1714
|
-
sortDirection: direction,
|
|
1715
|
-
sortField: column,
|
|
1716
|
-
sortFieldType: sortFieldType,
|
|
1717
|
-
}, columns);
|
|
1718
|
-
setPivot(tempPivot);
|
|
1719
|
-
setPivotHint('');
|
|
1720
|
-
let dateBucket = undefined;
|
|
1721
|
-
const tempDateRange = dateRanges &&
|
|
1722
|
-
pivot.rowField &&
|
|
1723
|
-
dateRanges[pivot.rowField];
|
|
1724
|
-
if (tempDateRange) {
|
|
1725
|
-
dateBucket = getDateBucketFromRange(tempDateRange.dateRange);
|
|
1726
|
-
}
|
|
1727
|
-
try {
|
|
1728
|
-
const pivotedData = await generatePivotTable({
|
|
1729
|
-
pivot: tempPivot,
|
|
1730
|
-
dateBucket,
|
|
1731
|
-
report: tempReport,
|
|
1732
|
-
client,
|
|
1733
|
-
uniqueValues: uniqueValues[currentTable],
|
|
1734
|
-
dashboardName,
|
|
1735
|
-
tenants,
|
|
1736
|
-
additionalProcessing: {
|
|
1737
|
-
page: REPORT_BUILDER_PAGINATION,
|
|
1738
|
-
},
|
|
1739
|
-
});
|
|
1740
|
-
resetProcessing();
|
|
1741
|
-
setErrorMessage('');
|
|
1742
|
-
setPivotData(pivotedData || []);
|
|
1743
|
-
const formattedRows = formatRows(pivotedData.rows, columns, true, pivot.aggregationType, dateBucket);
|
|
1744
|
-
setFormattedRows(formattedRows);
|
|
1745
|
-
}
|
|
1746
|
-
catch (e) {
|
|
1747
|
-
if (e instanceof Error)
|
|
1748
|
-
setPivotError(e.message);
|
|
1749
|
-
}
|
|
1750
|
-
setOpenPopover(null);
|
|
1751
|
-
setBaseAst(deepCopy(baseAst));
|
|
1752
|
-
return;
|
|
1753
|
-
}
|
|
1754
|
-
else {
|
|
1755
|
-
const newAst = { ...baseAst };
|
|
1756
|
-
if (!newAst.orderby)
|
|
1757
|
-
newAst.orderby = [];
|
|
1758
|
-
const existingSortIndex = newAst.orderby.findIndex((item) => getFieldFromExpression(item.expr) === column);
|
|
1759
|
-
if (existingSortIndex !== -1) {
|
|
1760
|
-
newAst.orderby[existingSortIndex] = {
|
|
1761
|
-
expr: { type: 'column_ref', column },
|
|
1762
|
-
type: direction,
|
|
1763
|
-
};
|
|
1764
|
-
}
|
|
1765
|
-
else {
|
|
1766
|
-
newAst.orderby.push({
|
|
1767
|
-
expr: { type: 'column_ref', column },
|
|
1768
|
-
type: direction,
|
|
1769
|
-
});
|
|
1770
|
-
}
|
|
1771
|
-
// look through the columns
|
|
1772
|
-
setOpenPopover(null);
|
|
1773
|
-
setBaseAst(deepCopy(newAst));
|
|
1774
|
-
fetchSqlQuery(deepCopy(newAst));
|
|
1775
|
-
}
|
|
1776
|
-
} }) }) })] }), _jsxs("div", { style: { width: '100%' }, children: [_jsx(SidebarHeadingComponent, { label: "Limit" }), (baseAst &&
|
|
1777
|
-
((baseAst.limit && baseAst.limit.value?.length > 0) ||
|
|
1778
|
-
(baseAst.top && baseAst.top.value))) ||
|
|
1779
|
-
pivot?.rowLimit ? (_jsx("div", { style: {
|
|
1087
|
+
}, popoverTitle: "Sort by", popoverChildren: _jsx(AddSortPopover, { columns: pivotData
|
|
1088
|
+
? pivotData.columns
|
|
1089
|
+
: reportColumnsToStateColumns, Select: SelectComponent, Button: ButtonComponent, SecondaryButton: SecondaryButtonComponent, onSave: async (column, direction) => {
|
|
1090
|
+
onSortChange({ field: column, direction });
|
|
1091
|
+
setOpenPopover(null);
|
|
1092
|
+
} }) }) })] }), _jsxs("div", { style: { width: '100%' }, children: [_jsx(SidebarHeadingComponent, { label: "Limit" }), limit || pivot?.rowLimit ? (_jsx("div", { style: {
|
|
1780
1093
|
display: 'flex',
|
|
1781
1094
|
flexDirection: 'column',
|
|
1782
1095
|
gap: 8,
|
|
1783
1096
|
marginBottom: 12,
|
|
1784
|
-
}, children: _jsx(LimitSentence, { limit:
|
|
1785
|
-
baseAst.top?.value ||
|
|
1786
|
-
pivot?.rowLimit ||
|
|
1787
|
-
0, setOpenPopover: setOpenPopover, LimitPopover: LimitPopoverComponent, EditPopover: AddLimitPopover, handleDelete: () => {
|
|
1097
|
+
}, children: _jsx(LimitSentence, { limit: limit?.value || pivot?.rowLimit || 0, setOpenPopover: setOpenPopover, LimitPopover: LimitPopoverComponent, EditPopover: AddLimitPopover, handleDelete: () => {
|
|
1788
1098
|
onLimitChange(null);
|
|
1099
|
+
setOpenPopover(null);
|
|
1789
1100
|
}, onSave: (limit) => {
|
|
1790
1101
|
onLimitChange(limit);
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
return;
|
|
1794
|
-
}
|
|
1795
|
-
if (!baseAst) {
|
|
1796
|
-
return;
|
|
1797
|
-
}
|
|
1102
|
+
setOpenPopover(null);
|
|
1103
|
+
}, TextInput: TextInputComponent, Button: ButtonComponent, SecondaryButton: SecondaryButtonComponent, disabled: tableLoading || loading }) })) : (_jsxs(_Fragment, { children: [_jsx(SecondaryButtonComponent, { disabled: columns.length === 0 || loading || tableLoading, onClick: () => {
|
|
1798
1104
|
if (!openPopover) {
|
|
1799
1105
|
setOpenPopover('AddLimitPopover');
|
|
1800
1106
|
}
|
|
@@ -1803,37 +1109,36 @@ export default function ReportBuilder({ initialTableName = '', onSubmitEditRepor
|
|
|
1803
1109
|
...(openPopover === 'AddLimitPopover' && { top: 12 }),
|
|
1804
1110
|
}, children: _jsx(PopoverComponent, { isOpen: openPopover === 'AddLimitPopover', setIsOpen: (isOpen) => {
|
|
1805
1111
|
if (!isOpen) {
|
|
1806
|
-
setActiveEditItem(null);
|
|
1807
1112
|
setOpenPopover(null);
|
|
1808
1113
|
}
|
|
1809
1114
|
}, popoverTitle: "Add limit", popoverChildren: _jsx(AddLimitPopover, { TextInputComponent: TextInputComponent, Button: ButtonComponent, SecondaryButton: SecondaryButtonComponent, onSave: (limit) => {
|
|
1810
1115
|
onLimitChange(limit);
|
|
1116
|
+
setOpenPopover(null);
|
|
1811
1117
|
} }) }) })] }))] }), _jsx("div", { style: { width: '100%', minHeight: '30vh' } })] }), _jsxs(ContainerComponent, { children: [isAIEnabled && (_jsx("form", { ref: askAIContainerRef, onSubmit: (event) => {
|
|
1812
1118
|
event.preventDefault();
|
|
1813
1119
|
}, style: {
|
|
1814
1120
|
display: 'flex',
|
|
1815
1121
|
flexDirection: 'row',
|
|
1816
1122
|
gap: 12,
|
|
1817
|
-
visibility: askAIInputWidth === -1
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
: enforceOrderOnColumns(Object.keys(rows[0] ?? {})).map((c) => {
|
|
1123
|
+
visibility: askAIInputWidth === -1 ? 'hidden' : 'visible',
|
|
1124
|
+
}, children: _jsxs(_Fragment, { children: [_jsx(TextInputComponent, { id: "ask_ai_input_bar", value: aiPrompt, width: askAIInputWidth, onChange: (e) => setAiPrompt(e.target.value), placeholder: 'Ask a question...' }), _jsx(QuillToolTip, { enabled: tables.length > 1, text: "Ask AI does not support joined tables right now", textStyle: {
|
|
1125
|
+
maxWidth: '250px',
|
|
1126
|
+
whiteSpace: 'normal',
|
|
1127
|
+
}, displayBelow: true, children: _jsx(ButtonComponent, { disabled: tables.length > 1, onClick: () => {
|
|
1128
|
+
if (tables.length <= 1) {
|
|
1129
|
+
fetchAstFromPromptHelper();
|
|
1130
|
+
}
|
|
1131
|
+
}, isLoading: askAILoading && columns.length === 0, label: 'Ask AI' }) }), !reportId && (_jsx(SecondaryButtonComponent, { label: 'New report', onClick: clearAllState, disabled: columns.length === 0 || loading }))] }) })), columns.length > 0 && (_jsx(TableComponent, { isLoading: tableLoading || (loading && errorMessage.length === 0), rows: formattedRows, rowCount: pivot ? pivotData?.rowCount : numberOfRows, rowCountIsLoading: rowCountIsLoading, rowsPerPage: 20, columns: pivot
|
|
1132
|
+
? pivotData?.columns || []
|
|
1133
|
+
: reportColumnsToStateColumns.map((col) => {
|
|
1829
1134
|
return {
|
|
1830
|
-
|
|
1831
|
-
|
|
1135
|
+
field: col.field,
|
|
1136
|
+
label: snakeAndCamelCaseToTitleCase(col.field),
|
|
1832
1137
|
};
|
|
1833
1138
|
}), onPageChange: onPageChange, onSortChange: (sort) => {
|
|
1834
1139
|
onSortChange(sort);
|
|
1835
1140
|
}, disableSort: !!mssqlSortWarning, containerStyle: {
|
|
1836
|
-
maxHeight: Math.max(window.innerHeight - 290, 75 + Math.min(Math.max(10,
|
|
1141
|
+
maxHeight: Math.max(window.innerHeight - 290, 75 + Math.min(Math.max(10, reportRows.length), 20) * 37),
|
|
1837
1142
|
} })), _jsxs("div", { style: {
|
|
1838
1143
|
display: 'flex',
|
|
1839
1144
|
flexDirection: 'row',
|
|
@@ -1848,130 +1153,122 @@ export default function ReportBuilder({ initialTableName = '', onSubmitEditRepor
|
|
|
1848
1153
|
width: '100%',
|
|
1849
1154
|
gap: 12,
|
|
1850
1155
|
alignItems: 'center',
|
|
1851
|
-
}, children: [_jsx(ErrorMessageComponent, { errorMessage: errorMessage || pivotError
|
|
1156
|
+
}, children: [_jsx(ErrorMessageComponent, { errorMessage: errorMessage || pivotError }), _jsx(SecondaryButtonComponent, { onClick: () => {
|
|
1852
1157
|
fetchAstFromPromptHelper();
|
|
1853
|
-
}, label: 'Retry' }), _jsx(SecondaryButtonComponent, { onClick: clearAllState, label: 'Reset' })] })) : (_jsx("div", { style: { width: '100%' } })),
|
|
1854
|
-
const tempReportColumns =
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
});
|
|
1866
|
-
}) ?? []);
|
|
1158
|
+
}, label: 'Retry' }), _jsx(SecondaryButtonComponent, { onClick: clearAllState, label: 'Reset' })] })) : (_jsx("div", { style: { width: '100%' } })), columns.length > 0 && activeQuery && (_jsxs(_Fragment, { children: [undoButtonEnabled && stateStack.length > 1 && (_jsx(SecondaryButtonComponent, { onClick: handleUndo, label: "Undo", disabled: stateStack.length <= 1 || loading })), onDiscardChanges && (_jsx(SecondaryButtonComponent, { onClick: onDiscardChanges, label: "Discard changes" })), !hideCopySQL && (_jsx(SecondaryButtonComponent, { label: isCopying ? 'Copied' : 'Copy SQL', onClick: () => copySQLToClipboard() })), !isAdminEnabled ? null : (_jsx(SecondaryButtonComponent, { onClick: async () => {
|
|
1159
|
+
const tempReportColumns = columns
|
|
1160
|
+
.map((column) => {
|
|
1161
|
+
return reportColumnsToStateColumns.find((col) => col.field === (column.alias || column.field));
|
|
1162
|
+
})
|
|
1163
|
+
.filter((col) => col !== undefined);
|
|
1164
|
+
let customFieldColumns = [];
|
|
1165
|
+
if (client && isSelectStar && schemaData.customFields) {
|
|
1166
|
+
customFieldColumns = tables.flatMap((table) => {
|
|
1167
|
+
return (schemaData.customFields?.[table.name || ''] || []).map((field) => field.field);
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1867
1170
|
setTempReport({
|
|
1868
|
-
...
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
id: TEMP_REPORT_ID,
|
|
1904
|
-
flags: tempReport?.flags,
|
|
1905
|
-
}),
|
|
1171
|
+
...tempReport,
|
|
1172
|
+
...(pivot
|
|
1173
|
+
? pivotFormData(pivot, reportColumnsToStateColumns, tempReport, tempReport.chartType, pivotData ?? undefined)
|
|
1174
|
+
: {}),
|
|
1175
|
+
id: TEMP_REPORT_ID,
|
|
1176
|
+
dashboardName: destinationDashboard,
|
|
1177
|
+
pivot: pivot,
|
|
1178
|
+
yAxisFields: tempReport?.pivot && !pivot
|
|
1179
|
+
? []
|
|
1180
|
+
: tempReport?.yAxisFields,
|
|
1181
|
+
columns: isSelectStar
|
|
1182
|
+
? // if SELECT *, filter out custom fields from tabular view
|
|
1183
|
+
// so Automatic Custom Fields can be applied
|
|
1184
|
+
tempReportColumns.filter((col) => {
|
|
1185
|
+
return !customFieldColumns.includes(col.field);
|
|
1186
|
+
})
|
|
1187
|
+
: tempReportColumns,
|
|
1188
|
+
columnInternal: isSelectStar
|
|
1189
|
+
? // if SELECT *, filter out custom fields from tabular view
|
|
1190
|
+
// so Automatic Custom Fields can be applied
|
|
1191
|
+
tempReportColumns.filter((col) => {
|
|
1192
|
+
return !customFieldColumns.includes(col.field);
|
|
1193
|
+
})
|
|
1194
|
+
: tempReportColumns,
|
|
1195
|
+
queryString: isSelectStar
|
|
1196
|
+
? convertQueryToSelectStar(activeQuery)
|
|
1197
|
+
: activeQuery,
|
|
1198
|
+
includeCustomFields: isSelectStar,
|
|
1199
|
+
rows: reportRows,
|
|
1200
|
+
pivotRows: pivotData?.rows,
|
|
1201
|
+
pivotColumns: pivotData?.columns,
|
|
1202
|
+
pivotRowCount: pivotData?.rowCount,
|
|
1203
|
+
pivotQuery: pivotData?.pivotQuery,
|
|
1204
|
+
comparisonPivotQuery: pivotData?.comparisonPivotQuery,
|
|
1205
|
+
flags: tempReport?.flags,
|
|
1906
1206
|
});
|
|
1907
1207
|
setIsSaveQueryModalOpen(true);
|
|
1908
1208
|
}, disabled: !!errorMessage ||
|
|
1909
1209
|
!!pivotError ||
|
|
1910
1210
|
tableLoading ||
|
|
1911
1211
|
loading ||
|
|
1912
|
-
unresolvedReportMessage, label: 'Save query', tooltipText: unresolvedReportMessage })), _jsx(ButtonComponent, { onClick: async () => {
|
|
1212
|
+
!!unresolvedReportMessage, label: 'Save query', tooltipText: unresolvedReportMessage })), _jsx(ButtonComponent, { onClick: async () => {
|
|
1913
1213
|
onSaveChanges && onSaveChanges();
|
|
1914
|
-
const tempReportColumns =
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
});
|
|
1926
|
-
}) ?? []);
|
|
1214
|
+
const tempReportColumns = columns
|
|
1215
|
+
.map((column) => {
|
|
1216
|
+
return reportColumnsToStateColumns.find((col) => col.field === (column.alias || column.field));
|
|
1217
|
+
})
|
|
1218
|
+
.filter((col) => col !== undefined);
|
|
1219
|
+
let customFieldColumns = [];
|
|
1220
|
+
if (client && isSelectStar && schemaData.customFields) {
|
|
1221
|
+
customFieldColumns = tables.flatMap((table) => {
|
|
1222
|
+
return (schemaData.customFields?.[table.name || ''] || []).map((field) => field.field);
|
|
1223
|
+
});
|
|
1224
|
+
}
|
|
1927
1225
|
setTempReport({
|
|
1928
|
-
...
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
}),
|
|
1226
|
+
...tempReport,
|
|
1227
|
+
...(pivot
|
|
1228
|
+
? pivotFormData(pivot, reportColumnsToStateColumns, tempReport, tempReport.chartType, pivotData ?? undefined)
|
|
1229
|
+
: {}),
|
|
1230
|
+
id: TEMP_REPORT_ID,
|
|
1231
|
+
dashboardName: destinationDashboard,
|
|
1232
|
+
pivot: pivot,
|
|
1233
|
+
yAxisFields: tempReport?.pivot && !pivot
|
|
1234
|
+
? []
|
|
1235
|
+
: tempReport?.yAxisFields,
|
|
1236
|
+
columns: isSelectStar
|
|
1237
|
+
? // if SELECT *, filter out custom fields from tabular view
|
|
1238
|
+
// so Automatic Custom Fields can be applied
|
|
1239
|
+
tempReportColumns.filter((col) => {
|
|
1240
|
+
return !customFieldColumns.includes(col.field);
|
|
1241
|
+
})
|
|
1242
|
+
: tempReportColumns,
|
|
1243
|
+
columnInternal: isSelectStar
|
|
1244
|
+
? // if SELECT *, filter out custom fields from tabular view
|
|
1245
|
+
// so Automatic Custom Fields can be applied
|
|
1246
|
+
tempReportColumns.filter((col) => {
|
|
1247
|
+
return !customFieldColumns.includes(col.field);
|
|
1248
|
+
})
|
|
1249
|
+
: tempReportColumns,
|
|
1250
|
+
queryString: isSelectStar
|
|
1251
|
+
? convertQueryToSelectStar(activeQuery)
|
|
1252
|
+
: activeQuery,
|
|
1253
|
+
includeCustomFields: isSelectStar,
|
|
1254
|
+
rows: reportRows,
|
|
1255
|
+
pivotRows: pivotData?.rows,
|
|
1256
|
+
pivotColumns: pivotData?.columns,
|
|
1257
|
+
pivotRowCount: pivotData?.rowCount,
|
|
1258
|
+
pivotQuery: pivotData?.pivotQuery,
|
|
1259
|
+
comparisonPivotQuery: pivotData?.comparisonPivotQuery,
|
|
1260
|
+
flags: tempReport?.flags,
|
|
1964
1261
|
});
|
|
1965
1262
|
setIsChartBuilderOpen(true);
|
|
1966
1263
|
}, disabled: !!errorMessage ||
|
|
1967
1264
|
!!pivotError ||
|
|
1968
1265
|
tableLoading ||
|
|
1969
1266
|
loading ||
|
|
1970
|
-
unresolvedReportMessage, label: reportId ? 'Save changes' : 'Add to dashboard', tooltipText: unresolvedReportMessage })] }))] })] }), _jsx("style", { children: `body{margin:0;}` })] })), (!isChartBuilderHorizontalView || isChartBuilderOpen) && (_jsx(ChartBuilderWithModal, { tempReport: tempReport, reportId: reportId, isAdmin: isAdminEnabled, title: chartBuilderTitle
|
|
1267
|
+
!!unresolvedReportMessage, label: reportId ? 'Save changes' : 'Add to dashboard', tooltipText: unresolvedReportMessage })] }))] })] }), _jsx("style", { children: `body{margin:0;}` })] })), (!isChartBuilderHorizontalView || isChartBuilderOpen) && (_jsx(ChartBuilderWithModal, { tempReport: tempReport, reportId: reportId, isAdmin: isAdminEnabled, title: chartBuilderTitle
|
|
1971
1268
|
? chartBuilderTitle
|
|
1972
1269
|
: reportId
|
|
1973
1270
|
? 'Save changes'
|
|
1974
|
-
: 'Add to dashboard', isHorizontalView: true, isOpen: isChartBuilderOpen, setIsOpen: setIsChartBuilderOpen, onAddToDashboardComplete: reportId ? onSubmitEditReport : onSubmitCreateReport, organizationName: organizationName, initialUniqueValues:
|
|
1271
|
+
: 'Add to dashboard', isHorizontalView: true, isOpen: isChartBuilderOpen, setIsOpen: setIsChartBuilderOpen, onAddToDashboardComplete: reportId ? onSubmitEditReport : onSubmitCreateReport, destinationDashboard: destinationDashboard, organizationName: organizationName, initialUniqueValues: columnUniqueValues, initialUniqueValuesIsLoading: filteredUniqueValuesIsLoading || unfilteredUniqueValuesIsLoading, pivotRecommendationsEnabled: pivotRecommendationsEnabledState, SelectComponent: SelectComponent, TextInputComponent: TextInputComponent, ButtonComponent: ButtonComponent, SecondaryButtonComponent: SecondaryButtonComponent, HeaderComponent: HeaderComponent, SubHeaderComponent: SubHeaderComponent, LabelComponent: LabelComponent, TextComponent: TextComponent, CardComponent: CardComponent, ModalComponent: ChartBuilderModalComponent, PopoverComponent: PopoverComponent, TableComponent: TableComponent, DeleteButtonComponent: DeleteButtonComponent, LoadingComponent: LoadingComponent, ChartBuilderInputRowContainer: ChartBuilderInputRowContainer, ChartBuilderInputColumnContainer: ChartBuilderInputColumnContainer, CheckboxComponent: CheckboxComponent, FormContainer: ChartBuilderFormContainer, hideDateRangeFilter: true, hideDeleteButton: true, buttonLabel: !!reportId ? 'Save changes' : 'Add to dashboard', onClickChartElement: onClickChartElement, filtersEnabled: filtersEnabled, onFiltersEnabledChanged: (enabled) => {
|
|
1975
1272
|
setFiltersEnabled(enabled);
|
|
1976
1273
|
}, isEditingMode: true })), isSaveQueryModalOpen && (_jsx(ChartBuilderWithModal, { isHorizontalView: false, hideTableView: true, hideChartView: true, hidePivotForm: true, hideChartType: true, isOpen: isSaveQueryModalOpen, setIsOpen: setIsSaveQueryModalOpen, onAddToDashboardComplete: onSubmitSaveQuery, destinationDashboard: 'quill-saved-queries', isAdmin: false, title: 'Save query', buttonLabel: 'Save query', tempReport: tempReport, reportId: reportId, organizationName: organizationName, CardComponent: CardComponent, TableComponent: TableComponent, ModalComponent: ModalComponent, ButtonComponent: ButtonComponent, TextInputComponent: TextInputComponent, SelectComponent: SelectComponent, SecondaryButtonComponent: SecondaryButtonComponent, HeaderComponent: HeaderComponent, SubHeaderComponent: SubHeaderComponent, LabelComponent: LabelComponent, TextComponent: TextComponent, PopoverComponent: PopoverComponent, DeleteButtonComponent: DeleteButtonComponent, LoadingComponent: LoadingComponent, ChartBuilderInputRowContainer: ChartBuilderInputRowContainer, ChartBuilderInputColumnContainer: ChartBuilderInputColumnContainer, ErrorMessageComponent: ErrorMessageComponent, PivotRowContainer: PivotRowContainer, PivotColumnContainer: PivotColumnContainer, FormContainer: ChartBuilderFormContainer, CheckboxComponent: CheckboxComponent, hideDateRangeFilter: true, hideDeleteButton: true, hideDiscardChanges: true, hideSQLQuery: false, onClickChartElement: onClickChartElement,
|
|
1977
1274
|
// hide filters table, make it a table chart etc
|