@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.
- package/dist/{CustomSQLEditor-DYeId0Gp.d.ts → ChatBubble-ARocmvZD.d.cts} +48 -4
- package/dist/{CustomSQLEditor-BXB4rf1q.d.cts → ChatBubble-BN_CjIpk.d.ts} +48 -4
- package/dist/{DashboardDialog-B3vYC5Gs.d.ts → DashboardDialog-UhUGXx2h.d.ts} +6 -4
- package/dist/{DashboardDialog-LHmrtNQU.d.cts → DashboardDialog-Z-HypxmG.d.cts} +6 -4
- package/dist/{accessibility-2yy5yqRR.d.cts → accessibility-Bu2mNtaB.d.cts} +1 -1
- package/dist/{accessibility-2yy5yqRR.d.ts → accessibility-Bu2mNtaB.d.ts} +1 -1
- package/dist/charts/index.cjs +27 -27
- package/dist/charts/index.d.cts +2 -2
- package/dist/charts/index.d.ts +2 -2
- package/dist/charts/index.js +2 -2
- package/dist/{chunk-NK7HKX2J.cjs → chunk-73TPDGXB.cjs} +7 -7
- package/dist/{chunk-NK7HKX2J.cjs.map → chunk-73TPDGXB.cjs.map} +1 -1
- package/dist/{chunk-FEABEF3J.cjs → chunk-FKXCINUF.cjs} +551 -299
- package/dist/chunk-FKXCINUF.cjs.map +1 -0
- package/dist/{chunk-2H5WTH4K.js → chunk-FQ23KG6G.js} +3 -3
- package/dist/{chunk-2H5WTH4K.js.map → chunk-FQ23KG6G.js.map} +1 -1
- package/dist/{chunk-UPYINBZU.js → chunk-GELI7MDZ.js} +982 -51
- package/dist/chunk-GELI7MDZ.js.map +1 -0
- package/dist/{chunk-WWTT2OJ5.js → chunk-HKZFEXT6.js} +27 -9
- package/dist/chunk-HKZFEXT6.js.map +1 -0
- package/dist/{chunk-MOAEEF5P.js → chunk-JBJ5LEAG.js} +362 -110
- package/dist/chunk-JBJ5LEAG.js.map +1 -0
- package/dist/{chunk-4AVL6GQK.cjs → chunk-KXB2IZI2.cjs} +36 -9
- package/dist/chunk-KXB2IZI2.cjs.map +1 -0
- package/dist/{chunk-EX74SI67.js → chunk-LBE6GIBC.js} +36 -9
- package/dist/chunk-LBE6GIBC.js.map +1 -0
- package/dist/{chunk-NY6TZLST.cjs → chunk-PG7QBH3G.cjs} +988 -53
- package/dist/chunk-PG7QBH3G.cjs.map +1 -0
- package/dist/{chunk-MDXGGZSW.cjs → chunk-ZYVN6XAZ.cjs} +35 -37
- package/dist/chunk-ZYVN6XAZ.cjs.map +1 -0
- package/dist/components/index.cjs +63 -55
- package/dist/components/index.d.cts +2 -2
- package/dist/components/index.d.ts +2 -2
- package/dist/components/index.js +2 -2
- package/dist/dashboard/index.cjs +36 -36
- package/dist/dashboard/index.d.cts +7 -5
- package/dist/dashboard/index.d.ts +7 -5
- package/dist/dashboard/index.js +4 -4
- package/dist/export/index.cjs +7 -7
- package/dist/export/index.d.cts +6 -4
- package/dist/export/index.d.ts +6 -4
- package/dist/export/index.js +1 -1
- package/dist/{index-C-Qcuu4Y.d.cts → index-B8DelfpL.d.cts} +2 -2
- package/dist/{index-rPc7ijt8.d.ts → index-RbfYPQD_.d.ts} +2 -2
- package/dist/index.cjs +150 -134
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +97 -9
- package/dist/index.d.ts +97 -9
- package/dist/index.js +7 -7
- package/dist/index.js.map +1 -1
- package/dist/{types-WrCbOeAV.d.cts → types-ccB9Ps3k.d.cts} +59 -1
- package/dist/{types-WrCbOeAV.d.ts → types-ccB9Ps3k.d.ts} +59 -1
- package/dist/utils/index.cjs +15 -15
- package/dist/utils/index.d.cts +5 -21
- package/dist/utils/index.d.ts +5 -21
- package/dist/utils/index.js +1 -1
- package/package.json +3 -7
- package/dist/chunk-4AVL6GQK.cjs.map +0 -1
- package/dist/chunk-EX74SI67.js.map +0 -1
- package/dist/chunk-FEABEF3J.cjs.map +0 -1
- package/dist/chunk-MDXGGZSW.cjs.map +0 -1
- package/dist/chunk-MOAEEF5P.js.map +0 -1
- package/dist/chunk-NY6TZLST.cjs.map +0 -1
- package/dist/chunk-UPYINBZU.js.map +0 -1
- package/dist/chunk-WWTT2OJ5.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { ScatterChart, PieChart, AreaChart, LineChart, BarChart, MetricCard } from './chunk-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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(
|
|
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
|
|
3132
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
3372
|
-
column: c.table_id ? `${c.table_id}.${c.column}` : c.column
|
|
3373
|
-
|
|
3374
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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?.
|
|
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
|
-
|
|
5431
|
-
|
|
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
|
-
|
|
5445
|
-
display: "flex",
|
|
5446
|
-
flexDirection: "column",
|
|
5447
|
-
minHeight: 0
|
|
5583
|
+
minWidth: 0
|
|
5448
5584
|
};
|
|
5449
5585
|
const previewPanelStyle = {
|
|
5450
|
-
height: "
|
|
5451
|
-
|
|
5452
|
-
|
|
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: "
|
|
5485
|
-
// Extra
|
|
5486
|
-
minHeight: "250px"
|
|
5612
|
+
paddingBottom: "200px"
|
|
5613
|
+
// Extra padding to create scroll room for filters
|
|
5487
5614
|
};
|
|
5488
5615
|
const sectionStyle = {
|
|
5489
|
-
|
|
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
|
-
|
|
6011
|
+
Tooltip,
|
|
5884
6012
|
{
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
children:
|
|
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
|
-
|
|
6029
|
+
Tooltip,
|
|
5893
6030
|
{
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
children:
|
|
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
|
-
|
|
6047
|
+
Tooltip,
|
|
5902
6048
|
{
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
children:
|
|
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(
|
|
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
|
|
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
|
-
|
|
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-
|
|
7510
|
-
//# sourceMappingURL=chunk-
|
|
7761
|
+
//# sourceMappingURL=chunk-JBJ5LEAG.js.map
|
|
7762
|
+
//# sourceMappingURL=chunk-JBJ5LEAG.js.map
|