@quillsql/react 2.16.45 → 2.16.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -15083,7 +15083,7 @@ function getDateString(value, dateRange, dateBucket, databaseType) {
15083
15083
  });
15084
15084
  }
15085
15085
  function isDateField(fieldType) {
15086
- return fieldType === "date" || fieldType === "datetime" || fieldType === "timestamp" || fieldType === "timestamptz" || fieldType === "MMM_yyyy" || fieldType === "MMM_dd" || fieldType === "MMM_dd_yyyy" || fieldType === "MMM_dd_hh:mm_ap_pm" || fieldType === "hh_ap_pm";
15086
+ return fieldType === "date" || fieldType === "datetime" || fieldType === "timestamp" || fieldType === "timestamptz" || fieldType === "yyyy" || fieldType === "MMM_yyyy" || fieldType === "MMM_dd" || fieldType === "MMM_dd_yyyy" || fieldType === "MMM_dd_hh:mm_ap_pm" || fieldType === "hh_ap_pm";
15087
15087
  }
15088
15088
  var import_date_fns5;
15089
15089
  var init_dates = __esm({
@@ -15116,11 +15116,11 @@ function isValidPivot(pivot) {
15116
15116
  reason: 'Value field cannot be empty when aggregation is not "count" or "percentage"'
15117
15117
  };
15118
15118
  } else if (pivot.aggregations?.some(
15119
- (a) => a.aggregationType === "percentage" && !a.valueField && (!pivot.rowField || pivot.columnField)
15119
+ (a) => a.aggregationType === "percentage" && !a.valueField && !pivot.rowField
15120
15120
  )) {
15121
15121
  return {
15122
15122
  valid: false,
15123
- reason: "Empty percentage field only supported for pivot tables with a row field and no column field"
15123
+ reason: "Empty percentage field only supported for pivot tables with a row field"
15124
15124
  };
15125
15125
  }
15126
15126
  return { valid: true, reason: "" };
@@ -16403,7 +16403,7 @@ function formatAggregationTypeForDisplay(aggregationType) {
16403
16403
  case "max":
16404
16404
  return "Max";
16405
16405
  case "percentage":
16406
- return "Percentage";
16406
+ return "Percent";
16407
16407
  default:
16408
16408
  return aggregationType ? capitalize(String(aggregationType)) : "Value";
16409
16409
  }
@@ -16445,6 +16445,21 @@ function getDefaultPivotMeasureLabel(report) {
16445
16445
  return pivot.columnField ? "Count" : `Count ${snakeAndCamelCaseToTitleCase(tableNames[0] ?? "table")}`;
16446
16446
  }
16447
16447
  const target = valueField !== void 0 && String(valueField).trim() ? resolveValueFieldDisplayLabel(report, valueField) : tableNames.length > 0 ? tableNames.map((n) => snakeAndCamelCaseToTitleCase(n)).join(", ") : "table";
16448
+ if (aggregationType.toLowerCase() === "percentage") {
16449
+ const valueField2 = String(
16450
+ fromAggregations?.valueField2 ?? ""
16451
+ ).trim();
16452
+ if (valueField && valueField2) {
16453
+ return `Percent ${target} of ${resolveValueFieldDisplayLabel(report, valueField2)}`;
16454
+ }
16455
+ const valueFieldType = String(
16456
+ fromAggregations?.valueFieldType ?? ""
16457
+ ).trim().toLowerCase();
16458
+ if (valueField && valueFieldType === "bool") {
16459
+ return `Percent ${target}`;
16460
+ }
16461
+ return `Percent of Total ${target}`;
16462
+ }
16448
16463
  return target ? `${aggWord} ${target}` : aggWord;
16449
16464
  }
16450
16465
  function humanizeLegacyPivotMeasureLabelFragment(text) {
@@ -16452,7 +16467,10 @@ function humanizeLegacyPivotMeasureLabelFragment(text) {
16452
16467
  if (!t) return t;
16453
16468
  const m = t.match(LEGACY_AGG_SNAKE_FIELD);
16454
16469
  if (!m) return t;
16455
- return `${formatAggregationTypeForDisplay(m[1])} ${snakeAndCamelCaseToTitleCase(m[2])}`;
16470
+ if (String(m[1]).toLowerCase() === "percentage") {
16471
+ return `Percent of Total ${snakeAndCamelCaseToTitleCase(m[2] ?? "")}`;
16472
+ }
16473
+ return `${formatAggregationTypeForDisplay(m[1])} ${snakeAndCamelCaseToTitleCase(m[2] ?? "")}`;
16456
16474
  }
16457
16475
  function humanizeChartSeriesTooltipLabel(label) {
16458
16476
  const s = String(label ?? "").trim();
@@ -19928,6 +19946,27 @@ function parseData(rows, queryType) {
19928
19946
  return rows;
19929
19947
  }
19930
19948
  }
19949
+ function isQuillFetchDebugEnabled() {
19950
+ if (typeof globalThis !== "undefined") {
19951
+ for (const debugFlag of QUILL_FETCH_DEBUG_FLAGS) {
19952
+ if (globalThis?.[debugFlag] === true) {
19953
+ return true;
19954
+ }
19955
+ }
19956
+ }
19957
+ if (typeof process !== "undefined" && typeof process.env !== "undefined") {
19958
+ const envCandidates = [
19959
+ process?.env?.QUILL_DEBUG_USEFORM_HYDRATION,
19960
+ process?.env?.QUILL_DEBUG_USEFORM_FILTERS,
19961
+ process?.env?.QUILL_DEBUG_DATA_FETCHER
19962
+ ];
19963
+ return envCandidates.some((candidate) => {
19964
+ const normalized = String(candidate ?? "").trim().toLowerCase();
19965
+ return normalized === "1" || normalized === "true";
19966
+ });
19967
+ }
19968
+ return false;
19969
+ }
19931
19970
  async function testSqlViewState(client, referencedTables, getToken) {
19932
19971
  let errorMessage = null;
19933
19972
  await Promise.all(
@@ -20413,7 +20452,7 @@ async function fetchRelevantInfoFromState(reportBuilderState, tables, columns, a
20413
20452
  return { error: error.message };
20414
20453
  }
20415
20454
  }
20416
- var quillFetch, parseFetchResponse;
20455
+ var QUILL_FETCH_DEBUG_FLAGS, quillFetchDebugSeq, logQuillFetchDebug, quillFetch, parseFetchResponse;
20417
20456
  var init_dataFetcher = __esm({
20418
20457
  "src/utils/dataFetcher.tsx"() {
20419
20458
  "use strict";
@@ -20422,6 +20461,16 @@ var init_dataFetcher = __esm({
20422
20461
  init_tableProcessing();
20423
20462
  init_dates();
20424
20463
  init_changelogNotify();
20464
+ QUILL_FETCH_DEBUG_FLAGS = [
20465
+ "__QUILL_DEBUG_USEFORM_HYDRATION__",
20466
+ "__QUILL_DEBUG_USEFORM_FILTERS__",
20467
+ "__QUILL_DEBUG_DATA_FETCHER__"
20468
+ ];
20469
+ quillFetchDebugSeq = 0;
20470
+ logQuillFetchDebug = (label, payload) => {
20471
+ if (!isQuillFetchDebugEnabled()) return;
20472
+ console.log(`[quillFetch-debug] ${label}`, payload);
20473
+ };
20425
20474
  quillFetch = async ({
20426
20475
  client,
20427
20476
  task,
@@ -20432,7 +20481,24 @@ var init_dataFetcher = __esm({
20432
20481
  urlParameters,
20433
20482
  getToken
20434
20483
  }) => {
20484
+ const debugSeq = ++quillFetchDebugSeq;
20485
+ const debugStart = Date.now();
20486
+ logQuillFetchDebug("request", {
20487
+ seq: debugSeq,
20488
+ task,
20489
+ reportId: metadata?.reportId,
20490
+ dashboardItemId: metadata?.dashboardItemId,
20491
+ hasPivot: Boolean(metadata?.pivot),
20492
+ hasReportBuilderState: Boolean(metadata?.reportBuilderState),
20493
+ filtersLength: Array.isArray(metadata?.filters) ? metadata.filters.length : void 0
20494
+ });
20435
20495
  const token = await getToken();
20496
+ logQuillFetchDebug("token-acquired", {
20497
+ seq: debugSeq,
20498
+ task,
20499
+ ms: Date.now() - debugStart,
20500
+ hasToken: Boolean(token)
20501
+ });
20436
20502
  const queryString = urlParameters ?? `task=${task}`;
20437
20503
  const endpoint = client.queryEndpoint ? `${client.queryEndpoint}?${queryString}` : `${QUILL_SERVER}${QUILL_QUERY_ENDPOINT}?${queryString}`;
20438
20504
  try {
@@ -20464,6 +20530,17 @@ var init_dataFetcher = __esm({
20464
20530
  if (task !== "fetch-changelog-list" && Array.isArray(normalizedData?.changelogs)) {
20465
20531
  notifyChangelogs(normalizedData.changelogs);
20466
20532
  }
20533
+ logQuillFetchDebug("response", {
20534
+ seq: debugSeq,
20535
+ task,
20536
+ ms: Date.now() - debugStart,
20537
+ status: result.status,
20538
+ error: result.error,
20539
+ dataError: normalizedData?.error,
20540
+ queryResultRowCounts: Array.isArray(result.queries?.queryResults) ? result.queries.queryResults.map(
20541
+ (qr) => Array.isArray(qr?.rows) ? qr.rows.length : null
20542
+ ) : void 0
20543
+ });
20467
20544
  return {
20468
20545
  data: normalizedData,
20469
20546
  queries: result.queries,
@@ -20472,8 +20549,19 @@ var init_dataFetcher = __esm({
20472
20549
  };
20473
20550
  } catch (e) {
20474
20551
  if (e instanceof Error && e.name === "AbortError") {
20552
+ logQuillFetchDebug("aborted", {
20553
+ seq: debugSeq,
20554
+ task,
20555
+ ms: Date.now() - debugStart
20556
+ });
20475
20557
  throw e;
20476
20558
  }
20559
+ logQuillFetchDebug("threw", {
20560
+ seq: debugSeq,
20561
+ task,
20562
+ ms: Date.now() - debugStart,
20563
+ message: e instanceof Error ? e.message : String(e)
20564
+ });
20477
20565
  if (task !== "set-section-order") {
20478
20566
  console.error("Failed to fetch:", e);
20479
20567
  }
@@ -21498,6 +21586,7 @@ var numberFormatOptions = [
21498
21586
  "percentage"
21499
21587
  ];
21500
21588
  var dateFormatOptions = [
21589
+ "yyyy",
21501
21590
  "MMM_yyyy",
21502
21591
  "MMM_dd",
21503
21592
  "MMM_dd_yyyy",
@@ -21515,6 +21604,7 @@ var NUMBER_OPTIONS = [
21515
21604
  { value: "percent", label: "percentage" }
21516
21605
  ];
21517
21606
  var DATE_OPTIONS = [
21607
+ { value: "yyyy", label: "year" },
21518
21608
  { value: "MMM_yyyy", label: "month" },
21519
21609
  { value: "MMM_dd", label: "day" },
21520
21610
  { value: "MMM_dd_yyyy", label: "day and year" },
@@ -21902,6 +21992,7 @@ function buildPivotOnlyReportFromResponse({
21902
21992
  rows: rawPivotRows,
21903
21993
  fields: rawPivotFields,
21904
21994
  pivot: effectivePivot,
21995
+ ...effectivePivot.dateBucket ? { dateBucket: effectivePivot.dateBucket } : {},
21905
21996
  client,
21906
21997
  rowCount: typeof response?.rowCount === "number" ? response.rowCount : void 0
21907
21998
  });
@@ -58804,7 +58895,9 @@ var filterTreeToQueryEntry = (node) => {
58804
58895
  };
58805
58896
  };
58806
58897
  var queryRuleToInternalFilter = (rule, fieldConfigByName) => {
58807
- const config = fieldConfigByName[rule.field];
58898
+ const ruleTableName = String(rule.table ?? "").trim();
58899
+ const tableQualifiedConfig = ruleTableName && rule.field && !rule.field.includes(".") ? fieldConfigByName[`${ruleTableName}.${rule.field}`] : void 0;
58900
+ const config = tableQualifiedConfig ?? fieldConfigByName[rule.field];
58808
58901
  if (!config?.fieldType) {
58809
58902
  throw new Error(
58810
58903
  `Missing field type config for "${rule.field}". Add it to fieldConfigByName.`
@@ -59772,7 +59865,8 @@ var AGGREGATION_OPTION_TYPES = [
59772
59865
  "average",
59773
59866
  "count",
59774
59867
  "max",
59775
- "min"
59868
+ "min",
59869
+ "percentage"
59776
59870
  ];
59777
59871
  var QUERY_BUILDER_MULTI_VALUE_OPERATORS = /* @__PURE__ */ new Set(["in", "notin"]);
59778
59872
  var TABLE_FORMAT_CACHE_MAX_ENTRIES = 5e3;
@@ -60262,9 +60356,31 @@ function buildPivotAggregationsWithFieldTables({
60262
60356
  schemaTables,
60263
60357
  preferredTables
60264
60358
  }) || void 0;
60359
+ const isPercentageAggregation = String(aggregation.aggregationType ?? "").toLowerCase() === "percentage";
60360
+ const typeFallbackColumns = [
60361
+ ...sourceReport?.columnInternal ?? sourceReport?.columns ?? []
60362
+ ];
60363
+ const valueFieldType = String(
60364
+ aggregation.valueFieldType ?? ""
60365
+ ).trim() || (isPercentageAggregation ? resolveFieldType({
60366
+ field: aggregation.valueField,
60367
+ table: valueFieldTable,
60368
+ schemaTables,
60369
+ fallbackColumns: typeFallbackColumns
60370
+ }) : void 0);
60371
+ const valueField2Type = String(
60372
+ aggregation.valueField2Type ?? ""
60373
+ ).trim() || (isPercentageAggregation ? resolveFieldType({
60374
+ field: aggregation.valueField2,
60375
+ table: valueFieldTable,
60376
+ schemaTables,
60377
+ fallbackColumns: typeFallbackColumns
60378
+ }) : void 0);
60265
60379
  result.push({
60266
60380
  ...aggregationWithoutPivotTables,
60267
60381
  aggregationType: aggregation.aggregationType,
60382
+ ...valueFieldType ? { valueFieldType } : {},
60383
+ ...valueField2Type ? { valueField2Type } : {},
60268
60384
  ...valueFieldTable ? { valueFieldTable } : {}
60269
60385
  });
60270
60386
  }
@@ -60308,8 +60424,9 @@ function getPivotAggregationSortFields(aggregationState, resolvedColumnField) {
60308
60424
  const aggregationType = String(aggregation.aggregationType ?? "").trim();
60309
60425
  if (!aggregationType) continue;
60310
60426
  const valueField = String(aggregation.valueField ?? "").trim();
60311
- const canonicalBaseField = valueField || "count";
60312
- const sortField = hasMultipleAggregations && aggregationType.toLowerCase() !== "count" ? `${canonicalBaseField}_${aggregationType}` : canonicalBaseField;
60427
+ const isValuelessPercentage = !valueField && aggregationType.toLowerCase() === "percentage";
60428
+ const canonicalBaseField = valueField || (isValuelessPercentage ? "percentage" : "count");
60429
+ const sortField = isValuelessPercentage ? "percentage" : hasMultipleAggregations && aggregationType.toLowerCase() !== "count" ? `${canonicalBaseField}_${aggregationType}` : canonicalBaseField;
60313
60430
  if (!seen.has(sortField)) {
60314
60431
  seen.add(sortField);
60315
60432
  fields.push(sortField);
@@ -60328,8 +60445,9 @@ function getPivotAggregationSortLabelOverrides(aggregationState, aggregationTabl
60328
60445
  const aggregationType = String(aggregation.aggregationType ?? "").trim();
60329
60446
  if (!aggregationType) continue;
60330
60447
  const valueField = String(aggregation.valueField ?? "").trim();
60331
- const canonicalBaseField = valueField || "count";
60332
- const sortField = hasMultipleAggregations && aggregationType.toLowerCase() !== "count" ? `${canonicalBaseField}_${aggregationType}` : canonicalBaseField;
60448
+ const isValuelessPercentage = !valueField && aggregationType.toLowerCase() === "percentage";
60449
+ const canonicalBaseField = valueField || (isValuelessPercentage ? "percentage" : "count");
60450
+ const sortField = isValuelessPercentage ? "percentage" : hasMultipleAggregations && aggregationType.toLowerCase() !== "count" ? `${canonicalBaseField}_${aggregationType}` : canonicalBaseField;
60333
60451
  if (labelBySortField.has(sortField)) continue;
60334
60452
  if (valueField || aggregationType.toLowerCase() !== "count") continue;
60335
60453
  const tableHint = String(aggregationTablesByIndex[index] ?? "").trim();
@@ -60483,6 +60601,9 @@ function trimAggregationSuffix(value, aggregationType) {
60483
60601
  }
60484
60602
  function getAggregationFieldKey(aggregation, hasMultipleAggregations) {
60485
60603
  if (!aggregation.aggregationType) return null;
60604
+ if (!aggregation.valueField && aggregation.aggregationType === "percentage") {
60605
+ return "percentage";
60606
+ }
60486
60607
  if (aggregation.valueField) {
60487
60608
  return hasMultipleAggregations ? `${aggregation.valueField}_${aggregation.aggregationType}` : aggregation.valueField;
60488
60609
  }
@@ -60603,6 +60724,22 @@ function buildAggregationLabel(aggregation, report) {
60603
60724
  }
60604
60725
  const rawTarget = aggregation.valueField ?? (tableNames.length > 0 ? tableNames.join(", ") : "table");
60605
60726
  const target = report ? aggregation.valueField ? resolveValueFieldDisplayLabel(report, aggregation.valueField) : rawTarget : rawTarget;
60727
+ if (String(aggregationType).toLowerCase() === "percentage") {
60728
+ const valueField2 = String(
60729
+ aggregation.valueField2 ?? ""
60730
+ ).trim();
60731
+ if (aggregation.valueField && valueField2) {
60732
+ const denominator = report ? resolveValueFieldDisplayLabel(report, valueField2) : valueField2;
60733
+ return `Percent ${target} of ${denominator}`;
60734
+ }
60735
+ const valueFieldType = String(
60736
+ aggregation.valueFieldType ?? ""
60737
+ ).trim().toLowerCase();
60738
+ if (aggregation.valueField && valueFieldType === "bool") {
60739
+ return `Percent ${target}`;
60740
+ }
60741
+ return `Percent of Total ${target}`;
60742
+ }
60606
60743
  return target ? `${aggWord} ${target}` : aggWord;
60607
60744
  }
60608
60745
  function normalizePivotColumnDisplayValue(value) {
@@ -60643,6 +60780,16 @@ function isPivotTableDateBucketRowAxis(chart, xAxisField) {
60643
60780
  if (!rowField || xf !== rowField) return false;
60644
60781
  return isDateType(String(chart.pivot.rowFieldType ?? ""));
60645
60782
  }
60783
+ var RESOLVABLE_X_AXIS_DATE_BUCKETS = /* @__PURE__ */ new Set(["year", "month", "day"]);
60784
+ function resolvePivotDateBucketXAxisFormat(chart, xAxisField) {
60785
+ if (String(chart?.chartType ?? "").toLowerCase() === "table") return null;
60786
+ const dateBucket = String(chart?.pivot?.dateBucket ?? "").trim().toLowerCase();
60787
+ if (!RESOLVABLE_X_AXIS_DATE_BUCKETS.has(dateBucket)) return null;
60788
+ const rowField = String(chart?.pivot?.rowField ?? "").trim();
60789
+ if (!rowField || String(xAxisField ?? "").trim() !== rowField) return null;
60790
+ if (!isDateType(String(chart?.pivot?.rowFieldType ?? ""))) return null;
60791
+ return getDateFormatFromBucket(dateBucket);
60792
+ }
60646
60793
  function chartDetailRowsMissingPivotRowBucket(chart) {
60647
60794
  if (!chart?.pivot) return false;
60648
60795
  const rowField = String(chart.pivot.rowField ?? "").trim();
@@ -60863,6 +61010,9 @@ function normalizePivotChartForDisplay(chart) {
60863
61010
  withResolvedXAxis.pivot?.rowFieldType ?? ""
60864
61011
  ).trim();
60865
61012
  const pivotDateRowMonthLabelRe = /^[A-Z][a-z]{2} \d{4}$/;
61013
+ const pivotDisplayDateFormat = getDateFormatFromBucket(
61014
+ String(withResolvedXAxis.pivot?.dateBucket ?? "").trim()
61015
+ );
60866
61016
  let displayRows = Array.isArray(
60867
61017
  withResolvedXAxis.rows
60868
61018
  ) ? withResolvedXAxis.rows : [];
@@ -60881,7 +61031,7 @@ function normalizePivotChartForDisplay(chart) {
60881
61031
  }
60882
61032
  const formatted = quillFormat({
60883
61033
  value: raw,
60884
- format: "MMM_yyyy"
61034
+ format: pivotDisplayDateFormat
60885
61035
  });
60886
61036
  if (!formatted || !String(formatted).trim()) {
60887
61037
  return row;
@@ -61925,7 +62075,8 @@ function processPivotState(next, options = {}) {
61925
62075
  const {
61926
62076
  nextState,
61927
62077
  promoteColumnToRow = false,
61928
- fallbackColumnGroupBy
62078
+ fallbackColumnGroupBy,
62079
+ isBoolAggregationField
61929
62080
  } = options;
61930
62081
  const normalizedNext = { ...next };
61931
62082
  const isClearingRowGrouping = nextState?.groupRowsBy !== void 0 && String(nextState.groupRowsBy ?? "").trim().length === 0;
@@ -61968,6 +62119,29 @@ function processPivotState(next, options = {}) {
61968
62119
  normalizedNext.aggregationState = [{ aggregationType: "count" }];
61969
62120
  normalizedNext.aggregationTablesByIndex = [void 0];
61970
62121
  }
62122
+ if (!resolvedRowField) {
62123
+ normalizedNext.aggregationState = normalizedNext.aggregationState.map(
62124
+ (aggregation, index) => {
62125
+ if (String(aggregation.aggregationType ?? "").toLowerCase() !== "percentage") {
62126
+ return aggregation;
62127
+ }
62128
+ const valueField = String(aggregation.valueField ?? "").trim();
62129
+ if (!valueField) {
62130
+ return { ...aggregation, aggregationType: "count" };
62131
+ }
62132
+ const valueField2 = String(aggregation.valueField2 ?? "").trim();
62133
+ if (valueField2 && valueField2 !== valueField) {
62134
+ return aggregation;
62135
+ }
62136
+ const table = String(aggregation.valueFieldTable ?? "").trim() || String(normalizedNext.aggregationTablesByIndex[index] ?? "").trim() || void 0;
62137
+ const isBool = isBoolAggregationField?.(valueField, table);
62138
+ if (isBool === false) {
62139
+ return { ...aggregation, aggregationType: "sum" };
62140
+ }
62141
+ return aggregation;
62142
+ }
62143
+ );
62144
+ }
61971
62145
  if (isSettingRowGrouping && normalizedNext.aggregationState.length > 0) {
61972
62146
  normalizedNext.aggregationState = [...normalizedNext.aggregationState];
61973
62147
  }
@@ -63222,6 +63396,19 @@ function useReport(reportIdArg, options = {}) {
63222
63396
  const customFieldsRef = (0, import_react62.useRef)(schemaData.customFields);
63223
63397
  schemaForReportBuilderStateRef.current = schemaForReportBuilderState;
63224
63398
  customFieldsRef.current = schemaData.customFields;
63399
+ const isBoolAggregationField = (0, import_react62.useCallback)(
63400
+ (field, table2) => {
63401
+ const fieldType = resolveFieldType({
63402
+ field,
63403
+ table: table2,
63404
+ schemaTables: schemaForReportBuilderStateRef.current
63405
+ });
63406
+ if (!fieldType) return void 0;
63407
+ const normalized = String(fieldType).trim().toLowerCase();
63408
+ return isBoolType(normalized) || normalized === "boolean";
63409
+ },
63410
+ []
63411
+ );
63225
63412
  const schemaForeignKeyMap = (0, import_react62.useMemo)(
63226
63413
  () => getSchemaForeignKeyMapping(schemaForReportBuilderState),
63227
63414
  [schemaForReportBuilderState]
@@ -64129,9 +64316,10 @@ function useReport(reportIdArg, options = {}) {
64129
64316
  const explicitTable = getPivotGroupOptionTable(targetRaw);
64130
64317
  const field = explicitTable ? decodePivotGroupOptionValue(targetRaw) : targetRaw;
64131
64318
  if (!field) return {};
64319
+ const fieldForTableResolution = field.includes(":") ? String(field.split(":")[0] ?? "").trim() : field;
64132
64320
  const table2 = explicitTable ?? optionTableLookup.get(rawValue) ?? resolveFieldTable({
64133
64321
  report: sourceReport,
64134
- field,
64322
+ field: fieldForTableResolution,
64135
64323
  schemaTables: schemaForReportBuilderState,
64136
64324
  preferredTables
64137
64325
  }) ?? void 0;
@@ -64146,7 +64334,19 @@ function useReport(reportIdArg, options = {}) {
64146
64334
  if (target === "table") {
64147
64335
  return table2 ? { table: table2 } : preferredTables[0] ? { table: preferredTables[0] } : {};
64148
64336
  }
64149
- if (aggregationType !== "count") {
64337
+ if (aggregationType === "percentage" && target.includes(":")) {
64338
+ const [numeratorRaw, denominatorRaw] = target.split(":");
64339
+ const numerator = String(numeratorRaw ?? "").trim();
64340
+ const denominator = String(denominatorRaw ?? "").trim();
64341
+ if (numerator && denominator) {
64342
+ return {
64343
+ valueField: numerator,
64344
+ valueField2: denominator,
64345
+ ...table2 ? { table: table2 } : {}
64346
+ };
64347
+ }
64348
+ }
64349
+ if (aggregationType !== "count" && aggregationType !== "percentage") {
64150
64350
  return {
64151
64351
  valueField: target,
64152
64352
  ...table2 ? { table: table2 } : {}
@@ -64330,7 +64530,9 @@ function useReport(reportIdArg, options = {}) {
64330
64530
  const pivotAggregationsFromSource = Array.isArray(sourceReport.pivot?.aggregations) ? sourceReport.pivot.aggregations : sourceReport.pivot?.aggregationType ? [
64331
64531
  {
64332
64532
  valueField: sourceReport.pivot?.valueField,
64533
+ valueFieldType: sourceReport.pivot?.valueFieldType,
64333
64534
  valueField2: sourceReport.pivot?.valueField2,
64535
+ valueField2Type: sourceReport.pivot?.valueField2Type,
64334
64536
  aggregationType: sourceReport.pivot?.aggregationType
64335
64537
  }
64336
64538
  ] : [];
@@ -64415,7 +64617,8 @@ function useReport(reportIdArg, options = {}) {
64415
64617
  chartType: prev.chartType ?? sourceReport.chartType
64416
64618
  };
64417
64619
  const normalized = processPivotState(nextFromSource, {
64418
- promoteColumnToRow: shouldPromoteColumnToRowFromPendingFlag
64620
+ promoteColumnToRow: shouldPromoteColumnToRowFromPendingFlag,
64621
+ isBoolAggregationField
64419
64622
  });
64420
64623
  return normalized;
64421
64624
  }
@@ -64598,6 +64801,19 @@ function useReport(reportIdArg, options = {}) {
64598
64801
  resolveCache.set(fieldName, resolved);
64599
64802
  return resolved;
64600
64803
  }
64804
+ if (isUseFormFiltersDebugEnabled()) {
64805
+ console.error(
64806
+ "[useForm-debug] normalizeQueryBuilderFieldNameForConfig ambiguous",
64807
+ {
64808
+ fieldName,
64809
+ candidateKeys: candidates.map(([key]) => key),
64810
+ preferredTableNames: Array.from(preferredTableNames),
64811
+ primaryTableName,
64812
+ effectiveReportBuilderTableNames,
64813
+ baseReportBuilderTableNames
64814
+ }
64815
+ );
64816
+ }
64601
64817
  return fieldName;
64602
64818
  };
64603
64819
  }, [
@@ -64617,7 +64833,9 @@ function useReport(reportIdArg, options = {}) {
64617
64833
  }
64618
64834
  if (entry && typeof entry === "object" && "field" in entry) {
64619
64835
  const previousFieldName = String(entry.field ?? "").trim();
64620
- const nextFieldName = normalizeQueryBuilderFieldNameForConfig(previousFieldName);
64836
+ const ruleTableName = String(entry.table ?? "").trim();
64837
+ const tableQualifiedKey = ruleTableName && previousFieldName && !previousFieldName.includes(".") ? `${ruleTableName}.${previousFieldName}` : "";
64838
+ const nextFieldName = tableQualifiedKey && queryBuilderFieldConfigByName[tableQualifiedKey] ? tableQualifiedKey : normalizeQueryBuilderFieldNameForConfig(previousFieldName);
64621
64839
  if (!nextFieldName || nextFieldName === previousFieldName) {
64622
64840
  return entry;
64623
64841
  }
@@ -64639,7 +64857,7 @@ function useReport(reportIdArg, options = {}) {
64639
64857
  }
64640
64858
  return normalizeGroup(group);
64641
64859
  };
64642
- }, [normalizeQueryBuilderFieldNameForConfig]);
64860
+ }, [normalizeQueryBuilderFieldNameForConfig, queryBuilderFieldConfigByName]);
64643
64861
  const refreshSelectionTables = (0, import_react62.useMemo)(() => {
64644
64862
  return [
64645
64863
  getPivotGroupOptionTable(groupRowsBy),
@@ -64740,6 +64958,10 @@ function useReport(reportIdArg, options = {}) {
64740
64958
  aggregation.valueField,
64741
64959
  aggregationTablesByIndex[index]
64742
64960
  );
64961
+ appendPivotColumn(
64962
+ aggregation.valueField2,
64963
+ aggregationTablesByIndex[index]
64964
+ );
64743
64965
  });
64744
64966
  const joinColumns = getJoinColumnsFromTables(effectiveReportBuilderTables);
64745
64967
  const useWideTableColumns = expandReportBuilderColumnsForFlatTable && !pivotState && String(chartType ?? "").toLowerCase() === "table";
@@ -65069,9 +65291,13 @@ function useReport(reportIdArg, options = {}) {
65069
65291
  const aggregationType = String(aggregation.aggregationType);
65070
65292
  const normalizedAggregationType = aggregationType.toLowerCase();
65071
65293
  const tableHint = String(aggregationTablesByIndex[index] ?? "").trim();
65072
- const target = aggregation.valueField && aggregation.valueField.trim().length > 0 ? aggregation.valueField : normalizedAggregationType === "count" ? tableHint || aggregationFallbackTableName : aggregationFallbackTarget;
65294
+ const valueField = String(aggregation.valueField ?? "").trim();
65295
+ const valueField2 = String(aggregation.valueField2 ?? "").trim();
65296
+ const isPercentageRatio = normalizedAggregationType === "percentage" && Boolean(valueField) && Boolean(valueField2);
65297
+ const target = isPercentageRatio ? `${valueField}:${valueField2}` : valueField ? valueField : normalizedAggregationType === "count" || normalizedAggregationType === "percentage" ? tableHint || aggregationFallbackTableName : aggregationFallbackTarget;
65298
+ const isBoolPercentage = normalizedAggregationType === "percentage" && Boolean(valueField) && isBoolAggregationField(valueField, tableHint || void 0) === true;
65073
65299
  return {
65074
- label: `${formatAggregationTypeForDisplay(aggregationType)} ${toTitleCaseLabel(target)}`,
65300
+ label: isPercentageRatio ? `Percent ${toTitleCaseLabel(valueField)} of ${toTitleCaseLabel(valueField2)}` : normalizedAggregationType === "percentage" ? isBoolPercentage ? `Percent ${toTitleCaseLabel(target)}` : `Percent of Total ${toTitleCaseLabel(target)}` : `${formatAggregationTypeForDisplay(aggregationType)} ${toTitleCaseLabel(target)}`,
65075
65301
  value: `${aggregationType}:${target}`
65076
65302
  };
65077
65303
  });
@@ -65079,7 +65305,8 @@ function useReport(reportIdArg, options = {}) {
65079
65305
  aggregationFallbackTableName,
65080
65306
  aggregationFallbackTarget,
65081
65307
  aggregationState,
65082
- aggregationTablesByIndex
65308
+ aggregationTablesByIndex,
65309
+ isBoolAggregationField
65083
65310
  ]);
65084
65311
  const { aggregationBaseOptions, aggregationOptionTableLookup } = (0, import_react62.useMemo)(() => {
65085
65312
  const joinScoped = fieldOptionsAllowedTableNames ? joinCompatibleSchemaTableNames.filter(
@@ -65105,17 +65332,34 @@ function useReport(reportIdArg, options = {}) {
65105
65332
  if (tableName && !tableLookup.has(countValue)) {
65106
65333
  tableLookup.set(countValue, tableName);
65107
65334
  }
65335
+ if (resolvedGroupRowsBy) {
65336
+ const percentageValue = `percentage:${countTarget}`;
65337
+ if (!seen.has(percentageValue)) {
65338
+ options2.push({
65339
+ value: percentageValue,
65340
+ label: `Percent of Total ${toTitleCaseLabel(countTarget)}`
65341
+ });
65342
+ seen.add(percentageValue);
65343
+ }
65344
+ if (tableName && !tableLookup.has(percentageValue)) {
65345
+ tableLookup.set(percentageValue, tableName);
65346
+ }
65347
+ }
65108
65348
  for (const column of table2.columns ?? []) {
65109
65349
  const fieldName = String(column.field ?? "").trim();
65110
65350
  if (!fieldName) continue;
65351
+ const isBoolColumn = isBoolType(String(column.fieldType ?? "").toLowerCase()) || String(column.jsType ?? "").toLowerCase() === "boolean";
65111
65352
  for (const aggregationType of AGGREGATION_OPTION_TYPES) {
65112
65353
  if (aggregationType === "count") continue;
65113
65354
  if (!canAggregateColumn(column, aggregationType)) continue;
65355
+ const isNumberSharePercentage = aggregationType === "percentage" && !isBoolColumn;
65356
+ if (isNumberSharePercentage && !resolvedGroupRowsBy) continue;
65114
65357
  const value = `${aggregationType}:${fieldName}`;
65115
65358
  if (seen.has(value)) continue;
65359
+ const aggregationWord = aggregationType === "percentage" ? isBoolColumn ? "Percent" : "Percent of Total" : formatAggregationTypeForDisplay(aggregationType);
65116
65360
  options2.push({
65117
65361
  value,
65118
- label: tableName ? `${formatAggregationTypeForDisplay(aggregationType)} ${toTitleCaseLabel(tableName)} ${toTitleCaseLabel(fieldName)}` : `${formatAggregationTypeForDisplay(aggregationType)} ${toTitleCaseLabel(fieldName)}`
65362
+ label: tableName ? `${aggregationWord} ${toTitleCaseLabel(tableName)} ${toTitleCaseLabel(fieldName)}` : `${aggregationWord} ${toTitleCaseLabel(fieldName)}`
65119
65363
  });
65120
65364
  seen.add(value);
65121
65365
  if (tableName && !tableLookup.has(value)) {
@@ -65138,6 +65382,7 @@ function useReport(reportIdArg, options = {}) {
65138
65382
  cleanedAggregations,
65139
65383
  fieldOptionsAllowedTableNames,
65140
65384
  joinCompatibleSchemaTableNames,
65385
+ resolvedGroupRowsBy,
65141
65386
  schemaForReportBuilderState
65142
65387
  ]);
65143
65388
  const { schemaColumnOptions, columnIdentifierLookup } = (0, import_react62.useMemo)(() => {
@@ -65380,9 +65625,13 @@ function useReport(reportIdArg, options = {}) {
65380
65625
  aggregation.aggregationType ?? ""
65381
65626
  ).toLowerCase();
65382
65627
  const tableHint = String(aggregationTablesByIndex[index] ?? "").trim();
65383
- const currentTarget = aggregation.valueField && aggregation.valueField.trim().length > 0 ? aggregation.valueField : normalizedAggregationType === "count" ? tableHint || aggregationFallbackTableName : aggregationFallbackTarget;
65628
+ const currentValueField = String(aggregation.valueField ?? "").trim();
65629
+ const currentValueField2 = String(aggregation.valueField2 ?? "").trim();
65630
+ const isPercentageRatio = normalizedAggregationType === "percentage" && Boolean(currentValueField) && Boolean(currentValueField2);
65631
+ const currentTarget = isPercentageRatio ? `${currentValueField}:${currentValueField2}` : currentValueField ? currentValueField : normalizedAggregationType === "count" || normalizedAggregationType === "percentage" ? tableHint || aggregationFallbackTableName : aggregationFallbackTarget;
65384
65632
  const currentValue = aggregation.aggregationType ? `${aggregation.aggregationType}:${currentTarget}` : void 0;
65385
- const currentLabel = aggregation.aggregationType ? `${formatAggregationTypeForDisplay(aggregation.aggregationType)} ${toTitleCaseLabel(currentTarget)}` : "Select aggregation";
65633
+ const isBoolPercentage = normalizedAggregationType === "percentage" && Boolean(currentValueField) && isBoolAggregationField(currentValueField, tableHint || void 0) === true;
65634
+ const currentLabel = aggregation.aggregationType ? isPercentageRatio ? `Percent ${toTitleCaseLabel(currentValueField)} of ${toTitleCaseLabel(currentValueField2)}` : normalizedAggregationType === "percentage" ? isBoolPercentage ? `Percent ${toTitleCaseLabel(currentTarget)}` : `Percent of Total ${toTitleCaseLabel(currentTarget)}` : `${formatAggregationTypeForDisplay(aggregation.aggregationType)} ${toTitleCaseLabel(currentTarget)}` : "Select aggregation";
65386
65635
  const options2 = currentValue ? baseOptions.some((option) => option.value === currentValue) ? baseOptions : [{ value: currentValue, label: currentLabel }, ...baseOptions] : baseOptions;
65387
65636
  return {
65388
65637
  key: `aggregation-${index}`,
@@ -65396,7 +65645,8 @@ function useReport(reportIdArg, options = {}) {
65396
65645
  aggregationFallbackTarget,
65397
65646
  aggregationState,
65398
65647
  aggregationBaseOptions,
65399
- aggregationTablesByIndex
65648
+ aggregationTablesByIndex,
65649
+ isBoolAggregationField
65400
65650
  ]);
65401
65651
  const nextPivot = (0, import_react62.useMemo)(() => {
65402
65652
  if (!sourceReport) return null;
@@ -65981,6 +66231,15 @@ function useReport(reportIdArg, options = {}) {
65981
66231
  chartAxisOptionByField.get(resolvedXAxisField)?.format ?? chartAxesBaseChart.xAxisFormat ?? resolvedYAxisFields[0]?.format,
65982
66232
  "string"
65983
66233
  );
66234
+ if (defaultFormat === "string") {
66235
+ const bucketFormat = resolvePivotDateBucketXAxisFormat(
66236
+ chartAxesBaseChart,
66237
+ resolvedXAxisField
66238
+ );
66239
+ if (bucketFormat) {
66240
+ defaultFormat = bucketFormat;
66241
+ }
66242
+ }
65984
66243
  if (isPivotTableDateBucketRowAxis(chartAxesBaseChart, resolvedXAxisField) && chartAxisEdits.xAxisFormat === void 0) {
65985
66244
  defaultFormat = "string";
65986
66245
  }
@@ -67160,10 +67419,12 @@ function useReport(reportIdArg, options = {}) {
67160
67419
  preferredTables
67161
67420
  );
67162
67421
  const existing = next.aggregationState[index];
67422
+ const preservedValueField2 = aggregationType !== "percentage" && existing?.valueField2 ? { valueField2: existing.valueField2 } : {};
67163
67423
  const nextAggregation = {
67164
- ...existing?.valueField2 ? { valueField2: existing.valueField2 } : {},
67424
+ ...preservedValueField2,
67165
67425
  aggregationType,
67166
67426
  ...normalizedSelection.valueField ? { valueField: normalizedSelection.valueField } : {},
67427
+ ...normalizedSelection.valueField2 ? { valueField2: normalizedSelection.valueField2 } : {},
67167
67428
  ...normalizedSelection.table ? { valueFieldTable: normalizedSelection.table } : {}
67168
67429
  };
67169
67430
  if (index === next.aggregationState.length) {
@@ -67200,6 +67461,7 @@ function useReport(reportIdArg, options = {}) {
67200
67461
  {
67201
67462
  aggregationType,
67202
67463
  ...normalizedSelection.valueField ? { valueField: normalizedSelection.valueField } : {},
67464
+ ...normalizedSelection.valueField2 ? { valueField2: normalizedSelection.valueField2 } : {},
67203
67465
  ...normalizedSelection.table ? { valueFieldTable: normalizedSelection.table } : {}
67204
67466
  }
67205
67467
  ];
@@ -67324,7 +67586,8 @@ function useReport(reportIdArg, options = {}) {
67324
67586
  next.chartType = effectiveNextState.chartType || void 0;
67325
67587
  const normalizedNext = processPivotState(next, {
67326
67588
  nextState: effectiveNextState,
67327
- promoteColumnToRow: false
67589
+ promoteColumnToRow: false,
67590
+ isBoolAggregationField
67328
67591
  });
67329
67592
  return normalizedNext;
67330
67593
  }
@@ -67475,6 +67738,17 @@ function useReport(reportIdArg, options = {}) {
67475
67738
  });
67476
67739
  }
67477
67740
  } catch (error) {
67741
+ if (isUseFormFiltersDebugEnabled()) {
67742
+ console.error("[useForm-debug] setFilters swallowed error", {
67743
+ error: error instanceof Error ? error.message : String(error),
67744
+ requestedRules: (nextFilters?.rules ?? []).map((rule) => ({
67745
+ table: rule?.table,
67746
+ field: rule?.field,
67747
+ operator: rule?.operator
67748
+ })),
67749
+ fieldConfigKeys: Object.keys(queryBuilderFieldConfigByName ?? {})
67750
+ });
67751
+ }
67478
67752
  }
67479
67753
  };
67480
67754
  const filterDraft = useReportFilterDraft({