@quillsql/react 2.15.17 → 2.16.0

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.
Files changed (3) hide show
  1. package/dist/index.cjs +854 -2041
  2. package/dist/index.js +831 -2018
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -620,12 +620,9 @@ var formatPercent = (value) => {
620
620
  return formatterPercent.format(Number(value));
621
621
  };
622
622
  var _getUTCDateHelper = (value, fmt) => {
623
- if (value === null || value === void 0) {
624
- return "-";
625
- }
626
623
  const parsedDate = parse(value, fmt, /* @__PURE__ */ new Date());
627
624
  const utcDate = isValid(parsedDate) ? utcToZonedTime(parsedDate, "UTC") : utcToZonedTime(new Date(value), "UTC");
628
- if (!isValid(utcDate)) return "-";
625
+ if (!isValid(utcDate)) return value;
629
626
  return format(utcDate, fmt);
630
627
  };
631
628
  var format_YYYY = (value) => _getUTCDateHelper(value, "yyyy");
@@ -636,11 +633,8 @@ var format_MMM_dd_yyyy = (value) => {
636
633
  return _getUTCDateHelper(value, "dd MMM yyyy");
637
634
  };
638
635
  var format_MMM_d_MMM_d = (value, dateRange, databaseType) => {
639
- if (value === null || value === void 0) {
640
- return "-";
641
- }
642
636
  const utcDate = parseISO(value.split("T")[0]);
643
- if (!isValid(utcDate)) return "-";
637
+ if (!isValid(utcDate)) return "Invalid date";
644
638
  let weekStartsOn = 1;
645
639
  if (databaseType && ["mssql"].includes(databaseType)) {
646
640
  weekStartsOn = 0;
@@ -671,21 +665,15 @@ var format_MMM_d_MMM_d = (value, dateRange, databaseType) => {
671
665
  }
672
666
  };
673
667
  var format_MMM_dd_hh_mm_ap_pm = (value) => {
674
- if (value === null || value === void 0) {
675
- return "-";
676
- }
677
668
  const utcDate = utcToZonedTime(new Date(value), "UTC");
678
- if (!isValid(utcDate)) return "-";
669
+ if (!isValid(utcDate)) return "Invalid date";
679
670
  const formatStr = utcDate.getMinutes() === 0 ? "MMM do h a" : "MMM do h:mm a";
680
671
  const res = format(utcDate, formatStr);
681
672
  return res.slice(0, -2) + res.slice(-2).toLowerCase();
682
673
  };
683
674
  var format_wo_yyyy = (value) => {
684
- if (value === null || value === void 0) {
685
- return "-";
686
- }
687
675
  const utcDate = utcToZonedTime(new Date(value), "UTC");
688
- if (!isValid(utcDate)) return "-";
676
+ if (!isValid(utcDate)) return "Invalid date";
689
677
  return `${getWeek(utcDate)},${utcDate.getFullYear()}`;
690
678
  };
691
679
  function parseNumber(value) {
@@ -14799,21 +14787,6 @@ function isDateFormat(xAxisFormat) {
14799
14787
  const isDate = DATE_FORMATS.includes(xAxisFormat);
14800
14788
  return isDate;
14801
14789
  }
14802
- function getComparisonInterval(comparisonRange, dateBucket) {
14803
- const dayCount = differenceInDays2(
14804
- comparisonRange.endDate,
14805
- comparisonRange.startDate
14806
- );
14807
- if (!isNaN(dayCount)) {
14808
- if (dateBucket === "month") {
14809
- return Math.floor(dayCount / 30) + " month";
14810
- } else if (dateBucket === "year") {
14811
- return Math.floor(dayCount / 365) + " year";
14812
- } else {
14813
- return dayCount + " day";
14814
- }
14815
- }
14816
- }
14817
14790
  function getDateBucketFromRange(dateRange) {
14818
14791
  const difference = differenceInDays2(dateRange.end, dateRange.start);
14819
14792
  if (difference < 14) {
@@ -16627,422 +16600,831 @@ function parseValueFromBigQueryDates(rows, columns) {
16627
16600
  });
16628
16601
  }
16629
16602
 
16630
- // src/utils/merge.ts
16631
- import {
16632
- add,
16633
- startOfDay as startOfDay3,
16634
- startOfMonth,
16635
- startOfWeek as startOfWeek3,
16636
- startOfYear
16637
- } from "date-fns";
16638
- import { utcToZonedTime as utcToZonedTime2 } from "date-fns-tz";
16639
- function mergeComparisonRange(resp) {
16640
- if (resp.chartType === "table") return resp;
16641
- const compRows = resp.compareRows;
16642
- if (!compRows) return resp;
16643
- const newRows = resp.rows.map((row, i) => {
16644
- if (i < compRows.length) {
16645
- const compRow = compRows[i];
16646
- const newRow = { ...row };
16647
- for (const [key, value] of Object.entries(compRow)) {
16648
- newRow[`comparison_${key}`] = value;
16649
- }
16650
- return newRow;
16651
- }
16652
- return row;
16653
- });
16654
- return { ...resp, rows: newRows };
16655
- }
16656
- function dateAdder(date, interval2) {
16657
- const match = interval2.match(/^(\d+)\s+(day|month|year)$/);
16658
- if (!match) {
16659
- throw new Error(`Invalid interval format: ${interval2}`);
16660
- }
16661
- const [, valueStr, unit] = match;
16662
- if (!valueStr || !unit) {
16663
- throw new Error("Could not parse interval");
16664
- }
16665
- const value = parseInt(valueStr, 10);
16666
- const parsedDate = typeof date === "string" ? new Date(date) : date;
16667
- switch (unit) {
16668
- case "day":
16669
- return add(parsedDate, { days: value });
16670
- case "month":
16671
- return add(parsedDate, { months: value });
16672
- case "year":
16673
- return add(parsedDate, { years: value });
16674
- default:
16675
- throw new Error(`Unsupported interval unit: ${unit}`);
16676
- }
16677
- }
16678
- function dateTrunc(date, granularity = "day", databaseType) {
16679
- const weekStartsOn = databaseType === "mssql" ? 0 : 1;
16680
- const timeZone = "UTC";
16681
- const zonedDate = utcToZonedTime2(date, timeZone);
16682
- switch (granularity.toLowerCase()) {
16683
- case "week":
16684
- return startOfWeek3(zonedDate, { weekStartsOn });
16685
- case "month":
16686
- return startOfMonth(zonedDate);
16687
- case "day":
16688
- return startOfDay3(zonedDate);
16689
- case "year":
16690
- return startOfYear(zonedDate);
16691
- default:
16692
- throw new Error(`Unsupported granularity: ${granularity}`);
16693
- }
16694
- }
16695
- function mergeComparisonPivotColumns({
16696
- pivot,
16697
- rows,
16698
- compRows
16699
- }) {
16700
- if (!compRows || !compRows.length) return rows;
16701
- const mappedCompRows = compRows.filter((compRow) => compRow.name !== pivot.rowField).map((compRow) => ({ ...compRow, name: `comparison_${compRow.name}` }));
16702
- return rows.concat(mappedCompRows);
16703
- }
16704
- function mergeComparisonPivotRows({
16603
+ // src/utils/pivotConstructor.ts
16604
+ async function generatePivotWithSQL({
16705
16605
  pivot,
16706
- rows,
16707
- compRows,
16708
- databaseType,
16606
+ report,
16607
+ client,
16709
16608
  dateBucket,
16710
- comparisonInterval,
16711
- columnFieldValues
16609
+ dateFilter,
16610
+ distinctStrings,
16611
+ dashboardName,
16612
+ tenants,
16613
+ additionalProcessing,
16614
+ pivotQuery,
16615
+ comparisonPivotQuery,
16616
+ getPivotRowCount = true,
16617
+ caller,
16618
+ getToken
16712
16619
  }) {
16713
- if (!compRows || !compRows.length) return rows;
16714
- if (pivot.columnField && columnFieldValues && columnFieldValues.length > 0) {
16715
- return merge2DPivotRows(
16716
- pivot,
16717
- rows,
16718
- compRows,
16719
- columnFieldValues,
16720
- databaseType,
16721
- dateBucket,
16722
- comparisonInterval
16723
- );
16724
- }
16725
- if (pivot.rowField) {
16726
- return merge1DPivotRows(
16727
- pivot,
16728
- rows,
16729
- compRows,
16730
- databaseType,
16731
- dateBucket,
16732
- comparisonInterval
16733
- );
16734
- }
16735
- return mergeAggregatedRows(pivot, rows, compRows);
16736
- }
16737
- function merge2DPivotRows(pivot, rows, compRows, columnFieldValues, databaseType, dateBucket, comparisonInterval) {
16738
- if (!pivot || !pivot.columnField) {
16739
- return [];
16740
- }
16741
- if (isStringType(pivot.rowFieldType || "") || !pivot.rowFieldType) {
16742
- return merge2DStringPivotRows(pivot, rows, compRows, columnFieldValues);
16743
- }
16744
- return merge2DDatePivotRows(
16745
- pivot,
16746
- rows,
16747
- compRows,
16748
- columnFieldValues,
16749
- databaseType,
16750
- dateBucket,
16751
- comparisonInterval
16752
- );
16753
- }
16754
- function merge2DStringPivotRows(pivot, rows, compRows, columnFieldValues) {
16755
- if (!pivot.rowField || !pivot.aggregations?.some((agg) => agg.valueField)) {
16756
- return rows;
16620
+ const databaseType = client.databaseType || "postgresql";
16621
+ if (!pivot.aggregations?.length && pivot.aggregationType) {
16622
+ pivot.aggregations = [
16623
+ {
16624
+ aggregationType: pivot.aggregationType,
16625
+ valueField: pivot.valueField,
16626
+ valueFieldType: pivot.valueFieldType,
16627
+ valueField2: pivot.valueField2,
16628
+ valueField2Type: pivot.valueField2Type
16629
+ }
16630
+ ];
16757
16631
  }
16758
- return rows.map((row) => {
16759
- const matchingCompRow = compRows.find(
16760
- (compRow) => compRow[pivot.rowField] === row[pivot.rowField]
16761
- );
16762
- const comparisonFields = columnFieldValues.reduce(
16763
- (acc, fieldValue) => {
16764
- acc[`comparison_${fieldValue}`] = matchingCompRow ? matchingCompRow[fieldValue] || null : null;
16765
- return acc;
16766
- },
16767
- {}
16768
- );
16769
- return {
16770
- ...row,
16771
- ...comparisonFields
16772
- };
16773
- });
16774
- }
16775
- function merge2DDatePivotRows(pivot, rows, compRows, columnFieldValues, databaseType, dateBucket, comparisonInterval) {
16776
- if (!comparisonInterval || !pivot.rowField) {
16777
- return rows;
16778
- }
16779
- return rows.map((row) => {
16780
- const rowDate = typeof row[pivot.rowField] === "string" ? new Date(row[pivot.rowField]) : row[pivot.rowField];
16781
- const truncatedRowDate = dateTrunc(rowDate, dateBucket, databaseType);
16782
- const matchingCompRow = compRows.find((compRow) => {
16783
- const comparisonDateWithInterval = dateAdder(
16784
- compRow[pivot.rowField],
16785
- comparisonInterval
16786
- );
16787
- const truncatedCompRowDate = dateTrunc(
16788
- comparisonDateWithInterval,
16789
- dateBucket,
16790
- databaseType
16791
- );
16792
- return truncatedRowDate.toISOString() === truncatedCompRowDate.toISOString();
16793
- });
16794
- const comparisonFields = columnFieldValues.reduce(
16795
- (acc, fieldValue) => {
16796
- acc[`comparison_${fieldValue}`] = matchingCompRow ? matchingCompRow[fieldValue] || null : null;
16797
- return acc;
16798
- },
16799
- {}
16800
- );
16801
- return {
16802
- ...row,
16803
- ...comparisonFields
16804
- };
16632
+ const pivotConfig = {
16633
+ rowField: pivot.rowField,
16634
+ rowFieldType: pivot.rowFieldType,
16635
+ columnField: pivot.columnField,
16636
+ aggregations: pivot.aggregations?.map((agg) => ({
16637
+ aggregationType: agg.aggregationType,
16638
+ valueField: agg.valueField,
16639
+ valueFieldType: agg.valueFieldType
16640
+ }))
16641
+ };
16642
+ const resp = await quillFetch({
16643
+ client,
16644
+ task: "pivot-template",
16645
+ metadata: {
16646
+ clientId: client.clientId,
16647
+ pivot: pivotConfig,
16648
+ itemQuery: report?.queryString,
16649
+ dashboardName,
16650
+ tenants
16651
+ },
16652
+ credentials: "same-origin",
16653
+ getToken
16805
16654
  });
16806
- }
16807
- function merge1DPivotRows(pivot, rows, compRows, databaseType, dateBucket, comparisonInterval) {
16808
- if (isStringType(pivot.rowFieldType || "") || !pivot.rowFieldType) {
16809
- return merge1DStringPivotRows(pivot, rows, compRows);
16655
+ if (resp.data?.success === false) {
16656
+ throw resp.data.errorMessage;
16810
16657
  }
16811
- return merge1DDatePivotRows(
16812
- pivot,
16813
- rows,
16814
- compRows,
16815
- databaseType,
16816
- dateBucket,
16817
- comparisonInterval
16818
- );
16819
- }
16820
- function merge1DStringPivotRows(pivot, rows, compRows) {
16821
- if (!pivot.rowField || !pivot.valueField) {
16822
- return rows;
16658
+ const queryResponseRows = resp.data?.rows || resp.queries?.queryResults?.[0]?.rows || [];
16659
+ const queryResponseFields = resp.data?.fields || resp.queries?.queryResults?.[0]?.fields || [];
16660
+ const rowCount = resp.queries?.queryResults?.[1]?.rows?.[0]?.row_count || 0;
16661
+ parseValueFromBigQueryDates(queryResponseRows, queryResponseFields);
16662
+ const responseRows = queryResponseRows;
16663
+ const responseFields = queryResponseFields;
16664
+ const rows = pivot.rowField ? responseRows.map(
16665
+ (row) => !row[pivot.rowField] ? { ...row, [pivot.rowField]: "-" } : row
16666
+ ) : responseRows;
16667
+ if (pivot.columnField && client.databaseType?.toLowerCase() === "bigquery") {
16668
+ rows.forEach((row) => {
16669
+ Object.keys(row).forEach((key) => {
16670
+ const processedKey = processColumnName(key);
16671
+ if (processedKey !== key) {
16672
+ row[processedKey] = row[key];
16673
+ delete row[key];
16674
+ }
16675
+ });
16676
+ });
16823
16677
  }
16824
- return rows.map((row) => {
16825
- const matchingCompRow = compRows.find(
16826
- (compRow) => compRow[pivot.rowField] === row[pivot.rowField]
16827
- );
16828
- let aggregationSuffix = "";
16829
- if (matchingCompRow && pivot.aggregationType === "percentage") {
16830
- if (matchingCompRow[`${pivot.valueField}_Percentage`]) {
16831
- aggregationSuffix = "_Percentage";
16832
- } else if (matchingCompRow[`${pivot.valueField}_PERCENTAGE`]) {
16833
- aggregationSuffix = "_PERCENTAGE";
16834
- } else {
16835
- aggregationSuffix = "_percentage";
16836
- }
16678
+ const columns = responseFields?.map((field) => ({
16679
+ field: processColumnName(field.field || field.name),
16680
+ label: snakeCaseToTitleCase(
16681
+ processColumnName(
16682
+ (field.field || field.name).replace("comparison_", "comparison ")
16683
+ )
16684
+ ),
16685
+ format: (field.field || field.name) === pivot.rowField ? "string" : pivot.aggregations?.find(
16686
+ (agg) => agg.valueField === (field.field || field.name) || `${agg.valueField}_percentage` === (field.field || field.name) || !agg.valueField && agg.aggregationType === "percentage" && (field.field || field.name)?.endsWith("percentage")
16687
+ )?.aggregationType === "percentage" ? "percent" : "whole_number",
16688
+ fieldType: field.fieldType,
16689
+ jsType: field.jsType,
16690
+ dataTypeID: field.dataTypeID
16691
+ })).filter(
16692
+ (field, index) => field.field !== "comparison_" + pivot.rowField || index === 0
16693
+ ).sort((a, b) => {
16694
+ if (a.field === pivot.rowField) {
16695
+ return -1;
16837
16696
  }
16838
- return {
16839
- ...row,
16840
- [`comparison_${pivot.valueField}${aggregationSuffix}`]: matchingCompRow ? matchingCompRow[`${pivot.valueField}${aggregationSuffix}`] : null
16841
- };
16697
+ if (b.field === pivot.rowField) {
16698
+ return 1;
16699
+ }
16700
+ return 0;
16842
16701
  });
16843
- }
16844
- function merge1DDatePivotRows(pivot, rows, compRows, databaseType, dateBucket, comparisonInterval) {
16845
- if (!comparisonInterval || !pivot.rowField) {
16846
- return rows;
16847
- }
16848
- return rows.map((row) => {
16849
- const rowDate = typeof row[pivot.rowField] === "string" ? new Date(row[pivot.rowField]) : row[pivot.rowField];
16850
- const truncatedRowDate = dateTrunc(rowDate, dateBucket, databaseType);
16851
- const matchingCompRow = compRows.find((compRow) => {
16852
- const comparisonDateWithInterval = dateAdder(
16853
- compRow[`${pivot.rowField}`],
16854
- comparisonInterval
16855
- );
16856
- const truncatedCompRowDate = dateTrunc(
16857
- comparisonDateWithInterval,
16702
+ if (pivot.rowField && !isStringType(pivot.rowFieldType || "")) {
16703
+ rows.forEach((row) => {
16704
+ row.__quillRawDate = typeof row[pivot.rowField || ""] === "object" ? row[pivot.rowField || ""].value : row[pivot.rowField || ""];
16705
+ let value = typeof row[pivot.rowField || ""] === "object" ? row[pivot.rowField || ""].value : row[pivot.rowField || ""];
16706
+ if (dateBucket === "week" && dateFilter?.startDate && dateFilter?.endDate) {
16707
+ const rowDate = new Date(value);
16708
+ if (rowDate < dateFilter.startDate) {
16709
+ value = dateFilter.startDate.toISOString();
16710
+ } else if (rowDate > dateFilter.endDate) {
16711
+ value = dateFilter.endDate.toISOString();
16712
+ }
16713
+ }
16714
+ const dateString = getDateString(
16715
+ value,
16716
+ dateFilter?.startDate && dateFilter?.endDate ? { start: dateFilter.startDate, end: dateFilter.endDate } : void 0,
16858
16717
  dateBucket,
16859
16718
  databaseType
16860
16719
  );
16861
- return truncatedRowDate.toISOString() === truncatedCompRowDate.toISOString();
16720
+ row[pivot.rowField || ""] = dateString;
16862
16721
  });
16863
- let aggregationSuffix = "";
16864
- if (matchingCompRow && pivot.aggregationType === "percentage") {
16865
- if (matchingCompRow[`${pivot.valueField}_Percentage`]) {
16866
- aggregationSuffix = "_Percentage";
16867
- } else if (matchingCompRow[`${pivot.valueField}_PERCENTAGE`]) {
16868
- aggregationSuffix = "_PERCENTAGE";
16869
- } else {
16870
- aggregationSuffix = "_percentage";
16722
+ if (pivot.rowField && pivot.rowFieldType && !isStringType(pivot.rowFieldType) && dateFilter?.startDate && dateFilter?.endDate) {
16723
+ const dateSet = new Set(
16724
+ rows.map((row) => row[pivot.rowField || ""])
16725
+ );
16726
+ for (let date = dateFilter.startDate; date <= dateFilter.endDate; date = new Date(date.getTime() + 24 * 60 * 60 * 1e3)) {
16727
+ const formattedDate = getDateString(
16728
+ date.toISOString(),
16729
+ { start: dateFilter.startDate, end: dateFilter.endDate },
16730
+ dateBucket,
16731
+ databaseType
16732
+ );
16733
+ if (!dateSet.has(formattedDate)) {
16734
+ const newRow = {};
16735
+ newRow[pivot.rowField] = formattedDate;
16736
+ newRow.__quillRawDate = date.toISOString();
16737
+ rows.push(newRow);
16738
+ dateSet.add(formattedDate);
16739
+ }
16871
16740
  }
16872
16741
  }
16873
- return {
16874
- ...row,
16875
- [`comparison_${pivot.valueField}${aggregationSuffix}`]: matchingCompRow?.[`${pivot.valueField}${aggregationSuffix}`] || null
16876
- };
16742
+ if (!pivot.sort) {
16743
+ rows.sort((a, b) => {
16744
+ if (a.__quillRawDate < b.__quillRawDate) {
16745
+ return -1;
16746
+ }
16747
+ if (a.__quillRawDate > b.__quillRawDate) {
16748
+ return 1;
16749
+ }
16750
+ return 0;
16751
+ });
16752
+ }
16753
+ }
16754
+ columns?.forEach((column, index) => {
16755
+ if (column.label && ["null", "undefined"].includes(column.label.toLowerCase()) && !pivot.columnField && !pivot.aggregations?.[index]?.valueField && pivot.aggregations?.[index]?.aggregationType === "count") {
16756
+ column.label = "Count";
16757
+ }
16758
+ });
16759
+ const numericColumns = columns?.filter(
16760
+ (column) => column.format === "whole_number" || column.format === "percentage"
16761
+ );
16762
+ rows.forEach((row) => {
16763
+ numericColumns?.forEach((column) => {
16764
+ row[column.field] = row[column.field] ?? 0;
16765
+ });
16877
16766
  });
16767
+ return {
16768
+ rows,
16769
+ columns: columns ?? [],
16770
+ rowCount: rowCount || rows.length,
16771
+ pivotQuery: report?.queryString || "",
16772
+ comparisonPivotQuery: comparisonPivotQuery || ""
16773
+ };
16878
16774
  }
16879
- function mergeAggregatedRows(pivot, rows, compRows) {
16880
- return rows.map((row, i) => {
16881
- let aggregationSuffix = "";
16882
- if (compRows[i] && pivot.aggregationType === "percentage") {
16883
- if (compRows[i][`${pivot.valueField}_Percentage`]) {
16884
- aggregationSuffix = "_Percentage";
16885
- } else if (compRows[i][`${pivot.valueField}_PERCENTAGE`]) {
16886
- aggregationSuffix = "_PERCENTAGE";
16887
- } else {
16888
- aggregationSuffix = "_percentage";
16775
+ function generatePivotTableYAxis(pivot, cols, yAxisField) {
16776
+ if (pivot?.aggregationType === "count") {
16777
+ return [
16778
+ {
16779
+ field: pivot.valueField ?? "count",
16780
+ label: yAxisField.label,
16781
+ format: yAxisField.format
16889
16782
  }
16783
+ ];
16784
+ }
16785
+ return [
16786
+ {
16787
+ field: pivot.valueField ?? "count",
16788
+ label: yAxisField.label,
16789
+ format: yAxisField.format
16890
16790
  }
16891
- const compRow = {
16892
- [`comparison_${pivot.valueField}${aggregationSuffix}`]: compRows[i]?.[`${pivot.valueField}${aggregationSuffix}`]
16893
- };
16894
- return { ...row, ...compRow };
16895
- });
16791
+ ];
16896
16792
  }
16897
-
16898
- // src/utils/queryConstructor.ts
16899
- function processSingleQuotes(value, databaseType) {
16900
- if (["postgresql", "snowflake", "clickhouse"].includes(
16901
- databaseType.toLowerCase()
16902
- )) {
16903
- return value.replaceAll("'", "''");
16793
+ function generatePivotTitle(pivot) {
16794
+ if (pivot.rowField && !pivot.valueField && pivot.aggregations?.[0]) {
16795
+ return snakeAndCamelCaseToTitleCase(
16796
+ `${pivot.aggregations[0].aggregationType} of ${pivot.rowField}${pivot.columnField ? ` by ${pivot.columnField}` : ""}
16797
+ `
16798
+ );
16799
+ } else if (!pivot.rowField && pivot.aggregations?.[0]?.valueField) {
16800
+ return snakeAndCamelCaseToTitleCase(
16801
+ `${pivot.aggregations[0].aggregationType} of ${pivot.aggregations[0].valueField}
16802
+ `
16803
+ );
16904
16804
  }
16905
- return value.replaceAll("'", "\\'");
16906
- }
16907
- function processAggType(aggType, hasColumnField = false) {
16908
- if (aggType === "count" && hasColumnField) return "SUM";
16909
- return aggType?.toLowerCase() === "average" ? "AVG" : aggType?.toLowerCase();
16910
- }
16911
- function replaceBigQuerySpecialCharacters(column) {
16912
- return column.replaceAll("/", "quill_forward_slash");
16805
+ return snakeAndCamelCaseToTitleCase(
16806
+ `${pivot.aggregations?.[0]?.aggregationType ?? "Aggregation"} of ${pivot.aggregations?.[0]?.valueField ?? "value"}${pivot.rowField ? ` by ${pivot.rowField}` : ""}${pivot.columnField ? ` and ${pivot.columnField}` : ""}`
16807
+ );
16913
16808
  }
16914
- function processColumnReference(column, databaseType, fallbackOnNull, isColumnFieldAlias, isValueFieldAlias) {
16915
- switch (databaseType.toLowerCase()) {
16916
- case "postgresql":
16917
- case "clickhouse": {
16918
- if (column === "") {
16919
- return fallbackOnNull ? `"${fallbackOnNull}"` : `"_"`;
16920
- }
16921
- if (isColumnFieldAlias) {
16922
- return `"${column.replaceAll('"', "")}"`;
16923
- }
16924
- const columnParts = column.split(".");
16925
- if (columnParts.length > 1) {
16926
- return `"` + columnParts.map((part) => part.replaceAll('"', "")).join('"."') + `"`;
16927
- }
16928
- return `"${column.replaceAll('"', "")}"`;
16929
- }
16930
- case "mysql": {
16931
- if (column === "") {
16932
- return fallbackOnNull ? `${fallbackOnNull}` : "_";
16933
- }
16934
- if (isColumnFieldAlias) {
16935
- return `\`${column.replaceAll(`\``, "").replaceAll(`"`, "")}\``;
16936
- }
16937
- const columnParts = column.split(".");
16938
- if (columnParts.length > 1) {
16939
- return `\`` + columnParts.map((part) => part.replaceAll(`\``, "")).join("`.`") + `\``;
16940
- }
16941
- return `\`${column.replaceAll(`\``, "")}\``;
16942
- }
16943
- case "snowflake": {
16944
- if (column === "") {
16945
- return fallbackOnNull ? `${fallbackOnNull}` : "_";
16946
- }
16947
- if (isColumnFieldAlias) {
16948
- return `"${column.replaceAll('"', "")}"`;
16949
- }
16950
- if (isValueFieldAlias) {
16951
- const cleanedColumn = column.replaceAll(")", "").replaceAll("(", "_");
16952
- return `${cleanedColumn}`;
16953
- }
16954
- return column;
16955
- }
16956
- case "bigquery": {
16957
- if (column === "") {
16958
- return fallbackOnNull ? `\`${fallbackOnNull}\`` : "`_`";
16959
- }
16960
- if (isColumnFieldAlias) {
16961
- return `\`${replaceBigQuerySpecialCharacters(column.replaceAll("`", ""))}\``;
16962
- }
16963
- const columnParts = column.split(".");
16964
- if (columnParts.length > 1) {
16965
- return `\`` + columnParts.map((part) => part.replaceAll(`\``, "")).join("`.`") + `\``;
16966
- }
16967
- return `\`${replaceBigQuerySpecialCharacters(column.replaceAll("`", ""))}\``;
16968
- }
16969
- case "mssql": {
16970
- if (column === "") {
16971
- return fallbackOnNull ? `[${fallbackOnNull}]` : `[_]`;
16972
- }
16973
- if (isColumnFieldAlias) {
16974
- return `[${column.replaceAll("]", "").replaceAll("[", "")}]`;
16975
- }
16976
- const columnParts = column.split(".");
16977
- if (columnParts.length > 1) {
16978
- return `[` + columnParts.map((part) => part.replaceAll("]", "").replaceAll("[", "")).join("].[`") + `]`;
16979
- }
16980
- return `[${column.replaceAll("]", "").replaceAll("[", "")}]`;
16809
+ async function generatePivotTable({
16810
+ pivot,
16811
+ dateBucket,
16812
+ dateFilter,
16813
+ report,
16814
+ client,
16815
+ getToken,
16816
+ eventTracking,
16817
+ uniqueValues,
16818
+ dashboardName,
16819
+ tenants,
16820
+ additionalProcessing,
16821
+ caller,
16822
+ pivotQuery
16823
+ }) {
16824
+ try {
16825
+ if (report && client) {
16826
+ const pivotTable = await generatePivotWithSQL({
16827
+ pivotQuery,
16828
+ pivot,
16829
+ report,
16830
+ client,
16831
+ dateBucket,
16832
+ dateFilter,
16833
+ dashboardName,
16834
+ tenants,
16835
+ additionalProcessing,
16836
+ caller,
16837
+ getToken
16838
+ });
16839
+ return pivotTable;
16981
16840
  }
16982
- case "databricks": {
16983
- if (column === "") {
16984
- return fallbackOnNull ? `[${fallbackOnNull}]` : `_`;
16985
- }
16986
- if (isColumnFieldAlias) {
16987
- return `\`${column.replaceAll(`\``, "").replaceAll(`"`, "")}\``;
16988
- }
16989
- const columnParts = column.split(".");
16990
- if (columnParts.length > 1) {
16991
- return `\`` + columnParts.map((part) => part.replaceAll(`\``, "")).join("`.`") + `\``;
16841
+ } catch (e) {
16842
+ eventTracking?.logError?.({
16843
+ type: "bug",
16844
+ // TODO: determine type
16845
+ severity: "high",
16846
+ message: "Error generating pivot table",
16847
+ errorMessage: e.message,
16848
+ errorStack: e.stack,
16849
+ errorData: {
16850
+ caller: "PivotModal",
16851
+ function: "generatePivotTable"
16992
16852
  }
16993
- return `\`${column.replaceAll(`\``, "")}\``;
16994
- }
16995
- default:
16996
- return column;
16853
+ });
16854
+ throw Error(`Failed to generate pivot table with SQL: ${e}`);
16997
16855
  }
16856
+ throw Error("Failed to generate pivot table: invalid report");
16998
16857
  }
16999
- function replaceSpacesWithUnderscores(column) {
17000
- return column.replaceAll(" ", "_");
17001
- }
17002
- function processInterval(interval2, rowField, databaseType) {
17003
- if (["postgresql", "snowflake", "clickhouse"].includes(
17004
- databaseType.toLowerCase()
17005
- )) {
17006
- return `(${processColumnReference(rowField, databaseType)} + INTERVAL '${interval2}')`;
17007
- } else if (databaseType.toLowerCase() === "mysql") {
17008
- return `(${processColumnReference(rowField, databaseType)} + INTERVAL ${interval2})`;
16858
+
16859
+ // src/utils/paginationProcessing.ts
16860
+ var DEFAULT_PAGINATION = {
16861
+ page: 0,
16862
+ rowsPerPage: 10,
16863
+ rowsPerRequest: 600
16864
+ };
16865
+ var DEFAULT_TABLE_PAGINATION = {
16866
+ page: 0,
16867
+ rowsPerPage: 10,
16868
+ rowsPerRequest: 50
16869
+ };
16870
+ function shouldFetchMore(pagination, page, maxPage, currentRowCount) {
16871
+ if (!pagination || currentRowCount && currentRowCount >= pagination.rowsPerPage * (page + 1)) {
16872
+ return false;
17009
16873
  }
17010
- return `TIMESTAMP_ADD(${processColumnReference(rowField, databaseType)}, INTERVAL ${interval2} )`;
16874
+ const indexAdjustedPage = page;
16875
+ const pageInterval = Math.floor(
16876
+ indexAdjustedPage * pagination.rowsPerPage / pagination.rowsPerRequest
16877
+ );
16878
+ const indexAdjustedPreviousPage = maxPage;
16879
+ const previousPageInterval = Math.floor(
16880
+ indexAdjustedPreviousPage * pagination.rowsPerPage / pagination.rowsPerRequest
16881
+ );
16882
+ return pageInterval > previousPageInterval;
17011
16883
  }
17012
- function processDateTrunc(dateBucket, rowField, databaseType, comparisonInterval) {
17013
- const dateField = comparisonInterval ? processInterval(comparisonInterval, rowField, databaseType) : processColumnReference(rowField, databaseType);
17014
- if (["postgresql", "snowflake"].includes(databaseType.toLowerCase())) {
17015
- return `date_trunc('${dateBucket}', ${dateField})`;
16884
+ function shouldSortInMemory(pagination, rowCount) {
16885
+ if (!rowCount || rowCount < pagination.rowsPerRequest) {
16886
+ return true;
17016
16887
  }
17017
- if (["clickhouse"].includes(databaseType.toLowerCase())) {
17018
- return `dateTrunc('${dateBucket}', ${dateField})`;
16888
+ return false;
16889
+ }
16890
+
16891
+ // src/utils/dashboard.ts
16892
+ var defaultDashboardItem = {
16893
+ id: "",
16894
+ name: "",
16895
+ dashboardName: "",
16896
+ rows: [],
16897
+ compareRows: [],
16898
+ columns: [],
16899
+ chartType: "",
16900
+ pivot: null,
16901
+ yAxisFields: [],
16902
+ xAxisLabel: "",
16903
+ xAxisField: "",
16904
+ xAxisFormat: "string",
16905
+ order: -1,
16906
+ filtersApplied: [],
16907
+ queryString: "",
16908
+ rowCount: 0,
16909
+ columnInternal: []
16910
+ };
16911
+ async function cleanDashboardItem({
16912
+ item,
16913
+ dashboardFilters,
16914
+ getToken,
16915
+ eventTracking,
16916
+ client,
16917
+ dateBucket,
16918
+ additionalProcessing,
16919
+ customFields,
16920
+ skipPivotFetch,
16921
+ tenants
16922
+ }) {
16923
+ if (!item) return defaultDashboardItem;
16924
+ if (!item.rows) {
16925
+ return {
16926
+ ...defaultDashboardItem,
16927
+ id: item._id,
16928
+ name: item.name
16929
+ };
17019
16930
  }
17020
- if (["databricks"].includes(databaseType.toLowerCase())) {
17021
- return `DATE_TRUNC('${dateBucket}', ${dateField})`;
16931
+ const fields = item.fields || [];
16932
+ const columnsWithCustomFields = [...item.columns ?? []];
16933
+ if (item.includeCustomFields && item.rows?.length > 0) {
16934
+ const tables = item.referencedTables ?? [];
16935
+ tables.forEach((table) => {
16936
+ const _customFields = customFields?.[table] ?? [];
16937
+ _customFields.forEach((field) => {
16938
+ const isJsonCustomField = !!field.refColumn;
16939
+ if (item.rows[0][field.field] !== void 0 && !item.columns.some((col) => col.field === field.field)) {
16940
+ const result_field = fields.find((f) => f.name === field.field);
16941
+ const converted_field = convertPostgresColumn(result_field ?? {});
16942
+ columnsWithCustomFields.push({
16943
+ field: field.field,
16944
+ format: converted_field.format,
16945
+ label: snakeAndCamelCaseToTitleCase(field.field),
16946
+ fieldType: converted_field.fieldType,
16947
+ dataTypeID: converted_field.dataTypeID,
16948
+ jsType: converted_field.jsType,
16949
+ inferFormat: isJsonCustomField
16950
+ });
16951
+ }
16952
+ });
16953
+ });
17022
16954
  }
17023
- if (["mysql"].includes(databaseType.toLowerCase())) {
17024
- switch (dateBucket.toLowerCase()) {
17025
- case "year":
17026
- return `DATE_FORMAT(${dateField}, '%Y-01-01 00:00:00')`;
17027
- case "month":
17028
- return `DATE_FORMAT(${dateField}, '%Y-%m-01 00:00:00')`;
17029
- case "week":
17030
- return `DATE_FORMAT(${dateField}, '%Y-%U-1 00:00:00')`;
17031
- case "day":
17032
- default:
17033
- return `DATE(${dateField})`;
16955
+ const processedColumns = item.columns.map((col) => {
16956
+ return { ...col, label: snakeAndCamelCaseToTitleCase(col.label) };
16957
+ });
16958
+ const columnInternal = (item.includeCustomFields ? columnsWithCustomFields : item.columns).map((col) => {
16959
+ const field = item.fields?.find((f) => f.name === col.field);
16960
+ const converted_field = convertPostgresColumn(field ?? {});
16961
+ return {
16962
+ fieldType: converted_field.fieldType,
16963
+ dataTypeID: converted_field.dataTypeID,
16964
+ jsType: converted_field.jsType,
16965
+ ...col,
16966
+ label: snakeAndCamelCaseToTitleCase(col.label)
16967
+ };
16968
+ });
16969
+ let pivotTable;
16970
+ let pivotError;
16971
+ try {
16972
+ const shouldPaginatePivotAsTable = item.chartType === "table";
16973
+ const pivotChartProcessing = {
16974
+ page: DEFAULT_PAGINATION
16975
+ };
16976
+ pivotTable = await getPivotTable(
16977
+ {
16978
+ ...item,
16979
+ pivot: item.pivot && !skipPivotFetch ? {
16980
+ ...item.pivot,
16981
+ aggregations: item.pivot.aggregations ?? [
16982
+ {
16983
+ valueField: item.pivot.valueField,
16984
+ valueFieldType: item.pivot.valueFieldType,
16985
+ valueField2: item.pivot.valueField2,
16986
+ valueField2Type: item.pivot.valueField2Type,
16987
+ aggregationType: item.pivot.aggregationType
16988
+ }
16989
+ ]
16990
+ } : void 0
16991
+ },
16992
+ dashboardFilters,
16993
+ item.dashboardName,
16994
+ getToken,
16995
+ client,
16996
+ eventTracking,
16997
+ dateBucket,
16998
+ shouldPaginatePivotAsTable ? additionalProcessing : pivotChartProcessing,
16999
+ tenants,
17000
+ customFields
17001
+ );
17002
+ } catch (e) {
17003
+ pivotTable = void 0;
17004
+ eventTracking?.logError?.({
17005
+ type: "bug",
17006
+ // TODO: determine type
17007
+ severity: "high",
17008
+ message: "Error fetching pivot table",
17009
+ errorMessage: e.message,
17010
+ errorStack: e.stack,
17011
+ errorData: {
17012
+ caller: "cleanDashboardItem",
17013
+ function: "cleanDashboardItem"
17014
+ }
17015
+ });
17016
+ console.error("Error fetching pivot table", e);
17017
+ pivotError = "Error fetching pivot table";
17018
+ }
17019
+ const referenceLineYValues = [];
17020
+ for (const key in item) {
17021
+ if (key.startsWith("referenceLine_")) {
17022
+ const field = key.slice(14);
17023
+ if (!item[key] || !item[key][0]) continue;
17024
+ const value = Object.values(item[key][0])[0];
17025
+ referenceLineYValues.push({ label: field, query: [value, value] });
17034
17026
  }
17035
17027
  }
17036
- if (["mssql"].includes(databaseType.toLowerCase())) {
17037
- return `DATETRUNC(${dateBucket}, ${dateField})`;
17028
+ if (item.referenceLines) {
17029
+ for (const referenceLine of item.referenceLines) {
17030
+ if (Array.isArray(referenceLine.query)) {
17031
+ referenceLineYValues.push({
17032
+ label: referenceLine.label,
17033
+ query: referenceLine.query
17034
+ });
17035
+ } else if (referenceLine.query === "") {
17036
+ referenceLineYValues.push({
17037
+ label: referenceLine.label,
17038
+ query: (pivotTable?.rows ? pivotTable.rows : item.rows).map(
17039
+ (row) => Number(row[referenceLine.label]) || 0
17040
+ )
17041
+ });
17042
+ }
17043
+ }
17038
17044
  }
17039
- return `TIMESTAMP_TRUNC(${dateField}, ${dateBucket})`;
17045
+ return {
17046
+ id: item._id ?? item.id,
17047
+ name: item.name,
17048
+ dashboardName: item.dashboardName,
17049
+ // section: item.section,
17050
+ rows: item.rows,
17051
+ pivotRows: pivotTable ? pivotTable.rows : void 0,
17052
+ pivotColumns: pivotTable ? pivotTable.columns : void 0,
17053
+ compareRows: item.compareRows,
17054
+ columns: processedColumns.map((column) => {
17055
+ return {
17056
+ field: column.field,
17057
+ format: column.format,
17058
+ label: column.label,
17059
+ inferFormat: column.inferFormat
17060
+ };
17061
+ }),
17062
+ includeCustomFields: item.includeCustomFields,
17063
+ columnInternal,
17064
+ columnsWithCustomFields,
17065
+ chartType: item.chartType,
17066
+ dateField: item.dateField,
17067
+ pivot: pivotError ? void 0 : item.pivot ? {
17068
+ ...item.pivot,
17069
+ aggregations: item.pivot.aggregations ?? [
17070
+ {
17071
+ valueField: item.pivot.valueField,
17072
+ valueFieldType: item.pivot.valueFieldType,
17073
+ valueField2: item.pivot.valueField2,
17074
+ valueField2Type: item.pivot.valueField2Type,
17075
+ aggregationType: item.pivot.aggregationType
17076
+ }
17077
+ ],
17078
+ columnValues: item.distinctStrings
17079
+ } : void 0,
17080
+ yAxisFields: pivotTable ? extractPivotedYAxis(pivotTable, item) : item.yAxisFields,
17081
+ xAxisLabel: item.xAxisLabel,
17082
+ xAxisField: item.xAxisField,
17083
+ xAxisFormat: item.xAxisFormat,
17084
+ order: item.order,
17085
+ filtersApplied: item.filtersApplied,
17086
+ filterMap: item.filterMap,
17087
+ flags: item.flags,
17088
+ rowCount: item.rowCount ? parseInt(item.rowCount) : item.rows.length,
17089
+ pivotRowCount: pivotTable ? pivotTable.rowCount : void 0,
17090
+ template: item.template,
17091
+ sort: item.sort,
17092
+ itemQuery: item.itemQuery,
17093
+ queryString: item.queryString,
17094
+ pivotQuery: pivotTable?.pivotQuery,
17095
+ comparisonPivotQuery: pivotTable?.comparisonPivotQuery,
17096
+ referencedTables: item?.referencedTables || [],
17097
+ referencedColumns: item?.referencedColumns || {},
17098
+ error: item.error ?? pivotError,
17099
+ referenceLineYValues,
17100
+ referenceLines: item.referenceLines
17101
+ };
17040
17102
  }
17041
- function processValueField(aggType, databaseType, valueField) {
17042
- if (aggType === "min" || aggType === "max" || aggType?.toLowerCase() === "average")
17043
- return `${processColumnReference(valueField, databaseType)} ELSE null`;
17044
- if (aggType === "count") return `1 ELSE 0`;
17045
- return valueField ? `${processColumnReference(valueField, databaseType)} ELSE 0` : `1 ELSE 0`;
17103
+ async function getPivotTable(report, dashboardFilters, dashboardName, getToken, client, eventTracking, dateBucketInitial, additionalProcessing, tenants, customFields) {
17104
+ if (!report) return void 0;
17105
+ const dateFilter = Object.values(dashboardFilters ?? {}).find(
17106
+ (filter) => filter.filterType === "date_range" || filter.operator === "BETWEEN"
17107
+ );
17108
+ if (dateFilter?.operator === "BETWEEN") {
17109
+ dateFilter.startDate = dateFilter.value[0];
17110
+ dateFilter.endDate = dateFilter.value[1];
17111
+ }
17112
+ const pivot = report?.pivot;
17113
+ const data = report || {};
17114
+ if (pivot && client) {
17115
+ if (report.rowCount === 0 || report.rows.length === 0) {
17116
+ const columns = [];
17117
+ if (pivot.rowField) {
17118
+ columns.push({
17119
+ field: pivot.rowField,
17120
+ label: snakeCaseToTitleCase(processColumnName(pivot.rowField)),
17121
+ format: pivot.rowFieldType || "string",
17122
+ jsType: convertFieldTypeToJSType(pivot.rowFieldType || "string"),
17123
+ fieldType: pivot.rowFieldType || "string",
17124
+ dataTypeID: fieldTypeToDataTypeID(pivot.rowFieldType || "string")
17125
+ });
17126
+ }
17127
+ for (const agg of pivot.aggregations ?? []) {
17128
+ if (agg.valueField) {
17129
+ columns.push({
17130
+ field: agg.valueField,
17131
+ label: snakeCaseToTitleCase(processColumnName(agg.valueField)),
17132
+ //FIXME: valueFieldType is not always the same as the format
17133
+ format: agg.valueFieldType ?? "whole_number",
17134
+ jsType: agg.valueFieldType ?? "number",
17135
+ fieldType: agg.valueFieldType ?? "number",
17136
+ dataTypeID: fieldTypeToDataTypeID(agg.valueFieldType ?? "number")
17137
+ });
17138
+ }
17139
+ }
17140
+ return {
17141
+ rows: [],
17142
+ rowCount: 0,
17143
+ pivotQuery: report.queryString ?? "",
17144
+ columns
17145
+ };
17146
+ }
17147
+ try {
17148
+ let dateBucket = dateBucketInitial;
17149
+ let filterDateRange = void 0;
17150
+ if (dateFilter && dateFilter.startDate && dateFilter.endDate) {
17151
+ filterDateRange = {
17152
+ start: dateFilter.startDate,
17153
+ end: dateFilter.endDate
17154
+ };
17155
+ } else if (report.dateRange) {
17156
+ filterDateRange = report.dateRange;
17157
+ }
17158
+ if (!dateBucket && filterDateRange) {
17159
+ dateBucket = getDateBucketFromRange(filterDateRange);
17160
+ }
17161
+ const pivotTable = await generatePivotWithSQL({
17162
+ pivot,
17163
+ report,
17164
+ client,
17165
+ dateBucket,
17166
+ dateFilter,
17167
+ dashboardName,
17168
+ tenants,
17169
+ additionalProcessing,
17170
+ getToken
17171
+ });
17172
+ return pivotTable;
17173
+ } catch (e) {
17174
+ eventTracking?.logError?.({
17175
+ type: "bug",
17176
+ // TODO: determine type
17177
+ severity: "high",
17178
+ message: "Error fetching pivot table",
17179
+ errorMessage: e.message,
17180
+ errorStack: e.stack,
17181
+ errorData: {
17182
+ caller: "getPivotTable",
17183
+ function: "getPivotTable"
17184
+ }
17185
+ });
17186
+ console.error("Error fetching pivot table", e);
17187
+ throw e;
17188
+ }
17189
+ }
17190
+ return pivot && data.rows ? generatePivotTable({
17191
+ pivot,
17192
+ report,
17193
+ client,
17194
+ uniqueValues: report.distinctStrings,
17195
+ dashboardName,
17196
+ tenants,
17197
+ dateFilter,
17198
+ additionalProcessing,
17199
+ getToken,
17200
+ eventTracking
17201
+ }) : void 0;
17202
+ }
17203
+ function extractPivotedYAxis(pivotTable, itemInfo, config = void 0) {
17204
+ if (!pivotTable) return itemInfo?.yAxisFields ?? [];
17205
+ const pivot = itemInfo?.pivot || config?.pivot;
17206
+ if (!pivot.columnField && !pivot.rowField) return itemInfo?.yAxisFields ?? [];
17207
+ const yAxisFields = config ? config.yAxisFields : itemInfo?.yAxisFields;
17208
+ return yAxisFields && yAxisFields.length > 0 ? generatePivotTableYAxis(pivot, pivotTable.columns, yAxisFields[0]) : yAxisFields;
17209
+ }
17210
+ async function getDashboard(dashboardName, client, getToken, tenants, flags) {
17211
+ const { data: resp } = await quillFetch({
17212
+ client,
17213
+ task: "dashboard",
17214
+ metadata: {
17215
+ name: dashboardName,
17216
+ clientId: client.publicKey,
17217
+ databaseType: client.databaseType,
17218
+ useNewNodeSql: true,
17219
+ tenants,
17220
+ flags
17221
+ },
17222
+ getToken
17223
+ });
17224
+ return {
17225
+ ...resp,
17226
+ createdAt: resp.createdAt && new Date(resp.createdAt),
17227
+ dateFilter: resp.dateFilter ? {
17228
+ ...resp.dateFilter,
17229
+ presetOptions: resp.dateFilter.presetOptions?.map(
17230
+ (preset) => ({
17231
+ ...preset,
17232
+ loopStart: preset.loopStart ? new Date(preset.loopStart) : void 0,
17233
+ loopEnd: preset.loopEnd ? new Date(preset.loopEnd) : void 0
17234
+ })
17235
+ ),
17236
+ defaultPresetRanges: resp.dateFilter.defaultPresetRanges?.map(
17237
+ (preset) => ({
17238
+ ...preset,
17239
+ loopStart: preset.loopStart ? new Date(preset.loopStart) : void 0,
17240
+ loopEnd: preset.loopEnd ? new Date(preset.loopEnd) : void 0
17241
+ })
17242
+ )
17243
+ } : void 0
17244
+ };
17245
+ }
17246
+
17247
+ // src/utils/chartBuilder.ts
17248
+ var numberFormatOptions = [
17249
+ "whole_number",
17250
+ "one_decimal_place",
17251
+ "two_decimal_places",
17252
+ "dollar_amount",
17253
+ "dollar_cents",
17254
+ "percentage"
17255
+ ];
17256
+ var dateFormatOptions = [
17257
+ "MMM_yyyy",
17258
+ "MMM_dd",
17259
+ "MMM_dd_yyyy",
17260
+ "MMM_dd_hh:mm_ap_pm",
17261
+ "hh_ap_pm",
17262
+ "date",
17263
+ "timestamptz"
17264
+ ];
17265
+ var NUMBER_OPTIONS = [
17266
+ { value: "whole_number", label: "whole number" },
17267
+ { value: "one_decimal_place", label: "one decimal place" },
17268
+ { value: "two_decimal_places", label: "two decimal places" },
17269
+ { value: "dollar_amount", label: "dollar amount" },
17270
+ { value: "dollar_cents", label: "dollar and cent amount" },
17271
+ { value: "percent", label: "percentage" }
17272
+ ];
17273
+ var DATE_OPTIONS = [
17274
+ { value: "MMM_yyyy", label: "month" },
17275
+ { value: "MMM_dd", label: "day" },
17276
+ { value: "MMM_dd_yyyy", label: "day and year" },
17277
+ { value: "MMM_dd_hh:mm_ap_pm", label: "day and time" },
17278
+ { value: "hh_ap_pm", label: "hour" }
17279
+ ];
17280
+ var ALL_FORMAT_OPTIONS = [
17281
+ ...NUMBER_OPTIONS,
17282
+ ...DATE_OPTIONS,
17283
+ { value: "string", label: "string" }
17284
+ ];
17285
+ function createInitialFormData(columns) {
17286
+ const firstNumberColumn = columns?.find(
17287
+ (col) => numberFormatOptions.includes(col.format)
17288
+ );
17289
+ const firstStringColumn = columns?.find(
17290
+ (col) => !numberFormatOptions.includes(col.format) && !dateFormatOptions.includes(col.format)
17291
+ );
17292
+ const firstDateColumn = columns?.find(
17293
+ (col) => dateFormatOptions.includes(col.format)
17294
+ );
17295
+ const xAxisField = firstStringColumn?.field || firstDateColumn?.field || firstNumberColumn?.field || columns?.[0]?.field || "";
17296
+ const xAxisFormat = firstStringColumn?.format || firstDateColumn?.format || firstNumberColumn?.format || columns?.[0]?.format || "string";
17297
+ const formEmptyState = {
17298
+ name: "",
17299
+ columns: columns.map((col) => {
17300
+ return { ...col, label: snakeAndCamelCaseToTitleCase(col.label) };
17301
+ }),
17302
+ xAxisField,
17303
+ xAxisFormat,
17304
+ yAxisFields: [
17305
+ {
17306
+ field: firstNumberColumn?.field || columns?.[0]?.field || "",
17307
+ label: "",
17308
+ format: firstNumberColumn?.format || columns?.[0]?.format || "string"
17309
+ }
17310
+ ],
17311
+ xAxisLabel: "",
17312
+ chartType: firstNumberColumn ? "line" : "table",
17313
+ pivot: null,
17314
+ template: true,
17315
+ referenceLines: []
17316
+ };
17317
+ return formEmptyState;
17318
+ }
17319
+
17320
+ // src/utils/error.ts
17321
+ var DataLoadError = class extends Error {
17322
+ data;
17323
+ constructor(message, data) {
17324
+ super(message);
17325
+ this.name = "DataLoadError";
17326
+ this.data = data;
17327
+ }
17328
+ };
17329
+
17330
+ // src/utils/errorProcessing.ts
17331
+ function processFilterErrorList(resp) {
17332
+ if (!resp || !resp.filterErrorList || !Array.isArray(resp.filterErrorList)) {
17333
+ return;
17334
+ }
17335
+ }
17336
+
17337
+ // src/utils/queryConstructor.ts
17338
+ function replaceBigQuerySpecialCharacters(column) {
17339
+ return column.replaceAll("/", "quill_forward_slash");
17340
+ }
17341
+ function processColumnReference(column, databaseType, fallbackOnNull, isColumnFieldAlias, isValueFieldAlias) {
17342
+ switch (databaseType.toLowerCase()) {
17343
+ case "postgresql":
17344
+ case "clickhouse": {
17345
+ if (column === "") {
17346
+ return fallbackOnNull ? `"${fallbackOnNull}"` : `"_"`;
17347
+ }
17348
+ if (isColumnFieldAlias) {
17349
+ return `"${column.replaceAll('"', "")}"`;
17350
+ }
17351
+ const columnParts = column.split(".");
17352
+ if (columnParts.length > 1) {
17353
+ return `"` + columnParts.map((part) => part.replaceAll('"', "")).join('"."') + `"`;
17354
+ }
17355
+ return `"${column.replaceAll('"', "")}"`;
17356
+ }
17357
+ case "mysql": {
17358
+ if (column === "") {
17359
+ return fallbackOnNull ? `${fallbackOnNull}` : "_";
17360
+ }
17361
+ if (isColumnFieldAlias) {
17362
+ return `\`${column.replaceAll(`\``, "").replaceAll(`"`, "")}\``;
17363
+ }
17364
+ const columnParts = column.split(".");
17365
+ if (columnParts.length > 1) {
17366
+ return `\`` + columnParts.map((part) => part.replaceAll(`\``, "")).join("`.`") + `\``;
17367
+ }
17368
+ return `\`${column.replaceAll(`\``, "")}\``;
17369
+ }
17370
+ case "snowflake": {
17371
+ if (column === "") {
17372
+ return fallbackOnNull ? `${fallbackOnNull}` : "_";
17373
+ }
17374
+ if (isColumnFieldAlias) {
17375
+ return `"${column.replaceAll('"', "")}"`;
17376
+ }
17377
+ if (isValueFieldAlias) {
17378
+ const cleanedColumn = column.replaceAll(")", "").replaceAll("(", "_");
17379
+ return `${cleanedColumn}`;
17380
+ }
17381
+ return column;
17382
+ }
17383
+ case "bigquery": {
17384
+ if (column === "") {
17385
+ return fallbackOnNull ? `\`${fallbackOnNull}\`` : "`_`";
17386
+ }
17387
+ if (isColumnFieldAlias) {
17388
+ return `\`${replaceBigQuerySpecialCharacters(column.replaceAll("`", ""))}\``;
17389
+ }
17390
+ const columnParts = column.split(".");
17391
+ if (columnParts.length > 1) {
17392
+ return `\`` + columnParts.map((part) => part.replaceAll(`\``, "")).join("`.`") + `\``;
17393
+ }
17394
+ return `\`${replaceBigQuerySpecialCharacters(column.replaceAll("`", ""))}\``;
17395
+ }
17396
+ case "mssql": {
17397
+ if (column === "") {
17398
+ return fallbackOnNull ? `[${fallbackOnNull}]` : `[_]`;
17399
+ }
17400
+ if (isColumnFieldAlias) {
17401
+ return `[${column.replaceAll("]", "").replaceAll("[", "")}]`;
17402
+ }
17403
+ const columnParts = column.split(".");
17404
+ if (columnParts.length > 1) {
17405
+ return `[` + columnParts.map((part) => part.replaceAll("]", "").replaceAll("[", "")).join("].[`") + `]`;
17406
+ }
17407
+ return `[${column.replaceAll("]", "").replaceAll("[", "")}]`;
17408
+ }
17409
+ case "databricks": {
17410
+ if (column === "") {
17411
+ return fallbackOnNull ? `[${fallbackOnNull}]` : `_`;
17412
+ }
17413
+ if (isColumnFieldAlias) {
17414
+ return `\`${column.replaceAll(`\``, "").replaceAll(`"`, "")}\``;
17415
+ }
17416
+ const columnParts = column.split(".");
17417
+ if (columnParts.length > 1) {
17418
+ return `\`` + columnParts.map((part) => part.replaceAll(`\``, "")).join("`.`") + `\``;
17419
+ }
17420
+ return `\`${column.replaceAll(`\``, "")}\``;
17421
+ }
17422
+ default:
17423
+ return column;
17424
+ }
17425
+ }
17426
+ function replaceSpacesWithUnderscores(column) {
17427
+ return column.replaceAll(" ", "_");
17046
17428
  }
17047
17429
  function generateCountQuery(fields, query, databaseType) {
17048
17430
  let countQuery = [];
@@ -17162,1123 +17544,12 @@ function generateMinMaxDateRangeQueries(columnFields, query, databaseType) {
17162
17544
  const distinctQueries = columnFields.map((field) => {
17163
17545
  const wrappedField = ["postgresql", "clickhouse"].includes(
17164
17546
  databaseType.toLowerCase()
17165
- ) ? `"${field}"` : field;
17166
- return `SELECT '${field}' AS field, ${min2}(${wrappedField})${cast} AS min_range, ${max2}(${wrappedField})${cast} AS max_range FROM querytable`;
17547
+ ) ? processColumnReference(field, databaseType) : field;
17548
+ return `SELECT '${field}' AS ${processColumnReference("field", databaseType)}, ${min2}(${wrappedField})${cast} AS ${processColumnReference("min_range", databaseType)}, ${max2}(${wrappedField})${cast} AS ${processColumnReference("max_range", databaseType)} FROM querytable`;
17167
17549
  });
17168
17550
  const distinctQuery = distinctQueries.join(" UNION ALL ");
17169
17551
  return `WITH querytable AS (${query.replace(";", "")}) ` + distinctQuery;
17170
17552
  }
17171
- function generatePivotQuery(pivot, itemQuery, databaseType, distinctStrings, dateBucket) {
17172
- if (!isValidPivot(pivot).valid) {
17173
- return void 0;
17174
- }
17175
- if (pivot.columnField && distinctStrings && distinctStrings.length > 0) {
17176
- return create2DPivotQuery(
17177
- pivot,
17178
- itemQuery,
17179
- databaseType,
17180
- distinctStrings,
17181
- dateBucket
17182
- );
17183
- }
17184
- if (pivot.rowField) {
17185
- return create1DPivotQuery(pivot, itemQuery, dateBucket, databaseType);
17186
- }
17187
- return createAggregationValuePivot(pivot, itemQuery, databaseType);
17188
- }
17189
- function create2DPivotQuery(pivot, itemQuery, databaseType, columnFieldValues, dateBucket) {
17190
- if (!pivot || !pivot.columnField) {
17191
- return void 0;
17192
- }
17193
- if (isStringType(pivot.rowFieldType ?? "") || !pivot.rowFieldType) {
17194
- return create2DStringPivotQuery(
17195
- pivot,
17196
- itemQuery,
17197
- columnFieldValues,
17198
- databaseType
17199
- );
17200
- }
17201
- return create2DDatePivotQuery(
17202
- pivot,
17203
- itemQuery,
17204
- columnFieldValues,
17205
- databaseType,
17206
- dateBucket
17207
- );
17208
- }
17209
- function create2DStringPivotQuery(pivot, itemQuery, columnFieldValues, databaseType) {
17210
- const isValidBaseQuery = itemQuery.match(
17211
- /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/
17212
- );
17213
- if (!isValidBaseQuery || !pivot.columnField || !pivot.rowField)
17214
- return void 0;
17215
- const rowField = pivot.rowField;
17216
- if (!pivot.aggregations?.[0]?.valueField && pivot.aggregations?.[0]?.aggregationType !== "count" && !pivot.valueField && pivot.aggregationType !== "count")
17217
- throw new Error("No value field provided for pivot");
17218
- if (!pivot.aggregations?.[0]?.aggregationType && !pivot.aggregationType)
17219
- throw new Error("No aggregation type provided for pivot");
17220
- const columnField = pivot.columnField;
17221
- const rowFieldAlias = processColumnReference(
17222
- rowField,
17223
- databaseType,
17224
- void 0,
17225
- false,
17226
- true
17227
- );
17228
- const columnFieldAlias = processColumnReference(
17229
- columnField,
17230
- databaseType,
17231
- void 0,
17232
- false,
17233
- true
17234
- );
17235
- let caseWhens = [];
17236
- let valueAliases = [];
17237
- const seenAggs = {};
17238
- pivot.aggregations?.forEach((currentAgg) => {
17239
- if (seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]) {
17240
- seenAggs[currentAgg.aggregationType ?? ""][currentAgg.valueField ?? ""] += 1;
17241
- } else {
17242
- seenAggs[currentAgg.aggregationType ?? ""] = {
17243
- ...seenAggs[currentAgg.aggregationType ?? ""],
17244
- [currentAgg.valueField ?? ""]: 1
17245
- };
17246
- }
17247
- let disambiguationIndex = seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]?.toString();
17248
- if (disambiguationIndex === "1") disambiguationIndex = "";
17249
- const valueFieldAlias = processColumnReference(
17250
- currentAgg.valueField || rowField || "count",
17251
- databaseType,
17252
- void 0,
17253
- false,
17254
- true
17255
- );
17256
- const valueAliasSubstring = currentAgg.valueField ? `${processColumnReference(currentAgg.valueField, databaseType, void 0, true)} AS ${valueFieldAlias}` : "";
17257
- let value2AliasSubstring = "";
17258
- const disambiguationField = Object.values(seenAggs[currentAgg.aggregationType ?? ""] ?? {}).reduce(
17259
- (acc, v) => acc + v
17260
- ) > 1 ? `_${currentAgg.valueField}${disambiguationIndex}` : "";
17261
- const disambiguation = pivot.aggregations?.length > 1 ? `${disambiguationField}_${disambiguationField ? matchCasing(currentAgg.aggregationType, currentAgg.valueField) : currentAgg.aggregationType}` : "";
17262
- const valueExpr = currentAgg.valueFieldType === "bool" ? `CASE WHEN ${valueFieldAlias} THEN 1 ELSE 0 END` : processValueField(
17263
- currentAgg.aggregationType,
17264
- databaseType,
17265
- valueFieldAlias
17266
- );
17267
- if (currentAgg.aggregationType === "percentage") {
17268
- if (!currentAgg.valueField)
17269
- throw new Error("No value field provided for pivot");
17270
- const valueField2Alias = processColumnReference(
17271
- currentAgg.valueField2 ?? currentAgg.valueField,
17272
- databaseType,
17273
- void 0,
17274
- false,
17275
- true
17276
- );
17277
- const value2Expr = (currentAgg.valueField2Type ?? currentAgg.valueFieldType) === "bool" ? `CASE WHEN ${valueField2Alias} THEN 1 ELSE 0 END` : valueField2Alias;
17278
- if (currentAgg.valueField === currentAgg.valueField2 || !currentAgg.valueField2) {
17279
- caseWhens = [
17280
- ...caseWhens,
17281
- ...columnFieldValues.map((column) => {
17282
- return `CAST(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17283
- column,
17284
- databaseType
17285
- )}' THEN ${valueExpr} END) AS FLOAT) / GREATEST(sum(${value2Expr}), 1) AS ${processColumnReference(
17286
- column + disambiguation,
17287
- databaseType,
17288
- "_",
17289
- true
17290
- )}`;
17291
- })
17292
- ];
17293
- } else {
17294
- value2AliasSubstring = `${processColumnReference(
17295
- currentAgg.valueField2 ?? currentAgg.valueField,
17296
- databaseType,
17297
- void 0,
17298
- true
17299
- )} AS ${valueField2Alias}`;
17300
- caseWhens = [
17301
- ...caseWhens,
17302
- ...columnFieldValues.map((column) => {
17303
- return `CAST(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17304
- column,
17305
- databaseType
17306
- )}' THEN ${valueExpr} END) AS FLOAT) / GREATEST(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17307
- column,
17308
- databaseType
17309
- )}' THEN ${value2Expr} END), 1) AS ${processColumnReference(
17310
- column + disambiguation,
17311
- databaseType,
17312
- "_",
17313
- true
17314
- )}`;
17315
- })
17316
- ];
17317
- }
17318
- } else {
17319
- caseWhens = [
17320
- ...caseWhens,
17321
- ...columnFieldValues.map((column) => {
17322
- return `${processAggType(currentAgg.aggregationType, true)}(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17323
- column,
17324
- databaseType
17325
- )}' THEN ${valueExpr} END) AS ${processColumnReference(column + disambiguation, databaseType, "_", true)}`;
17326
- })
17327
- ];
17328
- }
17329
- if (valueAliasSubstring) valueAliases.push(valueAliasSubstring);
17330
- if (value2AliasSubstring) valueAliases.push(value2AliasSubstring);
17331
- });
17332
- valueAliases = [
17333
- `${processColumnReference(rowField, databaseType, void 0, true)} AS ${rowFieldAlias}`,
17334
- ...valueAliases,
17335
- `${processColumnReference(columnField, databaseType, void 0, true)} AS ${columnFieldAlias}`
17336
- ];
17337
- valueAliases = Array.from(new Set(valueAliases));
17338
- const sortQuery = pivot.sort && pivot.sortField && pivot.rowLimit ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType, void 0, true)} ${pivot.sortDirection || ""} ` : "";
17339
- const pivotQuery = `
17340
- ,quill_alias AS (
17341
- SELECT ${valueAliases.length > 0 ? `${valueAliases.join(", ")}` : ""}
17342
- FROM quill_base_table
17343
- ),
17344
- quill_qt_cw AS (
17345
- SELECT ${rowFieldAlias}
17346
- ${caseWhens.length > 0 ? `, ${caseWhens.join(", ")}` : ""}
17347
- FROM quill_alias
17348
- GROUP BY ${rowFieldAlias}
17349
- ),
17350
- quill_base_pivot AS (
17351
- SELECT ${pivot.rowLimit && databaseType.toLowerCase() === "mssql" ? `TOP ${pivot.rowLimit}` : ""} *
17352
- FROM quill_qt_cw qt
17353
- ${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== "mssql" ? ` LIMIT ${pivot.rowLimit}` : ""}
17354
- )
17355
- SELECT * FROM quill_base_pivot
17356
- `.replace(/\s+/g, " ").trim();
17357
- return itemQuery.replace(
17358
- /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/,
17359
- pivotQuery
17360
- );
17361
- }
17362
- function create2DDatePivotQuery(pivot, itemQuery, columnFieldValues, databaseType, dateBucket = "month") {
17363
- const isValidBaseQuery = itemQuery.match(
17364
- /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/
17365
- );
17366
- if (!isValidBaseQuery || !pivot.columnField || !pivot.rowField) {
17367
- return void 0;
17368
- }
17369
- if (!pivot.aggregations?.[0]?.valueField && pivot.aggregations?.[0]?.aggregationType !== "count" && !pivot.valueField && pivot.aggregationType !== "count")
17370
- throw new Error("No value field provided for pivot");
17371
- if (!pivot.aggregations?.[0]?.aggregationType && !pivot.aggregationType)
17372
- throw new Error("No aggregation type provided for pivot");
17373
- const rowField = pivot.rowField;
17374
- const columnField = pivot.columnField;
17375
- const rowFieldAlias = processColumnReference(
17376
- rowField,
17377
- databaseType,
17378
- void 0,
17379
- false,
17380
- true
17381
- );
17382
- const columnFieldAlias = processColumnReference(
17383
- columnField,
17384
- databaseType,
17385
- void 0,
17386
- false,
17387
- true
17388
- );
17389
- let caseWhens = [];
17390
- let valueFieldAliases = [];
17391
- const seenAggs = {};
17392
- pivot.aggregations?.forEach((currentAgg) => {
17393
- if (seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]) {
17394
- seenAggs[currentAgg.aggregationType ?? ""][currentAgg.valueField ?? ""] += 1;
17395
- } else {
17396
- seenAggs[currentAgg.aggregationType ?? ""] = {
17397
- ...seenAggs[currentAgg.aggregationType ?? ""],
17398
- [currentAgg.valueField ?? ""]: 1
17399
- };
17400
- }
17401
- let disambiguationIndex = seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]?.toString();
17402
- if (disambiguationIndex === "1") disambiguationIndex = "";
17403
- const valueFieldAlias = processColumnReference(
17404
- currentAgg.valueField ?? rowField,
17405
- databaseType,
17406
- void 0,
17407
- false,
17408
- true
17409
- );
17410
- const valueAliasSubstring = currentAgg.valueField ? `${processColumnReference(currentAgg.valueField, databaseType, void 0, true)} AS ${valueFieldAlias}` : "";
17411
- let value2AliasSubstring = "";
17412
- const disambiguationField = Object.values(seenAggs[currentAgg.aggregationType ?? ""] ?? {}).reduce(
17413
- (acc, v) => acc + v
17414
- ) > 1 ? `_${currentAgg.valueField}${disambiguationIndex}` : "";
17415
- const disambiguation = pivot.aggregations?.length > 1 ? `${disambiguationField}_${disambiguationField ? matchCasing(currentAgg.aggregationType, currentAgg.valueField) : currentAgg.aggregationType}` : "";
17416
- const valueExpr = currentAgg.valueFieldType === "bool" ? `CASE WHEN ${valueFieldAlias} THEN 1 ELSE 0 END` : processValueField(
17417
- currentAgg.aggregationType,
17418
- databaseType,
17419
- valueFieldAlias
17420
- );
17421
- if (currentAgg.aggregationType === "percentage") {
17422
- if (!currentAgg.valueField)
17423
- throw new Error("No value field provided for pivot");
17424
- const valueField2Alias = processColumnReference(
17425
- currentAgg.valueField2 ?? currentAgg.valueField,
17426
- databaseType,
17427
- void 0,
17428
- false,
17429
- true
17430
- );
17431
- const value2Expr = (currentAgg.valueField2Type ?? currentAgg.valueFieldType) === "bool" ? `CASE WHEN ${valueField2Alias} THEN 1 ELSE 0 END` : valueField2Alias;
17432
- if (currentAgg.valueField === currentAgg.valueField2 || !currentAgg.valueField2) {
17433
- caseWhens = [
17434
- ...caseWhens,
17435
- ...columnFieldValues.map((column) => {
17436
- return `CAST(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17437
- column,
17438
- databaseType
17439
- )}' THEN ${valueExpr} END) AS FLOAT) / GREATEST(sum(${value2Expr}), 1) AS ${processColumnReference(
17440
- column + disambiguation,
17441
- databaseType,
17442
- "_",
17443
- true
17444
- )}`;
17445
- })
17446
- ];
17447
- } else {
17448
- value2AliasSubstring = `${processColumnReference(
17449
- currentAgg.valueField2 ?? currentAgg.valueField,
17450
- databaseType,
17451
- void 0,
17452
- true
17453
- )} AS ${valueField2Alias}`;
17454
- caseWhens = [
17455
- ...caseWhens,
17456
- ...columnFieldValues.map((column) => {
17457
- return `CAST(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17458
- column,
17459
- databaseType
17460
- )}' THEN ${valueExpr} END) AS FLOAT) / GREATEST(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17461
- column,
17462
- databaseType
17463
- )}' THEN ${value2Expr} END), 1) AS ${processColumnReference(
17464
- column + disambiguation,
17465
- databaseType,
17466
- "_",
17467
- true
17468
- )}`;
17469
- })
17470
- ];
17471
- }
17472
- } else {
17473
- caseWhens = [
17474
- ...caseWhens,
17475
- ...columnFieldValues.map((column) => {
17476
- return `${processAggType(currentAgg.aggregationType, true)}(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(
17477
- column,
17478
- databaseType
17479
- )}' THEN ${valueExpr} END) AS ${processColumnReference(column + disambiguation, databaseType, "_", true)}`;
17480
- })
17481
- ];
17482
- }
17483
- if (valueAliasSubstring) valueFieldAliases.push(valueAliasSubstring);
17484
- if (value2AliasSubstring) valueFieldAliases.push(value2AliasSubstring);
17485
- });
17486
- valueFieldAliases = [
17487
- `${processColumnReference(rowField, databaseType, void 0, true)} AS ${rowFieldAlias}`,
17488
- ...valueFieldAliases,
17489
- `${processColumnReference(columnField, databaseType, void 0, true)} AS ${columnFieldAlias}`
17490
- ];
17491
- valueFieldAliases = Array.from(new Set(valueFieldAliases));
17492
- const sortQuery = `${pivot.sort && pivot.sortField && pivot.rowLimit ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType, void 0, true)} ${pivot.sortDirection || ""} ` : ""}`;
17493
- const pivotQuery = `
17494
- , quill_alias AS (SELECT ${valueFieldAliases.length > 0 ? `${valueFieldAliases.join(", ")}` : ""} FROM quill_base_table),
17495
- 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)}),
17496
- quill_base_pivot AS (SELECT ${pivot.rowLimit && databaseType.toLowerCase() === "mssql" ? `TOP ${pivot.rowLimit}` : ""} * FROM quill_qt_agg qt
17497
- ${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== "mssql" ? ` LIMIT ${pivot.rowLimit}` : ""})
17498
- SELECT * FROM quill_base_pivot
17499
- `.replace(/\s+/g, " ").trim();
17500
- return itemQuery.replace(
17501
- /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/,
17502
- pivotQuery
17503
- );
17504
- }
17505
- function create1DPivotQuery(pivot, itemQuery, dateBucket = "month", databaseType) {
17506
- if (isStringType(pivot.rowFieldType || "") || !pivot.rowFieldType) {
17507
- return create1DStringPivotQuery(pivot, itemQuery, databaseType);
17508
- }
17509
- return create1DDatePivotQuery(pivot, itemQuery, dateBucket, databaseType);
17510
- }
17511
- function create1DStringPivotQuery(pivot, itemQuery, databaseType) {
17512
- const isValidBaseQuery = itemQuery.match(
17513
- /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/
17514
- );
17515
- if (!isValidBaseQuery) return void 0;
17516
- const rowField = pivot.rowField;
17517
- const rowAlias = processColumnReference(
17518
- rowField,
17519
- databaseType,
17520
- void 0,
17521
- true
17522
- );
17523
- let quillAggSelects = [rowAlias];
17524
- let valueFieldAliases = [];
17525
- const seenAggs = {};
17526
- pivot.aggregations?.forEach((currentAgg) => {
17527
- if (!currentAgg.valueField) currentAgg.valueField = void 0;
17528
- if (seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]) {
17529
- seenAggs[currentAgg.aggregationType ?? ""][currentAgg.valueField ?? ""] += 1;
17530
- } else {
17531
- seenAggs[currentAgg.aggregationType ?? ""] = {
17532
- ...seenAggs[currentAgg.aggregationType ?? ""],
17533
- [currentAgg.valueField ?? ""]: 1
17534
- };
17535
- }
17536
- let disambiguationIndex = seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]?.toString() ?? "";
17537
- if (disambiguationIndex === "1") disambiguationIndex = "";
17538
- const valueFieldAlias = processColumnReference(
17539
- currentAgg.valueField || rowField || "count",
17540
- databaseType,
17541
- void 0,
17542
- false,
17543
- true
17544
- );
17545
- const valueAliasSubstring = currentAgg.valueField ? `${processColumnReference(currentAgg.valueField, databaseType, void 0, true)} AS ${valueFieldAlias}` : "";
17546
- let value2AliasSubstring = "";
17547
- const disambiguation = pivot.aggregations?.length > 1 ? `${disambiguationIndex}` + (currentAgg.aggregationType !== "count" ? `_${currentAgg.aggregationType}` : "") : "";
17548
- let valueExpr = !currentAgg.valueField ? "*" : valueFieldAlias;
17549
- if (currentAgg.valueFieldType === "bool") {
17550
- valueExpr = `CASE WHEN ${valueFieldAlias} THEN 1 ELSE 0 END`;
17551
- }
17552
- if (currentAgg.aggregationType === "percentage") {
17553
- let countPercentage = false;
17554
- if (!currentAgg.valueField) {
17555
- countPercentage = true;
17556
- }
17557
- const valueField2Alias = processColumnReference(
17558
- currentAgg.valueField2 ?? currentAgg.valueField ?? "count",
17559
- databaseType,
17560
- void 0,
17561
- false,
17562
- true
17563
- );
17564
- let value2Expr = valueField2Alias;
17565
- if ((currentAgg.valueField2Type ?? currentAgg.valueFieldType) === "bool") {
17566
- value2Expr = `CASE WHEN ${valueField2Alias} THEN 1 ELSE 0 END`;
17567
- }
17568
- value2AliasSubstring = currentAgg.valueField2 && currentAgg.valueField !== currentAgg.valueField2 ? `${processColumnReference(currentAgg.valueField2, databaseType, void 0, true)} AS ${valueField2Alias}` : "";
17569
- 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)`;
17570
- quillAggSelects = [
17571
- ...quillAggSelects,
17572
- `${percentageExpr} as ${processColumnReference(
17573
- `${currentAgg.valueField ?? (disambiguation ? "count" : "percentage")}${disambiguation}`,
17574
- databaseType,
17575
- void 0,
17576
- false,
17577
- true
17578
- )}`
17579
- ];
17580
- } else {
17581
- quillAggSelects = [
17582
- ...quillAggSelects,
17583
- `${processAggType(currentAgg.aggregationType)}(${valueExpr}) AS ${processColumnReference(
17584
- (currentAgg.valueField || "count") + disambiguation,
17585
- databaseType
17586
- )}`
17587
- ];
17588
- }
17589
- if (valueAliasSubstring) valueFieldAliases.push(valueAliasSubstring);
17590
- if (value2AliasSubstring) valueFieldAliases.push(value2AliasSubstring);
17591
- });
17592
- valueFieldAliases = Array.from(new Set(valueFieldAliases));
17593
- const sortQuery = `${pivot.sort && pivot.sortField && pivot.rowLimit ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType, void 0, true)} ${pivot.sortDirection || ""} ` : ""}`;
17594
- const pivotQuery = `, quill_alias AS (
17595
- SELECT ${processColumnReference(`${rowField}`, databaseType, void 0, true)} AS ${rowAlias}${valueFieldAliases.length > 0 ? `, ${valueFieldAliases.join(", ")}` : ""}
17596
- FROM quill_base_table
17597
- ),
17598
- quill_qt_cw AS (
17599
- SELECT ${quillAggSelects.join(", ")} FROM quill_alias GROUP BY ${rowAlias}
17600
- ),
17601
- quill_base_pivot AS (
17602
- SELECT ${pivot.rowLimit && databaseType.toLowerCase() === "mssql" ? `TOP ${pivot.rowLimit}` : ""} *
17603
- FROM quill_qt_cw qt ${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== "mssql" ? ` LIMIT ${pivot.rowLimit}` : ""}
17604
- )
17605
- SELECT * FROM quill_base_pivot`.replace(/\s+/g, " ").trim();
17606
- return itemQuery.replace(
17607
- /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/,
17608
- pivotQuery
17609
- );
17610
- }
17611
- function create1DDatePivotQuery(pivot, itemQuery, dateBucket = "month", databaseType) {
17612
- const isValidBaseQuery = itemQuery.match(
17613
- /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/
17614
- );
17615
- if (!isValidBaseQuery) {
17616
- return void 0;
17617
- }
17618
- const rowField = pivot.rowField || "";
17619
- const rowFieldAlias = processColumnReference(
17620
- rowField,
17621
- databaseType,
17622
- void 0
17623
- );
17624
- let quillAggSelects = [
17625
- `${processDateTrunc(dateBucket, rowFieldAlias, databaseType)} as ${processColumnReference(rowField, databaseType)}`
17626
- ];
17627
- let valueFieldAliases = [];
17628
- const seenAggs = {};
17629
- pivot.aggregations?.forEach((currentAgg) => {
17630
- if (!currentAgg.valueField) currentAgg.valueField = void 0;
17631
- if (seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]) {
17632
- seenAggs[currentAgg.aggregationType ?? ""][currentAgg.valueField ?? ""] += 1;
17633
- } else {
17634
- seenAggs[currentAgg.aggregationType ?? ""] = {
17635
- ...seenAggs[currentAgg.aggregationType ?? ""],
17636
- [currentAgg.valueField ?? ""]: 1
17637
- };
17638
- }
17639
- let disambiguationIndex = seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]?.toString() ?? "";
17640
- if (disambiguationIndex === "1") disambiguationIndex = "";
17641
- const valueFieldAlias = processColumnReference(
17642
- currentAgg.valueField || rowField || "count",
17643
- databaseType,
17644
- void 0,
17645
- false,
17646
- true
17647
- );
17648
- const valueAliasSubstring = currentAgg.valueField ? `${processColumnReference(currentAgg.valueField, databaseType, void 0, true)} AS ${valueFieldAlias}` : "";
17649
- let value2AliasSubstring = "";
17650
- const disambiguation = pivot.aggregations?.length > 1 ? `${disambiguationIndex}` + (currentAgg.aggregationType !== "count" ? `_${currentAgg.aggregationType}` : "") : "";
17651
- let valueExpr = !currentAgg.valueField ? "*" : valueFieldAlias;
17652
- if (currentAgg.valueFieldType === "bool") {
17653
- valueExpr = `CASE WHEN ${valueFieldAlias} THEN 1 ELSE 0 END`;
17654
- }
17655
- if (currentAgg.aggregationType === "percentage") {
17656
- let countPercentage = false;
17657
- if (!currentAgg.valueField) {
17658
- countPercentage = true;
17659
- }
17660
- const valueField2Alias = processColumnReference(
17661
- currentAgg.valueField2 ?? currentAgg.valueField ?? "count",
17662
- databaseType,
17663
- void 0,
17664
- false,
17665
- true
17666
- );
17667
- value2AliasSubstring = currentAgg.valueField2 && currentAgg.valueField !== currentAgg.valueField2 ? `${processColumnReference(currentAgg.valueField2, databaseType, void 0, true)} AS ${valueField2Alias}` : "";
17668
- let value2Expr = valueField2Alias;
17669
- if ((currentAgg.valueField2Type ?? currentAgg.valueFieldType) === "bool") {
17670
- value2Expr = `CASE WHEN ${valueField2Alias} THEN 1 ELSE 0 END`;
17671
- }
17672
- 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)`;
17673
- quillAggSelects = [
17674
- ...quillAggSelects,
17675
- `${percentageExpr} as ${processColumnReference(
17676
- `${currentAgg.valueField ?? (disambiguation ? "count" : "percentage")}${disambiguation}`,
17677
- databaseType,
17678
- void 0,
17679
- false,
17680
- true
17681
- )}`
17682
- ];
17683
- } else {
17684
- quillAggSelects = [
17685
- ...quillAggSelects,
17686
- `${processAggType(currentAgg.aggregationType)}(${valueExpr}) AS ${processColumnReference((currentAgg.valueField || "count") + disambiguation, databaseType)}`
17687
- ];
17688
- }
17689
- if (valueAliasSubstring) valueFieldAliases.push(valueAliasSubstring);
17690
- if (value2AliasSubstring) valueFieldAliases.push(value2AliasSubstring);
17691
- });
17692
- valueFieldAliases = Array.from(new Set(valueFieldAliases));
17693
- const sortQuery = `${pivot.sort && pivot.sortField && pivot.rowLimit ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType, void 0, true)} ${pivot.sortDirection || ""} ` : ""}`;
17694
- const pivotQuery = `, quill_alias AS (SELECT ${processColumnReference(`${rowField}`, databaseType, void 0, true)} AS ${rowFieldAlias}${valueFieldAliases.length > 0 ? `, ${valueFieldAliases.join(", ")}` : ""}
17695
- FROM quill_base_table),
17696
- quill_qt_agg AS (SELECT ${quillAggSelects.join(", ")}
17697
- FROM quill_alias GROUP BY ${databaseType.toLowerCase() === "clickhouse" ? processColumnReference(`${rowField}`, databaseType) : processDateTrunc(dateBucket, rowFieldAlias, databaseType)}),
17698
- quill_base_pivot AS (SELECT ${pivot.rowLimit && databaseType.toLowerCase() === "mssql" ? `TOP ${pivot.rowLimit}` : ""} * FROM quill_qt_agg qt
17699
- ${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== "mssql" ? ` LIMIT ${pivot.rowLimit}` : ""})
17700
- SELECT * FROM quill_base_pivot
17701
- `.replace(/\s+/g, " ").trim();
17702
- return itemQuery.replace(
17703
- /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/,
17704
- pivotQuery
17705
- );
17706
- }
17707
- function createAggregationValuePivot(pivot, itemQuery, databaseType) {
17708
- const isValidBaseQuery = itemQuery.match(
17709
- /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/
17710
- );
17711
- if (!isValidBaseQuery) return void 0;
17712
- let quillAggSelects = [];
17713
- let valueFieldAliases = [];
17714
- const seenAggs = {};
17715
- pivot.aggregations?.forEach((currentAgg) => {
17716
- if (!currentAgg.valueField) currentAgg.valueField = void 0;
17717
- if (seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]) {
17718
- seenAggs[currentAgg.aggregationType ?? ""][currentAgg.valueField ?? ""] += 1;
17719
- } else {
17720
- seenAggs[currentAgg.aggregationType ?? ""] = {
17721
- ...seenAggs[currentAgg.aggregationType ?? ""],
17722
- [currentAgg.valueField ?? ""]: 1
17723
- };
17724
- }
17725
- let disambiguationIndex = seenAggs[currentAgg.aggregationType ?? ""]?.[currentAgg.valueField ?? ""]?.toString() ?? "";
17726
- if (disambiguationIndex === "1") disambiguationIndex = "";
17727
- const valueFieldAlias = processColumnReference(
17728
- currentAgg.valueField || "count",
17729
- databaseType,
17730
- void 0,
17731
- false,
17732
- true
17733
- );
17734
- const valueAliasSubstring = currentAgg.valueField ? `${processColumnReference(currentAgg.valueField, databaseType, void 0, true)} AS ${valueFieldAlias}` : "";
17735
- let value2AliasSubstring = "";
17736
- const disambiguation = pivot.aggregations?.length > 1 ? `${disambiguationIndex}_${currentAgg.aggregationType}` : "";
17737
- let valueExpr = !currentAgg.valueField ? "*" : valueFieldAlias;
17738
- valueExpr = currentAgg.valueFieldType === "bool" ? `CASE WHEN ${valueFieldAlias} THEN 1 ELSE 0 END` : valueExpr;
17739
- if (currentAgg.aggregationType === "percentage") {
17740
- if (!currentAgg.valueField) {
17741
- throw new Error("No value field provided for percentage aggregation");
17742
- }
17743
- const valueField2Alias = processColumnReference(
17744
- currentAgg.valueField2 ?? currentAgg.valueField,
17745
- databaseType,
17746
- void 0,
17747
- false,
17748
- true
17749
- );
17750
- const value2Expr = (currentAgg.valueField2Type ?? currentAgg.valueFieldType) === "bool" ? `CASE WHEN ${valueField2Alias} THEN 1 ELSE 0 END` : valueField2Alias;
17751
- value2AliasSubstring = currentAgg.valueField2 && currentAgg.valueField !== currentAgg.valueField2 ? `${processColumnReference(currentAgg.valueField2, databaseType, void 0, true)} AS ${valueField2Alias}` : "";
17752
- 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)`;
17753
- quillAggSelects = [
17754
- ...quillAggSelects,
17755
- `${percentageExpr} as ${processColumnReference(
17756
- `${currentAgg.valueField ?? ""}${disambiguation}`,
17757
- databaseType,
17758
- void 0,
17759
- false,
17760
- true
17761
- )}`
17762
- ];
17763
- } else {
17764
- quillAggSelects = [
17765
- ...quillAggSelects,
17766
- `${processAggType(currentAgg.aggregationType)}(${valueExpr}) AS ${processColumnReference(
17767
- (currentAgg.valueField || "count") + disambiguation,
17768
- databaseType
17769
- )}`
17770
- ];
17771
- }
17772
- if (valueAliasSubstring) valueFieldAliases.push(valueAliasSubstring);
17773
- if (value2AliasSubstring) valueFieldAliases.push(value2AliasSubstring);
17774
- });
17775
- valueFieldAliases = Array.from(new Set(valueFieldAliases));
17776
- if (valueFieldAliases.length === 0) {
17777
- valueFieldAliases = ["*"];
17778
- }
17779
- const sortQuery = pivot.sort && pivot.sortField && pivot.rowLimit ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType, void 0, true)} ${pivot.sortDirection || ""} ` : "";
17780
- const pivotQuery = `, quill_alias AS (
17781
- SELECT ${valueFieldAliases.join(", ")} FROM quill_base_table
17782
- ),
17783
- quill_qt_agg AS (
17784
- SELECT ${quillAggSelects.join(", ")} FROM quill_alias
17785
- ),
17786
- quill_base_pivot AS (
17787
- SELECT ${pivot.rowLimit && databaseType.toLowerCase() === "mssql" ? `TOP ${pivot.rowLimit}` : ""} * FROM quill_qt_agg qt
17788
- ${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== "mssql" ? ` LIMIT ${pivot.rowLimit}` : ""}
17789
- )
17790
- SELECT * FROM quill_base_pivot`.replace(/\s+/g, " ").trim();
17791
- return itemQuery.replace(
17792
- /SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/,
17793
- pivotQuery
17794
- );
17795
- }
17796
- function additionalProcessingOnPivotQuery(pivot, query, additionalProcessing, databaseType = "postgresql") {
17797
- if (!additionalProcessing || !query) return query;
17798
- const isValidBaseQuery = query.match(
17799
- /SELECT \* FROM\s+["'[`]?quill_base_pivot["'\]`]?\s*$/
17800
- );
17801
- if (!isValidBaseQuery) {
17802
- return void 0;
17803
- }
17804
- if (!pivot.aggregations || pivot.aggregations.length === 0) {
17805
- if (pivot.aggregationType) {
17806
- pivot.aggregations = [
17807
- {
17808
- aggregationType: pivot.aggregationType,
17809
- valueField: pivot.valueField,
17810
- valueField2: pivot.valueField2
17811
- }
17812
- ];
17813
- } else {
17814
- throw new Error("No aggregations provided for pivot");
17815
- }
17816
- }
17817
- let rowsPerPage = 0;
17818
- let currentInterval = 0;
17819
- let offset = 0;
17820
- let limit = 1e3;
17821
- let sortQuery = "";
17822
- if (additionalProcessing.page) {
17823
- const page = additionalProcessing.page.page || 0;
17824
- if (additionalProcessing.page.rowsPerRequest) {
17825
- limit = additionalProcessing.page.rowsPerRequest;
17826
- }
17827
- rowsPerPage = additionalProcessing.page.rowsPerPage || 0;
17828
- currentInterval = page ? Math.floor(page / (limit / rowsPerPage)) : 0;
17829
- offset = currentInterval * limit;
17830
- }
17831
- const disambiguation = pivot.aggregations.length > 1 ? `_${matchCasing(pivot.aggregations?.[0]?.aggregationType, pivot.aggregations?.[0]?.valueField)}` : "";
17832
- if (additionalProcessing.sort) {
17833
- sortQuery = `ORDER BY ${processColumnReference(additionalProcessing.sort.field, databaseType, void 0, true)} ${additionalProcessing.sort.direction || ""}`;
17834
- } else {
17835
- const valueFieldAlias = processColumnReference(
17836
- (pivot.aggregations?.[0]?.valueField ?? "") + disambiguation,
17837
- databaseType,
17838
- void 0,
17839
- false,
17840
- true
17841
- );
17842
- const defaultSortField = pivot.sortField || pivot.rowField || valueFieldAlias;
17843
- const defaultSortDirection = pivot.sortDirection || "";
17844
- if (defaultSortField !== `"_"`) {
17845
- sortQuery = `ORDER BY ${processColumnReference(defaultSortField, databaseType, void 0, true)} ${defaultSortDirection}`;
17846
- } else {
17847
- sortQuery = "";
17848
- }
17849
- }
17850
- const additionalProcessingQuery = `
17851
- SELECT *
17852
- FROM quill_base_pivot ${sortQuery}${databaseType.toLowerCase() === "mssql" ? ` OFFSET ${offset} ROWS FETCH NEXT ${limit} ROWS ONLY` : ` LIMIT ${limit} OFFSET ${offset}`}
17853
- `.replace(/\s+/g, " ").trim();
17854
- return query.replace(
17855
- /SELECT \* FROM\s+["'[`]?quill_base_pivot["'\]`]?\s*$/,
17856
- additionalProcessingQuery
17857
- );
17858
- }
17859
- function generateRowCountQuery(query, databaseType) {
17860
- if (!query) return query;
17861
- const isValidBaseQuery = query.match(
17862
- /SELECT \* FROM\s+["'[`]?quill_base_pivot["'\]`]?\s*$/
17863
- );
17864
- if (!isValidBaseQuery) {
17865
- return void 0;
17866
- }
17867
- const rowCountQuery = `, subview_row_count_cte AS (SELECT * FROM quill_base_pivot)
17868
- SELECT count(*) as ${processColumnReference("row_count", databaseType || "postgresql", void 0, true)} FROM subview_row_count_cte
17869
- `.replace(/\s+/g, " ").trim();
17870
- return query.replace(
17871
- /SELECT \* FROM\s+["'[`]?quill_base_pivot["'\]`]?\s*$/,
17872
- rowCountQuery
17873
- );
17874
- }
17875
-
17876
- // src/utils/pivotConstructor.ts
17877
- async function generatePivotWithSQL({
17878
- pivot,
17879
- report,
17880
- client,
17881
- dateBucket,
17882
- dateFilter,
17883
- distinctStrings,
17884
- dashboardName,
17885
- tenants,
17886
- additionalProcessing,
17887
- pivotQuery,
17888
- comparisonPivotQuery,
17889
- getPivotRowCount = true,
17890
- caller,
17891
- getToken
17892
- }) {
17893
- let sqlQuery = pivotQuery;
17894
- let comparisonPivotSql = comparisonPivotQuery;
17895
- const databaseType = client.databaseType || "postgresql";
17896
- if (!pivotQuery && pivot.columnField && !pivot.columnValues && !distinctStrings) {
17897
- if (!report?.rows) {
17898
- throw new Error("No distinct strings provided for column field");
17899
- } else {
17900
- distinctStrings = Array.from(
17901
- new Set(
17902
- report.rows.map((row) => row[pivot.columnField]).filter((value) => value !== null && value !== void 0)
17903
- )
17904
- );
17905
- }
17906
- }
17907
- if (!pivot.aggregations?.length && pivot.aggregationType) {
17908
- pivot.aggregations = [
17909
- {
17910
- aggregationType: pivot.aggregationType,
17911
- valueField: pivot.valueField,
17912
- valueFieldType: pivot.valueFieldType,
17913
- valueField2: pivot.valueField2,
17914
- valueField2Type: pivot.valueField2Type
17915
- }
17916
- ];
17917
- }
17918
- let comparisonInterval = void 0;
17919
- if (dateFilter && dateFilter.comparisonRange) {
17920
- comparisonInterval = getComparisonInterval(
17921
- {
17922
- startDate: dateFilter.comparisonRange.startDate,
17923
- endDate: dateFilter.comparisonRange.endDate
17924
- },
17925
- dateBucket
17926
- );
17927
- }
17928
- if (pivot.rowField && !pivot.rowFieldType) {
17929
- const rowColumn = report?.columns.find(
17930
- (column) => column.field === pivot.rowField
17931
- );
17932
- pivot.rowFieldType = rowColumn?.format || "string";
17933
- }
17934
- const filteredDistinctStrings = distinctStrings?.filter(
17935
- (value) => value !== null && value !== void 0 && value !== ""
17936
- );
17937
- const pivotColumnFields = filteredDistinctStrings?.slice(
17938
- 0,
17939
- MAX_PIVOT_UNIQUE_VALUES
17940
- );
17941
- if (!pivotQuery && report) {
17942
- if (!report.itemQuery) {
17943
- throw Error("No item query found in report");
17944
- }
17945
- const itemQuery = report.itemQuery[0];
17946
- const comparisonQuery = report.itemQuery[1];
17947
- if (!itemQuery) {
17948
- throw Error("No item query found in report");
17949
- }
17950
- sqlQuery = generatePivotQuery(
17951
- pivot,
17952
- itemQuery,
17953
- databaseType,
17954
- pivotColumnFields,
17955
- dateBucket
17956
- );
17957
- comparisonPivotSql = comparisonQuery ? generatePivotQuery(
17958
- pivot,
17959
- comparisonQuery,
17960
- databaseType,
17961
- pivotColumnFields,
17962
- dateBucket
17963
- ) : "";
17964
- }
17965
- if (!sqlQuery) {
17966
- throw "Error generating pivot query";
17967
- }
17968
- const paginatedSqlQuery = additionalProcessingOnPivotQuery(
17969
- pivot,
17970
- sqlQuery,
17971
- additionalProcessing,
17972
- client.databaseType
17973
- );
17974
- const paginatedComparisonQuery = comparisonPivotSql ? additionalProcessingOnPivotQuery(
17975
- pivot,
17976
- comparisonPivotSql,
17977
- additionalProcessing,
17978
- client.databaseType
17979
- ) : "";
17980
- const preQueries = [paginatedSqlQuery];
17981
- getPivotRowCount = getPivotRowCount && (report?.chartType === "table" || caller === "ReportBuilder");
17982
- if (getPivotRowCount) {
17983
- const pivotRowCountQuery = generateRowCountQuery(
17984
- sqlQuery,
17985
- client.databaseType
17986
- );
17987
- preQueries.push(pivotRowCountQuery);
17988
- }
17989
- if (paginatedComparisonQuery) {
17990
- preQueries.push(paginatedComparisonQuery);
17991
- }
17992
- const { data: resp } = await quillFetch({
17993
- client,
17994
- task: "query",
17995
- metadata: {
17996
- preQueries,
17997
- clientId: client.publicKey,
17998
- databaseType,
17999
- runQueryConfig: {
18000
- overridePost: true,
18001
- convertDatatypes: true
18002
- },
18003
- useNewNodeSql: true,
18004
- dashboardName,
18005
- tenants
18006
- },
18007
- urlParameters: `caller=generatePivot&task=query`,
18008
- credentials: "same-origin",
18009
- getToken
18010
- });
18011
- if (resp.success === false) {
18012
- throw resp.errorMessage;
18013
- }
18014
- const queryResponseRows = resp?.queryResults?.[0]?.rows || [];
18015
- const queryResponseFields = resp?.queryResults?.[0]?.fields || [];
18016
- const queryComparisonResponseRows = (getPivotRowCount ? resp?.queryResults?.[2]?.rows : resp?.queryResults?.[1]?.rows) || [];
18017
- const queryComparisonResponseFields = (getPivotRowCount ? resp?.queryResults?.[2]?.fields : resp?.queryResults?.[1]?.fields) || [];
18018
- parseValueFromBigQueryDates(queryResponseRows, queryResponseFields);
18019
- parseValueFromBigQueryDates(
18020
- queryComparisonResponseRows,
18021
- queryComparisonResponseFields
18022
- );
18023
- const responseRows = mergeComparisonPivotRows({
18024
- pivot,
18025
- rows: queryResponseRows,
18026
- compRows: queryComparisonResponseRows,
18027
- databaseType,
18028
- dateBucket,
18029
- comparisonInterval,
18030
- columnFieldValues: pivotColumnFields
18031
- });
18032
- const responseFields = mergeComparisonPivotColumns({
18033
- pivot,
18034
- rows: queryResponseFields,
18035
- compRows: queryComparisonResponseFields
18036
- });
18037
- const rows = pivot.rowField ? responseRows.map(
18038
- (row) => !row[pivot.rowField] ? { ...row, [pivot.rowField]: "-" } : row
18039
- ) : responseRows;
18040
- if (pivot.columnField && client.databaseType?.toLowerCase() === "bigquery") {
18041
- rows.forEach((row) => {
18042
- Object.keys(row).forEach((key) => {
18043
- const processedKey = processColumnName(key);
18044
- if (processedKey !== key) {
18045
- row[processedKey] = row[key];
18046
- delete row[key];
18047
- }
18048
- });
18049
- });
18050
- }
18051
- const columns = responseFields?.map((field) => ({
18052
- field: processColumnName(field.name),
18053
- label: snakeCaseToTitleCase(
18054
- processColumnName(field.name.replace("comparison_", "comparison "))
18055
- ),
18056
- format: field.name === pivot.rowField ? "string" : (
18057
- // This scary equation is calculating which aggregation a column is associated with.
18058
- // 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.
18059
- // pivot.aggregations?.[
18060
- // Math.floor(
18061
- // (index - 1) /
18062
- // Math.floor(
18063
- // (responseFields.length - 1) /
18064
- // (pivot.aggregations?.length ?? 1),
18065
- // ),
18066
- // )
18067
- // ]?.aggregationType === 'percentage'
18068
- pivot.aggregations?.find(
18069
- (agg) => agg.valueField === field.name || `${agg.valueField}_percentage` === field.name || !agg.valueField && agg.aggregationType === "percentage" && field.name.endsWith("percentage")
18070
- )?.aggregationType === "percentage" ? "percent" : "whole_number"
18071
- ),
18072
- fieldType: field.fieldType,
18073
- jsType: field.jsType,
18074
- dataTypeID: field.dataTypeID
18075
- })).filter(
18076
- (field, index) => field.field !== "comparison_" + pivot.rowField || index === 0
18077
- ).sort((a, b) => {
18078
- if (a.field === pivot.rowField) {
18079
- return -1;
18080
- }
18081
- if (b.field === pivot.rowField) {
18082
- return 1;
18083
- }
18084
- return 0;
18085
- });
18086
- if (pivot.rowField && !isStringType(pivot.rowFieldType || "")) {
18087
- rows.forEach((row) => {
18088
- row.__quillRawDate = typeof row[pivot.rowField || ""] === "object" ? row[pivot.rowField || ""].value : row[pivot.rowField || ""];
18089
- let value = typeof row[pivot.rowField || ""] === "object" ? row[pivot.rowField || ""].value : row[pivot.rowField || ""];
18090
- if (dateBucket === "week" && dateFilter?.startDate && dateFilter?.endDate) {
18091
- const rowDate = new Date(value);
18092
- if (rowDate < dateFilter.startDate) {
18093
- value = dateFilter.startDate.toISOString();
18094
- } else if (rowDate > dateFilter.endDate) {
18095
- value = dateFilter.endDate.toISOString();
18096
- }
18097
- }
18098
- const dateString = getDateString(
18099
- value,
18100
- dateFilter?.startDate && dateFilter?.endDate ? { start: dateFilter.startDate, end: dateFilter.endDate } : void 0,
18101
- dateBucket,
18102
- databaseType
18103
- );
18104
- row[pivot.rowField || ""] = dateString;
18105
- });
18106
- if (pivot.rowField && pivot.rowFieldType && !isStringType(pivot.rowFieldType) && dateFilter?.startDate && dateFilter?.endDate) {
18107
- const dateSet = new Set(
18108
- rows.map((row) => row[pivot.rowField || ""])
18109
- );
18110
- for (let date = dateFilter.startDate; date <= dateFilter.endDate; date = new Date(date.getTime() + 24 * 60 * 60 * 1e3)) {
18111
- const formattedDate = getDateString(
18112
- date.toISOString(),
18113
- { start: dateFilter.startDate, end: dateFilter.endDate },
18114
- dateBucket,
18115
- databaseType
18116
- );
18117
- if (!dateSet.has(formattedDate)) {
18118
- const newRow = {};
18119
- newRow[pivot.rowField] = formattedDate;
18120
- newRow.__quillRawDate = date.toISOString();
18121
- rows.push(newRow);
18122
- dateSet.add(formattedDate);
18123
- }
18124
- }
18125
- }
18126
- if (!pivot.sort) {
18127
- rows.sort((a, b) => {
18128
- if (a.__quillRawDate < b.__quillRawDate) {
18129
- return -1;
18130
- }
18131
- if (a.__quillRawDate > b.__quillRawDate) {
18132
- return 1;
18133
- }
18134
- return 0;
18135
- });
18136
- }
18137
- }
18138
- columns?.forEach((column, index) => {
18139
- if (column.label && ["null", "undefined"].includes(column.label.toLowerCase()) && !pivot.columnField && !pivot.aggregations?.[index]?.valueField && pivot.aggregations?.[index]?.aggregationType === "count") {
18140
- column.label = "Count";
18141
- }
18142
- });
18143
- const numericColumns = columns?.filter(
18144
- (column) => column.format === "whole_number" || column.format === "percentage"
18145
- );
18146
- rows.forEach((row) => {
18147
- numericColumns?.forEach((column) => {
18148
- row[column.field] = row[column.field] ?? 0;
18149
- });
18150
- });
18151
- return {
18152
- rows,
18153
- columns: columns ?? [],
18154
- rowCount: getPivotRowCount ? Number(resp?.queryResults?.[1]?.rows?.[0]?.["row_count"]) ?? rows.length : 0,
18155
- pivotQuery: sqlQuery,
18156
- comparisonPivotQuery: comparisonPivotSql
18157
- };
18158
- }
18159
- function generatePivotTableYAxis(pivot, cols, yAxisField) {
18160
- if (pivot?.aggregationType === "count") {
18161
- return [
18162
- {
18163
- field: pivot.valueField ?? "count",
18164
- label: yAxisField.label,
18165
- format: yAxisField.format
18166
- }
18167
- ];
18168
- }
18169
- return [
18170
- {
18171
- field: pivot.valueField ?? "count",
18172
- label: yAxisField.label,
18173
- format: yAxisField.format
18174
- }
18175
- ];
18176
- }
18177
- function generatePivotTitle(pivot) {
18178
- if (pivot.rowField && !pivot.valueField && pivot.aggregations?.[0]) {
18179
- return snakeAndCamelCaseToTitleCase(
18180
- `${pivot.aggregations[0].aggregationType} of ${pivot.rowField}${pivot.columnField ? ` by ${pivot.columnField}` : ""}
18181
- `
18182
- );
18183
- } else if (!pivot.rowField && pivot.aggregations?.[0]?.valueField) {
18184
- return snakeAndCamelCaseToTitleCase(
18185
- `${pivot.aggregations[0].aggregationType} of ${pivot.aggregations[0].valueField}
18186
- `
18187
- );
18188
- }
18189
- return snakeAndCamelCaseToTitleCase(
18190
- `${pivot.aggregations?.[0]?.aggregationType ?? "Aggregation"} of ${pivot.aggregations?.[0]?.valueField ?? "value"}${pivot.rowField ? ` by ${pivot.rowField}` : ""}${pivot.columnField ? ` and ${pivot.columnField}` : ""}`
18191
- );
18192
- }
18193
- async function generatePivotTable({
18194
- pivot,
18195
- dateBucket,
18196
- dateFilter,
18197
- report,
18198
- client,
18199
- getToken,
18200
- eventTracking,
18201
- uniqueValues,
18202
- dashboardName,
18203
- tenants,
18204
- additionalProcessing,
18205
- caller,
18206
- pivotQuery
18207
- }) {
18208
- try {
18209
- if (report && client) {
18210
- const pivotTable = await generatePivotWithSQL({
18211
- pivotQuery,
18212
- pivot,
18213
- report,
18214
- client,
18215
- dateBucket,
18216
- dateFilter,
18217
- distinctStrings: pivot.columnField && uniqueValues?.[pivot.columnField] ? uniqueValues[pivot.columnField] : [],
18218
- dashboardName,
18219
- tenants,
18220
- additionalProcessing,
18221
- caller,
18222
- getToken
18223
- });
18224
- return pivotTable;
18225
- }
18226
- } catch (e) {
18227
- eventTracking?.logError?.({
18228
- type: "bug",
18229
- // TODO: determine type
18230
- severity: "high",
18231
- message: "Error generating pivot table",
18232
- errorMessage: e.message,
18233
- errorStack: e.stack,
18234
- errorData: {
18235
- caller: "PivotModal",
18236
- function: "generatePivotTable"
18237
- }
18238
- });
18239
- throw Error(`Failed to generate pivot table with SQL: ${e}`);
18240
- }
18241
- throw Error("Failed to generate pivot table: invalid report");
18242
- }
18243
-
18244
- // src/utils/errorProcessing.ts
18245
- function processFilterErrorList(resp) {
18246
- if (!resp || !resp.filterErrorList || !Array.isArray(resp.filterErrorList)) {
18247
- return;
18248
- }
18249
- }
18250
-
18251
- // src/utils/paginationProcessing.ts
18252
- var DEFAULT_PAGINATION = {
18253
- page: 0,
18254
- rowsPerPage: 10,
18255
- rowsPerRequest: 600
18256
- };
18257
- var DEFAULT_TABLE_PAGINATION = {
18258
- page: 0,
18259
- rowsPerPage: 10,
18260
- rowsPerRequest: 50
18261
- };
18262
- function shouldFetchMore(pagination, page, maxPage, currentRowCount) {
18263
- if (!pagination || currentRowCount && currentRowCount >= pagination.rowsPerPage * (page + 1)) {
18264
- return false;
18265
- }
18266
- const indexAdjustedPage = page;
18267
- const pageInterval = Math.floor(
18268
- indexAdjustedPage * pagination.rowsPerPage / pagination.rowsPerRequest
18269
- );
18270
- const indexAdjustedPreviousPage = maxPage;
18271
- const previousPageInterval = Math.floor(
18272
- indexAdjustedPreviousPage * pagination.rowsPerPage / pagination.rowsPerRequest
18273
- );
18274
- return pageInterval > previousPageInterval;
18275
- }
18276
- function shouldSortInMemory(pagination, rowCount) {
18277
- if (!rowCount || rowCount < pagination.rowsPerRequest) {
18278
- return true;
18279
- }
18280
- return false;
18281
- }
18282
17553
 
18283
17554
  // src/utils/tableProcessing.ts
18284
17555
  var getUniqueValuesByQuery = async ({
@@ -19020,491 +18291,6 @@ var fetchTableByAST = async (ast, client, getToken, tenants, eventTracking, dash
19020
18291
  return { rows, columns, rowCount, error, itemQuery, referencedTables };
19021
18292
  };
19022
18293
 
19023
- // src/utils/dashboard.ts
19024
- var defaultDashboardItem = {
19025
- id: "",
19026
- name: "",
19027
- dashboardName: "",
19028
- rows: [],
19029
- compareRows: [],
19030
- columns: [],
19031
- chartType: "",
19032
- pivot: null,
19033
- yAxisFields: [],
19034
- xAxisLabel: "",
19035
- xAxisField: "",
19036
- xAxisFormat: "string",
19037
- order: -1,
19038
- filtersApplied: [],
19039
- queryString: "",
19040
- rowCount: 0,
19041
- columnInternal: []
19042
- };
19043
- async function cleanDashboardItem({
19044
- item,
19045
- dashboardFilters,
19046
- getToken,
19047
- eventTracking,
19048
- client,
19049
- dateBucket,
19050
- additionalProcessing,
19051
- customFields,
19052
- skipPivotFetch,
19053
- tenants
19054
- }) {
19055
- if (!item) return defaultDashboardItem;
19056
- if (!item.rows) {
19057
- return {
19058
- ...defaultDashboardItem,
19059
- id: item._id,
19060
- name: item.name
19061
- };
19062
- }
19063
- const fields = item.fields || [];
19064
- const columnsWithCustomFields = [...item.columns ?? []];
19065
- if (item.includeCustomFields && item.rows?.length > 0) {
19066
- const tables = item.referencedTables ?? [];
19067
- tables.forEach((table) => {
19068
- const _customFields = customFields?.[table] ?? [];
19069
- _customFields.forEach((field) => {
19070
- const isJsonCustomField = !!field.refColumn;
19071
- if (item.rows[0][field.field] !== void 0 && !item.columns.some((col) => col.field === field.field)) {
19072
- const result_field = fields.find((f) => f.name === field.field);
19073
- const converted_field = convertPostgresColumn(result_field ?? {});
19074
- columnsWithCustomFields.push({
19075
- field: field.field,
19076
- format: converted_field.format,
19077
- label: snakeAndCamelCaseToTitleCase(field.field),
19078
- fieldType: converted_field.fieldType,
19079
- dataTypeID: converted_field.dataTypeID,
19080
- jsType: converted_field.jsType,
19081
- inferFormat: isJsonCustomField
19082
- });
19083
- }
19084
- });
19085
- });
19086
- }
19087
- const processedColumns = item.columns.map((col) => {
19088
- return { ...col, label: snakeAndCamelCaseToTitleCase(col.label) };
19089
- });
19090
- const columnInternal = (item.includeCustomFields ? columnsWithCustomFields : item.columns).map((col) => {
19091
- const field = item.fields?.find((f) => f.name === col.field);
19092
- const converted_field = convertPostgresColumn(field ?? {});
19093
- return {
19094
- fieldType: converted_field.fieldType,
19095
- dataTypeID: converted_field.dataTypeID,
19096
- jsType: converted_field.jsType,
19097
- ...col,
19098
- label: snakeAndCamelCaseToTitleCase(col.label)
19099
- };
19100
- });
19101
- let pivotTable;
19102
- let pivotError;
19103
- try {
19104
- const shouldPaginatePivotAsTable = item.chartType === "table";
19105
- const pivotChartProcessing = {
19106
- page: DEFAULT_PAGINATION
19107
- };
19108
- pivotTable = await getPivotTable(
19109
- {
19110
- ...item,
19111
- pivot: item.pivot && !skipPivotFetch ? {
19112
- ...item.pivot,
19113
- aggregations: item.pivot.aggregations ?? [
19114
- {
19115
- valueField: item.pivot.valueField,
19116
- valueFieldType: item.pivot.valueFieldType,
19117
- valueField2: item.pivot.valueField2,
19118
- valueField2Type: item.pivot.valueField2Type,
19119
- aggregationType: item.pivot.aggregationType
19120
- }
19121
- ]
19122
- } : void 0
19123
- },
19124
- dashboardFilters,
19125
- item.dashboardName,
19126
- getToken,
19127
- client,
19128
- eventTracking,
19129
- dateBucket,
19130
- shouldPaginatePivotAsTable ? additionalProcessing : pivotChartProcessing,
19131
- tenants,
19132
- customFields
19133
- );
19134
- } catch (e) {
19135
- pivotTable = void 0;
19136
- eventTracking?.logError?.({
19137
- type: "bug",
19138
- // TODO: determine type
19139
- severity: "high",
19140
- message: "Error fetching pivot table",
19141
- errorMessage: e.message,
19142
- errorStack: e.stack,
19143
- errorData: {
19144
- caller: "cleanDashboardItem",
19145
- function: "cleanDashboardItem"
19146
- }
19147
- });
19148
- console.error("Error fetching pivot table", e);
19149
- pivotError = "Error fetching pivot table";
19150
- }
19151
- const referenceLineYValues = [];
19152
- for (const key in item) {
19153
- if (key.startsWith("referenceLine_")) {
19154
- const field = key.slice(14);
19155
- if (!item[key] || !item[key][0]) continue;
19156
- const value = Object.values(item[key][0])[0];
19157
- referenceLineYValues.push({ label: field, query: [value, value] });
19158
- }
19159
- }
19160
- if (item.referenceLines) {
19161
- for (const referenceLine of item.referenceLines) {
19162
- if (Array.isArray(referenceLine.query)) {
19163
- referenceLineYValues.push({
19164
- label: referenceLine.label,
19165
- query: referenceLine.query
19166
- });
19167
- } else if (referenceLine.query === "") {
19168
- referenceLineYValues.push({
19169
- label: referenceLine.label,
19170
- query: (pivotTable?.rows ? pivotTable.rows : item.rows).map(
19171
- (row) => Number(row[referenceLine.label]) || 0
19172
- )
19173
- });
19174
- }
19175
- }
19176
- }
19177
- return {
19178
- id: item._id ?? item.id,
19179
- name: item.name,
19180
- dashboardName: item.dashboardName,
19181
- // section: item.section,
19182
- rows: item.rows,
19183
- pivotRows: pivotTable ? pivotTable.rows : void 0,
19184
- pivotColumns: pivotTable ? pivotTable.columns : void 0,
19185
- compareRows: item.compareRows,
19186
- columns: processedColumns.map((column) => {
19187
- return {
19188
- field: column.field,
19189
- format: column.format,
19190
- label: column.label,
19191
- inferFormat: column.inferFormat
19192
- };
19193
- }),
19194
- includeCustomFields: item.includeCustomFields,
19195
- columnInternal,
19196
- columnsWithCustomFields,
19197
- chartType: item.chartType,
19198
- dateField: item.dateField,
19199
- pivot: pivotError ? void 0 : item.pivot ? {
19200
- ...item.pivot,
19201
- aggregations: item.pivot.aggregations ?? [
19202
- {
19203
- valueField: item.pivot.valueField,
19204
- valueFieldType: item.pivot.valueFieldType,
19205
- valueField2: item.pivot.valueField2,
19206
- valueField2Type: item.pivot.valueField2Type,
19207
- aggregationType: item.pivot.aggregationType
19208
- }
19209
- ],
19210
- columnValues: item.distinctStrings
19211
- } : void 0,
19212
- yAxisFields: pivotTable ? extractPivotedYAxis(pivotTable, item) : item.yAxisFields,
19213
- xAxisLabel: item.xAxisLabel,
19214
- xAxisField: item.xAxisField,
19215
- xAxisFormat: item.xAxisFormat,
19216
- order: item.order,
19217
- filtersApplied: item.filtersApplied,
19218
- filterMap: item.filterMap,
19219
- flags: item.flags,
19220
- rowCount: item.rowCount ? parseInt(item.rowCount) : item.rows.length,
19221
- pivotRowCount: pivotTable ? pivotTable.rowCount : void 0,
19222
- template: item.template,
19223
- sort: item.sort,
19224
- itemQuery: item.itemQuery,
19225
- queryString: item.queryString,
19226
- pivotQuery: pivotTable?.pivotQuery,
19227
- comparisonPivotQuery: pivotTable?.comparisonPivotQuery,
19228
- referencedTables: item?.referencedTables || [],
19229
- referencedColumns: item?.referencedColumns || {},
19230
- error: item.error ?? pivotError,
19231
- referenceLineYValues,
19232
- referenceLines: item.referenceLines
19233
- };
19234
- }
19235
- async function getPivotTable(report, dashboardFilters, dashboardName, getToken, client, eventTracking, dateBucketInitial, additionalProcessing, tenants, customFields) {
19236
- if (!report) return void 0;
19237
- const dateFilter = Object.values(dashboardFilters ?? {}).find(
19238
- (filter) => filter.filterType === "date_range" || filter.operator === "BETWEEN"
19239
- );
19240
- if (dateFilter?.operator === "BETWEEN") {
19241
- dateFilter.startDate = dateFilter.value[0];
19242
- dateFilter.endDate = dateFilter.value[1];
19243
- }
19244
- const pivot = report?.pivot;
19245
- const data = report || {};
19246
- if (pivot && client) {
19247
- if (report.rowCount === 0 || report.rows.length === 0) {
19248
- const columns = [];
19249
- if (pivot.rowField) {
19250
- columns.push({
19251
- field: pivot.rowField,
19252
- label: snakeCaseToTitleCase(processColumnName(pivot.rowField)),
19253
- format: pivot.rowFieldType || "string",
19254
- jsType: convertFieldTypeToJSType(pivot.rowFieldType || "string"),
19255
- fieldType: pivot.rowFieldType || "string",
19256
- dataTypeID: fieldTypeToDataTypeID(pivot.rowFieldType || "string")
19257
- });
19258
- }
19259
- for (const agg of pivot.aggregations ?? []) {
19260
- if (agg.valueField) {
19261
- columns.push({
19262
- field: agg.valueField,
19263
- label: snakeCaseToTitleCase(processColumnName(agg.valueField)),
19264
- //FIXME: valueFieldType is not always the same as the format
19265
- format: agg.valueFieldType ?? "whole_number",
19266
- jsType: agg.valueFieldType ?? "number",
19267
- fieldType: agg.valueFieldType ?? "number",
19268
- dataTypeID: fieldTypeToDataTypeID(agg.valueFieldType ?? "number")
19269
- });
19270
- }
19271
- }
19272
- const pivotQuery = generatePivotQuery(
19273
- pivot,
19274
- report.itemQuery?.[0],
19275
- client.databaseType
19276
- );
19277
- return {
19278
- rows: [],
19279
- rowCount: 0,
19280
- pivotQuery: pivotQuery ?? "",
19281
- columns
19282
- };
19283
- }
19284
- try {
19285
- let dateBucket = dateBucketInitial;
19286
- let filterDateRange = void 0;
19287
- if (dateFilter && dateFilter.startDate && dateFilter.endDate) {
19288
- filterDateRange = {
19289
- start: dateFilter.startDate,
19290
- end: dateFilter.endDate
19291
- };
19292
- } else if (report.dateRange) {
19293
- filterDateRange = report.dateRange;
19294
- }
19295
- if (!dateBucket && filterDateRange) {
19296
- dateBucket = getDateBucketFromRange(filterDateRange);
19297
- }
19298
- if (pivot.columnField && !report.distinctStrings) {
19299
- const columnFieldColumn = (report.columnsWithCustomFields ?? report.columns).find((col) => col.field === pivot.columnField);
19300
- if (!columnFieldColumn) {
19301
- console.error(
19302
- "could not find pivot column field on report",
19303
- pivot.columnField
19304
- );
19305
- }
19306
- const unique = await getUniqueValuesByQuery({
19307
- columns: [columnFieldColumn],
19308
- query: report.queryString,
19309
- client,
19310
- getToken,
19311
- dashboardName,
19312
- tenants,
19313
- customFields: customFields ?? {},
19314
- eventTracking
19315
- });
19316
- report.distinctStrings = unique?.[pivot.columnField] ?? [];
19317
- }
19318
- if (pivot.columnField && !report.distinctStrings) {
19319
- const columnFieldColumn = (report.columnsWithCustomFields ?? report.columns).find((col) => col.field === pivot.columnField);
19320
- if (!columnFieldColumn) {
19321
- console.error(
19322
- "could not find pivot column field on report",
19323
- pivot.columnField
19324
- );
19325
- }
19326
- const unique = await getUniqueValuesByQuery({
19327
- columns: [columnFieldColumn],
19328
- query: report.queryString,
19329
- client,
19330
- getToken,
19331
- dashboardName,
19332
- tenants,
19333
- customFields: customFields ?? {},
19334
- eventTracking
19335
- });
19336
- report.distinctStrings = unique?.[pivot.columnField] ?? [];
19337
- }
19338
- const pivotTable = await generatePivotWithSQL({
19339
- pivot,
19340
- report,
19341
- client,
19342
- dateBucket,
19343
- dateFilter,
19344
- distinctStrings: report.distinctStrings,
19345
- dashboardName,
19346
- tenants,
19347
- additionalProcessing,
19348
- getToken
19349
- });
19350
- return pivotTable;
19351
- } catch (e) {
19352
- eventTracking?.logError?.({
19353
- type: "bug",
19354
- // TODO: determine type
19355
- severity: "high",
19356
- message: "Error fetching pivot table",
19357
- errorMessage: e.message,
19358
- errorStack: e.stack,
19359
- errorData: {
19360
- caller: "getPivotTable",
19361
- function: "getPivotTable"
19362
- }
19363
- });
19364
- console.error("Error fetching pivot table", e);
19365
- throw e;
19366
- }
19367
- }
19368
- return pivot && data.rows ? generatePivotTable({
19369
- pivot,
19370
- report,
19371
- client,
19372
- uniqueValues: report.distinctStrings,
19373
- dashboardName,
19374
- tenants,
19375
- dateFilter,
19376
- additionalProcessing,
19377
- getToken,
19378
- eventTracking
19379
- }) : void 0;
19380
- }
19381
- function extractPivotedYAxis(pivotTable, itemInfo, config = void 0) {
19382
- if (!pivotTable) return itemInfo?.yAxisFields ?? [];
19383
- const pivot = itemInfo?.pivot || config?.pivot;
19384
- if (!pivot.columnField && !pivot.rowField) return itemInfo?.yAxisFields ?? [];
19385
- const yAxisFields = config ? config.yAxisFields : itemInfo?.yAxisFields;
19386
- return yAxisFields && yAxisFields.length > 0 ? generatePivotTableYAxis(pivot, pivotTable.columns, yAxisFields[0]) : yAxisFields;
19387
- }
19388
- async function getDashboard(dashboardName, client, getToken, tenants, flags) {
19389
- const { data: resp } = await quillFetch({
19390
- client,
19391
- task: "dashboard",
19392
- metadata: {
19393
- name: dashboardName,
19394
- clientId: client.publicKey,
19395
- databaseType: client.databaseType,
19396
- useNewNodeSql: true,
19397
- tenants,
19398
- flags
19399
- },
19400
- getToken
19401
- });
19402
- return {
19403
- ...resp,
19404
- createdAt: resp.createdAt && new Date(resp.createdAt),
19405
- dateFilter: resp.dateFilter ? {
19406
- ...resp.dateFilter,
19407
- presetOptions: resp.dateFilter.presetOptions?.map(
19408
- (preset) => ({
19409
- ...preset,
19410
- loopStart: preset.loopStart ? new Date(preset.loopStart) : void 0,
19411
- loopEnd: preset.loopEnd ? new Date(preset.loopEnd) : void 0
19412
- })
19413
- ),
19414
- defaultPresetRanges: resp.dateFilter.defaultPresetRanges?.map(
19415
- (preset) => ({
19416
- ...preset,
19417
- loopStart: preset.loopStart ? new Date(preset.loopStart) : void 0,
19418
- loopEnd: preset.loopEnd ? new Date(preset.loopEnd) : void 0
19419
- })
19420
- )
19421
- } : void 0
19422
- };
19423
- }
19424
-
19425
- // src/utils/chartBuilder.ts
19426
- var numberFormatOptions = [
19427
- "whole_number",
19428
- "one_decimal_place",
19429
- "two_decimal_places",
19430
- "dollar_amount",
19431
- "dollar_cents",
19432
- "percentage"
19433
- ];
19434
- var dateFormatOptions = [
19435
- "MMM_yyyy",
19436
- "MMM_dd",
19437
- "MMM_dd_yyyy",
19438
- "MMM_dd_hh:mm_ap_pm",
19439
- "hh_ap_pm",
19440
- "date",
19441
- "timestamptz"
19442
- ];
19443
- var NUMBER_OPTIONS = [
19444
- { value: "whole_number", label: "whole number" },
19445
- { value: "one_decimal_place", label: "one decimal place" },
19446
- { value: "two_decimal_places", label: "two decimal places" },
19447
- { value: "dollar_amount", label: "dollar amount" },
19448
- { value: "dollar_cents", label: "dollar and cent amount" },
19449
- { value: "percent", label: "percentage" }
19450
- ];
19451
- var DATE_OPTIONS = [
19452
- { value: "MMM_yyyy", label: "month" },
19453
- { value: "MMM_dd", label: "day" },
19454
- { value: "MMM_dd_yyyy", label: "day and year" },
19455
- { value: "MMM_dd_hh:mm_ap_pm", label: "day and time" },
19456
- { value: "hh_ap_pm", label: "hour" }
19457
- ];
19458
- var ALL_FORMAT_OPTIONS = [
19459
- ...NUMBER_OPTIONS,
19460
- ...DATE_OPTIONS,
19461
- { value: "string", label: "string" }
19462
- ];
19463
- function createInitialFormData(columns) {
19464
- const firstNumberColumn = columns?.find(
19465
- (col) => numberFormatOptions.includes(col.format)
19466
- );
19467
- const firstStringColumn = columns?.find(
19468
- (col) => !numberFormatOptions.includes(col.format) && !dateFormatOptions.includes(col.format)
19469
- );
19470
- const firstDateColumn = columns?.find(
19471
- (col) => dateFormatOptions.includes(col.format)
19472
- );
19473
- const xAxisField = firstStringColumn?.field || firstDateColumn?.field || firstNumberColumn?.field || columns?.[0]?.field || "";
19474
- const xAxisFormat = firstStringColumn?.format || firstDateColumn?.format || firstNumberColumn?.format || columns?.[0]?.format || "string";
19475
- const formEmptyState = {
19476
- name: "",
19477
- columns: columns.map((col) => {
19478
- return { ...col, label: snakeAndCamelCaseToTitleCase(col.label) };
19479
- }),
19480
- xAxisField,
19481
- xAxisFormat,
19482
- yAxisFields: [
19483
- {
19484
- field: firstNumberColumn?.field || columns?.[0]?.field || "",
19485
- label: "",
19486
- format: firstNumberColumn?.format || columns?.[0]?.format || "string"
19487
- }
19488
- ],
19489
- xAxisLabel: "",
19490
- chartType: firstNumberColumn ? "line" : "table",
19491
- pivot: null,
19492
- template: true,
19493
- referenceLines: []
19494
- };
19495
- return formEmptyState;
19496
- }
19497
-
19498
- // src/utils/error.ts
19499
- var DataLoadError = class extends Error {
19500
- data;
19501
- constructor(message, data) {
19502
- super(message);
19503
- this.name = "DataLoadError";
19504
- this.data = data;
19505
- }
19506
- };
19507
-
19508
18294
  // src/components/ReportBuilder/convert.ts
19509
18295
  import { isValid as isValid3, parseISO as parseISO2 } from "date-fns";
19510
18296
  function recursiveSearchAndReplace(node, search, replace) {
@@ -23156,6 +21942,35 @@ import jsPDF from "jspdf";
23156
21942
 
23157
21943
  // src/hooks/useDashboard.ts
23158
21944
  import { useContext, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
21945
+
21946
+ // src/utils/merge.ts
21947
+ import {
21948
+ add,
21949
+ startOfDay as startOfDay3,
21950
+ startOfMonth,
21951
+ startOfWeek as startOfWeek3,
21952
+ startOfYear
21953
+ } from "date-fns";
21954
+ import { utcToZonedTime as utcToZonedTime2 } from "date-fns-tz";
21955
+ function mergeComparisonRange(resp) {
21956
+ if (resp.chartType === "table") return resp;
21957
+ const compRows = resp.compareRows;
21958
+ if (!compRows) return resp;
21959
+ const newRows = resp.rows.map((row, i) => {
21960
+ if (i < compRows.length) {
21961
+ const compRow = compRows[i];
21962
+ const newRow = { ...row };
21963
+ for (const [key, value] of Object.entries(compRow)) {
21964
+ newRow[`comparison_${key}`] = value;
21965
+ }
21966
+ return newRow;
21967
+ }
21968
+ return row;
21969
+ });
21970
+ return { ...resp, rows: newRows };
21971
+ }
21972
+
21973
+ // src/hooks/useDashboard.ts
23159
21974
  var useDashboardInternal = (dashboardName, customFilters) => {
23160
21975
  const [dashboard] = useContext(DashboardContext);
23161
21976
  const {
@@ -28396,7 +27211,7 @@ var ChartTooltipPrimary = (props) => /* @__PURE__ */ jsxs22(ChartTooltipFrame2,
28396
27211
  paddingTop: 2,
28397
27212
  paddingBottom: 2
28398
27213
  },
28399
- children: props.label && !isNaN(new Date(props.label)) && props.dateFormatter ? props.dateFormatter(props.label) : props.label && !isNaN(new Date(props.label)) ? format6(new Date(props.label), "MMM yyyy") : props.label || "-"
27214
+ children: !isNaN(new Date(props.label)) && props.dateFormatter ? props.dateFormatter(props.label) : !isNaN(new Date(props.label)) ? format6(new Date(props.label), "MMM yyyy") : props.label
28400
27215
  }
28401
27216
  )
28402
27217
  }
@@ -28445,9 +27260,9 @@ function reformatComparisonPayload(props, primaryLabel, comparisonLabel) {
28445
27260
  const nameKey = isComparison ? `comparison_${props.xAxisField}` : props.xAxisField;
28446
27261
  const days = LABEL_TO_DAYS[primaryLabel] ?? 0;
28447
27262
  const primaryDate = item.payload[props.xAxisField] ?? 0;
28448
- const compDate = primaryDate ? subDays2(new Date(primaryDate), days + 1) : /* @__PURE__ */ new Date();
27263
+ const compDate = subDays2(new Date(primaryDate), days + 1);
28449
27264
  const date = item.payload[nameKey] ?? format6(compDate, props.xAxisFormat);
28450
- const name2 = props.dateFormatter ? props.dateFormatter(date) : date;
27265
+ const name2 = props.dateFormatter(date);
28451
27266
  const color2 = item.color;
28452
27267
  const value = props.valueFormatter(item.value, item.name);
28453
27268
  if (!columnsByKey[""]) {
@@ -28479,7 +27294,7 @@ function reformatComparisonPayload(props, primaryLabel, comparisonLabel) {
28479
27294
  return columnsByKey;
28480
27295
  }
28481
27296
  function getTooltipLabel(props, altTooltipLabel, isDateXAxis) {
28482
- return props.payload.length <= 2 && altTooltipLabel && isDateXAxis ? altTooltipLabel && !isNaN(new Date(altTooltipLabel)) && props.dateFormatter ? props.dateFormatter(altTooltipLabel) : altTooltipLabel && !isNaN(new Date(altTooltipLabel)) ? format6(new Date(altTooltipLabel), "MMM yyyy") : altTooltipLabel || "-" : props.label && !isNaN(new Date(props.label)) && props.dateFormatter ? props.dateFormatter(props.label) : props.label && !isNaN(new Date(props.label)) ? format6(new Date(props.label), "MMM yyyy") : props.label || "-";
27297
+ return props.payload.length <= 2 && altTooltipLabel && isDateXAxis ? !isNaN(new Date(altTooltipLabel)) && props.dateFormatter ? props.dateFormatter(altTooltipLabel) : !isNaN(new Date(altTooltipLabel)) ? format6(new Date(altTooltipLabel), "MMM yyyy") : altTooltipLabel : !isNaN(new Date(props.label)) && props.dateFormatter ? props.dateFormatter(props.label) : !isNaN(new Date(props.label)) ? format6(new Date(props.label), "MMM yyyy") : props.label;
28483
27298
  }
28484
27299
  function ChartTooltipComparison(props) {
28485
27300
  const isDateXAxis = isDateFormat(props.xAxisFormat);
@@ -42715,7 +41530,6 @@ function ChartBuilder({
42715
41530
  }, [reportId]);
42716
41531
  const [windowWidth, setWindowWidth] = useState28(1200);
42717
41532
  const [rows, setRows] = useState28(report?.rows ?? []);
42718
- const [itemQuery, setItemQuery] = useState28(report?.itemQuery);
42719
41533
  const [rowCount, setRowCount] = useState28(report?.rowCount ?? 0);
42720
41534
  const [maxPage, setMaxPage] = useState28(0);
42721
41535
  const [isLoading, setIsLoading] = useState28(false);
@@ -43445,7 +42259,7 @@ function ChartBuilder({
43445
42259
  pivot,
43446
42260
  dateBucket,
43447
42261
  dateFilter,
43448
- report: report ? { ...report, ...tableInfo ?? { itemQuery } } : void 0,
42262
+ report: report ? { ...report, ...tableInfo } : void 0,
43449
42263
  client,
43450
42264
  uniqueValues,
43451
42265
  dashboardName: destinationDashboardName,
@@ -43606,7 +42420,6 @@ function ChartBuilder({
43606
42420
  setCurrentProcessing(processing);
43607
42421
  setRows(tableInfo.rows);
43608
42422
  setProcessedColumns(processColumns(tableInfo.columns));
43609
- setItemQuery(tableInfo.itemQuery);
43610
42423
  fetchRowCount(processing, overrideFilters);
43611
42424
  if (formData.pivot) {
43612
42425
  try {
@@ -44512,7 +43325,7 @@ function ChartBuilder({
44512
43325
  uniqueValuesIsLoading: initialUniqueValuesIsLoading,
44513
43326
  initialSelectedPivotTable: selectedPivotTable,
44514
43327
  pivotRecommendationsEnabled,
44515
- report: report ? { ...report, ...{ itemQuery } } : void 0,
43328
+ report,
44516
43329
  dashboardName: destinationDashboardName || "",
44517
43330
  dateFilter: filtersEnabled ? currentDashboardFilters?.find(
44518
43331
  (f) => f.filterType === "date_range"