@quillsql/react 2.16.100 → 2.16.102

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.cjs +440 -25
  2. package/dist/index.js +440 -25
  3. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -23368,7 +23368,7 @@ async function fetchReportRows({
23368
23368
  additionalProcessing,
23369
23369
  overwriteCache = false
23370
23370
  }) {
23371
- const startedAt = performance.now();
23371
+ const startedAt2 = performance.now();
23372
23372
  profileDashboard("raw-report-rows-fetch-start", { reportId });
23373
23373
  const fetchResp = await quillFetch({
23374
23374
  client,
@@ -23389,7 +23389,7 @@ async function fetchReportRows({
23389
23389
  const resp = await parseFetchResponse(client, "report", fetchResp, getToken);
23390
23390
  profileDashboard("raw-report-rows-fetch-finish", {
23391
23391
  reportId,
23392
- elapsedMs: Math.round(performance.now() - startedAt),
23392
+ elapsedMs: Math.round(performance.now() - startedAt2),
23393
23393
  rowCount: resp.rows?.length ?? 0
23394
23394
  });
23395
23395
  if (client.databaseType?.toLowerCase() === "bigquery") {
@@ -23427,7 +23427,7 @@ async function fetchReport({
23427
23427
  }) {
23428
23428
  let reportInfo = void 0;
23429
23429
  let errorMessage = void 0;
23430
- const startedAt = performance.now();
23430
+ const startedAt2 = performance.now();
23431
23431
  try {
23432
23432
  const task = usePivotTask ? "pivot-template" : useReportTask ? "report" : "item";
23433
23433
  profileDashboard("fetch-report-start", {
@@ -23482,7 +23482,7 @@ async function fetchReport({
23482
23482
  profileDashboard("fetch-report-response", {
23483
23483
  reportId: reportId ?? null,
23484
23484
  task,
23485
- elapsedMs: Math.round(performance.now() - startedAt),
23485
+ elapsedMs: Math.round(performance.now() - startedAt2),
23486
23486
  hasRows: !!resp?.rows,
23487
23487
  rowCount: resp?.rows?.length ?? 0,
23488
23488
  hasFields: !!resp?.fields,
@@ -23507,7 +23507,7 @@ async function fetchReport({
23507
23507
  profileDashboard("fetch-report-processed", {
23508
23508
  reportId: reportId ?? null,
23509
23509
  task,
23510
- elapsedMs: Math.round(performance.now() - startedAt),
23510
+ elapsedMs: Math.round(performance.now() - startedAt2),
23511
23511
  rowCount: reportInfo.rows?.length ?? 0,
23512
23512
  pivotRowCount: reportInfo.pivotRows?.length ?? 0
23513
23513
  });
@@ -23736,6 +23736,15 @@ async function saveReport({
23736
23736
  }
23737
23737
  return data;
23738
23738
  }
23739
+ function isPlainNumericCellValue(value) {
23740
+ if (value === null || value === void 0 || value === "") return true;
23741
+ if (typeof value === "number") return Number.isFinite(value);
23742
+ if (typeof value === "bigint") return true;
23743
+ if (typeof value !== "string") return false;
23744
+ const trimmed = value.trim();
23745
+ if (!trimmed) return true;
23746
+ return /^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i.test(trimmed);
23747
+ }
23739
23748
  var formatRowsFromReport = (report, xAxisField, xAxisFormat) => {
23740
23749
  if (!report.rows || !report.columns) {
23741
23750
  return [];
@@ -23763,6 +23772,10 @@ var formatRowsFromReport = (report, xAxisField, xAxisFormat) => {
23763
23772
  const formattedRow = {};
23764
23773
  for (const column of resolvedColumns) {
23765
23774
  const rawValue = row[column.field];
23775
+ if (NUMBER_FORMAT_TYPES.includes(String(column.format ?? "")) && !isPlainNumericCellValue(rawValue)) {
23776
+ formattedRow[column.field] = rawValue === null || rawValue === void 0 || rawValue === "" ? "-" : String(rawValue);
23777
+ continue;
23778
+ }
23766
23779
  formattedRow[column.field] = column.inferFormat ? quillAutoFormat(rawValue) : getFormattedWithCache(column.format, rawValue);
23767
23780
  }
23768
23781
  return formattedRow;
@@ -39693,7 +39706,7 @@ var ChartDataLoader = ({
39693
39706
  }
39694
39707
  const fetchRowsRequestId = rowsRequestId.current + 1;
39695
39708
  rowsRequestId.current = fetchRowsRequestId;
39696
- const startedAt = performance.now();
39709
+ const startedAt2 = performance.now();
39697
39710
  profileDashboard("chart-data-loader-fetch-start", {
39698
39711
  reportId: item.id,
39699
39712
  requestId: fetchRowsRequestId,
@@ -39839,7 +39852,7 @@ var ChartDataLoader = ({
39839
39852
  reportId: item.id,
39840
39853
  requestId: fetchRowsRequestId,
39841
39854
  reason,
39842
- elapsedMs: Math.round(performance.now() - startedAt),
39855
+ elapsedMs: Math.round(performance.now() - startedAt2),
39843
39856
  isLatestRequest: fetchRowsRequestId === rowsRequestId.current,
39844
39857
  startsBackgroundFetch: !!backgroundFetch
39845
39858
  });
@@ -62391,6 +62404,345 @@ function resolveReportFromCacheCab({
62391
62404
 
62392
62405
  // src/hooks/useForm.tsx
62393
62406
  init_dashboardProfiler();
62407
+
62408
+ // src/hooks/useReport.nanPivotDiag.ts
62409
+ init_valueFormatter();
62410
+ var NAN_PIVOT_DEBUG_FLAG = "__QUILL_DEBUG_NAN_PIVOT__";
62411
+ var NAN_PIVOT_LOG_FLAG = "__QUILL_NAN_PIVOT_LOG__";
62412
+ var startedAt = Date.now();
62413
+ function globalRecord() {
62414
+ return globalThis;
62415
+ }
62416
+ function isNanPivotDebugEnabled() {
62417
+ const globalValue = globalRecord()[NAN_PIVOT_DEBUG_FLAG];
62418
+ if (globalValue === true) return true;
62419
+ if (typeof process !== "undefined" && typeof process.env !== "undefined") {
62420
+ const envValue = String(process.env.QUILL_DEBUG_NAN_PIVOT ?? "").trim().toLowerCase();
62421
+ return envValue === "1" || envValue === "true";
62422
+ }
62423
+ return false;
62424
+ }
62425
+ function getNanPivotLog() {
62426
+ const log = globalRecord()[NAN_PIVOT_LOG_FLAG];
62427
+ return Array.isArray(log) ? log : [];
62428
+ }
62429
+ function findNanValues(value, path = "root") {
62430
+ if (typeof value === "number" && Number.isNaN(value)) {
62431
+ return [`${path}=NaN`];
62432
+ }
62433
+ if (typeof value === "string" && /^[-+]?NaN(?:%|\s*)$/i.test(value.trim())) {
62434
+ return [`${path}=${JSON.stringify(value)}`];
62435
+ }
62436
+ if (Array.isArray(value)) {
62437
+ return value.flatMap(
62438
+ (entry, index) => findNanValues(entry, `${path}[${index}]`)
62439
+ );
62440
+ }
62441
+ if (value && typeof value === "object") {
62442
+ return Object.entries(value).flatMap(
62443
+ ([key, entry]) => findNanValues(entry, `${path}.${key}`)
62444
+ );
62445
+ }
62446
+ return [];
62447
+ }
62448
+ function pivotKindOf(pivot) {
62449
+ if (pivot == null) return "none";
62450
+ if (typeof pivot !== "object") return "none";
62451
+ const value = pivot;
62452
+ const rowField = String(value.rowField ?? "").trim();
62453
+ const columnField = String(value.columnField ?? "").trim();
62454
+ const hasAggregation = Array.isArray(value.aggregations) && value.aggregations.length > 0 || Boolean(String(value.aggregationType ?? "").trim());
62455
+ if (rowField && columnField) return "2D";
62456
+ if (rowField || columnField) return "1D";
62457
+ if (hasAggregation) return "aggregation-only";
62458
+ return "empty-object";
62459
+ }
62460
+ function reportDetailSurfaceOf(chart) {
62461
+ if (!chart || typeof chart !== "object") return "none";
62462
+ const value = chart;
62463
+ const chartType = String(value.chartType ?? "").trim().toLowerCase();
62464
+ if (chartType !== "table") {
62465
+ return "chart-display-chart";
62466
+ }
62467
+ const isPivotTable = Boolean(value.pivot) && Array.isArray(value.columns) && value.columns.length > 0;
62468
+ return isPivotTable ? "chart-display-pivot-table" : "flat-table";
62469
+ }
62470
+ function columnFormats(columns) {
62471
+ if (!Array.isArray(columns)) return [];
62472
+ return columns.map((column) => {
62473
+ const value = column ?? {};
62474
+ return {
62475
+ field: String(value.field ?? ""),
62476
+ label: String(value.label ?? ""),
62477
+ format: String(value.format ?? "")
62478
+ };
62479
+ });
62480
+ }
62481
+ function sampleRows(rows, count = 2) {
62482
+ return Array.isArray(rows) ? rows.slice(0, count) : [];
62483
+ }
62484
+ function hitsFor(surface, value) {
62485
+ return findNanValues(value).map((path) => ({ surface, path }));
62486
+ }
62487
+ function looksNumeric(value) {
62488
+ if (typeof value === "number") return Number.isFinite(value);
62489
+ if (typeof value !== "string") return false;
62490
+ const trimmed = value.trim();
62491
+ if (!trimmed) return false;
62492
+ return Number.isFinite(Number(trimmed.replace(/,/g, "")));
62493
+ }
62494
+ function alreadyGroupedNumberString(value) {
62495
+ return typeof value === "string" && /^-?\d{1,3}(?:,\d{3})+(?:\.\d+)?$/.test(value.trim());
62496
+ }
62497
+ function formatRowsAsChartDisplay(chart, rows, columns) {
62498
+ if (!Array.isArray(rows) || rows.length === 0) return [];
62499
+ const value = chart ?? {};
62500
+ try {
62501
+ return formatRowsFromReport(
62502
+ {
62503
+ ...value,
62504
+ rows,
62505
+ columns: Array.isArray(columns) ? columns : [],
62506
+ pivot: chart?.pivot
62507
+ },
62508
+ value.xAxisField,
62509
+ value.xAxisFormat
62510
+ );
62511
+ } catch {
62512
+ return [];
62513
+ }
62514
+ }
62515
+ function formatMismatchesFor(columns, rows) {
62516
+ if (!Array.isArray(rows) || rows.length === 0) return [];
62517
+ const mismatches = [];
62518
+ for (const column of columns) {
62519
+ if (!NUMBER_FORMAT_TYPES.includes(column.format)) continue;
62520
+ for (let index = 0; index < Math.min(rows.length, 8); index += 1) {
62521
+ const row = rows[index];
62522
+ if (!row || typeof row !== "object") continue;
62523
+ const cell = row[column.field];
62524
+ if (cell == null || cell === "") continue;
62525
+ if (looksNumeric(cell)) continue;
62526
+ mismatches.push(
62527
+ `${column.field} format=${column.format} row[${index}]=${JSON.stringify(cell)}`
62528
+ );
62529
+ break;
62530
+ }
62531
+ }
62532
+ return mismatches;
62533
+ }
62534
+ function chartDisplayRows(chart) {
62535
+ if (!chart || typeof chart !== "object") return [];
62536
+ const value = chart;
62537
+ if (String(value.chartType ?? "").toLowerCase() !== "table") {
62538
+ return Array.isArray(value.rows) ? value.rows : [];
62539
+ }
62540
+ const tableColumns = value.includeCustomFields && value.columnsWithCustomFields && !value.pivot ? value.columnsWithCustomFields : value.columns ?? value.yAxisFields ?? [];
62541
+ try {
62542
+ return formatRowsFromReport(
62543
+ { ...value, rows: value.rows ?? [], columns: tableColumns },
62544
+ value.xAxisField,
62545
+ value.xAxisFormat
62546
+ );
62547
+ } catch {
62548
+ return [];
62549
+ }
62550
+ }
62551
+ function pivotFieldHits(pivot) {
62552
+ if (!pivot || typeof pivot !== "object") return [];
62553
+ return hitsFor("pivotFields", {
62554
+ rowField: pivot.rowField,
62555
+ columnField: pivot.columnField,
62556
+ rowFieldTable: pivot.rowFieldTable,
62557
+ columnFieldTable: pivot.columnFieldTable
62558
+ });
62559
+ }
62560
+ function jsonSafe(value) {
62561
+ try {
62562
+ return JSON.parse(
62563
+ JSON.stringify(value, (_key, current) => {
62564
+ if (typeof current === "number" && Number.isNaN(current)) {
62565
+ return "__NaN__";
62566
+ }
62567
+ if (current === void 0) return "__undefined__";
62568
+ return current;
62569
+ })
62570
+ );
62571
+ } catch {
62572
+ return value;
62573
+ }
62574
+ }
62575
+ function snapshotUseReportNanState(input) {
62576
+ const chart = input.chart ?? null;
62577
+ const table = input.table ?? null;
62578
+ const sourceReport = input.sourceReport ?? null;
62579
+ const chartType = String(chart?.chartType ?? input.chartType ?? "").trim();
62580
+ const tableColumns = columnFormats(table?.columns);
62581
+ const chartColumns = columnFormats(chart?.columns);
62582
+ const tableRows = Array.isArray(table?.rows) ? table.rows : [];
62583
+ const chartRows = Array.isArray(chart?.rows) ? chart.rows : [];
62584
+ const pivotRows = Array.isArray(chart?.pivotRows) ? chart.pivotRows : [];
62585
+ const sourceRows = Array.isArray(sourceReport?.rows) ? sourceReport.rows : [];
62586
+ const sourcePivotRows = Array.isArray(sourceReport?.pivotRows) ? sourceReport.pivotRows : [];
62587
+ const displayRows = chartDisplayRows(chart);
62588
+ const doubleFormatRows = formatRowsAsChartDisplay(
62589
+ chart,
62590
+ tableRows,
62591
+ table?.columns ?? chart?.columns
62592
+ );
62593
+ const formPivot = input.formPivot ?? null;
62594
+ const nextPivot = input.nextPivot ?? null;
62595
+ const sourcePivot = sourceReport?.pivot ?? null;
62596
+ const chartPivot = chart?.pivot ?? null;
62597
+ const kind = pivotKindOf(chartPivot ?? nextPivot ?? formPivot ?? sourcePivot);
62598
+ const groupRowsBy = String(input.groupRowsBy ?? "").trim() || null;
62599
+ const groupColumnsBy = String(input.groupColumnsBy ?? "").trim() || null;
62600
+ const hasFormPivotSelection = Boolean(
62601
+ groupRowsBy || groupColumnsBy || Array.isArray(input.aggregations) && input.aggregations.some((value) => String(value ?? "").trim())
62602
+ );
62603
+ const unexpectedPivot = !hasFormPivotSelection && (kind === "1D" || kind === "2D" || kind === "aggregation-only" || kind === "empty-object");
62604
+ const nanHits = [
62605
+ ...hitsFor("useReport.table", tableRows),
62606
+ ...hitsFor("useReport.chart.rows", chartRows),
62607
+ ...hitsFor("useReport.chart.pivotRows", pivotRows),
62608
+ ...hitsFor("sourceReport.rows", sourceRows),
62609
+ ...hitsFor("sourceReport.pivotRows", sourcePivotRows),
62610
+ ...hitsFor("chartDisplay.formattedRows", displayRows),
62611
+ ...hitsFor("chartDisplay.doubleFormatTableRows", doubleFormatRows),
62612
+ ...hitsFor("columnLabels", [
62613
+ ...tableColumns.map((column) => column.label),
62614
+ ...chartColumns.map((column) => column.label)
62615
+ ]),
62616
+ ...hitsFor("columnFields", [
62617
+ ...tableColumns.map((column) => column.field),
62618
+ ...chartColumns.map((column) => column.field)
62619
+ ]),
62620
+ ...pivotFieldHits(chartPivot),
62621
+ ...pivotFieldHits(formPivot),
62622
+ ...pivotFieldHits(sourcePivot)
62623
+ ];
62624
+ return {
62625
+ at: (/* @__PURE__ */ new Date()).toISOString(),
62626
+ atMs: Date.now() - startedAt,
62627
+ source: input.source ?? "useReport",
62628
+ flow: input.flow,
62629
+ step: input.step,
62630
+ reportId: String(input.reportId ?? ""),
62631
+ datasources: Array.isArray(input.datasources) ? input.datasources.map((value) => String(value)) : [],
62632
+ chartType,
62633
+ chartLoading: Boolean(input.chartLoading),
62634
+ tableLoading: Boolean(input.tableLoading),
62635
+ loading: Boolean(input.loading),
62636
+ groupRowsBy,
62637
+ groupColumnsBy,
62638
+ aggregations: input.aggregations ?? [],
62639
+ formPivot,
62640
+ nextPivot,
62641
+ sourcePivot,
62642
+ chartPivot,
62643
+ pivotKind: kind,
62644
+ unexpectedPivot,
62645
+ isPivotTableChart: Boolean(input.isPivotTableChart),
62646
+ reportDetailSurface: reportDetailSurfaceOf(chart),
62647
+ tableColumnFormats: tableColumns,
62648
+ chartColumnFormats: chartColumns,
62649
+ tableRowCount: tableRows.length,
62650
+ chartRowCount: chartRows.length,
62651
+ pivotRowCount: pivotRows.length,
62652
+ tableSample: sampleRows(tableRows),
62653
+ chartRowSample: sampleRows(chartRows),
62654
+ pivotRowSample: sampleRows(pivotRows),
62655
+ chartDisplaySample: sampleRows(displayRows),
62656
+ doubleFormatSample: sampleRows(doubleFormatRows),
62657
+ formatMismatches: [
62658
+ ...formatMismatchesFor(tableColumns, sourceRows.length ? sourceRows : tableRows),
62659
+ ...formatMismatchesFor(chartColumns, chartRows.length ? chartRows : pivotRows),
62660
+ ...tableRows.flatMap((row, index) => {
62661
+ if (!row || typeof row !== "object") return [];
62662
+ return tableColumns.filter(
62663
+ (column) => NUMBER_FORMAT_TYPES.includes(column.format) && alreadyGroupedNumberString(
62664
+ row[column.field]
62665
+ )
62666
+ ).map(
62667
+ (column) => `already-formatted ${column.field} row[${index}]=${JSON.stringify(
62668
+ row[column.field]
62669
+ )}`
62670
+ );
62671
+ })
62672
+ ],
62673
+ nanHits
62674
+ };
62675
+ }
62676
+ var lastSerializedBySource = /* @__PURE__ */ new Map();
62677
+ function recordUseReportNanPivotState(input) {
62678
+ if (!isNanPivotDebugEnabled()) return null;
62679
+ const snapshot = snapshotUseReportNanState(input);
62680
+ const key = `${snapshot.source}:${snapshot.flow ?? ""}:${snapshot.reportId}`;
62681
+ const serialized = JSON.stringify(jsonSafe(snapshot));
62682
+ if (lastSerializedBySource.get(key) === serialized) {
62683
+ return snapshot;
62684
+ }
62685
+ lastSerializedBySource.set(key, serialized);
62686
+ const log = getNanPivotLog();
62687
+ log.push(snapshot);
62688
+ globalRecord()[NAN_PIVOT_LOG_FLAG] = log;
62689
+ const liveHits = snapshot.nanHits.filter(
62690
+ (hit) => hit.surface !== "chartDisplay.doubleFormatTableRows"
62691
+ );
62692
+ const compact = {
62693
+ atMs: snapshot.atMs,
62694
+ source: snapshot.source,
62695
+ flow: snapshot.flow,
62696
+ step: snapshot.step,
62697
+ reportId: snapshot.reportId,
62698
+ datasources: snapshot.datasources,
62699
+ chartType: snapshot.chartType,
62700
+ loading: snapshot.loading,
62701
+ chartLoading: snapshot.chartLoading,
62702
+ tableLoading: snapshot.tableLoading,
62703
+ groupRowsBy: snapshot.groupRowsBy,
62704
+ groupColumnsBy: snapshot.groupColumnsBy,
62705
+ pivotKind: snapshot.pivotKind,
62706
+ unexpectedPivot: snapshot.unexpectedPivot,
62707
+ isPivotTableChart: snapshot.isPivotTableChart,
62708
+ reportDetailSurface: snapshot.reportDetailSurface,
62709
+ tableRowCount: snapshot.tableRowCount,
62710
+ chartRowCount: snapshot.chartRowCount,
62711
+ pivotRowCount: snapshot.pivotRowCount,
62712
+ formatMismatches: snapshot.formatMismatches.slice(0, 8),
62713
+ nanHits: snapshot.nanHits.slice(0, 12)
62714
+ };
62715
+ if (liveHits.length > 0 || snapshot.unexpectedPivot) {
62716
+ console.log(
62717
+ "[useReport][nan-pivot][hit]",
62718
+ JSON.stringify(
62719
+ jsonSafe({
62720
+ ...compact,
62721
+ tableSample: snapshot.tableSample,
62722
+ chartRowSample: snapshot.chartRowSample,
62723
+ pivotRowSample: snapshot.pivotRowSample,
62724
+ chartDisplaySample: snapshot.chartDisplaySample,
62725
+ doubleFormatSample: snapshot.doubleFormatSample,
62726
+ formPivot: snapshot.formPivot,
62727
+ chartPivot: snapshot.chartPivot,
62728
+ sourcePivot: snapshot.sourcePivot
62729
+ }),
62730
+ null,
62731
+ 2
62732
+ )
62733
+ );
62734
+ } else if (snapshot.nanHits.length > 0) {
62735
+ console.log(
62736
+ "[useReport][nan-pivot][double-format]",
62737
+ JSON.stringify(compact)
62738
+ );
62739
+ } else {
62740
+ console.log("[useReport][nan-pivot][state]", JSON.stringify(compact));
62741
+ }
62742
+ return snapshot;
62743
+ }
62744
+
62745
+ // src/hooks/useForm.tsx
62394
62746
  init_reportRequestPool();
62395
62747
 
62396
62748
  // src/hooks/useForm.queries.ts
@@ -62444,6 +62796,18 @@ var DASHBOARD_REPORT_RESULT_FIELDS = [
62444
62796
  "pivotColumns",
62445
62797
  "pivotRowCount"
62446
62798
  ];
62799
+ function pivotDimensionKey(pivot) {
62800
+ const value = pivot && typeof pivot === "object" ? pivot : {};
62801
+ return [
62802
+ String(value.rowField ?? "").trim(),
62803
+ String(value.rowFieldTable ?? "").trim(),
62804
+ String(value.columnField ?? "").trim(),
62805
+ String(value.columnFieldTable ?? "").trim()
62806
+ ].join("\0");
62807
+ }
62808
+ function pivotDimensionsChanged(previous, next) {
62809
+ return pivotDimensionKey(previous?.pivot) !== pivotDimensionKey(next.pivot);
62810
+ }
62447
62811
  function dashboardReportQueryKey(reportId) {
62448
62812
  return ["quill", "dashboard-report", reportId];
62449
62813
  }
@@ -62455,8 +62819,15 @@ function mergeSavedReportIntoDashboardQueryData(previous, saved) {
62455
62819
  ...saved,
62456
62820
  ...savedId ? { id: savedId } : {}
62457
62821
  };
62822
+ const dimensionsChanged = pivotDimensionsChanged(previousReport, merged);
62458
62823
  for (const field of DASHBOARD_REPORT_RESULT_FIELDS) {
62459
- if (merged[field] == null && previousReport?.[field] != null) {
62824
+ const savedValue = saved[field];
62825
+ if (savedValue != null) continue;
62826
+ if (dimensionsChanged) {
62827
+ delete merged[field];
62828
+ continue;
62829
+ }
62830
+ if (previousReport?.[field] != null) {
62460
62831
  merged[field] = previousReport[field];
62461
62832
  }
62462
62833
  }
@@ -66579,7 +66950,7 @@ function useReport(reportIdArg, options = {}) {
66579
66950
  pivot,
66580
66951
  operation
66581
66952
  }) => {
66582
- const startedAt = performance.now();
66953
+ const startedAt2 = performance.now();
66583
66954
  const snapshot = await cacheCab.getReportSnapshot(
66584
66955
  effectiveReportId,
66585
66956
  tenants
@@ -66598,7 +66969,7 @@ function useReport(reportIdArg, options = {}) {
66598
66969
  )
66599
66970
  });
66600
66971
  const diagnostics = globalThis.__QUILL_CACHECAB_DIAGNOSTICS__;
66601
- const durationMs = performance.now() - startedAt;
66972
+ const durationMs = performance.now() - startedAt2;
66602
66973
  const outcome = resolution.resolved ? "hit" : "fallback";
66603
66974
  if (Array.isArray(diagnostics)) {
66604
66975
  diagnostics.push({
@@ -69586,11 +69957,12 @@ function useReport(reportIdArg, options = {}) {
69586
69957
  registerOption(column.field, column.label, column.format);
69587
69958
  }
69588
69959
  for (const yAxis2 of yAxisResolved.measures) {
69589
- registerOption(yAxis2.field, yAxis2.label, yAxis2.format);
69960
+ const field = String(yAxis2.field ?? "").trim();
69961
+ registerOption(field, field, yAxis2.format);
69590
69962
  if (yAxis2.format) {
69591
- const existing = optionsByField.get(yAxis2.field);
69963
+ const existing = optionsByField.get(field);
69592
69964
  if (existing) {
69593
- optionsByField.set(yAxis2.field, {
69965
+ optionsByField.set(field, {
69594
69966
  ...existing,
69595
69967
  format: toAxisFormat(yAxis2.format, existing.format)
69596
69968
  });
@@ -69667,12 +70039,17 @@ function useReport(reportIdArg, options = {}) {
69667
70039
  ];
69668
70040
  }, [chartAxisOptions, yAxisResolved.measures]);
69669
70041
  const yAxisOptions = (0, import_react63.useMemo)(
69670
- () => yAxisMeasures.map((measure) => ({
69671
- value: measure.field,
69672
- label: measure.label,
69673
- format: toAxisFormat(measure.format, "string")
69674
- })),
69675
- [yAxisMeasures]
70042
+ () => yAxisMeasures.map((measure) => {
70043
+ const field = String(measure.field ?? "").trim();
70044
+ const catalog = chartAxisOptionByField.get(field);
70045
+ if (catalog) return catalog;
70046
+ return {
70047
+ value: field,
70048
+ label: toTitleCaseLabel(field),
70049
+ format: toAxisFormat(measure.format, "string")
70050
+ };
70051
+ }),
70052
+ [chartAxisOptionByField, yAxisMeasures]
69676
70053
  );
69677
70054
  const normalizedChartYAxisOptions = (0, import_react63.useMemo)(() => {
69678
70055
  return yAxisOptions.map((option) => ({
@@ -70012,11 +70389,7 @@ function useReport(reportIdArg, options = {}) {
70012
70389
  );
70013
70390
  const isPivotTableChart = String(chartType ?? "").toLowerCase() === "table" && Boolean(pivotState);
70014
70391
  const pivotTableColumns = (0, import_react63.useMemo)(
70015
- () => isPivotTableChart ? buildPivotTableColumns(
70016
- pivotState,
70017
- chart,
70018
- resolveTableDisplayLabel
70019
- ) : null,
70392
+ () => isPivotTableChart ? buildPivotTableColumns(pivotState, chart, resolveTableDisplayLabel) : null,
70020
70393
  [chart, isPivotTableChart, pivotState, resolveTableDisplayLabel]
70021
70394
  );
70022
70395
  const chartAxes = (0, import_react63.useMemo)(() => {
@@ -70667,6 +71040,43 @@ function useReport(reportIdArg, options = {}) {
70667
71040
  tableRefreshQuery.isFetching,
70668
71041
  tableRefreshQueryEnabled
70669
71042
  ]);
71043
+ (0, import_react63.useEffect)(() => {
71044
+ if (!isNanPivotDebugEnabled()) return;
71045
+ recordUseReportNanPivotState({
71046
+ source: "useReport",
71047
+ reportId: effectiveReportId,
71048
+ datasources,
71049
+ chartType: resolvedChartType,
71050
+ chartLoading,
71051
+ tableLoading,
71052
+ loading,
71053
+ groupRowsBy,
71054
+ groupColumnsBy,
71055
+ aggregations: aggregationValues,
71056
+ formPivot: pivotState,
71057
+ nextPivot,
71058
+ sourceReport,
71059
+ chart: chartWithSort,
71060
+ table,
71061
+ isPivotTableChart
71062
+ });
71063
+ }, [
71064
+ aggregationValues,
71065
+ chartLoading,
71066
+ chartWithSort,
71067
+ datasources,
71068
+ effectiveReportId,
71069
+ groupColumnsBy,
71070
+ groupRowsBy,
71071
+ isPivotTableChart,
71072
+ loading,
71073
+ nextPivot,
71074
+ pivotState,
71075
+ resolvedChartType,
71076
+ sourceReport,
71077
+ table,
71078
+ tableLoading
71079
+ ]);
70670
71080
  const buildSetReportColumnsFromIds = (nextColumnIds, settingsById) => {
70671
71081
  const normalizedNextIds = nextColumnIds.map((columnId) => String(columnId ?? "").trim()).filter(Boolean);
70672
71082
  return normalizedNextIds.map((columnId) => {
@@ -71512,7 +71922,12 @@ function useReport(reportIdArg, options = {}) {
71512
71922
  yAxisFields: yAxisMeasures,
71513
71923
  pivot: effectiveReportBuilderState.pivot ?? null,
71514
71924
  reportBuilderState: effectiveReportBuilderState,
71515
- columns: columnsSelectedForSave
71925
+ columns: columnsSelectedForSave,
71926
+ rows: sourceReport.rows,
71927
+ compareRows: sourceReport.compareRows,
71928
+ pivotRows: sourceReport.pivotRows,
71929
+ pivotColumns: sourceReport.pivotColumns,
71930
+ pivotRowCount: sourceReport.pivotRowCount
71516
71931
  };
71517
71932
  syncDashboardReportQueryCacheAfterSave(
71518
71933
  queryClient,
package/dist/index.js CHANGED
@@ -23350,7 +23350,7 @@ async function fetchReportRows({
23350
23350
  additionalProcessing,
23351
23351
  overwriteCache = false
23352
23352
  }) {
23353
- const startedAt = performance.now();
23353
+ const startedAt2 = performance.now();
23354
23354
  profileDashboard("raw-report-rows-fetch-start", { reportId });
23355
23355
  const fetchResp = await quillFetch({
23356
23356
  client,
@@ -23371,7 +23371,7 @@ async function fetchReportRows({
23371
23371
  const resp = await parseFetchResponse(client, "report", fetchResp, getToken);
23372
23372
  profileDashboard("raw-report-rows-fetch-finish", {
23373
23373
  reportId,
23374
- elapsedMs: Math.round(performance.now() - startedAt),
23374
+ elapsedMs: Math.round(performance.now() - startedAt2),
23375
23375
  rowCount: resp.rows?.length ?? 0
23376
23376
  });
23377
23377
  if (client.databaseType?.toLowerCase() === "bigquery") {
@@ -23409,7 +23409,7 @@ async function fetchReport({
23409
23409
  }) {
23410
23410
  let reportInfo = void 0;
23411
23411
  let errorMessage = void 0;
23412
- const startedAt = performance.now();
23412
+ const startedAt2 = performance.now();
23413
23413
  try {
23414
23414
  const task = usePivotTask ? "pivot-template" : useReportTask ? "report" : "item";
23415
23415
  profileDashboard("fetch-report-start", {
@@ -23464,7 +23464,7 @@ async function fetchReport({
23464
23464
  profileDashboard("fetch-report-response", {
23465
23465
  reportId: reportId ?? null,
23466
23466
  task,
23467
- elapsedMs: Math.round(performance.now() - startedAt),
23467
+ elapsedMs: Math.round(performance.now() - startedAt2),
23468
23468
  hasRows: !!resp?.rows,
23469
23469
  rowCount: resp?.rows?.length ?? 0,
23470
23470
  hasFields: !!resp?.fields,
@@ -23489,7 +23489,7 @@ async function fetchReport({
23489
23489
  profileDashboard("fetch-report-processed", {
23490
23490
  reportId: reportId ?? null,
23491
23491
  task,
23492
- elapsedMs: Math.round(performance.now() - startedAt),
23492
+ elapsedMs: Math.round(performance.now() - startedAt2),
23493
23493
  rowCount: reportInfo.rows?.length ?? 0,
23494
23494
  pivotRowCount: reportInfo.pivotRows?.length ?? 0
23495
23495
  });
@@ -23718,6 +23718,15 @@ async function saveReport({
23718
23718
  }
23719
23719
  return data;
23720
23720
  }
23721
+ function isPlainNumericCellValue(value) {
23722
+ if (value === null || value === void 0 || value === "") return true;
23723
+ if (typeof value === "number") return Number.isFinite(value);
23724
+ if (typeof value === "bigint") return true;
23725
+ if (typeof value !== "string") return false;
23726
+ const trimmed = value.trim();
23727
+ if (!trimmed) return true;
23728
+ return /^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i.test(trimmed);
23729
+ }
23721
23730
  var formatRowsFromReport = (report, xAxisField, xAxisFormat) => {
23722
23731
  if (!report.rows || !report.columns) {
23723
23732
  return [];
@@ -23745,6 +23754,10 @@ var formatRowsFromReport = (report, xAxisField, xAxisFormat) => {
23745
23754
  const formattedRow = {};
23746
23755
  for (const column of resolvedColumns) {
23747
23756
  const rawValue = row[column.field];
23757
+ if (NUMBER_FORMAT_TYPES.includes(String(column.format ?? "")) && !isPlainNumericCellValue(rawValue)) {
23758
+ formattedRow[column.field] = rawValue === null || rawValue === void 0 || rawValue === "" ? "-" : String(rawValue);
23759
+ continue;
23760
+ }
23748
23761
  formattedRow[column.field] = column.inferFormat ? quillAutoFormat(rawValue) : getFormattedWithCache(column.format, rawValue);
23749
23762
  }
23750
23763
  return formattedRow;
@@ -39780,7 +39793,7 @@ var ChartDataLoader = ({
39780
39793
  }
39781
39794
  const fetchRowsRequestId = rowsRequestId.current + 1;
39782
39795
  rowsRequestId.current = fetchRowsRequestId;
39783
- const startedAt = performance.now();
39796
+ const startedAt2 = performance.now();
39784
39797
  profileDashboard("chart-data-loader-fetch-start", {
39785
39798
  reportId: item.id,
39786
39799
  requestId: fetchRowsRequestId,
@@ -39926,7 +39939,7 @@ var ChartDataLoader = ({
39926
39939
  reportId: item.id,
39927
39940
  requestId: fetchRowsRequestId,
39928
39941
  reason,
39929
- elapsedMs: Math.round(performance.now() - startedAt),
39942
+ elapsedMs: Math.round(performance.now() - startedAt2),
39930
39943
  isLatestRequest: fetchRowsRequestId === rowsRequestId.current,
39931
39944
  startsBackgroundFetch: !!backgroundFetch
39932
39945
  });
@@ -62599,6 +62612,345 @@ function resolveReportFromCacheCab({
62599
62612
 
62600
62613
  // src/hooks/useForm.tsx
62601
62614
  init_dashboardProfiler();
62615
+
62616
+ // src/hooks/useReport.nanPivotDiag.ts
62617
+ init_valueFormatter();
62618
+ var NAN_PIVOT_DEBUG_FLAG = "__QUILL_DEBUG_NAN_PIVOT__";
62619
+ var NAN_PIVOT_LOG_FLAG = "__QUILL_NAN_PIVOT_LOG__";
62620
+ var startedAt = Date.now();
62621
+ function globalRecord() {
62622
+ return globalThis;
62623
+ }
62624
+ function isNanPivotDebugEnabled() {
62625
+ const globalValue = globalRecord()[NAN_PIVOT_DEBUG_FLAG];
62626
+ if (globalValue === true) return true;
62627
+ if (typeof process !== "undefined" && typeof process.env !== "undefined") {
62628
+ const envValue = String(process.env.QUILL_DEBUG_NAN_PIVOT ?? "").trim().toLowerCase();
62629
+ return envValue === "1" || envValue === "true";
62630
+ }
62631
+ return false;
62632
+ }
62633
+ function getNanPivotLog() {
62634
+ const log = globalRecord()[NAN_PIVOT_LOG_FLAG];
62635
+ return Array.isArray(log) ? log : [];
62636
+ }
62637
+ function findNanValues(value, path = "root") {
62638
+ if (typeof value === "number" && Number.isNaN(value)) {
62639
+ return [`${path}=NaN`];
62640
+ }
62641
+ if (typeof value === "string" && /^[-+]?NaN(?:%|\s*)$/i.test(value.trim())) {
62642
+ return [`${path}=${JSON.stringify(value)}`];
62643
+ }
62644
+ if (Array.isArray(value)) {
62645
+ return value.flatMap(
62646
+ (entry, index) => findNanValues(entry, `${path}[${index}]`)
62647
+ );
62648
+ }
62649
+ if (value && typeof value === "object") {
62650
+ return Object.entries(value).flatMap(
62651
+ ([key, entry]) => findNanValues(entry, `${path}.${key}`)
62652
+ );
62653
+ }
62654
+ return [];
62655
+ }
62656
+ function pivotKindOf(pivot) {
62657
+ if (pivot == null) return "none";
62658
+ if (typeof pivot !== "object") return "none";
62659
+ const value = pivot;
62660
+ const rowField = String(value.rowField ?? "").trim();
62661
+ const columnField = String(value.columnField ?? "").trim();
62662
+ const hasAggregation = Array.isArray(value.aggregations) && value.aggregations.length > 0 || Boolean(String(value.aggregationType ?? "").trim());
62663
+ if (rowField && columnField) return "2D";
62664
+ if (rowField || columnField) return "1D";
62665
+ if (hasAggregation) return "aggregation-only";
62666
+ return "empty-object";
62667
+ }
62668
+ function reportDetailSurfaceOf(chart) {
62669
+ if (!chart || typeof chart !== "object") return "none";
62670
+ const value = chart;
62671
+ const chartType = String(value.chartType ?? "").trim().toLowerCase();
62672
+ if (chartType !== "table") {
62673
+ return "chart-display-chart";
62674
+ }
62675
+ const isPivotTable = Boolean(value.pivot) && Array.isArray(value.columns) && value.columns.length > 0;
62676
+ return isPivotTable ? "chart-display-pivot-table" : "flat-table";
62677
+ }
62678
+ function columnFormats(columns) {
62679
+ if (!Array.isArray(columns)) return [];
62680
+ return columns.map((column) => {
62681
+ const value = column ?? {};
62682
+ return {
62683
+ field: String(value.field ?? ""),
62684
+ label: String(value.label ?? ""),
62685
+ format: String(value.format ?? "")
62686
+ };
62687
+ });
62688
+ }
62689
+ function sampleRows(rows, count = 2) {
62690
+ return Array.isArray(rows) ? rows.slice(0, count) : [];
62691
+ }
62692
+ function hitsFor(surface, value) {
62693
+ return findNanValues(value).map((path) => ({ surface, path }));
62694
+ }
62695
+ function looksNumeric(value) {
62696
+ if (typeof value === "number") return Number.isFinite(value);
62697
+ if (typeof value !== "string") return false;
62698
+ const trimmed = value.trim();
62699
+ if (!trimmed) return false;
62700
+ return Number.isFinite(Number(trimmed.replace(/,/g, "")));
62701
+ }
62702
+ function alreadyGroupedNumberString(value) {
62703
+ return typeof value === "string" && /^-?\d{1,3}(?:,\d{3})+(?:\.\d+)?$/.test(value.trim());
62704
+ }
62705
+ function formatRowsAsChartDisplay(chart, rows, columns) {
62706
+ if (!Array.isArray(rows) || rows.length === 0) return [];
62707
+ const value = chart ?? {};
62708
+ try {
62709
+ return formatRowsFromReport(
62710
+ {
62711
+ ...value,
62712
+ rows,
62713
+ columns: Array.isArray(columns) ? columns : [],
62714
+ pivot: chart?.pivot
62715
+ },
62716
+ value.xAxisField,
62717
+ value.xAxisFormat
62718
+ );
62719
+ } catch {
62720
+ return [];
62721
+ }
62722
+ }
62723
+ function formatMismatchesFor(columns, rows) {
62724
+ if (!Array.isArray(rows) || rows.length === 0) return [];
62725
+ const mismatches = [];
62726
+ for (const column of columns) {
62727
+ if (!NUMBER_FORMAT_TYPES.includes(column.format)) continue;
62728
+ for (let index = 0; index < Math.min(rows.length, 8); index += 1) {
62729
+ const row = rows[index];
62730
+ if (!row || typeof row !== "object") continue;
62731
+ const cell = row[column.field];
62732
+ if (cell == null || cell === "") continue;
62733
+ if (looksNumeric(cell)) continue;
62734
+ mismatches.push(
62735
+ `${column.field} format=${column.format} row[${index}]=${JSON.stringify(cell)}`
62736
+ );
62737
+ break;
62738
+ }
62739
+ }
62740
+ return mismatches;
62741
+ }
62742
+ function chartDisplayRows(chart) {
62743
+ if (!chart || typeof chart !== "object") return [];
62744
+ const value = chart;
62745
+ if (String(value.chartType ?? "").toLowerCase() !== "table") {
62746
+ return Array.isArray(value.rows) ? value.rows : [];
62747
+ }
62748
+ const tableColumns = value.includeCustomFields && value.columnsWithCustomFields && !value.pivot ? value.columnsWithCustomFields : value.columns ?? value.yAxisFields ?? [];
62749
+ try {
62750
+ return formatRowsFromReport(
62751
+ { ...value, rows: value.rows ?? [], columns: tableColumns },
62752
+ value.xAxisField,
62753
+ value.xAxisFormat
62754
+ );
62755
+ } catch {
62756
+ return [];
62757
+ }
62758
+ }
62759
+ function pivotFieldHits(pivot) {
62760
+ if (!pivot || typeof pivot !== "object") return [];
62761
+ return hitsFor("pivotFields", {
62762
+ rowField: pivot.rowField,
62763
+ columnField: pivot.columnField,
62764
+ rowFieldTable: pivot.rowFieldTable,
62765
+ columnFieldTable: pivot.columnFieldTable
62766
+ });
62767
+ }
62768
+ function jsonSafe(value) {
62769
+ try {
62770
+ return JSON.parse(
62771
+ JSON.stringify(value, (_key, current) => {
62772
+ if (typeof current === "number" && Number.isNaN(current)) {
62773
+ return "__NaN__";
62774
+ }
62775
+ if (current === void 0) return "__undefined__";
62776
+ return current;
62777
+ })
62778
+ );
62779
+ } catch {
62780
+ return value;
62781
+ }
62782
+ }
62783
+ function snapshotUseReportNanState(input) {
62784
+ const chart = input.chart ?? null;
62785
+ const table = input.table ?? null;
62786
+ const sourceReport = input.sourceReport ?? null;
62787
+ const chartType = String(chart?.chartType ?? input.chartType ?? "").trim();
62788
+ const tableColumns = columnFormats(table?.columns);
62789
+ const chartColumns = columnFormats(chart?.columns);
62790
+ const tableRows = Array.isArray(table?.rows) ? table.rows : [];
62791
+ const chartRows = Array.isArray(chart?.rows) ? chart.rows : [];
62792
+ const pivotRows = Array.isArray(chart?.pivotRows) ? chart.pivotRows : [];
62793
+ const sourceRows = Array.isArray(sourceReport?.rows) ? sourceReport.rows : [];
62794
+ const sourcePivotRows = Array.isArray(sourceReport?.pivotRows) ? sourceReport.pivotRows : [];
62795
+ const displayRows = chartDisplayRows(chart);
62796
+ const doubleFormatRows = formatRowsAsChartDisplay(
62797
+ chart,
62798
+ tableRows,
62799
+ table?.columns ?? chart?.columns
62800
+ );
62801
+ const formPivot = input.formPivot ?? null;
62802
+ const nextPivot = input.nextPivot ?? null;
62803
+ const sourcePivot = sourceReport?.pivot ?? null;
62804
+ const chartPivot = chart?.pivot ?? null;
62805
+ const kind = pivotKindOf(chartPivot ?? nextPivot ?? formPivot ?? sourcePivot);
62806
+ const groupRowsBy = String(input.groupRowsBy ?? "").trim() || null;
62807
+ const groupColumnsBy = String(input.groupColumnsBy ?? "").trim() || null;
62808
+ const hasFormPivotSelection = Boolean(
62809
+ groupRowsBy || groupColumnsBy || Array.isArray(input.aggregations) && input.aggregations.some((value) => String(value ?? "").trim())
62810
+ );
62811
+ const unexpectedPivot = !hasFormPivotSelection && (kind === "1D" || kind === "2D" || kind === "aggregation-only" || kind === "empty-object");
62812
+ const nanHits = [
62813
+ ...hitsFor("useReport.table", tableRows),
62814
+ ...hitsFor("useReport.chart.rows", chartRows),
62815
+ ...hitsFor("useReport.chart.pivotRows", pivotRows),
62816
+ ...hitsFor("sourceReport.rows", sourceRows),
62817
+ ...hitsFor("sourceReport.pivotRows", sourcePivotRows),
62818
+ ...hitsFor("chartDisplay.formattedRows", displayRows),
62819
+ ...hitsFor("chartDisplay.doubleFormatTableRows", doubleFormatRows),
62820
+ ...hitsFor("columnLabels", [
62821
+ ...tableColumns.map((column) => column.label),
62822
+ ...chartColumns.map((column) => column.label)
62823
+ ]),
62824
+ ...hitsFor("columnFields", [
62825
+ ...tableColumns.map((column) => column.field),
62826
+ ...chartColumns.map((column) => column.field)
62827
+ ]),
62828
+ ...pivotFieldHits(chartPivot),
62829
+ ...pivotFieldHits(formPivot),
62830
+ ...pivotFieldHits(sourcePivot)
62831
+ ];
62832
+ return {
62833
+ at: (/* @__PURE__ */ new Date()).toISOString(),
62834
+ atMs: Date.now() - startedAt,
62835
+ source: input.source ?? "useReport",
62836
+ flow: input.flow,
62837
+ step: input.step,
62838
+ reportId: String(input.reportId ?? ""),
62839
+ datasources: Array.isArray(input.datasources) ? input.datasources.map((value) => String(value)) : [],
62840
+ chartType,
62841
+ chartLoading: Boolean(input.chartLoading),
62842
+ tableLoading: Boolean(input.tableLoading),
62843
+ loading: Boolean(input.loading),
62844
+ groupRowsBy,
62845
+ groupColumnsBy,
62846
+ aggregations: input.aggregations ?? [],
62847
+ formPivot,
62848
+ nextPivot,
62849
+ sourcePivot,
62850
+ chartPivot,
62851
+ pivotKind: kind,
62852
+ unexpectedPivot,
62853
+ isPivotTableChart: Boolean(input.isPivotTableChart),
62854
+ reportDetailSurface: reportDetailSurfaceOf(chart),
62855
+ tableColumnFormats: tableColumns,
62856
+ chartColumnFormats: chartColumns,
62857
+ tableRowCount: tableRows.length,
62858
+ chartRowCount: chartRows.length,
62859
+ pivotRowCount: pivotRows.length,
62860
+ tableSample: sampleRows(tableRows),
62861
+ chartRowSample: sampleRows(chartRows),
62862
+ pivotRowSample: sampleRows(pivotRows),
62863
+ chartDisplaySample: sampleRows(displayRows),
62864
+ doubleFormatSample: sampleRows(doubleFormatRows),
62865
+ formatMismatches: [
62866
+ ...formatMismatchesFor(tableColumns, sourceRows.length ? sourceRows : tableRows),
62867
+ ...formatMismatchesFor(chartColumns, chartRows.length ? chartRows : pivotRows),
62868
+ ...tableRows.flatMap((row, index) => {
62869
+ if (!row || typeof row !== "object") return [];
62870
+ return tableColumns.filter(
62871
+ (column) => NUMBER_FORMAT_TYPES.includes(column.format) && alreadyGroupedNumberString(
62872
+ row[column.field]
62873
+ )
62874
+ ).map(
62875
+ (column) => `already-formatted ${column.field} row[${index}]=${JSON.stringify(
62876
+ row[column.field]
62877
+ )}`
62878
+ );
62879
+ })
62880
+ ],
62881
+ nanHits
62882
+ };
62883
+ }
62884
+ var lastSerializedBySource = /* @__PURE__ */ new Map();
62885
+ function recordUseReportNanPivotState(input) {
62886
+ if (!isNanPivotDebugEnabled()) return null;
62887
+ const snapshot = snapshotUseReportNanState(input);
62888
+ const key = `${snapshot.source}:${snapshot.flow ?? ""}:${snapshot.reportId}`;
62889
+ const serialized = JSON.stringify(jsonSafe(snapshot));
62890
+ if (lastSerializedBySource.get(key) === serialized) {
62891
+ return snapshot;
62892
+ }
62893
+ lastSerializedBySource.set(key, serialized);
62894
+ const log = getNanPivotLog();
62895
+ log.push(snapshot);
62896
+ globalRecord()[NAN_PIVOT_LOG_FLAG] = log;
62897
+ const liveHits = snapshot.nanHits.filter(
62898
+ (hit) => hit.surface !== "chartDisplay.doubleFormatTableRows"
62899
+ );
62900
+ const compact = {
62901
+ atMs: snapshot.atMs,
62902
+ source: snapshot.source,
62903
+ flow: snapshot.flow,
62904
+ step: snapshot.step,
62905
+ reportId: snapshot.reportId,
62906
+ datasources: snapshot.datasources,
62907
+ chartType: snapshot.chartType,
62908
+ loading: snapshot.loading,
62909
+ chartLoading: snapshot.chartLoading,
62910
+ tableLoading: snapshot.tableLoading,
62911
+ groupRowsBy: snapshot.groupRowsBy,
62912
+ groupColumnsBy: snapshot.groupColumnsBy,
62913
+ pivotKind: snapshot.pivotKind,
62914
+ unexpectedPivot: snapshot.unexpectedPivot,
62915
+ isPivotTableChart: snapshot.isPivotTableChart,
62916
+ reportDetailSurface: snapshot.reportDetailSurface,
62917
+ tableRowCount: snapshot.tableRowCount,
62918
+ chartRowCount: snapshot.chartRowCount,
62919
+ pivotRowCount: snapshot.pivotRowCount,
62920
+ formatMismatches: snapshot.formatMismatches.slice(0, 8),
62921
+ nanHits: snapshot.nanHits.slice(0, 12)
62922
+ };
62923
+ if (liveHits.length > 0 || snapshot.unexpectedPivot) {
62924
+ console.log(
62925
+ "[useReport][nan-pivot][hit]",
62926
+ JSON.stringify(
62927
+ jsonSafe({
62928
+ ...compact,
62929
+ tableSample: snapshot.tableSample,
62930
+ chartRowSample: snapshot.chartRowSample,
62931
+ pivotRowSample: snapshot.pivotRowSample,
62932
+ chartDisplaySample: snapshot.chartDisplaySample,
62933
+ doubleFormatSample: snapshot.doubleFormatSample,
62934
+ formPivot: snapshot.formPivot,
62935
+ chartPivot: snapshot.chartPivot,
62936
+ sourcePivot: snapshot.sourcePivot
62937
+ }),
62938
+ null,
62939
+ 2
62940
+ )
62941
+ );
62942
+ } else if (snapshot.nanHits.length > 0) {
62943
+ console.log(
62944
+ "[useReport][nan-pivot][double-format]",
62945
+ JSON.stringify(compact)
62946
+ );
62947
+ } else {
62948
+ console.log("[useReport][nan-pivot][state]", JSON.stringify(compact));
62949
+ }
62950
+ return snapshot;
62951
+ }
62952
+
62953
+ // src/hooks/useForm.tsx
62602
62954
  init_reportRequestPool();
62603
62955
 
62604
62956
  // src/hooks/useForm.queries.ts
@@ -62652,6 +63004,18 @@ var DASHBOARD_REPORT_RESULT_FIELDS = [
62652
63004
  "pivotColumns",
62653
63005
  "pivotRowCount"
62654
63006
  ];
63007
+ function pivotDimensionKey(pivot) {
63008
+ const value = pivot && typeof pivot === "object" ? pivot : {};
63009
+ return [
63010
+ String(value.rowField ?? "").trim(),
63011
+ String(value.rowFieldTable ?? "").trim(),
63012
+ String(value.columnField ?? "").trim(),
63013
+ String(value.columnFieldTable ?? "").trim()
63014
+ ].join("\0");
63015
+ }
63016
+ function pivotDimensionsChanged(previous, next) {
63017
+ return pivotDimensionKey(previous?.pivot) !== pivotDimensionKey(next.pivot);
63018
+ }
62655
63019
  function dashboardReportQueryKey(reportId) {
62656
63020
  return ["quill", "dashboard-report", reportId];
62657
63021
  }
@@ -62663,8 +63027,15 @@ function mergeSavedReportIntoDashboardQueryData(previous, saved) {
62663
63027
  ...saved,
62664
63028
  ...savedId ? { id: savedId } : {}
62665
63029
  };
63030
+ const dimensionsChanged = pivotDimensionsChanged(previousReport, merged);
62666
63031
  for (const field of DASHBOARD_REPORT_RESULT_FIELDS) {
62667
- if (merged[field] == null && previousReport?.[field] != null) {
63032
+ const savedValue = saved[field];
63033
+ if (savedValue != null) continue;
63034
+ if (dimensionsChanged) {
63035
+ delete merged[field];
63036
+ continue;
63037
+ }
63038
+ if (previousReport?.[field] != null) {
62668
63039
  merged[field] = previousReport[field];
62669
63040
  }
62670
63041
  }
@@ -66787,7 +67158,7 @@ function useReport(reportIdArg, options = {}) {
66787
67158
  pivot,
66788
67159
  operation
66789
67160
  }) => {
66790
- const startedAt = performance.now();
67161
+ const startedAt2 = performance.now();
66791
67162
  const snapshot = await cacheCab.getReportSnapshot(
66792
67163
  effectiveReportId,
66793
67164
  tenants
@@ -66806,7 +67177,7 @@ function useReport(reportIdArg, options = {}) {
66806
67177
  )
66807
67178
  });
66808
67179
  const diagnostics = globalThis.__QUILL_CACHECAB_DIAGNOSTICS__;
66809
- const durationMs = performance.now() - startedAt;
67180
+ const durationMs = performance.now() - startedAt2;
66810
67181
  const outcome = resolution.resolved ? "hit" : "fallback";
66811
67182
  if (Array.isArray(diagnostics)) {
66812
67183
  diagnostics.push({
@@ -69794,11 +70165,12 @@ function useReport(reportIdArg, options = {}) {
69794
70165
  registerOption(column.field, column.label, column.format);
69795
70166
  }
69796
70167
  for (const yAxis2 of yAxisResolved.measures) {
69797
- registerOption(yAxis2.field, yAxis2.label, yAxis2.format);
70168
+ const field = String(yAxis2.field ?? "").trim();
70169
+ registerOption(field, field, yAxis2.format);
69798
70170
  if (yAxis2.format) {
69799
- const existing = optionsByField.get(yAxis2.field);
70171
+ const existing = optionsByField.get(field);
69800
70172
  if (existing) {
69801
- optionsByField.set(yAxis2.field, {
70173
+ optionsByField.set(field, {
69802
70174
  ...existing,
69803
70175
  format: toAxisFormat(yAxis2.format, existing.format)
69804
70176
  });
@@ -69875,12 +70247,17 @@ function useReport(reportIdArg, options = {}) {
69875
70247
  ];
69876
70248
  }, [chartAxisOptions, yAxisResolved.measures]);
69877
70249
  const yAxisOptions = useMemo33(
69878
- () => yAxisMeasures.map((measure) => ({
69879
- value: measure.field,
69880
- label: measure.label,
69881
- format: toAxisFormat(measure.format, "string")
69882
- })),
69883
- [yAxisMeasures]
70250
+ () => yAxisMeasures.map((measure) => {
70251
+ const field = String(measure.field ?? "").trim();
70252
+ const catalog = chartAxisOptionByField.get(field);
70253
+ if (catalog) return catalog;
70254
+ return {
70255
+ value: field,
70256
+ label: toTitleCaseLabel(field),
70257
+ format: toAxisFormat(measure.format, "string")
70258
+ };
70259
+ }),
70260
+ [chartAxisOptionByField, yAxisMeasures]
69884
70261
  );
69885
70262
  const normalizedChartYAxisOptions = useMemo33(() => {
69886
70263
  return yAxisOptions.map((option) => ({
@@ -70220,11 +70597,7 @@ function useReport(reportIdArg, options = {}) {
70220
70597
  );
70221
70598
  const isPivotTableChart = String(chartType ?? "").toLowerCase() === "table" && Boolean(pivotState);
70222
70599
  const pivotTableColumns = useMemo33(
70223
- () => isPivotTableChart ? buildPivotTableColumns(
70224
- pivotState,
70225
- chart,
70226
- resolveTableDisplayLabel
70227
- ) : null,
70600
+ () => isPivotTableChart ? buildPivotTableColumns(pivotState, chart, resolveTableDisplayLabel) : null,
70228
70601
  [chart, isPivotTableChart, pivotState, resolveTableDisplayLabel]
70229
70602
  );
70230
70603
  const chartAxes = useMemo33(() => {
@@ -70875,6 +71248,43 @@ function useReport(reportIdArg, options = {}) {
70875
71248
  tableRefreshQuery.isFetching,
70876
71249
  tableRefreshQueryEnabled
70877
71250
  ]);
71251
+ useEffect33(() => {
71252
+ if (!isNanPivotDebugEnabled()) return;
71253
+ recordUseReportNanPivotState({
71254
+ source: "useReport",
71255
+ reportId: effectiveReportId,
71256
+ datasources,
71257
+ chartType: resolvedChartType,
71258
+ chartLoading,
71259
+ tableLoading,
71260
+ loading,
71261
+ groupRowsBy,
71262
+ groupColumnsBy,
71263
+ aggregations: aggregationValues,
71264
+ formPivot: pivotState,
71265
+ nextPivot,
71266
+ sourceReport,
71267
+ chart: chartWithSort,
71268
+ table,
71269
+ isPivotTableChart
71270
+ });
71271
+ }, [
71272
+ aggregationValues,
71273
+ chartLoading,
71274
+ chartWithSort,
71275
+ datasources,
71276
+ effectiveReportId,
71277
+ groupColumnsBy,
71278
+ groupRowsBy,
71279
+ isPivotTableChart,
71280
+ loading,
71281
+ nextPivot,
71282
+ pivotState,
71283
+ resolvedChartType,
71284
+ sourceReport,
71285
+ table,
71286
+ tableLoading
71287
+ ]);
70878
71288
  const buildSetReportColumnsFromIds = (nextColumnIds, settingsById) => {
70879
71289
  const normalizedNextIds = nextColumnIds.map((columnId) => String(columnId ?? "").trim()).filter(Boolean);
70880
71290
  return normalizedNextIds.map((columnId) => {
@@ -71720,7 +72130,12 @@ function useReport(reportIdArg, options = {}) {
71720
72130
  yAxisFields: yAxisMeasures,
71721
72131
  pivot: effectiveReportBuilderState.pivot ?? null,
71722
72132
  reportBuilderState: effectiveReportBuilderState,
71723
- columns: columnsSelectedForSave
72133
+ columns: columnsSelectedForSave,
72134
+ rows: sourceReport.rows,
72135
+ compareRows: sourceReport.compareRows,
72136
+ pivotRows: sourceReport.pivotRows,
72137
+ pivotColumns: sourceReport.pivotColumns,
72138
+ pivotRowCount: sourceReport.pivotRowCount
71724
72139
  };
71725
72140
  syncDashboardReportQueryCacheAfterSave(
71726
72141
  queryClient,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quillsql/react",
3
- "version": "2.16.100",
3
+ "version": "2.16.102",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {