@quillsql/react 2.16.11 → 2.16.12

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
@@ -1401,6 +1401,9 @@ var init_dateRangePickerUtils = __esm({
1401
1401
  value: 12,
1402
1402
  unit: "months"
1403
1403
  },
1404
+ {
1405
+ type: "previous_month"
1406
+ },
1404
1407
  {
1405
1408
  type: "relative",
1406
1409
  value: 1,
@@ -1440,11 +1443,33 @@ var init_dateRangePickerUtils = __esm({
1440
1443
  })
1441
1444
  };
1442
1445
  }
1443
- case "relative":
1446
+ case "relative": {
1447
+ if (interval2.unit === "months" && interval2.value === 1 && (!interval2.label || interval2.label === "Last month")) {
1448
+ const lastMonth = (0, import_date_fns2.subMonths)((0, import_date_fns2.startOfToday)(), 1);
1449
+ return {
1450
+ startDate: (0, import_date_fns2.startOfMonth)(lastMonth),
1451
+ endDate: (0, import_date_fns2.endOfMonth)(lastMonth)
1452
+ };
1453
+ }
1444
1454
  return {
1445
1455
  startDate: (0, import_date_fns2.sub)((0, import_date_fns2.startOfToday)(), { [interval2.unit]: interval2.value }),
1446
1456
  endDate: (0, import_date_fns2.endOfToday)()
1447
1457
  };
1458
+ }
1459
+ case "previous_month": {
1460
+ const lastMonth = (0, import_date_fns2.subMonths)((0, import_date_fns2.startOfToday)(), 1);
1461
+ return {
1462
+ startDate: (0, import_date_fns2.startOfMonth)(lastMonth),
1463
+ endDate: (0, import_date_fns2.endOfMonth)(lastMonth)
1464
+ };
1465
+ }
1466
+ case "previous_quarter": {
1467
+ const lastQuarter = (0, import_date_fns2.subQuarters)((0, import_date_fns2.startOfToday)(), 1);
1468
+ return {
1469
+ startDate: (0, import_date_fns2.startOfQuarter)(lastQuarter),
1470
+ endDate: (0, import_date_fns2.endOfQuarter)(lastQuarter)
1471
+ };
1472
+ }
1448
1473
  case "repeating": {
1449
1474
  const currentDate = /* @__PURE__ */ new Date();
1450
1475
  const currentYear = currentDate.getFullYear();
@@ -1489,24 +1514,39 @@ var init_dateRangePickerUtils = __esm({
1489
1514
  getLabelForCustomInterval = (interval2) => {
1490
1515
  switch (interval2.type) {
1491
1516
  case "week":
1492
- return "This week";
1517
+ return interval2.label ?? "This week";
1493
1518
  case "static":
1494
1519
  return interval2.label ?? "Custom";
1495
1520
  case "relative":
1496
1521
  return interval2.label ?? `Last ${interval2.value === 1 ? "" : interval2.value + " "}${interval2.value === 1 ? interval2.unit.slice(0, -1) : interval2.unit}`;
1522
+ case "previous_month":
1523
+ return interval2.label ?? "Last month";
1524
+ case "previous_quarter":
1525
+ return interval2.label ?? "Last quarter";
1497
1526
  case "repeating":
1498
- return "This " + interval2.label.toLowerCase();
1527
+ if (interval2.label === "Month") return "This month";
1528
+ if (interval2.label === "Year") return "This year";
1529
+ return interval2.label;
1499
1530
  }
1500
1531
  };
1501
1532
  convertPresetOptionsToSelectableList = (customIntervals, defaultIntervals) => {
1502
- const defaultCustomIntervals = defaultIntervals.map(
1503
- (interval2) => {
1504
- if (interval2.label === "This week") {
1505
- return {
1533
+ const customLabelMap = /* @__PURE__ */ new Map();
1534
+ defaultIntervals.forEach((interval2) => {
1535
+ if (interval2.customLabel) {
1536
+ customLabelMap.set(interval2.label, interval2.customLabel);
1537
+ }
1538
+ });
1539
+ const defaultCustomIntervals = defaultIntervals.flatMap((interval2) => {
1540
+ let createdIntervals = [];
1541
+ if (interval2.label === "This week") {
1542
+ createdIntervals = [
1543
+ {
1506
1544
  type: "week"
1507
- };
1508
- } else if (interval2.label === "This month" || interval2.label === "Monthly") {
1509
- return {
1545
+ }
1546
+ ];
1547
+ } else if (interval2.label === "This month" || interval2.label === "Monthly") {
1548
+ createdIntervals = [
1549
+ {
1510
1550
  type: "repeating",
1511
1551
  label: "Month",
1512
1552
  loopDate: { day: 1, month: 1 },
@@ -1586,9 +1626,11 @@ var init_dateRangePickerUtils = __esm({
1586
1626
  endDate: { day: 31, month: 12 }
1587
1627
  }
1588
1628
  ]
1589
- };
1590
- } else if (interval2.label === "This year" || interval2.label === "Yearly") {
1591
- return {
1629
+ }
1630
+ ];
1631
+ } else if (interval2.label === "This year" || interval2.label === "Yearly") {
1632
+ createdIntervals = [
1633
+ {
1592
1634
  type: "repeating",
1593
1635
  label: "Year",
1594
1636
  loopDate: { day: 1, month: 1 },
@@ -1602,42 +1644,69 @@ var init_dateRangePickerUtils = __esm({
1602
1644
  endDate: { day: 31, month: 12 }
1603
1645
  }
1604
1646
  ]
1605
- };
1606
- } else if (interval2.label === "Last 7 days") {
1607
- return {
1647
+ }
1648
+ ];
1649
+ } else if (interval2.label === "Last 7 days") {
1650
+ createdIntervals = [
1651
+ {
1608
1652
  type: "relative",
1609
1653
  value: 7,
1610
1654
  unit: "days"
1611
- };
1612
- } else if (interval2.label === "Last 30 days") {
1613
- return {
1655
+ }
1656
+ ];
1657
+ } else if (interval2.label === "Last 30 days") {
1658
+ createdIntervals = [
1659
+ {
1614
1660
  type: "relative",
1615
1661
  value: 30,
1616
1662
  unit: "days"
1617
- };
1618
- } else if (interval2.label === "Last 90 days") {
1619
- return {
1663
+ }
1664
+ ];
1665
+ } else if (interval2.label === "Last 90 days") {
1666
+ createdIntervals = [
1667
+ {
1620
1668
  type: "relative",
1621
1669
  value: 90,
1622
1670
  unit: "days"
1623
- };
1624
- } else if (interval2.label === "Last 6 months") {
1625
- return {
1671
+ }
1672
+ ];
1673
+ } else if (interval2.label === "Last 6 months") {
1674
+ createdIntervals = [
1675
+ {
1626
1676
  type: "relative",
1627
1677
  value: 6,
1628
1678
  unit: "months"
1629
- };
1630
- } else if (interval2.label === "Last 12 months") {
1631
- return {
1679
+ }
1680
+ ];
1681
+ } else if (interval2.label === "Last 12 months") {
1682
+ createdIntervals = [
1683
+ {
1632
1684
  type: "relative",
1633
1685
  value: 12,
1634
1686
  unit: "months"
1635
- };
1636
- } else {
1637
- throw new Error("Invalid interval");
1638
- }
1687
+ }
1688
+ ];
1689
+ } else if (interval2.label === "Last month") {
1690
+ createdIntervals = [
1691
+ {
1692
+ type: "previous_month"
1693
+ }
1694
+ ];
1695
+ } else if (interval2.label === "Last quarter") {
1696
+ createdIntervals = [
1697
+ {
1698
+ type: "previous_quarter"
1699
+ }
1700
+ ];
1639
1701
  }
1640
- );
1702
+ if (interval2.customLabel && createdIntervals.length > 0) {
1703
+ return createdIntervals.map((i) => ({
1704
+ ...i,
1705
+ label: interval2.customLabel
1706
+ }));
1707
+ }
1708
+ return createdIntervals;
1709
+ });
1641
1710
  return customIntervals?.length || defaultCustomIntervals?.length ? (defaultCustomIntervals ?? []).concat(customIntervals ?? []).flatMap((option) => {
1642
1711
  if (option.type === "repeating" && option.loopStart) {
1643
1712
  const presets = [];
@@ -1679,8 +1748,19 @@ var init_dateRangePickerUtils = __esm({
1679
1748
  }
1680
1749
  };
1681
1750
  const currentYear = currentDate.getFullYear();
1751
+ const baseLabel = getLabelForSubInterval(currentSubInterval);
1752
+ let customLabel2;
1753
+ if (option.label && option.label !== "Month" && option.label !== "Year") {
1754
+ customLabel2 = option.label;
1755
+ } else {
1756
+ if (option.label === "Month") {
1757
+ customLabel2 = customLabelMap.get("This month") || customLabelMap.get("Monthly");
1758
+ } else if (option.label === "Year") {
1759
+ customLabel2 = customLabelMap.get("This year") || customLabelMap.get("Yearly");
1760
+ }
1761
+ }
1682
1762
  presets.push({
1683
- label: getLabelForSubInterval(currentSubInterval),
1763
+ label: customLabel2 || baseLabel,
1684
1764
  value: getLabelForSubInterval(currentSubInterval).toUpperCase().replace(/ /g, "_") ?? "",
1685
1765
  startDate: (0, import_date_fns2.set)(/* @__PURE__ */ new Date(), {
1686
1766
  year: currentYear,
@@ -1712,9 +1792,12 @@ var init_dateRangePickerUtils = __esm({
1712
1792
  return presets;
1713
1793
  }
1714
1794
  const dateRange = convertCustomIntervalToDateRange(option);
1795
+ const generatedLabel = getLabelForCustomInterval(option);
1796
+ const customLabel = customLabelMap.get(generatedLabel);
1797
+ const finalLabel = customLabel || generatedLabel;
1715
1798
  return [
1716
1799
  {
1717
- label: getLabelForCustomInterval(option),
1800
+ label: finalLabel,
1718
1801
  // Value is option label in uppercase with spaces replaced by underscores
1719
1802
  value: getLabelForCustomInterval(option).toUpperCase().replace(/ /g, "_") ?? "",
1720
1803
  startDate: dateRange.startDate,
@@ -1771,6 +1854,7 @@ var init_dateRangePickerUtils = __esm({
1771
1854
  LAST_12_MONTHS: convertCustomIntervalToDateRange(
1772
1855
  primaryRangeCustomIntervals[7]
1773
1856
  ),
1857
+ LAST_MONTH: convertCustomIntervalToDateRange(primaryRangeCustomIntervals[8]),
1774
1858
  ALL_TIME: { startDate: void 0, endDate: void 0 }
1775
1859
  };
1776
1860
  COMPARISON_OPTIONS = [
@@ -1845,6 +1929,12 @@ var init_dateRangePickerUtils = __esm({
1845
1929
  startDate: PRIMARY_RANGE["LAST_30_DAYS"].startDate,
1846
1930
  endDate: PRIMARY_RANGE["LAST_30_DAYS"].endDate
1847
1931
  },
1932
+ {
1933
+ value: "LAST_MONTH",
1934
+ label: "Last month",
1935
+ startDate: PRIMARY_RANGE["LAST_MONTH"].startDate,
1936
+ endDate: PRIMARY_RANGE["LAST_MONTH"].endDate
1937
+ },
1848
1938
  {
1849
1939
  value: "LAST_90_DAYS",
1850
1940
  label: "Last 90 days",
@@ -15335,7 +15425,10 @@ var init_filterProcessing = __esm({
15335
15425
  })
15336
15426
  ).values()
15337
15427
  ],
15338
- options: defaultOptionsV2,
15428
+ options: filter.presetOptions || filter.defaultPresetRanges ? convertPresetOptionsToSelectableList(
15429
+ filter.presetOptions ?? [],
15430
+ filter.defaultPresetRanges ?? []
15431
+ ) : defaultOptionsV2,
15339
15432
  preset: filter.primaryRange,
15340
15433
  dashboardName,
15341
15434
  comparisonRange: filter.comparison && comparisonRangeStart && comparisonRangeEnd ? {
@@ -17919,6 +18012,22 @@ var init_tableProcessing = __esm({
17919
18012
  return {};
17920
18013
  }
17921
18014
  let uniqueValuesByColumn = {};
18015
+ const aliasToField = {};
18016
+ const fieldToAlias = {};
18017
+ columns.forEach((col) => {
18018
+ if (col.alias) {
18019
+ aliasToField[col.alias] = col.field;
18020
+ fieldToAlias[col.field] = col.alias;
18021
+ }
18022
+ });
18023
+ reportBuilderState?.columns?.forEach((col) => {
18024
+ if (col.alias) {
18025
+ aliasToField[col.alias] = col.field;
18026
+ if (!fieldToAlias[col.field]) {
18027
+ fieldToAlias[col.field] = col.alias;
18028
+ }
18029
+ }
18030
+ });
17922
18031
  let rows = resp?.rows;
17923
18032
  if (!rows && fetchResult.queries?.queryResults?.[0]?.rows) {
17924
18033
  rows = fetchResult.queries.queryResults[0].rows;
@@ -17927,11 +18036,24 @@ var init_tableProcessing = __esm({
17927
18036
  uniqueValuesByColumn = resp.uniqueValues;
17928
18037
  } else if (rows && Array.isArray(rows)) {
17929
18038
  rows.forEach((row) => {
17930
- if (row.field && row.string_values) {
18039
+ if (row?.field && row?.string_values) {
17931
18040
  uniqueValuesByColumn[row.field] = row.string_values;
17932
18041
  }
17933
18042
  });
17934
18043
  }
18044
+ const normalizedUniqueValuesByColumn = {};
18045
+ Object.entries(uniqueValuesByColumn).forEach(([key, values]) => {
18046
+ if (!Array.isArray(values)) {
18047
+ return;
18048
+ }
18049
+ const fieldKey = aliasToField[key] || key;
18050
+ normalizedUniqueValuesByColumn[fieldKey] = values;
18051
+ const aliasKey = fieldToAlias[fieldKey];
18052
+ if (aliasKey) {
18053
+ normalizedUniqueValuesByColumn[aliasKey] = values;
18054
+ }
18055
+ });
18056
+ uniqueValuesByColumn = normalizedUniqueValuesByColumn;
17935
18057
  const uniqueValues2 = {};
17936
18058
  tables.forEach((tableName) => {
17937
18059
  const tableUniqueValues = {};
@@ -20351,7 +20473,18 @@ async function fetchReportRows({
20351
20473
  getToken
20352
20474
  });
20353
20475
  const resp = await parseFetchResponse(client, "report", fetchResp, getToken);
20354
- return { rows: resp.rows || [], rowCount: resp.rowCount || 0 };
20476
+ if (client.databaseType?.toLowerCase() === "bigquery") {
20477
+ parseValueFromBigQueryDates(resp.rows, resp.fields);
20478
+ }
20479
+ const columns = (resp.fields || []).map(
20480
+ (field) => convertPostgresColumn(field)
20481
+ );
20482
+ return {
20483
+ rows: resp.rows || [],
20484
+ rowCount: resp.rowCount || 0,
20485
+ columns,
20486
+ fields: resp.fields || []
20487
+ };
20355
20488
  }
20356
20489
  async function fetchReport({
20357
20490
  reportId,
@@ -20461,6 +20594,7 @@ async function processReportResponse({
20461
20594
  tenants,
20462
20595
  skipPivotFetch = false
20463
20596
  }) {
20597
+ const shouldSkipPivotFetch = skipPivotFetch || !!resp?.pivotRows && !!resp?.fields;
20464
20598
  const dashboardItem = {
20465
20599
  ...resp,
20466
20600
  filtersApplied: filters?.filter(
@@ -20491,7 +20625,7 @@ async function processReportResponse({
20491
20625
  getToken,
20492
20626
  tenants,
20493
20627
  eventTracking,
20494
- skipPivotFetch
20628
+ skipPivotFetch: shouldSkipPivotFetch
20495
20629
  });
20496
20630
  if (additionalProcessing) {
20497
20631
  reportInfo.pagination = additionalProcessing.page;
@@ -22520,6 +22654,34 @@ var ContextProvider = ({
22520
22654
  );
22521
22655
  if (currentProcessId === loadDashboardProcessId.current[dashboardName]) {
22522
22656
  if (!(0, import_fast_deep_equal.default)(resp, curDashboardConfig)) {
22657
+ Object.values(
22658
+ resp.sections ?? {}
22659
+ ).flat().forEach((report) => {
22660
+ const existing = reports[report.id];
22661
+ if (existing) {
22662
+ const merged = {
22663
+ ...report,
22664
+ // prefer fresh server metadata (chartType, flags, etc.)
22665
+ // preserve existing row payload if server response is metadata-only
22666
+ ...existing.rows ? { rows: existing.rows } : {},
22667
+ ...existing.rowCount !== void 0 ? { rowCount: existing.rowCount } : {},
22668
+ ...existing.columns ? { columns: existing.columns } : {},
22669
+ ...existing.columnInternal ? { columnInternal: existing.columnInternal } : {},
22670
+ ...existing.pagination ? { pagination: existing.pagination } : {}
22671
+ };
22672
+ reportsDispatch({
22673
+ type: "UPDATE_REPORT",
22674
+ id: report.id,
22675
+ data: merged
22676
+ });
22677
+ } else {
22678
+ reportsDispatch({
22679
+ type: "ADD_REPORT",
22680
+ id: report.id,
22681
+ data: report
22682
+ });
22683
+ }
22684
+ });
22523
22685
  dashboardConfigDispatch({
22524
22686
  type: "UPDATE_DASHBOARD",
22525
22687
  id: dashboardName,
@@ -23335,11 +23497,12 @@ var useDashboardInternal = (dashboardName, customFilters) => {
23335
23497
  dashboardName,
23336
23498
  sectionOrder
23337
23499
  };
23338
- void quillFetchWithToken({
23339
- client,
23340
- task: "set-section-order",
23341
- metadata: body
23342
- }).then((response) => {
23500
+ try {
23501
+ const response = await quillFetchWithToken({
23502
+ client,
23503
+ task: "set-section-order",
23504
+ metadata: body
23505
+ });
23343
23506
  Object.entries(response?.data?.newIds ?? {}).forEach(
23344
23507
  ([section, newId2]) => {
23345
23508
  dashboardConfigDispatch({
@@ -23361,11 +23524,24 @@ var useDashboardInternal = (dashboardName, customFilters) => {
23361
23524
  });
23362
23525
  }
23363
23526
  );
23364
- }).catch((error) => {
23365
- if (error instanceof Error && error.name === "AbortError") {
23527
+ } catch (e) {
23528
+ if (e instanceof Error && e.name === "AbortError") {
23366
23529
  return;
23367
23530
  }
23368
- });
23531
+ eventTracking?.logError?.({
23532
+ type: "bug",
23533
+ // TODO: determine type
23534
+ severity: "high",
23535
+ message: "Error setting section order",
23536
+ errorMessage: e?.message,
23537
+ errorStack: e?.stack,
23538
+ errorData: {
23539
+ caller: "useDashboard",
23540
+ function: "setSectionOrder"
23541
+ }
23542
+ });
23543
+ console.error(e);
23544
+ }
23369
23545
  }
23370
23546
  };
23371
23547
  function isDashboardFilterLoading(filterName) {
@@ -23623,7 +23799,8 @@ var useDashboards = () => {
23623
23799
  (preset) => ({
23624
23800
  ...preset,
23625
23801
  loopStart: preset.loopStart ? new Date(preset.loopStart) : void 0,
23626
- loopEnd: preset.loopEnd ? new Date(preset.loopEnd) : void 0
23802
+ loopEnd: preset.loopEnd ? new Date(preset.loopEnd) : void 0,
23803
+ customLabel: preset.customLabel
23627
23804
  })
23628
23805
  )
23629
23806
  } : void 0,
@@ -23683,7 +23860,8 @@ var useDashboards = () => {
23683
23860
  (preset) => ({
23684
23861
  ...preset,
23685
23862
  loopStart: preset.loopStart ? new Date(preset.loopStart) : void 0,
23686
- loopEnd: preset.loopEnd ? new Date(preset.loopEnd) : void 0
23863
+ loopEnd: preset.loopEnd ? new Date(preset.loopEnd) : void 0,
23864
+ customLabel: preset.customLabel
23687
23865
  })
23688
23866
  ),
23689
23867
  dashboardName: updated.data.dashboard.name,
@@ -23801,6 +23979,7 @@ var useDashboard = (dashboardName, config) => {
23801
23979
  const { customReportFilters } = (0, import_react2.useContext)(ReportFiltersContext);
23802
23980
  const { eventTracking } = (0, import_react2.useContext)(EventTrackingContext);
23803
23981
  const customFiltersRef = (0, import_react2.useRef)(customReportFilters);
23982
+ const reportRequestIds = (0, import_react2.useRef)({});
23804
23983
  (0, import_react2.useEffect)(() => {
23805
23984
  customFiltersRef.current = customReportFilters;
23806
23985
  }, [customReportFilters]);
@@ -23943,6 +24122,8 @@ var useDashboard = (dashboardName, config) => {
23943
24122
  await Promise.all(
23944
24123
  allReports.map(async (reportInfo) => {
23945
24124
  const reportId = reportInfo.id;
24125
+ const requestId = (reportRequestIds.current[reportId] ?? 0) + 1;
24126
+ reportRequestIds.current[reportId] = requestId;
23946
24127
  reportsLoadingStateDispatch({
23947
24128
  type: "SET_REPORT_LOADING",
23948
24129
  id: reportId,
@@ -23960,6 +24141,7 @@ var useDashboard = (dashboardName, config) => {
23960
24141
  const additionalProcessing = {
23961
24142
  page: pagination
23962
24143
  };
24144
+ const usePivotTask = !!reportInfo.pivot;
23963
24145
  const { report, error } = await fetchReport({
23964
24146
  reportId,
23965
24147
  client,
@@ -23968,12 +24150,16 @@ var useDashboard = (dashboardName, config) => {
23968
24150
  additionalProcessing,
23969
24151
  filters: dashboardFilters2.concat(customFilters).concat(customReportFiltersArray),
23970
24152
  getToken,
23971
- eventTracking
24153
+ eventTracking,
24154
+ usePivotTask
23972
24155
  });
23973
24156
  if (error) {
23974
24157
  console.error(error);
23975
24158
  return null;
23976
24159
  }
24160
+ if (reportRequestIds.current[reportId] !== requestId) {
24161
+ return null;
24162
+ }
23977
24163
  reportsDispatch({
23978
24164
  type: "UPDATE_REPORT",
23979
24165
  id: reportId,
@@ -23987,6 +24173,40 @@ var useDashboard = (dashboardName, config) => {
23987
24173
  id: reportId,
23988
24174
  data: false
23989
24175
  });
24176
+ if (usePivotTask) {
24177
+ fetchReportRows({
24178
+ reportId,
24179
+ client,
24180
+ tenants,
24181
+ filters: dashboardFilters2.concat(customFilters).concat(customReportFiltersArray),
24182
+ getToken,
24183
+ additionalProcessing
24184
+ }).then(({ rows, rowCount, columns, fields }) => {
24185
+ if (reportRequestIds.current[reportId] !== requestId) {
24186
+ return;
24187
+ }
24188
+ reportsDispatch({
24189
+ type: "UPDATE_REPORT",
24190
+ id: reportId,
24191
+ data: {
24192
+ rows,
24193
+ rowCount,
24194
+ columnInternal: columns,
24195
+ columns: columns.map((col) => ({
24196
+ field: col.field,
24197
+ format: col.format,
24198
+ label: col.label,
24199
+ inferFormat: col.inferFormat
24200
+ })),
24201
+ // @ts-ignore fields is not typed on QuillReportInternal
24202
+ fields
24203
+ }
24204
+ });
24205
+ }).catch((e) => {
24206
+ if (e instanceof Error && e.name === "AbortError") return;
24207
+ console.error("Failed to fetch background rows", e);
24208
+ });
24209
+ }
23990
24210
  return report;
23991
24211
  })
23992
24212
  );
@@ -28287,6 +28507,8 @@ var axisFormatter = ({ value, field, fields }) => {
28287
28507
  return formatPercent2(value);
28288
28508
  case "dollar_amount":
28289
28509
  return formatDollarAmount2(value);
28510
+ case "dollar_cents":
28511
+ return formatDollarCents2(value);
28290
28512
  case "whole_number":
28291
28513
  return formatWholeNumber2(value);
28292
28514
  case "two_decimal_places":
@@ -28351,6 +28573,12 @@ var formatterDollar2 = new Intl.NumberFormat("en-US", {
28351
28573
  currency: "USD",
28352
28574
  maximumFractionDigits: 0
28353
28575
  });
28576
+ var formatterDollarCents = new Intl.NumberFormat("en-US", {
28577
+ style: "currency",
28578
+ currency: "USD",
28579
+ minimumFractionDigits: 2,
28580
+ maximumFractionDigits: 2
28581
+ });
28354
28582
  var formatDollarAmount2 = (value) => {
28355
28583
  const num = Number(value ?? 0);
28356
28584
  if (num >= 1e3 || num === 0) {
@@ -28359,6 +28587,9 @@ var formatDollarAmount2 = (value) => {
28359
28587
  return formatterDollar2.format(num);
28360
28588
  }
28361
28589
  };
28590
+ var formatDollarCents2 = (value) => {
28591
+ return formatterDollarCents.format(Number(value ?? 0));
28592
+ };
28362
28593
  var formatterBigWholeNumber = new Intl.NumberFormat("en-US", {
28363
28594
  minimumSignificantDigits: 1,
28364
28595
  maximumSignificantDigits: 2,
@@ -32433,6 +32664,7 @@ function DataLoader({
32433
32664
  rowsAbortController.current?.abort();
32434
32665
  rowsAbortController.current = new AbortController();
32435
32666
  const usePivotTask = !!item.pivot;
32667
+ let backgroundFetch = null;
32436
32668
  try {
32437
32669
  if (dashboardName) {
32438
32670
  const { report: fetchedReport, error: error2 } = await fetchReport({
@@ -32451,16 +32683,59 @@ function DataLoader({
32451
32683
  eventTracking,
32452
32684
  usePivotTask
32453
32685
  });
32686
+ const existingReport = dashboard[item.id] ?? reports[item.id];
32687
+ const baseReport = error2 && existingReport ? { ...existingReport, error: error2 } : fetchedReport;
32688
+ const reportToAdd = {
32689
+ ...baseReport,
32690
+ rows: baseReport?.rows ?? [],
32691
+ columns: baseReport?.columns ?? [],
32692
+ columnInternal: baseReport?.columnInternal ?? []
32693
+ };
32454
32694
  addReport({
32455
- ...fetchedReport,
32695
+ ...reportToAdd,
32456
32696
  id: item.id,
32457
32697
  triggerReload: false,
32458
- rowCount: fetchedReport.pivot ? fetchedReport.rowCount : 0,
32698
+ rowCount: reportToAdd.pivot ? reportToAdd.rowCount : 0,
32459
32699
  filtersApplied: userFilters,
32460
32700
  loadingRows: false
32461
32701
  // rowCount 0 indicates it's still loading if row length is nonzero
32462
32702
  });
32463
32703
  setError(error2);
32704
+ if (usePivotTask) {
32705
+ const backgroundAbortController = new AbortController();
32706
+ backgroundFetch = () => {
32707
+ fetchReportRows({
32708
+ reportId: item.id,
32709
+ client,
32710
+ tenants,
32711
+ filters: filters.concat(userFilters ?? []),
32712
+ getToken,
32713
+ abortSignal: backgroundAbortController.signal,
32714
+ additionalProcessing: {
32715
+ ...processing,
32716
+ ...{ page: DEFAULT_PAGINATION }
32717
+ }
32718
+ }).then(({ rows, rowCount, columns, fields }) => {
32719
+ updateReport({
32720
+ id: item.id,
32721
+ rows,
32722
+ rowCount,
32723
+ columnInternal: columns,
32724
+ columns: columns.map((col) => ({
32725
+ field: col.field,
32726
+ format: col.format,
32727
+ label: col.label,
32728
+ inferFormat: col.inferFormat
32729
+ })),
32730
+ // @ts-ignore
32731
+ fields
32732
+ });
32733
+ }).catch((e) => {
32734
+ if (e instanceof Error && e.name === "AbortError") return;
32735
+ console.error("Failed to fetch background rows", e);
32736
+ });
32737
+ };
32738
+ }
32464
32739
  } else {
32465
32740
  try {
32466
32741
  await fetchIndividualReport({
@@ -32517,6 +32792,9 @@ function DataLoader({
32517
32792
  if (fetchRowsRequestId === rowsRequestId.current) {
32518
32793
  rowsAbortController.current = null;
32519
32794
  setLoading(false);
32795
+ if (backgroundFetch) {
32796
+ backgroundFetch();
32797
+ }
32520
32798
  }
32521
32799
  }
32522
32800
  };
@@ -32657,8 +32935,16 @@ var ChartDataLoader = ({
32657
32935
  eventTracking,
32658
32936
  usePivotTask
32659
32937
  });
32938
+ const existingReport = dashboard[item.id] ?? reports[item.id];
32939
+ const baseReport = error2 && existingReport ? { ...existingReport, error: error2 } : report;
32940
+ const reportToAdd = {
32941
+ ...baseReport,
32942
+ rows: baseReport?.rows ?? [],
32943
+ columns: baseReport?.columns ?? [],
32944
+ columnInternal: baseReport?.columnInternal ?? []
32945
+ };
32660
32946
  addReport({
32661
- ...report,
32947
+ ...reportToAdd,
32662
32948
  id: item.id,
32663
32949
  triggerReload: false,
32664
32950
  filtersApplied: userFilters,
@@ -32679,11 +32965,20 @@ var ChartDataLoader = ({
32679
32965
  ...additionalProcessing,
32680
32966
  ...{ page: DEFAULT_PAGINATION }
32681
32967
  }
32682
- }).then(({ rows, rowCount }) => {
32968
+ }).then(({ rows, rowCount, columns, fields }) => {
32683
32969
  updateReport({
32684
32970
  id: item.id,
32685
32971
  rows,
32686
- rowCount
32972
+ rowCount,
32973
+ columnInternal: columns,
32974
+ columns: columns.map((col) => ({
32975
+ field: col.field,
32976
+ format: col.format,
32977
+ label: col.label,
32978
+ inferFormat: col.inferFormat
32979
+ })),
32980
+ // @ts-ignore
32981
+ fields
32687
32982
  });
32688
32983
  }).catch((e) => {
32689
32984
  if (e instanceof Error && e.name === "AbortError") return;
@@ -41207,7 +41502,7 @@ var PivotModal = ({
41207
41502
  });
41208
41503
  }
41209
41504
  },
41210
- options: (samplePivotTable?.columns ?? []).map((column) => ({
41505
+ options: (samplePivotTable?.columns && samplePivotTable.columns.length > 0 ? samplePivotTable.columns : pivotRowField ? [{ field: pivotRowField }] : []).map((column) => ({
41211
41506
  label: snakeAndCamelCaseToTitleCase(
41212
41507
  column.field
41213
41508
  ),
@@ -41620,6 +41915,7 @@ init_Filter();
41620
41915
  init_filterProcessing();
41621
41916
  init_dateRangePickerUtils();
41622
41917
  var import_jsx_runtime63 = require("react/jsx-runtime");
41918
+ var MULTISELECT_REQUEST_DEBOUNCE_MS = 1e3;
41623
41919
  var TogglePrimitive = ({
41624
41920
  value,
41625
41921
  onClick,
@@ -41759,6 +42055,20 @@ function InternalChart({
41759
42055
  ) : defaultOptionsV2;
41760
42056
  }, [reportDateFilter]);
41761
42057
  const [filterValues, setFilterValues] = (0, import_react42.useState)({});
42058
+ const multiselectDebounceRef = (0, import_react42.useRef)({});
42059
+ const latestMultiselectValueRef = (0, import_react42.useRef)({});
42060
+ const [lockedFilters, setLockedFilters] = (0, import_react42.useState)(
42061
+ {}
42062
+ );
42063
+ (0, import_react42.useEffect)(() => {
42064
+ return () => {
42065
+ Object.values(multiselectDebounceRef.current).forEach((timer2) => {
42066
+ if (timer2) {
42067
+ clearTimeout(timer2);
42068
+ }
42069
+ });
42070
+ };
42071
+ }, []);
41762
42072
  (0, import_react42.useEffect)(() => {
41763
42073
  if (reportDateFilter) {
41764
42074
  const customDateFilter = filters?.find(
@@ -41865,6 +42175,46 @@ function InternalChart({
41865
42175
  ...filterValues2,
41866
42176
  [filter.label]: filterValue
41867
42177
  }));
42178
+ if (filter.filterType === "string" /* String */ && filter.stringFilterType === "multiselect") {
42179
+ latestMultiselectValueRef.current[filter.label] = filterValue;
42180
+ const existingTimer = multiselectDebounceRef.current[filter.label];
42181
+ if (existingTimer) {
42182
+ clearTimeout(existingTimer);
42183
+ }
42184
+ multiselectDebounceRef.current[filter.label] = setTimeout(() => {
42185
+ multiselectDebounceRef.current[filter.label] = void 0;
42186
+ setLockedFilters((prev) => ({
42187
+ ...prev,
42188
+ [filter.label]: true
42189
+ }));
42190
+ try {
42191
+ const maybePromise = onDashboardFilterChange(
42192
+ filter.label,
42193
+ latestMultiselectValueRef.current[filter.label]
42194
+ );
42195
+ if (maybePromise && typeof maybePromise.finally === "function") {
42196
+ maybePromise.finally(() => {
42197
+ setLockedFilters((prev) => ({
42198
+ ...prev,
42199
+ [filter.label]: false
42200
+ }));
42201
+ });
42202
+ } else {
42203
+ setLockedFilters((prev) => ({
42204
+ ...prev,
42205
+ [filter.label]: false
42206
+ }));
42207
+ }
42208
+ } catch (e) {
42209
+ setLockedFilters((prev) => ({
42210
+ ...prev,
42211
+ [filter.label]: false
42212
+ }));
42213
+ throw e;
42214
+ }
42215
+ }, MULTISELECT_REQUEST_DEBOUNCE_MS);
42216
+ return;
42217
+ }
41868
42218
  onDashboardFilterChange(filter.label, filterValue);
41869
42219
  };
41870
42220
  const [filtersExpanded, setFiltersExpanded] = (0, import_react42.useState)(false);
@@ -42022,7 +42372,7 @@ function InternalChart({
42022
42372
  SelectComponent,
42023
42373
  MultiSelectComponent,
42024
42374
  DateRangePickerComponent,
42025
- disabled: !filtersEnabled,
42375
+ disabled: !filtersEnabled || lockedFilters[filter.filter.label],
42026
42376
  containerStyle: {
42027
42377
  // display: !filtersExpanded && visibleFilters[index] ? 'none' : 'inline',
42028
42378
  visibility: !filtersExpanded && visibleFilters[index] ? "hidden" : "visible"
@@ -43883,6 +44233,7 @@ function ChartBuilder({
43883
44233
  client,
43884
44234
  uniqueValues,
43885
44235
  dashboardName: destinationDashboardName,
44236
+ dashboardFilters: dashboardFilters2,
43886
44237
  tenants,
43887
44238
  additionalProcessing: baseProcessing,
43888
44239
  getToken,
@@ -43994,7 +44345,7 @@ function ChartBuilder({
43994
44345
  }
43995
44346
  return filter;
43996
44347
  });
43997
- loadFiltersForReport(
44348
+ return loadFiltersForReport(
43998
44349
  report?.id ?? TEMP_REPORT_ID,
43999
44350
  "ChartBuilder",
44000
44351
  updatedFilters,
@@ -44004,7 +44355,7 @@ function ChartBuilder({
44004
44355
  ).then(() => {
44005
44356
  setCurrentPage(0);
44006
44357
  setMaxPage(0);
44007
- handleRunQuery(baseProcessing, updatedFilters);
44358
+ return handleRunQuery(baseProcessing, updatedFilters);
44008
44359
  });
44009
44360
  };
44010
44361
  const filtersEnabledRef = (0, import_react44.useRef)(filtersEnabled);
@@ -44563,12 +44914,21 @@ function ChartBuilder({
44563
44914
  error: void 0,
44564
44915
  section: formData.section
44565
44916
  };
44566
- addReport(data);
44567
- reportsDispatch({
44568
- type: "ADD_REPORT",
44569
- id: resp.id,
44570
- data
44571
- });
44917
+ const reportFlags = data.flags;
44918
+ const hasRestrictedFlags = reportFlags && Object.values(reportFlags).some(
44919
+ (v) => Array.isArray(v) && v.length > 0
44920
+ );
44921
+ const currentTenantCanSee = !hasRestrictedFlags || flags && Object.values(reportFlags).some(
44922
+ (v) => v === "QUILL_ALL_TENANTS" || Array.isArray(v) && v.some((f) => flags.includes(f))
44923
+ );
44924
+ if (currentTenantCanSee) {
44925
+ addReport(data);
44926
+ reportsDispatch({
44927
+ type: "ADD_REPORT",
44928
+ id: resp.id,
44929
+ data
44930
+ });
44931
+ }
44572
44932
  if (onAddToDashboardComplete) {
44573
44933
  onAddToDashboardComplete(data);
44574
44934
  } else {
@@ -48985,6 +49345,14 @@ var useReportBuilderInternal = ({
48985
49345
  report,
48986
49346
  skipPivotColumnFetch
48987
49347
  }) => {
49348
+ const tablesEqual = (a, b) => {
49349
+ if (a.length !== b.length) return false;
49350
+ const aNames = a.map((t) => t.name).sort();
49351
+ const bNames = b.map((t) => t.name).sort();
49352
+ return aNames.every((name2, idx) => name2 === bNames[idx]);
49353
+ };
49354
+ const haveTablesChanged = tablesChanged ?? !tablesEqual(state.tables, tables);
49355
+ const needsFilteredUniqueValues = !!filtersChanged || !!limitChanged || haveTablesChanged;
48988
49356
  if (!client) {
48989
49357
  return;
48990
49358
  }
@@ -48992,8 +49360,8 @@ var useReportBuilderInternal = ({
48992
49360
  state,
48993
49361
  pivot: state.pivot,
48994
49362
  previousReport: report,
48995
- requiresNewFilteredUniqueValues: filtersChanged || limitChanged,
48996
- requiresNewUnfilteredUniqueValues: tablesChanged,
49363
+ requiresNewFilteredUniqueValues: needsFilteredUniqueValues,
49364
+ requiresNewUnfilteredUniqueValues: haveTablesChanged,
48997
49365
  skipPivotColumnFetch
48998
49366
  });
48999
49367
  };
@@ -49604,18 +49972,10 @@ var useReportBuilderInternal = ({
49604
49972
  }, [client]);
49605
49973
  (0, import_react48.useEffect)(() => {
49606
49974
  const loadChart = async () => {
49607
- let report;
49608
- if (!client) {
49609
- return;
49610
- }
49975
+ if (!client || !reportId) return;
49976
+ const report = allReportsById[reportId];
49977
+ if (!report) return;
49611
49978
  try {
49612
- if (!reportId) {
49613
- throw new Error("Report ID is required");
49614
- }
49615
- report = allReportsById[reportId];
49616
- if (!report) {
49617
- throw new Error("Report not found");
49618
- }
49619
49979
  const { ast: newAst, pivot: newPivot } = await fetchASTFromQuillReport(
49620
49980
  report,
49621
49981
  client,
@@ -52909,15 +53269,20 @@ function ChartEditor({
52909
53269
  const parentRef = (0, import_react55.useRef)(null);
52910
53270
  const [modalWidth, setModalWidth] = (0, import_react55.useState)(200);
52911
53271
  const [modalHeight, setModalHeight] = (0, import_react55.useState)(200);
52912
- const { addReport } = useDashboardReports(destinationDashboard);
52913
53272
  const { allReportsById } = useAllReports();
53273
+ const report = allReportsById[reportId];
53274
+ const resolvedDashboard = (0, import_react55.useMemo)(
53275
+ () => destinationDashboard ?? report?.dashboardName ?? null,
53276
+ [destinationDashboard, report?.dashboardName]
53277
+ );
53278
+ const { addReport } = useDashboardReports(resolvedDashboard ?? void 0);
52914
53279
  const { tenants, flags } = (0, import_react55.useContext)(TenantContext);
52915
53280
  const { getToken } = (0, import_react55.useContext)(FetchContext);
52916
- const report = allReportsById[reportId];
52917
53281
  const [client, isClientLoading] = (0, import_react55.useContext)(ClientContext);
52918
53282
  const [schemaData] = (0, import_react55.useContext)(SchemaDataContext);
52919
53283
  const { dashboardFilters } = (0, import_react55.useContext)(DashboardFiltersContext);
52920
53284
  const { eventTracking } = (0, import_react55.useContext)(EventTrackingContext);
53285
+ const { reload } = useDashboardInternal(resolvedDashboard);
52921
53286
  const specificDashboardFilters = (0, import_react55.useMemo)(() => {
52922
53287
  if (!report) {
52923
53288
  return [];
@@ -52925,7 +53290,7 @@ function ChartEditor({
52925
53290
  return Object.values(dashboardFilters[report.dashboardName] || {}).map(
52926
53291
  (f) => f.filter
52927
53292
  );
52928
- }, [dashboardFilters]);
53293
+ }, [dashboardFilters, report?.dashboardName]);
52929
53294
  const [filtersEnabled, setFiltersEnabled] = (0, import_react55.useState)(true);
52930
53295
  const [chartBuilderKey, setChartBuilderKey] = (0, import_react55.useState)(0);
52931
53296
  const dateFilter = Object.values(specificDashboardFilters).find(
@@ -52951,7 +53316,7 @@ function ChartEditor({
52951
53316
  };
52952
53317
  }, []);
52953
53318
  const fetchReportHelper = async (processing) => {
52954
- if (!client) {
53319
+ if (!client || !reportId) {
52955
53320
  return;
52956
53321
  }
52957
53322
  const minimalFilters = Object.values(specificDashboardFilters).length ? Object.values(specificDashboardFilters).map((filter) => {
@@ -52983,10 +53348,13 @@ function ChartEditor({
52983
53348
  addReport(report2);
52984
53349
  };
52985
53350
  (0, import_react55.useEffect)(() => {
53351
+ if (!isOpen || !reportId) {
53352
+ return;
53353
+ }
52986
53354
  if (!isClientLoading && !report) {
52987
53355
  fetchReportHelper();
52988
53356
  }
52989
- }, [reportId, isClientLoading, allReportsById]);
53357
+ }, [reportId, isClientLoading, allReportsById, isOpen]);
52990
53358
  return /* @__PURE__ */ (0, import_jsx_runtime82.jsx)("div", { ref: parentRef, style: { height: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime82.jsx)(
52991
53359
  ModalComponent,
52992
53360
  {
@@ -53000,7 +53368,7 @@ function ChartEditor({
53000
53368
  title: chartBuilderTitle || "Add to dashboard",
53001
53369
  width: isHorizontalView ? modalWidth : void 0,
53002
53370
  height: isHorizontalView ? modalHeight : void 0,
53003
- children: allReportsById[reportId] ? /* @__PURE__ */ (0, import_jsx_runtime82.jsx)(
53371
+ children: reportId && allReportsById[reportId] ? /* @__PURE__ */ (0, import_jsx_runtime82.jsx)(
53004
53372
  ChartBuilder,
53005
53373
  {
53006
53374
  reportId,
@@ -53015,8 +53383,15 @@ function ChartEditor({
53015
53383
  } else if (onAddToDashboardComplete) {
53016
53384
  onAddToDashboardComplete(data);
53017
53385
  }
53386
+ const targetDashboard = data?.dashboardName ?? resolvedDashboard;
53387
+ if (targetDashboard) {
53388
+ reload(targetDashboard, true, {
53389
+ report: data,
53390
+ action: "upsert"
53391
+ });
53392
+ }
53018
53393
  },
53019
- destinationDashboard,
53394
+ destinationDashboard: resolvedDashboard ?? void 0,
53020
53395
  destinationSection,
53021
53396
  dateRange,
53022
53397
  SelectComponent,