@devtable/dashboard 0.4.0 → 1.0.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 +1066 -509
- 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/main/main.d.ts +3 -1
- package/dist/main/read-only.d.ts +3 -1
- package/dist/panel/index.d.ts +1 -1
- 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 { LoadingOverlay, Table, Group, Text, Select, Textarea, ActionIcon, TextInput, Button, useMantineTheme, ColorSwatch, Switch, Slider, JsonInput, Divider, Box, Modal, AppShell, Tabs, Tooltip, Menu, Container, 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, Trash, InfoCircle, 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: "",
|
|
@@ -140,6 +149,15 @@ const initialContext$1 = {
|
|
|
140
149
|
}
|
|
141
150
|
};
|
|
142
151
|
const PanelContext = React.createContext(initialContext$1);
|
|
152
|
+
const initialContext = {
|
|
153
|
+
sqlSnippets: [],
|
|
154
|
+
setSQLSnippets: () => {
|
|
155
|
+
},
|
|
156
|
+
dataSources: [],
|
|
157
|
+
setDataSources: () => {
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
const DefinitionContext = React.createContext(initialContext);
|
|
143
161
|
var jsxRuntime = { exports: {} };
|
|
144
162
|
var reactJsxRuntime_production_min = {};
|
|
145
163
|
/**
|
|
@@ -173,6 +191,152 @@ reactJsxRuntime_production_min.jsxs = q;
|
|
|
173
191
|
const jsx = jsxRuntime.exports.jsx;
|
|
174
192
|
const jsxs = jsxRuntime.exports.jsxs;
|
|
175
193
|
const Fragment = jsxRuntime.exports.Fragment;
|
|
194
|
+
function DataPreview({
|
|
195
|
+
id
|
|
196
|
+
}) {
|
|
197
|
+
const definitions = React.useContext(DefinitionContext);
|
|
198
|
+
const contextInfo = React.useContext(ContextInfoContext);
|
|
199
|
+
const dataSource = React.useMemo(() => {
|
|
200
|
+
return definitions.dataSources.find((d) => d.id === id);
|
|
201
|
+
}, [definitions.dataSources, id]);
|
|
202
|
+
const {
|
|
203
|
+
data = [],
|
|
204
|
+
loading
|
|
205
|
+
} = useRequest(queryBySQL({
|
|
206
|
+
context: contextInfo,
|
|
207
|
+
definitions,
|
|
208
|
+
title: id,
|
|
209
|
+
dataSource
|
|
210
|
+
}), {
|
|
211
|
+
refreshDeps: [contextInfo, definitions, dataSource]
|
|
212
|
+
});
|
|
213
|
+
if (loading) {
|
|
214
|
+
return /* @__PURE__ */ jsx(LoadingOverlay, {
|
|
215
|
+
visible: loading
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
if (data.length === 0) {
|
|
219
|
+
return /* @__PURE__ */ jsx(Table, {});
|
|
220
|
+
}
|
|
221
|
+
return /* @__PURE__ */ jsxs(Group, {
|
|
222
|
+
my: "xl",
|
|
223
|
+
direction: "column",
|
|
224
|
+
grow: true,
|
|
225
|
+
sx: {
|
|
226
|
+
border: "1px solid #eee"
|
|
227
|
+
},
|
|
228
|
+
children: [/* @__PURE__ */ jsx(Group, {
|
|
229
|
+
position: "left",
|
|
230
|
+
py: "md",
|
|
231
|
+
pl: "md",
|
|
232
|
+
sx: {
|
|
233
|
+
borderBottom: "1px solid #eee",
|
|
234
|
+
background: "#efefef"
|
|
235
|
+
},
|
|
236
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
237
|
+
weight: 500,
|
|
238
|
+
children: "Preview Data"
|
|
239
|
+
})
|
|
240
|
+
}), /* @__PURE__ */ jsxs(Table, {
|
|
241
|
+
children: [/* @__PURE__ */ jsx("thead", {
|
|
242
|
+
children: /* @__PURE__ */ jsx("tr", {
|
|
243
|
+
children: Object.keys(data == null ? void 0 : data[0]).map((label) => /* @__PURE__ */ jsx("th", {
|
|
244
|
+
children: label
|
|
245
|
+
}, label))
|
|
246
|
+
})
|
|
247
|
+
}), /* @__PURE__ */ jsx("tbody", {
|
|
248
|
+
children: data.map((row, index2) => /* @__PURE__ */ jsx("tr", {
|
|
249
|
+
children: Object.values(row).map((v, i) => /* @__PURE__ */ jsx("td", {
|
|
250
|
+
children: /* @__PURE__ */ jsx(Group, {
|
|
251
|
+
sx: {
|
|
252
|
+
"&, .mantine-Text-root": {
|
|
253
|
+
fontFamily: "monospace"
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
257
|
+
children: v
|
|
258
|
+
})
|
|
259
|
+
})
|
|
260
|
+
}, `${v}--${i}`))
|
|
261
|
+
}, `row-${index2}`))
|
|
262
|
+
})]
|
|
263
|
+
})]
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
function PickDataSource({}) {
|
|
267
|
+
const {
|
|
268
|
+
dataSources
|
|
269
|
+
} = React.useContext(DefinitionContext);
|
|
270
|
+
const {
|
|
271
|
+
dataSourceID,
|
|
272
|
+
setDataSourceID,
|
|
273
|
+
data,
|
|
274
|
+
loading
|
|
275
|
+
} = React.useContext(PanelContext);
|
|
276
|
+
const options = React.useMemo(() => {
|
|
277
|
+
return dataSources.map((d) => ({
|
|
278
|
+
value: d.id,
|
|
279
|
+
label: d.id
|
|
280
|
+
}));
|
|
281
|
+
}, [dataSources]);
|
|
282
|
+
return /* @__PURE__ */ jsxs(Group, {
|
|
283
|
+
direction: "column",
|
|
284
|
+
grow: true,
|
|
285
|
+
noWrap: true,
|
|
286
|
+
children: [/* @__PURE__ */ jsxs(Group, {
|
|
287
|
+
position: "left",
|
|
288
|
+
sx: {
|
|
289
|
+
maxWidth: "600px",
|
|
290
|
+
alignItems: "baseline"
|
|
291
|
+
},
|
|
292
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
293
|
+
children: "Select a Data Source"
|
|
294
|
+
}), /* @__PURE__ */ jsx(Select, {
|
|
295
|
+
data: options,
|
|
296
|
+
value: dataSourceID,
|
|
297
|
+
onChange: setDataSourceID,
|
|
298
|
+
allowDeselect: false,
|
|
299
|
+
clearable: false,
|
|
300
|
+
sx: {
|
|
301
|
+
flexGrow: 1
|
|
302
|
+
}
|
|
303
|
+
})]
|
|
304
|
+
}), /* @__PURE__ */ jsx(DataPreview, {
|
|
305
|
+
id: dataSourceID
|
|
306
|
+
})]
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
function EditDescription() {
|
|
310
|
+
const {
|
|
311
|
+
description,
|
|
312
|
+
setDescription
|
|
313
|
+
} = React.useContext(PanelContext);
|
|
314
|
+
const [localDesc, setLocalDesc] = useInputState(description);
|
|
315
|
+
const changed = description !== localDesc;
|
|
316
|
+
const submit = React.useCallback(() => {
|
|
317
|
+
if (!changed) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
setDescription(localDesc);
|
|
321
|
+
}, [changed, localDesc]);
|
|
322
|
+
return /* @__PURE__ */ jsx(Textarea, {
|
|
323
|
+
label: "Panel Description",
|
|
324
|
+
value: localDesc,
|
|
325
|
+
onChange: setLocalDesc,
|
|
326
|
+
minRows: 2,
|
|
327
|
+
rightSection: /* @__PURE__ */ jsx(ActionIcon, {
|
|
328
|
+
disabled: !changed,
|
|
329
|
+
onClick: submit,
|
|
330
|
+
sx: {
|
|
331
|
+
alignSelf: "flex-start",
|
|
332
|
+
marginTop: "4px"
|
|
333
|
+
},
|
|
334
|
+
children: /* @__PURE__ */ jsx(DeviceFloppy, {
|
|
335
|
+
size: 20
|
|
336
|
+
})
|
|
337
|
+
})
|
|
338
|
+
});
|
|
339
|
+
}
|
|
176
340
|
class ErrorBoundary extends React.Component {
|
|
177
341
|
constructor(props) {
|
|
178
342
|
super(props);
|
|
@@ -244,8 +408,8 @@ function Sunbrust({
|
|
|
244
408
|
}
|
|
245
409
|
}
|
|
246
410
|
}
|
|
247
|
-
}), []);
|
|
248
|
-
const option = _.merge(defaultOption$1, labelOption, restConf, {
|
|
411
|
+
}), [max]);
|
|
412
|
+
const option = _.merge({}, defaultOption$1, labelOption, restConf, {
|
|
249
413
|
series: {
|
|
250
414
|
data: chartData
|
|
251
415
|
}
|
|
@@ -560,7 +724,7 @@ function VizBar3D({
|
|
|
560
724
|
}
|
|
561
725
|
});
|
|
562
726
|
}
|
|
563
|
-
var index$
|
|
727
|
+
var index$2 = "";
|
|
564
728
|
function renderViz(width, height, data, viz) {
|
|
565
729
|
const props = {
|
|
566
730
|
width,
|
|
@@ -613,328 +777,90 @@ function Viz({
|
|
|
613
777
|
}), !empty && renderViz(width, height, data, viz)]
|
|
614
778
|
});
|
|
615
779
|
}
|
|
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({}) {
|
|
780
|
+
function PreviewViz({}) {
|
|
650
781
|
const {
|
|
651
|
-
|
|
652
|
-
|
|
782
|
+
data,
|
|
783
|
+
loading,
|
|
784
|
+
viz
|
|
653
785
|
} = React.useContext(PanelContext);
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
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
|
-
})]
|
|
786
|
+
return /* @__PURE__ */ jsx(ErrorBoundary, {
|
|
787
|
+
children: /* @__PURE__ */ jsx(Viz, {
|
|
788
|
+
viz,
|
|
789
|
+
data,
|
|
790
|
+
loading
|
|
791
|
+
})
|
|
693
792
|
});
|
|
694
793
|
}
|
|
695
|
-
|
|
696
|
-
function QueryResult({}) {
|
|
794
|
+
function EditTitle() {
|
|
697
795
|
const {
|
|
698
|
-
|
|
796
|
+
title,
|
|
797
|
+
setTitle
|
|
699
798
|
} = React.useContext(PanelContext);
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
799
|
+
const [localTitle, setLocalTitle] = useInputState(title);
|
|
800
|
+
const changed = title !== localTitle;
|
|
801
|
+
const submit = React.useCallback(() => {
|
|
802
|
+
if (!changed) {
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
setTitle(localTitle);
|
|
806
|
+
}, [changed, localTitle]);
|
|
807
|
+
return /* @__PURE__ */ jsx(TextInput, {
|
|
808
|
+
label: "Panel Title",
|
|
809
|
+
value: localTitle,
|
|
810
|
+
onChange: setLocalTitle,
|
|
811
|
+
rightSection: /* @__PURE__ */ jsx(ActionIcon, {
|
|
812
|
+
disabled: !changed,
|
|
813
|
+
onClick: submit,
|
|
814
|
+
children: /* @__PURE__ */ jsx(DeviceFloppy, {
|
|
815
|
+
size: 20
|
|
816
|
+
})
|
|
817
|
+
})
|
|
713
818
|
});
|
|
714
819
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
}
|
|
719
|
-
};
|
|
720
|
-
const DefinitionContext = React.createContext(initialContext);
|
|
721
|
-
function SQLSnippetsForm({
|
|
722
|
-
sqlSnippets,
|
|
723
|
-
setSQLSnippets
|
|
820
|
+
function VizBar3DPanel({
|
|
821
|
+
conf,
|
|
822
|
+
setConf
|
|
724
823
|
}) {
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
824
|
+
const defaultValues = _.assign({}, {
|
|
825
|
+
"x_axis_data_key": "x",
|
|
826
|
+
"y_axis_data_key": "y",
|
|
827
|
+
"z_axis_data_key": "z",
|
|
828
|
+
"xAxis3D": {
|
|
829
|
+
"type": "value",
|
|
830
|
+
"name": "X Axis Name"
|
|
831
|
+
},
|
|
832
|
+
"yAxis3D": {
|
|
833
|
+
"type": "value",
|
|
834
|
+
"name": "Y Axis Name"
|
|
835
|
+
},
|
|
836
|
+
"zAxis3D": {
|
|
837
|
+
"type": "value",
|
|
838
|
+
"name": "Z Axis Name"
|
|
839
|
+
}
|
|
840
|
+
}, conf);
|
|
841
|
+
const {
|
|
842
|
+
control,
|
|
843
|
+
handleSubmit,
|
|
844
|
+
formState
|
|
845
|
+
} = useForm({
|
|
846
|
+
defaultValues
|
|
734
847
|
});
|
|
735
|
-
const changed = React.useMemo(() => !_.isEqual(form.values, initialValues), [form.values, initialValues]);
|
|
736
|
-
const submit = ({
|
|
737
|
-
snippets
|
|
738
|
-
}) => {
|
|
739
|
-
setSQLSnippets(snippets);
|
|
740
|
-
};
|
|
741
848
|
return /* @__PURE__ */ jsx(Group, {
|
|
742
849
|
direction: "column",
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
position: "relative"
|
|
746
|
-
},
|
|
850
|
+
mt: "md",
|
|
851
|
+
spacing: "xs",
|
|
747
852
|
grow: true,
|
|
748
853
|
children: /* @__PURE__ */ jsxs("form", {
|
|
749
|
-
onSubmit:
|
|
750
|
-
children: [
|
|
751
|
-
|
|
854
|
+
onSubmit: handleSubmit(setConf),
|
|
855
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
856
|
+
children: "X Axis"
|
|
857
|
+
}), /* @__PURE__ */ jsxs(Group, {
|
|
858
|
+
position: "apart",
|
|
752
859
|
grow: true,
|
|
753
|
-
my: 0,
|
|
754
860
|
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
861
|
sx: {
|
|
785
|
-
|
|
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
|
|
921
|
-
});
|
|
922
|
-
return /* @__PURE__ */ jsx(Group, {
|
|
923
|
-
direction: "column",
|
|
924
|
-
mt: "md",
|
|
925
|
-
spacing: "xs",
|
|
926
|
-
grow: true,
|
|
927
|
-
children: /* @__PURE__ */ jsxs("form", {
|
|
928
|
-
onSubmit: handleSubmit(setConf),
|
|
929
|
-
children: [/* @__PURE__ */ jsx(Text, {
|
|
930
|
-
children: "X Axis"
|
|
931
|
-
}), /* @__PURE__ */ jsxs(Group, {
|
|
932
|
-
position: "apart",
|
|
933
|
-
grow: true,
|
|
934
|
-
p: "md",
|
|
935
|
-
sx: {
|
|
936
|
-
position: "relative",
|
|
937
|
-
border: "1px solid #eee"
|
|
862
|
+
position: "relative",
|
|
863
|
+
border: "1px solid #eee"
|
|
938
864
|
},
|
|
939
865
|
children: [/* @__PURE__ */ jsx(Controller, {
|
|
940
866
|
name: "x_axis_data_key",
|
|
@@ -1119,7 +1045,7 @@ function VizLineBarChartPanel({
|
|
|
1119
1045
|
const initialValues = React.useMemo(() => __spreadValues({
|
|
1120
1046
|
series: formList(series != null ? series : [])
|
|
1121
1047
|
}, restConf), [series, restConf]);
|
|
1122
|
-
const form = useForm({
|
|
1048
|
+
const form = useForm$1({
|
|
1123
1049
|
initialValues
|
|
1124
1050
|
});
|
|
1125
1051
|
const addSeries = () => form.addListItem("series", {
|
|
@@ -1231,7 +1157,7 @@ function SunburstPanel({
|
|
|
1231
1157
|
},
|
|
1232
1158
|
setConf
|
|
1233
1159
|
}) {
|
|
1234
|
-
const form = useForm({
|
|
1160
|
+
const form = useForm$1({
|
|
1235
1161
|
initialValues: {
|
|
1236
1162
|
label_field,
|
|
1237
1163
|
value_field
|
|
@@ -1318,7 +1244,7 @@ function VizTablePanel(_a) {
|
|
|
1318
1244
|
]), {
|
|
1319
1245
|
setConf
|
|
1320
1246
|
} = _b;
|
|
1321
|
-
const form = useForm({
|
|
1247
|
+
const form = useForm$1({
|
|
1322
1248
|
initialValues: __spreadValues({
|
|
1323
1249
|
id_field: "id",
|
|
1324
1250
|
use_raw_columns: true,
|
|
@@ -1593,7 +1519,7 @@ function VizTextPanel({
|
|
|
1593
1519
|
setConf
|
|
1594
1520
|
}) {
|
|
1595
1521
|
var _a;
|
|
1596
|
-
const form = useForm({
|
|
1522
|
+
const form = useForm$1({
|
|
1597
1523
|
initialValues: {
|
|
1598
1524
|
paragraphs: formList((_a = conf.paragraphs) != null ? _a : sampleParagraphs)
|
|
1599
1525
|
}
|
|
@@ -1788,9 +1714,34 @@ function EditVizConf() {
|
|
|
1788
1714
|
}
|
|
1789
1715
|
function VizConfig({}) {
|
|
1790
1716
|
return /* @__PURE__ */ jsxs(Group, {
|
|
1717
|
+
direction: "row",
|
|
1791
1718
|
grow: true,
|
|
1792
|
-
|
|
1793
|
-
|
|
1719
|
+
noWrap: true,
|
|
1720
|
+
align: "stretch",
|
|
1721
|
+
sx: {
|
|
1722
|
+
height: "100%"
|
|
1723
|
+
},
|
|
1724
|
+
children: [/* @__PURE__ */ jsxs(Group, {
|
|
1725
|
+
grow: true,
|
|
1726
|
+
direction: "column",
|
|
1727
|
+
sx: {
|
|
1728
|
+
width: "40%",
|
|
1729
|
+
flexShrink: 0,
|
|
1730
|
+
flexGrow: 0
|
|
1731
|
+
},
|
|
1732
|
+
children: [/* @__PURE__ */ jsx(EditTitle, {}), /* @__PURE__ */ jsx(EditDescription, {}), /* @__PURE__ */ jsx(Divider, {
|
|
1733
|
+
sx: {
|
|
1734
|
+
flexGrow: 0
|
|
1735
|
+
}
|
|
1736
|
+
}), /* @__PURE__ */ jsx(EditVizConf, {})]
|
|
1737
|
+
}), /* @__PURE__ */ jsx(Box, {
|
|
1738
|
+
sx: {
|
|
1739
|
+
height: "100%",
|
|
1740
|
+
flexGrow: 1,
|
|
1741
|
+
maxWidth: "60%"
|
|
1742
|
+
},
|
|
1743
|
+
children: /* @__PURE__ */ jsx(PreviewViz, {})
|
|
1744
|
+
})]
|
|
1794
1745
|
});
|
|
1795
1746
|
}
|
|
1796
1747
|
function PanelSettingsModal({
|
|
@@ -1832,40 +1783,17 @@ function PanelSettingsModal({
|
|
|
1832
1783
|
}
|
|
1833
1784
|
},
|
|
1834
1785
|
padding: "md",
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
}), /* @__PURE__ */ jsx(Tabs.Tab, {
|
|
1847
|
-
label: "SQL Snippets",
|
|
1848
|
-
children: /* @__PURE__ */ jsx(SQLSnippetsTab, {})
|
|
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
|
-
})
|
|
1786
|
+
children: /* @__PURE__ */ jsxs(Tabs, {
|
|
1787
|
+
initialTab: 2,
|
|
1788
|
+
children: [/* @__PURE__ */ jsxs(Tabs.Tab, {
|
|
1789
|
+
label: "Data Source",
|
|
1790
|
+
children: [/* @__PURE__ */ jsx(LoadingOverlay, {
|
|
1791
|
+
visible: loading
|
|
1792
|
+
}), /* @__PURE__ */ jsx(PickDataSource, {})]
|
|
1793
|
+
}), /* @__PURE__ */ jsx(Tabs.Tab, {
|
|
1794
|
+
label: "Viz Config",
|
|
1795
|
+
children: /* @__PURE__ */ jsx(VizConfig, {})
|
|
1796
|
+
})]
|
|
1869
1797
|
})
|
|
1870
1798
|
})
|
|
1871
1799
|
});
|
|
@@ -1947,7 +1875,7 @@ function PanelTitleBar({}) {
|
|
|
1947
1875
|
var index$1 = "";
|
|
1948
1876
|
function Panel({
|
|
1949
1877
|
viz: initialViz,
|
|
1950
|
-
|
|
1878
|
+
dataSourceID: initialDataSourceID,
|
|
1951
1879
|
title: initialTitle,
|
|
1952
1880
|
description: initialDesc,
|
|
1953
1881
|
update,
|
|
@@ -1958,24 +1886,35 @@ function Panel({
|
|
|
1958
1886
|
const definitions = React.useContext(DefinitionContext);
|
|
1959
1887
|
const [title, setTitle] = React.useState(initialTitle);
|
|
1960
1888
|
const [description, setDescription] = React.useState(initialDesc);
|
|
1961
|
-
const [
|
|
1889
|
+
const [dataSourceID, setDataSourceID] = React.useState(initialDataSourceID);
|
|
1962
1890
|
const [viz, setViz] = React.useState(initialViz);
|
|
1891
|
+
const dataSource = React.useMemo(() => {
|
|
1892
|
+
if (!dataSourceID) {
|
|
1893
|
+
return void 0;
|
|
1894
|
+
}
|
|
1895
|
+
return definitions.dataSources.find((d) => d.id === dataSourceID);
|
|
1896
|
+
}, [dataSourceID, definitions.dataSources]);
|
|
1963
1897
|
React.useEffect(() => {
|
|
1964
1898
|
update == null ? void 0 : update({
|
|
1965
1899
|
id,
|
|
1966
1900
|
layout,
|
|
1967
1901
|
title,
|
|
1968
1902
|
description,
|
|
1969
|
-
|
|
1903
|
+
dataSourceID,
|
|
1970
1904
|
viz
|
|
1971
1905
|
});
|
|
1972
|
-
}, [title, description,
|
|
1906
|
+
}, [title, description, dataSource, viz, id, layout, dataSourceID]);
|
|
1973
1907
|
const {
|
|
1974
1908
|
data = [],
|
|
1975
1909
|
loading,
|
|
1976
1910
|
refresh
|
|
1977
|
-
} = useRequest(queryBySQL(
|
|
1978
|
-
|
|
1911
|
+
} = useRequest(queryBySQL({
|
|
1912
|
+
context: contextInfo,
|
|
1913
|
+
definitions,
|
|
1914
|
+
title,
|
|
1915
|
+
dataSource
|
|
1916
|
+
}), {
|
|
1917
|
+
refreshDeps: [contextInfo, definitions, dataSource]
|
|
1979
1918
|
});
|
|
1980
1919
|
const refreshData = refresh;
|
|
1981
1920
|
return /* @__PURE__ */ jsx(PanelContext.Provider, {
|
|
@@ -1986,8 +1925,8 @@ function Panel({
|
|
|
1986
1925
|
setTitle,
|
|
1987
1926
|
description,
|
|
1988
1927
|
setDescription,
|
|
1989
|
-
|
|
1990
|
-
|
|
1928
|
+
dataSourceID,
|
|
1929
|
+
setDataSourceID,
|
|
1991
1930
|
viz,
|
|
1992
1931
|
setViz,
|
|
1993
1932
|
refreshData
|
|
@@ -2041,7 +1980,7 @@ function DashboardLayout({
|
|
|
2041
1980
|
const newPanels = panels.map((p2) => __spreadProps(__spreadValues({}, p2), {
|
|
2042
1981
|
layout: m2.get(p2.id)
|
|
2043
1982
|
}));
|
|
2044
|
-
setPanels
|
|
1983
|
+
setPanels(newPanels);
|
|
2045
1984
|
}, [panels, setPanels]);
|
|
2046
1985
|
return /* @__PURE__ */ jsx(ResponsiveReactGridLayout$1, {
|
|
2047
1986
|
onBreakpointChange,
|
|
@@ -2064,7 +2003,10 @@ function DashboardLayout({
|
|
|
2064
2003
|
}, rest), {
|
|
2065
2004
|
destroy: () => onRemoveItem(id),
|
|
2066
2005
|
update: (panel) => {
|
|
2067
|
-
setPanels
|
|
2006
|
+
setPanels((prevs) => {
|
|
2007
|
+
prevs.splice(index2, 1, panel);
|
|
2008
|
+
return prevs;
|
|
2009
|
+
});
|
|
2068
2010
|
}
|
|
2069
2011
|
}))
|
|
2070
2012
|
}, id);
|
|
@@ -2101,95 +2043,698 @@ function ModeToggler({
|
|
|
2101
2043
|
}]
|
|
2102
2044
|
});
|
|
2103
2045
|
}
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2046
|
+
const example = `
|
|
2047
|
+
-- You may reference context data or SQL snippets *by name*
|
|
2048
|
+
-- in SQL or VizConfig.
|
|
2049
|
+
SELECT *
|
|
2050
|
+
FROM commit
|
|
2051
|
+
WHERE
|
|
2052
|
+
-- context data
|
|
2053
|
+
author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.[1].toISOString()}'
|
|
2054
|
+
-- SQL snippets
|
|
2055
|
+
AND \${author_email_condition}
|
|
2056
|
+
\${order_by_clause}
|
|
2057
|
+
`;
|
|
2058
|
+
function ContextAndSnippets({}) {
|
|
2059
|
+
const contextInfo = React.useContext(ContextInfoContext);
|
|
2060
|
+
const {
|
|
2061
|
+
sqlSnippets
|
|
2062
|
+
} = React.useContext(DefinitionContext);
|
|
2063
|
+
const snippets = React.useMemo(() => {
|
|
2064
|
+
const snippets2 = sqlSnippets.reduce((prev, curr) => {
|
|
2065
|
+
prev[curr.key] = curr.value;
|
|
2066
|
+
return prev;
|
|
2067
|
+
}, {});
|
|
2068
|
+
return JSON.stringify(snippets2, null, 2);
|
|
2069
|
+
}, [sqlSnippets]);
|
|
2070
|
+
const context = React.useMemo(() => {
|
|
2071
|
+
return JSON.stringify(contextInfo, null, 2);
|
|
2072
|
+
}, [contextInfo]);
|
|
2112
2073
|
return /* @__PURE__ */ jsxs(Group, {
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2074
|
+
direction: "column",
|
|
2075
|
+
grow: true,
|
|
2076
|
+
sx: {
|
|
2077
|
+
border: "1px solid #eee",
|
|
2078
|
+
maxWidth: "48%",
|
|
2079
|
+
overflow: "hidden"
|
|
2080
|
+
},
|
|
2116
2081
|
children: [/* @__PURE__ */ jsx(Group, {
|
|
2117
2082
|
position: "left",
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2083
|
+
pl: "md",
|
|
2084
|
+
py: "md",
|
|
2085
|
+
sx: {
|
|
2086
|
+
borderBottom: "1px solid #eee",
|
|
2087
|
+
background: "#efefef",
|
|
2088
|
+
flexGrow: 0
|
|
2089
|
+
},
|
|
2090
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
2091
|
+
weight: 500,
|
|
2092
|
+
children: "Context"
|
|
2121
2093
|
})
|
|
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
|
-
|
|
2094
|
+
}), /* @__PURE__ */ jsxs(Group, {
|
|
2095
|
+
direction: "column",
|
|
2096
|
+
px: "md",
|
|
2097
|
+
pb: "md",
|
|
2098
|
+
sx: {
|
|
2099
|
+
width: "100%"
|
|
2100
|
+
},
|
|
2101
|
+
children: [/* @__PURE__ */ jsx(Prism, {
|
|
2102
|
+
language: "sql",
|
|
2103
|
+
sx: {
|
|
2104
|
+
width: "100%"
|
|
2105
|
+
},
|
|
2106
|
+
noCopy: true,
|
|
2107
|
+
colorScheme: "dark",
|
|
2108
|
+
children: example
|
|
2109
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
2110
|
+
weight: 500,
|
|
2111
|
+
sx: {
|
|
2112
|
+
flexGrow: 0
|
|
2113
|
+
},
|
|
2114
|
+
children: "Avaiable context"
|
|
2115
|
+
}), /* @__PURE__ */ jsx(Prism, {
|
|
2116
|
+
language: "json",
|
|
2117
|
+
sx: {
|
|
2118
|
+
width: "100%"
|
|
2119
|
+
},
|
|
2120
|
+
noCopy: true,
|
|
2121
|
+
colorScheme: "dark",
|
|
2122
|
+
children: context
|
|
2123
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
2124
|
+
weight: 500,
|
|
2125
|
+
sx: {
|
|
2126
|
+
flexGrow: 0
|
|
2127
|
+
},
|
|
2128
|
+
children: "Avaiable SQL Snippets"
|
|
2129
|
+
}), /* @__PURE__ */ jsx(Prism, {
|
|
2130
|
+
language: "json",
|
|
2131
|
+
sx: {
|
|
2132
|
+
width: "100%"
|
|
2133
|
+
},
|
|
2134
|
+
noCopy: true,
|
|
2135
|
+
colorScheme: "dark",
|
|
2136
|
+
children: snippets
|
|
2149
2137
|
})]
|
|
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
2138
|
})]
|
|
2159
2139
|
});
|
|
2160
2140
|
}
|
|
2161
|
-
function
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
className = "dashboard"
|
|
2141
|
+
function DataSourceForm({
|
|
2142
|
+
value,
|
|
2143
|
+
onChange
|
|
2165
2144
|
}) {
|
|
2166
|
-
const
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
const
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
const
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2145
|
+
const form = useForm$1({
|
|
2146
|
+
initialValues: value
|
|
2147
|
+
});
|
|
2148
|
+
const submit = React.useCallback((values) => {
|
|
2149
|
+
onChange(values);
|
|
2150
|
+
}, [onChange]);
|
|
2151
|
+
const changed = React.useMemo(() => {
|
|
2152
|
+
return !_.isEqual(value, form.values);
|
|
2153
|
+
}, [value, form.values]);
|
|
2154
|
+
React.useEffect(() => {
|
|
2155
|
+
form.reset();
|
|
2156
|
+
}, [value]);
|
|
2157
|
+
return /* @__PURE__ */ jsx(Group, {
|
|
2158
|
+
direction: "column",
|
|
2159
|
+
grow: true,
|
|
2160
|
+
sx: {
|
|
2161
|
+
border: "1px solid #eee",
|
|
2162
|
+
flexGrow: 1
|
|
2163
|
+
},
|
|
2164
|
+
children: /* @__PURE__ */ jsxs("form", {
|
|
2165
|
+
onSubmit: form.onSubmit(submit),
|
|
2166
|
+
children: [/* @__PURE__ */ jsxs(Group, {
|
|
2167
|
+
position: "left",
|
|
2168
|
+
py: "md",
|
|
2169
|
+
pl: "md",
|
|
2170
|
+
sx: {
|
|
2171
|
+
borderBottom: "1px solid #eee",
|
|
2172
|
+
background: "#efefef"
|
|
2173
|
+
},
|
|
2174
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
2175
|
+
weight: 500,
|
|
2176
|
+
children: "Data Source Configuration"
|
|
2177
|
+
}), /* @__PURE__ */ jsx(ActionIcon, {
|
|
2178
|
+
type: "submit",
|
|
2179
|
+
mr: 5,
|
|
2180
|
+
variant: "filled",
|
|
2181
|
+
color: "blue",
|
|
2182
|
+
disabled: !changed,
|
|
2183
|
+
children: /* @__PURE__ */ jsx(DeviceFloppy, {
|
|
2184
|
+
size: 20
|
|
2185
|
+
})
|
|
2186
|
+
})]
|
|
2187
|
+
}), /* @__PURE__ */ jsxs(Group, {
|
|
2188
|
+
direction: "column",
|
|
2189
|
+
grow: true,
|
|
2190
|
+
my: 0,
|
|
2191
|
+
p: "md",
|
|
2192
|
+
pr: 40,
|
|
2193
|
+
children: [/* @__PURE__ */ jsxs(Group, {
|
|
2194
|
+
grow: true,
|
|
2195
|
+
children: [/* @__PURE__ */ jsx(TextInput, __spreadValues({
|
|
2196
|
+
placeholder: "An ID unique in this dashboard",
|
|
2197
|
+
label: "ID",
|
|
2198
|
+
required: true,
|
|
2199
|
+
sx: {
|
|
2200
|
+
flex: 1
|
|
2201
|
+
}
|
|
2202
|
+
}, form.getInputProps("id"))), /* @__PURE__ */ jsx(TextInput, __spreadValues({
|
|
2203
|
+
placeholder: "TODO: use a dedicated UI component for this",
|
|
2204
|
+
label: "Data Source Key",
|
|
2205
|
+
required: true,
|
|
2206
|
+
sx: {
|
|
2207
|
+
flex: 1
|
|
2208
|
+
}
|
|
2209
|
+
}, form.getInputProps("key"))), /* @__PURE__ */ jsx(TextInput, __spreadValues({
|
|
2210
|
+
placeholder: "Type of the data source",
|
|
2211
|
+
label: "Type",
|
|
2212
|
+
disabled: true,
|
|
2213
|
+
sx: {
|
|
2214
|
+
flex: 1
|
|
2215
|
+
}
|
|
2216
|
+
}, form.getInputProps("type")))]
|
|
2217
|
+
}), /* @__PURE__ */ jsx(Textarea, __spreadValues({
|
|
2218
|
+
autosize: true,
|
|
2219
|
+
minRows: 12,
|
|
2220
|
+
maxRows: 24
|
|
2221
|
+
}, form.getInputProps("sql")))]
|
|
2222
|
+
})]
|
|
2223
|
+
})
|
|
2224
|
+
});
|
|
2225
|
+
}
|
|
2226
|
+
function DataSourceEditor({
|
|
2227
|
+
id
|
|
2228
|
+
}) {
|
|
2229
|
+
const {
|
|
2230
|
+
dataSources,
|
|
2231
|
+
setDataSources
|
|
2232
|
+
} = React.useContext(DefinitionContext);
|
|
2233
|
+
const dataSource = React.useMemo(() => {
|
|
2234
|
+
return dataSources.find((d) => d.id === id);
|
|
2235
|
+
}, [dataSources, id]);
|
|
2236
|
+
const update = React.useCallback((value) => {
|
|
2237
|
+
const index2 = dataSources.findIndex((d) => d.id === value.id);
|
|
2238
|
+
if (!index2) {
|
|
2239
|
+
console.error(new Error("Invalid data source id when updating by id"));
|
|
2240
|
+
return;
|
|
2241
|
+
}
|
|
2242
|
+
setDataSources((prevs) => {
|
|
2243
|
+
return prevs.map((p2) => {
|
|
2244
|
+
if (p2.id === value.id) {
|
|
2245
|
+
return value;
|
|
2246
|
+
}
|
|
2247
|
+
return p2;
|
|
2248
|
+
});
|
|
2249
|
+
});
|
|
2250
|
+
}, [setDataSources]);
|
|
2251
|
+
if (!dataSource) {
|
|
2252
|
+
return /* @__PURE__ */ jsx("span", {
|
|
2253
|
+
children: "Invalid Data Source ID"
|
|
2254
|
+
});
|
|
2255
|
+
}
|
|
2256
|
+
return /* @__PURE__ */ jsxs(Group, {
|
|
2257
|
+
direction: "row",
|
|
2258
|
+
position: "apart",
|
|
2259
|
+
grow: true,
|
|
2260
|
+
align: "stretch",
|
|
2261
|
+
noWrap: true,
|
|
2262
|
+
children: [/* @__PURE__ */ jsx(DataSourceForm, {
|
|
2263
|
+
value: dataSource,
|
|
2264
|
+
onChange: update
|
|
2265
|
+
}), /* @__PURE__ */ jsx(ContextAndSnippets, {})]
|
|
2266
|
+
});
|
|
2267
|
+
}
|
|
2268
|
+
function SelectOrAddDataSource({
|
|
2269
|
+
id,
|
|
2270
|
+
setID
|
|
2271
|
+
}) {
|
|
2272
|
+
const {
|
|
2273
|
+
dataSources,
|
|
2274
|
+
setDataSources
|
|
2275
|
+
} = React.useContext(DefinitionContext);
|
|
2276
|
+
React.useEffect(() => {
|
|
2277
|
+
var _a, _b;
|
|
2278
|
+
if (!id) {
|
|
2279
|
+
setID((_b = (_a = dataSources[0]) == null ? void 0 : _a.id) != null ? _b : "");
|
|
2280
|
+
}
|
|
2281
|
+
}, [id, setID, dataSources]);
|
|
2282
|
+
const options = React.useMemo(() => {
|
|
2283
|
+
return dataSources.map((d) => ({
|
|
2284
|
+
value: d.id,
|
|
2285
|
+
label: d.id
|
|
2286
|
+
}));
|
|
2287
|
+
}, [dataSources]);
|
|
2288
|
+
const add = React.useCallback(() => {
|
|
2289
|
+
const newDataSource = {
|
|
2290
|
+
id: randomId(),
|
|
2291
|
+
type: "postgresql",
|
|
2292
|
+
key: "",
|
|
2293
|
+
sql: ""
|
|
2294
|
+
};
|
|
2295
|
+
setDataSources((prevs) => [...prevs, newDataSource]);
|
|
2296
|
+
}, [setDataSources]);
|
|
2297
|
+
return /* @__PURE__ */ jsx(Group, {
|
|
2298
|
+
pb: "xl",
|
|
2299
|
+
children: /* @__PURE__ */ jsxs(Group, {
|
|
2300
|
+
position: "left",
|
|
2301
|
+
sx: {
|
|
2302
|
+
maxWidth: "600px",
|
|
2303
|
+
alignItems: "baseline"
|
|
2304
|
+
},
|
|
2305
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
2306
|
+
children: "Select a Data Source"
|
|
2307
|
+
}), /* @__PURE__ */ jsx(Select, {
|
|
2308
|
+
data: options,
|
|
2309
|
+
value: id,
|
|
2310
|
+
onChange: setID,
|
|
2311
|
+
allowDeselect: false,
|
|
2312
|
+
clearable: false,
|
|
2313
|
+
sx: {
|
|
2314
|
+
flexGrow: 1
|
|
2315
|
+
}
|
|
2316
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
2317
|
+
children: "or"
|
|
2318
|
+
}), /* @__PURE__ */ jsx(Group, {
|
|
2319
|
+
position: "center",
|
|
2320
|
+
mt: "md",
|
|
2321
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
2322
|
+
onClick: add,
|
|
2323
|
+
children: "Add a Data Source"
|
|
2324
|
+
})
|
|
2325
|
+
})]
|
|
2326
|
+
})
|
|
2327
|
+
});
|
|
2328
|
+
}
|
|
2329
|
+
function EditDataSourcesModal({
|
|
2330
|
+
opened,
|
|
2331
|
+
close
|
|
2332
|
+
}) {
|
|
2333
|
+
const [id, setID] = React.useState("");
|
|
2334
|
+
const {
|
|
2335
|
+
freezeLayout
|
|
2336
|
+
} = React.useContext(LayoutStateContext);
|
|
2337
|
+
React.useEffect(() => {
|
|
2338
|
+
freezeLayout(opened);
|
|
2339
|
+
}, [opened]);
|
|
2340
|
+
return /* @__PURE__ */ jsx(Modal, {
|
|
2341
|
+
size: "96vw",
|
|
2342
|
+
overflow: "inside",
|
|
2343
|
+
opened,
|
|
2344
|
+
onClose: close,
|
|
2345
|
+
title: "Data Sources",
|
|
2346
|
+
trapFocus: true,
|
|
2347
|
+
onDragStart: (e) => {
|
|
2348
|
+
e.stopPropagation();
|
|
2349
|
+
},
|
|
2350
|
+
children: /* @__PURE__ */ jsxs(AppShell, {
|
|
2351
|
+
sx: {
|
|
2352
|
+
height: "90vh",
|
|
2353
|
+
maxHeight: "calc(100vh - 185px)",
|
|
2354
|
+
".mantine-AppShell-body": {
|
|
2355
|
+
height: "100%"
|
|
2356
|
+
},
|
|
2357
|
+
main: {
|
|
2358
|
+
height: "100%",
|
|
2359
|
+
width: "100%",
|
|
2360
|
+
padding: 0,
|
|
2361
|
+
margin: 0
|
|
2362
|
+
}
|
|
2363
|
+
},
|
|
2364
|
+
padding: "md",
|
|
2365
|
+
header: /* @__PURE__ */ jsx(SelectOrAddDataSource, {
|
|
2366
|
+
id,
|
|
2367
|
+
setID
|
|
2368
|
+
}),
|
|
2369
|
+
children: [/* @__PURE__ */ jsx(DataSourceEditor, {
|
|
2370
|
+
id
|
|
2371
|
+
}), /* @__PURE__ */ jsx(DataPreview, {
|
|
2372
|
+
id
|
|
2373
|
+
})]
|
|
2374
|
+
})
|
|
2375
|
+
});
|
|
2376
|
+
}
|
|
2377
|
+
function ContextInfo({}) {
|
|
2378
|
+
const contextInfo = React.useContext(ContextInfoContext);
|
|
2379
|
+
const sampleSQL = `SELECT *
|
|
2380
|
+
FROM commit
|
|
2381
|
+
WHERE author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.[1].toISOString()}'`;
|
|
2382
|
+
return /* @__PURE__ */ jsxs(Group, {
|
|
2383
|
+
direction: "column",
|
|
2384
|
+
grow: true,
|
|
2385
|
+
sx: {
|
|
2386
|
+
border: "1px solid #eee",
|
|
2387
|
+
maxWidth: "48%",
|
|
2388
|
+
overflow: "hidden"
|
|
2389
|
+
},
|
|
2390
|
+
children: [/* @__PURE__ */ jsx(Group, {
|
|
2391
|
+
position: "left",
|
|
2392
|
+
pl: "md",
|
|
2393
|
+
py: "md",
|
|
2394
|
+
sx: {
|
|
2395
|
+
borderBottom: "1px solid #eee",
|
|
2396
|
+
background: "#efefef",
|
|
2397
|
+
flexGrow: 0
|
|
2398
|
+
},
|
|
2399
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
2400
|
+
weight: 500,
|
|
2401
|
+
children: "Context"
|
|
2402
|
+
})
|
|
2403
|
+
}), /* @__PURE__ */ jsxs(Group, {
|
|
2404
|
+
direction: "column",
|
|
2405
|
+
px: "md",
|
|
2406
|
+
pb: "md",
|
|
2407
|
+
sx: {
|
|
2408
|
+
width: "100%"
|
|
2409
|
+
},
|
|
2410
|
+
children: [/* @__PURE__ */ jsx(Prism, {
|
|
2411
|
+
language: "sql",
|
|
2412
|
+
sx: {
|
|
2413
|
+
width: "100%"
|
|
2414
|
+
},
|
|
2415
|
+
noCopy: true,
|
|
2416
|
+
colorScheme: "dark",
|
|
2417
|
+
children: `-- You may refer context data *by name*
|
|
2418
|
+
-- in SQL or VizConfig.
|
|
2419
|
+
|
|
2420
|
+
${sampleSQL}`
|
|
2421
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
2422
|
+
weight: 500,
|
|
2423
|
+
sx: {
|
|
2424
|
+
flexGrow: 0
|
|
2425
|
+
},
|
|
2426
|
+
children: "Avaiable context entries"
|
|
2427
|
+
}), /* @__PURE__ */ jsx(Prism, {
|
|
2428
|
+
language: "json",
|
|
2429
|
+
sx: {
|
|
2430
|
+
width: "100%"
|
|
2431
|
+
},
|
|
2432
|
+
noCopy: true,
|
|
2433
|
+
colorScheme: "dark",
|
|
2434
|
+
children: JSON.stringify(contextInfo, null, 2)
|
|
2435
|
+
})]
|
|
2436
|
+
})]
|
|
2437
|
+
});
|
|
2438
|
+
}
|
|
2439
|
+
function SQLSnippetsEditor({}) {
|
|
2440
|
+
const {
|
|
2441
|
+
sqlSnippets,
|
|
2442
|
+
setSQLSnippets
|
|
2443
|
+
} = React.useContext(DefinitionContext);
|
|
2444
|
+
const sampleSQL = `SELECT *
|
|
2445
|
+
FROM commit
|
|
2446
|
+
WHERE \${author_time_condition}`;
|
|
2447
|
+
const initialValues = React.useMemo(() => ({
|
|
2448
|
+
snippets: formList(sqlSnippets != null ? sqlSnippets : [])
|
|
2449
|
+
}), [sqlSnippets]);
|
|
2450
|
+
const form = useForm$1({
|
|
2451
|
+
initialValues
|
|
2452
|
+
});
|
|
2453
|
+
const addSnippet = () => form.addListItem("snippets", {
|
|
2454
|
+
key: randomId(),
|
|
2455
|
+
value: ""
|
|
2456
|
+
});
|
|
2457
|
+
const changed = React.useMemo(() => !_.isEqual(form.values, initialValues), [form.values, initialValues]);
|
|
2458
|
+
const submit = ({
|
|
2459
|
+
snippets
|
|
2460
|
+
}) => {
|
|
2461
|
+
setSQLSnippets(snippets);
|
|
2462
|
+
};
|
|
2463
|
+
return /* @__PURE__ */ jsxs(Group, {
|
|
2464
|
+
direction: "column",
|
|
2465
|
+
grow: true,
|
|
2466
|
+
sx: {
|
|
2467
|
+
border: "1px solid #eee"
|
|
2468
|
+
},
|
|
2469
|
+
children: [/* @__PURE__ */ jsxs(Group, {
|
|
2470
|
+
position: "left",
|
|
2471
|
+
pl: "md",
|
|
2472
|
+
py: "md",
|
|
2473
|
+
sx: {
|
|
2474
|
+
borderBottom: "1px solid #eee",
|
|
2475
|
+
background: "#efefef",
|
|
2476
|
+
flexGrow: 0
|
|
2477
|
+
},
|
|
2478
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
2479
|
+
weight: 500,
|
|
2480
|
+
children: "SQL Snippets"
|
|
2481
|
+
}), /* @__PURE__ */ jsx(ActionIcon, {
|
|
2482
|
+
type: "submit",
|
|
2483
|
+
mr: 5,
|
|
2484
|
+
variant: "filled",
|
|
2485
|
+
color: "blue",
|
|
2486
|
+
disabled: !changed,
|
|
2487
|
+
children: /* @__PURE__ */ jsx(DeviceFloppy, {
|
|
2488
|
+
size: 20
|
|
2489
|
+
})
|
|
2490
|
+
})]
|
|
2491
|
+
}), /* @__PURE__ */ jsxs(Group, {
|
|
2492
|
+
px: "md",
|
|
2493
|
+
pb: "md",
|
|
2494
|
+
children: [/* @__PURE__ */ jsx(Prism, {
|
|
2495
|
+
language: "sql",
|
|
2496
|
+
sx: {
|
|
2497
|
+
width: "100%"
|
|
2498
|
+
},
|
|
2499
|
+
noCopy: true,
|
|
2500
|
+
trim: false,
|
|
2501
|
+
colorScheme: "dark",
|
|
2502
|
+
children: `-- You may refer context data *by name*
|
|
2503
|
+
-- in SQL or VizConfig.
|
|
2504
|
+
|
|
2505
|
+
${sampleSQL}
|
|
2506
|
+
|
|
2507
|
+
-- where author_time_condition is:
|
|
2508
|
+
author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.[1].toISOString()}'
|
|
2509
|
+
`
|
|
2510
|
+
}), /* @__PURE__ */ jsx(Group, {
|
|
2511
|
+
direction: "column",
|
|
2512
|
+
sx: {
|
|
2513
|
+
width: "100%",
|
|
2514
|
+
position: "relative"
|
|
2515
|
+
},
|
|
2516
|
+
grow: true,
|
|
2517
|
+
children: /* @__PURE__ */ jsxs("form", {
|
|
2518
|
+
onSubmit: form.onSubmit(submit),
|
|
2519
|
+
children: [form.values.snippets.map((_item, index2) => /* @__PURE__ */ jsxs(Group, {
|
|
2520
|
+
direction: "column",
|
|
2521
|
+
grow: true,
|
|
2522
|
+
my: 0,
|
|
2523
|
+
p: "md",
|
|
2524
|
+
pr: 40,
|
|
2525
|
+
sx: {
|
|
2526
|
+
border: "1px solid #eee",
|
|
2527
|
+
position: "relative"
|
|
2528
|
+
},
|
|
2529
|
+
children: [/* @__PURE__ */ jsx(TextInput, __spreadValues({
|
|
2530
|
+
label: "Key",
|
|
2531
|
+
required: true
|
|
2532
|
+
}, form.getListInputProps("snippets", index2, "key"))), /* @__PURE__ */ jsx(Textarea, __spreadValues({
|
|
2533
|
+
minRows: 3,
|
|
2534
|
+
label: "Value",
|
|
2535
|
+
required: true
|
|
2536
|
+
}, form.getListInputProps("snippets", index2, "value"))), /* @__PURE__ */ jsx(ActionIcon, {
|
|
2537
|
+
color: "red",
|
|
2538
|
+
variant: "hover",
|
|
2539
|
+
onClick: () => form.removeListItem("snippets", index2),
|
|
2540
|
+
sx: {
|
|
2541
|
+
position: "absolute",
|
|
2542
|
+
top: 15,
|
|
2543
|
+
right: 5
|
|
2544
|
+
},
|
|
2545
|
+
children: /* @__PURE__ */ jsx(Trash, {
|
|
2546
|
+
size: 16
|
|
2547
|
+
})
|
|
2548
|
+
})]
|
|
2549
|
+
}, index2)), /* @__PURE__ */ jsx(Group, {
|
|
2550
|
+
position: "center",
|
|
2551
|
+
mt: "xl",
|
|
2552
|
+
grow: true,
|
|
2553
|
+
sx: {
|
|
2554
|
+
width: "40%"
|
|
2555
|
+
},
|
|
2556
|
+
mx: "auto",
|
|
2557
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
2558
|
+
variant: "default",
|
|
2559
|
+
onClick: addSnippet,
|
|
2560
|
+
children: "Add a snippet"
|
|
2561
|
+
})
|
|
2562
|
+
})]
|
|
2563
|
+
})
|
|
2564
|
+
})]
|
|
2565
|
+
})]
|
|
2566
|
+
});
|
|
2567
|
+
}
|
|
2568
|
+
function EditSQLSnippetsModal({
|
|
2569
|
+
opened,
|
|
2570
|
+
close
|
|
2571
|
+
}) {
|
|
2572
|
+
const {
|
|
2573
|
+
freezeLayout
|
|
2574
|
+
} = React.useContext(LayoutStateContext);
|
|
2575
|
+
React.useEffect(() => {
|
|
2576
|
+
freezeLayout(opened);
|
|
2577
|
+
}, [opened]);
|
|
2578
|
+
return /* @__PURE__ */ jsx(Modal, {
|
|
2579
|
+
size: "96vw",
|
|
2580
|
+
overflow: "inside",
|
|
2581
|
+
opened,
|
|
2582
|
+
onClose: close,
|
|
2583
|
+
title: "SQL Snippets",
|
|
2584
|
+
trapFocus: true,
|
|
2585
|
+
onDragStart: (e) => {
|
|
2586
|
+
e.stopPropagation();
|
|
2587
|
+
},
|
|
2588
|
+
children: /* @__PURE__ */ jsx(AppShell, {
|
|
2589
|
+
sx: {
|
|
2590
|
+
height: "90vh",
|
|
2591
|
+
maxHeight: "calc(100vh - 185px)",
|
|
2592
|
+
".mantine-AppShell-body": {
|
|
2593
|
+
height: "100%"
|
|
2594
|
+
},
|
|
2595
|
+
main: {
|
|
2596
|
+
height: "100%",
|
|
2597
|
+
width: "100%",
|
|
2598
|
+
padding: 0,
|
|
2599
|
+
margin: 0
|
|
2600
|
+
}
|
|
2601
|
+
},
|
|
2602
|
+
padding: "md",
|
|
2603
|
+
children: /* @__PURE__ */ jsxs(Group, {
|
|
2604
|
+
direction: "row",
|
|
2605
|
+
position: "apart",
|
|
2606
|
+
grow: true,
|
|
2607
|
+
align: "stretch",
|
|
2608
|
+
noWrap: true,
|
|
2609
|
+
children: [/* @__PURE__ */ jsx(SQLSnippetsEditor, {}), /* @__PURE__ */ jsx(ContextInfo, {})]
|
|
2610
|
+
})
|
|
2611
|
+
})
|
|
2612
|
+
});
|
|
2613
|
+
}
|
|
2614
|
+
function DashboardActions({
|
|
2615
|
+
mode,
|
|
2616
|
+
setMode,
|
|
2617
|
+
hasChanges,
|
|
2618
|
+
addPanel,
|
|
2619
|
+
saveChanges
|
|
2620
|
+
}) {
|
|
2621
|
+
const [dataSourcesOpened, setDataSourcesOpened] = React.useState(false);
|
|
2622
|
+
const openDataSources = () => setDataSourcesOpened(true);
|
|
2623
|
+
const closeDataSources = () => setDataSourcesOpened(false);
|
|
2624
|
+
const [sqlSnippetsOpened, setSQLSnippetsOpened] = React.useState(false);
|
|
2625
|
+
const openSQLSnippets = () => setSQLSnippetsOpened(true);
|
|
2626
|
+
const closeSQLSnippets = () => setSQLSnippetsOpened(false);
|
|
2627
|
+
const inEditMode = mode === DashboardMode.Edit;
|
|
2628
|
+
return /* @__PURE__ */ jsxs(Group, {
|
|
2629
|
+
position: "apart",
|
|
2630
|
+
pt: "sm",
|
|
2631
|
+
pb: "xs",
|
|
2632
|
+
children: [/* @__PURE__ */ jsx(Group, {
|
|
2633
|
+
position: "left",
|
|
2634
|
+
children: /* @__PURE__ */ jsx(ModeToggler, {
|
|
2635
|
+
mode,
|
|
2636
|
+
setMode
|
|
2637
|
+
})
|
|
2638
|
+
}), inEditMode && /* @__PURE__ */ jsxs(Fragment, {
|
|
2639
|
+
children: [/* @__PURE__ */ jsxs(Group, {
|
|
2640
|
+
position: "right",
|
|
2641
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
2642
|
+
variant: "default",
|
|
2643
|
+
size: "sm",
|
|
2644
|
+
onClick: addPanel,
|
|
2645
|
+
leftIcon: /* @__PURE__ */ jsx(PlaylistAdd, {
|
|
2646
|
+
size: 20
|
|
2647
|
+
}),
|
|
2648
|
+
children: "Add a Panel"
|
|
2649
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
2650
|
+
variant: "default",
|
|
2651
|
+
size: "sm",
|
|
2652
|
+
onClick: openSQLSnippets,
|
|
2653
|
+
leftIcon: /* @__PURE__ */ jsx(ClipboardText, {
|
|
2654
|
+
size: 20
|
|
2655
|
+
}),
|
|
2656
|
+
children: "SQL Snippets"
|
|
2657
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
2658
|
+
variant: "default",
|
|
2659
|
+
size: "sm",
|
|
2660
|
+
onClick: openDataSources,
|
|
2661
|
+
leftIcon: /* @__PURE__ */ jsx(Database, {
|
|
2662
|
+
size: 20
|
|
2663
|
+
}),
|
|
2664
|
+
children: "Data Sources"
|
|
2665
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
2666
|
+
variant: "default",
|
|
2667
|
+
size: "sm",
|
|
2668
|
+
onClick: saveChanges,
|
|
2669
|
+
disabled: !hasChanges,
|
|
2670
|
+
leftIcon: /* @__PURE__ */ jsx(DeviceFloppy, {
|
|
2671
|
+
size: 20
|
|
2672
|
+
}),
|
|
2673
|
+
children: "Save Changes"
|
|
2674
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
2675
|
+
color: "red",
|
|
2676
|
+
size: "sm",
|
|
2677
|
+
disabled: !hasChanges,
|
|
2678
|
+
leftIcon: /* @__PURE__ */ jsx(Recycle, {
|
|
2679
|
+
size: 20
|
|
2680
|
+
}),
|
|
2681
|
+
children: "Revert Changes"
|
|
2682
|
+
})]
|
|
2683
|
+
}), /* @__PURE__ */ jsx(EditDataSourcesModal, {
|
|
2684
|
+
opened: dataSourcesOpened,
|
|
2685
|
+
close: closeDataSources
|
|
2686
|
+
}), /* @__PURE__ */ jsx(EditSQLSnippetsModal, {
|
|
2687
|
+
opened: sqlSnippetsOpened,
|
|
2688
|
+
close: closeSQLSnippets
|
|
2689
|
+
})]
|
|
2690
|
+
}), !inEditMode && /* @__PURE__ */ jsx(Button, {
|
|
2691
|
+
variant: "default",
|
|
2692
|
+
size: "sm",
|
|
2693
|
+
disabled: true,
|
|
2694
|
+
leftIcon: /* @__PURE__ */ jsx(Share, {
|
|
2695
|
+
size: 20
|
|
2696
|
+
}),
|
|
2697
|
+
children: "Share"
|
|
2698
|
+
})]
|
|
2699
|
+
});
|
|
2700
|
+
}
|
|
2701
|
+
function Dashboard({
|
|
2702
|
+
context,
|
|
2703
|
+
dashboard,
|
|
2704
|
+
update,
|
|
2705
|
+
className = "dashboard"
|
|
2706
|
+
}) {
|
|
2707
|
+
const [layoutFrozen, freezeLayout] = React.useState(false);
|
|
2708
|
+
const [breakpoint, setBreakpoint] = React.useState();
|
|
2709
|
+
const [localCols, setLocalCols] = React.useState();
|
|
2710
|
+
const [panels, setPanels] = React.useState(dashboard.panels);
|
|
2711
|
+
const [sqlSnippets, setSQLSnippets] = React.useState(dashboard.definition.sqlSnippets);
|
|
2712
|
+
const [dataSources, setDataSources] = React.useState(dashboard.definition.dataSources);
|
|
2713
|
+
const [mode, setMode] = React.useState(DashboardMode.Edit);
|
|
2714
|
+
const hasChanges = React.useMemo(() => {
|
|
2715
|
+
const cleanJSON = (v) => JSON.parse(JSON.stringify(v));
|
|
2716
|
+
const panelsEqual = _.isEqual(cleanJSON(panels), cleanJSON(dashboard.panels));
|
|
2717
|
+
if (!panelsEqual) {
|
|
2718
|
+
return true;
|
|
2719
|
+
}
|
|
2720
|
+
if (!_.isEqual(sqlSnippets, dashboard.definition.sqlSnippets)) {
|
|
2721
|
+
return true;
|
|
2722
|
+
}
|
|
2723
|
+
return !_.isEqual(dataSources, dashboard.definition.dataSources);
|
|
2724
|
+
}, [dashboard, panels, sqlSnippets, dataSources]);
|
|
2725
|
+
const saveDashboardChanges = async () => {
|
|
2726
|
+
const d = _.merge({}, dashboard, {
|
|
2727
|
+
panels
|
|
2728
|
+
}, {
|
|
2729
|
+
definition: {
|
|
2730
|
+
sqlSnippets
|
|
2731
|
+
}
|
|
2732
|
+
});
|
|
2733
|
+
await update(d);
|
|
2734
|
+
};
|
|
2735
|
+
const addPanel = () => {
|
|
2736
|
+
const id = randomId();
|
|
2737
|
+
const newItem = {
|
|
2193
2738
|
id,
|
|
2194
2739
|
layout: {
|
|
2195
2740
|
x: 0,
|
|
@@ -2199,49 +2744,57 @@ function Dashboard({
|
|
|
2199
2744
|
},
|
|
2200
2745
|
title: `New Panel - ${id}`,
|
|
2201
2746
|
description: "description goes here",
|
|
2202
|
-
|
|
2747
|
+
dataSourceID: "",
|
|
2203
2748
|
viz: {
|
|
2204
2749
|
type: "table",
|
|
2205
2750
|
conf: {}
|
|
2206
2751
|
}
|
|
2207
2752
|
};
|
|
2208
|
-
setPanels
|
|
2753
|
+
setPanels((prevs) => [...prevs, newItem]);
|
|
2209
2754
|
};
|
|
2210
2755
|
const removePanelByID = (id) => {
|
|
2211
2756
|
const index2 = panels.findIndex((p2) => p2.id === id);
|
|
2212
|
-
setPanels
|
|
2757
|
+
setPanels((prevs) => {
|
|
2758
|
+
prevs.splice(index2, 1);
|
|
2759
|
+
return prevs;
|
|
2760
|
+
});
|
|
2213
2761
|
};
|
|
2214
2762
|
const inEditMode = mode === DashboardMode.Edit;
|
|
2215
2763
|
const definitions = React.useMemo(() => ({
|
|
2216
2764
|
sqlSnippets,
|
|
2217
|
-
setSQLSnippets
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2765
|
+
setSQLSnippets,
|
|
2766
|
+
dataSources,
|
|
2767
|
+
setDataSources
|
|
2768
|
+
}), [sqlSnippets, setSQLSnippets, dataSources, setDataSources]);
|
|
2769
|
+
return /* @__PURE__ */ jsx(ContextInfoContext.Provider, {
|
|
2770
|
+
value: context,
|
|
2771
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
2772
|
+
className,
|
|
2773
|
+
children: /* @__PURE__ */ jsx(DefinitionContext.Provider, {
|
|
2774
|
+
value: definitions,
|
|
2775
|
+
children: /* @__PURE__ */ jsxs(LayoutStateContext.Provider, {
|
|
2776
|
+
value: {
|
|
2777
|
+
layoutFrozen,
|
|
2778
|
+
freezeLayout,
|
|
2779
|
+
mode,
|
|
2780
|
+
inEditMode
|
|
2781
|
+
},
|
|
2782
|
+
children: [/* @__PURE__ */ jsx(DashboardActions, {
|
|
2783
|
+
mode,
|
|
2784
|
+
setMode,
|
|
2785
|
+
hasChanges,
|
|
2786
|
+
addPanel,
|
|
2787
|
+
saveChanges: saveDashboardChanges
|
|
2788
|
+
}), /* @__PURE__ */ jsx(DashboardLayout, {
|
|
2789
|
+
panels,
|
|
2790
|
+
setPanels,
|
|
2791
|
+
isDraggable: inEditMode && !layoutFrozen,
|
|
2792
|
+
isResizable: inEditMode && !layoutFrozen,
|
|
2793
|
+
onRemoveItem: removePanelByID,
|
|
2794
|
+
setLocalCols,
|
|
2795
|
+
setBreakpoint
|
|
2796
|
+
})]
|
|
2797
|
+
})
|
|
2245
2798
|
})
|
|
2246
2799
|
})
|
|
2247
2800
|
});
|
|
@@ -2281,29 +2834,33 @@ function ReadOnlyDashboardLayout({
|
|
|
2281
2834
|
});
|
|
2282
2835
|
}
|
|
2283
2836
|
function ReadOnlyDashboard({
|
|
2837
|
+
context,
|
|
2284
2838
|
dashboard,
|
|
2285
2839
|
className = "dashboard"
|
|
2286
2840
|
}) {
|
|
2287
|
-
const
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
}), [
|
|
2293
|
-
return /* @__PURE__ */ jsx(
|
|
2294
|
-
|
|
2295
|
-
children: /* @__PURE__ */ jsx(
|
|
2296
|
-
|
|
2297
|
-
children: /* @__PURE__ */ jsx(
|
|
2298
|
-
value:
|
|
2299
|
-
|
|
2300
|
-
|
|
2841
|
+
const definition = React.useMemo(() => __spreadProps(__spreadValues({}, dashboard.definition), {
|
|
2842
|
+
setSQLSnippets: () => {
|
|
2843
|
+
},
|
|
2844
|
+
setDataSources: () => {
|
|
2845
|
+
}
|
|
2846
|
+
}), [dashboard]);
|
|
2847
|
+
return /* @__PURE__ */ jsx(ContextInfoContext.Provider, {
|
|
2848
|
+
value: context,
|
|
2849
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
2850
|
+
className,
|
|
2851
|
+
children: /* @__PURE__ */ jsx(DefinitionContext.Provider, {
|
|
2852
|
+
value: definition,
|
|
2853
|
+
children: /* @__PURE__ */ jsx(LayoutStateContext.Provider, {
|
|
2854
|
+
value: {
|
|
2855
|
+
layoutFrozen: true,
|
|
2856
|
+
freezeLayout: () => {
|
|
2857
|
+
},
|
|
2858
|
+
mode: DashboardMode.Use,
|
|
2859
|
+
inEditMode: false
|
|
2301
2860
|
},
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
children: /* @__PURE__ */ jsx(ReadOnlyDashboardLayout, {
|
|
2306
|
-
panels
|
|
2861
|
+
children: /* @__PURE__ */ jsx(ReadOnlyDashboardLayout, {
|
|
2862
|
+
panels: dashboard.panels
|
|
2863
|
+
})
|
|
2307
2864
|
})
|
|
2308
2865
|
})
|
|
2309
2866
|
})
|