@prismiq/react 0.1.0 → 0.1.1

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 (57) hide show
  1. package/dist/{CustomSQLEditor-BXB4rf1q.d.cts → CustomSQLEditor-CYlOtecq.d.ts} +10 -3
  2. package/dist/{CustomSQLEditor-DYeId0Gp.d.ts → CustomSQLEditor-d84v_Cgp.d.cts} +10 -3
  3. package/dist/{DashboardDialog-LHmrtNQU.d.cts → DashboardDialog-CZD8I-6z.d.cts} +4 -4
  4. package/dist/{DashboardDialog-B3vYC5Gs.d.ts → DashboardDialog-DBNTVVSp.d.ts} +4 -4
  5. package/dist/{accessibility-2yy5yqRR.d.cts → accessibility-Bu2mNtaB.d.cts} +1 -1
  6. package/dist/{accessibility-2yy5yqRR.d.ts → accessibility-Bu2mNtaB.d.ts} +1 -1
  7. package/dist/charts/index.cjs +27 -27
  8. package/dist/charts/index.d.cts +2 -2
  9. package/dist/charts/index.d.ts +2 -2
  10. package/dist/charts/index.js +2 -2
  11. package/dist/{chunk-MOAEEF5P.js → chunk-3LDRRDJ6.js} +185 -91
  12. package/dist/chunk-3LDRRDJ6.js.map +1 -0
  13. package/dist/{chunk-NK7HKX2J.cjs → chunk-73TPDGXB.cjs} +7 -7
  14. package/dist/{chunk-NK7HKX2J.cjs.map → chunk-73TPDGXB.cjs.map} +1 -1
  15. package/dist/{chunk-UPYINBZU.js → chunk-ET7GCREP.js} +502 -46
  16. package/dist/chunk-ET7GCREP.js.map +1 -0
  17. package/dist/{chunk-2H5WTH4K.js → chunk-FQ23KG6G.js} +3 -3
  18. package/dist/{chunk-2H5WTH4K.js.map → chunk-FQ23KG6G.js.map} +1 -1
  19. package/dist/{chunk-4AVL6GQK.cjs → chunk-KXB2IZI2.cjs} +36 -9
  20. package/dist/chunk-KXB2IZI2.cjs.map +1 -0
  21. package/dist/{chunk-EX74SI67.js → chunk-LBE6GIBC.js} +36 -9
  22. package/dist/chunk-LBE6GIBC.js.map +1 -0
  23. package/dist/{chunk-NY6TZLST.cjs → chunk-URJH4H6G.cjs} +505 -49
  24. package/dist/chunk-URJH4H6G.cjs.map +1 -0
  25. package/dist/{chunk-FEABEF3J.cjs → chunk-VQDFS6VS.cjs} +374 -280
  26. package/dist/chunk-VQDFS6VS.cjs.map +1 -0
  27. package/dist/components/index.cjs +55 -55
  28. package/dist/components/index.d.cts +2 -2
  29. package/dist/components/index.d.ts +2 -2
  30. package/dist/components/index.js +2 -2
  31. package/dist/dashboard/index.cjs +36 -36
  32. package/dist/dashboard/index.d.cts +3 -3
  33. package/dist/dashboard/index.d.ts +3 -3
  34. package/dist/dashboard/index.js +4 -4
  35. package/dist/export/index.d.cts +1 -1
  36. package/dist/export/index.d.ts +1 -1
  37. package/dist/{index-C-Qcuu4Y.d.cts → index-CvKj3SWO.d.cts} +2 -2
  38. package/dist/{index-rPc7ijt8.d.ts → index-DXGLs1yY.d.ts} +2 -2
  39. package/dist/index.cjs +127 -127
  40. package/dist/index.cjs.map +1 -1
  41. package/dist/index.d.cts +30 -9
  42. package/dist/index.d.ts +30 -9
  43. package/dist/index.js +6 -6
  44. package/dist/index.js.map +1 -1
  45. package/dist/{types-WrCbOeAV.d.cts → types-j0kPJ9Hz.d.cts} +16 -1
  46. package/dist/{types-WrCbOeAV.d.ts → types-j0kPJ9Hz.d.ts} +16 -1
  47. package/dist/utils/index.cjs +15 -15
  48. package/dist/utils/index.d.cts +5 -21
  49. package/dist/utils/index.d.ts +5 -21
  50. package/dist/utils/index.js +1 -1
  51. package/package.json +2 -2
  52. package/dist/chunk-4AVL6GQK.cjs.map +0 -1
  53. package/dist/chunk-EX74SI67.js.map +0 -1
  54. package/dist/chunk-FEABEF3J.cjs.map +0 -1
  55. package/dist/chunk-MOAEEF5P.js.map +0 -1
  56. package/dist/chunk-NY6TZLST.cjs.map +0 -1
  57. package/dist/chunk-UPYINBZU.js.map +0 -1
@@ -1,7 +1,7 @@
1
- import { ScatterChart, PieChart, AreaChart, LineChart, BarChart, MetricCard } from './chunk-2H5WTH4K.js';
2
- import { Dropdown, DropdownItem, Icon, DropdownSeparator, Button, Skeleton, EmptyState, Dialog, Input, Checkbox, DialogFooter, useAnalytics, useCrossFilterOptional, useDebouncedLayoutSave, AutoSaveIndicator, ResultsTable, CrossFilterProvider, QueryBuilder, SavedQueryPicker, useSchema, Select, CollapsibleSection, ColorPaletteSelector, FilterBuilder, TableSelector, JoinBuilder, TimeSeriesConfig, CalculatedFieldBuilder } from './chunk-UPYINBZU.js';
1
+ import { ScatterChart, PieChart, AreaChart, LineChart, BarChart, MetricCard } from './chunk-FQ23KG6G.js';
2
+ import { Dropdown, DropdownItem, Icon, DropdownSeparator, Button, Skeleton, EmptyState, Dialog, Input, Checkbox, DialogFooter, useAnalytics, useCrossFilterOptional, useDebouncedLayoutSave, AutoSaveIndicator, ResultsTable, CrossFilterProvider, Tooltip, QueryBuilder, SavedQueryPicker, useSchema, Select, CollapsibleSection, ColorPaletteSelector, FilterBuilder, TableSelector, JoinBuilder, TimeSeriesConfig, CalculatedFieldBuilder } from './chunk-ET7GCREP.js';
3
3
  import { useTheme } from './chunk-T6STUE7E.js';
4
- import { formatRelativeTime, createDateFormatters, pivotQueryResult, parseMarkdownSafe, parseColumnRef } from './chunk-EX74SI67.js';
4
+ import { formatRelativeTime, createDateFormatters, pivotQueryResult, parseMarkdownSafe, parseColumnRef } from './chunk-LBE6GIBC.js';
5
5
  import { createContext, forwardRef, useState, useEffect, useCallback, useRef, useMemo, useContext } from 'react';
6
6
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
7
7
  import { WidthProvider, Responsive } from 'react-grid-layout/legacy';
@@ -169,6 +169,7 @@ function DashboardProvider({
169
169
  const clientRef = useRef(client);
170
170
  clientRef.current = client;
171
171
  const loadedDashboardRef = useRef(null);
172
+ const abortControllerRef = useRef(null);
172
173
  const setDashboardData = useCallback((data) => {
173
174
  setDashboard(data);
174
175
  const defaults = data.filters.filter((f) => f.default_value !== void 0).map((f) => ({
@@ -187,7 +188,7 @@ function DashboardProvider({
187
188
  }
188
189
  }, []);
189
190
  const executeWidgetQuery = useCallback(
190
- async (widget, currentDashboard, currentFilters, currentCrossFilters, bypassCache = false) => {
191
+ async (widget, currentDashboard, currentFilters, currentCrossFilters, bypassCache = false, signal) => {
191
192
  if (!widget.query) {
192
193
  return;
193
194
  }
@@ -207,16 +208,21 @@ function DashboardProvider({
207
208
  currentFilters
208
209
  );
209
210
  query = applyCrossFiltersToQuery(query, currentCrossFilters, widget.id);
210
- const result = await client.executeQuery(query, bypassCache);
211
+ const result = await client.executeQuery(query, bypassCache, signal);
212
+ if (signal?.aborted) return;
211
213
  setWidgetResults((prev) => ({ ...prev, [widget.id]: result }));
212
214
  const refreshTime = result.cached_at ?? Date.now() / 1e3;
213
215
  setWidgetRefreshTimes((prev) => ({ ...prev, [widget.id]: refreshTime }));
214
216
  } catch (err) {
217
+ if (err instanceof Error && err.name === "AbortError") {
218
+ return;
219
+ }
215
220
  setWidgetErrors((prev) => ({
216
221
  ...prev,
217
222
  [widget.id]: err instanceof Error ? err : new Error("Query failed")
218
223
  }));
219
224
  } finally {
225
+ if (signal?.aborted) return;
220
226
  setWidgetLoading((prev) => ({ ...prev, [widget.id]: false }));
221
227
  if (bypassCache) {
222
228
  setRefreshingWidgets((prev) => {
@@ -230,9 +236,10 @@ function DashboardProvider({
230
236
  [client]
231
237
  );
232
238
  const executeWidgetsInBatches = useCallback(
233
- async (widgets, currentDashboard, currentFilters, currentCrossFilters, bypassCache = false, currentBatchSize = batchSize) => {
239
+ async (widgets, currentDashboard, currentFilters, currentCrossFilters, bypassCache = false, currentBatchSize = batchSize, signal) => {
234
240
  const widgetsWithQueries = widgets.filter((w) => w.query !== null);
235
241
  for (let i = 0; i < widgetsWithQueries.length; i += currentBatchSize) {
242
+ if (signal?.aborted) return;
236
243
  const batch = widgetsWithQueries.slice(i, i + currentBatchSize);
237
244
  await Promise.all(
238
245
  batch.map(
@@ -241,7 +248,8 @@ function DashboardProvider({
241
248
  currentDashboard,
242
249
  currentFilters,
243
250
  currentCrossFilters,
244
- bypassCache
251
+ bypassCache,
252
+ signal
245
253
  )
246
254
  )
247
255
  );
@@ -253,16 +261,19 @@ function DashboardProvider({
253
261
  async (currentDashboard, currentFilters, currentCrossFilters) => {
254
262
  const requestId = Math.random().toString(36).substring(2, 11);
255
263
  requestIdRef.current = requestId;
264
+ const signal = abortControllerRef.current?.signal;
256
265
  await executeWidgetsInBatches(
257
266
  currentDashboard.widgets,
258
267
  currentDashboard,
259
268
  currentFilters,
260
269
  currentCrossFilters,
261
- false
270
+ false,
262
271
  // Don't bypass cache on initial load
272
+ batchSize,
273
+ signal
263
274
  );
264
275
  },
265
- [executeWidgetsInBatches]
276
+ [executeWidgetsInBatches, batchSize]
266
277
  );
267
278
  const refreshDashboard = useCallback(async () => {
268
279
  if (!dashboard) return;
@@ -428,13 +439,16 @@ function DashboardProvider({
428
439
  (w) => visibleWidgets.has(w.id) && w.query !== null && !widgetResults[w.id] && !widgetLoading[w.id]
429
440
  );
430
441
  if (widgetsToLoad.length > 0) {
442
+ const signal = abortControllerRef.current?.signal;
431
443
  executeWidgetsInBatches(
432
444
  widgetsToLoad,
433
445
  dashboard,
434
446
  filterValues,
435
447
  crossFilters,
436
- false
448
+ false,
437
449
  // Don't bypass cache
450
+ batchSize,
451
+ signal
438
452
  );
439
453
  }
440
454
  }, [
@@ -446,7 +460,8 @@ function DashboardProvider({
446
460
  widgetLoading,
447
461
  filterValues,
448
462
  crossFilters,
449
- executeWidgetsInBatches
463
+ executeWidgetsInBatches,
464
+ batchSize
450
465
  ]);
451
466
  const filterValuesKey = JSON.stringify(filterValues);
452
467
  const prevFilterValuesRef = useRef(filterValuesKey);
@@ -459,12 +474,17 @@ function DashboardProvider({
459
474
  // Only re-execute if previously loaded
460
475
  );
461
476
  if (widgetsToRefresh.length > 0) {
477
+ abortControllerRef.current?.abort();
478
+ const controller = new AbortController();
479
+ abortControllerRef.current = controller;
462
480
  executeWidgetsInBatches(
463
481
  widgetsToRefresh,
464
482
  dashboard,
465
483
  filterValues,
466
484
  crossFilters,
467
- false
485
+ false,
486
+ batchSize,
487
+ controller.signal
468
488
  );
469
489
  }
470
490
  }, [
@@ -476,8 +496,16 @@ function DashboardProvider({
476
496
  widgetResults,
477
497
  filterValues,
478
498
  crossFilters,
479
- executeWidgetsInBatches
499
+ executeWidgetsInBatches,
500
+ batchSize
480
501
  ]);
502
+ useEffect(() => {
503
+ const controller = new AbortController();
504
+ abortControllerRef.current = controller;
505
+ return () => {
506
+ controller.abort();
507
+ };
508
+ }, [dashboardId]);
481
509
  const contextValue = useMemo(
482
510
  () => ({
483
511
  dashboard,
@@ -1450,6 +1478,9 @@ function WidgetContent({
1450
1478
  if (widget.type === "text") {
1451
1479
  return /* @__PURE__ */ jsx("div", { style: containerStyle, children: /* @__PURE__ */ jsx(TextContent, { config: widget.config }) });
1452
1480
  }
1481
+ if (!result && !error && widget.query) {
1482
+ return /* @__PURE__ */ jsx("div", { style: containerStyle, children: /* @__PURE__ */ jsx(LoadingState, {}) });
1483
+ }
1453
1484
  if (!result || result.row_count === 0) {
1454
1485
  return /* @__PURE__ */ jsx("div", { style: containerStyle, children: /* @__PURE__ */ jsx(EmptyState2, {}) });
1455
1486
  }
@@ -1586,10 +1617,22 @@ function WidgetContent({
1586
1617
  const dimensionColumns = result.columns.filter(
1587
1618
  (col) => col !== widget.config.pivot_column && col !== widget.config.value_column
1588
1619
  );
1620
+ let pivotColumnFormat;
1621
+ if (widget.config.dateFormats?.[widget.config.pivot_column]) {
1622
+ pivotColumnFormat = widget.config.dateFormats[widget.config.pivot_column];
1623
+ } else if (widget.query) {
1624
+ const pivotCol = widget.query.columns.find(
1625
+ (c) => c.alias === widget.config.pivot_column || c.column === widget.config.pivot_column
1626
+ );
1627
+ if (pivotCol?.date_format) {
1628
+ pivotColumnFormat = pivotCol.date_format;
1629
+ }
1630
+ }
1589
1631
  tableResult = pivotQueryResult(result, {
1590
1632
  pivotColumn: widget.config.pivot_column,
1591
1633
  valueColumn: widget.config.value_column,
1592
- dimensionColumns
1634
+ dimensionColumns,
1635
+ pivotColumnFormat
1593
1636
  });
1594
1637
  }
1595
1638
  const tableContainerStyle = {
@@ -2995,7 +3038,9 @@ function WidgetTypeSelector({
2995
3038
  borderRadius: theme.radius.sm,
2996
3039
  border: `2px solid ${isSelected ? theme.colors.primary : theme.colors.border}`,
2997
3040
  cursor: "pointer",
2998
- transition: "all 0.15s ease"
3041
+ transition: "all 0.15s ease",
3042
+ minWidth: 0
3043
+ // Allow grid item to shrink below content width
2999
3044
  });
3000
3045
  const iconStyle = (isSelected) => ({
3001
3046
  width: "28px",
@@ -3022,9 +3067,7 @@ function WidgetTypeSelector({
3022
3067
  const descStyle = {
3023
3068
  fontSize: theme.fontSizes.xs,
3024
3069
  color: theme.colors.textMuted,
3025
- whiteSpace: "nowrap",
3026
- overflow: "hidden",
3027
- textOverflow: "ellipsis"
3070
+ whiteSpace: "nowrap"
3028
3071
  };
3029
3072
  return /* @__PURE__ */ jsxs("div", { className: `prismiq-widget-type-selector ${className}`, style: containerStyle, children: [
3030
3073
  /* @__PURE__ */ jsx("label", { style: labelStyle, children: "Widget Type" }),
@@ -3191,19 +3234,26 @@ function MetricConfig({
3191
3234
  onChange
3192
3235
  }) {
3193
3236
  const { theme } = useTheme();
3237
+ const { getDisplayName } = useSchema();
3194
3238
  const initialTable = query?.tables[0]?.name ?? "";
3195
3239
  const initialAggregation = query?.columns[0]?.aggregation ?? "count";
3196
- const initialColumn = query?.columns[0]?.column ?? "*";
3240
+ const rawInitialColumn = query?.columns[0]?.column ?? "*";
3241
+ const needsColumnForInitial = AGGREGATIONS.find((a) => a.value === initialAggregation)?.needsColumn ?? false;
3242
+ const initialColumn = rawInitialColumn === "*" && needsColumnForInitial ? "" : rawInitialColumn;
3243
+ const initialFilters = (query?.filters ?? []).map((f) => ({
3244
+ ...f,
3245
+ table_id: "t1"
3246
+ }));
3197
3247
  const [selectedTable, setSelectedTable] = useState(initialTable);
3198
3248
  const [aggregation, setAggregation] = useState(initialAggregation);
3199
3249
  const [selectedColumn, setSelectedColumn] = useState(initialColumn);
3200
- const [filters, setFilters] = useState(query?.filters ?? []);
3250
+ const [filters, setFilters] = useState(initialFilters);
3201
3251
  const tableOptions = useMemo(() => {
3202
3252
  return schema.tables.map((t) => ({
3203
3253
  value: t.name,
3204
- label: t.name
3254
+ label: getDisplayName(t.name)
3205
3255
  }));
3206
- }, [schema.tables]);
3256
+ }, [schema.tables, getDisplayName]);
3207
3257
  const currentTable = useMemo(() => {
3208
3258
  return schema.tables.find((t) => t.name === selectedTable);
3209
3259
  }, [schema.tables, selectedTable]);
@@ -3218,8 +3268,12 @@ function MetricConfig({
3218
3268
  const needsColumn = useMemo(() => {
3219
3269
  return AGGREGATIONS.find((a) => a.value === aggregation)?.needsColumn ?? false;
3220
3270
  }, [aggregation]);
3271
+ const isValidAggregation = useMemo(() => {
3272
+ return AGGREGATIONS.some((a) => a.value === aggregation);
3273
+ }, [aggregation]);
3221
3274
  useEffect(() => {
3222
3275
  if (!selectedTable) return;
3276
+ if (!isValidAggregation) return;
3223
3277
  const tableId = "t1";
3224
3278
  const tables = [{ id: tableId, name: selectedTable }];
3225
3279
  const column = needsColumn ? selectedColumn : "*";
@@ -3237,7 +3291,7 @@ function MetricConfig({
3237
3291
  filters: filters.length > 0 ? filters : void 0
3238
3292
  };
3239
3293
  onChange(queryDef);
3240
- }, [selectedTable, aggregation, selectedColumn, needsColumn, filters, onChange]);
3294
+ }, [selectedTable, aggregation, selectedColumn, needsColumn, isValidAggregation, filters, onChange]);
3241
3295
  const handleTableChange = useCallback((value) => {
3242
3296
  setSelectedTable(value);
3243
3297
  setSelectedColumn("");
@@ -3362,17 +3416,23 @@ function ChartConfig({
3362
3416
  onChange
3363
3417
  }) {
3364
3418
  const { theme } = useTheme();
3419
+ const { getDisplayName } = useSchema();
3365
3420
  const initialTables = query?.tables ?? [];
3366
3421
  const groupByCol = query?.columns.find((c) => c.aggregation === "none");
3367
3422
  const initialGroupBy = groupByCol?.column ?? query?.group_by?.[0]?.column ?? "";
3368
3423
  const initialGroupByTableId = groupByCol?.table_id ?? query?.tables?.[0]?.id ?? "t1";
3369
3424
  const initialDateTrunc = groupByCol?.date_trunc ?? "";
3370
- const initialMeasures = query?.columns.filter((c) => c.aggregation !== "none").map((c) => ({
3371
- // Store as table-qualified ref so parseColumnRef can resolve it correctly
3372
- column: c.table_id ? `${c.table_id}.${c.column}` : c.column,
3373
- aggregation: c.aggregation,
3374
- table_id: c.table_id
3375
- })) ?? [];
3425
+ const initialMeasures = query?.columns.filter((c) => c.aggregation !== "none").map((c) => {
3426
+ const isStarWithNonCount = (c.column === "*" || c.column.endsWith(".*")) && c.aggregation !== "count";
3427
+ const column = isStarWithNonCount ? "" : c.table_id ? `${c.table_id}.${c.column}` : c.column;
3428
+ return {
3429
+ column,
3430
+ aggregation: c.aggregation,
3431
+ table_id: c.table_id,
3432
+ // Preserve original alias to maintain compatibility with widget config
3433
+ alias: c.alias
3434
+ };
3435
+ }) ?? [];
3376
3436
  const initialJoins = query?.joins ?? [];
3377
3437
  const [tables, setTables] = useState(initialTables);
3378
3438
  const [joins, setJoins] = useState(initialJoins);
@@ -3393,9 +3453,9 @@ function ChartConfig({
3393
3453
  const tableOptions = useMemo(() => {
3394
3454
  return schema.tables.map((t) => ({
3395
3455
  value: t.name,
3396
- label: t.name
3456
+ label: getDisplayName(t.name)
3397
3457
  }));
3398
- }, [schema.tables]);
3458
+ }, [schema.tables, getDisplayName]);
3399
3459
  const currentTable = useMemo(() => {
3400
3460
  return schema.tables.find((t) => t.name === selectedTable);
3401
3461
  }, [schema.tables, selectedTable]);
@@ -3465,7 +3525,7 @@ function ChartConfig({
3465
3525
  const parsedMeasures = [];
3466
3526
  const invalidColumns = [];
3467
3527
  validMeasures.forEach((m, i) => {
3468
- const measureAlias = validMeasures.length > 1 ? `value_${i + 1}` : "value";
3528
+ const measureAlias = m.alias ?? (validMeasures.length > 1 ? `value_${i + 1}` : "value");
3469
3529
  if (m.aggregation === "count" && (!m.column || m.column.endsWith(".*") || m.column === "*")) {
3470
3530
  parsedMeasures.push({
3471
3531
  table_id: m.table_id ?? tables[0]?.id ?? "t1",
@@ -3529,6 +3589,9 @@ function ChartConfig({
3529
3589
  setJoins(
3530
3590
  (prev) => prev.filter((j) => tableIds.has(j.from_table_id) && tableIds.has(j.to_table_id))
3531
3591
  );
3592
+ setFilters(
3593
+ (prev) => prev.filter((f) => tableIds.has(f.table_id))
3594
+ );
3532
3595
  if (!tableIds.has(groupByTableId)) {
3533
3596
  setGroupByColumn("");
3534
3597
  setGroupByTableId(newTables[0]?.id ?? "t1");
@@ -3783,25 +3846,31 @@ function PieConfig({
3783
3846
  onChange
3784
3847
  }) {
3785
3848
  const { theme } = useTheme();
3849
+ const { getDisplayName } = useSchema();
3786
3850
  const initialTable = query?.tables[0]?.name ?? "";
3787
3851
  const labelCol = query?.columns.find((c) => c.aggregation === "none");
3788
3852
  const initialLabel = labelCol?.column ?? "";
3789
3853
  const initialDateTrunc = labelCol?.date_trunc ?? "";
3790
3854
  const measureCol = query?.columns.find((c) => c.aggregation !== "none");
3791
- const initialValueColumn = measureCol?.column ?? "";
3855
+ const rawInitialValueColumn = measureCol?.column ?? "";
3792
3856
  const initialAggregation = measureCol?.aggregation ?? "sum";
3857
+ const initialValueColumn = rawInitialValueColumn === "*" && initialAggregation !== "count" ? "" : rawInitialValueColumn;
3858
+ const initialFilters = (query?.filters ?? []).map((f) => ({
3859
+ ...f,
3860
+ table_id: "t1"
3861
+ }));
3793
3862
  const [selectedTable, setSelectedTable] = useState(initialTable);
3794
3863
  const [labelColumn, setLabelColumn] = useState(initialLabel);
3795
3864
  const [dateTrunc, setDateTrunc] = useState(initialDateTrunc);
3796
3865
  const [valueColumn, setValueColumn] = useState(initialValueColumn);
3797
3866
  const [aggregation, setAggregation] = useState(initialAggregation);
3798
- const [filters, setFilters] = useState(query?.filters ?? []);
3867
+ const [filters, setFilters] = useState(initialFilters);
3799
3868
  const tableOptions = useMemo(() => {
3800
3869
  return schema.tables.map((t) => ({
3801
3870
  value: t.name,
3802
- label: t.name
3871
+ label: getDisplayName(t.name)
3803
3872
  }));
3804
- }, [schema.tables]);
3873
+ }, [schema.tables, getDisplayName]);
3805
3874
  const currentTable = useMemo(() => {
3806
3875
  return schema.tables.find((t) => t.name === selectedTable);
3807
3876
  }, [schema.tables, selectedTable]);
@@ -3826,8 +3895,12 @@ function PieConfig({
3826
3895
  label: `${c.name} (${c.data_type})`
3827
3896
  }));
3828
3897
  }, [currentTable]);
3898
+ const isValidAggregation = useMemo(() => {
3899
+ return AGGREGATIONS3.some((a) => a.value === aggregation);
3900
+ }, [aggregation]);
3829
3901
  useEffect(() => {
3830
3902
  if (!selectedTable || !labelColumn) return;
3903
+ if (!isValidAggregation) return;
3831
3904
  const needsValueColumn = aggregation !== "count";
3832
3905
  if (needsValueColumn && !valueColumn) return;
3833
3906
  const tableId = "t1";
@@ -3857,7 +3930,7 @@ function PieConfig({
3857
3930
  order_by: [{ table_id: tableId, column: labelColumn, direction: "ASC" }]
3858
3931
  };
3859
3932
  onChange(queryDef);
3860
- }, [selectedTable, labelColumn, dateTrunc, valueColumn, aggregation, filters, onChange]);
3933
+ }, [selectedTable, labelColumn, dateTrunc, valueColumn, aggregation, isValidAggregation, filters, onChange]);
3861
3934
  const handleTableChange = useCallback((value) => {
3862
3935
  setSelectedTable(value);
3863
3936
  setLabelColumn("");
@@ -3978,18 +4051,23 @@ function TableConfig({
3978
4051
  onChange
3979
4052
  }) {
3980
4053
  const { theme } = useTheme();
4054
+ const { getDisplayName } = useSchema();
3981
4055
  const initialTable = query?.tables[0]?.name ?? "";
3982
4056
  const initialColumns = query?.columns.map((c) => c.column) ?? [];
4057
+ const initialFilters = (query?.filters ?? []).map((f) => ({
4058
+ ...f,
4059
+ table_id: "t1"
4060
+ }));
3983
4061
  const [selectedTable, setSelectedTable] = useState(initialTable);
3984
4062
  const [selectedColumns, setSelectedColumns] = useState(initialColumns);
3985
- const [filters, setFilters] = useState(query?.filters ?? []);
4063
+ const [filters, setFilters] = useState(initialFilters);
3986
4064
  const [limit, setLimit] = useState(query?.limit ?? 100);
3987
4065
  const tableOptions = useMemo(() => {
3988
4066
  return schema.tables.map((t) => ({
3989
4067
  value: t.name,
3990
- label: t.name
4068
+ label: getDisplayName(t.name)
3991
4069
  }));
3992
- }, [schema.tables]);
4070
+ }, [schema.tables, getDisplayName]);
3993
4071
  const currentTable = useMemo(() => {
3994
4072
  return schema.tables.find((t) => t.name === selectedTable);
3995
4073
  }, [schema.tables, selectedTable]);
@@ -5303,7 +5381,7 @@ function WidgetEditorPage({
5303
5381
  widget?.position ?? { x: 0, y: 0, w: 6, h: 4, minW: 2, minH: 2 }
5304
5382
  );
5305
5383
  const [dataSourceMode, setDataSourceMode] = useState(
5306
- isNew ? "guided" : widget?.dataSourceMode ?? "guided"
5384
+ isNew ? "guided" : widget?.config?.data_source_mode ?? "guided"
5307
5385
  );
5308
5386
  const [previewResult, setPreviewResult] = useState(null);
5309
5387
  const [previewLoading, setPreviewLoading] = useState(false);
@@ -5350,6 +5428,26 @@ function WidgetEditorPage({
5350
5428
  setPreviewError(null);
5351
5429
  }
5352
5430
  }, [query, refreshPreview]);
5431
+ const queryHasAdvancedFeatures = useCallback((q) => {
5432
+ if (!q) return false;
5433
+ if (q.tables && q.tables.length > 1 && q.joins && q.joins.length > 0) return true;
5434
+ if (q.calculated_fields && q.calculated_fields.length > 0) return true;
5435
+ if (q.group_by && q.group_by.length > 0) return true;
5436
+ if (q.order_by && q.order_by.length > 0) return true;
5437
+ if (q.limit != null) return true;
5438
+ if (q.offset != null) return true;
5439
+ if (q.time_series) return true;
5440
+ return false;
5441
+ }, []);
5442
+ const handleModeSwitch = useCallback((newMode) => {
5443
+ if (newMode === "guided" && (dataSourceMode === "advanced" || dataSourceMode === "saved") && queryHasAdvancedFeatures(query)) {
5444
+ const confirmed = window.confirm(
5445
+ "This query uses advanced features (e.g. joins, calculated fields, grouping, sorting, pagination, time series) that Guided mode may not fully represent. The query will be preserved but some settings may not be editable in Guided mode.\n\nSwitch to Guided mode?"
5446
+ );
5447
+ if (!confirmed) return;
5448
+ }
5449
+ setDataSourceMode(newMode);
5450
+ }, [dataSourceMode, query, queryHasAdvancedFeatures]);
5353
5451
  const handleSavedQuerySelect = useCallback((savedQuery) => {
5354
5452
  setQuery(savedQuery.query);
5355
5453
  }, []);
@@ -5366,11 +5464,10 @@ function WidgetEditorPage({
5366
5464
  id: widget?.id ?? generateId(),
5367
5465
  type,
5368
5466
  title,
5369
- config,
5467
+ config: { ...config, data_source_mode: dataSourceMode },
5370
5468
  query,
5371
5469
  position,
5372
- hyperlink,
5373
- dataSourceMode
5470
+ hyperlink
5374
5471
  };
5375
5472
  onSave(savedWidget);
5376
5473
  }, [widget, type, title, config, query, position, hyperlink, dataSourceMode, onSave]);
@@ -5396,7 +5493,8 @@ function WidgetEditorPage({
5396
5493
  justifyContent: "space-between",
5397
5494
  padding: `${theme.spacing.md} ${theme.spacing.lg}`,
5398
5495
  borderBottom: `1px solid ${theme.colors.border}`,
5399
- backgroundColor: theme.colors.surface
5496
+ backgroundColor: theme.colors.surface,
5497
+ flexShrink: 0
5400
5498
  };
5401
5499
  const headerLeftStyle = {
5402
5500
  display: "flex",
@@ -5427,38 +5525,28 @@ function WidgetEditorPage({
5427
5525
  };
5428
5526
  const bodyStyle = {
5429
5527
  flex: 1,
5430
- display: "flex",
5431
- minHeight: 0
5528
+ overflow: "auto",
5529
+ display: "grid",
5530
+ gridTemplateColumns: "360px 1fr"
5432
5531
  };
5433
5532
  const leftPanelStyle = {
5434
- width: "320px",
5435
- flexShrink: 0,
5436
5533
  borderRight: `1px solid ${theme.colors.border}`,
5437
- overflow: "auto",
5438
5534
  padding: theme.spacing.md,
5535
+ paddingBottom: theme.spacing.xl,
5439
5536
  display: "flex",
5440
5537
  flexDirection: "column",
5441
5538
  gap: theme.spacing.lg
5442
5539
  };
5443
5540
  const mainPanelStyle = {
5444
- flex: 1,
5445
- display: "flex",
5446
- flexDirection: "column",
5447
- minHeight: 0
5541
+ minWidth: 0
5448
5542
  };
5449
5543
  const previewPanelStyle = {
5450
- height: "300px",
5451
- flexShrink: 0,
5452
- padding: theme.spacing.md,
5453
- borderBottom: `1px solid ${theme.colors.border}`
5454
- };
5455
- const dataSourcePanelStyle = {
5456
- flex: "1 1 auto",
5457
- display: "flex",
5458
- flexDirection: "column",
5459
- minHeight: 0,
5544
+ height: "440px",
5545
+ padding: theme.spacing.lg,
5546
+ borderBottom: `1px solid ${theme.colors.border}`,
5460
5547
  overflow: "hidden"
5461
5548
  };
5549
+ const dataSourcePanelStyle = {};
5462
5550
  const dataSourceHeaderStyle = {
5463
5551
  display: "flex",
5464
5552
  alignItems: "center",
@@ -5478,15 +5566,12 @@ function WidgetEditorPage({
5478
5566
  transition: "all 0.15s ease"
5479
5567
  });
5480
5568
  const dataSourceContentStyle = {
5481
- flex: "1 1 auto",
5482
- overflow: "auto",
5483
5569
  padding: theme.spacing.md,
5484
- paddingBottom: "100px",
5485
- // Extra space to ensure filter buttons are visible
5486
- minHeight: "250px"
5570
+ paddingBottom: "200px"
5571
+ // Extra padding to create scroll room for filters
5487
5572
  };
5488
5573
  const sectionStyle = {
5489
- marginBottom: theme.spacing.md
5574
+ // Note: Parent has flex gap for spacing between sections
5490
5575
  };
5491
5576
  const sectionTitleStyle = {
5492
5577
  fontSize: theme.fontSizes.xs,
@@ -5880,30 +5965,39 @@ function WidgetEditorPage({
5880
5965
  /* @__PURE__ */ jsxs("div", { style: dataSourceHeaderStyle, children: [
5881
5966
  /* @__PURE__ */ jsx("span", { style: { fontSize: theme.fontSizes.sm, fontWeight: 500, marginRight: "auto" }, children: "Data Source" }),
5882
5967
  /* @__PURE__ */ jsx(
5883
- "button",
5884
- {
5885
- type: "button",
5886
- style: tabStyle(dataSourceMode === "guided"),
5887
- onClick: () => setDataSourceMode("guided"),
5888
- children: "Guided"
5889
- }
5890
- ),
5891
- /* @__PURE__ */ jsx(
5892
- "button",
5968
+ Tooltip,
5893
5969
  {
5894
- type: "button",
5895
- style: tabStyle(dataSourceMode === "advanced"),
5896
- onClick: () => setDataSourceMode("advanced"),
5897
- children: "Advanced"
5970
+ content: "Quick setup: pick columns from dropdowns to build your chart",
5971
+ position: "bottom",
5972
+ style: { whiteSpace: "normal" },
5973
+ children: /* @__PURE__ */ jsx(
5974
+ "button",
5975
+ {
5976
+ type: "button",
5977
+ "data-testid": "data-source-mode-guided",
5978
+ style: tabStyle(dataSourceMode === "guided"),
5979
+ onClick: () => handleModeSwitch("guided"),
5980
+ children: "Guided"
5981
+ }
5982
+ )
5898
5983
  }
5899
5984
  ),
5900
5985
  /* @__PURE__ */ jsx(
5901
- "button",
5986
+ Tooltip,
5902
5987
  {
5903
- type: "button",
5904
- style: tabStyle(dataSourceMode === "saved"),
5905
- onClick: () => setDataSourceMode("saved"),
5906
- children: "Saved Query"
5988
+ content: "Full control: combine tables, add filters, and create custom calculations",
5989
+ position: "bottom",
5990
+ style: { whiteSpace: "normal" },
5991
+ children: /* @__PURE__ */ jsx(
5992
+ "button",
5993
+ {
5994
+ type: "button",
5995
+ "data-testid": "data-source-mode-advanced",
5996
+ style: tabStyle(dataSourceMode === "advanced"),
5997
+ onClick: () => handleModeSwitch("advanced"),
5998
+ children: "Advanced"
5999
+ }
6000
+ )
5907
6001
  }
5908
6002
  )
5909
6003
  ] }),
@@ -7506,5 +7600,5 @@ var DashboardDialog = forwardRef(
7506
7600
  );
7507
7601
 
7508
7602
  export { Dashboard, DashboardCard, DashboardContext, DashboardDialog, DashboardEditor, DashboardLayout, DashboardList, DashboardProvider, DateRangeFilter, EditableDashboardLayout, EditorToolbar, FilterBar, LazyWidget, MultiSelectFilter, SelectFilter, TextFilter, Widget, WidgetContainer, WidgetContent, WidgetEditor, WidgetEditorPage, WidgetHeader, WidgetPalette, WidgetPlaceholder, WidgetPreview, WidgetTypeSelector, useAutoRefresh, useDashboard, useDashboardFilters, useFullscreen, useWidget, useWidgetVisibility };
7509
- //# sourceMappingURL=chunk-MOAEEF5P.js.map
7510
- //# sourceMappingURL=chunk-MOAEEF5P.js.map
7603
+ //# sourceMappingURL=chunk-3LDRRDJ6.js.map
7604
+ //# sourceMappingURL=chunk-3LDRRDJ6.js.map