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