@devtable/dashboard 0.3.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 +1111 -492
- 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/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,25 +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
|
-
|
|
107
|
-
|
|
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);
|
|
108
124
|
return [];
|
|
109
125
|
}
|
|
110
|
-
const formattedSQL = formatSQL(sql, params);
|
|
111
|
-
if (needParams) {
|
|
112
|
-
console.groupCollapsed(`Final SQL for: ${title}`);
|
|
113
|
-
console.log(formattedSQL);
|
|
114
|
-
console.groupEnd();
|
|
115
|
-
}
|
|
116
|
-
const res = await post("/query", { sql: formattedSQL });
|
|
117
|
-
return res;
|
|
118
126
|
};
|
|
119
127
|
const initialContext$2 = {};
|
|
120
128
|
const initialContextInfoContext = initialContext$2;
|
|
@@ -128,8 +136,8 @@ const initialContext$1 = {
|
|
|
128
136
|
description: "",
|
|
129
137
|
setDescription: () => {
|
|
130
138
|
},
|
|
131
|
-
|
|
132
|
-
|
|
139
|
+
dataSourceID: "",
|
|
140
|
+
setDataSourceID: () => {
|
|
133
141
|
},
|
|
134
142
|
viz: {
|
|
135
143
|
type: "",
|
|
@@ -141,6 +149,15 @@ const initialContext$1 = {
|
|
|
141
149
|
}
|
|
142
150
|
};
|
|
143
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);
|
|
144
161
|
var jsxRuntime = { exports: {} };
|
|
145
162
|
var reactJsxRuntime_production_min = {};
|
|
146
163
|
/**
|
|
@@ -174,6 +191,152 @@ reactJsxRuntime_production_min.jsxs = q;
|
|
|
174
191
|
const jsx = jsxRuntime.exports.jsx;
|
|
175
192
|
const jsxs = jsxRuntime.exports.jsxs;
|
|
176
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
|
+
}
|
|
177
340
|
class ErrorBoundary extends React.Component {
|
|
178
341
|
constructor(props) {
|
|
179
342
|
super(props);
|
|
@@ -245,8 +408,8 @@ function Sunbrust({
|
|
|
245
408
|
}
|
|
246
409
|
}
|
|
247
410
|
}
|
|
248
|
-
}), []);
|
|
249
|
-
const option = _.merge(defaultOption$1, labelOption, restConf, {
|
|
411
|
+
}), [max]);
|
|
412
|
+
const option = _.merge({}, defaultOption$1, labelOption, restConf, {
|
|
250
413
|
series: {
|
|
251
414
|
data: chartData
|
|
252
415
|
}
|
|
@@ -561,7 +724,7 @@ function VizBar3D({
|
|
|
561
724
|
}
|
|
562
725
|
});
|
|
563
726
|
}
|
|
564
|
-
var index$
|
|
727
|
+
var index$2 = "";
|
|
565
728
|
function renderViz(width, height, data, viz) {
|
|
566
729
|
const props = {
|
|
567
730
|
width,
|
|
@@ -614,328 +777,90 @@ function Viz({
|
|
|
614
777
|
}), !empty && renderViz(width, height, data, viz)]
|
|
615
778
|
});
|
|
616
779
|
}
|
|
617
|
-
function
|
|
618
|
-
const contextInfo = React.useContext(ContextInfoContext);
|
|
619
|
-
const sampleSQL = `SELECT *
|
|
620
|
-
FROM commit
|
|
621
|
-
WHERE author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.[1].toISOString()}'`;
|
|
622
|
-
return /* @__PURE__ */ jsxs(Group, {
|
|
623
|
-
direction: "column",
|
|
624
|
-
children: [/* @__PURE__ */ jsx(Prism, {
|
|
625
|
-
language: "sql",
|
|
626
|
-
sx: {
|
|
627
|
-
width: "100%"
|
|
628
|
-
},
|
|
629
|
-
noCopy: true,
|
|
630
|
-
colorScheme: "dark",
|
|
631
|
-
children: `-- You may refer context data *by name*
|
|
632
|
-
-- in SQL or VizConfig.
|
|
633
|
-
|
|
634
|
-
${sampleSQL}`
|
|
635
|
-
}), /* @__PURE__ */ jsx(Text, {
|
|
636
|
-
weight: 700,
|
|
637
|
-
children: "Avaiable context entries"
|
|
638
|
-
}), /* @__PURE__ */ jsx(Prism, {
|
|
639
|
-
language: "json",
|
|
640
|
-
sx: {
|
|
641
|
-
width: "100%"
|
|
642
|
-
},
|
|
643
|
-
noCopy: true,
|
|
644
|
-
colorScheme: "dark",
|
|
645
|
-
children: JSON.stringify(contextInfo, null, 2)
|
|
646
|
-
})]
|
|
647
|
-
});
|
|
648
|
-
}
|
|
649
|
-
var index$2 = "";
|
|
650
|
-
function SQLQueryEditor({}) {
|
|
780
|
+
function PreviewViz({}) {
|
|
651
781
|
const {
|
|
652
|
-
|
|
653
|
-
|
|
782
|
+
data,
|
|
783
|
+
loading,
|
|
784
|
+
viz
|
|
654
785
|
} = React.useContext(PanelContext);
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
}, []);
|
|
662
|
-
const format = React.useCallback(() => {
|
|
663
|
-
setSQL((sql2) => sql2.trim());
|
|
664
|
-
}, [setSQL]);
|
|
665
|
-
return /* @__PURE__ */ jsxs(Box, {
|
|
666
|
-
className: "sql-query-editor-root",
|
|
667
|
-
sx: {
|
|
668
|
-
height: "100%"
|
|
669
|
-
},
|
|
670
|
-
children: [/* @__PURE__ */ jsx(Textarea, {
|
|
671
|
-
value: sql,
|
|
672
|
-
onChange: handleChange,
|
|
673
|
-
minRows: 20,
|
|
674
|
-
maxRows: 20
|
|
675
|
-
}), /* @__PURE__ */ jsxs(Group, {
|
|
676
|
-
m: "md",
|
|
677
|
-
position: "right",
|
|
678
|
-
children: [/* @__PURE__ */ jsx(Button, {
|
|
679
|
-
color: "blue",
|
|
680
|
-
onClick: format,
|
|
681
|
-
children: "Format"
|
|
682
|
-
}), /* @__PURE__ */ jsx(Button, {
|
|
683
|
-
variant: "default",
|
|
684
|
-
onClick: togglePreview,
|
|
685
|
-
children: "Toggle Preview"
|
|
686
|
-
})]
|
|
687
|
-
}), showPreview && /* @__PURE__ */ jsx(Prism, {
|
|
688
|
-
language: "sql",
|
|
689
|
-
withLineNumbers: true,
|
|
690
|
-
noCopy: true,
|
|
691
|
-
colorScheme: "dark",
|
|
692
|
-
children: sql
|
|
693
|
-
})]
|
|
786
|
+
return /* @__PURE__ */ jsx(ErrorBoundary, {
|
|
787
|
+
children: /* @__PURE__ */ jsx(Viz, {
|
|
788
|
+
viz,
|
|
789
|
+
data,
|
|
790
|
+
loading
|
|
791
|
+
})
|
|
694
792
|
});
|
|
695
793
|
}
|
|
696
|
-
|
|
697
|
-
function QueryResult({}) {
|
|
794
|
+
function EditTitle() {
|
|
698
795
|
const {
|
|
699
|
-
|
|
796
|
+
title,
|
|
797
|
+
setTitle
|
|
700
798
|
} = React.useContext(PanelContext);
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
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
|
+
})
|
|
714
818
|
});
|
|
715
819
|
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
}
|
|
720
|
-
};
|
|
721
|
-
const DefinitionContext = React.createContext(initialContext);
|
|
722
|
-
function SQLSnippetsForm({
|
|
723
|
-
sqlSnippets,
|
|
724
|
-
setSQLSnippets
|
|
820
|
+
function VizBar3DPanel({
|
|
821
|
+
conf,
|
|
822
|
+
setConf
|
|
725
823
|
}) {
|
|
726
|
-
const
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
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
|
|
735
847
|
});
|
|
736
|
-
const changed = React.useMemo(() => !_.isEqual(form.values, initialValues), [form.values, initialValues]);
|
|
737
|
-
const submit = ({
|
|
738
|
-
snippets
|
|
739
|
-
}) => {
|
|
740
|
-
setSQLSnippets(snippets);
|
|
741
|
-
};
|
|
742
848
|
return /* @__PURE__ */ jsx(Group, {
|
|
743
849
|
direction: "column",
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
position: "relative"
|
|
747
|
-
},
|
|
850
|
+
mt: "md",
|
|
851
|
+
spacing: "xs",
|
|
748
852
|
grow: true,
|
|
749
853
|
children: /* @__PURE__ */ jsxs("form", {
|
|
750
|
-
onSubmit:
|
|
751
|
-
children: [
|
|
752
|
-
|
|
854
|
+
onSubmit: handleSubmit(setConf),
|
|
855
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
856
|
+
children: "X Axis"
|
|
857
|
+
}), /* @__PURE__ */ jsxs(Group, {
|
|
858
|
+
position: "apart",
|
|
753
859
|
grow: true,
|
|
754
|
-
my: 0,
|
|
755
860
|
p: "md",
|
|
756
|
-
pr: 40,
|
|
757
|
-
sx: {
|
|
758
|
-
border: "1px solid #eee",
|
|
759
|
-
position: "relative"
|
|
760
|
-
},
|
|
761
|
-
children: [/* @__PURE__ */ jsx(TextInput, __spreadValues({
|
|
762
|
-
label: "Key",
|
|
763
|
-
required: true
|
|
764
|
-
}, form.getListInputProps("snippets", index2, "key"))), /* @__PURE__ */ jsx(Textarea, __spreadValues({
|
|
765
|
-
minRows: 3,
|
|
766
|
-
label: "Value",
|
|
767
|
-
required: true
|
|
768
|
-
}, form.getListInputProps("snippets", index2, "value"))), /* @__PURE__ */ jsx(ActionIcon, {
|
|
769
|
-
color: "red",
|
|
770
|
-
variant: "hover",
|
|
771
|
-
onClick: () => form.removeListItem("snippets", index2),
|
|
772
|
-
sx: {
|
|
773
|
-
position: "absolute",
|
|
774
|
-
top: 15,
|
|
775
|
-
right: 5
|
|
776
|
-
},
|
|
777
|
-
children: /* @__PURE__ */ jsx(Trash, {
|
|
778
|
-
size: 16
|
|
779
|
-
})
|
|
780
|
-
})]
|
|
781
|
-
}, index2)), /* @__PURE__ */ jsxs(Group, {
|
|
782
|
-
position: "center",
|
|
783
|
-
mt: "xl",
|
|
784
|
-
grow: true,
|
|
785
861
|
sx: {
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
mx: "auto",
|
|
789
|
-
children: [/* @__PURE__ */ jsx(Button, {
|
|
790
|
-
variant: "default",
|
|
791
|
-
onClick: addSnippet,
|
|
792
|
-
children: "Add a snippet"
|
|
793
|
-
}), /* @__PURE__ */ jsx(Button, {
|
|
794
|
-
color: "blue",
|
|
795
|
-
type: "submit",
|
|
796
|
-
disabled: !changed,
|
|
797
|
-
children: "Submit"
|
|
798
|
-
})]
|
|
799
|
-
})]
|
|
800
|
-
})
|
|
801
|
-
});
|
|
802
|
-
}
|
|
803
|
-
function SQLSnippetsTab({}) {
|
|
804
|
-
const {
|
|
805
|
-
sqlSnippets,
|
|
806
|
-
setSQLSnippets
|
|
807
|
-
} = React.useContext(DefinitionContext);
|
|
808
|
-
const sampleSQL = `SELECT *
|
|
809
|
-
FROM commit
|
|
810
|
-
WHERE \${author_time_condition}`;
|
|
811
|
-
return /* @__PURE__ */ jsxs(Group, {
|
|
812
|
-
direction: "column",
|
|
813
|
-
children: [/* @__PURE__ */ jsx(Prism, {
|
|
814
|
-
language: "sql",
|
|
815
|
-
sx: {
|
|
816
|
-
width: "100%"
|
|
817
|
-
},
|
|
818
|
-
noCopy: true,
|
|
819
|
-
trim: false,
|
|
820
|
-
colorScheme: "dark",
|
|
821
|
-
children: `-- You may refer context data *by name*
|
|
822
|
-
-- in SQL or VizConfig.
|
|
823
|
-
|
|
824
|
-
${sampleSQL}
|
|
825
|
-
|
|
826
|
-
-- where author_time_condition is:
|
|
827
|
-
author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.[1].toISOString()}'
|
|
828
|
-
`
|
|
829
|
-
}), /* @__PURE__ */ jsx(Text, {
|
|
830
|
-
weight: 700,
|
|
831
|
-
children: "SQL Snippets"
|
|
832
|
-
}), /* @__PURE__ */ jsx(SQLSnippetsForm, {
|
|
833
|
-
sqlSnippets,
|
|
834
|
-
setSQLSnippets
|
|
835
|
-
})]
|
|
836
|
-
});
|
|
837
|
-
}
|
|
838
|
-
function EditDescription() {
|
|
839
|
-
const {
|
|
840
|
-
description,
|
|
841
|
-
setDescription
|
|
842
|
-
} = React.useContext(PanelContext);
|
|
843
|
-
const [localDesc, setLocalDesc] = useInputState(description);
|
|
844
|
-
const changed = description !== localDesc;
|
|
845
|
-
const submit = React.useCallback(() => {
|
|
846
|
-
if (!changed) {
|
|
847
|
-
return;
|
|
848
|
-
}
|
|
849
|
-
setDescription(localDesc);
|
|
850
|
-
}, [changed, localDesc]);
|
|
851
|
-
return /* @__PURE__ */ jsx(Textarea, {
|
|
852
|
-
label: "Panel Description",
|
|
853
|
-
value: localDesc,
|
|
854
|
-
onChange: setLocalDesc,
|
|
855
|
-
minRows: 2,
|
|
856
|
-
rightSection: /* @__PURE__ */ jsx(ActionIcon, {
|
|
857
|
-
disabled: !changed,
|
|
858
|
-
onClick: submit,
|
|
859
|
-
sx: {
|
|
860
|
-
alignSelf: "flex-start",
|
|
861
|
-
marginTop: "4px"
|
|
862
|
-
},
|
|
863
|
-
children: /* @__PURE__ */ jsx(DeviceFloppy, {
|
|
864
|
-
size: 20
|
|
865
|
-
})
|
|
866
|
-
})
|
|
867
|
-
});
|
|
868
|
-
}
|
|
869
|
-
function EditTitle() {
|
|
870
|
-
const {
|
|
871
|
-
title,
|
|
872
|
-
setTitle
|
|
873
|
-
} = React.useContext(PanelContext);
|
|
874
|
-
const [localTitle, setLocalTitle] = useInputState(title);
|
|
875
|
-
const changed = title !== localTitle;
|
|
876
|
-
const submit = React.useCallback(() => {
|
|
877
|
-
if (!changed) {
|
|
878
|
-
return;
|
|
879
|
-
}
|
|
880
|
-
setTitle(localTitle);
|
|
881
|
-
}, [changed, localTitle]);
|
|
882
|
-
return /* @__PURE__ */ jsx(TextInput, {
|
|
883
|
-
label: "Panel Title",
|
|
884
|
-
value: localTitle,
|
|
885
|
-
onChange: setLocalTitle,
|
|
886
|
-
rightSection: /* @__PURE__ */ jsx(ActionIcon, {
|
|
887
|
-
disabled: !changed,
|
|
888
|
-
onClick: submit,
|
|
889
|
-
children: /* @__PURE__ */ jsx(DeviceFloppy, {
|
|
890
|
-
size: 20
|
|
891
|
-
})
|
|
892
|
-
})
|
|
893
|
-
});
|
|
894
|
-
}
|
|
895
|
-
function VizBar3DPanel({
|
|
896
|
-
conf,
|
|
897
|
-
setConf
|
|
898
|
-
}) {
|
|
899
|
-
const defaultValues = _.assign({}, {
|
|
900
|
-
"x_axis_data_key": "x",
|
|
901
|
-
"y_axis_data_key": "y",
|
|
902
|
-
"z_axis_data_key": "z",
|
|
903
|
-
"xAxis3D": {
|
|
904
|
-
"type": "value",
|
|
905
|
-
"name": "X Axis Name"
|
|
906
|
-
},
|
|
907
|
-
"yAxis3D": {
|
|
908
|
-
"type": "value",
|
|
909
|
-
"name": "Y Axis Name"
|
|
910
|
-
},
|
|
911
|
-
"zAxis3D": {
|
|
912
|
-
"type": "value",
|
|
913
|
-
"name": "Z Axis Name"
|
|
914
|
-
}
|
|
915
|
-
}, conf);
|
|
916
|
-
const {
|
|
917
|
-
control,
|
|
918
|
-
handleSubmit,
|
|
919
|
-
formState
|
|
920
|
-
} = useForm$1({
|
|
921
|
-
defaultValues
|
|
922
|
-
});
|
|
923
|
-
return /* @__PURE__ */ jsx(Group, {
|
|
924
|
-
direction: "column",
|
|
925
|
-
mt: "md",
|
|
926
|
-
spacing: "xs",
|
|
927
|
-
grow: true,
|
|
928
|
-
children: /* @__PURE__ */ jsxs("form", {
|
|
929
|
-
onSubmit: handleSubmit(setConf),
|
|
930
|
-
children: [/* @__PURE__ */ jsx(Text, {
|
|
931
|
-
children: "X Axis"
|
|
932
|
-
}), /* @__PURE__ */ jsxs(Group, {
|
|
933
|
-
position: "apart",
|
|
934
|
-
grow: true,
|
|
935
|
-
p: "md",
|
|
936
|
-
sx: {
|
|
937
|
-
position: "relative",
|
|
938
|
-
border: "1px solid #eee"
|
|
862
|
+
position: "relative",
|
|
863
|
+
border: "1px solid #eee"
|
|
939
864
|
},
|
|
940
865
|
children: [/* @__PURE__ */ jsx(Controller, {
|
|
941
866
|
name: "x_axis_data_key",
|
|
@@ -1120,7 +1045,7 @@ function VizLineBarChartPanel({
|
|
|
1120
1045
|
const initialValues = React.useMemo(() => __spreadValues({
|
|
1121
1046
|
series: formList(series != null ? series : [])
|
|
1122
1047
|
}, restConf), [series, restConf]);
|
|
1123
|
-
const form = useForm({
|
|
1048
|
+
const form = useForm$1({
|
|
1124
1049
|
initialValues
|
|
1125
1050
|
});
|
|
1126
1051
|
const addSeries = () => form.addListItem("series", {
|
|
@@ -1232,7 +1157,7 @@ function SunburstPanel({
|
|
|
1232
1157
|
},
|
|
1233
1158
|
setConf
|
|
1234
1159
|
}) {
|
|
1235
|
-
const form = useForm({
|
|
1160
|
+
const form = useForm$1({
|
|
1236
1161
|
initialValues: {
|
|
1237
1162
|
label_field,
|
|
1238
1163
|
value_field
|
|
@@ -1319,7 +1244,7 @@ function VizTablePanel(_a) {
|
|
|
1319
1244
|
]), {
|
|
1320
1245
|
setConf
|
|
1321
1246
|
} = _b;
|
|
1322
|
-
const form = useForm({
|
|
1247
|
+
const form = useForm$1({
|
|
1323
1248
|
initialValues: __spreadValues({
|
|
1324
1249
|
id_field: "id",
|
|
1325
1250
|
use_raw_columns: true,
|
|
@@ -1594,7 +1519,7 @@ function VizTextPanel({
|
|
|
1594
1519
|
setConf
|
|
1595
1520
|
}) {
|
|
1596
1521
|
var _a;
|
|
1597
|
-
const form = useForm({
|
|
1522
|
+
const form = useForm$1({
|
|
1598
1523
|
initialValues: {
|
|
1599
1524
|
paragraphs: formList((_a = conf.paragraphs) != null ? _a : sampleParagraphs)
|
|
1600
1525
|
}
|
|
@@ -1789,9 +1714,34 @@ function EditVizConf() {
|
|
|
1789
1714
|
}
|
|
1790
1715
|
function VizConfig({}) {
|
|
1791
1716
|
return /* @__PURE__ */ jsxs(Group, {
|
|
1717
|
+
direction: "row",
|
|
1792
1718
|
grow: true,
|
|
1793
|
-
|
|
1794
|
-
|
|
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
|
+
})]
|
|
1795
1745
|
});
|
|
1796
1746
|
}
|
|
1797
1747
|
function PanelSettingsModal({
|
|
@@ -1833,40 +1783,17 @@ function PanelSettingsModal({
|
|
|
1833
1783
|
}
|
|
1834
1784
|
},
|
|
1835
1785
|
padding: "md",
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
}), /* @__PURE__ */ jsx(Tabs.Tab, {
|
|
1848
|
-
label: "SQL Snippets",
|
|
1849
|
-
children: /* @__PURE__ */ jsx(SQLSnippetsTab, {})
|
|
1850
|
-
}), /* @__PURE__ */ jsx(Tabs.Tab, {
|
|
1851
|
-
label: "SQL",
|
|
1852
|
-
children: /* @__PURE__ */ jsx(QueryEditor, {})
|
|
1853
|
-
}), /* @__PURE__ */ jsxs(Tabs.Tab, {
|
|
1854
|
-
label: "Data",
|
|
1855
|
-
children: [/* @__PURE__ */ jsx(LoadingOverlay, {
|
|
1856
|
-
visible: loading
|
|
1857
|
-
}), /* @__PURE__ */ jsx(QueryResult, {})]
|
|
1858
|
-
}), /* @__PURE__ */ jsx(Tabs.Tab, {
|
|
1859
|
-
label: "Viz Config",
|
|
1860
|
-
children: /* @__PURE__ */ jsx(VizConfig, {})
|
|
1861
|
-
})]
|
|
1862
|
-
})
|
|
1863
|
-
}),
|
|
1864
|
-
children: /* @__PURE__ */ jsx(ErrorBoundary, {
|
|
1865
|
-
children: /* @__PURE__ */ jsx(Viz, {
|
|
1866
|
-
viz,
|
|
1867
|
-
data,
|
|
1868
|
-
loading
|
|
1869
|
-
})
|
|
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
|
+
})]
|
|
1870
1797
|
})
|
|
1871
1798
|
})
|
|
1872
1799
|
});
|
|
@@ -1948,7 +1875,7 @@ function PanelTitleBar({}) {
|
|
|
1948
1875
|
var index$1 = "";
|
|
1949
1876
|
function Panel({
|
|
1950
1877
|
viz: initialViz,
|
|
1951
|
-
|
|
1878
|
+
dataSourceID: initialDataSourceID,
|
|
1952
1879
|
title: initialTitle,
|
|
1953
1880
|
description: initialDesc,
|
|
1954
1881
|
update,
|
|
@@ -1959,24 +1886,35 @@ function Panel({
|
|
|
1959
1886
|
const definitions = React.useContext(DefinitionContext);
|
|
1960
1887
|
const [title, setTitle] = React.useState(initialTitle);
|
|
1961
1888
|
const [description, setDescription] = React.useState(initialDesc);
|
|
1962
|
-
const [
|
|
1889
|
+
const [dataSourceID, setDataSourceID] = React.useState(initialDataSourceID);
|
|
1963
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]);
|
|
1964
1897
|
React.useEffect(() => {
|
|
1965
|
-
update({
|
|
1898
|
+
update == null ? void 0 : update({
|
|
1966
1899
|
id,
|
|
1967
1900
|
layout,
|
|
1968
1901
|
title,
|
|
1969
1902
|
description,
|
|
1970
|
-
|
|
1903
|
+
dataSourceID,
|
|
1971
1904
|
viz
|
|
1972
1905
|
});
|
|
1973
|
-
}, [title, description,
|
|
1906
|
+
}, [title, description, dataSource, viz, id, layout, dataSourceID]);
|
|
1974
1907
|
const {
|
|
1975
1908
|
data = [],
|
|
1976
1909
|
loading,
|
|
1977
1910
|
refresh
|
|
1978
|
-
} = useRequest(queryBySQL(
|
|
1979
|
-
|
|
1911
|
+
} = useRequest(queryBySQL({
|
|
1912
|
+
context: contextInfo,
|
|
1913
|
+
definitions,
|
|
1914
|
+
title,
|
|
1915
|
+
dataSource
|
|
1916
|
+
}), {
|
|
1917
|
+
refreshDeps: [contextInfo, definitions, dataSource]
|
|
1980
1918
|
});
|
|
1981
1919
|
const refreshData = refresh;
|
|
1982
1920
|
return /* @__PURE__ */ jsx(PanelContext.Provider, {
|
|
@@ -1987,8 +1925,8 @@ function Panel({
|
|
|
1987
1925
|
setTitle,
|
|
1988
1926
|
description,
|
|
1989
1927
|
setDescription,
|
|
1990
|
-
|
|
1991
|
-
|
|
1928
|
+
dataSourceID,
|
|
1929
|
+
setDataSourceID,
|
|
1992
1930
|
viz,
|
|
1993
1931
|
setViz,
|
|
1994
1932
|
refreshData
|
|
@@ -2006,7 +1944,7 @@ function Panel({
|
|
|
2006
1944
|
});
|
|
2007
1945
|
}
|
|
2008
1946
|
var index = "";
|
|
2009
|
-
const ResponsiveReactGridLayout = WidthProvider(Responsive);
|
|
1947
|
+
const ResponsiveReactGridLayout$1 = WidthProvider(Responsive);
|
|
2010
1948
|
function DashboardLayout({
|
|
2011
1949
|
panels,
|
|
2012
1950
|
setPanels,
|
|
@@ -2042,9 +1980,9 @@ function DashboardLayout({
|
|
|
2042
1980
|
const newPanels = panels.map((p2) => __spreadProps(__spreadValues({}, p2), {
|
|
2043
1981
|
layout: m2.get(p2.id)
|
|
2044
1982
|
}));
|
|
2045
|
-
setPanels
|
|
1983
|
+
setPanels(newPanels);
|
|
2046
1984
|
}, [panels, setPanels]);
|
|
2047
|
-
return /* @__PURE__ */ jsx(ResponsiveReactGridLayout, {
|
|
1985
|
+
return /* @__PURE__ */ jsx(ResponsiveReactGridLayout$1, {
|
|
2048
1986
|
onBreakpointChange,
|
|
2049
1987
|
onLayoutChange,
|
|
2050
1988
|
className,
|
|
@@ -2065,7 +2003,10 @@ function DashboardLayout({
|
|
|
2065
2003
|
}, rest), {
|
|
2066
2004
|
destroy: () => onRemoveItem(id),
|
|
2067
2005
|
update: (panel) => {
|
|
2068
|
-
setPanels
|
|
2006
|
+
setPanels((prevs) => {
|
|
2007
|
+
prevs.splice(index2, 1, panel);
|
|
2008
|
+
return prevs;
|
|
2009
|
+
});
|
|
2069
2010
|
}
|
|
2070
2011
|
}))
|
|
2071
2012
|
}, id);
|
|
@@ -2102,93 +2043,696 @@ function ModeToggler({
|
|
|
2102
2043
|
}]
|
|
2103
2044
|
});
|
|
2104
2045
|
}
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
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]);
|
|
2113
2073
|
return /* @__PURE__ */ jsxs(Group, {
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2074
|
+
direction: "column",
|
|
2075
|
+
grow: true,
|
|
2076
|
+
sx: {
|
|
2077
|
+
border: "1px solid #eee",
|
|
2078
|
+
maxWidth: "48%",
|
|
2079
|
+
overflow: "hidden"
|
|
2080
|
+
},
|
|
2117
2081
|
children: [/* @__PURE__ */ jsx(Group, {
|
|
2118
2082
|
position: "left",
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
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"
|
|
2122
2093
|
})
|
|
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
|
-
}
|
|
2149
|
-
|
|
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
|
|
2150
2137
|
})]
|
|
2151
|
-
}), !inEditMode && /* @__PURE__ */ jsx(Button, {
|
|
2152
|
-
variant: "default",
|
|
2153
|
-
size: "sm",
|
|
2154
|
-
disabled: true,
|
|
2155
|
-
leftIcon: /* @__PURE__ */ jsx(Share, {
|
|
2156
|
-
size: 20
|
|
2157
|
-
}),
|
|
2158
|
-
children: "Share"
|
|
2159
2138
|
})]
|
|
2160
2139
|
});
|
|
2161
2140
|
}
|
|
2162
|
-
function
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
className = "dashboard"
|
|
2141
|
+
function DataSourceForm({
|
|
2142
|
+
value,
|
|
2143
|
+
onChange
|
|
2166
2144
|
}) {
|
|
2167
|
-
const
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
const
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
const
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
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 = () => {
|
|
2192
2736
|
const id = randomId();
|
|
2193
2737
|
const newItem = {
|
|
2194
2738
|
id,
|
|
@@ -2200,51 +2744,126 @@ function Dashboard({
|
|
|
2200
2744
|
},
|
|
2201
2745
|
title: `New Panel - ${id}`,
|
|
2202
2746
|
description: "description goes here",
|
|
2203
|
-
|
|
2747
|
+
dataSourceID: "",
|
|
2204
2748
|
viz: {
|
|
2205
2749
|
type: "table",
|
|
2206
2750
|
conf: {}
|
|
2207
2751
|
}
|
|
2208
2752
|
};
|
|
2209
|
-
setPanels
|
|
2753
|
+
setPanels((prevs) => [...prevs, newItem]);
|
|
2210
2754
|
};
|
|
2211
2755
|
const removePanelByID = (id) => {
|
|
2212
2756
|
const index2 = panels.findIndex((p2) => p2.id === id);
|
|
2213
|
-
setPanels
|
|
2757
|
+
setPanels((prevs) => {
|
|
2758
|
+
prevs.splice(index2, 1);
|
|
2759
|
+
return prevs;
|
|
2760
|
+
});
|
|
2214
2761
|
};
|
|
2215
2762
|
const inEditMode = mode === DashboardMode.Edit;
|
|
2216
2763
|
const definitions = React.useMemo(() => ({
|
|
2217
2764
|
sqlSnippets,
|
|
2218
|
-
setSQLSnippets
|
|
2219
|
-
|
|
2220
|
-
|
|
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
|
+
})
|
|
2798
|
+
})
|
|
2799
|
+
})
|
|
2800
|
+
});
|
|
2801
|
+
}
|
|
2802
|
+
const ResponsiveReactGridLayout = WidthProvider(Responsive);
|
|
2803
|
+
function ReadOnlyDashboardLayout({
|
|
2804
|
+
panels,
|
|
2805
|
+
className = "layout",
|
|
2806
|
+
cols = {
|
|
2807
|
+
lg: 12,
|
|
2808
|
+
md: 10,
|
|
2809
|
+
sm: 8,
|
|
2810
|
+
xs: 6,
|
|
2811
|
+
xxs: 4
|
|
2812
|
+
},
|
|
2813
|
+
rowHeight = 10
|
|
2814
|
+
}) {
|
|
2815
|
+
return /* @__PURE__ */ jsx(ResponsiveReactGridLayout, {
|
|
2221
2816
|
className,
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2817
|
+
cols,
|
|
2818
|
+
rowHeight,
|
|
2819
|
+
isDraggable: false,
|
|
2820
|
+
isResizable: false,
|
|
2821
|
+
children: panels.map((_a) => {
|
|
2822
|
+
var _b = _a, {
|
|
2823
|
+
id
|
|
2824
|
+
} = _b, rest = __objRest(_b, [
|
|
2825
|
+
"id"
|
|
2826
|
+
]);
|
|
2827
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2828
|
+
"data-grid": rest.layout,
|
|
2829
|
+
children: /* @__PURE__ */ jsx(Panel, __spreadValues({
|
|
2830
|
+
id
|
|
2831
|
+
}, rest))
|
|
2832
|
+
}, id);
|
|
2833
|
+
})
|
|
2834
|
+
});
|
|
2835
|
+
}
|
|
2836
|
+
function ReadOnlyDashboard({
|
|
2837
|
+
context,
|
|
2838
|
+
dashboard,
|
|
2839
|
+
className = "dashboard"
|
|
2840
|
+
}) {
|
|
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
|
|
2860
|
+
},
|
|
2861
|
+
children: /* @__PURE__ */ jsx(ReadOnlyDashboardLayout, {
|
|
2862
|
+
panels: dashboard.panels
|
|
2863
|
+
})
|
|
2864
|
+
})
|
|
2246
2865
|
})
|
|
2247
2866
|
})
|
|
2248
2867
|
});
|
|
2249
2868
|
}
|
|
2250
|
-
export { ContextInfoContext, Dashboard, DashboardLayout, DashboardMode, DefinitionContext, LayoutStateContext, Panel, PanelContext, initialContextInfoContext };
|
|
2869
|
+
export { ContextInfoContext, Dashboard, DashboardLayout, DashboardMode, DefinitionContext, LayoutStateContext, Panel, PanelContext, ReadOnlyDashboard, initialContextInfoContext };
|