@quillsql/react 2.16.0 → 2.16.1

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/index.js CHANGED
@@ -14787,6 +14787,21 @@ function isDateFormat(xAxisFormat) {
14787
14787
  const isDate = DATE_FORMATS.includes(xAxisFormat);
14788
14788
  return isDate;
14789
14789
  }
14790
+ function getComparisonInterval(comparisonRange, dateBucket) {
14791
+ const dayCount = differenceInDays2(
14792
+ comparisonRange.endDate,
14793
+ comparisonRange.startDate
14794
+ );
14795
+ if (!isNaN(dayCount)) {
14796
+ if (dateBucket === "month") {
14797
+ return Math.floor(dayCount / 30) + " month";
14798
+ } else if (dateBucket === "year") {
14799
+ return Math.floor(dayCount / 365) + " year";
14800
+ } else {
14801
+ return dayCount + " day";
14802
+ }
14803
+ }
14804
+ }
14790
14805
  function getDateBucketFromRange(dateRange) {
14791
14806
  const difference = differenceInDays2(dateRange.end, dateRange.start);
14792
14807
  if (difference < 14) {
@@ -16600,759 +16615,305 @@ function parseValueFromBigQueryDates(rows, columns) {
16600
16615
  });
16601
16616
  }
16602
16617
 
16603
- // src/utils/pivotConstructor.ts
16604
- async function generatePivotWithSQL({
16605
- pivot,
16606
- report,
16607
- client,
16608
- dateBucket,
16609
- dateFilter,
16610
- distinctStrings,
16611
- dashboardName,
16612
- tenants,
16613
- additionalProcessing,
16614
- pivotQuery,
16615
- comparisonPivotQuery,
16616
- getPivotRowCount = true,
16617
- caller,
16618
- getToken
16619
- }) {
16620
- const databaseType = client.databaseType || "postgresql";
16621
- if (!pivot.aggregations?.length && pivot.aggregationType) {
16622
- pivot.aggregations = [
16623
- {
16624
- aggregationType: pivot.aggregationType,
16625
- valueField: pivot.valueField,
16626
- valueFieldType: pivot.valueFieldType,
16627
- valueField2: pivot.valueField2,
16628
- valueField2Type: pivot.valueField2Type
16618
+ // src/utils/merge.ts
16619
+ import {
16620
+ add,
16621
+ startOfDay as startOfDay3,
16622
+ startOfMonth,
16623
+ startOfWeek as startOfWeek3,
16624
+ startOfYear
16625
+ } from "date-fns";
16626
+ import { utcToZonedTime as utcToZonedTime2 } from "date-fns-tz";
16627
+ function mergeComparisonRange(resp) {
16628
+ if (resp.chartType === "table") return resp;
16629
+ const compRows = resp.compareRows;
16630
+ if (!compRows) return resp;
16631
+ const newRows = resp.rows.map((row, i) => {
16632
+ if (i < compRows.length) {
16633
+ const compRow = compRows[i];
16634
+ const newRow = { ...row };
16635
+ for (const [key, value] of Object.entries(compRow)) {
16636
+ newRow[`comparison_${key}`] = value;
16629
16637
  }
16630
- ];
16631
- }
16632
- const pivotConfig = {
16633
- rowField: pivot.rowField,
16634
- rowFieldType: pivot.rowFieldType,
16635
- columnField: pivot.columnField,
16636
- aggregations: pivot.aggregations?.map((agg) => ({
16637
- aggregationType: agg.aggregationType,
16638
- valueField: agg.valueField,
16639
- valueFieldType: agg.valueFieldType
16640
- }))
16641
- };
16642
- const resp = await quillFetch({
16643
- client,
16644
- task: "pivot-template",
16645
- metadata: {
16646
- clientId: client.clientId,
16647
- pivot: pivotConfig,
16648
- itemQuery: report?.queryString,
16649
- dashboardName,
16650
- tenants
16651
- },
16652
- credentials: "same-origin",
16653
- getToken
16638
+ return newRow;
16639
+ }
16640
+ return row;
16654
16641
  });
16655
- if (resp.data?.success === false) {
16656
- throw resp.data.errorMessage;
16642
+ return { ...resp, rows: newRows };
16643
+ }
16644
+ function dateAdder(date, interval2) {
16645
+ const match = interval2.match(/^(\d+)\s+(day|month|year)$/);
16646
+ if (!match) {
16647
+ throw new Error(`Invalid interval format: ${interval2}`);
16657
16648
  }
16658
- const queryResponseRows = resp.data?.rows || resp.queries?.queryResults?.[0]?.rows || [];
16659
- const queryResponseFields = resp.data?.fields || resp.queries?.queryResults?.[0]?.fields || [];
16660
- const rowCount = resp.queries?.queryResults?.[1]?.rows?.[0]?.row_count || 0;
16661
- parseValueFromBigQueryDates(queryResponseRows, queryResponseFields);
16662
- const responseRows = queryResponseRows;
16663
- const responseFields = queryResponseFields;
16664
- const rows = pivot.rowField ? responseRows.map(
16665
- (row) => !row[pivot.rowField] ? { ...row, [pivot.rowField]: "-" } : row
16666
- ) : responseRows;
16667
- if (pivot.columnField && client.databaseType?.toLowerCase() === "bigquery") {
16668
- rows.forEach((row) => {
16669
- Object.keys(row).forEach((key) => {
16670
- const processedKey = processColumnName(key);
16671
- if (processedKey !== key) {
16672
- row[processedKey] = row[key];
16673
- delete row[key];
16674
- }
16675
- });
16676
- });
16649
+ const [, valueStr, unit] = match;
16650
+ if (!valueStr || !unit) {
16651
+ throw new Error("Could not parse interval");
16677
16652
  }
16678
- const columns = responseFields?.map((field) => ({
16679
- field: processColumnName(field.field || field.name),
16680
- label: snakeCaseToTitleCase(
16681
- processColumnName(
16682
- (field.field || field.name).replace("comparison_", "comparison ")
16683
- )
16684
- ),
16685
- format: (field.field || field.name) === pivot.rowField ? "string" : pivot.aggregations?.find(
16686
- (agg) => agg.valueField === (field.field || field.name) || `${agg.valueField}_percentage` === (field.field || field.name) || !agg.valueField && agg.aggregationType === "percentage" && (field.field || field.name)?.endsWith("percentage")
16687
- )?.aggregationType === "percentage" ? "percent" : "whole_number",
16688
- fieldType: field.fieldType,
16689
- jsType: field.jsType,
16690
- dataTypeID: field.dataTypeID
16691
- })).filter(
16692
- (field, index) => field.field !== "comparison_" + pivot.rowField || index === 0
16693
- ).sort((a, b) => {
16694
- if (a.field === pivot.rowField) {
16695
- return -1;
16696
- }
16697
- if (b.field === pivot.rowField) {
16698
- return 1;
16699
- }
16700
- return 0;
16701
- });
16702
- if (pivot.rowField && !isStringType(pivot.rowFieldType || "")) {
16703
- rows.forEach((row) => {
16704
- row.__quillRawDate = typeof row[pivot.rowField || ""] === "object" ? row[pivot.rowField || ""].value : row[pivot.rowField || ""];
16705
- let value = typeof row[pivot.rowField || ""] === "object" ? row[pivot.rowField || ""].value : row[pivot.rowField || ""];
16706
- if (dateBucket === "week" && dateFilter?.startDate && dateFilter?.endDate) {
16707
- const rowDate = new Date(value);
16708
- if (rowDate < dateFilter.startDate) {
16709
- value = dateFilter.startDate.toISOString();
16710
- } else if (rowDate > dateFilter.endDate) {
16711
- value = dateFilter.endDate.toISOString();
16712
- }
16713
- }
16714
- const dateString = getDateString(
16715
- value,
16716
- dateFilter?.startDate && dateFilter?.endDate ? { start: dateFilter.startDate, end: dateFilter.endDate } : void 0,
16717
- dateBucket,
16718
- databaseType
16719
- );
16720
- row[pivot.rowField || ""] = dateString;
16721
- });
16722
- if (pivot.rowField && pivot.rowFieldType && !isStringType(pivot.rowFieldType) && dateFilter?.startDate && dateFilter?.endDate) {
16723
- const dateSet = new Set(
16724
- rows.map((row) => row[pivot.rowField || ""])
16725
- );
16726
- for (let date = dateFilter.startDate; date <= dateFilter.endDate; date = new Date(date.getTime() + 24 * 60 * 60 * 1e3)) {
16727
- const formattedDate = getDateString(
16728
- date.toISOString(),
16729
- { start: dateFilter.startDate, end: dateFilter.endDate },
16730
- dateBucket,
16731
- databaseType
16732
- );
16733
- if (!dateSet.has(formattedDate)) {
16734
- const newRow = {};
16735
- newRow[pivot.rowField] = formattedDate;
16736
- newRow.__quillRawDate = date.toISOString();
16737
- rows.push(newRow);
16738
- dateSet.add(formattedDate);
16739
- }
16740
- }
16741
- }
16742
- if (!pivot.sort) {
16743
- rows.sort((a, b) => {
16744
- if (a.__quillRawDate < b.__quillRawDate) {
16745
- return -1;
16746
- }
16747
- if (a.__quillRawDate > b.__quillRawDate) {
16748
- return 1;
16749
- }
16750
- return 0;
16751
- });
16752
- }
16653
+ const value = parseInt(valueStr, 10);
16654
+ const parsedDate = typeof date === "string" ? new Date(date) : date;
16655
+ switch (unit) {
16656
+ case "day":
16657
+ return add(parsedDate, { days: value });
16658
+ case "month":
16659
+ return add(parsedDate, { months: value });
16660
+ case "year":
16661
+ return add(parsedDate, { years: value });
16662
+ default:
16663
+ throw new Error(`Unsupported interval unit: ${unit}`);
16753
16664
  }
16754
- columns?.forEach((column, index) => {
16755
- if (column.label && ["null", "undefined"].includes(column.label.toLowerCase()) && !pivot.columnField && !pivot.aggregations?.[index]?.valueField && pivot.aggregations?.[index]?.aggregationType === "count") {
16756
- column.label = "Count";
16757
- }
16758
- });
16759
- const numericColumns = columns?.filter(
16760
- (column) => column.format === "whole_number" || column.format === "percentage"
16761
- );
16762
- rows.forEach((row) => {
16763
- numericColumns?.forEach((column) => {
16764
- row[column.field] = row[column.field] ?? 0;
16765
- });
16766
- });
16767
- return {
16768
- rows,
16769
- columns: columns ?? [],
16770
- rowCount: rowCount || rows.length,
16771
- pivotQuery: report?.queryString || "",
16772
- comparisonPivotQuery: comparisonPivotQuery || ""
16773
- };
16774
16665
  }
16775
- function generatePivotTableYAxis(pivot, cols, yAxisField) {
16776
- if (pivot?.aggregationType === "count") {
16777
- return [
16778
- {
16779
- field: pivot.valueField ?? "count",
16780
- label: yAxisField.label,
16781
- format: yAxisField.format
16782
- }
16783
- ];
16666
+ function dateTrunc(date, granularity = "day", databaseType) {
16667
+ const weekStartsOn = databaseType === "mssql" ? 0 : 1;
16668
+ const timeZone = "UTC";
16669
+ const zonedDate = utcToZonedTime2(date, timeZone);
16670
+ switch (granularity.toLowerCase()) {
16671
+ case "week":
16672
+ return startOfWeek3(zonedDate, { weekStartsOn });
16673
+ case "month":
16674
+ return startOfMonth(zonedDate);
16675
+ case "day":
16676
+ return startOfDay3(zonedDate);
16677
+ case "year":
16678
+ return startOfYear(zonedDate);
16679
+ default:
16680
+ throw new Error(`Unsupported granularity: ${granularity}`);
16784
16681
  }
16785
- return [
16786
- {
16787
- field: pivot.valueField ?? "count",
16788
- label: yAxisField.label,
16789
- format: yAxisField.format
16790
- }
16791
- ];
16792
16682
  }
16793
- function generatePivotTitle(pivot) {
16794
- if (pivot.rowField && !pivot.valueField && pivot.aggregations?.[0]) {
16795
- return snakeAndCamelCaseToTitleCase(
16796
- `${pivot.aggregations[0].aggregationType} of ${pivot.rowField}${pivot.columnField ? ` by ${pivot.columnField}` : ""}
16797
- `
16798
- );
16799
- } else if (!pivot.rowField && pivot.aggregations?.[0]?.valueField) {
16800
- return snakeAndCamelCaseToTitleCase(
16801
- `${pivot.aggregations[0].aggregationType} of ${pivot.aggregations[0].valueField}
16802
- `
16803
- );
16804
- }
16805
- return snakeAndCamelCaseToTitleCase(
16806
- `${pivot.aggregations?.[0]?.aggregationType ?? "Aggregation"} of ${pivot.aggregations?.[0]?.valueField ?? "value"}${pivot.rowField ? ` by ${pivot.rowField}` : ""}${pivot.columnField ? ` and ${pivot.columnField}` : ""}`
16807
- );
16683
+ function mergeComparisonPivotColumns({
16684
+ pivot,
16685
+ rows,
16686
+ compRows
16687
+ }) {
16688
+ if (!compRows || !compRows.length) return rows;
16689
+ const mappedCompRows = compRows.filter((compRow) => compRow.name !== pivot.rowField).map((compRow) => ({ ...compRow, name: `comparison_${compRow.name}` }));
16690
+ return rows.concat(mappedCompRows);
16808
16691
  }
16809
- async function generatePivotTable({
16692
+ function mergeComparisonPivotRows({
16810
16693
  pivot,
16694
+ rows,
16695
+ compRows,
16696
+ databaseType,
16811
16697
  dateBucket,
16812
- dateFilter,
16813
- report,
16814
- client,
16815
- getToken,
16816
- eventTracking,
16817
- uniqueValues,
16818
- dashboardName,
16819
- tenants,
16820
- additionalProcessing,
16821
- caller,
16822
- pivotQuery
16698
+ comparisonInterval,
16699
+ columnFieldValues
16823
16700
  }) {
16824
- try {
16825
- if (report && client) {
16826
- const pivotTable = await generatePivotWithSQL({
16827
- pivotQuery,
16828
- pivot,
16829
- report,
16830
- client,
16831
- dateBucket,
16832
- dateFilter,
16833
- dashboardName,
16834
- tenants,
16835
- additionalProcessing,
16836
- caller,
16837
- getToken
16838
- });
16839
- return pivotTable;
16840
- }
16841
- } catch (e) {
16842
- eventTracking?.logError?.({
16843
- type: "bug",
16844
- // TODO: determine type
16845
- severity: "high",
16846
- message: "Error generating pivot table",
16847
- errorMessage: e.message,
16848
- errorStack: e.stack,
16849
- errorData: {
16850
- caller: "PivotModal",
16851
- function: "generatePivotTable"
16852
- }
16853
- });
16854
- throw Error(`Failed to generate pivot table with SQL: ${e}`);
16701
+ if (!compRows || !compRows.length) return rows;
16702
+ if (pivot.columnField && columnFieldValues && columnFieldValues.length > 0) {
16703
+ return merge2DPivotRows(
16704
+ pivot,
16705
+ rows,
16706
+ compRows,
16707
+ columnFieldValues,
16708
+ databaseType,
16709
+ dateBucket,
16710
+ comparisonInterval
16711
+ );
16855
16712
  }
16856
- throw Error("Failed to generate pivot table: invalid report");
16713
+ if (pivot.rowField) {
16714
+ return merge1DPivotRows(
16715
+ pivot,
16716
+ rows,
16717
+ compRows,
16718
+ databaseType,
16719
+ dateBucket,
16720
+ comparisonInterval
16721
+ );
16722
+ }
16723
+ return mergeAggregatedRows(pivot, rows, compRows);
16857
16724
  }
16858
-
16859
- // src/utils/paginationProcessing.ts
16860
- var DEFAULT_PAGINATION = {
16861
- page: 0,
16862
- rowsPerPage: 10,
16863
- rowsPerRequest: 600
16864
- };
16865
- var DEFAULT_TABLE_PAGINATION = {
16866
- page: 0,
16867
- rowsPerPage: 10,
16868
- rowsPerRequest: 50
16869
- };
16870
- function shouldFetchMore(pagination, page, maxPage, currentRowCount) {
16871
- if (!pagination || currentRowCount && currentRowCount >= pagination.rowsPerPage * (page + 1)) {
16872
- return false;
16725
+ function merge2DPivotRows(pivot, rows, compRows, columnFieldValues, databaseType, dateBucket, comparisonInterval) {
16726
+ if (!pivot || !pivot.columnField) {
16727
+ return [];
16873
16728
  }
16874
- const indexAdjustedPage = page;
16875
- const pageInterval = Math.floor(
16876
- indexAdjustedPage * pagination.rowsPerPage / pagination.rowsPerRequest
16877
- );
16878
- const indexAdjustedPreviousPage = maxPage;
16879
- const previousPageInterval = Math.floor(
16880
- indexAdjustedPreviousPage * pagination.rowsPerPage / pagination.rowsPerRequest
16729
+ if (isStringType(pivot.rowFieldType || "") || !pivot.rowFieldType) {
16730
+ return merge2DStringPivotRows(pivot, rows, compRows, columnFieldValues);
16731
+ }
16732
+ return merge2DDatePivotRows(
16733
+ pivot,
16734
+ rows,
16735
+ compRows,
16736
+ columnFieldValues,
16737
+ databaseType,
16738
+ dateBucket,
16739
+ comparisonInterval
16881
16740
  );
16882
- return pageInterval > previousPageInterval;
16883
16741
  }
16884
- function shouldSortInMemory(pagination, rowCount) {
16885
- if (!rowCount || rowCount < pagination.rowsPerRequest) {
16886
- return true;
16742
+ function merge2DStringPivotRows(pivot, rows, compRows, columnFieldValues) {
16743
+ if (!pivot.rowField || !pivot.aggregations?.some((agg) => agg.valueField)) {
16744
+ return rows;
16887
16745
  }
16888
- return false;
16746
+ return rows.map((row) => {
16747
+ const matchingCompRow = compRows.find(
16748
+ (compRow) => compRow[pivot.rowField] === row[pivot.rowField]
16749
+ );
16750
+ const comparisonFields = columnFieldValues.reduce(
16751
+ (acc, fieldValue) => {
16752
+ acc[`comparison_${fieldValue}`] = matchingCompRow ? matchingCompRow[fieldValue] || null : null;
16753
+ return acc;
16754
+ },
16755
+ {}
16756
+ );
16757
+ return {
16758
+ ...row,
16759
+ ...comparisonFields
16760
+ };
16761
+ });
16889
16762
  }
16890
-
16891
- // src/utils/dashboard.ts
16892
- var defaultDashboardItem = {
16893
- id: "",
16894
- name: "",
16895
- dashboardName: "",
16896
- rows: [],
16897
- compareRows: [],
16898
- columns: [],
16899
- chartType: "",
16900
- pivot: null,
16901
- yAxisFields: [],
16902
- xAxisLabel: "",
16903
- xAxisField: "",
16904
- xAxisFormat: "string",
16905
- order: -1,
16906
- filtersApplied: [],
16907
- queryString: "",
16908
- rowCount: 0,
16909
- columnInternal: []
16910
- };
16911
- async function cleanDashboardItem({
16912
- item,
16913
- dashboardFilters,
16914
- getToken,
16915
- eventTracking,
16916
- client,
16917
- dateBucket,
16918
- additionalProcessing,
16919
- customFields,
16920
- skipPivotFetch,
16921
- tenants
16922
- }) {
16923
- if (!item) return defaultDashboardItem;
16924
- if (!item.rows) {
16763
+ function merge2DDatePivotRows(pivot, rows, compRows, columnFieldValues, databaseType, dateBucket, comparisonInterval) {
16764
+ if (!comparisonInterval || !pivot.rowField) {
16765
+ return rows;
16766
+ }
16767
+ return rows.map((row) => {
16768
+ const rowDate = typeof row[pivot.rowField] === "string" ? new Date(row[pivot.rowField]) : row[pivot.rowField];
16769
+ const truncatedRowDate = dateTrunc(rowDate, dateBucket, databaseType);
16770
+ const matchingCompRow = compRows.find((compRow) => {
16771
+ const comparisonDateWithInterval = dateAdder(
16772
+ compRow[pivot.rowField],
16773
+ comparisonInterval
16774
+ );
16775
+ const truncatedCompRowDate = dateTrunc(
16776
+ comparisonDateWithInterval,
16777
+ dateBucket,
16778
+ databaseType
16779
+ );
16780
+ return truncatedRowDate.toISOString() === truncatedCompRowDate.toISOString();
16781
+ });
16782
+ const comparisonFields = columnFieldValues.reduce(
16783
+ (acc, fieldValue) => {
16784
+ acc[`comparison_${fieldValue}`] = matchingCompRow ? matchingCompRow[fieldValue] || null : null;
16785
+ return acc;
16786
+ },
16787
+ {}
16788
+ );
16925
16789
  return {
16926
- ...defaultDashboardItem,
16927
- id: item._id,
16928
- name: item.name
16790
+ ...row,
16791
+ ...comparisonFields
16929
16792
  };
16793
+ });
16794
+ }
16795
+ function merge1DPivotRows(pivot, rows, compRows, databaseType, dateBucket, comparisonInterval) {
16796
+ if (isStringType(pivot.rowFieldType || "") || !pivot.rowFieldType) {
16797
+ return merge1DStringPivotRows(pivot, rows, compRows);
16930
16798
  }
16931
- const fields = item.fields || [];
16932
- const columnsWithCustomFields = [...item.columns ?? []];
16933
- if (item.includeCustomFields && item.rows?.length > 0) {
16934
- const tables = item.referencedTables ?? [];
16935
- tables.forEach((table) => {
16936
- const _customFields = customFields?.[table] ?? [];
16937
- _customFields.forEach((field) => {
16938
- const isJsonCustomField = !!field.refColumn;
16939
- if (item.rows[0][field.field] !== void 0 && !item.columns.some((col) => col.field === field.field)) {
16940
- const result_field = fields.find((f) => f.name === field.field);
16941
- const converted_field = convertPostgresColumn(result_field ?? {});
16942
- columnsWithCustomFields.push({
16943
- field: field.field,
16944
- format: converted_field.format,
16945
- label: snakeAndCamelCaseToTitleCase(field.field),
16946
- fieldType: converted_field.fieldType,
16947
- dataTypeID: converted_field.dataTypeID,
16948
- jsType: converted_field.jsType,
16949
- inferFormat: isJsonCustomField
16950
- });
16951
- }
16952
- });
16953
- });
16799
+ return merge1DDatePivotRows(
16800
+ pivot,
16801
+ rows,
16802
+ compRows,
16803
+ databaseType,
16804
+ dateBucket,
16805
+ comparisonInterval
16806
+ );
16807
+ }
16808
+ function merge1DStringPivotRows(pivot, rows, compRows) {
16809
+ if (!pivot.rowField || !pivot.valueField) {
16810
+ return rows;
16954
16811
  }
16955
- const processedColumns = item.columns.map((col) => {
16956
- return { ...col, label: snakeAndCamelCaseToTitleCase(col.label) };
16957
- });
16958
- const columnInternal = (item.includeCustomFields ? columnsWithCustomFields : item.columns).map((col) => {
16959
- const field = item.fields?.find((f) => f.name === col.field);
16960
- const converted_field = convertPostgresColumn(field ?? {});
16812
+ return rows.map((row) => {
16813
+ const matchingCompRow = compRows.find(
16814
+ (compRow) => compRow[pivot.rowField] === row[pivot.rowField]
16815
+ );
16816
+ let aggregationSuffix = "";
16817
+ if (matchingCompRow && pivot.aggregationType === "percentage") {
16818
+ if (matchingCompRow[`${pivot.valueField}_Percentage`]) {
16819
+ aggregationSuffix = "_Percentage";
16820
+ } else if (matchingCompRow[`${pivot.valueField}_PERCENTAGE`]) {
16821
+ aggregationSuffix = "_PERCENTAGE";
16822
+ } else {
16823
+ aggregationSuffix = "_percentage";
16824
+ }
16825
+ }
16961
16826
  return {
16962
- fieldType: converted_field.fieldType,
16963
- dataTypeID: converted_field.dataTypeID,
16964
- jsType: converted_field.jsType,
16965
- ...col,
16966
- label: snakeAndCamelCaseToTitleCase(col.label)
16827
+ ...row,
16828
+ [`comparison_${pivot.valueField}${aggregationSuffix}`]: matchingCompRow ? matchingCompRow[`${pivot.valueField}${aggregationSuffix}`] : null
16967
16829
  };
16968
16830
  });
16969
- let pivotTable;
16970
- let pivotError;
16971
- try {
16972
- const shouldPaginatePivotAsTable = item.chartType === "table";
16973
- const pivotChartProcessing = {
16974
- page: DEFAULT_PAGINATION
16831
+ }
16832
+ function merge1DDatePivotRows(pivot, rows, compRows, databaseType, dateBucket, comparisonInterval) {
16833
+ if (!comparisonInterval || !pivot.rowField) {
16834
+ return rows;
16835
+ }
16836
+ return rows.map((row) => {
16837
+ const rowDate = typeof row[pivot.rowField] === "string" ? new Date(row[pivot.rowField]) : row[pivot.rowField];
16838
+ const truncatedRowDate = dateTrunc(rowDate, dateBucket, databaseType);
16839
+ const matchingCompRow = compRows.find((compRow) => {
16840
+ const comparisonDateWithInterval = dateAdder(
16841
+ compRow[`${pivot.rowField}`],
16842
+ comparisonInterval
16843
+ );
16844
+ const truncatedCompRowDate = dateTrunc(
16845
+ comparisonDateWithInterval,
16846
+ dateBucket,
16847
+ databaseType
16848
+ );
16849
+ return truncatedRowDate.toISOString() === truncatedCompRowDate.toISOString();
16850
+ });
16851
+ let aggregationSuffix = "";
16852
+ if (matchingCompRow && pivot.aggregationType === "percentage") {
16853
+ if (matchingCompRow[`${pivot.valueField}_Percentage`]) {
16854
+ aggregationSuffix = "_Percentage";
16855
+ } else if (matchingCompRow[`${pivot.valueField}_PERCENTAGE`]) {
16856
+ aggregationSuffix = "_PERCENTAGE";
16857
+ } else {
16858
+ aggregationSuffix = "_percentage";
16859
+ }
16860
+ }
16861
+ return {
16862
+ ...row,
16863
+ [`comparison_${pivot.valueField}${aggregationSuffix}`]: matchingCompRow?.[`${pivot.valueField}${aggregationSuffix}`] || null
16975
16864
  };
16976
- pivotTable = await getPivotTable(
16977
- {
16978
- ...item,
16979
- pivot: item.pivot && !skipPivotFetch ? {
16980
- ...item.pivot,
16981
- aggregations: item.pivot.aggregations ?? [
16982
- {
16983
- valueField: item.pivot.valueField,
16984
- valueFieldType: item.pivot.valueFieldType,
16985
- valueField2: item.pivot.valueField2,
16986
- valueField2Type: item.pivot.valueField2Type,
16987
- aggregationType: item.pivot.aggregationType
16988
- }
16989
- ]
16990
- } : void 0
16991
- },
16992
- dashboardFilters,
16993
- item.dashboardName,
16994
- getToken,
16995
- client,
16996
- eventTracking,
16997
- dateBucket,
16998
- shouldPaginatePivotAsTable ? additionalProcessing : pivotChartProcessing,
16999
- tenants,
17000
- customFields
17001
- );
17002
- } catch (e) {
17003
- pivotTable = void 0;
17004
- eventTracking?.logError?.({
17005
- type: "bug",
17006
- // TODO: determine type
17007
- severity: "high",
17008
- message: "Error fetching pivot table",
17009
- errorMessage: e.message,
17010
- errorStack: e.stack,
17011
- errorData: {
17012
- caller: "cleanDashboardItem",
17013
- function: "cleanDashboardItem"
16865
+ });
16866
+ }
16867
+ function mergeAggregatedRows(pivot, rows, compRows) {
16868
+ return rows.map((row, i) => {
16869
+ let aggregationSuffix = "";
16870
+ if (compRows[i] && pivot.aggregationType === "percentage") {
16871
+ if (compRows[i][`${pivot.valueField}_Percentage`]) {
16872
+ aggregationSuffix = "_Percentage";
16873
+ } else if (compRows[i][`${pivot.valueField}_PERCENTAGE`]) {
16874
+ aggregationSuffix = "_PERCENTAGE";
16875
+ } else {
16876
+ aggregationSuffix = "_percentage";
17014
16877
  }
17015
- });
17016
- console.error("Error fetching pivot table", e);
17017
- pivotError = "Error fetching pivot table";
17018
- }
17019
- const referenceLineYValues = [];
17020
- for (const key in item) {
17021
- if (key.startsWith("referenceLine_")) {
17022
- const field = key.slice(14);
17023
- if (!item[key] || !item[key][0]) continue;
17024
- const value = Object.values(item[key][0])[0];
17025
- referenceLineYValues.push({ label: field, query: [value, value] });
17026
16878
  }
16879
+ const compRow = {
16880
+ [`comparison_${pivot.valueField}${aggregationSuffix}`]: compRows[i]?.[`${pivot.valueField}${aggregationSuffix}`]
16881
+ };
16882
+ return { ...row, ...compRow };
16883
+ });
16884
+ }
16885
+
16886
+ // src/utils/queryConstructor.ts
16887
+ function processSingleQuotes(value, databaseType) {
16888
+ if (["postgresql", "snowflake", "clickhouse"].includes(
16889
+ databaseType.toLowerCase()
16890
+ )) {
16891
+ return value.replaceAll("'", "''");
17027
16892
  }
17028
- if (item.referenceLines) {
17029
- for (const referenceLine of item.referenceLines) {
17030
- if (Array.isArray(referenceLine.query)) {
17031
- referenceLineYValues.push({
17032
- label: referenceLine.label,
17033
- query: referenceLine.query
17034
- });
17035
- } else if (referenceLine.query === "") {
17036
- referenceLineYValues.push({
17037
- label: referenceLine.label,
17038
- query: (pivotTable?.rows ? pivotTable.rows : item.rows).map(
17039
- (row) => Number(row[referenceLine.label]) || 0
17040
- )
17041
- });
16893
+ return value.replaceAll("'", "\\'");
16894
+ }
16895
+ function processAggType(aggType, hasColumnField = false) {
16896
+ if (aggType === "count" && hasColumnField) return "SUM";
16897
+ return aggType?.toLowerCase() === "average" ? "AVG" : aggType?.toLowerCase();
16898
+ }
16899
+ function replaceBigQuerySpecialCharacters(column) {
16900
+ return column.replaceAll("/", "quill_forward_slash");
16901
+ }
16902
+ function processColumnReference(column, databaseType, fallbackOnNull, isColumnFieldAlias, isValueFieldAlias) {
16903
+ switch (databaseType.toLowerCase()) {
16904
+ case "postgresql":
16905
+ case "clickhouse": {
16906
+ if (column === "") {
16907
+ return fallbackOnNull ? `"${fallbackOnNull}"` : `"_"`;
16908
+ }
16909
+ if (isColumnFieldAlias) {
16910
+ return `"${column.replaceAll('"', "")}"`;
16911
+ }
16912
+ const columnParts = column.split(".");
16913
+ if (columnParts.length > 1) {
16914
+ return `"` + columnParts.map((part) => part.replaceAll('"', "")).join('"."') + `"`;
17042
16915
  }
17043
- }
17044
- }
17045
- return {
17046
- id: item._id ?? item.id,
17047
- name: item.name,
17048
- dashboardName: item.dashboardName,
17049
- // section: item.section,
17050
- rows: item.rows,
17051
- pivotRows: pivotTable ? pivotTable.rows : void 0,
17052
- pivotColumns: pivotTable ? pivotTable.columns : void 0,
17053
- compareRows: item.compareRows,
17054
- columns: processedColumns.map((column) => {
17055
- return {
17056
- field: column.field,
17057
- format: column.format,
17058
- label: column.label,
17059
- inferFormat: column.inferFormat
17060
- };
17061
- }),
17062
- includeCustomFields: item.includeCustomFields,
17063
- columnInternal,
17064
- columnsWithCustomFields,
17065
- chartType: item.chartType,
17066
- dateField: item.dateField,
17067
- pivot: pivotError ? void 0 : item.pivot ? {
17068
- ...item.pivot,
17069
- aggregations: item.pivot.aggregations ?? [
17070
- {
17071
- valueField: item.pivot.valueField,
17072
- valueFieldType: item.pivot.valueFieldType,
17073
- valueField2: item.pivot.valueField2,
17074
- valueField2Type: item.pivot.valueField2Type,
17075
- aggregationType: item.pivot.aggregationType
17076
- }
17077
- ],
17078
- columnValues: item.distinctStrings
17079
- } : void 0,
17080
- yAxisFields: pivotTable ? extractPivotedYAxis(pivotTable, item) : item.yAxisFields,
17081
- xAxisLabel: item.xAxisLabel,
17082
- xAxisField: item.xAxisField,
17083
- xAxisFormat: item.xAxisFormat,
17084
- order: item.order,
17085
- filtersApplied: item.filtersApplied,
17086
- filterMap: item.filterMap,
17087
- flags: item.flags,
17088
- rowCount: item.rowCount ? parseInt(item.rowCount) : item.rows.length,
17089
- pivotRowCount: pivotTable ? pivotTable.rowCount : void 0,
17090
- template: item.template,
17091
- sort: item.sort,
17092
- itemQuery: item.itemQuery,
17093
- queryString: item.queryString,
17094
- pivotQuery: pivotTable?.pivotQuery,
17095
- comparisonPivotQuery: pivotTable?.comparisonPivotQuery,
17096
- referencedTables: item?.referencedTables || [],
17097
- referencedColumns: item?.referencedColumns || {},
17098
- error: item.error ?? pivotError,
17099
- referenceLineYValues,
17100
- referenceLines: item.referenceLines
17101
- };
17102
- }
17103
- async function getPivotTable(report, dashboardFilters, dashboardName, getToken, client, eventTracking, dateBucketInitial, additionalProcessing, tenants, customFields) {
17104
- if (!report) return void 0;
17105
- const dateFilter = Object.values(dashboardFilters ?? {}).find(
17106
- (filter) => filter.filterType === "date_range" || filter.operator === "BETWEEN"
17107
- );
17108
- if (dateFilter?.operator === "BETWEEN") {
17109
- dateFilter.startDate = dateFilter.value[0];
17110
- dateFilter.endDate = dateFilter.value[1];
17111
- }
17112
- const pivot = report?.pivot;
17113
- const data = report || {};
17114
- if (pivot && client) {
17115
- if (report.rowCount === 0 || report.rows.length === 0) {
17116
- const columns = [];
17117
- if (pivot.rowField) {
17118
- columns.push({
17119
- field: pivot.rowField,
17120
- label: snakeCaseToTitleCase(processColumnName(pivot.rowField)),
17121
- format: pivot.rowFieldType || "string",
17122
- jsType: convertFieldTypeToJSType(pivot.rowFieldType || "string"),
17123
- fieldType: pivot.rowFieldType || "string",
17124
- dataTypeID: fieldTypeToDataTypeID(pivot.rowFieldType || "string")
17125
- });
17126
- }
17127
- for (const agg of pivot.aggregations ?? []) {
17128
- if (agg.valueField) {
17129
- columns.push({
17130
- field: agg.valueField,
17131
- label: snakeCaseToTitleCase(processColumnName(agg.valueField)),
17132
- //FIXME: valueFieldType is not always the same as the format
17133
- format: agg.valueFieldType ?? "whole_number",
17134
- jsType: agg.valueFieldType ?? "number",
17135
- fieldType: agg.valueFieldType ?? "number",
17136
- dataTypeID: fieldTypeToDataTypeID(agg.valueFieldType ?? "number")
17137
- });
17138
- }
17139
- }
17140
- return {
17141
- rows: [],
17142
- rowCount: 0,
17143
- pivotQuery: report.queryString ?? "",
17144
- columns
17145
- };
17146
- }
17147
- try {
17148
- let dateBucket = dateBucketInitial;
17149
- let filterDateRange = void 0;
17150
- if (dateFilter && dateFilter.startDate && dateFilter.endDate) {
17151
- filterDateRange = {
17152
- start: dateFilter.startDate,
17153
- end: dateFilter.endDate
17154
- };
17155
- } else if (report.dateRange) {
17156
- filterDateRange = report.dateRange;
17157
- }
17158
- if (!dateBucket && filterDateRange) {
17159
- dateBucket = getDateBucketFromRange(filterDateRange);
17160
- }
17161
- const pivotTable = await generatePivotWithSQL({
17162
- pivot,
17163
- report,
17164
- client,
17165
- dateBucket,
17166
- dateFilter,
17167
- dashboardName,
17168
- tenants,
17169
- additionalProcessing,
17170
- getToken
17171
- });
17172
- return pivotTable;
17173
- } catch (e) {
17174
- eventTracking?.logError?.({
17175
- type: "bug",
17176
- // TODO: determine type
17177
- severity: "high",
17178
- message: "Error fetching pivot table",
17179
- errorMessage: e.message,
17180
- errorStack: e.stack,
17181
- errorData: {
17182
- caller: "getPivotTable",
17183
- function: "getPivotTable"
17184
- }
17185
- });
17186
- console.error("Error fetching pivot table", e);
17187
- throw e;
17188
- }
17189
- }
17190
- return pivot && data.rows ? generatePivotTable({
17191
- pivot,
17192
- report,
17193
- client,
17194
- uniqueValues: report.distinctStrings,
17195
- dashboardName,
17196
- tenants,
17197
- dateFilter,
17198
- additionalProcessing,
17199
- getToken,
17200
- eventTracking
17201
- }) : void 0;
17202
- }
17203
- function extractPivotedYAxis(pivotTable, itemInfo, config = void 0) {
17204
- if (!pivotTable) return itemInfo?.yAxisFields ?? [];
17205
- const pivot = itemInfo?.pivot || config?.pivot;
17206
- if (!pivot.columnField && !pivot.rowField) return itemInfo?.yAxisFields ?? [];
17207
- const yAxisFields = config ? config.yAxisFields : itemInfo?.yAxisFields;
17208
- return yAxisFields && yAxisFields.length > 0 ? generatePivotTableYAxis(pivot, pivotTable.columns, yAxisFields[0]) : yAxisFields;
17209
- }
17210
- async function getDashboard(dashboardName, client, getToken, tenants, flags) {
17211
- const { data: resp } = await quillFetch({
17212
- client,
17213
- task: "dashboard",
17214
- metadata: {
17215
- name: dashboardName,
17216
- clientId: client.publicKey,
17217
- databaseType: client.databaseType,
17218
- useNewNodeSql: true,
17219
- tenants,
17220
- flags
17221
- },
17222
- getToken
17223
- });
17224
- return {
17225
- ...resp,
17226
- createdAt: resp.createdAt && new Date(resp.createdAt),
17227
- dateFilter: resp.dateFilter ? {
17228
- ...resp.dateFilter,
17229
- presetOptions: resp.dateFilter.presetOptions?.map(
17230
- (preset) => ({
17231
- ...preset,
17232
- loopStart: preset.loopStart ? new Date(preset.loopStart) : void 0,
17233
- loopEnd: preset.loopEnd ? new Date(preset.loopEnd) : void 0
17234
- })
17235
- ),
17236
- defaultPresetRanges: resp.dateFilter.defaultPresetRanges?.map(
17237
- (preset) => ({
17238
- ...preset,
17239
- loopStart: preset.loopStart ? new Date(preset.loopStart) : void 0,
17240
- loopEnd: preset.loopEnd ? new Date(preset.loopEnd) : void 0
17241
- })
17242
- )
17243
- } : void 0
17244
- };
17245
- }
17246
-
17247
- // src/utils/chartBuilder.ts
17248
- var numberFormatOptions = [
17249
- "whole_number",
17250
- "one_decimal_place",
17251
- "two_decimal_places",
17252
- "dollar_amount",
17253
- "dollar_cents",
17254
- "percentage"
17255
- ];
17256
- var dateFormatOptions = [
17257
- "MMM_yyyy",
17258
- "MMM_dd",
17259
- "MMM_dd_yyyy",
17260
- "MMM_dd_hh:mm_ap_pm",
17261
- "hh_ap_pm",
17262
- "date",
17263
- "timestamptz"
17264
- ];
17265
- var NUMBER_OPTIONS = [
17266
- { value: "whole_number", label: "whole number" },
17267
- { value: "one_decimal_place", label: "one decimal place" },
17268
- { value: "two_decimal_places", label: "two decimal places" },
17269
- { value: "dollar_amount", label: "dollar amount" },
17270
- { value: "dollar_cents", label: "dollar and cent amount" },
17271
- { value: "percent", label: "percentage" }
17272
- ];
17273
- var DATE_OPTIONS = [
17274
- { value: "MMM_yyyy", label: "month" },
17275
- { value: "MMM_dd", label: "day" },
17276
- { value: "MMM_dd_yyyy", label: "day and year" },
17277
- { value: "MMM_dd_hh:mm_ap_pm", label: "day and time" },
17278
- { value: "hh_ap_pm", label: "hour" }
17279
- ];
17280
- var ALL_FORMAT_OPTIONS = [
17281
- ...NUMBER_OPTIONS,
17282
- ...DATE_OPTIONS,
17283
- { value: "string", label: "string" }
17284
- ];
17285
- function createInitialFormData(columns) {
17286
- const firstNumberColumn = columns?.find(
17287
- (col) => numberFormatOptions.includes(col.format)
17288
- );
17289
- const firstStringColumn = columns?.find(
17290
- (col) => !numberFormatOptions.includes(col.format) && !dateFormatOptions.includes(col.format)
17291
- );
17292
- const firstDateColumn = columns?.find(
17293
- (col) => dateFormatOptions.includes(col.format)
17294
- );
17295
- const xAxisField = firstStringColumn?.field || firstDateColumn?.field || firstNumberColumn?.field || columns?.[0]?.field || "";
17296
- const xAxisFormat = firstStringColumn?.format || firstDateColumn?.format || firstNumberColumn?.format || columns?.[0]?.format || "string";
17297
- const formEmptyState = {
17298
- name: "",
17299
- columns: columns.map((col) => {
17300
- return { ...col, label: snakeAndCamelCaseToTitleCase(col.label) };
17301
- }),
17302
- xAxisField,
17303
- xAxisFormat,
17304
- yAxisFields: [
17305
- {
17306
- field: firstNumberColumn?.field || columns?.[0]?.field || "",
17307
- label: "",
17308
- format: firstNumberColumn?.format || columns?.[0]?.format || "string"
17309
- }
17310
- ],
17311
- xAxisLabel: "",
17312
- chartType: firstNumberColumn ? "line" : "table",
17313
- pivot: null,
17314
- template: true,
17315
- referenceLines: []
17316
- };
17317
- return formEmptyState;
17318
- }
17319
-
17320
- // src/utils/error.ts
17321
- var DataLoadError = class extends Error {
17322
- data;
17323
- constructor(message, data) {
17324
- super(message);
17325
- this.name = "DataLoadError";
17326
- this.data = data;
17327
- }
17328
- };
17329
-
17330
- // src/utils/errorProcessing.ts
17331
- function processFilterErrorList(resp) {
17332
- if (!resp || !resp.filterErrorList || !Array.isArray(resp.filterErrorList)) {
17333
- return;
17334
- }
17335
- }
17336
-
17337
- // src/utils/queryConstructor.ts
17338
- function replaceBigQuerySpecialCharacters(column) {
17339
- return column.replaceAll("/", "quill_forward_slash");
17340
- }
17341
- function processColumnReference(column, databaseType, fallbackOnNull, isColumnFieldAlias, isValueFieldAlias) {
17342
- switch (databaseType.toLowerCase()) {
17343
- case "postgresql":
17344
- case "clickhouse": {
17345
- if (column === "") {
17346
- return fallbackOnNull ? `"${fallbackOnNull}"` : `"_"`;
17347
- }
17348
- if (isColumnFieldAlias) {
17349
- return `"${column.replaceAll('"', "")}"`;
17350
- }
17351
- const columnParts = column.split(".");
17352
- if (columnParts.length > 1) {
17353
- return `"` + columnParts.map((part) => part.replaceAll('"', "")).join('"."') + `"`;
17354
- }
17355
- return `"${column.replaceAll('"', "")}"`;
16916
+ return `"${column.replaceAll('"', "")}"`;
17356
16917
  }
17357
16918
  case "mysql": {
17358
16919
  if (column === "") {
@@ -17426,6 +16987,51 @@ function processColumnReference(column, databaseType, fallbackOnNull, isColumnFi
17426
16987
  function replaceSpacesWithUnderscores(column) {
17427
16988
  return column.replaceAll(" ", "_");
17428
16989
  }
16990
+ function processInterval(interval2, rowField, databaseType) {
16991
+ if (["postgresql", "snowflake", "clickhouse"].includes(
16992
+ databaseType.toLowerCase()
16993
+ )) {
16994
+ return `(${processColumnReference(rowField, databaseType)} + INTERVAL '${interval2}')`;
16995
+ } else if (databaseType.toLowerCase() === "mysql") {
16996
+ return `(${processColumnReference(rowField, databaseType)} + INTERVAL ${interval2})`;
16997
+ }
16998
+ return `TIMESTAMP_ADD(${processColumnReference(rowField, databaseType)}, INTERVAL ${interval2} )`;
16999
+ }
17000
+ function processDateTrunc(dateBucket, rowField, databaseType, comparisonInterval) {
17001
+ const dateField = comparisonInterval ? processInterval(comparisonInterval, rowField, databaseType) : processColumnReference(rowField, databaseType);
17002
+ if (["postgresql", "snowflake"].includes(databaseType.toLowerCase())) {
17003
+ return `date_trunc('${dateBucket}', ${dateField})`;
17004
+ }
17005
+ if (["clickhouse"].includes(databaseType.toLowerCase())) {
17006
+ return `dateTrunc('${dateBucket}', ${dateField})`;
17007
+ }
17008
+ if (["databricks"].includes(databaseType.toLowerCase())) {
17009
+ return `DATE_TRUNC('${dateBucket}', ${dateField})`;
17010
+ }
17011
+ if (["mysql"].includes(databaseType.toLowerCase())) {
17012
+ switch (dateBucket.toLowerCase()) {
17013
+ case "year":
17014
+ return `DATE_FORMAT(${dateField}, '%Y-01-01 00:00:00')`;
17015
+ case "month":
17016
+ return `DATE_FORMAT(${dateField}, '%Y-%m-01 00:00:00')`;
17017
+ case "week":
17018
+ return `DATE_FORMAT(${dateField}, '%Y-%U-1 00:00:00')`;
17019
+ case "day":
17020
+ default:
17021
+ return `DATE(${dateField})`;
17022
+ }
17023
+ }
17024
+ if (["mssql"].includes(databaseType.toLowerCase())) {
17025
+ return `DATETRUNC(${dateBucket}, ${dateField})`;
17026
+ }
17027
+ return `TIMESTAMP_TRUNC(${dateField}, ${dateBucket})`;
17028
+ }
17029
+ function processValueField(aggType, databaseType, valueField) {
17030
+ if (aggType === "min" || aggType === "max" || aggType?.toLowerCase() === "average")
17031
+ return `${processColumnReference(valueField, databaseType)} ELSE null`;
17032
+ if (aggType === "count") return `1 ELSE 0`;
17033
+ return valueField ? `${processColumnReference(valueField, databaseType)} ELSE 0` : `1 ELSE 0`;
17034
+ }
17429
17035
  function generateCountQuery(fields, query, databaseType) {
17430
17036
  let countQuery = [];
17431
17037
  let cteQuery = `WITH querytable AS (${query.replace(";", "")}) `;
@@ -17544,12 +17150,1123 @@ function generateMinMaxDateRangeQueries(columnFields, query, databaseType) {
17544
17150
  const distinctQueries = columnFields.map((field) => {
17545
17151
  const wrappedField = ["postgresql", "clickhouse"].includes(
17546
17152
  databaseType.toLowerCase()
17547
- ) ? processColumnReference(field, databaseType) : field;
17548
- return `SELECT '${field}' AS ${processColumnReference("field", databaseType)}, ${min2}(${wrappedField})${cast} AS ${processColumnReference("min_range", databaseType)}, ${max2}(${wrappedField})${cast} AS ${processColumnReference("max_range", databaseType)} FROM querytable`;
17153
+ ) ? `"${field}"` : field;
17154
+ return `SELECT '${field}' AS field, ${min2}(${wrappedField})${cast} AS min_range, ${max2}(${wrappedField})${cast} AS max_range FROM querytable`;
17549
17155
  });
17550
17156
  const distinctQuery = distinctQueries.join(" UNION ALL ");
17551
17157
  return `WITH querytable AS (${query.replace(";", "")}) ` + distinctQuery;
17552
17158
  }
17159
+ function generatePivotQuery(pivot, itemQuery, databaseType, distinctStrings, dateBucket) {
17160
+ if (!isValidPivot(pivot).valid) {
17161
+ return void 0;
17162
+ }
17163
+ if (pivot.columnField && distinctStrings && distinctStrings.length > 0) {
17164
+ return create2DPivotQuery(
17165
+ pivot,
17166
+ itemQuery,
17167
+ databaseType,
17168
+ distinctStrings,
17169
+ dateBucket
17170
+ );
17171
+ }
17172
+ if (pivot.rowField) {
17173
+ return create1DPivotQuery(pivot, itemQuery, dateBucket, databaseType);
17174
+ }
17175
+ return createAggregationValuePivot(pivot, itemQuery, databaseType);
17176
+ }
17177
+ function create2DPivotQuery(pivot, itemQuery, databaseType, columnFieldValues, dateBucket) {
17178
+ if (!pivot || !pivot.columnField) {
17179
+ return void 0;
17180
+ }
17181
+ if (isStringType(pivot.rowFieldType ?? "") || !pivot.rowFieldType) {
17182
+ return create2DStringPivotQuery(
17183
+ pivot,
17184
+ itemQuery,
17185
+ columnFieldValues,
17186
+ databaseType
17187
+ );
17188
+ }
17189
+ return create2DDatePivotQuery(
17190
+ pivot,
17191
+ itemQuery,
17192
+ columnFieldValues,
17193
+ databaseType,
17194
+ dateBucket
17195
+ );
17196
+ }
17197
+ function create2DStringPivotQuery(pivot, itemQuery, columnFieldValues, databaseType) {
17198
+ const isValidBaseQuery = itemQuery.match(
17199
+ /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/
17200
+ );
17201
+ if (!isValidBaseQuery || !pivot.columnField || !pivot.rowField)
17202
+ return void 0;
17203
+ const rowField = pivot.rowField;
17204
+ if (!pivot.aggregations?.[0]?.valueField && pivot.aggregations?.[0]?.aggregationType !== "count" && !pivot.valueField && pivot.aggregationType !== "count")
17205
+ throw new Error("No value field provided for pivot");
17206
+ if (!pivot.aggregations?.[0]?.aggregationType && !pivot.aggregationType)
17207
+ throw new Error("No aggregation type provided for pivot");
17208
+ const columnField = pivot.columnField;
17209
+ const rowFieldAlias = processColumnReference(
17210
+ rowField,
17211
+ databaseType,
17212
+ void 0,
17213
+ false,
17214
+ true
17215
+ );
17216
+ const columnFieldAlias = processColumnReference(
17217
+ columnField,
17218
+ databaseType,
17219
+ void 0,
17220
+ false,
17221
+ true
17222
+ );
17223
+ let caseWhens = [];
17224
+ let valueAliases = [];
17225
+ const seenAggs = {};
17226
+ pivot.aggregations?.forEach((currentAgg) => {
17227
+ if (seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]) {
17228
+ seenAggs[currentAgg.aggregationType ?? ""][currentAgg.valueField ?? ""] += 1;
17229
+ } else {
17230
+ seenAggs[currentAgg.aggregationType ?? ""] = {
17231
+ ...seenAggs[currentAgg.aggregationType ?? ""],
17232
+ [currentAgg.valueField ?? ""]: 1
17233
+ };
17234
+ }
17235
+ let disambiguationIndex = seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]?.toString();
17236
+ if (disambiguationIndex === "1") disambiguationIndex = "";
17237
+ const valueFieldAlias = processColumnReference(
17238
+ currentAgg.valueField || rowField || "count",
17239
+ databaseType,
17240
+ void 0,
17241
+ false,
17242
+ true
17243
+ );
17244
+ const valueAliasSubstring = currentAgg.valueField ? `${processColumnReference(currentAgg.valueField, databaseType, void 0, true)} AS ${valueFieldAlias}` : "";
17245
+ let value2AliasSubstring = "";
17246
+ const disambiguationField = Object.values(seenAggs[currentAgg.aggregationType ?? ""] ?? {}).reduce(
17247
+ (acc, v) => acc + v
17248
+ ) > 1 ? `_${currentAgg.valueField}${disambiguationIndex}` : "";
17249
+ const disambiguation = pivot.aggregations?.length > 1 ? `${disambiguationField}_${disambiguationField ? matchCasing(currentAgg.aggregationType, currentAgg.valueField) : currentAgg.aggregationType}` : "";
17250
+ const valueExpr = currentAgg.valueFieldType === "bool" ? `CASE WHEN ${valueFieldAlias} THEN 1 ELSE 0 END` : processValueField(
17251
+ currentAgg.aggregationType,
17252
+ databaseType,
17253
+ valueFieldAlias
17254
+ );
17255
+ if (currentAgg.aggregationType === "percentage") {
17256
+ if (!currentAgg.valueField)
17257
+ throw new Error("No value field provided for pivot");
17258
+ const valueField2Alias = processColumnReference(
17259
+ currentAgg.valueField2 ?? currentAgg.valueField,
17260
+ databaseType,
17261
+ void 0,
17262
+ false,
17263
+ true
17264
+ );
17265
+ const value2Expr = (currentAgg.valueField2Type ?? currentAgg.valueFieldType) === "bool" ? `CASE WHEN ${valueField2Alias} THEN 1 ELSE 0 END` : valueField2Alias;
17266
+ if (currentAgg.valueField === currentAgg.valueField2 || !currentAgg.valueField2) {
17267
+ caseWhens = [
17268
+ ...caseWhens,
17269
+ ...columnFieldValues.map((column) => {
17270
+ return `CAST(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17271
+ column,
17272
+ databaseType
17273
+ )}' THEN ${valueExpr} END) AS FLOAT) / GREATEST(sum(${value2Expr}), 1) AS ${processColumnReference(
17274
+ column + disambiguation,
17275
+ databaseType,
17276
+ "_",
17277
+ true
17278
+ )}`;
17279
+ })
17280
+ ];
17281
+ } else {
17282
+ value2AliasSubstring = `${processColumnReference(
17283
+ currentAgg.valueField2 ?? currentAgg.valueField,
17284
+ databaseType,
17285
+ void 0,
17286
+ true
17287
+ )} AS ${valueField2Alias}`;
17288
+ caseWhens = [
17289
+ ...caseWhens,
17290
+ ...columnFieldValues.map((column) => {
17291
+ return `CAST(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17292
+ column,
17293
+ databaseType
17294
+ )}' THEN ${valueExpr} END) AS FLOAT) / GREATEST(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17295
+ column,
17296
+ databaseType
17297
+ )}' THEN ${value2Expr} END), 1) AS ${processColumnReference(
17298
+ column + disambiguation,
17299
+ databaseType,
17300
+ "_",
17301
+ true
17302
+ )}`;
17303
+ })
17304
+ ];
17305
+ }
17306
+ } else {
17307
+ caseWhens = [
17308
+ ...caseWhens,
17309
+ ...columnFieldValues.map((column) => {
17310
+ return `${processAggType(currentAgg.aggregationType, true)}(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17311
+ column,
17312
+ databaseType
17313
+ )}' THEN ${valueExpr} END) AS ${processColumnReference(column + disambiguation, databaseType, "_", true)}`;
17314
+ })
17315
+ ];
17316
+ }
17317
+ if (valueAliasSubstring) valueAliases.push(valueAliasSubstring);
17318
+ if (value2AliasSubstring) valueAliases.push(value2AliasSubstring);
17319
+ });
17320
+ valueAliases = [
17321
+ `${processColumnReference(rowField, databaseType, void 0, true)} AS ${rowFieldAlias}`,
17322
+ ...valueAliases,
17323
+ `${processColumnReference(columnField, databaseType, void 0, true)} AS ${columnFieldAlias}`
17324
+ ];
17325
+ valueAliases = Array.from(new Set(valueAliases));
17326
+ const sortQuery = pivot.sort && pivot.sortField && pivot.rowLimit ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType, void 0, true)} ${pivot.sortDirection || ""} ` : "";
17327
+ const pivotQuery = `
17328
+ ,quill_alias AS (
17329
+ SELECT ${valueAliases.length > 0 ? `${valueAliases.join(", ")}` : ""}
17330
+ FROM quill_base_table
17331
+ ),
17332
+ quill_qt_cw AS (
17333
+ SELECT ${rowFieldAlias}
17334
+ ${caseWhens.length > 0 ? `, ${caseWhens.join(", ")}` : ""}
17335
+ FROM quill_alias
17336
+ GROUP BY ${rowFieldAlias}
17337
+ ),
17338
+ quill_base_pivot AS (
17339
+ SELECT ${pivot.rowLimit && databaseType.toLowerCase() === "mssql" ? `TOP ${pivot.rowLimit}` : ""} *
17340
+ FROM quill_qt_cw qt
17341
+ ${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== "mssql" ? ` LIMIT ${pivot.rowLimit}` : ""}
17342
+ )
17343
+ SELECT * FROM quill_base_pivot
17344
+ `.replace(/\s+/g, " ").trim();
17345
+ return itemQuery.replace(
17346
+ /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/,
17347
+ pivotQuery
17348
+ );
17349
+ }
17350
+ function create2DDatePivotQuery(pivot, itemQuery, columnFieldValues, databaseType, dateBucket = "month") {
17351
+ const isValidBaseQuery = itemQuery.match(
17352
+ /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/
17353
+ );
17354
+ if (!isValidBaseQuery || !pivot.columnField || !pivot.rowField) {
17355
+ return void 0;
17356
+ }
17357
+ if (!pivot.aggregations?.[0]?.valueField && pivot.aggregations?.[0]?.aggregationType !== "count" && !pivot.valueField && pivot.aggregationType !== "count")
17358
+ throw new Error("No value field provided for pivot");
17359
+ if (!pivot.aggregations?.[0]?.aggregationType && !pivot.aggregationType)
17360
+ throw new Error("No aggregation type provided for pivot");
17361
+ const rowField = pivot.rowField;
17362
+ const columnField = pivot.columnField;
17363
+ const rowFieldAlias = processColumnReference(
17364
+ rowField,
17365
+ databaseType,
17366
+ void 0,
17367
+ false,
17368
+ true
17369
+ );
17370
+ const columnFieldAlias = processColumnReference(
17371
+ columnField,
17372
+ databaseType,
17373
+ void 0,
17374
+ false,
17375
+ true
17376
+ );
17377
+ let caseWhens = [];
17378
+ let valueFieldAliases = [];
17379
+ const seenAggs = {};
17380
+ pivot.aggregations?.forEach((currentAgg) => {
17381
+ if (seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]) {
17382
+ seenAggs[currentAgg.aggregationType ?? ""][currentAgg.valueField ?? ""] += 1;
17383
+ } else {
17384
+ seenAggs[currentAgg.aggregationType ?? ""] = {
17385
+ ...seenAggs[currentAgg.aggregationType ?? ""],
17386
+ [currentAgg.valueField ?? ""]: 1
17387
+ };
17388
+ }
17389
+ let disambiguationIndex = seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]?.toString();
17390
+ if (disambiguationIndex === "1") disambiguationIndex = "";
17391
+ const valueFieldAlias = processColumnReference(
17392
+ currentAgg.valueField ?? rowField,
17393
+ databaseType,
17394
+ void 0,
17395
+ false,
17396
+ true
17397
+ );
17398
+ const valueAliasSubstring = currentAgg.valueField ? `${processColumnReference(currentAgg.valueField, databaseType, void 0, true)} AS ${valueFieldAlias}` : "";
17399
+ let value2AliasSubstring = "";
17400
+ const disambiguationField = Object.values(seenAggs[currentAgg.aggregationType ?? ""] ?? {}).reduce(
17401
+ (acc, v) => acc + v
17402
+ ) > 1 ? `_${currentAgg.valueField}${disambiguationIndex}` : "";
17403
+ const disambiguation = pivot.aggregations?.length > 1 ? `${disambiguationField}_${disambiguationField ? matchCasing(currentAgg.aggregationType, currentAgg.valueField) : currentAgg.aggregationType}` : "";
17404
+ const valueExpr = currentAgg.valueFieldType === "bool" ? `CASE WHEN ${valueFieldAlias} THEN 1 ELSE 0 END` : processValueField(
17405
+ currentAgg.aggregationType,
17406
+ databaseType,
17407
+ valueFieldAlias
17408
+ );
17409
+ if (currentAgg.aggregationType === "percentage") {
17410
+ if (!currentAgg.valueField)
17411
+ throw new Error("No value field provided for pivot");
17412
+ const valueField2Alias = processColumnReference(
17413
+ currentAgg.valueField2 ?? currentAgg.valueField,
17414
+ databaseType,
17415
+ void 0,
17416
+ false,
17417
+ true
17418
+ );
17419
+ const value2Expr = (currentAgg.valueField2Type ?? currentAgg.valueFieldType) === "bool" ? `CASE WHEN ${valueField2Alias} THEN 1 ELSE 0 END` : valueField2Alias;
17420
+ if (currentAgg.valueField === currentAgg.valueField2 || !currentAgg.valueField2) {
17421
+ caseWhens = [
17422
+ ...caseWhens,
17423
+ ...columnFieldValues.map((column) => {
17424
+ return `CAST(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17425
+ column,
17426
+ databaseType
17427
+ )}' THEN ${valueExpr} END) AS FLOAT) / GREATEST(sum(${value2Expr}), 1) AS ${processColumnReference(
17428
+ column + disambiguation,
17429
+ databaseType,
17430
+ "_",
17431
+ true
17432
+ )}`;
17433
+ })
17434
+ ];
17435
+ } else {
17436
+ value2AliasSubstring = `${processColumnReference(
17437
+ currentAgg.valueField2 ?? currentAgg.valueField,
17438
+ databaseType,
17439
+ void 0,
17440
+ true
17441
+ )} AS ${valueField2Alias}`;
17442
+ caseWhens = [
17443
+ ...caseWhens,
17444
+ ...columnFieldValues.map((column) => {
17445
+ return `CAST(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17446
+ column,
17447
+ databaseType
17448
+ )}' THEN ${valueExpr} END) AS FLOAT) / GREATEST(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17449
+ column,
17450
+ databaseType
17451
+ )}' THEN ${value2Expr} END), 1) AS ${processColumnReference(
17452
+ column + disambiguation,
17453
+ databaseType,
17454
+ "_",
17455
+ true
17456
+ )}`;
17457
+ })
17458
+ ];
17459
+ }
17460
+ } else {
17461
+ caseWhens = [
17462
+ ...caseWhens,
17463
+ ...columnFieldValues.map((column) => {
17464
+ return `${processAggType(currentAgg.aggregationType, true)}(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17465
+ column,
17466
+ databaseType
17467
+ )}' THEN ${valueExpr} END) AS ${processColumnReference(column + disambiguation, databaseType, "_", true)}`;
17468
+ })
17469
+ ];
17470
+ }
17471
+ if (valueAliasSubstring) valueFieldAliases.push(valueAliasSubstring);
17472
+ if (value2AliasSubstring) valueFieldAliases.push(value2AliasSubstring);
17473
+ });
17474
+ valueFieldAliases = [
17475
+ `${processColumnReference(rowField, databaseType, void 0, true)} AS ${rowFieldAlias}`,
17476
+ ...valueFieldAliases,
17477
+ `${processColumnReference(columnField, databaseType, void 0, true)} AS ${columnFieldAlias}`
17478
+ ];
17479
+ valueFieldAliases = Array.from(new Set(valueFieldAliases));
17480
+ const sortQuery = `${pivot.sort && pivot.sortField && pivot.rowLimit ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType, void 0, true)} ${pivot.sortDirection || ""} ` : ""}`;
17481
+ const pivotQuery = `
17482
+ , quill_alias AS (SELECT ${valueFieldAliases.length > 0 ? `${valueFieldAliases.join(", ")}` : ""} FROM quill_base_table),
17483
+ quill_qt_agg AS (SELECT ${processDateTrunc(dateBucket, rowFieldAlias, databaseType)} as ${rowFieldAlias}${caseWhens.length > 0 ? `, ${caseWhens.join(", ")}` : ""} FROM quill_alias GROUP BY ${databaseType.toLowerCase() === "clickhouse" ? processColumnReference(`${rowField}`, databaseType) : processDateTrunc(dateBucket, rowFieldAlias, databaseType)}),
17484
+ quill_base_pivot AS (SELECT ${pivot.rowLimit && databaseType.toLowerCase() === "mssql" ? `TOP ${pivot.rowLimit}` : ""} * FROM quill_qt_agg qt
17485
+ ${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== "mssql" ? ` LIMIT ${pivot.rowLimit}` : ""})
17486
+ SELECT * FROM quill_base_pivot
17487
+ `.replace(/\s+/g, " ").trim();
17488
+ return itemQuery.replace(
17489
+ /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/,
17490
+ pivotQuery
17491
+ );
17492
+ }
17493
+ function create1DPivotQuery(pivot, itemQuery, dateBucket = "month", databaseType) {
17494
+ if (isStringType(pivot.rowFieldType || "") || !pivot.rowFieldType) {
17495
+ return create1DStringPivotQuery(pivot, itemQuery, databaseType);
17496
+ }
17497
+ return create1DDatePivotQuery(pivot, itemQuery, dateBucket, databaseType);
17498
+ }
17499
+ function create1DStringPivotQuery(pivot, itemQuery, databaseType) {
17500
+ const isValidBaseQuery = itemQuery.match(
17501
+ /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/
17502
+ );
17503
+ if (!isValidBaseQuery) return void 0;
17504
+ const rowField = pivot.rowField;
17505
+ const rowAlias = processColumnReference(
17506
+ rowField,
17507
+ databaseType,
17508
+ void 0,
17509
+ true
17510
+ );
17511
+ let quillAggSelects = [rowAlias];
17512
+ let valueFieldAliases = [];
17513
+ const seenAggs = {};
17514
+ pivot.aggregations?.forEach((currentAgg) => {
17515
+ if (!currentAgg.valueField) currentAgg.valueField = void 0;
17516
+ if (seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]) {
17517
+ seenAggs[currentAgg.aggregationType ?? ""][currentAgg.valueField ?? ""] += 1;
17518
+ } else {
17519
+ seenAggs[currentAgg.aggregationType ?? ""] = {
17520
+ ...seenAggs[currentAgg.aggregationType ?? ""],
17521
+ [currentAgg.valueField ?? ""]: 1
17522
+ };
17523
+ }
17524
+ let disambiguationIndex = seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]?.toString() ?? "";
17525
+ if (disambiguationIndex === "1") disambiguationIndex = "";
17526
+ const valueFieldAlias = processColumnReference(
17527
+ currentAgg.valueField || rowField || "count",
17528
+ databaseType,
17529
+ void 0,
17530
+ false,
17531
+ true
17532
+ );
17533
+ const valueAliasSubstring = currentAgg.valueField ? `${processColumnReference(currentAgg.valueField, databaseType, void 0, true)} AS ${valueFieldAlias}` : "";
17534
+ let value2AliasSubstring = "";
17535
+ const disambiguation = pivot.aggregations?.length > 1 ? `${disambiguationIndex}` + (currentAgg.aggregationType !== "count" ? `_${currentAgg.aggregationType}` : "") : "";
17536
+ let valueExpr = !currentAgg.valueField ? "*" : valueFieldAlias;
17537
+ if (currentAgg.valueFieldType === "bool") {
17538
+ valueExpr = `CASE WHEN ${valueFieldAlias} THEN 1 ELSE 0 END`;
17539
+ }
17540
+ if (currentAgg.aggregationType === "percentage") {
17541
+ let countPercentage = false;
17542
+ if (!currentAgg.valueField) {
17543
+ countPercentage = true;
17544
+ }
17545
+ const valueField2Alias = processColumnReference(
17546
+ currentAgg.valueField2 ?? currentAgg.valueField ?? "count",
17547
+ databaseType,
17548
+ void 0,
17549
+ false,
17550
+ true
17551
+ );
17552
+ let value2Expr = valueField2Alias;
17553
+ if ((currentAgg.valueField2Type ?? currentAgg.valueFieldType) === "bool") {
17554
+ value2Expr = `CASE WHEN ${valueField2Alias} THEN 1 ELSE 0 END`;
17555
+ }
17556
+ value2AliasSubstring = currentAgg.valueField2 && currentAgg.valueField !== currentAgg.valueField2 ? `${processColumnReference(currentAgg.valueField2, databaseType, void 0, true)} AS ${valueField2Alias}` : "";
17557
+ const percentageExpr = countPercentage ? "CAST(COUNT(*) AS FLOAT) / (SELECT COUNT(*) FROM quill_base_table)" : currentAgg.valueField === currentAgg.valueField2 || !currentAgg.valueField2 ? `CAST(sum(${valueExpr}) AS FLOAT) / ${(currentAgg.valueField2Type ?? currentAgg.valueFieldType) === "bool" ? "COUNT(*)" : "SUM(sum(" + valueExpr + ")) OVER ()"}` : `CAST(sum(${valueExpr}) AS FLOAT) / GREATEST(sum(${value2Expr}), 1)`;
17558
+ quillAggSelects = [
17559
+ ...quillAggSelects,
17560
+ `${percentageExpr} as ${processColumnReference(
17561
+ `${currentAgg.valueField ?? (disambiguation ? "count" : "percentage")}${disambiguation}`,
17562
+ databaseType,
17563
+ void 0,
17564
+ false,
17565
+ true
17566
+ )}`
17567
+ ];
17568
+ } else {
17569
+ quillAggSelects = [
17570
+ ...quillAggSelects,
17571
+ `${processAggType(currentAgg.aggregationType)}(${valueExpr}) AS ${processColumnReference(
17572
+ (currentAgg.valueField || "count") + disambiguation,
17573
+ databaseType
17574
+ )}`
17575
+ ];
17576
+ }
17577
+ if (valueAliasSubstring) valueFieldAliases.push(valueAliasSubstring);
17578
+ if (value2AliasSubstring) valueFieldAliases.push(value2AliasSubstring);
17579
+ });
17580
+ valueFieldAliases = Array.from(new Set(valueFieldAliases));
17581
+ const sortQuery = `${pivot.sort && pivot.sortField && pivot.rowLimit ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType, void 0, true)} ${pivot.sortDirection || ""} ` : ""}`;
17582
+ const pivotQuery = `, quill_alias AS (
17583
+ SELECT ${processColumnReference(`${rowField}`, databaseType, void 0, true)} AS ${rowAlias}${valueFieldAliases.length > 0 ? `, ${valueFieldAliases.join(", ")}` : ""}
17584
+ FROM quill_base_table
17585
+ ),
17586
+ quill_qt_cw AS (
17587
+ SELECT ${quillAggSelects.join(", ")} FROM quill_alias GROUP BY ${rowAlias}
17588
+ ),
17589
+ quill_base_pivot AS (
17590
+ SELECT ${pivot.rowLimit && databaseType.toLowerCase() === "mssql" ? `TOP ${pivot.rowLimit}` : ""} *
17591
+ FROM quill_qt_cw qt ${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== "mssql" ? ` LIMIT ${pivot.rowLimit}` : ""}
17592
+ )
17593
+ SELECT * FROM quill_base_pivot`.replace(/\s+/g, " ").trim();
17594
+ return itemQuery.replace(
17595
+ /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/,
17596
+ pivotQuery
17597
+ );
17598
+ }
17599
+ function create1DDatePivotQuery(pivot, itemQuery, dateBucket = "month", databaseType) {
17600
+ const isValidBaseQuery = itemQuery.match(
17601
+ /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/
17602
+ );
17603
+ if (!isValidBaseQuery) {
17604
+ return void 0;
17605
+ }
17606
+ const rowField = pivot.rowField || "";
17607
+ const rowFieldAlias = processColumnReference(
17608
+ rowField,
17609
+ databaseType,
17610
+ void 0
17611
+ );
17612
+ let quillAggSelects = [
17613
+ `${processDateTrunc(dateBucket, rowFieldAlias, databaseType)} as ${processColumnReference(rowField, databaseType)}`
17614
+ ];
17615
+ let valueFieldAliases = [];
17616
+ const seenAggs = {};
17617
+ pivot.aggregations?.forEach((currentAgg) => {
17618
+ if (!currentAgg.valueField) currentAgg.valueField = void 0;
17619
+ if (seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]) {
17620
+ seenAggs[currentAgg.aggregationType ?? ""][currentAgg.valueField ?? ""] += 1;
17621
+ } else {
17622
+ seenAggs[currentAgg.aggregationType ?? ""] = {
17623
+ ...seenAggs[currentAgg.aggregationType ?? ""],
17624
+ [currentAgg.valueField ?? ""]: 1
17625
+ };
17626
+ }
17627
+ let disambiguationIndex = seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]?.toString() ?? "";
17628
+ if (disambiguationIndex === "1") disambiguationIndex = "";
17629
+ const valueFieldAlias = processColumnReference(
17630
+ currentAgg.valueField || rowField || "count",
17631
+ databaseType,
17632
+ void 0,
17633
+ false,
17634
+ true
17635
+ );
17636
+ const valueAliasSubstring = currentAgg.valueField ? `${processColumnReference(currentAgg.valueField, databaseType, void 0, true)} AS ${valueFieldAlias}` : "";
17637
+ let value2AliasSubstring = "";
17638
+ const disambiguation = pivot.aggregations?.length > 1 ? `${disambiguationIndex}` + (currentAgg.aggregationType !== "count" ? `_${currentAgg.aggregationType}` : "") : "";
17639
+ let valueExpr = !currentAgg.valueField ? "*" : valueFieldAlias;
17640
+ if (currentAgg.valueFieldType === "bool") {
17641
+ valueExpr = `CASE WHEN ${valueFieldAlias} THEN 1 ELSE 0 END`;
17642
+ }
17643
+ if (currentAgg.aggregationType === "percentage") {
17644
+ let countPercentage = false;
17645
+ if (!currentAgg.valueField) {
17646
+ countPercentage = true;
17647
+ }
17648
+ const valueField2Alias = processColumnReference(
17649
+ currentAgg.valueField2 ?? currentAgg.valueField ?? "count",
17650
+ databaseType,
17651
+ void 0,
17652
+ false,
17653
+ true
17654
+ );
17655
+ value2AliasSubstring = currentAgg.valueField2 && currentAgg.valueField !== currentAgg.valueField2 ? `${processColumnReference(currentAgg.valueField2, databaseType, void 0, true)} AS ${valueField2Alias}` : "";
17656
+ let value2Expr = valueField2Alias;
17657
+ if ((currentAgg.valueField2Type ?? currentAgg.valueFieldType) === "bool") {
17658
+ value2Expr = `CASE WHEN ${valueField2Alias} THEN 1 ELSE 0 END`;
17659
+ }
17660
+ const percentageExpr = countPercentage ? "CAST(COUNT(*) AS FLOAT) / (SELECT COUNT(*) FROM quill_base_table)" : currentAgg.valueField === currentAgg.valueField2 || !currentAgg.valueField2 ? `CAST(sum(${valueExpr}) AS FLOAT) / ${(currentAgg.valueField2Type ?? currentAgg.valueFieldType) === "bool" ? "COUNT(*)" : "SUM(sum(" + valueExpr + ")) OVER ()"}` : `CAST(sum(${valueExpr}) AS FLOAT) / GREATEST(sum(${value2Expr}), 1)`;
17661
+ quillAggSelects = [
17662
+ ...quillAggSelects,
17663
+ `${percentageExpr} as ${processColumnReference(
17664
+ `${currentAgg.valueField ?? (disambiguation ? "count" : "percentage")}${disambiguation}`,
17665
+ databaseType,
17666
+ void 0,
17667
+ false,
17668
+ true
17669
+ )}`
17670
+ ];
17671
+ } else {
17672
+ quillAggSelects = [
17673
+ ...quillAggSelects,
17674
+ `${processAggType(currentAgg.aggregationType)}(${valueExpr}) AS ${processColumnReference((currentAgg.valueField || "count") + disambiguation, databaseType)}`
17675
+ ];
17676
+ }
17677
+ if (valueAliasSubstring) valueFieldAliases.push(valueAliasSubstring);
17678
+ if (value2AliasSubstring) valueFieldAliases.push(value2AliasSubstring);
17679
+ });
17680
+ valueFieldAliases = Array.from(new Set(valueFieldAliases));
17681
+ const sortQuery = `${pivot.sort && pivot.sortField && pivot.rowLimit ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType, void 0, true)} ${pivot.sortDirection || ""} ` : ""}`;
17682
+ const pivotQuery = `, quill_alias AS (SELECT ${processColumnReference(`${rowField}`, databaseType, void 0, true)} AS ${rowFieldAlias}${valueFieldAliases.length > 0 ? `, ${valueFieldAliases.join(", ")}` : ""}
17683
+ FROM quill_base_table),
17684
+ quill_qt_agg AS (SELECT ${quillAggSelects.join(", ")}
17685
+ FROM quill_alias GROUP BY ${databaseType.toLowerCase() === "clickhouse" ? processColumnReference(`${rowField}`, databaseType) : processDateTrunc(dateBucket, rowFieldAlias, databaseType)}),
17686
+ quill_base_pivot AS (SELECT ${pivot.rowLimit && databaseType.toLowerCase() === "mssql" ? `TOP ${pivot.rowLimit}` : ""} * FROM quill_qt_agg qt
17687
+ ${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== "mssql" ? ` LIMIT ${pivot.rowLimit}` : ""})
17688
+ SELECT * FROM quill_base_pivot
17689
+ `.replace(/\s+/g, " ").trim();
17690
+ return itemQuery.replace(
17691
+ /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/,
17692
+ pivotQuery
17693
+ );
17694
+ }
17695
+ function createAggregationValuePivot(pivot, itemQuery, databaseType) {
17696
+ const isValidBaseQuery = itemQuery.match(
17697
+ /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/
17698
+ );
17699
+ if (!isValidBaseQuery) return void 0;
17700
+ let quillAggSelects = [];
17701
+ let valueFieldAliases = [];
17702
+ const seenAggs = {};
17703
+ pivot.aggregations?.forEach((currentAgg) => {
17704
+ if (!currentAgg.valueField) currentAgg.valueField = void 0;
17705
+ if (seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]) {
17706
+ seenAggs[currentAgg.aggregationType ?? ""][currentAgg.valueField ?? ""] += 1;
17707
+ } else {
17708
+ seenAggs[currentAgg.aggregationType ?? ""] = {
17709
+ ...seenAggs[currentAgg.aggregationType ?? ""],
17710
+ [currentAgg.valueField ?? ""]: 1
17711
+ };
17712
+ }
17713
+ let disambiguationIndex = seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]?.toString() ?? "";
17714
+ if (disambiguationIndex === "1") disambiguationIndex = "";
17715
+ const valueFieldAlias = processColumnReference(
17716
+ currentAgg.valueField || "count",
17717
+ databaseType,
17718
+ void 0,
17719
+ false,
17720
+ true
17721
+ );
17722
+ const valueAliasSubstring = currentAgg.valueField ? `${processColumnReference(currentAgg.valueField, databaseType, void 0, true)} AS ${valueFieldAlias}` : "";
17723
+ let value2AliasSubstring = "";
17724
+ const disambiguation = pivot.aggregations?.length > 1 ? `${disambiguationIndex}_${currentAgg.aggregationType}` : "";
17725
+ let valueExpr = !currentAgg.valueField ? "*" : valueFieldAlias;
17726
+ valueExpr = currentAgg.valueFieldType === "bool" ? `CASE WHEN ${valueFieldAlias} THEN 1 ELSE 0 END` : valueExpr;
17727
+ if (currentAgg.aggregationType === "percentage") {
17728
+ if (!currentAgg.valueField) {
17729
+ throw new Error("No value field provided for percentage aggregation");
17730
+ }
17731
+ const valueField2Alias = processColumnReference(
17732
+ currentAgg.valueField2 ?? currentAgg.valueField,
17733
+ databaseType,
17734
+ void 0,
17735
+ false,
17736
+ true
17737
+ );
17738
+ const value2Expr = (currentAgg.valueField2Type ?? currentAgg.valueFieldType) === "bool" ? `CASE WHEN ${valueField2Alias} THEN 1 ELSE 0 END` : valueField2Alias;
17739
+ value2AliasSubstring = currentAgg.valueField2 && currentAgg.valueField !== currentAgg.valueField2 ? `${processColumnReference(currentAgg.valueField2, databaseType, void 0, true)} AS ${valueField2Alias}` : "";
17740
+ const percentageExpr = currentAgg.valueField === currentAgg.valueField2 || !currentAgg.valueField2 ? `CAST(sum(${valueExpr}) AS FLOAT) / ${(currentAgg.valueField2Type ?? currentAgg.valueFieldType) === "bool" ? "COUNT(*)" : "SUM(sum(" + valueExpr + ")) OVER ()"}` : `CAST(sum(${valueExpr}) AS FLOAT) / GREATEST(sum(${value2Expr}), 1)`;
17741
+ quillAggSelects = [
17742
+ ...quillAggSelects,
17743
+ `${percentageExpr} as ${processColumnReference(
17744
+ `${currentAgg.valueField ?? ""}${disambiguation}`,
17745
+ databaseType,
17746
+ void 0,
17747
+ false,
17748
+ true
17749
+ )}`
17750
+ ];
17751
+ } else {
17752
+ quillAggSelects = [
17753
+ ...quillAggSelects,
17754
+ `${processAggType(currentAgg.aggregationType)}(${valueExpr}) AS ${processColumnReference(
17755
+ (currentAgg.valueField || "count") + disambiguation,
17756
+ databaseType
17757
+ )}`
17758
+ ];
17759
+ }
17760
+ if (valueAliasSubstring) valueFieldAliases.push(valueAliasSubstring);
17761
+ if (value2AliasSubstring) valueFieldAliases.push(value2AliasSubstring);
17762
+ });
17763
+ valueFieldAliases = Array.from(new Set(valueFieldAliases));
17764
+ if (valueFieldAliases.length === 0) {
17765
+ valueFieldAliases = ["*"];
17766
+ }
17767
+ const sortQuery = pivot.sort && pivot.sortField && pivot.rowLimit ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType, void 0, true)} ${pivot.sortDirection || ""} ` : "";
17768
+ const pivotQuery = `, quill_alias AS (
17769
+ SELECT ${valueFieldAliases.join(", ")} FROM quill_base_table
17770
+ ),
17771
+ quill_qt_agg AS (
17772
+ SELECT ${quillAggSelects.join(", ")} FROM quill_alias
17773
+ ),
17774
+ quill_base_pivot AS (
17775
+ SELECT ${pivot.rowLimit && databaseType.toLowerCase() === "mssql" ? `TOP ${pivot.rowLimit}` : ""} * FROM quill_qt_agg qt
17776
+ ${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== "mssql" ? ` LIMIT ${pivot.rowLimit}` : ""}
17777
+ )
17778
+ SELECT * FROM quill_base_pivot`.replace(/\s+/g, " ").trim();
17779
+ return itemQuery.replace(
17780
+ /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/,
17781
+ pivotQuery
17782
+ );
17783
+ }
17784
+ function additionalProcessingOnPivotQuery(pivot, query, additionalProcessing, databaseType = "postgresql") {
17785
+ if (!additionalProcessing || !query) return query;
17786
+ const isValidBaseQuery = query.match(
17787
+ /SELECT \* FROM\s+["'[`]?quill_base_pivot["'\]`]?\s*$/
17788
+ );
17789
+ if (!isValidBaseQuery) {
17790
+ return void 0;
17791
+ }
17792
+ if (!pivot.aggregations || pivot.aggregations.length === 0) {
17793
+ if (pivot.aggregationType) {
17794
+ pivot.aggregations = [
17795
+ {
17796
+ aggregationType: pivot.aggregationType,
17797
+ valueField: pivot.valueField,
17798
+ valueField2: pivot.valueField2
17799
+ }
17800
+ ];
17801
+ } else {
17802
+ throw new Error("No aggregations provided for pivot");
17803
+ }
17804
+ }
17805
+ let rowsPerPage = 0;
17806
+ let currentInterval = 0;
17807
+ let offset = 0;
17808
+ let limit = 1e3;
17809
+ let sortQuery = "";
17810
+ if (additionalProcessing.page) {
17811
+ const page = additionalProcessing.page.page || 0;
17812
+ if (additionalProcessing.page.rowsPerRequest) {
17813
+ limit = additionalProcessing.page.rowsPerRequest;
17814
+ }
17815
+ rowsPerPage = additionalProcessing.page.rowsPerPage || 0;
17816
+ currentInterval = page ? Math.floor(page / (limit / rowsPerPage)) : 0;
17817
+ offset = currentInterval * limit;
17818
+ }
17819
+ const disambiguation = pivot.aggregations.length > 1 ? `_${matchCasing(pivot.aggregations?.[0]?.aggregationType, pivot.aggregations?.[0]?.valueField)}` : "";
17820
+ if (additionalProcessing.sort) {
17821
+ sortQuery = `ORDER BY ${processColumnReference(additionalProcessing.sort.field, databaseType, void 0, true)} ${additionalProcessing.sort.direction || ""}`;
17822
+ } else {
17823
+ const valueFieldAlias = processColumnReference(
17824
+ (pivot.aggregations?.[0]?.valueField ?? "") + disambiguation,
17825
+ databaseType,
17826
+ void 0,
17827
+ false,
17828
+ true
17829
+ );
17830
+ const defaultSortField = pivot.sortField || pivot.rowField || valueFieldAlias;
17831
+ const defaultSortDirection = pivot.sortDirection || "";
17832
+ if (defaultSortField !== `"_"`) {
17833
+ sortQuery = `ORDER BY ${processColumnReference(defaultSortField, databaseType, void 0, true)} ${defaultSortDirection}`;
17834
+ } else {
17835
+ sortQuery = "";
17836
+ }
17837
+ }
17838
+ const additionalProcessingQuery = `
17839
+ SELECT *
17840
+ FROM quill_base_pivot ${sortQuery}${databaseType.toLowerCase() === "mssql" ? ` OFFSET ${offset} ROWS FETCH NEXT ${limit} ROWS ONLY` : ` LIMIT ${limit} OFFSET ${offset}`}
17841
+ `.replace(/\s+/g, " ").trim();
17842
+ return query.replace(
17843
+ /SELECT \* FROM\s+["'[`]?quill_base_pivot["'\]`]?\s*$/,
17844
+ additionalProcessingQuery
17845
+ );
17846
+ }
17847
+ function generateRowCountQuery(query, databaseType) {
17848
+ if (!query) return query;
17849
+ const isValidBaseQuery = query.match(
17850
+ /SELECT \* FROM\s+["'[`]?quill_base_pivot["'\]`]?\s*$/
17851
+ );
17852
+ if (!isValidBaseQuery) {
17853
+ return void 0;
17854
+ }
17855
+ const rowCountQuery = `, subview_row_count_cte AS (SELECT * FROM quill_base_pivot)
17856
+ SELECT count(*) as ${processColumnReference("row_count", databaseType || "postgresql", void 0, true)} FROM subview_row_count_cte
17857
+ `.replace(/\s+/g, " ").trim();
17858
+ return query.replace(
17859
+ /SELECT \* FROM\s+["'[`]?quill_base_pivot["'\]`]?\s*$/,
17860
+ rowCountQuery
17861
+ );
17862
+ }
17863
+
17864
+ // src/utils/pivotConstructor.ts
17865
+ async function generatePivotWithSQL({
17866
+ pivot,
17867
+ report,
17868
+ client,
17869
+ dateBucket,
17870
+ dateFilter,
17871
+ distinctStrings,
17872
+ dashboardName,
17873
+ tenants,
17874
+ additionalProcessing,
17875
+ pivotQuery,
17876
+ comparisonPivotQuery,
17877
+ getPivotRowCount = true,
17878
+ caller,
17879
+ getToken
17880
+ }) {
17881
+ let sqlQuery = pivotQuery;
17882
+ let comparisonPivotSql = comparisonPivotQuery;
17883
+ const databaseType = client.databaseType || "postgresql";
17884
+ if (!pivotQuery && pivot.columnField && !pivot.columnValues && !distinctStrings) {
17885
+ if (!report?.rows) {
17886
+ throw new Error("No distinct strings provided for column field");
17887
+ } else {
17888
+ distinctStrings = Array.from(
17889
+ new Set(
17890
+ report.rows.map((row) => row[pivot.columnField]).filter((value) => value !== null && value !== void 0)
17891
+ )
17892
+ );
17893
+ }
17894
+ }
17895
+ if (!pivot.aggregations?.length && pivot.aggregationType) {
17896
+ pivot.aggregations = [
17897
+ {
17898
+ aggregationType: pivot.aggregationType,
17899
+ valueField: pivot.valueField,
17900
+ valueFieldType: pivot.valueFieldType,
17901
+ valueField2: pivot.valueField2,
17902
+ valueField2Type: pivot.valueField2Type
17903
+ }
17904
+ ];
17905
+ }
17906
+ let comparisonInterval = void 0;
17907
+ if (dateFilter && dateFilter.comparisonRange) {
17908
+ comparisonInterval = getComparisonInterval(
17909
+ {
17910
+ startDate: dateFilter.comparisonRange.startDate,
17911
+ endDate: dateFilter.comparisonRange.endDate
17912
+ },
17913
+ dateBucket
17914
+ );
17915
+ }
17916
+ if (pivot.rowField && !pivot.rowFieldType) {
17917
+ const rowColumn = report?.columns.find(
17918
+ (column) => column.field === pivot.rowField
17919
+ );
17920
+ pivot.rowFieldType = rowColumn?.format || "string";
17921
+ }
17922
+ const filteredDistinctStrings = distinctStrings?.filter(
17923
+ (value) => value !== null && value !== void 0 && value !== ""
17924
+ );
17925
+ const pivotColumnFields = filteredDistinctStrings?.slice(
17926
+ 0,
17927
+ MAX_PIVOT_UNIQUE_VALUES
17928
+ );
17929
+ if (!pivotQuery && report) {
17930
+ if (!report.itemQuery) {
17931
+ throw Error("No item query found in report");
17932
+ }
17933
+ const itemQuery = report.itemQuery[0];
17934
+ const comparisonQuery = report.itemQuery[1];
17935
+ if (!itemQuery) {
17936
+ throw Error("No item query found in report");
17937
+ }
17938
+ sqlQuery = generatePivotQuery(
17939
+ pivot,
17940
+ itemQuery,
17941
+ databaseType,
17942
+ pivotColumnFields,
17943
+ dateBucket
17944
+ );
17945
+ comparisonPivotSql = comparisonQuery ? generatePivotQuery(
17946
+ pivot,
17947
+ comparisonQuery,
17948
+ databaseType,
17949
+ pivotColumnFields,
17950
+ dateBucket
17951
+ ) : "";
17952
+ }
17953
+ if (!sqlQuery) {
17954
+ throw "Error generating pivot query";
17955
+ }
17956
+ const paginatedSqlQuery = additionalProcessingOnPivotQuery(
17957
+ pivot,
17958
+ sqlQuery,
17959
+ additionalProcessing,
17960
+ client.databaseType
17961
+ );
17962
+ const paginatedComparisonQuery = comparisonPivotSql ? additionalProcessingOnPivotQuery(
17963
+ pivot,
17964
+ comparisonPivotSql,
17965
+ additionalProcessing,
17966
+ client.databaseType
17967
+ ) : "";
17968
+ const preQueries = [paginatedSqlQuery];
17969
+ getPivotRowCount = getPivotRowCount && (report?.chartType === "table" || caller === "ReportBuilder");
17970
+ if (getPivotRowCount) {
17971
+ const pivotRowCountQuery = generateRowCountQuery(
17972
+ sqlQuery,
17973
+ client.databaseType
17974
+ );
17975
+ preQueries.push(pivotRowCountQuery);
17976
+ }
17977
+ if (paginatedComparisonQuery) {
17978
+ preQueries.push(paginatedComparisonQuery);
17979
+ }
17980
+ const { data: resp } = await quillFetch({
17981
+ client,
17982
+ task: "query",
17983
+ metadata: {
17984
+ preQueries,
17985
+ clientId: client.publicKey,
17986
+ databaseType,
17987
+ runQueryConfig: {
17988
+ overridePost: true,
17989
+ convertDatatypes: true
17990
+ },
17991
+ useNewNodeSql: true,
17992
+ dashboardName,
17993
+ tenants
17994
+ },
17995
+ urlParameters: `caller=generatePivot&task=query`,
17996
+ credentials: "same-origin",
17997
+ getToken
17998
+ });
17999
+ if (resp.success === false) {
18000
+ throw resp.errorMessage;
18001
+ }
18002
+ const queryResponseRows = resp?.queryResults?.[0]?.rows || [];
18003
+ const queryResponseFields = resp?.queryResults?.[0]?.fields || [];
18004
+ const queryComparisonResponseRows = (getPivotRowCount ? resp?.queryResults?.[2]?.rows : resp?.queryResults?.[1]?.rows) || [];
18005
+ const queryComparisonResponseFields = (getPivotRowCount ? resp?.queryResults?.[2]?.fields : resp?.queryResults?.[1]?.fields) || [];
18006
+ parseValueFromBigQueryDates(queryResponseRows, queryResponseFields);
18007
+ parseValueFromBigQueryDates(
18008
+ queryComparisonResponseRows,
18009
+ queryComparisonResponseFields
18010
+ );
18011
+ const responseRows = mergeComparisonPivotRows({
18012
+ pivot,
18013
+ rows: queryResponseRows,
18014
+ compRows: queryComparisonResponseRows,
18015
+ databaseType,
18016
+ dateBucket,
18017
+ comparisonInterval,
18018
+ columnFieldValues: pivotColumnFields
18019
+ });
18020
+ const responseFields = mergeComparisonPivotColumns({
18021
+ pivot,
18022
+ rows: queryResponseFields,
18023
+ compRows: queryComparisonResponseFields
18024
+ });
18025
+ const rows = pivot.rowField ? responseRows.map(
18026
+ (row) => !row[pivot.rowField] ? { ...row, [pivot.rowField]: "-" } : row
18027
+ ) : responseRows;
18028
+ if (pivot.columnField && client.databaseType?.toLowerCase() === "bigquery") {
18029
+ rows.forEach((row) => {
18030
+ Object.keys(row).forEach((key) => {
18031
+ const processedKey = processColumnName(key);
18032
+ if (processedKey !== key) {
18033
+ row[processedKey] = row[key];
18034
+ delete row[key];
18035
+ }
18036
+ });
18037
+ });
18038
+ }
18039
+ const columns = responseFields?.map((field) => ({
18040
+ field: processColumnName(field.name),
18041
+ label: snakeCaseToTitleCase(
18042
+ processColumnName(field.name.replace("comparison_", "comparison "))
18043
+ ),
18044
+ format: field.name === pivot.rowField ? "string" : (
18045
+ // This scary equation is calculating which aggregation a column is associated with.
18046
+ // Eg you have 7 columns and 2 aggregations, you can assume column 0 is the row field, 1-3 is aggregation 0, and 4-6 is aggregation 1.
18047
+ // pivot.aggregations?.[
18048
+ // Math.floor(
18049
+ // (index - 1) /
18050
+ // Math.floor(
18051
+ // (responseFields.length - 1) /
18052
+ // (pivot.aggregations?.length ?? 1),
18053
+ // ),
18054
+ // )
18055
+ // ]?.aggregationType === 'percentage'
18056
+ pivot.aggregations?.find(
18057
+ (agg) => agg.valueField === field.name || `${agg.valueField}_percentage` === field.name || !agg.valueField && agg.aggregationType === "percentage" && field.name.endsWith("percentage")
18058
+ )?.aggregationType === "percentage" ? "percent" : "whole_number"
18059
+ ),
18060
+ fieldType: field.fieldType,
18061
+ jsType: field.jsType,
18062
+ dataTypeID: field.dataTypeID
18063
+ })).filter(
18064
+ (field, index) => field.field !== "comparison_" + pivot.rowField || index === 0
18065
+ ).sort((a, b) => {
18066
+ if (a.field === pivot.rowField) {
18067
+ return -1;
18068
+ }
18069
+ if (b.field === pivot.rowField) {
18070
+ return 1;
18071
+ }
18072
+ return 0;
18073
+ });
18074
+ if (pivot.rowField && !isStringType(pivot.rowFieldType || "")) {
18075
+ rows.forEach((row) => {
18076
+ row.__quillRawDate = typeof row[pivot.rowField || ""] === "object" ? row[pivot.rowField || ""].value : row[pivot.rowField || ""];
18077
+ let value = typeof row[pivot.rowField || ""] === "object" ? row[pivot.rowField || ""].value : row[pivot.rowField || ""];
18078
+ if (dateBucket === "week" && dateFilter?.startDate && dateFilter?.endDate) {
18079
+ const rowDate = new Date(value);
18080
+ if (rowDate < dateFilter.startDate) {
18081
+ value = dateFilter.startDate.toISOString();
18082
+ } else if (rowDate > dateFilter.endDate) {
18083
+ value = dateFilter.endDate.toISOString();
18084
+ }
18085
+ }
18086
+ const dateString = getDateString(
18087
+ value,
18088
+ dateFilter?.startDate && dateFilter?.endDate ? { start: dateFilter.startDate, end: dateFilter.endDate } : void 0,
18089
+ dateBucket,
18090
+ databaseType
18091
+ );
18092
+ row[pivot.rowField || ""] = dateString;
18093
+ });
18094
+ if (pivot.rowField && pivot.rowFieldType && !isStringType(pivot.rowFieldType) && dateFilter?.startDate && dateFilter?.endDate) {
18095
+ const dateSet = new Set(
18096
+ rows.map((row) => row[pivot.rowField || ""])
18097
+ );
18098
+ for (let date = dateFilter.startDate; date <= dateFilter.endDate; date = new Date(date.getTime() + 24 * 60 * 60 * 1e3)) {
18099
+ const formattedDate = getDateString(
18100
+ date.toISOString(),
18101
+ { start: dateFilter.startDate, end: dateFilter.endDate },
18102
+ dateBucket,
18103
+ databaseType
18104
+ );
18105
+ if (!dateSet.has(formattedDate)) {
18106
+ const newRow = {};
18107
+ newRow[pivot.rowField] = formattedDate;
18108
+ newRow.__quillRawDate = date.toISOString();
18109
+ rows.push(newRow);
18110
+ dateSet.add(formattedDate);
18111
+ }
18112
+ }
18113
+ }
18114
+ if (!pivot.sort) {
18115
+ rows.sort((a, b) => {
18116
+ if (a.__quillRawDate < b.__quillRawDate) {
18117
+ return -1;
18118
+ }
18119
+ if (a.__quillRawDate > b.__quillRawDate) {
18120
+ return 1;
18121
+ }
18122
+ return 0;
18123
+ });
18124
+ }
18125
+ }
18126
+ columns?.forEach((column, index) => {
18127
+ if (column.label && ["null", "undefined"].includes(column.label.toLowerCase()) && !pivot.columnField && !pivot.aggregations?.[index]?.valueField && pivot.aggregations?.[index]?.aggregationType === "count") {
18128
+ column.label = "Count";
18129
+ }
18130
+ });
18131
+ const numericColumns = columns?.filter(
18132
+ (column) => column.format === "whole_number" || column.format === "percentage"
18133
+ );
18134
+ rows.forEach((row) => {
18135
+ numericColumns?.forEach((column) => {
18136
+ row[column.field] = row[column.field] ?? 0;
18137
+ });
18138
+ });
18139
+ return {
18140
+ rows,
18141
+ columns: columns ?? [],
18142
+ rowCount: getPivotRowCount ? Number(resp?.queryResults?.[1]?.rows?.[0]?.["row_count"]) ?? rows.length : 0,
18143
+ pivotQuery: sqlQuery,
18144
+ comparisonPivotQuery: comparisonPivotSql
18145
+ };
18146
+ }
18147
+ function generatePivotTableYAxis(pivot, cols, yAxisField) {
18148
+ if (pivot?.aggregationType === "count") {
18149
+ return [
18150
+ {
18151
+ field: pivot.valueField ?? "count",
18152
+ label: yAxisField.label,
18153
+ format: yAxisField.format
18154
+ }
18155
+ ];
18156
+ }
18157
+ return [
18158
+ {
18159
+ field: pivot.valueField ?? "count",
18160
+ label: yAxisField.label,
18161
+ format: yAxisField.format
18162
+ }
18163
+ ];
18164
+ }
18165
+ function generatePivotTitle(pivot) {
18166
+ if (pivot.rowField && !pivot.valueField && pivot.aggregations?.[0]) {
18167
+ return snakeAndCamelCaseToTitleCase(
18168
+ `${pivot.aggregations[0].aggregationType} of ${pivot.rowField}${pivot.columnField ? ` by ${pivot.columnField}` : ""}
18169
+ `
18170
+ );
18171
+ } else if (!pivot.rowField && pivot.aggregations?.[0]?.valueField) {
18172
+ return snakeAndCamelCaseToTitleCase(
18173
+ `${pivot.aggregations[0].aggregationType} of ${pivot.aggregations[0].valueField}
18174
+ `
18175
+ );
18176
+ }
18177
+ return snakeAndCamelCaseToTitleCase(
18178
+ `${pivot.aggregations?.[0]?.aggregationType ?? "Aggregation"} of ${pivot.aggregations?.[0]?.valueField ?? "value"}${pivot.rowField ? ` by ${pivot.rowField}` : ""}${pivot.columnField ? ` and ${pivot.columnField}` : ""}`
18179
+ );
18180
+ }
18181
+ async function generatePivotTable({
18182
+ pivot,
18183
+ dateBucket,
18184
+ dateFilter,
18185
+ report,
18186
+ client,
18187
+ getToken,
18188
+ eventTracking,
18189
+ uniqueValues,
18190
+ dashboardName,
18191
+ tenants,
18192
+ additionalProcessing,
18193
+ caller,
18194
+ pivotQuery
18195
+ }) {
18196
+ try {
18197
+ if (report && client) {
18198
+ const pivotTable = await generatePivotWithSQL({
18199
+ pivotQuery,
18200
+ pivot,
18201
+ report,
18202
+ client,
18203
+ dateBucket,
18204
+ dateFilter,
18205
+ distinctStrings: pivot.columnField && uniqueValues?.[pivot.columnField] ? uniqueValues[pivot.columnField] : [],
18206
+ dashboardName,
18207
+ tenants,
18208
+ additionalProcessing,
18209
+ caller,
18210
+ getToken
18211
+ });
18212
+ return pivotTable;
18213
+ }
18214
+ } catch (e) {
18215
+ eventTracking?.logError?.({
18216
+ type: "bug",
18217
+ // TODO: determine type
18218
+ severity: "high",
18219
+ message: "Error generating pivot table",
18220
+ errorMessage: e.message,
18221
+ errorStack: e.stack,
18222
+ errorData: {
18223
+ caller: "PivotModal",
18224
+ function: "generatePivotTable"
18225
+ }
18226
+ });
18227
+ throw Error(`Failed to generate pivot table with SQL: ${e}`);
18228
+ }
18229
+ throw Error("Failed to generate pivot table: invalid report");
18230
+ }
18231
+
18232
+ // src/utils/errorProcessing.ts
18233
+ function processFilterErrorList(resp) {
18234
+ if (!resp || !resp.filterErrorList || !Array.isArray(resp.filterErrorList)) {
18235
+ return;
18236
+ }
18237
+ }
18238
+
18239
+ // src/utils/paginationProcessing.ts
18240
+ var DEFAULT_PAGINATION = {
18241
+ page: 0,
18242
+ rowsPerPage: 10,
18243
+ rowsPerRequest: 600
18244
+ };
18245
+ var DEFAULT_TABLE_PAGINATION = {
18246
+ page: 0,
18247
+ rowsPerPage: 10,
18248
+ rowsPerRequest: 50
18249
+ };
18250
+ function shouldFetchMore(pagination, page, maxPage, currentRowCount) {
18251
+ if (!pagination || currentRowCount && currentRowCount >= pagination.rowsPerPage * (page + 1)) {
18252
+ return false;
18253
+ }
18254
+ const indexAdjustedPage = page;
18255
+ const pageInterval = Math.floor(
18256
+ indexAdjustedPage * pagination.rowsPerPage / pagination.rowsPerRequest
18257
+ );
18258
+ const indexAdjustedPreviousPage = maxPage;
18259
+ const previousPageInterval = Math.floor(
18260
+ indexAdjustedPreviousPage * pagination.rowsPerPage / pagination.rowsPerRequest
18261
+ );
18262
+ return pageInterval > previousPageInterval;
18263
+ }
18264
+ function shouldSortInMemory(pagination, rowCount) {
18265
+ if (!rowCount || rowCount < pagination.rowsPerRequest) {
18266
+ return true;
18267
+ }
18268
+ return false;
18269
+ }
17553
18270
 
17554
18271
  // src/utils/tableProcessing.ts
17555
18272
  var getUniqueValuesByQuery = async ({
@@ -18209,86 +18926,571 @@ var fetchResultsByReport = async ({
18209
18926
  if (client.databaseType && client.databaseType.toLowerCase() === "bigquery") {
18210
18927
  parseValueFromBigQueryDates(rows, columns);
18211
18928
  }
18212
- return { rows, columns, rowCount, error };
18213
- };
18214
- var fetchTableByAST = async (ast, client, getToken, tenants, eventTracking, dashboardName, processing, customFields, rowsOnly, rowCountOnly) => {
18215
- let rows = [];
18216
- let columns = [];
18217
- let rowCount;
18218
- let error;
18219
- let itemQuery;
18220
- let referencedTables;
18221
- try {
18222
- const fetchResp = await quillFetch({
18223
- client,
18224
- task: "patterns",
18225
- metadata: {
18226
- clientId: client.publicKey,
18227
- ast,
18228
- customFields,
18229
- additionalProcessing: processing,
18230
- useUpdatedDataGathering: true,
18231
- useNewNodeSql: true,
18232
- rowsOnly,
18233
- rowCountOnly,
18234
- dashboardName,
18235
- tenants
18236
- },
18237
- getToken
18238
- });
18239
- const resp = await parseFetchResponse(
18240
- client,
18241
- "patterns",
18242
- fetchResp,
18243
- getToken
18244
- );
18245
- if (resp.errorMessage || resp.name === "error") {
18246
- throw new Error(resp.errorMessage);
18247
- }
18248
- processFilterErrorList(resp);
18249
- if (resp.rowCount !== void 0) {
18250
- rowCount = resp.rowCount;
18929
+ return { rows, columns, rowCount, error };
18930
+ };
18931
+ var fetchTableByAST = async (ast, client, getToken, tenants, eventTracking, dashboardName, processing, customFields, rowsOnly, rowCountOnly) => {
18932
+ let rows = [];
18933
+ let columns = [];
18934
+ let rowCount;
18935
+ let error;
18936
+ let itemQuery;
18937
+ let referencedTables;
18938
+ try {
18939
+ const fetchResp = await quillFetch({
18940
+ client,
18941
+ task: "patterns",
18942
+ metadata: {
18943
+ clientId: client.publicKey,
18944
+ ast,
18945
+ customFields,
18946
+ additionalProcessing: processing,
18947
+ useUpdatedDataGathering: true,
18948
+ useNewNodeSql: true,
18949
+ rowsOnly,
18950
+ rowCountOnly,
18951
+ dashboardName,
18952
+ tenants
18953
+ },
18954
+ getToken
18955
+ });
18956
+ const resp = await parseFetchResponse(
18957
+ client,
18958
+ "patterns",
18959
+ fetchResp,
18960
+ getToken
18961
+ );
18962
+ if (resp.errorMessage || resp.name === "error") {
18963
+ throw new Error(resp.errorMessage);
18964
+ }
18965
+ processFilterErrorList(resp);
18966
+ if (resp.rowCount !== void 0) {
18967
+ rowCount = resp.rowCount;
18968
+ }
18969
+ const gatheredRows = resp.rows && resp.rows.length ? resp.rows : [];
18970
+ columns = resp.fields.map((elem) => convertPostgresColumn(elem));
18971
+ rows = gatheredRows;
18972
+ itemQuery = resp.itemQuery;
18973
+ referencedTables = resp.referencedTables;
18974
+ if (customFields) {
18975
+ const tables = referencedTables ?? [];
18976
+ tables.forEach((table) => {
18977
+ const _customFields = customFields?.[table] ?? [];
18978
+ columns.forEach((col) => {
18979
+ if (_customFields.some((field) => {
18980
+ return field.field === col.field;
18981
+ })) {
18982
+ col.inferFormat = true;
18983
+ }
18984
+ });
18985
+ });
18986
+ }
18987
+ } catch (e) {
18988
+ eventTracking?.logError?.({
18989
+ type: "bug",
18990
+ // TODO: determine type
18991
+ severity: "high",
18992
+ message: "Error fetching table by AST",
18993
+ errorMessage: e.message,
18994
+ errorStack: e.stack,
18995
+ errorData: {
18996
+ caller: "fetchTableByAST",
18997
+ function: "fetchTableByAST"
18998
+ }
18999
+ });
19000
+ rows = [];
19001
+ columns = [];
19002
+ rowCount = 0;
19003
+ error = e.message;
19004
+ }
19005
+ if (client.databaseType && client.databaseType.toLowerCase() === "bigquery") {
19006
+ parseValueFromBigQueryDates(rows, columns);
19007
+ }
19008
+ return { rows, columns, rowCount, error, itemQuery, referencedTables };
19009
+ };
19010
+
19011
+ // src/utils/dashboard.ts
19012
+ var defaultDashboardItem = {
19013
+ id: "",
19014
+ name: "",
19015
+ dashboardName: "",
19016
+ rows: [],
19017
+ compareRows: [],
19018
+ columns: [],
19019
+ chartType: "",
19020
+ pivot: null,
19021
+ yAxisFields: [],
19022
+ xAxisLabel: "",
19023
+ xAxisField: "",
19024
+ xAxisFormat: "string",
19025
+ order: -1,
19026
+ filtersApplied: [],
19027
+ queryString: "",
19028
+ rowCount: 0,
19029
+ columnInternal: []
19030
+ };
19031
+ async function cleanDashboardItem({
19032
+ item,
19033
+ dashboardFilters,
19034
+ getToken,
19035
+ eventTracking,
19036
+ client,
19037
+ dateBucket,
19038
+ additionalProcessing,
19039
+ customFields,
19040
+ skipPivotFetch,
19041
+ tenants
19042
+ }) {
19043
+ if (!item) return defaultDashboardItem;
19044
+ if (!item.rows) {
19045
+ return {
19046
+ ...defaultDashboardItem,
19047
+ id: item._id,
19048
+ name: item.name
19049
+ };
19050
+ }
19051
+ const fields = item.fields || [];
19052
+ const columnsWithCustomFields = [...item.columns ?? []];
19053
+ if (item.includeCustomFields && item.rows?.length > 0) {
19054
+ const tables = item.referencedTables ?? [];
19055
+ tables.forEach((table) => {
19056
+ const _customFields = customFields?.[table] ?? [];
19057
+ _customFields.forEach((field) => {
19058
+ const isJsonCustomField = !!field.refColumn;
19059
+ if (item.rows[0][field.field] !== void 0 && !item.columns.some((col) => col.field === field.field)) {
19060
+ const result_field = fields.find((f) => f.name === field.field);
19061
+ const converted_field = convertPostgresColumn(result_field ?? {});
19062
+ columnsWithCustomFields.push({
19063
+ field: field.field,
19064
+ format: converted_field.format,
19065
+ label: snakeAndCamelCaseToTitleCase(field.field),
19066
+ fieldType: converted_field.fieldType,
19067
+ dataTypeID: converted_field.dataTypeID,
19068
+ jsType: converted_field.jsType,
19069
+ inferFormat: isJsonCustomField
19070
+ });
19071
+ }
19072
+ });
19073
+ });
19074
+ }
19075
+ const processedColumns = item.columns.map((col) => {
19076
+ return { ...col, label: snakeAndCamelCaseToTitleCase(col.label) };
19077
+ });
19078
+ const columnInternal = (item.includeCustomFields ? columnsWithCustomFields : item.columns).map((col) => {
19079
+ const field = item.fields?.find((f) => f.name === col.field);
19080
+ const converted_field = convertPostgresColumn(field ?? {});
19081
+ return {
19082
+ fieldType: converted_field.fieldType,
19083
+ dataTypeID: converted_field.dataTypeID,
19084
+ jsType: converted_field.jsType,
19085
+ ...col,
19086
+ label: snakeAndCamelCaseToTitleCase(col.label)
19087
+ };
19088
+ });
19089
+ let pivotTable;
19090
+ let pivotError;
19091
+ try {
19092
+ const shouldPaginatePivotAsTable = item.chartType === "table";
19093
+ const pivotChartProcessing = {
19094
+ page: DEFAULT_PAGINATION
19095
+ };
19096
+ pivotTable = await getPivotTable(
19097
+ {
19098
+ ...item,
19099
+ pivot: item.pivot && !skipPivotFetch ? {
19100
+ ...item.pivot,
19101
+ aggregations: item.pivot.aggregations ?? [
19102
+ {
19103
+ valueField: item.pivot.valueField,
19104
+ valueFieldType: item.pivot.valueFieldType,
19105
+ valueField2: item.pivot.valueField2,
19106
+ valueField2Type: item.pivot.valueField2Type,
19107
+ aggregationType: item.pivot.aggregationType
19108
+ }
19109
+ ]
19110
+ } : void 0
19111
+ },
19112
+ dashboardFilters,
19113
+ item.dashboardName,
19114
+ getToken,
19115
+ client,
19116
+ eventTracking,
19117
+ dateBucket,
19118
+ shouldPaginatePivotAsTable ? additionalProcessing : pivotChartProcessing,
19119
+ tenants,
19120
+ customFields
19121
+ );
19122
+ } catch (e) {
19123
+ pivotTable = void 0;
19124
+ eventTracking?.logError?.({
19125
+ type: "bug",
19126
+ // TODO: determine type
19127
+ severity: "high",
19128
+ message: "Error fetching pivot table",
19129
+ errorMessage: e.message,
19130
+ errorStack: e.stack,
19131
+ errorData: {
19132
+ caller: "cleanDashboardItem",
19133
+ function: "cleanDashboardItem"
19134
+ }
19135
+ });
19136
+ console.error("Error fetching pivot table", e);
19137
+ pivotError = "Error fetching pivot table";
19138
+ }
19139
+ const referenceLineYValues = [];
19140
+ for (const key in item) {
19141
+ if (key.startsWith("referenceLine_")) {
19142
+ const field = key.slice(14);
19143
+ if (!item[key] || !item[key][0]) continue;
19144
+ const value = Object.values(item[key][0])[0];
19145
+ referenceLineYValues.push({ label: field, query: [value, value] });
19146
+ }
19147
+ }
19148
+ if (item.referenceLines) {
19149
+ for (const referenceLine of item.referenceLines) {
19150
+ if (Array.isArray(referenceLine.query)) {
19151
+ referenceLineYValues.push({
19152
+ label: referenceLine.label,
19153
+ query: referenceLine.query
19154
+ });
19155
+ } else if (referenceLine.query === "") {
19156
+ referenceLineYValues.push({
19157
+ label: referenceLine.label,
19158
+ query: (pivotTable?.rows ? pivotTable.rows : item.rows).map(
19159
+ (row) => Number(row[referenceLine.label]) || 0
19160
+ )
19161
+ });
19162
+ }
19163
+ }
19164
+ }
19165
+ return {
19166
+ id: item._id ?? item.id,
19167
+ name: item.name,
19168
+ dashboardName: item.dashboardName,
19169
+ // section: item.section,
19170
+ rows: item.rows,
19171
+ pivotRows: pivotTable ? pivotTable.rows : void 0,
19172
+ pivotColumns: pivotTable ? pivotTable.columns : void 0,
19173
+ compareRows: item.compareRows,
19174
+ columns: processedColumns.map((column) => {
19175
+ return {
19176
+ field: column.field,
19177
+ format: column.format,
19178
+ label: column.label,
19179
+ inferFormat: column.inferFormat
19180
+ };
19181
+ }),
19182
+ includeCustomFields: item.includeCustomFields,
19183
+ columnInternal,
19184
+ columnsWithCustomFields,
19185
+ chartType: item.chartType,
19186
+ dateField: item.dateField,
19187
+ pivot: pivotError ? void 0 : item.pivot ? {
19188
+ ...item.pivot,
19189
+ aggregations: item.pivot.aggregations ?? [
19190
+ {
19191
+ valueField: item.pivot.valueField,
19192
+ valueFieldType: item.pivot.valueFieldType,
19193
+ valueField2: item.pivot.valueField2,
19194
+ valueField2Type: item.pivot.valueField2Type,
19195
+ aggregationType: item.pivot.aggregationType
19196
+ }
19197
+ ],
19198
+ columnValues: item.distinctStrings
19199
+ } : void 0,
19200
+ yAxisFields: pivotTable ? extractPivotedYAxis(pivotTable, item) : item.yAxisFields,
19201
+ xAxisLabel: item.xAxisLabel,
19202
+ xAxisField: item.xAxisField,
19203
+ xAxisFormat: item.xAxisFormat,
19204
+ order: item.order,
19205
+ filtersApplied: item.filtersApplied,
19206
+ filterMap: item.filterMap,
19207
+ flags: item.flags,
19208
+ rowCount: item.rowCount ? parseInt(item.rowCount) : item.rows.length,
19209
+ pivotRowCount: pivotTable ? pivotTable.rowCount : void 0,
19210
+ template: item.template,
19211
+ sort: item.sort,
19212
+ itemQuery: item.itemQuery,
19213
+ queryString: item.queryString,
19214
+ pivotQuery: pivotTable?.pivotQuery,
19215
+ comparisonPivotQuery: pivotTable?.comparisonPivotQuery,
19216
+ referencedTables: item?.referencedTables || [],
19217
+ referencedColumns: item?.referencedColumns || {},
19218
+ error: item.error ?? pivotError,
19219
+ referenceLineYValues,
19220
+ referenceLines: item.referenceLines
19221
+ };
19222
+ }
19223
+ async function getPivotTable(report, dashboardFilters, dashboardName, getToken, client, eventTracking, dateBucketInitial, additionalProcessing, tenants, customFields) {
19224
+ if (!report) return void 0;
19225
+ const dateFilter = Object.values(dashboardFilters ?? {}).find(
19226
+ (filter) => filter.filterType === "date_range" || filter.operator === "BETWEEN"
19227
+ );
19228
+ if (dateFilter?.operator === "BETWEEN") {
19229
+ dateFilter.startDate = dateFilter.value[0];
19230
+ dateFilter.endDate = dateFilter.value[1];
19231
+ }
19232
+ const pivot = report?.pivot;
19233
+ const data = report || {};
19234
+ if (pivot && client) {
19235
+ if (report.rowCount === 0 || report.rows.length === 0) {
19236
+ const columns = [];
19237
+ if (pivot.rowField) {
19238
+ columns.push({
19239
+ field: pivot.rowField,
19240
+ label: snakeCaseToTitleCase(processColumnName(pivot.rowField)),
19241
+ format: pivot.rowFieldType || "string",
19242
+ jsType: convertFieldTypeToJSType(pivot.rowFieldType || "string"),
19243
+ fieldType: pivot.rowFieldType || "string",
19244
+ dataTypeID: fieldTypeToDataTypeID(pivot.rowFieldType || "string")
19245
+ });
19246
+ }
19247
+ for (const agg of pivot.aggregations ?? []) {
19248
+ if (agg.valueField) {
19249
+ columns.push({
19250
+ field: agg.valueField,
19251
+ label: snakeCaseToTitleCase(processColumnName(agg.valueField)),
19252
+ //FIXME: valueFieldType is not always the same as the format
19253
+ format: agg.valueFieldType ?? "whole_number",
19254
+ jsType: agg.valueFieldType ?? "number",
19255
+ fieldType: agg.valueFieldType ?? "number",
19256
+ dataTypeID: fieldTypeToDataTypeID(agg.valueFieldType ?? "number")
19257
+ });
19258
+ }
19259
+ }
19260
+ const pivotQuery = generatePivotQuery(
19261
+ pivot,
19262
+ report.itemQuery?.[0],
19263
+ client.databaseType
19264
+ );
19265
+ return {
19266
+ rows: [],
19267
+ rowCount: 0,
19268
+ pivotQuery: pivotQuery ?? "",
19269
+ columns
19270
+ };
18251
19271
  }
18252
- const gatheredRows = resp.rows && resp.rows.length ? resp.rows : [];
18253
- columns = resp.fields.map((elem) => convertPostgresColumn(elem));
18254
- rows = gatheredRows;
18255
- itemQuery = resp.itemQuery;
18256
- referencedTables = resp.referencedTables;
18257
- if (customFields) {
18258
- const tables = referencedTables ?? [];
18259
- tables.forEach((table) => {
18260
- const _customFields = customFields?.[table] ?? [];
18261
- columns.forEach((col) => {
18262
- if (_customFields.some((field) => {
18263
- return field.field === col.field;
18264
- })) {
18265
- col.inferFormat = true;
18266
- }
19272
+ try {
19273
+ let dateBucket = dateBucketInitial;
19274
+ let filterDateRange = void 0;
19275
+ if (dateFilter && dateFilter.startDate && dateFilter.endDate) {
19276
+ filterDateRange = {
19277
+ start: dateFilter.startDate,
19278
+ end: dateFilter.endDate
19279
+ };
19280
+ } else if (report.dateRange) {
19281
+ filterDateRange = report.dateRange;
19282
+ }
19283
+ if (!dateBucket && filterDateRange) {
19284
+ dateBucket = getDateBucketFromRange(filterDateRange);
19285
+ }
19286
+ if (pivot.columnField && !report.distinctStrings) {
19287
+ const columnFieldColumn = (report.columnsWithCustomFields ?? report.columns).find((col) => col.field === pivot.columnField);
19288
+ if (!columnFieldColumn) {
19289
+ console.error(
19290
+ "could not find pivot column field on report",
19291
+ pivot.columnField
19292
+ );
19293
+ }
19294
+ const unique = await getUniqueValuesByQuery({
19295
+ columns: [columnFieldColumn],
19296
+ query: report.queryString,
19297
+ client,
19298
+ getToken,
19299
+ dashboardName,
19300
+ tenants,
19301
+ customFields: customFields ?? {},
19302
+ eventTracking
19303
+ });
19304
+ report.distinctStrings = unique?.[pivot.columnField] ?? [];
19305
+ }
19306
+ if (pivot.columnField && !report.distinctStrings) {
19307
+ const columnFieldColumn = (report.columnsWithCustomFields ?? report.columns).find((col) => col.field === pivot.columnField);
19308
+ if (!columnFieldColumn) {
19309
+ console.error(
19310
+ "could not find pivot column field on report",
19311
+ pivot.columnField
19312
+ );
19313
+ }
19314
+ const unique = await getUniqueValuesByQuery({
19315
+ columns: [columnFieldColumn],
19316
+ query: report.queryString,
19317
+ client,
19318
+ getToken,
19319
+ dashboardName,
19320
+ tenants,
19321
+ customFields: customFields ?? {},
19322
+ eventTracking
18267
19323
  });
19324
+ report.distinctStrings = unique?.[pivot.columnField] ?? [];
19325
+ }
19326
+ const pivotTable = await generatePivotWithSQL({
19327
+ pivot,
19328
+ report,
19329
+ client,
19330
+ dateBucket,
19331
+ dateFilter,
19332
+ distinctStrings: report.distinctStrings,
19333
+ dashboardName,
19334
+ tenants,
19335
+ additionalProcessing,
19336
+ getToken
19337
+ });
19338
+ return pivotTable;
19339
+ } catch (e) {
19340
+ eventTracking?.logError?.({
19341
+ type: "bug",
19342
+ // TODO: determine type
19343
+ severity: "high",
19344
+ message: "Error fetching pivot table",
19345
+ errorMessage: e.message,
19346
+ errorStack: e.stack,
19347
+ errorData: {
19348
+ caller: "getPivotTable",
19349
+ function: "getPivotTable"
19350
+ }
18268
19351
  });
19352
+ console.error("Error fetching pivot table", e);
19353
+ throw e;
18269
19354
  }
18270
- } catch (e) {
18271
- eventTracking?.logError?.({
18272
- type: "bug",
18273
- // TODO: determine type
18274
- severity: "high",
18275
- message: "Error fetching table by AST",
18276
- errorMessage: e.message,
18277
- errorStack: e.stack,
18278
- errorData: {
18279
- caller: "fetchTableByAST",
18280
- function: "fetchTableByAST"
18281
- }
18282
- });
18283
- rows = [];
18284
- columns = [];
18285
- rowCount = 0;
18286
- error = e.message;
18287
19355
  }
18288
- if (client.databaseType && client.databaseType.toLowerCase() === "bigquery") {
18289
- parseValueFromBigQueryDates(rows, columns);
19356
+ return pivot && data.rows ? generatePivotTable({
19357
+ pivot,
19358
+ report,
19359
+ client,
19360
+ uniqueValues: report.distinctStrings,
19361
+ dashboardName,
19362
+ tenants,
19363
+ dateFilter,
19364
+ additionalProcessing,
19365
+ getToken,
19366
+ eventTracking
19367
+ }) : void 0;
19368
+ }
19369
+ function extractPivotedYAxis(pivotTable, itemInfo, config = void 0) {
19370
+ if (!pivotTable) return itemInfo?.yAxisFields ?? [];
19371
+ const pivot = itemInfo?.pivot || config?.pivot;
19372
+ if (!pivot.columnField && !pivot.rowField) return itemInfo?.yAxisFields ?? [];
19373
+ const yAxisFields = config ? config.yAxisFields : itemInfo?.yAxisFields;
19374
+ return yAxisFields && yAxisFields.length > 0 ? generatePivotTableYAxis(pivot, pivotTable.columns, yAxisFields[0]) : yAxisFields;
19375
+ }
19376
+ async function getDashboard(dashboardName, client, getToken, tenants, flags) {
19377
+ const { data: resp } = await quillFetch({
19378
+ client,
19379
+ task: "dashboard",
19380
+ metadata: {
19381
+ name: dashboardName,
19382
+ clientId: client.publicKey,
19383
+ databaseType: client.databaseType,
19384
+ useNewNodeSql: true,
19385
+ tenants,
19386
+ flags
19387
+ },
19388
+ getToken
19389
+ });
19390
+ return {
19391
+ ...resp,
19392
+ createdAt: resp.createdAt && new Date(resp.createdAt),
19393
+ dateFilter: resp.dateFilter ? {
19394
+ ...resp.dateFilter,
19395
+ presetOptions: resp.dateFilter.presetOptions?.map(
19396
+ (preset) => ({
19397
+ ...preset,
19398
+ loopStart: preset.loopStart ? new Date(preset.loopStart) : void 0,
19399
+ loopEnd: preset.loopEnd ? new Date(preset.loopEnd) : void 0
19400
+ })
19401
+ ),
19402
+ defaultPresetRanges: resp.dateFilter.defaultPresetRanges?.map(
19403
+ (preset) => ({
19404
+ ...preset,
19405
+ loopStart: preset.loopStart ? new Date(preset.loopStart) : void 0,
19406
+ loopEnd: preset.loopEnd ? new Date(preset.loopEnd) : void 0
19407
+ })
19408
+ )
19409
+ } : void 0
19410
+ };
19411
+ }
19412
+
19413
+ // src/utils/chartBuilder.ts
19414
+ var numberFormatOptions = [
19415
+ "whole_number",
19416
+ "one_decimal_place",
19417
+ "two_decimal_places",
19418
+ "dollar_amount",
19419
+ "dollar_cents",
19420
+ "percentage"
19421
+ ];
19422
+ var dateFormatOptions = [
19423
+ "MMM_yyyy",
19424
+ "MMM_dd",
19425
+ "MMM_dd_yyyy",
19426
+ "MMM_dd_hh:mm_ap_pm",
19427
+ "hh_ap_pm",
19428
+ "date",
19429
+ "timestamptz"
19430
+ ];
19431
+ var NUMBER_OPTIONS = [
19432
+ { value: "whole_number", label: "whole number" },
19433
+ { value: "one_decimal_place", label: "one decimal place" },
19434
+ { value: "two_decimal_places", label: "two decimal places" },
19435
+ { value: "dollar_amount", label: "dollar amount" },
19436
+ { value: "dollar_cents", label: "dollar and cent amount" },
19437
+ { value: "percent", label: "percentage" }
19438
+ ];
19439
+ var DATE_OPTIONS = [
19440
+ { value: "MMM_yyyy", label: "month" },
19441
+ { value: "MMM_dd", label: "day" },
19442
+ { value: "MMM_dd_yyyy", label: "day and year" },
19443
+ { value: "MMM_dd_hh:mm_ap_pm", label: "day and time" },
19444
+ { value: "hh_ap_pm", label: "hour" }
19445
+ ];
19446
+ var ALL_FORMAT_OPTIONS = [
19447
+ ...NUMBER_OPTIONS,
19448
+ ...DATE_OPTIONS,
19449
+ { value: "string", label: "string" }
19450
+ ];
19451
+ function createInitialFormData(columns) {
19452
+ const firstNumberColumn = columns?.find(
19453
+ (col) => numberFormatOptions.includes(col.format)
19454
+ );
19455
+ const firstStringColumn = columns?.find(
19456
+ (col) => !numberFormatOptions.includes(col.format) && !dateFormatOptions.includes(col.format)
19457
+ );
19458
+ const firstDateColumn = columns?.find(
19459
+ (col) => dateFormatOptions.includes(col.format)
19460
+ );
19461
+ const xAxisField = firstStringColumn?.field || firstDateColumn?.field || firstNumberColumn?.field || columns?.[0]?.field || "";
19462
+ const xAxisFormat = firstStringColumn?.format || firstDateColumn?.format || firstNumberColumn?.format || columns?.[0]?.format || "string";
19463
+ const formEmptyState = {
19464
+ name: "",
19465
+ columns: columns.map((col) => {
19466
+ return { ...col, label: snakeAndCamelCaseToTitleCase(col.label) };
19467
+ }),
19468
+ xAxisField,
19469
+ xAxisFormat,
19470
+ yAxisFields: [
19471
+ {
19472
+ field: firstNumberColumn?.field || columns?.[0]?.field || "",
19473
+ label: "",
19474
+ format: firstNumberColumn?.format || columns?.[0]?.format || "string"
19475
+ }
19476
+ ],
19477
+ xAxisLabel: "",
19478
+ chartType: firstNumberColumn ? "line" : "table",
19479
+ pivot: null,
19480
+ template: true,
19481
+ referenceLines: []
19482
+ };
19483
+ return formEmptyState;
19484
+ }
19485
+
19486
+ // src/utils/error.ts
19487
+ var DataLoadError = class extends Error {
19488
+ data;
19489
+ constructor(message, data) {
19490
+ super(message);
19491
+ this.name = "DataLoadError";
19492
+ this.data = data;
18290
19493
  }
18291
- return { rows, columns, rowCount, error, itemQuery, referencedTables };
18292
19494
  };
18293
19495
 
18294
19496
  // src/components/ReportBuilder/convert.ts
@@ -21942,35 +23144,6 @@ import jsPDF from "jspdf";
21942
23144
 
21943
23145
  // src/hooks/useDashboard.ts
21944
23146
  import { useContext, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
21945
-
21946
- // src/utils/merge.ts
21947
- import {
21948
- add,
21949
- startOfDay as startOfDay3,
21950
- startOfMonth,
21951
- startOfWeek as startOfWeek3,
21952
- startOfYear
21953
- } from "date-fns";
21954
- import { utcToZonedTime as utcToZonedTime2 } from "date-fns-tz";
21955
- function mergeComparisonRange(resp) {
21956
- if (resp.chartType === "table") return resp;
21957
- const compRows = resp.compareRows;
21958
- if (!compRows) return resp;
21959
- const newRows = resp.rows.map((row, i) => {
21960
- if (i < compRows.length) {
21961
- const compRow = compRows[i];
21962
- const newRow = { ...row };
21963
- for (const [key, value] of Object.entries(compRow)) {
21964
- newRow[`comparison_${key}`] = value;
21965
- }
21966
- return newRow;
21967
- }
21968
- return row;
21969
- });
21970
- return { ...resp, rows: newRows };
21971
- }
21972
-
21973
- // src/hooks/useDashboard.ts
21974
23147
  var useDashboardInternal = (dashboardName, customFilters) => {
21975
23148
  const [dashboard] = useContext(DashboardContext);
21976
23149
  const {
@@ -30549,23 +31722,53 @@ function QuillMetricComponent({
30549
31722
  width: "100%"
30550
31723
  }
30551
31724
  }
30552
- ) : !report?.rows?.[0] || report.rows[0][report.xAxisField] === null ? /* @__PURE__ */ jsx43(
31725
+ ) : !report?.rows || report?.rows?.length === 0 || report.rows[0]?.[report.xAxisField] === null || report.rows[0]?.[report.xAxisField] === void 0 ? /* @__PURE__ */ jsx43(
30553
31726
  "div",
30554
31727
  {
30555
31728
  style: {
30556
- display: "flex",
30557
- flex: "1 0 auto",
30558
- // height: '100%',
30559
- margin: "auto",
30560
- justifyContent: "center",
30561
- alignItems: "center",
30562
- fontSize: 13,
30563
- fontFamily: theme?.fontFamily,
30564
- color: theme?.secondaryTextColor,
30565
- maxWidth: "100%",
30566
- width: "100%"
31729
+ padding: 0,
31730
+ height: "100%",
31731
+ width: "100%",
31732
+ boxSizing: "content-box",
31733
+ flex: 1
30567
31734
  },
30568
- children: "No results"
31735
+ children: /* @__PURE__ */ jsx43(
31736
+ "div",
31737
+ {
31738
+ style: {
31739
+ fontFamily: theme?.fontFamily,
31740
+ fontSize: 32,
31741
+ color: theme?.primaryTextColor,
31742
+ fontWeight: "600",
31743
+ textOverflow: "ellipsis",
31744
+ margin: 0,
31745
+ whiteSpace: "nowrap",
31746
+ boxSizing: "content-box",
31747
+ maxWidth: "100%",
31748
+ textAlign: "left",
31749
+ overflow: "hidden",
31750
+ height: "100%",
31751
+ minHeight: "80px",
31752
+ display: "flex",
31753
+ width: "100%",
31754
+ flexDirection: "row",
31755
+ justifyContent: "center",
31756
+ alignItems: "center"
31757
+ },
31758
+ children: /* @__PURE__ */ jsx43(
31759
+ "span",
31760
+ {
31761
+ style: {
31762
+ fontFamily: theme?.fontFamily,
31763
+ fontSize: 13,
31764
+ color: theme?.secondaryTextColor,
31765
+ fontWeight: "normal"
31766
+ },
31767
+ children: "No results"
31768
+ }
31769
+ )
31770
+ }
31771
+ )
30569
31772
  }
30570
31773
  ) : /* @__PURE__ */ jsx43(
30571
31774
  MetricDisplay,
@@ -31756,7 +32959,7 @@ function USMap({
31756
32959
  acc[curr[xAxisField]?.toString()] = curr;
31757
32960
  return acc;
31758
32961
  }, {});
31759
- const measureField = yAxisFields[0].field;
32962
+ const measureField = yAxisFields[0]?.field;
31760
32963
  const [scaleLog, setScaleLog] = useState14(null);
31761
32964
  useEffect12(() => {
31762
32965
  import("d3-scale").then((scale) => {
@@ -31792,6 +32995,29 @@ function USMap({
31792
32995
  const hoveredValue = useMemo12(() => {
31793
32996
  return !hoveredState ? void 0 : mappedData[fipsToNames[hoveredState]?.abbreviation ?? ""]?.[measureField] ?? mappedData[fipsToNames[hoveredState]?.name ?? ""]?.[measureField] ?? mappedData[fipsToNames[hoveredState]?.abbreviation?.toLowerCase() ?? ""]?.[measureField] ?? mappedData[fipsToNames[hoveredState]?.name?.toLowerCase() ?? ""]?.[measureField];
31794
32997
  }, [hoveredState, mappedData, measureField]);
32998
+ if (!measureField) {
32999
+ return /* @__PURE__ */ jsx45(
33000
+ "div",
33001
+ {
33002
+ style: {
33003
+ display: "flex",
33004
+ flex: "1 0 auto",
33005
+ marginLeft: "auto",
33006
+ marginRight: "auto",
33007
+ marginTop: "auto",
33008
+ marginBottom: "auto",
33009
+ justifyContent: "center",
33010
+ alignItems: "center",
33011
+ fontSize: 13,
33012
+ color: theme?.secondaryTextColor,
33013
+ fontFamily: theme?.fontFamily,
33014
+ ...containerStyle
33015
+ },
33016
+ className,
33017
+ children: "No results"
33018
+ }
33019
+ );
33020
+ }
31795
33021
  return /* @__PURE__ */ jsxs34(
31796
33022
  "div",
31797
33023
  {
@@ -31924,7 +33150,7 @@ function WorldMap({
31924
33150
  acc[curr[xAxisField]?.toString()] = curr;
31925
33151
  return acc;
31926
33152
  }, {});
31927
- const measureField = yAxisFields[0].field;
33153
+ const measureField = yAxisFields[0]?.field;
31928
33154
  const [scaleLog, setScaleLog] = useState14(null);
31929
33155
  useEffect12(() => {
31930
33156
  import("d3-scale").then((scale) => {
@@ -31960,6 +33186,29 @@ function WorldMap({
31960
33186
  const hoveredValue = useMemo12(() => {
31961
33187
  return !hoveredCountry ? void 0 : mappedData[isoToNames[hoveredCountry]?.abbreviation ?? ""]?.[measureField] ?? mappedData[isoToNames[hoveredCountry]?.name ?? ""]?.[measureField] ?? mappedData[isoToNames[hoveredCountry]?.abbreviation?.toLowerCase() ?? ""]?.[measureField] ?? mappedData[isoToNames[hoveredCountry]?.name?.toLowerCase() ?? ""]?.[measureField];
31962
33188
  }, [hoveredCountry, mappedData, measureField]);
33189
+ if (!measureField) {
33190
+ return /* @__PURE__ */ jsx45(
33191
+ "div",
33192
+ {
33193
+ style: {
33194
+ display: "flex",
33195
+ flex: "1 0 auto",
33196
+ marginLeft: "auto",
33197
+ marginRight: "auto",
33198
+ marginTop: "auto",
33199
+ marginBottom: "auto",
33200
+ justifyContent: "center",
33201
+ alignItems: "center",
33202
+ fontSize: 13,
33203
+ color: theme?.secondaryTextColor,
33204
+ fontFamily: theme?.fontFamily,
33205
+ ...containerStyle
33206
+ },
33207
+ className,
33208
+ children: "No results"
33209
+ }
33210
+ );
33211
+ }
31963
33212
  return /* @__PURE__ */ jsxs34(
31964
33213
  "div",
31965
33214
  {
@@ -34467,27 +35716,41 @@ var ChartDisplay = ({
34467
35716
  );
34468
35717
  }
34469
35718
  if (config?.chartType?.toLowerCase() === "metric") {
34470
- if (!config?.rows || config?.rows?.length === 0 || // @ts-ignore
34471
- config?.rows[0][config?.xAxisField] === null) {
35719
+ if (!config?.rows || config?.rows?.length === 0 || config?.rows[0]?.[config?.xAxisField] === null || config?.rows[0]?.[config?.xAxisField] === void 0) {
34472
35720
  return /* @__PURE__ */ jsx48(
34473
35721
  "div",
34474
35722
  {
34475
35723
  style: {
34476
- display: "flex",
34477
- flex: "1 0 auto",
34478
- // height: containerStyle?.height || '100%',
34479
- margin: "auto",
34480
- justifyContent: "center",
34481
- alignItems: "center",
34482
- fontSize: 13,
34483
35724
  fontFamily: theme?.fontFamily,
34484
- color: theme?.secondaryTextColor,
35725
+ fontSize: 32,
35726
+ color: theme?.primaryTextColor,
35727
+ fontWeight: "600",
35728
+ textOverflow: "ellipsis",
35729
+ margin: 0,
35730
+ whiteSpace: "nowrap",
35731
+ boxSizing: "content-box",
34485
35732
  maxWidth: "100%",
35733
+ textAlign: "left",
35734
+ overflow: "hidden",
35735
+ height: containerStyle?.height || "100%",
35736
+ display: "flex",
34486
35737
  width: "100%",
35738
+ flexDirection: "row",
35739
+ alignItems: "center",
34487
35740
  ...containerStyle
34488
35741
  },
34489
35742
  className,
34490
- children: "No results"
35743
+ children: /* @__PURE__ */ jsx48(
35744
+ "span",
35745
+ {
35746
+ style: {
35747
+ fontFamily: theme?.fontFamily,
35748
+ fontSize: 13,
35749
+ color: theme?.secondaryTextColor
35750
+ },
35751
+ children: "No results"
35752
+ }
35753
+ )
34491
35754
  }
34492
35755
  );
34493
35756
  }
@@ -41440,15 +42703,19 @@ function ChartBuilderWithModal(props) {
41440
42703
  title: title || "Add to dashboard",
41441
42704
  width: isHorizontalView ? modalWidth : void 0,
41442
42705
  height: isHorizontalView ? modalHeight : void 0,
41443
- children: (props.reportId ? dashboard[props.reportId] ?? props.tempReport : props.tempReport) ? /* @__PURE__ */ jsx65(
41444
- ChartBuilder,
41445
- {
41446
- ...props,
41447
- filtersEnabled: filtersEnabledState,
41448
- onFiltersEnabledChanged: setFiltersEnabledState,
41449
- runQueryOnMount: filtersEnabledState
41450
- }
41451
- ) : /* @__PURE__ */ jsx65("div", { style: { padding: 20 }, children: /* @__PURE__ */ jsx65(QuillLoadingComponent, {}) })
42706
+ children: (() => {
42707
+ const resolvedReport = props.reportId ? dashboard[props.reportId] ?? props.tempReport : props.tempReport;
42708
+ return resolvedReport ? /* @__PURE__ */ jsx65(
42709
+ ChartBuilder,
42710
+ {
42711
+ ...props,
42712
+ tempReport: resolvedReport,
42713
+ filtersEnabled: filtersEnabledState,
42714
+ onFiltersEnabledChanged: setFiltersEnabledState,
42715
+ runQueryOnMount: filtersEnabledState
42716
+ }
42717
+ ) : /* @__PURE__ */ jsx65("div", { style: { padding: 20 }, children: /* @__PURE__ */ jsx65(QuillLoadingComponent, {}) });
42718
+ })()
41452
42719
  }
41453
42720
  ) });
41454
42721
  }
@@ -41498,6 +42765,7 @@ function ChartBuilder({
41498
42765
  hideSubmitButton = false,
41499
42766
  hideDateRangeFilter = false,
41500
42767
  showTableFormatOptions,
42768
+ showDashboardFilterFields,
41501
42769
  initialUniqueValues,
41502
42770
  initialUniqueValuesIsLoading,
41503
42771
  pivotRecommendationsEnabled = true,
@@ -41527,9 +42795,10 @@ function ChartBuilder({
41527
42795
  const { tenants, flags } = useContext26(TenantContext);
41528
42796
  const report = useMemo21(() => {
41529
42797
  return reportId && !tempReport ? allReportsById[reportId] : tempReport;
41530
- }, [reportId]);
42798
+ }, [reportId, tempReport, allReportsById]);
41531
42799
  const [windowWidth, setWindowWidth] = useState28(1200);
41532
42800
  const [rows, setRows] = useState28(report?.rows ?? []);
42801
+ const [itemQuery, setItemQuery] = useState28(report?.itemQuery);
41533
42802
  const [rowCount, setRowCount] = useState28(report?.rowCount ?? 0);
41534
42803
  const [maxPage, setMaxPage] = useState28(0);
41535
42804
  const [isLoading, setIsLoading] = useState28(false);
@@ -42259,7 +43528,7 @@ function ChartBuilder({
42259
43528
  pivot,
42260
43529
  dateBucket,
42261
43530
  dateFilter,
42262
- report: report ? { ...report, ...tableInfo } : void 0,
43531
+ report: report ? { ...report, ...tableInfo ?? { itemQuery } } : void 0,
42263
43532
  client,
42264
43533
  uniqueValues,
42265
43534
  dashboardName: destinationDashboardName,
@@ -42420,6 +43689,7 @@ function ChartBuilder({
42420
43689
  setCurrentProcessing(processing);
42421
43690
  setRows(tableInfo.rows);
42422
43691
  setProcessedColumns(processColumns(tableInfo.columns));
43692
+ setItemQuery(tableInfo.itemQuery);
42423
43693
  fetchRowCount(processing, overrideFilters);
42424
43694
  if (formData.pivot) {
42425
43695
  try {
@@ -43325,7 +44595,7 @@ function ChartBuilder({
43325
44595
  uniqueValuesIsLoading: initialUniqueValuesIsLoading,
43326
44596
  initialSelectedPivotTable: selectedPivotTable,
43327
44597
  pivotRecommendationsEnabled,
43328
- report,
44598
+ report: report ? { ...report, ...{ itemQuery } } : void 0,
43329
44599
  dashboardName: destinationDashboardName || "",
43330
44600
  dateFilter: filtersEnabled ? currentDashboardFilters?.find(
43331
44601
  (f) => f.filterType === "date_range"
@@ -44018,7 +45288,7 @@ function ChartBuilder({
44018
45288
  ]
44019
45289
  }
44020
45290
  ),
44021
- specificDashboardFilters.length > 0 && isAdmin && /* @__PURE__ */ jsxs47(
45291
+ specificDashboardFilters.length > 0 && (showDashboardFilterFields || isAdmin) && /* @__PURE__ */ jsxs47(
44022
45292
  "div",
44023
45293
  {
44024
45294
  style: {
@@ -44029,7 +45299,7 @@ function ChartBuilder({
44029
45299
  },
44030
45300
  children: [
44031
45301
  /* @__PURE__ */ jsx65(HeaderComponent, { label: "Dashboard filter fields" }),
44032
- isAdmin && formData.dateField && dashboardConfig[formData.dashboardName ?? destinationDashboardName ?? ""]?.config.dateFilter?.label && /* @__PURE__ */ jsxs47(ChartBuilderInputRowContainer, { children: [
45302
+ (showDashboardFilterFields || isAdmin) && formData.dateField && dashboardConfig[formData.dashboardName ?? destinationDashboardName ?? ""]?.config.dateFilter?.label && /* @__PURE__ */ jsxs47(ChartBuilderInputRowContainer, { children: [
44033
45303
  /* @__PURE__ */ jsx65(
44034
45304
  TextInputComponent,
44035
45305
  {
@@ -44968,6 +46238,7 @@ function SQLEditor({
44968
46238
  CheckboxComponent = QuillChartBuilderCheckboxComponent,
44969
46239
  defaultQuery,
44970
46240
  destinationDashboard,
46241
+ destinationSection,
44971
46242
  onChangeQuery,
44972
46243
  onChangeData,
44973
46244
  onChangeColumns,
@@ -44977,12 +46248,16 @@ function SQLEditor({
44977
46248
  onCloseChartBuilder,
44978
46249
  isChartBuilderEnabled = false,
44979
46250
  isAdminEnabled = false,
46251
+ chartBuilderOptions,
44980
46252
  chartBuilderTitle,
44981
46253
  runQueryOnMount = false,
44982
46254
  onAddToDashboardComplete,
46255
+ onSubmitCreateReport,
46256
+ onSubmitEditReport,
44983
46257
  onSaveQueryComplete,
44984
46258
  addToDashboardButtonLabel = "Add to dashboard",
44985
46259
  report = void 0,
46260
+ reportId = void 0,
44986
46261
  organizationName = void 0,
44987
46262
  isChartBuilderHorizontalView = true,
44988
46263
  containerStyle = { height: "100vh" },
@@ -44993,7 +46268,7 @@ function SQLEditor({
44993
46268
  const [sqlPrompt, setSqlPrompt] = useState30("");
44994
46269
  const [client] = useContext28(ClientContext);
44995
46270
  const [theme] = useContext28(ThemeContext);
44996
- const { tenants } = useContext28(TenantContext);
46271
+ const { tenants, flags } = useContext28(TenantContext);
44997
46272
  const { dashboards } = useDashboards();
44998
46273
  const {
44999
46274
  data,
@@ -45002,6 +46277,7 @@ function SQLEditor({
45002
46277
  } = useDashboardInternal(destinationDashboard);
45003
46278
  const { getToken, quillFetchWithToken } = useContext28(FetchContext);
45004
46279
  const { eventTracking } = useContext28(EventTrackingContext);
46280
+ const { allReportsById } = useAllReports();
45005
46281
  const destinationDashboardConfig = useMemo22(() => {
45006
46282
  return dashboards?.find((d) => d.name === destinationDashboard);
45007
46283
  }, [dashboards, destinationDashboard]);
@@ -45069,6 +46345,46 @@ function SQLEditor({
45069
46345
  reload();
45070
46346
  }
45071
46347
  }, [data, dashboardIsLoading]);
46348
+ useEffect24(() => {
46349
+ const loadReport = async () => {
46350
+ let reportToLoad;
46351
+ if (!client) {
46352
+ return;
46353
+ }
46354
+ try {
46355
+ if (!reportId) {
46356
+ throw new Error("Report ID is required");
46357
+ }
46358
+ reportToLoad = allReportsById[reportId];
46359
+ if (!reportToLoad) {
46360
+ throw new Error("Report not found");
46361
+ }
46362
+ setQuery(reportToLoad.queryString || "");
46363
+ setTempReport(reportToLoad);
46364
+ if (reportToLoad.rows && reportToLoad.rows.length > 0) {
46365
+ setRows(reportToLoad.rows);
46366
+ setDisplayTable(true);
46367
+ }
46368
+ } catch (err) {
46369
+ console.error(err);
46370
+ eventTracking?.logError?.({
46371
+ type: "bug",
46372
+ severity: "high",
46373
+ message: "Error loading report",
46374
+ errorMessage: err.message,
46375
+ errorStack: err.stack,
46376
+ errorData: {
46377
+ caller: "SQLEditor",
46378
+ function: "loadReport"
46379
+ }
46380
+ });
46381
+ setErrorMessage("Error when loading report");
46382
+ }
46383
+ };
46384
+ if (reportId && client) {
46385
+ loadReport();
46386
+ }
46387
+ }, [allReportsById[reportId || ""], client]);
45072
46388
  const dynamicHeight = tableRef.current ? tableRef.current.clientHeight : cachedHeight;
45073
46389
  const rowsPerPage = Math.max(
45074
46390
  DEFAULT_ROWS_PER_PAGE,
@@ -45887,13 +47203,38 @@ function SQLEditor({
45887
47203
  isHorizontalView: isChartBuilderHorizontalView,
45888
47204
  isOpen: isChartBuilderOpen,
45889
47205
  setIsOpen: setIsChartBuilderOpen,
45890
- onAddToDashboardComplete,
47206
+ onAddToDashboardComplete: reportId || report?.id ? (data2) => {
47207
+ if (onSubmitEditReport) {
47208
+ onSubmitEditReport(data2);
47209
+ } else if (onAddToDashboardComplete) {
47210
+ onAddToDashboardComplete(data2);
47211
+ }
47212
+ if (!isAdminEnabled && destinationDashboard) {
47213
+ reload(destinationDashboard, false, {
47214
+ report: data2,
47215
+ action: "upsert"
47216
+ });
47217
+ }
47218
+ } : (data2) => {
47219
+ if (onSubmitCreateReport) {
47220
+ onSubmitCreateReport(data2);
47221
+ } else if (onAddToDashboardComplete) {
47222
+ onAddToDashboardComplete(data2);
47223
+ }
47224
+ if (!isAdminEnabled && destinationDashboard) {
47225
+ reload(destinationDashboard, false, {
47226
+ report: data2,
47227
+ action: "upsert"
47228
+ });
47229
+ }
47230
+ },
45891
47231
  destinationDashboard,
47232
+ destinationSection,
45892
47233
  isAdmin: isAdminEnabled,
45893
47234
  title: chartBuilderTitle,
45894
47235
  buttonLabel: addToDashboardButtonLabel,
45895
47236
  tempReport,
45896
- reportId: report?.id,
47237
+ reportId: reportId || report?.id,
45897
47238
  organizationName,
45898
47239
  CardComponent,
45899
47240
  TableComponent,
@@ -45918,6 +47259,8 @@ function SQLEditor({
45918
47259
  CheckboxComponent,
45919
47260
  hideDateRangeFilter: true,
45920
47261
  hideDeleteButton: true,
47262
+ showTableFormatOptions: chartBuilderOptions?.showTableFormatOptions,
47263
+ showDashboardFilterFields: chartBuilderOptions?.showDashboardFilterFields,
45921
47264
  onClickChartElement,
45922
47265
  isEditingMode: true
45923
47266
  }
@@ -45937,7 +47280,7 @@ function SQLEditor({
45937
47280
  isAdmin: false,
45938
47281
  title: "Save query",
45939
47282
  buttonLabel: "Save query",
45940
- tempReport,
47283
+ tempReport: { ...tempReport, dashboardName: SAVED_QUERIES_DASHBOARD },
45941
47284
  reportId: report?.id,
45942
47285
  organizationName,
45943
47286
  CardComponent,
@@ -47705,7 +49048,6 @@ var useReportBuilderInternal = ({
47705
49048
  }
47706
49049
  report = allReportsById[reportId];
47707
49050
  if (!report) {
47708
- console.log("no report");
47709
49051
  throw new Error("Report not found");
47710
49052
  }
47711
49053
  const { ast: newAst, pivot: newPivot } = await fetchASTFromQuillReport(
@@ -50255,6 +51597,8 @@ var SaveReport = ({
50255
51597
  onSubmitEditReport = () => void 0,
50256
51598
  onSubmitCreateReport = () => void 0,
50257
51599
  destinationSection,
51600
+ showTableFormatOptions,
51601
+ showDashboardFilterFields,
50258
51602
  SelectComponent = QuillSelectComponent,
50259
51603
  TextInputComponent = QuillTextInput,
50260
51604
  ButtonComponent = MemoizedButton,
@@ -50287,6 +51631,7 @@ var SaveReport = ({
50287
51631
  ),
50288
51632
  submitButtonLabel
50289
51633
  }) => {
51634
+ const { reload } = useDashboardInternal(reportBuilder.destinationDashboard ?? null);
50290
51635
  return /* @__PURE__ */ jsxs59("div", { children: [
50291
51636
  SaveTrigger,
50292
51637
  /* @__PURE__ */ jsx79(
@@ -50299,7 +51644,17 @@ var SaveReport = ({
50299
51644
  isHorizontalView: true,
50300
51645
  isOpen,
50301
51646
  setIsOpen,
50302
- onAddToDashboardComplete: reportBuilder.reportId ? onSubmitEditReport : onSubmitCreateReport,
51647
+ onAddToDashboardComplete: reportBuilder.reportId ? (data) => {
51648
+ onSubmitEditReport(data);
51649
+ if (!isAdminEnabled && reportBuilder.destinationDashboard) {
51650
+ reload(reportBuilder.destinationDashboard, false, { report: data, action: "upsert" });
51651
+ }
51652
+ } : (data) => {
51653
+ onSubmitCreateReport(data);
51654
+ if (!isAdminEnabled && reportBuilder.destinationDashboard) {
51655
+ reload(reportBuilder.destinationDashboard, false, { report: data, action: "upsert" });
51656
+ }
51657
+ },
50303
51658
  destinationDashboard: reportBuilder.destinationDashboard,
50304
51659
  destinationSection,
50305
51660
  initialUniqueValues: reportBuilder.columnUniqueValues,
@@ -50328,6 +51683,8 @@ var SaveReport = ({
50328
51683
  FormContainer: ChartBuilderFormContainer,
50329
51684
  hideDateRangeFilter: true,
50330
51685
  hideDeleteButton: true,
51686
+ showTableFormatOptions,
51687
+ showDashboardFilterFields,
50331
51688
  buttonLabel: submitButtonLabel ?? (!!reportBuilder.reportId ? "Save changes" : "Add to dashboard"),
50332
51689
  onClickChartElement,
50333
51690
  isEditingMode: true
@@ -50423,6 +51780,7 @@ function ReportBuilder({
50423
51780
  containerStyle,
50424
51781
  className,
50425
51782
  pivotRecommendationsEnabled = true,
51783
+ chartBuilderOptions,
50426
51784
  reportId,
50427
51785
  hideCopySQL = true,
50428
51786
  isChartBuilderHorizontalView = true,
@@ -50862,6 +52220,8 @@ function ReportBuilder({
50862
52220
  ErrorMessageComponent,
50863
52221
  PivotRowContainer,
50864
52222
  PivotColumnContainer,
52223
+ showTableFormatOptions: chartBuilderOptions?.showTableFormatOptions,
52224
+ showDashboardFilterFields: chartBuilderOptions?.showDashboardFilterFields,
50865
52225
  onClickChartElement,
50866
52226
  submitButtonLabel
50867
52227
  }
@@ -50882,7 +52242,7 @@ function ReportBuilder({
50882
52242
  isAdmin: false,
50883
52243
  title: "Save query",
50884
52244
  buttonLabel: "Save query",
50885
- tempReport,
52245
+ tempReport: { ...tempReport, dashboardName: SAVED_QUERIES_DASHBOARD },
50886
52246
  reportId,
50887
52247
  organizationName,
50888
52248
  CardComponent,