@quillsql/react 2.16.20 → 2.16.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -263,6 +263,9 @@ var init_valueFormatter = __esm({
263
263
  maximumFractionDigits: 2
264
264
  });
265
265
  formatPercent = (value) => {
266
+ if (typeof value === "string" && /^\s*[+-]?(?:\d+(?:\.\d+)?|\.\d+)\s*%\s*$/.test(value)) {
267
+ return value;
268
+ }
266
269
  return formatterPercent.format(Number(value));
267
270
  };
268
271
  _getUTCDateHelper = (value, fmt) => {
@@ -1537,6 +1540,7 @@ var init_dateRangePickerUtils = __esm({
1537
1540
  }
1538
1541
  });
1539
1542
  const defaultCustomIntervals = defaultIntervals.flatMap((interval2) => {
1543
+ const normalizedLabel = interval2.label?.toLowerCase().trim();
1540
1544
  let createdIntervals = [];
1541
1545
  if (interval2.label === "This week") {
1542
1546
  createdIntervals = [
@@ -1686,13 +1690,13 @@ var init_dateRangePickerUtils = __esm({
1686
1690
  unit: "months"
1687
1691
  }
1688
1692
  ];
1689
- } else if (interval2.label === "Last month") {
1693
+ } else if (normalizedLabel === "last month") {
1690
1694
  createdIntervals = [
1691
1695
  {
1692
1696
  type: "previous_month"
1693
1697
  }
1694
1698
  ];
1695
- } else if (interval2.label === "Last quarter") {
1699
+ } else if (normalizedLabel === "previous quarter" || normalizedLabel === "last quarter") {
1696
1700
  createdIntervals = [
1697
1701
  {
1698
1702
  type: "previous_quarter"
@@ -2086,13 +2090,13 @@ var init_Filter = __esm({
2086
2090
  TimeUnit2[TimeUnit2["Hour"] = HOUR] = "Hour";
2087
2091
  return TimeUnit2;
2088
2092
  })(TimeUnit || {});
2089
- FieldType = /* @__PURE__ */ ((FieldType2) => {
2090
- FieldType2[FieldType2["String"] = STRING] = "String";
2091
- FieldType2[FieldType2["Number"] = NUMBER] = "Number";
2092
- FieldType2[FieldType2["Date"] = DATE] = "Date";
2093
- FieldType2[FieldType2["Null"] = NULL] = "Null";
2094
- FieldType2[FieldType2["Boolean"] = BOOLEAN] = "Boolean";
2095
- return FieldType2;
2093
+ FieldType = /* @__PURE__ */ ((FieldType3) => {
2094
+ FieldType3[FieldType3["String"] = STRING] = "String";
2095
+ FieldType3[FieldType3["Number"] = NUMBER] = "Number";
2096
+ FieldType3[FieldType3["Date"] = DATE] = "Date";
2097
+ FieldType3[FieldType3["Null"] = NULL] = "Null";
2098
+ FieldType3[FieldType3["Boolean"] = BOOLEAN] = "Boolean";
2099
+ return FieldType3;
2096
2100
  })(FieldType || {});
2097
2101
  InternalFilterType = /* @__PURE__ */ ((InternalFilterType2) => {
2098
2102
  InternalFilterType2["StringFilter"] = "string-filter";
@@ -2238,7 +2242,7 @@ var init_Filter = __esm({
2238
2242
  fieldType = FieldType.Boolean;
2239
2243
  operator = filter.operator;
2240
2244
  field = filter.field;
2241
- value = null;
2245
+ value = filter.value;
2242
2246
  table = filter.table;
2243
2247
  return {
2244
2248
  filterType,
@@ -2249,7 +2253,7 @@ var init_Filter = __esm({
2249
2253
  operator
2250
2254
  };
2251
2255
  case "date" /* Date */:
2252
- if (typeof filter.value === "object" && filter.value && "value" in filter.value && filter.value.unit) {
2256
+ if (typeof filter.value === "object" && filter.value && "unit" in filter.value) {
2253
2257
  if (filter.value.value && typeof filter.value.value !== "number" || typeof filter.value.unit !== "string") {
2254
2258
  throw new Error(
2255
2259
  `Invalid value for DateFilter, expected { value: number, unit: string }, got ${filter.value}`
@@ -2305,7 +2309,7 @@ var init_Filter = __esm({
2305
2309
  table = filter.table;
2306
2310
  } else {
2307
2311
  throw new Error(
2308
- `Invalid value for DateFilter, expected { value: number, unit: string }, { startDate: string, endDate: string}, or string, got ${filter.value}`
2312
+ `Invalid value for DateFilter, expected { value?: number, unit: string }, { startDate: string, endDate: string}, or string, got ${filter.value}`
2309
2313
  );
2310
2314
  }
2311
2315
  return {
@@ -15908,7 +15912,7 @@ var init_dataProcessing = __esm({
15908
15912
  } else {
15909
15913
  if (data.dateField && data.dateField.field) {
15910
15914
  const dateField = data.dateField.field.replaceAll('"', "");
15911
- const maxDate = new Date(
15915
+ const maxDate2 = new Date(
15912
15916
  data.rows.reduce((acc, row) => {
15913
15917
  const rowValue = row[dateField];
15914
15918
  if (rowValue) {
@@ -15917,7 +15921,7 @@ var init_dataProcessing = __esm({
15917
15921
  return Math.max(acc, 0);
15918
15922
  }, 0)
15919
15923
  );
15920
- const minDate = new Date(
15924
+ const minDate2 = new Date(
15921
15925
  data.rows.reduce((acc, row) => {
15922
15926
  const rowValue = row[dateField];
15923
15927
  if (rowValue) {
@@ -15927,16 +15931,16 @@ var init_dataProcessing = __esm({
15927
15931
  }, (/* @__PURE__ */ new Date("3022-01-01")).getTime())
15928
15932
  );
15929
15933
  const dateBucket = getDateBucketFromRange({
15930
- start: minDate,
15931
- end: maxDate
15934
+ start: minDate2,
15935
+ end: maxDate2
15932
15936
  });
15933
15937
  const minDateBucket = getDateString(
15934
- minDate.toISOString(),
15938
+ minDate2.toISOString(),
15935
15939
  void 0,
15936
15940
  dateBucket
15937
15941
  );
15938
15942
  const maxDateBucket = getDateString(
15939
- maxDate.toISOString(),
15943
+ maxDate2.toISOString(),
15940
15944
  void 0,
15941
15945
  dateBucket
15942
15946
  );
@@ -16053,8 +16057,9 @@ async function generatePivotWithSQL({
16053
16057
  getPivotRowCount = true,
16054
16058
  caller,
16055
16059
  getToken,
16056
- reportBuilderState
16060
+ reportBuilderState,
16057
16061
  // Add reportBuilderState parameter
16062
+ overwriteCache = false
16058
16063
  }) {
16059
16064
  const databaseType = client.databaseType || "postgresql";
16060
16065
  if (!pivot.aggregations?.length && pivot.aggregationType) {
@@ -16103,7 +16108,8 @@ async function generatePivotWithSQL({
16103
16108
  })),
16104
16109
  // Only pass dashboard filters in Dashboard context, not ReportBuilder
16105
16110
  tenants,
16106
- additionalProcessing
16111
+ additionalProcessing,
16112
+ overwriteCache
16107
16113
  },
16108
16114
  getToken
16109
16115
  });
@@ -16277,8 +16283,9 @@ async function generatePivotTable({
16277
16283
  additionalProcessing,
16278
16284
  caller,
16279
16285
  pivotQuery,
16280
- reportBuilderState
16286
+ reportBuilderState,
16281
16287
  // Add reportBuilderState parameter
16288
+ overwriteCache
16282
16289
  }) {
16283
16290
  try {
16284
16291
  if (report && client) {
@@ -16295,8 +16302,9 @@ async function generatePivotTable({
16295
16302
  additionalProcessing,
16296
16303
  caller,
16297
16304
  getToken,
16298
- reportBuilderState
16305
+ reportBuilderState,
16299
16306
  // Pass reportBuilderState
16307
+ overwriteCache
16300
16308
  });
16301
16309
  return pivotTable;
16302
16310
  }
@@ -18182,7 +18190,7 @@ var init_tableProcessing = __esm({
18182
18190
  try {
18183
18191
  let data;
18184
18192
  if (reportBuilderState) {
18185
- let { data: data2 } = await quillFetch({
18193
+ ({ data } = await quillFetch({
18186
18194
  client,
18187
18195
  task: "report-builder-counts",
18188
18196
  metadata: {
@@ -18196,9 +18204,9 @@ var init_tableProcessing = __esm({
18196
18204
  tenants
18197
18205
  },
18198
18206
  getToken
18199
- });
18207
+ }));
18200
18208
  } else {
18201
- let { data: data2 } = await quillFetch({
18209
+ ({ data } = await quillFetch({
18202
18210
  client,
18203
18211
  task: "query",
18204
18212
  metadata: {
@@ -18212,7 +18220,7 @@ var init_tableProcessing = __esm({
18212
18220
  },
18213
18221
  urlParameters: `caller=getCounts&task=query`,
18214
18222
  getToken
18215
- });
18223
+ }));
18216
18224
  }
18217
18225
  if (data.errorMessage) {
18218
18226
  return { filteredColumns: columns, exceededColumns: void 0 };
@@ -19202,9 +19210,9 @@ var init_dataFetcher = __esm({
19202
19210
  return { error: "Failed to fetch data" };
19203
19211
  }
19204
19212
  };
19205
- parseFetchResponse = async (client, task, response, getToken) => {
19213
+ parseFetchResponse = async (client, task, response, getToken, useInMemory = false) => {
19206
19214
  try {
19207
- if (response.status === "error" || response.data?.error) {
19215
+ if (response.status === "error" || response.data?.error && !useInMemory) {
19208
19216
  let errorPrefix = "Error: ";
19209
19217
  let errorMessage = "Failed to fetch report: " + (response.error || response.data?.error);
19210
19218
  if (task === "query" || task === "report-builder") {
@@ -19817,7 +19825,8 @@ async function cleanDashboardItem({
19817
19825
  additionalProcessing,
19818
19826
  customFields,
19819
19827
  skipPivotFetch,
19820
- tenants
19828
+ tenants,
19829
+ overwriteCache
19821
19830
  }) {
19822
19831
  if (!item) return defaultDashboardItem;
19823
19832
  if (!item.rows) {
@@ -19873,11 +19882,12 @@ async function cleanDashboardItem({
19873
19882
  page: DEFAULT_PAGINATION
19874
19883
  };
19875
19884
  if (item.pivot && skipPivotFetch && item.rows && item.fields) {
19885
+ const pivotSourceRows = item.rows.map((row) => ({ ...row }));
19876
19886
  const dateFilter = dashboardFilters?.find(
19877
19887
  (filter) => filter.filterType === "date_range"
19878
19888
  );
19879
19889
  pivotTable = processPivotData({
19880
- rows: item.rows,
19890
+ rows: pivotSourceRows,
19881
19891
  fields: item.fields,
19882
19892
  pivot: {
19883
19893
  ...item.pivot,
@@ -19921,7 +19931,8 @@ async function cleanDashboardItem({
19921
19931
  dateBucket,
19922
19932
  shouldPaginatePivotAsTable ? additionalProcessing : pivotChartProcessing,
19923
19933
  tenants,
19924
- customFields
19934
+ customFields,
19935
+ overwriteCache
19925
19936
  );
19926
19937
  }
19927
19938
  } catch (e) {
@@ -20026,7 +20037,7 @@ async function cleanDashboardItem({
20026
20037
  referenceLines: item.referenceLines
20027
20038
  };
20028
20039
  }
20029
- async function getPivotTable(report, dashboardFilters, dashboardName, getToken, client, eventTracking, dateBucketInitial, additionalProcessing, tenants, customFields) {
20040
+ async function getPivotTable(report, dashboardFilters, dashboardName, getToken, client, eventTracking, dateBucketInitial, additionalProcessing, tenants, customFields, overwriteCache) {
20030
20041
  if (!report) return void 0;
20031
20042
  const dateFilter = Object.values(dashboardFilters ?? {}).find(
20032
20043
  (filter) => filter.filterType === "date_range" || filter.operator === "BETWEEN"
@@ -20094,7 +20105,8 @@ async function getPivotTable(report, dashboardFilters, dashboardName, getToken,
20094
20105
  dashboardFilters,
20095
20106
  tenants,
20096
20107
  additionalProcessing,
20097
- getToken
20108
+ getToken,
20109
+ overwriteCache
20098
20110
  });
20099
20111
  return pivotTable;
20100
20112
  } catch (e) {
@@ -20342,7 +20354,8 @@ async function fetchReportRows({
20342
20354
  filters = [],
20343
20355
  getToken,
20344
20356
  abortSignal,
20345
- additionalProcessing
20357
+ additionalProcessing,
20358
+ overwriteCache = false
20346
20359
  }) {
20347
20360
  const fetchResp = await quillFetch({
20348
20361
  client,
@@ -20354,7 +20367,8 @@ async function fetchReportRows({
20354
20367
  filters: filters.map((filter) => ({ ...filter, options: void 0 })),
20355
20368
  useNewNodeSql: true,
20356
20369
  tenants,
20357
- additionalProcessing
20370
+ additionalProcessing,
20371
+ overwriteCache
20358
20372
  },
20359
20373
  abortSignal,
20360
20374
  getToken
@@ -20385,7 +20399,8 @@ async function fetchReport({
20385
20399
  abortSignal,
20386
20400
  getToken,
20387
20401
  eventTracking,
20388
- usePivotTask = false
20402
+ usePivotTask = false,
20403
+ overwriteCache = false
20389
20404
  }) {
20390
20405
  let reportInfo = void 0;
20391
20406
  let errorMessage = void 0;
@@ -20405,7 +20420,8 @@ async function fetchReport({
20405
20420
  rowsOnly,
20406
20421
  rowCountOnly,
20407
20422
  tenants,
20408
- flags
20423
+ flags,
20424
+ overwriteCache
20409
20425
  },
20410
20426
  abortSignal,
20411
20427
  getToken
@@ -20426,7 +20442,9 @@ async function fetchReport({
20426
20442
  getToken,
20427
20443
  eventTracking,
20428
20444
  tenants,
20429
- skipPivotFetch: usePivotTask
20445
+ // When not using pivot-template, avoid fallback pivot-template fetches.
20446
+ skipPivotFetch: !usePivotTask,
20447
+ overwriteCache
20430
20448
  });
20431
20449
  } catch (error) {
20432
20450
  if (error instanceof Error && error.name === "AbortError") {
@@ -20476,7 +20494,8 @@ async function processReportResponse({
20476
20494
  getToken,
20477
20495
  eventTracking,
20478
20496
  tenants,
20479
- skipPivotFetch = false
20497
+ skipPivotFetch = false,
20498
+ overwriteCache
20480
20499
  }) {
20481
20500
  const shouldSkipPivotFetch = skipPivotFetch || !!resp?.pivotRows && !!resp?.fields;
20482
20501
  const dashboardItem = {
@@ -20509,7 +20528,8 @@ async function processReportResponse({
20509
20528
  getToken,
20510
20529
  tenants,
20511
20530
  eventTracking,
20512
- skipPivotFetch: shouldSkipPivotFetch
20531
+ skipPivotFetch: shouldSkipPivotFetch,
20532
+ overwriteCache
20513
20533
  });
20514
20534
  if (additionalProcessing) {
20515
20535
  reportInfo.pagination = additionalProcessing.page;
@@ -21586,6 +21606,1934 @@ async function getClientTenantIds({
21586
21606
 
21587
21607
  // src/Context.tsx
21588
21608
  init_columnProcessing();
21609
+
21610
+ // src/utils/cacheCab.ts
21611
+ init_dataFetcher();
21612
+
21613
+ // src/utils/inMemoryFilterEngine.ts
21614
+ init_Filter();
21615
+ var import_date_fns8 = require("date-fns");
21616
+ init_dateRangePickerUtils();
21617
+ var UNSET = Symbol("unset");
21618
+ var isFilter = (f) => {
21619
+ return f && typeof f === "object" && "filterType" in f && !("id" in f);
21620
+ };
21621
+ var isDashboardFilterUpdate = (f) => {
21622
+ return f && typeof f === "object" && "label" in f;
21623
+ };
21624
+ var normalizeFilterString = (value) => value.replaceAll("%", "").toLowerCase();
21625
+ var normalizeRowString = (value) => value.toLowerCase();
21626
+ var resolveFieldKeys = (field, table, resolver) => {
21627
+ if (!field) return [];
21628
+ const resolved = resolver?.(field, table);
21629
+ if (Array.isArray(resolved)) return resolved;
21630
+ if (typeof resolved === "string") return [resolved];
21631
+ return [field];
21632
+ };
21633
+ var getRowValue = (row, fieldKeys, cache) => {
21634
+ if (fieldKeys.length === 0) return void 0;
21635
+ const cacheKey = fieldKeys.join("|");
21636
+ if (Object.prototype.hasOwnProperty.call(cache, cacheKey)) {
21637
+ return cache[cacheKey];
21638
+ }
21639
+ for (const key of fieldKeys) {
21640
+ if (Object.prototype.hasOwnProperty.call(row, key)) {
21641
+ cache[cacheKey] = row[key];
21642
+ return row[key];
21643
+ }
21644
+ }
21645
+ cache[cacheKey] = void 0;
21646
+ return void 0;
21647
+ };
21648
+ var coerceNumber = (value) => {
21649
+ if (typeof value === "number" && Number.isFinite(value)) return value;
21650
+ if (typeof value === "string" && value !== "") {
21651
+ const parsed = Number(value);
21652
+ return Number.isFinite(parsed) ? parsed : null;
21653
+ }
21654
+ return null;
21655
+ };
21656
+ var coerceBoolean = (value) => {
21657
+ if (typeof value === "boolean") return value;
21658
+ if (typeof value === "number") return value !== 0;
21659
+ if (typeof value === "string") {
21660
+ const normalized = value.toLowerCase();
21661
+ if (normalized === "true" || normalized === "1") return true;
21662
+ if (normalized === "false" || normalized === "0") return false;
21663
+ }
21664
+ return null;
21665
+ };
21666
+ var toDateMs = (value) => {
21667
+ if (value instanceof Date) {
21668
+ return (0, import_date_fns8.isValid)(value) ? value.getTime() : null;
21669
+ }
21670
+ if (typeof value === "number") {
21671
+ const date = new Date(value);
21672
+ return (0, import_date_fns8.isValid)(date) ? date.getTime() : null;
21673
+ }
21674
+ if (typeof value === "string") {
21675
+ const date = new Date(value);
21676
+ return (0, import_date_fns8.isValid)(date) ? date.getTime() : null;
21677
+ }
21678
+ return null;
21679
+ };
21680
+ var buildColumnTypeMap = (columns) => {
21681
+ if (!columns?.length) return null;
21682
+ const map = /* @__PURE__ */ Object.create(null);
21683
+ for (const column of columns) {
21684
+ map[column.field] = column.jsType;
21685
+ }
21686
+ return map;
21687
+ };
21688
+ var resolveDashboardField = (filter, options) => {
21689
+ const mapped = options.filterMap?.[filter.label];
21690
+ if (mapped?.field) {
21691
+ return { field: mapped.field, table: mapped.table };
21692
+ }
21693
+ if (filter.filterType === "date_range" /* Date */ && options.dateField?.field) {
21694
+ return { field: options.dateField.field, table: options.dateField.table };
21695
+ }
21696
+ if (filter.field) {
21697
+ return { field: filter.field, table: filter.table };
21698
+ }
21699
+ return null;
21700
+ };
21701
+ var buildRelativeDateRange = (operator, value, now2, weekStartsOn) => {
21702
+ const amount = value.value || 1;
21703
+ const unit = value.unit;
21704
+ const currentStart = (() => {
21705
+ switch (unit) {
21706
+ case TimeUnit.Hour:
21707
+ return (0, import_date_fns8.startOfHour)(now2);
21708
+ case TimeUnit.Day:
21709
+ return (0, import_date_fns8.startOfDay)(now2);
21710
+ case TimeUnit.Week:
21711
+ return (0, import_date_fns8.startOfWeek)(now2, { weekStartsOn });
21712
+ case TimeUnit.Month:
21713
+ return (0, import_date_fns8.startOfMonth)(now2);
21714
+ case TimeUnit.Quarter:
21715
+ return (0, import_date_fns8.startOfQuarter)(now2);
21716
+ case TimeUnit.Year:
21717
+ return (0, import_date_fns8.startOfYear)(now2);
21718
+ default:
21719
+ return (0, import_date_fns8.startOfDay)(now2);
21720
+ }
21721
+ })();
21722
+ const addUnit = (base, multiplier) => {
21723
+ switch (unit) {
21724
+ case TimeUnit.Hour:
21725
+ return (0, import_date_fns8.addHours)(base, multiplier);
21726
+ case TimeUnit.Day:
21727
+ return (0, import_date_fns8.addDays)(base, multiplier);
21728
+ case TimeUnit.Week:
21729
+ return (0, import_date_fns8.addWeeks)(base, multiplier);
21730
+ case TimeUnit.Month:
21731
+ return (0, import_date_fns8.addMonths)(base, multiplier);
21732
+ case TimeUnit.Quarter:
21733
+ return (0, import_date_fns8.addMonths)(base, multiplier * 3);
21734
+ case TimeUnit.Year:
21735
+ return (0, import_date_fns8.addYears)(base, multiplier);
21736
+ default:
21737
+ return (0, import_date_fns8.addDays)(base, multiplier);
21738
+ }
21739
+ };
21740
+ const subUnit = (base, multiplier) => {
21741
+ switch (unit) {
21742
+ case TimeUnit.Hour:
21743
+ return (0, import_date_fns8.subHours)(base, multiplier);
21744
+ case TimeUnit.Day:
21745
+ return (0, import_date_fns8.subDays)(base, multiplier);
21746
+ case TimeUnit.Week:
21747
+ return (0, import_date_fns8.subWeeks)(base, multiplier);
21748
+ case TimeUnit.Month:
21749
+ return (0, import_date_fns8.subMonths)(base, multiplier);
21750
+ case TimeUnit.Quarter:
21751
+ return (0, import_date_fns8.subMonths)(base, multiplier * 3);
21752
+ case TimeUnit.Year:
21753
+ return (0, import_date_fns8.subYears)(base, multiplier);
21754
+ default:
21755
+ return (0, import_date_fns8.subDays)(base, multiplier);
21756
+ }
21757
+ };
21758
+ switch (operator) {
21759
+ case DateOperator.InTheLast: {
21760
+ const start2 = subUnit(now2, amount);
21761
+ return { start: start2.getTime() };
21762
+ }
21763
+ case DateOperator.InThePrevious: {
21764
+ const start2 = subUnit(currentStart, amount);
21765
+ return { start: start2.getTime(), end: currentStart.getTime(), endExclusive: true };
21766
+ }
21767
+ case DateOperator.InTheCurrent: {
21768
+ const end = addUnit(currentStart, 1);
21769
+ return {
21770
+ start: currentStart.getTime(),
21771
+ end: end.getTime(),
21772
+ endExclusive: true
21773
+ };
21774
+ }
21775
+ default:
21776
+ return null;
21777
+ }
21778
+ };
21779
+ var buildStringPredicate = (operator, value, fieldKeys) => {
21780
+ if (value === void 0 || value === null) return null;
21781
+ const normalizedValue = Array.isArray(value) ? value.map((v) => normalizeFilterString(String(v))) : normalizeFilterString(String(value));
21782
+ if (Array.isArray(normalizedValue)) {
21783
+ if (normalizedValue.length === 0) return null;
21784
+ } else if (normalizedValue === "") {
21785
+ return null;
21786
+ }
21787
+ if (operator === StringOperator.Is || operator === StringOperator.IsNot) {
21788
+ const values = Array.isArray(normalizedValue) ? normalizedValue : [normalizedValue];
21789
+ const valueSet = new Set(values);
21790
+ return (row, cache) => {
21791
+ const rowValue = getRowValue(row, fieldKeys, cache);
21792
+ if (rowValue === null || rowValue === void 0) return false;
21793
+ const normalized = normalizeRowString(String(rowValue));
21794
+ const hasValue = valueSet.has(normalized);
21795
+ return operator === StringOperator.Is ? hasValue : !hasValue;
21796
+ };
21797
+ }
21798
+ if (Array.isArray(normalizedValue)) {
21799
+ return null;
21800
+ }
21801
+ return (row, cache) => {
21802
+ const rowValue = getRowValue(row, fieldKeys, cache);
21803
+ if (rowValue === null || rowValue === void 0) return false;
21804
+ const normalizedRow = normalizeRowString(String(rowValue));
21805
+ switch (operator) {
21806
+ case StringOperator.IsExactly:
21807
+ return normalizedRow === normalizedValue;
21808
+ case StringOperator.IsNotExactly:
21809
+ return normalizedRow !== normalizedValue;
21810
+ case StringOperator.Contains:
21811
+ return normalizedRow.includes(normalizedValue);
21812
+ default:
21813
+ return false;
21814
+ }
21815
+ };
21816
+ };
21817
+ var buildNumericPredicate = (operator, value, fieldKeys) => {
21818
+ const filterValue = coerceNumber(value);
21819
+ if (filterValue === null) return null;
21820
+ return (row, cache) => {
21821
+ const rowValue = getRowValue(row, fieldKeys, cache);
21822
+ const parsed = coerceNumber(rowValue);
21823
+ if (parsed === null) return false;
21824
+ switch (operator) {
21825
+ case NumberOperator.EqualTo:
21826
+ return parsed === filterValue;
21827
+ case NumberOperator.NotEqualTo:
21828
+ return parsed !== filterValue;
21829
+ case NumberOperator.GreaterThan:
21830
+ return parsed > filterValue;
21831
+ case NumberOperator.LessThan:
21832
+ return parsed < filterValue;
21833
+ case NumberOperator.GreaterThanOrEqualTo:
21834
+ return parsed >= filterValue;
21835
+ case NumberOperator.LessThanOrEqualTo:
21836
+ return parsed <= filterValue;
21837
+ default:
21838
+ return false;
21839
+ }
21840
+ };
21841
+ };
21842
+ var buildNullPredicate = (operator, fieldKeys) => {
21843
+ return (row, cache) => {
21844
+ const rowValue = getRowValue(row, fieldKeys, cache);
21845
+ const isNullish = rowValue === null || rowValue === void 0;
21846
+ return operator === NullOperator.IsNull ? isNullish : !isNullish;
21847
+ };
21848
+ };
21849
+ var buildBooleanPredicate = (operator, value, fieldKeys) => {
21850
+ const filterValue = coerceBoolean(value);
21851
+ if (filterValue === null) return null;
21852
+ return (row, cache) => {
21853
+ const rowValue = getRowValue(row, fieldKeys, cache);
21854
+ const parsed = coerceBoolean(rowValue);
21855
+ if (parsed === null) return false;
21856
+ return operator === BoolOperator.EqualTo ? parsed === filterValue : parsed !== filterValue;
21857
+ };
21858
+ };
21859
+ var buildDateComparisonPredicate = (operator, value, fieldKeys) => {
21860
+ const filterDate = toDateMs(value);
21861
+ if (filterDate === null) return null;
21862
+ return (row, cache) => {
21863
+ const rowValue = getRowValue(row, fieldKeys, cache);
21864
+ const rowDate = toDateMs(rowValue);
21865
+ if (rowDate === null) return false;
21866
+ switch (operator) {
21867
+ case DateOperator.EqualTo:
21868
+ return rowDate === filterDate;
21869
+ case DateOperator.NotEqualTo:
21870
+ return rowDate !== filterDate;
21871
+ case DateOperator.GreaterThan:
21872
+ return rowDate > filterDate;
21873
+ case DateOperator.LessThan:
21874
+ return rowDate < filterDate;
21875
+ case DateOperator.GreaterThanOrEqualTo:
21876
+ return rowDate >= filterDate;
21877
+ case DateOperator.LessThanOrEqualTo:
21878
+ return rowDate <= filterDate;
21879
+ default:
21880
+ return false;
21881
+ }
21882
+ };
21883
+ };
21884
+ var buildDateRangePredicate = (range, fieldKeys) => {
21885
+ if (range.start === void 0 && range.end === void 0) return null;
21886
+ return (row, cache) => {
21887
+ const rowValue = getRowValue(row, fieldKeys, cache);
21888
+ const rowDate = toDateMs(rowValue);
21889
+ if (rowDate === null) return false;
21890
+ if (range.start !== void 0 && rowDate < range.start) return false;
21891
+ if (range.end !== void 0) {
21892
+ if (range.endExclusive) {
21893
+ return rowDate < range.end;
21894
+ }
21895
+ return rowDate <= range.end;
21896
+ }
21897
+ return true;
21898
+ };
21899
+ };
21900
+ var buildCustomDatePredicate = (value, fieldKeys) => {
21901
+ const start2 = value.startDate ? toDateMs(value.startDate) : null;
21902
+ const end = value.endDate ? toDateMs(value.endDate) : null;
21903
+ if (start2 === null && end === null) return null;
21904
+ return buildDateRangePredicate(
21905
+ {
21906
+ start: start2 ?? void 0,
21907
+ end: end ?? void 0,
21908
+ endExclusive: false
21909
+ },
21910
+ fieldKeys
21911
+ );
21912
+ };
21913
+ var compileCustomFilter = (filter, options) => {
21914
+ const fieldKeys = resolveFieldKeys(
21915
+ filter.field,
21916
+ filter.table,
21917
+ options.fieldKeyResolver
21918
+ );
21919
+ if (fieldKeys.length === 0) return null;
21920
+ switch (filter.filterType) {
21921
+ case "string-filter" /* StringFilter */:
21922
+ case "string-in-filter" /* StringInFilter */:
21923
+ return buildStringPredicate(
21924
+ filter.operator,
21925
+ filter.value,
21926
+ fieldKeys
21927
+ );
21928
+ case "numeric-filter" /* NumericFilter */:
21929
+ return buildNumericPredicate(
21930
+ filter.operator,
21931
+ filter.value,
21932
+ fieldKeys
21933
+ );
21934
+ case "null-filter" /* NullFilter */:
21935
+ return buildNullPredicate(
21936
+ filter.operator,
21937
+ fieldKeys
21938
+ );
21939
+ case "boolean-filter" /* BooleanFilter */:
21940
+ return buildBooleanPredicate(
21941
+ filter.operator,
21942
+ filter.value,
21943
+ fieldKeys
21944
+ );
21945
+ case "date-custom-filter" /* DateCustomFilter */:
21946
+ return buildCustomDatePredicate(
21947
+ filter.value,
21948
+ fieldKeys
21949
+ );
21950
+ case "date-comparison-filter" /* DateComparisonFilter */:
21951
+ return buildDateComparisonPredicate(
21952
+ filter.operator,
21953
+ filter.value,
21954
+ fieldKeys
21955
+ );
21956
+ case "date-filter" /* DateFilter */: {
21957
+ const value = filter.value;
21958
+ if (!value?.unit) return null;
21959
+ const now2 = options.now ?? /* @__PURE__ */ new Date();
21960
+ const range = buildRelativeDateRange(
21961
+ filter.operator,
21962
+ value,
21963
+ now2,
21964
+ options.weekStartsOn ?? 0
21965
+ );
21966
+ if (!range) return null;
21967
+ return buildDateRangePredicate(range, fieldKeys);
21968
+ }
21969
+ default:
21970
+ return null;
21971
+ }
21972
+ };
21973
+ var compileDashboardFilter = (filter, options, columnTypeMap) => {
21974
+ const resolved = resolveDashboardField(filter, options);
21975
+ if (!resolved?.field) return null;
21976
+ const fieldKeys = resolveFieldKeys(
21977
+ resolved.field,
21978
+ resolved.table,
21979
+ options.fieldKeyResolver
21980
+ );
21981
+ if (fieldKeys.length === 0) return null;
21982
+ switch (filter.filterType) {
21983
+ case "string" /* String */: {
21984
+ const stringFilter = filter;
21985
+ if (stringFilter.stringFilterType === "multiselect" /* Multiselect */) {
21986
+ if (!stringFilter.values || stringFilter.values.length === 0) {
21987
+ return null;
21988
+ }
21989
+ return buildStringPredicate(
21990
+ StringOperator.Is,
21991
+ stringFilter.values,
21992
+ fieldKeys
21993
+ );
21994
+ }
21995
+ if (!stringFilter.selectedValue) return null;
21996
+ return buildStringPredicate(
21997
+ StringOperator.IsExactly,
21998
+ stringFilter.selectedValue,
21999
+ fieldKeys
22000
+ );
22001
+ }
22002
+ case "date_range" /* Date */: {
22003
+ const dateFilter = filter;
22004
+ const start2 = dateFilter.startDate ? dateFilter.startDate.getTime() : void 0;
22005
+ const end = dateFilter.endDate ? dateFilter.endDate.getTime() : void 0;
22006
+ return buildDateRangePredicate(
22007
+ { start: start2, end, endExclusive: false },
22008
+ fieldKeys
22009
+ );
22010
+ }
22011
+ case "tenant" /* Tenant */: {
22012
+ const tenantFilter = filter;
22013
+ if (!tenantFilter.values || tenantFilter.values.length === 0) {
22014
+ return null;
22015
+ }
22016
+ const normalizedValues = tenantFilter.values.map(
22017
+ (v) => normalizeFilterString(String(v))
22018
+ );
22019
+ const valueSet = new Set(normalizedValues);
22020
+ const fieldType = columnTypeMap?.[resolved.field] ?? "string";
22021
+ return (row, cache) => {
22022
+ const rowValue = getRowValue(row, fieldKeys, cache);
22023
+ if (rowValue === null || rowValue === void 0) return false;
22024
+ if (fieldType === "number") {
22025
+ const numeric = coerceNumber(rowValue);
22026
+ return numeric !== null && valueSet.has(normalizeFilterString(String(numeric)));
22027
+ }
22028
+ return valueSet.has(normalizeRowString(String(rowValue)));
22029
+ };
22030
+ }
22031
+ default:
22032
+ return null;
22033
+ }
22034
+ };
22035
+ var updateDashboardFilters = (filtersToUpdate, dashboardFilters) => {
22036
+ return dashboardFilters.map((filter) => {
22037
+ const update = filtersToUpdate.find((u) => u.label === filter.label);
22038
+ if (!update) return filter;
22039
+ if (filter.filterType === "string" /* String */) {
22040
+ if (filter.stringFilterType === "multiselect" /* Multiselect */) {
22041
+ return {
22042
+ ...filter,
22043
+ values: update.value
22044
+ };
22045
+ }
22046
+ return {
22047
+ ...filter,
22048
+ selectedValue: update.value
22049
+ };
22050
+ }
22051
+ if (filter.filterType === "date_range" /* Date */) {
22052
+ const presetOptions = convertPresetOptionsToSelectableList(
22053
+ filter.presetOptions ?? [],
22054
+ filter.defaultPresetRanges ?? []
22055
+ );
22056
+ const value = update.value;
22057
+ const preset = presetOptions.find((p) => {
22058
+ const pStart = p.startDate ? new Date(p.startDate).toISOString() : void 0;
22059
+ const vStart = value.startDate ? new Date(value.startDate).toISOString() : void 0;
22060
+ const pEnd = p.endDate ? new Date(p.endDate).toISOString() : void 0;
22061
+ const vEnd = value.endDate ? new Date(value.endDate).toISOString() : void 0;
22062
+ return pStart === vStart && pEnd === vEnd;
22063
+ });
22064
+ if (!preset) {
22065
+ return { ...filter, startDate: value.startDate, endDate: value.endDate };
22066
+ }
22067
+ return {
22068
+ ...filter,
22069
+ preset,
22070
+ startDate: value.startDate,
22071
+ endDate: value.endDate
22072
+ };
22073
+ }
22074
+ if (filter.filterType === "tenant" /* Tenant */) {
22075
+ const value = update.value;
22076
+ let values;
22077
+ if (Array.isArray(value)) {
22078
+ values = value;
22079
+ } else if (typeof value === "string") {
22080
+ values = [value];
22081
+ } else {
22082
+ values = [];
22083
+ }
22084
+ return {
22085
+ ...filter,
22086
+ values
22087
+ };
22088
+ }
22089
+ return filter;
22090
+ });
22091
+ };
22092
+ var compileApplyFiltersPredicate = (filters, options) => {
22093
+ const dashboardUpdates = filters.filter(isDashboardFilterUpdate);
22094
+ const customFilters = filters.filter(isFilter);
22095
+ const internalCustomFilters = customFilters.map((filter) => {
22096
+ try {
22097
+ return convertCustomFilter(filter);
22098
+ } catch (error) {
22099
+ return null;
22100
+ }
22101
+ }).concat(options.customFilters ?? []).filter((filter) => filter !== null && filter !== void 0);
22102
+ const dashboardFilters = options.dashboardFilters ?? [];
22103
+ const updatedDashboardFilters = dashboardUpdates.length ? updateDashboardFilters(dashboardUpdates, dashboardFilters) : dashboardFilters;
22104
+ const columnTypeMap = buildColumnTypeMap(options.columns);
22105
+ const predicates = [];
22106
+ for (const filter of internalCustomFilters) {
22107
+ const predicate = compileCustomFilter(filter, options);
22108
+ if (predicate) predicates.push(predicate);
22109
+ }
22110
+ for (const filter of updatedDashboardFilters) {
22111
+ const predicate = compileDashboardFilter(
22112
+ filter,
22113
+ options,
22114
+ columnTypeMap
22115
+ );
22116
+ if (predicate) predicates.push(predicate);
22117
+ }
22118
+ if (predicates.length === 0) {
22119
+ return { predicate: () => true, hasPredicates: false };
22120
+ }
22121
+ return {
22122
+ predicate: (row) => {
22123
+ const cache = /* @__PURE__ */ Object.create(null);
22124
+ for (const predicate of predicates) {
22125
+ if (!predicate(row, cache)) return false;
22126
+ }
22127
+ return true;
22128
+ },
22129
+ hasPredicates: true
22130
+ };
22131
+ };
22132
+ var applyFiltersInMemory = (rows, filters, options) => {
22133
+ if (!rows.length) return rows;
22134
+ const { predicate, hasPredicates } = compileApplyFiltersPredicate(
22135
+ filters,
22136
+ options
22137
+ );
22138
+ if (!hasPredicates) return rows;
22139
+ const results = [];
22140
+ for (let i = 0; i < rows.length; i += 1) {
22141
+ const row = rows[i];
22142
+ if (predicate(row)) {
22143
+ results.push(row);
22144
+ }
22145
+ }
22146
+ return results;
22147
+ };
22148
+
22149
+ // src/utils/inMemoryPivotEngine.ts
22150
+ init_columnType();
22151
+ var MS_IN_DAY = 24 * 60 * 60 * 1e3;
22152
+ var MONTHS = [
22153
+ "Jan",
22154
+ "Feb",
22155
+ "Mar",
22156
+ "Apr",
22157
+ "May",
22158
+ "Jun",
22159
+ "Jul",
22160
+ "Aug",
22161
+ "Sep",
22162
+ "Oct",
22163
+ "Nov",
22164
+ "Dec"
22165
+ ];
22166
+ var INVALID_DATE_KEY = "-2026";
22167
+ function singleAggToMultiAgg(pivot) {
22168
+ if (pivot.aggregations) {
22169
+ return pivot;
22170
+ }
22171
+ const newPivot = { ...pivot };
22172
+ const newAgg = { aggregationType: pivot.aggregationType };
22173
+ const fieldsToCopy = [
22174
+ "valueField",
22175
+ "valueFieldType",
22176
+ "valueField2",
22177
+ "valueField2Type"
22178
+ ];
22179
+ fieldsToCopy.forEach((x) => {
22180
+ if (pivot[x]) {
22181
+ newAgg[x] = pivot[x];
22182
+ delete newPivot[x];
22183
+ }
22184
+ });
22185
+ newPivot.aggregations = [newAgg];
22186
+ return newPivot;
22187
+ }
22188
+ function isDateFormat2(xAxisFormat) {
22189
+ const DATE_FORMATS = [
22190
+ "yyyy",
22191
+ "mmm_dd",
22192
+ "mmm_yyyy",
22193
+ "mmm_dd_yyyy",
22194
+ "hh_ap_pm",
22195
+ "mmm_dd-mmm_dd",
22196
+ "mmm_dd_hh:mm_ap_pm",
22197
+ "wo, yyyy"
22198
+ ];
22199
+ const isDate = DATE_FORMATS.includes(xAxisFormat.toLowerCase());
22200
+ return isDate;
22201
+ }
22202
+ function isValidDate3(date) {
22203
+ try {
22204
+ return date instanceof Date && !isNaN(date.getTime());
22205
+ } catch {
22206
+ return false;
22207
+ }
22208
+ }
22209
+ function isDateLikeType(fieldType) {
22210
+ if (!fieldType) {
22211
+ return false;
22212
+ }
22213
+ return isDateType(fieldType) || isDateFormat2(fieldType);
22214
+ }
22215
+ function inferDateBucketFromFilters(pivot, filters) {
22216
+ if (!pivot?.rowFieldType) {
22217
+ return void 0;
22218
+ }
22219
+ const normalizedType = String(pivot.rowFieldType).toLowerCase();
22220
+ if (isDateFormat2(normalizedType)) {
22221
+ switch (normalizedType) {
22222
+ case "mmm_dd-mmm_dd":
22223
+ return "week";
22224
+ case "yyyy":
22225
+ case "wo, yyyy":
22226
+ return "year";
22227
+ case "mmm_dd_hh:mm_ap_pm":
22228
+ case "hh_ap_pm":
22229
+ return "day";
22230
+ default:
22231
+ return "month";
22232
+ }
22233
+ }
22234
+ const isDateLike = isDateType(normalizedType);
22235
+ if (!isDateLike) {
22236
+ return void 0;
22237
+ }
22238
+ let earliest;
22239
+ let latest;
22240
+ const extractTimestamp = (value) => {
22241
+ if (!value) {
22242
+ return void 0;
22243
+ }
22244
+ if (value instanceof Date) {
22245
+ return value.getTime();
22246
+ }
22247
+ const parsed = Date.parse(value);
22248
+ return Number.isNaN(parsed) ? void 0 : parsed;
22249
+ };
22250
+ (Array.isArray(filters) ? filters : []).forEach((filter) => {
22251
+ if (!filter || filter.filterType !== "date_range") {
22252
+ return;
22253
+ }
22254
+ if (filter.primaryRange?.value === "ALL_TIME") {
22255
+ earliest = -1;
22256
+ return;
22257
+ }
22258
+ const possibleStarts = [
22259
+ filter.startDate,
22260
+ filter.start,
22261
+ filter.primaryRange?.startDate,
22262
+ filter.primaryRange?.start,
22263
+ filter.range?.startDate,
22264
+ filter.range?.start
22265
+ ];
22266
+ const possibleEnds = [
22267
+ filter.endDate,
22268
+ filter.end,
22269
+ filter.primaryRange?.endDate,
22270
+ filter.primaryRange?.end,
22271
+ filter.range?.endDate,
22272
+ filter.range?.end
22273
+ ];
22274
+ possibleStarts.forEach((value) => {
22275
+ const timestamp = extractTimestamp(value);
22276
+ if (timestamp === void 0) {
22277
+ return;
22278
+ }
22279
+ earliest = earliest === void 0 ? timestamp : Math.min(earliest, timestamp);
22280
+ });
22281
+ possibleEnds.forEach((value) => {
22282
+ const timestamp = extractTimestamp(value);
22283
+ if (timestamp === void 0) {
22284
+ return;
22285
+ }
22286
+ latest = latest === void 0 ? timestamp : Math.max(latest, timestamp);
22287
+ });
22288
+ });
22289
+ if (earliest === -1) {
22290
+ return "month";
22291
+ }
22292
+ if (earliest === void 0 || latest === void 0) {
22293
+ return void 0;
22294
+ }
22295
+ if (latest < earliest) {
22296
+ [earliest, latest] = [latest, earliest];
22297
+ }
22298
+ const diffInDays = Math.max(1, Math.round((latest - earliest) / MS_IN_DAY));
22299
+ if (diffInDays < 20) {
22300
+ return "day";
22301
+ }
22302
+ if (diffInDays <= 59) {
22303
+ return "week";
22304
+ }
22305
+ if (diffInDays <= 400) {
22306
+ return "month";
22307
+ }
22308
+ return "year";
22309
+ }
22310
+ function getDateRangeFromFilters(filters) {
22311
+ let earliest;
22312
+ let latest;
22313
+ const extractTimestamp = (value) => {
22314
+ if (!value) return void 0;
22315
+ if (value instanceof Date) return value.getTime();
22316
+ const parsed = Date.parse(value);
22317
+ return Number.isNaN(parsed) ? void 0 : parsed;
22318
+ };
22319
+ (Array.isArray(filters) ? filters : []).forEach((filter) => {
22320
+ if (!filter || filter.filterType !== "date_range") return;
22321
+ if (filter.primaryRange?.value === "ALL_TIME") return;
22322
+ const possibleStarts = [
22323
+ filter.startDate,
22324
+ filter.start,
22325
+ filter.primaryRange?.startDate,
22326
+ filter.primaryRange?.start,
22327
+ filter.range?.startDate,
22328
+ filter.range?.start
22329
+ ];
22330
+ const possibleEnds = [
22331
+ filter.endDate,
22332
+ filter.end,
22333
+ filter.primaryRange?.endDate,
22334
+ filter.primaryRange?.end,
22335
+ filter.range?.endDate,
22336
+ filter.range?.end
22337
+ ];
22338
+ possibleStarts.forEach((v) => {
22339
+ const ts = extractTimestamp(v);
22340
+ if (ts === void 0) return;
22341
+ earliest = earliest === void 0 ? ts : Math.min(earliest, ts);
22342
+ });
22343
+ possibleEnds.forEach((v) => {
22344
+ const ts = extractTimestamp(v);
22345
+ if (ts === void 0) return;
22346
+ latest = latest === void 0 ? ts : Math.max(latest, ts);
22347
+ });
22348
+ });
22349
+ if (earliest === void 0 || latest === void 0) return void 0;
22350
+ if (latest < earliest) [earliest, latest] = [latest, earliest];
22351
+ return {
22352
+ start: new Date(earliest),
22353
+ end: new Date(latest)
22354
+ };
22355
+ }
22356
+ function generateBucketKeys(start2, end, bucket) {
22357
+ const keys = /* @__PURE__ */ new Set();
22358
+ const startDay = new Date(
22359
+ Date.UTC(start2.getUTCFullYear(), start2.getUTCMonth(), start2.getUTCDate())
22360
+ );
22361
+ const endDay = new Date(
22362
+ Date.UTC(end.getUTCFullYear(), end.getUTCMonth(), end.getUTCDate())
22363
+ );
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(
22388
+ Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate())
22389
+ );
22390
+ end = new Date(start2);
22391
+ break;
22392
+ case "week":
22393
+ start2 = new Date(d);
22394
+ end = new Date(start2);
22395
+ end.setUTCDate(start2.getUTCDate() + 6);
22396
+ break;
22397
+ case "month":
22398
+ start2 = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 1));
22399
+ end = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth() + 1, 0));
22400
+ break;
22401
+ case "year":
22402
+ start2 = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
22403
+ end = new Date(Date.UTC(d.getUTCFullYear(), 11, 31));
22404
+ break;
22405
+ }
22406
+ return { start: start2, end };
22407
+ }
22408
+ function formatBucketNameFromRange(start2, end, bucket) {
22409
+ const pad = (n) => String(n).padStart(2, "0");
22410
+ switch (bucket) {
22411
+ case "day":
22412
+ return `${pad(start2.getUTCDate())} ${MONTHS[start2.getUTCMonth()]} ${start2.getUTCFullYear()}`;
22413
+ case "week": {
22414
+ const sameMonth = start2.getUTCMonth() === end.getUTCMonth();
22415
+ return sameMonth ? `${MONTHS[start2.getUTCMonth()]} ${start2.getUTCDate()} - ${end.getUTCDate()}` : `${MONTHS[start2.getUTCMonth()]} ${start2.getUTCDate()} - ${MONTHS[end.getUTCMonth()]} ${end.getUTCDate()}`;
22416
+ }
22417
+ case "month":
22418
+ return `${MONTHS[start2.getUTCMonth()]} ${start2.getUTCFullYear()}`;
22419
+ case "year":
22420
+ return `${start2.getUTCFullYear()}`;
22421
+ default:
22422
+ return "";
22423
+ }
22424
+ }
22425
+ function getDateKey(date, bucket) {
22426
+ if (!isValidDate3(date)) {
22427
+ return INVALID_DATE_KEY;
22428
+ }
22429
+ switch (bucket) {
22430
+ case "day":
22431
+ return Date.UTC(
22432
+ date.getUTCFullYear(),
22433
+ date.getUTCMonth(),
22434
+ date.getUTCDate()
22435
+ ).toString();
22436
+ case "week": {
22437
+ const day = date.getUTCDay();
22438
+ const mondayUtc = Date.UTC(
22439
+ date.getUTCFullYear(),
22440
+ date.getUTCMonth(),
22441
+ date.getUTCDate() - (day + 6) % 7
22442
+ );
22443
+ return mondayUtc.toString();
22444
+ }
22445
+ case "month":
22446
+ return Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1).toString();
22447
+ case "year":
22448
+ return Date.UTC(date.getUTCFullYear(), 0, 1).toString();
22449
+ default:
22450
+ return INVALID_DATE_KEY;
22451
+ }
22452
+ }
22453
+ function sortRows(rows, pivot) {
22454
+ try {
22455
+ const { sort, sortField, sortFieldType, sortDirection } = pivot;
22456
+ if (!sort || sortField == null || sortFieldType == null) return rows;
22457
+ const sortFunc = isBoolType(sortFieldType) ? (a, b) => a === b ? 0 : a ? 1 : -1 : isDateType(sortFieldType) ? (a, b) => {
22458
+ const ta = new Date(a), tb = new Date(b);
22459
+ const aValid = isValidDate3(ta), bValid = isValidDate3(tb);
22460
+ if (!aValid && !bValid) return 0;
22461
+ if (!aValid) return 1;
22462
+ if (!bValid) return -1;
22463
+ return ta.getTime() - tb.getTime();
22464
+ } : isStringType(sortFieldType) ? (a, b) => a.localeCompare(b) : (a, b) => a - b;
22465
+ const dir = sortDirection === "ASC" ? 1 : -1;
22466
+ rows.sort((a, b) => {
22467
+ const av = a[sortField], bv = b[sortField];
22468
+ if (av == null && bv == null) return 0;
22469
+ if (av == null) return 1;
22470
+ if (bv == null) return -1;
22471
+ return dir * sortFunc(av, bv);
22472
+ });
22473
+ } catch {
22474
+ console.warn(`Sorting failed.`);
22475
+ }
22476
+ return rows;
22477
+ }
22478
+ function assembleFinalRows(seen, rowKey, dateBucket, dateRange) {
22479
+ const allColumns = /* @__PURE__ */ new Set();
22480
+ Object.keys(seen).forEach((rowKeyVal) => {
22481
+ Object.keys(seen[rowKeyVal]).forEach((col) => {
22482
+ allColumns.add(col);
22483
+ });
22484
+ });
22485
+ const filled = {};
22486
+ Object.keys(seen).forEach((rowKeyVal) => {
22487
+ filled[rowKeyVal] = {};
22488
+ allColumns.forEach((col) => {
22489
+ const value = seen[rowKeyVal][col];
22490
+ filled[rowKeyVal][col] = value ?? 0;
22491
+ });
22492
+ });
22493
+ if (dateBucket) {
22494
+ return Object.keys(filled).sort((a, b) => parseInt(a) - parseInt(b)).map((k) => {
22495
+ if (k === INVALID_DATE_KEY) {
22496
+ return { [rowKey]: "Invalid Date", ...filled[k], __quillRawDate: 0 };
22497
+ }
22498
+ const { start: start2, end } = getBucketRange(k, dateBucket);
22499
+ let bucketStart = start2;
22500
+ let bucketEnd = end;
22501
+ if (dateRange) {
22502
+ if (bucketStart < dateRange.start) bucketStart = dateRange.start;
22503
+ if (bucketEnd > dateRange.end) bucketEnd = dateRange.end;
22504
+ }
22505
+ return {
22506
+ [rowKey]: formatBucketNameFromRange(
22507
+ bucketStart,
22508
+ bucketEnd,
22509
+ dateBucket
22510
+ ),
22511
+ ...filled[k],
22512
+ __quillRawDate: bucketStart.toISOString()
22513
+ };
22514
+ });
22515
+ }
22516
+ return Object.keys(filled).sort().map((k) => ({ [rowKey]: k, ...filled[k] }));
22517
+ }
22518
+ function anyToNum(valueFieldType, obj) {
22519
+ if (!valueFieldType) {
22520
+ if (typeof obj === "number") return obj;
22521
+ if (typeof obj === "string" && !isNaN(Number(obj))) return Number(obj);
22522
+ return obj ? 1 : 0;
22523
+ }
22524
+ if (isNumberType(valueFieldType)) {
22525
+ return Number(obj);
22526
+ }
22527
+ return obj ? 1 : 0;
22528
+ }
22529
+ function updateAccumulator(acc, row, agg, isOneRow) {
22530
+ const {
22531
+ aggregationType: type,
22532
+ valueField,
22533
+ valueFieldType,
22534
+ valueField2,
22535
+ valueField2Type
22536
+ } = agg;
22537
+ if (!valueField) return;
22538
+ const distinctDenom = !!valueField2 && !!valueField2Type && valueField !== valueField2;
22539
+ const rawVal = row[valueField];
22540
+ if (valueFieldType && (type === "min" || type === "max") && (isDateType(valueFieldType) || isDateFormat2(valueFieldType))) {
22541
+ const dateVal = rawVal instanceof Date ? rawVal : new Date(rawVal);
22542
+ if (!isNaN(dateVal.getTime())) {
22543
+ if (type === "min") {
22544
+ acc.minDate = acc.minDate && acc.minDate.getTime() < dateVal.getTime() ? acc.minDate : dateVal;
22545
+ } else {
22546
+ acc.maxDate = acc.maxDate && acc.maxDate.getTime() > dateVal.getTime() ? acc.maxDate : dateVal;
22547
+ }
22548
+ }
22549
+ return;
22550
+ }
22551
+ const numVal = anyToNum(valueFieldType, rawVal);
22552
+ switch (type) {
22553
+ case "sum":
22554
+ acc.sum += numVal;
22555
+ break;
22556
+ case "average":
22557
+ case "avg":
22558
+ acc.sum += numVal;
22559
+ acc.count++;
22560
+ break;
22561
+ case "min":
22562
+ acc.min = acc.hasVal ? Math.min(acc.min, numVal) : numVal;
22563
+ acc.hasVal = true;
22564
+ break;
22565
+ case "max":
22566
+ acc.max = acc.hasVal ? Math.max(acc.max, numVal) : numVal;
22567
+ acc.hasVal = true;
22568
+ break;
22569
+ case "count":
22570
+ acc.count += numVal;
22571
+ break;
22572
+ case "percentage":
22573
+ acc.sum += numVal;
22574
+ acc.count += distinctDenom ? anyToNum(valueField2Type, row[valueField2]) : isOneRow ? 1 : numVal;
22575
+ break;
22576
+ }
22577
+ }
22578
+ function getFinalAggregationValue({
22579
+ type,
22580
+ valueFieldType,
22581
+ valueField,
22582
+ acc,
22583
+ distinctDenom = false,
22584
+ sumOfDenom,
22585
+ rowsLength,
22586
+ totalRowsForCount
22587
+ }) {
22588
+ if (valueFieldType && (type === "min" || type === "max") && (isDateType(valueFieldType) || isDateFormat2(valueFieldType))) {
22589
+ const dateVal = type === "min" ? acc.minDate : acc.maxDate;
22590
+ return dateVal?.toISOString() ?? "";
22591
+ }
22592
+ if (type === "count") {
22593
+ return totalRowsForCount ?? acc.count;
22594
+ }
22595
+ let final;
22596
+ if (!valueField && type === "percentage" && rowsLength !== void 0) {
22597
+ final = rowsLength ? (totalRowsForCount ?? 0) / rowsLength : 0;
22598
+ } else {
22599
+ switch (type) {
22600
+ case "sum":
22601
+ final = acc.sum;
22602
+ break;
22603
+ case "average":
22604
+ case "avg":
22605
+ final = acc.count ? acc.sum / acc.count : 0;
22606
+ break;
22607
+ case "min":
22608
+ final = acc.hasVal ? acc.min : 0;
22609
+ break;
22610
+ case "max":
22611
+ final = acc.hasVal ? acc.max : 0;
22612
+ break;
22613
+ case "percentage": {
22614
+ const denom = distinctDenom ? acc.count : sumOfDenom ?? acc.count;
22615
+ final = denom ? acc.sum / denom : 0;
22616
+ break;
22617
+ }
22618
+ default:
22619
+ final = 0;
22620
+ }
22621
+ }
22622
+ return final;
22623
+ }
22624
+ function aggregateToOneRow(rows, aggregations) {
22625
+ const multiAgg = aggregations.length > 1;
22626
+ const resultArray = aggregations.map((agg) => {
22627
+ const { aggregationType: type, valueField, valueFieldType } = agg;
22628
+ if (!valueField) {
22629
+ if (type !== "count") {
22630
+ return {};
22631
+ }
22632
+ const finalKey2 = "count" + (multiAgg ? "_count" : "");
22633
+ return { [finalKey2]: rows.length };
22634
+ }
22635
+ const finalKey = `${valueField}` + (multiAgg ? `_${type}` : "");
22636
+ const acc = {
22637
+ sum: 0,
22638
+ count: 0,
22639
+ min: Infinity,
22640
+ max: -Infinity,
22641
+ hasVal: false,
22642
+ minDate: null,
22643
+ maxDate: null
22644
+ };
22645
+ for (const row of rows) {
22646
+ updateAccumulator(acc, row, agg, true);
22647
+ }
22648
+ return {
22649
+ [finalKey]: getFinalAggregationValue({
22650
+ type,
22651
+ valueFieldType,
22652
+ valueField,
22653
+ acc,
22654
+ totalRowsForCount: acc.count
22655
+ })
22656
+ };
22657
+ });
22658
+ return [
22659
+ resultArray.reduce((acc, current) => {
22660
+ return { ...acc, ...current };
22661
+ }, {})
22662
+ ];
22663
+ }
22664
+ function aggregateWithColumn(rows, colKey, aggregations, getGroupKey) {
22665
+ if (!colKey) return;
22666
+ const seen = {};
22667
+ const multiAgg = aggregations.length > 1;
22668
+ aggregations.forEach((agg) => {
22669
+ const {
22670
+ aggregationType: type,
22671
+ valueField,
22672
+ valueFieldType,
22673
+ valueField2,
22674
+ valueField2Type
22675
+ } = agg;
22676
+ if (!valueField) {
22677
+ if (type !== "count" && type !== "percentage") return;
22678
+ }
22679
+ const distinctDenom = !!valueField2 && !!valueField2Type && valueField !== valueField2;
22680
+ const accs = {};
22681
+ rows.forEach((row) => {
22682
+ const groupKey = getGroupKey(row);
22683
+ const colVal = row[colKey];
22684
+ if (!accs[groupKey]) accs[groupKey] = {};
22685
+ if (!accs[groupKey][colVal]) {
22686
+ accs[groupKey][colVal] = {
22687
+ sum: 0,
22688
+ count: 0,
22689
+ min: Infinity,
22690
+ max: -Infinity,
22691
+ hasVal: false,
22692
+ minDate: null,
22693
+ maxDate: null,
22694
+ totalRows: 0
22695
+ };
22696
+ }
22697
+ const acc = accs[groupKey][colVal];
22698
+ acc.totalRows += 1;
22699
+ if (!valueField) {
22700
+ if (type === "count") acc.count += 1;
22701
+ else if (type === "percentage") acc.sum += 1;
22702
+ return;
22703
+ }
22704
+ updateAccumulator(acc, row, agg, false);
22705
+ });
22706
+ Object.keys(accs).forEach((groupKey) => {
22707
+ if (!seen[groupKey]) seen[groupKey] = {};
22708
+ const sumOfDenom = Object.values(accs[groupKey]).reduce(
22709
+ (t, a) => t + a.count,
22710
+ 0
22711
+ );
22712
+ Object.keys(accs[groupKey]).forEach((colVal) => {
22713
+ const acc = accs[groupKey][colVal];
22714
+ const finalKey = `${colVal}` + (multiAgg ? `_${type}` : "");
22715
+ seen[groupKey][finalKey] = getFinalAggregationValue({
22716
+ type,
22717
+ valueFieldType,
22718
+ valueField,
22719
+ acc,
22720
+ distinctDenom,
22721
+ sumOfDenom,
22722
+ totalRowsForCount: acc.totalRows
22723
+ });
22724
+ });
22725
+ });
22726
+ });
22727
+ return seen;
22728
+ }
22729
+ function aggregateWithoutColumn(rows, aggregations, getGroupKey) {
22730
+ const seen = {};
22731
+ const multiAgg = aggregations.length > 1;
22732
+ aggregations.forEach((agg) => {
22733
+ const {
22734
+ aggregationType: type,
22735
+ valueField,
22736
+ valueFieldType,
22737
+ valueField2,
22738
+ valueField2Type
22739
+ } = agg;
22740
+ if (!valueField) {
22741
+ if (type !== "count" && type !== "percentage") return;
22742
+ }
22743
+ const finalKey = !valueField ? `${type}` + (multiAgg ? `_${type}` : "") : `${valueField}` + (multiAgg ? `_${type}` : "");
22744
+ const distinctDenom = !!valueField2 && !!valueField2Type && valueField !== valueField2;
22745
+ const accs = {};
22746
+ rows.forEach((row) => {
22747
+ const key = getGroupKey(row);
22748
+ if (!seen[key]) seen[key] = {};
22749
+ if (!accs[key]) {
22750
+ accs[key] = {
22751
+ sum: 0,
22752
+ count: 0,
22753
+ min: Infinity,
22754
+ max: -Infinity,
22755
+ hasVal: false,
22756
+ minDate: null,
22757
+ maxDate: null,
22758
+ totalRows: 0
22759
+ };
22760
+ }
22761
+ const acc = accs[key];
22762
+ acc.totalRows += 1;
22763
+ if (!valueField) {
22764
+ if (type === "count") acc.count += 1;
22765
+ else if (type === "percentage") acc.sum += 1;
22766
+ return;
22767
+ }
22768
+ updateAccumulator(acc, row, agg, false);
22769
+ });
22770
+ const sumOfDenom = Object.values(accs).reduce(
22771
+ (t, a) => t + a.count,
22772
+ 0
22773
+ );
22774
+ Object.keys(accs).forEach((k) => {
22775
+ const acc = accs[k];
22776
+ seen[k][finalKey] = getFinalAggregationValue({
22777
+ type,
22778
+ valueFieldType,
22779
+ valueField,
22780
+ acc,
22781
+ distinctDenom,
22782
+ sumOfDenom,
22783
+ rowsLength: rows.length,
22784
+ totalRowsForCount: acc.totalRows
22785
+ });
22786
+ });
22787
+ });
22788
+ return seen;
22789
+ }
22790
+ function applyPivotInMemory(rows, pivot, filters) {
22791
+ if (!pivot) return rows;
22792
+ const multiAggPivot = pivot.aggregations ? pivot : singleAggToMultiAgg(pivot);
22793
+ if (multiAggPivot.rowField === void 0) {
22794
+ return aggregateToOneRow(rows, multiAggPivot.aggregations);
22795
+ }
22796
+ const { rowField, columnField, aggregations } = multiAggPivot;
22797
+ const dateBucket = inferDateBucketFromFilters(pivot, filters);
22798
+ const dateRange = dateBucket ? getDateRangeFromFilters(filters) : void 0;
22799
+ const getRowKey = dateBucket ? (row) => {
22800
+ const rawValue = row[rowField];
22801
+ const rawDateFromQuill = row.__quillRawDate;
22802
+ const rawDateValue = rawDateFromQuill ?? (rawValue !== null && typeof rawValue === "object" && rawValue.value ? rawValue.value : rawValue);
22803
+ return getDateKey(new Date(rawDateValue), dateBucket);
22804
+ } : (row) => row[rowField];
22805
+ const aggregated = columnField ? aggregateWithColumn(rows, columnField, aggregations, getRowKey) : aggregateWithoutColumn(rows, aggregations, getRowKey);
22806
+ const aggregatedRows = aggregated || {};
22807
+ if (rows.length === 0 && dateBucket && dateRange && !columnField) {
22808
+ const bucketKeys = generateBucketKeys(
22809
+ dateRange.start,
22810
+ dateRange.end,
22811
+ dateBucket
22812
+ );
22813
+ const emptyAgg = buildEmptyAggRow(aggregations);
22814
+ bucketKeys.forEach((k) => {
22815
+ aggregatedRows[k] = { ...emptyAgg };
22816
+ });
22817
+ }
22818
+ const finalRows = assembleFinalRows(
22819
+ aggregatedRows,
22820
+ rowField,
22821
+ dateBucket,
22822
+ rows.length === 0 ? dateRange : void 0
22823
+ );
22824
+ const sorted = sortRows(finalRows, pivot);
22825
+ if (pivot.rowLimit) {
22826
+ return sorted.slice(0, pivot.rowLimit);
22827
+ }
22828
+ return sorted;
22829
+ }
22830
+ function generatePivotColumnsInMemory({
22831
+ pivot,
22832
+ pivotRows,
22833
+ sourceColumns
22834
+ }) {
22835
+ if (!pivot?.rowField || !Array.isArray(pivotRows) || pivotRows.length === 0) {
22836
+ return void 0;
22837
+ }
22838
+ const columns = Array.isArray(sourceColumns) ? sourceColumns : [];
22839
+ const rowSource = columns.find((col) => col.field === pivot.rowField);
22840
+ const isRowFieldDate = isDateLikeType(pivot.rowFieldType);
22841
+ const rowFieldType = rowSource?.fieldType ?? pivot.rowFieldType ?? "string";
22842
+ const rowColumn = {
22843
+ field: pivot.rowField,
22844
+ label: rowSource?.label ?? pivot.rowField,
22845
+ format: isRowFieldDate ? "string" : rowSource?.format ?? "string",
22846
+ fieldType: rowFieldType,
22847
+ dataTypeID: rowSource?.dataTypeID ?? 1043,
22848
+ jsType: rowSource?.jsType ?? (isRowFieldDate ? "date" : "string")
22849
+ };
22850
+ const valueFieldFormat = (valueField) => columns.find((col) => col.field === valueField)?.format ?? "whole_number";
22851
+ const multiAgg = (pivot.aggregations?.length ?? 0) > 1;
22852
+ const aggregationBySuffix = new Map(
22853
+ (pivot.aggregations ?? []).map((agg) => [
22854
+ multiAgg ? `_${agg.aggregationType}` : "",
22855
+ agg
22856
+ ])
22857
+ );
22858
+ const valueKeys = Array.from(
22859
+ new Set(
22860
+ pivotRows.flatMap(
22861
+ (row) => Object.keys(row).filter(
22862
+ (key) => key !== pivot.rowField && key !== "__quillRawDate"
22863
+ )
22864
+ )
22865
+ )
22866
+ );
22867
+ const valueColumns = valueKeys.map((key) => {
22868
+ const matchingSuffix = Array.from(aggregationBySuffix.keys()).find(
22869
+ (suffix) => suffix && key.endsWith(suffix)
22870
+ );
22871
+ const matchedAgg = matchingSuffix ? aggregationBySuffix.get(matchingSuffix) : pivot.aggregations?.[0];
22872
+ const format9 = matchedAgg?.aggregationType === "percentage" ? "percent" : valueFieldFormat(matchedAgg?.valueField);
22873
+ const valueFieldType = matchedAgg?.valueFieldType ?? "number";
22874
+ return {
22875
+ field: key,
22876
+ label: key,
22877
+ format: format9,
22878
+ fieldType: valueFieldType,
22879
+ jsType: valueFieldType,
22880
+ dataTypeID: columns.find((col) => col.field === matchedAgg?.valueField)?.dataTypeID ?? 20
22881
+ };
22882
+ });
22883
+ return [rowColumn, ...valueColumns];
22884
+ }
22885
+
22886
+ // src/utils/cacheCab.ts
22887
+ var import_idb = require("idb");
22888
+ var import_date_fns9 = require("date-fns");
22889
+ var import_date_fns_tz2 = require("date-fns-tz");
22890
+ var TZ = "America/Los_Angeles";
22891
+ var CacheCab = class {
22892
+ fetchedRange = {};
22893
+ cachedReportIds = [];
22894
+ uncacheableReportIDs = [];
22895
+ uncacheableInFlight = /* @__PURE__ */ new Map();
22896
+ storage;
22897
+ META_KEY = "cachecab:meta";
22898
+ DATA_PREFIX = "cachecab:data:";
22899
+ UNCACHEABLE_PREFIX = "cachecab:uncacheable:";
22900
+ constructor({
22901
+ storageType = "memory"
22902
+ } = {}) {
22903
+ this.storage = storageType !== "idb" ? new MemoryStorage() : new IdbStorage();
22904
+ void this.loadMetaFromStorage();
22905
+ }
22906
+ async get(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, pageSize, getToken, eventTracking, forceRefresh) {
22907
+ if (this.isCached(reportId, tenants) && !forceRefresh) {
22908
+ return this.getFromCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking);
22909
+ } else {
22910
+ return this.addToCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking, true, forceRefresh);
22911
+ }
22912
+ }
22913
+ isCached(reportId, tenants) {
22914
+ return this.cachedReportIds.includes(this.getCacheKey(reportId, tenants));
22915
+ }
22916
+ isCacheable(reportId) {
22917
+ return !this.uncacheableReportIDs.includes(reportId);
22918
+ }
22919
+ async getUncacheableResult(reportId, client, tenants, flags, filters, additionalProcessing, pivot) {
22920
+ const key = this.getUncacheableRequestKey(
22921
+ reportId,
22922
+ client,
22923
+ tenants,
22924
+ flags,
22925
+ filters,
22926
+ additionalProcessing,
22927
+ pivot
22928
+ );
22929
+ try {
22930
+ const raw = await this.storage.getItem(key);
22931
+ return raw ? JSON.parse(raw) : null;
22932
+ } catch {
22933
+ return null;
22934
+ }
22935
+ }
22936
+ async setUncacheableResult(reportId, client, tenants, flags, filters, additionalProcessing, pivot, report) {
22937
+ const key = this.getUncacheableRequestKey(
22938
+ reportId,
22939
+ client,
22940
+ tenants,
22941
+ flags,
22942
+ filters,
22943
+ additionalProcessing,
22944
+ pivot
22945
+ );
22946
+ try {
22947
+ await this.storage.setItem(key, JSON.stringify(report));
22948
+ } catch {
22949
+ }
22950
+ }
22951
+ async getOrFetchUncacheableResult(reportId, client, tenants, flags, filters, additionalProcessing, pivot, fetcher, forceRefresh = false) {
22952
+ const key = this.getUncacheableRequestKey(
22953
+ reportId,
22954
+ client,
22955
+ tenants,
22956
+ flags,
22957
+ filters,
22958
+ additionalProcessing,
22959
+ pivot
22960
+ );
22961
+ if (!forceRefresh) {
22962
+ const raw = await this.storage.getItem(key);
22963
+ if (raw) {
22964
+ return { report: JSON.parse(raw), fromCache: true };
22965
+ }
22966
+ const inFlight = this.uncacheableInFlight.get(key);
22967
+ if (inFlight) {
22968
+ const report = await inFlight;
22969
+ return { report, fromCache: false };
22970
+ }
22971
+ }
22972
+ const requestPromise = (async () => {
22973
+ const report = await fetcher();
22974
+ try {
22975
+ await this.storage.setItem(key, JSON.stringify(report));
22976
+ } catch {
22977
+ }
22978
+ return report;
22979
+ })();
22980
+ this.uncacheableInFlight.set(key, requestPromise);
22981
+ try {
22982
+ const report = await requestPromise;
22983
+ return { report, fromCache: false };
22984
+ } finally {
22985
+ this.uncacheableInFlight.delete(key);
22986
+ }
22987
+ }
22988
+ async addToCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking, firstTime = true, forceRefresh = false) {
22989
+ const tenantPart = tenants ? JSON.stringify(tenants) : "";
22990
+ const cacheKey = this.getCacheKey(reportId, tenants);
22991
+ if (firstTime) {
22992
+ const result = await this.fetchInitialReport({ reportId, client, tenants, flags, filters: dashboardFilters, getToken, eventTracking, forceRefresh });
22993
+ try {
22994
+ await this.storage.setItem(this.DATA_PREFIX + cacheKey, JSON.stringify(result));
22995
+ this.cachedReportIds.push(cacheKey);
22996
+ await this.persistMetaToStorage();
22997
+ } catch {
22998
+ console.warn(`Failed to cache report: ${reportId}. Cache full?`);
22999
+ }
23000
+ const newRows = await this.applyPivotsAndFilters(result, dashboardFilters, customFilters, pivot, tenantPart, false);
23001
+ if (pivot) {
23002
+ const pivotColumns = generatePivotColumnsInMemory({
23003
+ pivot,
23004
+ pivotRows: newRows,
23005
+ sourceColumns: result.columnInternal ?? result.columns
23006
+ });
23007
+ return { ...result, pivotRows: newRows, pivotColumns: pivotColumns ?? result.pivotColumns, pivotRowCount: newRows.length };
23008
+ }
23009
+ return { ...result, rows: newRows, rowCount: newRows.length };
23010
+ } else {
23011
+ const dbDateFilter = dashboardFilters.find((x) => x.filterType === "date_range");
23012
+ const { start: requestedStart, end: requestedEnd } = normalizePSTRanges(
23013
+ dbDateFilter?.startDate ? new Date(dbDateFilter.startDate) : void 0,
23014
+ dbDateFilter?.endDate ? new Date(dbDateFilter.endDate) : void 0
23015
+ );
23016
+ if (!requestedStart || !requestedEnd) {
23017
+ return this.addToCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking, true);
23018
+ }
23019
+ const currentRange = this.fetchedRange[cacheKey];
23020
+ const rangeStart = currentRange?.start ?? requestedStart;
23021
+ const rangeEnd = currentRange?.end ?? endOfDayPST(/* @__PURE__ */ new Date());
23022
+ const existingRaw = await this.storage.getItem(this.DATA_PREFIX + cacheKey);
23023
+ let existing;
23024
+ let fetchedRows = [];
23025
+ try {
23026
+ existing = JSON.parse(existingRaw);
23027
+ if (!existing.rows) {
23028
+ throw new Error("Invalid Cache!");
23029
+ }
23030
+ if (requestedStart < rangeStart) {
23031
+ const olderReport = await this.fetchReport({
23032
+ reportId,
23033
+ client,
23034
+ tenants,
23035
+ flags,
23036
+ filters: dashboardFilters,
23037
+ getToken,
23038
+ eventTracking,
23039
+ start: requestedStart,
23040
+ end: rangeStart
23041
+ });
23042
+ fetchedRows = olderReport.rows;
23043
+ }
23044
+ if (requestedEnd && requestedEnd > rangeEnd) {
23045
+ const newerReport = await this.fetchReport({
23046
+ reportId,
23047
+ client,
23048
+ tenants,
23049
+ flags,
23050
+ filters: dashboardFilters,
23051
+ getToken,
23052
+ eventTracking,
23053
+ start: rangeEnd,
23054
+ end: requestedEnd
23055
+ });
23056
+ fetchedRows = fetchedRows.concat(newerReport.rows);
23057
+ }
23058
+ } catch {
23059
+ existing = await this.fetchReport({
23060
+ reportId,
23061
+ client,
23062
+ tenants,
23063
+ flags,
23064
+ filters: dashboardFilters,
23065
+ getToken,
23066
+ eventTracking,
23067
+ start: requestedStart,
23068
+ end: requestedEnd
23069
+ });
23070
+ fetchedRows = existing.rows;
23071
+ }
23072
+ const dateKey = existing.dateField?.field ? removeQuotes(existing.dateField.field) : void 0;
23073
+ let mergedRows;
23074
+ if (dateKey) {
23075
+ const startMs = rangeStart.getTime();
23076
+ const endMs = rangeEnd.getTime();
23077
+ const boundaryRows = existing.rows.filter((row) => {
23078
+ const raw = row[dateKey];
23079
+ if (!raw) return false;
23080
+ const t = new Date(raw).getTime();
23081
+ return Math.abs(t - startMs) <= MS_IN_DAY || Math.abs(t - endMs) <= MS_IN_DAY;
23082
+ });
23083
+ const filteredNew = filterNewRows(boundaryRows, fetchedRows);
23084
+ mergedRows = filteredNew.concat(existing.rows);
23085
+ } else {
23086
+ mergedRows = filterNewRows(existing.rows, fetchedRows).concat(existing.rows);
23087
+ }
23088
+ const merged = { ...existing, rows: mergedRows };
23089
+ await this.storage.setItem(this.DATA_PREFIX + cacheKey, JSON.stringify(merged));
23090
+ await this.persistMetaToStorage();
23091
+ const newRows = await this.applyPivotsAndFilters(merged, dashboardFilters, customFilters, pivot, tenantPart, false);
23092
+ if (pivot) {
23093
+ const pivotColumns = generatePivotColumnsInMemory({
23094
+ pivot,
23095
+ pivotRows: newRows,
23096
+ sourceColumns: merged.columnInternal ?? merged.columns
23097
+ });
23098
+ return { ...merged, pivotRows: newRows, pivotColumns: pivotColumns ?? merged.pivotColumns, pivotRowCount: newRows.length };
23099
+ }
23100
+ return { ...merged, rows: newRows, rowCount: newRows.length };
23101
+ }
23102
+ }
23103
+ /**
23104
+ * Returns cache entry (or re-queries if stale)
23105
+ */
23106
+ async getFromCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking) {
23107
+ const dateRangeFilter = dashboardFilters.find((x) => x.filterType === "date_range");
23108
+ const cacheKey = this.getCacheKey(reportId, tenants);
23109
+ if (!dateRangeFilter || dateRangeFilter?.primaryRange.value === "ALL_TIME") {
23110
+ const fetchInfo = this.fetchedRange[cacheKey];
23111
+ if (!fetchInfo || fetchInfo?.allTimeDoneBefore === false || Date.now() - fetchInfo.fetchedAtUTCMS >= MS_IN_DAY) {
23112
+ return this.addToCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking, true);
23113
+ }
23114
+ } else if (this.weShouldReQuery(cacheKey, dateRangeFilter)) {
23115
+ return this.addToCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking, false);
23116
+ }
23117
+ try {
23118
+ const raw = await this.storage.getItem(this.DATA_PREFIX + cacheKey);
23119
+ if (!raw) {
23120
+ throw new Error("Invalid Cache!");
23121
+ }
23122
+ const parsed = JSON.parse(raw);
23123
+ const tenantPart = tenants ? JSON.stringify(tenants) : "";
23124
+ const newRows = await this.applyPivotsAndFilters(parsed, dashboardFilters, customFilters, pivot, tenantPart);
23125
+ if (pivot) {
23126
+ const pivotColumns = generatePivotColumnsInMemory({
23127
+ pivot,
23128
+ pivotRows: newRows,
23129
+ sourceColumns: parsed.columnInternal ?? parsed.columns
23130
+ });
23131
+ return { ...parsed, pivotRows: newRows, pivotColumns: pivotColumns ?? parsed.pivotColumns, pivotRowCount: newRows.length };
23132
+ }
23133
+ return { ...parsed, rows: newRows, rowCount: newRows.length };
23134
+ } catch (err) {
23135
+ console.error(err);
23136
+ return this.addToCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking, false);
23137
+ }
23138
+ }
23139
+ weShouldReQuery(cacheKey, dateRangeFilter) {
23140
+ const start2 = dateRangeFilter.startDate;
23141
+ const end = dateRangeFilter.endDate;
23142
+ const range = this.fetchedRange[cacheKey];
23143
+ const { start: s, end: e } = normalizePSTRanges(
23144
+ start2 ? new Date(start2) : void 0,
23145
+ end ? new Date(end) : void 0
23146
+ );
23147
+ return !range || !s || !e || range.start > s || range.end < e;
23148
+ }
23149
+ async fetchInitialReport({
23150
+ reportId,
23151
+ client,
23152
+ tenants,
23153
+ flags,
23154
+ filters = [],
23155
+ getToken,
23156
+ eventTracking,
23157
+ forceRefresh
23158
+ }) {
23159
+ let start2;
23160
+ let end;
23161
+ const dateRangeFilter = filters.find((x) => x.filterType === "date_range");
23162
+ const cacheRange = dateRangeFilter?.initialCacheDateRange;
23163
+ if (cacheRange && cacheRange.startDate) {
23164
+ const startDate = dateRangeFilter?.startDate;
23165
+ start2 = startDate ? (0, import_date_fns9.min)([new Date(cacheRange.startDate), new Date(startDate)]) : new Date(cacheRange.startDate);
23166
+ end = endOfDayPST(/* @__PURE__ */ new Date());
23167
+ } else {
23168
+ const endDate = dateRangeFilter?.endDate ? new Date(dateRangeFilter.endDate) : void 0;
23169
+ const currentDay = endOfDayPST(/* @__PURE__ */ new Date());
23170
+ end = endDate ? (0, import_date_fns9.max)([currentDay, endDate]) : currentDay;
23171
+ start2 = (0, import_date_fns9.subMonths)(end, 7);
23172
+ }
23173
+ const normalized = normalizePSTRanges(start2, end);
23174
+ return this.fetchReport({
23175
+ reportId,
23176
+ client,
23177
+ tenants,
23178
+ flags,
23179
+ filters,
23180
+ getToken,
23181
+ eventTracking,
23182
+ start: normalized.start,
23183
+ end: normalized.end,
23184
+ forceRefresh
23185
+ });
23186
+ }
23187
+ async fetchReport({
23188
+ reportId,
23189
+ client,
23190
+ tenants,
23191
+ flags,
23192
+ filters = [],
23193
+ getToken,
23194
+ eventTracking,
23195
+ start: start2,
23196
+ end,
23197
+ forceRefresh
23198
+ }) {
23199
+ let reportInfo = void 0;
23200
+ try {
23201
+ const { start: s, end: e } = normalizePSTRanges(start2, end);
23202
+ const adjusted = [...filters].map((x) => {
23203
+ return { ...x, options: void 0 };
23204
+ });
23205
+ const dateFilterIndex = adjusted.findIndex((x) => x.filterType === "date_range");
23206
+ const isAllTime = dateFilterIndex !== -1 && adjusted[dateFilterIndex].primaryRange?.value === "ALL_TIME";
23207
+ if (!isAllTime) {
23208
+ const dateFilter = adjusted[dateFilterIndex];
23209
+ if (dateFilter && dateFilter.startDate && dateFilter.endDate) {
23210
+ adjusted[dateFilterIndex] = { ...dateFilter, startDate: s, endDate: e };
23211
+ }
23212
+ }
23213
+ const fetchResp = await quillFetch({
23214
+ client,
23215
+ task: "report",
23216
+ metadata: {
23217
+ reportId,
23218
+ clientId: client.publicKey,
23219
+ databaseType: client.databaseType,
23220
+ filters: adjusted,
23221
+ additionalProcessing: { page: { rowsPerPage: 1e3, rowsPerRequest: 1e5 } },
23222
+ useNewNodeSql: true,
23223
+ tenants,
23224
+ flags,
23225
+ overwriteCache: forceRefresh ?? false
23226
+ },
23227
+ getToken
23228
+ });
23229
+ const resp = await parseFetchResponse(
23230
+ client,
23231
+ "report",
23232
+ fetchResp,
23233
+ getToken,
23234
+ true
23235
+ );
23236
+ reportInfo = await processReportResponse({
23237
+ resp,
23238
+ client,
23239
+ filters: adjusted,
23240
+ dateBucket: resp?.dateBucket,
23241
+ additionalProcessing: { page: { rowsPerPage: 1e3, rowsPerRequest: 1e5 } },
23242
+ getToken,
23243
+ eventTracking,
23244
+ tenants,
23245
+ // CacheCab fetches with task: 'report'; keep pivot processing local.
23246
+ skipPivotFetch: true,
23247
+ overwriteCache: forceRefresh ?? false
23248
+ });
23249
+ const dateField = reportInfo.dateField?.field;
23250
+ if (!isAllTime && dateField !== void 0 && reportInfo.rows.length > 0) {
23251
+ const cleanedDateField = removeQuotes(dateField);
23252
+ const missingDateFieldCount = reportInfo.rows.some((row) => row[cleanedDateField] === void 0);
23253
+ if (missingDateFieldCount) {
23254
+ this.uncacheableReportIDs.push(reportId);
23255
+ return EMPTY_INTERNAL_REPORT;
23256
+ }
23257
+ }
23258
+ const requiredFilterFields = adjusted.flatMap((x, idx) => {
23259
+ if (idx === dateFilterIndex || !x.values && !x.selectedValue) return [];
23260
+ return removeQuotes(x.field);
23261
+ });
23262
+ const missingFields = [...new Set(requiredFilterFields.filter(
23263
+ (field) => reportInfo?.rows.some((row) => row[field] === void 0)
23264
+ ))];
23265
+ if (missingFields.length > 0) {
23266
+ this.uncacheableReportIDs.push(reportId);
23267
+ return EMPTY_INTERNAL_REPORT;
23268
+ }
23269
+ const cacheKey = this.getCacheKey(reportId, tenants);
23270
+ if (isAllTime) {
23271
+ this.fetchedRange[cacheKey] = { start: /* @__PURE__ */ new Date(0), end: /* @__PURE__ */ new Date(), fetchedAtUTCMS: Date.now(), allTimeDoneBefore: true };
23272
+ } else {
23273
+ const existing = this.fetchedRange[cacheKey];
23274
+ if (existing && !forceRefresh) {
23275
+ const oldStart = existing.start;
23276
+ const oldEnd = existing.end;
23277
+ this.fetchedRange[cacheKey] = { start: getMinDate(oldStart, s), end: getMaxDate(oldEnd, e), fetchedAtUTCMS: Date.now(), allTimeDoneBefore: false };
23278
+ } else {
23279
+ this.fetchedRange[cacheKey] = { start: s, end: e, fetchedAtUTCMS: Date.now(), allTimeDoneBefore: false };
23280
+ }
23281
+ }
23282
+ } catch (error) {
23283
+ console.warn(error);
23284
+ if (error instanceof Error && error.name === "AbortError") {
23285
+ throw error;
23286
+ }
23287
+ }
23288
+ return reportInfo || EMPTY_INTERNAL_REPORT;
23289
+ }
23290
+ async applyPivotsAndFilters(report, dashboardFilters, customFilters, pivot, tenantPart, useCache = true) {
23291
+ const datasetVersion = this.fetchedRange[report.id + tenantPart]?.fetchedAtUTCMS ?? 0;
23292
+ const keyParts = [
23293
+ report.queryString,
23294
+ tenantPart,
23295
+ datasetVersion,
23296
+ // prevents fetching stale filters on hard refresh
23297
+ hashString(stableStringify(dashboardFilters)),
23298
+ hashString(stableStringify(customFilters))
23299
+ ];
23300
+ const queryKey = this.DATA_PREFIX + keyParts.join("|");
23301
+ let filtersApplied;
23302
+ if (useCache) {
23303
+ try {
23304
+ const cache = await this.storage.getItem(queryKey);
23305
+ if (cache) {
23306
+ filtersApplied = JSON.parse(cache);
23307
+ }
23308
+ } catch {
23309
+ }
23310
+ }
23311
+ if (!filtersApplied) {
23312
+ const dateIndex = dashboardFilters.findIndex((x) => x.filterType === "date_range");
23313
+ const requiredFilterFields = dashboardFilters.flatMap((x, idx) => {
23314
+ if (idx === dateIndex || !x.values?.length && !x.selectedValue) return [];
23315
+ return removeQuotes(x.field);
23316
+ });
23317
+ const missingFields = [...new Set(requiredFilterFields.filter(
23318
+ (field) => report.rows.some((row) => row[field] === void 0)
23319
+ ))];
23320
+ if (missingFields.length > 0) {
23321
+ this.uncacheableReportIDs.push(report.id);
23322
+ return [];
23323
+ }
23324
+ const dbDateFilter = dashboardFilters.find((x) => x.filterType === "date_range");
23325
+ if (report.dateField && dbDateFilter?.startDate) {
23326
+ const { start: startDate, end: endDate } = normalizePSTRanges(
23327
+ new Date(dbDateFilter.startDate),
23328
+ dbDateFilter.endDate ? new Date(dbDateFilter.endDate) : void 0
23329
+ );
23330
+ const fieldToUse = report.rows[0] && report.rows[0]["__quillRawDate"] ? "__quillRawDate" : removeQuotes(report.dateField.field);
23331
+ const dateFilteredRows = report.rows.filter((x) => {
23332
+ const rowDate = new Date(x[fieldToUse]);
23333
+ return startDate <= rowDate && (endDate ? rowDate <= endDate : true);
23334
+ });
23335
+ filtersApplied = applyFiltersInMemory(dateFilteredRows, [], { dashboardFilters, customFilters });
23336
+ } else {
23337
+ filtersApplied = applyFiltersInMemory(report.rows, [], { dashboardFilters, customFilters });
23338
+ }
23339
+ }
23340
+ try {
23341
+ await this.storage.setItem(queryKey, JSON.stringify(filtersApplied));
23342
+ } catch {
23343
+ }
23344
+ const withPivot = applyPivotInMemory(filtersApplied, pivot, dashboardFilters.concat(customFilters));
23345
+ return withPivot;
23346
+ }
23347
+ getCacheKey(reportId, tenants) {
23348
+ const tenantPart = tenants ? JSON.stringify(tenants) : "";
23349
+ return reportId + tenantPart;
23350
+ }
23351
+ getUncacheableRequestKey(reportId, client, tenants, flags, filters, additionalProcessing, pivot) {
23352
+ const canonicalFilters = filters.map(
23353
+ (f) => canonicalizeFilterForUncacheableKey(f)
23354
+ );
23355
+ const keyParts = [
23356
+ reportId,
23357
+ client.publicKey,
23358
+ client.databaseType,
23359
+ hashString(stableStringify(canonicalizeForKey(tenants ?? null))),
23360
+ hashString(stableStringify(canonicalizeForKey(flags ?? null))),
23361
+ hashString(stableStringify(canonicalFilters)),
23362
+ hashString(stableStringify(canonicalizeForKey(additionalProcessing ?? null))),
23363
+ hashString(stableStringify(canonicalizeForKey(pivot ?? null)))
23364
+ ];
23365
+ return this.UNCACHEABLE_PREFIX + keyParts.join("|");
23366
+ }
23367
+ async loadMetaFromStorage() {
23368
+ const meta = await this.storage.getItem(this.META_KEY);
23369
+ if (!meta) return;
23370
+ try {
23371
+ const parsed = JSON.parse(meta);
23372
+ this.cachedReportIds = parsed.cached || [];
23373
+ this.uncacheableReportIDs = parsed.cannotBeCached;
23374
+ this.fetchedRange = Object.fromEntries(
23375
+ Object.entries(parsed.fetchedRange || {}).map(([id2, range]) => [
23376
+ id2,
23377
+ { ...range, start: new Date(range.start), end: new Date(range.end) }
23378
+ ])
23379
+ );
23380
+ } catch (err) {
23381
+ console.warn("CacheCab: could not parse meta from storage", err);
23382
+ }
23383
+ }
23384
+ async persistMetaToStorage() {
23385
+ const serializableRange = Object.fromEntries(
23386
+ Object.entries(this.fetchedRange).map(([id2, range]) => [
23387
+ id2,
23388
+ { ...range }
23389
+ ])
23390
+ );
23391
+ await this.storage.setItem(
23392
+ this.META_KEY,
23393
+ JSON.stringify({
23394
+ fetchedRange: serializableRange,
23395
+ cached: this.cachedReportIds,
23396
+ cannotBeCached: this.uncacheableReportIDs
23397
+ })
23398
+ );
23399
+ }
23400
+ };
23401
+ var MemoryStorage = class {
23402
+ store = /* @__PURE__ */ new Map();
23403
+ async getItem(key) {
23404
+ return this.store.get(key) ?? null;
23405
+ }
23406
+ async setItem(key, value) {
23407
+ this.store.set(key, value);
23408
+ }
23409
+ };
23410
+ var DB_NAME = "quill.cachecab";
23411
+ var STORE_NAME = "kv";
23412
+ var IdbStorage = class {
23413
+ dbPromise;
23414
+ constructor() {
23415
+ this.dbPromise = (0, import_idb.openDB)(DB_NAME, 1, {
23416
+ upgrade(db) {
23417
+ if (!db.objectStoreNames.contains(STORE_NAME)) {
23418
+ db.createObjectStore(STORE_NAME);
23419
+ }
23420
+ }
23421
+ });
23422
+ }
23423
+ async getItem(key) {
23424
+ const db = await this.dbPromise;
23425
+ return await db.get(STORE_NAME, key) ?? null;
23426
+ }
23427
+ async setItem(key, value) {
23428
+ const db = await this.dbPromise;
23429
+ await db.put(STORE_NAME, value, key);
23430
+ }
23431
+ };
23432
+ function removeQuotes(str) {
23433
+ return str.replace(/^['"]|['"]$/g, "");
23434
+ }
23435
+ function getMaxDate(d1, d2) {
23436
+ return d1 > d2 ? d1 : d2;
23437
+ }
23438
+ function getMinDate(d1, d2) {
23439
+ return d2 > d1 ? d1 : d2;
23440
+ }
23441
+ function hashString(str) {
23442
+ let h1 = 3735928559 ^ 0, h2 = 1103547991 ^ 0;
23443
+ for (let i = 0, ch; i < str.length; i++) {
23444
+ ch = str.charCodeAt(i);
23445
+ h1 = Math.imul(h1 ^ ch, 2654435761);
23446
+ h2 = Math.imul(h2 ^ ch, 1597334677);
23447
+ }
23448
+ h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507) ^ Math.imul(h2 ^ h2 >>> 13, 3266489909);
23449
+ h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507) ^ Math.imul(h1 ^ h1 >>> 13, 3266489909);
23450
+ return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(36);
23451
+ }
23452
+ function filterNewRows(existingRows, newRows) {
23453
+ const seen = /* @__PURE__ */ new Set();
23454
+ const getKey = (row) => stableStringify(row);
23455
+ existingRows.forEach((row) => seen.add(getKey(row)));
23456
+ return newRows.filter((row) => {
23457
+ const k = getKey(row);
23458
+ if (seen.has(k)) return false;
23459
+ seen.add(k);
23460
+ return true;
23461
+ });
23462
+ }
23463
+ function stableStringify(obj) {
23464
+ if (obj === null || typeof obj !== "object") return JSON.stringify(obj);
23465
+ if (Array.isArray(obj)) {
23466
+ return `[${obj.map(stableStringify).join(",")}]`;
23467
+ }
23468
+ const keys = Object.keys(obj).sort();
23469
+ return `{${keys.map((k) => JSON.stringify(k) + ":" + stableStringify(obj[k])).join(",")}}`;
23470
+ }
23471
+ function canonicalizeForKey(value) {
23472
+ if (value instanceof Date) {
23473
+ return value.toISOString();
23474
+ }
23475
+ if (Array.isArray(value)) {
23476
+ return value.map(canonicalizeForKey);
23477
+ }
23478
+ if (value && typeof value === "object") {
23479
+ const out = {};
23480
+ Object.keys(value).sort().forEach((key) => {
23481
+ if (value[key] !== void 0) {
23482
+ out[key] = canonicalizeForKey(value[key]);
23483
+ }
23484
+ });
23485
+ return out;
23486
+ }
23487
+ return value;
23488
+ }
23489
+ function canonicalizeFilterForUncacheableKey(filter) {
23490
+ const raw = canonicalizeForKey(filter) ?? {};
23491
+ const base = {
23492
+ filterType: raw.filterType,
23493
+ field: raw.field,
23494
+ table: raw.table,
23495
+ label: raw.label,
23496
+ operator: raw.operator
23497
+ };
23498
+ if (raw.selectedValue !== void 0) {
23499
+ base.selectedValue = raw.selectedValue;
23500
+ }
23501
+ if (Array.isArray(raw.values)) {
23502
+ base.values = [...raw.values].map(canonicalizeForKey).sort();
23503
+ }
23504
+ if (raw.value !== void 0) {
23505
+ base.value = canonicalizeForKey(raw.value);
23506
+ }
23507
+ if (raw.filterType === "date_range") {
23508
+ const { start: start2, end } = normalizePSTRanges(
23509
+ raw.startDate ? new Date(raw.startDate) : void 0,
23510
+ raw.endDate ? new Date(raw.endDate) : void 0
23511
+ );
23512
+ base.startDate = start2 ? start2.toISOString() : void 0;
23513
+ base.endDate = end ? end.toISOString() : void 0;
23514
+ base.primaryRange = raw.primaryRange?.value;
23515
+ base.comparisonRange = raw.comparisonRange?.value;
23516
+ }
23517
+ return canonicalizeForKey(base);
23518
+ }
23519
+ function startOfDayPST(date) {
23520
+ const pstDate = (0, import_date_fns_tz2.utcToZonedTime)(date, TZ);
23521
+ const startPst = (0, import_date_fns9.startOfDay)(pstDate);
23522
+ return (0, import_date_fns_tz2.zonedTimeToUtc)(startPst, TZ);
23523
+ }
23524
+ function endOfDayPST(date) {
23525
+ const pstDate = (0, import_date_fns_tz2.utcToZonedTime)(date, TZ);
23526
+ const endPst = (0, import_date_fns9.endOfDay)(pstDate);
23527
+ return (0, import_date_fns_tz2.zonedTimeToUtc)(endPst, TZ);
23528
+ }
23529
+ function normalizePSTRanges(start2, end) {
23530
+ return {
23531
+ start: start2 ? startOfDayPST(start2) : void 0,
23532
+ end: end ? endOfDayPST(end) : void 0
23533
+ };
23534
+ }
23535
+
23536
+ // src/Context.tsx
21589
23537
  var import_jsx_runtime = require("react/jsx-runtime");
21590
23538
  var dummySetter = () => {
21591
23539
  };
@@ -21917,7 +23865,9 @@ var ReportFiltersContext = (0, import_react.createContext)({
21917
23865
  loadFiltersForReport: async () => {
21918
23866
  },
21919
23867
  abortLoadingFilters: () => {
21920
- }
23868
+ },
23869
+ customFiltersLoaded: false,
23870
+ setCustomFiltersLoaded: dummySetter
21921
23871
  });
21922
23872
  var customDashboardFiltersReducer = (state, action) => {
21923
23873
  switch (action.type) {
@@ -22007,6 +23957,9 @@ var FetchContext = (0, import_react.createContext)({
22007
23957
  var EventTrackingContext = (0, import_react.createContext)({
22008
23958
  eventTracking: null
22009
23959
  });
23960
+ var CacheCabContext = (0, import_react.createContext)({
23961
+ getCacheCab: (storageType) => new CacheCab({ storageType })
23962
+ });
22010
23963
  var ContextProvider = ({
22011
23964
  children,
22012
23965
  initialTheme,
@@ -22020,6 +23973,14 @@ var ContextProvider = ({
22020
23973
  getAuthorizationToken = async () => "",
22021
23974
  eventTracking = null
22022
23975
  }) => {
23976
+ const cacheCabByTypeRef = (0, import_react.useRef)({});
23977
+ const getCacheCab = (0, import_react.useCallback)((storageType) => {
23978
+ const key = storageType === "idb" ? "idb" : "memory";
23979
+ if (!cacheCabByTypeRef.current[key]) {
23980
+ cacheCabByTypeRef.current[key] = new CacheCab({ storageType: key });
23981
+ }
23982
+ return cacheCabByTypeRef.current[key];
23983
+ }, []);
22023
23984
  const [client, setClient] = (0, import_react.useState)(
22024
23985
  typeof window !== "undefined" && sessionStorage ? JSON.parse(sessionStorage.getItem("quill-client") ?? "null") : null
22025
23986
  );
@@ -22082,6 +24043,7 @@ var ContextProvider = ({
22082
24043
  reportFiltersReducer,
22083
24044
  {}
22084
24045
  );
24046
+ const [customFiltersLoaded, setCustomFiltersLoaded] = (0, import_react.useState)(false);
22085
24047
  const reportFilterOptionsAbortControllers = (0, import_react.useRef)({});
22086
24048
  const [reports, reportsDispatch] = (0, import_react.useReducer)(reportsReducer, {});
22087
24049
  const [reportsLoadingState, reportsLoadingStateDispatch] = (0, import_react.useReducer)(
@@ -22105,6 +24067,11 @@ var ContextProvider = ({
22105
24067
  const viewerTenantsByOwnerRequests = (0, import_react.useRef)(/* @__PURE__ */ new Set());
22106
24068
  const mappedTenantsRequests = (0, import_react.useRef)(/* @__PURE__ */ new Set());
22107
24069
  const loadDashboardProcessId = (0, import_react.useRef)({});
24070
+ (0, import_react.useEffect)(() => {
24071
+ if (Object.keys(customReportFilters).length) {
24072
+ setCustomFiltersLoaded(true);
24073
+ }
24074
+ }, [customReportFilters]);
22108
24075
  (0, import_react.useEffect)(() => {
22109
24076
  if (!theme) {
22110
24077
  const populatedTheme = {
@@ -22873,6 +24840,7 @@ var ContextProvider = ({
22873
24840
  ...dashboard2,
22874
24841
  dateFilter: dashboard2.dateFilter ? {
22875
24842
  ...dashboard2.dateFilter,
24843
+ initialCacheDateRange: dashboard2.initialCacheDateRange ? { ...dashboard2.initialCacheDateRange } : void 0,
22876
24844
  presetOptions: dashboard2.dateFilter.presetOptions?.map(
22877
24845
  (preset) => ({
22878
24846
  ...preset,
@@ -23215,6 +25183,7 @@ var ContextProvider = ({
23215
25183
  ...dashboard2,
23216
25184
  dateFilter: dashboard2.dateFilter ? {
23217
25185
  ...dashboard2.dateFilter,
25186
+ initialCacheDateRange: dashboard2.initialCacheDateRange ? { ...dashboard2.initialCacheDateRange } : void 0,
23218
25187
  presetOptions: dashboard2.dateFilter.presetOptions?.map(
23219
25188
  (preset) => ({
23220
25189
  ...preset,
@@ -23259,6 +25228,7 @@ var ContextProvider = ({
23259
25228
  if (dashboard2.dateFilter) {
23260
25229
  dashboard2.dateFilter = {
23261
25230
  ...dashboard2.dateFilter,
25231
+ initialCacheDateRange: dashboard2.initialCacheDateRange ? { ...dashboard2.initialCacheDateRange } : void 0,
23262
25232
  startDate: presetOptions.find(
23263
25233
  (elem) => elem.value === dashboard2.dateFilter.primaryRange?.value
23264
25234
  )?.startDate,
@@ -23527,7 +25497,7 @@ var ContextProvider = ({
23527
25497
  if (!theme) {
23528
25498
  return null;
23529
25499
  }
23530
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
25500
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CacheCabContext.Provider, { value: { getCacheCab }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
23531
25501
  ClientContext.Provider,
23532
25502
  {
23533
25503
  value: [
@@ -23568,7 +25538,9 @@ var ContextProvider = ({
23568
25538
  reportFiltersDispatch,
23569
25539
  customReportFiltersDispatch,
23570
25540
  loadFiltersForReport,
23571
- abortLoadingFilters
25541
+ abortLoadingFilters,
25542
+ customFiltersLoaded,
25543
+ setCustomFiltersLoaded
23572
25544
  },
23573
25545
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
23574
25546
  ReportsContext.Provider,
@@ -23611,7 +25583,7 @@ var ContextProvider = ({
23611
25583
  }
23612
25584
  ) })
23613
25585
  }
23614
- );
25586
+ ) });
23615
25587
  };
23616
25588
 
23617
25589
  // src/hooks/useExport.tsx
@@ -23627,9 +25599,9 @@ init_Filter();
23627
25599
  init_filterProcessing();
23628
25600
 
23629
25601
  // src/utils/merge.ts
23630
- var import_date_fns8 = require("date-fns");
25602
+ var import_date_fns10 = require("date-fns");
23631
25603
  init_columnType();
23632
- var import_date_fns_tz2 = require("date-fns-tz");
25604
+ var import_date_fns_tz3 = require("date-fns-tz");
23633
25605
  function mergeComparisonRange(resp) {
23634
25606
  if (resp.chartType === "table") return resp;
23635
25607
  const compRows = resp.compareRows;
@@ -23672,6 +25644,18 @@ var useDashboardInternal = (dashboardName, customFilters) => {
23672
25644
  (f) => f.filter
23673
25645
  ) : null;
23674
25646
  }, [dashboardFilters, dashboardName]);
25647
+ const getExistingDateFilter = (0, import_react2.useMemo)(
25648
+ () => (name2) => {
25649
+ if (!name2) return void 0;
25650
+ const filtersForDashboard = dashboardFilters[name2];
25651
+ if (!filtersForDashboard) return void 0;
25652
+ const entry = Object.values(filtersForDashboard).find(
25653
+ (f) => f?.filter?.filterType === "date_range" /* Date */
25654
+ );
25655
+ return entry?.filter;
25656
+ },
25657
+ [dashboardFilters]
25658
+ );
23675
25659
  (0, import_react2.useEffect)(() => {
23676
25660
  if (!dashboardName) return;
23677
25661
  if (backfilledDashboards.current.has(dashboardName)) return;
@@ -23709,7 +25693,7 @@ var useDashboardInternal = (dashboardName, customFilters) => {
23709
25693
  customFilters
23710
25694
  );
23711
25695
  }, [dashboardName, loading, dashboardConfig, dashboardFilters]);
23712
- const handleReload = async (overrideDashboardName, fetchFromServer = false, reportAction, overrideFilters) => {
25696
+ const handleReload = async (overrideDashboardName, fetchFromServer = false, reportAction, overrideFilters, options) => {
23713
25697
  if (!dashboardName) return;
23714
25698
  if (overrideFilters) {
23715
25699
  const dateFilter2 = overrideFilters.filters.find(
@@ -23762,10 +25746,19 @@ var useDashboardInternal = (dashboardName, customFilters) => {
23762
25746
  Object.values(dashboard?.sections ?? {}).flat(),
23763
25747
  dashboardName
23764
25748
  ) : void 0;
25749
+ const existingDateFilter = options?.preserveExistingDateFilter ? getExistingDateFilter(dashboardName) : void 0;
25750
+ const mergedDateFilter = dateFilter && existingDateFilter ? {
25751
+ ...dateFilter,
25752
+ startDate: existingDateFilter.startDate ?? dateFilter.startDate,
25753
+ endDate: existingDateFilter.endDate ?? dateFilter.endDate,
25754
+ preset: existingDateFilter.preset ?? dateFilter.preset,
25755
+ primaryRange: existingDateFilter.primaryRange ?? dateFilter.primaryRange,
25756
+ comparisonRange: existingDateFilter.comparisonRange ?? dateFilter.comparisonRange
25757
+ } : dateFilter ?? existingDateFilter;
23765
25758
  loadFiltersForDashboard(
23766
25759
  dashboardName,
23767
25760
  [
23768
- ...dateFilter ? [dateFilter] : [],
25761
+ ...mergedDateFilter ? [mergedDateFilter] : [],
23769
25762
  ...updatedDashboard?.filters?.map((filter) => ({
23770
25763
  ...filter,
23771
25764
  query: void 0
@@ -24050,7 +26043,8 @@ var useDashboards = () => {
24050
26043
  tenantFilters,
24051
26044
  dateFilter,
24052
26045
  customFilters,
24053
- tenantKeys
26046
+ tenantKeys,
26047
+ initialCacheDateRange
24054
26048
  }) => {
24055
26049
  if (!client) return;
24056
26050
  if (tenantKeys?.some((key) => !key)) {
@@ -24071,6 +26065,7 @@ var useDashboards = () => {
24071
26065
  filters,
24072
26066
  tenantFilters,
24073
26067
  dateFilter: dateFilter ?? null,
26068
+ initialCacheDateRange,
24074
26069
  name: name2.trim(),
24075
26070
  task: "edit-dashboard",
24076
26071
  clientId: client.clientId,
@@ -24282,7 +26277,9 @@ var useDashboards = () => {
24282
26277
  };
24283
26278
  };
24284
26279
  var useDashboard = (dashboardName, config) => {
26280
+ const logCacheStatistics = config?.showCacheLogs || false;
24285
26281
  const { data, dashboardFilters, reload, isLoading } = useDashboardInternal(dashboardName);
26282
+ const [lastUpdated, setLastUpdated] = (0, import_react2.useState)(0);
24286
26283
  const { customFilterDispatch } = (0, import_react2.useContext)(DashboardFiltersContext);
24287
26284
  const { reportsDispatch, reportsLoadingStateDispatch } = (0, import_react2.useContext)(ReportsContext);
24288
26285
  const initialLoad = (0, import_react2.useRef)(true);
@@ -24291,15 +26288,19 @@ var useDashboard = (dashboardName, config) => {
24291
26288
  const [client] = (0, import_react2.useContext)(ClientContext);
24292
26289
  const { tenants, flags } = (0, import_react2.useContext)(TenantContext);
24293
26290
  const { getToken } = (0, import_react2.useContext)(FetchContext);
24294
- const { customReportFilters } = (0, import_react2.useContext)(ReportFiltersContext);
26291
+ const { customReportFilters, customFiltersLoaded, setCustomFiltersLoaded } = (0, import_react2.useContext)(ReportFiltersContext);
24295
26292
  const { eventTracking } = (0, import_react2.useContext)(EventTrackingContext);
24296
- const customFiltersRef = (0, import_react2.useRef)(customReportFilters);
26293
+ const { getCacheCab } = (0, import_react2.useContext)(CacheCabContext);
24297
26294
  const reportRequestIds = (0, import_react2.useRef)({});
24298
26295
  const lastDashboardName = (0, import_react2.useRef)(null);
24299
26296
  const pendingNameChangeReload = (0, import_react2.useRef)(false);
24300
- (0, import_react2.useEffect)(() => {
24301
- customFiltersRef.current = customReportFilters;
24302
- }, [customReportFilters]);
26297
+ const [loadedDashes, setLoadedDashes] = (0, import_react2.useState)([]);
26298
+ const [lastUpdatedDict, setLastUpdatedDict] = (0, import_react2.useState)({});
26299
+ const cacheEnabled = config?.cacheEnabled ?? true;
26300
+ const cacheCab = (0, import_react2.useMemo)(
26301
+ () => getCacheCab(config?.cacheType),
26302
+ [getCacheCab, config?.cacheType]
26303
+ );
24303
26304
  (0, import_react2.useEffect)(() => {
24304
26305
  const nameChanged = dashboardName !== lastDashboardName.current;
24305
26306
  if (nameChanged) {
@@ -24309,19 +26310,29 @@ var useDashboard = (dashboardName, config) => {
24309
26310
  backfilledDashboards.current = false;
24310
26311
  reportRequestIds.current = {};
24311
26312
  pendingNameChangeReload.current = true;
26313
+ if (customFiltersLoaded && !loadedDashes.includes(dashboardName)) {
26314
+ setCustomFiltersLoaded(false);
26315
+ }
26316
+ setLastUpdated(lastUpdatedDict[dashboardName] || 0);
24312
26317
  }
24313
26318
  if (!dashboardName) return;
24314
26319
  if (pendingNameChangeReload.current && data) {
24315
26320
  pendingNameChangeReload.current = false;
24316
26321
  initialLoad.current = false;
26322
+ fetchedInitialReports.current = false;
24317
26323
  return;
24318
26324
  }
24319
26325
  if (pendingNameChangeReload.current && !isLoading && !data && initialLoad.current) {
24320
26326
  initialLoad.current = false;
24321
26327
  pendingNameChangeReload.current = false;
24322
- reload();
26328
+ reload(dashboardName, false, void 0, void 0, {
26329
+ preserveExistingDateFilter: true
26330
+ });
24323
26331
  }
24324
26332
  }, [isLoading, data, dashboardName]);
26333
+ (0, import_react2.useEffect)(() => {
26334
+ setLastUpdated(lastUpdatedDict[dashboardName] || 0);
26335
+ }, [lastUpdatedDict, dashboardName]);
24325
26336
  (0, import_react2.useEffect)(() => {
24326
26337
  if (backfilledDashboards.current) return;
24327
26338
  if (isLoading || !data) return;
@@ -24330,7 +26341,9 @@ var useDashboard = (dashboardName, config) => {
24330
26341
  );
24331
26342
  if (!needsBackfill) return;
24332
26343
  backfilledDashboards.current = true;
24333
- reload(dashboardName, false);
26344
+ reload(dashboardName, false, void 0, void 0, {
26345
+ preserveExistingDateFilter: true
26346
+ });
24334
26347
  }, [isLoading, data, dashboardFilters, dashboardName]);
24335
26348
  const { allReportsById } = useAllReports();
24336
26349
  const sections = (0, import_react2.useMemo)(() => {
@@ -24354,7 +26367,13 @@ var useDashboard = (dashboardName, config) => {
24354
26367
  fetchedInitialReports.current = true;
24355
26368
  fetchReports([], dashboardFilters ?? [], config?.pageSize);
24356
26369
  }
24357
- }, [fetchedInitialReports, data, dashboardFilters]);
26370
+ }, [fetchedInitialReports, data, dashboardFilters, customFiltersLoaded]);
26371
+ (0, import_react2.useEffect)(() => {
26372
+ if (customFiltersLoaded && data && dashboardFilters !== null) {
26373
+ fetchedInitialReports.current = true;
26374
+ fetchReports([], dashboardFilters ?? [], config?.pageSize);
26375
+ }
26376
+ }, [customFiltersLoaded]);
24358
26377
  const applyDashboardFilters = (filtersToUpdate) => {
24359
26378
  const newFilters = (dashboardFilters ?? []).map((f) => {
24360
26379
  const update = filtersToUpdate.find((u) => u.label === f.label);
@@ -24433,7 +26452,7 @@ var useDashboard = (dashboardName, config) => {
24433
26452
  });
24434
26453
  return internalFilters;
24435
26454
  };
24436
- const isFilter = (f) => {
26455
+ const isFilter2 = (f) => {
24437
26456
  return f && typeof f === "object" && "filterType" in f && !("id" in f);
24438
26457
  };
24439
26458
  const isDashboardFilter = (f) => {
@@ -24441,29 +26460,22 @@ var useDashboard = (dashboardName, config) => {
24441
26460
  };
24442
26461
  const applyFilters = (filters2) => {
24443
26462
  const dashboardFilters2 = filters2.filter(isDashboardFilter);
24444
- const customFilters = filters2.filter(isFilter);
26463
+ const customFilters = filters2.filter(isFilter2);
24445
26464
  const newCustomFilters = applyCustomFilters(customFilters);
24446
26465
  const newDashboardFilters = applyDashboardFilters(dashboardFilters2);
24447
26466
  fetchReports(newCustomFilters, newDashboardFilters);
24448
26467
  };
24449
- const waitForCustomFilters = async (reportId) => {
24450
- let attempts = 0;
24451
- const maxAttempts = 10;
24452
- while (attempts < maxAttempts) {
24453
- const filters2 = customFiltersRef.current[reportId];
24454
- if (filters2 && filters2.length > 0) {
24455
- return filters2;
24456
- }
24457
- await new Promise((resolve) => setTimeout(resolve, 100));
24458
- attempts++;
24459
- }
24460
- return [];
26468
+ const forceCacheRefresh = () => {
26469
+ if (!cacheEnabled) return;
26470
+ fetchReports([], dashboardFilters ?? [], config?.pageSize, true);
24461
26471
  };
24462
- const fetchReports = async (customFilters, dashboardFilters2, pageSize) => {
26472
+ const fetchReports = async (customFilters, dashboardFilters2, pageSize, forceCacheToRefresh = false) => {
24463
26473
  if (!client || !sections) return;
24464
26474
  const allReports = Object.values(sections).flat();
26475
+ const fetchStartTime = Date.now();
26476
+ let totalCached = 0;
24465
26477
  await Promise.all(
24466
- allReports.map(async (reportInfo) => {
26478
+ allReports.map(async (reportInfo, idx) => {
24467
26479
  const reportId = reportInfo.id;
24468
26480
  const requestId = (reportRequestIds.current[reportId] ?? 0) + 1;
24469
26481
  reportRequestIds.current[reportId] = requestId;
@@ -24472,7 +26484,7 @@ var useDashboard = (dashboardName, config) => {
24472
26484
  id: reportId,
24473
26485
  data: true
24474
26486
  });
24475
- const customReportFiltersArray = await waitForCustomFilters(reportId);
26487
+ const customReportFiltersArray = customReportFilters[reportId] ?? [];
24476
26488
  let rowsPerRequest = pageSize || 600;
24477
26489
  if (!pageSize && (reportInfo.chartType === "table" || reportInfo.chartType === "metric")) {
24478
26490
  rowsPerRequest = 10;
@@ -24484,17 +26496,162 @@ var useDashboard = (dashboardName, config) => {
24484
26496
  const additionalProcessing = {
24485
26497
  page: pagination
24486
26498
  };
24487
- const usePivotTask = !!reportInfo.pivot;
26499
+ const usePivotTask = !cacheEnabled && !!reportInfo.pivot;
26500
+ const allFilters = dashboardFilters2.concat(customFilters).concat(customReportFiltersArray);
26501
+ const applyInMemoryPivotIfNeeded = (report2) => {
26502
+ const pivotToApply = reportInfo.pivot ?? report2.pivot;
26503
+ if (!pivotToApply || usePivotTask) {
26504
+ return report2;
26505
+ }
26506
+ const pivotRows = applyPivotInMemory(
26507
+ report2.rows ?? [],
26508
+ pivotToApply,
26509
+ allFilters
26510
+ );
26511
+ const pivotColumns = generatePivotColumnsInMemory({
26512
+ pivot: pivotToApply,
26513
+ pivotRows,
26514
+ sourceColumns: report2.columnInternal ?? report2.columns
26515
+ });
26516
+ return {
26517
+ ...report2,
26518
+ pivot: pivotToApply,
26519
+ pivotRows,
26520
+ pivotColumns: pivotColumns ?? report2.pivotColumns,
26521
+ pivotRowCount: pivotRows.length
26522
+ };
26523
+ };
26524
+ if (cacheEnabled && cacheCab.isCacheable(reportId)) {
26525
+ const report2 = await cacheCab.get(reportId, dashboardFilters2, customFilters.concat(customReportFiltersArray), reportInfo.pivot, client, tenants, flags, pageSize, getToken, eventTracking, forceCacheToRefresh);
26526
+ if (reportRequestIds.current[reportId] !== requestId) {
26527
+ return null;
26528
+ }
26529
+ if (cacheCab.isCacheable(reportId)) {
26530
+ reportsDispatch({
26531
+ type: "UPDATE_REPORT",
26532
+ id: reportId,
26533
+ data: {
26534
+ ...report2,
26535
+ pagination,
26536
+ triggerReload: false
26537
+ }
26538
+ });
26539
+ reportsLoadingStateDispatch({
26540
+ type: "SET_REPORT_LOADING",
26541
+ id: reportId,
26542
+ data: false
26543
+ });
26544
+ totalCached += 1;
26545
+ return report2;
26546
+ }
26547
+ }
26548
+ if (cacheEnabled && !cacheCab.isCacheable(reportId)) {
26549
+ try {
26550
+ const { report: uncacheableReport, fromCache } = await cacheCab.getOrFetchUncacheableResult(
26551
+ reportId,
26552
+ client,
26553
+ tenants,
26554
+ flags,
26555
+ allFilters,
26556
+ additionalProcessing,
26557
+ reportInfo.pivot,
26558
+ async () => {
26559
+ const { report: report2, error: error2 } = await fetchReport({
26560
+ reportId,
26561
+ client,
26562
+ tenants,
26563
+ flags,
26564
+ additionalProcessing,
26565
+ filters: allFilters,
26566
+ getToken,
26567
+ eventTracking,
26568
+ usePivotTask,
26569
+ overwriteCache: forceCacheToRefresh
26570
+ });
26571
+ if (error2 || !report2) {
26572
+ throw error2 ?? new Error("Failed to fetch uncacheable report");
26573
+ }
26574
+ return report2;
26575
+ },
26576
+ forceCacheToRefresh
26577
+ );
26578
+ if (reportRequestIds.current[reportId] !== requestId) {
26579
+ return null;
26580
+ }
26581
+ const uncacheableReportWithPivot = applyInMemoryPivotIfNeeded(uncacheableReport);
26582
+ reportsDispatch({
26583
+ type: "UPDATE_REPORT",
26584
+ id: reportId,
26585
+ data: {
26586
+ ...uncacheableReportWithPivot,
26587
+ pagination,
26588
+ triggerReload: false
26589
+ }
26590
+ });
26591
+ reportsLoadingStateDispatch({
26592
+ type: "SET_REPORT_LOADING",
26593
+ id: reportId,
26594
+ data: false
26595
+ });
26596
+ if (fromCache) {
26597
+ totalCached += 1;
26598
+ }
26599
+ if (usePivotTask) {
26600
+ fetchReportRows({
26601
+ reportId,
26602
+ client,
26603
+ tenants,
26604
+ filters: allFilters,
26605
+ getToken,
26606
+ additionalProcessing,
26607
+ overwriteCache: forceCacheToRefresh
26608
+ }).then(({ rows, rowCount, columns, fields }) => {
26609
+ if (reportRequestIds.current[reportId] !== requestId) {
26610
+ return;
26611
+ }
26612
+ reportsDispatch({
26613
+ type: "UPDATE_REPORT",
26614
+ id: reportId,
26615
+ data: {
26616
+ rows,
26617
+ rowCount,
26618
+ columnInternal: columns,
26619
+ columns: columns.map((col) => ({
26620
+ field: col.field,
26621
+ format: col.format,
26622
+ label: col.label,
26623
+ inferFormat: col.inferFormat
26624
+ })),
26625
+ // @ts-ignore fields is not typed on QuillReportInternal
26626
+ fields,
26627
+ triggerReload: false
26628
+ }
26629
+ });
26630
+ }).catch((e) => {
26631
+ if (e instanceof Error && e.name === "AbortError") {
26632
+ return;
26633
+ }
26634
+ console.error("Failed to fetch background rows", e);
26635
+ });
26636
+ }
26637
+ return uncacheableReportWithPivot;
26638
+ } catch (error2) {
26639
+ console.error(error2);
26640
+ return null;
26641
+ }
26642
+ }
24488
26643
  const { report, error } = await fetchReport({
24489
26644
  reportId,
24490
26645
  client,
24491
26646
  tenants,
24492
26647
  flags,
24493
26648
  additionalProcessing,
24494
- filters: dashboardFilters2.concat(customFilters).concat(customReportFiltersArray),
26649
+ filters: allFilters,
24495
26650
  getToken,
24496
26651
  eventTracking,
24497
- usePivotTask
26652
+ usePivotTask,
26653
+ overwriteCache: forceCacheToRefresh
26654
+ // usePivotTask: false,
24498
26655
  });
24499
26656
  if (error) {
24500
26657
  console.error(error);
@@ -24522,9 +26679,10 @@ var useDashboard = (dashboardName, config) => {
24522
26679
  reportId,
24523
26680
  client,
24524
26681
  tenants,
24525
- filters: dashboardFilters2.concat(customFilters).concat(customReportFiltersArray),
26682
+ filters: allFilters,
24526
26683
  getToken,
24527
- additionalProcessing
26684
+ additionalProcessing,
26685
+ overwriteCache: forceCacheToRefresh
24528
26686
  }).then(({ rows, rowCount, columns, fields }) => {
24529
26687
  if (reportRequestIds.current[reportId] !== requestId) {
24530
26688
  return;
@@ -24548,19 +26706,30 @@ var useDashboard = (dashboardName, config) => {
24548
26706
  }
24549
26707
  });
24550
26708
  }).catch((e) => {
24551
- if (e instanceof Error && e.name === "AbortError") return;
26709
+ if (e instanceof Error && e.name === "AbortError") {
26710
+ return;
26711
+ }
24552
26712
  console.error("Failed to fetch background rows", e);
24553
26713
  });
24554
26714
  }
24555
26715
  return report;
24556
26716
  })
24557
26717
  );
26718
+ if (!loadedDashes.includes(dashboardName) || !cacheEnabled || forceCacheToRefresh) {
26719
+ setLoadedDashes((prev) => prev.includes(dashboardName) ? prev : [...prev, dashboardName]);
26720
+ setLastUpdatedDict((prev) => ({ ...prev, [dashboardName]: Date.now() }));
26721
+ }
26722
+ if (logCacheStatistics) {
26723
+ console.log(`Dashboard "${dashboardName}": Cache Rate: ${(100 * totalCached / allReports.length).toFixed(1)}%, load time: ${((Date.now() - fetchStartTime) / 1e3).toFixed(2)} secs`);
26724
+ }
24558
26725
  };
24559
26726
  return {
24560
26727
  isLoading: !!isLoading || !sections,
24561
26728
  sections: isLoading ? null : sections,
24562
26729
  filters,
24563
- applyFilters
26730
+ applyFilters,
26731
+ lastUpdated,
26732
+ forceCacheRefresh
24564
26733
  };
24565
26734
  };
24566
26735
  var useDashboardReportInternal = (reportId, config) => {
@@ -24584,15 +26753,6 @@ var useDashboardReportInternal = (reportId, config) => {
24584
26753
  } = useDashboardInternal(reports[reportId]?.dashboardName ?? null);
24585
26754
  const [pageLoading, setPageLoading] = (0, import_react2.useState)(false);
24586
26755
  const [maxPage, setMaxPage] = (0, import_react2.useState)(0);
24587
- (0, import_react2.useEffect)(() => {
24588
- if (config?.initialFilters) {
24589
- customReportFiltersDispatch({
24590
- type: "ADD_CUSTOM_REPORT_FILTERS",
24591
- reportId,
24592
- data: config.initialFilters.map(convertCustomFilter)
24593
- });
24594
- }
24595
- }, []);
24596
26756
  const processedReport = (0, import_react2.useMemo)(() => {
24597
26757
  if (!reports[reportId]) return null;
24598
26758
  const dashboardName = reports[reportId].dashboardName;
@@ -24797,6 +26957,7 @@ var useDashboardReportInternal = (reportId, config) => {
24797
26957
  };
24798
26958
  var useDashboardReport = (reportId, config) => {
24799
26959
  const { report, loading, applyFilters, deleteReport, nextPage } = useDashboardReportInternal(reportId, config);
26960
+ const { customReportFiltersDispatch } = (0, import_react2.useContext)(ReportFiltersContext);
24800
26961
  const fetchingRef = (0, import_react2.useRef)(false);
24801
26962
  const fetchNextPage = async () => {
24802
26963
  if (fetchingRef.current) {
@@ -24811,6 +26972,15 @@ var useDashboardReport = (reportId, config) => {
24811
26972
  fetchingRef.current = false;
24812
26973
  }
24813
26974
  };
26975
+ (0, import_react2.useEffect)(() => {
26976
+ if (config?.initialFilters) {
26977
+ customReportFiltersDispatch({
26978
+ type: "ADD_CUSTOM_REPORT_FILTERS",
26979
+ reportId,
26980
+ data: config.initialFilters.map(convertCustomFilter)
26981
+ });
26982
+ }
26983
+ }, []);
24814
26984
  return {
24815
26985
  report,
24816
26986
  isLoading: loading || fetchingRef.current,
@@ -25275,7 +27445,7 @@ function QuillCustomDocumentComponent({
25275
27445
  }
25276
27446
 
25277
27447
  // src/Chart.tsx
25278
- var import_date_fns12 = require("date-fns");
27448
+ var import_date_fns14 = require("date-fns");
25279
27449
 
25280
27450
  // src/components/Chart/BarList.tsx
25281
27451
  var import_react4 = __toESM(require("react"), 1);
@@ -27964,19 +30134,23 @@ var RenderLegend = ({
27964
30134
  const seeAllMeasureRef = (0, import_react12.useRef)(null);
27965
30135
  const seeAllTriggerRef = (0, import_react12.useRef)(null);
27966
30136
  const itemRefs = (0, import_react12.useRef)([]);
27967
- if (!payload || payload.length === 0) return null;
27968
- const maxItems = limit ?? payload.length;
30137
+ const safePayload = payload ?? [];
30138
+ const maxItems = limit ?? safePayload.length;
27969
30139
  const measuredLimit = visibleCount ?? maxItems;
27970
- const visiblePayload = payload.slice(0, Math.min(maxItems, measuredLimit));
30140
+ const visiblePayload = safePayload.slice(0, Math.min(maxItems, measuredLimit));
27971
30141
  const handleOpen = () => setIsOpen(true);
27972
30142
  const handleClose = () => setIsOpen(false);
27973
30143
  (0, import_react12.useLayoutEffect)(() => {
27974
30144
  if (!containerRef.current) return;
27975
- if (!payload.length) return;
30145
+ if (!safePayload.length) {
30146
+ setShowSeeAll(false);
30147
+ setVisibleCount(null);
30148
+ return;
30149
+ }
27976
30150
  const updateVisibleCount = () => {
27977
30151
  const containerWidth = containerRef.current?.offsetWidth ?? 0;
27978
30152
  if (!containerWidth) return;
27979
- const maxCount = Math.min(maxItems, payload.length);
30153
+ const maxCount = Math.min(maxItems, safePayload.length);
27980
30154
  const seeAllWidth = getOuterWidth(seeAllMeasureRef.current);
27981
30155
  const computeCount = (reserveSeeAll) => {
27982
30156
  const availableWidth = containerWidth - (reserveSeeAll ? seeAllWidth : 0);
@@ -27992,7 +30166,7 @@ var RenderLegend = ({
27992
30166
  return Math.max(1, nextCount);
27993
30167
  };
27994
30168
  const countWithSeeAll = computeCount(true);
27995
- const shouldShowSeeAll = payload.length > countWithSeeAll;
30169
+ const shouldShowSeeAll = safePayload.length > countWithSeeAll;
27996
30170
  const finalCount = shouldShowSeeAll ? countWithSeeAll : computeCount(false);
27997
30171
  setShowSeeAll(shouldShowSeeAll);
27998
30172
  setVisibleCount(finalCount);
@@ -28001,7 +30175,8 @@ var RenderLegend = ({
28001
30175
  const observer = new ResizeObserver(updateVisibleCount);
28002
30176
  observer.observe(containerRef.current);
28003
30177
  return () => observer.disconnect();
28004
- }, [payload.length, maxItems]);
30178
+ }, [safePayload.length, maxItems]);
30179
+ if (!safePayload.length) return null;
28005
30180
  return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
28006
30181
  "div",
28007
30182
  {
@@ -28041,7 +30216,7 @@ var RenderLegend = ({
28041
30216
  justifyContent: "flex-start",
28042
30217
  flexWrap: "nowrap"
28043
30218
  },
28044
- children: payload.slice(0, maxItems).map((entry, index) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
30219
+ children: safePayload.slice(0, maxItems).map((entry, index) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
28045
30220
  "div",
28046
30221
  {
28047
30222
  ref: (element) => {
@@ -28130,7 +30305,7 @@ var RenderLegend = ({
28130
30305
  maxWidth: 520,
28131
30306
  gap: "0.5rem"
28132
30307
  },
28133
- children: payload.map((entry, index) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
30308
+ children: safePayload.map((entry, index) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
28134
30309
  LegendItem,
28135
30310
  {
28136
30311
  entry,
@@ -28480,8 +30655,8 @@ var PieChartWrapper = import_react13.default.forwardRef(
28480
30655
  cy: "50%",
28481
30656
  startAngle: 90,
28482
30657
  endAngle: -270,
28483
- innerRadius: isDonut ? showLegend ? "50%" : "70%" : "0%",
28484
- outerRadius: showLegend ? "70%" : "100%",
30658
+ innerRadius: isDonut ? "70%" : "0%",
30659
+ outerRadius: "100%",
28485
30660
  paddingAngle: 0,
28486
30661
  dataKey: category,
28487
30662
  nameKey: index,
@@ -29074,8 +31249,8 @@ var import_recharts2 = require("recharts");
29074
31249
  init_valueFormatter();
29075
31250
 
29076
31251
  // src/utils/axisFormatter.ts
29077
- var import_date_fns9 = require("date-fns");
29078
- var import_date_fns_tz3 = require("date-fns-tz");
31252
+ var import_date_fns11 = require("date-fns");
31253
+ var import_date_fns_tz4 = require("date-fns-tz");
29079
31254
  var axisFormatter = ({ value, field, fields }) => {
29080
31255
  if (field === void 0 || field === null) return "";
29081
31256
  if (value === void 0 || value === null) return "";
@@ -29191,9 +31366,9 @@ var formatPercent2 = (value) => {
29191
31366
  return formatter.format(Number(value));
29192
31367
  };
29193
31368
  var _getUTCDateHelper2 = (value, fmt) => {
29194
- const utcDate = (0, import_date_fns_tz3.utcToZonedTime)(new Date(value), "UTC");
29195
- if (!(0, import_date_fns9.isValid)(utcDate)) return "Invalid date";
29196
- return (0, import_date_fns9.format)(utcDate, fmt);
31369
+ const utcDate = (0, import_date_fns_tz4.utcToZonedTime)(new Date(value), "UTC");
31370
+ if (!(0, import_date_fns11.isValid)(utcDate)) return "Invalid date";
31371
+ return (0, import_date_fns11.format)(utcDate, fmt);
29197
31372
  };
29198
31373
  var format_YYYY2 = (value) => _getUTCDateHelper2(value, "yyyy");
29199
31374
  var format_MMM_dd = (value) => _getUTCDateHelper2(value, "dd MMM");
@@ -29201,31 +31376,31 @@ var format_MMM_yyyy2 = (value) => _getUTCDateHelper2(value, "MMM yyyy");
29201
31376
  var format_hh_ap_pm2 = (value) => _getUTCDateHelper2(value, "hh:mm aa");
29202
31377
  var format_MMM_dd_yyyy2 = (value) => _getUTCDateHelper2(value, "dd MMM yyyy");
29203
31378
  var format_MMM_dd_MMM_dd = (value) => {
29204
- const utcDate = (0, import_date_fns_tz3.utcToZonedTime)(new Date(value), "UTC");
29205
- if (!(0, import_date_fns9.isValid)(utcDate)) return "Invalid date";
29206
- const monday = (0, import_date_fns9.startOfWeek)(utcDate, { weekStartsOn: 1 });
29207
- const sunday = (0, import_date_fns9.endOfWeek)(utcDate, { weekStartsOn: 1 });
29208
- if ((0, import_date_fns9.format)(monday, "MMM") === (0, import_date_fns9.format)(sunday, "MMM")) {
29209
- return `${(0, import_date_fns9.format)(monday, "MMM dd")} - ${(0, import_date_fns9.format)(sunday, "dd")}`;
31379
+ const utcDate = (0, import_date_fns_tz4.utcToZonedTime)(new Date(value), "UTC");
31380
+ if (!(0, import_date_fns11.isValid)(utcDate)) return "Invalid date";
31381
+ const monday = (0, import_date_fns11.startOfWeek)(utcDate, { weekStartsOn: 1 });
31382
+ const sunday = (0, import_date_fns11.endOfWeek)(utcDate, { weekStartsOn: 1 });
31383
+ if ((0, import_date_fns11.format)(monday, "MMM") === (0, import_date_fns11.format)(sunday, "MMM")) {
31384
+ return `${(0, import_date_fns11.format)(monday, "MMM dd")} - ${(0, import_date_fns11.format)(sunday, "dd")}`;
29210
31385
  } else {
29211
- return `${(0, import_date_fns9.format)(monday, "MMM dd")} - ${(0, import_date_fns9.format)(sunday, "MMM dd")}`;
31386
+ return `${(0, import_date_fns11.format)(monday, "MMM dd")} - ${(0, import_date_fns11.format)(sunday, "MMM dd")}`;
29212
31387
  }
29213
31388
  };
29214
31389
  var format_MMM_dd_hh_mm_ap_pm2 = (value) => {
29215
- const utcDate = (0, import_date_fns_tz3.utcToZonedTime)(new Date(value), "UTC");
29216
- if (!(0, import_date_fns9.isValid)(utcDate)) return "Invalid date";
31390
+ const utcDate = (0, import_date_fns_tz4.utcToZonedTime)(new Date(value), "UTC");
31391
+ if (!(0, import_date_fns11.isValid)(utcDate)) return "Invalid date";
29217
31392
  const formatStr = utcDate.getMinutes() === 0 ? "MMM do h a" : "MMM do h:mm a";
29218
- const res = (0, import_date_fns9.format)(utcDate, formatStr);
31393
+ const res = (0, import_date_fns11.format)(utcDate, formatStr);
29219
31394
  return res.slice(0, -2) + res.slice(-2).toLowerCase();
29220
31395
  };
29221
31396
  var format_wo_yyyy2 = (value) => {
29222
- const utcDate = (0, import_date_fns_tz3.utcToZonedTime)(new Date(value), "UTC");
29223
- if (!(0, import_date_fns9.isValid)(utcDate)) return "Invalid date";
29224
- return `${(0, import_date_fns9.getWeek)(utcDate)},${utcDate.getFullYear()}`;
31397
+ const utcDate = (0, import_date_fns_tz4.utcToZonedTime)(new Date(value), "UTC");
31398
+ if (!(0, import_date_fns11.isValid)(utcDate)) return "Invalid date";
31399
+ return `${(0, import_date_fns11.getWeek)(utcDate)},${utcDate.getFullYear()}`;
29225
31400
  };
29226
31401
 
29227
31402
  // src/components/Chart/ChartTooltip.tsx
29228
- var import_date_fns10 = require("date-fns");
31403
+ var import_date_fns12 = require("date-fns");
29229
31404
 
29230
31405
  // src/components/Chart/ChartTooltipFrame.tsx
29231
31406
  var import_jsx_runtime30 = require("react/jsx-runtime");
@@ -29420,7 +31595,7 @@ var ChartTooltipPrimary = (props) => /* @__PURE__ */ (0, import_jsx_runtime33.js
29420
31595
  paddingTop: 2,
29421
31596
  paddingBottom: 2
29422
31597
  },
29423
- children: !isNaN(new Date(props.label)) && props.dateFormatter ? props.dateFormatter(props.label) : !isNaN(new Date(props.label)) ? (0, import_date_fns10.format)(new Date(props.label), "MMM yyyy") : props.label
31598
+ children: !isNaN(new Date(props.label)) && props.dateFormatter ? props.dateFormatter(props.label) : !isNaN(new Date(props.label)) ? (0, import_date_fns12.format)(new Date(props.label), "MMM yyyy") : props.label
29424
31599
  }
29425
31600
  )
29426
31601
  }
@@ -29469,8 +31644,8 @@ function reformatComparisonPayload(props, primaryLabel, comparisonLabel) {
29469
31644
  const nameKey = isComparison ? `comparison_${props.xAxisField}` : props.xAxisField;
29470
31645
  const days = LABEL_TO_DAYS[primaryLabel] ?? 0;
29471
31646
  const primaryDate = item.payload[props.xAxisField] ?? 0;
29472
- const compDate = (0, import_date_fns10.subDays)(new Date(primaryDate), days + 1);
29473
- const date = item.payload[nameKey] ?? (0, import_date_fns10.format)(compDate, props.xAxisFormat);
31647
+ const compDate = (0, import_date_fns12.subDays)(new Date(primaryDate), days + 1);
31648
+ const date = item.payload[nameKey] ?? (0, import_date_fns12.format)(compDate, props.xAxisFormat);
29474
31649
  const name2 = props.dateFormatter(date);
29475
31650
  const color2 = item.color;
29476
31651
  const value = props.valueFormatter(item.value, item.name);
@@ -29503,7 +31678,7 @@ function reformatComparisonPayload(props, primaryLabel, comparisonLabel) {
29503
31678
  return columnsByKey;
29504
31679
  }
29505
31680
  function getTooltipLabel(props, altTooltipLabel, isDateXAxis) {
29506
- return props.payload.length <= 2 && altTooltipLabel && isDateXAxis ? !isNaN(new Date(altTooltipLabel)) && props.dateFormatter ? props.dateFormatter(altTooltipLabel) : !isNaN(new Date(altTooltipLabel)) ? (0, import_date_fns10.format)(new Date(altTooltipLabel), "MMM yyyy") : altTooltipLabel : !isNaN(new Date(props.label)) && props.dateFormatter ? props.dateFormatter(props.label) : !isNaN(new Date(props.label)) ? (0, import_date_fns10.format)(new Date(props.label), "MMM yyyy") : props.label;
31681
+ return props.payload.length <= 2 && altTooltipLabel && isDateXAxis ? !isNaN(new Date(altTooltipLabel)) && props.dateFormatter ? props.dateFormatter(altTooltipLabel) : !isNaN(new Date(altTooltipLabel)) ? (0, import_date_fns12.format)(new Date(altTooltipLabel), "MMM yyyy") : altTooltipLabel : !isNaN(new Date(props.label)) && props.dateFormatter ? props.dateFormatter(props.label) : !isNaN(new Date(props.label)) ? (0, import_date_fns12.format)(new Date(props.label), "MMM yyyy") : props.label;
29507
31682
  }
29508
31683
  function ChartTooltipComparison(props) {
29509
31684
  const isDateXAxis = isDateFormat(props.xAxisFormat);
@@ -30519,7 +32694,7 @@ init_dateRangePickerUtils();
30519
32694
 
30520
32695
  // src/DateRangePicker/QuillDateRangePicker.tsx
30521
32696
  var import_react20 = require("react");
30522
- var import_date_fns11 = require("date-fns");
32697
+ var import_date_fns13 = require("date-fns");
30523
32698
 
30524
32699
  // src/components/QuillSelect.tsx
30525
32700
  var import_react19 = require("react");
@@ -30965,9 +33140,9 @@ function QuillDateRangePicker({
30965
33140
  ]
30966
33141
  }
30967
33142
  ),
30968
- localStartDate && (0, import_date_fns11.format)(localStartDate, "MMM dd, yyyy"),
33143
+ localStartDate && (0, import_date_fns13.format)(localStartDate, "MMM dd, yyyy"),
30969
33144
  localStartDate ? " - " : "Pick a date",
30970
- localEndDate && (0, import_date_fns11.format)(localEndDate, "MMM dd, yyyy")
33145
+ localEndDate && (0, import_date_fns13.format)(localEndDate, "MMM dd, yyyy")
30971
33146
  ]
30972
33147
  }
30973
33148
  ),
@@ -31034,21 +33209,21 @@ function CalendarRow({
31034
33209
  updateDateFilter,
31035
33210
  setLocalPreset
31036
33211
  }) {
31037
- const firstMonthDisplayedDates = (0, import_date_fns11.eachDayOfInterval)({
31038
- start: (0, import_date_fns11.startOfWeek)(anchorStartDate),
31039
- end: (0, import_date_fns11.endOfWeek)((0, import_date_fns11.endOfMonth)(anchorStartDate))
33212
+ const firstMonthDisplayedDates = (0, import_date_fns13.eachDayOfInterval)({
33213
+ start: (0, import_date_fns13.startOfWeek)(anchorStartDate),
33214
+ end: (0, import_date_fns13.endOfWeek)((0, import_date_fns13.endOfMonth)(anchorStartDate))
31040
33215
  });
31041
- const secondMonthDisplayedDates = (0, import_date_fns11.eachDayOfInterval)({
31042
- start: (0, import_date_fns11.startOfWeek)((0, import_date_fns11.startOfMonth)(anchorEndDate)),
31043
- end: (0, import_date_fns11.endOfWeek)(anchorEndDate)
33216
+ const secondMonthDisplayedDates = (0, import_date_fns13.eachDayOfInterval)({
33217
+ start: (0, import_date_fns13.startOfWeek)((0, import_date_fns13.startOfMonth)(anchorEndDate)),
33218
+ end: (0, import_date_fns13.endOfWeek)(anchorEndDate)
31044
33219
  });
31045
33220
  const incrementAnchorDates = () => {
31046
- setAnchorStartDate((0, import_date_fns11.startOfMonth)((0, import_date_fns11.addMonths)(anchorStartDate, 1)));
31047
- setAnchorEndDate((0, import_date_fns11.endOfMonth)((0, import_date_fns11.addMonths)(anchorEndDate, 1)));
33221
+ setAnchorStartDate((0, import_date_fns13.startOfMonth)((0, import_date_fns13.addMonths)(anchorStartDate, 1)));
33222
+ setAnchorEndDate((0, import_date_fns13.endOfMonth)((0, import_date_fns13.addMonths)(anchorEndDate, 1)));
31048
33223
  };
31049
33224
  const decrementAnchorDates = () => {
31050
- setAnchorStartDate((0, import_date_fns11.startOfMonth)((0, import_date_fns11.subMonths)(anchorStartDate, 1)));
31051
- setAnchorEndDate((0, import_date_fns11.endOfMonth)((0, import_date_fns11.subMonths)(anchorEndDate, 1)));
33225
+ setAnchorStartDate((0, import_date_fns13.startOfMonth)((0, import_date_fns13.subMonths)(anchorStartDate, 1)));
33226
+ setAnchorEndDate((0, import_date_fns13.endOfMonth)((0, import_date_fns13.subMonths)(anchorEndDate, 1)));
31052
33227
  };
31053
33228
  return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { style: { position: "absolute", zIndex: 100, marginTop: 12 }, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
31054
33229
  "div",
@@ -31144,7 +33319,7 @@ function CalendarRow({
31144
33319
  color: theme.primaryTextColor,
31145
33320
  fontWeight: 600
31146
33321
  },
31147
- children: (0, import_date_fns11.format)(anchorStartDate, "MMM yyyy")
33322
+ children: (0, import_date_fns13.format)(anchorStartDate, "MMM yyyy")
31148
33323
  }
31149
33324
  )
31150
33325
  ]
@@ -31208,7 +33383,7 @@ function CalendarRow({
31208
33383
  color: theme.primaryTextColor,
31209
33384
  fontWeight: 600
31210
33385
  },
31211
- children: (0, import_date_fns11.format)(anchorEndDate, "MMM yyyy")
33386
+ children: (0, import_date_fns13.format)(anchorEndDate, "MMM yyyy")
31212
33387
  }
31213
33388
  ),
31214
33389
  /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
@@ -31302,11 +33477,11 @@ function DayPicker({
31302
33477
  setLocalPreset,
31303
33478
  theme
31304
33479
  }) {
31305
- const isStartDate = (0, import_date_fns11.differenceInDays)((0, import_date_fns11.startOfDay)(date), (0, import_date_fns11.startOfDay)(localStartDate || 0)) === 0;
31306
- const isEndDate = (0, import_date_fns11.differenceInDays)((0, import_date_fns11.startOfDay)(date), (0, import_date_fns11.startOfDay)(localEndDate || 0)) === 0;
31307
- const isBetweenStartAndEnd = (0, import_date_fns11.isBefore)(date, localEndDate || 0) && (0, import_date_fns11.isAfter)(date, localStartDate || 0);
31308
- const isBeginningOfWeek = (0, import_date_fns11.differenceInDays)((0, import_date_fns11.startOfWeek)(date), date) === 0;
31309
- const isEndOfWeek = (0, import_date_fns11.differenceInDays)((0, import_date_fns11.endOfWeek)(date), date) === 0;
33480
+ const isStartDate = (0, import_date_fns13.differenceInDays)((0, import_date_fns13.startOfDay)(date), (0, import_date_fns13.startOfDay)(localStartDate || 0)) === 0;
33481
+ const isEndDate = (0, import_date_fns13.differenceInDays)((0, import_date_fns13.startOfDay)(date), (0, import_date_fns13.startOfDay)(localEndDate || 0)) === 0;
33482
+ const isBetweenStartAndEnd = (0, import_date_fns13.isBefore)(date, localEndDate || 0) && (0, import_date_fns13.isAfter)(date, localStartDate || 0);
33483
+ const isBeginningOfWeek = (0, import_date_fns13.differenceInDays)((0, import_date_fns13.startOfWeek)(date), date) === 0;
33484
+ const isEndOfWeek = (0, import_date_fns13.differenceInDays)((0, import_date_fns13.endOfWeek)(date), date) === 0;
31310
33485
  return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
31311
33486
  "button",
31312
33487
  {
@@ -31338,7 +33513,7 @@ function DayPicker({
31338
33513
  });
31339
33514
  setLocalPreset("");
31340
33515
  }
31341
- if (localStartDate && localEndDate && (0, import_date_fns11.isAfter)(date, localStartDate)) {
33516
+ if (localStartDate && localEndDate && (0, import_date_fns13.isAfter)(date, localStartDate)) {
31342
33517
  setLocalEndDate(date);
31343
33518
  updateDateFilter({
31344
33519
  startDate: localStartDate,
@@ -31346,7 +33521,7 @@ function DayPicker({
31346
33521
  });
31347
33522
  setLocalPreset("");
31348
33523
  }
31349
- if (localStartDate && localEndDate && (0, import_date_fns11.isBefore)(date, localStartDate)) {
33524
+ if (localStartDate && localEndDate && (0, import_date_fns13.isBefore)(date, localStartDate)) {
31350
33525
  setLocalStartDate(date);
31351
33526
  updateDateFilter({
31352
33527
  startDate: date,
@@ -31354,42 +33529,42 @@ function DayPicker({
31354
33529
  });
31355
33530
  setLocalPreset("");
31356
33531
  }
31357
- if (localStartDate && localEndDate && (0, import_date_fns11.differenceInDays)((0, import_date_fns11.startOfDay)(date), (0, import_date_fns11.startOfDay)(localStartDate)) === 0) {
33532
+ if (localStartDate && localEndDate && (0, import_date_fns13.differenceInDays)((0, import_date_fns13.startOfDay)(date), (0, import_date_fns13.startOfDay)(localStartDate)) === 0) {
31358
33533
  setLocalStartDate(void 0);
31359
33534
  setLocalEndDate(void 0);
31360
33535
  }
31361
33536
  },
31362
- children: (0, import_date_fns11.format)(date, "d")
33537
+ children: (0, import_date_fns13.format)(date, "d")
31363
33538
  }
31364
33539
  );
31365
33540
  }
31366
33541
  function getAnchorStartDate(startDate, endDate) {
31367
33542
  if (!startDate && !endDate) {
31368
- return (0, import_date_fns11.startOfMonth)((0, import_date_fns11.subMonths)(/* @__PURE__ */ new Date(), 1));
33543
+ return (0, import_date_fns13.startOfMonth)((0, import_date_fns13.subMonths)(/* @__PURE__ */ new Date(), 1));
31369
33544
  }
31370
33545
  if (startDate && !endDate) {
31371
- return (0, import_date_fns11.startOfMonth)(startDate);
33546
+ return (0, import_date_fns13.startOfMonth)(startDate);
31372
33547
  }
31373
33548
  if (!startDate && endDate) {
31374
- return (0, import_date_fns11.startOfMonth)((0, import_date_fns11.subMonths)(endDate, 1));
33549
+ return (0, import_date_fns13.startOfMonth)((0, import_date_fns13.subMonths)(endDate, 1));
31375
33550
  }
31376
33551
  if (startDate && endDate) {
31377
- return (0, import_date_fns11.startOfMonth)(startDate);
33552
+ return (0, import_date_fns13.startOfMonth)(startDate);
31378
33553
  }
31379
33554
  return /* @__PURE__ */ new Date();
31380
33555
  }
31381
33556
  function getAnchorEndDate(startDate, endDate) {
31382
33557
  if (!startDate && !endDate) {
31383
- return (0, import_date_fns11.endOfMonth)(/* @__PURE__ */ new Date());
33558
+ return (0, import_date_fns13.endOfMonth)(/* @__PURE__ */ new Date());
31384
33559
  }
31385
33560
  if (startDate && !endDate) {
31386
- return (0, import_date_fns11.endOfMonth)((0, import_date_fns11.addMonths)(startDate, 1));
33561
+ return (0, import_date_fns13.endOfMonth)((0, import_date_fns13.addMonths)(startDate, 1));
31387
33562
  }
31388
33563
  if (!startDate && endDate) {
31389
- return (0, import_date_fns11.endOfMonth)(endDate);
33564
+ return (0, import_date_fns13.endOfMonth)(endDate);
31390
33565
  }
31391
33566
  if (startDate && endDate) {
31392
- return (0, import_date_fns11.endOfMonth)((0, import_date_fns11.addMonths)(startDate, 1));
33567
+ return (0, import_date_fns13.endOfMonth)((0, import_date_fns13.addMonths)(startDate, 1));
31393
33568
  }
31394
33569
  return /* @__PURE__ */ new Date();
31395
33570
  }
@@ -32573,6 +34748,164 @@ function DashboardFilter2({
32573
34748
  // src/Chart.tsx
32574
34749
  init_paginationProcessing();
32575
34750
 
34751
+ // src/utils/cloudCacheValidation.ts
34752
+ var LIMIT_CLAUSE_REGEX = /^limit\b\s+(?:all|\d+|\$\d+|:[a-zA-Z_][a-zA-Z0-9_]*|\?)/i;
34753
+ var SQL_CONTENT_TO_IGNORE_REGEX = /'(?:''|[^'])*'|"(?:[^"]|"")*"|`[^`]*`|\[[^\]]*\]|--[^\n]*|\/\*[\s\S]*?\*\//g;
34754
+ function isWordChar(char) {
34755
+ if (!char) {
34756
+ return false;
34757
+ }
34758
+ return /[A-Za-z0-9_]/.test(char);
34759
+ }
34760
+ function getFinalSqlStatement(query) {
34761
+ const statements = query.split(";").map((statement) => statement.trim()).filter((statement) => statement.length > 0);
34762
+ return statements[statements.length - 1] ?? "";
34763
+ }
34764
+ function hasTopLevelLimitClause(statement) {
34765
+ let depth = 0;
34766
+ for (let i = 0; i < statement.length; i += 1) {
34767
+ const char = statement[i];
34768
+ if (char === "(") {
34769
+ depth += 1;
34770
+ continue;
34771
+ }
34772
+ if (char === ")") {
34773
+ depth = Math.max(0, depth - 1);
34774
+ continue;
34775
+ }
34776
+ if (depth !== 0) {
34777
+ continue;
34778
+ }
34779
+ const previousChar = i > 0 ? statement[i - 1] : void 0;
34780
+ if (isWordChar(previousChar)) {
34781
+ continue;
34782
+ }
34783
+ if (!LIMIT_CLAUSE_REGEX.test(statement.slice(i))) {
34784
+ continue;
34785
+ }
34786
+ return true;
34787
+ }
34788
+ return false;
34789
+ }
34790
+ function unwrapIdentifier(identifier) {
34791
+ if (!identifier) {
34792
+ return "";
34793
+ }
34794
+ const trimmed = identifier.trim();
34795
+ if (trimmed.length < 2) {
34796
+ return trimmed;
34797
+ }
34798
+ const startsAndEndsWithDoubleQuote = trimmed.startsWith('"') && trimmed.endsWith('"');
34799
+ const startsAndEndsWithSingleQuote = trimmed.startsWith("'") && trimmed.endsWith("'");
34800
+ const startsAndEndsWithBackticks = trimmed.startsWith("`") && trimmed.endsWith("`");
34801
+ const startsAndEndsWithBrackets = trimmed.startsWith("[") && trimmed.endsWith("]");
34802
+ if (startsAndEndsWithDoubleQuote || startsAndEndsWithSingleQuote || startsAndEndsWithBackticks || startsAndEndsWithBrackets) {
34803
+ return trimmed.slice(1, -1);
34804
+ }
34805
+ return trimmed;
34806
+ }
34807
+ function normalizeIdentifier(identifier) {
34808
+ return unwrapIdentifier(identifier).toLowerCase();
34809
+ }
34810
+ function hasRowFieldWithIdentifier(row, normalizedField) {
34811
+ if (!row || !normalizedField) {
34812
+ return false;
34813
+ }
34814
+ return Object.keys(row).some(
34815
+ (fieldName) => normalizeIdentifier(fieldName) === normalizedField
34816
+ );
34817
+ }
34818
+ function reportReferencesField(report, field, table) {
34819
+ const normalizedField = normalizeIdentifier(field);
34820
+ if (!normalizedField) {
34821
+ return false;
34822
+ }
34823
+ const referencedColumns = report?.referencedColumns ?? {};
34824
+ let entries = Object.entries(referencedColumns);
34825
+ if (entries.length === 0) {
34826
+ return false;
34827
+ }
34828
+ const normalizedTable = normalizeIdentifier(table);
34829
+ if (normalizedTable) {
34830
+ entries = entries.filter(
34831
+ ([tableName]) => normalizeIdentifier(tableName) === normalizedTable
34832
+ );
34833
+ }
34834
+ const referencedFields = entries.flatMap(([, fields]) => fields ?? []);
34835
+ if (referencedFields.length === 0) {
34836
+ return false;
34837
+ }
34838
+ return referencedFields.some((referencedField) => {
34839
+ const normalizedReferencedField = normalizeIdentifier(referencedField);
34840
+ return normalizedReferencedField === normalizedField || normalizedReferencedField === "*";
34841
+ });
34842
+ }
34843
+ function isDateFieldMissingInReport(report) {
34844
+ if (!report?.dateField?.field || !report.dateField.table) {
34845
+ return false;
34846
+ }
34847
+ const dateField = report.dateField;
34848
+ if (!reportReferencesField(report, dateField.field, dateField.table)) {
34849
+ return true;
34850
+ }
34851
+ const firstRow = report.rows?.[0];
34852
+ if (!firstRow) {
34853
+ return false;
34854
+ }
34855
+ const normalizedDateField = normalizeIdentifier(dateField.field);
34856
+ const dateFieldExistsInFirstRow = hasRowFieldWithIdentifier(
34857
+ firstRow,
34858
+ normalizedDateField
34859
+ );
34860
+ return !dateFieldExistsInFirstRow;
34861
+ }
34862
+ function hasLimitClause(query) {
34863
+ if (!query) {
34864
+ return false;
34865
+ }
34866
+ const sanitizedQuery = query.replace(SQL_CONTENT_TO_IGNORE_REGEX, " ");
34867
+ const finalStatement = getFinalSqlStatement(sanitizedQuery);
34868
+ if (!finalStatement) {
34869
+ return false;
34870
+ }
34871
+ return hasTopLevelLimitClause(finalStatement);
34872
+ }
34873
+ function reportUsesLimitClause(report) {
34874
+ const queriesToInspect = report?.itemQuery && report.itemQuery.length > 0 ? report.itemQuery : report?.queryString ? [report.queryString] : [];
34875
+ return queriesToInspect.some(hasLimitClause);
34876
+ }
34877
+ function getMissingDashboardFilterFields({
34878
+ rows = [],
34879
+ dashboardFilters = []
34880
+ }) {
34881
+ const dateIndex = dashboardFilters.findIndex(
34882
+ (filter) => filter.filterType === "date_range"
34883
+ );
34884
+ const requiredFields = dashboardFilters.flatMap((filter, index) => {
34885
+ if (index === dateIndex || !filter.field) {
34886
+ return [];
34887
+ }
34888
+ const normalizedField = normalizeIdentifier(filter.field);
34889
+ if (!normalizedField) {
34890
+ return [];
34891
+ }
34892
+ return [
34893
+ {
34894
+ displayField: unwrapIdentifier(filter.field),
34895
+ normalizedField
34896
+ }
34897
+ ];
34898
+ });
34899
+ const dedupedRequiredFields = requiredFields.filter(
34900
+ (field, index, fields) => fields.findIndex(
34901
+ (candidate) => candidate.normalizedField === field.normalizedField
34902
+ ) === index
34903
+ );
34904
+ return dedupedRequiredFields.filter(
34905
+ ({ normalizedField }) => rows.some((row) => !hasRowFieldWithIdentifier(row, normalizedField))
34906
+ ).map(({ displayField }) => displayField);
34907
+ }
34908
+
32576
34909
  // src/components/Dashboard/MetricComponent.tsx
32577
34910
  var import_react24 = require("react");
32578
34911
  init_dateRangePickerUtils();
@@ -32680,7 +35013,7 @@ function QuillMetricComponent({
32680
35013
  width: "100%"
32681
35014
  }
32682
35015
  }
32683
- ) : !report?.rows || report?.rows?.length === 0 || report.rows[0]?.[report.xAxisField] === null || report.rows[0]?.[report.xAxisField] === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
35016
+ ) : !report?.rows || report?.rows?.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
32684
35017
  "div",
32685
35018
  {
32686
35019
  style: {
@@ -36194,6 +38527,7 @@ function QuillTableDashboardComponent({
36194
38527
  }
36195
38528
 
36196
38529
  // src/Chart.tsx
38530
+ init_valueFormatter();
36197
38531
  var import_jsx_runtime49 = require("react/jsx-runtime");
36198
38532
  var MAX_ROWS_FOR_GENERIC_TABLE = 500;
36199
38533
  function sumByKey(arr, key) {
@@ -36230,7 +38564,8 @@ function Chart({
36230
38564
  filters,
36231
38565
  onClickChartElement,
36232
38566
  dateBucket,
36233
- propagateChanges
38567
+ propagateChanges,
38568
+ isAdmin = false
36234
38569
  }) {
36235
38570
  const [schemaData] = (0, import_react30.useContext)(SchemaDataContext);
36236
38571
  const { reload } = useReportInternal(reportId);
@@ -36352,6 +38687,25 @@ function Chart({
36352
38687
  }
36353
38688
  }, [report, theme]);
36354
38689
  const [client, clientLoading] = (0, import_react30.useContext)(ClientContext);
38690
+ const cloudCacheEnabled = (0, import_react30.useMemo)(() => {
38691
+ return !!client?.features?.cloudCache;
38692
+ }, [client]);
38693
+ const shouldShowCloudCacheValidationErrors = isAdmin && cloudCacheEnabled;
38694
+ const isMissingDateField = (0, import_react30.useMemo)(() => {
38695
+ if (!shouldShowCloudCacheValidationErrors) return false;
38696
+ return isDateFieldMissingInReport(report);
38697
+ }, [report, shouldShowCloudCacheValidationErrors]);
38698
+ const usesLimitInQuery = (0, import_react30.useMemo)(() => {
38699
+ if (!shouldShowCloudCacheValidationErrors) return false;
38700
+ return reportUsesLimitClause(report);
38701
+ }, [report, shouldShowCloudCacheValidationErrors]);
38702
+ const missingDashboardFilterFields = (0, import_react30.useMemo)(() => {
38703
+ if (!shouldShowCloudCacheValidationErrors || !report) return [];
38704
+ return getMissingDashboardFilterFields({
38705
+ rows: report.rows,
38706
+ dashboardFilters
38707
+ });
38708
+ }, [report, dashboardFilters, shouldShowCloudCacheValidationErrors]);
36355
38709
  const [error, setError] = (0, import_react30.useState)(void 0);
36356
38710
  const updateFilter = (filter, value, comparison) => {
36357
38711
  let filterValue = {};
@@ -36492,6 +38846,30 @@ function Chart({
36492
38846
  if (report?.error || error) {
36493
38847
  return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { style: containerStyle, className, children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ChartError, { errorMessage: report?.error ?? error }) });
36494
38848
  }
38849
+ if (isMissingDateField) {
38850
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { style: containerStyle, className, children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
38851
+ ChartError,
38852
+ {
38853
+ 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.`
38854
+ }
38855
+ ) });
38856
+ }
38857
+ if (usesLimitInQuery) {
38858
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { style: containerStyle, className, children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
38859
+ ChartError,
38860
+ {
38861
+ errorMessage: `The query for this report uses the LIMIT keyword, which is not supported when the cache is enabled.`
38862
+ }
38863
+ ) });
38864
+ }
38865
+ if (missingDashboardFilterFields.length) {
38866
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { style: containerStyle, className, children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
38867
+ ChartError,
38868
+ {
38869
+ errorMessage: `The query for this report is missing dashboard filter fields: ${missingDashboardFilterFields.join(", ")}`
38870
+ }
38871
+ ) });
38872
+ }
36495
38873
  return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
36496
38874
  "div",
36497
38875
  {
@@ -36822,7 +39200,7 @@ var ChartDisplay = ({
36822
39200
  );
36823
39201
  }
36824
39202
  if (config?.chartType?.toLowerCase() === "metric") {
36825
- if (!config?.rows || config?.rows?.length === 0 || config?.rows[0]?.[config?.xAxisField] === null || config?.rows[0]?.[config?.xAxisField] === void 0) {
39203
+ if (!config?.rows || config?.rows?.length === 0) {
36826
39204
  return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
36827
39205
  "div",
36828
39206
  {
@@ -36860,6 +39238,36 @@ var ChartDisplay = ({
36860
39238
  }
36861
39239
  );
36862
39240
  }
39241
+ if (config?.pivotRows && config?.pivotRows?.length > 0) {
39242
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
39243
+ "div",
39244
+ {
39245
+ style: {
39246
+ fontFamily: theme?.fontFamily,
39247
+ fontSize: 32,
39248
+ color: theme?.primaryTextColor,
39249
+ fontWeight: "600",
39250
+ textOverflow: "ellipsis",
39251
+ margin: 0,
39252
+ whiteSpace: "nowrap",
39253
+ boxSizing: "content-box",
39254
+ maxWidth: "100%",
39255
+ textAlign: "left",
39256
+ overflow: "hidden",
39257
+ height: containerStyle?.height || "100%",
39258
+ display: "flex",
39259
+ width: "100%",
39260
+ flexDirection: "row",
39261
+ ...containerStyle
39262
+ },
39263
+ className,
39264
+ children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(QuillMetricComponent, { report: config ?? EMPTY_REPORT, children: quillFormat({
39265
+ value: config?.pivotRows?.[0]?.[config?.xAxisField],
39266
+ format: config?.xAxisFormat
39267
+ }) ?? "No results" })
39268
+ }
39269
+ );
39270
+ }
36863
39271
  return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
36864
39272
  "div",
36865
39273
  {
@@ -37799,7 +40207,7 @@ var FilterPopoverWrapper = ({
37799
40207
  // src/components/ReportBuilder/FilterModal.tsx
37800
40208
  var import_react35 = require("react");
37801
40209
  init_Filter();
37802
- var import_date_fns13 = require("date-fns");
40210
+ var import_date_fns15 = require("date-fns");
37803
40211
  init_textProcessing();
37804
40212
  init_filterProcessing();
37805
40213
  var import_jsx_runtime55 = require("react/jsx-runtime");
@@ -37833,10 +40241,10 @@ function FilterModal({
37833
40241
  const [unit, setUnit] = (0, import_react35.useState)("");
37834
40242
  const [unitOptions, setUnitOptions] = (0, import_react35.useState)([]);
37835
40243
  const [startDate, setStartDate] = (0, import_react35.useState)(
37836
- (0, import_date_fns13.startOfToday)().toISOString().substring(0, 10)
40244
+ (0, import_date_fns15.startOfToday)().toISOString().substring(0, 10)
37837
40245
  );
37838
40246
  const [endDate, setEndDate] = (0, import_react35.useState)(
37839
- (0, import_date_fns13.startOfToday)().toISOString().substring(0, 10)
40247
+ (0, import_date_fns15.startOfToday)().toISOString().substring(0, 10)
37840
40248
  );
37841
40249
  const [filterInitialized, setFilterInitialized] = (0, import_react35.useState)(false);
37842
40250
  const [table, setTable] = (0, import_react35.useState)(void 0);
@@ -37904,10 +40312,10 @@ function FilterModal({
37904
40312
  onOperatorChange(filter.operator);
37905
40313
  setUnit(filter.filterType === "date-filter" ? filter.value.unit : "");
37906
40314
  setStartDate(
37907
- filter.value?.startDate ? filter.value.startDate.substring(0, 10) : (0, import_date_fns13.startOfToday)().toISOString().substring(0, 10)
40315
+ filter.value?.startDate ? filter.value.startDate.substring(0, 10) : (0, import_date_fns15.startOfToday)().toISOString().substring(0, 10)
37908
40316
  );
37909
40317
  setEndDate(
37910
- filter.value?.endDate ? filter.value.endDate.substring(0, 10) : (0, import_date_fns13.startOfToday)().toISOString().substring(0, 10)
40318
+ filter.value?.endDate ? filter.value.endDate.substring(0, 10) : (0, import_date_fns15.startOfToday)().toISOString().substring(0, 10)
37911
40319
  );
37912
40320
  if (filter.filterType === "string-in-filter") {
37913
40321
  setSelectedOptions(
@@ -38002,8 +40410,8 @@ function FilterModal({
38002
40410
  DateOperator.LessThan,
38003
40411
  DateOperator.LessThanOrEqualTo,
38004
40412
  DateOperator.NotEqualTo
38005
- ].includes(operator2) && !(0, import_date_fns13.isValid)((0, import_date_fns13.parse)(value, "yyyy-mm-dd", /* @__PURE__ */ new Date())) && (filterInitialized || !filter)) {
38006
- setValue((0, import_date_fns13.startOfToday)().toISOString().substring(0, 10));
40413
+ ].includes(operator2) && !(0, import_date_fns15.isValid)((0, import_date_fns15.parse)(value, "yyyy-mm-dd", /* @__PURE__ */ new Date())) && (filterInitialized || !filter)) {
40414
+ setValue((0, import_date_fns15.startOfToday)().toISOString().substring(0, 10));
38007
40415
  } else if (type === FieldType.Date && [
38008
40416
  DateOperator.InTheLast,
38009
40417
  DateOperator.InTheCurrent,
@@ -38191,12 +40599,12 @@ function FilterModal({
38191
40599
  case DateOperator.LessThan:
38192
40600
  case DateOperator.GreaterThanOrEqualTo:
38193
40601
  case DateOperator.LessThanOrEqualTo: {
38194
- const parsedDate = (0, import_date_fns13.parse)(value, "yyyy-mm-dd", /* @__PURE__ */ new Date());
38195
- if (!(0, import_date_fns13.isValid)(parsedDate)) {
40602
+ const parsedDate = (0, import_date_fns15.parse)(value, "yyyy-mm-dd", /* @__PURE__ */ new Date());
40603
+ if (!(0, import_date_fns15.isValid)(parsedDate)) {
38196
40604
  alert("Please specify a valid date in yyyy-mm-dd");
38197
40605
  return;
38198
40606
  }
38199
- const normalizedDate = (0, import_date_fns13.format)(parsedDate, "yyyy-mm-dd");
40607
+ const normalizedDate = (0, import_date_fns15.format)(parsedDate, "yyyy-mm-dd");
38200
40608
  onSubmitFilter({
38201
40609
  field,
38202
40610
  filterType: "date-comparison-filter" /* DateComparisonFilter */,
@@ -38492,8 +40900,8 @@ function FilterModal({
38492
40900
  QuillDateRangePicker,
38493
40901
  {
38494
40902
  dateRange: {
38495
- startDate: startDate ? /* @__PURE__ */ new Date(startDate + "T00:00:00") : (0, import_date_fns13.startOfToday)(),
38496
- endDate: endDate ? /* @__PURE__ */ new Date(endDate + "T00:00:00") : (0, import_date_fns13.startOfToday)()
40903
+ startDate: startDate ? /* @__PURE__ */ new Date(startDate + "T00:00:00") : (0, import_date_fns15.startOfToday)(),
40904
+ endDate: endDate ? /* @__PURE__ */ new Date(endDate + "T00:00:00") : (0, import_date_fns15.startOfToday)()
38497
40905
  },
38498
40906
  preset: "",
38499
40907
  presetOptions: [],
@@ -38720,6 +41128,9 @@ function validateTemplatesAgainstFilters(filters, templates) {
38720
41128
  }
38721
41129
  async function addTemplatesToDashboard(name2, newTemplates, client, dashboardData, getToken, eventTracking) {
38722
41130
  try {
41131
+ if (!newTemplates?.length) {
41132
+ return null;
41133
+ }
38723
41134
  const { publicKey, tenants } = client;
38724
41135
  if (tenants) {
38725
41136
  throw new Error("Adding from template not yet supported for tenants");
@@ -38772,6 +41183,7 @@ async function addTemplatesToDashboard(name2, newTemplates, client, dashboardDat
38772
41183
  function: "addTemplatesToDashboard"
38773
41184
  }
38774
41185
  });
41186
+ return null;
38775
41187
  }
38776
41188
  }
38777
41189
 
@@ -40649,7 +43061,7 @@ var PivotList = ({
40649
43061
  };
40650
43062
 
40651
43063
  // src/internals/ReportBuilder/PivotModal.tsx
40652
- var import_date_fns14 = require("date-fns");
43064
+ var import_date_fns16 = require("date-fns");
40653
43065
  init_textProcessing();
40654
43066
  init_pivotProcessing();
40655
43067
  init_tableProcessing();
@@ -42477,7 +44889,7 @@ var validateReport = (formData, dashboardData, defaultDateFilter, allTables) =>
42477
44889
 
42478
44890
  // src/components/Chart/InternalChart.tsx
42479
44891
  var import_react42 = require("react");
42480
- var import_date_fns15 = require("date-fns");
44892
+ var import_date_fns17 = require("date-fns");
42481
44893
  init_Filter();
42482
44894
  init_filterProcessing();
42483
44895
  init_dateRangePickerUtils();
@@ -44317,7 +46729,7 @@ function ChartBuilder({
44317
46729
  template: true,
44318
46730
  referenceLines: []
44319
46731
  };
44320
- const updateDashboardFilters = async (dashboardName) => {
46732
+ const updateDashboardFilters2 = async (dashboardName) => {
44321
46733
  if (dashboardConfig && dashboardConfig[dashboardName]) {
44322
46734
  return dashboardConfig[dashboardName];
44323
46735
  }
@@ -44639,7 +47051,7 @@ function ChartBuilder({
44639
47051
  if (destinationDashboardName) {
44640
47052
  dashboardName = destinationDashboardName;
44641
47053
  }
44642
- const curDashboard = await updateDashboardFilters(dashboardName);
47054
+ const curDashboard = await updateDashboardFilters2(dashboardName);
44643
47055
  setDashboardOptions(dashboardOptions2);
44644
47056
  curFormData.dashboardName = dashboardName;
44645
47057
  const curSchemaData = schemaData.schemaWithCustomFields;
@@ -45926,7 +48338,7 @@ function ChartBuilder({
45926
48338
  value: formData.dashboardName || "",
45927
48339
  onChange: async (e) => {
45928
48340
  handleChange(e.target.value, "dashboardName");
45929
- await updateDashboardFilters(e.target.value);
48341
+ await updateDashboardFilters2(e.target.value);
45930
48342
  },
45931
48343
  options: dashboardOptions.map((elem) => ({
45932
48344
  label: elem.label,
@@ -54257,7 +56669,7 @@ function StaticChart(props) {
54257
56669
  pageLoading,
54258
56670
  nextPage,
54259
56671
  prevPage,
54260
- sortRows
56672
+ sortRows: sortRows2
54261
56673
  } = useDashboardReportInternal(reportId);
54262
56674
  const baseStyle = report?.chartType && CHART_TYPE_STYLES[report.chartType] ? CHART_TYPE_STYLES[report.chartType] : DEFAULT_STYLE;
54263
56675
  const mergedStyle = className ? { ...containerStyle ?? {} } : { ...baseStyle, ...containerStyle ?? {} };
@@ -54277,7 +56689,7 @@ function StaticChart(props) {
54277
56689
  }
54278
56690
  };
54279
56691
  const onSortChange = (sort) => {
54280
- sortRows({
56692
+ sortRows2({
54281
56693
  field: sort.field,
54282
56694
  direction: sort.direction
54283
56695
  });