@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.cjs CHANGED
@@ -14781,6 +14781,21 @@ function isDateFormat(xAxisFormat) {
14781
14781
  const isDate = DATE_FORMATS.includes(xAxisFormat);
14782
14782
  return isDate;
14783
14783
  }
14784
+ function getComparisonInterval(comparisonRange, dateBucket) {
14785
+ const dayCount = (0, import_date_fns5.differenceInDays)(
14786
+ comparisonRange.endDate,
14787
+ comparisonRange.startDate
14788
+ );
14789
+ if (!isNaN(dayCount)) {
14790
+ if (dateBucket === "month") {
14791
+ return Math.floor(dayCount / 30) + " month";
14792
+ } else if (dateBucket === "year") {
14793
+ return Math.floor(dayCount / 365) + " year";
14794
+ } else {
14795
+ return dayCount + " day";
14796
+ }
14797
+ }
14798
+ }
14784
14799
  function getDateBucketFromRange(dateRange) {
14785
14800
  const difference = (0, import_date_fns5.differenceInDays)(dateRange.end, dateRange.start);
14786
14801
  if (difference < 14) {
@@ -16594,759 +16609,299 @@ function parseValueFromBigQueryDates(rows, columns) {
16594
16609
  });
16595
16610
  }
16596
16611
 
16597
- // src/utils/pivotConstructor.ts
16598
- async function generatePivotWithSQL({
16599
- pivot,
16600
- report,
16601
- client,
16602
- dateBucket,
16603
- dateFilter,
16604
- distinctStrings,
16605
- dashboardName,
16606
- tenants,
16607
- additionalProcessing,
16608
- pivotQuery,
16609
- comparisonPivotQuery,
16610
- getPivotRowCount = true,
16611
- caller,
16612
- getToken
16613
- }) {
16614
- const databaseType = client.databaseType || "postgresql";
16615
- if (!pivot.aggregations?.length && pivot.aggregationType) {
16616
- pivot.aggregations = [
16617
- {
16618
- aggregationType: pivot.aggregationType,
16619
- valueField: pivot.valueField,
16620
- valueFieldType: pivot.valueFieldType,
16621
- valueField2: pivot.valueField2,
16622
- valueField2Type: pivot.valueField2Type
16612
+ // src/utils/merge.ts
16613
+ var import_date_fns7 = require("date-fns");
16614
+ var import_date_fns_tz2 = require("date-fns-tz");
16615
+ function mergeComparisonRange(resp) {
16616
+ if (resp.chartType === "table") return resp;
16617
+ const compRows = resp.compareRows;
16618
+ if (!compRows) return resp;
16619
+ const newRows = resp.rows.map((row, i) => {
16620
+ if (i < compRows.length) {
16621
+ const compRow = compRows[i];
16622
+ const newRow = { ...row };
16623
+ for (const [key, value] of Object.entries(compRow)) {
16624
+ newRow[`comparison_${key}`] = value;
16623
16625
  }
16624
- ];
16625
- }
16626
- const pivotConfig = {
16627
- rowField: pivot.rowField,
16628
- rowFieldType: pivot.rowFieldType,
16629
- columnField: pivot.columnField,
16630
- aggregations: pivot.aggregations?.map((agg) => ({
16631
- aggregationType: agg.aggregationType,
16632
- valueField: agg.valueField,
16633
- valueFieldType: agg.valueFieldType
16634
- }))
16635
- };
16636
- const resp = await quillFetch({
16637
- client,
16638
- task: "pivot-template",
16639
- metadata: {
16640
- clientId: client.clientId,
16641
- pivot: pivotConfig,
16642
- itemQuery: report?.queryString,
16643
- dashboardName,
16644
- tenants
16645
- },
16646
- credentials: "same-origin",
16647
- getToken
16626
+ return newRow;
16627
+ }
16628
+ return row;
16648
16629
  });
16649
- if (resp.data?.success === false) {
16650
- throw resp.data.errorMessage;
16630
+ return { ...resp, rows: newRows };
16631
+ }
16632
+ function dateAdder(date, interval2) {
16633
+ const match = interval2.match(/^(\d+)\s+(day|month|year)$/);
16634
+ if (!match) {
16635
+ throw new Error(`Invalid interval format: ${interval2}`);
16651
16636
  }
16652
- const queryResponseRows = resp.data?.rows || resp.queries?.queryResults?.[0]?.rows || [];
16653
- const queryResponseFields = resp.data?.fields || resp.queries?.queryResults?.[0]?.fields || [];
16654
- const rowCount = resp.queries?.queryResults?.[1]?.rows?.[0]?.row_count || 0;
16655
- parseValueFromBigQueryDates(queryResponseRows, queryResponseFields);
16656
- const responseRows = queryResponseRows;
16657
- const responseFields = queryResponseFields;
16658
- const rows = pivot.rowField ? responseRows.map(
16659
- (row) => !row[pivot.rowField] ? { ...row, [pivot.rowField]: "-" } : row
16660
- ) : responseRows;
16661
- if (pivot.columnField && client.databaseType?.toLowerCase() === "bigquery") {
16662
- rows.forEach((row) => {
16663
- Object.keys(row).forEach((key) => {
16664
- const processedKey = processColumnName(key);
16665
- if (processedKey !== key) {
16666
- row[processedKey] = row[key];
16667
- delete row[key];
16668
- }
16669
- });
16670
- });
16637
+ const [, valueStr, unit] = match;
16638
+ if (!valueStr || !unit) {
16639
+ throw new Error("Could not parse interval");
16671
16640
  }
16672
- const columns = responseFields?.map((field) => ({
16673
- field: processColumnName(field.field || field.name),
16674
- label: snakeCaseToTitleCase(
16675
- processColumnName(
16676
- (field.field || field.name).replace("comparison_", "comparison ")
16677
- )
16678
- ),
16679
- format: (field.field || field.name) === pivot.rowField ? "string" : pivot.aggregations?.find(
16680
- (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")
16681
- )?.aggregationType === "percentage" ? "percent" : "whole_number",
16682
- fieldType: field.fieldType,
16683
- jsType: field.jsType,
16684
- dataTypeID: field.dataTypeID
16685
- })).filter(
16686
- (field, index) => field.field !== "comparison_" + pivot.rowField || index === 0
16687
- ).sort((a, b) => {
16688
- if (a.field === pivot.rowField) {
16689
- return -1;
16690
- }
16691
- if (b.field === pivot.rowField) {
16692
- return 1;
16693
- }
16694
- return 0;
16695
- });
16696
- if (pivot.rowField && !isStringType(pivot.rowFieldType || "")) {
16697
- rows.forEach((row) => {
16698
- row.__quillRawDate = typeof row[pivot.rowField || ""] === "object" ? row[pivot.rowField || ""].value : row[pivot.rowField || ""];
16699
- let value = typeof row[pivot.rowField || ""] === "object" ? row[pivot.rowField || ""].value : row[pivot.rowField || ""];
16700
- if (dateBucket === "week" && dateFilter?.startDate && dateFilter?.endDate) {
16701
- const rowDate = new Date(value);
16702
- if (rowDate < dateFilter.startDate) {
16703
- value = dateFilter.startDate.toISOString();
16704
- } else if (rowDate > dateFilter.endDate) {
16705
- value = dateFilter.endDate.toISOString();
16706
- }
16707
- }
16708
- const dateString = getDateString(
16709
- value,
16710
- dateFilter?.startDate && dateFilter?.endDate ? { start: dateFilter.startDate, end: dateFilter.endDate } : void 0,
16711
- dateBucket,
16712
- databaseType
16713
- );
16714
- row[pivot.rowField || ""] = dateString;
16715
- });
16716
- if (pivot.rowField && pivot.rowFieldType && !isStringType(pivot.rowFieldType) && dateFilter?.startDate && dateFilter?.endDate) {
16717
- const dateSet = new Set(
16718
- rows.map((row) => row[pivot.rowField || ""])
16719
- );
16720
- for (let date = dateFilter.startDate; date <= dateFilter.endDate; date = new Date(date.getTime() + 24 * 60 * 60 * 1e3)) {
16721
- const formattedDate = getDateString(
16722
- date.toISOString(),
16723
- { start: dateFilter.startDate, end: dateFilter.endDate },
16724
- dateBucket,
16725
- databaseType
16726
- );
16727
- if (!dateSet.has(formattedDate)) {
16728
- const newRow = {};
16729
- newRow[pivot.rowField] = formattedDate;
16730
- newRow.__quillRawDate = date.toISOString();
16731
- rows.push(newRow);
16732
- dateSet.add(formattedDate);
16733
- }
16734
- }
16735
- }
16736
- if (!pivot.sort) {
16737
- rows.sort((a, b) => {
16738
- if (a.__quillRawDate < b.__quillRawDate) {
16739
- return -1;
16740
- }
16741
- if (a.__quillRawDate > b.__quillRawDate) {
16742
- return 1;
16743
- }
16744
- return 0;
16745
- });
16746
- }
16641
+ const value = parseInt(valueStr, 10);
16642
+ const parsedDate = typeof date === "string" ? new Date(date) : date;
16643
+ switch (unit) {
16644
+ case "day":
16645
+ return (0, import_date_fns7.add)(parsedDate, { days: value });
16646
+ case "month":
16647
+ return (0, import_date_fns7.add)(parsedDate, { months: value });
16648
+ case "year":
16649
+ return (0, import_date_fns7.add)(parsedDate, { years: value });
16650
+ default:
16651
+ throw new Error(`Unsupported interval unit: ${unit}`);
16747
16652
  }
16748
- columns?.forEach((column, index) => {
16749
- if (column.label && ["null", "undefined"].includes(column.label.toLowerCase()) && !pivot.columnField && !pivot.aggregations?.[index]?.valueField && pivot.aggregations?.[index]?.aggregationType === "count") {
16750
- column.label = "Count";
16751
- }
16752
- });
16753
- const numericColumns = columns?.filter(
16754
- (column) => column.format === "whole_number" || column.format === "percentage"
16755
- );
16756
- rows.forEach((row) => {
16757
- numericColumns?.forEach((column) => {
16758
- row[column.field] = row[column.field] ?? 0;
16759
- });
16760
- });
16761
- return {
16762
- rows,
16763
- columns: columns ?? [],
16764
- rowCount: rowCount || rows.length,
16765
- pivotQuery: report?.queryString || "",
16766
- comparisonPivotQuery: comparisonPivotQuery || ""
16767
- };
16768
16653
  }
16769
- function generatePivotTableYAxis(pivot, cols, yAxisField) {
16770
- if (pivot?.aggregationType === "count") {
16771
- return [
16772
- {
16773
- field: pivot.valueField ?? "count",
16774
- label: yAxisField.label,
16775
- format: yAxisField.format
16776
- }
16777
- ];
16654
+ function dateTrunc(date, granularity = "day", databaseType) {
16655
+ const weekStartsOn = databaseType === "mssql" ? 0 : 1;
16656
+ const timeZone = "UTC";
16657
+ const zonedDate = (0, import_date_fns_tz2.utcToZonedTime)(date, timeZone);
16658
+ switch (granularity.toLowerCase()) {
16659
+ case "week":
16660
+ return (0, import_date_fns7.startOfWeek)(zonedDate, { weekStartsOn });
16661
+ case "month":
16662
+ return (0, import_date_fns7.startOfMonth)(zonedDate);
16663
+ case "day":
16664
+ return (0, import_date_fns7.startOfDay)(zonedDate);
16665
+ case "year":
16666
+ return (0, import_date_fns7.startOfYear)(zonedDate);
16667
+ default:
16668
+ throw new Error(`Unsupported granularity: ${granularity}`);
16778
16669
  }
16779
- return [
16780
- {
16781
- field: pivot.valueField ?? "count",
16782
- label: yAxisField.label,
16783
- format: yAxisField.format
16784
- }
16785
- ];
16786
16670
  }
16787
- function generatePivotTitle(pivot) {
16788
- if (pivot.rowField && !pivot.valueField && pivot.aggregations?.[0]) {
16789
- return snakeAndCamelCaseToTitleCase(
16790
- `${pivot.aggregations[0].aggregationType} of ${pivot.rowField}${pivot.columnField ? ` by ${pivot.columnField}` : ""}
16791
- `
16792
- );
16793
- } else if (!pivot.rowField && pivot.aggregations?.[0]?.valueField) {
16794
- return snakeAndCamelCaseToTitleCase(
16795
- `${pivot.aggregations[0].aggregationType} of ${pivot.aggregations[0].valueField}
16796
- `
16797
- );
16798
- }
16799
- return snakeAndCamelCaseToTitleCase(
16800
- `${pivot.aggregations?.[0]?.aggregationType ?? "Aggregation"} of ${pivot.aggregations?.[0]?.valueField ?? "value"}${pivot.rowField ? ` by ${pivot.rowField}` : ""}${pivot.columnField ? ` and ${pivot.columnField}` : ""}`
16801
- );
16671
+ function mergeComparisonPivotColumns({
16672
+ pivot,
16673
+ rows,
16674
+ compRows
16675
+ }) {
16676
+ if (!compRows || !compRows.length) return rows;
16677
+ const mappedCompRows = compRows.filter((compRow) => compRow.name !== pivot.rowField).map((compRow) => ({ ...compRow, name: `comparison_${compRow.name}` }));
16678
+ return rows.concat(mappedCompRows);
16802
16679
  }
16803
- async function generatePivotTable({
16680
+ function mergeComparisonPivotRows({
16804
16681
  pivot,
16682
+ rows,
16683
+ compRows,
16684
+ databaseType,
16805
16685
  dateBucket,
16806
- dateFilter,
16807
- report,
16808
- client,
16809
- getToken,
16810
- eventTracking,
16811
- uniqueValues,
16812
- dashboardName,
16813
- tenants,
16814
- additionalProcessing,
16815
- caller,
16816
- pivotQuery
16686
+ comparisonInterval,
16687
+ columnFieldValues
16817
16688
  }) {
16818
- try {
16819
- if (report && client) {
16820
- const pivotTable = await generatePivotWithSQL({
16821
- pivotQuery,
16822
- pivot,
16823
- report,
16824
- client,
16825
- dateBucket,
16826
- dateFilter,
16827
- dashboardName,
16828
- tenants,
16829
- additionalProcessing,
16830
- caller,
16831
- getToken
16832
- });
16833
- return pivotTable;
16834
- }
16835
- } catch (e) {
16836
- eventTracking?.logError?.({
16837
- type: "bug",
16838
- // TODO: determine type
16839
- severity: "high",
16840
- message: "Error generating pivot table",
16841
- errorMessage: e.message,
16842
- errorStack: e.stack,
16843
- errorData: {
16844
- caller: "PivotModal",
16845
- function: "generatePivotTable"
16846
- }
16847
- });
16848
- throw Error(`Failed to generate pivot table with SQL: ${e}`);
16689
+ if (!compRows || !compRows.length) return rows;
16690
+ if (pivot.columnField && columnFieldValues && columnFieldValues.length > 0) {
16691
+ return merge2DPivotRows(
16692
+ pivot,
16693
+ rows,
16694
+ compRows,
16695
+ columnFieldValues,
16696
+ databaseType,
16697
+ dateBucket,
16698
+ comparisonInterval
16699
+ );
16849
16700
  }
16850
- throw Error("Failed to generate pivot table: invalid report");
16701
+ if (pivot.rowField) {
16702
+ return merge1DPivotRows(
16703
+ pivot,
16704
+ rows,
16705
+ compRows,
16706
+ databaseType,
16707
+ dateBucket,
16708
+ comparisonInterval
16709
+ );
16710
+ }
16711
+ return mergeAggregatedRows(pivot, rows, compRows);
16851
16712
  }
16852
-
16853
- // src/utils/paginationProcessing.ts
16854
- var DEFAULT_PAGINATION = {
16855
- page: 0,
16856
- rowsPerPage: 10,
16857
- rowsPerRequest: 600
16858
- };
16859
- var DEFAULT_TABLE_PAGINATION = {
16860
- page: 0,
16861
- rowsPerPage: 10,
16862
- rowsPerRequest: 50
16863
- };
16864
- function shouldFetchMore(pagination, page, maxPage, currentRowCount) {
16865
- if (!pagination || currentRowCount && currentRowCount >= pagination.rowsPerPage * (page + 1)) {
16866
- return false;
16713
+ function merge2DPivotRows(pivot, rows, compRows, columnFieldValues, databaseType, dateBucket, comparisonInterval) {
16714
+ if (!pivot || !pivot.columnField) {
16715
+ return [];
16867
16716
  }
16868
- const indexAdjustedPage = page;
16869
- const pageInterval = Math.floor(
16870
- indexAdjustedPage * pagination.rowsPerPage / pagination.rowsPerRequest
16871
- );
16872
- const indexAdjustedPreviousPage = maxPage;
16873
- const previousPageInterval = Math.floor(
16874
- indexAdjustedPreviousPage * pagination.rowsPerPage / pagination.rowsPerRequest
16717
+ if (isStringType(pivot.rowFieldType || "") || !pivot.rowFieldType) {
16718
+ return merge2DStringPivotRows(pivot, rows, compRows, columnFieldValues);
16719
+ }
16720
+ return merge2DDatePivotRows(
16721
+ pivot,
16722
+ rows,
16723
+ compRows,
16724
+ columnFieldValues,
16725
+ databaseType,
16726
+ dateBucket,
16727
+ comparisonInterval
16875
16728
  );
16876
- return pageInterval > previousPageInterval;
16877
16729
  }
16878
- function shouldSortInMemory(pagination, rowCount) {
16879
- if (!rowCount || rowCount < pagination.rowsPerRequest) {
16880
- return true;
16730
+ function merge2DStringPivotRows(pivot, rows, compRows, columnFieldValues) {
16731
+ if (!pivot.rowField || !pivot.aggregations?.some((agg) => agg.valueField)) {
16732
+ return rows;
16881
16733
  }
16882
- return false;
16734
+ return rows.map((row) => {
16735
+ const matchingCompRow = compRows.find(
16736
+ (compRow) => compRow[pivot.rowField] === row[pivot.rowField]
16737
+ );
16738
+ const comparisonFields = columnFieldValues.reduce(
16739
+ (acc, fieldValue) => {
16740
+ acc[`comparison_${fieldValue}`] = matchingCompRow ? matchingCompRow[fieldValue] || null : null;
16741
+ return acc;
16742
+ },
16743
+ {}
16744
+ );
16745
+ return {
16746
+ ...row,
16747
+ ...comparisonFields
16748
+ };
16749
+ });
16883
16750
  }
16884
-
16885
- // src/utils/dashboard.ts
16886
- var defaultDashboardItem = {
16887
- id: "",
16888
- name: "",
16889
- dashboardName: "",
16890
- rows: [],
16891
- compareRows: [],
16892
- columns: [],
16893
- chartType: "",
16894
- pivot: null,
16895
- yAxisFields: [],
16896
- xAxisLabel: "",
16897
- xAxisField: "",
16898
- xAxisFormat: "string",
16899
- order: -1,
16900
- filtersApplied: [],
16901
- queryString: "",
16902
- rowCount: 0,
16903
- columnInternal: []
16904
- };
16905
- async function cleanDashboardItem({
16906
- item,
16907
- dashboardFilters,
16908
- getToken,
16909
- eventTracking,
16910
- client,
16911
- dateBucket,
16912
- additionalProcessing,
16913
- customFields,
16914
- skipPivotFetch,
16915
- tenants
16916
- }) {
16917
- if (!item) return defaultDashboardItem;
16918
- if (!item.rows) {
16751
+ function merge2DDatePivotRows(pivot, rows, compRows, columnFieldValues, databaseType, dateBucket, comparisonInterval) {
16752
+ if (!comparisonInterval || !pivot.rowField) {
16753
+ return rows;
16754
+ }
16755
+ return rows.map((row) => {
16756
+ const rowDate = typeof row[pivot.rowField] === "string" ? new Date(row[pivot.rowField]) : row[pivot.rowField];
16757
+ const truncatedRowDate = dateTrunc(rowDate, dateBucket, databaseType);
16758
+ const matchingCompRow = compRows.find((compRow) => {
16759
+ const comparisonDateWithInterval = dateAdder(
16760
+ compRow[pivot.rowField],
16761
+ comparisonInterval
16762
+ );
16763
+ const truncatedCompRowDate = dateTrunc(
16764
+ comparisonDateWithInterval,
16765
+ dateBucket,
16766
+ databaseType
16767
+ );
16768
+ return truncatedRowDate.toISOString() === truncatedCompRowDate.toISOString();
16769
+ });
16770
+ const comparisonFields = columnFieldValues.reduce(
16771
+ (acc, fieldValue) => {
16772
+ acc[`comparison_${fieldValue}`] = matchingCompRow ? matchingCompRow[fieldValue] || null : null;
16773
+ return acc;
16774
+ },
16775
+ {}
16776
+ );
16919
16777
  return {
16920
- ...defaultDashboardItem,
16921
- id: item._id,
16922
- name: item.name
16778
+ ...row,
16779
+ ...comparisonFields
16923
16780
  };
16781
+ });
16782
+ }
16783
+ function merge1DPivotRows(pivot, rows, compRows, databaseType, dateBucket, comparisonInterval) {
16784
+ if (isStringType(pivot.rowFieldType || "") || !pivot.rowFieldType) {
16785
+ return merge1DStringPivotRows(pivot, rows, compRows);
16924
16786
  }
16925
- const fields = item.fields || [];
16926
- const columnsWithCustomFields = [...item.columns ?? []];
16927
- if (item.includeCustomFields && item.rows?.length > 0) {
16928
- const tables = item.referencedTables ?? [];
16929
- tables.forEach((table) => {
16930
- const _customFields = customFields?.[table] ?? [];
16931
- _customFields.forEach((field) => {
16932
- const isJsonCustomField = !!field.refColumn;
16933
- if (item.rows[0][field.field] !== void 0 && !item.columns.some((col) => col.field === field.field)) {
16934
- const result_field = fields.find((f) => f.name === field.field);
16935
- const converted_field = convertPostgresColumn(result_field ?? {});
16936
- columnsWithCustomFields.push({
16937
- field: field.field,
16938
- format: converted_field.format,
16939
- label: snakeAndCamelCaseToTitleCase(field.field),
16940
- fieldType: converted_field.fieldType,
16941
- dataTypeID: converted_field.dataTypeID,
16942
- jsType: converted_field.jsType,
16943
- inferFormat: isJsonCustomField
16944
- });
16945
- }
16946
- });
16947
- });
16787
+ return merge1DDatePivotRows(
16788
+ pivot,
16789
+ rows,
16790
+ compRows,
16791
+ databaseType,
16792
+ dateBucket,
16793
+ comparisonInterval
16794
+ );
16795
+ }
16796
+ function merge1DStringPivotRows(pivot, rows, compRows) {
16797
+ if (!pivot.rowField || !pivot.valueField) {
16798
+ return rows;
16948
16799
  }
16949
- const processedColumns = item.columns.map((col) => {
16950
- return { ...col, label: snakeAndCamelCaseToTitleCase(col.label) };
16951
- });
16952
- const columnInternal = (item.includeCustomFields ? columnsWithCustomFields : item.columns).map((col) => {
16953
- const field = item.fields?.find((f) => f.name === col.field);
16954
- const converted_field = convertPostgresColumn(field ?? {});
16800
+ return rows.map((row) => {
16801
+ const matchingCompRow = compRows.find(
16802
+ (compRow) => compRow[pivot.rowField] === row[pivot.rowField]
16803
+ );
16804
+ let aggregationSuffix = "";
16805
+ if (matchingCompRow && pivot.aggregationType === "percentage") {
16806
+ if (matchingCompRow[`${pivot.valueField}_Percentage`]) {
16807
+ aggregationSuffix = "_Percentage";
16808
+ } else if (matchingCompRow[`${pivot.valueField}_PERCENTAGE`]) {
16809
+ aggregationSuffix = "_PERCENTAGE";
16810
+ } else {
16811
+ aggregationSuffix = "_percentage";
16812
+ }
16813
+ }
16955
16814
  return {
16956
- fieldType: converted_field.fieldType,
16957
- dataTypeID: converted_field.dataTypeID,
16958
- jsType: converted_field.jsType,
16959
- ...col,
16960
- label: snakeAndCamelCaseToTitleCase(col.label)
16815
+ ...row,
16816
+ [`comparison_${pivot.valueField}${aggregationSuffix}`]: matchingCompRow ? matchingCompRow[`${pivot.valueField}${aggregationSuffix}`] : null
16961
16817
  };
16962
16818
  });
16963
- let pivotTable;
16964
- let pivotError;
16965
- try {
16966
- const shouldPaginatePivotAsTable = item.chartType === "table";
16967
- const pivotChartProcessing = {
16968
- page: DEFAULT_PAGINATION
16819
+ }
16820
+ function merge1DDatePivotRows(pivot, rows, compRows, databaseType, dateBucket, comparisonInterval) {
16821
+ if (!comparisonInterval || !pivot.rowField) {
16822
+ return rows;
16823
+ }
16824
+ return rows.map((row) => {
16825
+ const rowDate = typeof row[pivot.rowField] === "string" ? new Date(row[pivot.rowField]) : row[pivot.rowField];
16826
+ const truncatedRowDate = dateTrunc(rowDate, dateBucket, databaseType);
16827
+ const matchingCompRow = compRows.find((compRow) => {
16828
+ const comparisonDateWithInterval = dateAdder(
16829
+ compRow[`${pivot.rowField}`],
16830
+ comparisonInterval
16831
+ );
16832
+ const truncatedCompRowDate = dateTrunc(
16833
+ comparisonDateWithInterval,
16834
+ dateBucket,
16835
+ databaseType
16836
+ );
16837
+ return truncatedRowDate.toISOString() === truncatedCompRowDate.toISOString();
16838
+ });
16839
+ let aggregationSuffix = "";
16840
+ if (matchingCompRow && pivot.aggregationType === "percentage") {
16841
+ if (matchingCompRow[`${pivot.valueField}_Percentage`]) {
16842
+ aggregationSuffix = "_Percentage";
16843
+ } else if (matchingCompRow[`${pivot.valueField}_PERCENTAGE`]) {
16844
+ aggregationSuffix = "_PERCENTAGE";
16845
+ } else {
16846
+ aggregationSuffix = "_percentage";
16847
+ }
16848
+ }
16849
+ return {
16850
+ ...row,
16851
+ [`comparison_${pivot.valueField}${aggregationSuffix}`]: matchingCompRow?.[`${pivot.valueField}${aggregationSuffix}`] || null
16969
16852
  };
16970
- pivotTable = await getPivotTable(
16971
- {
16972
- ...item,
16973
- pivot: item.pivot && !skipPivotFetch ? {
16974
- ...item.pivot,
16975
- aggregations: item.pivot.aggregations ?? [
16976
- {
16977
- valueField: item.pivot.valueField,
16978
- valueFieldType: item.pivot.valueFieldType,
16979
- valueField2: item.pivot.valueField2,
16980
- valueField2Type: item.pivot.valueField2Type,
16981
- aggregationType: item.pivot.aggregationType
16982
- }
16983
- ]
16984
- } : void 0
16985
- },
16986
- dashboardFilters,
16987
- item.dashboardName,
16988
- getToken,
16989
- client,
16990
- eventTracking,
16991
- dateBucket,
16992
- shouldPaginatePivotAsTable ? additionalProcessing : pivotChartProcessing,
16993
- tenants,
16994
- customFields
16995
- );
16996
- } catch (e) {
16997
- pivotTable = void 0;
16998
- eventTracking?.logError?.({
16999
- type: "bug",
17000
- // TODO: determine type
17001
- severity: "high",
17002
- message: "Error fetching pivot table",
17003
- errorMessage: e.message,
17004
- errorStack: e.stack,
17005
- errorData: {
17006
- caller: "cleanDashboardItem",
17007
- function: "cleanDashboardItem"
16853
+ });
16854
+ }
16855
+ function mergeAggregatedRows(pivot, rows, compRows) {
16856
+ return rows.map((row, i) => {
16857
+ let aggregationSuffix = "";
16858
+ if (compRows[i] && pivot.aggregationType === "percentage") {
16859
+ if (compRows[i][`${pivot.valueField}_Percentage`]) {
16860
+ aggregationSuffix = "_Percentage";
16861
+ } else if (compRows[i][`${pivot.valueField}_PERCENTAGE`]) {
16862
+ aggregationSuffix = "_PERCENTAGE";
16863
+ } else {
16864
+ aggregationSuffix = "_percentage";
17008
16865
  }
17009
- });
17010
- console.error("Error fetching pivot table", e);
17011
- pivotError = "Error fetching pivot table";
17012
- }
17013
- const referenceLineYValues = [];
17014
- for (const key in item) {
17015
- if (key.startsWith("referenceLine_")) {
17016
- const field = key.slice(14);
17017
- if (!item[key] || !item[key][0]) continue;
17018
- const value = Object.values(item[key][0])[0];
17019
- referenceLineYValues.push({ label: field, query: [value, value] });
17020
16866
  }
16867
+ const compRow = {
16868
+ [`comparison_${pivot.valueField}${aggregationSuffix}`]: compRows[i]?.[`${pivot.valueField}${aggregationSuffix}`]
16869
+ };
16870
+ return { ...row, ...compRow };
16871
+ });
16872
+ }
16873
+
16874
+ // src/utils/queryConstructor.ts
16875
+ function processSingleQuotes(value, databaseType) {
16876
+ if (["postgresql", "snowflake", "clickhouse"].includes(
16877
+ databaseType.toLowerCase()
16878
+ )) {
16879
+ return value.replaceAll("'", "''");
17021
16880
  }
17022
- if (item.referenceLines) {
17023
- for (const referenceLine of item.referenceLines) {
17024
- if (Array.isArray(referenceLine.query)) {
17025
- referenceLineYValues.push({
17026
- label: referenceLine.label,
17027
- query: referenceLine.query
17028
- });
17029
- } else if (referenceLine.query === "") {
17030
- referenceLineYValues.push({
17031
- label: referenceLine.label,
17032
- query: (pivotTable?.rows ? pivotTable.rows : item.rows).map(
17033
- (row) => Number(row[referenceLine.label]) || 0
17034
- )
17035
- });
16881
+ return value.replaceAll("'", "\\'");
16882
+ }
16883
+ function processAggType(aggType, hasColumnField = false) {
16884
+ if (aggType === "count" && hasColumnField) return "SUM";
16885
+ return aggType?.toLowerCase() === "average" ? "AVG" : aggType?.toLowerCase();
16886
+ }
16887
+ function replaceBigQuerySpecialCharacters(column) {
16888
+ return column.replaceAll("/", "quill_forward_slash");
16889
+ }
16890
+ function processColumnReference(column, databaseType, fallbackOnNull, isColumnFieldAlias, isValueFieldAlias) {
16891
+ switch (databaseType.toLowerCase()) {
16892
+ case "postgresql":
16893
+ case "clickhouse": {
16894
+ if (column === "") {
16895
+ return fallbackOnNull ? `"${fallbackOnNull}"` : `"_"`;
16896
+ }
16897
+ if (isColumnFieldAlias) {
16898
+ return `"${column.replaceAll('"', "")}"`;
17036
16899
  }
17037
- }
17038
- }
17039
- return {
17040
- id: item._id ?? item.id,
17041
- name: item.name,
17042
- dashboardName: item.dashboardName,
17043
- // section: item.section,
17044
- rows: item.rows,
17045
- pivotRows: pivotTable ? pivotTable.rows : void 0,
17046
- pivotColumns: pivotTable ? pivotTable.columns : void 0,
17047
- compareRows: item.compareRows,
17048
- columns: processedColumns.map((column) => {
17049
- return {
17050
- field: column.field,
17051
- format: column.format,
17052
- label: column.label,
17053
- inferFormat: column.inferFormat
17054
- };
17055
- }),
17056
- includeCustomFields: item.includeCustomFields,
17057
- columnInternal,
17058
- columnsWithCustomFields,
17059
- chartType: item.chartType,
17060
- dateField: item.dateField,
17061
- pivot: pivotError ? void 0 : item.pivot ? {
17062
- ...item.pivot,
17063
- aggregations: item.pivot.aggregations ?? [
17064
- {
17065
- valueField: item.pivot.valueField,
17066
- valueFieldType: item.pivot.valueFieldType,
17067
- valueField2: item.pivot.valueField2,
17068
- valueField2Type: item.pivot.valueField2Type,
17069
- aggregationType: item.pivot.aggregationType
17070
- }
17071
- ],
17072
- columnValues: item.distinctStrings
17073
- } : void 0,
17074
- yAxisFields: pivotTable ? extractPivotedYAxis(pivotTable, item) : item.yAxisFields,
17075
- xAxisLabel: item.xAxisLabel,
17076
- xAxisField: item.xAxisField,
17077
- xAxisFormat: item.xAxisFormat,
17078
- order: item.order,
17079
- filtersApplied: item.filtersApplied,
17080
- filterMap: item.filterMap,
17081
- flags: item.flags,
17082
- rowCount: item.rowCount ? parseInt(item.rowCount) : item.rows.length,
17083
- pivotRowCount: pivotTable ? pivotTable.rowCount : void 0,
17084
- template: item.template,
17085
- sort: item.sort,
17086
- itemQuery: item.itemQuery,
17087
- queryString: item.queryString,
17088
- pivotQuery: pivotTable?.pivotQuery,
17089
- comparisonPivotQuery: pivotTable?.comparisonPivotQuery,
17090
- referencedTables: item?.referencedTables || [],
17091
- referencedColumns: item?.referencedColumns || {},
17092
- error: item.error ?? pivotError,
17093
- referenceLineYValues,
17094
- referenceLines: item.referenceLines
17095
- };
17096
- }
17097
- async function getPivotTable(report, dashboardFilters, dashboardName, getToken, client, eventTracking, dateBucketInitial, additionalProcessing, tenants, customFields) {
17098
- if (!report) return void 0;
17099
- const dateFilter = Object.values(dashboardFilters ?? {}).find(
17100
- (filter) => filter.filterType === "date_range" || filter.operator === "BETWEEN"
17101
- );
17102
- if (dateFilter?.operator === "BETWEEN") {
17103
- dateFilter.startDate = dateFilter.value[0];
17104
- dateFilter.endDate = dateFilter.value[1];
17105
- }
17106
- const pivot = report?.pivot;
17107
- const data = report || {};
17108
- if (pivot && client) {
17109
- if (report.rowCount === 0 || report.rows.length === 0) {
17110
- const columns = [];
17111
- if (pivot.rowField) {
17112
- columns.push({
17113
- field: pivot.rowField,
17114
- label: snakeCaseToTitleCase(processColumnName(pivot.rowField)),
17115
- format: pivot.rowFieldType || "string",
17116
- jsType: convertFieldTypeToJSType(pivot.rowFieldType || "string"),
17117
- fieldType: pivot.rowFieldType || "string",
17118
- dataTypeID: fieldTypeToDataTypeID(pivot.rowFieldType || "string")
17119
- });
17120
- }
17121
- for (const agg of pivot.aggregations ?? []) {
17122
- if (agg.valueField) {
17123
- columns.push({
17124
- field: agg.valueField,
17125
- label: snakeCaseToTitleCase(processColumnName(agg.valueField)),
17126
- //FIXME: valueFieldType is not always the same as the format
17127
- format: agg.valueFieldType ?? "whole_number",
17128
- jsType: agg.valueFieldType ?? "number",
17129
- fieldType: agg.valueFieldType ?? "number",
17130
- dataTypeID: fieldTypeToDataTypeID(agg.valueFieldType ?? "number")
17131
- });
17132
- }
17133
- }
17134
- return {
17135
- rows: [],
17136
- rowCount: 0,
17137
- pivotQuery: report.queryString ?? "",
17138
- columns
17139
- };
17140
- }
17141
- try {
17142
- let dateBucket = dateBucketInitial;
17143
- let filterDateRange = void 0;
17144
- if (dateFilter && dateFilter.startDate && dateFilter.endDate) {
17145
- filterDateRange = {
17146
- start: dateFilter.startDate,
17147
- end: dateFilter.endDate
17148
- };
17149
- } else if (report.dateRange) {
17150
- filterDateRange = report.dateRange;
17151
- }
17152
- if (!dateBucket && filterDateRange) {
17153
- dateBucket = getDateBucketFromRange(filterDateRange);
17154
- }
17155
- const pivotTable = await generatePivotWithSQL({
17156
- pivot,
17157
- report,
17158
- client,
17159
- dateBucket,
17160
- dateFilter,
17161
- dashboardName,
17162
- tenants,
17163
- additionalProcessing,
17164
- getToken
17165
- });
17166
- return pivotTable;
17167
- } catch (e) {
17168
- eventTracking?.logError?.({
17169
- type: "bug",
17170
- // TODO: determine type
17171
- severity: "high",
17172
- message: "Error fetching pivot table",
17173
- errorMessage: e.message,
17174
- errorStack: e.stack,
17175
- errorData: {
17176
- caller: "getPivotTable",
17177
- function: "getPivotTable"
17178
- }
17179
- });
17180
- console.error("Error fetching pivot table", e);
17181
- throw e;
17182
- }
17183
- }
17184
- return pivot && data.rows ? generatePivotTable({
17185
- pivot,
17186
- report,
17187
- client,
17188
- uniqueValues: report.distinctStrings,
17189
- dashboardName,
17190
- tenants,
17191
- dateFilter,
17192
- additionalProcessing,
17193
- getToken,
17194
- eventTracking
17195
- }) : void 0;
17196
- }
17197
- function extractPivotedYAxis(pivotTable, itemInfo, config = void 0) {
17198
- if (!pivotTable) return itemInfo?.yAxisFields ?? [];
17199
- const pivot = itemInfo?.pivot || config?.pivot;
17200
- if (!pivot.columnField && !pivot.rowField) return itemInfo?.yAxisFields ?? [];
17201
- const yAxisFields = config ? config.yAxisFields : itemInfo?.yAxisFields;
17202
- return yAxisFields && yAxisFields.length > 0 ? generatePivotTableYAxis(pivot, pivotTable.columns, yAxisFields[0]) : yAxisFields;
17203
- }
17204
- async function getDashboard(dashboardName, client, getToken, tenants, flags) {
17205
- const { data: resp } = await quillFetch({
17206
- client,
17207
- task: "dashboard",
17208
- metadata: {
17209
- name: dashboardName,
17210
- clientId: client.publicKey,
17211
- databaseType: client.databaseType,
17212
- useNewNodeSql: true,
17213
- tenants,
17214
- flags
17215
- },
17216
- getToken
17217
- });
17218
- return {
17219
- ...resp,
17220
- createdAt: resp.createdAt && new Date(resp.createdAt),
17221
- dateFilter: resp.dateFilter ? {
17222
- ...resp.dateFilter,
17223
- presetOptions: resp.dateFilter.presetOptions?.map(
17224
- (preset) => ({
17225
- ...preset,
17226
- loopStart: preset.loopStart ? new Date(preset.loopStart) : void 0,
17227
- loopEnd: preset.loopEnd ? new Date(preset.loopEnd) : void 0
17228
- })
17229
- ),
17230
- defaultPresetRanges: resp.dateFilter.defaultPresetRanges?.map(
17231
- (preset) => ({
17232
- ...preset,
17233
- loopStart: preset.loopStart ? new Date(preset.loopStart) : void 0,
17234
- loopEnd: preset.loopEnd ? new Date(preset.loopEnd) : void 0
17235
- })
17236
- )
17237
- } : void 0
17238
- };
17239
- }
17240
-
17241
- // src/utils/chartBuilder.ts
17242
- var numberFormatOptions = [
17243
- "whole_number",
17244
- "one_decimal_place",
17245
- "two_decimal_places",
17246
- "dollar_amount",
17247
- "dollar_cents",
17248
- "percentage"
17249
- ];
17250
- var dateFormatOptions = [
17251
- "MMM_yyyy",
17252
- "MMM_dd",
17253
- "MMM_dd_yyyy",
17254
- "MMM_dd_hh:mm_ap_pm",
17255
- "hh_ap_pm",
17256
- "date",
17257
- "timestamptz"
17258
- ];
17259
- var NUMBER_OPTIONS = [
17260
- { value: "whole_number", label: "whole number" },
17261
- { value: "one_decimal_place", label: "one decimal place" },
17262
- { value: "two_decimal_places", label: "two decimal places" },
17263
- { value: "dollar_amount", label: "dollar amount" },
17264
- { value: "dollar_cents", label: "dollar and cent amount" },
17265
- { value: "percent", label: "percentage" }
17266
- ];
17267
- var DATE_OPTIONS = [
17268
- { value: "MMM_yyyy", label: "month" },
17269
- { value: "MMM_dd", label: "day" },
17270
- { value: "MMM_dd_yyyy", label: "day and year" },
17271
- { value: "MMM_dd_hh:mm_ap_pm", label: "day and time" },
17272
- { value: "hh_ap_pm", label: "hour" }
17273
- ];
17274
- var ALL_FORMAT_OPTIONS = [
17275
- ...NUMBER_OPTIONS,
17276
- ...DATE_OPTIONS,
17277
- { value: "string", label: "string" }
17278
- ];
17279
- function createInitialFormData(columns) {
17280
- const firstNumberColumn = columns?.find(
17281
- (col) => numberFormatOptions.includes(col.format)
17282
- );
17283
- const firstStringColumn = columns?.find(
17284
- (col) => !numberFormatOptions.includes(col.format) && !dateFormatOptions.includes(col.format)
17285
- );
17286
- const firstDateColumn = columns?.find(
17287
- (col) => dateFormatOptions.includes(col.format)
17288
- );
17289
- const xAxisField = firstStringColumn?.field || firstDateColumn?.field || firstNumberColumn?.field || columns?.[0]?.field || "";
17290
- const xAxisFormat = firstStringColumn?.format || firstDateColumn?.format || firstNumberColumn?.format || columns?.[0]?.format || "string";
17291
- const formEmptyState = {
17292
- name: "",
17293
- columns: columns.map((col) => {
17294
- return { ...col, label: snakeAndCamelCaseToTitleCase(col.label) };
17295
- }),
17296
- xAxisField,
17297
- xAxisFormat,
17298
- yAxisFields: [
17299
- {
17300
- field: firstNumberColumn?.field || columns?.[0]?.field || "",
17301
- label: "",
17302
- format: firstNumberColumn?.format || columns?.[0]?.format || "string"
17303
- }
17304
- ],
17305
- xAxisLabel: "",
17306
- chartType: firstNumberColumn ? "line" : "table",
17307
- pivot: null,
17308
- template: true,
17309
- referenceLines: []
17310
- };
17311
- return formEmptyState;
17312
- }
17313
-
17314
- // src/utils/error.ts
17315
- var DataLoadError = class extends Error {
17316
- data;
17317
- constructor(message, data) {
17318
- super(message);
17319
- this.name = "DataLoadError";
17320
- this.data = data;
17321
- }
17322
- };
17323
-
17324
- // src/utils/errorProcessing.ts
17325
- function processFilterErrorList(resp) {
17326
- if (!resp || !resp.filterErrorList || !Array.isArray(resp.filterErrorList)) {
17327
- return;
17328
- }
17329
- }
17330
-
17331
- // src/utils/queryConstructor.ts
17332
- function replaceBigQuerySpecialCharacters(column) {
17333
- return column.replaceAll("/", "quill_forward_slash");
17334
- }
17335
- function processColumnReference(column, databaseType, fallbackOnNull, isColumnFieldAlias, isValueFieldAlias) {
17336
- switch (databaseType.toLowerCase()) {
17337
- case "postgresql":
17338
- case "clickhouse": {
17339
- if (column === "") {
17340
- return fallbackOnNull ? `"${fallbackOnNull}"` : `"_"`;
17341
- }
17342
- if (isColumnFieldAlias) {
17343
- return `"${column.replaceAll('"', "")}"`;
17344
- }
17345
- const columnParts = column.split(".");
17346
- if (columnParts.length > 1) {
17347
- return `"` + columnParts.map((part) => part.replaceAll('"', "")).join('"."') + `"`;
17348
- }
17349
- return `"${column.replaceAll('"', "")}"`;
16900
+ const columnParts = column.split(".");
16901
+ if (columnParts.length > 1) {
16902
+ return `"` + columnParts.map((part) => part.replaceAll('"', "")).join('"."') + `"`;
16903
+ }
16904
+ return `"${column.replaceAll('"', "")}"`;
17350
16905
  }
17351
16906
  case "mysql": {
17352
16907
  if (column === "") {
@@ -17420,6 +16975,51 @@ function processColumnReference(column, databaseType, fallbackOnNull, isColumnFi
17420
16975
  function replaceSpacesWithUnderscores(column) {
17421
16976
  return column.replaceAll(" ", "_");
17422
16977
  }
16978
+ function processInterval(interval2, rowField, databaseType) {
16979
+ if (["postgresql", "snowflake", "clickhouse"].includes(
16980
+ databaseType.toLowerCase()
16981
+ )) {
16982
+ return `(${processColumnReference(rowField, databaseType)} + INTERVAL '${interval2}')`;
16983
+ } else if (databaseType.toLowerCase() === "mysql") {
16984
+ return `(${processColumnReference(rowField, databaseType)} + INTERVAL ${interval2})`;
16985
+ }
16986
+ return `TIMESTAMP_ADD(${processColumnReference(rowField, databaseType)}, INTERVAL ${interval2} )`;
16987
+ }
16988
+ function processDateTrunc(dateBucket, rowField, databaseType, comparisonInterval) {
16989
+ const dateField = comparisonInterval ? processInterval(comparisonInterval, rowField, databaseType) : processColumnReference(rowField, databaseType);
16990
+ if (["postgresql", "snowflake"].includes(databaseType.toLowerCase())) {
16991
+ return `date_trunc('${dateBucket}', ${dateField})`;
16992
+ }
16993
+ if (["clickhouse"].includes(databaseType.toLowerCase())) {
16994
+ return `dateTrunc('${dateBucket}', ${dateField})`;
16995
+ }
16996
+ if (["databricks"].includes(databaseType.toLowerCase())) {
16997
+ return `DATE_TRUNC('${dateBucket}', ${dateField})`;
16998
+ }
16999
+ if (["mysql"].includes(databaseType.toLowerCase())) {
17000
+ switch (dateBucket.toLowerCase()) {
17001
+ case "year":
17002
+ return `DATE_FORMAT(${dateField}, '%Y-01-01 00:00:00')`;
17003
+ case "month":
17004
+ return `DATE_FORMAT(${dateField}, '%Y-%m-01 00:00:00')`;
17005
+ case "week":
17006
+ return `DATE_FORMAT(${dateField}, '%Y-%U-1 00:00:00')`;
17007
+ case "day":
17008
+ default:
17009
+ return `DATE(${dateField})`;
17010
+ }
17011
+ }
17012
+ if (["mssql"].includes(databaseType.toLowerCase())) {
17013
+ return `DATETRUNC(${dateBucket}, ${dateField})`;
17014
+ }
17015
+ return `TIMESTAMP_TRUNC(${dateField}, ${dateBucket})`;
17016
+ }
17017
+ function processValueField(aggType, databaseType, valueField) {
17018
+ if (aggType === "min" || aggType === "max" || aggType?.toLowerCase() === "average")
17019
+ return `${processColumnReference(valueField, databaseType)} ELSE null`;
17020
+ if (aggType === "count") return `1 ELSE 0`;
17021
+ return valueField ? `${processColumnReference(valueField, databaseType)} ELSE 0` : `1 ELSE 0`;
17022
+ }
17423
17023
  function generateCountQuery(fields, query, databaseType) {
17424
17024
  let countQuery = [];
17425
17025
  let cteQuery = `WITH querytable AS (${query.replace(";", "")}) `;
@@ -17538,12 +17138,1123 @@ function generateMinMaxDateRangeQueries(columnFields, query, databaseType) {
17538
17138
  const distinctQueries = columnFields.map((field) => {
17539
17139
  const wrappedField = ["postgresql", "clickhouse"].includes(
17540
17140
  databaseType.toLowerCase()
17541
- ) ? processColumnReference(field, databaseType) : field;
17542
- 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`;
17141
+ ) ? `"${field}"` : field;
17142
+ return `SELECT '${field}' AS field, ${min2}(${wrappedField})${cast} AS min_range, ${max2}(${wrappedField})${cast} AS max_range FROM querytable`;
17543
17143
  });
17544
17144
  const distinctQuery = distinctQueries.join(" UNION ALL ");
17545
17145
  return `WITH querytable AS (${query.replace(";", "")}) ` + distinctQuery;
17546
17146
  }
17147
+ function generatePivotQuery(pivot, itemQuery, databaseType, distinctStrings, dateBucket) {
17148
+ if (!isValidPivot(pivot).valid) {
17149
+ return void 0;
17150
+ }
17151
+ if (pivot.columnField && distinctStrings && distinctStrings.length > 0) {
17152
+ return create2DPivotQuery(
17153
+ pivot,
17154
+ itemQuery,
17155
+ databaseType,
17156
+ distinctStrings,
17157
+ dateBucket
17158
+ );
17159
+ }
17160
+ if (pivot.rowField) {
17161
+ return create1DPivotQuery(pivot, itemQuery, dateBucket, databaseType);
17162
+ }
17163
+ return createAggregationValuePivot(pivot, itemQuery, databaseType);
17164
+ }
17165
+ function create2DPivotQuery(pivot, itemQuery, databaseType, columnFieldValues, dateBucket) {
17166
+ if (!pivot || !pivot.columnField) {
17167
+ return void 0;
17168
+ }
17169
+ if (isStringType(pivot.rowFieldType ?? "") || !pivot.rowFieldType) {
17170
+ return create2DStringPivotQuery(
17171
+ pivot,
17172
+ itemQuery,
17173
+ columnFieldValues,
17174
+ databaseType
17175
+ );
17176
+ }
17177
+ return create2DDatePivotQuery(
17178
+ pivot,
17179
+ itemQuery,
17180
+ columnFieldValues,
17181
+ databaseType,
17182
+ dateBucket
17183
+ );
17184
+ }
17185
+ function create2DStringPivotQuery(pivot, itemQuery, columnFieldValues, databaseType) {
17186
+ const isValidBaseQuery = itemQuery.match(
17187
+ /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/
17188
+ );
17189
+ if (!isValidBaseQuery || !pivot.columnField || !pivot.rowField)
17190
+ return void 0;
17191
+ const rowField = pivot.rowField;
17192
+ if (!pivot.aggregations?.[0]?.valueField && pivot.aggregations?.[0]?.aggregationType !== "count" && !pivot.valueField && pivot.aggregationType !== "count")
17193
+ throw new Error("No value field provided for pivot");
17194
+ if (!pivot.aggregations?.[0]?.aggregationType && !pivot.aggregationType)
17195
+ throw new Error("No aggregation type provided for pivot");
17196
+ const columnField = pivot.columnField;
17197
+ const rowFieldAlias = processColumnReference(
17198
+ rowField,
17199
+ databaseType,
17200
+ void 0,
17201
+ false,
17202
+ true
17203
+ );
17204
+ const columnFieldAlias = processColumnReference(
17205
+ columnField,
17206
+ databaseType,
17207
+ void 0,
17208
+ false,
17209
+ true
17210
+ );
17211
+ let caseWhens = [];
17212
+ let valueAliases = [];
17213
+ const seenAggs = {};
17214
+ pivot.aggregations?.forEach((currentAgg) => {
17215
+ if (seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]) {
17216
+ seenAggs[currentAgg.aggregationType ?? ""][currentAgg.valueField ?? ""] += 1;
17217
+ } else {
17218
+ seenAggs[currentAgg.aggregationType ?? ""] = {
17219
+ ...seenAggs[currentAgg.aggregationType ?? ""],
17220
+ [currentAgg.valueField ?? ""]: 1
17221
+ };
17222
+ }
17223
+ let disambiguationIndex = seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]?.toString();
17224
+ if (disambiguationIndex === "1") disambiguationIndex = "";
17225
+ const valueFieldAlias = processColumnReference(
17226
+ currentAgg.valueField || rowField || "count",
17227
+ databaseType,
17228
+ void 0,
17229
+ false,
17230
+ true
17231
+ );
17232
+ const valueAliasSubstring = currentAgg.valueField ? `${processColumnReference(currentAgg.valueField, databaseType, void 0, true)} AS ${valueFieldAlias}` : "";
17233
+ let value2AliasSubstring = "";
17234
+ const disambiguationField = Object.values(seenAggs[currentAgg.aggregationType ?? ""] ?? {}).reduce(
17235
+ (acc, v) => acc + v
17236
+ ) > 1 ? `_${currentAgg.valueField}${disambiguationIndex}` : "";
17237
+ const disambiguation = pivot.aggregations?.length > 1 ? `${disambiguationField}_${disambiguationField ? matchCasing(currentAgg.aggregationType, currentAgg.valueField) : currentAgg.aggregationType}` : "";
17238
+ const valueExpr = currentAgg.valueFieldType === "bool" ? `CASE WHEN ${valueFieldAlias} THEN 1 ELSE 0 END` : processValueField(
17239
+ currentAgg.aggregationType,
17240
+ databaseType,
17241
+ valueFieldAlias
17242
+ );
17243
+ if (currentAgg.aggregationType === "percentage") {
17244
+ if (!currentAgg.valueField)
17245
+ throw new Error("No value field provided for pivot");
17246
+ const valueField2Alias = processColumnReference(
17247
+ currentAgg.valueField2 ?? currentAgg.valueField,
17248
+ databaseType,
17249
+ void 0,
17250
+ false,
17251
+ true
17252
+ );
17253
+ const value2Expr = (currentAgg.valueField2Type ?? currentAgg.valueFieldType) === "bool" ? `CASE WHEN ${valueField2Alias} THEN 1 ELSE 0 END` : valueField2Alias;
17254
+ if (currentAgg.valueField === currentAgg.valueField2 || !currentAgg.valueField2) {
17255
+ caseWhens = [
17256
+ ...caseWhens,
17257
+ ...columnFieldValues.map((column) => {
17258
+ return `CAST(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17259
+ column,
17260
+ databaseType
17261
+ )}' THEN ${valueExpr} END) AS FLOAT) / GREATEST(sum(${value2Expr}), 1) AS ${processColumnReference(
17262
+ column + disambiguation,
17263
+ databaseType,
17264
+ "_",
17265
+ true
17266
+ )}`;
17267
+ })
17268
+ ];
17269
+ } else {
17270
+ value2AliasSubstring = `${processColumnReference(
17271
+ currentAgg.valueField2 ?? currentAgg.valueField,
17272
+ databaseType,
17273
+ void 0,
17274
+ true
17275
+ )} AS ${valueField2Alias}`;
17276
+ caseWhens = [
17277
+ ...caseWhens,
17278
+ ...columnFieldValues.map((column) => {
17279
+ return `CAST(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17280
+ column,
17281
+ databaseType
17282
+ )}' THEN ${valueExpr} END) AS FLOAT) / GREATEST(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17283
+ column,
17284
+ databaseType
17285
+ )}' THEN ${value2Expr} END), 1) AS ${processColumnReference(
17286
+ column + disambiguation,
17287
+ databaseType,
17288
+ "_",
17289
+ true
17290
+ )}`;
17291
+ })
17292
+ ];
17293
+ }
17294
+ } else {
17295
+ caseWhens = [
17296
+ ...caseWhens,
17297
+ ...columnFieldValues.map((column) => {
17298
+ return `${processAggType(currentAgg.aggregationType, true)}(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17299
+ column,
17300
+ databaseType
17301
+ )}' THEN ${valueExpr} END) AS ${processColumnReference(column + disambiguation, databaseType, "_", true)}`;
17302
+ })
17303
+ ];
17304
+ }
17305
+ if (valueAliasSubstring) valueAliases.push(valueAliasSubstring);
17306
+ if (value2AliasSubstring) valueAliases.push(value2AliasSubstring);
17307
+ });
17308
+ valueAliases = [
17309
+ `${processColumnReference(rowField, databaseType, void 0, true)} AS ${rowFieldAlias}`,
17310
+ ...valueAliases,
17311
+ `${processColumnReference(columnField, databaseType, void 0, true)} AS ${columnFieldAlias}`
17312
+ ];
17313
+ valueAliases = Array.from(new Set(valueAliases));
17314
+ const sortQuery = pivot.sort && pivot.sortField && pivot.rowLimit ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType, void 0, true)} ${pivot.sortDirection || ""} ` : "";
17315
+ const pivotQuery = `
17316
+ ,quill_alias AS (
17317
+ SELECT ${valueAliases.length > 0 ? `${valueAliases.join(", ")}` : ""}
17318
+ FROM quill_base_table
17319
+ ),
17320
+ quill_qt_cw AS (
17321
+ SELECT ${rowFieldAlias}
17322
+ ${caseWhens.length > 0 ? `, ${caseWhens.join(", ")}` : ""}
17323
+ FROM quill_alias
17324
+ GROUP BY ${rowFieldAlias}
17325
+ ),
17326
+ quill_base_pivot AS (
17327
+ SELECT ${pivot.rowLimit && databaseType.toLowerCase() === "mssql" ? `TOP ${pivot.rowLimit}` : ""} *
17328
+ FROM quill_qt_cw qt
17329
+ ${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== "mssql" ? ` LIMIT ${pivot.rowLimit}` : ""}
17330
+ )
17331
+ SELECT * FROM quill_base_pivot
17332
+ `.replace(/\s+/g, " ").trim();
17333
+ return itemQuery.replace(
17334
+ /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/,
17335
+ pivotQuery
17336
+ );
17337
+ }
17338
+ function create2DDatePivotQuery(pivot, itemQuery, columnFieldValues, databaseType, dateBucket = "month") {
17339
+ const isValidBaseQuery = itemQuery.match(
17340
+ /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/
17341
+ );
17342
+ if (!isValidBaseQuery || !pivot.columnField || !pivot.rowField) {
17343
+ return void 0;
17344
+ }
17345
+ if (!pivot.aggregations?.[0]?.valueField && pivot.aggregations?.[0]?.aggregationType !== "count" && !pivot.valueField && pivot.aggregationType !== "count")
17346
+ throw new Error("No value field provided for pivot");
17347
+ if (!pivot.aggregations?.[0]?.aggregationType && !pivot.aggregationType)
17348
+ throw new Error("No aggregation type provided for pivot");
17349
+ const rowField = pivot.rowField;
17350
+ const columnField = pivot.columnField;
17351
+ const rowFieldAlias = processColumnReference(
17352
+ rowField,
17353
+ databaseType,
17354
+ void 0,
17355
+ false,
17356
+ true
17357
+ );
17358
+ const columnFieldAlias = processColumnReference(
17359
+ columnField,
17360
+ databaseType,
17361
+ void 0,
17362
+ false,
17363
+ true
17364
+ );
17365
+ let caseWhens = [];
17366
+ let valueFieldAliases = [];
17367
+ const seenAggs = {};
17368
+ pivot.aggregations?.forEach((currentAgg) => {
17369
+ if (seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]) {
17370
+ seenAggs[currentAgg.aggregationType ?? ""][currentAgg.valueField ?? ""] += 1;
17371
+ } else {
17372
+ seenAggs[currentAgg.aggregationType ?? ""] = {
17373
+ ...seenAggs[currentAgg.aggregationType ?? ""],
17374
+ [currentAgg.valueField ?? ""]: 1
17375
+ };
17376
+ }
17377
+ let disambiguationIndex = seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]?.toString();
17378
+ if (disambiguationIndex === "1") disambiguationIndex = "";
17379
+ const valueFieldAlias = processColumnReference(
17380
+ currentAgg.valueField ?? rowField,
17381
+ databaseType,
17382
+ void 0,
17383
+ false,
17384
+ true
17385
+ );
17386
+ const valueAliasSubstring = currentAgg.valueField ? `${processColumnReference(currentAgg.valueField, databaseType, void 0, true)} AS ${valueFieldAlias}` : "";
17387
+ let value2AliasSubstring = "";
17388
+ const disambiguationField = Object.values(seenAggs[currentAgg.aggregationType ?? ""] ?? {}).reduce(
17389
+ (acc, v) => acc + v
17390
+ ) > 1 ? `_${currentAgg.valueField}${disambiguationIndex}` : "";
17391
+ const disambiguation = pivot.aggregations?.length > 1 ? `${disambiguationField}_${disambiguationField ? matchCasing(currentAgg.aggregationType, currentAgg.valueField) : currentAgg.aggregationType}` : "";
17392
+ const valueExpr = currentAgg.valueFieldType === "bool" ? `CASE WHEN ${valueFieldAlias} THEN 1 ELSE 0 END` : processValueField(
17393
+ currentAgg.aggregationType,
17394
+ databaseType,
17395
+ valueFieldAlias
17396
+ );
17397
+ if (currentAgg.aggregationType === "percentage") {
17398
+ if (!currentAgg.valueField)
17399
+ throw new Error("No value field provided for pivot");
17400
+ const valueField2Alias = processColumnReference(
17401
+ currentAgg.valueField2 ?? currentAgg.valueField,
17402
+ databaseType,
17403
+ void 0,
17404
+ false,
17405
+ true
17406
+ );
17407
+ const value2Expr = (currentAgg.valueField2Type ?? currentAgg.valueFieldType) === "bool" ? `CASE WHEN ${valueField2Alias} THEN 1 ELSE 0 END` : valueField2Alias;
17408
+ if (currentAgg.valueField === currentAgg.valueField2 || !currentAgg.valueField2) {
17409
+ caseWhens = [
17410
+ ...caseWhens,
17411
+ ...columnFieldValues.map((column) => {
17412
+ return `CAST(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17413
+ column,
17414
+ databaseType
17415
+ )}' THEN ${valueExpr} END) AS FLOAT) / GREATEST(sum(${value2Expr}), 1) AS ${processColumnReference(
17416
+ column + disambiguation,
17417
+ databaseType,
17418
+ "_",
17419
+ true
17420
+ )}`;
17421
+ })
17422
+ ];
17423
+ } else {
17424
+ value2AliasSubstring = `${processColumnReference(
17425
+ currentAgg.valueField2 ?? currentAgg.valueField,
17426
+ databaseType,
17427
+ void 0,
17428
+ true
17429
+ )} AS ${valueField2Alias}`;
17430
+ caseWhens = [
17431
+ ...caseWhens,
17432
+ ...columnFieldValues.map((column) => {
17433
+ return `CAST(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17434
+ column,
17435
+ databaseType
17436
+ )}' THEN ${valueExpr} END) AS FLOAT) / GREATEST(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17437
+ column,
17438
+ databaseType
17439
+ )}' THEN ${value2Expr} END), 1) AS ${processColumnReference(
17440
+ column + disambiguation,
17441
+ databaseType,
17442
+ "_",
17443
+ true
17444
+ )}`;
17445
+ })
17446
+ ];
17447
+ }
17448
+ } else {
17449
+ caseWhens = [
17450
+ ...caseWhens,
17451
+ ...columnFieldValues.map((column) => {
17452
+ return `${processAggType(currentAgg.aggregationType, true)}(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17453
+ column,
17454
+ databaseType
17455
+ )}' THEN ${valueExpr} END) AS ${processColumnReference(column + disambiguation, databaseType, "_", true)}`;
17456
+ })
17457
+ ];
17458
+ }
17459
+ if (valueAliasSubstring) valueFieldAliases.push(valueAliasSubstring);
17460
+ if (value2AliasSubstring) valueFieldAliases.push(value2AliasSubstring);
17461
+ });
17462
+ valueFieldAliases = [
17463
+ `${processColumnReference(rowField, databaseType, void 0, true)} AS ${rowFieldAlias}`,
17464
+ ...valueFieldAliases,
17465
+ `${processColumnReference(columnField, databaseType, void 0, true)} AS ${columnFieldAlias}`
17466
+ ];
17467
+ valueFieldAliases = Array.from(new Set(valueFieldAliases));
17468
+ const sortQuery = `${pivot.sort && pivot.sortField && pivot.rowLimit ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType, void 0, true)} ${pivot.sortDirection || ""} ` : ""}`;
17469
+ const pivotQuery = `
17470
+ , quill_alias AS (SELECT ${valueFieldAliases.length > 0 ? `${valueFieldAliases.join(", ")}` : ""} FROM quill_base_table),
17471
+ 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)}),
17472
+ quill_base_pivot AS (SELECT ${pivot.rowLimit && databaseType.toLowerCase() === "mssql" ? `TOP ${pivot.rowLimit}` : ""} * FROM quill_qt_agg qt
17473
+ ${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== "mssql" ? ` LIMIT ${pivot.rowLimit}` : ""})
17474
+ SELECT * FROM quill_base_pivot
17475
+ `.replace(/\s+/g, " ").trim();
17476
+ return itemQuery.replace(
17477
+ /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/,
17478
+ pivotQuery
17479
+ );
17480
+ }
17481
+ function create1DPivotQuery(pivot, itemQuery, dateBucket = "month", databaseType) {
17482
+ if (isStringType(pivot.rowFieldType || "") || !pivot.rowFieldType) {
17483
+ return create1DStringPivotQuery(pivot, itemQuery, databaseType);
17484
+ }
17485
+ return create1DDatePivotQuery(pivot, itemQuery, dateBucket, databaseType);
17486
+ }
17487
+ function create1DStringPivotQuery(pivot, itemQuery, databaseType) {
17488
+ const isValidBaseQuery = itemQuery.match(
17489
+ /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/
17490
+ );
17491
+ if (!isValidBaseQuery) return void 0;
17492
+ const rowField = pivot.rowField;
17493
+ const rowAlias = processColumnReference(
17494
+ rowField,
17495
+ databaseType,
17496
+ void 0,
17497
+ true
17498
+ );
17499
+ let quillAggSelects = [rowAlias];
17500
+ let valueFieldAliases = [];
17501
+ const seenAggs = {};
17502
+ pivot.aggregations?.forEach((currentAgg) => {
17503
+ if (!currentAgg.valueField) currentAgg.valueField = void 0;
17504
+ if (seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]) {
17505
+ seenAggs[currentAgg.aggregationType ?? ""][currentAgg.valueField ?? ""] += 1;
17506
+ } else {
17507
+ seenAggs[currentAgg.aggregationType ?? ""] = {
17508
+ ...seenAggs[currentAgg.aggregationType ?? ""],
17509
+ [currentAgg.valueField ?? ""]: 1
17510
+ };
17511
+ }
17512
+ let disambiguationIndex = seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]?.toString() ?? "";
17513
+ if (disambiguationIndex === "1") disambiguationIndex = "";
17514
+ const valueFieldAlias = processColumnReference(
17515
+ currentAgg.valueField || rowField || "count",
17516
+ databaseType,
17517
+ void 0,
17518
+ false,
17519
+ true
17520
+ );
17521
+ const valueAliasSubstring = currentAgg.valueField ? `${processColumnReference(currentAgg.valueField, databaseType, void 0, true)} AS ${valueFieldAlias}` : "";
17522
+ let value2AliasSubstring = "";
17523
+ const disambiguation = pivot.aggregations?.length > 1 ? `${disambiguationIndex}` + (currentAgg.aggregationType !== "count" ? `_${currentAgg.aggregationType}` : "") : "";
17524
+ let valueExpr = !currentAgg.valueField ? "*" : valueFieldAlias;
17525
+ if (currentAgg.valueFieldType === "bool") {
17526
+ valueExpr = `CASE WHEN ${valueFieldAlias} THEN 1 ELSE 0 END`;
17527
+ }
17528
+ if (currentAgg.aggregationType === "percentage") {
17529
+ let countPercentage = false;
17530
+ if (!currentAgg.valueField) {
17531
+ countPercentage = true;
17532
+ }
17533
+ const valueField2Alias = processColumnReference(
17534
+ currentAgg.valueField2 ?? currentAgg.valueField ?? "count",
17535
+ databaseType,
17536
+ void 0,
17537
+ false,
17538
+ true
17539
+ );
17540
+ let value2Expr = valueField2Alias;
17541
+ if ((currentAgg.valueField2Type ?? currentAgg.valueFieldType) === "bool") {
17542
+ value2Expr = `CASE WHEN ${valueField2Alias} THEN 1 ELSE 0 END`;
17543
+ }
17544
+ value2AliasSubstring = currentAgg.valueField2 && currentAgg.valueField !== currentAgg.valueField2 ? `${processColumnReference(currentAgg.valueField2, databaseType, void 0, true)} AS ${valueField2Alias}` : "";
17545
+ 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)`;
17546
+ quillAggSelects = [
17547
+ ...quillAggSelects,
17548
+ `${percentageExpr} as ${processColumnReference(
17549
+ `${currentAgg.valueField ?? (disambiguation ? "count" : "percentage")}${disambiguation}`,
17550
+ databaseType,
17551
+ void 0,
17552
+ false,
17553
+ true
17554
+ )}`
17555
+ ];
17556
+ } else {
17557
+ quillAggSelects = [
17558
+ ...quillAggSelects,
17559
+ `${processAggType(currentAgg.aggregationType)}(${valueExpr}) AS ${processColumnReference(
17560
+ (currentAgg.valueField || "count") + disambiguation,
17561
+ databaseType
17562
+ )}`
17563
+ ];
17564
+ }
17565
+ if (valueAliasSubstring) valueFieldAliases.push(valueAliasSubstring);
17566
+ if (value2AliasSubstring) valueFieldAliases.push(value2AliasSubstring);
17567
+ });
17568
+ valueFieldAliases = Array.from(new Set(valueFieldAliases));
17569
+ const sortQuery = `${pivot.sort && pivot.sortField && pivot.rowLimit ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType, void 0, true)} ${pivot.sortDirection || ""} ` : ""}`;
17570
+ const pivotQuery = `, quill_alias AS (
17571
+ SELECT ${processColumnReference(`${rowField}`, databaseType, void 0, true)} AS ${rowAlias}${valueFieldAliases.length > 0 ? `, ${valueFieldAliases.join(", ")}` : ""}
17572
+ FROM quill_base_table
17573
+ ),
17574
+ quill_qt_cw AS (
17575
+ SELECT ${quillAggSelects.join(", ")} FROM quill_alias GROUP BY ${rowAlias}
17576
+ ),
17577
+ quill_base_pivot AS (
17578
+ SELECT ${pivot.rowLimit && databaseType.toLowerCase() === "mssql" ? `TOP ${pivot.rowLimit}` : ""} *
17579
+ FROM quill_qt_cw qt ${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== "mssql" ? ` LIMIT ${pivot.rowLimit}` : ""}
17580
+ )
17581
+ SELECT * FROM quill_base_pivot`.replace(/\s+/g, " ").trim();
17582
+ return itemQuery.replace(
17583
+ /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/,
17584
+ pivotQuery
17585
+ );
17586
+ }
17587
+ function create1DDatePivotQuery(pivot, itemQuery, dateBucket = "month", databaseType) {
17588
+ const isValidBaseQuery = itemQuery.match(
17589
+ /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/
17590
+ );
17591
+ if (!isValidBaseQuery) {
17592
+ return void 0;
17593
+ }
17594
+ const rowField = pivot.rowField || "";
17595
+ const rowFieldAlias = processColumnReference(
17596
+ rowField,
17597
+ databaseType,
17598
+ void 0
17599
+ );
17600
+ let quillAggSelects = [
17601
+ `${processDateTrunc(dateBucket, rowFieldAlias, databaseType)} as ${processColumnReference(rowField, databaseType)}`
17602
+ ];
17603
+ let valueFieldAliases = [];
17604
+ const seenAggs = {};
17605
+ pivot.aggregations?.forEach((currentAgg) => {
17606
+ if (!currentAgg.valueField) currentAgg.valueField = void 0;
17607
+ if (seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]) {
17608
+ seenAggs[currentAgg.aggregationType ?? ""][currentAgg.valueField ?? ""] += 1;
17609
+ } else {
17610
+ seenAggs[currentAgg.aggregationType ?? ""] = {
17611
+ ...seenAggs[currentAgg.aggregationType ?? ""],
17612
+ [currentAgg.valueField ?? ""]: 1
17613
+ };
17614
+ }
17615
+ let disambiguationIndex = seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]?.toString() ?? "";
17616
+ if (disambiguationIndex === "1") disambiguationIndex = "";
17617
+ const valueFieldAlias = processColumnReference(
17618
+ currentAgg.valueField || rowField || "count",
17619
+ databaseType,
17620
+ void 0,
17621
+ false,
17622
+ true
17623
+ );
17624
+ const valueAliasSubstring = currentAgg.valueField ? `${processColumnReference(currentAgg.valueField, databaseType, void 0, true)} AS ${valueFieldAlias}` : "";
17625
+ let value2AliasSubstring = "";
17626
+ const disambiguation = pivot.aggregations?.length > 1 ? `${disambiguationIndex}` + (currentAgg.aggregationType !== "count" ? `_${currentAgg.aggregationType}` : "") : "";
17627
+ let valueExpr = !currentAgg.valueField ? "*" : valueFieldAlias;
17628
+ if (currentAgg.valueFieldType === "bool") {
17629
+ valueExpr = `CASE WHEN ${valueFieldAlias} THEN 1 ELSE 0 END`;
17630
+ }
17631
+ if (currentAgg.aggregationType === "percentage") {
17632
+ let countPercentage = false;
17633
+ if (!currentAgg.valueField) {
17634
+ countPercentage = true;
17635
+ }
17636
+ const valueField2Alias = processColumnReference(
17637
+ currentAgg.valueField2 ?? currentAgg.valueField ?? "count",
17638
+ databaseType,
17639
+ void 0,
17640
+ false,
17641
+ true
17642
+ );
17643
+ value2AliasSubstring = currentAgg.valueField2 && currentAgg.valueField !== currentAgg.valueField2 ? `${processColumnReference(currentAgg.valueField2, databaseType, void 0, true)} AS ${valueField2Alias}` : "";
17644
+ let value2Expr = valueField2Alias;
17645
+ if ((currentAgg.valueField2Type ?? currentAgg.valueFieldType) === "bool") {
17646
+ value2Expr = `CASE WHEN ${valueField2Alias} THEN 1 ELSE 0 END`;
17647
+ }
17648
+ 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)`;
17649
+ quillAggSelects = [
17650
+ ...quillAggSelects,
17651
+ `${percentageExpr} as ${processColumnReference(
17652
+ `${currentAgg.valueField ?? (disambiguation ? "count" : "percentage")}${disambiguation}`,
17653
+ databaseType,
17654
+ void 0,
17655
+ false,
17656
+ true
17657
+ )}`
17658
+ ];
17659
+ } else {
17660
+ quillAggSelects = [
17661
+ ...quillAggSelects,
17662
+ `${processAggType(currentAgg.aggregationType)}(${valueExpr}) AS ${processColumnReference((currentAgg.valueField || "count") + disambiguation, databaseType)}`
17663
+ ];
17664
+ }
17665
+ if (valueAliasSubstring) valueFieldAliases.push(valueAliasSubstring);
17666
+ if (value2AliasSubstring) valueFieldAliases.push(value2AliasSubstring);
17667
+ });
17668
+ valueFieldAliases = Array.from(new Set(valueFieldAliases));
17669
+ const sortQuery = `${pivot.sort && pivot.sortField && pivot.rowLimit ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType, void 0, true)} ${pivot.sortDirection || ""} ` : ""}`;
17670
+ const pivotQuery = `, quill_alias AS (SELECT ${processColumnReference(`${rowField}`, databaseType, void 0, true)} AS ${rowFieldAlias}${valueFieldAliases.length > 0 ? `, ${valueFieldAliases.join(", ")}` : ""}
17671
+ FROM quill_base_table),
17672
+ quill_qt_agg AS (SELECT ${quillAggSelects.join(", ")}
17673
+ FROM quill_alias GROUP BY ${databaseType.toLowerCase() === "clickhouse" ? processColumnReference(`${rowField}`, databaseType) : processDateTrunc(dateBucket, rowFieldAlias, databaseType)}),
17674
+ quill_base_pivot AS (SELECT ${pivot.rowLimit && databaseType.toLowerCase() === "mssql" ? `TOP ${pivot.rowLimit}` : ""} * FROM quill_qt_agg qt
17675
+ ${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== "mssql" ? ` LIMIT ${pivot.rowLimit}` : ""})
17676
+ SELECT * FROM quill_base_pivot
17677
+ `.replace(/\s+/g, " ").trim();
17678
+ return itemQuery.replace(
17679
+ /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/,
17680
+ pivotQuery
17681
+ );
17682
+ }
17683
+ function createAggregationValuePivot(pivot, itemQuery, databaseType) {
17684
+ const isValidBaseQuery = itemQuery.match(
17685
+ /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/
17686
+ );
17687
+ if (!isValidBaseQuery) return void 0;
17688
+ let quillAggSelects = [];
17689
+ let valueFieldAliases = [];
17690
+ const seenAggs = {};
17691
+ pivot.aggregations?.forEach((currentAgg) => {
17692
+ if (!currentAgg.valueField) currentAgg.valueField = void 0;
17693
+ if (seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]) {
17694
+ seenAggs[currentAgg.aggregationType ?? ""][currentAgg.valueField ?? ""] += 1;
17695
+ } else {
17696
+ seenAggs[currentAgg.aggregationType ?? ""] = {
17697
+ ...seenAggs[currentAgg.aggregationType ?? ""],
17698
+ [currentAgg.valueField ?? ""]: 1
17699
+ };
17700
+ }
17701
+ let disambiguationIndex = seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]?.toString() ?? "";
17702
+ if (disambiguationIndex === "1") disambiguationIndex = "";
17703
+ const valueFieldAlias = processColumnReference(
17704
+ currentAgg.valueField || "count",
17705
+ databaseType,
17706
+ void 0,
17707
+ false,
17708
+ true
17709
+ );
17710
+ const valueAliasSubstring = currentAgg.valueField ? `${processColumnReference(currentAgg.valueField, databaseType, void 0, true)} AS ${valueFieldAlias}` : "";
17711
+ let value2AliasSubstring = "";
17712
+ const disambiguation = pivot.aggregations?.length > 1 ? `${disambiguationIndex}_${currentAgg.aggregationType}` : "";
17713
+ let valueExpr = !currentAgg.valueField ? "*" : valueFieldAlias;
17714
+ valueExpr = currentAgg.valueFieldType === "bool" ? `CASE WHEN ${valueFieldAlias} THEN 1 ELSE 0 END` : valueExpr;
17715
+ if (currentAgg.aggregationType === "percentage") {
17716
+ if (!currentAgg.valueField) {
17717
+ throw new Error("No value field provided for percentage aggregation");
17718
+ }
17719
+ const valueField2Alias = processColumnReference(
17720
+ currentAgg.valueField2 ?? currentAgg.valueField,
17721
+ databaseType,
17722
+ void 0,
17723
+ false,
17724
+ true
17725
+ );
17726
+ const value2Expr = (currentAgg.valueField2Type ?? currentAgg.valueFieldType) === "bool" ? `CASE WHEN ${valueField2Alias} THEN 1 ELSE 0 END` : valueField2Alias;
17727
+ value2AliasSubstring = currentAgg.valueField2 && currentAgg.valueField !== currentAgg.valueField2 ? `${processColumnReference(currentAgg.valueField2, databaseType, void 0, true)} AS ${valueField2Alias}` : "";
17728
+ 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)`;
17729
+ quillAggSelects = [
17730
+ ...quillAggSelects,
17731
+ `${percentageExpr} as ${processColumnReference(
17732
+ `${currentAgg.valueField ?? ""}${disambiguation}`,
17733
+ databaseType,
17734
+ void 0,
17735
+ false,
17736
+ true
17737
+ )}`
17738
+ ];
17739
+ } else {
17740
+ quillAggSelects = [
17741
+ ...quillAggSelects,
17742
+ `${processAggType(currentAgg.aggregationType)}(${valueExpr}) AS ${processColumnReference(
17743
+ (currentAgg.valueField || "count") + disambiguation,
17744
+ databaseType
17745
+ )}`
17746
+ ];
17747
+ }
17748
+ if (valueAliasSubstring) valueFieldAliases.push(valueAliasSubstring);
17749
+ if (value2AliasSubstring) valueFieldAliases.push(value2AliasSubstring);
17750
+ });
17751
+ valueFieldAliases = Array.from(new Set(valueFieldAliases));
17752
+ if (valueFieldAliases.length === 0) {
17753
+ valueFieldAliases = ["*"];
17754
+ }
17755
+ const sortQuery = pivot.sort && pivot.sortField && pivot.rowLimit ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType, void 0, true)} ${pivot.sortDirection || ""} ` : "";
17756
+ const pivotQuery = `, quill_alias AS (
17757
+ SELECT ${valueFieldAliases.join(", ")} FROM quill_base_table
17758
+ ),
17759
+ quill_qt_agg AS (
17760
+ SELECT ${quillAggSelects.join(", ")} FROM quill_alias
17761
+ ),
17762
+ quill_base_pivot AS (
17763
+ SELECT ${pivot.rowLimit && databaseType.toLowerCase() === "mssql" ? `TOP ${pivot.rowLimit}` : ""} * FROM quill_qt_agg qt
17764
+ ${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== "mssql" ? ` LIMIT ${pivot.rowLimit}` : ""}
17765
+ )
17766
+ SELECT * FROM quill_base_pivot`.replace(/\s+/g, " ").trim();
17767
+ return itemQuery.replace(
17768
+ /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/,
17769
+ pivotQuery
17770
+ );
17771
+ }
17772
+ function additionalProcessingOnPivotQuery(pivot, query, additionalProcessing, databaseType = "postgresql") {
17773
+ if (!additionalProcessing || !query) return query;
17774
+ const isValidBaseQuery = query.match(
17775
+ /SELECT \* FROM\s+["'[`]?quill_base_pivot["'\]`]?\s*$/
17776
+ );
17777
+ if (!isValidBaseQuery) {
17778
+ return void 0;
17779
+ }
17780
+ if (!pivot.aggregations || pivot.aggregations.length === 0) {
17781
+ if (pivot.aggregationType) {
17782
+ pivot.aggregations = [
17783
+ {
17784
+ aggregationType: pivot.aggregationType,
17785
+ valueField: pivot.valueField,
17786
+ valueField2: pivot.valueField2
17787
+ }
17788
+ ];
17789
+ } else {
17790
+ throw new Error("No aggregations provided for pivot");
17791
+ }
17792
+ }
17793
+ let rowsPerPage = 0;
17794
+ let currentInterval = 0;
17795
+ let offset = 0;
17796
+ let limit = 1e3;
17797
+ let sortQuery = "";
17798
+ if (additionalProcessing.page) {
17799
+ const page = additionalProcessing.page.page || 0;
17800
+ if (additionalProcessing.page.rowsPerRequest) {
17801
+ limit = additionalProcessing.page.rowsPerRequest;
17802
+ }
17803
+ rowsPerPage = additionalProcessing.page.rowsPerPage || 0;
17804
+ currentInterval = page ? Math.floor(page / (limit / rowsPerPage)) : 0;
17805
+ offset = currentInterval * limit;
17806
+ }
17807
+ const disambiguation = pivot.aggregations.length > 1 ? `_${matchCasing(pivot.aggregations?.[0]?.aggregationType, pivot.aggregations?.[0]?.valueField)}` : "";
17808
+ if (additionalProcessing.sort) {
17809
+ sortQuery = `ORDER BY ${processColumnReference(additionalProcessing.sort.field, databaseType, void 0, true)} ${additionalProcessing.sort.direction || ""}`;
17810
+ } else {
17811
+ const valueFieldAlias = processColumnReference(
17812
+ (pivot.aggregations?.[0]?.valueField ?? "") + disambiguation,
17813
+ databaseType,
17814
+ void 0,
17815
+ false,
17816
+ true
17817
+ );
17818
+ const defaultSortField = pivot.sortField || pivot.rowField || valueFieldAlias;
17819
+ const defaultSortDirection = pivot.sortDirection || "";
17820
+ if (defaultSortField !== `"_"`) {
17821
+ sortQuery = `ORDER BY ${processColumnReference(defaultSortField, databaseType, void 0, true)} ${defaultSortDirection}`;
17822
+ } else {
17823
+ sortQuery = "";
17824
+ }
17825
+ }
17826
+ const additionalProcessingQuery = `
17827
+ SELECT *
17828
+ FROM quill_base_pivot ${sortQuery}${databaseType.toLowerCase() === "mssql" ? ` OFFSET ${offset} ROWS FETCH NEXT ${limit} ROWS ONLY` : ` LIMIT ${limit} OFFSET ${offset}`}
17829
+ `.replace(/\s+/g, " ").trim();
17830
+ return query.replace(
17831
+ /SELECT \* FROM\s+["'[`]?quill_base_pivot["'\]`]?\s*$/,
17832
+ additionalProcessingQuery
17833
+ );
17834
+ }
17835
+ function generateRowCountQuery(query, databaseType) {
17836
+ if (!query) return query;
17837
+ const isValidBaseQuery = query.match(
17838
+ /SELECT \* FROM\s+["'[`]?quill_base_pivot["'\]`]?\s*$/
17839
+ );
17840
+ if (!isValidBaseQuery) {
17841
+ return void 0;
17842
+ }
17843
+ const rowCountQuery = `, subview_row_count_cte AS (SELECT * FROM quill_base_pivot)
17844
+ SELECT count(*) as ${processColumnReference("row_count", databaseType || "postgresql", void 0, true)} FROM subview_row_count_cte
17845
+ `.replace(/\s+/g, " ").trim();
17846
+ return query.replace(
17847
+ /SELECT \* FROM\s+["'[`]?quill_base_pivot["'\]`]?\s*$/,
17848
+ rowCountQuery
17849
+ );
17850
+ }
17851
+
17852
+ // src/utils/pivotConstructor.ts
17853
+ async function generatePivotWithSQL({
17854
+ pivot,
17855
+ report,
17856
+ client,
17857
+ dateBucket,
17858
+ dateFilter,
17859
+ distinctStrings,
17860
+ dashboardName,
17861
+ tenants,
17862
+ additionalProcessing,
17863
+ pivotQuery,
17864
+ comparisonPivotQuery,
17865
+ getPivotRowCount = true,
17866
+ caller,
17867
+ getToken
17868
+ }) {
17869
+ let sqlQuery = pivotQuery;
17870
+ let comparisonPivotSql = comparisonPivotQuery;
17871
+ const databaseType = client.databaseType || "postgresql";
17872
+ if (!pivotQuery && pivot.columnField && !pivot.columnValues && !distinctStrings) {
17873
+ if (!report?.rows) {
17874
+ throw new Error("No distinct strings provided for column field");
17875
+ } else {
17876
+ distinctStrings = Array.from(
17877
+ new Set(
17878
+ report.rows.map((row) => row[pivot.columnField]).filter((value) => value !== null && value !== void 0)
17879
+ )
17880
+ );
17881
+ }
17882
+ }
17883
+ if (!pivot.aggregations?.length && pivot.aggregationType) {
17884
+ pivot.aggregations = [
17885
+ {
17886
+ aggregationType: pivot.aggregationType,
17887
+ valueField: pivot.valueField,
17888
+ valueFieldType: pivot.valueFieldType,
17889
+ valueField2: pivot.valueField2,
17890
+ valueField2Type: pivot.valueField2Type
17891
+ }
17892
+ ];
17893
+ }
17894
+ let comparisonInterval = void 0;
17895
+ if (dateFilter && dateFilter.comparisonRange) {
17896
+ comparisonInterval = getComparisonInterval(
17897
+ {
17898
+ startDate: dateFilter.comparisonRange.startDate,
17899
+ endDate: dateFilter.comparisonRange.endDate
17900
+ },
17901
+ dateBucket
17902
+ );
17903
+ }
17904
+ if (pivot.rowField && !pivot.rowFieldType) {
17905
+ const rowColumn = report?.columns.find(
17906
+ (column) => column.field === pivot.rowField
17907
+ );
17908
+ pivot.rowFieldType = rowColumn?.format || "string";
17909
+ }
17910
+ const filteredDistinctStrings = distinctStrings?.filter(
17911
+ (value) => value !== null && value !== void 0 && value !== ""
17912
+ );
17913
+ const pivotColumnFields = filteredDistinctStrings?.slice(
17914
+ 0,
17915
+ MAX_PIVOT_UNIQUE_VALUES
17916
+ );
17917
+ if (!pivotQuery && report) {
17918
+ if (!report.itemQuery) {
17919
+ throw Error("No item query found in report");
17920
+ }
17921
+ const itemQuery = report.itemQuery[0];
17922
+ const comparisonQuery = report.itemQuery[1];
17923
+ if (!itemQuery) {
17924
+ throw Error("No item query found in report");
17925
+ }
17926
+ sqlQuery = generatePivotQuery(
17927
+ pivot,
17928
+ itemQuery,
17929
+ databaseType,
17930
+ pivotColumnFields,
17931
+ dateBucket
17932
+ );
17933
+ comparisonPivotSql = comparisonQuery ? generatePivotQuery(
17934
+ pivot,
17935
+ comparisonQuery,
17936
+ databaseType,
17937
+ pivotColumnFields,
17938
+ dateBucket
17939
+ ) : "";
17940
+ }
17941
+ if (!sqlQuery) {
17942
+ throw "Error generating pivot query";
17943
+ }
17944
+ const paginatedSqlQuery = additionalProcessingOnPivotQuery(
17945
+ pivot,
17946
+ sqlQuery,
17947
+ additionalProcessing,
17948
+ client.databaseType
17949
+ );
17950
+ const paginatedComparisonQuery = comparisonPivotSql ? additionalProcessingOnPivotQuery(
17951
+ pivot,
17952
+ comparisonPivotSql,
17953
+ additionalProcessing,
17954
+ client.databaseType
17955
+ ) : "";
17956
+ const preQueries = [paginatedSqlQuery];
17957
+ getPivotRowCount = getPivotRowCount && (report?.chartType === "table" || caller === "ReportBuilder");
17958
+ if (getPivotRowCount) {
17959
+ const pivotRowCountQuery = generateRowCountQuery(
17960
+ sqlQuery,
17961
+ client.databaseType
17962
+ );
17963
+ preQueries.push(pivotRowCountQuery);
17964
+ }
17965
+ if (paginatedComparisonQuery) {
17966
+ preQueries.push(paginatedComparisonQuery);
17967
+ }
17968
+ const { data: resp } = await quillFetch({
17969
+ client,
17970
+ task: "query",
17971
+ metadata: {
17972
+ preQueries,
17973
+ clientId: client.publicKey,
17974
+ databaseType,
17975
+ runQueryConfig: {
17976
+ overridePost: true,
17977
+ convertDatatypes: true
17978
+ },
17979
+ useNewNodeSql: true,
17980
+ dashboardName,
17981
+ tenants
17982
+ },
17983
+ urlParameters: `caller=generatePivot&task=query`,
17984
+ credentials: "same-origin",
17985
+ getToken
17986
+ });
17987
+ if (resp.success === false) {
17988
+ throw resp.errorMessage;
17989
+ }
17990
+ const queryResponseRows = resp?.queryResults?.[0]?.rows || [];
17991
+ const queryResponseFields = resp?.queryResults?.[0]?.fields || [];
17992
+ const queryComparisonResponseRows = (getPivotRowCount ? resp?.queryResults?.[2]?.rows : resp?.queryResults?.[1]?.rows) || [];
17993
+ const queryComparisonResponseFields = (getPivotRowCount ? resp?.queryResults?.[2]?.fields : resp?.queryResults?.[1]?.fields) || [];
17994
+ parseValueFromBigQueryDates(queryResponseRows, queryResponseFields);
17995
+ parseValueFromBigQueryDates(
17996
+ queryComparisonResponseRows,
17997
+ queryComparisonResponseFields
17998
+ );
17999
+ const responseRows = mergeComparisonPivotRows({
18000
+ pivot,
18001
+ rows: queryResponseRows,
18002
+ compRows: queryComparisonResponseRows,
18003
+ databaseType,
18004
+ dateBucket,
18005
+ comparisonInterval,
18006
+ columnFieldValues: pivotColumnFields
18007
+ });
18008
+ const responseFields = mergeComparisonPivotColumns({
18009
+ pivot,
18010
+ rows: queryResponseFields,
18011
+ compRows: queryComparisonResponseFields
18012
+ });
18013
+ const rows = pivot.rowField ? responseRows.map(
18014
+ (row) => !row[pivot.rowField] ? { ...row, [pivot.rowField]: "-" } : row
18015
+ ) : responseRows;
18016
+ if (pivot.columnField && client.databaseType?.toLowerCase() === "bigquery") {
18017
+ rows.forEach((row) => {
18018
+ Object.keys(row).forEach((key) => {
18019
+ const processedKey = processColumnName(key);
18020
+ if (processedKey !== key) {
18021
+ row[processedKey] = row[key];
18022
+ delete row[key];
18023
+ }
18024
+ });
18025
+ });
18026
+ }
18027
+ const columns = responseFields?.map((field) => ({
18028
+ field: processColumnName(field.name),
18029
+ label: snakeCaseToTitleCase(
18030
+ processColumnName(field.name.replace("comparison_", "comparison "))
18031
+ ),
18032
+ format: field.name === pivot.rowField ? "string" : (
18033
+ // This scary equation is calculating which aggregation a column is associated with.
18034
+ // 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.
18035
+ // pivot.aggregations?.[
18036
+ // Math.floor(
18037
+ // (index - 1) /
18038
+ // Math.floor(
18039
+ // (responseFields.length - 1) /
18040
+ // (pivot.aggregations?.length ?? 1),
18041
+ // ),
18042
+ // )
18043
+ // ]?.aggregationType === 'percentage'
18044
+ pivot.aggregations?.find(
18045
+ (agg) => agg.valueField === field.name || `${agg.valueField}_percentage` === field.name || !agg.valueField && agg.aggregationType === "percentage" && field.name.endsWith("percentage")
18046
+ )?.aggregationType === "percentage" ? "percent" : "whole_number"
18047
+ ),
18048
+ fieldType: field.fieldType,
18049
+ jsType: field.jsType,
18050
+ dataTypeID: field.dataTypeID
18051
+ })).filter(
18052
+ (field, index) => field.field !== "comparison_" + pivot.rowField || index === 0
18053
+ ).sort((a, b) => {
18054
+ if (a.field === pivot.rowField) {
18055
+ return -1;
18056
+ }
18057
+ if (b.field === pivot.rowField) {
18058
+ return 1;
18059
+ }
18060
+ return 0;
18061
+ });
18062
+ if (pivot.rowField && !isStringType(pivot.rowFieldType || "")) {
18063
+ rows.forEach((row) => {
18064
+ row.__quillRawDate = typeof row[pivot.rowField || ""] === "object" ? row[pivot.rowField || ""].value : row[pivot.rowField || ""];
18065
+ let value = typeof row[pivot.rowField || ""] === "object" ? row[pivot.rowField || ""].value : row[pivot.rowField || ""];
18066
+ if (dateBucket === "week" && dateFilter?.startDate && dateFilter?.endDate) {
18067
+ const rowDate = new Date(value);
18068
+ if (rowDate < dateFilter.startDate) {
18069
+ value = dateFilter.startDate.toISOString();
18070
+ } else if (rowDate > dateFilter.endDate) {
18071
+ value = dateFilter.endDate.toISOString();
18072
+ }
18073
+ }
18074
+ const dateString = getDateString(
18075
+ value,
18076
+ dateFilter?.startDate && dateFilter?.endDate ? { start: dateFilter.startDate, end: dateFilter.endDate } : void 0,
18077
+ dateBucket,
18078
+ databaseType
18079
+ );
18080
+ row[pivot.rowField || ""] = dateString;
18081
+ });
18082
+ if (pivot.rowField && pivot.rowFieldType && !isStringType(pivot.rowFieldType) && dateFilter?.startDate && dateFilter?.endDate) {
18083
+ const dateSet = new Set(
18084
+ rows.map((row) => row[pivot.rowField || ""])
18085
+ );
18086
+ for (let date = dateFilter.startDate; date <= dateFilter.endDate; date = new Date(date.getTime() + 24 * 60 * 60 * 1e3)) {
18087
+ const formattedDate = getDateString(
18088
+ date.toISOString(),
18089
+ { start: dateFilter.startDate, end: dateFilter.endDate },
18090
+ dateBucket,
18091
+ databaseType
18092
+ );
18093
+ if (!dateSet.has(formattedDate)) {
18094
+ const newRow = {};
18095
+ newRow[pivot.rowField] = formattedDate;
18096
+ newRow.__quillRawDate = date.toISOString();
18097
+ rows.push(newRow);
18098
+ dateSet.add(formattedDate);
18099
+ }
18100
+ }
18101
+ }
18102
+ if (!pivot.sort) {
18103
+ rows.sort((a, b) => {
18104
+ if (a.__quillRawDate < b.__quillRawDate) {
18105
+ return -1;
18106
+ }
18107
+ if (a.__quillRawDate > b.__quillRawDate) {
18108
+ return 1;
18109
+ }
18110
+ return 0;
18111
+ });
18112
+ }
18113
+ }
18114
+ columns?.forEach((column, index) => {
18115
+ if (column.label && ["null", "undefined"].includes(column.label.toLowerCase()) && !pivot.columnField && !pivot.aggregations?.[index]?.valueField && pivot.aggregations?.[index]?.aggregationType === "count") {
18116
+ column.label = "Count";
18117
+ }
18118
+ });
18119
+ const numericColumns = columns?.filter(
18120
+ (column) => column.format === "whole_number" || column.format === "percentage"
18121
+ );
18122
+ rows.forEach((row) => {
18123
+ numericColumns?.forEach((column) => {
18124
+ row[column.field] = row[column.field] ?? 0;
18125
+ });
18126
+ });
18127
+ return {
18128
+ rows,
18129
+ columns: columns ?? [],
18130
+ rowCount: getPivotRowCount ? Number(resp?.queryResults?.[1]?.rows?.[0]?.["row_count"]) ?? rows.length : 0,
18131
+ pivotQuery: sqlQuery,
18132
+ comparisonPivotQuery: comparisonPivotSql
18133
+ };
18134
+ }
18135
+ function generatePivotTableYAxis(pivot, cols, yAxisField) {
18136
+ if (pivot?.aggregationType === "count") {
18137
+ return [
18138
+ {
18139
+ field: pivot.valueField ?? "count",
18140
+ label: yAxisField.label,
18141
+ format: yAxisField.format
18142
+ }
18143
+ ];
18144
+ }
18145
+ return [
18146
+ {
18147
+ field: pivot.valueField ?? "count",
18148
+ label: yAxisField.label,
18149
+ format: yAxisField.format
18150
+ }
18151
+ ];
18152
+ }
18153
+ function generatePivotTitle(pivot) {
18154
+ if (pivot.rowField && !pivot.valueField && pivot.aggregations?.[0]) {
18155
+ return snakeAndCamelCaseToTitleCase(
18156
+ `${pivot.aggregations[0].aggregationType} of ${pivot.rowField}${pivot.columnField ? ` by ${pivot.columnField}` : ""}
18157
+ `
18158
+ );
18159
+ } else if (!pivot.rowField && pivot.aggregations?.[0]?.valueField) {
18160
+ return snakeAndCamelCaseToTitleCase(
18161
+ `${pivot.aggregations[0].aggregationType} of ${pivot.aggregations[0].valueField}
18162
+ `
18163
+ );
18164
+ }
18165
+ return snakeAndCamelCaseToTitleCase(
18166
+ `${pivot.aggregations?.[0]?.aggregationType ?? "Aggregation"} of ${pivot.aggregations?.[0]?.valueField ?? "value"}${pivot.rowField ? ` by ${pivot.rowField}` : ""}${pivot.columnField ? ` and ${pivot.columnField}` : ""}`
18167
+ );
18168
+ }
18169
+ async function generatePivotTable({
18170
+ pivot,
18171
+ dateBucket,
18172
+ dateFilter,
18173
+ report,
18174
+ client,
18175
+ getToken,
18176
+ eventTracking,
18177
+ uniqueValues,
18178
+ dashboardName,
18179
+ tenants,
18180
+ additionalProcessing,
18181
+ caller,
18182
+ pivotQuery
18183
+ }) {
18184
+ try {
18185
+ if (report && client) {
18186
+ const pivotTable = await generatePivotWithSQL({
18187
+ pivotQuery,
18188
+ pivot,
18189
+ report,
18190
+ client,
18191
+ dateBucket,
18192
+ dateFilter,
18193
+ distinctStrings: pivot.columnField && uniqueValues?.[pivot.columnField] ? uniqueValues[pivot.columnField] : [],
18194
+ dashboardName,
18195
+ tenants,
18196
+ additionalProcessing,
18197
+ caller,
18198
+ getToken
18199
+ });
18200
+ return pivotTable;
18201
+ }
18202
+ } catch (e) {
18203
+ eventTracking?.logError?.({
18204
+ type: "bug",
18205
+ // TODO: determine type
18206
+ severity: "high",
18207
+ message: "Error generating pivot table",
18208
+ errorMessage: e.message,
18209
+ errorStack: e.stack,
18210
+ errorData: {
18211
+ caller: "PivotModal",
18212
+ function: "generatePivotTable"
18213
+ }
18214
+ });
18215
+ throw Error(`Failed to generate pivot table with SQL: ${e}`);
18216
+ }
18217
+ throw Error("Failed to generate pivot table: invalid report");
18218
+ }
18219
+
18220
+ // src/utils/errorProcessing.ts
18221
+ function processFilterErrorList(resp) {
18222
+ if (!resp || !resp.filterErrorList || !Array.isArray(resp.filterErrorList)) {
18223
+ return;
18224
+ }
18225
+ }
18226
+
18227
+ // src/utils/paginationProcessing.ts
18228
+ var DEFAULT_PAGINATION = {
18229
+ page: 0,
18230
+ rowsPerPage: 10,
18231
+ rowsPerRequest: 600
18232
+ };
18233
+ var DEFAULT_TABLE_PAGINATION = {
18234
+ page: 0,
18235
+ rowsPerPage: 10,
18236
+ rowsPerRequest: 50
18237
+ };
18238
+ function shouldFetchMore(pagination, page, maxPage, currentRowCount) {
18239
+ if (!pagination || currentRowCount && currentRowCount >= pagination.rowsPerPage * (page + 1)) {
18240
+ return false;
18241
+ }
18242
+ const indexAdjustedPage = page;
18243
+ const pageInterval = Math.floor(
18244
+ indexAdjustedPage * pagination.rowsPerPage / pagination.rowsPerRequest
18245
+ );
18246
+ const indexAdjustedPreviousPage = maxPage;
18247
+ const previousPageInterval = Math.floor(
18248
+ indexAdjustedPreviousPage * pagination.rowsPerPage / pagination.rowsPerRequest
18249
+ );
18250
+ return pageInterval > previousPageInterval;
18251
+ }
18252
+ function shouldSortInMemory(pagination, rowCount) {
18253
+ if (!rowCount || rowCount < pagination.rowsPerRequest) {
18254
+ return true;
18255
+ }
18256
+ return false;
18257
+ }
17547
18258
 
17548
18259
  // src/utils/tableProcessing.ts
17549
18260
  var getUniqueValuesByQuery = async ({
@@ -18203,90 +18914,575 @@ var fetchResultsByReport = async ({
18203
18914
  if (client.databaseType && client.databaseType.toLowerCase() === "bigquery") {
18204
18915
  parseValueFromBigQueryDates(rows, columns);
18205
18916
  }
18206
- return { rows, columns, rowCount, error };
18207
- };
18208
- var fetchTableByAST = async (ast, client, getToken, tenants, eventTracking, dashboardName, processing, customFields, rowsOnly, rowCountOnly) => {
18209
- let rows = [];
18210
- let columns = [];
18211
- let rowCount;
18212
- let error;
18213
- let itemQuery;
18214
- let referencedTables;
18215
- try {
18216
- const fetchResp = await quillFetch({
18217
- client,
18218
- task: "patterns",
18219
- metadata: {
18220
- clientId: client.publicKey,
18221
- ast,
18222
- customFields,
18223
- additionalProcessing: processing,
18224
- useUpdatedDataGathering: true,
18225
- useNewNodeSql: true,
18226
- rowsOnly,
18227
- rowCountOnly,
18228
- dashboardName,
18229
- tenants
18230
- },
18231
- getToken
18232
- });
18233
- const resp = await parseFetchResponse(
18234
- client,
18235
- "patterns",
18236
- fetchResp,
18237
- getToken
18238
- );
18239
- if (resp.errorMessage || resp.name === "error") {
18240
- throw new Error(resp.errorMessage);
18241
- }
18242
- processFilterErrorList(resp);
18243
- if (resp.rowCount !== void 0) {
18244
- rowCount = resp.rowCount;
18917
+ return { rows, columns, rowCount, error };
18918
+ };
18919
+ var fetchTableByAST = async (ast, client, getToken, tenants, eventTracking, dashboardName, processing, customFields, rowsOnly, rowCountOnly) => {
18920
+ let rows = [];
18921
+ let columns = [];
18922
+ let rowCount;
18923
+ let error;
18924
+ let itemQuery;
18925
+ let referencedTables;
18926
+ try {
18927
+ const fetchResp = await quillFetch({
18928
+ client,
18929
+ task: "patterns",
18930
+ metadata: {
18931
+ clientId: client.publicKey,
18932
+ ast,
18933
+ customFields,
18934
+ additionalProcessing: processing,
18935
+ useUpdatedDataGathering: true,
18936
+ useNewNodeSql: true,
18937
+ rowsOnly,
18938
+ rowCountOnly,
18939
+ dashboardName,
18940
+ tenants
18941
+ },
18942
+ getToken
18943
+ });
18944
+ const resp = await parseFetchResponse(
18945
+ client,
18946
+ "patterns",
18947
+ fetchResp,
18948
+ getToken
18949
+ );
18950
+ if (resp.errorMessage || resp.name === "error") {
18951
+ throw new Error(resp.errorMessage);
18952
+ }
18953
+ processFilterErrorList(resp);
18954
+ if (resp.rowCount !== void 0) {
18955
+ rowCount = resp.rowCount;
18956
+ }
18957
+ const gatheredRows = resp.rows && resp.rows.length ? resp.rows : [];
18958
+ columns = resp.fields.map((elem) => convertPostgresColumn(elem));
18959
+ rows = gatheredRows;
18960
+ itemQuery = resp.itemQuery;
18961
+ referencedTables = resp.referencedTables;
18962
+ if (customFields) {
18963
+ const tables = referencedTables ?? [];
18964
+ tables.forEach((table) => {
18965
+ const _customFields = customFields?.[table] ?? [];
18966
+ columns.forEach((col) => {
18967
+ if (_customFields.some((field) => {
18968
+ return field.field === col.field;
18969
+ })) {
18970
+ col.inferFormat = true;
18971
+ }
18972
+ });
18973
+ });
18974
+ }
18975
+ } catch (e) {
18976
+ eventTracking?.logError?.({
18977
+ type: "bug",
18978
+ // TODO: determine type
18979
+ severity: "high",
18980
+ message: "Error fetching table by AST",
18981
+ errorMessage: e.message,
18982
+ errorStack: e.stack,
18983
+ errorData: {
18984
+ caller: "fetchTableByAST",
18985
+ function: "fetchTableByAST"
18986
+ }
18987
+ });
18988
+ rows = [];
18989
+ columns = [];
18990
+ rowCount = 0;
18991
+ error = e.message;
18992
+ }
18993
+ if (client.databaseType && client.databaseType.toLowerCase() === "bigquery") {
18994
+ parseValueFromBigQueryDates(rows, columns);
18995
+ }
18996
+ return { rows, columns, rowCount, error, itemQuery, referencedTables };
18997
+ };
18998
+
18999
+ // src/utils/dashboard.ts
19000
+ var defaultDashboardItem = {
19001
+ id: "",
19002
+ name: "",
19003
+ dashboardName: "",
19004
+ rows: [],
19005
+ compareRows: [],
19006
+ columns: [],
19007
+ chartType: "",
19008
+ pivot: null,
19009
+ yAxisFields: [],
19010
+ xAxisLabel: "",
19011
+ xAxisField: "",
19012
+ xAxisFormat: "string",
19013
+ order: -1,
19014
+ filtersApplied: [],
19015
+ queryString: "",
19016
+ rowCount: 0,
19017
+ columnInternal: []
19018
+ };
19019
+ async function cleanDashboardItem({
19020
+ item,
19021
+ dashboardFilters,
19022
+ getToken,
19023
+ eventTracking,
19024
+ client,
19025
+ dateBucket,
19026
+ additionalProcessing,
19027
+ customFields,
19028
+ skipPivotFetch,
19029
+ tenants
19030
+ }) {
19031
+ if (!item) return defaultDashboardItem;
19032
+ if (!item.rows) {
19033
+ return {
19034
+ ...defaultDashboardItem,
19035
+ id: item._id,
19036
+ name: item.name
19037
+ };
19038
+ }
19039
+ const fields = item.fields || [];
19040
+ const columnsWithCustomFields = [...item.columns ?? []];
19041
+ if (item.includeCustomFields && item.rows?.length > 0) {
19042
+ const tables = item.referencedTables ?? [];
19043
+ tables.forEach((table) => {
19044
+ const _customFields = customFields?.[table] ?? [];
19045
+ _customFields.forEach((field) => {
19046
+ const isJsonCustomField = !!field.refColumn;
19047
+ if (item.rows[0][field.field] !== void 0 && !item.columns.some((col) => col.field === field.field)) {
19048
+ const result_field = fields.find((f) => f.name === field.field);
19049
+ const converted_field = convertPostgresColumn(result_field ?? {});
19050
+ columnsWithCustomFields.push({
19051
+ field: field.field,
19052
+ format: converted_field.format,
19053
+ label: snakeAndCamelCaseToTitleCase(field.field),
19054
+ fieldType: converted_field.fieldType,
19055
+ dataTypeID: converted_field.dataTypeID,
19056
+ jsType: converted_field.jsType,
19057
+ inferFormat: isJsonCustomField
19058
+ });
19059
+ }
19060
+ });
19061
+ });
19062
+ }
19063
+ const processedColumns = item.columns.map((col) => {
19064
+ return { ...col, label: snakeAndCamelCaseToTitleCase(col.label) };
19065
+ });
19066
+ const columnInternal = (item.includeCustomFields ? columnsWithCustomFields : item.columns).map((col) => {
19067
+ const field = item.fields?.find((f) => f.name === col.field);
19068
+ const converted_field = convertPostgresColumn(field ?? {});
19069
+ return {
19070
+ fieldType: converted_field.fieldType,
19071
+ dataTypeID: converted_field.dataTypeID,
19072
+ jsType: converted_field.jsType,
19073
+ ...col,
19074
+ label: snakeAndCamelCaseToTitleCase(col.label)
19075
+ };
19076
+ });
19077
+ let pivotTable;
19078
+ let pivotError;
19079
+ try {
19080
+ const shouldPaginatePivotAsTable = item.chartType === "table";
19081
+ const pivotChartProcessing = {
19082
+ page: DEFAULT_PAGINATION
19083
+ };
19084
+ pivotTable = await getPivotTable(
19085
+ {
19086
+ ...item,
19087
+ pivot: item.pivot && !skipPivotFetch ? {
19088
+ ...item.pivot,
19089
+ aggregations: item.pivot.aggregations ?? [
19090
+ {
19091
+ valueField: item.pivot.valueField,
19092
+ valueFieldType: item.pivot.valueFieldType,
19093
+ valueField2: item.pivot.valueField2,
19094
+ valueField2Type: item.pivot.valueField2Type,
19095
+ aggregationType: item.pivot.aggregationType
19096
+ }
19097
+ ]
19098
+ } : void 0
19099
+ },
19100
+ dashboardFilters,
19101
+ item.dashboardName,
19102
+ getToken,
19103
+ client,
19104
+ eventTracking,
19105
+ dateBucket,
19106
+ shouldPaginatePivotAsTable ? additionalProcessing : pivotChartProcessing,
19107
+ tenants,
19108
+ customFields
19109
+ );
19110
+ } catch (e) {
19111
+ pivotTable = void 0;
19112
+ eventTracking?.logError?.({
19113
+ type: "bug",
19114
+ // TODO: determine type
19115
+ severity: "high",
19116
+ message: "Error fetching pivot table",
19117
+ errorMessage: e.message,
19118
+ errorStack: e.stack,
19119
+ errorData: {
19120
+ caller: "cleanDashboardItem",
19121
+ function: "cleanDashboardItem"
19122
+ }
19123
+ });
19124
+ console.error("Error fetching pivot table", e);
19125
+ pivotError = "Error fetching pivot table";
19126
+ }
19127
+ const referenceLineYValues = [];
19128
+ for (const key in item) {
19129
+ if (key.startsWith("referenceLine_")) {
19130
+ const field = key.slice(14);
19131
+ if (!item[key] || !item[key][0]) continue;
19132
+ const value = Object.values(item[key][0])[0];
19133
+ referenceLineYValues.push({ label: field, query: [value, value] });
19134
+ }
19135
+ }
19136
+ if (item.referenceLines) {
19137
+ for (const referenceLine of item.referenceLines) {
19138
+ if (Array.isArray(referenceLine.query)) {
19139
+ referenceLineYValues.push({
19140
+ label: referenceLine.label,
19141
+ query: referenceLine.query
19142
+ });
19143
+ } else if (referenceLine.query === "") {
19144
+ referenceLineYValues.push({
19145
+ label: referenceLine.label,
19146
+ query: (pivotTable?.rows ? pivotTable.rows : item.rows).map(
19147
+ (row) => Number(row[referenceLine.label]) || 0
19148
+ )
19149
+ });
19150
+ }
19151
+ }
19152
+ }
19153
+ return {
19154
+ id: item._id ?? item.id,
19155
+ name: item.name,
19156
+ dashboardName: item.dashboardName,
19157
+ // section: item.section,
19158
+ rows: item.rows,
19159
+ pivotRows: pivotTable ? pivotTable.rows : void 0,
19160
+ pivotColumns: pivotTable ? pivotTable.columns : void 0,
19161
+ compareRows: item.compareRows,
19162
+ columns: processedColumns.map((column) => {
19163
+ return {
19164
+ field: column.field,
19165
+ format: column.format,
19166
+ label: column.label,
19167
+ inferFormat: column.inferFormat
19168
+ };
19169
+ }),
19170
+ includeCustomFields: item.includeCustomFields,
19171
+ columnInternal,
19172
+ columnsWithCustomFields,
19173
+ chartType: item.chartType,
19174
+ dateField: item.dateField,
19175
+ pivot: pivotError ? void 0 : item.pivot ? {
19176
+ ...item.pivot,
19177
+ aggregations: item.pivot.aggregations ?? [
19178
+ {
19179
+ valueField: item.pivot.valueField,
19180
+ valueFieldType: item.pivot.valueFieldType,
19181
+ valueField2: item.pivot.valueField2,
19182
+ valueField2Type: item.pivot.valueField2Type,
19183
+ aggregationType: item.pivot.aggregationType
19184
+ }
19185
+ ],
19186
+ columnValues: item.distinctStrings
19187
+ } : void 0,
19188
+ yAxisFields: pivotTable ? extractPivotedYAxis(pivotTable, item) : item.yAxisFields,
19189
+ xAxisLabel: item.xAxisLabel,
19190
+ xAxisField: item.xAxisField,
19191
+ xAxisFormat: item.xAxisFormat,
19192
+ order: item.order,
19193
+ filtersApplied: item.filtersApplied,
19194
+ filterMap: item.filterMap,
19195
+ flags: item.flags,
19196
+ rowCount: item.rowCount ? parseInt(item.rowCount) : item.rows.length,
19197
+ pivotRowCount: pivotTable ? pivotTable.rowCount : void 0,
19198
+ template: item.template,
19199
+ sort: item.sort,
19200
+ itemQuery: item.itemQuery,
19201
+ queryString: item.queryString,
19202
+ pivotQuery: pivotTable?.pivotQuery,
19203
+ comparisonPivotQuery: pivotTable?.comparisonPivotQuery,
19204
+ referencedTables: item?.referencedTables || [],
19205
+ referencedColumns: item?.referencedColumns || {},
19206
+ error: item.error ?? pivotError,
19207
+ referenceLineYValues,
19208
+ referenceLines: item.referenceLines
19209
+ };
19210
+ }
19211
+ async function getPivotTable(report, dashboardFilters, dashboardName, getToken, client, eventTracking, dateBucketInitial, additionalProcessing, tenants, customFields) {
19212
+ if (!report) return void 0;
19213
+ const dateFilter = Object.values(dashboardFilters ?? {}).find(
19214
+ (filter) => filter.filterType === "date_range" || filter.operator === "BETWEEN"
19215
+ );
19216
+ if (dateFilter?.operator === "BETWEEN") {
19217
+ dateFilter.startDate = dateFilter.value[0];
19218
+ dateFilter.endDate = dateFilter.value[1];
19219
+ }
19220
+ const pivot = report?.pivot;
19221
+ const data = report || {};
19222
+ if (pivot && client) {
19223
+ if (report.rowCount === 0 || report.rows.length === 0) {
19224
+ const columns = [];
19225
+ if (pivot.rowField) {
19226
+ columns.push({
19227
+ field: pivot.rowField,
19228
+ label: snakeCaseToTitleCase(processColumnName(pivot.rowField)),
19229
+ format: pivot.rowFieldType || "string",
19230
+ jsType: convertFieldTypeToJSType(pivot.rowFieldType || "string"),
19231
+ fieldType: pivot.rowFieldType || "string",
19232
+ dataTypeID: fieldTypeToDataTypeID(pivot.rowFieldType || "string")
19233
+ });
19234
+ }
19235
+ for (const agg of pivot.aggregations ?? []) {
19236
+ if (agg.valueField) {
19237
+ columns.push({
19238
+ field: agg.valueField,
19239
+ label: snakeCaseToTitleCase(processColumnName(agg.valueField)),
19240
+ //FIXME: valueFieldType is not always the same as the format
19241
+ format: agg.valueFieldType ?? "whole_number",
19242
+ jsType: agg.valueFieldType ?? "number",
19243
+ fieldType: agg.valueFieldType ?? "number",
19244
+ dataTypeID: fieldTypeToDataTypeID(agg.valueFieldType ?? "number")
19245
+ });
19246
+ }
19247
+ }
19248
+ const pivotQuery = generatePivotQuery(
19249
+ pivot,
19250
+ report.itemQuery?.[0],
19251
+ client.databaseType
19252
+ );
19253
+ return {
19254
+ rows: [],
19255
+ rowCount: 0,
19256
+ pivotQuery: pivotQuery ?? "",
19257
+ columns
19258
+ };
18245
19259
  }
18246
- const gatheredRows = resp.rows && resp.rows.length ? resp.rows : [];
18247
- columns = resp.fields.map((elem) => convertPostgresColumn(elem));
18248
- rows = gatheredRows;
18249
- itemQuery = resp.itemQuery;
18250
- referencedTables = resp.referencedTables;
18251
- if (customFields) {
18252
- const tables = referencedTables ?? [];
18253
- tables.forEach((table) => {
18254
- const _customFields = customFields?.[table] ?? [];
18255
- columns.forEach((col) => {
18256
- if (_customFields.some((field) => {
18257
- return field.field === col.field;
18258
- })) {
18259
- col.inferFormat = true;
18260
- }
19260
+ try {
19261
+ let dateBucket = dateBucketInitial;
19262
+ let filterDateRange = void 0;
19263
+ if (dateFilter && dateFilter.startDate && dateFilter.endDate) {
19264
+ filterDateRange = {
19265
+ start: dateFilter.startDate,
19266
+ end: dateFilter.endDate
19267
+ };
19268
+ } else if (report.dateRange) {
19269
+ filterDateRange = report.dateRange;
19270
+ }
19271
+ if (!dateBucket && filterDateRange) {
19272
+ dateBucket = getDateBucketFromRange(filterDateRange);
19273
+ }
19274
+ if (pivot.columnField && !report.distinctStrings) {
19275
+ const columnFieldColumn = (report.columnsWithCustomFields ?? report.columns).find((col) => col.field === pivot.columnField);
19276
+ if (!columnFieldColumn) {
19277
+ console.error(
19278
+ "could not find pivot column field on report",
19279
+ pivot.columnField
19280
+ );
19281
+ }
19282
+ const unique = await getUniqueValuesByQuery({
19283
+ columns: [columnFieldColumn],
19284
+ query: report.queryString,
19285
+ client,
19286
+ getToken,
19287
+ dashboardName,
19288
+ tenants,
19289
+ customFields: customFields ?? {},
19290
+ eventTracking
19291
+ });
19292
+ report.distinctStrings = unique?.[pivot.columnField] ?? [];
19293
+ }
19294
+ if (pivot.columnField && !report.distinctStrings) {
19295
+ const columnFieldColumn = (report.columnsWithCustomFields ?? report.columns).find((col) => col.field === pivot.columnField);
19296
+ if (!columnFieldColumn) {
19297
+ console.error(
19298
+ "could not find pivot column field on report",
19299
+ pivot.columnField
19300
+ );
19301
+ }
19302
+ const unique = await getUniqueValuesByQuery({
19303
+ columns: [columnFieldColumn],
19304
+ query: report.queryString,
19305
+ client,
19306
+ getToken,
19307
+ dashboardName,
19308
+ tenants,
19309
+ customFields: customFields ?? {},
19310
+ eventTracking
18261
19311
  });
19312
+ report.distinctStrings = unique?.[pivot.columnField] ?? [];
19313
+ }
19314
+ const pivotTable = await generatePivotWithSQL({
19315
+ pivot,
19316
+ report,
19317
+ client,
19318
+ dateBucket,
19319
+ dateFilter,
19320
+ distinctStrings: report.distinctStrings,
19321
+ dashboardName,
19322
+ tenants,
19323
+ additionalProcessing,
19324
+ getToken
19325
+ });
19326
+ return pivotTable;
19327
+ } catch (e) {
19328
+ eventTracking?.logError?.({
19329
+ type: "bug",
19330
+ // TODO: determine type
19331
+ severity: "high",
19332
+ message: "Error fetching pivot table",
19333
+ errorMessage: e.message,
19334
+ errorStack: e.stack,
19335
+ errorData: {
19336
+ caller: "getPivotTable",
19337
+ function: "getPivotTable"
19338
+ }
18262
19339
  });
19340
+ console.error("Error fetching pivot table", e);
19341
+ throw e;
18263
19342
  }
18264
- } catch (e) {
18265
- eventTracking?.logError?.({
18266
- type: "bug",
18267
- // TODO: determine type
18268
- severity: "high",
18269
- message: "Error fetching table by AST",
18270
- errorMessage: e.message,
18271
- errorStack: e.stack,
18272
- errorData: {
18273
- caller: "fetchTableByAST",
18274
- function: "fetchTableByAST"
18275
- }
18276
- });
18277
- rows = [];
18278
- columns = [];
18279
- rowCount = 0;
18280
- error = e.message;
18281
19343
  }
18282
- if (client.databaseType && client.databaseType.toLowerCase() === "bigquery") {
18283
- parseValueFromBigQueryDates(rows, columns);
19344
+ return pivot && data.rows ? generatePivotTable({
19345
+ pivot,
19346
+ report,
19347
+ client,
19348
+ uniqueValues: report.distinctStrings,
19349
+ dashboardName,
19350
+ tenants,
19351
+ dateFilter,
19352
+ additionalProcessing,
19353
+ getToken,
19354
+ eventTracking
19355
+ }) : void 0;
19356
+ }
19357
+ function extractPivotedYAxis(pivotTable, itemInfo, config = void 0) {
19358
+ if (!pivotTable) return itemInfo?.yAxisFields ?? [];
19359
+ const pivot = itemInfo?.pivot || config?.pivot;
19360
+ if (!pivot.columnField && !pivot.rowField) return itemInfo?.yAxisFields ?? [];
19361
+ const yAxisFields = config ? config.yAxisFields : itemInfo?.yAxisFields;
19362
+ return yAxisFields && yAxisFields.length > 0 ? generatePivotTableYAxis(pivot, pivotTable.columns, yAxisFields[0]) : yAxisFields;
19363
+ }
19364
+ async function getDashboard(dashboardName, client, getToken, tenants, flags) {
19365
+ const { data: resp } = await quillFetch({
19366
+ client,
19367
+ task: "dashboard",
19368
+ metadata: {
19369
+ name: dashboardName,
19370
+ clientId: client.publicKey,
19371
+ databaseType: client.databaseType,
19372
+ useNewNodeSql: true,
19373
+ tenants,
19374
+ flags
19375
+ },
19376
+ getToken
19377
+ });
19378
+ return {
19379
+ ...resp,
19380
+ createdAt: resp.createdAt && new Date(resp.createdAt),
19381
+ dateFilter: resp.dateFilter ? {
19382
+ ...resp.dateFilter,
19383
+ presetOptions: resp.dateFilter.presetOptions?.map(
19384
+ (preset) => ({
19385
+ ...preset,
19386
+ loopStart: preset.loopStart ? new Date(preset.loopStart) : void 0,
19387
+ loopEnd: preset.loopEnd ? new Date(preset.loopEnd) : void 0
19388
+ })
19389
+ ),
19390
+ defaultPresetRanges: resp.dateFilter.defaultPresetRanges?.map(
19391
+ (preset) => ({
19392
+ ...preset,
19393
+ loopStart: preset.loopStart ? new Date(preset.loopStart) : void 0,
19394
+ loopEnd: preset.loopEnd ? new Date(preset.loopEnd) : void 0
19395
+ })
19396
+ )
19397
+ } : void 0
19398
+ };
19399
+ }
19400
+
19401
+ // src/utils/chartBuilder.ts
19402
+ var numberFormatOptions = [
19403
+ "whole_number",
19404
+ "one_decimal_place",
19405
+ "two_decimal_places",
19406
+ "dollar_amount",
19407
+ "dollar_cents",
19408
+ "percentage"
19409
+ ];
19410
+ var dateFormatOptions = [
19411
+ "MMM_yyyy",
19412
+ "MMM_dd",
19413
+ "MMM_dd_yyyy",
19414
+ "MMM_dd_hh:mm_ap_pm",
19415
+ "hh_ap_pm",
19416
+ "date",
19417
+ "timestamptz"
19418
+ ];
19419
+ var NUMBER_OPTIONS = [
19420
+ { value: "whole_number", label: "whole number" },
19421
+ { value: "one_decimal_place", label: "one decimal place" },
19422
+ { value: "two_decimal_places", label: "two decimal places" },
19423
+ { value: "dollar_amount", label: "dollar amount" },
19424
+ { value: "dollar_cents", label: "dollar and cent amount" },
19425
+ { value: "percent", label: "percentage" }
19426
+ ];
19427
+ var DATE_OPTIONS = [
19428
+ { value: "MMM_yyyy", label: "month" },
19429
+ { value: "MMM_dd", label: "day" },
19430
+ { value: "MMM_dd_yyyy", label: "day and year" },
19431
+ { value: "MMM_dd_hh:mm_ap_pm", label: "day and time" },
19432
+ { value: "hh_ap_pm", label: "hour" }
19433
+ ];
19434
+ var ALL_FORMAT_OPTIONS = [
19435
+ ...NUMBER_OPTIONS,
19436
+ ...DATE_OPTIONS,
19437
+ { value: "string", label: "string" }
19438
+ ];
19439
+ function createInitialFormData(columns) {
19440
+ const firstNumberColumn = columns?.find(
19441
+ (col) => numberFormatOptions.includes(col.format)
19442
+ );
19443
+ const firstStringColumn = columns?.find(
19444
+ (col) => !numberFormatOptions.includes(col.format) && !dateFormatOptions.includes(col.format)
19445
+ );
19446
+ const firstDateColumn = columns?.find(
19447
+ (col) => dateFormatOptions.includes(col.format)
19448
+ );
19449
+ const xAxisField = firstStringColumn?.field || firstDateColumn?.field || firstNumberColumn?.field || columns?.[0]?.field || "";
19450
+ const xAxisFormat = firstStringColumn?.format || firstDateColumn?.format || firstNumberColumn?.format || columns?.[0]?.format || "string";
19451
+ const formEmptyState = {
19452
+ name: "",
19453
+ columns: columns.map((col) => {
19454
+ return { ...col, label: snakeAndCamelCaseToTitleCase(col.label) };
19455
+ }),
19456
+ xAxisField,
19457
+ xAxisFormat,
19458
+ yAxisFields: [
19459
+ {
19460
+ field: firstNumberColumn?.field || columns?.[0]?.field || "",
19461
+ label: "",
19462
+ format: firstNumberColumn?.format || columns?.[0]?.format || "string"
19463
+ }
19464
+ ],
19465
+ xAxisLabel: "",
19466
+ chartType: firstNumberColumn ? "line" : "table",
19467
+ pivot: null,
19468
+ template: true,
19469
+ referenceLines: []
19470
+ };
19471
+ return formEmptyState;
19472
+ }
19473
+
19474
+ // src/utils/error.ts
19475
+ var DataLoadError = class extends Error {
19476
+ data;
19477
+ constructor(message, data) {
19478
+ super(message);
19479
+ this.name = "DataLoadError";
19480
+ this.data = data;
18284
19481
  }
18285
- return { rows, columns, rowCount, error, itemQuery, referencedTables };
18286
19482
  };
18287
19483
 
18288
19484
  // src/components/ReportBuilder/convert.ts
18289
- var import_date_fns7 = require("date-fns");
19485
+ var import_date_fns8 = require("date-fns");
18290
19486
  function recursiveSearchAndReplace(node, search, replace) {
18291
19487
  if (typeof node !== "object") {
18292
19488
  return;
@@ -18595,7 +19791,7 @@ function convertStringComparison(node, databaseType) {
18595
19791
  searchAndReplace(element);
18596
19792
  });
18597
19793
  } else if (typeof obj === "object" && obj !== null) {
18598
- if (obj.type === "binary_expr" && (obj.operator === "=" && !(0, import_date_fns7.isValid)((0, import_date_fns7.parseISO)(obj.right.value)) || // don't do it on date objects
19794
+ if (obj.type === "binary_expr" && (obj.operator === "=" && !(0, import_date_fns8.isValid)((0, import_date_fns8.parseISO)(obj.right.value)) || // don't do it on date objects
18599
19795
  obj.operator === "LIKE" || obj.operator === "ILIKE") && obj.left && (obj.left.type === "column_ref" || obj.left.type === "double_quote_string") && obj.right && obj.right.type === "single_quote_string") {
18600
19796
  obj.operator = "LIKE";
18601
19797
  obj.left = {
@@ -21936,29 +23132,6 @@ var import_jspdf = __toESM(require("jspdf"), 1);
21936
23132
 
21937
23133
  // src/hooks/useDashboard.ts
21938
23134
  var import_react2 = require("react");
21939
-
21940
- // src/utils/merge.ts
21941
- var import_date_fns8 = require("date-fns");
21942
- var import_date_fns_tz2 = require("date-fns-tz");
21943
- function mergeComparisonRange(resp) {
21944
- if (resp.chartType === "table") return resp;
21945
- const compRows = resp.compareRows;
21946
- if (!compRows) return resp;
21947
- const newRows = resp.rows.map((row, i) => {
21948
- if (i < compRows.length) {
21949
- const compRow = compRows[i];
21950
- const newRow = { ...row };
21951
- for (const [key, value] of Object.entries(compRow)) {
21952
- newRow[`comparison_${key}`] = value;
21953
- }
21954
- return newRow;
21955
- }
21956
- return row;
21957
- });
21958
- return { ...resp, rows: newRows };
21959
- }
21960
-
21961
- // src/hooks/useDashboard.ts
21962
23135
  var useDashboardInternal = (dashboardName, customFilters) => {
21963
23136
  const [dashboard] = (0, import_react2.useContext)(DashboardContext);
21964
23137
  const {
@@ -30476,23 +31649,53 @@ function QuillMetricComponent({
30476
31649
  width: "100%"
30477
31650
  }
30478
31651
  }
30479
- ) : !report?.rows?.[0] || report.rows[0][report.xAxisField] === null ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
31652
+ ) : !report?.rows || report?.rows?.length === 0 || report.rows[0]?.[report.xAxisField] === null || report.rows[0]?.[report.xAxisField] === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
30480
31653
  "div",
30481
31654
  {
30482
31655
  style: {
30483
- display: "flex",
30484
- flex: "1 0 auto",
30485
- // height: '100%',
30486
- margin: "auto",
30487
- justifyContent: "center",
30488
- alignItems: "center",
30489
- fontSize: 13,
30490
- fontFamily: theme?.fontFamily,
30491
- color: theme?.secondaryTextColor,
30492
- maxWidth: "100%",
30493
- width: "100%"
31656
+ padding: 0,
31657
+ height: "100%",
31658
+ width: "100%",
31659
+ boxSizing: "content-box",
31660
+ flex: 1
30494
31661
  },
30495
- children: "No results"
31662
+ children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
31663
+ "div",
31664
+ {
31665
+ style: {
31666
+ fontFamily: theme?.fontFamily,
31667
+ fontSize: 32,
31668
+ color: theme?.primaryTextColor,
31669
+ fontWeight: "600",
31670
+ textOverflow: "ellipsis",
31671
+ margin: 0,
31672
+ whiteSpace: "nowrap",
31673
+ boxSizing: "content-box",
31674
+ maxWidth: "100%",
31675
+ textAlign: "left",
31676
+ overflow: "hidden",
31677
+ height: "100%",
31678
+ minHeight: "80px",
31679
+ display: "flex",
31680
+ width: "100%",
31681
+ flexDirection: "row",
31682
+ justifyContent: "center",
31683
+ alignItems: "center"
31684
+ },
31685
+ children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
31686
+ "span",
31687
+ {
31688
+ style: {
31689
+ fontFamily: theme?.fontFamily,
31690
+ fontSize: 13,
31691
+ color: theme?.secondaryTextColor,
31692
+ fontWeight: "normal"
31693
+ },
31694
+ children: "No results"
31695
+ }
31696
+ )
31697
+ }
31698
+ )
30496
31699
  }
30497
31700
  ) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
30498
31701
  MetricDisplay,
@@ -31678,7 +32881,7 @@ function USMap({
31678
32881
  acc[curr[xAxisField]?.toString()] = curr;
31679
32882
  return acc;
31680
32883
  }, {});
31681
- const measureField = yAxisFields[0].field;
32884
+ const measureField = yAxisFields[0]?.field;
31682
32885
  const [scaleLog, setScaleLog] = (0, import_react26.useState)(null);
31683
32886
  (0, import_react26.useEffect)(() => {
31684
32887
  import("d3-scale").then((scale) => {
@@ -31714,6 +32917,29 @@ function USMap({
31714
32917
  const hoveredValue = (0, import_react26.useMemo)(() => {
31715
32918
  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];
31716
32919
  }, [hoveredState, mappedData, measureField]);
32920
+ if (!measureField) {
32921
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
32922
+ "div",
32923
+ {
32924
+ style: {
32925
+ display: "flex",
32926
+ flex: "1 0 auto",
32927
+ marginLeft: "auto",
32928
+ marginRight: "auto",
32929
+ marginTop: "auto",
32930
+ marginBottom: "auto",
32931
+ justifyContent: "center",
32932
+ alignItems: "center",
32933
+ fontSize: 13,
32934
+ color: theme?.secondaryTextColor,
32935
+ fontFamily: theme?.fontFamily,
32936
+ ...containerStyle
32937
+ },
32938
+ className,
32939
+ children: "No results"
32940
+ }
32941
+ );
32942
+ }
31717
32943
  return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
31718
32944
  "div",
31719
32945
  {
@@ -31846,7 +33072,7 @@ function WorldMap({
31846
33072
  acc[curr[xAxisField]?.toString()] = curr;
31847
33073
  return acc;
31848
33074
  }, {});
31849
- const measureField = yAxisFields[0].field;
33075
+ const measureField = yAxisFields[0]?.field;
31850
33076
  const [scaleLog, setScaleLog] = (0, import_react26.useState)(null);
31851
33077
  (0, import_react26.useEffect)(() => {
31852
33078
  import("d3-scale").then((scale) => {
@@ -31882,6 +33108,29 @@ function WorldMap({
31882
33108
  const hoveredValue = (0, import_react26.useMemo)(() => {
31883
33109
  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];
31884
33110
  }, [hoveredCountry, mappedData, measureField]);
33111
+ if (!measureField) {
33112
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
33113
+ "div",
33114
+ {
33115
+ style: {
33116
+ display: "flex",
33117
+ flex: "1 0 auto",
33118
+ marginLeft: "auto",
33119
+ marginRight: "auto",
33120
+ marginTop: "auto",
33121
+ marginBottom: "auto",
33122
+ justifyContent: "center",
33123
+ alignItems: "center",
33124
+ fontSize: 13,
33125
+ color: theme?.secondaryTextColor,
33126
+ fontFamily: theme?.fontFamily,
33127
+ ...containerStyle
33128
+ },
33129
+ className,
33130
+ children: "No results"
33131
+ }
33132
+ );
33133
+ }
31885
33134
  return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
31886
33135
  "div",
31887
33136
  {
@@ -34389,27 +35638,41 @@ var ChartDisplay = ({
34389
35638
  );
34390
35639
  }
34391
35640
  if (config?.chartType?.toLowerCase() === "metric") {
34392
- if (!config?.rows || config?.rows?.length === 0 || // @ts-ignore
34393
- config?.rows[0][config?.xAxisField] === null) {
35641
+ if (!config?.rows || config?.rows?.length === 0 || config?.rows[0]?.[config?.xAxisField] === null || config?.rows[0]?.[config?.xAxisField] === void 0) {
34394
35642
  return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
34395
35643
  "div",
34396
35644
  {
34397
35645
  style: {
34398
- display: "flex",
34399
- flex: "1 0 auto",
34400
- // height: containerStyle?.height || '100%',
34401
- margin: "auto",
34402
- justifyContent: "center",
34403
- alignItems: "center",
34404
- fontSize: 13,
34405
35646
  fontFamily: theme?.fontFamily,
34406
- color: theme?.secondaryTextColor,
35647
+ fontSize: 32,
35648
+ color: theme?.primaryTextColor,
35649
+ fontWeight: "600",
35650
+ textOverflow: "ellipsis",
35651
+ margin: 0,
35652
+ whiteSpace: "nowrap",
35653
+ boxSizing: "content-box",
34407
35654
  maxWidth: "100%",
35655
+ textAlign: "left",
35656
+ overflow: "hidden",
35657
+ height: containerStyle?.height || "100%",
35658
+ display: "flex",
34408
35659
  width: "100%",
35660
+ flexDirection: "row",
35661
+ alignItems: "center",
34409
35662
  ...containerStyle
34410
35663
  },
34411
35664
  className,
34412
- children: "No results"
35665
+ children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
35666
+ "span",
35667
+ {
35668
+ style: {
35669
+ fontFamily: theme?.fontFamily,
35670
+ fontSize: 13,
35671
+ color: theme?.secondaryTextColor
35672
+ },
35673
+ children: "No results"
35674
+ }
35675
+ )
34413
35676
  }
34414
35677
  );
34415
35678
  }
@@ -41297,15 +42560,19 @@ function ChartBuilderWithModal(props) {
41297
42560
  title: title || "Add to dashboard",
41298
42561
  width: isHorizontalView ? modalWidth : void 0,
41299
42562
  height: isHorizontalView ? modalHeight : void 0,
41300
- children: (props.reportId ? dashboard[props.reportId] ?? props.tempReport : props.tempReport) ? /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
41301
- ChartBuilder,
41302
- {
41303
- ...props,
41304
- filtersEnabled: filtersEnabledState,
41305
- onFiltersEnabledChanged: setFiltersEnabledState,
41306
- runQueryOnMount: filtersEnabledState
41307
- }
41308
- ) : /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("div", { style: { padding: 20 }, children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(QuillLoadingComponent, {}) })
42563
+ children: (() => {
42564
+ const resolvedReport = props.reportId ? dashboard[props.reportId] ?? props.tempReport : props.tempReport;
42565
+ return resolvedReport ? /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
42566
+ ChartBuilder,
42567
+ {
42568
+ ...props,
42569
+ tempReport: resolvedReport,
42570
+ filtersEnabled: filtersEnabledState,
42571
+ onFiltersEnabledChanged: setFiltersEnabledState,
42572
+ runQueryOnMount: filtersEnabledState
42573
+ }
42574
+ ) : /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("div", { style: { padding: 20 }, children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(QuillLoadingComponent, {}) });
42575
+ })()
41309
42576
  }
41310
42577
  ) });
41311
42578
  }
@@ -41355,6 +42622,7 @@ function ChartBuilder({
41355
42622
  hideSubmitButton = false,
41356
42623
  hideDateRangeFilter = false,
41357
42624
  showTableFormatOptions,
42625
+ showDashboardFilterFields,
41358
42626
  initialUniqueValues,
41359
42627
  initialUniqueValuesIsLoading,
41360
42628
  pivotRecommendationsEnabled = true,
@@ -41384,9 +42652,10 @@ function ChartBuilder({
41384
42652
  const { tenants, flags } = (0, import_react43.useContext)(TenantContext);
41385
42653
  const report = (0, import_react43.useMemo)(() => {
41386
42654
  return reportId && !tempReport ? allReportsById[reportId] : tempReport;
41387
- }, [reportId]);
42655
+ }, [reportId, tempReport, allReportsById]);
41388
42656
  const [windowWidth, setWindowWidth] = (0, import_react43.useState)(1200);
41389
42657
  const [rows, setRows] = (0, import_react43.useState)(report?.rows ?? []);
42658
+ const [itemQuery, setItemQuery] = (0, import_react43.useState)(report?.itemQuery);
41390
42659
  const [rowCount, setRowCount] = (0, import_react43.useState)(report?.rowCount ?? 0);
41391
42660
  const [maxPage, setMaxPage] = (0, import_react43.useState)(0);
41392
42661
  const [isLoading, setIsLoading] = (0, import_react43.useState)(false);
@@ -42116,7 +43385,7 @@ function ChartBuilder({
42116
43385
  pivot,
42117
43386
  dateBucket,
42118
43387
  dateFilter,
42119
- report: report ? { ...report, ...tableInfo } : void 0,
43388
+ report: report ? { ...report, ...tableInfo ?? { itemQuery } } : void 0,
42120
43389
  client,
42121
43390
  uniqueValues,
42122
43391
  dashboardName: destinationDashboardName,
@@ -42277,6 +43546,7 @@ function ChartBuilder({
42277
43546
  setCurrentProcessing(processing);
42278
43547
  setRows(tableInfo.rows);
42279
43548
  setProcessedColumns(processColumns(tableInfo.columns));
43549
+ setItemQuery(tableInfo.itemQuery);
42280
43550
  fetchRowCount(processing, overrideFilters);
42281
43551
  if (formData.pivot) {
42282
43552
  try {
@@ -43182,7 +44452,7 @@ function ChartBuilder({
43182
44452
  uniqueValuesIsLoading: initialUniqueValuesIsLoading,
43183
44453
  initialSelectedPivotTable: selectedPivotTable,
43184
44454
  pivotRecommendationsEnabled,
43185
- report,
44455
+ report: report ? { ...report, ...{ itemQuery } } : void 0,
43186
44456
  dashboardName: destinationDashboardName || "",
43187
44457
  dateFilter: filtersEnabled ? currentDashboardFilters?.find(
43188
44458
  (f) => f.filterType === "date_range"
@@ -43875,7 +45145,7 @@ function ChartBuilder({
43875
45145
  ]
43876
45146
  }
43877
45147
  ),
43878
- specificDashboardFilters.length > 0 && isAdmin && /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(
45148
+ specificDashboardFilters.length > 0 && (showDashboardFilterFields || isAdmin) && /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(
43879
45149
  "div",
43880
45150
  {
43881
45151
  style: {
@@ -43886,7 +45156,7 @@ function ChartBuilder({
43886
45156
  },
43887
45157
  children: [
43888
45158
  /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(HeaderComponent, { label: "Dashboard filter fields" }),
43889
- isAdmin && formData.dateField && dashboardConfig[formData.dashboardName ?? destinationDashboardName ?? ""]?.config.dateFilter?.label && /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(ChartBuilderInputRowContainer, { children: [
45159
+ (showDashboardFilterFields || isAdmin) && formData.dateField && dashboardConfig[formData.dashboardName ?? destinationDashboardName ?? ""]?.config.dateFilter?.label && /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(ChartBuilderInputRowContainer, { children: [
43890
45160
  /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
43891
45161
  TextInputComponent,
43892
45162
  {
@@ -44825,6 +46095,7 @@ function SQLEditor({
44825
46095
  CheckboxComponent = QuillChartBuilderCheckboxComponent,
44826
46096
  defaultQuery,
44827
46097
  destinationDashboard,
46098
+ destinationSection,
44828
46099
  onChangeQuery,
44829
46100
  onChangeData,
44830
46101
  onChangeColumns,
@@ -44834,12 +46105,16 @@ function SQLEditor({
44834
46105
  onCloseChartBuilder,
44835
46106
  isChartBuilderEnabled = false,
44836
46107
  isAdminEnabled = false,
46108
+ chartBuilderOptions,
44837
46109
  chartBuilderTitle,
44838
46110
  runQueryOnMount = false,
44839
46111
  onAddToDashboardComplete,
46112
+ onSubmitCreateReport,
46113
+ onSubmitEditReport,
44840
46114
  onSaveQueryComplete,
44841
46115
  addToDashboardButtonLabel = "Add to dashboard",
44842
46116
  report = void 0,
46117
+ reportId = void 0,
44843
46118
  organizationName = void 0,
44844
46119
  isChartBuilderHorizontalView = true,
44845
46120
  containerStyle = { height: "100vh" },
@@ -44850,7 +46125,7 @@ function SQLEditor({
44850
46125
  const [sqlPrompt, setSqlPrompt] = (0, import_react45.useState)("");
44851
46126
  const [client] = (0, import_react45.useContext)(ClientContext);
44852
46127
  const [theme] = (0, import_react45.useContext)(ThemeContext);
44853
- const { tenants } = (0, import_react45.useContext)(TenantContext);
46128
+ const { tenants, flags } = (0, import_react45.useContext)(TenantContext);
44854
46129
  const { dashboards } = useDashboards();
44855
46130
  const {
44856
46131
  data,
@@ -44859,6 +46134,7 @@ function SQLEditor({
44859
46134
  } = useDashboardInternal(destinationDashboard);
44860
46135
  const { getToken, quillFetchWithToken } = (0, import_react45.useContext)(FetchContext);
44861
46136
  const { eventTracking } = (0, import_react45.useContext)(EventTrackingContext);
46137
+ const { allReportsById } = useAllReports();
44862
46138
  const destinationDashboardConfig = (0, import_react45.useMemo)(() => {
44863
46139
  return dashboards?.find((d) => d.name === destinationDashboard);
44864
46140
  }, [dashboards, destinationDashboard]);
@@ -44926,6 +46202,46 @@ function SQLEditor({
44926
46202
  reload();
44927
46203
  }
44928
46204
  }, [data, dashboardIsLoading]);
46205
+ (0, import_react45.useEffect)(() => {
46206
+ const loadReport = async () => {
46207
+ let reportToLoad;
46208
+ if (!client) {
46209
+ return;
46210
+ }
46211
+ try {
46212
+ if (!reportId) {
46213
+ throw new Error("Report ID is required");
46214
+ }
46215
+ reportToLoad = allReportsById[reportId];
46216
+ if (!reportToLoad) {
46217
+ throw new Error("Report not found");
46218
+ }
46219
+ setQuery(reportToLoad.queryString || "");
46220
+ setTempReport(reportToLoad);
46221
+ if (reportToLoad.rows && reportToLoad.rows.length > 0) {
46222
+ setRows(reportToLoad.rows);
46223
+ setDisplayTable(true);
46224
+ }
46225
+ } catch (err) {
46226
+ console.error(err);
46227
+ eventTracking?.logError?.({
46228
+ type: "bug",
46229
+ severity: "high",
46230
+ message: "Error loading report",
46231
+ errorMessage: err.message,
46232
+ errorStack: err.stack,
46233
+ errorData: {
46234
+ caller: "SQLEditor",
46235
+ function: "loadReport"
46236
+ }
46237
+ });
46238
+ setErrorMessage("Error when loading report");
46239
+ }
46240
+ };
46241
+ if (reportId && client) {
46242
+ loadReport();
46243
+ }
46244
+ }, [allReportsById[reportId || ""], client]);
44929
46245
  const dynamicHeight = tableRef.current ? tableRef.current.clientHeight : cachedHeight;
44930
46246
  const rowsPerPage = Math.max(
44931
46247
  DEFAULT_ROWS_PER_PAGE,
@@ -45744,13 +47060,38 @@ function SQLEditor({
45744
47060
  isHorizontalView: isChartBuilderHorizontalView,
45745
47061
  isOpen: isChartBuilderOpen,
45746
47062
  setIsOpen: setIsChartBuilderOpen,
45747
- onAddToDashboardComplete,
47063
+ onAddToDashboardComplete: reportId || report?.id ? (data2) => {
47064
+ if (onSubmitEditReport) {
47065
+ onSubmitEditReport(data2);
47066
+ } else if (onAddToDashboardComplete) {
47067
+ onAddToDashboardComplete(data2);
47068
+ }
47069
+ if (!isAdminEnabled && destinationDashboard) {
47070
+ reload(destinationDashboard, false, {
47071
+ report: data2,
47072
+ action: "upsert"
47073
+ });
47074
+ }
47075
+ } : (data2) => {
47076
+ if (onSubmitCreateReport) {
47077
+ onSubmitCreateReport(data2);
47078
+ } else if (onAddToDashboardComplete) {
47079
+ onAddToDashboardComplete(data2);
47080
+ }
47081
+ if (!isAdminEnabled && destinationDashboard) {
47082
+ reload(destinationDashboard, false, {
47083
+ report: data2,
47084
+ action: "upsert"
47085
+ });
47086
+ }
47087
+ },
45748
47088
  destinationDashboard,
47089
+ destinationSection,
45749
47090
  isAdmin: isAdminEnabled,
45750
47091
  title: chartBuilderTitle,
45751
47092
  buttonLabel: addToDashboardButtonLabel,
45752
47093
  tempReport,
45753
- reportId: report?.id,
47094
+ reportId: reportId || report?.id,
45754
47095
  organizationName,
45755
47096
  CardComponent,
45756
47097
  TableComponent,
@@ -45775,6 +47116,8 @@ function SQLEditor({
45775
47116
  CheckboxComponent,
45776
47117
  hideDateRangeFilter: true,
45777
47118
  hideDeleteButton: true,
47119
+ showTableFormatOptions: chartBuilderOptions?.showTableFormatOptions,
47120
+ showDashboardFilterFields: chartBuilderOptions?.showDashboardFilterFields,
45778
47121
  onClickChartElement,
45779
47122
  isEditingMode: true
45780
47123
  }
@@ -45794,7 +47137,7 @@ function SQLEditor({
45794
47137
  isAdmin: false,
45795
47138
  title: "Save query",
45796
47139
  buttonLabel: "Save query",
45797
- tempReport,
47140
+ tempReport: { ...tempReport, dashboardName: SAVED_QUERIES_DASHBOARD },
45798
47141
  reportId: report?.id,
45799
47142
  organizationName,
45800
47143
  CardComponent,
@@ -47557,7 +48900,6 @@ var useReportBuilderInternal = ({
47557
48900
  }
47558
48901
  report = allReportsById[reportId];
47559
48902
  if (!report) {
47560
- console.log("no report");
47561
48903
  throw new Error("Report not found");
47562
48904
  }
47563
48905
  const { ast: newAst, pivot: newPivot } = await fetchASTFromQuillReport(
@@ -50082,6 +51424,8 @@ var SaveReport = ({
50082
51424
  onSubmitEditReport = () => void 0,
50083
51425
  onSubmitCreateReport = () => void 0,
50084
51426
  destinationSection,
51427
+ showTableFormatOptions,
51428
+ showDashboardFilterFields,
50085
51429
  SelectComponent = QuillSelectComponent,
50086
51430
  TextInputComponent = QuillTextInput,
50087
51431
  ButtonComponent = MemoizedButton,
@@ -50114,6 +51458,7 @@ var SaveReport = ({
50114
51458
  ),
50115
51459
  submitButtonLabel
50116
51460
  }) => {
51461
+ const { reload } = useDashboardInternal(reportBuilder.destinationDashboard ?? null);
50117
51462
  return /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { children: [
50118
51463
  SaveTrigger,
50119
51464
  /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
@@ -50126,7 +51471,17 @@ var SaveReport = ({
50126
51471
  isHorizontalView: true,
50127
51472
  isOpen,
50128
51473
  setIsOpen,
50129
- onAddToDashboardComplete: reportBuilder.reportId ? onSubmitEditReport : onSubmitCreateReport,
51474
+ onAddToDashboardComplete: reportBuilder.reportId ? (data) => {
51475
+ onSubmitEditReport(data);
51476
+ if (!isAdminEnabled && reportBuilder.destinationDashboard) {
51477
+ reload(reportBuilder.destinationDashboard, false, { report: data, action: "upsert" });
51478
+ }
51479
+ } : (data) => {
51480
+ onSubmitCreateReport(data);
51481
+ if (!isAdminEnabled && reportBuilder.destinationDashboard) {
51482
+ reload(reportBuilder.destinationDashboard, false, { report: data, action: "upsert" });
51483
+ }
51484
+ },
50130
51485
  destinationDashboard: reportBuilder.destinationDashboard,
50131
51486
  destinationSection,
50132
51487
  initialUniqueValues: reportBuilder.columnUniqueValues,
@@ -50155,6 +51510,8 @@ var SaveReport = ({
50155
51510
  FormContainer: ChartBuilderFormContainer,
50156
51511
  hideDateRangeFilter: true,
50157
51512
  hideDeleteButton: true,
51513
+ showTableFormatOptions,
51514
+ showDashboardFilterFields,
50158
51515
  buttonLabel: submitButtonLabel ?? (!!reportBuilder.reportId ? "Save changes" : "Add to dashboard"),
50159
51516
  onClickChartElement,
50160
51517
  isEditingMode: true
@@ -50250,6 +51607,7 @@ function ReportBuilder({
50250
51607
  containerStyle,
50251
51608
  className,
50252
51609
  pivotRecommendationsEnabled = true,
51610
+ chartBuilderOptions,
50253
51611
  reportId,
50254
51612
  hideCopySQL = true,
50255
51613
  isChartBuilderHorizontalView = true,
@@ -50689,6 +52047,8 @@ function ReportBuilder({
50689
52047
  ErrorMessageComponent,
50690
52048
  PivotRowContainer,
50691
52049
  PivotColumnContainer,
52050
+ showTableFormatOptions: chartBuilderOptions?.showTableFormatOptions,
52051
+ showDashboardFilterFields: chartBuilderOptions?.showDashboardFilterFields,
50692
52052
  onClickChartElement,
50693
52053
  submitButtonLabel
50694
52054
  }
@@ -50709,7 +52069,7 @@ function ReportBuilder({
50709
52069
  isAdmin: false,
50710
52070
  title: "Save query",
50711
52071
  buttonLabel: "Save query",
50712
- tempReport,
52072
+ tempReport: { ...tempReport, dashboardName: SAVED_QUERIES_DASHBOARD },
50713
52073
  reportId,
50714
52074
  organizationName,
50715
52075
  CardComponent,