@prismiq/react 0.1.0 → 0.2.0

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 (65) hide show
  1. package/dist/{CustomSQLEditor-DYeId0Gp.d.ts → ChatBubble-ARocmvZD.d.cts} +48 -4
  2. package/dist/{CustomSQLEditor-BXB4rf1q.d.cts → ChatBubble-BN_CjIpk.d.ts} +48 -4
  3. package/dist/{DashboardDialog-B3vYC5Gs.d.ts → DashboardDialog-UhUGXx2h.d.ts} +6 -4
  4. package/dist/{DashboardDialog-LHmrtNQU.d.cts → DashboardDialog-Z-HypxmG.d.cts} +6 -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-NK7HKX2J.cjs → chunk-73TPDGXB.cjs} +7 -7
  12. package/dist/{chunk-NK7HKX2J.cjs.map → chunk-73TPDGXB.cjs.map} +1 -1
  13. package/dist/{chunk-FEABEF3J.cjs → chunk-FKXCINUF.cjs} +551 -299
  14. package/dist/chunk-FKXCINUF.cjs.map +1 -0
  15. package/dist/{chunk-2H5WTH4K.js → chunk-FQ23KG6G.js} +3 -3
  16. package/dist/{chunk-2H5WTH4K.js.map → chunk-FQ23KG6G.js.map} +1 -1
  17. package/dist/{chunk-UPYINBZU.js → chunk-GELI7MDZ.js} +982 -51
  18. package/dist/chunk-GELI7MDZ.js.map +1 -0
  19. package/dist/{chunk-WWTT2OJ5.js → chunk-HKZFEXT6.js} +27 -9
  20. package/dist/chunk-HKZFEXT6.js.map +1 -0
  21. package/dist/{chunk-MOAEEF5P.js → chunk-JBJ5LEAG.js} +362 -110
  22. package/dist/chunk-JBJ5LEAG.js.map +1 -0
  23. package/dist/{chunk-4AVL6GQK.cjs → chunk-KXB2IZI2.cjs} +36 -9
  24. package/dist/chunk-KXB2IZI2.cjs.map +1 -0
  25. package/dist/{chunk-EX74SI67.js → chunk-LBE6GIBC.js} +36 -9
  26. package/dist/chunk-LBE6GIBC.js.map +1 -0
  27. package/dist/{chunk-NY6TZLST.cjs → chunk-PG7QBH3G.cjs} +988 -53
  28. package/dist/chunk-PG7QBH3G.cjs.map +1 -0
  29. package/dist/{chunk-MDXGGZSW.cjs → chunk-ZYVN6XAZ.cjs} +35 -37
  30. package/dist/chunk-ZYVN6XAZ.cjs.map +1 -0
  31. package/dist/components/index.cjs +63 -55
  32. package/dist/components/index.d.cts +2 -2
  33. package/dist/components/index.d.ts +2 -2
  34. package/dist/components/index.js +2 -2
  35. package/dist/dashboard/index.cjs +36 -36
  36. package/dist/dashboard/index.d.cts +7 -5
  37. package/dist/dashboard/index.d.ts +7 -5
  38. package/dist/dashboard/index.js +4 -4
  39. package/dist/export/index.cjs +7 -7
  40. package/dist/export/index.d.cts +6 -4
  41. package/dist/export/index.d.ts +6 -4
  42. package/dist/export/index.js +1 -1
  43. package/dist/{index-C-Qcuu4Y.d.cts → index-B8DelfpL.d.cts} +2 -2
  44. package/dist/{index-rPc7ijt8.d.ts → index-RbfYPQD_.d.ts} +2 -2
  45. package/dist/index.cjs +150 -134
  46. package/dist/index.cjs.map +1 -1
  47. package/dist/index.d.cts +97 -9
  48. package/dist/index.d.ts +97 -9
  49. package/dist/index.js +7 -7
  50. package/dist/index.js.map +1 -1
  51. package/dist/{types-WrCbOeAV.d.cts → types-ccB9Ps3k.d.cts} +59 -1
  52. package/dist/{types-WrCbOeAV.d.ts → types-ccB9Ps3k.d.ts} +59 -1
  53. package/dist/utils/index.cjs +15 -15
  54. package/dist/utils/index.d.cts +5 -21
  55. package/dist/utils/index.d.ts +5 -21
  56. package/dist/utils/index.js +1 -1
  57. package/package.json +3 -7
  58. package/dist/chunk-4AVL6GQK.cjs.map +0 -1
  59. package/dist/chunk-EX74SI67.js.map +0 -1
  60. package/dist/chunk-FEABEF3J.cjs.map +0 -1
  61. package/dist/chunk-MDXGGZSW.cjs.map +0 -1
  62. package/dist/chunk-MOAEEF5P.js.map +0 -1
  63. package/dist/chunk-NY6TZLST.cjs.map +0 -1
  64. package/dist/chunk-UPYINBZU.js.map +0 -1
  65. package/dist/chunk-WWTT2OJ5.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, useLLMStatus, Tooltip, QueryBuilder, SavedQueryPicker, SchemaExplorer, CustomSQLEditor, ChatPanel, useSchema, Select, CollapsibleSection, ColorPaletteSelector, FilterBuilder, TableSelector, JoinBuilder, TimeSeriesConfig, CalculatedFieldBuilder } from './chunk-GELI7MDZ.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) => ({
@@ -179,7 +180,8 @@ function DashboardProvider({
179
180
  if (!lazyLoadingEnabledRef.current) {
180
181
  const initialLoadingState = {};
181
182
  data.widgets.forEach((widget) => {
182
- if (widget.query) {
183
+ const isSqlMode = widget.config?.data_source_mode === "sql" && widget.config?.raw_sql;
184
+ if (widget.query || isSqlMode) {
183
185
  initialLoadingState[widget.id] = true;
184
186
  }
185
187
  });
@@ -187,8 +189,9 @@ function DashboardProvider({
187
189
  }
188
190
  }, []);
189
191
  const executeWidgetQuery = useCallback(
190
- async (widget, currentDashboard, currentFilters, currentCrossFilters, bypassCache = false) => {
191
- if (!widget.query) {
192
+ async (widget, currentDashboard, currentFilters, currentCrossFilters, bypassCache = false, signal) => {
193
+ const isSqlMode = widget.config?.data_source_mode === "sql" && widget.config?.raw_sql;
194
+ if (!widget.query && !isSqlMode) {
192
195
  return;
193
196
  }
194
197
  setWidgetLoading((prev) => ({ ...prev, [widget.id]: true }));
@@ -201,22 +204,32 @@ function DashboardProvider({
201
204
  return next;
202
205
  });
203
206
  try {
204
- let query = applyFiltersToQuery(
205
- widget.query,
206
- currentDashboard,
207
- currentFilters
208
- );
209
- query = applyCrossFiltersToQuery(query, currentCrossFilters, widget.id);
210
- const result = await client.executeQuery(query, bypassCache);
207
+ let result;
208
+ if (isSqlMode) {
209
+ result = await client.executeSQL(widget.config.raw_sql);
210
+ } else {
211
+ let query = applyFiltersToQuery(
212
+ widget.query,
213
+ currentDashboard,
214
+ currentFilters
215
+ );
216
+ query = applyCrossFiltersToQuery(query, currentCrossFilters, widget.id);
217
+ result = await client.executeQuery(query, bypassCache, signal);
218
+ }
219
+ if (signal?.aborted) return;
211
220
  setWidgetResults((prev) => ({ ...prev, [widget.id]: result }));
212
221
  const refreshTime = result.cached_at ?? Date.now() / 1e3;
213
222
  setWidgetRefreshTimes((prev) => ({ ...prev, [widget.id]: refreshTime }));
214
223
  } catch (err) {
224
+ if (err instanceof Error && err.name === "AbortError") {
225
+ return;
226
+ }
215
227
  setWidgetErrors((prev) => ({
216
228
  ...prev,
217
229
  [widget.id]: err instanceof Error ? err : new Error("Query failed")
218
230
  }));
219
231
  } finally {
232
+ if (signal?.aborted) return;
220
233
  setWidgetLoading((prev) => ({ ...prev, [widget.id]: false }));
221
234
  if (bypassCache) {
222
235
  setRefreshingWidgets((prev) => {
@@ -230,9 +243,12 @@ function DashboardProvider({
230
243
  [client]
231
244
  );
232
245
  const executeWidgetsInBatches = useCallback(
233
- async (widgets, currentDashboard, currentFilters, currentCrossFilters, bypassCache = false, currentBatchSize = batchSize) => {
234
- const widgetsWithQueries = widgets.filter((w) => w.query !== null);
246
+ async (widgets, currentDashboard, currentFilters, currentCrossFilters, bypassCache = false, currentBatchSize = batchSize, signal) => {
247
+ const widgetsWithQueries = widgets.filter(
248
+ (w) => w.query !== null || w.config?.data_source_mode === "sql" && w.config?.raw_sql
249
+ );
235
250
  for (let i = 0; i < widgetsWithQueries.length; i += currentBatchSize) {
251
+ if (signal?.aborted) return;
236
252
  const batch = widgetsWithQueries.slice(i, i + currentBatchSize);
237
253
  await Promise.all(
238
254
  batch.map(
@@ -241,7 +257,8 @@ function DashboardProvider({
241
257
  currentDashboard,
242
258
  currentFilters,
243
259
  currentCrossFilters,
244
- bypassCache
260
+ bypassCache,
261
+ signal
245
262
  )
246
263
  )
247
264
  );
@@ -253,16 +270,19 @@ function DashboardProvider({
253
270
  async (currentDashboard, currentFilters, currentCrossFilters) => {
254
271
  const requestId = Math.random().toString(36).substring(2, 11);
255
272
  requestIdRef.current = requestId;
273
+ const signal = abortControllerRef.current?.signal;
256
274
  await executeWidgetsInBatches(
257
275
  currentDashboard.widgets,
258
276
  currentDashboard,
259
277
  currentFilters,
260
278
  currentCrossFilters,
261
- false
279
+ false,
262
280
  // Don't bypass cache on initial load
281
+ batchSize,
282
+ signal
263
283
  );
264
284
  },
265
- [executeWidgetsInBatches]
285
+ [executeWidgetsInBatches, batchSize]
266
286
  );
267
287
  const refreshDashboard = useCallback(async () => {
268
288
  if (!dashboard) return;
@@ -425,16 +445,19 @@ function DashboardProvider({
425
445
  useEffect(() => {
426
446
  if (!lazyLoadingEnabled || !dashboard || isLoading) return;
427
447
  const widgetsToLoad = dashboard.widgets.filter(
428
- (w) => visibleWidgets.has(w.id) && w.query !== null && !widgetResults[w.id] && !widgetLoading[w.id]
448
+ (w) => visibleWidgets.has(w.id) && (w.query !== null || w.config?.data_source_mode === "sql" && w.config?.raw_sql) && !widgetResults[w.id] && !widgetLoading[w.id]
429
449
  );
430
450
  if (widgetsToLoad.length > 0) {
451
+ const signal = abortControllerRef.current?.signal;
431
452
  executeWidgetsInBatches(
432
453
  widgetsToLoad,
433
454
  dashboard,
434
455
  filterValues,
435
456
  crossFilters,
436
- false
457
+ false,
437
458
  // Don't bypass cache
459
+ batchSize,
460
+ signal
438
461
  );
439
462
  }
440
463
  }, [
@@ -446,7 +469,8 @@ function DashboardProvider({
446
469
  widgetLoading,
447
470
  filterValues,
448
471
  crossFilters,
449
- executeWidgetsInBatches
472
+ executeWidgetsInBatches,
473
+ batchSize
450
474
  ]);
451
475
  const filterValuesKey = JSON.stringify(filterValues);
452
476
  const prevFilterValuesRef = useRef(filterValuesKey);
@@ -455,16 +479,21 @@ function DashboardProvider({
455
479
  if (prevFilterValuesRef.current === filterValuesKey) return;
456
480
  prevFilterValuesRef.current = filterValuesKey;
457
481
  const widgetsToRefresh = dashboard.widgets.filter(
458
- (w) => everVisibleWidgets.has(w.id) && w.query !== null && widgetResults[w.id]
482
+ (w) => everVisibleWidgets.has(w.id) && (w.query !== null || w.config?.data_source_mode === "sql" && w.config?.raw_sql) && widgetResults[w.id]
459
483
  // Only re-execute if previously loaded
460
484
  );
461
485
  if (widgetsToRefresh.length > 0) {
486
+ abortControllerRef.current?.abort();
487
+ const controller = new AbortController();
488
+ abortControllerRef.current = controller;
462
489
  executeWidgetsInBatches(
463
490
  widgetsToRefresh,
464
491
  dashboard,
465
492
  filterValues,
466
493
  crossFilters,
467
- false
494
+ false,
495
+ batchSize,
496
+ controller.signal
468
497
  );
469
498
  }
470
499
  }, [
@@ -476,8 +505,16 @@ function DashboardProvider({
476
505
  widgetResults,
477
506
  filterValues,
478
507
  crossFilters,
479
- executeWidgetsInBatches
508
+ executeWidgetsInBatches,
509
+ batchSize
480
510
  ]);
511
+ useEffect(() => {
512
+ const controller = new AbortController();
513
+ abortControllerRef.current = controller;
514
+ return () => {
515
+ controller.abort();
516
+ };
517
+ }, [dashboardId]);
481
518
  const contextValue = useMemo(
482
519
  () => ({
483
520
  dashboard,
@@ -1450,6 +1487,9 @@ function WidgetContent({
1450
1487
  if (widget.type === "text") {
1451
1488
  return /* @__PURE__ */ jsx("div", { style: containerStyle, children: /* @__PURE__ */ jsx(TextContent, { config: widget.config }) });
1452
1489
  }
1490
+ if (!result && !error && widget.query) {
1491
+ return /* @__PURE__ */ jsx("div", { style: containerStyle, children: /* @__PURE__ */ jsx(LoadingState, {}) });
1492
+ }
1453
1493
  if (!result || result.row_count === 0) {
1454
1494
  return /* @__PURE__ */ jsx("div", { style: containerStyle, children: /* @__PURE__ */ jsx(EmptyState2, {}) });
1455
1495
  }
@@ -1586,10 +1626,22 @@ function WidgetContent({
1586
1626
  const dimensionColumns = result.columns.filter(
1587
1627
  (col) => col !== widget.config.pivot_column && col !== widget.config.value_column
1588
1628
  );
1629
+ let pivotColumnFormat;
1630
+ if (widget.config.dateFormats?.[widget.config.pivot_column]) {
1631
+ pivotColumnFormat = widget.config.dateFormats[widget.config.pivot_column];
1632
+ } else if (widget.query) {
1633
+ const pivotCol = widget.query.columns.find(
1634
+ (c) => c.alias === widget.config.pivot_column || c.column === widget.config.pivot_column
1635
+ );
1636
+ if (pivotCol?.date_format) {
1637
+ pivotColumnFormat = pivotCol.date_format;
1638
+ }
1639
+ }
1589
1640
  tableResult = pivotQueryResult(result, {
1590
1641
  pivotColumn: widget.config.pivot_column,
1591
1642
  valueColumn: widget.config.value_column,
1592
- dimensionColumns
1643
+ dimensionColumns,
1644
+ pivotColumnFormat
1593
1645
  });
1594
1646
  }
1595
1647
  const tableContainerStyle = {
@@ -2995,7 +3047,9 @@ function WidgetTypeSelector({
2995
3047
  borderRadius: theme.radius.sm,
2996
3048
  border: `2px solid ${isSelected ? theme.colors.primary : theme.colors.border}`,
2997
3049
  cursor: "pointer",
2998
- transition: "all 0.15s ease"
3050
+ transition: "all 0.15s ease",
3051
+ minWidth: 0
3052
+ // Allow grid item to shrink below content width
2999
3053
  });
3000
3054
  const iconStyle = (isSelected) => ({
3001
3055
  width: "28px",
@@ -3022,9 +3076,7 @@ function WidgetTypeSelector({
3022
3076
  const descStyle = {
3023
3077
  fontSize: theme.fontSizes.xs,
3024
3078
  color: theme.colors.textMuted,
3025
- whiteSpace: "nowrap",
3026
- overflow: "hidden",
3027
- textOverflow: "ellipsis"
3079
+ whiteSpace: "nowrap"
3028
3080
  };
3029
3081
  return /* @__PURE__ */ jsxs("div", { className: `prismiq-widget-type-selector ${className}`, style: containerStyle, children: [
3030
3082
  /* @__PURE__ */ jsx("label", { style: labelStyle, children: "Widget Type" }),
@@ -3074,6 +3126,7 @@ function WidgetPreview({
3074
3126
  title,
3075
3127
  config,
3076
3128
  query,
3129
+ rawSql,
3077
3130
  result,
3078
3131
  isLoading = false,
3079
3132
  error,
@@ -3128,13 +3181,14 @@ function WidgetPreview({
3128
3181
  };
3129
3182
  const previewWidget = createPreviewWidget(type, title, config, query);
3130
3183
  const needsQuery = type !== "text";
3131
- const showEmptyState = needsQuery && !query && !isLoading;
3132
- return /* @__PURE__ */ jsxs("div", { className: `prismiq-widget-preview ${className}`, style: containerStyle, children: [
3184
+ const hasDataSource = !!query || !!rawSql;
3185
+ const showEmptyState = needsQuery && !hasDataSource && !isLoading;
3186
+ return /* @__PURE__ */ jsxs("div", { className: `prismiq-widget-preview ${className}`, style: containerStyle, "data-testid": "widget-preview", children: [
3133
3187
  /* @__PURE__ */ jsxs("div", { style: headerStyle, children: [
3134
3188
  /* @__PURE__ */ jsx("h3", { style: titleStyle, children: title || "Widget Preview" }),
3135
3189
  /* @__PURE__ */ jsx("span", { style: labelStyle, children: "Preview" })
3136
3190
  ] }),
3137
- /* @__PURE__ */ jsx("div", { style: contentStyle, children: showEmptyState ? /* @__PURE__ */ jsxs("div", { style: emptyStateStyle, children: [
3191
+ /* @__PURE__ */ jsx("div", { style: contentStyle, children: showEmptyState ? /* @__PURE__ */ jsxs("div", { style: emptyStateStyle, "data-testid": "widget-preview-empty", children: [
3138
3192
  /* @__PURE__ */ jsx(
3139
3193
  "div",
3140
3194
  {
@@ -3191,19 +3245,26 @@ function MetricConfig({
3191
3245
  onChange
3192
3246
  }) {
3193
3247
  const { theme } = useTheme();
3248
+ const { getDisplayName } = useSchema();
3194
3249
  const initialTable = query?.tables[0]?.name ?? "";
3195
3250
  const initialAggregation = query?.columns[0]?.aggregation ?? "count";
3196
- const initialColumn = query?.columns[0]?.column ?? "*";
3251
+ const rawInitialColumn = query?.columns[0]?.column ?? "*";
3252
+ const needsColumnForInitial = AGGREGATIONS.find((a) => a.value === initialAggregation)?.needsColumn ?? false;
3253
+ const initialColumn = rawInitialColumn === "*" && needsColumnForInitial ? "" : rawInitialColumn;
3254
+ const initialFilters = (query?.filters ?? []).map((f) => ({
3255
+ ...f,
3256
+ table_id: "t1"
3257
+ }));
3197
3258
  const [selectedTable, setSelectedTable] = useState(initialTable);
3198
3259
  const [aggregation, setAggregation] = useState(initialAggregation);
3199
3260
  const [selectedColumn, setSelectedColumn] = useState(initialColumn);
3200
- const [filters, setFilters] = useState(query?.filters ?? []);
3261
+ const [filters, setFilters] = useState(initialFilters);
3201
3262
  const tableOptions = useMemo(() => {
3202
3263
  return schema.tables.map((t) => ({
3203
3264
  value: t.name,
3204
- label: t.name
3265
+ label: getDisplayName(t.name)
3205
3266
  }));
3206
- }, [schema.tables]);
3267
+ }, [schema.tables, getDisplayName]);
3207
3268
  const currentTable = useMemo(() => {
3208
3269
  return schema.tables.find((t) => t.name === selectedTable);
3209
3270
  }, [schema.tables, selectedTable]);
@@ -3218,8 +3279,12 @@ function MetricConfig({
3218
3279
  const needsColumn = useMemo(() => {
3219
3280
  return AGGREGATIONS.find((a) => a.value === aggregation)?.needsColumn ?? false;
3220
3281
  }, [aggregation]);
3282
+ const isValidAggregation = useMemo(() => {
3283
+ return AGGREGATIONS.some((a) => a.value === aggregation);
3284
+ }, [aggregation]);
3221
3285
  useEffect(() => {
3222
3286
  if (!selectedTable) return;
3287
+ if (!isValidAggregation) return;
3223
3288
  const tableId = "t1";
3224
3289
  const tables = [{ id: tableId, name: selectedTable }];
3225
3290
  const column = needsColumn ? selectedColumn : "*";
@@ -3237,7 +3302,7 @@ function MetricConfig({
3237
3302
  filters: filters.length > 0 ? filters : void 0
3238
3303
  };
3239
3304
  onChange(queryDef);
3240
- }, [selectedTable, aggregation, selectedColumn, needsColumn, filters, onChange]);
3305
+ }, [selectedTable, aggregation, selectedColumn, needsColumn, isValidAggregation, filters, onChange]);
3241
3306
  const handleTableChange = useCallback((value) => {
3242
3307
  setSelectedTable(value);
3243
3308
  setSelectedColumn("");
@@ -3362,17 +3427,23 @@ function ChartConfig({
3362
3427
  onChange
3363
3428
  }) {
3364
3429
  const { theme } = useTheme();
3430
+ const { getDisplayName } = useSchema();
3365
3431
  const initialTables = query?.tables ?? [];
3366
3432
  const groupByCol = query?.columns.find((c) => c.aggregation === "none");
3367
3433
  const initialGroupBy = groupByCol?.column ?? query?.group_by?.[0]?.column ?? "";
3368
3434
  const initialGroupByTableId = groupByCol?.table_id ?? query?.tables?.[0]?.id ?? "t1";
3369
3435
  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
- })) ?? [];
3436
+ const initialMeasures = query?.columns.filter((c) => c.aggregation !== "none").map((c) => {
3437
+ const isStarWithNonCount = (c.column === "*" || c.column.endsWith(".*")) && c.aggregation !== "count";
3438
+ const column = isStarWithNonCount ? "" : c.table_id ? `${c.table_id}.${c.column}` : c.column;
3439
+ return {
3440
+ column,
3441
+ aggregation: c.aggregation,
3442
+ table_id: c.table_id,
3443
+ // Preserve original alias to maintain compatibility with widget config
3444
+ alias: c.alias
3445
+ };
3446
+ }) ?? [];
3376
3447
  const initialJoins = query?.joins ?? [];
3377
3448
  const [tables, setTables] = useState(initialTables);
3378
3449
  const [joins, setJoins] = useState(initialJoins);
@@ -3393,9 +3464,9 @@ function ChartConfig({
3393
3464
  const tableOptions = useMemo(() => {
3394
3465
  return schema.tables.map((t) => ({
3395
3466
  value: t.name,
3396
- label: t.name
3467
+ label: getDisplayName(t.name)
3397
3468
  }));
3398
- }, [schema.tables]);
3469
+ }, [schema.tables, getDisplayName]);
3399
3470
  const currentTable = useMemo(() => {
3400
3471
  return schema.tables.find((t) => t.name === selectedTable);
3401
3472
  }, [schema.tables, selectedTable]);
@@ -3465,7 +3536,7 @@ function ChartConfig({
3465
3536
  const parsedMeasures = [];
3466
3537
  const invalidColumns = [];
3467
3538
  validMeasures.forEach((m, i) => {
3468
- const measureAlias = validMeasures.length > 1 ? `value_${i + 1}` : "value";
3539
+ const measureAlias = m.alias ?? (validMeasures.length > 1 ? `value_${i + 1}` : "value");
3469
3540
  if (m.aggregation === "count" && (!m.column || m.column.endsWith(".*") || m.column === "*")) {
3470
3541
  parsedMeasures.push({
3471
3542
  table_id: m.table_id ?? tables[0]?.id ?? "t1",
@@ -3529,6 +3600,9 @@ function ChartConfig({
3529
3600
  setJoins(
3530
3601
  (prev) => prev.filter((j) => tableIds.has(j.from_table_id) && tableIds.has(j.to_table_id))
3531
3602
  );
3603
+ setFilters(
3604
+ (prev) => prev.filter((f) => tableIds.has(f.table_id))
3605
+ );
3532
3606
  if (!tableIds.has(groupByTableId)) {
3533
3607
  setGroupByColumn("");
3534
3608
  setGroupByTableId(newTables[0]?.id ?? "t1");
@@ -3783,25 +3857,31 @@ function PieConfig({
3783
3857
  onChange
3784
3858
  }) {
3785
3859
  const { theme } = useTheme();
3860
+ const { getDisplayName } = useSchema();
3786
3861
  const initialTable = query?.tables[0]?.name ?? "";
3787
3862
  const labelCol = query?.columns.find((c) => c.aggregation === "none");
3788
3863
  const initialLabel = labelCol?.column ?? "";
3789
3864
  const initialDateTrunc = labelCol?.date_trunc ?? "";
3790
3865
  const measureCol = query?.columns.find((c) => c.aggregation !== "none");
3791
- const initialValueColumn = measureCol?.column ?? "";
3866
+ const rawInitialValueColumn = measureCol?.column ?? "";
3792
3867
  const initialAggregation = measureCol?.aggregation ?? "sum";
3868
+ const initialValueColumn = rawInitialValueColumn === "*" && initialAggregation !== "count" ? "" : rawInitialValueColumn;
3869
+ const initialFilters = (query?.filters ?? []).map((f) => ({
3870
+ ...f,
3871
+ table_id: "t1"
3872
+ }));
3793
3873
  const [selectedTable, setSelectedTable] = useState(initialTable);
3794
3874
  const [labelColumn, setLabelColumn] = useState(initialLabel);
3795
3875
  const [dateTrunc, setDateTrunc] = useState(initialDateTrunc);
3796
3876
  const [valueColumn, setValueColumn] = useState(initialValueColumn);
3797
3877
  const [aggregation, setAggregation] = useState(initialAggregation);
3798
- const [filters, setFilters] = useState(query?.filters ?? []);
3878
+ const [filters, setFilters] = useState(initialFilters);
3799
3879
  const tableOptions = useMemo(() => {
3800
3880
  return schema.tables.map((t) => ({
3801
3881
  value: t.name,
3802
- label: t.name
3882
+ label: getDisplayName(t.name)
3803
3883
  }));
3804
- }, [schema.tables]);
3884
+ }, [schema.tables, getDisplayName]);
3805
3885
  const currentTable = useMemo(() => {
3806
3886
  return schema.tables.find((t) => t.name === selectedTable);
3807
3887
  }, [schema.tables, selectedTable]);
@@ -3826,8 +3906,12 @@ function PieConfig({
3826
3906
  label: `${c.name} (${c.data_type})`
3827
3907
  }));
3828
3908
  }, [currentTable]);
3909
+ const isValidAggregation = useMemo(() => {
3910
+ return AGGREGATIONS3.some((a) => a.value === aggregation);
3911
+ }, [aggregation]);
3829
3912
  useEffect(() => {
3830
3913
  if (!selectedTable || !labelColumn) return;
3914
+ if (!isValidAggregation) return;
3831
3915
  const needsValueColumn = aggregation !== "count";
3832
3916
  if (needsValueColumn && !valueColumn) return;
3833
3917
  const tableId = "t1";
@@ -3857,7 +3941,7 @@ function PieConfig({
3857
3941
  order_by: [{ table_id: tableId, column: labelColumn, direction: "ASC" }]
3858
3942
  };
3859
3943
  onChange(queryDef);
3860
- }, [selectedTable, labelColumn, dateTrunc, valueColumn, aggregation, filters, onChange]);
3944
+ }, [selectedTable, labelColumn, dateTrunc, valueColumn, aggregation, isValidAggregation, filters, onChange]);
3861
3945
  const handleTableChange = useCallback((value) => {
3862
3946
  setSelectedTable(value);
3863
3947
  setLabelColumn("");
@@ -3978,18 +4062,23 @@ function TableConfig({
3978
4062
  onChange
3979
4063
  }) {
3980
4064
  const { theme } = useTheme();
4065
+ const { getDisplayName } = useSchema();
3981
4066
  const initialTable = query?.tables[0]?.name ?? "";
3982
4067
  const initialColumns = query?.columns.map((c) => c.column) ?? [];
4068
+ const initialFilters = (query?.filters ?? []).map((f) => ({
4069
+ ...f,
4070
+ table_id: "t1"
4071
+ }));
3983
4072
  const [selectedTable, setSelectedTable] = useState(initialTable);
3984
4073
  const [selectedColumns, setSelectedColumns] = useState(initialColumns);
3985
- const [filters, setFilters] = useState(query?.filters ?? []);
4074
+ const [filters, setFilters] = useState(initialFilters);
3986
4075
  const [limit, setLimit] = useState(query?.limit ?? 100);
3987
4076
  const tableOptions = useMemo(() => {
3988
4077
  return schema.tables.map((t) => ({
3989
4078
  value: t.name,
3990
- label: t.name
4079
+ label: getDisplayName(t.name)
3991
4080
  }));
3992
- }, [schema.tables]);
4081
+ }, [schema.tables, getDisplayName]);
3993
4082
  const currentTable = useMemo(() => {
3994
4083
  return schema.tables.find((t) => t.name === selectedTable);
3995
4084
  }, [schema.tables, selectedTable]);
@@ -5291,6 +5380,7 @@ function WidgetEditorPage({
5291
5380
  }) {
5292
5381
  const { theme } = useTheme();
5293
5382
  const { client } = useAnalytics();
5383
+ const { enabled: llmEnabled, isLoading: llmStatusLoading } = useLLMStatus();
5294
5384
  const isNew = widget === null;
5295
5385
  const [type, setType] = useState(widget?.type ?? "bar_chart");
5296
5386
  const [title, setTitle] = useState(widget?.title ?? "New Widget");
@@ -5302,8 +5392,10 @@ function WidgetEditorPage({
5302
5392
  const [position, setPosition] = useState(
5303
5393
  widget?.position ?? { x: 0, y: 0, w: 6, h: 4, minW: 2, minH: 2 }
5304
5394
  );
5395
+ const [rawSql, setRawSql] = useState(widget?.config?.raw_sql ?? "");
5396
+ const [schemaOpen, setSchemaOpen] = useState(true);
5305
5397
  const [dataSourceMode, setDataSourceMode] = useState(
5306
- isNew ? "guided" : widget?.dataSourceMode ?? "guided"
5398
+ isNew ? "guided" : widget?.config?.data_source_mode ?? "guided"
5307
5399
  );
5308
5400
  const [previewResult, setPreviewResult] = useState(null);
5309
5401
  const [previewLoading, setPreviewLoading] = useState(false);
@@ -5326,6 +5418,24 @@ function WidgetEditorPage({
5326
5418
  []
5327
5419
  );
5328
5420
  const refreshPreview = useCallback(async () => {
5421
+ if (dataSourceMode === "sql") {
5422
+ if (!rawSql.trim() || !client) {
5423
+ setPreviewResult(null);
5424
+ return;
5425
+ }
5426
+ setPreviewLoading(true);
5427
+ setPreviewError(null);
5428
+ try {
5429
+ const result = await client.executeSQL(rawSql);
5430
+ setPreviewResult(result);
5431
+ } catch (err) {
5432
+ setPreviewError(err instanceof Error ? err : new Error("SQL execution failed"));
5433
+ setPreviewResult(null);
5434
+ } finally {
5435
+ setPreviewLoading(false);
5436
+ }
5437
+ return;
5438
+ }
5329
5439
  if (!query || !client) {
5330
5440
  setPreviewResult(null);
5331
5441
  return;
@@ -5341,15 +5451,38 @@ function WidgetEditorPage({
5341
5451
  } finally {
5342
5452
  setPreviewLoading(false);
5343
5453
  }
5344
- }, [query, client]);
5454
+ }, [query, rawSql, dataSourceMode, client]);
5345
5455
  useEffect(() => {
5456
+ if (dataSourceMode === "sql") {
5457
+ return;
5458
+ }
5346
5459
  if (query) {
5347
5460
  void refreshPreview();
5348
5461
  } else {
5349
5462
  setPreviewResult(null);
5350
5463
  setPreviewError(null);
5351
5464
  }
5352
- }, [query, refreshPreview]);
5465
+ }, [query, dataSourceMode, refreshPreview]);
5466
+ const queryHasAdvancedFeatures = useCallback((q) => {
5467
+ if (!q) return false;
5468
+ if (q.tables && q.tables.length > 1 && q.joins && q.joins.length > 0) return true;
5469
+ if (q.calculated_fields && q.calculated_fields.length > 0) return true;
5470
+ if (q.group_by && q.group_by.length > 0) return true;
5471
+ if (q.order_by && q.order_by.length > 0) return true;
5472
+ if (q.limit != null) return true;
5473
+ if (q.offset != null) return true;
5474
+ if (q.time_series) return true;
5475
+ return false;
5476
+ }, []);
5477
+ const handleModeSwitch = useCallback((newMode) => {
5478
+ if (newMode === "guided" && (dataSourceMode === "advanced" || dataSourceMode === "saved") && queryHasAdvancedFeatures(query)) {
5479
+ const confirmed = window.confirm(
5480
+ "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?"
5481
+ );
5482
+ if (!confirmed) return;
5483
+ }
5484
+ setDataSourceMode(newMode);
5485
+ }, [dataSourceMode, query, queryHasAdvancedFeatures]);
5353
5486
  const handleSavedQuerySelect = useCallback((savedQuery) => {
5354
5487
  setQuery(savedQuery.query);
5355
5488
  }, []);
@@ -5361,19 +5494,25 @@ function WidgetEditorPage({
5361
5494
  setPreviewResult(null);
5362
5495
  setPreviewError(null);
5363
5496
  }, []);
5497
+ const handleApplySql = useCallback((sql) => {
5498
+ setRawSql(sql);
5499
+ }, []);
5364
5500
  const handleSave = useCallback(() => {
5501
+ const savedConfig = { ...config, data_source_mode: dataSourceMode };
5502
+ if (dataSourceMode === "sql") {
5503
+ savedConfig.raw_sql = rawSql.trim() || void 0;
5504
+ }
5365
5505
  const savedWidget = {
5366
5506
  id: widget?.id ?? generateId(),
5367
5507
  type,
5368
5508
  title,
5369
- config,
5370
- query,
5509
+ config: savedConfig,
5510
+ query: dataSourceMode === "sql" ? null : query,
5371
5511
  position,
5372
- hyperlink,
5373
- dataSourceMode
5512
+ hyperlink
5374
5513
  };
5375
5514
  onSave(savedWidget);
5376
- }, [widget, type, title, config, query, position, hyperlink, dataSourceMode, onSave]);
5515
+ }, [widget, type, title, config, query, rawSql, position, hyperlink, dataSourceMode, onSave]);
5377
5516
  const columnSelectOptions = useMemo(() => {
5378
5517
  if (!schema) return [];
5379
5518
  return schema.tables.flatMap(
@@ -5396,7 +5535,8 @@ function WidgetEditorPage({
5396
5535
  justifyContent: "space-between",
5397
5536
  padding: `${theme.spacing.md} ${theme.spacing.lg}`,
5398
5537
  borderBottom: `1px solid ${theme.colors.border}`,
5399
- backgroundColor: theme.colors.surface
5538
+ backgroundColor: theme.colors.surface,
5539
+ flexShrink: 0
5400
5540
  };
5401
5541
  const headerLeftStyle = {
5402
5542
  display: "flex",
@@ -5427,38 +5567,28 @@ function WidgetEditorPage({
5427
5567
  };
5428
5568
  const bodyStyle = {
5429
5569
  flex: 1,
5430
- display: "flex",
5431
- minHeight: 0
5570
+ overflow: "auto",
5571
+ display: "grid",
5572
+ gridTemplateColumns: "360px 1fr"
5432
5573
  };
5433
5574
  const leftPanelStyle = {
5434
- width: "320px",
5435
- flexShrink: 0,
5436
5575
  borderRight: `1px solid ${theme.colors.border}`,
5437
- overflow: "auto",
5438
5576
  padding: theme.spacing.md,
5577
+ paddingBottom: theme.spacing.xl,
5439
5578
  display: "flex",
5440
5579
  flexDirection: "column",
5441
5580
  gap: theme.spacing.lg
5442
5581
  };
5443
5582
  const mainPanelStyle = {
5444
- flex: 1,
5445
- display: "flex",
5446
- flexDirection: "column",
5447
- minHeight: 0
5583
+ minWidth: 0
5448
5584
  };
5449
5585
  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,
5586
+ height: "440px",
5587
+ padding: theme.spacing.lg,
5588
+ borderBottom: `1px solid ${theme.colors.border}`,
5460
5589
  overflow: "hidden"
5461
5590
  };
5591
+ const dataSourcePanelStyle = {};
5462
5592
  const dataSourceHeaderStyle = {
5463
5593
  display: "flex",
5464
5594
  alignItems: "center",
@@ -5478,15 +5608,12 @@ function WidgetEditorPage({
5478
5608
  transition: "all 0.15s ease"
5479
5609
  });
5480
5610
  const dataSourceContentStyle = {
5481
- flex: "1 1 auto",
5482
- overflow: "auto",
5483
5611
  padding: theme.spacing.md,
5484
- paddingBottom: "100px",
5485
- // Extra space to ensure filter buttons are visible
5486
- minHeight: "250px"
5612
+ paddingBottom: "200px"
5613
+ // Extra padding to create scroll room for filters
5487
5614
  };
5488
5615
  const sectionStyle = {
5489
- marginBottom: theme.spacing.md
5616
+ // Note: Parent has flex gap for spacing between sections
5490
5617
  };
5491
5618
  const sectionTitleStyle = {
5492
5619
  fontSize: theme.fontSizes.xs,
@@ -5871,6 +5998,7 @@ function WidgetEditorPage({
5871
5998
  title,
5872
5999
  config,
5873
6000
  query,
6001
+ rawSql: dataSourceMode === "sql" ? rawSql : void 0,
5874
6002
  result: previewResult,
5875
6003
  isLoading: previewLoading,
5876
6004
  error: previewError
@@ -5880,30 +6008,57 @@ function WidgetEditorPage({
5880
6008
  /* @__PURE__ */ jsxs("div", { style: dataSourceHeaderStyle, children: [
5881
6009
  /* @__PURE__ */ jsx("span", { style: { fontSize: theme.fontSizes.sm, fontWeight: 500, marginRight: "auto" }, children: "Data Source" }),
5882
6010
  /* @__PURE__ */ jsx(
5883
- "button",
6011
+ Tooltip,
5884
6012
  {
5885
- type: "button",
5886
- style: tabStyle(dataSourceMode === "guided"),
5887
- onClick: () => setDataSourceMode("guided"),
5888
- children: "Guided"
6013
+ content: "Quick setup: pick columns from dropdowns to build your chart",
6014
+ position: "bottom",
6015
+ style: { whiteSpace: "normal" },
6016
+ children: /* @__PURE__ */ jsx(
6017
+ "button",
6018
+ {
6019
+ type: "button",
6020
+ "data-testid": "data-source-mode-guided",
6021
+ style: tabStyle(dataSourceMode === "guided"),
6022
+ onClick: () => handleModeSwitch("guided"),
6023
+ children: "Guided"
6024
+ }
6025
+ )
5889
6026
  }
5890
6027
  ),
5891
6028
  /* @__PURE__ */ jsx(
5892
- "button",
6029
+ Tooltip,
5893
6030
  {
5894
- type: "button",
5895
- style: tabStyle(dataSourceMode === "advanced"),
5896
- onClick: () => setDataSourceMode("advanced"),
5897
- children: "Advanced"
6031
+ content: "Full control: combine tables, add filters, and create custom calculations",
6032
+ position: "bottom",
6033
+ style: { whiteSpace: "normal" },
6034
+ children: /* @__PURE__ */ jsx(
6035
+ "button",
6036
+ {
6037
+ type: "button",
6038
+ "data-testid": "data-source-mode-advanced",
6039
+ style: tabStyle(dataSourceMode === "advanced"),
6040
+ onClick: () => handleModeSwitch("advanced"),
6041
+ children: "Advanced"
6042
+ }
6043
+ )
5898
6044
  }
5899
6045
  ),
5900
6046
  /* @__PURE__ */ jsx(
5901
- "button",
6047
+ Tooltip,
5902
6048
  {
5903
- type: "button",
5904
- style: tabStyle(dataSourceMode === "saved"),
5905
- onClick: () => setDataSourceMode("saved"),
5906
- children: "Saved Query"
6049
+ content: "Write raw SQL queries directly, with optional AI assistance",
6050
+ position: "bottom",
6051
+ style: { whiteSpace: "normal" },
6052
+ children: /* @__PURE__ */ jsx(
6053
+ "button",
6054
+ {
6055
+ type: "button",
6056
+ "data-testid": "data-source-mode-sql",
6057
+ style: tabStyle(dataSourceMode === "sql"),
6058
+ onClick: () => handleModeSwitch("sql"),
6059
+ children: "SQL"
6060
+ }
6061
+ )
5907
6062
  }
5908
6063
  )
5909
6064
  ] }),
@@ -5936,7 +6091,100 @@ function WidgetEditorPage({
5936
6091
  onSelect: handleSavedQuerySelect,
5937
6092
  showSave: false
5938
6093
  }
5939
- )
6094
+ ),
6095
+ dataSourceMode === "sql" && /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 0, height: "100%", minHeight: "400px" }, children: [
6096
+ /* @__PURE__ */ jsx("div", { "data-testid": "schema-panel", style: {
6097
+ width: schemaOpen ? "220px" : "36px",
6098
+ flexShrink: 0,
6099
+ transition: "width 0.2s ease",
6100
+ borderRight: `1px solid ${theme.colors.border}`,
6101
+ display: "flex",
6102
+ flexDirection: "column",
6103
+ overflow: "hidden"
6104
+ }, children: schemaOpen ? /* @__PURE__ */ jsx(
6105
+ SchemaExplorer,
6106
+ {
6107
+ searchable: true,
6108
+ collapsible: true,
6109
+ onColumnSelect: (table, col) => {
6110
+ const ref = `"${table.name}"."${col.name}"`;
6111
+ setRawSql((prev) => prev ? `${prev} ${ref}` : ref);
6112
+ },
6113
+ headerAction: /* @__PURE__ */ jsx(
6114
+ "button",
6115
+ {
6116
+ type: "button",
6117
+ onClick: () => setSchemaOpen(false),
6118
+ title: "Collapse schema panel",
6119
+ "data-testid": "schema-toggle-open",
6120
+ style: {
6121
+ display: "flex",
6122
+ alignItems: "center",
6123
+ justifyContent: "center",
6124
+ width: "22px",
6125
+ height: "22px",
6126
+ backgroundColor: "transparent",
6127
+ border: `1px solid ${theme.colors.border}`,
6128
+ borderRadius: theme.radius.sm,
6129
+ cursor: "pointer",
6130
+ color: theme.colors.textMuted,
6131
+ flexShrink: 0
6132
+ },
6133
+ children: /* @__PURE__ */ jsx(Icon, { name: "chevron-left", size: 12 })
6134
+ }
6135
+ ),
6136
+ style: { flex: 1, border: "none", borderRadius: 0 }
6137
+ }
6138
+ ) : /* @__PURE__ */ jsxs(
6139
+ "button",
6140
+ {
6141
+ type: "button",
6142
+ onClick: () => setSchemaOpen(true),
6143
+ title: "Show schema browser",
6144
+ "data-testid": "schema-toggle-collapsed",
6145
+ style: {
6146
+ display: "flex",
6147
+ flexDirection: "column",
6148
+ alignItems: "center",
6149
+ gap: "6px",
6150
+ paddingTop: theme.spacing.sm,
6151
+ width: "100%",
6152
+ height: "100%",
6153
+ backgroundColor: theme.colors.surface,
6154
+ border: "none",
6155
+ cursor: "pointer",
6156
+ color: theme.colors.textMuted,
6157
+ fontFamily: theme.fonts.sans
6158
+ },
6159
+ children: [
6160
+ /* @__PURE__ */ jsx(Icon, { name: "table", size: 16 }),
6161
+ /* @__PURE__ */ jsx("span", { style: {
6162
+ writingMode: "vertical-rl",
6163
+ fontSize: "11px",
6164
+ fontWeight: 500,
6165
+ letterSpacing: "0.04em"
6166
+ }, children: "Schema" })
6167
+ ]
6168
+ }
6169
+ ) }),
6170
+ /* @__PURE__ */ jsx("div", { style: { flex: 1, minWidth: 0 }, "data-testid": "sql-editor", children: /* @__PURE__ */ jsx(
6171
+ CustomSQLEditor,
6172
+ {
6173
+ initialSql: rawSql,
6174
+ onSqlChange: setRawSql,
6175
+ onExecute: () => void refreshPreview(),
6176
+ showResults: false,
6177
+ placeholder: "Write your SQL query here..."
6178
+ }
6179
+ ) }),
6180
+ !llmStatusLoading && llmEnabled && /* @__PURE__ */ jsx("div", { style: { width: "340px", flexShrink: 0 }, "data-testid": "chat-panel", children: /* @__PURE__ */ jsx(
6181
+ ChatPanel,
6182
+ {
6183
+ currentSql: rawSql || null,
6184
+ onApplySql: handleApplySql
6185
+ }
6186
+ ) })
6187
+ ] })
5940
6188
  ] })
5941
6189
  ] })
5942
6190
  ] })
@@ -6018,7 +6266,9 @@ function DashboardEditor({
6018
6266
  clientRef.current = client;
6019
6267
  const loadedDashboardRef = useRef(null);
6020
6268
  const executeWidgetQueries = useCallback(async (widgets, currentClient) => {
6021
- const widgetsWithQueries = widgets.filter((w) => w.query);
6269
+ const widgetsWithQueries = widgets.filter(
6270
+ (w) => w.query || w.config?.data_source_mode === "sql" && w.config?.raw_sql
6271
+ );
6022
6272
  if (widgetsWithQueries.length === 0) return;
6023
6273
  for (let i = 0; i < widgetsWithQueries.length; i += batchSize) {
6024
6274
  const batch = widgetsWithQueries.slice(i, i + batchSize);
@@ -6032,7 +6282,8 @@ function DashboardEditor({
6032
6282
  await Promise.all(
6033
6283
  batch.map(async (widget) => {
6034
6284
  try {
6035
- const result = await currentClient.executeQuery(widget.query);
6285
+ const isSqlMode = widget.config?.data_source_mode === "sql" && widget.config?.raw_sql;
6286
+ const result = isSqlMode ? await currentClient.executeSQL(widget.config.raw_sql) : await currentClient.executeQuery(widget.query);
6036
6287
  setWidgetResults((prev) => ({ ...prev, [widget.id]: result }));
6037
6288
  setWidgetRefreshTimes((prev) => ({ ...prev, [widget.id]: Math.floor(Date.now() / 1e3) }));
6038
6289
  } catch (err) {
@@ -6109,11 +6360,12 @@ function DashboardEditor({
6109
6360
  const refreshWidget = useCallback(
6110
6361
  async (widgetId, widgetOverride) => {
6111
6362
  const widget = widgetOverride ?? dashboard.widgets.find((w) => w.id === widgetId);
6112
- if (!widget?.query || !client) return;
6363
+ const isSqlMode = widget?.config?.data_source_mode === "sql" && widget?.config?.raw_sql;
6364
+ if (!widget?.query && !isSqlMode || !client) return;
6113
6365
  setWidgetLoading((prev) => ({ ...prev, [widgetId]: true }));
6114
6366
  setRefreshingWidgets((prev) => new Set(prev).add(widgetId));
6115
6367
  try {
6116
- const result = await client.executeQuery(widget.query, true);
6368
+ const result = isSqlMode ? await client.executeSQL(widget.config.raw_sql) : await client.executeQuery(widget.query, true);
6117
6369
  setWidgetResults((prev) => ({ ...prev, [widgetId]: result }));
6118
6370
  setWidgetRefreshTimes((prev) => ({ ...prev, [widgetId]: Math.floor(Date.now() / 1e3) }));
6119
6371
  setWidgetErrors((prev) => {
@@ -7506,5 +7758,5 @@ var DashboardDialog = forwardRef(
7506
7758
  );
7507
7759
 
7508
7760
  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
7761
+ //# sourceMappingURL=chunk-JBJ5LEAG.js.map
7762
+ //# sourceMappingURL=chunk-JBJ5LEAG.js.map