@devtable/dashboard 1.15.1 → 1.18.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.
@@ -35,11 +35,11 @@ var __publicField = (obj, key, value) => {
35
35
  };
36
36
  import React from "react";
37
37
  import _ from "lodash";
38
- import { WidthProvider, Responsive } from "react-grid-layout";
38
+ 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,25 +132,6 @@ 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
135
  const queryBySQL = ({ context, definitions, title, dataSource }) => async () => {
117
136
  if (!dataSource || !dataSource.sql) {
118
137
  return [];
@@ -1530,6 +1549,18 @@ function getColorByColorConf(conf, dataRow) {
1530
1549
  }
1531
1550
  return "black";
1532
1551
  }
1552
+ function getNonStatsDataText(data) {
1553
+ if (data === null) {
1554
+ return "null";
1555
+ }
1556
+ if (data === void 0) {
1557
+ return "undefined";
1558
+ }
1559
+ if (Array.isArray(data)) {
1560
+ return `Array(${data.length})`;
1561
+ }
1562
+ return data.toString();
1563
+ }
1533
1564
  function VizStats(_a) {
1534
1565
  var _b = _a, {
1535
1566
  conf: _c
@@ -1556,6 +1587,9 @@ function VizStats(_a) {
1556
1587
  formatter
1557
1588
  } = content;
1558
1589
  const contentData = (_a2 = data == null ? void 0 : data[0]) == null ? void 0 : _a2[data_field];
1590
+ if (!["string", "number"].includes(typeof contentData)) {
1591
+ return getNonStatsDataText(contentData);
1592
+ }
1559
1593
  const contents = [prefix, numbro(contentData).format(formatter), postfix];
1560
1594
  return contents.join(" ");
1561
1595
  }, [content, data]);
@@ -1639,9 +1673,39 @@ function PreviewViz({}) {
1639
1673
  loading
1640
1674
  });
1641
1675
  }
1676
+ function _DataFieldSelector({
1677
+ label,
1678
+ required,
1679
+ value,
1680
+ onChange,
1681
+ data,
1682
+ sx
1683
+ }, ref) {
1684
+ const options = React.useMemo(() => {
1685
+ if (!Array.isArray(data) || data.length === 0) {
1686
+ return [];
1687
+ }
1688
+ const keys = Object.keys(data[0]);
1689
+ return keys.map((k2) => ({
1690
+ label: k2,
1691
+ value: k2
1692
+ }));
1693
+ }, [data]);
1694
+ return /* @__PURE__ */ jsx(Select, {
1695
+ ref,
1696
+ label,
1697
+ data: options,
1698
+ value,
1699
+ onChange,
1700
+ required,
1701
+ sx
1702
+ });
1703
+ }
1704
+ const DataFieldSelector = React.forwardRef(_DataFieldSelector);
1642
1705
  function VizBar3DPanel({
1643
1706
  conf,
1644
- setConf
1707
+ setConf,
1708
+ data
1645
1709
  }) {
1646
1710
  const defaultValues = _.assign({}, {
1647
1711
  "x_axis_data_key": "x",
@@ -1689,12 +1753,10 @@ function VizBar3DPanel({
1689
1753
  control,
1690
1754
  render: ({
1691
1755
  field
1692
- }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
1693
- sx: {
1694
- flexGrow: 1
1695
- },
1696
- size: "md",
1697
- label: "Data Key"
1756
+ }) => /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
1757
+ label: "Data Field",
1758
+ required: true,
1759
+ data
1698
1760
  }, field))
1699
1761
  }), /* @__PURE__ */ jsx(Controller, {
1700
1762
  name: "xAxis3D.name",
@@ -1725,12 +1787,10 @@ function VizBar3DPanel({
1725
1787
  control,
1726
1788
  render: ({
1727
1789
  field
1728
- }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
1729
- sx: {
1730
- flexGrow: 1
1731
- },
1732
- size: "md",
1733
- label: "Data Key"
1790
+ }) => /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
1791
+ label: "Data Field",
1792
+ required: true,
1793
+ data
1734
1794
  }, field))
1735
1795
  }), /* @__PURE__ */ jsx(Controller, {
1736
1796
  name: "yAxis3D.name",
@@ -1761,12 +1821,10 @@ function VizBar3DPanel({
1761
1821
  control,
1762
1822
  render: ({
1763
1823
  field
1764
- }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
1765
- sx: {
1766
- flexGrow: 1
1767
- },
1768
- size: "md",
1769
- label: "Data Key"
1824
+ }) => /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
1825
+ label: "Data Field",
1826
+ required: true,
1827
+ data
1770
1828
  }, field))
1771
1829
  }), /* @__PURE__ */ jsx(Controller, {
1772
1830
  name: "zAxis3D.name",
@@ -1904,7 +1962,8 @@ function SeriesItemField({
1904
1962
  index: index2,
1905
1963
  remove,
1906
1964
  seriesItem,
1907
- yAxisOptions
1965
+ yAxisOptions,
1966
+ data
1908
1967
  }) {
1909
1968
  const type = seriesItem.type;
1910
1969
  return /* @__PURE__ */ jsxs(Group, {
@@ -1965,9 +2024,10 @@ function SeriesItemField({
1965
2024
  control,
1966
2025
  render: ({
1967
2026
  field
1968
- }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
1969
- label: "Value key",
2027
+ }) => /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
2028
+ label: "Value Field",
1970
2029
  required: true,
2030
+ data,
1971
2031
  sx: {
1972
2032
  flex: 1
1973
2033
  }
@@ -2074,7 +2134,8 @@ function SeriesItemField({
2074
2134
  function SeriesField({
2075
2135
  control,
2076
2136
  watch,
2077
- getValues
2137
+ getValues,
2138
+ data
2078
2139
  }) {
2079
2140
  const {
2080
2141
  fields,
@@ -2118,7 +2179,8 @@ function SeriesField({
2118
2179
  index: index2,
2119
2180
  remove,
2120
2181
  seriesItem,
2121
- yAxisOptions
2182
+ yAxisOptions,
2183
+ data
2122
2184
  })), /* @__PURE__ */ jsx(Group, {
2123
2185
  position: "center",
2124
2186
  mt: "xs",
@@ -2319,7 +2381,8 @@ function withDefaults(series) {
2319
2381
  }
2320
2382
  function VizCartesianChartPanel({
2321
2383
  conf,
2322
- setConf
2384
+ setConf,
2385
+ data
2323
2386
  }) {
2324
2387
  const _a = conf, {
2325
2388
  series,
@@ -2385,38 +2448,41 @@ function VizCartesianChartPanel({
2385
2448
  size: 20
2386
2449
  })
2387
2450
  })]
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, {
2451
+ }), /* @__PURE__ */ jsxs(Group, {
2399
2452
  direction: "column",
2400
2453
  grow: true,
2401
2454
  noWrap: true,
2402
2455
  mb: "lg",
2403
- children: /* @__PURE__ */ jsx(Controller, {
2456
+ children: [/* @__PURE__ */ jsx(Controller, {
2457
+ name: "x_axis_data_key",
2458
+ control,
2459
+ render: ({
2460
+ field
2461
+ }) => /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
2462
+ label: "X Axis Data Field",
2463
+ required: true,
2464
+ data,
2465
+ sx: {
2466
+ flex: 1
2467
+ }
2468
+ }, field))
2469
+ }), /* @__PURE__ */ jsx(Controller, {
2404
2470
  name: "x_axis_name",
2405
2471
  control,
2406
2472
  render: ({
2407
2473
  field
2408
2474
  }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
2409
- size: "md",
2410
2475
  label: "X Axis Name"
2411
2476
  }, field))
2412
- })
2477
+ })]
2413
2478
  }), /* @__PURE__ */ jsx(YAxesField, {
2414
2479
  control,
2415
2480
  watch
2416
2481
  }), /* @__PURE__ */ jsx(SeriesField, {
2417
2482
  control,
2418
2483
  watch,
2419
- getValues
2484
+ getValues,
2485
+ data
2420
2486
  })]
2421
2487
  })
2422
2488
  });
@@ -2426,7 +2492,8 @@ function VizPiePanel({
2426
2492
  label_field,
2427
2493
  value_field
2428
2494
  },
2429
- setConf
2495
+ setConf,
2496
+ data
2430
2497
  }) {
2431
2498
  const form = useForm$1({
2432
2499
  initialValues: {
@@ -2469,19 +2536,14 @@ function VizPiePanel({
2469
2536
  border: "1px solid #eee",
2470
2537
  borderRadius: "5px"
2471
2538
  },
2472
- children: [/* @__PURE__ */ jsx(TextInput, __spreadValues({
2539
+ children: [/* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
2473
2540
  label: "Label Field",
2474
2541
  required: true,
2475
- sx: {
2476
- flex: 1
2477
- }
2478
- }, form.getInputProps("label_field"))), /* @__PURE__ */ jsx(TextInput, __spreadValues({
2542
+ data
2543
+ }, form.getInputProps("label_field"))), /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
2479
2544
  label: "Value Field",
2480
- placeholder: "get column value by this field",
2481
2545
  required: true,
2482
- sx: {
2483
- flex: 1
2484
- }
2546
+ data
2485
2547
  }, form.getInputProps("value_field")))]
2486
2548
  })]
2487
2549
  })
@@ -2679,7 +2741,8 @@ function _ColorArrayInput({
2679
2741
  const ColorArrayInput = React.forwardRef(_ColorArrayInput);
2680
2742
  function VizStatsPanel({
2681
2743
  conf,
2682
- setConf
2744
+ setConf,
2745
+ data
2683
2746
  }) {
2684
2747
  const defaultValues = _.merge({}, {
2685
2748
  align: "center",
@@ -2755,6 +2818,7 @@ function VizStatsPanel({
2755
2818
  children: [/* @__PURE__ */ jsxs(Group, {
2756
2819
  direction: "row",
2757
2820
  grow: true,
2821
+ noWrap: true,
2758
2822
  children: [/* @__PURE__ */ jsx(Controller, {
2759
2823
  name: "content.prefix",
2760
2824
  control,
@@ -2771,12 +2835,10 @@ function VizStatsPanel({
2771
2835
  control,
2772
2836
  render: ({
2773
2837
  field
2774
- }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
2838
+ }) => /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
2775
2839
  label: "Data Field",
2776
2840
  required: true,
2777
- sx: {
2778
- flexGrow: 1
2779
- }
2841
+ data
2780
2842
  }, field))
2781
2843
  }), /* @__PURE__ */ jsx(Controller, {
2782
2844
  name: "content.postfix",
@@ -2905,7 +2967,8 @@ function SunburstPanel({
2905
2967
  label_field,
2906
2968
  value_field
2907
2969
  },
2908
- setConf
2970
+ setConf,
2971
+ data
2909
2972
  }) {
2910
2973
  const form = useForm$1({
2911
2974
  initialValues: {
@@ -2948,19 +3011,14 @@ function SunburstPanel({
2948
3011
  border: "1px solid #eee",
2949
3012
  borderRadius: "5px"
2950
3013
  },
2951
- children: [/* @__PURE__ */ jsx(TextInput, __spreadValues({
3014
+ children: [/* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
2952
3015
  label: "Label Field",
2953
3016
  required: true,
2954
- sx: {
2955
- flex: 1
2956
- }
2957
- }, form.getInputProps("label_field"))), /* @__PURE__ */ jsx(TextInput, __spreadValues({
3017
+ data
3018
+ }, form.getInputProps("label_field"))), /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
2958
3019
  label: "Value Field",
2959
- placeholder: "get column value by this field",
2960
3020
  required: true,
2961
- sx: {
2962
- flex: 1
2963
- }
3021
+ data
2964
3022
  }, form.getInputProps("value_field")))]
2965
3023
  })]
2966
3024
  })
@@ -2992,7 +3050,8 @@ function VizTablePanel(_e) {
2992
3050
  } = _h, restConf = __objRest(_h, [
2993
3051
  "columns"
2994
3052
  ]), {
2995
- setConf
3053
+ setConf,
3054
+ data
2996
3055
  } = _f;
2997
3056
  const form = useForm$1({
2998
3057
  initialValues: __spreadValues({
@@ -3046,10 +3105,10 @@ function VizTablePanel(_e) {
3046
3105
  border: "1px solid #eee",
3047
3106
  borderRadius: "5px"
3048
3107
  },
3049
- children: [/* @__PURE__ */ jsx(TextInput, __spreadValues({
3050
- size: "md",
3051
- mb: "lg",
3052
- label: "ID Field"
3108
+ children: [/* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
3109
+ label: "ID Field",
3110
+ required: true,
3111
+ data
3053
3112
  }, form.getInputProps("id_field"))), /* @__PURE__ */ jsxs(Group, {
3054
3113
  position: "apart",
3055
3114
  mb: "lg",
@@ -3145,25 +3204,16 @@ function VizTablePanel(_e) {
3145
3204
  children: [/* @__PURE__ */ jsxs(Group, {
3146
3205
  position: "apart",
3147
3206
  grow: true,
3148
- sx: {
3149
- "> *": {
3150
- flexGrow: 1,
3151
- maxWidth: "100%"
3152
- }
3153
- },
3154
3207
  children: [/* @__PURE__ */ jsx(TextInput, __spreadValues({
3155
3208
  label: "Label",
3156
3209
  required: true,
3157
3210
  sx: {
3158
3211
  flex: 1
3159
3212
  }
3160
- }, form.getListInputProps("columns", index2, "label"))), /* @__PURE__ */ jsx(TextInput, __spreadValues({
3213
+ }, form.getListInputProps("columns", index2, "label"))), /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
3161
3214
  label: "Value Field",
3162
- placeholder: "get column value by this field",
3163
3215
  required: true,
3164
- sx: {
3165
- flex: 1
3166
- }
3216
+ data
3167
3217
  }, form.getListInputProps("columns", index2, "value_field"))), /* @__PURE__ */ jsx(ValueTypeSelector, __spreadValues({
3168
3218
  label: "Value Type",
3169
3219
  sx: {
@@ -3371,6 +3421,7 @@ const types = [{
3371
3421
  }];
3372
3422
  function EditVizConf() {
3373
3423
  const {
3424
+ data,
3374
3425
  viz,
3375
3426
  setViz
3376
3427
  } = React.useContext(PanelContext);
@@ -3415,7 +3466,8 @@ function EditVizConf() {
3415
3466
  })
3416
3467
  }), Panel2 && /* @__PURE__ */ jsx(Panel2, {
3417
3468
  conf: viz.conf,
3418
- setConf: setVizConf
3469
+ setConf: setVizConf,
3470
+ data
3419
3471
  }), !Panel2 && /* @__PURE__ */ jsx(JsonInput, {
3420
3472
  minRows: 20,
3421
3473
  label: "Config",
@@ -3643,29 +3695,16 @@ function Panel({
3643
3695
  });
3644
3696
  }
3645
3697
  var index = "";
3646
- const ResponsiveReactGridLayout$1 = WidthProvider(Responsive);
3698
+ const ReactGridLayout$1 = WidthProvider(RGL);
3647
3699
  function DashboardLayout({
3648
3700
  panels,
3649
3701
  setPanels,
3650
3702
  className = "layout",
3651
- cols = {
3652
- lg: 12,
3653
- md: 10,
3654
- sm: 8,
3655
- xs: 6,
3656
- xxs: 4
3657
- },
3658
3703
  rowHeight = 10,
3659
3704
  onRemoveItem,
3660
3705
  isDraggable,
3661
- isResizable,
3662
- setLocalCols,
3663
- setBreakpoint
3706
+ isResizable
3664
3707
  }) {
3665
- const onBreakpointChange = (breakpoint, localCols) => {
3666
- setBreakpoint(breakpoint);
3667
- setLocalCols(localCols);
3668
- };
3669
3708
  const onLayoutChange = React.useCallback((currentLayout) => {
3670
3709
  const m2 = /* @__PURE__ */ new Map();
3671
3710
  currentLayout.forEach((_a) => {
@@ -3681,11 +3720,9 @@ function DashboardLayout({
3681
3720
  }));
3682
3721
  setPanels(newPanels);
3683
3722
  }, [panels, setPanels]);
3684
- return /* @__PURE__ */ jsx(ResponsiveReactGridLayout$1, {
3685
- onBreakpointChange,
3723
+ return /* @__PURE__ */ jsx(ReactGridLayout$1, {
3686
3724
  onLayoutChange,
3687
3725
  className,
3688
- cols,
3689
3726
  rowHeight,
3690
3727
  isDraggable,
3691
3728
  isResizable,
@@ -3779,7 +3816,7 @@ function ContextAndSnippets({}) {
3779
3816
  grow: true,
3780
3817
  sx: {
3781
3818
  border: "1px solid #eee",
3782
- maxWidth: "48%",
3819
+ maxWidth: "40%",
3783
3820
  overflow: "hidden"
3784
3821
  },
3785
3822
  children: [/* @__PURE__ */ jsx(Group, {
@@ -3842,6 +3879,20 @@ function ContextAndSnippets({}) {
3842
3879
  })]
3843
3880
  });
3844
3881
  }
3882
+ function PreviewSQL({
3883
+ value
3884
+ }) {
3885
+ const context = React.useContext(ContextInfoContext);
3886
+ const definition = React.useContext(DefinitionContext);
3887
+ const explained = React.useMemo(() => {
3888
+ return explainSQL(value, context, definition);
3889
+ }, [value, context, definition]);
3890
+ return /* @__PURE__ */ jsx(Prism, {
3891
+ language: "sql",
3892
+ colorScheme: "light",
3893
+ children: explained
3894
+ });
3895
+ }
3845
3896
  function DataSourceForm({
3846
3897
  value,
3847
3898
  onChange
@@ -3941,11 +3992,23 @@ function DataSourceForm({
3941
3992
  },
3942
3993
  disabled: loading
3943
3994
  }, form.getInputProps("key")))]
3944
- }), /* @__PURE__ */ jsx(Textarea, __spreadValues({
3945
- autosize: true,
3946
- minRows: 12,
3947
- maxRows: 24
3948
- }, form.getInputProps("sql")))]
3995
+ }), /* @__PURE__ */ jsxs(Tabs, {
3996
+ children: [/* @__PURE__ */ jsx(Tabs.Tab, {
3997
+ label: "SQL",
3998
+ children: /* @__PURE__ */ jsx(Textarea, __spreadProps(__spreadValues({
3999
+ autosize: true,
4000
+ minRows: 12,
4001
+ maxRows: 24
4002
+ }, form.getInputProps("sql")), {
4003
+ className: "code-textarea"
4004
+ }))
4005
+ }), /* @__PURE__ */ jsx(Tabs.Tab, {
4006
+ label: "Preview",
4007
+ children: /* @__PURE__ */ jsx(PreviewSQL, {
4008
+ value: form.values.sql
4009
+ })
4010
+ })]
4011
+ })]
3949
4012
  })]
3950
4013
  })
3951
4014
  });
@@ -3982,16 +4045,9 @@ function DataSourceEditor({
3982
4045
  children: "Invalid Data Source ID"
3983
4046
  });
3984
4047
  }
3985
- return /* @__PURE__ */ jsxs(Group, {
3986
- direction: "row",
3987
- position: "apart",
3988
- grow: true,
3989
- align: "stretch",
3990
- noWrap: true,
3991
- children: [/* @__PURE__ */ jsx(DataSourceForm, {
3992
- value: dataSource,
3993
- onChange: update
3994
- }), /* @__PURE__ */ jsx(ContextAndSnippets, {})]
4048
+ return /* @__PURE__ */ jsx(DataSourceForm, {
4049
+ value: dataSource,
4050
+ onChange: update
3995
4051
  });
3996
4052
  }
3997
4053
  function SelectOrAddDataSource({
@@ -4064,58 +4120,52 @@ function SelectOrAddDataSource({
4064
4120
  })
4065
4121
  });
4066
4122
  }
4067
- function EditDataSourcesModal({
4068
- opened,
4069
- close
4070
- }) {
4123
+ function EditDataSources({}) {
4071
4124
  const [id, setID] = React.useState("");
4072
- const {
4073
- freezeLayout
4074
- } = React.useContext(LayoutStateContext);
4075
- React.useEffect(() => {
4076
- freezeLayout(opened);
4077
- }, [opened]);
4078
- return /* @__PURE__ */ jsx(Modal, {
4079
- size: "96vw",
4080
- overflow: "inside",
4081
- opened,
4082
- onClose: close,
4083
- title: "Data Sources",
4084
- trapFocus: true,
4085
- onDragStart: (e) => {
4086
- e.stopPropagation();
4125
+ return /* @__PURE__ */ jsxs(AppShell, {
4126
+ sx: {
4127
+ height: "90vh",
4128
+ maxHeight: "calc(100vh - 225px)",
4129
+ ".mantine-AppShell-body": {
4130
+ height: "100%"
4131
+ },
4132
+ main: {
4133
+ height: "100%",
4134
+ width: "100%",
4135
+ padding: 0,
4136
+ margin: 0
4137
+ }
4087
4138
  },
4088
- children: /* @__PURE__ */ jsxs(AppShell, {
4089
- sx: {
4090
- height: "90vh",
4091
- maxHeight: "calc(100vh - 185px)",
4092
- ".mantine-AppShell-body": {
4093
- height: "100%"
4139
+ padding: "md",
4140
+ children: [/* @__PURE__ */ jsxs(Group, {
4141
+ direction: "row",
4142
+ position: "apart",
4143
+ grow: true,
4144
+ align: "stretch",
4145
+ noWrap: true,
4146
+ children: [/* @__PURE__ */ jsxs(Group, {
4147
+ direction: "column",
4148
+ grow: true,
4149
+ sx: {
4150
+ flexGrow: 1,
4151
+ maxWidth: "calc(60% - 16px)"
4094
4152
  },
4095
- main: {
4096
- height: "100%",
4097
- width: "100%",
4098
- padding: 0,
4099
- margin: 0
4100
- }
4101
- },
4102
- padding: "md",
4103
- header: /* @__PURE__ */ jsx(SelectOrAddDataSource, {
4104
- id,
4105
- setID
4106
- }),
4107
- children: [/* @__PURE__ */ jsx(DataSourceEditor, {
4108
- id,
4109
- setID
4110
- }), /* @__PURE__ */ jsx(DataPreview, {
4111
- id
4112
- })]
4113
- })
4153
+ children: [/* @__PURE__ */ jsx(SelectOrAddDataSource, {
4154
+ id,
4155
+ setID
4156
+ }), /* @__PURE__ */ jsx(DataSourceEditor, {
4157
+ id,
4158
+ setID
4159
+ })]
4160
+ }), /* @__PURE__ */ jsx(ContextAndSnippets, {})]
4161
+ }), /* @__PURE__ */ jsx(DataPreview, {
4162
+ id
4163
+ })]
4114
4164
  });
4115
4165
  }
4116
4166
  function ContextInfo({}) {
4117
4167
  const contextInfo = React.useContext(ContextInfoContext);
4118
- const sampleSQL = `SELECT *
4168
+ const sampleSQL2 = `SELECT *
4119
4169
  FROM commit
4120
4170
  WHERE author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.[1].toISOString()}'`;
4121
4171
  return /* @__PURE__ */ jsxs(Group, {
@@ -4123,7 +4173,6 @@ WHERE author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.
4123
4173
  grow: true,
4124
4174
  sx: {
4125
4175
  border: "1px solid #eee",
4126
- maxWidth: "48%",
4127
4176
  overflow: "hidden"
4128
4177
  },
4129
4178
  children: [/* @__PURE__ */ jsx(Group, {
@@ -4156,7 +4205,7 @@ WHERE author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.
4156
4205
  children: `-- You may refer context data *by name*
4157
4206
  -- in SQL or VizConfig.
4158
4207
 
4159
- ${sampleSQL}`
4208
+ ${sampleSQL2}`
4160
4209
  }), /* @__PURE__ */ jsx(Text, {
4161
4210
  weight: 500,
4162
4211
  sx: {
@@ -4175,14 +4224,32 @@ ${sampleSQL}`
4175
4224
  })]
4176
4225
  });
4177
4226
  }
4227
+ function PreviewSnippet({
4228
+ value
4229
+ }) {
4230
+ const context = React.useContext(ContextInfoContext);
4231
+ const explained = React.useMemo(() => {
4232
+ return explainSQLSnippet(value, context);
4233
+ }, [value, context]);
4234
+ return /* @__PURE__ */ jsxs(Group, {
4235
+ direction: "column",
4236
+ noWrap: true,
4237
+ grow: true,
4238
+ children: [/* @__PURE__ */ jsx(Text, {
4239
+ children: "Preview"
4240
+ }), /* @__PURE__ */ jsx(Prism, {
4241
+ language: "sql",
4242
+ noCopy: true,
4243
+ colorScheme: "dark",
4244
+ children: explained
4245
+ })]
4246
+ });
4247
+ }
4178
4248
  function SQLSnippetsEditor({}) {
4179
4249
  const {
4180
4250
  sqlSnippets,
4181
4251
  setSQLSnippets
4182
4252
  } = React.useContext(DefinitionContext);
4183
- const sampleSQL = `SELECT *
4184
- FROM commit
4185
- WHERE \${author_time_condition}`;
4186
4253
  const initialValues = React.useMemo(() => ({
4187
4254
  snippets: formList(sqlSnippets != null ? sqlSnippets : [])
4188
4255
  }), [sqlSnippets]);
@@ -4203,7 +4270,8 @@ WHERE \${author_time_condition}`;
4203
4270
  direction: "column",
4204
4271
  grow: true,
4205
4272
  sx: {
4206
- border: "1px solid #eee"
4273
+ border: "1px solid #eee",
4274
+ flexGrow: 1
4207
4275
  },
4208
4276
  children: /* @__PURE__ */ jsxs("form", {
4209
4277
  onSubmit: form.onSubmit(submit),
@@ -4229,26 +4297,11 @@ WHERE \${author_time_condition}`;
4229
4297
  size: 20
4230
4298
  })
4231
4299
  })]
4232
- }), /* @__PURE__ */ jsxs(Group, {
4300
+ }), /* @__PURE__ */ jsx(Group, {
4233
4301
  px: "md",
4234
4302
  pb: "md",
4235
- children: [/* @__PURE__ */ jsx(Prism, {
4236
- language: "sql",
4237
- sx: {
4238
- width: "100%"
4239
- },
4240
- noCopy: true,
4241
- trim: false,
4242
- colorScheme: "dark",
4243
- children: `-- You may refer context data *by name*
4244
- -- in SQL or VizConfig.
4245
-
4246
- ${sampleSQL}
4247
-
4248
- -- where author_time_condition is:
4249
- author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.[1].toISOString()}'
4250
- `
4251
- }), /* @__PURE__ */ jsxs(Group, {
4303
+ pt: "md",
4304
+ children: /* @__PURE__ */ jsxs(Group, {
4252
4305
  direction: "column",
4253
4306
  sx: {
4254
4307
  width: "100%",
@@ -4268,11 +4321,15 @@ author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.[1].to
4268
4321
  children: [/* @__PURE__ */ jsx(TextInput, __spreadValues({
4269
4322
  label: "Key",
4270
4323
  required: true
4271
- }, form.getListInputProps("snippets", index2, "key"))), /* @__PURE__ */ jsx(Textarea, __spreadValues({
4324
+ }, form.getListInputProps("snippets", index2, "key"))), /* @__PURE__ */ jsx(Textarea, __spreadProps(__spreadValues({
4272
4325
  minRows: 3,
4273
4326
  label: "Value",
4274
4327
  required: true
4275
- }, form.getListInputProps("snippets", index2, "value"))), /* @__PURE__ */ jsx(ActionIcon, {
4328
+ }, form.getListInputProps("snippets", index2, "value")), {
4329
+ className: "code-textarea"
4330
+ })), /* @__PURE__ */ jsx(PreviewSnippet, {
4331
+ value: form.values.snippets[index2].value
4332
+ }), /* @__PURE__ */ jsx(ActionIcon, {
4276
4333
  color: "red",
4277
4334
  variant: "hover",
4278
4335
  onClick: () => form.removeListItem("snippets", index2),
@@ -4299,12 +4356,97 @@ author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.[1].to
4299
4356
  children: "Add a snippet"
4300
4357
  })
4301
4358
  })]
4302
- })]
4359
+ })
4303
4360
  })]
4304
4361
  })
4305
4362
  });
4306
4363
  }
4307
- function EditSQLSnippetsModal({
4364
+ const sampleSQL = `SELECT *
4365
+ FROM commit
4366
+ WHERE \${author_time_condition}`;
4367
+ function SQLSnippetGuide() {
4368
+ return /* @__PURE__ */ jsxs(Group, {
4369
+ direction: "column",
4370
+ grow: true,
4371
+ sx: {
4372
+ border: "1px solid #eee",
4373
+ overflow: "hidden"
4374
+ },
4375
+ children: [/* @__PURE__ */ jsx(Group, {
4376
+ position: "left",
4377
+ pl: "md",
4378
+ py: "md",
4379
+ sx: {
4380
+ borderBottom: "1px solid #eee",
4381
+ background: "#efefef",
4382
+ flexGrow: 0
4383
+ },
4384
+ children: /* @__PURE__ */ jsx(Text, {
4385
+ weight: 500,
4386
+ children: "Guide"
4387
+ })
4388
+ }), /* @__PURE__ */ jsx(Group, {
4389
+ direction: "column",
4390
+ px: "md",
4391
+ pb: "md",
4392
+ sx: {
4393
+ width: "100%"
4394
+ },
4395
+ children: /* @__PURE__ */ jsx(Prism, {
4396
+ language: "sql",
4397
+ sx: {
4398
+ width: "100%"
4399
+ },
4400
+ noCopy: true,
4401
+ trim: false,
4402
+ colorScheme: "dark",
4403
+ children: `-- You may refer context data *by name*
4404
+ -- in SQL or VizConfig.
4405
+
4406
+ ${sampleSQL}
4407
+
4408
+ -- where author_time_condition is:
4409
+ author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.[1].toISOString()}'
4410
+ `
4411
+ })
4412
+ })]
4413
+ });
4414
+ }
4415
+ function EditSQLSnippets({}) {
4416
+ return /* @__PURE__ */ jsx(AppShell, {
4417
+ sx: {
4418
+ height: "90vh",
4419
+ maxHeight: "calc(100vh - 225px)",
4420
+ ".mantine-AppShell-body": {
4421
+ height: "100%"
4422
+ },
4423
+ main: {
4424
+ height: "100%",
4425
+ width: "100%",
4426
+ padding: 0,
4427
+ margin: 0
4428
+ }
4429
+ },
4430
+ padding: "md",
4431
+ children: /* @__PURE__ */ jsxs(Group, {
4432
+ direction: "row",
4433
+ position: "apart",
4434
+ grow: true,
4435
+ align: "stretch",
4436
+ noWrap: true,
4437
+ children: [/* @__PURE__ */ jsx(SQLSnippetsEditor, {}), /* @__PURE__ */ jsxs(Group, {
4438
+ direction: "column",
4439
+ grow: true,
4440
+ noWrap: true,
4441
+ sx: {
4442
+ maxWidth: "40%"
4443
+ },
4444
+ children: [/* @__PURE__ */ jsx(SQLSnippetGuide, {}), /* @__PURE__ */ jsx(ContextInfo, {})]
4445
+ })]
4446
+ })
4447
+ });
4448
+ }
4449
+ function DataEditorModal({
4308
4450
  opened,
4309
4451
  close
4310
4452
  }) {
@@ -4319,34 +4461,19 @@ function EditSQLSnippetsModal({
4319
4461
  overflow: "inside",
4320
4462
  opened,
4321
4463
  onClose: close,
4322
- title: "SQL Snippets",
4464
+ title: "Data Settings",
4323
4465
  trapFocus: true,
4324
4466
  onDragStart: (e) => {
4325
4467
  e.stopPropagation();
4326
4468
  },
4327
- children: /* @__PURE__ */ jsx(AppShell, {
4328
- sx: {
4329
- height: "90vh",
4330
- maxHeight: "calc(100vh - 185px)",
4331
- ".mantine-AppShell-body": {
4332
- height: "100%"
4333
- },
4334
- main: {
4335
- height: "100%",
4336
- width: "100%",
4337
- padding: 0,
4338
- margin: 0
4339
- }
4340
- },
4341
- padding: "md",
4342
- children: /* @__PURE__ */ jsxs(Group, {
4343
- direction: "row",
4344
- position: "apart",
4345
- grow: true,
4346
- align: "stretch",
4347
- noWrap: true,
4348
- children: [/* @__PURE__ */ jsx(SQLSnippetsEditor, {}), /* @__PURE__ */ jsx(ContextInfo, {})]
4349
- })
4469
+ children: /* @__PURE__ */ jsxs(Tabs, {
4470
+ children: [/* @__PURE__ */ jsx(Tabs.Tab, {
4471
+ label: "SQL Snippet",
4472
+ children: /* @__PURE__ */ jsx(EditSQLSnippets, {})
4473
+ }), /* @__PURE__ */ jsx(Tabs.Tab, {
4474
+ label: "Data Source",
4475
+ children: /* @__PURE__ */ jsx(EditDataSources, {})
4476
+ })]
4350
4477
  })
4351
4478
  });
4352
4479
  }
@@ -4362,12 +4489,9 @@ function DashboardActions({
4362
4489
  inEditMode,
4363
4490
  inUseMode
4364
4491
  } = React.useContext(LayoutStateContext);
4365
- const [dataSourcesOpened, setDataSourcesOpened] = React.useState(false);
4366
- const openDataSources = () => setDataSourcesOpened(true);
4367
- const closeDataSources = () => setDataSourcesOpened(false);
4368
- const [sqlSnippetsOpened, setSQLSnippetsOpened] = React.useState(false);
4369
- const openSQLSnippets = () => setSQLSnippetsOpened(true);
4370
- const closeSQLSnippets = () => setSQLSnippetsOpened(false);
4492
+ const [dataEditorOpened, setDataEditorOpened] = React.useState(false);
4493
+ const openDataSources = () => setDataEditorOpened(true);
4494
+ const closeDataSources = () => setDataEditorOpened(false);
4371
4495
  return /* @__PURE__ */ jsxs(Group, {
4372
4496
  position: "apart",
4373
4497
  pt: "sm",
@@ -4388,14 +4512,6 @@ function DashboardActions({
4388
4512
  size: 20
4389
4513
  }),
4390
4514
  children: "Add a Panel"
4391
- }), inEditMode && /* @__PURE__ */ jsx(Button, {
4392
- variant: "default",
4393
- size: "sm",
4394
- onClick: openSQLSnippets,
4395
- leftIcon: /* @__PURE__ */ jsx(ClipboardText, {
4396
- size: 20
4397
- }),
4398
- children: "SQL Snippets"
4399
4515
  }), inEditMode && /* @__PURE__ */ jsx(Button, {
4400
4516
  variant: "default",
4401
4517
  size: "sm",
@@ -4403,7 +4519,7 @@ function DashboardActions({
4403
4519
  leftIcon: /* @__PURE__ */ jsx(Database, {
4404
4520
  size: 20
4405
4521
  }),
4406
- children: "Data Sources"
4522
+ children: "Data Settings"
4407
4523
  }), !inUseMode && /* @__PURE__ */ jsx(Button, {
4408
4524
  variant: "default",
4409
4525
  size: "sm",
@@ -4422,12 +4538,9 @@ function DashboardActions({
4422
4538
  }),
4423
4539
  children: "Revert Changes"
4424
4540
  })]
4425
- }), /* @__PURE__ */ jsx(EditDataSourcesModal, {
4426
- opened: dataSourcesOpened,
4541
+ }), /* @__PURE__ */ jsx(DataEditorModal, {
4542
+ opened: dataEditorOpened,
4427
4543
  close: closeDataSources
4428
- }), /* @__PURE__ */ jsx(EditSQLSnippetsModal, {
4429
- opened: sqlSnippetsOpened,
4430
- close: closeSQLSnippets
4431
4544
  }), inUseMode && /* @__PURE__ */ jsx(Button, {
4432
4545
  variant: "default",
4433
4546
  size: "sm",
@@ -4450,8 +4563,6 @@ function Dashboard({
4450
4563
  APIClient.baseURL = config.apiBaseURL;
4451
4564
  }
4452
4565
  const [layoutFrozen, freezeLayout] = React.useState(false);
4453
- const [breakpoint, setBreakpoint] = React.useState();
4454
- const [localCols, setLocalCols] = React.useState();
4455
4566
  const [panels, setPanels] = React.useState(dashboard.panels);
4456
4567
  const [sqlSnippets, setSQLSnippets] = React.useState(dashboard.definition.sqlSnippets);
4457
4568
  const [dataSources, setDataSources] = React.useState(dashboard.definition.dataSources);
@@ -4539,31 +4650,21 @@ function Dashboard({
4539
4650
  setPanels,
4540
4651
  isDraggable: inLayoutMode,
4541
4652
  isResizable: inLayoutMode,
4542
- onRemoveItem: removePanelByID,
4543
- setLocalCols,
4544
- setBreakpoint
4653
+ onRemoveItem: removePanelByID
4545
4654
  })]
4546
4655
  })
4547
4656
  })
4548
4657
  })
4549
4658
  });
4550
4659
  }
4551
- const ResponsiveReactGridLayout = WidthProvider(Responsive);
4660
+ const ReactGridLayout = WidthProvider(RGL);
4552
4661
  function ReadOnlyDashboardLayout({
4553
4662
  panels,
4554
4663
  className = "layout",
4555
- cols = {
4556
- lg: 12,
4557
- md: 10,
4558
- sm: 8,
4559
- xs: 6,
4560
- xxs: 4
4561
- },
4562
4664
  rowHeight = 10
4563
4665
  }) {
4564
- return /* @__PURE__ */ jsx(ResponsiveReactGridLayout, {
4666
+ return /* @__PURE__ */ jsx(ReactGridLayout, {
4565
4667
  className,
4566
- cols,
4567
4668
  rowHeight,
4568
4669
  isDraggable: false,
4569
4670
  isResizable: false,