From f6c0fc894081fd17efec58dfc81c7f5cfd953079 Mon Sep 17 00:00:00 2001 From: ktx-vaidehi <134508096+ktx-vaidehi@users.noreply.github.com> Date: Tue, 29 Oct 2024 10:54:33 +0530 Subject: [PATCH] feat: Dashboard Config Option (#4617) - #4589 ## Summary by CodeRabbit ## Release Notes - **New Features** - Enhanced dashboard panel configuration with new options for line thickness, label positioning, symbol visibility, and line interpolation. - Introduced new icons for visual representation of chart options, including Linear, NoSymbol, Smooth, StepBefore, StepAfter, and StepMiddle. - **Bug Fixes** - Improved error handling and validation for dashboard panel configurations. - **Documentation** - Added new localization entries for enhanced user interface labels related to dashboard features. These updates significantly improve the customization and usability of dashboard panels, providing users with greater flexibility in visualizing their data. --- src/common/meta/dashboards/v5/mod.rs | 38 +++ .../dashboards/addPanel/ConfigPanel.vue | 309 +++++++++++++++++- .../icons/dashboards/LinearIcon.vue | 22 ++ .../components/icons/dashboards/NoSymbol.vue | 19 ++ .../components/icons/dashboards/Smooth.vue | 26 ++ .../components/icons/dashboards/StepAfter.vue | 40 +++ .../icons/dashboards/StepBefore.vue | 22 ++ .../icons/dashboards/StepMiddle.vue | 33 ++ web/src/composables/useDashboardPanel.ts | 7 + web/src/locales/languages/en.json | 18 +- web/src/utils/dashboard/convertSQLData.ts | 97 +++++- 11 files changed, 606 insertions(+), 25 deletions(-) create mode 100644 web/src/components/icons/dashboards/LinearIcon.vue create mode 100644 web/src/components/icons/dashboards/NoSymbol.vue create mode 100644 web/src/components/icons/dashboards/Smooth.vue create mode 100644 web/src/components/icons/dashboards/StepAfter.vue create mode 100644 web/src/components/icons/dashboards/StepBefore.vue create mode 100644 web/src/components/icons/dashboards/StepMiddle.vue diff --git a/src/common/meta/dashboards/v5/mod.rs b/src/common/meta/dashboards/v5/mod.rs index 53bed11ced..acf4363a6e 100644 --- a/src/common/meta/dashboards/v5/mod.rs +++ b/src/common/meta/dashboards/v5/mod.rs @@ -196,6 +196,8 @@ pub struct PanelConfig { #[serde(skip_serializing_if = "Option::is_none")] decimals: Option, #[serde(skip_serializing_if = "Option::is_none")] + line_thickness: Option, + #[serde(skip_serializing_if = "Option::is_none")] top_results: Option, #[serde(skip_serializing_if = "Option::is_none")] top_results_others: Option, @@ -204,6 +206,12 @@ pub struct PanelConfig { #[serde(skip_serializing_if = "Option::is_none")] axis_border_show: Option, #[serde(skip_serializing_if = "Option::is_none")] + label_option: Option, + #[serde(skip_serializing_if = "Option::is_none")] + show_symbol: Option, + #[serde(skip_serializing_if = "Option::is_none")] + line_interpolation: Option, + #[serde(skip_serializing_if = "Option::is_none")] legend_width: Option, base_map: Option, map_view: Option, @@ -427,3 +435,33 @@ pub struct LegendWidth { #[serde(skip_serializing_if = "Option::is_none")] pub unit: Option, } + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)] +pub struct LabelOption { + #[serde(skip_serializing_if = "Option::is_none")] + pub position: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub rotate: Option, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "lowercase")] +pub enum LineInterpolation { + Smooth, + Linear, + #[serde(rename = "step-start")] + StepStart, + #[serde(rename = "step-end")] + StepEnd, + #[serde(rename = "step-middle")] + StepMiddle, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "lowercase")] +pub enum LabelPosition { + Top, + Inside, + InsideTop, + InsideBottom +} \ No newline at end of file diff --git a/web/src/components/dashboards/addPanel/ConfigPanel.vue b/web/src/components/dashboards/addPanel/ConfigPanel.vue index 99449c99ce..b5a60f18f4 100644 --- a/web/src/components/dashboards/addPanel/ConfigPanel.vue +++ b/web/src/components/dashboards/addPanel/ConfigPanel.vue @@ -190,7 +190,7 @@ along with this program. If not, see . :display-value="`${ dashboardPanelData.data.config.unit ? unitOptions.find( - (it) => it.value == dashboardPanelData.data.config.unit + (it) => it.value == dashboardPanelData.data.config.unit, )?.label : 'Default' }`" @@ -348,7 +348,7 @@ along with this program. If not, see . handleBlur( dashboardPanelData.data.config.map_symbol_style.size_by_value, 1, - 'min' + 'min', ) " :label="t('dashboard.minimum')" @@ -379,7 +379,7 @@ along with this program. If not, see . handleBlur( dashboardPanelData.data.config.map_symbol_style.size_by_value, 100, - 'max' + 'max', ) " :label="t('dashboard.maximum')" @@ -409,7 +409,7 @@ along with this program. If not, see . handleBlur( dashboardPanelData.data.config.map_symbol_style, 2, - 'size_fixed' + 'size_fixed', ) " :label="t('dashboard.fixedValue')" @@ -678,7 +678,7 @@ along with this program. If not, see . . . dashboardPanelData.layout.currentQueryIndex ].config, 1, - 'weight_fixed' + 'weight_fixed', ) " :label="t('common.weight')" @@ -895,10 +895,197 @@ along with this program. If not, see . />
+ + + + +
+ + + + +
+ + + + + +
+ + + + +
+ + + + +
+ { const layoutFields = dashboardPanelData.data.queries[ @@ -1289,8 +1571,8 @@ export default defineComponent({ label: it.name, value: it.name, }; - } - ) + }, + ), ); const timeShifts = []; @@ -1341,6 +1623,9 @@ export default defineComponent({ symbolOptions, legendsPositionOptions, unitOptions, + labelPositionOptions, + showSymbol, + lineInterpolationOptions, isWeightFieldPresent, setUnit, handleBlur, diff --git a/web/src/components/icons/dashboards/LinearIcon.vue b/web/src/components/icons/dashboards/LinearIcon.vue new file mode 100644 index 0000000000..85d37ead8d --- /dev/null +++ b/web/src/components/icons/dashboards/LinearIcon.vue @@ -0,0 +1,22 @@ + + + diff --git a/web/src/components/icons/dashboards/NoSymbol.vue b/web/src/components/icons/dashboards/NoSymbol.vue new file mode 100644 index 0000000000..c16373a937 --- /dev/null +++ b/web/src/components/icons/dashboards/NoSymbol.vue @@ -0,0 +1,19 @@ + + + diff --git a/web/src/components/icons/dashboards/Smooth.vue b/web/src/components/icons/dashboards/Smooth.vue new file mode 100644 index 0000000000..30063400c6 --- /dev/null +++ b/web/src/components/icons/dashboards/Smooth.vue @@ -0,0 +1,26 @@ + + + diff --git a/web/src/components/icons/dashboards/StepAfter.vue b/web/src/components/icons/dashboards/StepAfter.vue new file mode 100644 index 0000000000..c6794e8525 --- /dev/null +++ b/web/src/components/icons/dashboards/StepAfter.vue @@ -0,0 +1,40 @@ + + + diff --git a/web/src/components/icons/dashboards/StepBefore.vue b/web/src/components/icons/dashboards/StepBefore.vue new file mode 100644 index 0000000000..07d7cbf4e9 --- /dev/null +++ b/web/src/components/icons/dashboards/StepBefore.vue @@ -0,0 +1,22 @@ + + + diff --git a/web/src/components/icons/dashboards/StepMiddle.vue b/web/src/components/icons/dashboards/StepMiddle.vue new file mode 100644 index 0000000000..e55997effc --- /dev/null +++ b/web/src/components/icons/dashboards/StepMiddle.vue @@ -0,0 +1,33 @@ + + + diff --git a/web/src/composables/useDashboardPanel.ts b/web/src/composables/useDashboardPanel.ts index 5601b33d46..55ad153f31 100644 --- a/web/src/composables/useDashboardPanel.ts +++ b/web/src/composables/useDashboardPanel.ts @@ -48,10 +48,17 @@ const getDefaultDashboardPanelData: any = () => ({ unit: null, unit_custom: null, decimals: 2, + line_thickness: 1.5, top_results: null, top_results_others: false, axis_width: null, axis_border_show: false, + label_option: { + position: null, + rotate: 0, + }, + show_symbol: false, + line_interpolation: "smooth", legend_width: { value: null, unit: "px", diff --git a/web/src/locales/languages/en.json b/web/src/locales/languages/en.json index 34c6d18433..99ec7c27da 100644 --- a/web/src/locales/languages/en.json +++ b/web/src/locales/languages/en.json @@ -582,6 +582,8 @@ "showBorder": "Show Axis Border", "legendsPositionLabel": "Legends Positions", "unitLabel": "Unit", + "labelPosition": "Value Position", + "labelRotate": "Value Rotate", "basemapLabel": "Base Map", "customunitLabel": "Custom Unit", "currencyDollar": "Currency - Dollar ($)", @@ -657,7 +659,21 @@ "scheduledReports": "Scheduled Reports", "newReport": "New Report", "scheduledDashboards": "Scheduled Dashboards", - "panelHeight": "Panel Height" + "panelHeight": "Panel Height", + "none": "None", + "inside": "Inside", + "insideTop": "Inside Top", + "insideBottom": "Inside Bottom", + "top": "Top", + "showSymbol": "Show Symbol", + "yes": "Yes", + "no": "No", + "lineInterpolation": "Line Interpolation", + "linear": "Linear", + "smooth": "Smooth", + "stepBefore": "Step Before", + "stepAfter": "Step After", + "stepMiddle": "Step Middle" }, "panel": { "header": "Panel", diff --git a/web/src/utils/dashboard/convertSQLData.ts b/web/src/utils/dashboard/convertSQLData.ts index 2b88d4c9e6..98a25bfd33 100644 --- a/web/src/utils/dashboard/convertSQLData.ts +++ b/web/src/utils/dashboard/convertSQLData.ts @@ -722,6 +722,12 @@ export const convertSQLData = async ( // inverse data for h-stacked and h-bar inverse: ["h-stacked", "h-bar"].includes(panelSchema.type), name: index == 0 ? panelSchema.queries[0]?.fields?.x[index]?.label : "", + label: { + show: + panelSchema.config?.label_option?.position != null, + position: panelSchema.config?.label_option?.position || "None", + rotate: panelSchema.config?.label_option?.rotate || 0, + }, nameLocation: "middle", nameGap: 25, nameTextStyle: { @@ -830,7 +836,7 @@ export const convertSQLData = async ( series: [], }; - const defaultSeriesProps = getPropsByChartTypeForSeries(panelSchema.type); + const defaultSeriesProps = getPropsByChartTypeForSeries(panelSchema); // Now set the series values as per the chart data // Override any configs if required as per the chart type @@ -897,11 +903,19 @@ export const convertSQLData = async ( const data = missingValueData.filter( (it: any) => it[key1] == key ); + const seriesObj = { //only append if yaxiskeys length is more than 1 name: yAxisKeys.length == 1 ? key : key + " (" + yAxisName + ")", ...defaultSeriesProps, + label: { + show: + panelSchema.config?.label_option?.position != null, + position: + panelSchema.config?.label_option?.position || "None", + rotate: panelSchema.config?.label_option?.rotate || 0, + }, // markLine if exist markLine: { silent: true, @@ -928,6 +942,12 @@ export const convertSQLData = async ( name: panelSchema?.queries[0]?.fields?.y.find( (it: any) => it.alias == key )?.label, + label: { + show: + panelSchema.config?.label_option?.position != null, + position: panelSchema.config?.label_option?.position || "None", + rotate: panelSchema.config?.label_option?.rotate || 0, + }, // color: // panelSchema.queries[0]?.fields?.y.find( // (it: any) => it.alias == key, @@ -1021,6 +1041,12 @@ export const convertSQLData = async ( name: panelSchema?.queries[0]?.fields?.y.find( (it: any) => it.alias == key )?.label, + label: { + show: + panelSchema.config?.label_option?.position != null, + position: panelSchema.config?.label_option?.position || "None", + rotate: panelSchema.config?.label_option?.rotate || 0, + }, // color: // panelSchema.queries[0]?.fields?.y.find( // (it: any) => it.alias == key, @@ -1053,6 +1079,12 @@ export const convertSQLData = async ( // ?.color || "#5960b2", opacity: 0.8, ...defaultSeriesProps, + label: { + show: + panelSchema.config?.label_option?.position != null, + position: panelSchema.config?.label_option?.position || "None", + rotate: panelSchema.config?.label_option?.rotate || 0, + }, // markLine if exist markLine: { silent: true, @@ -1094,6 +1126,12 @@ export const convertSQLData = async ( animation: false, data: getMarkLineData(panelSchema), }, + label: { + show: + panelSchema.config?.label_option?.position != null, + position: panelSchema.config?.label_option?.position || "None", + rotate: panelSchema.config?.label_option?.rotate || 0, + }, data: getAxisDataFromKey(key), barMinHeight: 1, }; @@ -1296,6 +1334,12 @@ export const convertSQLData = async ( const seriesObj = { name: key, ...defaultSeriesProps, + label: { + show: + panelSchema.config?.label_option?.position != null, + position: panelSchema.config?.label_option?.position || "None", + rotate: panelSchema.config?.label_option?.rotate || 0, + }, // markLine if exist markLine: { silent: true, @@ -1357,6 +1401,7 @@ export const convertSQLData = async ( { ...defaultSeriesProps, name: panelSchema?.queries[0]?.fields?.y[0].label, + data: zValues .map((it: any, index: any) => { return xAxisZerothPositionUniqueValue.map( @@ -1506,6 +1551,12 @@ export const convertSQLData = async ( const seriesObj = { name: key, ...defaultSeriesProps, + label: { + show: + panelSchema.config?.label_option?.position != null, + position: panelSchema.config?.label_option?.position || "None", + rotate: panelSchema.config?.label_option?.rotate || 0, + }, // markLine if exist markLine: { silent: true, @@ -2231,8 +2282,8 @@ const largestLabel = (data: any) => { * @param {string} type - The type of chart. * @return {Object} - The properties for the given chart type. */ -const getPropsByChartTypeForSeries = (type: string) => { - switch (type) { +const getPropsByChartTypeForSeries = (panelSchema: any) => { + switch (panelSchema.type) { case "bar": return { type: "bar", @@ -2243,10 +2294,18 @@ const getPropsByChartTypeForSeries = (type: string) => { return { type: "line", emphasis: { focus: "series" }, - smooth: true, - showSymbol: false, + smooth: + panelSchema.config?.line_interpolation === "smooth" || + panelSchema.config?.line_interpolation == null, + step: ["step-start", "step-end", "step-middle"].includes( + panelSchema.config?.line_interpolation, + ) + // TODO: replace this with type integrations + ? panelSchema.config.line_interpolation.replace("step-", "") + : false, + showSymbol: panelSchema.config?.show_symbol ?? false, areaStyle: null, - lineStyle: { width: 1.5 }, + lineStyle: { width: panelSchema.config?.line_thickness ?? 1.5 }, }; case "scatter": return { @@ -2304,11 +2363,18 @@ const getPropsByChartTypeForSeries = (type: string) => { case "area": return { type: "line", - smooth: true, + smooth: + panelSchema.config?.line_interpolation === "smooth" || + panelSchema.config?.line_interpolation == null, + step: ["step-start", "step-end", "step-middle"].includes( + panelSchema.config?.line_interpolation, + ) + ? panelSchema.config.line_interpolation.replace("step-", "") + : false, emphasis: { focus: "series" }, areaStyle: {}, - showSymbol: false, - lineStyle: { width: 1.5 }, + showSymbol: panelSchema.config?.show_symbol ?? false, + lineStyle: { width: panelSchema.config?.line_thickness ?? 1.5 }, }; case "stacked": return { @@ -2336,14 +2402,21 @@ const getPropsByChartTypeForSeries = (type: string) => { case "area-stacked": return { type: "line", - smooth: true, + smooth: + panelSchema.config?.line_interpolation === "smooth" || + panelSchema.config?.line_interpolation == null, + step: ["step-start", "step-end", "step-middle"].includes( + panelSchema.config?.line_interpolation, + ) + ? panelSchema.config.line_interpolation.replace("step-", "") + : false, stack: "Total", areaStyle: {}, emphasis: { focus: "series", }, - showSymbol: false, - lineStyle: { width: 1.5 }, + showSymbol: panelSchema.config?.show_symbol ?? false, + lineStyle: { width: panelSchema.config?.line_thickness ?? 1.5 }, }; case "metric": return {