@quillsql/react 2.16.20 → 2.16.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1572,6 +1572,7 @@ var init_dateRangePickerUtils = __esm({
1572
1572
  }
1573
1573
  });
1574
1574
  const defaultCustomIntervals = defaultIntervals.flatMap((interval2) => {
1575
+ const normalizedLabel = interval2.label?.toLowerCase().trim();
1575
1576
  let createdIntervals = [];
1576
1577
  if (interval2.label === "This week") {
1577
1578
  createdIntervals = [
@@ -1721,13 +1722,13 @@ var init_dateRangePickerUtils = __esm({
1721
1722
  unit: "months"
1722
1723
  }
1723
1724
  ];
1724
- } else if (interval2.label === "Last month") {
1725
+ } else if (normalizedLabel === "last month") {
1725
1726
  createdIntervals = [
1726
1727
  {
1727
1728
  type: "previous_month"
1728
1729
  }
1729
1730
  ];
1730
- } else if (interval2.label === "Last quarter") {
1731
+ } else if (normalizedLabel === "previous quarter" || normalizedLabel === "last quarter") {
1731
1732
  createdIntervals = [
1732
1733
  {
1733
1734
  type: "previous_quarter"
@@ -2121,13 +2122,13 @@ var init_Filter = __esm({
2121
2122
  TimeUnit2[TimeUnit2["Hour"] = HOUR] = "Hour";
2122
2123
  return TimeUnit2;
2123
2124
  })(TimeUnit || {});
2124
- FieldType = /* @__PURE__ */ ((FieldType2) => {
2125
- FieldType2[FieldType2["String"] = STRING] = "String";
2126
- FieldType2[FieldType2["Number"] = NUMBER] = "Number";
2127
- FieldType2[FieldType2["Date"] = DATE] = "Date";
2128
- FieldType2[FieldType2["Null"] = NULL] = "Null";
2129
- FieldType2[FieldType2["Boolean"] = BOOLEAN] = "Boolean";
2130
- return FieldType2;
2125
+ FieldType = /* @__PURE__ */ ((FieldType3) => {
2126
+ FieldType3[FieldType3["String"] = STRING] = "String";
2127
+ FieldType3[FieldType3["Number"] = NUMBER] = "Number";
2128
+ FieldType3[FieldType3["Date"] = DATE] = "Date";
2129
+ FieldType3[FieldType3["Null"] = NULL] = "Null";
2130
+ FieldType3[FieldType3["Boolean"] = BOOLEAN] = "Boolean";
2131
+ return FieldType3;
2131
2132
  })(FieldType || {});
2132
2133
  InternalFilterType = /* @__PURE__ */ ((InternalFilterType2) => {
2133
2134
  InternalFilterType2["StringFilter"] = "string-filter";
@@ -2273,7 +2274,7 @@ var init_Filter = __esm({
2273
2274
  fieldType = FieldType.Boolean;
2274
2275
  operator = filter.operator;
2275
2276
  field = filter.field;
2276
- value = null;
2277
+ value = filter.value;
2277
2278
  table = filter.table;
2278
2279
  return {
2279
2280
  filterType,
@@ -2284,7 +2285,7 @@ var init_Filter = __esm({
2284
2285
  operator
2285
2286
  };
2286
2287
  case "date" /* Date */:
2287
- if (typeof filter.value === "object" && filter.value && "value" in filter.value && filter.value.unit) {
2288
+ if (typeof filter.value === "object" && filter.value && "unit" in filter.value) {
2288
2289
  if (filter.value.value && typeof filter.value.value !== "number" || typeof filter.value.unit !== "string") {
2289
2290
  throw new Error(
2290
2291
  `Invalid value for DateFilter, expected { value: number, unit: string }, got ${filter.value}`
@@ -2340,7 +2341,7 @@ var init_Filter = __esm({
2340
2341
  table = filter.table;
2341
2342
  } else {
2342
2343
  throw new Error(
2343
- `Invalid value for DateFilter, expected { value: number, unit: string }, { startDate: string, endDate: string}, or string, got ${filter.value}`
2344
+ `Invalid value for DateFilter, expected { value?: number, unit: string }, { startDate: string, endDate: string}, or string, got ${filter.value}`
2344
2345
  );
2345
2346
  }
2346
2347
  return {
@@ -15951,7 +15952,7 @@ var init_dataProcessing = __esm({
15951
15952
  } else {
15952
15953
  if (data.dateField && data.dateField.field) {
15953
15954
  const dateField = data.dateField.field.replaceAll('"', "");
15954
- const maxDate = new Date(
15955
+ const maxDate2 = new Date(
15955
15956
  data.rows.reduce((acc, row) => {
15956
15957
  const rowValue = row[dateField];
15957
15958
  if (rowValue) {
@@ -15960,7 +15961,7 @@ var init_dataProcessing = __esm({
15960
15961
  return Math.max(acc, 0);
15961
15962
  }, 0)
15962
15963
  );
15963
- const minDate = new Date(
15964
+ const minDate2 = new Date(
15964
15965
  data.rows.reduce((acc, row) => {
15965
15966
  const rowValue = row[dateField];
15966
15967
  if (rowValue) {
@@ -15970,16 +15971,16 @@ var init_dataProcessing = __esm({
15970
15971
  }, (/* @__PURE__ */ new Date("3022-01-01")).getTime())
15971
15972
  );
15972
15973
  const dateBucket = getDateBucketFromRange({
15973
- start: minDate,
15974
- end: maxDate
15974
+ start: minDate2,
15975
+ end: maxDate2
15975
15976
  });
15976
15977
  const minDateBucket = getDateString(
15977
- minDate.toISOString(),
15978
+ minDate2.toISOString(),
15978
15979
  void 0,
15979
15980
  dateBucket
15980
15981
  );
15981
15982
  const maxDateBucket = getDateString(
15982
- maxDate.toISOString(),
15983
+ maxDate2.toISOString(),
15983
15984
  void 0,
15984
15985
  dateBucket
15985
15986
  );
@@ -16096,8 +16097,9 @@ async function generatePivotWithSQL({
16096
16097
  getPivotRowCount = true,
16097
16098
  caller,
16098
16099
  getToken,
16099
- reportBuilderState
16100
+ reportBuilderState,
16100
16101
  // Add reportBuilderState parameter
16102
+ overwriteCache = false
16101
16103
  }) {
16102
16104
  const databaseType = client.databaseType || "postgresql";
16103
16105
  if (!pivot.aggregations?.length && pivot.aggregationType) {
@@ -16146,7 +16148,8 @@ async function generatePivotWithSQL({
16146
16148
  })),
16147
16149
  // Only pass dashboard filters in Dashboard context, not ReportBuilder
16148
16150
  tenants,
16149
- additionalProcessing
16151
+ additionalProcessing,
16152
+ overwriteCache
16150
16153
  },
16151
16154
  getToken
16152
16155
  });
@@ -16320,8 +16323,9 @@ async function generatePivotTable({
16320
16323
  additionalProcessing,
16321
16324
  caller,
16322
16325
  pivotQuery,
16323
- reportBuilderState
16326
+ reportBuilderState,
16324
16327
  // Add reportBuilderState parameter
16328
+ overwriteCache
16325
16329
  }) {
16326
16330
  try {
16327
16331
  if (report && client) {
@@ -16338,8 +16342,9 @@ async function generatePivotTable({
16338
16342
  additionalProcessing,
16339
16343
  caller,
16340
16344
  getToken,
16341
- reportBuilderState
16345
+ reportBuilderState,
16342
16346
  // Pass reportBuilderState
16347
+ overwriteCache
16343
16348
  });
16344
16349
  return pivotTable;
16345
16350
  }
@@ -18224,7 +18229,7 @@ var init_tableProcessing = __esm({
18224
18229
  try {
18225
18230
  let data;
18226
18231
  if (reportBuilderState) {
18227
- let { data: data2 } = await quillFetch({
18232
+ ({ data } = await quillFetch({
18228
18233
  client,
18229
18234
  task: "report-builder-counts",
18230
18235
  metadata: {
@@ -18238,9 +18243,9 @@ var init_tableProcessing = __esm({
18238
18243
  tenants
18239
18244
  },
18240
18245
  getToken
18241
- });
18246
+ }));
18242
18247
  } else {
18243
- let { data: data2 } = await quillFetch({
18248
+ ({ data } = await quillFetch({
18244
18249
  client,
18245
18250
  task: "query",
18246
18251
  metadata: {
@@ -18254,7 +18259,7 @@ var init_tableProcessing = __esm({
18254
18259
  },
18255
18260
  urlParameters: `caller=getCounts&task=query`,
18256
18261
  getToken
18257
- });
18262
+ }));
18258
18263
  }
18259
18264
  if (data.errorMessage) {
18260
18265
  return { filteredColumns: columns, exceededColumns: void 0 };
@@ -19244,9 +19249,9 @@ var init_dataFetcher = __esm({
19244
19249
  return { error: "Failed to fetch data" };
19245
19250
  }
19246
19251
  };
19247
- parseFetchResponse = async (client, task, response, getToken) => {
19252
+ parseFetchResponse = async (client, task, response, getToken, useInMemory = false) => {
19248
19253
  try {
19249
- if (response.status === "error" || response.data?.error) {
19254
+ if (response.status === "error" || response.data?.error && !useInMemory) {
19250
19255
  let errorPrefix = "Error: ";
19251
19256
  let errorMessage = "Failed to fetch report: " + (response.error || response.data?.error);
19252
19257
  if (task === "query" || task === "report-builder") {
@@ -19831,7 +19836,8 @@ async function cleanDashboardItem({
19831
19836
  additionalProcessing,
19832
19837
  customFields,
19833
19838
  skipPivotFetch,
19834
- tenants
19839
+ tenants,
19840
+ overwriteCache
19835
19841
  }) {
19836
19842
  if (!item) return defaultDashboardItem;
19837
19843
  if (!item.rows) {
@@ -19935,7 +19941,8 @@ async function cleanDashboardItem({
19935
19941
  dateBucket,
19936
19942
  shouldPaginatePivotAsTable ? additionalProcessing : pivotChartProcessing,
19937
19943
  tenants,
19938
- customFields
19944
+ customFields,
19945
+ overwriteCache
19939
19946
  );
19940
19947
  }
19941
19948
  } catch (e) {
@@ -20040,7 +20047,7 @@ async function cleanDashboardItem({
20040
20047
  referenceLines: item.referenceLines
20041
20048
  };
20042
20049
  }
20043
- async function getPivotTable(report, dashboardFilters, dashboardName, getToken, client, eventTracking, dateBucketInitial, additionalProcessing, tenants, customFields) {
20050
+ async function getPivotTable(report, dashboardFilters, dashboardName, getToken, client, eventTracking, dateBucketInitial, additionalProcessing, tenants, customFields, overwriteCache) {
20044
20051
  if (!report) return void 0;
20045
20052
  const dateFilter = Object.values(dashboardFilters ?? {}).find(
20046
20053
  (filter) => filter.filterType === "date_range" || filter.operator === "BETWEEN"
@@ -20108,7 +20115,8 @@ async function getPivotTable(report, dashboardFilters, dashboardName, getToken,
20108
20115
  dashboardFilters,
20109
20116
  tenants,
20110
20117
  additionalProcessing,
20111
- getToken
20118
+ getToken,
20119
+ overwriteCache
20112
20120
  });
20113
20121
  return pivotTable;
20114
20122
  } catch (e) {
@@ -20356,7 +20364,8 @@ async function fetchReportRows({
20356
20364
  filters = [],
20357
20365
  getToken,
20358
20366
  abortSignal,
20359
- additionalProcessing
20367
+ additionalProcessing,
20368
+ overwriteCache = false
20360
20369
  }) {
20361
20370
  const fetchResp = await quillFetch({
20362
20371
  client,
@@ -20368,7 +20377,8 @@ async function fetchReportRows({
20368
20377
  filters: filters.map((filter) => ({ ...filter, options: void 0 })),
20369
20378
  useNewNodeSql: true,
20370
20379
  tenants,
20371
- additionalProcessing
20380
+ additionalProcessing,
20381
+ overwriteCache
20372
20382
  },
20373
20383
  abortSignal,
20374
20384
  getToken
@@ -20399,7 +20409,8 @@ async function fetchReport({
20399
20409
  abortSignal,
20400
20410
  getToken,
20401
20411
  eventTracking,
20402
- usePivotTask = false
20412
+ usePivotTask = false,
20413
+ overwriteCache = false
20403
20414
  }) {
20404
20415
  let reportInfo = void 0;
20405
20416
  let errorMessage = void 0;
@@ -20419,7 +20430,8 @@ async function fetchReport({
20419
20430
  rowsOnly,
20420
20431
  rowCountOnly,
20421
20432
  tenants,
20422
- flags
20433
+ flags,
20434
+ overwriteCache
20423
20435
  },
20424
20436
  abortSignal,
20425
20437
  getToken
@@ -20440,7 +20452,9 @@ async function fetchReport({
20440
20452
  getToken,
20441
20453
  eventTracking,
20442
20454
  tenants,
20443
- skipPivotFetch: usePivotTask
20455
+ // When not using pivot-template, avoid fallback pivot-template fetches.
20456
+ skipPivotFetch: !usePivotTask,
20457
+ overwriteCache
20444
20458
  });
20445
20459
  } catch (error) {
20446
20460
  if (error instanceof Error && error.name === "AbortError") {
@@ -20490,7 +20504,8 @@ async function processReportResponse({
20490
20504
  getToken,
20491
20505
  eventTracking,
20492
20506
  tenants,
20493
- skipPivotFetch = false
20507
+ skipPivotFetch = false,
20508
+ overwriteCache
20494
20509
  }) {
20495
20510
  const shouldSkipPivotFetch = skipPivotFetch || !!resp?.pivotRows && !!resp?.fields;
20496
20511
  const dashboardItem = {
@@ -20523,7 +20538,8 @@ async function processReportResponse({
20523
20538
  getToken,
20524
20539
  tenants,
20525
20540
  eventTracking,
20526
- skipPivotFetch: shouldSkipPivotFetch
20541
+ skipPivotFetch: shouldSkipPivotFetch,
20542
+ overwriteCache
20527
20543
  });
20528
20544
  if (additionalProcessing) {
20529
20545
  reportInfo.pagination = additionalProcessing.page;
@@ -21600,6 +21616,1817 @@ async function getClientTenantIds({
21600
21616
 
21601
21617
  // src/Context.tsx
21602
21618
  init_columnProcessing();
21619
+
21620
+ // src/utils/cacheCab.ts
21621
+ init_dataFetcher();
21622
+
21623
+ // src/utils/inMemoryFilterEngine.ts
21624
+ init_Filter();
21625
+ init_dateRangePickerUtils();
21626
+ import {
21627
+ addHours,
21628
+ addMonths,
21629
+ addWeeks,
21630
+ addYears,
21631
+ addDays as addDays2,
21632
+ isValid as isValid4,
21633
+ startOfDay as startOfDay3,
21634
+ startOfHour,
21635
+ startOfMonth as startOfMonth2,
21636
+ startOfQuarter as startOfQuarter2,
21637
+ startOfWeek as startOfWeek3,
21638
+ startOfYear,
21639
+ subDays as subDays2,
21640
+ subHours as subHours2,
21641
+ subMonths as subMonths3,
21642
+ subWeeks as subWeeks2,
21643
+ subYears as subYears2
21644
+ } from "date-fns";
21645
+ var UNSET = Symbol("unset");
21646
+ var isFilter = (f) => {
21647
+ return f && typeof f === "object" && "filterType" in f && !("id" in f);
21648
+ };
21649
+ var isDashboardFilterUpdate = (f) => {
21650
+ return f && typeof f === "object" && "label" in f;
21651
+ };
21652
+ var normalizeFilterString = (value) => value.replaceAll("%", "").toLowerCase();
21653
+ var normalizeRowString = (value) => value.toLowerCase();
21654
+ var resolveFieldKeys = (field, table, resolver) => {
21655
+ if (!field) return [];
21656
+ const resolved = resolver?.(field, table);
21657
+ if (Array.isArray(resolved)) return resolved;
21658
+ if (typeof resolved === "string") return [resolved];
21659
+ return [field];
21660
+ };
21661
+ var getRowValue = (row, fieldKeys, cache) => {
21662
+ if (fieldKeys.length === 0) return void 0;
21663
+ const cacheKey = fieldKeys.join("|");
21664
+ if (Object.prototype.hasOwnProperty.call(cache, cacheKey)) {
21665
+ return cache[cacheKey];
21666
+ }
21667
+ for (const key of fieldKeys) {
21668
+ if (Object.prototype.hasOwnProperty.call(row, key)) {
21669
+ cache[cacheKey] = row[key];
21670
+ return row[key];
21671
+ }
21672
+ }
21673
+ cache[cacheKey] = void 0;
21674
+ return void 0;
21675
+ };
21676
+ var coerceNumber = (value) => {
21677
+ if (typeof value === "number" && Number.isFinite(value)) return value;
21678
+ if (typeof value === "string" && value !== "") {
21679
+ const parsed = Number(value);
21680
+ return Number.isFinite(parsed) ? parsed : null;
21681
+ }
21682
+ return null;
21683
+ };
21684
+ var coerceBoolean = (value) => {
21685
+ if (typeof value === "boolean") return value;
21686
+ if (typeof value === "number") return value !== 0;
21687
+ if (typeof value === "string") {
21688
+ const normalized = value.toLowerCase();
21689
+ if (normalized === "true" || normalized === "1") return true;
21690
+ if (normalized === "false" || normalized === "0") return false;
21691
+ }
21692
+ return null;
21693
+ };
21694
+ var toDateMs = (value) => {
21695
+ if (value instanceof Date) {
21696
+ return isValid4(value) ? value.getTime() : null;
21697
+ }
21698
+ if (typeof value === "number") {
21699
+ const date = new Date(value);
21700
+ return isValid4(date) ? date.getTime() : null;
21701
+ }
21702
+ if (typeof value === "string") {
21703
+ const date = new Date(value);
21704
+ return isValid4(date) ? date.getTime() : null;
21705
+ }
21706
+ return null;
21707
+ };
21708
+ var buildColumnTypeMap = (columns) => {
21709
+ if (!columns?.length) return null;
21710
+ const map = /* @__PURE__ */ Object.create(null);
21711
+ for (const column of columns) {
21712
+ map[column.field] = column.jsType;
21713
+ }
21714
+ return map;
21715
+ };
21716
+ var resolveDashboardField = (filter, options) => {
21717
+ const mapped = options.filterMap?.[filter.label];
21718
+ if (mapped?.field) {
21719
+ return { field: mapped.field, table: mapped.table };
21720
+ }
21721
+ if (filter.filterType === "date_range" /* Date */ && options.dateField?.field) {
21722
+ return { field: options.dateField.field, table: options.dateField.table };
21723
+ }
21724
+ if (filter.field) {
21725
+ return { field: filter.field, table: filter.table };
21726
+ }
21727
+ return null;
21728
+ };
21729
+ var buildRelativeDateRange = (operator, value, now2, weekStartsOn) => {
21730
+ const amount = value.value || 1;
21731
+ const unit = value.unit;
21732
+ const currentStart = (() => {
21733
+ switch (unit) {
21734
+ case TimeUnit.Hour:
21735
+ return startOfHour(now2);
21736
+ case TimeUnit.Day:
21737
+ return startOfDay3(now2);
21738
+ case TimeUnit.Week:
21739
+ return startOfWeek3(now2, { weekStartsOn });
21740
+ case TimeUnit.Month:
21741
+ return startOfMonth2(now2);
21742
+ case TimeUnit.Quarter:
21743
+ return startOfQuarter2(now2);
21744
+ case TimeUnit.Year:
21745
+ return startOfYear(now2);
21746
+ default:
21747
+ return startOfDay3(now2);
21748
+ }
21749
+ })();
21750
+ const addUnit = (base, multiplier) => {
21751
+ switch (unit) {
21752
+ case TimeUnit.Hour:
21753
+ return addHours(base, multiplier);
21754
+ case TimeUnit.Day:
21755
+ return addDays2(base, multiplier);
21756
+ case TimeUnit.Week:
21757
+ return addWeeks(base, multiplier);
21758
+ case TimeUnit.Month:
21759
+ return addMonths(base, multiplier);
21760
+ case TimeUnit.Quarter:
21761
+ return addMonths(base, multiplier * 3);
21762
+ case TimeUnit.Year:
21763
+ return addYears(base, multiplier);
21764
+ default:
21765
+ return addDays2(base, multiplier);
21766
+ }
21767
+ };
21768
+ const subUnit = (base, multiplier) => {
21769
+ switch (unit) {
21770
+ case TimeUnit.Hour:
21771
+ return subHours2(base, multiplier);
21772
+ case TimeUnit.Day:
21773
+ return subDays2(base, multiplier);
21774
+ case TimeUnit.Week:
21775
+ return subWeeks2(base, multiplier);
21776
+ case TimeUnit.Month:
21777
+ return subMonths3(base, multiplier);
21778
+ case TimeUnit.Quarter:
21779
+ return subMonths3(base, multiplier * 3);
21780
+ case TimeUnit.Year:
21781
+ return subYears2(base, multiplier);
21782
+ default:
21783
+ return subDays2(base, multiplier);
21784
+ }
21785
+ };
21786
+ switch (operator) {
21787
+ case DateOperator.InTheLast: {
21788
+ const start2 = subUnit(now2, amount);
21789
+ return { start: start2.getTime() };
21790
+ }
21791
+ case DateOperator.InThePrevious: {
21792
+ const start2 = subUnit(currentStart, amount);
21793
+ return { start: start2.getTime(), end: currentStart.getTime(), endExclusive: true };
21794
+ }
21795
+ case DateOperator.InTheCurrent: {
21796
+ const end = addUnit(currentStart, 1);
21797
+ return {
21798
+ start: currentStart.getTime(),
21799
+ end: end.getTime(),
21800
+ endExclusive: true
21801
+ };
21802
+ }
21803
+ default:
21804
+ return null;
21805
+ }
21806
+ };
21807
+ var buildStringPredicate = (operator, value, fieldKeys) => {
21808
+ if (value === void 0 || value === null) return null;
21809
+ const normalizedValue = Array.isArray(value) ? value.map((v) => normalizeFilterString(String(v))) : normalizeFilterString(String(value));
21810
+ if (Array.isArray(normalizedValue)) {
21811
+ if (normalizedValue.length === 0) return null;
21812
+ } else if (normalizedValue === "") {
21813
+ return null;
21814
+ }
21815
+ if (operator === StringOperator.Is || operator === StringOperator.IsNot) {
21816
+ const values = Array.isArray(normalizedValue) ? normalizedValue : [normalizedValue];
21817
+ const valueSet = new Set(values);
21818
+ return (row, cache) => {
21819
+ const rowValue = getRowValue(row, fieldKeys, cache);
21820
+ if (rowValue === null || rowValue === void 0) return false;
21821
+ const normalized = normalizeRowString(String(rowValue));
21822
+ const hasValue = valueSet.has(normalized);
21823
+ return operator === StringOperator.Is ? hasValue : !hasValue;
21824
+ };
21825
+ }
21826
+ if (Array.isArray(normalizedValue)) {
21827
+ return null;
21828
+ }
21829
+ return (row, cache) => {
21830
+ const rowValue = getRowValue(row, fieldKeys, cache);
21831
+ if (rowValue === null || rowValue === void 0) return false;
21832
+ const normalizedRow = normalizeRowString(String(rowValue));
21833
+ switch (operator) {
21834
+ case StringOperator.IsExactly:
21835
+ return normalizedRow === normalizedValue;
21836
+ case StringOperator.IsNotExactly:
21837
+ return normalizedRow !== normalizedValue;
21838
+ case StringOperator.Contains:
21839
+ return normalizedRow.includes(normalizedValue);
21840
+ default:
21841
+ return false;
21842
+ }
21843
+ };
21844
+ };
21845
+ var buildNumericPredicate = (operator, value, fieldKeys) => {
21846
+ const filterValue = coerceNumber(value);
21847
+ if (filterValue === null) return null;
21848
+ return (row, cache) => {
21849
+ const rowValue = getRowValue(row, fieldKeys, cache);
21850
+ const parsed = coerceNumber(rowValue);
21851
+ if (parsed === null) return false;
21852
+ switch (operator) {
21853
+ case NumberOperator.EqualTo:
21854
+ return parsed === filterValue;
21855
+ case NumberOperator.NotEqualTo:
21856
+ return parsed !== filterValue;
21857
+ case NumberOperator.GreaterThan:
21858
+ return parsed > filterValue;
21859
+ case NumberOperator.LessThan:
21860
+ return parsed < filterValue;
21861
+ case NumberOperator.GreaterThanOrEqualTo:
21862
+ return parsed >= filterValue;
21863
+ case NumberOperator.LessThanOrEqualTo:
21864
+ return parsed <= filterValue;
21865
+ default:
21866
+ return false;
21867
+ }
21868
+ };
21869
+ };
21870
+ var buildNullPredicate = (operator, fieldKeys) => {
21871
+ return (row, cache) => {
21872
+ const rowValue = getRowValue(row, fieldKeys, cache);
21873
+ const isNullish = rowValue === null || rowValue === void 0;
21874
+ return operator === NullOperator.IsNull ? isNullish : !isNullish;
21875
+ };
21876
+ };
21877
+ var buildBooleanPredicate = (operator, value, fieldKeys) => {
21878
+ const filterValue = coerceBoolean(value);
21879
+ if (filterValue === null) return null;
21880
+ return (row, cache) => {
21881
+ const rowValue = getRowValue(row, fieldKeys, cache);
21882
+ const parsed = coerceBoolean(rowValue);
21883
+ if (parsed === null) return false;
21884
+ return operator === BoolOperator.EqualTo ? parsed === filterValue : parsed !== filterValue;
21885
+ };
21886
+ };
21887
+ var buildDateComparisonPredicate = (operator, value, fieldKeys) => {
21888
+ const filterDate = toDateMs(value);
21889
+ if (filterDate === null) return null;
21890
+ return (row, cache) => {
21891
+ const rowValue = getRowValue(row, fieldKeys, cache);
21892
+ const rowDate = toDateMs(rowValue);
21893
+ if (rowDate === null) return false;
21894
+ switch (operator) {
21895
+ case DateOperator.EqualTo:
21896
+ return rowDate === filterDate;
21897
+ case DateOperator.NotEqualTo:
21898
+ return rowDate !== filterDate;
21899
+ case DateOperator.GreaterThan:
21900
+ return rowDate > filterDate;
21901
+ case DateOperator.LessThan:
21902
+ return rowDate < filterDate;
21903
+ case DateOperator.GreaterThanOrEqualTo:
21904
+ return rowDate >= filterDate;
21905
+ case DateOperator.LessThanOrEqualTo:
21906
+ return rowDate <= filterDate;
21907
+ default:
21908
+ return false;
21909
+ }
21910
+ };
21911
+ };
21912
+ var buildDateRangePredicate = (range, fieldKeys) => {
21913
+ if (range.start === void 0 && range.end === void 0) return null;
21914
+ return (row, cache) => {
21915
+ const rowValue = getRowValue(row, fieldKeys, cache);
21916
+ const rowDate = toDateMs(rowValue);
21917
+ if (rowDate === null) return false;
21918
+ if (range.start !== void 0 && rowDate < range.start) return false;
21919
+ if (range.end !== void 0) {
21920
+ if (range.endExclusive) {
21921
+ return rowDate < range.end;
21922
+ }
21923
+ return rowDate <= range.end;
21924
+ }
21925
+ return true;
21926
+ };
21927
+ };
21928
+ var buildCustomDatePredicate = (value, fieldKeys) => {
21929
+ const start2 = value.startDate ? toDateMs(value.startDate) : null;
21930
+ const end = value.endDate ? toDateMs(value.endDate) : null;
21931
+ if (start2 === null && end === null) return null;
21932
+ return buildDateRangePredicate(
21933
+ {
21934
+ start: start2 ?? void 0,
21935
+ end: end ?? void 0,
21936
+ endExclusive: false
21937
+ },
21938
+ fieldKeys
21939
+ );
21940
+ };
21941
+ var compileCustomFilter = (filter, options) => {
21942
+ const fieldKeys = resolveFieldKeys(
21943
+ filter.field,
21944
+ filter.table,
21945
+ options.fieldKeyResolver
21946
+ );
21947
+ if (fieldKeys.length === 0) return null;
21948
+ switch (filter.filterType) {
21949
+ case "string-filter" /* StringFilter */:
21950
+ case "string-in-filter" /* StringInFilter */:
21951
+ return buildStringPredicate(
21952
+ filter.operator,
21953
+ filter.value,
21954
+ fieldKeys
21955
+ );
21956
+ case "numeric-filter" /* NumericFilter */:
21957
+ return buildNumericPredicate(
21958
+ filter.operator,
21959
+ filter.value,
21960
+ fieldKeys
21961
+ );
21962
+ case "null-filter" /* NullFilter */:
21963
+ return buildNullPredicate(
21964
+ filter.operator,
21965
+ fieldKeys
21966
+ );
21967
+ case "boolean-filter" /* BooleanFilter */:
21968
+ return buildBooleanPredicate(
21969
+ filter.operator,
21970
+ filter.value,
21971
+ fieldKeys
21972
+ );
21973
+ case "date-custom-filter" /* DateCustomFilter */:
21974
+ return buildCustomDatePredicate(
21975
+ filter.value,
21976
+ fieldKeys
21977
+ );
21978
+ case "date-comparison-filter" /* DateComparisonFilter */:
21979
+ return buildDateComparisonPredicate(
21980
+ filter.operator,
21981
+ filter.value,
21982
+ fieldKeys
21983
+ );
21984
+ case "date-filter" /* DateFilter */: {
21985
+ const value = filter.value;
21986
+ if (!value?.unit) return null;
21987
+ const now2 = options.now ?? /* @__PURE__ */ new Date();
21988
+ const range = buildRelativeDateRange(
21989
+ filter.operator,
21990
+ value,
21991
+ now2,
21992
+ options.weekStartsOn ?? 0
21993
+ );
21994
+ if (!range) return null;
21995
+ return buildDateRangePredicate(range, fieldKeys);
21996
+ }
21997
+ default:
21998
+ return null;
21999
+ }
22000
+ };
22001
+ var compileDashboardFilter = (filter, options, columnTypeMap) => {
22002
+ const resolved = resolveDashboardField(filter, options);
22003
+ if (!resolved?.field) return null;
22004
+ const fieldKeys = resolveFieldKeys(
22005
+ resolved.field,
22006
+ resolved.table,
22007
+ options.fieldKeyResolver
22008
+ );
22009
+ if (fieldKeys.length === 0) return null;
22010
+ switch (filter.filterType) {
22011
+ case "string" /* String */: {
22012
+ const stringFilter = filter;
22013
+ if (stringFilter.stringFilterType === "multiselect" /* Multiselect */) {
22014
+ if (!stringFilter.values || stringFilter.values.length === 0) {
22015
+ return null;
22016
+ }
22017
+ return buildStringPredicate(
22018
+ StringOperator.Is,
22019
+ stringFilter.values,
22020
+ fieldKeys
22021
+ );
22022
+ }
22023
+ if (!stringFilter.selectedValue) return null;
22024
+ return buildStringPredicate(
22025
+ StringOperator.IsExactly,
22026
+ stringFilter.selectedValue,
22027
+ fieldKeys
22028
+ );
22029
+ }
22030
+ case "date_range" /* Date */: {
22031
+ const dateFilter = filter;
22032
+ const start2 = dateFilter.startDate ? dateFilter.startDate.getTime() : void 0;
22033
+ const end = dateFilter.endDate ? dateFilter.endDate.getTime() : void 0;
22034
+ return buildDateRangePredicate(
22035
+ { start: start2, end, endExclusive: false },
22036
+ fieldKeys
22037
+ );
22038
+ }
22039
+ case "tenant" /* Tenant */: {
22040
+ const tenantFilter = filter;
22041
+ if (!tenantFilter.values || tenantFilter.values.length === 0) {
22042
+ return null;
22043
+ }
22044
+ const normalizedValues = tenantFilter.values.map(
22045
+ (v) => normalizeFilterString(String(v))
22046
+ );
22047
+ const valueSet = new Set(normalizedValues);
22048
+ const fieldType = columnTypeMap?.[resolved.field] ?? "string";
22049
+ return (row, cache) => {
22050
+ const rowValue = getRowValue(row, fieldKeys, cache);
22051
+ if (rowValue === null || rowValue === void 0) return false;
22052
+ if (fieldType === "number") {
22053
+ const numeric = coerceNumber(rowValue);
22054
+ return numeric !== null && valueSet.has(normalizeFilterString(String(numeric)));
22055
+ }
22056
+ return valueSet.has(normalizeRowString(String(rowValue)));
22057
+ };
22058
+ }
22059
+ default:
22060
+ return null;
22061
+ }
22062
+ };
22063
+ var updateDashboardFilters = (filtersToUpdate, dashboardFilters) => {
22064
+ return dashboardFilters.map((filter) => {
22065
+ const update = filtersToUpdate.find((u) => u.label === filter.label);
22066
+ if (!update) return filter;
22067
+ if (filter.filterType === "string" /* String */) {
22068
+ if (filter.stringFilterType === "multiselect" /* Multiselect */) {
22069
+ return {
22070
+ ...filter,
22071
+ values: update.value
22072
+ };
22073
+ }
22074
+ return {
22075
+ ...filter,
22076
+ selectedValue: update.value
22077
+ };
22078
+ }
22079
+ if (filter.filterType === "date_range" /* Date */) {
22080
+ const presetOptions = convertPresetOptionsToSelectableList(
22081
+ filter.presetOptions ?? [],
22082
+ filter.defaultPresetRanges ?? []
22083
+ );
22084
+ const value = update.value;
22085
+ const preset = presetOptions.find((p) => {
22086
+ const pStart = p.startDate ? new Date(p.startDate).toISOString() : void 0;
22087
+ const vStart = value.startDate ? new Date(value.startDate).toISOString() : void 0;
22088
+ const pEnd = p.endDate ? new Date(p.endDate).toISOString() : void 0;
22089
+ const vEnd = value.endDate ? new Date(value.endDate).toISOString() : void 0;
22090
+ return pStart === vStart && pEnd === vEnd;
22091
+ });
22092
+ if (!preset) {
22093
+ return { ...filter, startDate: value.startDate, endDate: value.endDate };
22094
+ }
22095
+ return {
22096
+ ...filter,
22097
+ preset,
22098
+ startDate: value.startDate,
22099
+ endDate: value.endDate
22100
+ };
22101
+ }
22102
+ if (filter.filterType === "tenant" /* Tenant */) {
22103
+ const value = update.value;
22104
+ let values;
22105
+ if (Array.isArray(value)) {
22106
+ values = value;
22107
+ } else if (typeof value === "string") {
22108
+ values = [value];
22109
+ } else {
22110
+ values = [];
22111
+ }
22112
+ return {
22113
+ ...filter,
22114
+ values
22115
+ };
22116
+ }
22117
+ return filter;
22118
+ });
22119
+ };
22120
+ var compileApplyFiltersPredicate = (filters, options) => {
22121
+ const dashboardUpdates = filters.filter(isDashboardFilterUpdate);
22122
+ const customFilters = filters.filter(isFilter);
22123
+ const internalCustomFilters = customFilters.map((filter) => {
22124
+ try {
22125
+ return convertCustomFilter(filter);
22126
+ } catch (error) {
22127
+ return null;
22128
+ }
22129
+ }).concat(options.customFilters ?? []).filter((filter) => filter !== null && filter !== void 0);
22130
+ const dashboardFilters = options.dashboardFilters ?? [];
22131
+ const updatedDashboardFilters = dashboardUpdates.length ? updateDashboardFilters(dashboardUpdates, dashboardFilters) : dashboardFilters;
22132
+ const columnTypeMap = buildColumnTypeMap(options.columns);
22133
+ const predicates = [];
22134
+ for (const filter of internalCustomFilters) {
22135
+ const predicate = compileCustomFilter(filter, options);
22136
+ if (predicate) predicates.push(predicate);
22137
+ }
22138
+ for (const filter of updatedDashboardFilters) {
22139
+ const predicate = compileDashboardFilter(
22140
+ filter,
22141
+ options,
22142
+ columnTypeMap
22143
+ );
22144
+ if (predicate) predicates.push(predicate);
22145
+ }
22146
+ if (predicates.length === 0) {
22147
+ return { predicate: () => true, hasPredicates: false };
22148
+ }
22149
+ return {
22150
+ predicate: (row) => {
22151
+ const cache = /* @__PURE__ */ Object.create(null);
22152
+ for (const predicate of predicates) {
22153
+ if (!predicate(row, cache)) return false;
22154
+ }
22155
+ return true;
22156
+ },
22157
+ hasPredicates: true
22158
+ };
22159
+ };
22160
+ var applyFiltersInMemory = (rows, filters, options) => {
22161
+ if (!rows.length) return rows;
22162
+ const { predicate, hasPredicates } = compileApplyFiltersPredicate(
22163
+ filters,
22164
+ options
22165
+ );
22166
+ if (!hasPredicates) return rows;
22167
+ const results = [];
22168
+ for (let i = 0; i < rows.length; i += 1) {
22169
+ const row = rows[i];
22170
+ if (predicate(row)) {
22171
+ results.push(row);
22172
+ }
22173
+ }
22174
+ return results;
22175
+ };
22176
+
22177
+ // src/utils/inMemoryPivotEngine.ts
22178
+ init_columnType();
22179
+ var MS_IN_DAY = 24 * 60 * 60 * 1e3;
22180
+ var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
22181
+ var INVALID_DATE_KEY = "-2026";
22182
+ function singleAggToMultiAgg(pivot) {
22183
+ if (pivot.aggregations) {
22184
+ return pivot;
22185
+ }
22186
+ const newPivot = { ...pivot };
22187
+ const newAgg = { aggregationType: pivot.aggregationType };
22188
+ const fieldsToCopy = ["valueField", "valueFieldType", "valueField2", "valueField2Type"];
22189
+ fieldsToCopy.forEach((x) => {
22190
+ if (pivot[x]) {
22191
+ newAgg[x] = pivot[x];
22192
+ delete newPivot[x];
22193
+ }
22194
+ });
22195
+ newPivot.aggregations = [newAgg];
22196
+ return newPivot;
22197
+ }
22198
+ function isDateFormat2(xAxisFormat) {
22199
+ const DATE_FORMATS = [
22200
+ "yyyy",
22201
+ "mmm_dd",
22202
+ "mmm_yyyy",
22203
+ "mmm_dd_yyyy",
22204
+ "hh_ap_pm",
22205
+ "mmm_dd-mmm_dd",
22206
+ "mmm_dd_hh:mm_ap_pm",
22207
+ "wo, yyyy"
22208
+ ];
22209
+ const isDate = DATE_FORMATS.includes(xAxisFormat.toLowerCase());
22210
+ return isDate;
22211
+ }
22212
+ function isValidDate3(date) {
22213
+ try {
22214
+ return date instanceof Date && !isNaN(date.getTime());
22215
+ } catch {
22216
+ return false;
22217
+ }
22218
+ }
22219
+ function inferDateBucketFromFilters(pivot, filters) {
22220
+ if (!pivot?.rowFieldType) {
22221
+ return void 0;
22222
+ }
22223
+ const normalizedType = String(pivot.rowFieldType).toLowerCase();
22224
+ if (isDateFormat2(normalizedType)) {
22225
+ switch (normalizedType) {
22226
+ case "mmm_dd-mmm_dd":
22227
+ return "week";
22228
+ case "yyyy":
22229
+ case "wo, yyyy":
22230
+ return "year";
22231
+ case "mmm_dd_hh:mm_ap_pm":
22232
+ case "hh_ap_pm":
22233
+ return "day";
22234
+ default:
22235
+ return "month";
22236
+ }
22237
+ }
22238
+ const isDateLike = isDateType(normalizedType);
22239
+ if (!isDateLike) {
22240
+ return void 0;
22241
+ }
22242
+ let earliest;
22243
+ let latest;
22244
+ const extractTimestamp = (value) => {
22245
+ if (!value) {
22246
+ return void 0;
22247
+ }
22248
+ if (value instanceof Date) {
22249
+ return value.getTime();
22250
+ }
22251
+ const parsed = Date.parse(value);
22252
+ return Number.isNaN(parsed) ? void 0 : parsed;
22253
+ };
22254
+ (Array.isArray(filters) ? filters : []).forEach((filter) => {
22255
+ if (!filter || filter.filterType !== "date_range") {
22256
+ return;
22257
+ }
22258
+ if (filter.primaryRange?.value === "ALL_TIME") {
22259
+ earliest = -1;
22260
+ return;
22261
+ }
22262
+ const possibleStarts = [
22263
+ filter.startDate,
22264
+ filter.start,
22265
+ filter.primaryRange?.startDate,
22266
+ filter.primaryRange?.start,
22267
+ filter.range?.startDate,
22268
+ filter.range?.start
22269
+ ];
22270
+ const possibleEnds = [
22271
+ filter.endDate,
22272
+ filter.end,
22273
+ filter.primaryRange?.endDate,
22274
+ filter.primaryRange?.end,
22275
+ filter.range?.endDate,
22276
+ filter.range?.end
22277
+ ];
22278
+ possibleStarts.forEach((value) => {
22279
+ const timestamp = extractTimestamp(value);
22280
+ if (timestamp === void 0) {
22281
+ return;
22282
+ }
22283
+ earliest = earliest === void 0 ? timestamp : Math.min(earliest, timestamp);
22284
+ });
22285
+ possibleEnds.forEach((value) => {
22286
+ const timestamp = extractTimestamp(value);
22287
+ if (timestamp === void 0) {
22288
+ return;
22289
+ }
22290
+ latest = latest === void 0 ? timestamp : Math.max(latest, timestamp);
22291
+ });
22292
+ });
22293
+ if (earliest === -1) {
22294
+ return "month";
22295
+ }
22296
+ if (earliest === void 0 || latest === void 0) {
22297
+ return void 0;
22298
+ }
22299
+ if (latest < earliest) {
22300
+ [earliest, latest] = [latest, earliest];
22301
+ }
22302
+ const diffInDays = Math.max(1, Math.round((latest - earliest) / MS_IN_DAY));
22303
+ if (diffInDays < 20) {
22304
+ return "day";
22305
+ }
22306
+ if (diffInDays <= 59) {
22307
+ return "week";
22308
+ }
22309
+ if (diffInDays <= 400) {
22310
+ return "month";
22311
+ }
22312
+ return "year";
22313
+ }
22314
+ function getDateRangeFromFilters(filters) {
22315
+ let earliest;
22316
+ let latest;
22317
+ const extractTimestamp = (value) => {
22318
+ if (!value) return void 0;
22319
+ if (value instanceof Date) return value.getTime();
22320
+ const parsed = Date.parse(value);
22321
+ return Number.isNaN(parsed) ? void 0 : parsed;
22322
+ };
22323
+ (Array.isArray(filters) ? filters : []).forEach((filter) => {
22324
+ if (!filter || filter.filterType !== "date_range") return;
22325
+ if (filter.primaryRange?.value === "ALL_TIME") return;
22326
+ const possibleStarts = [
22327
+ filter.startDate,
22328
+ filter.start,
22329
+ filter.primaryRange?.startDate,
22330
+ filter.primaryRange?.start,
22331
+ filter.range?.startDate,
22332
+ filter.range?.start
22333
+ ];
22334
+ const possibleEnds = [
22335
+ filter.endDate,
22336
+ filter.end,
22337
+ filter.primaryRange?.endDate,
22338
+ filter.primaryRange?.end,
22339
+ filter.range?.endDate,
22340
+ filter.range?.end
22341
+ ];
22342
+ possibleStarts.forEach((v) => {
22343
+ const ts = extractTimestamp(v);
22344
+ if (ts === void 0) return;
22345
+ earliest = earliest === void 0 ? ts : Math.min(earliest, ts);
22346
+ });
22347
+ possibleEnds.forEach((v) => {
22348
+ const ts = extractTimestamp(v);
22349
+ if (ts === void 0) return;
22350
+ latest = latest === void 0 ? ts : Math.max(latest, ts);
22351
+ });
22352
+ });
22353
+ if (earliest === void 0 || latest === void 0) return void 0;
22354
+ if (latest < earliest) [earliest, latest] = [latest, earliest];
22355
+ return {
22356
+ start: new Date(earliest),
22357
+ end: new Date(latest)
22358
+ };
22359
+ }
22360
+ function generateBucketKeys(start2, end, bucket) {
22361
+ const keys = /* @__PURE__ */ new Set();
22362
+ const startDay = new Date(Date.UTC(start2.getUTCFullYear(), start2.getUTCMonth(), start2.getUTCDate()));
22363
+ const endDay = new Date(Date.UTC(end.getUTCFullYear(), end.getUTCMonth(), end.getUTCDate()));
22364
+ const cur = new Date(startDay);
22365
+ while (cur <= endDay) {
22366
+ keys.add(getDateKey(cur, bucket));
22367
+ cur.setUTCDate(cur.getUTCDate() + 1);
22368
+ }
22369
+ return Array.from(keys).sort((a, b) => parseInt(a) - parseInt(b));
22370
+ }
22371
+ function buildEmptyAggRow(aggregations) {
22372
+ const multiAgg = aggregations.length > 1;
22373
+ const empty = {};
22374
+ aggregations.forEach((agg) => {
22375
+ const { aggregationType: type, valueField } = agg;
22376
+ const key = !valueField ? `${type}` + (multiAgg ? `_${type}` : "") : `${valueField}` + (multiAgg ? `_${type}` : "");
22377
+ empty[key] = 0;
22378
+ });
22379
+ return empty;
22380
+ }
22381
+ function getBucketRange(dateKey, bucket) {
22382
+ const d = new Date(parseInt(dateKey, 10));
22383
+ let start2 = new Date(d);
22384
+ let end = new Date(d);
22385
+ switch (bucket) {
22386
+ case "day":
22387
+ start2 = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()));
22388
+ end = new Date(start2);
22389
+ break;
22390
+ case "week":
22391
+ start2 = new Date(d);
22392
+ end = new Date(start2);
22393
+ end.setUTCDate(start2.getUTCDate() + 6);
22394
+ break;
22395
+ case "month":
22396
+ start2 = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 1));
22397
+ end = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth() + 1, 0));
22398
+ break;
22399
+ case "year":
22400
+ start2 = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
22401
+ end = new Date(Date.UTC(d.getUTCFullYear(), 11, 31));
22402
+ break;
22403
+ }
22404
+ return { start: start2, end };
22405
+ }
22406
+ function formatBucketNameFromRange(start2, end, bucket) {
22407
+ const pad = (n) => String(n).padStart(2, "0");
22408
+ switch (bucket) {
22409
+ case "day":
22410
+ return `${pad(start2.getUTCDate())} ${MONTHS[start2.getUTCMonth()]} ${start2.getUTCFullYear()}`;
22411
+ case "week": {
22412
+ const sameMonth = start2.getUTCMonth() === end.getUTCMonth();
22413
+ return sameMonth ? `${MONTHS[start2.getUTCMonth()]} ${start2.getUTCDate()} - ${end.getUTCDate()}` : `${MONTHS[start2.getUTCMonth()]} ${start2.getUTCDate()} - ${MONTHS[end.getUTCMonth()]} ${end.getUTCDate()}`;
22414
+ }
22415
+ case "month":
22416
+ return `${MONTHS[start2.getUTCMonth()]} ${start2.getUTCFullYear()}`;
22417
+ case "year":
22418
+ return `${start2.getUTCFullYear()}`;
22419
+ default:
22420
+ return "";
22421
+ }
22422
+ }
22423
+ function getDateKey(date, bucket) {
22424
+ if (!isValidDate3(date)) {
22425
+ return INVALID_DATE_KEY;
22426
+ }
22427
+ switch (bucket) {
22428
+ case "day":
22429
+ return Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()).toString();
22430
+ case "week": {
22431
+ const day = date.getUTCDay();
22432
+ const mondayUtc = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate() - (day + 6) % 7);
22433
+ return mondayUtc.toString();
22434
+ }
22435
+ case "month":
22436
+ return Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1).toString();
22437
+ case "year":
22438
+ return Date.UTC(date.getUTCFullYear(), 0, 1).toString();
22439
+ default:
22440
+ return INVALID_DATE_KEY;
22441
+ }
22442
+ }
22443
+ function sortRows(rows, pivot) {
22444
+ try {
22445
+ const { sort, sortField, sortFieldType, sortDirection } = pivot;
22446
+ if (!sort || sortField == null || sortFieldType == null) return rows;
22447
+ const sortFunc = isBoolType(sortFieldType) ? (a, b) => a === b ? 0 : a ? 1 : -1 : isDateType(sortFieldType) ? (a, b) => {
22448
+ const ta = new Date(a), tb = new Date(b);
22449
+ const aValid = isValidDate3(ta), bValid = isValidDate3(tb);
22450
+ if (!aValid && !bValid) return 0;
22451
+ if (!aValid) return 1;
22452
+ if (!bValid) return -1;
22453
+ return ta.getTime() - tb.getTime();
22454
+ } : isStringType(sortFieldType) ? (a, b) => a.localeCompare(b) : (a, b) => a - b;
22455
+ const dir = sortDirection === "ASC" ? 1 : -1;
22456
+ rows.sort((a, b) => {
22457
+ const av = a[sortField], bv = b[sortField];
22458
+ if (av == null && bv == null) return 0;
22459
+ if (av == null) return 1;
22460
+ if (bv == null) return -1;
22461
+ return dir * sortFunc(av, bv);
22462
+ });
22463
+ } catch {
22464
+ console.warn(`Sorting failed.`);
22465
+ }
22466
+ return rows;
22467
+ }
22468
+ function assembleFinalRows(seen, rowKey, dateBucket, dateRange) {
22469
+ const allColumns = /* @__PURE__ */ new Set();
22470
+ Object.keys(seen).forEach((rowKeyVal) => {
22471
+ Object.keys(seen[rowKeyVal]).forEach((col) => {
22472
+ allColumns.add(col);
22473
+ });
22474
+ });
22475
+ const filled = {};
22476
+ Object.keys(seen).forEach((rowKeyVal) => {
22477
+ filled[rowKeyVal] = {};
22478
+ allColumns.forEach((col) => {
22479
+ const value = seen[rowKeyVal][col];
22480
+ filled[rowKeyVal][col] = value ?? 0;
22481
+ });
22482
+ });
22483
+ if (dateBucket) {
22484
+ return Object.keys(filled).sort((a, b) => parseInt(a) - parseInt(b)).map((k) => {
22485
+ if (k === INVALID_DATE_KEY) {
22486
+ return { [rowKey]: "Invalid Date", ...filled[k], __quillRawDate: 0 };
22487
+ }
22488
+ const { start: start2, end } = getBucketRange(k, dateBucket);
22489
+ let bucketStart = start2;
22490
+ let bucketEnd = end;
22491
+ if (dateRange) {
22492
+ if (bucketStart < dateRange.start) bucketStart = dateRange.start;
22493
+ if (bucketEnd > dateRange.end) bucketEnd = dateRange.end;
22494
+ }
22495
+ return {
22496
+ [rowKey]: formatBucketNameFromRange(bucketStart, bucketEnd, dateBucket),
22497
+ ...filled[k],
22498
+ __quillRawDate: bucketStart.toISOString()
22499
+ };
22500
+ });
22501
+ }
22502
+ return Object.keys(filled).sort().map((k) => ({ [rowKey]: k, ...filled[k] }));
22503
+ }
22504
+ function anyToNum(valueFieldType, obj) {
22505
+ if (!valueFieldType) {
22506
+ if (typeof obj === "number") return obj;
22507
+ if (typeof obj === "string" && !isNaN(Number(obj))) return Number(obj);
22508
+ return obj ? 1 : 0;
22509
+ }
22510
+ if (isNumberType(valueFieldType)) {
22511
+ return Number(obj);
22512
+ }
22513
+ return obj ? 1 : 0;
22514
+ }
22515
+ function updateAccumulator(acc, row, agg, isOneRow) {
22516
+ const {
22517
+ aggregationType: type,
22518
+ valueField,
22519
+ valueFieldType,
22520
+ valueField2,
22521
+ valueField2Type
22522
+ } = agg;
22523
+ if (!valueField) return;
22524
+ const distinctDenom = !!valueField2 && !!valueField2Type && valueField !== valueField2;
22525
+ const rawVal = row[valueField];
22526
+ if (valueFieldType && (type === "min" || type === "max") && (isDateType(valueFieldType) || isDateFormat2(valueFieldType))) {
22527
+ const dateVal = rawVal instanceof Date ? rawVal : new Date(rawVal);
22528
+ if (!isNaN(dateVal.getTime())) {
22529
+ if (type === "min") {
22530
+ acc.minDate = acc.minDate && acc.minDate.getTime() < dateVal.getTime() ? acc.minDate : dateVal;
22531
+ } else {
22532
+ acc.maxDate = acc.maxDate && acc.maxDate.getTime() > dateVal.getTime() ? acc.maxDate : dateVal;
22533
+ }
22534
+ }
22535
+ return;
22536
+ }
22537
+ const numVal = anyToNum(valueFieldType, rawVal);
22538
+ switch (type) {
22539
+ case "sum":
22540
+ acc.sum += numVal;
22541
+ break;
22542
+ case "average":
22543
+ case "avg":
22544
+ acc.sum += numVal;
22545
+ acc.count++;
22546
+ break;
22547
+ case "min":
22548
+ acc.min = acc.hasVal ? Math.min(acc.min, numVal) : numVal;
22549
+ acc.hasVal = true;
22550
+ break;
22551
+ case "max":
22552
+ acc.max = acc.hasVal ? Math.max(acc.max, numVal) : numVal;
22553
+ acc.hasVal = true;
22554
+ break;
22555
+ case "count":
22556
+ acc.count += numVal;
22557
+ break;
22558
+ case "percentage":
22559
+ acc.sum += numVal;
22560
+ acc.count += distinctDenom ? anyToNum(valueField2Type, row[valueField2]) : isOneRow ? 1 : numVal;
22561
+ break;
22562
+ }
22563
+ }
22564
+ function getFinalAggregationValue({
22565
+ type,
22566
+ valueFieldType,
22567
+ valueField,
22568
+ acc,
22569
+ distinctDenom = false,
22570
+ sumOfDenom,
22571
+ rowsLength,
22572
+ totalRowsForCount
22573
+ }) {
22574
+ if (valueFieldType && (type === "min" || type === "max") && (isDateType(valueFieldType) || isDateFormat2(valueFieldType))) {
22575
+ const dateVal = type === "min" ? acc.minDate : acc.maxDate;
22576
+ return dateVal?.toISOString() ?? "";
22577
+ }
22578
+ if (type === "count") {
22579
+ return totalRowsForCount ?? acc.count;
22580
+ }
22581
+ let final;
22582
+ if (!valueField && type === "percentage" && rowsLength !== void 0) {
22583
+ final = rowsLength ? (totalRowsForCount ?? 0) / rowsLength : 0;
22584
+ } else {
22585
+ switch (type) {
22586
+ case "sum":
22587
+ final = acc.sum;
22588
+ break;
22589
+ case "average":
22590
+ case "avg":
22591
+ final = acc.count ? acc.sum / acc.count : 0;
22592
+ break;
22593
+ case "min":
22594
+ final = acc.hasVal ? acc.min : 0;
22595
+ break;
22596
+ case "max":
22597
+ final = acc.hasVal ? acc.max : 0;
22598
+ break;
22599
+ case "percentage": {
22600
+ const denom = distinctDenom ? acc.count : sumOfDenom ?? acc.count;
22601
+ final = denom ? acc.sum / denom : 0;
22602
+ break;
22603
+ }
22604
+ default:
22605
+ final = 0;
22606
+ }
22607
+ }
22608
+ return final;
22609
+ }
22610
+ function aggregateToOneRow(rows, aggregations) {
22611
+ const multiAgg = aggregations.length > 1;
22612
+ const resultArray = aggregations.map((agg) => {
22613
+ const { aggregationType: type, valueField, valueFieldType } = agg;
22614
+ if (!valueField) {
22615
+ if (type !== "count") {
22616
+ return {};
22617
+ }
22618
+ const finalKey2 = "count" + (multiAgg ? "_count" : "");
22619
+ return { [finalKey2]: rows.length };
22620
+ }
22621
+ const finalKey = `${valueField}` + (multiAgg ? `_${type}` : "");
22622
+ const acc = {
22623
+ sum: 0,
22624
+ count: 0,
22625
+ min: Infinity,
22626
+ max: -Infinity,
22627
+ hasVal: false,
22628
+ minDate: null,
22629
+ maxDate: null
22630
+ };
22631
+ for (const row of rows) {
22632
+ updateAccumulator(acc, row, agg, true);
22633
+ }
22634
+ return {
22635
+ [finalKey]: getFinalAggregationValue({
22636
+ type,
22637
+ valueFieldType,
22638
+ valueField,
22639
+ acc,
22640
+ totalRowsForCount: acc.count
22641
+ })
22642
+ };
22643
+ });
22644
+ return [
22645
+ resultArray.reduce((acc, current) => {
22646
+ return { ...acc, ...current };
22647
+ }, {})
22648
+ ];
22649
+ }
22650
+ function aggregateWithColumn(rows, colKey, aggregations, getGroupKey) {
22651
+ if (!colKey) return;
22652
+ const seen = {};
22653
+ const multiAgg = aggregations.length > 1;
22654
+ aggregations.forEach((agg) => {
22655
+ const { aggregationType: type, valueField, valueFieldType, valueField2, valueField2Type } = agg;
22656
+ if (!valueField) {
22657
+ if (type !== "count" && type !== "percentage") return;
22658
+ }
22659
+ const distinctDenom = !!valueField2 && !!valueField2Type && valueField !== valueField2;
22660
+ const accs = {};
22661
+ rows.forEach((row) => {
22662
+ const groupKey = getGroupKey(row);
22663
+ const colVal = row[colKey];
22664
+ if (!accs[groupKey]) accs[groupKey] = {};
22665
+ if (!accs[groupKey][colVal]) {
22666
+ accs[groupKey][colVal] = {
22667
+ sum: 0,
22668
+ count: 0,
22669
+ min: Infinity,
22670
+ max: -Infinity,
22671
+ hasVal: false,
22672
+ minDate: null,
22673
+ maxDate: null,
22674
+ totalRows: 0
22675
+ };
22676
+ }
22677
+ const acc = accs[groupKey][colVal];
22678
+ acc.totalRows += 1;
22679
+ if (!valueField) {
22680
+ if (type === "count") acc.count += 1;
22681
+ else if (type === "percentage") acc.sum += 1;
22682
+ return;
22683
+ }
22684
+ updateAccumulator(acc, row, agg, false);
22685
+ });
22686
+ Object.keys(accs).forEach((groupKey) => {
22687
+ if (!seen[groupKey]) seen[groupKey] = {};
22688
+ const sumOfDenom = Object.values(accs[groupKey]).reduce((t, a) => t + a.count, 0);
22689
+ Object.keys(accs[groupKey]).forEach((colVal) => {
22690
+ const acc = accs[groupKey][colVal];
22691
+ const finalKey = `${colVal}` + (multiAgg ? `_${type}` : "");
22692
+ seen[groupKey][finalKey] = getFinalAggregationValue({
22693
+ type,
22694
+ valueFieldType,
22695
+ valueField,
22696
+ acc,
22697
+ distinctDenom,
22698
+ sumOfDenom,
22699
+ totalRowsForCount: acc.totalRows
22700
+ });
22701
+ });
22702
+ });
22703
+ });
22704
+ return seen;
22705
+ }
22706
+ function aggregateWithoutColumn(rows, aggregations, getGroupKey) {
22707
+ const seen = {};
22708
+ const multiAgg = aggregations.length > 1;
22709
+ aggregations.forEach((agg) => {
22710
+ const { aggregationType: type, valueField, valueFieldType, valueField2, valueField2Type } = agg;
22711
+ if (!valueField) {
22712
+ if (type !== "count" && type !== "percentage") return;
22713
+ }
22714
+ const finalKey = !valueField ? `${type}` + (multiAgg ? `_${type}` : "") : `${valueField}` + (multiAgg ? `_${type}` : "");
22715
+ const distinctDenom = !!valueField2 && !!valueField2Type && valueField !== valueField2;
22716
+ const accs = {};
22717
+ rows.forEach((row) => {
22718
+ const key = getGroupKey(row);
22719
+ if (!seen[key]) seen[key] = {};
22720
+ if (!accs[key]) {
22721
+ accs[key] = {
22722
+ sum: 0,
22723
+ count: 0,
22724
+ min: Infinity,
22725
+ max: -Infinity,
22726
+ hasVal: false,
22727
+ minDate: null,
22728
+ maxDate: null,
22729
+ totalRows: 0
22730
+ };
22731
+ }
22732
+ const acc = accs[key];
22733
+ acc.totalRows += 1;
22734
+ if (!valueField) {
22735
+ if (type === "count") acc.count += 1;
22736
+ else if (type === "percentage") acc.sum += 1;
22737
+ return;
22738
+ }
22739
+ updateAccumulator(acc, row, agg, false);
22740
+ });
22741
+ const sumOfDenom = Object.values(accs).reduce((t, a) => t + a.count, 0);
22742
+ Object.keys(accs).forEach((k) => {
22743
+ const acc = accs[k];
22744
+ seen[k][finalKey] = getFinalAggregationValue({
22745
+ type,
22746
+ valueFieldType,
22747
+ valueField,
22748
+ acc,
22749
+ distinctDenom,
22750
+ sumOfDenom,
22751
+ rowsLength: rows.length,
22752
+ totalRowsForCount: acc.totalRows
22753
+ });
22754
+ });
22755
+ });
22756
+ return seen;
22757
+ }
22758
+ function applyPivotInMemory(rows, pivot, filters) {
22759
+ if (!pivot) return rows;
22760
+ const multiAggPivot = pivot.aggregations ? pivot : singleAggToMultiAgg(pivot);
22761
+ if (multiAggPivot.rowField === void 0) {
22762
+ return aggregateToOneRow(rows, multiAggPivot.aggregations);
22763
+ }
22764
+ const { rowField, columnField, aggregations } = multiAggPivot;
22765
+ const dateBucket = inferDateBucketFromFilters(pivot, filters);
22766
+ const dateRange = dateBucket ? getDateRangeFromFilters(filters) : void 0;
22767
+ const getRowKey = dateBucket ? (row) => {
22768
+ const rawValue = row[rowField];
22769
+ const rawDateValue = rawValue !== null && typeof rawValue === "object" && rawValue.value ? rawValue.value : rawValue;
22770
+ return getDateKey(new Date(rawDateValue), dateBucket);
22771
+ } : (row) => row[rowField];
22772
+ const aggregated = columnField ? aggregateWithColumn(rows, columnField, aggregations, getRowKey) : aggregateWithoutColumn(rows, aggregations, getRowKey);
22773
+ const aggregatedRows = aggregated || {};
22774
+ if (rows.length === 0 && dateBucket && dateRange && !columnField) {
22775
+ const bucketKeys = generateBucketKeys(dateRange.start, dateRange.end, dateBucket);
22776
+ const emptyAgg = buildEmptyAggRow(aggregations);
22777
+ bucketKeys.forEach((k) => {
22778
+ aggregatedRows[k] = { ...emptyAgg };
22779
+ });
22780
+ }
22781
+ const finalRows = assembleFinalRows(
22782
+ aggregatedRows,
22783
+ rowField,
22784
+ dateBucket,
22785
+ rows.length === 0 ? dateRange : void 0
22786
+ );
22787
+ const sorted = sortRows(finalRows, pivot);
22788
+ if (pivot.rowLimit) {
22789
+ return sorted.slice(0, pivot.rowLimit);
22790
+ }
22791
+ return sorted;
22792
+ }
22793
+
22794
+ // src/utils/cacheCab.ts
22795
+ import { openDB } from "idb";
22796
+ import { endOfDay as endOfDay3, max as maxDate, subMonths as subMonths4, min as minDate, startOfDay as startOfDay4 } from "date-fns";
22797
+ import { utcToZonedTime as utcToZonedTime2, zonedTimeToUtc } from "date-fns-tz";
22798
+ var TZ = "America/Los_Angeles";
22799
+ var CacheCab = class {
22800
+ fetchedRange = {};
22801
+ cachedReportIds = [];
22802
+ uncacheableReportIDs = [];
22803
+ uncacheableInFlight = /* @__PURE__ */ new Map();
22804
+ storage;
22805
+ META_KEY = "cachecab:meta";
22806
+ DATA_PREFIX = "cachecab:data:";
22807
+ UNCACHEABLE_PREFIX = "cachecab:uncacheable:";
22808
+ constructor({
22809
+ storageType = "memory"
22810
+ } = {}) {
22811
+ this.storage = storageType !== "idb" ? new MemoryStorage() : new IdbStorage();
22812
+ void this.loadMetaFromStorage();
22813
+ }
22814
+ async get(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, pageSize, getToken, eventTracking, forceRefresh) {
22815
+ if (this.isCached(reportId, tenants) && !forceRefresh) {
22816
+ return this.getFromCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking);
22817
+ } else {
22818
+ return this.addToCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking, true, forceRefresh);
22819
+ }
22820
+ }
22821
+ isCached(reportId, tenants) {
22822
+ return this.cachedReportIds.includes(this.getCacheKey(reportId, tenants));
22823
+ }
22824
+ isCacheable(reportId) {
22825
+ return !this.uncacheableReportIDs.includes(reportId);
22826
+ }
22827
+ async getUncacheableResult(reportId, client, tenants, flags, filters, additionalProcessing, pivot) {
22828
+ const key = this.getUncacheableRequestKey(
22829
+ reportId,
22830
+ client,
22831
+ tenants,
22832
+ flags,
22833
+ filters,
22834
+ additionalProcessing,
22835
+ pivot
22836
+ );
22837
+ try {
22838
+ const raw = await this.storage.getItem(key);
22839
+ return raw ? JSON.parse(raw) : null;
22840
+ } catch {
22841
+ return null;
22842
+ }
22843
+ }
22844
+ async setUncacheableResult(reportId, client, tenants, flags, filters, additionalProcessing, pivot, report) {
22845
+ const key = this.getUncacheableRequestKey(
22846
+ reportId,
22847
+ client,
22848
+ tenants,
22849
+ flags,
22850
+ filters,
22851
+ additionalProcessing,
22852
+ pivot
22853
+ );
22854
+ try {
22855
+ await this.storage.setItem(key, JSON.stringify(report));
22856
+ } catch {
22857
+ }
22858
+ }
22859
+ async getOrFetchUncacheableResult(reportId, client, tenants, flags, filters, additionalProcessing, pivot, fetcher, forceRefresh = false) {
22860
+ const key = this.getUncacheableRequestKey(
22861
+ reportId,
22862
+ client,
22863
+ tenants,
22864
+ flags,
22865
+ filters,
22866
+ additionalProcessing,
22867
+ pivot
22868
+ );
22869
+ if (!forceRefresh) {
22870
+ const raw = await this.storage.getItem(key);
22871
+ if (raw) {
22872
+ return { report: JSON.parse(raw), fromCache: true };
22873
+ }
22874
+ const inFlight = this.uncacheableInFlight.get(key);
22875
+ if (inFlight) {
22876
+ const report = await inFlight;
22877
+ return { report, fromCache: false };
22878
+ }
22879
+ }
22880
+ const requestPromise = (async () => {
22881
+ const report = await fetcher();
22882
+ try {
22883
+ await this.storage.setItem(key, JSON.stringify(report));
22884
+ } catch {
22885
+ }
22886
+ return report;
22887
+ })();
22888
+ this.uncacheableInFlight.set(key, requestPromise);
22889
+ try {
22890
+ const report = await requestPromise;
22891
+ return { report, fromCache: false };
22892
+ } finally {
22893
+ this.uncacheableInFlight.delete(key);
22894
+ }
22895
+ }
22896
+ async addToCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking, firstTime = true, forceRefresh = false) {
22897
+ const tenantPart = tenants ? JSON.stringify(tenants) : "";
22898
+ const cacheKey = this.getCacheKey(reportId, tenants);
22899
+ if (firstTime) {
22900
+ const result = await this.fetchInitialReport({ reportId, client, tenants, flags, filters: dashboardFilters, getToken, eventTracking, forceRefresh });
22901
+ try {
22902
+ await this.storage.setItem(this.DATA_PREFIX + cacheKey, JSON.stringify(result));
22903
+ this.cachedReportIds.push(cacheKey);
22904
+ await this.persistMetaToStorage();
22905
+ } catch {
22906
+ console.warn(`Failed to cache report: ${reportId}. Cache full?`);
22907
+ }
22908
+ const newRows = await this.applyPivotsAndFilters(result, dashboardFilters, customFilters, pivot, tenantPart, false);
22909
+ if (pivot) {
22910
+ return { ...result, pivotRows: newRows, pivotRowCount: newRows.length };
22911
+ }
22912
+ return { ...result, rows: newRows, rowCount: newRows.length };
22913
+ } else {
22914
+ const dbDateFilter = dashboardFilters.find((x) => x.filterType === "date_range");
22915
+ const { start: requestedStart, end: requestedEnd } = normalizePSTRanges(
22916
+ dbDateFilter?.startDate ? new Date(dbDateFilter.startDate) : void 0,
22917
+ dbDateFilter?.endDate ? new Date(dbDateFilter.endDate) : void 0
22918
+ );
22919
+ if (!requestedStart || !requestedEnd) {
22920
+ return this.addToCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking, true);
22921
+ }
22922
+ const currentRange = this.fetchedRange[cacheKey];
22923
+ const rangeStart = currentRange?.start ?? requestedStart;
22924
+ const rangeEnd = currentRange?.end ?? endOfDayPST(/* @__PURE__ */ new Date());
22925
+ const existingRaw = await this.storage.getItem(this.DATA_PREFIX + cacheKey);
22926
+ let existing;
22927
+ let fetchedRows = [];
22928
+ try {
22929
+ existing = JSON.parse(existingRaw);
22930
+ if (!existing.rows) {
22931
+ throw new Error("Invalid Cache!");
22932
+ }
22933
+ if (requestedStart < rangeStart) {
22934
+ const olderReport = await this.fetchReport({
22935
+ reportId,
22936
+ client,
22937
+ tenants,
22938
+ flags,
22939
+ filters: dashboardFilters,
22940
+ getToken,
22941
+ eventTracking,
22942
+ start: requestedStart,
22943
+ end: rangeStart
22944
+ });
22945
+ fetchedRows = olderReport.rows;
22946
+ }
22947
+ if (requestedEnd && requestedEnd > rangeEnd) {
22948
+ const newerReport = await this.fetchReport({
22949
+ reportId,
22950
+ client,
22951
+ tenants,
22952
+ flags,
22953
+ filters: dashboardFilters,
22954
+ getToken,
22955
+ eventTracking,
22956
+ start: rangeEnd,
22957
+ end: requestedEnd
22958
+ });
22959
+ fetchedRows = fetchedRows.concat(newerReport.rows);
22960
+ }
22961
+ } catch {
22962
+ existing = await this.fetchReport({
22963
+ reportId,
22964
+ client,
22965
+ tenants,
22966
+ flags,
22967
+ filters: dashboardFilters,
22968
+ getToken,
22969
+ eventTracking,
22970
+ start: requestedStart,
22971
+ end: requestedEnd
22972
+ });
22973
+ fetchedRows = existing.rows;
22974
+ }
22975
+ const dateKey = existing.dateField?.field ? removeQuotes(existing.dateField.field) : void 0;
22976
+ let mergedRows;
22977
+ if (dateKey) {
22978
+ const startMs = rangeStart.getTime();
22979
+ const endMs = rangeEnd.getTime();
22980
+ const boundaryRows = existing.rows.filter((row) => {
22981
+ const raw = row[dateKey];
22982
+ if (!raw) return false;
22983
+ const t = new Date(raw).getTime();
22984
+ return Math.abs(t - startMs) <= MS_IN_DAY || Math.abs(t - endMs) <= MS_IN_DAY;
22985
+ });
22986
+ const filteredNew = filterNewRows(boundaryRows, fetchedRows);
22987
+ mergedRows = filteredNew.concat(existing.rows);
22988
+ } else {
22989
+ mergedRows = filterNewRows(existing.rows, fetchedRows).concat(existing.rows);
22990
+ }
22991
+ const merged = { ...existing, rows: mergedRows };
22992
+ await this.storage.setItem(this.DATA_PREFIX + cacheKey, JSON.stringify(merged));
22993
+ await this.persistMetaToStorage();
22994
+ const newRows = await this.applyPivotsAndFilters(merged, dashboardFilters, customFilters, pivot, tenantPart, false);
22995
+ if (pivot) {
22996
+ return { ...merged, pivotRows: newRows, pivotRowCount: newRows.length };
22997
+ }
22998
+ return { ...merged, rows: newRows, rowCount: newRows.length };
22999
+ }
23000
+ }
23001
+ /**
23002
+ * Returns cache entry (or re-queries if stale)
23003
+ */
23004
+ async getFromCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking) {
23005
+ const dateRangeFilter = dashboardFilters.find((x) => x.filterType === "date_range");
23006
+ const cacheKey = this.getCacheKey(reportId, tenants);
23007
+ if (!dateRangeFilter || dateRangeFilter?.primaryRange.value === "ALL_TIME") {
23008
+ const fetchInfo = this.fetchedRange[cacheKey];
23009
+ if (!fetchInfo || fetchInfo?.allTimeDoneBefore === false || Date.now() - fetchInfo.fetchedAtUTCMS >= MS_IN_DAY) {
23010
+ return this.addToCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking, true);
23011
+ }
23012
+ } else if (this.weShouldReQuery(cacheKey, dateRangeFilter)) {
23013
+ return this.addToCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking, false);
23014
+ }
23015
+ try {
23016
+ const raw = await this.storage.getItem(this.DATA_PREFIX + cacheKey);
23017
+ if (!raw) {
23018
+ throw new Error("Invalid Cache!");
23019
+ }
23020
+ const parsed = JSON.parse(raw);
23021
+ const tenantPart = tenants ? JSON.stringify(tenants) : "";
23022
+ const newRows = await this.applyPivotsAndFilters(parsed, dashboardFilters, customFilters, pivot, tenantPart);
23023
+ if (pivot) {
23024
+ return { ...parsed, pivotRows: newRows, pivotRowCount: newRows.length };
23025
+ }
23026
+ return { ...parsed, rows: newRows, rowCount: newRows.length };
23027
+ } catch (err) {
23028
+ console.error(err);
23029
+ return this.addToCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking, false);
23030
+ }
23031
+ }
23032
+ weShouldReQuery(cacheKey, dateRangeFilter) {
23033
+ const start2 = dateRangeFilter.startDate;
23034
+ const end = dateRangeFilter.endDate;
23035
+ const range = this.fetchedRange[cacheKey];
23036
+ const { start: s, end: e } = normalizePSTRanges(
23037
+ start2 ? new Date(start2) : void 0,
23038
+ end ? new Date(end) : void 0
23039
+ );
23040
+ return !range || !s || !e || range.start > s || range.end < e;
23041
+ }
23042
+ async fetchInitialReport({
23043
+ reportId,
23044
+ client,
23045
+ tenants,
23046
+ flags,
23047
+ filters = [],
23048
+ getToken,
23049
+ eventTracking,
23050
+ forceRefresh
23051
+ }) {
23052
+ let start2;
23053
+ let end;
23054
+ const dateRangeFilter = filters.find((x) => x.filterType === "date_range");
23055
+ const cacheRange = dateRangeFilter?.initialCacheDateRange;
23056
+ if (cacheRange && cacheRange.startDate) {
23057
+ const startDate = dateRangeFilter?.startDate;
23058
+ start2 = startDate ? minDate([new Date(cacheRange.startDate), new Date(startDate)]) : new Date(cacheRange.startDate);
23059
+ end = endOfDayPST(/* @__PURE__ */ new Date());
23060
+ } else {
23061
+ const endDate = dateRangeFilter?.endDate ? new Date(dateRangeFilter.endDate) : void 0;
23062
+ const currentDay = endOfDayPST(/* @__PURE__ */ new Date());
23063
+ end = endDate ? maxDate([currentDay, endDate]) : currentDay;
23064
+ start2 = subMonths4(end, 7);
23065
+ }
23066
+ const normalized = normalizePSTRanges(start2, end);
23067
+ return this.fetchReport({
23068
+ reportId,
23069
+ client,
23070
+ tenants,
23071
+ flags,
23072
+ filters,
23073
+ getToken,
23074
+ eventTracking,
23075
+ start: normalized.start,
23076
+ end: normalized.end,
23077
+ forceRefresh
23078
+ });
23079
+ }
23080
+ async fetchReport({
23081
+ reportId,
23082
+ client,
23083
+ tenants,
23084
+ flags,
23085
+ filters = [],
23086
+ getToken,
23087
+ eventTracking,
23088
+ start: start2,
23089
+ end,
23090
+ forceRefresh
23091
+ }) {
23092
+ let reportInfo = void 0;
23093
+ try {
23094
+ const { start: s, end: e } = normalizePSTRanges(start2, end);
23095
+ const adjusted = [...filters].map((x) => {
23096
+ return { ...x, options: void 0 };
23097
+ });
23098
+ const dateFilterIndex = adjusted.findIndex((x) => x.filterType === "date_range");
23099
+ const isAllTime = dateFilterIndex !== -1 && adjusted[dateFilterIndex].primaryRange?.value === "ALL_TIME";
23100
+ if (!isAllTime) {
23101
+ const dateFilter = adjusted[dateFilterIndex];
23102
+ if (dateFilter && dateFilter.startDate && dateFilter.endDate) {
23103
+ adjusted[dateFilterIndex] = { ...dateFilter, startDate: s, endDate: e };
23104
+ }
23105
+ }
23106
+ const fetchResp = await quillFetch({
23107
+ client,
23108
+ task: "report",
23109
+ metadata: {
23110
+ reportId,
23111
+ clientId: client.publicKey,
23112
+ databaseType: client.databaseType,
23113
+ filters: adjusted,
23114
+ additionalProcessing: { page: { rowsPerPage: 1e3, rowsPerRequest: 1e5 } },
23115
+ useNewNodeSql: true,
23116
+ tenants,
23117
+ flags,
23118
+ overwriteCache: forceRefresh ?? false
23119
+ },
23120
+ getToken
23121
+ });
23122
+ const resp = await parseFetchResponse(
23123
+ client,
23124
+ "report",
23125
+ fetchResp,
23126
+ getToken,
23127
+ true
23128
+ );
23129
+ reportInfo = await processReportResponse({
23130
+ resp,
23131
+ client,
23132
+ filters: adjusted,
23133
+ dateBucket: resp?.dateBucket,
23134
+ additionalProcessing: { page: { rowsPerPage: 1e3, rowsPerRequest: 1e5 } },
23135
+ getToken,
23136
+ eventTracking,
23137
+ tenants,
23138
+ // CacheCab fetches with task: 'report'; keep pivot processing local.
23139
+ skipPivotFetch: true,
23140
+ overwriteCache: forceRefresh ?? false
23141
+ });
23142
+ const dateField = reportInfo.dateField?.field;
23143
+ if (!isAllTime && dateField !== void 0 && reportInfo.rows.length > 0) {
23144
+ const cleanedDateField = removeQuotes(dateField);
23145
+ const missingDateFieldCount = reportInfo.rows.some((row) => row[cleanedDateField] === void 0);
23146
+ if (missingDateFieldCount) {
23147
+ this.uncacheableReportIDs.push(reportId);
23148
+ return EMPTY_INTERNAL_REPORT;
23149
+ }
23150
+ }
23151
+ const requiredFilterFields = adjusted.flatMap((x, idx) => {
23152
+ if (idx === dateFilterIndex || !x.values && !x.selectedValue) return [];
23153
+ return removeQuotes(x.field);
23154
+ });
23155
+ const missingFields = [...new Set(requiredFilterFields.filter(
23156
+ (field) => reportInfo?.rows.some((row) => row[field] === void 0)
23157
+ ))];
23158
+ if (missingFields.length > 0) {
23159
+ this.uncacheableReportIDs.push(reportId);
23160
+ return EMPTY_INTERNAL_REPORT;
23161
+ }
23162
+ const cacheKey = this.getCacheKey(reportId, tenants);
23163
+ if (isAllTime) {
23164
+ this.fetchedRange[cacheKey] = { start: /* @__PURE__ */ new Date(0), end: /* @__PURE__ */ new Date(), fetchedAtUTCMS: Date.now(), allTimeDoneBefore: true };
23165
+ } else {
23166
+ const existing = this.fetchedRange[cacheKey];
23167
+ if (existing && !forceRefresh) {
23168
+ const oldStart = existing.start;
23169
+ const oldEnd = existing.end;
23170
+ this.fetchedRange[cacheKey] = { start: getMinDate(oldStart, s), end: getMaxDate(oldEnd, e), fetchedAtUTCMS: Date.now(), allTimeDoneBefore: false };
23171
+ } else {
23172
+ this.fetchedRange[cacheKey] = { start: s, end: e, fetchedAtUTCMS: Date.now(), allTimeDoneBefore: false };
23173
+ }
23174
+ }
23175
+ } catch (error) {
23176
+ console.warn(error);
23177
+ if (error instanceof Error && error.name === "AbortError") {
23178
+ throw error;
23179
+ }
23180
+ }
23181
+ return reportInfo || EMPTY_INTERNAL_REPORT;
23182
+ }
23183
+ async applyPivotsAndFilters(report, dashboardFilters, customFilters, pivot, tenantPart, useCache = true) {
23184
+ const datasetVersion = this.fetchedRange[report.id + tenantPart]?.fetchedAtUTCMS ?? 0;
23185
+ const keyParts = [
23186
+ report.queryString,
23187
+ tenantPart,
23188
+ datasetVersion,
23189
+ // prevents fetching stale filters on hard refresh
23190
+ hashString(stableStringify(dashboardFilters)),
23191
+ hashString(stableStringify(customFilters))
23192
+ ];
23193
+ const queryKey = this.DATA_PREFIX + keyParts.join("|");
23194
+ let filtersApplied;
23195
+ if (useCache) {
23196
+ try {
23197
+ const cache = await this.storage.getItem(queryKey);
23198
+ if (cache) {
23199
+ filtersApplied = JSON.parse(cache);
23200
+ }
23201
+ } catch {
23202
+ }
23203
+ }
23204
+ if (!filtersApplied) {
23205
+ const dateIndex = dashboardFilters.findIndex((x) => x.filterType === "date_range");
23206
+ const requiredFilterFields = dashboardFilters.flatMap((x, idx) => {
23207
+ if (idx === dateIndex || !x.values?.length && !x.selectedValue) return [];
23208
+ return removeQuotes(x.field);
23209
+ });
23210
+ const missingFields = [...new Set(requiredFilterFields.filter(
23211
+ (field) => report.rows.some((row) => row[field] === void 0)
23212
+ ))];
23213
+ if (missingFields.length > 0) {
23214
+ this.uncacheableReportIDs.push(report.id);
23215
+ return [];
23216
+ }
23217
+ const dbDateFilter = dashboardFilters.find((x) => x.filterType === "date_range");
23218
+ if (report.dateField && dbDateFilter?.startDate) {
23219
+ const { start: startDate, end: endDate } = normalizePSTRanges(
23220
+ new Date(dbDateFilter.startDate),
23221
+ dbDateFilter.endDate ? new Date(dbDateFilter.endDate) : void 0
23222
+ );
23223
+ const fieldToUse = report.rows[0] && report.rows[0]["__quillRawDate"] ? "__quillRawDate" : removeQuotes(report.dateField.field);
23224
+ const dateFilteredRows = report.rows.filter((x) => {
23225
+ const rowDate = new Date(x[fieldToUse]);
23226
+ return startDate <= rowDate && (endDate ? rowDate <= endDate : true);
23227
+ });
23228
+ filtersApplied = applyFiltersInMemory(dateFilteredRows, [], { dashboardFilters, customFilters });
23229
+ } else {
23230
+ filtersApplied = applyFiltersInMemory(report.rows, [], { dashboardFilters, customFilters });
23231
+ }
23232
+ }
23233
+ try {
23234
+ await this.storage.setItem(queryKey, JSON.stringify(filtersApplied));
23235
+ } catch {
23236
+ }
23237
+ const withPivot = applyPivotInMemory(filtersApplied, pivot, dashboardFilters.concat(customFilters));
23238
+ return withPivot;
23239
+ }
23240
+ getCacheKey(reportId, tenants) {
23241
+ const tenantPart = tenants ? JSON.stringify(tenants) : "";
23242
+ return reportId + tenantPart;
23243
+ }
23244
+ getUncacheableRequestKey(reportId, client, tenants, flags, filters, additionalProcessing, pivot) {
23245
+ const canonicalFilters = filters.map(
23246
+ (f) => canonicalizeFilterForUncacheableKey(f)
23247
+ );
23248
+ const keyParts = [
23249
+ reportId,
23250
+ client.publicKey,
23251
+ client.databaseType,
23252
+ hashString(stableStringify(canonicalizeForKey(tenants ?? null))),
23253
+ hashString(stableStringify(canonicalizeForKey(flags ?? null))),
23254
+ hashString(stableStringify(canonicalFilters)),
23255
+ hashString(stableStringify(canonicalizeForKey(additionalProcessing ?? null))),
23256
+ hashString(stableStringify(canonicalizeForKey(pivot ?? null)))
23257
+ ];
23258
+ return this.UNCACHEABLE_PREFIX + keyParts.join("|");
23259
+ }
23260
+ async loadMetaFromStorage() {
23261
+ const meta = await this.storage.getItem(this.META_KEY);
23262
+ if (!meta) return;
23263
+ try {
23264
+ const parsed = JSON.parse(meta);
23265
+ this.cachedReportIds = parsed.cached || [];
23266
+ this.uncacheableReportIDs = parsed.cannotBeCached;
23267
+ this.fetchedRange = Object.fromEntries(
23268
+ Object.entries(parsed.fetchedRange || {}).map(([id2, range]) => [
23269
+ id2,
23270
+ { ...range, start: new Date(range.start), end: new Date(range.end) }
23271
+ ])
23272
+ );
23273
+ } catch (err) {
23274
+ console.warn("CacheCab: could not parse meta from storage", err);
23275
+ }
23276
+ }
23277
+ async persistMetaToStorage() {
23278
+ const serializableRange = Object.fromEntries(
23279
+ Object.entries(this.fetchedRange).map(([id2, range]) => [
23280
+ id2,
23281
+ { ...range }
23282
+ ])
23283
+ );
23284
+ await this.storage.setItem(
23285
+ this.META_KEY,
23286
+ JSON.stringify({
23287
+ fetchedRange: serializableRange,
23288
+ cached: this.cachedReportIds,
23289
+ cannotBeCached: this.uncacheableReportIDs
23290
+ })
23291
+ );
23292
+ }
23293
+ };
23294
+ var MemoryStorage = class {
23295
+ store = /* @__PURE__ */ new Map();
23296
+ async getItem(key) {
23297
+ return this.store.get(key) ?? null;
23298
+ }
23299
+ async setItem(key, value) {
23300
+ this.store.set(key, value);
23301
+ }
23302
+ };
23303
+ var DB_NAME = "quill.cachecab";
23304
+ var STORE_NAME = "kv";
23305
+ var IdbStorage = class {
23306
+ dbPromise;
23307
+ constructor() {
23308
+ this.dbPromise = openDB(DB_NAME, 1, {
23309
+ upgrade(db) {
23310
+ if (!db.objectStoreNames.contains(STORE_NAME)) {
23311
+ db.createObjectStore(STORE_NAME);
23312
+ }
23313
+ }
23314
+ });
23315
+ }
23316
+ async getItem(key) {
23317
+ const db = await this.dbPromise;
23318
+ return await db.get(STORE_NAME, key) ?? null;
23319
+ }
23320
+ async setItem(key, value) {
23321
+ const db = await this.dbPromise;
23322
+ await db.put(STORE_NAME, value, key);
23323
+ }
23324
+ };
23325
+ function removeQuotes(str) {
23326
+ return str.replace(/^['"]|['"]$/g, "");
23327
+ }
23328
+ function getMaxDate(d1, d2) {
23329
+ return d1 > d2 ? d1 : d2;
23330
+ }
23331
+ function getMinDate(d1, d2) {
23332
+ return d2 > d1 ? d1 : d2;
23333
+ }
23334
+ function hashString(str) {
23335
+ let h1 = 3735928559 ^ 0, h2 = 1103547991 ^ 0;
23336
+ for (let i = 0, ch; i < str.length; i++) {
23337
+ ch = str.charCodeAt(i);
23338
+ h1 = Math.imul(h1 ^ ch, 2654435761);
23339
+ h2 = Math.imul(h2 ^ ch, 1597334677);
23340
+ }
23341
+ h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507) ^ Math.imul(h2 ^ h2 >>> 13, 3266489909);
23342
+ h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507) ^ Math.imul(h1 ^ h1 >>> 13, 3266489909);
23343
+ return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(36);
23344
+ }
23345
+ function filterNewRows(existingRows, newRows) {
23346
+ const seen = /* @__PURE__ */ new Set();
23347
+ const getKey = (row) => stableStringify(row);
23348
+ existingRows.forEach((row) => seen.add(getKey(row)));
23349
+ return newRows.filter((row) => {
23350
+ const k = getKey(row);
23351
+ if (seen.has(k)) return false;
23352
+ seen.add(k);
23353
+ return true;
23354
+ });
23355
+ }
23356
+ function stableStringify(obj) {
23357
+ if (obj === null || typeof obj !== "object") return JSON.stringify(obj);
23358
+ if (Array.isArray(obj)) {
23359
+ return `[${obj.map(stableStringify).join(",")}]`;
23360
+ }
23361
+ const keys = Object.keys(obj).sort();
23362
+ return `{${keys.map((k) => JSON.stringify(k) + ":" + stableStringify(obj[k])).join(",")}}`;
23363
+ }
23364
+ function canonicalizeForKey(value) {
23365
+ if (value instanceof Date) {
23366
+ return value.toISOString();
23367
+ }
23368
+ if (Array.isArray(value)) {
23369
+ return value.map(canonicalizeForKey);
23370
+ }
23371
+ if (value && typeof value === "object") {
23372
+ const out = {};
23373
+ Object.keys(value).sort().forEach((key) => {
23374
+ if (value[key] !== void 0) {
23375
+ out[key] = canonicalizeForKey(value[key]);
23376
+ }
23377
+ });
23378
+ return out;
23379
+ }
23380
+ return value;
23381
+ }
23382
+ function canonicalizeFilterForUncacheableKey(filter) {
23383
+ const raw = canonicalizeForKey(filter) ?? {};
23384
+ const base = {
23385
+ filterType: raw.filterType,
23386
+ field: raw.field,
23387
+ table: raw.table,
23388
+ label: raw.label,
23389
+ operator: raw.operator
23390
+ };
23391
+ if (raw.selectedValue !== void 0) {
23392
+ base.selectedValue = raw.selectedValue;
23393
+ }
23394
+ if (Array.isArray(raw.values)) {
23395
+ base.values = [...raw.values].map(canonicalizeForKey).sort();
23396
+ }
23397
+ if (raw.value !== void 0) {
23398
+ base.value = canonicalizeForKey(raw.value);
23399
+ }
23400
+ if (raw.filterType === "date_range") {
23401
+ const { start: start2, end } = normalizePSTRanges(
23402
+ raw.startDate ? new Date(raw.startDate) : void 0,
23403
+ raw.endDate ? new Date(raw.endDate) : void 0
23404
+ );
23405
+ base.startDate = start2 ? start2.toISOString() : void 0;
23406
+ base.endDate = end ? end.toISOString() : void 0;
23407
+ base.primaryRange = raw.primaryRange?.value;
23408
+ base.comparisonRange = raw.comparisonRange?.value;
23409
+ }
23410
+ return canonicalizeForKey(base);
23411
+ }
23412
+ function startOfDayPST(date) {
23413
+ const pstDate = utcToZonedTime2(date, TZ);
23414
+ const startPst = startOfDay4(pstDate);
23415
+ return zonedTimeToUtc(startPst, TZ);
23416
+ }
23417
+ function endOfDayPST(date) {
23418
+ const pstDate = utcToZonedTime2(date, TZ);
23419
+ const endPst = endOfDay3(pstDate);
23420
+ return zonedTimeToUtc(endPst, TZ);
23421
+ }
23422
+ function normalizePSTRanges(start2, end) {
23423
+ return {
23424
+ start: start2 ? startOfDayPST(start2) : void 0,
23425
+ end: end ? endOfDayPST(end) : void 0
23426
+ };
23427
+ }
23428
+
23429
+ // src/Context.tsx
21603
23430
  import { jsx } from "react/jsx-runtime";
21604
23431
  var dummySetter = () => {
21605
23432
  };
@@ -21931,7 +23758,9 @@ var ReportFiltersContext = createContext({
21931
23758
  loadFiltersForReport: async () => {
21932
23759
  },
21933
23760
  abortLoadingFilters: () => {
21934
- }
23761
+ },
23762
+ customFiltersLoaded: false,
23763
+ setCustomFiltersLoaded: dummySetter
21935
23764
  });
21936
23765
  var customDashboardFiltersReducer = (state, action) => {
21937
23766
  switch (action.type) {
@@ -22021,6 +23850,9 @@ var FetchContext = createContext({
22021
23850
  var EventTrackingContext = createContext({
22022
23851
  eventTracking: null
22023
23852
  });
23853
+ var CacheCabContext = createContext({
23854
+ getCacheCab: (storageType) => new CacheCab({ storageType })
23855
+ });
22024
23856
  var ContextProvider = ({
22025
23857
  children,
22026
23858
  initialTheme,
@@ -22034,6 +23866,14 @@ var ContextProvider = ({
22034
23866
  getAuthorizationToken = async () => "",
22035
23867
  eventTracking = null
22036
23868
  }) => {
23869
+ const cacheCabByTypeRef = useRef({});
23870
+ const getCacheCab = useCallback((storageType) => {
23871
+ const key = storageType === "idb" ? "idb" : "memory";
23872
+ if (!cacheCabByTypeRef.current[key]) {
23873
+ cacheCabByTypeRef.current[key] = new CacheCab({ storageType: key });
23874
+ }
23875
+ return cacheCabByTypeRef.current[key];
23876
+ }, []);
22037
23877
  const [client, setClient] = useState(
22038
23878
  typeof window !== "undefined" && sessionStorage ? JSON.parse(sessionStorage.getItem("quill-client") ?? "null") : null
22039
23879
  );
@@ -22096,6 +23936,7 @@ var ContextProvider = ({
22096
23936
  reportFiltersReducer,
22097
23937
  {}
22098
23938
  );
23939
+ const [customFiltersLoaded, setCustomFiltersLoaded] = useState(false);
22099
23940
  const reportFilterOptionsAbortControllers = useRef({});
22100
23941
  const [reports, reportsDispatch] = useReducer(reportsReducer, {});
22101
23942
  const [reportsLoadingState, reportsLoadingStateDispatch] = useReducer(
@@ -22119,6 +23960,11 @@ var ContextProvider = ({
22119
23960
  const viewerTenantsByOwnerRequests = useRef(/* @__PURE__ */ new Set());
22120
23961
  const mappedTenantsRequests = useRef(/* @__PURE__ */ new Set());
22121
23962
  const loadDashboardProcessId = useRef({});
23963
+ useEffect(() => {
23964
+ if (Object.keys(customReportFilters).length) {
23965
+ setCustomFiltersLoaded(true);
23966
+ }
23967
+ }, [customReportFilters]);
22122
23968
  useEffect(() => {
22123
23969
  if (!theme) {
22124
23970
  const populatedTheme = {
@@ -22887,6 +24733,7 @@ var ContextProvider = ({
22887
24733
  ...dashboard2,
22888
24734
  dateFilter: dashboard2.dateFilter ? {
22889
24735
  ...dashboard2.dateFilter,
24736
+ initialCacheDateRange: dashboard2.initialCacheDateRange ? { ...dashboard2.initialCacheDateRange } : void 0,
22890
24737
  presetOptions: dashboard2.dateFilter.presetOptions?.map(
22891
24738
  (preset) => ({
22892
24739
  ...preset,
@@ -23229,6 +25076,7 @@ var ContextProvider = ({
23229
25076
  ...dashboard2,
23230
25077
  dateFilter: dashboard2.dateFilter ? {
23231
25078
  ...dashboard2.dateFilter,
25079
+ initialCacheDateRange: dashboard2.initialCacheDateRange ? { ...dashboard2.initialCacheDateRange } : void 0,
23232
25080
  presetOptions: dashboard2.dateFilter.presetOptions?.map(
23233
25081
  (preset) => ({
23234
25082
  ...preset,
@@ -23273,6 +25121,7 @@ var ContextProvider = ({
23273
25121
  if (dashboard2.dateFilter) {
23274
25122
  dashboard2.dateFilter = {
23275
25123
  ...dashboard2.dateFilter,
25124
+ initialCacheDateRange: dashboard2.initialCacheDateRange ? { ...dashboard2.initialCacheDateRange } : void 0,
23276
25125
  startDate: presetOptions.find(
23277
25126
  (elem) => elem.value === dashboard2.dateFilter.primaryRange?.value
23278
25127
  )?.startDate,
@@ -23541,7 +25390,7 @@ var ContextProvider = ({
23541
25390
  if (!theme) {
23542
25391
  return null;
23543
25392
  }
23544
- return /* @__PURE__ */ jsx(
25393
+ return /* @__PURE__ */ jsx(CacheCabContext.Provider, { value: { getCacheCab }, children: /* @__PURE__ */ jsx(
23545
25394
  ClientContext.Provider,
23546
25395
  {
23547
25396
  value: [
@@ -23582,7 +25431,9 @@ var ContextProvider = ({
23582
25431
  reportFiltersDispatch,
23583
25432
  customReportFiltersDispatch,
23584
25433
  loadFiltersForReport,
23585
- abortLoadingFilters
25434
+ abortLoadingFilters,
25435
+ customFiltersLoaded,
25436
+ setCustomFiltersLoaded
23586
25437
  },
23587
25438
  children: /* @__PURE__ */ jsx(
23588
25439
  ReportsContext.Provider,
@@ -23625,7 +25476,7 @@ var ContextProvider = ({
23625
25476
  }
23626
25477
  ) })
23627
25478
  }
23628
- );
25479
+ ) });
23629
25480
  };
23630
25481
 
23631
25482
  // src/hooks/useExport.tsx
@@ -23644,12 +25495,12 @@ init_filterProcessing();
23644
25495
  init_columnType();
23645
25496
  import {
23646
25497
  add,
23647
- startOfDay as startOfDay3,
23648
- startOfMonth as startOfMonth2,
23649
- startOfWeek as startOfWeek3,
23650
- startOfYear
25498
+ startOfDay as startOfDay5,
25499
+ startOfMonth as startOfMonth3,
25500
+ startOfWeek as startOfWeek4,
25501
+ startOfYear as startOfYear2
23651
25502
  } from "date-fns";
23652
- import { utcToZonedTime as utcToZonedTime2 } from "date-fns-tz";
25503
+ import { utcToZonedTime as utcToZonedTime3 } from "date-fns-tz";
23653
25504
  function mergeComparisonRange(resp) {
23654
25505
  if (resp.chartType === "table") return resp;
23655
25506
  const compRows = resp.compareRows;
@@ -23692,6 +25543,18 @@ var useDashboardInternal = (dashboardName, customFilters) => {
23692
25543
  (f) => f.filter
23693
25544
  ) : null;
23694
25545
  }, [dashboardFilters, dashboardName]);
25546
+ const getExistingDateFilter = useMemo2(
25547
+ () => (name2) => {
25548
+ if (!name2) return void 0;
25549
+ const filtersForDashboard = dashboardFilters[name2];
25550
+ if (!filtersForDashboard) return void 0;
25551
+ const entry = Object.values(filtersForDashboard).find(
25552
+ (f) => f?.filter?.filterType === "date_range" /* Date */
25553
+ );
25554
+ return entry?.filter;
25555
+ },
25556
+ [dashboardFilters]
25557
+ );
23695
25558
  useEffect2(() => {
23696
25559
  if (!dashboardName) return;
23697
25560
  if (backfilledDashboards.current.has(dashboardName)) return;
@@ -23729,7 +25592,7 @@ var useDashboardInternal = (dashboardName, customFilters) => {
23729
25592
  customFilters
23730
25593
  );
23731
25594
  }, [dashboardName, loading, dashboardConfig, dashboardFilters]);
23732
- const handleReload = async (overrideDashboardName, fetchFromServer = false, reportAction, overrideFilters) => {
25595
+ const handleReload = async (overrideDashboardName, fetchFromServer = false, reportAction, overrideFilters, options) => {
23733
25596
  if (!dashboardName) return;
23734
25597
  if (overrideFilters) {
23735
25598
  const dateFilter2 = overrideFilters.filters.find(
@@ -23782,10 +25645,19 @@ var useDashboardInternal = (dashboardName, customFilters) => {
23782
25645
  Object.values(dashboard?.sections ?? {}).flat(),
23783
25646
  dashboardName
23784
25647
  ) : void 0;
25648
+ const existingDateFilter = options?.preserveExistingDateFilter ? getExistingDateFilter(dashboardName) : void 0;
25649
+ const mergedDateFilter = dateFilter && existingDateFilter ? {
25650
+ ...dateFilter,
25651
+ startDate: existingDateFilter.startDate ?? dateFilter.startDate,
25652
+ endDate: existingDateFilter.endDate ?? dateFilter.endDate,
25653
+ preset: existingDateFilter.preset ?? dateFilter.preset,
25654
+ primaryRange: existingDateFilter.primaryRange ?? dateFilter.primaryRange,
25655
+ comparisonRange: existingDateFilter.comparisonRange ?? dateFilter.comparisonRange
25656
+ } : dateFilter ?? existingDateFilter;
23785
25657
  loadFiltersForDashboard(
23786
25658
  dashboardName,
23787
25659
  [
23788
- ...dateFilter ? [dateFilter] : [],
25660
+ ...mergedDateFilter ? [mergedDateFilter] : [],
23789
25661
  ...updatedDashboard?.filters?.map((filter) => ({
23790
25662
  ...filter,
23791
25663
  query: void 0
@@ -24070,7 +25942,8 @@ var useDashboards = () => {
24070
25942
  tenantFilters,
24071
25943
  dateFilter,
24072
25944
  customFilters,
24073
- tenantKeys
25945
+ tenantKeys,
25946
+ initialCacheDateRange
24074
25947
  }) => {
24075
25948
  if (!client) return;
24076
25949
  if (tenantKeys?.some((key) => !key)) {
@@ -24091,6 +25964,7 @@ var useDashboards = () => {
24091
25964
  filters,
24092
25965
  tenantFilters,
24093
25966
  dateFilter: dateFilter ?? null,
25967
+ initialCacheDateRange,
24094
25968
  name: name2.trim(),
24095
25969
  task: "edit-dashboard",
24096
25970
  clientId: client.clientId,
@@ -24302,7 +26176,9 @@ var useDashboards = () => {
24302
26176
  };
24303
26177
  };
24304
26178
  var useDashboard = (dashboardName, config) => {
26179
+ const logCacheStatistics = config?.showCacheLogs || false;
24305
26180
  const { data, dashboardFilters, reload, isLoading } = useDashboardInternal(dashboardName);
26181
+ const [lastUpdated, setLastUpdated] = useState2(0);
24306
26182
  const { customFilterDispatch } = useContext(DashboardFiltersContext);
24307
26183
  const { reportsDispatch, reportsLoadingStateDispatch } = useContext(ReportsContext);
24308
26184
  const initialLoad = useRef2(true);
@@ -24311,15 +26187,19 @@ var useDashboard = (dashboardName, config) => {
24311
26187
  const [client] = useContext(ClientContext);
24312
26188
  const { tenants, flags } = useContext(TenantContext);
24313
26189
  const { getToken } = useContext(FetchContext);
24314
- const { customReportFilters } = useContext(ReportFiltersContext);
26190
+ const { customReportFilters, customFiltersLoaded, setCustomFiltersLoaded } = useContext(ReportFiltersContext);
24315
26191
  const { eventTracking } = useContext(EventTrackingContext);
24316
- const customFiltersRef = useRef2(customReportFilters);
26192
+ const { getCacheCab } = useContext(CacheCabContext);
24317
26193
  const reportRequestIds = useRef2({});
24318
26194
  const lastDashboardName = useRef2(null);
24319
26195
  const pendingNameChangeReload = useRef2(false);
24320
- useEffect2(() => {
24321
- customFiltersRef.current = customReportFilters;
24322
- }, [customReportFilters]);
26196
+ const [loadedDashes, setLoadedDashes] = useState2([]);
26197
+ const [lastUpdatedDict, setLastUpdatedDict] = useState2({});
26198
+ const cacheEnabled = config?.cacheEnabled ?? true;
26199
+ const cacheCab = useMemo2(
26200
+ () => getCacheCab(config?.cacheType),
26201
+ [getCacheCab, config?.cacheType]
26202
+ );
24323
26203
  useEffect2(() => {
24324
26204
  const nameChanged = dashboardName !== lastDashboardName.current;
24325
26205
  if (nameChanged) {
@@ -24329,19 +26209,29 @@ var useDashboard = (dashboardName, config) => {
24329
26209
  backfilledDashboards.current = false;
24330
26210
  reportRequestIds.current = {};
24331
26211
  pendingNameChangeReload.current = true;
26212
+ if (customFiltersLoaded && !loadedDashes.includes(dashboardName)) {
26213
+ setCustomFiltersLoaded(false);
26214
+ }
26215
+ setLastUpdated(lastUpdatedDict[dashboardName] || 0);
24332
26216
  }
24333
26217
  if (!dashboardName) return;
24334
26218
  if (pendingNameChangeReload.current && data) {
24335
26219
  pendingNameChangeReload.current = false;
24336
26220
  initialLoad.current = false;
26221
+ fetchedInitialReports.current = false;
24337
26222
  return;
24338
26223
  }
24339
26224
  if (pendingNameChangeReload.current && !isLoading && !data && initialLoad.current) {
24340
26225
  initialLoad.current = false;
24341
26226
  pendingNameChangeReload.current = false;
24342
- reload();
26227
+ reload(dashboardName, false, void 0, void 0, {
26228
+ preserveExistingDateFilter: true
26229
+ });
24343
26230
  }
24344
26231
  }, [isLoading, data, dashboardName]);
26232
+ useEffect2(() => {
26233
+ setLastUpdated(lastUpdatedDict[dashboardName] || 0);
26234
+ }, [lastUpdatedDict, dashboardName]);
24345
26235
  useEffect2(() => {
24346
26236
  if (backfilledDashboards.current) return;
24347
26237
  if (isLoading || !data) return;
@@ -24350,7 +26240,9 @@ var useDashboard = (dashboardName, config) => {
24350
26240
  );
24351
26241
  if (!needsBackfill) return;
24352
26242
  backfilledDashboards.current = true;
24353
- reload(dashboardName, false);
26243
+ reload(dashboardName, false, void 0, void 0, {
26244
+ preserveExistingDateFilter: true
26245
+ });
24354
26246
  }, [isLoading, data, dashboardFilters, dashboardName]);
24355
26247
  const { allReportsById } = useAllReports();
24356
26248
  const sections = useMemo2(() => {
@@ -24374,7 +26266,13 @@ var useDashboard = (dashboardName, config) => {
24374
26266
  fetchedInitialReports.current = true;
24375
26267
  fetchReports([], dashboardFilters ?? [], config?.pageSize);
24376
26268
  }
24377
- }, [fetchedInitialReports, data, dashboardFilters]);
26269
+ }, [fetchedInitialReports, data, dashboardFilters, customFiltersLoaded]);
26270
+ useEffect2(() => {
26271
+ if (customFiltersLoaded && data && dashboardFilters !== null) {
26272
+ fetchedInitialReports.current = true;
26273
+ fetchReports([], dashboardFilters ?? [], config?.pageSize);
26274
+ }
26275
+ }, [customFiltersLoaded]);
24378
26276
  const applyDashboardFilters = (filtersToUpdate) => {
24379
26277
  const newFilters = (dashboardFilters ?? []).map((f) => {
24380
26278
  const update = filtersToUpdate.find((u) => u.label === f.label);
@@ -24453,7 +26351,7 @@ var useDashboard = (dashboardName, config) => {
24453
26351
  });
24454
26352
  return internalFilters;
24455
26353
  };
24456
- const isFilter = (f) => {
26354
+ const isFilter2 = (f) => {
24457
26355
  return f && typeof f === "object" && "filterType" in f && !("id" in f);
24458
26356
  };
24459
26357
  const isDashboardFilter = (f) => {
@@ -24461,29 +26359,22 @@ var useDashboard = (dashboardName, config) => {
24461
26359
  };
24462
26360
  const applyFilters = (filters2) => {
24463
26361
  const dashboardFilters2 = filters2.filter(isDashboardFilter);
24464
- const customFilters = filters2.filter(isFilter);
26362
+ const customFilters = filters2.filter(isFilter2);
24465
26363
  const newCustomFilters = applyCustomFilters(customFilters);
24466
26364
  const newDashboardFilters = applyDashboardFilters(dashboardFilters2);
24467
26365
  fetchReports(newCustomFilters, newDashboardFilters);
24468
26366
  };
24469
- const waitForCustomFilters = async (reportId) => {
24470
- let attempts = 0;
24471
- const maxAttempts = 10;
24472
- while (attempts < maxAttempts) {
24473
- const filters2 = customFiltersRef.current[reportId];
24474
- if (filters2 && filters2.length > 0) {
24475
- return filters2;
24476
- }
24477
- await new Promise((resolve) => setTimeout(resolve, 100));
24478
- attempts++;
24479
- }
24480
- return [];
26367
+ const forceCacheRefresh = () => {
26368
+ if (!cacheEnabled) return;
26369
+ fetchReports([], dashboardFilters ?? [], config?.pageSize, true);
24481
26370
  };
24482
- const fetchReports = async (customFilters, dashboardFilters2, pageSize) => {
26371
+ const fetchReports = async (customFilters, dashboardFilters2, pageSize, forceCacheToRefresh = false) => {
24483
26372
  if (!client || !sections) return;
24484
26373
  const allReports = Object.values(sections).flat();
26374
+ const fetchStartTime = Date.now();
26375
+ let totalCached = 0;
24485
26376
  await Promise.all(
24486
- allReports.map(async (reportInfo) => {
26377
+ allReports.map(async (reportInfo, idx) => {
24487
26378
  const reportId = reportInfo.id;
24488
26379
  const requestId = (reportRequestIds.current[reportId] ?? 0) + 1;
24489
26380
  reportRequestIds.current[reportId] = requestId;
@@ -24492,7 +26383,7 @@ var useDashboard = (dashboardName, config) => {
24492
26383
  id: reportId,
24493
26384
  data: true
24494
26385
  });
24495
- const customReportFiltersArray = await waitForCustomFilters(reportId);
26386
+ const customReportFiltersArray = customReportFilters[reportId] ?? [];
24496
26387
  let rowsPerRequest = pageSize || 600;
24497
26388
  if (!pageSize && (reportInfo.chartType === "table" || reportInfo.chartType === "metric")) {
24498
26389
  rowsPerRequest = 10;
@@ -24504,17 +26395,138 @@ var useDashboard = (dashboardName, config) => {
24504
26395
  const additionalProcessing = {
24505
26396
  page: pagination
24506
26397
  };
24507
- const usePivotTask = !!reportInfo.pivot;
26398
+ const usePivotTask = !cacheEnabled && !!reportInfo.pivot;
26399
+ const allFilters = dashboardFilters2.concat(customFilters).concat(customReportFiltersArray);
26400
+ if (cacheEnabled && cacheCab.isCacheable(reportId)) {
26401
+ const report2 = await cacheCab.get(reportId, dashboardFilters2, customFilters.concat(customReportFiltersArray), reportInfo.pivot, client, tenants, flags, pageSize, getToken, eventTracking, forceCacheToRefresh);
26402
+ if (reportRequestIds.current[reportId] !== requestId) {
26403
+ return null;
26404
+ }
26405
+ if (cacheCab.isCacheable(reportId)) {
26406
+ reportsDispatch({
26407
+ type: "UPDATE_REPORT",
26408
+ id: reportId,
26409
+ data: {
26410
+ ...report2,
26411
+ pagination,
26412
+ triggerReload: false
26413
+ }
26414
+ });
26415
+ reportsLoadingStateDispatch({
26416
+ type: "SET_REPORT_LOADING",
26417
+ id: reportId,
26418
+ data: false
26419
+ });
26420
+ totalCached += 1;
26421
+ return report2;
26422
+ }
26423
+ }
26424
+ if (cacheEnabled && !cacheCab.isCacheable(reportId)) {
26425
+ try {
26426
+ const { report: uncacheableReport, fromCache } = await cacheCab.getOrFetchUncacheableResult(
26427
+ reportId,
26428
+ client,
26429
+ tenants,
26430
+ flags,
26431
+ allFilters,
26432
+ additionalProcessing,
26433
+ reportInfo.pivot,
26434
+ async () => {
26435
+ const { report: report2, error: error2 } = await fetchReport({
26436
+ reportId,
26437
+ client,
26438
+ tenants,
26439
+ flags,
26440
+ additionalProcessing,
26441
+ filters: allFilters,
26442
+ getToken,
26443
+ eventTracking,
26444
+ usePivotTask,
26445
+ overwriteCache: forceCacheToRefresh
26446
+ });
26447
+ if (error2 || !report2) {
26448
+ throw error2 ?? new Error("Failed to fetch uncacheable report");
26449
+ }
26450
+ return report2;
26451
+ },
26452
+ forceCacheToRefresh
26453
+ );
26454
+ if (reportRequestIds.current[reportId] !== requestId) {
26455
+ return null;
26456
+ }
26457
+ reportsDispatch({
26458
+ type: "UPDATE_REPORT",
26459
+ id: reportId,
26460
+ data: {
26461
+ ...uncacheableReport,
26462
+ pagination,
26463
+ triggerReload: false
26464
+ }
26465
+ });
26466
+ reportsLoadingStateDispatch({
26467
+ type: "SET_REPORT_LOADING",
26468
+ id: reportId,
26469
+ data: false
26470
+ });
26471
+ if (fromCache) {
26472
+ totalCached += 1;
26473
+ }
26474
+ if (usePivotTask) {
26475
+ fetchReportRows({
26476
+ reportId,
26477
+ client,
26478
+ tenants,
26479
+ filters: allFilters,
26480
+ getToken,
26481
+ additionalProcessing,
26482
+ overwriteCache: forceCacheToRefresh
26483
+ }).then(({ rows, rowCount, columns, fields }) => {
26484
+ if (reportRequestIds.current[reportId] !== requestId) {
26485
+ return;
26486
+ }
26487
+ reportsDispatch({
26488
+ type: "UPDATE_REPORT",
26489
+ id: reportId,
26490
+ data: {
26491
+ rows,
26492
+ rowCount,
26493
+ columnInternal: columns,
26494
+ columns: columns.map((col) => ({
26495
+ field: col.field,
26496
+ format: col.format,
26497
+ label: col.label,
26498
+ inferFormat: col.inferFormat
26499
+ })),
26500
+ // @ts-ignore fields is not typed on QuillReportInternal
26501
+ fields,
26502
+ triggerReload: false
26503
+ }
26504
+ });
26505
+ }).catch((e) => {
26506
+ if (e instanceof Error && e.name === "AbortError") {
26507
+ return;
26508
+ }
26509
+ console.error("Failed to fetch background rows", e);
26510
+ });
26511
+ }
26512
+ return uncacheableReport;
26513
+ } catch (error2) {
26514
+ console.error(error2);
26515
+ return null;
26516
+ }
26517
+ }
24508
26518
  const { report, error } = await fetchReport({
24509
26519
  reportId,
24510
26520
  client,
24511
26521
  tenants,
24512
26522
  flags,
24513
26523
  additionalProcessing,
24514
- filters: dashboardFilters2.concat(customFilters).concat(customReportFiltersArray),
26524
+ filters: allFilters,
24515
26525
  getToken,
24516
26526
  eventTracking,
24517
- usePivotTask
26527
+ usePivotTask,
26528
+ overwriteCache: forceCacheToRefresh
26529
+ // usePivotTask: false,
24518
26530
  });
24519
26531
  if (error) {
24520
26532
  console.error(error);
@@ -24542,9 +26554,10 @@ var useDashboard = (dashboardName, config) => {
24542
26554
  reportId,
24543
26555
  client,
24544
26556
  tenants,
24545
- filters: dashboardFilters2.concat(customFilters).concat(customReportFiltersArray),
26557
+ filters: allFilters,
24546
26558
  getToken,
24547
- additionalProcessing
26559
+ additionalProcessing,
26560
+ overwriteCache: forceCacheToRefresh
24548
26561
  }).then(({ rows, rowCount, columns, fields }) => {
24549
26562
  if (reportRequestIds.current[reportId] !== requestId) {
24550
26563
  return;
@@ -24568,19 +26581,30 @@ var useDashboard = (dashboardName, config) => {
24568
26581
  }
24569
26582
  });
24570
26583
  }).catch((e) => {
24571
- if (e instanceof Error && e.name === "AbortError") return;
26584
+ if (e instanceof Error && e.name === "AbortError") {
26585
+ return;
26586
+ }
24572
26587
  console.error("Failed to fetch background rows", e);
24573
26588
  });
24574
26589
  }
24575
26590
  return report;
24576
26591
  })
24577
26592
  );
26593
+ if (!loadedDashes.includes(dashboardName) || !cacheEnabled || forceCacheToRefresh) {
26594
+ setLoadedDashes((prev) => prev.includes(dashboardName) ? prev : [...prev, dashboardName]);
26595
+ setLastUpdatedDict((prev) => ({ ...prev, [dashboardName]: Date.now() }));
26596
+ }
26597
+ if (logCacheStatistics) {
26598
+ console.log(`Dashboard "${dashboardName}": Cache Rate: ${(100 * totalCached / allReports.length).toFixed(1)}%, load time: ${((Date.now() - fetchStartTime) / 1e3).toFixed(2)} secs`);
26599
+ }
24578
26600
  };
24579
26601
  return {
24580
26602
  isLoading: !!isLoading || !sections,
24581
26603
  sections: isLoading ? null : sections,
24582
26604
  filters,
24583
- applyFilters
26605
+ applyFilters,
26606
+ lastUpdated,
26607
+ forceCacheRefresh
24584
26608
  };
24585
26609
  };
24586
26610
  var useDashboardReportInternal = (reportId, config) => {
@@ -24604,15 +26628,6 @@ var useDashboardReportInternal = (reportId, config) => {
24604
26628
  } = useDashboardInternal(reports[reportId]?.dashboardName ?? null);
24605
26629
  const [pageLoading, setPageLoading] = useState2(false);
24606
26630
  const [maxPage, setMaxPage] = useState2(0);
24607
- useEffect2(() => {
24608
- if (config?.initialFilters) {
24609
- customReportFiltersDispatch({
24610
- type: "ADD_CUSTOM_REPORT_FILTERS",
24611
- reportId,
24612
- data: config.initialFilters.map(convertCustomFilter)
24613
- });
24614
- }
24615
- }, []);
24616
26631
  const processedReport = useMemo2(() => {
24617
26632
  if (!reports[reportId]) return null;
24618
26633
  const dashboardName = reports[reportId].dashboardName;
@@ -24817,6 +26832,7 @@ var useDashboardReportInternal = (reportId, config) => {
24817
26832
  };
24818
26833
  var useDashboardReport = (reportId, config) => {
24819
26834
  const { report, loading, applyFilters, deleteReport, nextPage } = useDashboardReportInternal(reportId, config);
26835
+ const { customReportFiltersDispatch } = useContext(ReportFiltersContext);
24820
26836
  const fetchingRef = useRef2(false);
24821
26837
  const fetchNextPage = async () => {
24822
26838
  if (fetchingRef.current) {
@@ -24831,6 +26847,15 @@ var useDashboardReport = (reportId, config) => {
24831
26847
  fetchingRef.current = false;
24832
26848
  }
24833
26849
  };
26850
+ useEffect2(() => {
26851
+ if (config?.initialFilters) {
26852
+ customReportFiltersDispatch({
26853
+ type: "ADD_CUSTOM_REPORT_FILTERS",
26854
+ reportId,
26855
+ data: config.initialFilters.map(convertCustomFilter)
26856
+ });
26857
+ }
26858
+ }, []);
24834
26859
  return {
24835
26860
  report,
24836
26861
  isLoading: loading || fetchingRef.current,
@@ -27995,19 +30020,23 @@ var RenderLegend = ({
27995
30020
  const seeAllMeasureRef = useRef5(null);
27996
30021
  const seeAllTriggerRef = useRef5(null);
27997
30022
  const itemRefs = useRef5([]);
27998
- if (!payload || payload.length === 0) return null;
27999
- const maxItems = limit ?? payload.length;
30023
+ const safePayload = payload ?? [];
30024
+ const maxItems = limit ?? safePayload.length;
28000
30025
  const measuredLimit = visibleCount ?? maxItems;
28001
- const visiblePayload = payload.slice(0, Math.min(maxItems, measuredLimit));
30026
+ const visiblePayload = safePayload.slice(0, Math.min(maxItems, measuredLimit));
28002
30027
  const handleOpen = () => setIsOpen(true);
28003
30028
  const handleClose = () => setIsOpen(false);
28004
30029
  useLayoutEffect2(() => {
28005
30030
  if (!containerRef.current) return;
28006
- if (!payload.length) return;
30031
+ if (!safePayload.length) {
30032
+ setShowSeeAll(false);
30033
+ setVisibleCount(null);
30034
+ return;
30035
+ }
28007
30036
  const updateVisibleCount = () => {
28008
30037
  const containerWidth = containerRef.current?.offsetWidth ?? 0;
28009
30038
  if (!containerWidth) return;
28010
- const maxCount = Math.min(maxItems, payload.length);
30039
+ const maxCount = Math.min(maxItems, safePayload.length);
28011
30040
  const seeAllWidth = getOuterWidth(seeAllMeasureRef.current);
28012
30041
  const computeCount = (reserveSeeAll) => {
28013
30042
  const availableWidth = containerWidth - (reserveSeeAll ? seeAllWidth : 0);
@@ -28023,7 +30052,7 @@ var RenderLegend = ({
28023
30052
  return Math.max(1, nextCount);
28024
30053
  };
28025
30054
  const countWithSeeAll = computeCount(true);
28026
- const shouldShowSeeAll = payload.length > countWithSeeAll;
30055
+ const shouldShowSeeAll = safePayload.length > countWithSeeAll;
28027
30056
  const finalCount = shouldShowSeeAll ? countWithSeeAll : computeCount(false);
28028
30057
  setShowSeeAll(shouldShowSeeAll);
28029
30058
  setVisibleCount(finalCount);
@@ -28032,7 +30061,8 @@ var RenderLegend = ({
28032
30061
  const observer = new ResizeObserver(updateVisibleCount);
28033
30062
  observer.observe(containerRef.current);
28034
30063
  return () => observer.disconnect();
28035
- }, [payload.length, maxItems]);
30064
+ }, [safePayload.length, maxItems]);
30065
+ if (!safePayload.length) return null;
28036
30066
  return /* @__PURE__ */ jsxs19(
28037
30067
  "div",
28038
30068
  {
@@ -28072,7 +30102,7 @@ var RenderLegend = ({
28072
30102
  justifyContent: "flex-start",
28073
30103
  flexWrap: "nowrap"
28074
30104
  },
28075
- children: payload.slice(0, maxItems).map((entry, index) => /* @__PURE__ */ jsx27(
30105
+ children: safePayload.slice(0, maxItems).map((entry, index) => /* @__PURE__ */ jsx27(
28076
30106
  "div",
28077
30107
  {
28078
30108
  ref: (element) => {
@@ -28161,7 +30191,7 @@ var RenderLegend = ({
28161
30191
  maxWidth: 520,
28162
30192
  gap: "0.5rem"
28163
30193
  },
28164
- children: payload.map((entry, index) => /* @__PURE__ */ jsx27(
30194
+ children: safePayload.map((entry, index) => /* @__PURE__ */ jsx27(
28165
30195
  LegendItem,
28166
30196
  {
28167
30197
  entry,
@@ -28511,8 +30541,8 @@ var PieChartWrapper = React4.forwardRef(
28511
30541
  cy: "50%",
28512
30542
  startAngle: 90,
28513
30543
  endAngle: -270,
28514
- innerRadius: isDonut ? showLegend ? "50%" : "70%" : "0%",
28515
- outerRadius: showLegend ? "70%" : "100%",
30544
+ innerRadius: isDonut ? "70%" : "0%",
30545
+ outerRadius: "100%",
28516
30546
  paddingAngle: 0,
28517
30547
  dataKey: category,
28518
30548
  nameKey: index,
@@ -29115,8 +31145,8 @@ import {
29115
31145
  } from "recharts";
29116
31146
 
29117
31147
  // src/utils/axisFormatter.ts
29118
- import { endOfWeek as endOfWeek2, format as format5, getWeek as getWeek2, isValid as isValid4, startOfWeek as startOfWeek4 } from "date-fns";
29119
- import { utcToZonedTime as utcToZonedTime3 } from "date-fns-tz";
31148
+ import { endOfWeek as endOfWeek2, format as format5, getWeek as getWeek2, isValid as isValid5, startOfWeek as startOfWeek5 } from "date-fns";
31149
+ import { utcToZonedTime as utcToZonedTime4 } from "date-fns-tz";
29120
31150
  var axisFormatter = ({ value, field, fields }) => {
29121
31151
  if (field === void 0 || field === null) return "";
29122
31152
  if (value === void 0 || value === null) return "";
@@ -29232,8 +31262,8 @@ var formatPercent2 = (value) => {
29232
31262
  return formatter.format(Number(value));
29233
31263
  };
29234
31264
  var _getUTCDateHelper2 = (value, fmt) => {
29235
- const utcDate = utcToZonedTime3(new Date(value), "UTC");
29236
- if (!isValid4(utcDate)) return "Invalid date";
31265
+ const utcDate = utcToZonedTime4(new Date(value), "UTC");
31266
+ if (!isValid5(utcDate)) return "Invalid date";
29237
31267
  return format5(utcDate, fmt);
29238
31268
  };
29239
31269
  var format_YYYY2 = (value) => _getUTCDateHelper2(value, "yyyy");
@@ -29242,9 +31272,9 @@ var format_MMM_yyyy2 = (value) => _getUTCDateHelper2(value, "MMM yyyy");
29242
31272
  var format_hh_ap_pm2 = (value) => _getUTCDateHelper2(value, "hh:mm aa");
29243
31273
  var format_MMM_dd_yyyy2 = (value) => _getUTCDateHelper2(value, "dd MMM yyyy");
29244
31274
  var format_MMM_dd_MMM_dd = (value) => {
29245
- const utcDate = utcToZonedTime3(new Date(value), "UTC");
29246
- if (!isValid4(utcDate)) return "Invalid date";
29247
- const monday = startOfWeek4(utcDate, { weekStartsOn: 1 });
31275
+ const utcDate = utcToZonedTime4(new Date(value), "UTC");
31276
+ if (!isValid5(utcDate)) return "Invalid date";
31277
+ const monday = startOfWeek5(utcDate, { weekStartsOn: 1 });
29248
31278
  const sunday = endOfWeek2(utcDate, { weekStartsOn: 1 });
29249
31279
  if (format5(monday, "MMM") === format5(sunday, "MMM")) {
29250
31280
  return `${format5(monday, "MMM dd")} - ${format5(sunday, "dd")}`;
@@ -29253,20 +31283,20 @@ var format_MMM_dd_MMM_dd = (value) => {
29253
31283
  }
29254
31284
  };
29255
31285
  var format_MMM_dd_hh_mm_ap_pm2 = (value) => {
29256
- const utcDate = utcToZonedTime3(new Date(value), "UTC");
29257
- if (!isValid4(utcDate)) return "Invalid date";
31286
+ const utcDate = utcToZonedTime4(new Date(value), "UTC");
31287
+ if (!isValid5(utcDate)) return "Invalid date";
29258
31288
  const formatStr = utcDate.getMinutes() === 0 ? "MMM do h a" : "MMM do h:mm a";
29259
31289
  const res = format5(utcDate, formatStr);
29260
31290
  return res.slice(0, -2) + res.slice(-2).toLowerCase();
29261
31291
  };
29262
31292
  var format_wo_yyyy2 = (value) => {
29263
- const utcDate = utcToZonedTime3(new Date(value), "UTC");
29264
- if (!isValid4(utcDate)) return "Invalid date";
31293
+ const utcDate = utcToZonedTime4(new Date(value), "UTC");
31294
+ if (!isValid5(utcDate)) return "Invalid date";
29265
31295
  return `${getWeek2(utcDate)},${utcDate.getFullYear()}`;
29266
31296
  };
29267
31297
 
29268
31298
  // src/components/Chart/ChartTooltip.tsx
29269
- import { format as format6, subDays as subDays2 } from "date-fns";
31299
+ import { format as format6, subDays as subDays3 } from "date-fns";
29270
31300
 
29271
31301
  // src/components/Chart/ChartTooltipFrame.tsx
29272
31302
  import { jsx as jsx30 } from "react/jsx-runtime";
@@ -29510,7 +31540,7 @@ function reformatComparisonPayload(props, primaryLabel, comparisonLabel) {
29510
31540
  const nameKey = isComparison ? `comparison_${props.xAxisField}` : props.xAxisField;
29511
31541
  const days = LABEL_TO_DAYS[primaryLabel] ?? 0;
29512
31542
  const primaryDate = item.payload[props.xAxisField] ?? 0;
29513
- const compDate = subDays2(new Date(primaryDate), days + 1);
31543
+ const compDate = subDays3(new Date(primaryDate), days + 1);
29514
31544
  const date = item.payload[nameKey] ?? format6(compDate, props.xAxisFormat);
29515
31545
  const name2 = props.dateFormatter(date);
29516
31546
  const color2 = item.color;
@@ -30576,16 +32606,16 @@ import {
30576
32606
  useState as useState12
30577
32607
  } from "react";
30578
32608
  import {
30579
- startOfMonth as startOfMonth3,
32609
+ startOfMonth as startOfMonth4,
30580
32610
  endOfMonth as endOfMonth2,
30581
32611
  format as format7,
30582
32612
  eachDayOfInterval,
30583
- subMonths as subMonths3,
30584
- startOfWeek as startOfWeek5,
32613
+ subMonths as subMonths5,
32614
+ startOfWeek as startOfWeek6,
30585
32615
  endOfWeek as endOfWeek3,
30586
32616
  differenceInDays as differenceInDays3,
30587
- startOfDay as startOfDay4,
30588
- addMonths,
32617
+ startOfDay as startOfDay6,
32618
+ addMonths as addMonths2,
30589
32619
  isBefore as isBefore2,
30590
32620
  isAfter as isAfter2
30591
32621
  } from "date-fns";
@@ -31110,20 +33140,20 @@ function CalendarRow({
31110
33140
  setLocalPreset
31111
33141
  }) {
31112
33142
  const firstMonthDisplayedDates = eachDayOfInterval({
31113
- start: startOfWeek5(anchorStartDate),
33143
+ start: startOfWeek6(anchorStartDate),
31114
33144
  end: endOfWeek3(endOfMonth2(anchorStartDate))
31115
33145
  });
31116
33146
  const secondMonthDisplayedDates = eachDayOfInterval({
31117
- start: startOfWeek5(startOfMonth3(anchorEndDate)),
33147
+ start: startOfWeek6(startOfMonth4(anchorEndDate)),
31118
33148
  end: endOfWeek3(anchorEndDate)
31119
33149
  });
31120
33150
  const incrementAnchorDates = () => {
31121
- setAnchorStartDate(startOfMonth3(addMonths(anchorStartDate, 1)));
31122
- setAnchorEndDate(endOfMonth2(addMonths(anchorEndDate, 1)));
33151
+ setAnchorStartDate(startOfMonth4(addMonths2(anchorStartDate, 1)));
33152
+ setAnchorEndDate(endOfMonth2(addMonths2(anchorEndDate, 1)));
31123
33153
  };
31124
33154
  const decrementAnchorDates = () => {
31125
- setAnchorStartDate(startOfMonth3(subMonths3(anchorStartDate, 1)));
31126
- setAnchorEndDate(endOfMonth2(subMonths3(anchorEndDate, 1)));
33155
+ setAnchorStartDate(startOfMonth4(subMonths5(anchorStartDate, 1)));
33156
+ setAnchorEndDate(endOfMonth2(subMonths5(anchorEndDate, 1)));
31127
33157
  };
31128
33158
  return /* @__PURE__ */ jsx40("div", { style: { position: "absolute", zIndex: 100, marginTop: 12 }, children: /* @__PURE__ */ jsxs30(
31129
33159
  "div",
@@ -31377,10 +33407,10 @@ function DayPicker({
31377
33407
  setLocalPreset,
31378
33408
  theme
31379
33409
  }) {
31380
- const isStartDate = differenceInDays3(startOfDay4(date), startOfDay4(localStartDate || 0)) === 0;
31381
- const isEndDate = differenceInDays3(startOfDay4(date), startOfDay4(localEndDate || 0)) === 0;
33410
+ const isStartDate = differenceInDays3(startOfDay6(date), startOfDay6(localStartDate || 0)) === 0;
33411
+ const isEndDate = differenceInDays3(startOfDay6(date), startOfDay6(localEndDate || 0)) === 0;
31382
33412
  const isBetweenStartAndEnd = isBefore2(date, localEndDate || 0) && isAfter2(date, localStartDate || 0);
31383
- const isBeginningOfWeek = differenceInDays3(startOfWeek5(date), date) === 0;
33413
+ const isBeginningOfWeek = differenceInDays3(startOfWeek6(date), date) === 0;
31384
33414
  const isEndOfWeek = differenceInDays3(endOfWeek3(date), date) === 0;
31385
33415
  return /* @__PURE__ */ jsx40(
31386
33416
  "button",
@@ -31429,7 +33459,7 @@ function DayPicker({
31429
33459
  });
31430
33460
  setLocalPreset("");
31431
33461
  }
31432
- if (localStartDate && localEndDate && differenceInDays3(startOfDay4(date), startOfDay4(localStartDate)) === 0) {
33462
+ if (localStartDate && localEndDate && differenceInDays3(startOfDay6(date), startOfDay6(localStartDate)) === 0) {
31433
33463
  setLocalStartDate(void 0);
31434
33464
  setLocalEndDate(void 0);
31435
33465
  }
@@ -31440,16 +33470,16 @@ function DayPicker({
31440
33470
  }
31441
33471
  function getAnchorStartDate(startDate, endDate) {
31442
33472
  if (!startDate && !endDate) {
31443
- return startOfMonth3(subMonths3(/* @__PURE__ */ new Date(), 1));
33473
+ return startOfMonth4(subMonths5(/* @__PURE__ */ new Date(), 1));
31444
33474
  }
31445
33475
  if (startDate && !endDate) {
31446
- return startOfMonth3(startDate);
33476
+ return startOfMonth4(startDate);
31447
33477
  }
31448
33478
  if (!startDate && endDate) {
31449
- return startOfMonth3(subMonths3(endDate, 1));
33479
+ return startOfMonth4(subMonths5(endDate, 1));
31450
33480
  }
31451
33481
  if (startDate && endDate) {
31452
- return startOfMonth3(startDate);
33482
+ return startOfMonth4(startDate);
31453
33483
  }
31454
33484
  return /* @__PURE__ */ new Date();
31455
33485
  }
@@ -31458,13 +33488,13 @@ function getAnchorEndDate(startDate, endDate) {
31458
33488
  return endOfMonth2(/* @__PURE__ */ new Date());
31459
33489
  }
31460
33490
  if (startDate && !endDate) {
31461
- return endOfMonth2(addMonths(startDate, 1));
33491
+ return endOfMonth2(addMonths2(startDate, 1));
31462
33492
  }
31463
33493
  if (!startDate && endDate) {
31464
33494
  return endOfMonth2(endDate);
31465
33495
  }
31466
33496
  if (startDate && endDate) {
31467
- return endOfMonth2(addMonths(startDate, 1));
33497
+ return endOfMonth2(addMonths2(startDate, 1));
31468
33498
  }
31469
33499
  return /* @__PURE__ */ new Date();
31470
33500
  }
@@ -32659,6 +34689,164 @@ function DashboardFilter2({
32659
34689
  // src/Chart.tsx
32660
34690
  init_paginationProcessing();
32661
34691
 
34692
+ // src/utils/cloudCacheValidation.ts
34693
+ var LIMIT_CLAUSE_REGEX = /^limit\b\s+(?:all|\d+|\$\d+|:[a-zA-Z_][a-zA-Z0-9_]*|\?)/i;
34694
+ var SQL_CONTENT_TO_IGNORE_REGEX = /'(?:''|[^'])*'|"(?:[^"]|"")*"|`[^`]*`|\[[^\]]*\]|--[^\n]*|\/\*[\s\S]*?\*\//g;
34695
+ function isWordChar(char) {
34696
+ if (!char) {
34697
+ return false;
34698
+ }
34699
+ return /[A-Za-z0-9_]/.test(char);
34700
+ }
34701
+ function getFinalSqlStatement(query) {
34702
+ const statements = query.split(";").map((statement) => statement.trim()).filter((statement) => statement.length > 0);
34703
+ return statements[statements.length - 1] ?? "";
34704
+ }
34705
+ function hasTopLevelLimitClause(statement) {
34706
+ let depth = 0;
34707
+ for (let i = 0; i < statement.length; i += 1) {
34708
+ const char = statement[i];
34709
+ if (char === "(") {
34710
+ depth += 1;
34711
+ continue;
34712
+ }
34713
+ if (char === ")") {
34714
+ depth = Math.max(0, depth - 1);
34715
+ continue;
34716
+ }
34717
+ if (depth !== 0) {
34718
+ continue;
34719
+ }
34720
+ const previousChar = i > 0 ? statement[i - 1] : void 0;
34721
+ if (isWordChar(previousChar)) {
34722
+ continue;
34723
+ }
34724
+ if (!LIMIT_CLAUSE_REGEX.test(statement.slice(i))) {
34725
+ continue;
34726
+ }
34727
+ return true;
34728
+ }
34729
+ return false;
34730
+ }
34731
+ function unwrapIdentifier(identifier) {
34732
+ if (!identifier) {
34733
+ return "";
34734
+ }
34735
+ const trimmed = identifier.trim();
34736
+ if (trimmed.length < 2) {
34737
+ return trimmed;
34738
+ }
34739
+ const startsAndEndsWithDoubleQuote = trimmed.startsWith('"') && trimmed.endsWith('"');
34740
+ const startsAndEndsWithSingleQuote = trimmed.startsWith("'") && trimmed.endsWith("'");
34741
+ const startsAndEndsWithBackticks = trimmed.startsWith("`") && trimmed.endsWith("`");
34742
+ const startsAndEndsWithBrackets = trimmed.startsWith("[") && trimmed.endsWith("]");
34743
+ if (startsAndEndsWithDoubleQuote || startsAndEndsWithSingleQuote || startsAndEndsWithBackticks || startsAndEndsWithBrackets) {
34744
+ return trimmed.slice(1, -1);
34745
+ }
34746
+ return trimmed;
34747
+ }
34748
+ function normalizeIdentifier(identifier) {
34749
+ return unwrapIdentifier(identifier).toLowerCase();
34750
+ }
34751
+ function hasRowFieldWithIdentifier(row, normalizedField) {
34752
+ if (!row || !normalizedField) {
34753
+ return false;
34754
+ }
34755
+ return Object.keys(row).some(
34756
+ (fieldName) => normalizeIdentifier(fieldName) === normalizedField
34757
+ );
34758
+ }
34759
+ function reportReferencesField(report, field, table) {
34760
+ const normalizedField = normalizeIdentifier(field);
34761
+ if (!normalizedField) {
34762
+ return false;
34763
+ }
34764
+ const referencedColumns = report?.referencedColumns ?? {};
34765
+ let entries = Object.entries(referencedColumns);
34766
+ if (entries.length === 0) {
34767
+ return false;
34768
+ }
34769
+ const normalizedTable = normalizeIdentifier(table);
34770
+ if (normalizedTable) {
34771
+ entries = entries.filter(
34772
+ ([tableName]) => normalizeIdentifier(tableName) === normalizedTable
34773
+ );
34774
+ }
34775
+ const referencedFields = entries.flatMap(([, fields]) => fields ?? []);
34776
+ if (referencedFields.length === 0) {
34777
+ return false;
34778
+ }
34779
+ return referencedFields.some((referencedField) => {
34780
+ const normalizedReferencedField = normalizeIdentifier(referencedField);
34781
+ return normalizedReferencedField === normalizedField || normalizedReferencedField === "*";
34782
+ });
34783
+ }
34784
+ function isDateFieldMissingInReport(report) {
34785
+ if (!report?.dateField?.field || !report.dateField.table) {
34786
+ return false;
34787
+ }
34788
+ const dateField = report.dateField;
34789
+ if (!reportReferencesField(report, dateField.field, dateField.table)) {
34790
+ return true;
34791
+ }
34792
+ const firstRow = report.rows?.[0];
34793
+ if (!firstRow) {
34794
+ return false;
34795
+ }
34796
+ const normalizedDateField = normalizeIdentifier(dateField.field);
34797
+ const dateFieldExistsInFirstRow = hasRowFieldWithIdentifier(
34798
+ firstRow,
34799
+ normalizedDateField
34800
+ );
34801
+ return !dateFieldExistsInFirstRow;
34802
+ }
34803
+ function hasLimitClause(query) {
34804
+ if (!query) {
34805
+ return false;
34806
+ }
34807
+ const sanitizedQuery = query.replace(SQL_CONTENT_TO_IGNORE_REGEX, " ");
34808
+ const finalStatement = getFinalSqlStatement(sanitizedQuery);
34809
+ if (!finalStatement) {
34810
+ return false;
34811
+ }
34812
+ return hasTopLevelLimitClause(finalStatement);
34813
+ }
34814
+ function reportUsesLimitClause(report) {
34815
+ const queriesToInspect = report?.itemQuery && report.itemQuery.length > 0 ? report.itemQuery : report?.queryString ? [report.queryString] : [];
34816
+ return queriesToInspect.some(hasLimitClause);
34817
+ }
34818
+ function getMissingDashboardFilterFields({
34819
+ rows = [],
34820
+ dashboardFilters = []
34821
+ }) {
34822
+ const dateIndex = dashboardFilters.findIndex(
34823
+ (filter) => filter.filterType === "date_range"
34824
+ );
34825
+ const requiredFields = dashboardFilters.flatMap((filter, index) => {
34826
+ if (index === dateIndex || !filter.field) {
34827
+ return [];
34828
+ }
34829
+ const normalizedField = normalizeIdentifier(filter.field);
34830
+ if (!normalizedField) {
34831
+ return [];
34832
+ }
34833
+ return [
34834
+ {
34835
+ displayField: unwrapIdentifier(filter.field),
34836
+ normalizedField
34837
+ }
34838
+ ];
34839
+ });
34840
+ const dedupedRequiredFields = requiredFields.filter(
34841
+ (field, index, fields) => fields.findIndex(
34842
+ (candidate) => candidate.normalizedField === field.normalizedField
34843
+ ) === index
34844
+ );
34845
+ return dedupedRequiredFields.filter(
34846
+ ({ normalizedField }) => rows.some((row) => !hasRowFieldWithIdentifier(row, normalizedField))
34847
+ ).map(({ displayField }) => displayField);
34848
+ }
34849
+
32662
34850
  // src/components/Dashboard/MetricComponent.tsx
32663
34851
  init_dateRangePickerUtils();
32664
34852
  import { useContext as useContext12 } from "react";
@@ -32766,7 +34954,7 @@ function QuillMetricComponent({
32766
34954
  width: "100%"
32767
34955
  }
32768
34956
  }
32769
- ) : !report?.rows || report?.rows?.length === 0 || report.rows[0]?.[report.xAxisField] === null || report.rows[0]?.[report.xAxisField] === void 0 ? /* @__PURE__ */ jsx44(
34957
+ ) : !report?.rows || report?.rows?.length === 0 ? /* @__PURE__ */ jsx44(
32770
34958
  "div",
32771
34959
  {
32772
34960
  style: {
@@ -36285,6 +38473,7 @@ function QuillTableDashboardComponent({
36285
38473
  }
36286
38474
 
36287
38475
  // src/Chart.tsx
38476
+ init_valueFormatter();
36288
38477
  import { Fragment as Fragment5, jsx as jsx49, jsxs as jsxs37 } from "react/jsx-runtime";
36289
38478
  var MAX_ROWS_FOR_GENERIC_TABLE = 500;
36290
38479
  function sumByKey(arr, key) {
@@ -36321,7 +38510,8 @@ function Chart({
36321
38510
  filters,
36322
38511
  onClickChartElement,
36323
38512
  dateBucket,
36324
- propagateChanges
38513
+ propagateChanges,
38514
+ isAdmin = false
36325
38515
  }) {
36326
38516
  const [schemaData] = useContext16(SchemaDataContext);
36327
38517
  const { reload } = useReportInternal(reportId);
@@ -36443,6 +38633,25 @@ function Chart({
36443
38633
  }
36444
38634
  }, [report, theme]);
36445
38635
  const [client, clientLoading] = useContext16(ClientContext);
38636
+ const cloudCacheEnabled = useMemo13(() => {
38637
+ return !!client?.features?.cloudCache;
38638
+ }, [client]);
38639
+ const shouldShowCloudCacheValidationErrors = isAdmin && cloudCacheEnabled;
38640
+ const isMissingDateField = useMemo13(() => {
38641
+ if (!shouldShowCloudCacheValidationErrors) return false;
38642
+ return isDateFieldMissingInReport(report);
38643
+ }, [report, shouldShowCloudCacheValidationErrors]);
38644
+ const usesLimitInQuery = useMemo13(() => {
38645
+ if (!shouldShowCloudCacheValidationErrors) return false;
38646
+ return reportUsesLimitClause(report);
38647
+ }, [report, shouldShowCloudCacheValidationErrors]);
38648
+ const missingDashboardFilterFields = useMemo13(() => {
38649
+ if (!shouldShowCloudCacheValidationErrors || !report) return [];
38650
+ return getMissingDashboardFilterFields({
38651
+ rows: report.rows,
38652
+ dashboardFilters
38653
+ });
38654
+ }, [report, dashboardFilters, shouldShowCloudCacheValidationErrors]);
36446
38655
  const [error, setError] = useState19(void 0);
36447
38656
  const updateFilter = (filter, value, comparison) => {
36448
38657
  let filterValue = {};
@@ -36583,6 +38792,15 @@ function Chart({
36583
38792
  if (report?.error || error) {
36584
38793
  return /* @__PURE__ */ jsx49("div", { style: containerStyle, className, children: /* @__PURE__ */ jsx49(ChartError, { errorMessage: report?.error ?? error }) });
36585
38794
  }
38795
+ if (isMissingDateField) {
38796
+ return /* @__PURE__ */ jsx49("div", { style: containerStyle, className, children: /* @__PURE__ */ jsx49(ChartError, { errorMessage: `The query for this report is missing dashboard date filter field ${report?.dateField?.field}, which is necessary for date filtering on this dashboard when the cache is enabled.` }) });
38797
+ }
38798
+ if (usesLimitInQuery) {
38799
+ return /* @__PURE__ */ jsx49("div", { style: containerStyle, className, children: /* @__PURE__ */ jsx49(ChartError, { errorMessage: `The query for this report uses the LIMIT keyword, which is not supported when the cache is enabled.` }) });
38800
+ }
38801
+ if (missingDashboardFilterFields.length) {
38802
+ return /* @__PURE__ */ jsx49("div", { style: containerStyle, className, children: /* @__PURE__ */ jsx49(ChartError, { errorMessage: `The query for this report is missing dashboard filter fields: ${missingDashboardFilterFields.join(", ")}` }) });
38803
+ }
36586
38804
  return /* @__PURE__ */ jsxs37(
36587
38805
  "div",
36588
38806
  {
@@ -36913,7 +39131,7 @@ var ChartDisplay = ({
36913
39131
  );
36914
39132
  }
36915
39133
  if (config?.chartType?.toLowerCase() === "metric") {
36916
- if (!config?.rows || config?.rows?.length === 0 || config?.rows[0]?.[config?.xAxisField] === null || config?.rows[0]?.[config?.xAxisField] === void 0) {
39134
+ if (!config?.rows || config?.rows?.length === 0) {
36917
39135
  return /* @__PURE__ */ jsx49(
36918
39136
  "div",
36919
39137
  {
@@ -36951,6 +39169,36 @@ var ChartDisplay = ({
36951
39169
  }
36952
39170
  );
36953
39171
  }
39172
+ if (config?.pivotRows && config?.pivotRows?.length > 0) {
39173
+ return /* @__PURE__ */ jsx49(
39174
+ "div",
39175
+ {
39176
+ style: {
39177
+ fontFamily: theme?.fontFamily,
39178
+ fontSize: 32,
39179
+ color: theme?.primaryTextColor,
39180
+ fontWeight: "600",
39181
+ textOverflow: "ellipsis",
39182
+ margin: 0,
39183
+ whiteSpace: "nowrap",
39184
+ boxSizing: "content-box",
39185
+ maxWidth: "100%",
39186
+ textAlign: "left",
39187
+ overflow: "hidden",
39188
+ height: containerStyle?.height || "100%",
39189
+ display: "flex",
39190
+ width: "100%",
39191
+ flexDirection: "row",
39192
+ ...containerStyle
39193
+ },
39194
+ className,
39195
+ children: /* @__PURE__ */ jsx49(QuillMetricComponent, { report: config ?? EMPTY_REPORT, children: quillFormat({
39196
+ value: config?.pivotRows?.[0]?.[config?.xAxisField],
39197
+ format: config?.xAxisFormat
39198
+ }) ?? "No results" })
39199
+ }
39200
+ );
39201
+ }
36954
39202
  return /* @__PURE__ */ jsx49(
36955
39203
  "div",
36956
39204
  {
@@ -37899,7 +40147,7 @@ init_Filter();
37899
40147
  import { useState as useState22, useEffect as useEffect18, useMemo as useMemo15 } from "react";
37900
40148
  init_textProcessing();
37901
40149
  init_filterProcessing();
37902
- import { format as format8, isValid as isValid5, parse as parse4, startOfToday as startOfToday2 } from "date-fns";
40150
+ import { format as format8, isValid as isValid6, parse as parse4, startOfToday as startOfToday2 } from "date-fns";
37903
40151
  import { Fragment as Fragment8, jsx as jsx55, jsxs as jsxs41 } from "react/jsx-runtime";
37904
40152
  function FilterModal({
37905
40153
  schema,
@@ -38100,7 +40348,7 @@ function FilterModal({
38100
40348
  DateOperator.LessThan,
38101
40349
  DateOperator.LessThanOrEqualTo,
38102
40350
  DateOperator.NotEqualTo
38103
- ].includes(operator2) && !isValid5(parse4(value, "yyyy-mm-dd", /* @__PURE__ */ new Date())) && (filterInitialized || !filter)) {
40351
+ ].includes(operator2) && !isValid6(parse4(value, "yyyy-mm-dd", /* @__PURE__ */ new Date())) && (filterInitialized || !filter)) {
38104
40352
  setValue(startOfToday2().toISOString().substring(0, 10));
38105
40353
  } else if (type === FieldType.Date && [
38106
40354
  DateOperator.InTheLast,
@@ -38290,7 +40538,7 @@ function FilterModal({
38290
40538
  case DateOperator.GreaterThanOrEqualTo:
38291
40539
  case DateOperator.LessThanOrEqualTo: {
38292
40540
  const parsedDate = parse4(value, "yyyy-mm-dd", /* @__PURE__ */ new Date());
38293
- if (!isValid5(parsedDate)) {
40541
+ if (!isValid6(parsedDate)) {
38294
40542
  alert("Please specify a valid date in yyyy-mm-dd");
38295
40543
  return;
38296
40544
  }
@@ -38818,6 +41066,9 @@ function validateTemplatesAgainstFilters(filters, templates) {
38818
41066
  }
38819
41067
  async function addTemplatesToDashboard(name2, newTemplates, client, dashboardData, getToken, eventTracking) {
38820
41068
  try {
41069
+ if (!newTemplates?.length) {
41070
+ return null;
41071
+ }
38821
41072
  const { publicKey, tenants } = client;
38822
41073
  if (tenants) {
38823
41074
  throw new Error("Adding from template not yet supported for tenants");
@@ -38870,6 +41121,7 @@ async function addTemplatesToDashboard(name2, newTemplates, client, dashboardDat
38870
41121
  function: "addTemplatesToDashboard"
38871
41122
  }
38872
41123
  });
41124
+ return null;
38873
41125
  }
38874
41126
  }
38875
41127
 
@@ -40366,7 +42618,7 @@ import {
40366
42618
  useEffect as useEffect25,
40367
42619
  useRef as useRef20,
40368
42620
  useMemo as useMemo22,
40369
- useCallback as useCallback4
42621
+ useCallback as useCallback5
40370
42622
  } from "react";
40371
42623
  import MonacoEditor from "@monaco-editor/react";
40372
42624
 
@@ -40377,7 +42629,7 @@ import {
40377
42629
  useState as useState30,
40378
42630
  useContext as useContext26,
40379
42631
  useMemo as useMemo21,
40380
- useCallback as useCallback3
42632
+ useCallback as useCallback4
40381
42633
  } from "react";
40382
42634
  import {
40383
42635
  closestCenter,
@@ -40398,7 +42650,7 @@ import { CSS as DND_CSS } from "@dnd-kit/utilities";
40398
42650
 
40399
42651
  // src/internals/ReportBuilder/PivotModal.tsx
40400
42652
  import {
40401
- useCallback as useCallback2,
42653
+ useCallback as useCallback3,
40402
42654
  useContext as useContext23,
40403
42655
  useMemo as useMemo18,
40404
42656
  useState as useState27,
@@ -40793,7 +43045,7 @@ import {
40793
43045
  eachMonthOfInterval,
40794
43046
  eachWeekOfInterval,
40795
43047
  eachYearOfInterval,
40796
- isValid as isValid6,
43048
+ isValid as isValid7,
40797
43049
  parseISO as parseISO3
40798
43050
  } from "date-fns";
40799
43051
  init_pivotProcessing();
@@ -41281,7 +43533,7 @@ var PivotModal = ({
41281
43533
  setIsOpen(false);
41282
43534
  setPopUpTitle("Add pivot");
41283
43535
  };
41284
- const onCommitPivot = useCallback2(() => {
43536
+ const onCommitPivot = useCallback3(() => {
41285
43537
  const errors2 = [];
41286
43538
  if ((pivotAggregations?.length ?? 0) === 0) {
41287
43539
  errors2.push("You must have at least one aggregation");
@@ -41423,7 +43675,7 @@ var PivotModal = ({
41423
43675
  const onEditRecommendedPivot = (pivot) => {
41424
43676
  onEditPivot(pivot, null);
41425
43677
  };
41426
- const refreshPivots = useCallback2(async () => {
43678
+ const refreshPivots = useCallback3(async () => {
41427
43679
  if (!client) {
41428
43680
  return;
41429
43681
  }
@@ -44474,7 +46726,7 @@ function ChartBuilder({
44474
46726
  template: true,
44475
46727
  referenceLines: []
44476
46728
  };
44477
- const updateDashboardFilters = async (dashboardName) => {
46729
+ const updateDashboardFilters2 = async (dashboardName) => {
44478
46730
  if (dashboardConfig && dashboardConfig[dashboardName]) {
44479
46731
  return dashboardConfig[dashboardName];
44480
46732
  }
@@ -44796,7 +47048,7 @@ function ChartBuilder({
44796
47048
  if (destinationDashboardName) {
44797
47049
  dashboardName = destinationDashboardName;
44798
47050
  }
44799
- const curDashboard = await updateDashboardFilters(dashboardName);
47051
+ const curDashboard = await updateDashboardFilters2(dashboardName);
44800
47052
  setDashboardOptions(dashboardOptions2);
44801
47053
  curFormData.dashboardName = dashboardName;
44802
47054
  const curSchemaData = schemaData.schemaWithCustomFields;
@@ -44940,7 +47192,7 @@ function ChartBuilder({
44940
47192
  ) ?? {};
44941
47193
  }, [client?.allTenantTypes]);
44942
47194
  const [selectedPivotTable, setSelectedPivotTable] = useState30(pivotData);
44943
- const getDefaultXAxisFormat = useCallback3(
47195
+ const getDefaultXAxisFormat = useCallback4(
44944
47196
  (form) => {
44945
47197
  if (form.pivot?.rowField) {
44946
47198
  if (isDateField(form.pivot.rowFieldType ?? "")) {
@@ -46083,7 +48335,7 @@ function ChartBuilder({
46083
48335
  value: formData.dashboardName || "",
46084
48336
  onChange: async (e) => {
46085
48337
  handleChange(e.target.value, "dashboardName");
46086
- await updateDashboardFilters(e.target.value);
48338
+ await updateDashboardFilters2(e.target.value);
46087
48339
  },
46088
48340
  options: dashboardOptions.map((elem) => ({
46089
48341
  label: elem.label,
@@ -48316,7 +50568,7 @@ function SQLEditor({
48316
50568
  onCloseChartBuilder && onCloseChartBuilder();
48317
50569
  }
48318
50570
  }, [isChartBuilderOpen]);
48319
- const handleRunSqlPrompt = useCallback4(async () => {
50571
+ const handleRunSqlPrompt = useCallback5(async () => {
48320
50572
  if (!client || sqlResponseLoading) {
48321
50573
  return;
48322
50574
  }
@@ -48335,7 +50587,7 @@ function SQLEditor({
48335
50587
  setQuery(resp.message);
48336
50588
  setSqlResponseLoading(false);
48337
50589
  }, [sqlPrompt, sqlResponseLoading]);
48338
- const debounceRunSqlPrompt = useCallback4(
50590
+ const debounceRunSqlPrompt = useCallback5(
48339
50591
  createDebounce(handleRunSqlPrompt, 500),
48340
50592
  [handleRunSqlPrompt]
48341
50593
  );
@@ -54450,7 +56702,7 @@ function StaticChart(props) {
54450
56702
  pageLoading,
54451
56703
  nextPage,
54452
56704
  prevPage,
54453
- sortRows
56705
+ sortRows: sortRows2
54454
56706
  } = useDashboardReportInternal(reportId);
54455
56707
  const baseStyle = report?.chartType && CHART_TYPE_STYLES[report.chartType] ? CHART_TYPE_STYLES[report.chartType] : DEFAULT_STYLE;
54456
56708
  const mergedStyle = className ? { ...containerStyle ?? {} } : { ...baseStyle, ...containerStyle ?? {} };
@@ -54470,7 +56722,7 @@ function StaticChart(props) {
54470
56722
  }
54471
56723
  };
54472
56724
  const onSortChange = (sort) => {
54473
- sortRows({
56725
+ sortRows2({
54474
56726
  field: sort.field,
54475
56727
  direction: sort.direction
54476
56728
  });
@@ -54479,29 +56731,10 @@ function StaticChart(props) {
54479
56731
  ...report,
54480
56732
  ...report.pivot ? formattedPivotRowsAndColumns(report) : {}
54481
56733
  } : void 0;
54482
- const patchedConfig = config && config.pivot ? {
54483
- ...config,
54484
- yAxisFields: config.columns?.slice(1).map((col) => ({
54485
- field: col.field,
54486
- label: col.label ?? col.field,
54487
- format: col.format ?? "number"
54488
- })) ?? config.yAxisFields
54489
- } : config;
54490
- if (patchedConfig) {
54491
- const sample = (patchedConfig.pivotRows ?? patchedConfig.rows ?? []).slice(0, 1).map((row) => {
54492
- const field = patchedConfig.yAxisFields?.[0]?.field;
54493
- return {
54494
- ...row,
54495
- _firstField: field,
54496
- _firstFieldType: field ? typeof row[field] : void 0
54497
- };
54498
- });
54499
- }
54500
56734
  return /* @__PURE__ */ jsx84(
54501
56735
  ChartDisplay,
54502
56736
  {
54503
- reportId,
54504
- config: patchedConfig,
56737
+ config,
54505
56738
  onClickChartElement,
54506
56739
  loading,
54507
56740
  className,