@devtable/dashboard 0.3.1 → 1.1.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/api-caller/index.d.ts +9 -2
- package/dist/contexts/definition-context.d.ts +2 -1
- package/dist/contexts/panel-context.d.ts +2 -2
- package/dist/dashboard.es.js +1165 -462
- package/dist/dashboard.umd.js +16 -5
- package/dist/definition-editor/data-source-editor/context-and-snippets.d.ts +5 -0
- package/dist/definition-editor/data-source-editor/data-preview.d.ts +4 -0
- package/dist/definition-editor/data-source-editor/editor.d.ts +6 -0
- package/dist/definition-editor/data-source-editor/form.d.ts +8 -0
- package/dist/definition-editor/data-source-editor/index.d.ts +7 -0
- package/dist/definition-editor/data-source-editor/select-or-add-data-source.d.ts +7 -0
- package/dist/definition-editor/index.d.ts +2 -0
- package/dist/{panel/settings/context-info/index.d.ts → definition-editor/sql-snippet-editor/context-info.d.ts} +0 -0
- package/dist/definition-editor/sql-snippet-editor/editor.d.ts +5 -0
- package/dist/definition-editor/sql-snippet-editor/index.d.ts +7 -0
- package/dist/layout/index.d.ts +1 -2
- package/dist/layout/read-only.d.ts +16 -0
- package/dist/main/index.d.ts +2 -9
- package/dist/main/main.d.ts +11 -0
- package/dist/main/read-only.d.ts +10 -0
- package/dist/panel/index.d.ts +3 -3
- package/dist/panel/settings/{viz-config → panel-config}/description.d.ts +0 -0
- package/dist/panel/settings/panel-config/index.d.ts +5 -0
- package/dist/panel/settings/panel-config/preview-panel.d.ts +2 -0
- package/dist/panel/settings/{viz-config → panel-config}/title.d.ts +0 -0
- package/dist/panel/settings/pick-data-source/index.d.ts +5 -0
- package/dist/panel/settings/viz-config/preview-viz.d.ts +5 -0
- package/dist/style.css +1 -1
- package/dist/types/dashboard.d.ts +8 -1
- package/package.json +2 -2
- package/dist/panel/settings/query-editor/index.d.ts +0 -2
- package/dist/panel/settings/query-editor/sql-query-editor/index.d.ts +0 -5
- package/dist/panel/settings/query-result/index.d.ts +0 -5
- package/dist/panel/settings/sql-snippets/form.d.ts +0 -9
- package/dist/panel/settings/sql-snippets/index.d.ts +0 -5
package/dist/dashboard.es.js
CHANGED
|
@@ -32,11 +32,11 @@ var __objRest = (source, exclude) => {
|
|
|
32
32
|
import React from "react";
|
|
33
33
|
import _ from "lodash";
|
|
34
34
|
import { WidthProvider, Responsive } from "react-grid-layout";
|
|
35
|
-
import {
|
|
35
|
+
import { Textarea, ActionIcon, Container, Group, Tooltip, Text, TextInput, Box, LoadingOverlay, Table, Select, Button, useMantineTheme, ColorSwatch, Switch, Slider, JsonInput, Modal, AppShell, Tabs, Menu, Divider, SegmentedControl } from "@mantine/core";
|
|
36
36
|
import { useRequest } from "ahooks";
|
|
37
37
|
import axios from "axios";
|
|
38
|
-
import {
|
|
39
|
-
import { useElementSize, randomId
|
|
38
|
+
import { DeviceFloppy, InfoCircle, Trash, Refresh, Settings, Paint, PlayerPlay, PlaylistAdd, ClipboardText, Database, Recycle, Share } from "tabler-icons-react";
|
|
39
|
+
import { useInputState, useElementSize, randomId } from "@mantine/hooks";
|
|
40
40
|
import ReactEChartsCore from "echarts-for-react/lib/core";
|
|
41
41
|
import * as echarts from "echarts/core";
|
|
42
42
|
import { SunburstChart, BarChart, LineChart } from "echarts/charts";
|
|
@@ -44,9 +44,9 @@ import { CanvasRenderer } from "echarts/renderers";
|
|
|
44
44
|
import { GridComponent, LegendComponent, TooltipComponent, VisualMapComponent } from "echarts/components";
|
|
45
45
|
import numbro from "numbro";
|
|
46
46
|
import "echarts-gl";
|
|
47
|
+
import { useForm, Controller } from "react-hook-form";
|
|
48
|
+
import { formList, useForm as useForm$1 } from "@mantine/form";
|
|
47
49
|
import { Prism } from "@mantine/prism";
|
|
48
|
-
import { formList, useForm } from "@mantine/form";
|
|
49
|
-
import { useForm as useForm$1, Controller } from "react-hook-form";
|
|
50
50
|
var DashboardMode = /* @__PURE__ */ ((DashboardMode2) => {
|
|
51
51
|
DashboardMode2["Use"] = "use";
|
|
52
52
|
DashboardMode2["Edit"] = "edit";
|
|
@@ -87,7 +87,14 @@ const post = getRequest("POST");
|
|
|
87
87
|
function formatSQL(sql, params) {
|
|
88
88
|
const names = Object.keys(params);
|
|
89
89
|
const vals = Object.values(params);
|
|
90
|
-
|
|
90
|
+
try {
|
|
91
|
+
return new Function(...names, `return \`${sql}\`;`)(...vals);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (names.length === 0 && sql.includes("$")) {
|
|
94
|
+
throw new Error("[formatSQL] insufficient params");
|
|
95
|
+
}
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
91
98
|
}
|
|
92
99
|
function getSQLParams(context, definitions) {
|
|
93
100
|
const sqlSnippetRecord = definitions.sqlSnippets.reduce((ret, curr) => {
|
|
@@ -96,24 +103,26 @@ function getSQLParams(context, definitions) {
|
|
|
96
103
|
}, {});
|
|
97
104
|
return _.merge({}, sqlSnippetRecord, context);
|
|
98
105
|
}
|
|
99
|
-
const queryBySQL = (
|
|
100
|
-
if (!sql) {
|
|
106
|
+
const queryBySQL = ({ context, definitions, title, dataSource }) => async () => {
|
|
107
|
+
if (!dataSource || !dataSource.sql) {
|
|
101
108
|
return [];
|
|
102
109
|
}
|
|
110
|
+
const { type, key, sql } = dataSource;
|
|
103
111
|
const needParams = sql.includes("$");
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
112
|
+
try {
|
|
113
|
+
const params = getSQLParams(context, definitions);
|
|
114
|
+
const formattedSQL = formatSQL(sql, params);
|
|
115
|
+
if (needParams) {
|
|
116
|
+
console.groupCollapsed(`Final SQL for: ${title}`);
|
|
117
|
+
console.log(formattedSQL);
|
|
118
|
+
console.groupEnd();
|
|
119
|
+
}
|
|
120
|
+
const res = await post("/query", { type, key, sql: formattedSQL });
|
|
121
|
+
return res;
|
|
122
|
+
} catch (error) {
|
|
123
|
+
console.error(error);
|
|
107
124
|
return [];
|
|
108
125
|
}
|
|
109
|
-
const formattedSQL = formatSQL(sql, params);
|
|
110
|
-
if (needParams) {
|
|
111
|
-
console.groupCollapsed(`Final SQL for: ${title}`);
|
|
112
|
-
console.log(formattedSQL);
|
|
113
|
-
console.groupEnd();
|
|
114
|
-
}
|
|
115
|
-
const res = await post("/query", { sql: formattedSQL });
|
|
116
|
-
return res;
|
|
117
126
|
};
|
|
118
127
|
const initialContext$2 = {};
|
|
119
128
|
const initialContextInfoContext = initialContext$2;
|
|
@@ -127,8 +136,8 @@ const initialContext$1 = {
|
|
|
127
136
|
description: "",
|
|
128
137
|
setDescription: () => {
|
|
129
138
|
},
|
|
130
|
-
|
|
131
|
-
|
|
139
|
+
dataSourceID: "",
|
|
140
|
+
setDataSourceID: () => {
|
|
132
141
|
},
|
|
133
142
|
viz: {
|
|
134
143
|
type: "",
|
|
@@ -173,6 +182,48 @@ reactJsxRuntime_production_min.jsxs = q;
|
|
|
173
182
|
const jsx = jsxRuntime.exports.jsx;
|
|
174
183
|
const jsxs = jsxRuntime.exports.jsxs;
|
|
175
184
|
const Fragment = jsxRuntime.exports.Fragment;
|
|
185
|
+
function EditDescription() {
|
|
186
|
+
const {
|
|
187
|
+
description,
|
|
188
|
+
setDescription
|
|
189
|
+
} = React.useContext(PanelContext);
|
|
190
|
+
const [localDesc, setLocalDesc] = useInputState(description);
|
|
191
|
+
const changed = description !== localDesc;
|
|
192
|
+
const submit = React.useCallback(() => {
|
|
193
|
+
if (!changed) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
setDescription(localDesc);
|
|
197
|
+
}, [changed, localDesc]);
|
|
198
|
+
return /* @__PURE__ */ jsx(Textarea, {
|
|
199
|
+
label: "Panel Description",
|
|
200
|
+
value: localDesc,
|
|
201
|
+
onChange: setLocalDesc,
|
|
202
|
+
minRows: 10,
|
|
203
|
+
autosize: true,
|
|
204
|
+
maxRows: 30,
|
|
205
|
+
rightSection: /* @__PURE__ */ jsx(ActionIcon, {
|
|
206
|
+
disabled: !changed,
|
|
207
|
+
onClick: submit,
|
|
208
|
+
sx: {
|
|
209
|
+
alignSelf: "flex-start",
|
|
210
|
+
marginTop: "4px"
|
|
211
|
+
},
|
|
212
|
+
children: /* @__PURE__ */ jsx(DeviceFloppy, {
|
|
213
|
+
size: 20
|
|
214
|
+
})
|
|
215
|
+
})
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
const initialContext = {
|
|
219
|
+
sqlSnippets: [],
|
|
220
|
+
setSQLSnippets: () => {
|
|
221
|
+
},
|
|
222
|
+
dataSources: [],
|
|
223
|
+
setDataSources: () => {
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
const DefinitionContext = React.createContext(initialContext);
|
|
176
227
|
class ErrorBoundary extends React.Component {
|
|
177
228
|
constructor(props) {
|
|
178
229
|
super(props);
|
|
@@ -194,6 +245,229 @@ class ErrorBoundary extends React.Component {
|
|
|
194
245
|
return this.props.children;
|
|
195
246
|
}
|
|
196
247
|
}
|
|
248
|
+
function PreviewPanel() {
|
|
249
|
+
const {
|
|
250
|
+
title,
|
|
251
|
+
description
|
|
252
|
+
} = React.useContext(PanelContext);
|
|
253
|
+
return /* @__PURE__ */ jsx(ErrorBoundary, {
|
|
254
|
+
children: /* @__PURE__ */ jsx(Container, {
|
|
255
|
+
mt: "xl",
|
|
256
|
+
p: "5px",
|
|
257
|
+
sx: {
|
|
258
|
+
width: "600px",
|
|
259
|
+
height: "450px",
|
|
260
|
+
background: "transparent",
|
|
261
|
+
borderRadius: "5px",
|
|
262
|
+
boxShadow: "0px 0px 10px 0px rgba(0,0,0,.2)"
|
|
263
|
+
},
|
|
264
|
+
children: /* @__PURE__ */ jsxs(Group, {
|
|
265
|
+
position: "apart",
|
|
266
|
+
noWrap: true,
|
|
267
|
+
sx: {
|
|
268
|
+
borderBottom: "1px solid #eee",
|
|
269
|
+
paddingBottom: "5px"
|
|
270
|
+
},
|
|
271
|
+
children: [/* @__PURE__ */ jsx(Group, {
|
|
272
|
+
children: description && /* @__PURE__ */ jsx(Tooltip, {
|
|
273
|
+
label: description,
|
|
274
|
+
withArrow: true,
|
|
275
|
+
children: /* @__PURE__ */ jsx(InfoCircle, {
|
|
276
|
+
size: 12,
|
|
277
|
+
style: {
|
|
278
|
+
verticalAlign: "baseline",
|
|
279
|
+
cursor: "pointer"
|
|
280
|
+
}
|
|
281
|
+
})
|
|
282
|
+
})
|
|
283
|
+
}), /* @__PURE__ */ jsx(Group, {
|
|
284
|
+
grow: true,
|
|
285
|
+
position: "center",
|
|
286
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
287
|
+
lineClamp: 1,
|
|
288
|
+
weight: "bold",
|
|
289
|
+
children: title
|
|
290
|
+
})
|
|
291
|
+
}), /* @__PURE__ */ jsx(Group, {
|
|
292
|
+
position: "right",
|
|
293
|
+
spacing: 0,
|
|
294
|
+
sx: {
|
|
295
|
+
height: "28px"
|
|
296
|
+
}
|
|
297
|
+
})]
|
|
298
|
+
})
|
|
299
|
+
})
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
function EditTitle() {
|
|
303
|
+
const {
|
|
304
|
+
title,
|
|
305
|
+
setTitle
|
|
306
|
+
} = React.useContext(PanelContext);
|
|
307
|
+
const [localTitle, setLocalTitle] = useInputState(title);
|
|
308
|
+
const changed = title !== localTitle;
|
|
309
|
+
const submit = React.useCallback(() => {
|
|
310
|
+
if (!changed) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
setTitle(localTitle);
|
|
314
|
+
}, [changed, localTitle]);
|
|
315
|
+
return /* @__PURE__ */ jsx(TextInput, {
|
|
316
|
+
label: "Panel Title",
|
|
317
|
+
value: localTitle,
|
|
318
|
+
onChange: setLocalTitle,
|
|
319
|
+
rightSection: /* @__PURE__ */ jsx(ActionIcon, {
|
|
320
|
+
disabled: !changed,
|
|
321
|
+
onClick: submit,
|
|
322
|
+
children: /* @__PURE__ */ jsx(DeviceFloppy, {
|
|
323
|
+
size: 20
|
|
324
|
+
})
|
|
325
|
+
})
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
function PanelConfig({}) {
|
|
329
|
+
return /* @__PURE__ */ jsxs(Group, {
|
|
330
|
+
direction: "row",
|
|
331
|
+
grow: true,
|
|
332
|
+
noWrap: true,
|
|
333
|
+
align: "stretch",
|
|
334
|
+
sx: {
|
|
335
|
+
height: "100%"
|
|
336
|
+
},
|
|
337
|
+
children: [/* @__PURE__ */ jsxs(Group, {
|
|
338
|
+
grow: true,
|
|
339
|
+
direction: "column",
|
|
340
|
+
sx: {
|
|
341
|
+
width: "40%",
|
|
342
|
+
flexShrink: 0,
|
|
343
|
+
flexGrow: 0
|
|
344
|
+
},
|
|
345
|
+
children: [/* @__PURE__ */ jsx(EditTitle, {}), /* @__PURE__ */ jsx(EditDescription, {})]
|
|
346
|
+
}), /* @__PURE__ */ jsx(Box, {
|
|
347
|
+
sx: {
|
|
348
|
+
height: "100%",
|
|
349
|
+
flexGrow: 1,
|
|
350
|
+
maxWidth: "60%"
|
|
351
|
+
},
|
|
352
|
+
children: /* @__PURE__ */ jsx(PreviewPanel, {})
|
|
353
|
+
})]
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
function DataPreview({
|
|
357
|
+
id
|
|
358
|
+
}) {
|
|
359
|
+
const definitions = React.useContext(DefinitionContext);
|
|
360
|
+
const contextInfo = React.useContext(ContextInfoContext);
|
|
361
|
+
const dataSource = React.useMemo(() => {
|
|
362
|
+
return definitions.dataSources.find((d) => d.id === id);
|
|
363
|
+
}, [definitions.dataSources, id]);
|
|
364
|
+
const {
|
|
365
|
+
data = [],
|
|
366
|
+
loading
|
|
367
|
+
} = useRequest(queryBySQL({
|
|
368
|
+
context: contextInfo,
|
|
369
|
+
definitions,
|
|
370
|
+
title: id,
|
|
371
|
+
dataSource
|
|
372
|
+
}), {
|
|
373
|
+
refreshDeps: [contextInfo, definitions, dataSource]
|
|
374
|
+
});
|
|
375
|
+
if (loading) {
|
|
376
|
+
return /* @__PURE__ */ jsx(LoadingOverlay, {
|
|
377
|
+
visible: loading
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
if (data.length === 0) {
|
|
381
|
+
return /* @__PURE__ */ jsx(Table, {});
|
|
382
|
+
}
|
|
383
|
+
return /* @__PURE__ */ jsxs(Group, {
|
|
384
|
+
my: "xl",
|
|
385
|
+
direction: "column",
|
|
386
|
+
grow: true,
|
|
387
|
+
sx: {
|
|
388
|
+
border: "1px solid #eee"
|
|
389
|
+
},
|
|
390
|
+
children: [/* @__PURE__ */ jsx(Group, {
|
|
391
|
+
position: "left",
|
|
392
|
+
py: "md",
|
|
393
|
+
pl: "md",
|
|
394
|
+
sx: {
|
|
395
|
+
borderBottom: "1px solid #eee",
|
|
396
|
+
background: "#efefef"
|
|
397
|
+
},
|
|
398
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
399
|
+
weight: 500,
|
|
400
|
+
children: "Preview Data"
|
|
401
|
+
})
|
|
402
|
+
}), /* @__PURE__ */ jsxs(Table, {
|
|
403
|
+
children: [/* @__PURE__ */ jsx("thead", {
|
|
404
|
+
children: /* @__PURE__ */ jsx("tr", {
|
|
405
|
+
children: Object.keys(data == null ? void 0 : data[0]).map((label) => /* @__PURE__ */ jsx("th", {
|
|
406
|
+
children: label
|
|
407
|
+
}, label))
|
|
408
|
+
})
|
|
409
|
+
}), /* @__PURE__ */ jsx("tbody", {
|
|
410
|
+
children: data.map((row, index2) => /* @__PURE__ */ jsx("tr", {
|
|
411
|
+
children: Object.values(row).map((v, i) => /* @__PURE__ */ jsx("td", {
|
|
412
|
+
children: /* @__PURE__ */ jsx(Group, {
|
|
413
|
+
sx: {
|
|
414
|
+
"&, .mantine-Text-root": {
|
|
415
|
+
fontFamily: "monospace"
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
419
|
+
children: v
|
|
420
|
+
})
|
|
421
|
+
})
|
|
422
|
+
}, `${v}--${i}`))
|
|
423
|
+
}, `row-${index2}`))
|
|
424
|
+
})]
|
|
425
|
+
})]
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
function PickDataSource({}) {
|
|
429
|
+
const {
|
|
430
|
+
dataSources
|
|
431
|
+
} = React.useContext(DefinitionContext);
|
|
432
|
+
const {
|
|
433
|
+
dataSourceID,
|
|
434
|
+
setDataSourceID,
|
|
435
|
+
data,
|
|
436
|
+
loading
|
|
437
|
+
} = React.useContext(PanelContext);
|
|
438
|
+
const options = React.useMemo(() => {
|
|
439
|
+
return dataSources.map((d) => ({
|
|
440
|
+
value: d.id,
|
|
441
|
+
label: d.id
|
|
442
|
+
}));
|
|
443
|
+
}, [dataSources]);
|
|
444
|
+
return /* @__PURE__ */ jsxs(Group, {
|
|
445
|
+
direction: "column",
|
|
446
|
+
grow: true,
|
|
447
|
+
noWrap: true,
|
|
448
|
+
children: [/* @__PURE__ */ jsxs(Group, {
|
|
449
|
+
position: "left",
|
|
450
|
+
sx: {
|
|
451
|
+
maxWidth: "600px",
|
|
452
|
+
alignItems: "baseline"
|
|
453
|
+
},
|
|
454
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
455
|
+
children: "Select a Data Source"
|
|
456
|
+
}), /* @__PURE__ */ jsx(Select, {
|
|
457
|
+
data: options,
|
|
458
|
+
value: dataSourceID,
|
|
459
|
+
onChange: setDataSourceID,
|
|
460
|
+
allowDeselect: false,
|
|
461
|
+
clearable: false,
|
|
462
|
+
sx: {
|
|
463
|
+
flexGrow: 1
|
|
464
|
+
}
|
|
465
|
+
})]
|
|
466
|
+
}), /* @__PURE__ */ jsx(DataPreview, {
|
|
467
|
+
id: dataSourceID
|
|
468
|
+
})]
|
|
469
|
+
});
|
|
470
|
+
}
|
|
197
471
|
echarts.use([SunburstChart, CanvasRenderer]);
|
|
198
472
|
const defaultOption$1 = {
|
|
199
473
|
tooltip: {
|
|
@@ -244,8 +518,8 @@ function Sunbrust({
|
|
|
244
518
|
}
|
|
245
519
|
}
|
|
246
520
|
}
|
|
247
|
-
}), []);
|
|
248
|
-
const option = _.merge(defaultOption$1, labelOption, restConf, {
|
|
521
|
+
}), [max]);
|
|
522
|
+
const option = _.merge({}, defaultOption$1, labelOption, restConf, {
|
|
249
523
|
series: {
|
|
250
524
|
data: chartData
|
|
251
525
|
}
|
|
@@ -560,7 +834,7 @@ function VizBar3D({
|
|
|
560
834
|
}
|
|
561
835
|
});
|
|
562
836
|
}
|
|
563
|
-
var index$
|
|
837
|
+
var index$2 = "";
|
|
564
838
|
function renderViz(width, height, data, viz) {
|
|
565
839
|
const props = {
|
|
566
840
|
width,
|
|
@@ -613,311 +887,47 @@ function Viz({
|
|
|
613
887
|
}), !empty && renderViz(width, height, data, viz)]
|
|
614
888
|
});
|
|
615
889
|
}
|
|
616
|
-
function
|
|
617
|
-
const contextInfo = React.useContext(ContextInfoContext);
|
|
618
|
-
const sampleSQL = `SELECT *
|
|
619
|
-
FROM commit
|
|
620
|
-
WHERE author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.[1].toISOString()}'`;
|
|
621
|
-
return /* @__PURE__ */ jsxs(Group, {
|
|
622
|
-
direction: "column",
|
|
623
|
-
children: [/* @__PURE__ */ jsx(Prism, {
|
|
624
|
-
language: "sql",
|
|
625
|
-
sx: {
|
|
626
|
-
width: "100%"
|
|
627
|
-
},
|
|
628
|
-
noCopy: true,
|
|
629
|
-
colorScheme: "dark",
|
|
630
|
-
children: `-- You may refer context data *by name*
|
|
631
|
-
-- in SQL or VizConfig.
|
|
632
|
-
|
|
633
|
-
${sampleSQL}`
|
|
634
|
-
}), /* @__PURE__ */ jsx(Text, {
|
|
635
|
-
weight: 700,
|
|
636
|
-
children: "Avaiable context entries"
|
|
637
|
-
}), /* @__PURE__ */ jsx(Prism, {
|
|
638
|
-
language: "json",
|
|
639
|
-
sx: {
|
|
640
|
-
width: "100%"
|
|
641
|
-
},
|
|
642
|
-
noCopy: true,
|
|
643
|
-
colorScheme: "dark",
|
|
644
|
-
children: JSON.stringify(contextInfo, null, 2)
|
|
645
|
-
})]
|
|
646
|
-
});
|
|
647
|
-
}
|
|
648
|
-
var index$2 = "";
|
|
649
|
-
function SQLQueryEditor({}) {
|
|
650
|
-
const {
|
|
651
|
-
sql,
|
|
652
|
-
setSQL
|
|
653
|
-
} = React.useContext(PanelContext);
|
|
654
|
-
const [showPreview, setShowPreview] = React.useState(true);
|
|
655
|
-
const handleChange = (e) => {
|
|
656
|
-
setSQL(e.target.value);
|
|
657
|
-
};
|
|
658
|
-
const togglePreview = React.useCallback(() => {
|
|
659
|
-
setShowPreview((v) => !v);
|
|
660
|
-
}, []);
|
|
661
|
-
const format = React.useCallback(() => {
|
|
662
|
-
setSQL((sql2) => sql2.trim());
|
|
663
|
-
}, [setSQL]);
|
|
664
|
-
return /* @__PURE__ */ jsxs(Box, {
|
|
665
|
-
className: "sql-query-editor-root",
|
|
666
|
-
sx: {
|
|
667
|
-
height: "100%"
|
|
668
|
-
},
|
|
669
|
-
children: [/* @__PURE__ */ jsx(Textarea, {
|
|
670
|
-
value: sql,
|
|
671
|
-
onChange: handleChange,
|
|
672
|
-
minRows: 20,
|
|
673
|
-
maxRows: 20
|
|
674
|
-
}), /* @__PURE__ */ jsxs(Group, {
|
|
675
|
-
m: "md",
|
|
676
|
-
position: "right",
|
|
677
|
-
children: [/* @__PURE__ */ jsx(Button, {
|
|
678
|
-
color: "blue",
|
|
679
|
-
onClick: format,
|
|
680
|
-
children: "Format"
|
|
681
|
-
}), /* @__PURE__ */ jsx(Button, {
|
|
682
|
-
variant: "default",
|
|
683
|
-
onClick: togglePreview,
|
|
684
|
-
children: "Toggle Preview"
|
|
685
|
-
})]
|
|
686
|
-
}), showPreview && /* @__PURE__ */ jsx(Prism, {
|
|
687
|
-
language: "sql",
|
|
688
|
-
withLineNumbers: true,
|
|
689
|
-
noCopy: true,
|
|
690
|
-
colorScheme: "dark",
|
|
691
|
-
children: sql
|
|
692
|
-
})]
|
|
693
|
-
});
|
|
694
|
-
}
|
|
695
|
-
const QueryEditor = SQLQueryEditor;
|
|
696
|
-
function QueryResult({}) {
|
|
890
|
+
function PreviewViz({}) {
|
|
697
891
|
const {
|
|
698
|
-
data
|
|
892
|
+
data,
|
|
893
|
+
loading,
|
|
894
|
+
viz
|
|
699
895
|
} = React.useContext(PanelContext);
|
|
700
|
-
return /* @__PURE__ */
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
children: "Data Length: "
|
|
707
|
-
}), data.length]
|
|
708
|
-
}), /* @__PURE__ */ jsx(Prism, {
|
|
709
|
-
language: "json",
|
|
710
|
-
colorScheme: "dark",
|
|
711
|
-
children: JSON.stringify(data, null, 2)
|
|
712
|
-
})]
|
|
896
|
+
return /* @__PURE__ */ jsx(ErrorBoundary, {
|
|
897
|
+
children: /* @__PURE__ */ jsx(Viz, {
|
|
898
|
+
viz,
|
|
899
|
+
data,
|
|
900
|
+
loading
|
|
901
|
+
})
|
|
713
902
|
});
|
|
714
903
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
}
|
|
719
|
-
};
|
|
720
|
-
const DefinitionContext = React.createContext(initialContext);
|
|
721
|
-
function SQLSnippetsForm({
|
|
722
|
-
sqlSnippets,
|
|
723
|
-
setSQLSnippets
|
|
904
|
+
function VizBar3DPanel({
|
|
905
|
+
conf,
|
|
906
|
+
setConf
|
|
724
907
|
}) {
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
children: /* @__PURE__ */ jsxs("form", {
|
|
749
|
-
onSubmit: form.onSubmit(submit),
|
|
750
|
-
children: [form.values.snippets.map((_item, index2) => /* @__PURE__ */ jsxs(Group, {
|
|
751
|
-
direction: "column",
|
|
752
|
-
grow: true,
|
|
753
|
-
my: 0,
|
|
754
|
-
p: "md",
|
|
755
|
-
pr: 40,
|
|
756
|
-
sx: {
|
|
757
|
-
border: "1px solid #eee",
|
|
758
|
-
position: "relative"
|
|
759
|
-
},
|
|
760
|
-
children: [/* @__PURE__ */ jsx(TextInput, __spreadValues({
|
|
761
|
-
label: "Key",
|
|
762
|
-
required: true
|
|
763
|
-
}, form.getListInputProps("snippets", index2, "key"))), /* @__PURE__ */ jsx(Textarea, __spreadValues({
|
|
764
|
-
minRows: 3,
|
|
765
|
-
label: "Value",
|
|
766
|
-
required: true
|
|
767
|
-
}, form.getListInputProps("snippets", index2, "value"))), /* @__PURE__ */ jsx(ActionIcon, {
|
|
768
|
-
color: "red",
|
|
769
|
-
variant: "hover",
|
|
770
|
-
onClick: () => form.removeListItem("snippets", index2),
|
|
771
|
-
sx: {
|
|
772
|
-
position: "absolute",
|
|
773
|
-
top: 15,
|
|
774
|
-
right: 5
|
|
775
|
-
},
|
|
776
|
-
children: /* @__PURE__ */ jsx(Trash, {
|
|
777
|
-
size: 16
|
|
778
|
-
})
|
|
779
|
-
})]
|
|
780
|
-
}, index2)), /* @__PURE__ */ jsxs(Group, {
|
|
781
|
-
position: "center",
|
|
782
|
-
mt: "xl",
|
|
783
|
-
grow: true,
|
|
784
|
-
sx: {
|
|
785
|
-
width: "80%"
|
|
786
|
-
},
|
|
787
|
-
mx: "auto",
|
|
788
|
-
children: [/* @__PURE__ */ jsx(Button, {
|
|
789
|
-
variant: "default",
|
|
790
|
-
onClick: addSnippet,
|
|
791
|
-
children: "Add a snippet"
|
|
792
|
-
}), /* @__PURE__ */ jsx(Button, {
|
|
793
|
-
color: "blue",
|
|
794
|
-
type: "submit",
|
|
795
|
-
disabled: !changed,
|
|
796
|
-
children: "Submit"
|
|
797
|
-
})]
|
|
798
|
-
})]
|
|
799
|
-
})
|
|
800
|
-
});
|
|
801
|
-
}
|
|
802
|
-
function SQLSnippetsTab({}) {
|
|
803
|
-
const {
|
|
804
|
-
sqlSnippets,
|
|
805
|
-
setSQLSnippets
|
|
806
|
-
} = React.useContext(DefinitionContext);
|
|
807
|
-
const sampleSQL = `SELECT *
|
|
808
|
-
FROM commit
|
|
809
|
-
WHERE \${author_time_condition}`;
|
|
810
|
-
return /* @__PURE__ */ jsxs(Group, {
|
|
811
|
-
direction: "column",
|
|
812
|
-
children: [/* @__PURE__ */ jsx(Prism, {
|
|
813
|
-
language: "sql",
|
|
814
|
-
sx: {
|
|
815
|
-
width: "100%"
|
|
816
|
-
},
|
|
817
|
-
noCopy: true,
|
|
818
|
-
trim: false,
|
|
819
|
-
colorScheme: "dark",
|
|
820
|
-
children: `-- You may refer context data *by name*
|
|
821
|
-
-- in SQL or VizConfig.
|
|
822
|
-
|
|
823
|
-
${sampleSQL}
|
|
824
|
-
|
|
825
|
-
-- where author_time_condition is:
|
|
826
|
-
author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.[1].toISOString()}'
|
|
827
|
-
`
|
|
828
|
-
}), /* @__PURE__ */ jsx(Text, {
|
|
829
|
-
weight: 700,
|
|
830
|
-
children: "SQL Snippets"
|
|
831
|
-
}), /* @__PURE__ */ jsx(SQLSnippetsForm, {
|
|
832
|
-
sqlSnippets,
|
|
833
|
-
setSQLSnippets
|
|
834
|
-
})]
|
|
835
|
-
});
|
|
836
|
-
}
|
|
837
|
-
function EditDescription() {
|
|
838
|
-
const {
|
|
839
|
-
description,
|
|
840
|
-
setDescription
|
|
841
|
-
} = React.useContext(PanelContext);
|
|
842
|
-
const [localDesc, setLocalDesc] = useInputState(description);
|
|
843
|
-
const changed = description !== localDesc;
|
|
844
|
-
const submit = React.useCallback(() => {
|
|
845
|
-
if (!changed) {
|
|
846
|
-
return;
|
|
847
|
-
}
|
|
848
|
-
setDescription(localDesc);
|
|
849
|
-
}, [changed, localDesc]);
|
|
850
|
-
return /* @__PURE__ */ jsx(Textarea, {
|
|
851
|
-
label: "Panel Description",
|
|
852
|
-
value: localDesc,
|
|
853
|
-
onChange: setLocalDesc,
|
|
854
|
-
minRows: 2,
|
|
855
|
-
rightSection: /* @__PURE__ */ jsx(ActionIcon, {
|
|
856
|
-
disabled: !changed,
|
|
857
|
-
onClick: submit,
|
|
858
|
-
sx: {
|
|
859
|
-
alignSelf: "flex-start",
|
|
860
|
-
marginTop: "4px"
|
|
861
|
-
},
|
|
862
|
-
children: /* @__PURE__ */ jsx(DeviceFloppy, {
|
|
863
|
-
size: 20
|
|
864
|
-
})
|
|
865
|
-
})
|
|
866
|
-
});
|
|
867
|
-
}
|
|
868
|
-
function EditTitle() {
|
|
869
|
-
const {
|
|
870
|
-
title,
|
|
871
|
-
setTitle
|
|
872
|
-
} = React.useContext(PanelContext);
|
|
873
|
-
const [localTitle, setLocalTitle] = useInputState(title);
|
|
874
|
-
const changed = title !== localTitle;
|
|
875
|
-
const submit = React.useCallback(() => {
|
|
876
|
-
if (!changed) {
|
|
877
|
-
return;
|
|
878
|
-
}
|
|
879
|
-
setTitle(localTitle);
|
|
880
|
-
}, [changed, localTitle]);
|
|
881
|
-
return /* @__PURE__ */ jsx(TextInput, {
|
|
882
|
-
label: "Panel Title",
|
|
883
|
-
value: localTitle,
|
|
884
|
-
onChange: setLocalTitle,
|
|
885
|
-
rightSection: /* @__PURE__ */ jsx(ActionIcon, {
|
|
886
|
-
disabled: !changed,
|
|
887
|
-
onClick: submit,
|
|
888
|
-
children: /* @__PURE__ */ jsx(DeviceFloppy, {
|
|
889
|
-
size: 20
|
|
890
|
-
})
|
|
891
|
-
})
|
|
892
|
-
});
|
|
893
|
-
}
|
|
894
|
-
function VizBar3DPanel({
|
|
895
|
-
conf,
|
|
896
|
-
setConf
|
|
897
|
-
}) {
|
|
898
|
-
const defaultValues = _.assign({}, {
|
|
899
|
-
"x_axis_data_key": "x",
|
|
900
|
-
"y_axis_data_key": "y",
|
|
901
|
-
"z_axis_data_key": "z",
|
|
902
|
-
"xAxis3D": {
|
|
903
|
-
"type": "value",
|
|
904
|
-
"name": "X Axis Name"
|
|
905
|
-
},
|
|
906
|
-
"yAxis3D": {
|
|
907
|
-
"type": "value",
|
|
908
|
-
"name": "Y Axis Name"
|
|
909
|
-
},
|
|
910
|
-
"zAxis3D": {
|
|
911
|
-
"type": "value",
|
|
912
|
-
"name": "Z Axis Name"
|
|
913
|
-
}
|
|
914
|
-
}, conf);
|
|
915
|
-
const {
|
|
916
|
-
control,
|
|
917
|
-
handleSubmit,
|
|
918
|
-
formState
|
|
919
|
-
} = useForm$1({
|
|
920
|
-
defaultValues
|
|
908
|
+
const defaultValues = _.assign({}, {
|
|
909
|
+
"x_axis_data_key": "x",
|
|
910
|
+
"y_axis_data_key": "y",
|
|
911
|
+
"z_axis_data_key": "z",
|
|
912
|
+
"xAxis3D": {
|
|
913
|
+
"type": "value",
|
|
914
|
+
"name": "X Axis Name"
|
|
915
|
+
},
|
|
916
|
+
"yAxis3D": {
|
|
917
|
+
"type": "value",
|
|
918
|
+
"name": "Y Axis Name"
|
|
919
|
+
},
|
|
920
|
+
"zAxis3D": {
|
|
921
|
+
"type": "value",
|
|
922
|
+
"name": "Z Axis Name"
|
|
923
|
+
}
|
|
924
|
+
}, conf);
|
|
925
|
+
const {
|
|
926
|
+
control,
|
|
927
|
+
handleSubmit,
|
|
928
|
+
formState
|
|
929
|
+
} = useForm({
|
|
930
|
+
defaultValues
|
|
921
931
|
});
|
|
922
932
|
return /* @__PURE__ */ jsx(Group, {
|
|
923
933
|
direction: "column",
|
|
@@ -1119,7 +1129,7 @@ function VizLineBarChartPanel({
|
|
|
1119
1129
|
const initialValues = React.useMemo(() => __spreadValues({
|
|
1120
1130
|
series: formList(series != null ? series : [])
|
|
1121
1131
|
}, restConf), [series, restConf]);
|
|
1122
|
-
const form = useForm({
|
|
1132
|
+
const form = useForm$1({
|
|
1123
1133
|
initialValues
|
|
1124
1134
|
});
|
|
1125
1135
|
const addSeries = () => form.addListItem("series", {
|
|
@@ -1231,7 +1241,7 @@ function SunburstPanel({
|
|
|
1231
1241
|
},
|
|
1232
1242
|
setConf
|
|
1233
1243
|
}) {
|
|
1234
|
-
const form = useForm({
|
|
1244
|
+
const form = useForm$1({
|
|
1235
1245
|
initialValues: {
|
|
1236
1246
|
label_field,
|
|
1237
1247
|
value_field
|
|
@@ -1318,7 +1328,7 @@ function VizTablePanel(_a) {
|
|
|
1318
1328
|
]), {
|
|
1319
1329
|
setConf
|
|
1320
1330
|
} = _b;
|
|
1321
|
-
const form = useForm({
|
|
1331
|
+
const form = useForm$1({
|
|
1322
1332
|
initialValues: __spreadValues({
|
|
1323
1333
|
id_field: "id",
|
|
1324
1334
|
use_raw_columns: true,
|
|
@@ -1593,7 +1603,7 @@ function VizTextPanel({
|
|
|
1593
1603
|
setConf
|
|
1594
1604
|
}) {
|
|
1595
1605
|
var _a;
|
|
1596
|
-
const form = useForm({
|
|
1606
|
+
const form = useForm$1({
|
|
1597
1607
|
initialValues: {
|
|
1598
1608
|
paragraphs: formList((_a = conf.paragraphs) != null ? _a : sampleParagraphs)
|
|
1599
1609
|
}
|
|
@@ -1788,9 +1798,30 @@ function EditVizConf() {
|
|
|
1788
1798
|
}
|
|
1789
1799
|
function VizConfig({}) {
|
|
1790
1800
|
return /* @__PURE__ */ jsxs(Group, {
|
|
1801
|
+
direction: "row",
|
|
1791
1802
|
grow: true,
|
|
1792
|
-
|
|
1793
|
-
|
|
1803
|
+
noWrap: true,
|
|
1804
|
+
align: "stretch",
|
|
1805
|
+
sx: {
|
|
1806
|
+
height: "100%"
|
|
1807
|
+
},
|
|
1808
|
+
children: [/* @__PURE__ */ jsx(Group, {
|
|
1809
|
+
grow: true,
|
|
1810
|
+
direction: "column",
|
|
1811
|
+
sx: {
|
|
1812
|
+
width: "40%",
|
|
1813
|
+
flexShrink: 0,
|
|
1814
|
+
flexGrow: 0
|
|
1815
|
+
},
|
|
1816
|
+
children: /* @__PURE__ */ jsx(EditVizConf, {})
|
|
1817
|
+
}), /* @__PURE__ */ jsx(Box, {
|
|
1818
|
+
sx: {
|
|
1819
|
+
height: "100%",
|
|
1820
|
+
flexGrow: 1,
|
|
1821
|
+
maxWidth: "60%"
|
|
1822
|
+
},
|
|
1823
|
+
children: /* @__PURE__ */ jsx(PreviewViz, {})
|
|
1824
|
+
})]
|
|
1794
1825
|
});
|
|
1795
1826
|
}
|
|
1796
1827
|
function PanelSettingsModal({
|
|
@@ -1832,40 +1863,20 @@ function PanelSettingsModal({
|
|
|
1832
1863
|
}
|
|
1833
1864
|
},
|
|
1834
1865
|
padding: "md",
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
}), /* @__PURE__ */ jsx(Tabs.Tab, {
|
|
1850
|
-
label: "SQL",
|
|
1851
|
-
children: /* @__PURE__ */ jsx(QueryEditor, {})
|
|
1852
|
-
}), /* @__PURE__ */ jsxs(Tabs.Tab, {
|
|
1853
|
-
label: "Data",
|
|
1854
|
-
children: [/* @__PURE__ */ jsx(LoadingOverlay, {
|
|
1855
|
-
visible: loading
|
|
1856
|
-
}), /* @__PURE__ */ jsx(QueryResult, {})]
|
|
1857
|
-
}), /* @__PURE__ */ jsx(Tabs.Tab, {
|
|
1858
|
-
label: "Viz Config",
|
|
1859
|
-
children: /* @__PURE__ */ jsx(VizConfig, {})
|
|
1860
|
-
})]
|
|
1861
|
-
})
|
|
1862
|
-
}),
|
|
1863
|
-
children: /* @__PURE__ */ jsx(ErrorBoundary, {
|
|
1864
|
-
children: /* @__PURE__ */ jsx(Viz, {
|
|
1865
|
-
viz,
|
|
1866
|
-
data,
|
|
1867
|
-
loading
|
|
1868
|
-
})
|
|
1866
|
+
children: /* @__PURE__ */ jsxs(Tabs, {
|
|
1867
|
+
initialTab: 2,
|
|
1868
|
+
children: [/* @__PURE__ */ jsxs(Tabs.Tab, {
|
|
1869
|
+
label: "Data Source",
|
|
1870
|
+
children: [/* @__PURE__ */ jsx(LoadingOverlay, {
|
|
1871
|
+
visible: loading
|
|
1872
|
+
}), /* @__PURE__ */ jsx(PickDataSource, {})]
|
|
1873
|
+
}), /* @__PURE__ */ jsx(Tabs.Tab, {
|
|
1874
|
+
label: "Panel",
|
|
1875
|
+
children: /* @__PURE__ */ jsx(PanelConfig, {})
|
|
1876
|
+
}), /* @__PURE__ */ jsx(Tabs.Tab, {
|
|
1877
|
+
label: "Visualization",
|
|
1878
|
+
children: /* @__PURE__ */ jsx(VizConfig, {})
|
|
1879
|
+
})]
|
|
1869
1880
|
})
|
|
1870
1881
|
})
|
|
1871
1882
|
});
|
|
@@ -1947,7 +1958,7 @@ function PanelTitleBar({}) {
|
|
|
1947
1958
|
var index$1 = "";
|
|
1948
1959
|
function Panel({
|
|
1949
1960
|
viz: initialViz,
|
|
1950
|
-
|
|
1961
|
+
dataSourceID: initialDataSourceID,
|
|
1951
1962
|
title: initialTitle,
|
|
1952
1963
|
description: initialDesc,
|
|
1953
1964
|
update,
|
|
@@ -1958,24 +1969,35 @@ function Panel({
|
|
|
1958
1969
|
const definitions = React.useContext(DefinitionContext);
|
|
1959
1970
|
const [title, setTitle] = React.useState(initialTitle);
|
|
1960
1971
|
const [description, setDescription] = React.useState(initialDesc);
|
|
1961
|
-
const [
|
|
1972
|
+
const [dataSourceID, setDataSourceID] = React.useState(initialDataSourceID);
|
|
1962
1973
|
const [viz, setViz] = React.useState(initialViz);
|
|
1974
|
+
const dataSource = React.useMemo(() => {
|
|
1975
|
+
if (!dataSourceID) {
|
|
1976
|
+
return void 0;
|
|
1977
|
+
}
|
|
1978
|
+
return definitions.dataSources.find((d) => d.id === dataSourceID);
|
|
1979
|
+
}, [dataSourceID, definitions.dataSources]);
|
|
1963
1980
|
React.useEffect(() => {
|
|
1964
|
-
update({
|
|
1981
|
+
update == null ? void 0 : update({
|
|
1965
1982
|
id,
|
|
1966
1983
|
layout,
|
|
1967
1984
|
title,
|
|
1968
1985
|
description,
|
|
1969
|
-
|
|
1986
|
+
dataSourceID,
|
|
1970
1987
|
viz
|
|
1971
1988
|
});
|
|
1972
|
-
}, [title, description,
|
|
1989
|
+
}, [title, description, dataSource, viz, id, layout, dataSourceID]);
|
|
1973
1990
|
const {
|
|
1974
1991
|
data = [],
|
|
1975
1992
|
loading,
|
|
1976
1993
|
refresh
|
|
1977
|
-
} = useRequest(queryBySQL(
|
|
1978
|
-
|
|
1994
|
+
} = useRequest(queryBySQL({
|
|
1995
|
+
context: contextInfo,
|
|
1996
|
+
definitions,
|
|
1997
|
+
title,
|
|
1998
|
+
dataSource
|
|
1999
|
+
}), {
|
|
2000
|
+
refreshDeps: [contextInfo, definitions, dataSource]
|
|
1979
2001
|
});
|
|
1980
2002
|
const refreshData = refresh;
|
|
1981
2003
|
return /* @__PURE__ */ jsx(PanelContext.Provider, {
|
|
@@ -1986,8 +2008,8 @@ function Panel({
|
|
|
1986
2008
|
setTitle,
|
|
1987
2009
|
description,
|
|
1988
2010
|
setDescription,
|
|
1989
|
-
|
|
1990
|
-
|
|
2011
|
+
dataSourceID,
|
|
2012
|
+
setDataSourceID,
|
|
1991
2013
|
viz,
|
|
1992
2014
|
setViz,
|
|
1993
2015
|
refreshData
|
|
@@ -2005,7 +2027,7 @@ function Panel({
|
|
|
2005
2027
|
});
|
|
2006
2028
|
}
|
|
2007
2029
|
var index = "";
|
|
2008
|
-
const ResponsiveReactGridLayout = WidthProvider(Responsive);
|
|
2030
|
+
const ResponsiveReactGridLayout$1 = WidthProvider(Responsive);
|
|
2009
2031
|
function DashboardLayout({
|
|
2010
2032
|
panels,
|
|
2011
2033
|
setPanels,
|
|
@@ -2041,9 +2063,9 @@ function DashboardLayout({
|
|
|
2041
2063
|
const newPanels = panels.map((p2) => __spreadProps(__spreadValues({}, p2), {
|
|
2042
2064
|
layout: m2.get(p2.id)
|
|
2043
2065
|
}));
|
|
2044
|
-
setPanels
|
|
2066
|
+
setPanels(newPanels);
|
|
2045
2067
|
}, [panels, setPanels]);
|
|
2046
|
-
return /* @__PURE__ */ jsx(ResponsiveReactGridLayout, {
|
|
2068
|
+
return /* @__PURE__ */ jsx(ResponsiveReactGridLayout$1, {
|
|
2047
2069
|
onBreakpointChange,
|
|
2048
2070
|
onLayoutChange,
|
|
2049
2071
|
className,
|
|
@@ -2064,7 +2086,10 @@ function DashboardLayout({
|
|
|
2064
2086
|
}, rest), {
|
|
2065
2087
|
destroy: () => onRemoveItem(id),
|
|
2066
2088
|
update: (panel) => {
|
|
2067
|
-
setPanels
|
|
2089
|
+
setPanels((prevs) => {
|
|
2090
|
+
prevs.splice(index2, 1, panel);
|
|
2091
|
+
return prevs;
|
|
2092
|
+
});
|
|
2068
2093
|
}
|
|
2069
2094
|
}))
|
|
2070
2095
|
}, id);
|
|
@@ -2101,73 +2126,673 @@ function ModeToggler({
|
|
|
2101
2126
|
}]
|
|
2102
2127
|
});
|
|
2103
2128
|
}
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2129
|
+
const example = `
|
|
2130
|
+
-- You may reference context data or SQL snippets *by name*
|
|
2131
|
+
-- in SQL or VizConfig.
|
|
2132
|
+
SELECT *
|
|
2133
|
+
FROM commit
|
|
2134
|
+
WHERE
|
|
2135
|
+
-- context data
|
|
2136
|
+
author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.[1].toISOString()}'
|
|
2137
|
+
-- SQL snippets
|
|
2138
|
+
AND \${author_email_condition}
|
|
2139
|
+
\${order_by_clause}
|
|
2140
|
+
`;
|
|
2141
|
+
function ContextAndSnippets({}) {
|
|
2142
|
+
const contextInfo = React.useContext(ContextInfoContext);
|
|
2143
|
+
const {
|
|
2144
|
+
sqlSnippets
|
|
2145
|
+
} = React.useContext(DefinitionContext);
|
|
2146
|
+
const snippets = React.useMemo(() => {
|
|
2147
|
+
const snippets2 = sqlSnippets.reduce((prev, curr) => {
|
|
2148
|
+
prev[curr.key] = curr.value;
|
|
2149
|
+
return prev;
|
|
2150
|
+
}, {});
|
|
2151
|
+
return JSON.stringify(snippets2, null, 2);
|
|
2152
|
+
}, [sqlSnippets]);
|
|
2153
|
+
const context = React.useMemo(() => {
|
|
2154
|
+
return JSON.stringify(contextInfo, null, 2);
|
|
2155
|
+
}, [contextInfo]);
|
|
2112
2156
|
return /* @__PURE__ */ jsxs(Group, {
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2157
|
+
direction: "column",
|
|
2158
|
+
grow: true,
|
|
2159
|
+
sx: {
|
|
2160
|
+
border: "1px solid #eee",
|
|
2161
|
+
maxWidth: "48%",
|
|
2162
|
+
overflow: "hidden"
|
|
2163
|
+
},
|
|
2116
2164
|
children: [/* @__PURE__ */ jsx(Group, {
|
|
2117
2165
|
position: "left",
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2166
|
+
pl: "md",
|
|
2167
|
+
py: "md",
|
|
2168
|
+
sx: {
|
|
2169
|
+
borderBottom: "1px solid #eee",
|
|
2170
|
+
background: "#efefef",
|
|
2171
|
+
flexGrow: 0
|
|
2172
|
+
},
|
|
2173
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
2174
|
+
weight: 500,
|
|
2175
|
+
children: "Context"
|
|
2121
2176
|
})
|
|
2122
|
-
}),
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
}
|
|
2148
|
-
|
|
2177
|
+
}), /* @__PURE__ */ jsxs(Group, {
|
|
2178
|
+
direction: "column",
|
|
2179
|
+
px: "md",
|
|
2180
|
+
pb: "md",
|
|
2181
|
+
sx: {
|
|
2182
|
+
width: "100%"
|
|
2183
|
+
},
|
|
2184
|
+
children: [/* @__PURE__ */ jsx(Prism, {
|
|
2185
|
+
language: "sql",
|
|
2186
|
+
sx: {
|
|
2187
|
+
width: "100%"
|
|
2188
|
+
},
|
|
2189
|
+
noCopy: true,
|
|
2190
|
+
colorScheme: "dark",
|
|
2191
|
+
children: example
|
|
2192
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
2193
|
+
weight: 500,
|
|
2194
|
+
sx: {
|
|
2195
|
+
flexGrow: 0
|
|
2196
|
+
},
|
|
2197
|
+
children: "Avaiable context"
|
|
2198
|
+
}), /* @__PURE__ */ jsx(Prism, {
|
|
2199
|
+
language: "json",
|
|
2200
|
+
sx: {
|
|
2201
|
+
width: "100%"
|
|
2202
|
+
},
|
|
2203
|
+
noCopy: true,
|
|
2204
|
+
colorScheme: "dark",
|
|
2205
|
+
children: context
|
|
2206
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
2207
|
+
weight: 500,
|
|
2208
|
+
sx: {
|
|
2209
|
+
flexGrow: 0
|
|
2210
|
+
},
|
|
2211
|
+
children: "Avaiable SQL Snippets"
|
|
2212
|
+
}), /* @__PURE__ */ jsx(Prism, {
|
|
2213
|
+
language: "json",
|
|
2214
|
+
sx: {
|
|
2215
|
+
width: "100%"
|
|
2216
|
+
},
|
|
2217
|
+
noCopy: true,
|
|
2218
|
+
colorScheme: "dark",
|
|
2219
|
+
children: snippets
|
|
2149
2220
|
})]
|
|
2150
|
-
}), !inEditMode && /* @__PURE__ */ jsx(Button, {
|
|
2151
|
-
variant: "default",
|
|
2152
|
-
size: "sm",
|
|
2153
|
-
disabled: true,
|
|
2154
|
-
leftIcon: /* @__PURE__ */ jsx(Share, {
|
|
2155
|
-
size: 20
|
|
2156
|
-
}),
|
|
2157
|
-
children: "Share"
|
|
2158
2221
|
})]
|
|
2159
2222
|
});
|
|
2160
2223
|
}
|
|
2161
|
-
function
|
|
2162
|
-
|
|
2224
|
+
function DataSourceForm({
|
|
2225
|
+
value,
|
|
2226
|
+
onChange
|
|
2227
|
+
}) {
|
|
2228
|
+
const form = useForm$1({
|
|
2229
|
+
initialValues: value
|
|
2230
|
+
});
|
|
2231
|
+
const submit = React.useCallback((values) => {
|
|
2232
|
+
onChange(values);
|
|
2233
|
+
}, [onChange]);
|
|
2234
|
+
const changed = React.useMemo(() => {
|
|
2235
|
+
return !_.isEqual(value, form.values);
|
|
2236
|
+
}, [value, form.values]);
|
|
2237
|
+
React.useEffect(() => {
|
|
2238
|
+
form.reset();
|
|
2239
|
+
}, [value]);
|
|
2240
|
+
return /* @__PURE__ */ jsx(Group, {
|
|
2241
|
+
direction: "column",
|
|
2242
|
+
grow: true,
|
|
2243
|
+
sx: {
|
|
2244
|
+
border: "1px solid #eee",
|
|
2245
|
+
flexGrow: 1
|
|
2246
|
+
},
|
|
2247
|
+
children: /* @__PURE__ */ jsxs("form", {
|
|
2248
|
+
onSubmit: form.onSubmit(submit),
|
|
2249
|
+
children: [/* @__PURE__ */ jsxs(Group, {
|
|
2250
|
+
position: "left",
|
|
2251
|
+
py: "md",
|
|
2252
|
+
pl: "md",
|
|
2253
|
+
sx: {
|
|
2254
|
+
borderBottom: "1px solid #eee",
|
|
2255
|
+
background: "#efefef"
|
|
2256
|
+
},
|
|
2257
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
2258
|
+
weight: 500,
|
|
2259
|
+
children: "Data Source Configuration"
|
|
2260
|
+
}), /* @__PURE__ */ jsx(ActionIcon, {
|
|
2261
|
+
type: "submit",
|
|
2262
|
+
mr: 5,
|
|
2263
|
+
variant: "filled",
|
|
2264
|
+
color: "blue",
|
|
2265
|
+
disabled: !changed,
|
|
2266
|
+
children: /* @__PURE__ */ jsx(DeviceFloppy, {
|
|
2267
|
+
size: 20
|
|
2268
|
+
})
|
|
2269
|
+
})]
|
|
2270
|
+
}), /* @__PURE__ */ jsxs(Group, {
|
|
2271
|
+
direction: "column",
|
|
2272
|
+
grow: true,
|
|
2273
|
+
my: 0,
|
|
2274
|
+
p: "md",
|
|
2275
|
+
pr: 40,
|
|
2276
|
+
children: [/* @__PURE__ */ jsxs(Group, {
|
|
2277
|
+
grow: true,
|
|
2278
|
+
children: [/* @__PURE__ */ jsx(TextInput, __spreadValues({
|
|
2279
|
+
placeholder: "An ID unique in this dashboard",
|
|
2280
|
+
label: "ID",
|
|
2281
|
+
required: true,
|
|
2282
|
+
sx: {
|
|
2283
|
+
flex: 1
|
|
2284
|
+
}
|
|
2285
|
+
}, form.getInputProps("id"))), /* @__PURE__ */ jsx(TextInput, __spreadValues({
|
|
2286
|
+
placeholder: "TODO: use a dedicated UI component for this",
|
|
2287
|
+
label: "Data Source Key",
|
|
2288
|
+
required: true,
|
|
2289
|
+
sx: {
|
|
2290
|
+
flex: 1
|
|
2291
|
+
}
|
|
2292
|
+
}, form.getInputProps("key"))), /* @__PURE__ */ jsx(TextInput, __spreadValues({
|
|
2293
|
+
placeholder: "Type of the data source",
|
|
2294
|
+
label: "Type",
|
|
2295
|
+
disabled: true,
|
|
2296
|
+
sx: {
|
|
2297
|
+
flex: 1
|
|
2298
|
+
}
|
|
2299
|
+
}, form.getInputProps("type")))]
|
|
2300
|
+
}), /* @__PURE__ */ jsx(Textarea, __spreadValues({
|
|
2301
|
+
autosize: true,
|
|
2302
|
+
minRows: 12,
|
|
2303
|
+
maxRows: 24
|
|
2304
|
+
}, form.getInputProps("sql")))]
|
|
2305
|
+
})]
|
|
2306
|
+
})
|
|
2307
|
+
});
|
|
2308
|
+
}
|
|
2309
|
+
function DataSourceEditor({
|
|
2310
|
+
id
|
|
2311
|
+
}) {
|
|
2312
|
+
const {
|
|
2313
|
+
dataSources,
|
|
2314
|
+
setDataSources
|
|
2315
|
+
} = React.useContext(DefinitionContext);
|
|
2316
|
+
const dataSource = React.useMemo(() => {
|
|
2317
|
+
return dataSources.find((d) => d.id === id);
|
|
2318
|
+
}, [dataSources, id]);
|
|
2319
|
+
const update = React.useCallback((value) => {
|
|
2320
|
+
const index2 = dataSources.findIndex((d) => d.id === value.id);
|
|
2321
|
+
if (!index2) {
|
|
2322
|
+
console.error(new Error("Invalid data source id when updating by id"));
|
|
2323
|
+
return;
|
|
2324
|
+
}
|
|
2325
|
+
setDataSources((prevs) => {
|
|
2326
|
+
return prevs.map((p2) => {
|
|
2327
|
+
if (p2.id === value.id) {
|
|
2328
|
+
return value;
|
|
2329
|
+
}
|
|
2330
|
+
return p2;
|
|
2331
|
+
});
|
|
2332
|
+
});
|
|
2333
|
+
}, [setDataSources]);
|
|
2334
|
+
if (!dataSource) {
|
|
2335
|
+
return /* @__PURE__ */ jsx("span", {
|
|
2336
|
+
children: "Invalid Data Source ID"
|
|
2337
|
+
});
|
|
2338
|
+
}
|
|
2339
|
+
return /* @__PURE__ */ jsxs(Group, {
|
|
2340
|
+
direction: "row",
|
|
2341
|
+
position: "apart",
|
|
2342
|
+
grow: true,
|
|
2343
|
+
align: "stretch",
|
|
2344
|
+
noWrap: true,
|
|
2345
|
+
children: [/* @__PURE__ */ jsx(DataSourceForm, {
|
|
2346
|
+
value: dataSource,
|
|
2347
|
+
onChange: update
|
|
2348
|
+
}), /* @__PURE__ */ jsx(ContextAndSnippets, {})]
|
|
2349
|
+
});
|
|
2350
|
+
}
|
|
2351
|
+
function SelectOrAddDataSource({
|
|
2352
|
+
id,
|
|
2353
|
+
setID
|
|
2354
|
+
}) {
|
|
2355
|
+
const {
|
|
2356
|
+
dataSources,
|
|
2357
|
+
setDataSources
|
|
2358
|
+
} = React.useContext(DefinitionContext);
|
|
2359
|
+
React.useEffect(() => {
|
|
2360
|
+
var _a, _b;
|
|
2361
|
+
if (!id) {
|
|
2362
|
+
setID((_b = (_a = dataSources[0]) == null ? void 0 : _a.id) != null ? _b : "");
|
|
2363
|
+
}
|
|
2364
|
+
}, [id, setID, dataSources]);
|
|
2365
|
+
const options = React.useMemo(() => {
|
|
2366
|
+
return dataSources.map((d) => ({
|
|
2367
|
+
value: d.id,
|
|
2368
|
+
label: d.id
|
|
2369
|
+
}));
|
|
2370
|
+
}, [dataSources]);
|
|
2371
|
+
const add = React.useCallback(() => {
|
|
2372
|
+
const newDataSource = {
|
|
2373
|
+
id: randomId(),
|
|
2374
|
+
type: "postgresql",
|
|
2375
|
+
key: "",
|
|
2376
|
+
sql: ""
|
|
2377
|
+
};
|
|
2378
|
+
setDataSources((prevs) => [...prevs, newDataSource]);
|
|
2379
|
+
}, [setDataSources]);
|
|
2380
|
+
return /* @__PURE__ */ jsx(Group, {
|
|
2381
|
+
pb: "xl",
|
|
2382
|
+
children: /* @__PURE__ */ jsxs(Group, {
|
|
2383
|
+
position: "left",
|
|
2384
|
+
sx: {
|
|
2385
|
+
maxWidth: "600px",
|
|
2386
|
+
alignItems: "baseline"
|
|
2387
|
+
},
|
|
2388
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
2389
|
+
children: "Select a Data Source"
|
|
2390
|
+
}), /* @__PURE__ */ jsx(Select, {
|
|
2391
|
+
data: options,
|
|
2392
|
+
value: id,
|
|
2393
|
+
onChange: setID,
|
|
2394
|
+
allowDeselect: false,
|
|
2395
|
+
clearable: false,
|
|
2396
|
+
sx: {
|
|
2397
|
+
flexGrow: 1
|
|
2398
|
+
}
|
|
2399
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
2400
|
+
children: "or"
|
|
2401
|
+
}), /* @__PURE__ */ jsx(Group, {
|
|
2402
|
+
position: "center",
|
|
2403
|
+
mt: "md",
|
|
2404
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
2405
|
+
onClick: add,
|
|
2406
|
+
children: "Add a Data Source"
|
|
2407
|
+
})
|
|
2408
|
+
})]
|
|
2409
|
+
})
|
|
2410
|
+
});
|
|
2411
|
+
}
|
|
2412
|
+
function EditDataSourcesModal({
|
|
2413
|
+
opened,
|
|
2414
|
+
close
|
|
2415
|
+
}) {
|
|
2416
|
+
const [id, setID] = React.useState("");
|
|
2417
|
+
const {
|
|
2418
|
+
freezeLayout
|
|
2419
|
+
} = React.useContext(LayoutStateContext);
|
|
2420
|
+
React.useEffect(() => {
|
|
2421
|
+
freezeLayout(opened);
|
|
2422
|
+
}, [opened]);
|
|
2423
|
+
return /* @__PURE__ */ jsx(Modal, {
|
|
2424
|
+
size: "96vw",
|
|
2425
|
+
overflow: "inside",
|
|
2426
|
+
opened,
|
|
2427
|
+
onClose: close,
|
|
2428
|
+
title: "Data Sources",
|
|
2429
|
+
trapFocus: true,
|
|
2430
|
+
onDragStart: (e) => {
|
|
2431
|
+
e.stopPropagation();
|
|
2432
|
+
},
|
|
2433
|
+
children: /* @__PURE__ */ jsxs(AppShell, {
|
|
2434
|
+
sx: {
|
|
2435
|
+
height: "90vh",
|
|
2436
|
+
maxHeight: "calc(100vh - 185px)",
|
|
2437
|
+
".mantine-AppShell-body": {
|
|
2438
|
+
height: "100%"
|
|
2439
|
+
},
|
|
2440
|
+
main: {
|
|
2441
|
+
height: "100%",
|
|
2442
|
+
width: "100%",
|
|
2443
|
+
padding: 0,
|
|
2444
|
+
margin: 0
|
|
2445
|
+
}
|
|
2446
|
+
},
|
|
2447
|
+
padding: "md",
|
|
2448
|
+
header: /* @__PURE__ */ jsx(SelectOrAddDataSource, {
|
|
2449
|
+
id,
|
|
2450
|
+
setID
|
|
2451
|
+
}),
|
|
2452
|
+
children: [/* @__PURE__ */ jsx(DataSourceEditor, {
|
|
2453
|
+
id
|
|
2454
|
+
}), /* @__PURE__ */ jsx(DataPreview, {
|
|
2455
|
+
id
|
|
2456
|
+
})]
|
|
2457
|
+
})
|
|
2458
|
+
});
|
|
2459
|
+
}
|
|
2460
|
+
function ContextInfo({}) {
|
|
2461
|
+
const contextInfo = React.useContext(ContextInfoContext);
|
|
2462
|
+
const sampleSQL = `SELECT *
|
|
2463
|
+
FROM commit
|
|
2464
|
+
WHERE author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.[1].toISOString()}'`;
|
|
2465
|
+
return /* @__PURE__ */ jsxs(Group, {
|
|
2466
|
+
direction: "column",
|
|
2467
|
+
grow: true,
|
|
2468
|
+
sx: {
|
|
2469
|
+
border: "1px solid #eee",
|
|
2470
|
+
maxWidth: "48%",
|
|
2471
|
+
overflow: "hidden"
|
|
2472
|
+
},
|
|
2473
|
+
children: [/* @__PURE__ */ jsx(Group, {
|
|
2474
|
+
position: "left",
|
|
2475
|
+
pl: "md",
|
|
2476
|
+
py: "md",
|
|
2477
|
+
sx: {
|
|
2478
|
+
borderBottom: "1px solid #eee",
|
|
2479
|
+
background: "#efefef",
|
|
2480
|
+
flexGrow: 0
|
|
2481
|
+
},
|
|
2482
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
2483
|
+
weight: 500,
|
|
2484
|
+
children: "Context"
|
|
2485
|
+
})
|
|
2486
|
+
}), /* @__PURE__ */ jsxs(Group, {
|
|
2487
|
+
direction: "column",
|
|
2488
|
+
px: "md",
|
|
2489
|
+
pb: "md",
|
|
2490
|
+
sx: {
|
|
2491
|
+
width: "100%"
|
|
2492
|
+
},
|
|
2493
|
+
children: [/* @__PURE__ */ jsx(Prism, {
|
|
2494
|
+
language: "sql",
|
|
2495
|
+
sx: {
|
|
2496
|
+
width: "100%"
|
|
2497
|
+
},
|
|
2498
|
+
noCopy: true,
|
|
2499
|
+
colorScheme: "dark",
|
|
2500
|
+
children: `-- You may refer context data *by name*
|
|
2501
|
+
-- in SQL or VizConfig.
|
|
2502
|
+
|
|
2503
|
+
${sampleSQL}`
|
|
2504
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
2505
|
+
weight: 500,
|
|
2506
|
+
sx: {
|
|
2507
|
+
flexGrow: 0
|
|
2508
|
+
},
|
|
2509
|
+
children: "Avaiable context entries"
|
|
2510
|
+
}), /* @__PURE__ */ jsx(Prism, {
|
|
2511
|
+
language: "json",
|
|
2512
|
+
sx: {
|
|
2513
|
+
width: "100%"
|
|
2514
|
+
},
|
|
2515
|
+
noCopy: true,
|
|
2516
|
+
colorScheme: "dark",
|
|
2517
|
+
children: JSON.stringify(contextInfo, null, 2)
|
|
2518
|
+
})]
|
|
2519
|
+
})]
|
|
2520
|
+
});
|
|
2521
|
+
}
|
|
2522
|
+
function SQLSnippetsEditor({}) {
|
|
2523
|
+
const {
|
|
2524
|
+
sqlSnippets,
|
|
2525
|
+
setSQLSnippets
|
|
2526
|
+
} = React.useContext(DefinitionContext);
|
|
2527
|
+
const sampleSQL = `SELECT *
|
|
2528
|
+
FROM commit
|
|
2529
|
+
WHERE \${author_time_condition}`;
|
|
2530
|
+
const initialValues = React.useMemo(() => ({
|
|
2531
|
+
snippets: formList(sqlSnippets != null ? sqlSnippets : [])
|
|
2532
|
+
}), [sqlSnippets]);
|
|
2533
|
+
const form = useForm$1({
|
|
2534
|
+
initialValues
|
|
2535
|
+
});
|
|
2536
|
+
const addSnippet = () => form.addListItem("snippets", {
|
|
2537
|
+
key: randomId(),
|
|
2538
|
+
value: ""
|
|
2539
|
+
});
|
|
2540
|
+
const changed = React.useMemo(() => !_.isEqual(form.values, initialValues), [form.values, initialValues]);
|
|
2541
|
+
const submit = ({
|
|
2542
|
+
snippets
|
|
2543
|
+
}) => {
|
|
2544
|
+
setSQLSnippets(snippets);
|
|
2545
|
+
};
|
|
2546
|
+
return /* @__PURE__ */ jsxs(Group, {
|
|
2547
|
+
direction: "column",
|
|
2548
|
+
grow: true,
|
|
2549
|
+
sx: {
|
|
2550
|
+
border: "1px solid #eee"
|
|
2551
|
+
},
|
|
2552
|
+
children: [/* @__PURE__ */ jsxs(Group, {
|
|
2553
|
+
position: "left",
|
|
2554
|
+
pl: "md",
|
|
2555
|
+
py: "md",
|
|
2556
|
+
sx: {
|
|
2557
|
+
borderBottom: "1px solid #eee",
|
|
2558
|
+
background: "#efefef",
|
|
2559
|
+
flexGrow: 0
|
|
2560
|
+
},
|
|
2561
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
2562
|
+
weight: 500,
|
|
2563
|
+
children: "SQL Snippets"
|
|
2564
|
+
}), /* @__PURE__ */ jsx(ActionIcon, {
|
|
2565
|
+
type: "submit",
|
|
2566
|
+
mr: 5,
|
|
2567
|
+
variant: "filled",
|
|
2568
|
+
color: "blue",
|
|
2569
|
+
disabled: !changed,
|
|
2570
|
+
children: /* @__PURE__ */ jsx(DeviceFloppy, {
|
|
2571
|
+
size: 20
|
|
2572
|
+
})
|
|
2573
|
+
})]
|
|
2574
|
+
}), /* @__PURE__ */ jsxs(Group, {
|
|
2575
|
+
px: "md",
|
|
2576
|
+
pb: "md",
|
|
2577
|
+
children: [/* @__PURE__ */ jsx(Prism, {
|
|
2578
|
+
language: "sql",
|
|
2579
|
+
sx: {
|
|
2580
|
+
width: "100%"
|
|
2581
|
+
},
|
|
2582
|
+
noCopy: true,
|
|
2583
|
+
trim: false,
|
|
2584
|
+
colorScheme: "dark",
|
|
2585
|
+
children: `-- You may refer context data *by name*
|
|
2586
|
+
-- in SQL or VizConfig.
|
|
2587
|
+
|
|
2588
|
+
${sampleSQL}
|
|
2589
|
+
|
|
2590
|
+
-- where author_time_condition is:
|
|
2591
|
+
author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.[1].toISOString()}'
|
|
2592
|
+
`
|
|
2593
|
+
}), /* @__PURE__ */ jsx(Group, {
|
|
2594
|
+
direction: "column",
|
|
2595
|
+
sx: {
|
|
2596
|
+
width: "100%",
|
|
2597
|
+
position: "relative"
|
|
2598
|
+
},
|
|
2599
|
+
grow: true,
|
|
2600
|
+
children: /* @__PURE__ */ jsxs("form", {
|
|
2601
|
+
onSubmit: form.onSubmit(submit),
|
|
2602
|
+
children: [form.values.snippets.map((_item, index2) => /* @__PURE__ */ jsxs(Group, {
|
|
2603
|
+
direction: "column",
|
|
2604
|
+
grow: true,
|
|
2605
|
+
my: 0,
|
|
2606
|
+
p: "md",
|
|
2607
|
+
pr: 40,
|
|
2608
|
+
sx: {
|
|
2609
|
+
border: "1px solid #eee",
|
|
2610
|
+
position: "relative"
|
|
2611
|
+
},
|
|
2612
|
+
children: [/* @__PURE__ */ jsx(TextInput, __spreadValues({
|
|
2613
|
+
label: "Key",
|
|
2614
|
+
required: true
|
|
2615
|
+
}, form.getListInputProps("snippets", index2, "key"))), /* @__PURE__ */ jsx(Textarea, __spreadValues({
|
|
2616
|
+
minRows: 3,
|
|
2617
|
+
label: "Value",
|
|
2618
|
+
required: true
|
|
2619
|
+
}, form.getListInputProps("snippets", index2, "value"))), /* @__PURE__ */ jsx(ActionIcon, {
|
|
2620
|
+
color: "red",
|
|
2621
|
+
variant: "hover",
|
|
2622
|
+
onClick: () => form.removeListItem("snippets", index2),
|
|
2623
|
+
sx: {
|
|
2624
|
+
position: "absolute",
|
|
2625
|
+
top: 15,
|
|
2626
|
+
right: 5
|
|
2627
|
+
},
|
|
2628
|
+
children: /* @__PURE__ */ jsx(Trash, {
|
|
2629
|
+
size: 16
|
|
2630
|
+
})
|
|
2631
|
+
})]
|
|
2632
|
+
}, index2)), /* @__PURE__ */ jsx(Group, {
|
|
2633
|
+
position: "center",
|
|
2634
|
+
mt: "xl",
|
|
2635
|
+
grow: true,
|
|
2636
|
+
sx: {
|
|
2637
|
+
width: "40%"
|
|
2638
|
+
},
|
|
2639
|
+
mx: "auto",
|
|
2640
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
2641
|
+
variant: "default",
|
|
2642
|
+
onClick: addSnippet,
|
|
2643
|
+
children: "Add a snippet"
|
|
2644
|
+
})
|
|
2645
|
+
})]
|
|
2646
|
+
})
|
|
2647
|
+
})]
|
|
2648
|
+
})]
|
|
2649
|
+
});
|
|
2650
|
+
}
|
|
2651
|
+
function EditSQLSnippetsModal({
|
|
2652
|
+
opened,
|
|
2653
|
+
close
|
|
2654
|
+
}) {
|
|
2655
|
+
const {
|
|
2656
|
+
freezeLayout
|
|
2657
|
+
} = React.useContext(LayoutStateContext);
|
|
2658
|
+
React.useEffect(() => {
|
|
2659
|
+
freezeLayout(opened);
|
|
2660
|
+
}, [opened]);
|
|
2661
|
+
return /* @__PURE__ */ jsx(Modal, {
|
|
2662
|
+
size: "96vw",
|
|
2663
|
+
overflow: "inside",
|
|
2664
|
+
opened,
|
|
2665
|
+
onClose: close,
|
|
2666
|
+
title: "SQL Snippets",
|
|
2667
|
+
trapFocus: true,
|
|
2668
|
+
onDragStart: (e) => {
|
|
2669
|
+
e.stopPropagation();
|
|
2670
|
+
},
|
|
2671
|
+
children: /* @__PURE__ */ jsx(AppShell, {
|
|
2672
|
+
sx: {
|
|
2673
|
+
height: "90vh",
|
|
2674
|
+
maxHeight: "calc(100vh - 185px)",
|
|
2675
|
+
".mantine-AppShell-body": {
|
|
2676
|
+
height: "100%"
|
|
2677
|
+
},
|
|
2678
|
+
main: {
|
|
2679
|
+
height: "100%",
|
|
2680
|
+
width: "100%",
|
|
2681
|
+
padding: 0,
|
|
2682
|
+
margin: 0
|
|
2683
|
+
}
|
|
2684
|
+
},
|
|
2685
|
+
padding: "md",
|
|
2686
|
+
children: /* @__PURE__ */ jsxs(Group, {
|
|
2687
|
+
direction: "row",
|
|
2688
|
+
position: "apart",
|
|
2689
|
+
grow: true,
|
|
2690
|
+
align: "stretch",
|
|
2691
|
+
noWrap: true,
|
|
2692
|
+
children: [/* @__PURE__ */ jsx(SQLSnippetsEditor, {}), /* @__PURE__ */ jsx(ContextInfo, {})]
|
|
2693
|
+
})
|
|
2694
|
+
})
|
|
2695
|
+
});
|
|
2696
|
+
}
|
|
2697
|
+
function DashboardActions({
|
|
2698
|
+
mode,
|
|
2699
|
+
setMode,
|
|
2700
|
+
hasChanges,
|
|
2701
|
+
addPanel,
|
|
2702
|
+
saveChanges
|
|
2703
|
+
}) {
|
|
2704
|
+
const [dataSourcesOpened, setDataSourcesOpened] = React.useState(false);
|
|
2705
|
+
const openDataSources = () => setDataSourcesOpened(true);
|
|
2706
|
+
const closeDataSources = () => setDataSourcesOpened(false);
|
|
2707
|
+
const [sqlSnippetsOpened, setSQLSnippetsOpened] = React.useState(false);
|
|
2708
|
+
const openSQLSnippets = () => setSQLSnippetsOpened(true);
|
|
2709
|
+
const closeSQLSnippets = () => setSQLSnippetsOpened(false);
|
|
2710
|
+
const inEditMode = mode === DashboardMode.Edit;
|
|
2711
|
+
return /* @__PURE__ */ jsxs(Group, {
|
|
2712
|
+
position: "apart",
|
|
2713
|
+
pt: "sm",
|
|
2714
|
+
pb: "xs",
|
|
2715
|
+
children: [/* @__PURE__ */ jsx(Group, {
|
|
2716
|
+
position: "left",
|
|
2717
|
+
children: /* @__PURE__ */ jsx(ModeToggler, {
|
|
2718
|
+
mode,
|
|
2719
|
+
setMode
|
|
2720
|
+
})
|
|
2721
|
+
}), inEditMode && /* @__PURE__ */ jsxs(Fragment, {
|
|
2722
|
+
children: [/* @__PURE__ */ jsxs(Group, {
|
|
2723
|
+
position: "right",
|
|
2724
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
2725
|
+
variant: "default",
|
|
2726
|
+
size: "sm",
|
|
2727
|
+
onClick: addPanel,
|
|
2728
|
+
leftIcon: /* @__PURE__ */ jsx(PlaylistAdd, {
|
|
2729
|
+
size: 20
|
|
2730
|
+
}),
|
|
2731
|
+
children: "Add a Panel"
|
|
2732
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
2733
|
+
variant: "default",
|
|
2734
|
+
size: "sm",
|
|
2735
|
+
onClick: openSQLSnippets,
|
|
2736
|
+
leftIcon: /* @__PURE__ */ jsx(ClipboardText, {
|
|
2737
|
+
size: 20
|
|
2738
|
+
}),
|
|
2739
|
+
children: "SQL Snippets"
|
|
2740
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
2741
|
+
variant: "default",
|
|
2742
|
+
size: "sm",
|
|
2743
|
+
onClick: openDataSources,
|
|
2744
|
+
leftIcon: /* @__PURE__ */ jsx(Database, {
|
|
2745
|
+
size: 20
|
|
2746
|
+
}),
|
|
2747
|
+
children: "Data Sources"
|
|
2748
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
2749
|
+
variant: "default",
|
|
2750
|
+
size: "sm",
|
|
2751
|
+
onClick: saveChanges,
|
|
2752
|
+
disabled: !hasChanges,
|
|
2753
|
+
leftIcon: /* @__PURE__ */ jsx(DeviceFloppy, {
|
|
2754
|
+
size: 20
|
|
2755
|
+
}),
|
|
2756
|
+
children: "Save Changes"
|
|
2757
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
2758
|
+
color: "red",
|
|
2759
|
+
size: "sm",
|
|
2760
|
+
disabled: !hasChanges,
|
|
2761
|
+
leftIcon: /* @__PURE__ */ jsx(Recycle, {
|
|
2762
|
+
size: 20
|
|
2763
|
+
}),
|
|
2764
|
+
children: "Revert Changes"
|
|
2765
|
+
})]
|
|
2766
|
+
}), /* @__PURE__ */ jsx(EditDataSourcesModal, {
|
|
2767
|
+
opened: dataSourcesOpened,
|
|
2768
|
+
close: closeDataSources
|
|
2769
|
+
}), /* @__PURE__ */ jsx(EditSQLSnippetsModal, {
|
|
2770
|
+
opened: sqlSnippetsOpened,
|
|
2771
|
+
close: closeSQLSnippets
|
|
2772
|
+
})]
|
|
2773
|
+
}), !inEditMode && /* @__PURE__ */ jsx(Button, {
|
|
2774
|
+
variant: "default",
|
|
2775
|
+
size: "sm",
|
|
2776
|
+
disabled: true,
|
|
2777
|
+
leftIcon: /* @__PURE__ */ jsx(Share, {
|
|
2778
|
+
size: 20
|
|
2779
|
+
}),
|
|
2780
|
+
children: "Share"
|
|
2781
|
+
})]
|
|
2782
|
+
});
|
|
2783
|
+
}
|
|
2784
|
+
function Dashboard({
|
|
2785
|
+
context,
|
|
2786
|
+
dashboard,
|
|
2163
2787
|
update,
|
|
2164
2788
|
className = "dashboard"
|
|
2165
2789
|
}) {
|
|
2166
2790
|
const [layoutFrozen, freezeLayout] = React.useState(false);
|
|
2167
2791
|
const [breakpoint, setBreakpoint] = React.useState();
|
|
2168
2792
|
const [localCols, setLocalCols] = React.useState();
|
|
2169
|
-
const [panels, setPanels] =
|
|
2793
|
+
const [panels, setPanels] = React.useState(dashboard.panels);
|
|
2170
2794
|
const [sqlSnippets, setSQLSnippets] = React.useState(dashboard.definition.sqlSnippets);
|
|
2795
|
+
const [dataSources, setDataSources] = React.useState(dashboard.definition.dataSources);
|
|
2171
2796
|
const [mode, setMode] = React.useState(DashboardMode.Edit);
|
|
2172
2797
|
const hasChanges = React.useMemo(() => {
|
|
2173
2798
|
const cleanJSON = (v) => JSON.parse(JSON.stringify(v));
|
|
@@ -2175,8 +2800,11 @@ function Dashboard({
|
|
|
2175
2800
|
if (!panelsEqual) {
|
|
2176
2801
|
return true;
|
|
2177
2802
|
}
|
|
2178
|
-
|
|
2179
|
-
|
|
2803
|
+
if (!_.isEqual(sqlSnippets, dashboard.definition.sqlSnippets)) {
|
|
2804
|
+
return true;
|
|
2805
|
+
}
|
|
2806
|
+
return !_.isEqual(dataSources, dashboard.definition.dataSources);
|
|
2807
|
+
}, [dashboard, panels, sqlSnippets, dataSources]);
|
|
2180
2808
|
const saveDashboardChanges = async () => {
|
|
2181
2809
|
const d = _.merge({}, dashboard, {
|
|
2182
2810
|
panels
|
|
@@ -2199,51 +2827,126 @@ function Dashboard({
|
|
|
2199
2827
|
},
|
|
2200
2828
|
title: `New Panel - ${id}`,
|
|
2201
2829
|
description: "description goes here",
|
|
2202
|
-
|
|
2830
|
+
dataSourceID: "",
|
|
2203
2831
|
viz: {
|
|
2204
2832
|
type: "table",
|
|
2205
2833
|
conf: {}
|
|
2206
2834
|
}
|
|
2207
2835
|
};
|
|
2208
|
-
setPanels
|
|
2836
|
+
setPanels((prevs) => [...prevs, newItem]);
|
|
2209
2837
|
};
|
|
2210
2838
|
const removePanelByID = (id) => {
|
|
2211
2839
|
const index2 = panels.findIndex((p2) => p2.id === id);
|
|
2212
|
-
setPanels
|
|
2840
|
+
setPanels((prevs) => {
|
|
2841
|
+
prevs.splice(index2, 1);
|
|
2842
|
+
return prevs;
|
|
2843
|
+
});
|
|
2213
2844
|
};
|
|
2214
2845
|
const inEditMode = mode === DashboardMode.Edit;
|
|
2215
2846
|
const definitions = React.useMemo(() => ({
|
|
2216
2847
|
sqlSnippets,
|
|
2217
|
-
setSQLSnippets
|
|
2218
|
-
|
|
2219
|
-
|
|
2848
|
+
setSQLSnippets,
|
|
2849
|
+
dataSources,
|
|
2850
|
+
setDataSources
|
|
2851
|
+
}), [sqlSnippets, setSQLSnippets, dataSources, setDataSources]);
|
|
2852
|
+
return /* @__PURE__ */ jsx(ContextInfoContext.Provider, {
|
|
2853
|
+
value: context,
|
|
2854
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
2855
|
+
className,
|
|
2856
|
+
children: /* @__PURE__ */ jsx(DefinitionContext.Provider, {
|
|
2857
|
+
value: definitions,
|
|
2858
|
+
children: /* @__PURE__ */ jsxs(LayoutStateContext.Provider, {
|
|
2859
|
+
value: {
|
|
2860
|
+
layoutFrozen,
|
|
2861
|
+
freezeLayout,
|
|
2862
|
+
mode,
|
|
2863
|
+
inEditMode
|
|
2864
|
+
},
|
|
2865
|
+
children: [/* @__PURE__ */ jsx(DashboardActions, {
|
|
2866
|
+
mode,
|
|
2867
|
+
setMode,
|
|
2868
|
+
hasChanges,
|
|
2869
|
+
addPanel,
|
|
2870
|
+
saveChanges: saveDashboardChanges
|
|
2871
|
+
}), /* @__PURE__ */ jsx(DashboardLayout, {
|
|
2872
|
+
panels,
|
|
2873
|
+
setPanels,
|
|
2874
|
+
isDraggable: inEditMode && !layoutFrozen,
|
|
2875
|
+
isResizable: inEditMode && !layoutFrozen,
|
|
2876
|
+
onRemoveItem: removePanelByID,
|
|
2877
|
+
setLocalCols,
|
|
2878
|
+
setBreakpoint
|
|
2879
|
+
})]
|
|
2880
|
+
})
|
|
2881
|
+
})
|
|
2882
|
+
})
|
|
2883
|
+
});
|
|
2884
|
+
}
|
|
2885
|
+
const ResponsiveReactGridLayout = WidthProvider(Responsive);
|
|
2886
|
+
function ReadOnlyDashboardLayout({
|
|
2887
|
+
panels,
|
|
2888
|
+
className = "layout",
|
|
2889
|
+
cols = {
|
|
2890
|
+
lg: 12,
|
|
2891
|
+
md: 10,
|
|
2892
|
+
sm: 8,
|
|
2893
|
+
xs: 6,
|
|
2894
|
+
xxs: 4
|
|
2895
|
+
},
|
|
2896
|
+
rowHeight = 10
|
|
2897
|
+
}) {
|
|
2898
|
+
return /* @__PURE__ */ jsx(ResponsiveReactGridLayout, {
|
|
2220
2899
|
className,
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2900
|
+
cols,
|
|
2901
|
+
rowHeight,
|
|
2902
|
+
isDraggable: false,
|
|
2903
|
+
isResizable: false,
|
|
2904
|
+
children: panels.map((_a) => {
|
|
2905
|
+
var _b = _a, {
|
|
2906
|
+
id
|
|
2907
|
+
} = _b, rest = __objRest(_b, [
|
|
2908
|
+
"id"
|
|
2909
|
+
]);
|
|
2910
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2911
|
+
"data-grid": rest.layout,
|
|
2912
|
+
children: /* @__PURE__ */ jsx(Panel, __spreadValues({
|
|
2913
|
+
id
|
|
2914
|
+
}, rest))
|
|
2915
|
+
}, id);
|
|
2916
|
+
})
|
|
2917
|
+
});
|
|
2918
|
+
}
|
|
2919
|
+
function ReadOnlyDashboard({
|
|
2920
|
+
context,
|
|
2921
|
+
dashboard,
|
|
2922
|
+
className = "dashboard"
|
|
2923
|
+
}) {
|
|
2924
|
+
const definition = React.useMemo(() => __spreadProps(__spreadValues({}, dashboard.definition), {
|
|
2925
|
+
setSQLSnippets: () => {
|
|
2926
|
+
},
|
|
2927
|
+
setDataSources: () => {
|
|
2928
|
+
}
|
|
2929
|
+
}), [dashboard]);
|
|
2930
|
+
return /* @__PURE__ */ jsx(ContextInfoContext.Provider, {
|
|
2931
|
+
value: context,
|
|
2932
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
2933
|
+
className,
|
|
2934
|
+
children: /* @__PURE__ */ jsx(DefinitionContext.Provider, {
|
|
2935
|
+
value: definition,
|
|
2936
|
+
children: /* @__PURE__ */ jsx(LayoutStateContext.Provider, {
|
|
2937
|
+
value: {
|
|
2938
|
+
layoutFrozen: true,
|
|
2939
|
+
freezeLayout: () => {
|
|
2940
|
+
},
|
|
2941
|
+
mode: DashboardMode.Use,
|
|
2942
|
+
inEditMode: false
|
|
2943
|
+
},
|
|
2944
|
+
children: /* @__PURE__ */ jsx(ReadOnlyDashboardLayout, {
|
|
2945
|
+
panels: dashboard.panels
|
|
2946
|
+
})
|
|
2947
|
+
})
|
|
2245
2948
|
})
|
|
2246
2949
|
})
|
|
2247
2950
|
});
|
|
2248
2951
|
}
|
|
2249
|
-
export { ContextInfoContext, Dashboard, DashboardLayout, DashboardMode, DefinitionContext, LayoutStateContext, Panel, PanelContext, initialContextInfoContext };
|
|
2952
|
+
export { ContextInfoContext, Dashboard, DashboardLayout, DashboardMode, DefinitionContext, LayoutStateContext, Panel, PanelContext, ReadOnlyDashboard, initialContextInfoContext };
|