@quillsql/react 2.16.101 → 2.16.103

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 +446 -38
  2. package/dist/index.js +446 -38
  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
@@ -63570,22 +63922,21 @@ function isPlaceholderAxisLabel(label, field) {
63570
63922
  function resolvedAxisLabel(savedLabel, field, generatedLabel) {
63571
63923
  return isPlaceholderAxisLabel(savedLabel, field) ? generatedLabel : savedLabel;
63572
63924
  }
63925
+ function uniqueAxisFormat(formats) {
63926
+ const values = formats.map((format9) => String(format9 ?? "").trim()).filter(Boolean);
63927
+ if (values.length === 0) return void 0;
63928
+ return values.every((format9) => format9 === values[0]) ? values[0] : void 0;
63929
+ }
63573
63930
  function resolveYAxisFormatForField(field, aggregation, saved, fallback) {
63574
63931
  const formatByField = new Map(
63575
63932
  saved.map((axis) => [String(axis.field ?? "").trim(), axis.format])
63576
63933
  );
63934
+ const savedFormats = uniqueAxisFormat(saved.map((axis) => axis.format));
63577
63935
  if (aggregation?.aggregationType === "count") {
63578
- return formatByField.get(field) ?? formatByField.get("count") ?? saved[0]?.format ?? "whole_number";
63579
- }
63580
- if (aggregation?.aggregationType === "percentage") {
63581
- const savedFormat = formatByField.get(field);
63582
- if (savedFormat === "percent" || savedFormat === "percentage") {
63583
- return savedFormat;
63584
- }
63585
- return "percentage";
63936
+ return formatByField.get(field) ?? formatByField.get("count") ?? savedFormats ?? "whole_number";
63586
63937
  }
63587
63938
  const aggType = aggregation?.aggregationType ? String(aggregation.aggregationType) : void 0;
63588
- return formatByField.get(field) ?? formatByField.get(trimAggregationSuffix(field, aggType)) ?? (aggregation?.valueField ? formatByField.get(aggregation.valueField) : void 0) ?? fallback;
63939
+ return formatByField.get(field) ?? formatByField.get(trimAggregationSuffix(field, aggType)) ?? (aggregation?.valueField ? formatByField.get(aggregation.valueField) : void 0) ?? savedFormats ?? fallback;
63589
63940
  }
63590
63941
  function aggregationIdentityForField(field, aggregations) {
63591
63942
  return getAggregationIdentityKey(
@@ -63656,6 +64007,7 @@ function applyYAxisEdits(measures, edits, aggregations) {
63656
64007
  const base = (identity ? byIdentity.get(identity) : void 0) ?? byField.get(field);
63657
64008
  const label = String(edit.label ?? "").trim();
63658
64009
  if (!base) {
64010
+ if (aggregations.length > 0) continue;
63659
64011
  next.push({
63660
64012
  field,
63661
64013
  label: label || toTitleCaseLabel(field),
@@ -63669,7 +64021,16 @@ function applyYAxisEdits(measures, edits, aggregations) {
63669
64021
  format: toAxisFormat(edit.format, base.format)
63670
64022
  });
63671
64023
  }
63672
- return next.length > 0 ? next : measures;
64024
+ if (next.length > 0) return next;
64025
+ if (aggregations.length > 0 && measures.length === 1 && edits.length === 1 && edits[0]?.format) {
64026
+ return [
64027
+ {
64028
+ ...measures[0],
64029
+ format: toAxisFormat(edits[0].format, measures[0].format)
64030
+ }
64031
+ ];
64032
+ }
64033
+ return measures;
63673
64034
  }
63674
64035
  function resolveReportYAxis(chart, edits, resolveTableLabel) {
63675
64036
  const saved = chart.yAxisFields ?? [];
@@ -63680,7 +64041,6 @@ function resolveReportYAxis(chart, edits, resolveTableLabel) {
63680
64041
  const aggregations = getPivotAggregations(chart);
63681
64042
  const hasMultipleAggregations = aggregations.length > 1;
63682
64043
  const columnField = String(chart.pivot?.columnField ?? "").trim();
63683
- const isAggregationOnlyPivot = !String(chart.pivot?.rowField ?? "").trim() && !columnField;
63684
64044
  const fallback = saved[0]?.format ?? chart.xAxisFormat ?? "whole_number";
63685
64045
  const valueKeys = pivotValueFieldKeys(chart);
63686
64046
  const fallbackKeys = pivotFallbackValueKeys(chart);
@@ -63702,9 +64062,9 @@ function resolveReportYAxis(chart, edits, resolveTableLabel) {
63702
64062
  measure.field
63703
64063
  );
63704
64064
  if (matchedKey) return [{ ...measure, field: matchedKey }];
63705
- return isAggregationOnlyPivot ? [measure] : [];
64065
+ return [measure];
63706
64066
  });
63707
- if (!columnField && measures.length === 0 && saved.length > 0) {
64067
+ if (!columnField && measures.length === 0 && saved.length > 0 && aggregations.length === 0) {
63708
64068
  measures = saved;
63709
64069
  }
63710
64070
  measures = applyYAxisEdits(measures, edits, aggregations);
@@ -63731,7 +64091,12 @@ function resolveReportYAxis(chart, edits, resolveTableLabel) {
63731
64091
  measureLabel,
63732
64092
  hasMultipleAggregations
63733
64093
  ),
63734
- format: measure?.format ?? resolveYAxisFormatForField(field, aggregation, saved, fallback)
64094
+ format: measure?.format ?? resolveYAxisFormatForField(
64095
+ field,
64096
+ aggregation,
64097
+ saved,
64098
+ fallback
64099
+ )
63735
64100
  }
63736
64101
  ];
63737
64102
  });
@@ -63771,7 +64136,11 @@ function normalizePivotChartForDisplay(chart) {
63771
64136
  pivotRowFieldEarly,
63772
64137
  String(rowColumn?.format ?? "")
63773
64138
  ) ? pivotRowFieldEarly : config.xAxisField;
63774
- const withResolvedXAxis = resolvedXAxisField && resolvedXAxisField !== config.xAxisField ? { ...config, xAxisField: resolvedXAxisField } : config;
64139
+ const withResolvedXAxis = resolvedXAxisField && resolvedXAxisField !== config.xAxisField ? {
64140
+ ...config,
64141
+ xAxisField: resolvedXAxisField,
64142
+ xAxisFormat: toAxisFormat(rowColumn?.format, "string")
64143
+ } : config;
63775
64144
  const isMetricOrGauge = ["metric", "gauge"].includes(
63776
64145
  String(withResolvedXAxis.chartType ?? "").toLowerCase()
63777
64146
  );
@@ -66598,7 +66967,7 @@ function useReport(reportIdArg, options = {}) {
66598
66967
  pivot,
66599
66968
  operation
66600
66969
  }) => {
66601
- const startedAt = performance.now();
66970
+ const startedAt2 = performance.now();
66602
66971
  const snapshot = await cacheCab.getReportSnapshot(
66603
66972
  effectiveReportId,
66604
66973
  tenants
@@ -66617,7 +66986,7 @@ function useReport(reportIdArg, options = {}) {
66617
66986
  )
66618
66987
  });
66619
66988
  const diagnostics = globalThis.__QUILL_CACHECAB_DIAGNOSTICS__;
66620
- const durationMs = performance.now() - startedAt;
66989
+ const durationMs = performance.now() - startedAt2;
66621
66990
  const outcome = resolution.resolved ? "hit" : "fallback";
66622
66991
  if (Array.isArray(diagnostics)) {
66623
66992
  diagnostics.push({
@@ -69605,11 +69974,12 @@ function useReport(reportIdArg, options = {}) {
69605
69974
  registerOption(column.field, column.label, column.format);
69606
69975
  }
69607
69976
  for (const yAxis2 of yAxisResolved.measures) {
69608
- registerOption(yAxis2.field, yAxis2.label, yAxis2.format);
69977
+ const field = String(yAxis2.field ?? "").trim();
69978
+ registerOption(field, field, yAxis2.format);
69609
69979
  if (yAxis2.format) {
69610
- const existing = optionsByField.get(yAxis2.field);
69980
+ const existing = optionsByField.get(field);
69611
69981
  if (existing) {
69612
- optionsByField.set(yAxis2.field, {
69982
+ optionsByField.set(field, {
69613
69983
  ...existing,
69614
69984
  format: toAxisFormat(yAxis2.format, existing.format)
69615
69985
  });
@@ -69686,12 +70056,17 @@ function useReport(reportIdArg, options = {}) {
69686
70056
  ];
69687
70057
  }, [chartAxisOptions, yAxisResolved.measures]);
69688
70058
  const yAxisOptions = (0, import_react63.useMemo)(
69689
- () => yAxisMeasures.map((measure) => ({
69690
- value: measure.field,
69691
- label: measure.label,
69692
- format: toAxisFormat(measure.format, "string")
69693
- })),
69694
- [yAxisMeasures]
70059
+ () => yAxisMeasures.map((measure) => {
70060
+ const field = String(measure.field ?? "").trim();
70061
+ const catalog = chartAxisOptionByField.get(field);
70062
+ if (catalog) return catalog;
70063
+ return {
70064
+ value: field,
70065
+ label: toTitleCaseLabel(field),
70066
+ format: toAxisFormat(measure.format, "string")
70067
+ };
70068
+ }),
70069
+ [chartAxisOptionByField, yAxisMeasures]
69695
70070
  );
69696
70071
  const normalizedChartYAxisOptions = (0, import_react63.useMemo)(() => {
69697
70072
  return yAxisOptions.map((option) => ({
@@ -70031,11 +70406,7 @@ function useReport(reportIdArg, options = {}) {
70031
70406
  );
70032
70407
  const isPivotTableChart = String(chartType ?? "").toLowerCase() === "table" && Boolean(pivotState);
70033
70408
  const pivotTableColumns = (0, import_react63.useMemo)(
70034
- () => isPivotTableChart ? buildPivotTableColumns(
70035
- pivotState,
70036
- chart,
70037
- resolveTableDisplayLabel
70038
- ) : null,
70409
+ () => isPivotTableChart ? buildPivotTableColumns(pivotState, chart, resolveTableDisplayLabel) : null,
70039
70410
  [chart, isPivotTableChart, pivotState, resolveTableDisplayLabel]
70040
70411
  );
70041
70412
  const chartAxes = (0, import_react63.useMemo)(() => {
@@ -70686,6 +71057,43 @@ function useReport(reportIdArg, options = {}) {
70686
71057
  tableRefreshQuery.isFetching,
70687
71058
  tableRefreshQueryEnabled
70688
71059
  ]);
71060
+ (0, import_react63.useEffect)(() => {
71061
+ if (!isNanPivotDebugEnabled()) return;
71062
+ recordUseReportNanPivotState({
71063
+ source: "useReport",
71064
+ reportId: effectiveReportId,
71065
+ datasources,
71066
+ chartType: resolvedChartType,
71067
+ chartLoading,
71068
+ tableLoading,
71069
+ loading,
71070
+ groupRowsBy,
71071
+ groupColumnsBy,
71072
+ aggregations: aggregationValues,
71073
+ formPivot: pivotState,
71074
+ nextPivot,
71075
+ sourceReport,
71076
+ chart: chartWithSort,
71077
+ table,
71078
+ isPivotTableChart
71079
+ });
71080
+ }, [
71081
+ aggregationValues,
71082
+ chartLoading,
71083
+ chartWithSort,
71084
+ datasources,
71085
+ effectiveReportId,
71086
+ groupColumnsBy,
71087
+ groupRowsBy,
71088
+ isPivotTableChart,
71089
+ loading,
71090
+ nextPivot,
71091
+ pivotState,
71092
+ resolvedChartType,
71093
+ sourceReport,
71094
+ table,
71095
+ tableLoading
71096
+ ]);
70689
71097
  const buildSetReportColumnsFromIds = (nextColumnIds, settingsById) => {
70690
71098
  const normalizedNextIds = nextColumnIds.map((columnId) => String(columnId ?? "").trim()).filter(Boolean);
70691
71099
  return normalizedNextIds.map((columnId) => {
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
@@ -63778,22 +64130,21 @@ function isPlaceholderAxisLabel(label, field) {
63778
64130
  function resolvedAxisLabel(savedLabel, field, generatedLabel) {
63779
64131
  return isPlaceholderAxisLabel(savedLabel, field) ? generatedLabel : savedLabel;
63780
64132
  }
64133
+ function uniqueAxisFormat(formats) {
64134
+ const values = formats.map((format9) => String(format9 ?? "").trim()).filter(Boolean);
64135
+ if (values.length === 0) return void 0;
64136
+ return values.every((format9) => format9 === values[0]) ? values[0] : void 0;
64137
+ }
63781
64138
  function resolveYAxisFormatForField(field, aggregation, saved, fallback) {
63782
64139
  const formatByField = new Map(
63783
64140
  saved.map((axis) => [String(axis.field ?? "").trim(), axis.format])
63784
64141
  );
64142
+ const savedFormats = uniqueAxisFormat(saved.map((axis) => axis.format));
63785
64143
  if (aggregation?.aggregationType === "count") {
63786
- return formatByField.get(field) ?? formatByField.get("count") ?? saved[0]?.format ?? "whole_number";
63787
- }
63788
- if (aggregation?.aggregationType === "percentage") {
63789
- const savedFormat = formatByField.get(field);
63790
- if (savedFormat === "percent" || savedFormat === "percentage") {
63791
- return savedFormat;
63792
- }
63793
- return "percentage";
64144
+ return formatByField.get(field) ?? formatByField.get("count") ?? savedFormats ?? "whole_number";
63794
64145
  }
63795
64146
  const aggType = aggregation?.aggregationType ? String(aggregation.aggregationType) : void 0;
63796
- return formatByField.get(field) ?? formatByField.get(trimAggregationSuffix(field, aggType)) ?? (aggregation?.valueField ? formatByField.get(aggregation.valueField) : void 0) ?? fallback;
64147
+ return formatByField.get(field) ?? formatByField.get(trimAggregationSuffix(field, aggType)) ?? (aggregation?.valueField ? formatByField.get(aggregation.valueField) : void 0) ?? savedFormats ?? fallback;
63797
64148
  }
63798
64149
  function aggregationIdentityForField(field, aggregations) {
63799
64150
  return getAggregationIdentityKey(
@@ -63864,6 +64215,7 @@ function applyYAxisEdits(measures, edits, aggregations) {
63864
64215
  const base = (identity ? byIdentity.get(identity) : void 0) ?? byField.get(field);
63865
64216
  const label = String(edit.label ?? "").trim();
63866
64217
  if (!base) {
64218
+ if (aggregations.length > 0) continue;
63867
64219
  next.push({
63868
64220
  field,
63869
64221
  label: label || toTitleCaseLabel(field),
@@ -63877,7 +64229,16 @@ function applyYAxisEdits(measures, edits, aggregations) {
63877
64229
  format: toAxisFormat(edit.format, base.format)
63878
64230
  });
63879
64231
  }
63880
- return next.length > 0 ? next : measures;
64232
+ if (next.length > 0) return next;
64233
+ if (aggregations.length > 0 && measures.length === 1 && edits.length === 1 && edits[0]?.format) {
64234
+ return [
64235
+ {
64236
+ ...measures[0],
64237
+ format: toAxisFormat(edits[0].format, measures[0].format)
64238
+ }
64239
+ ];
64240
+ }
64241
+ return measures;
63881
64242
  }
63882
64243
  function resolveReportYAxis(chart, edits, resolveTableLabel) {
63883
64244
  const saved = chart.yAxisFields ?? [];
@@ -63888,7 +64249,6 @@ function resolveReportYAxis(chart, edits, resolveTableLabel) {
63888
64249
  const aggregations = getPivotAggregations(chart);
63889
64250
  const hasMultipleAggregations = aggregations.length > 1;
63890
64251
  const columnField = String(chart.pivot?.columnField ?? "").trim();
63891
- const isAggregationOnlyPivot = !String(chart.pivot?.rowField ?? "").trim() && !columnField;
63892
64252
  const fallback = saved[0]?.format ?? chart.xAxisFormat ?? "whole_number";
63893
64253
  const valueKeys = pivotValueFieldKeys(chart);
63894
64254
  const fallbackKeys = pivotFallbackValueKeys(chart);
@@ -63910,9 +64270,9 @@ function resolveReportYAxis(chart, edits, resolveTableLabel) {
63910
64270
  measure.field
63911
64271
  );
63912
64272
  if (matchedKey) return [{ ...measure, field: matchedKey }];
63913
- return isAggregationOnlyPivot ? [measure] : [];
64273
+ return [measure];
63914
64274
  });
63915
- if (!columnField && measures.length === 0 && saved.length > 0) {
64275
+ if (!columnField && measures.length === 0 && saved.length > 0 && aggregations.length === 0) {
63916
64276
  measures = saved;
63917
64277
  }
63918
64278
  measures = applyYAxisEdits(measures, edits, aggregations);
@@ -63939,7 +64299,12 @@ function resolveReportYAxis(chart, edits, resolveTableLabel) {
63939
64299
  measureLabel,
63940
64300
  hasMultipleAggregations
63941
64301
  ),
63942
- format: measure?.format ?? resolveYAxisFormatForField(field, aggregation, saved, fallback)
64302
+ format: measure?.format ?? resolveYAxisFormatForField(
64303
+ field,
64304
+ aggregation,
64305
+ saved,
64306
+ fallback
64307
+ )
63943
64308
  }
63944
64309
  ];
63945
64310
  });
@@ -63979,7 +64344,11 @@ function normalizePivotChartForDisplay(chart) {
63979
64344
  pivotRowFieldEarly,
63980
64345
  String(rowColumn?.format ?? "")
63981
64346
  ) ? pivotRowFieldEarly : config.xAxisField;
63982
- const withResolvedXAxis = resolvedXAxisField && resolvedXAxisField !== config.xAxisField ? { ...config, xAxisField: resolvedXAxisField } : config;
64347
+ const withResolvedXAxis = resolvedXAxisField && resolvedXAxisField !== config.xAxisField ? {
64348
+ ...config,
64349
+ xAxisField: resolvedXAxisField,
64350
+ xAxisFormat: toAxisFormat(rowColumn?.format, "string")
64351
+ } : config;
63983
64352
  const isMetricOrGauge = ["metric", "gauge"].includes(
63984
64353
  String(withResolvedXAxis.chartType ?? "").toLowerCase()
63985
64354
  );
@@ -66806,7 +67175,7 @@ function useReport(reportIdArg, options = {}) {
66806
67175
  pivot,
66807
67176
  operation
66808
67177
  }) => {
66809
- const startedAt = performance.now();
67178
+ const startedAt2 = performance.now();
66810
67179
  const snapshot = await cacheCab.getReportSnapshot(
66811
67180
  effectiveReportId,
66812
67181
  tenants
@@ -66825,7 +67194,7 @@ function useReport(reportIdArg, options = {}) {
66825
67194
  )
66826
67195
  });
66827
67196
  const diagnostics = globalThis.__QUILL_CACHECAB_DIAGNOSTICS__;
66828
- const durationMs = performance.now() - startedAt;
67197
+ const durationMs = performance.now() - startedAt2;
66829
67198
  const outcome = resolution.resolved ? "hit" : "fallback";
66830
67199
  if (Array.isArray(diagnostics)) {
66831
67200
  diagnostics.push({
@@ -69813,11 +70182,12 @@ function useReport(reportIdArg, options = {}) {
69813
70182
  registerOption(column.field, column.label, column.format);
69814
70183
  }
69815
70184
  for (const yAxis2 of yAxisResolved.measures) {
69816
- registerOption(yAxis2.field, yAxis2.label, yAxis2.format);
70185
+ const field = String(yAxis2.field ?? "").trim();
70186
+ registerOption(field, field, yAxis2.format);
69817
70187
  if (yAxis2.format) {
69818
- const existing = optionsByField.get(yAxis2.field);
70188
+ const existing = optionsByField.get(field);
69819
70189
  if (existing) {
69820
- optionsByField.set(yAxis2.field, {
70190
+ optionsByField.set(field, {
69821
70191
  ...existing,
69822
70192
  format: toAxisFormat(yAxis2.format, existing.format)
69823
70193
  });
@@ -69894,12 +70264,17 @@ function useReport(reportIdArg, options = {}) {
69894
70264
  ];
69895
70265
  }, [chartAxisOptions, yAxisResolved.measures]);
69896
70266
  const yAxisOptions = useMemo33(
69897
- () => yAxisMeasures.map((measure) => ({
69898
- value: measure.field,
69899
- label: measure.label,
69900
- format: toAxisFormat(measure.format, "string")
69901
- })),
69902
- [yAxisMeasures]
70267
+ () => yAxisMeasures.map((measure) => {
70268
+ const field = String(measure.field ?? "").trim();
70269
+ const catalog = chartAxisOptionByField.get(field);
70270
+ if (catalog) return catalog;
70271
+ return {
70272
+ value: field,
70273
+ label: toTitleCaseLabel(field),
70274
+ format: toAxisFormat(measure.format, "string")
70275
+ };
70276
+ }),
70277
+ [chartAxisOptionByField, yAxisMeasures]
69903
70278
  );
69904
70279
  const normalizedChartYAxisOptions = useMemo33(() => {
69905
70280
  return yAxisOptions.map((option) => ({
@@ -70239,11 +70614,7 @@ function useReport(reportIdArg, options = {}) {
70239
70614
  );
70240
70615
  const isPivotTableChart = String(chartType ?? "").toLowerCase() === "table" && Boolean(pivotState);
70241
70616
  const pivotTableColumns = useMemo33(
70242
- () => isPivotTableChart ? buildPivotTableColumns(
70243
- pivotState,
70244
- chart,
70245
- resolveTableDisplayLabel
70246
- ) : null,
70617
+ () => isPivotTableChart ? buildPivotTableColumns(pivotState, chart, resolveTableDisplayLabel) : null,
70247
70618
  [chart, isPivotTableChart, pivotState, resolveTableDisplayLabel]
70248
70619
  );
70249
70620
  const chartAxes = useMemo33(() => {
@@ -70894,6 +71265,43 @@ function useReport(reportIdArg, options = {}) {
70894
71265
  tableRefreshQuery.isFetching,
70895
71266
  tableRefreshQueryEnabled
70896
71267
  ]);
71268
+ useEffect33(() => {
71269
+ if (!isNanPivotDebugEnabled()) return;
71270
+ recordUseReportNanPivotState({
71271
+ source: "useReport",
71272
+ reportId: effectiveReportId,
71273
+ datasources,
71274
+ chartType: resolvedChartType,
71275
+ chartLoading,
71276
+ tableLoading,
71277
+ loading,
71278
+ groupRowsBy,
71279
+ groupColumnsBy,
71280
+ aggregations: aggregationValues,
71281
+ formPivot: pivotState,
71282
+ nextPivot,
71283
+ sourceReport,
71284
+ chart: chartWithSort,
71285
+ table,
71286
+ isPivotTableChart
71287
+ });
71288
+ }, [
71289
+ aggregationValues,
71290
+ chartLoading,
71291
+ chartWithSort,
71292
+ datasources,
71293
+ effectiveReportId,
71294
+ groupColumnsBy,
71295
+ groupRowsBy,
71296
+ isPivotTableChart,
71297
+ loading,
71298
+ nextPivot,
71299
+ pivotState,
71300
+ resolvedChartType,
71301
+ sourceReport,
71302
+ table,
71303
+ tableLoading
71304
+ ]);
70897
71305
  const buildSetReportColumnsFromIds = (nextColumnIds, settingsById) => {
70898
71306
  const normalizedNextIds = nextColumnIds.map((columnId) => String(columnId ?? "").trim()).filter(Boolean);
70899
71307
  return normalizedNextIds.map((columnId) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quillsql/react",
3
- "version": "2.16.101",
3
+ "version": "2.16.103",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {