diff --git a/ui/external-plugins/panel/radar/Editor.tsx b/ui/external-plugins/panel/radar/Editor.tsx
new file mode 100644
index 000000000..b3e87e6de
--- /dev/null
+++ b/ui/external-plugins/panel/radar/Editor.tsx
@@ -0,0 +1,112 @@
+// Copyright 2023 Datav.io Team
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+import { Select, Switch, Textarea } from "@chakra-ui/react"
+import PanelAccordion from "src/views/dashboard/edit-panel/Accordion"
+import PanelEditItem from "src/views/dashboard/edit-panel/PanelEditItem"
+import { Panel, PanelEditorProps } from "types/dashboard"
+import React, { memo } from "react";
+import { useStore } from "@nanostores/react"
+import { commonMsg, textPanelMsg } from "src/i18n/locales/en"
+import { PluginSettings, initSettings, } from "./types"
+import { dispatch } from "use-bus";
+import { PanelForceRebuildEvent } from "src/data/bus-events";
+import { defaultsDeep } from "lodash";
+import RadionButtons from "components/RadioButtons";
+import { EditorNumberItem } from "components/editor/EditorItem";
+
+
+const PanelEditor = memo(({ panel, onChange }: PanelEditorProps) => {
+ const t = useStore(commonMsg)
+ panel.plugins[panel.type] = defaultsDeep(panel.plugins[panel.type], initSettings)
+ const options: PluginSettings = panel.plugins[panel.type]
+ return (
+ <>
+
+
+ onChange((panel: Panel) => {
+ const plugin: PluginSettings = panel.plugins[panel.type]
+ plugin.animation = e.currentTarget.checked
+ // force the panel to rebuild to avoid some problems
+ dispatch(PanelForceRebuildEvent + panel.id)
+ })} />
+
+
+
+
+
+
+
+ onChange((panel: Panel) => {
+ const plugin: PluginSettings = panel.plugins[panel.type]
+ plugin.graph.legend.mode = v
+ dispatch(PanelForceRebuildEvent + panel.id)
+ })} />
+
+
+
+
+
+ {
+ const plugin: PluginSettings = panel.plugins[panel.type]
+ plugin.graph.legend.top = e
+ dispatch(PanelForceRebuildEvent + panel.id)
+ }} />
+
+
+ {
+ const plugin: PluginSettings = panel.plugins[panel.type]
+ plugin.graph.legend.bottom = e
+ dispatch(PanelForceRebuildEvent + panel.id)
+ }} />
+
+
+ {
+ const plugin: PluginSettings = panel.plugins[panel.type]
+ plugin.graph.legend.left = e
+ dispatch(PanelForceRebuildEvent + panel.id)
+ }} />
+
+
+ {
+ const plugin: PluginSettings = panel.plugins[panel.type]
+ plugin.graph.legend.right = e
+ dispatch(PanelForceRebuildEvent + panel.id)
+ }} />
+
+
+ {
+ const plugin: PluginSettings = panel.plugins[panel.type]
+ plugin.graph.legend.itemGap = e
+ dispatch(PanelForceRebuildEvent + panel.id)
+ }} />
+
+
+ >
+ )
+})
+
+export default PanelEditor
\ No newline at end of file
diff --git a/ui/external-plugins/panel/radar/OverrideEditor.tsx b/ui/external-plugins/panel/radar/OverrideEditor.tsx
new file mode 100644
index 000000000..79146b874
--- /dev/null
+++ b/ui/external-plugins/panel/radar/OverrideEditor.tsx
@@ -0,0 +1,38 @@
+// Copyright 2023 Datav.io Team
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { OverrideRule } from "types/dashboard";
+import React from "react";
+
+interface Props {
+ override: OverrideRule
+ onChange: any
+}
+
+
+const OverrideEditor = (props: Props) => {
+ return <>>
+}
+
+export default OverrideEditor
+
+export enum OverrideRules {
+ // basic
+}
+
+// The above example will get targets from SeriesData, Table and Graph panels are using this method to get targets
+// If return [] or null or undefined, Datav will use the default function to get override targets
+export const getOverrideTargets = (panel, data) => {
+ // for demonstration purpose, we just return a hard coded targets list
+ return []
+}
\ No newline at end of file
diff --git a/ui/external-plugins/panel/radar/Panel.tsx b/ui/external-plugins/panel/radar/Panel.tsx
new file mode 100644
index 000000000..1d1a3d69d
--- /dev/null
+++ b/ui/external-plugins/panel/radar/Panel.tsx
@@ -0,0 +1,70 @@
+// Copyright 2023 Datav.io Team
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+import { Box, Center, Text, useColorMode } from "@chakra-ui/react";
+import ChartComponent from "src/components/charts/Chart";
+import { memo, useMemo, useState } from "react";
+import { PanelProps } from "types/dashboard"
+import { FieldType, SeriesData } from "types/seriesData";
+import React from "react";
+import { isEmpty } from "utils/validate";
+import NoData from "src/views/dashboard/components/PanelNoData";
+import { defaultsDeep } from "lodash";
+import { PluginSettings, initSettings } from "./types";
+import { buildOptions } from "./buildOptions";
+import mockData from './mockData.json'
+import { isSeriesData } from "utils/seriesData";
+
+interface Props extends PanelProps {
+ data: SeriesData[][]
+}
+
+const PanelComponentWrapper = memo((props: Props) => {
+ const d: SeriesData[] = props.data.flat()
+ if (isEmpty(d)) {
+ return
+ }
+
+ return (<>
+
+ >
+ )
+})
+
+export default PanelComponentWrapper
+
+const PanelComponent = (props: Props) => {
+ const { panel, height, width } = props
+ const [chart, setChart] = useState(null)
+ const { colorMode } = useColorMode()
+
+
+ // init panel plugin settings
+ props.panel.plugins[panel.type] = defaultsDeep(props.panel.plugins[panel.type], initSettings)
+ // give plugin settings a name for easy access
+ const options: PluginSettings = props.panel.plugins[panel.type]
+
+ const echartOptions = useMemo(() => {
+ // transform SeriesData to candlestick data format
+ const option = buildOptions(panel, props.data.flat(), colorMode)
+ console.log('====>option:', option)
+ return option
+ }, [props.data, panel.overrides, colorMode, options])
+
+ return (<>
+ {options && setChart(c)} onChartEvents={null} />}
+ >)
+}
+
+export const mockDataForTestDataDs = () => {
+ return mockData
+}
diff --git a/ui/external-plugins/panel/radar/buildOptions.ts b/ui/external-plugins/panel/radar/buildOptions.ts
new file mode 100644
index 000000000..b74bd1758
--- /dev/null
+++ b/ui/external-plugins/panel/radar/buildOptions.ts
@@ -0,0 +1,49 @@
+import { Panel } from "types/dashboard";
+import { SeriesData } from "types/seriesData";
+import { PluginSettings } from "./types";
+
+
+export const buildOptions = (panel: Panel, data: SeriesData[], colorMode: "light" | "dark") => {
+ const options: PluginSettings = panel.plugins[panel.type]
+
+ const legend = data.map(item => item.name)
+ const seriesData = legend.map(i => ({ name: i, value: [] }))
+ const indicator = {}
+ data.forEach(item => {
+ item.fields.forEach(field => {
+ const total = field.values.reduce((a, b) => a + b)
+ if (indicator[field.name] && indicator[field.name] > total) {
+ indicator[field.name] = field.values.reduce((a, b) => a + b)
+ } else {
+ indicator[field.name] = field.values.reduce((a, b) => a + b)
+ }
+ const idx = seriesData.findIndex(i => i.name === item.name)
+ seriesData[idx].value.push(total)
+ })
+ })
+ return {
+ darkMode: colorMode === 'dark',
+ legend: {
+ data: legend,
+ show: options.graph.legend.mode,
+ top: options.graph.legend.top,
+ bottom: options.graph.legend.bottom,
+ left: options.graph.legend.left,
+ right: options.graph.legend.right,
+ orient: options.graph.legend.orient,
+ itemGap: options.graph.legend.itemGap,
+ },
+ animation: options.animation,
+ radar: {
+ shape: options.radar.shape,
+ indicator: Object.keys(indicator).map(key => ({ name: key, value: indicator[key] * 1.25 })),
+ },
+ series: [
+ {
+ type: 'radar',
+ data: seriesData,
+
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/ui/external-plugins/panel/radar/index.ts b/ui/external-plugins/panel/radar/index.ts
new file mode 100644
index 000000000..9782e19fc
--- /dev/null
+++ b/ui/external-plugins/panel/radar/index.ts
@@ -0,0 +1,16 @@
+import { PanelPluginComponents } from "types/plugins/plugin";
+import PanelComponent, { mockDataForTestDataDs } from "./Panel";
+import PanelEditor from "./Editor";
+import OverrideEditor, { OverrideRules, getOverrideTargets } from "./OverrideEditor";
+
+
+const panelComponents: PanelPluginComponents = {
+ panel: PanelComponent,
+ editor: PanelEditor,
+ overrideEditor: OverrideEditor,
+ overrideRules: OverrideRules,
+ getOverrideTargets: getOverrideTargets,
+ mockDataForTestDataDs: mockDataForTestDataDs
+}
+
+export default panelComponents
\ No newline at end of file
diff --git a/ui/external-plugins/panel/radar/mockData.json b/ui/external-plugins/panel/radar/mockData.json
new file mode 100644
index 000000000..e91d4e7d2
--- /dev/null
+++ b/ui/external-plugins/panel/radar/mockData.json
@@ -0,0 +1,125 @@
+[
+ {
+ "name": "Beijing",
+ "fields": [
+ {
+ "name": "PM2.5",
+ "type": "number",
+ "values": [
+ 10,
+ 20,
+ 30
+ ]
+ },
+ {
+ "name": "PM10",
+ "type": "number",
+ "values": [
+ 15,
+ 25,
+ 35
+ ]
+ },
+ {
+ "name": "CO",
+ "type": "number",
+ "values": [
+ 15,
+ 25,
+ 35
+ ]
+ },
+ {
+ "name": "NO2",
+ "type": "number",
+ "values": [
+ 25,
+ 27,
+ 30
+ ]
+ }
+ ]
+ },
+ {
+ "name": "Shanghai",
+ "fields": [
+ {
+ "name": "PM2.5",
+ "type": "number",
+ "values": [
+ 13,
+ 17,
+ 20
+ ]
+ },
+ {
+ "name": "PM10",
+ "type": "number",
+ "values": [
+ 15,
+ 27,
+ 40
+ ]
+ },
+ {
+ "name": "CO",
+ "type": "number",
+ "values": [
+ 35,
+ 47,
+ 60
+ ]
+ },
+ {
+ "name": "NO2",
+ "type": "number",
+ "values": [
+ 35,
+ 47,
+ 60
+ ]
+ }
+ ]
+ },
+ {
+ "name": "Shenzhen",
+ "fields": [
+ {
+ "name": "PM2.5",
+ "type": "number",
+ "values": [
+ 5,
+ 7,
+ 13
+ ]
+ },
+ {
+ "name": "PM10",
+ "type": "number",
+ "values": [
+ 19,
+ 26,
+ 50
+ ]
+ },
+ {
+ "name": "CO",
+ "type": "number",
+ "values": [
+ 15,
+ 27,
+ 30
+ ]
+ },
+ {
+ "name": "NO2",
+ "type": "number",
+ "values": [
+ 42,
+ 56,
+ 60
+ ]
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/ui/external-plugins/panel/radar/radar.svg b/ui/external-plugins/panel/radar/radar.svg
new file mode 100644
index 000000000..345057093
--- /dev/null
+++ b/ui/external-plugins/panel/radar/radar.svg
@@ -0,0 +1,1593 @@
+
+
+
diff --git a/ui/external-plugins/panel/radar/types.ts b/ui/external-plugins/panel/radar/types.ts
new file mode 100644
index 000000000..9d76a455b
--- /dev/null
+++ b/ui/external-plugins/panel/radar/types.ts
@@ -0,0 +1,34 @@
+export interface PluginSettings {
+ animation: boolean
+ graph: {
+ legend: {
+ mode: boolean
+ left?: number
+ right?: number
+ top?: number
+ bottom?: number
+ orient?: string
+ itemGap?: number
+ }
+ }
+ radar: {
+ shape?: string
+ }
+}
+
+
+export const initSettings: PluginSettings = {
+ animation: false,
+ radar: {
+ shape: 'polygon'
+ },
+ graph: {
+ legend: {
+ mode: true,
+ right: 0,
+ top: 0,
+ orient: 'horizontal',
+ itemGap: 20
+ }
+ }
+}
\ No newline at end of file
diff --git a/ui/public/plugins/external/panel/plugins.json b/ui/public/plugins/external/panel/plugins.json
index 902e1c742..7211b55ae 100644
--- a/ui/public/plugins/external/panel/plugins.json
+++ b/ui/public/plugins/external/panel/plugins.json
@@ -1 +1 @@
-[{"type":"candlestick"}]
\ No newline at end of file
+[{"type":"candlestick"},{"type":"radar"}]
\ No newline at end of file
diff --git a/ui/public/plugins/external/panel/radar.svg b/ui/public/plugins/external/panel/radar.svg
new file mode 100644
index 000000000..17ec18ffe
--- /dev/null
+++ b/ui/public/plugins/external/panel/radar.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/ui/src/i18n/locales/en.ts b/ui/src/i18n/locales/en.ts
index 854f9ac2a..108e34664 100644
--- a/ui/src/i18n/locales/en.ts
+++ b/ui/src/i18n/locales/en.ts
@@ -16,7 +16,7 @@ export const commonMsg = i18n("common", {
"basic": "Basic",
"variable": "Variable",
"annotation": "Annotation",
- "settings": "Settings",
+ "settings": "Settings",
"new": "New",
"login": "Sign in",
"logout": "Sign out",
@@ -97,7 +97,7 @@ export const commonMsg = i18n("common", {
"animationTips": "Display chart animation",
"display": "Display",
"show": "Show",
- "calc":"Calculation",
+ "calc": "Calculation",
"calcTips": "calculate results from series data with this reducer function",
"value": "Value",
"pickColor": "Pick color",
@@ -133,7 +133,7 @@ export const commonMsg = i18n("common", {
"height": "Height",
"sortWeight": "Sort weight",
"textinput": "Text input",
- "landscapeModeTips":"Please turn your phone to landscape mode for better experience.",
+ "landscapeModeTips": "Please turn your phone to landscape mode for better experience.",
"userStats": "User stats",
"builtIn": "Built-in",
"external": "External",
@@ -155,14 +155,14 @@ export const miscMsg = i18n("misc", {
})
export const sidebarMsg = i18n("sidebar", {
- "search": "Search",
- "selectSidemenu": "Select Sidemenu",
- "selectSideMenuTips": "Select a team sidemenu",
- "themeChange": "Change Theme",
- "accountSetting": "Account",
- "adminPanel": "Admin Panel",
- "currentLang": "Current Lang",
- "newItem": "Add new item",
+ "search": "Search",
+ "selectSidemenu": "Select Sidemenu",
+ "selectSideMenuTips": "Select a team sidemenu",
+ "themeChange": "Change Theme",
+ "accountSetting": "Account",
+ "adminPanel": "Admin Panel",
+ "currentLang": "Current Lang",
+ "newItem": "Add new item",
})
@@ -243,23 +243,23 @@ export const cfgTeam = i18n("cfgTeam", {
"sidemenuTip1": "Customize the top section of your team's side menu, you can add, edit, delete and reorder the menu items.",
"sidemenuTip2": "Menu item format",
"sidemenuTip3": "Url format",
- "level": "Level",
+ "level": "Level",
"sidemenuTip4": "if level 1 is /x, level 2 must be /x/a or /x/b, obviously /y/a is invalid",
"sidemenuTip5": "You can find icons in",
"modifySidemenu": "Modify Side Menu",
"addMenuItem": "Add Menu Item",
"removeMenuItem": "Remove Menu Item",
"sidemenuErrTitle": "title is required",
- "sidemenuErrDashId": "dashboard id is required",
+ "sidemenuErrDashId": "dashboard id is required",
"sidemenuErrLevel1Icon": "Menu item of level 1 must have an icon",
"sidemenuErrIcon": params("icon {name} is not exist"),
"sidemenuErrUrl": params("{name} is not a valid url"),
"sidemenuErrLevel1Url": "level 1 url must be /x, /x/y is invalid",
- "sidemenuErrLevel2Url":"level 2 url must use level1 url as prefix",
- 'sidemenuErrLevel2Url1': "level 2 url must be /x/y, /x or /x/y/z is invalid",
- "sidemenuErrChildTitle": "child title or dashboard id is required",
- "sidemenuErrChildUrl": params("{name} is not a valid url"),
- "sidemenuReload": "Side menu updated, reloading..."
+ "sidemenuErrLevel2Url": "level 2 url must use level1 url as prefix",
+ 'sidemenuErrLevel2Url1': "level 2 url must be /x/y, /x or /x/y/z is invalid",
+ "sidemenuErrChildTitle": "child title or dashboard id is required",
+ "sidemenuErrChildUrl": params("{name} is not a valid url"),
+ "sidemenuReload": "Side menu updated, reloading..."
})
@@ -273,7 +273,7 @@ export const newMsg = i18n("new", {
"importToast": "Dashboard imported, redirecting...",
"jsonInvalid": "Meta json is not valid",
"dsToast": "Datasource added, redirecting...",
- "testDsFailed": "Test failed",
+ "testDsFailed": "Test failed",
})
@@ -294,8 +294,8 @@ export const dashboardSaveMsg = i18n("dashboardSave", {
"autoSaveNotAvail1": "Auto save is not available in history preview mode",
"saveMsgRequired": "A save message must be provided when saving in history preview mode",
"savedMsg": params("Dashboard {name} saved"),
- "saveDueToChanges": "Current dashboard has changes, please save it before viewing history.",
- "onPreviewMsg1": "Changed to history preview mode",
+ "saveDueToChanges": "Current dashboard has changes, please save it before viewing history.",
+ "onPreviewMsg1": "Changed to history preview mode",
"onPreviewMsg2": "Changed to current dashboard",
"onPreviewMsg3": "If you want to use preview version, please save it by click save button.",
"viewHistory": "View History",
@@ -333,7 +333,7 @@ export const dashboardSettingMsg = i18n("dashboardSetting", {
"autoSave": "Enable auto save",
"autoSaveTips": "Dashboard will be auto saved at intervals, you can find old versions in save history list",
"autoSaveInterval": "Auto save interval(seconds)",
- "hiddenPanel": "Hidden panels",
+ "hiddenPanel": "Hidden panels",
"hiddenPanelTips": "You can hide a panel by clicking its header and select hide panel",
"sortWeight": "Sort priority",
"sortWeightTips": "Higher value means higher sort priority, this is used in Search dashboards",
@@ -344,7 +344,7 @@ export const dashboardSettingMsg = i18n("dashboardSetting", {
"backgroundColorModeTips": "Change to this color mode when using background image",
"enableBg": "Enable background",
"enableBgTips": "Whether using the background image set above",
- "dashBorder": "Dashboard border",
+ "dashBorder": "Dashboard border",
"dashBorderTips": "Select a cool border for your dashboard",
"dashSaved": "Dashboard saved",
@@ -414,7 +414,7 @@ export const panelMsg = i18n("panel", {
"transformTips": `Define a function to transform the panel data query from datasource into the format which the panel chart requires`,
"enableTransform": "Whether enable transform",
- "conditionRender": "Conditional render",
+ "conditionRender": "Conditional render",
"conditionRenderTips": "If the condition you set is satisfied, the panel will be rendered, otherwise it will be hidden",
"condition": "Condition",
"conditionTips": "Check a variable is set to a given value"
@@ -491,7 +491,7 @@ export const nodeGraphPanelMsg = i18n("nodeGraphPanel", {
"highlightNodesInputTips": "support multiple regex, split with comma",
"invalidHighlight": "Invalid highlight function",
"highlightColor": "Highlight color",
- "pickLightColor": "Pick light color",
+ "pickLightColor": "Pick light color",
"pickDarkColor": "Pick dark color",
"tooltipTrigger": "Tooltip trigger",
"layout": "Layout",
@@ -509,7 +509,7 @@ export const nodeGraphPanelMsg = i18n("nodeGraphPanel", {
"quadratic": "Quadratic",
"polyline": "Polyline",
"edgeColor": "Edge color",
- "noAttrsToSet" : "No attrs to set"
+ "noAttrsToSet": "No attrs to set"
})
export const echartsPanelMsg = i18n("echartsPanel", {
@@ -524,7 +524,7 @@ export const echartsPanelMsg = i18n("echartsPanel", {
"liveEdit": params("Live Edit( fetch data from {name} datasource)"),
"regEvents": "Register events function",
"regEventsTips": "custom your chart events, e.g mouseclick, mouseover etc",
- "editRegFunc":"Edit registerEvents function",
+ "editRegFunc": "Edit registerEvents function",
})
export const textPanelMsg = i18n("textPanel", {
@@ -535,19 +535,19 @@ export const textPanelMsg = i18n("textPanel", {
"left": "Left",
"center": "Center",
"right": "Right",
- "top": "Top",
+ "top": "Top",
"bottom": "Bottom",
})
export const piePanelMsg = i18n("piePanel", {
- "showLabel": "Show label",
- "showLabelTips": "When view in mobile screen, show label will automatically become show legend for better user experience",
- "shape": "Shape",
- "borderRadius": "Border radius",
- "pieRadius": "Pie radius",
- "innerRadius": "Inner radius",
- "orient": "Orient",
- "placement": "Placement"
+ "showLabel": "Show label",
+ "showLabelTips": "When view in mobile screen, show label will automatically become show legend for better user experience",
+ "shape": "Shape",
+ "borderRadius": "Border radius",
+ "pieRadius": "Pie radius",
+ "innerRadius": "Inner radius",
+ "orient": "Orient",
+ "placement": "Placement"
})
export const gaugePanelMsg = i18n("gaugePanel", {
@@ -572,24 +572,24 @@ export const statsPanelMsg = i18n("statsPanel", {
})
export const tracePanelMsg = i18n("tracePanel", {
- "maxDuration": "Max duration",
- "minDuration": "Min duration",
- "limitResults": "Limit results",
- "findTraces": "Find traces",
- "useLatestTime": "Use latest time",
- "tracesTotal": "Traces Total",
- "tracesSelected": "Traces Selected",
- "clearSelection": "Clear selection",
- "recent": "Most Recent",
- "mostErrors": "Most Errors",
- "longest": "Longest Duration",
- "shortest": "Shortest Duration",
- "mostSpans": "Most Spans",
- "leastSpans": "Least Spans",
- "traceIdsTips": "Searching by trace ids has the highest priority, so if you want to search with options, leave this empty",
- "traceIdsInputTips": "search with trace ids, separated with comma",
- "selectForCompre": "selected for comparison",
- "startTime": "Start time",
+ "maxDuration": "Max duration",
+ "minDuration": "Min duration",
+ "limitResults": "Limit results",
+ "findTraces": "Find traces",
+ "useLatestTime": "Use latest time",
+ "tracesTotal": "Traces Total",
+ "tracesSelected": "Traces Selected",
+ "clearSelection": "Clear selection",
+ "recent": "Most Recent",
+ "mostErrors": "Most Errors",
+ "longest": "Longest Duration",
+ "shortest": "Shortest Duration",
+ "mostSpans": "Most Spans",
+ "leastSpans": "Least Spans",
+ "traceIdsTips": "Searching by trace ids has the highest priority, so if you want to search with options, leave this empty",
+ "traceIdsInputTips": "search with trace ids, separated with comma",
+ "selectForCompre": "selected for comparison",
+ "startTime": "Start time",
})
@@ -606,32 +606,32 @@ export const componentsMsg = i18n("components", {
})
export const tablePanelMsg = i18n("tablePanel", {
- "tableSetting": "Table Setting",
- "showHeader": "Show header",
- "showHeaderTips": "whether display table's header",
- "showBorder": "Show border",
- "stickyHeader": "Sticky header",
- "stickyHeaderTips": "fix header to top, useful for viewing many rows in one page",
- "cellSize": "Cell size",
- "tableWidth": "Table width",
- "column": "Column",
- "columnAlignment": "Column alignment",
- "columnSort": "Column sort",
- "columnSortTips": "click the column title to sort it by asc or desc",
- "columnFilter": "Column filter",
- "columnFilterTips": "filter the column values in table",
- "onRowClick": "On row click",
- "onRowClickTips": "when click on a row, this event will be executed",
- "rowActions": "Click actions",
- "rowActionsTips": "add some actions to panel, e.g edit, delete",
- "addAction": "Add action",
- "actionColumnName": "Action column name",
- "actionColumnWidth": "Action column width",
- "actionButtonSize": "Action button size",
- "seriesName": "change column display name",
- "seriesFilter1": "Number min/max",
- "seriesFilter2": "String match",
- "colorTitle": "Title color"
+ "tableSetting": "Table Setting",
+ "showHeader": "Show header",
+ "showHeaderTips": "whether display table's header",
+ "showBorder": "Show border",
+ "stickyHeader": "Sticky header",
+ "stickyHeaderTips": "fix header to top, useful for viewing many rows in one page",
+ "cellSize": "Cell size",
+ "tableWidth": "Table width",
+ "column": "Column",
+ "columnAlignment": "Column alignment",
+ "columnSort": "Column sort",
+ "columnSortTips": "click the column title to sort it by asc or desc",
+ "columnFilter": "Column filter",
+ "columnFilterTips": "filter the column values in table",
+ "onRowClick": "On row click",
+ "onRowClickTips": "when click on a row, this event will be executed",
+ "rowActions": "Click actions",
+ "rowActionsTips": "add some actions to panel, e.g edit, delete",
+ "addAction": "Add action",
+ "actionColumnName": "Action column name",
+ "actionColumnWidth": "Action column width",
+ "actionButtonSize": "Action button size",
+ "seriesName": "change column display name",
+ "seriesFilter1": "Number min/max",
+ "seriesFilter2": "String match",
+ "colorTitle": "Title color"
})
export const barGaugePanelMsg = i18n("barGaugePanel", {
@@ -649,15 +649,15 @@ export const barGaugePanelMsg = i18n("barGaugePanel", {
"showUnfilledTips": "When enabled renders the unfilled region as gray",
"titleSize": "Title font size",
"valueSize": "Value font size",
- "layoutDir" : "Layout direction",
+ "layoutDir": "Layout direction",
})
export const ValueMappingMsg = i18n("valueMapping", {
-
+
})
export const alertMsg = i18n("alert", {
- "alertFilter": "Alert filter",
- "alertState": "Alert state",
- "datasourceTips": "Query alerts from these datasources"
+ "alertFilter": "Alert filter",
+ "alertState": "Alert state",
+ "datasourceTips": "Query alerts from these datasources"
})
diff --git a/ui/src/views/dashboard/plugins/external/panel/radar/Editor.tsx b/ui/src/views/dashboard/plugins/external/panel/radar/Editor.tsx
new file mode 100644
index 000000000..b3e87e6de
--- /dev/null
+++ b/ui/src/views/dashboard/plugins/external/panel/radar/Editor.tsx
@@ -0,0 +1,112 @@
+// Copyright 2023 Datav.io Team
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+import { Select, Switch, Textarea } from "@chakra-ui/react"
+import PanelAccordion from "src/views/dashboard/edit-panel/Accordion"
+import PanelEditItem from "src/views/dashboard/edit-panel/PanelEditItem"
+import { Panel, PanelEditorProps } from "types/dashboard"
+import React, { memo } from "react";
+import { useStore } from "@nanostores/react"
+import { commonMsg, textPanelMsg } from "src/i18n/locales/en"
+import { PluginSettings, initSettings, } from "./types"
+import { dispatch } from "use-bus";
+import { PanelForceRebuildEvent } from "src/data/bus-events";
+import { defaultsDeep } from "lodash";
+import RadionButtons from "components/RadioButtons";
+import { EditorNumberItem } from "components/editor/EditorItem";
+
+
+const PanelEditor = memo(({ panel, onChange }: PanelEditorProps) => {
+ const t = useStore(commonMsg)
+ panel.plugins[panel.type] = defaultsDeep(panel.plugins[panel.type], initSettings)
+ const options: PluginSettings = panel.plugins[panel.type]
+ return (
+ <>
+
+
+ onChange((panel: Panel) => {
+ const plugin: PluginSettings = panel.plugins[panel.type]
+ plugin.animation = e.currentTarget.checked
+ // force the panel to rebuild to avoid some problems
+ dispatch(PanelForceRebuildEvent + panel.id)
+ })} />
+
+
+
+
+
+
+
+ onChange((panel: Panel) => {
+ const plugin: PluginSettings = panel.plugins[panel.type]
+ plugin.graph.legend.mode = v
+ dispatch(PanelForceRebuildEvent + panel.id)
+ })} />
+
+
+
+
+
+ {
+ const plugin: PluginSettings = panel.plugins[panel.type]
+ plugin.graph.legend.top = e
+ dispatch(PanelForceRebuildEvent + panel.id)
+ }} />
+
+
+ {
+ const plugin: PluginSettings = panel.plugins[panel.type]
+ plugin.graph.legend.bottom = e
+ dispatch(PanelForceRebuildEvent + panel.id)
+ }} />
+
+
+ {
+ const plugin: PluginSettings = panel.plugins[panel.type]
+ plugin.graph.legend.left = e
+ dispatch(PanelForceRebuildEvent + panel.id)
+ }} />
+
+
+ {
+ const plugin: PluginSettings = panel.plugins[panel.type]
+ plugin.graph.legend.right = e
+ dispatch(PanelForceRebuildEvent + panel.id)
+ }} />
+
+
+ {
+ const plugin: PluginSettings = panel.plugins[panel.type]
+ plugin.graph.legend.itemGap = e
+ dispatch(PanelForceRebuildEvent + panel.id)
+ }} />
+
+
+ >
+ )
+})
+
+export default PanelEditor
\ No newline at end of file
diff --git a/ui/src/views/dashboard/plugins/external/panel/radar/OverrideEditor.tsx b/ui/src/views/dashboard/plugins/external/panel/radar/OverrideEditor.tsx
new file mode 100644
index 000000000..79146b874
--- /dev/null
+++ b/ui/src/views/dashboard/plugins/external/panel/radar/OverrideEditor.tsx
@@ -0,0 +1,38 @@
+// Copyright 2023 Datav.io Team
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { OverrideRule } from "types/dashboard";
+import React from "react";
+
+interface Props {
+ override: OverrideRule
+ onChange: any
+}
+
+
+const OverrideEditor = (props: Props) => {
+ return <>>
+}
+
+export default OverrideEditor
+
+export enum OverrideRules {
+ // basic
+}
+
+// The above example will get targets from SeriesData, Table and Graph panels are using this method to get targets
+// If return [] or null or undefined, Datav will use the default function to get override targets
+export const getOverrideTargets = (panel, data) => {
+ // for demonstration purpose, we just return a hard coded targets list
+ return []
+}
\ No newline at end of file
diff --git a/ui/src/views/dashboard/plugins/external/panel/radar/Panel.tsx b/ui/src/views/dashboard/plugins/external/panel/radar/Panel.tsx
new file mode 100644
index 000000000..1d1a3d69d
--- /dev/null
+++ b/ui/src/views/dashboard/plugins/external/panel/radar/Panel.tsx
@@ -0,0 +1,70 @@
+// Copyright 2023 Datav.io Team
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+import { Box, Center, Text, useColorMode } from "@chakra-ui/react";
+import ChartComponent from "src/components/charts/Chart";
+import { memo, useMemo, useState } from "react";
+import { PanelProps } from "types/dashboard"
+import { FieldType, SeriesData } from "types/seriesData";
+import React from "react";
+import { isEmpty } from "utils/validate";
+import NoData from "src/views/dashboard/components/PanelNoData";
+import { defaultsDeep } from "lodash";
+import { PluginSettings, initSettings } from "./types";
+import { buildOptions } from "./buildOptions";
+import mockData from './mockData.json'
+import { isSeriesData } from "utils/seriesData";
+
+interface Props extends PanelProps {
+ data: SeriesData[][]
+}
+
+const PanelComponentWrapper = memo((props: Props) => {
+ const d: SeriesData[] = props.data.flat()
+ if (isEmpty(d)) {
+ return