@devtable/dashboard 1.16.0 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/api-caller/index.d.ts +3 -3
  2. package/dist/contexts/definition-context.d.ts +2 -2
  3. package/dist/contexts/panel-context.d.ts +2 -2
  4. package/dist/dashboard.es.js +463 -332
  5. package/dist/dashboard.umd.js +8 -8
  6. package/dist/definition-editor/data-editor-modal.d.ts +7 -0
  7. package/dist/definition-editor/index.d.ts +1 -2
  8. package/dist/definition-editor/{data-source-editor → query-editor}/context-and-snippets.d.ts +0 -0
  9. package/dist/definition-editor/{data-source-editor → query-editor}/data-preview.d.ts +0 -0
  10. package/dist/definition-editor/query-editor/editor.d.ts +7 -0
  11. package/dist/definition-editor/query-editor/form.d.ts +8 -0
  12. package/dist/definition-editor/query-editor/index.d.ts +5 -0
  13. package/dist/definition-editor/query-editor/preview-sql.d.ts +6 -0
  14. package/dist/definition-editor/query-editor/select-or-add-query.d.ts +7 -0
  15. package/dist/definition-editor/sql-snippet-editor/guide.d.ts +2 -0
  16. package/dist/definition-editor/sql-snippet-editor/index.d.ts +2 -4
  17. package/dist/definition-editor/sql-snippet-editor/preview-snippet.d.ts +6 -0
  18. package/dist/panel/index.d.ts +1 -1
  19. package/dist/panel/settings/common/data-field-selector.d.ts +12 -0
  20. package/dist/panel/settings/pick-query/index.d.ts +5 -0
  21. package/dist/panel/viz/bar-3d/panel.d.ts +2 -2
  22. package/dist/panel/viz/cartesian/panel/index.d.ts +1 -1
  23. package/dist/panel/viz/cartesian/panel/series.d.ts +2 -1
  24. package/dist/panel/viz/pie/panel.d.ts +1 -1
  25. package/dist/panel/viz/stats/panel.d.ts +1 -1
  26. package/dist/panel/viz/sunburst/panel.d.ts +1 -1
  27. package/dist/panel/viz/table/panel.d.ts +1 -1
  28. package/dist/style.css +1 -1
  29. package/dist/types/dashboard.d.ts +3 -3
  30. package/dist/types/viz-panel.d.ts +1 -0
  31. package/dist/utils/sql.d.ts +6 -0
  32. package/package.json +1 -1
  33. package/dist/definition-editor/data-source-editor/editor.d.ts +0 -7
  34. package/dist/definition-editor/data-source-editor/form.d.ts +0 -8
  35. package/dist/definition-editor/data-source-editor/index.d.ts +0 -7
  36. package/dist/definition-editor/data-source-editor/select-or-add-data-source.d.ts +0 -7
  37. package/dist/panel/settings/pick-data-source/index.d.ts +0 -5
@@ -39,7 +39,7 @@ import RGL, { WidthProvider } from "react-grid-layout";
39
39
  import { Popover, Tooltip, Group, Text, ActionIcon, Box, Button, TextInput, LoadingOverlay, Table, Select, useMantineTheme, ColorSwatch, SegmentedControl, NumberInput, Switch, Slider, ColorInput, Accordion, JsonInput, Modal, AppShell, Tabs, Menu, Divider, Container, Textarea } from "@mantine/core";
40
40
  import { useRequest } from "ahooks";
41
41
  import axios from "axios";
42
- import { InfoCircle, DeviceFloppy, Refresh, Trash, PlaylistAdd, Settings, Resize, Paint, PlayerPlay, ClipboardText, Database, Recycle, Share } from "tabler-icons-react";
42
+ import { InfoCircle, DeviceFloppy, Refresh, Trash, PlaylistAdd, Settings, Resize, Paint, PlayerPlay, Database, Recycle, Share } from "tabler-icons-react";
43
43
  import RichTextEditor, { RichTextEditor as RichTextEditor$1 } from "@mantine/rte";
44
44
  import { useInputState, useElementSize, randomId } from "@mantine/hooks";
45
45
  import ReactEChartsCore from "echarts-for-react/lib/core";
@@ -68,6 +68,44 @@ const initialContext$3 = {
68
68
  inUseMode: true
69
69
  };
70
70
  const LayoutStateContext = React.createContext(initialContext$3);
71
+ function explainSQLSnippet(snippet, context) {
72
+ const names = Object.keys(context);
73
+ const vals = Object.values(context);
74
+ try {
75
+ return new Function(...names, `return \`${snippet}\`;`)(...vals);
76
+ } catch (error) {
77
+ console.error(error);
78
+ return error.message;
79
+ }
80
+ }
81
+ function formatSQL(sql, params) {
82
+ const names = Object.keys(params);
83
+ const vals = Object.values(params);
84
+ try {
85
+ return new Function(...names, `return \`${sql}\`;`)(...vals);
86
+ } catch (error) {
87
+ if (names.length === 0 && sql.includes("$")) {
88
+ throw new Error("[formatSQL] insufficient params");
89
+ }
90
+ throw error;
91
+ }
92
+ }
93
+ function getSQLParams(context, definitions) {
94
+ const sqlSnippetRecord = definitions.sqlSnippets.reduce((ret, curr) => {
95
+ ret[curr.key] = formatSQL(curr.value, context);
96
+ return ret;
97
+ }, {});
98
+ return _.merge({}, sqlSnippetRecord, context);
99
+ }
100
+ function explainSQL(sql, context, definitions) {
101
+ try {
102
+ const params = getSQLParams(context, definitions);
103
+ return formatSQL(sql, params);
104
+ } catch (error) {
105
+ console.error(error);
106
+ return error.message;
107
+ }
108
+ }
71
109
  const APIClient = {
72
110
  baseURL: "http://localhost:31200",
73
111
  getRequest(method) {
@@ -94,30 +132,11 @@ const APIClient = {
94
132
  };
95
133
  }
96
134
  };
97
- function formatSQL(sql, params) {
98
- const names = Object.keys(params);
99
- const vals = Object.values(params);
100
- try {
101
- return new Function(...names, `return \`${sql}\`;`)(...vals);
102
- } catch (error) {
103
- if (names.length === 0 && sql.includes("$")) {
104
- throw new Error("[formatSQL] insufficient params");
105
- }
106
- throw error;
107
- }
108
- }
109
- function getSQLParams(context, definitions) {
110
- const sqlSnippetRecord = definitions.sqlSnippets.reduce((ret, curr) => {
111
- ret[curr.key] = formatSQL(curr.value, context);
112
- return ret;
113
- }, {});
114
- return _.merge({}, sqlSnippetRecord, context);
115
- }
116
- const queryBySQL = ({ context, definitions, title, dataSource }) => async () => {
117
- if (!dataSource || !dataSource.sql) {
135
+ const queryBySQL = ({ context, definitions, title, query }) => async () => {
136
+ if (!query || !query.sql) {
118
137
  return [];
119
138
  }
120
- const { type, key, sql } = dataSource;
139
+ const { type, key, sql } = query;
121
140
  const needParams = sql.includes("$");
122
141
  try {
123
142
  const params = getSQLParams(context, definitions);
@@ -155,8 +174,8 @@ const initialContext$1 = {
155
174
  description: "",
156
175
  setDescription: () => {
157
176
  },
158
- dataSourceID: "",
159
- setDataSourceID: () => {
177
+ queryID: "",
178
+ setQueryID: () => {
160
179
  },
161
180
  viz: {
162
181
  type: "",
@@ -172,8 +191,8 @@ const initialContext = {
172
191
  sqlSnippets: [],
173
192
  setSQLSnippets: () => {
174
193
  },
175
- dataSources: [],
176
- setDataSources: () => {
194
+ queries: [],
195
+ setQueries: () => {
177
196
  }
178
197
  };
179
198
  const DefinitionContext = React.createContext(initialContext);
@@ -211,8 +230,8 @@ const jsx = jsxRuntime.exports.jsx;
211
230
  const jsxs = jsxRuntime.exports.jsxs;
212
231
  const Fragment = jsxRuntime.exports.Fragment;
213
232
  function DescriptionPopover({
214
- position,
215
- trigger = "click"
233
+ position = "bottom",
234
+ trigger = "hover"
216
235
  }) {
217
236
  const {
218
237
  freezeLayout
@@ -250,13 +269,14 @@ function DescriptionPopover({
250
269
  return /* @__PURE__ */ jsx(Popover, {
251
270
  opened,
252
271
  onClose: () => setOpened(false),
253
- withCloseButton: true,
272
+ withCloseButton: trigger === "click",
254
273
  withArrow: true,
255
274
  trapFocus: true,
256
275
  closeOnEscape: false,
257
276
  placement: "center",
258
277
  position,
259
278
  target,
279
+ width: "40vw",
260
280
  children: /* @__PURE__ */ jsx(RichTextEditor, {
261
281
  readOnly: true,
262
282
  value: description,
@@ -361,7 +381,7 @@ function PreviewPanel() {
361
381
  mx: "auto",
362
382
  mt: "xl",
363
383
  p: "5px",
364
- spacing: "xs",
384
+ spacing: 5,
365
385
  sx: {
366
386
  width: "600px",
367
387
  height: "450px",
@@ -373,8 +393,6 @@ function PreviewPanel() {
373
393
  position: "apart",
374
394
  noWrap: true,
375
395
  sx: {
376
- borderBottom: "1px solid #eee",
377
- paddingBottom: "5px",
378
396
  flexGrow: 0,
379
397
  flexShrink: 0
380
398
  },
@@ -473,9 +491,9 @@ function DataPreview({
473
491
  }) {
474
492
  const definitions = React.useContext(DefinitionContext);
475
493
  const contextInfo = React.useContext(ContextInfoContext);
476
- const dataSource = React.useMemo(() => {
477
- return definitions.dataSources.find((d) => d.id === id);
478
- }, [definitions.dataSources, id]);
494
+ const query = React.useMemo(() => {
495
+ return definitions.queries.find((d) => d.id === id);
496
+ }, [definitions.queries, id]);
479
497
  const {
480
498
  data = [],
481
499
  loading,
@@ -484,9 +502,9 @@ function DataPreview({
484
502
  context: contextInfo,
485
503
  definitions,
486
504
  title: id,
487
- dataSource
505
+ query
488
506
  }), {
489
- refreshDeps: [contextInfo, definitions, dataSource]
507
+ refreshDeps: [contextInfo, definitions, query]
490
508
  });
491
509
  if (loading) {
492
510
  return /* @__PURE__ */ jsx(LoadingOverlay, {
@@ -562,22 +580,22 @@ function DataPreview({
562
580
  })]
563
581
  });
564
582
  }
565
- function PickDataSource({}) {
583
+ function PickQuery({}) {
566
584
  const {
567
- dataSources
585
+ queries
568
586
  } = React.useContext(DefinitionContext);
569
587
  const {
570
- dataSourceID,
571
- setDataSourceID,
588
+ queryID,
589
+ setQueryID,
572
590
  data,
573
591
  loading
574
592
  } = React.useContext(PanelContext);
575
593
  const options = React.useMemo(() => {
576
- return dataSources.map((d) => ({
594
+ return queries.map((d) => ({
577
595
  value: d.id,
578
596
  label: d.id
579
597
  }));
580
- }, [dataSources]);
598
+ }, [queries]);
581
599
  return /* @__PURE__ */ jsxs(Group, {
582
600
  direction: "column",
583
601
  grow: true,
@@ -589,11 +607,11 @@ function PickDataSource({}) {
589
607
  alignItems: "baseline"
590
608
  },
591
609
  children: [/* @__PURE__ */ jsx(Text, {
592
- children: "Select a Data Source"
610
+ children: "Select a Query"
593
611
  }), /* @__PURE__ */ jsx(Select, {
594
612
  data: options,
595
- value: dataSourceID,
596
- onChange: setDataSourceID,
613
+ value: queryID,
614
+ onChange: setQueryID,
597
615
  allowDeselect: false,
598
616
  clearable: false,
599
617
  sx: {
@@ -601,7 +619,7 @@ function PickDataSource({}) {
601
619
  }
602
620
  })]
603
621
  }), /* @__PURE__ */ jsx(DataPreview, {
604
- id: dataSourceID
622
+ id: queryID
605
623
  })]
606
624
  });
607
625
  }
@@ -1530,6 +1548,18 @@ function getColorByColorConf(conf, dataRow) {
1530
1548
  }
1531
1549
  return "black";
1532
1550
  }
1551
+ function getNonStatsDataText(data) {
1552
+ if (data === null) {
1553
+ return "null";
1554
+ }
1555
+ if (data === void 0) {
1556
+ return "undefined";
1557
+ }
1558
+ if (Array.isArray(data)) {
1559
+ return `Array(${data.length})`;
1560
+ }
1561
+ return data.toString();
1562
+ }
1533
1563
  function VizStats(_a) {
1534
1564
  var _b = _a, {
1535
1565
  conf: _c
@@ -1556,6 +1586,9 @@ function VizStats(_a) {
1556
1586
  formatter
1557
1587
  } = content;
1558
1588
  const contentData = (_a2 = data == null ? void 0 : data[0]) == null ? void 0 : _a2[data_field];
1589
+ if (!["string", "number"].includes(typeof contentData)) {
1590
+ return getNonStatsDataText(contentData);
1591
+ }
1559
1592
  const contents = [prefix, numbro(contentData).format(formatter), postfix];
1560
1593
  return contents.join(" ");
1561
1594
  }, [content, data]);
@@ -1639,9 +1672,39 @@ function PreviewViz({}) {
1639
1672
  loading
1640
1673
  });
1641
1674
  }
1675
+ function _DataFieldSelector({
1676
+ label,
1677
+ required,
1678
+ value,
1679
+ onChange,
1680
+ data,
1681
+ sx
1682
+ }, ref) {
1683
+ const options = React.useMemo(() => {
1684
+ if (!Array.isArray(data) || data.length === 0) {
1685
+ return [];
1686
+ }
1687
+ const keys = Object.keys(data[0]);
1688
+ return keys.map((k2) => ({
1689
+ label: k2,
1690
+ value: k2
1691
+ }));
1692
+ }, [data]);
1693
+ return /* @__PURE__ */ jsx(Select, {
1694
+ ref,
1695
+ label,
1696
+ data: options,
1697
+ value,
1698
+ onChange,
1699
+ required,
1700
+ sx
1701
+ });
1702
+ }
1703
+ const DataFieldSelector = React.forwardRef(_DataFieldSelector);
1642
1704
  function VizBar3DPanel({
1643
1705
  conf,
1644
- setConf
1706
+ setConf,
1707
+ data
1645
1708
  }) {
1646
1709
  const defaultValues = _.assign({}, {
1647
1710
  "x_axis_data_key": "x",
@@ -1689,12 +1752,10 @@ function VizBar3DPanel({
1689
1752
  control,
1690
1753
  render: ({
1691
1754
  field
1692
- }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
1693
- sx: {
1694
- flexGrow: 1
1695
- },
1696
- size: "md",
1697
- label: "Data Key"
1755
+ }) => /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
1756
+ label: "Data Field",
1757
+ required: true,
1758
+ data
1698
1759
  }, field))
1699
1760
  }), /* @__PURE__ */ jsx(Controller, {
1700
1761
  name: "xAxis3D.name",
@@ -1725,12 +1786,10 @@ function VizBar3DPanel({
1725
1786
  control,
1726
1787
  render: ({
1727
1788
  field
1728
- }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
1729
- sx: {
1730
- flexGrow: 1
1731
- },
1732
- size: "md",
1733
- label: "Data Key"
1789
+ }) => /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
1790
+ label: "Data Field",
1791
+ required: true,
1792
+ data
1734
1793
  }, field))
1735
1794
  }), /* @__PURE__ */ jsx(Controller, {
1736
1795
  name: "yAxis3D.name",
@@ -1761,12 +1820,10 @@ function VizBar3DPanel({
1761
1820
  control,
1762
1821
  render: ({
1763
1822
  field
1764
- }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
1765
- sx: {
1766
- flexGrow: 1
1767
- },
1768
- size: "md",
1769
- label: "Data Key"
1823
+ }) => /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
1824
+ label: "Data Field",
1825
+ required: true,
1826
+ data
1770
1827
  }, field))
1771
1828
  }), /* @__PURE__ */ jsx(Controller, {
1772
1829
  name: "zAxis3D.name",
@@ -1904,7 +1961,8 @@ function SeriesItemField({
1904
1961
  index: index2,
1905
1962
  remove,
1906
1963
  seriesItem,
1907
- yAxisOptions
1964
+ yAxisOptions,
1965
+ data
1908
1966
  }) {
1909
1967
  const type = seriesItem.type;
1910
1968
  return /* @__PURE__ */ jsxs(Group, {
@@ -1965,9 +2023,10 @@ function SeriesItemField({
1965
2023
  control,
1966
2024
  render: ({
1967
2025
  field
1968
- }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
1969
- label: "Value key",
2026
+ }) => /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
2027
+ label: "Value Field",
1970
2028
  required: true,
2029
+ data,
1971
2030
  sx: {
1972
2031
  flex: 1
1973
2032
  }
@@ -2074,7 +2133,8 @@ function SeriesItemField({
2074
2133
  function SeriesField({
2075
2134
  control,
2076
2135
  watch,
2077
- getValues
2136
+ getValues,
2137
+ data
2078
2138
  }) {
2079
2139
  const {
2080
2140
  fields,
@@ -2118,7 +2178,8 @@ function SeriesField({
2118
2178
  index: index2,
2119
2179
  remove,
2120
2180
  seriesItem,
2121
- yAxisOptions
2181
+ yAxisOptions,
2182
+ data
2122
2183
  })), /* @__PURE__ */ jsx(Group, {
2123
2184
  position: "center",
2124
2185
  mt: "xs",
@@ -2319,7 +2380,8 @@ function withDefaults(series) {
2319
2380
  }
2320
2381
  function VizCartesianChartPanel({
2321
2382
  conf,
2322
- setConf
2383
+ setConf,
2384
+ data
2323
2385
  }) {
2324
2386
  const _a = conf, {
2325
2387
  series,
@@ -2385,38 +2447,41 @@ function VizCartesianChartPanel({
2385
2447
  size: 20
2386
2448
  })
2387
2449
  })]
2388
- }), /* @__PURE__ */ jsx(Controller, {
2389
- name: "x_axis_data_key",
2390
- control,
2391
- render: ({
2392
- field
2393
- }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
2394
- size: "md",
2395
- mb: "lg",
2396
- label: "X Axis Data Key"
2397
- }, field))
2398
- }), /* @__PURE__ */ jsx(Group, {
2450
+ }), /* @__PURE__ */ jsxs(Group, {
2399
2451
  direction: "column",
2400
2452
  grow: true,
2401
2453
  noWrap: true,
2402
2454
  mb: "lg",
2403
- children: /* @__PURE__ */ jsx(Controller, {
2455
+ children: [/* @__PURE__ */ jsx(Controller, {
2456
+ name: "x_axis_data_key",
2457
+ control,
2458
+ render: ({
2459
+ field
2460
+ }) => /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
2461
+ label: "X Axis Data Field",
2462
+ required: true,
2463
+ data,
2464
+ sx: {
2465
+ flex: 1
2466
+ }
2467
+ }, field))
2468
+ }), /* @__PURE__ */ jsx(Controller, {
2404
2469
  name: "x_axis_name",
2405
2470
  control,
2406
2471
  render: ({
2407
2472
  field
2408
2473
  }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
2409
- size: "md",
2410
2474
  label: "X Axis Name"
2411
2475
  }, field))
2412
- })
2476
+ })]
2413
2477
  }), /* @__PURE__ */ jsx(YAxesField, {
2414
2478
  control,
2415
2479
  watch
2416
2480
  }), /* @__PURE__ */ jsx(SeriesField, {
2417
2481
  control,
2418
2482
  watch,
2419
- getValues
2483
+ getValues,
2484
+ data
2420
2485
  })]
2421
2486
  })
2422
2487
  });
@@ -2426,7 +2491,8 @@ function VizPiePanel({
2426
2491
  label_field,
2427
2492
  value_field
2428
2493
  },
2429
- setConf
2494
+ setConf,
2495
+ data
2430
2496
  }) {
2431
2497
  const form = useForm$1({
2432
2498
  initialValues: {
@@ -2469,19 +2535,14 @@ function VizPiePanel({
2469
2535
  border: "1px solid #eee",
2470
2536
  borderRadius: "5px"
2471
2537
  },
2472
- children: [/* @__PURE__ */ jsx(TextInput, __spreadValues({
2538
+ children: [/* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
2473
2539
  label: "Label Field",
2474
2540
  required: true,
2475
- sx: {
2476
- flex: 1
2477
- }
2478
- }, form.getInputProps("label_field"))), /* @__PURE__ */ jsx(TextInput, __spreadValues({
2541
+ data
2542
+ }, form.getInputProps("label_field"))), /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
2479
2543
  label: "Value Field",
2480
- placeholder: "get column value by this field",
2481
2544
  required: true,
2482
- sx: {
2483
- flex: 1
2484
- }
2545
+ data
2485
2546
  }, form.getInputProps("value_field")))]
2486
2547
  })]
2487
2548
  })
@@ -2679,7 +2740,8 @@ function _ColorArrayInput({
2679
2740
  const ColorArrayInput = React.forwardRef(_ColorArrayInput);
2680
2741
  function VizStatsPanel({
2681
2742
  conf,
2682
- setConf
2743
+ setConf,
2744
+ data
2683
2745
  }) {
2684
2746
  const defaultValues = _.merge({}, {
2685
2747
  align: "center",
@@ -2755,6 +2817,7 @@ function VizStatsPanel({
2755
2817
  children: [/* @__PURE__ */ jsxs(Group, {
2756
2818
  direction: "row",
2757
2819
  grow: true,
2820
+ noWrap: true,
2758
2821
  children: [/* @__PURE__ */ jsx(Controller, {
2759
2822
  name: "content.prefix",
2760
2823
  control,
@@ -2771,12 +2834,10 @@ function VizStatsPanel({
2771
2834
  control,
2772
2835
  render: ({
2773
2836
  field
2774
- }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
2837
+ }) => /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
2775
2838
  label: "Data Field",
2776
2839
  required: true,
2777
- sx: {
2778
- flexGrow: 1
2779
- }
2840
+ data
2780
2841
  }, field))
2781
2842
  }), /* @__PURE__ */ jsx(Controller, {
2782
2843
  name: "content.postfix",
@@ -2905,7 +2966,8 @@ function SunburstPanel({
2905
2966
  label_field,
2906
2967
  value_field
2907
2968
  },
2908
- setConf
2969
+ setConf,
2970
+ data
2909
2971
  }) {
2910
2972
  const form = useForm$1({
2911
2973
  initialValues: {
@@ -2948,19 +3010,14 @@ function SunburstPanel({
2948
3010
  border: "1px solid #eee",
2949
3011
  borderRadius: "5px"
2950
3012
  },
2951
- children: [/* @__PURE__ */ jsx(TextInput, __spreadValues({
3013
+ children: [/* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
2952
3014
  label: "Label Field",
2953
3015
  required: true,
2954
- sx: {
2955
- flex: 1
2956
- }
2957
- }, form.getInputProps("label_field"))), /* @__PURE__ */ jsx(TextInput, __spreadValues({
3016
+ data
3017
+ }, form.getInputProps("label_field"))), /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
2958
3018
  label: "Value Field",
2959
- placeholder: "get column value by this field",
2960
3019
  required: true,
2961
- sx: {
2962
- flex: 1
2963
- }
3020
+ data
2964
3021
  }, form.getInputProps("value_field")))]
2965
3022
  })]
2966
3023
  })
@@ -2992,7 +3049,8 @@ function VizTablePanel(_e) {
2992
3049
  } = _h, restConf = __objRest(_h, [
2993
3050
  "columns"
2994
3051
  ]), {
2995
- setConf
3052
+ setConf,
3053
+ data
2996
3054
  } = _f;
2997
3055
  const form = useForm$1({
2998
3056
  initialValues: __spreadValues({
@@ -3046,10 +3104,10 @@ function VizTablePanel(_e) {
3046
3104
  border: "1px solid #eee",
3047
3105
  borderRadius: "5px"
3048
3106
  },
3049
- children: [/* @__PURE__ */ jsx(TextInput, __spreadValues({
3050
- size: "md",
3051
- mb: "lg",
3052
- label: "ID Field"
3107
+ children: [/* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
3108
+ label: "ID Field",
3109
+ required: true,
3110
+ data
3053
3111
  }, form.getInputProps("id_field"))), /* @__PURE__ */ jsxs(Group, {
3054
3112
  position: "apart",
3055
3113
  mb: "lg",
@@ -3145,25 +3203,16 @@ function VizTablePanel(_e) {
3145
3203
  children: [/* @__PURE__ */ jsxs(Group, {
3146
3204
  position: "apart",
3147
3205
  grow: true,
3148
- sx: {
3149
- "> *": {
3150
- flexGrow: 1,
3151
- maxWidth: "100%"
3152
- }
3153
- },
3154
3206
  children: [/* @__PURE__ */ jsx(TextInput, __spreadValues({
3155
3207
  label: "Label",
3156
3208
  required: true,
3157
3209
  sx: {
3158
3210
  flex: 1
3159
3211
  }
3160
- }, form.getListInputProps("columns", index2, "label"))), /* @__PURE__ */ jsx(TextInput, __spreadValues({
3212
+ }, form.getListInputProps("columns", index2, "label"))), /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
3161
3213
  label: "Value Field",
3162
- placeholder: "get column value by this field",
3163
3214
  required: true,
3164
- sx: {
3165
- flex: 1
3166
- }
3215
+ data
3167
3216
  }, form.getListInputProps("columns", index2, "value_field"))), /* @__PURE__ */ jsx(ValueTypeSelector, __spreadValues({
3168
3217
  label: "Value Type",
3169
3218
  sx: {
@@ -3371,6 +3420,7 @@ const types = [{
3371
3420
  }];
3372
3421
  function EditVizConf() {
3373
3422
  const {
3423
+ data,
3374
3424
  viz,
3375
3425
  setViz
3376
3426
  } = React.useContext(PanelContext);
@@ -3415,7 +3465,8 @@ function EditVizConf() {
3415
3465
  })
3416
3466
  }), Panel2 && /* @__PURE__ */ jsx(Panel2, {
3417
3467
  conf: viz.conf,
3418
- setConf: setVizConf
3468
+ setConf: setVizConf,
3469
+ data
3419
3470
  }), !Panel2 && /* @__PURE__ */ jsx(JsonInput, {
3420
3471
  minRows: 20,
3421
3472
  label: "Config",
@@ -3495,11 +3546,11 @@ function PanelSettingsModal({
3495
3546
  children: /* @__PURE__ */ jsxs(Tabs, {
3496
3547
  initialTab: 2,
3497
3548
  children: [/* @__PURE__ */ jsxs(Tabs.Tab, {
3498
- label: "Data Source",
3549
+ label: "Data",
3499
3550
  children: [/* @__PURE__ */ jsx(LoadingOverlay, {
3500
3551
  visible: loading,
3501
3552
  exitTransitionDuration: 0
3502
- }), /* @__PURE__ */ jsx(PickDataSource, {})]
3553
+ }), /* @__PURE__ */ jsx(PickQuery, {})]
3503
3554
  }), /* @__PURE__ */ jsx(Tabs.Tab, {
3504
3555
  label: "Panel",
3505
3556
  children: /* @__PURE__ */ jsx(PanelConfig, {})
@@ -3511,6 +3562,7 @@ function PanelSettingsModal({
3511
3562
  })
3512
3563
  });
3513
3564
  }
3565
+ var titleBar = "";
3514
3566
  function PanelTitleBar({}) {
3515
3567
  const [opened, setOpened] = React.useState(false);
3516
3568
  const open = () => setOpened(true);
@@ -3522,30 +3574,33 @@ function PanelTitleBar({}) {
3522
3574
  const {
3523
3575
  inEditMode
3524
3576
  } = React.useContext(LayoutStateContext);
3525
- return /* @__PURE__ */ jsxs(Group, {
3526
- position: "apart",
3527
- noWrap: true,
3577
+ return /* @__PURE__ */ jsxs(Box, {
3528
3578
  sx: {
3529
- borderBottom: "1px solid #eee",
3530
- paddingBottom: "5px"
3579
+ position: "relative"
3531
3580
  },
3532
- children: [/* @__PURE__ */ jsx(Group, {
3581
+ children: [/* @__PURE__ */ jsx(Box, {
3582
+ sx: {
3583
+ position: "absolute",
3584
+ left: 0,
3585
+ top: 0,
3586
+ height: 28
3587
+ },
3533
3588
  children: /* @__PURE__ */ jsx(DescriptionPopover, {})
3534
3589
  }), /* @__PURE__ */ jsx(Group, {
3535
3590
  grow: true,
3536
3591
  position: "center",
3537
- children: /* @__PURE__ */ jsx(Text, {
3538
- lineClamp: 1,
3539
- weight: "bold",
3540
- children: title
3541
- })
3542
- }), /* @__PURE__ */ jsx(Group, {
3543
- position: "right",
3544
- spacing: 0,
3592
+ px: 20,
3593
+ className: "panel-title-wrapper",
3545
3594
  sx: {
3546
- height: "28px"
3595
+ flexGrow: 1
3547
3596
  },
3548
3597
  children: /* @__PURE__ */ jsxs(Menu, {
3598
+ control: /* @__PURE__ */ jsx(Text, {
3599
+ lineClamp: 1,
3600
+ weight: "bold",
3601
+ children: title
3602
+ }),
3603
+ placement: "center",
3549
3604
  children: [/* @__PURE__ */ jsx(Menu.Item, {
3550
3605
  onClick: refreshData,
3551
3606
  icon: /* @__PURE__ */ jsx(Refresh, {
@@ -3576,7 +3631,7 @@ function PanelTitleBar({}) {
3576
3631
  var index$1 = "";
3577
3632
  function Panel({
3578
3633
  viz: initialViz,
3579
- dataSourceID: initialDataSourceID,
3634
+ queryID: initialQueryID,
3580
3635
  title: initialTitle,
3581
3636
  description: initialDesc,
3582
3637
  update,
@@ -3587,24 +3642,24 @@ function Panel({
3587
3642
  const definitions = React.useContext(DefinitionContext);
3588
3643
  const [title, setTitle] = React.useState(initialTitle);
3589
3644
  const [description, setDescription] = React.useState(initialDesc);
3590
- const [dataSourceID, setDataSourceID] = React.useState(initialDataSourceID);
3645
+ const [queryID, setQueryID] = React.useState(initialQueryID);
3591
3646
  const [viz, setViz] = React.useState(initialViz);
3592
- const dataSource = React.useMemo(() => {
3593
- if (!dataSourceID) {
3647
+ const query = React.useMemo(() => {
3648
+ if (!queryID) {
3594
3649
  return void 0;
3595
3650
  }
3596
- return definitions.dataSources.find((d) => d.id === dataSourceID);
3597
- }, [dataSourceID, definitions.dataSources]);
3651
+ return definitions.queries.find((d) => d.id === queryID);
3652
+ }, [queryID, definitions.queries]);
3598
3653
  React.useEffect(() => {
3599
3654
  update == null ? void 0 : update({
3600
3655
  id,
3601
3656
  layout,
3602
3657
  title,
3603
3658
  description,
3604
- dataSourceID,
3659
+ queryID,
3605
3660
  viz
3606
3661
  });
3607
- }, [title, description, dataSource, viz, id, layout, dataSourceID]);
3662
+ }, [title, description, query, viz, id, layout, queryID]);
3608
3663
  const {
3609
3664
  data = [],
3610
3665
  loading,
@@ -3613,9 +3668,9 @@ function Panel({
3613
3668
  context: contextInfo,
3614
3669
  definitions,
3615
3670
  title,
3616
- dataSource
3671
+ query
3617
3672
  }), {
3618
- refreshDeps: [contextInfo, definitions, dataSource]
3673
+ refreshDeps: [contextInfo, definitions, query]
3619
3674
  });
3620
3675
  const refreshData = refresh;
3621
3676
  return /* @__PURE__ */ jsx(PanelContext.Provider, {
@@ -3626,8 +3681,8 @@ function Panel({
3626
3681
  setTitle,
3627
3682
  description,
3628
3683
  setDescription,
3629
- dataSourceID,
3630
- setDataSourceID,
3684
+ queryID,
3685
+ setQueryID,
3631
3686
  viz,
3632
3687
  setViz,
3633
3688
  refreshData
@@ -3764,7 +3819,7 @@ function ContextAndSnippets({}) {
3764
3819
  grow: true,
3765
3820
  sx: {
3766
3821
  border: "1px solid #eee",
3767
- maxWidth: "48%",
3822
+ maxWidth: "40%",
3768
3823
  overflow: "hidden"
3769
3824
  },
3770
3825
  children: [/* @__PURE__ */ jsx(Group, {
@@ -3827,7 +3882,21 @@ function ContextAndSnippets({}) {
3827
3882
  })]
3828
3883
  });
3829
3884
  }
3830
- function DataSourceForm({
3885
+ function PreviewSQL({
3886
+ value
3887
+ }) {
3888
+ const context = React.useContext(ContextInfoContext);
3889
+ const definition = React.useContext(DefinitionContext);
3890
+ const explained = React.useMemo(() => {
3891
+ return explainSQL(value, context, definition);
3892
+ }, [value, context, definition]);
3893
+ return /* @__PURE__ */ jsx(Prism, {
3894
+ language: "sql",
3895
+ colorScheme: "light",
3896
+ children: explained
3897
+ });
3898
+ }
3899
+ function QueryForm({
3831
3900
  value,
3832
3901
  onChange
3833
3902
  }) {
@@ -3884,7 +3953,7 @@ function DataSourceForm({
3884
3953
  },
3885
3954
  children: [/* @__PURE__ */ jsx(Text, {
3886
3955
  weight: 500,
3887
- children: "Data Source Configuration"
3956
+ children: "Edit Query"
3888
3957
  }), /* @__PURE__ */ jsx(ActionIcon, {
3889
3958
  type: "submit",
3890
3959
  mr: 5,
@@ -3926,97 +3995,102 @@ function DataSourceForm({
3926
3995
  },
3927
3996
  disabled: loading
3928
3997
  }, form.getInputProps("key")))]
3929
- }), /* @__PURE__ */ jsx(Textarea, __spreadValues({
3930
- autosize: true,
3931
- minRows: 12,
3932
- maxRows: 24
3933
- }, form.getInputProps("sql")))]
3998
+ }), /* @__PURE__ */ jsxs(Tabs, {
3999
+ children: [/* @__PURE__ */ jsx(Tabs.Tab, {
4000
+ label: "SQL",
4001
+ children: /* @__PURE__ */ jsx(Textarea, __spreadProps(__spreadValues({
4002
+ autosize: true,
4003
+ minRows: 12,
4004
+ maxRows: 24
4005
+ }, form.getInputProps("sql")), {
4006
+ className: "code-textarea"
4007
+ }))
4008
+ }), /* @__PURE__ */ jsx(Tabs.Tab, {
4009
+ label: "Preview",
4010
+ children: /* @__PURE__ */ jsx(PreviewSQL, {
4011
+ value: form.values.sql
4012
+ })
4013
+ })]
4014
+ })]
3934
4015
  })]
3935
4016
  })
3936
4017
  });
3937
4018
  }
3938
- function DataSourceEditor({
4019
+ function QueryEditor({
3939
4020
  id,
3940
4021
  setID
3941
4022
  }) {
3942
4023
  const {
3943
- dataSources,
3944
- setDataSources
4024
+ queries,
4025
+ setQueries
3945
4026
  } = React.useContext(DefinitionContext);
3946
- const dataSource = React.useMemo(() => {
3947
- return dataSources.find((d) => d.id === id);
3948
- }, [dataSources, id]);
4027
+ const query = React.useMemo(() => {
4028
+ return queries.find((d) => d.id === id);
4029
+ }, [queries, id]);
3949
4030
  const update = React.useCallback((value) => {
3950
- const index2 = dataSources.findIndex((d) => d.id === id);
4031
+ const index2 = queries.findIndex((d) => d.id === id);
3951
4032
  if (index2 === -1) {
3952
4033
  console.error(new Error("Invalid data source id when updating by id"));
3953
4034
  return;
3954
4035
  }
3955
- setDataSources((prevs) => {
4036
+ setQueries((prevs) => {
3956
4037
  const index22 = prevs.findIndex((p2) => p2.id === id);
3957
4038
  prevs.splice(index22, 1, value);
3958
4039
  return [...prevs];
3959
4040
  });
3960
4041
  setID(value.id);
3961
- }, [id, dataSources, setDataSources, setID]);
4042
+ }, [id, queries, setQueries, setID]);
3962
4043
  if (!id) {
3963
4044
  return null;
3964
4045
  }
3965
- if (!dataSource) {
4046
+ if (!query) {
3966
4047
  return /* @__PURE__ */ jsx("span", {
3967
4048
  children: "Invalid Data Source ID"
3968
4049
  });
3969
4050
  }
3970
- return /* @__PURE__ */ jsxs(Group, {
3971
- direction: "row",
3972
- position: "apart",
3973
- grow: true,
3974
- align: "stretch",
3975
- noWrap: true,
3976
- children: [/* @__PURE__ */ jsx(DataSourceForm, {
3977
- value: dataSource,
3978
- onChange: update
3979
- }), /* @__PURE__ */ jsx(ContextAndSnippets, {})]
4051
+ return /* @__PURE__ */ jsx(QueryForm, {
4052
+ value: query,
4053
+ onChange: update
3980
4054
  });
3981
4055
  }
3982
- function SelectOrAddDataSource({
4056
+ function SelectOrAddQuery({
3983
4057
  id,
3984
4058
  setID
3985
4059
  }) {
3986
4060
  const {
3987
- dataSources,
3988
- setDataSources
4061
+ queries,
4062
+ setQueries
3989
4063
  } = React.useContext(DefinitionContext);
3990
4064
  const chooseDefault = React.useCallback(() => {
3991
4065
  var _a, _b;
3992
- setID((_b = (_a = dataSources[0]) == null ? void 0 : _a.id) != null ? _b : "");
3993
- }, [setID, dataSources]);
4066
+ setID((_b = (_a = queries[0]) == null ? void 0 : _a.id) != null ? _b : "");
4067
+ }, [setID, queries]);
3994
4068
  React.useEffect(() => {
3995
4069
  if (!id) {
3996
4070
  chooseDefault();
3997
4071
  return;
3998
4072
  }
3999
- const index2 = dataSources.findIndex((d) => d.id === id);
4073
+ const index2 = queries.findIndex((d) => d.id === id);
4000
4074
  if (index2 === -1) {
4001
4075
  chooseDefault();
4002
4076
  }
4003
- }, [id, dataSources, chooseDefault]);
4077
+ }, [id, queries, chooseDefault]);
4004
4078
  const options = React.useMemo(() => {
4005
- return dataSources.map((d) => ({
4079
+ return queries.map((d) => ({
4006
4080
  value: d.id,
4007
4081
  label: d.id
4008
4082
  }));
4009
- }, [dataSources]);
4083
+ }, [queries]);
4010
4084
  const add = React.useCallback(() => {
4011
- const newDataSource = {
4085
+ const newQuery = {
4012
4086
  id: randomId(),
4013
4087
  type: "postgresql",
4014
4088
  key: "",
4015
4089
  sql: ""
4016
4090
  };
4017
- setDataSources((prevs) => [...prevs, newDataSource]);
4018
- setID(newDataSource.id);
4019
- }, [setDataSources, setID]);
4091
+ setQueries((prevs) => [...prevs, newQuery]);
4092
+ setID(newQuery.id);
4093
+ }, [setQueries, setID]);
4020
4094
  return /* @__PURE__ */ jsx(Group, {
4021
4095
  pb: "xl",
4022
4096
  children: /* @__PURE__ */ jsxs(Group, {
@@ -4026,7 +4100,7 @@ function SelectOrAddDataSource({
4026
4100
  alignItems: "baseline"
4027
4101
  },
4028
4102
  children: [/* @__PURE__ */ jsx(Text, {
4029
- children: "Select a Data Source"
4103
+ children: "Select a Query"
4030
4104
  }), /* @__PURE__ */ jsx(Select, {
4031
4105
  data: options,
4032
4106
  value: id,
@@ -4043,64 +4117,58 @@ function SelectOrAddDataSource({
4043
4117
  mt: "md",
4044
4118
  children: /* @__PURE__ */ jsx(Button, {
4045
4119
  onClick: add,
4046
- children: "Add a Data Source"
4120
+ children: "Add a Query"
4047
4121
  })
4048
4122
  })]
4049
4123
  })
4050
4124
  });
4051
4125
  }
4052
- function EditDataSourcesModal({
4053
- opened,
4054
- close
4055
- }) {
4126
+ function EditQueries({}) {
4056
4127
  const [id, setID] = React.useState("");
4057
- const {
4058
- freezeLayout
4059
- } = React.useContext(LayoutStateContext);
4060
- React.useEffect(() => {
4061
- freezeLayout(opened);
4062
- }, [opened]);
4063
- return /* @__PURE__ */ jsx(Modal, {
4064
- size: "96vw",
4065
- overflow: "inside",
4066
- opened,
4067
- onClose: close,
4068
- title: "Data Sources",
4069
- trapFocus: true,
4070
- onDragStart: (e) => {
4071
- e.stopPropagation();
4128
+ return /* @__PURE__ */ jsxs(AppShell, {
4129
+ sx: {
4130
+ height: "90vh",
4131
+ maxHeight: "calc(100vh - 225px)",
4132
+ ".mantine-AppShell-body": {
4133
+ height: "100%"
4134
+ },
4135
+ main: {
4136
+ height: "100%",
4137
+ width: "100%",
4138
+ padding: 0,
4139
+ margin: 0
4140
+ }
4072
4141
  },
4073
- children: /* @__PURE__ */ jsxs(AppShell, {
4074
- sx: {
4075
- height: "90vh",
4076
- maxHeight: "calc(100vh - 185px)",
4077
- ".mantine-AppShell-body": {
4078
- height: "100%"
4142
+ padding: "md",
4143
+ children: [/* @__PURE__ */ jsxs(Group, {
4144
+ direction: "row",
4145
+ position: "apart",
4146
+ grow: true,
4147
+ align: "stretch",
4148
+ noWrap: true,
4149
+ children: [/* @__PURE__ */ jsxs(Group, {
4150
+ direction: "column",
4151
+ grow: true,
4152
+ sx: {
4153
+ flexGrow: 1,
4154
+ maxWidth: "calc(60% - 16px)"
4079
4155
  },
4080
- main: {
4081
- height: "100%",
4082
- width: "100%",
4083
- padding: 0,
4084
- margin: 0
4085
- }
4086
- },
4087
- padding: "md",
4088
- header: /* @__PURE__ */ jsx(SelectOrAddDataSource, {
4089
- id,
4090
- setID
4091
- }),
4092
- children: [/* @__PURE__ */ jsx(DataSourceEditor, {
4093
- id,
4094
- setID
4095
- }), /* @__PURE__ */ jsx(DataPreview, {
4096
- id
4097
- })]
4098
- })
4156
+ children: [/* @__PURE__ */ jsx(SelectOrAddQuery, {
4157
+ id,
4158
+ setID
4159
+ }), /* @__PURE__ */ jsx(QueryEditor, {
4160
+ id,
4161
+ setID
4162
+ })]
4163
+ }), /* @__PURE__ */ jsx(ContextAndSnippets, {})]
4164
+ }), /* @__PURE__ */ jsx(DataPreview, {
4165
+ id
4166
+ })]
4099
4167
  });
4100
4168
  }
4101
4169
  function ContextInfo({}) {
4102
4170
  const contextInfo = React.useContext(ContextInfoContext);
4103
- const sampleSQL = `SELECT *
4171
+ const sampleSQL2 = `SELECT *
4104
4172
  FROM commit
4105
4173
  WHERE author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.[1].toISOString()}'`;
4106
4174
  return /* @__PURE__ */ jsxs(Group, {
@@ -4108,7 +4176,6 @@ WHERE author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.
4108
4176
  grow: true,
4109
4177
  sx: {
4110
4178
  border: "1px solid #eee",
4111
- maxWidth: "48%",
4112
4179
  overflow: "hidden"
4113
4180
  },
4114
4181
  children: [/* @__PURE__ */ jsx(Group, {
@@ -4141,7 +4208,7 @@ WHERE author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.
4141
4208
  children: `-- You may refer context data *by name*
4142
4209
  -- in SQL or VizConfig.
4143
4210
 
4144
- ${sampleSQL}`
4211
+ ${sampleSQL2}`
4145
4212
  }), /* @__PURE__ */ jsx(Text, {
4146
4213
  weight: 500,
4147
4214
  sx: {
@@ -4160,14 +4227,32 @@ ${sampleSQL}`
4160
4227
  })]
4161
4228
  });
4162
4229
  }
4230
+ function PreviewSnippet({
4231
+ value
4232
+ }) {
4233
+ const context = React.useContext(ContextInfoContext);
4234
+ const explained = React.useMemo(() => {
4235
+ return explainSQLSnippet(value, context);
4236
+ }, [value, context]);
4237
+ return /* @__PURE__ */ jsxs(Group, {
4238
+ direction: "column",
4239
+ noWrap: true,
4240
+ grow: true,
4241
+ children: [/* @__PURE__ */ jsx(Text, {
4242
+ children: "Preview"
4243
+ }), /* @__PURE__ */ jsx(Prism, {
4244
+ language: "sql",
4245
+ noCopy: true,
4246
+ colorScheme: "dark",
4247
+ children: explained
4248
+ })]
4249
+ });
4250
+ }
4163
4251
  function SQLSnippetsEditor({}) {
4164
4252
  const {
4165
4253
  sqlSnippets,
4166
4254
  setSQLSnippets
4167
4255
  } = React.useContext(DefinitionContext);
4168
- const sampleSQL = `SELECT *
4169
- FROM commit
4170
- WHERE \${author_time_condition}`;
4171
4256
  const initialValues = React.useMemo(() => ({
4172
4257
  snippets: formList(sqlSnippets != null ? sqlSnippets : [])
4173
4258
  }), [sqlSnippets]);
@@ -4188,7 +4273,8 @@ WHERE \${author_time_condition}`;
4188
4273
  direction: "column",
4189
4274
  grow: true,
4190
4275
  sx: {
4191
- border: "1px solid #eee"
4276
+ border: "1px solid #eee",
4277
+ flexGrow: 1
4192
4278
  },
4193
4279
  children: /* @__PURE__ */ jsxs("form", {
4194
4280
  onSubmit: form.onSubmit(submit),
@@ -4214,26 +4300,11 @@ WHERE \${author_time_condition}`;
4214
4300
  size: 20
4215
4301
  })
4216
4302
  })]
4217
- }), /* @__PURE__ */ jsxs(Group, {
4303
+ }), /* @__PURE__ */ jsx(Group, {
4218
4304
  px: "md",
4219
4305
  pb: "md",
4220
- children: [/* @__PURE__ */ jsx(Prism, {
4221
- language: "sql",
4222
- sx: {
4223
- width: "100%"
4224
- },
4225
- noCopy: true,
4226
- trim: false,
4227
- colorScheme: "dark",
4228
- children: `-- You may refer context data *by name*
4229
- -- in SQL or VizConfig.
4230
-
4231
- ${sampleSQL}
4232
-
4233
- -- where author_time_condition is:
4234
- author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.[1].toISOString()}'
4235
- `
4236
- }), /* @__PURE__ */ jsxs(Group, {
4306
+ pt: "md",
4307
+ children: /* @__PURE__ */ jsxs(Group, {
4237
4308
  direction: "column",
4238
4309
  sx: {
4239
4310
  width: "100%",
@@ -4253,11 +4324,15 @@ author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.[1].to
4253
4324
  children: [/* @__PURE__ */ jsx(TextInput, __spreadValues({
4254
4325
  label: "Key",
4255
4326
  required: true
4256
- }, form.getListInputProps("snippets", index2, "key"))), /* @__PURE__ */ jsx(Textarea, __spreadValues({
4327
+ }, form.getListInputProps("snippets", index2, "key"))), /* @__PURE__ */ jsx(Textarea, __spreadProps(__spreadValues({
4257
4328
  minRows: 3,
4258
4329
  label: "Value",
4259
4330
  required: true
4260
- }, form.getListInputProps("snippets", index2, "value"))), /* @__PURE__ */ jsx(ActionIcon, {
4331
+ }, form.getListInputProps("snippets", index2, "value")), {
4332
+ className: "code-textarea"
4333
+ })), /* @__PURE__ */ jsx(PreviewSnippet, {
4334
+ value: form.values.snippets[index2].value
4335
+ }), /* @__PURE__ */ jsx(ActionIcon, {
4261
4336
  color: "red",
4262
4337
  variant: "hover",
4263
4338
  onClick: () => form.removeListItem("snippets", index2),
@@ -4284,12 +4359,97 @@ author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.[1].to
4284
4359
  children: "Add a snippet"
4285
4360
  })
4286
4361
  })]
4287
- })]
4362
+ })
4288
4363
  })]
4289
4364
  })
4290
4365
  });
4291
4366
  }
4292
- function EditSQLSnippetsModal({
4367
+ const sampleSQL = `SELECT *
4368
+ FROM commit
4369
+ WHERE \${author_time_condition}`;
4370
+ function SQLSnippetGuide() {
4371
+ return /* @__PURE__ */ jsxs(Group, {
4372
+ direction: "column",
4373
+ grow: true,
4374
+ sx: {
4375
+ border: "1px solid #eee",
4376
+ overflow: "hidden"
4377
+ },
4378
+ children: [/* @__PURE__ */ jsx(Group, {
4379
+ position: "left",
4380
+ pl: "md",
4381
+ py: "md",
4382
+ sx: {
4383
+ borderBottom: "1px solid #eee",
4384
+ background: "#efefef",
4385
+ flexGrow: 0
4386
+ },
4387
+ children: /* @__PURE__ */ jsx(Text, {
4388
+ weight: 500,
4389
+ children: "Guide"
4390
+ })
4391
+ }), /* @__PURE__ */ jsx(Group, {
4392
+ direction: "column",
4393
+ px: "md",
4394
+ pb: "md",
4395
+ sx: {
4396
+ width: "100%"
4397
+ },
4398
+ children: /* @__PURE__ */ jsx(Prism, {
4399
+ language: "sql",
4400
+ sx: {
4401
+ width: "100%"
4402
+ },
4403
+ noCopy: true,
4404
+ trim: false,
4405
+ colorScheme: "dark",
4406
+ children: `-- You may refer context data *by name*
4407
+ -- in SQL or VizConfig.
4408
+
4409
+ ${sampleSQL}
4410
+
4411
+ -- where author_time_condition is:
4412
+ author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.[1].toISOString()}'
4413
+ `
4414
+ })
4415
+ })]
4416
+ });
4417
+ }
4418
+ function EditSQLSnippets({}) {
4419
+ return /* @__PURE__ */ jsx(AppShell, {
4420
+ sx: {
4421
+ height: "90vh",
4422
+ maxHeight: "calc(100vh - 225px)",
4423
+ ".mantine-AppShell-body": {
4424
+ height: "100%"
4425
+ },
4426
+ main: {
4427
+ height: "100%",
4428
+ width: "100%",
4429
+ padding: 0,
4430
+ margin: 0
4431
+ }
4432
+ },
4433
+ padding: "md",
4434
+ children: /* @__PURE__ */ jsxs(Group, {
4435
+ direction: "row",
4436
+ position: "apart",
4437
+ grow: true,
4438
+ align: "stretch",
4439
+ noWrap: true,
4440
+ children: [/* @__PURE__ */ jsx(SQLSnippetsEditor, {}), /* @__PURE__ */ jsxs(Group, {
4441
+ direction: "column",
4442
+ grow: true,
4443
+ noWrap: true,
4444
+ sx: {
4445
+ maxWidth: "40%"
4446
+ },
4447
+ children: [/* @__PURE__ */ jsx(SQLSnippetGuide, {}), /* @__PURE__ */ jsx(ContextInfo, {})]
4448
+ })]
4449
+ })
4450
+ });
4451
+ }
4452
+ function DataEditorModal({
4293
4453
  opened,
4294
4454
  close
4295
4455
  }) {
@@ -4304,34 +4464,19 @@ function EditSQLSnippetsModal({
4304
4464
  overflow: "inside",
4305
4465
  opened,
4306
4466
  onClose: close,
4307
- title: "SQL Snippets",
4467
+ title: "Data Settings",
4308
4468
  trapFocus: true,
4309
4469
  onDragStart: (e) => {
4310
4470
  e.stopPropagation();
4311
4471
  },
4312
- children: /* @__PURE__ */ jsx(AppShell, {
4313
- sx: {
4314
- height: "90vh",
4315
- maxHeight: "calc(100vh - 185px)",
4316
- ".mantine-AppShell-body": {
4317
- height: "100%"
4318
- },
4319
- main: {
4320
- height: "100%",
4321
- width: "100%",
4322
- padding: 0,
4323
- margin: 0
4324
- }
4325
- },
4326
- padding: "md",
4327
- children: /* @__PURE__ */ jsxs(Group, {
4328
- direction: "row",
4329
- position: "apart",
4330
- grow: true,
4331
- align: "stretch",
4332
- noWrap: true,
4333
- children: [/* @__PURE__ */ jsx(SQLSnippetsEditor, {}), /* @__PURE__ */ jsx(ContextInfo, {})]
4334
- })
4472
+ children: /* @__PURE__ */ jsxs(Tabs, {
4473
+ children: [/* @__PURE__ */ jsx(Tabs.Tab, {
4474
+ label: "SQL Snippet",
4475
+ children: /* @__PURE__ */ jsx(EditSQLSnippets, {})
4476
+ }), /* @__PURE__ */ jsx(Tabs.Tab, {
4477
+ label: "Queries",
4478
+ children: /* @__PURE__ */ jsx(EditQueries, {})
4479
+ })]
4335
4480
  })
4336
4481
  });
4337
4482
  }
@@ -4347,12 +4492,9 @@ function DashboardActions({
4347
4492
  inEditMode,
4348
4493
  inUseMode
4349
4494
  } = React.useContext(LayoutStateContext);
4350
- const [dataSourcesOpened, setDataSourcesOpened] = React.useState(false);
4351
- const openDataSources = () => setDataSourcesOpened(true);
4352
- const closeDataSources = () => setDataSourcesOpened(false);
4353
- const [sqlSnippetsOpened, setSQLSnippetsOpened] = React.useState(false);
4354
- const openSQLSnippets = () => setSQLSnippetsOpened(true);
4355
- const closeSQLSnippets = () => setSQLSnippetsOpened(false);
4495
+ const [dataEditorOpened, setDataEditorOpened] = React.useState(false);
4496
+ const openQueries = () => setDataEditorOpened(true);
4497
+ const closeQueries = () => setDataEditorOpened(false);
4356
4498
  return /* @__PURE__ */ jsxs(Group, {
4357
4499
  position: "apart",
4358
4500
  pt: "sm",
@@ -4376,19 +4518,11 @@ function DashboardActions({
4376
4518
  }), inEditMode && /* @__PURE__ */ jsx(Button, {
4377
4519
  variant: "default",
4378
4520
  size: "sm",
4379
- onClick: openSQLSnippets,
4380
- leftIcon: /* @__PURE__ */ jsx(ClipboardText, {
4381
- size: 20
4382
- }),
4383
- children: "SQL Snippets"
4384
- }), inEditMode && /* @__PURE__ */ jsx(Button, {
4385
- variant: "default",
4386
- size: "sm",
4387
- onClick: openDataSources,
4521
+ onClick: openQueries,
4388
4522
  leftIcon: /* @__PURE__ */ jsx(Database, {
4389
4523
  size: 20
4390
4524
  }),
4391
- children: "Data Sources"
4525
+ children: "Data Settings"
4392
4526
  }), !inUseMode && /* @__PURE__ */ jsx(Button, {
4393
4527
  variant: "default",
4394
4528
  size: "sm",
@@ -4407,12 +4541,9 @@ function DashboardActions({
4407
4541
  }),
4408
4542
  children: "Revert Changes"
4409
4543
  })]
4410
- }), /* @__PURE__ */ jsx(EditDataSourcesModal, {
4411
- opened: dataSourcesOpened,
4412
- close: closeDataSources
4413
- }), /* @__PURE__ */ jsx(EditSQLSnippetsModal, {
4414
- opened: sqlSnippetsOpened,
4415
- close: closeSQLSnippets
4544
+ }), /* @__PURE__ */ jsx(DataEditorModal, {
4545
+ opened: dataEditorOpened,
4546
+ close: closeQueries
4416
4547
  }), inUseMode && /* @__PURE__ */ jsx(Button, {
4417
4548
  variant: "default",
4418
4549
  size: "sm",
@@ -4437,7 +4568,7 @@ function Dashboard({
4437
4568
  const [layoutFrozen, freezeLayout] = React.useState(false);
4438
4569
  const [panels, setPanels] = React.useState(dashboard.panels);
4439
4570
  const [sqlSnippets, setSQLSnippets] = React.useState(dashboard.definition.sqlSnippets);
4440
- const [dataSources, setDataSources] = React.useState(dashboard.definition.dataSources);
4571
+ const [queries, setQueries] = React.useState(dashboard.definition.queries);
4441
4572
  const [mode, setMode] = React.useState(DashboardMode.Edit);
4442
4573
  const hasChanges = React.useMemo(() => {
4443
4574
  const cleanJSON = (v) => JSON.parse(JSON.stringify(v));
@@ -4448,14 +4579,14 @@ function Dashboard({
4448
4579
  if (!_.isEqual(sqlSnippets, dashboard.definition.sqlSnippets)) {
4449
4580
  return true;
4450
4581
  }
4451
- return !_.isEqual(dataSources, dashboard.definition.dataSources);
4452
- }, [dashboard, panels, sqlSnippets, dataSources]);
4582
+ return !_.isEqual(queries, dashboard.definition.queries);
4583
+ }, [dashboard, panels, sqlSnippets, queries]);
4453
4584
  const saveDashboardChanges = async () => {
4454
4585
  const d = __spreadProps(__spreadValues({}, dashboard), {
4455
4586
  panels,
4456
4587
  definition: {
4457
4588
  sqlSnippets,
4458
- dataSources
4589
+ queries
4459
4590
  }
4460
4591
  });
4461
4592
  await update(d);
@@ -4470,9 +4601,9 @@ function Dashboard({
4470
4601
  w: 3,
4471
4602
  h: 15
4472
4603
  },
4473
- title: `New Panel - ${id}`,
4474
- description: "",
4475
- dataSourceID: "",
4604
+ title: `Panel - ${id}`,
4605
+ description: "<p><br></p>",
4606
+ queryID: "",
4476
4607
  viz: {
4477
4608
  type: "text",
4478
4609
  conf: {}
@@ -4493,9 +4624,9 @@ function Dashboard({
4493
4624
  const definitions = React.useMemo(() => ({
4494
4625
  sqlSnippets,
4495
4626
  setSQLSnippets,
4496
- dataSources,
4497
- setDataSources
4498
- }), [sqlSnippets, setSQLSnippets, dataSources, setDataSources]);
4627
+ queries,
4628
+ setQueries
4629
+ }), [sqlSnippets, setSQLSnippets, queries, setQueries]);
4499
4630
  return /* @__PURE__ */ jsx(ContextInfoContext.Provider, {
4500
4631
  value: context,
4501
4632
  children: /* @__PURE__ */ jsx("div", {
@@ -4567,7 +4698,7 @@ function ReadOnlyDashboard({
4567
4698
  const definition = React.useMemo(() => __spreadProps(__spreadValues({}, dashboard.definition), {
4568
4699
  setSQLSnippets: () => {
4569
4700
  },
4570
- setDataSources: () => {
4701
+ setQueries: () => {
4571
4702
  }
4572
4703
  }), [dashboard]);
4573
4704
  return /* @__PURE__ */ jsx(ContextInfoContext.Provider, {