From 1568f964df3ed3e07650dcdb1109c63bf45f4568 Mon Sep 17 00:00:00 2001 From: Maksym Yadlovskyi Date: Fri, 10 Jan 2025 13:54:37 +0100 Subject: [PATCH 1/8] Add a feature flag which hides the Executive Dashboard from the user. --- .../resources/org/graylog2/featureflag/feature-flag.config | 3 +++ 1 file changed, 3 insertions(+) diff --git a/graylog2-server/src/main/resources/org/graylog2/featureflag/feature-flag.config b/graylog2-server/src/main/resources/org/graylog2/featureflag/feature-flag.config index 598fc99fe0bf..c6cd2305c6e3 100644 --- a/graylog2-server/src/main/resources/org/graylog2/featureflag/feature-flag.config +++ b/graylog2-server/src/main/resources/org/graylog2/featureflag/feature-flag.config @@ -96,3 +96,6 @@ setup_mode=off # Show security events in paginated entity data table show_security_events_in_pedt=off + +# Show executive dashboard +show_executive_dashboard_page=off From a0f8ece4a96dc9b224642d76b9bb5173bfb67a29 Mon Sep 17 00:00:00 2001 From: Maksym Yadlovskyi Date: Fri, 10 Jan 2025 14:05:03 +0100 Subject: [PATCH 2/8] Revert "Add a feature flag which hides the Executive Dashboard from the user." This reverts commit 2bee26679ebe739e518dbf3e0854191d1edb5ce2. --- .../resources/org/graylog2/featureflag/feature-flag.config | 3 --- 1 file changed, 3 deletions(-) diff --git a/graylog2-server/src/main/resources/org/graylog2/featureflag/feature-flag.config b/graylog2-server/src/main/resources/org/graylog2/featureflag/feature-flag.config index c6cd2305c6e3..598fc99fe0bf 100644 --- a/graylog2-server/src/main/resources/org/graylog2/featureflag/feature-flag.config +++ b/graylog2-server/src/main/resources/org/graylog2/featureflag/feature-flag.config @@ -96,6 +96,3 @@ setup_mode=off # Show security events in paginated entity data table show_security_events_in_pedt=off - -# Show executive dashboard -show_executive_dashboard_page=off From 7e429ec72b70cf233b91261384cd053b1b84fbdb Mon Sep 17 00:00:00 2001 From: Maksym Yadlovskyi Date: Mon, 13 Jan 2025 17:17:11 +0100 Subject: [PATCH 3/8] Add units to number visualization --- .../number/NumberVisualization.tsx | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/graylog2-web-interface/src/views/components/visualizations/number/NumberVisualization.tsx b/graylog2-web-interface/src/views/components/visualizations/number/NumberVisualization.tsx index 1c1559a5357f..ebd7faaec1cd 100644 --- a/graylog2-web-interface/src/views/components/visualizations/number/NumberVisualization.tsx +++ b/graylog2-web-interface/src/views/components/visualizations/number/NumberVisualization.tsx @@ -14,7 +14,7 @@ * along with this program. If not, see * . */ -import React, { useContext, useEffect, useRef } from 'react'; +import React, { useContext, useEffect, useRef, useMemo } from 'react'; import styled, { css } from 'styled-components'; import type { Rows } from 'views/logic/searchtypes/pivot/PivotHandler'; @@ -27,9 +27,13 @@ import NumberVisualizationConfig from 'views/logic/aggregationbuilder/visualizat import type { VisualizationComponentProps } from 'views/components/aggregationbuilder/AggregationBuilder'; import { makeVisualization, retrieveChartData } from 'views/components/aggregationbuilder/AggregationBuilder'; import ElementDimensions from 'components/common/ElementDimensions'; +import useWidgetUnits from 'views/components/visualizations/hooks/useWidgetUnits'; +import useFeature from 'hooks/useFeature'; +import { UNIT_FEATURE_FLAG } from 'views/components/visualizations/Constants'; +import { parseSeries } from 'views/logic/aggregationbuilder/Series'; -import Trend from './Trend'; import AutoFontSizer from './AutoFontSizer'; +import Trend from './Trend'; const Container = styled.div<{ $height: number }>(({ $height }) => css` height: ${$height}px; @@ -89,6 +93,8 @@ const _extractFirstSeriesName = (config) => { const NumberVisualization = ({ config, fields, data, height: heightProp }: VisualizationComponentProps) => { const targetRef = useRef(); + const unitFeatureEnabled = useFeature(UNIT_FEATURE_FLAG); + const widgetUnits = useWidgetUnits(config); const onRenderComplete = useContext(RenderCompletionCallback); const visualizationConfig = (config.visualizationConfig as NumberVisualizationConfig) ?? NumberVisualizationConfig.create(); @@ -99,6 +105,13 @@ const NumberVisualization = ({ config, fields, data, height: heightProp }: Visua const trendRows = data.trend; const { value } = _extractValueAndField(chartRows); const { value: previousValue } = _extractValueAndField(trendRows || []); + const unit = useMemo(() => { + if (!unitFeatureEnabled || visualizationConfig.trend) return undefined; + + const fieldNameKey = parseSeries(field).field; + + return widgetUnits.getFieldUnit(fieldNameKey); + }, [field, unitFeatureEnabled, visualizationConfig.trend, widgetUnits]); if (!field || (value !== 0 && !value)) { return <>N/A; @@ -115,7 +128,8 @@ const NumberVisualization = ({ config, fields, data, height: heightProp }: Visua + render={DecoratedValue} + unit={unit} /> )} From 74aed2caf1c265ab880dd4403e65c31eea447993 Mon Sep 17 00:00:00 2001 From: Maksym Yadlovskyi Date: Tue, 14 Jan 2025 10:48:47 +0100 Subject: [PATCH 4/8] Add units to trend component. Add to _getPrettifiedValue support of negative values --- .../number/NumberVisualization.tsx | 7 ++++--- .../visualizations/number/Trend.tsx | 12 ++++++++--- .../visualizations/utils/unitConverters.ts | 20 +++++++++++++------ 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/graylog2-web-interface/src/views/components/visualizations/number/NumberVisualization.tsx b/graylog2-web-interface/src/views/components/visualizations/number/NumberVisualization.tsx index ebd7faaec1cd..aaee11fd9cfa 100644 --- a/graylog2-web-interface/src/views/components/visualizations/number/NumberVisualization.tsx +++ b/graylog2-web-interface/src/views/components/visualizations/number/NumberVisualization.tsx @@ -106,12 +106,12 @@ const NumberVisualization = ({ config, fields, data, height: heightProp }: Visua const { value } = _extractValueAndField(chartRows); const { value: previousValue } = _extractValueAndField(trendRows || []); const unit = useMemo(() => { - if (!unitFeatureEnabled || visualizationConfig.trend) return undefined; + if (!unitFeatureEnabled) return undefined; const fieldNameKey = parseSeries(field).field; return widgetUnits.getFieldUnit(fieldNameKey); - }, [field, unitFeatureEnabled, visualizationConfig.trend, widgetUnits]); + }, [field, unitFeatureEnabled, widgetUnits]); if (!field || (value !== 0 && !value)) { return <>N/A; @@ -141,7 +141,8 @@ const NumberVisualization = ({ config, fields, data, height: heightProp }: Visua + trendPreference={visualizationConfig.trendPreference} + unit={unit} /> )} diff --git a/graylog2-web-interface/src/views/components/visualizations/number/Trend.tsx b/graylog2-web-interface/src/views/components/visualizations/number/Trend.tsx index cc8682f03189..f7e897045926 100644 --- a/graylog2-web-interface/src/views/components/visualizations/number/Trend.tsx +++ b/graylog2-web-interface/src/views/components/visualizations/number/Trend.tsx @@ -21,6 +21,8 @@ import numeral from 'numeral'; import Icon from 'components/common/Icon'; import type { TrendPreference } from 'views/logic/aggregationbuilder/visualizations/NumberVisualizationConfig'; +import type FieldUnit from 'views/logic/aggregationbuilder/FieldUnit'; +import { getPrettifiedValue } from 'views/components/visualizations/utils/unitConverters'; type TrendDirection = 'good' | 'bad' | 'neutral'; @@ -28,6 +30,7 @@ type Props = { current: number, previous: number | undefined | null, trendPreference: TrendPreference, + unit?: FieldUnit, }; const background = (theme: DefaultTheme, trend: TrendDirection = 'neutral') => ({ @@ -98,19 +101,22 @@ const diff = (current: number | undefined, previous: number | undefined): [numbe return [NaN, NaN]; }; -const Trend = React.forwardRef(({ current, previous, trendPreference }: Props, ref) => { +const Trend = React.forwardRef(({ current, previous, trendPreference, unit }: Props, ref) => { const [difference, differencePercent] = diff(current, previous); const backgroundTrend = _trendDirection(difference, trendPreference); const trendIcon = _trendIcon(difference); - const absoluteDifference = Number.isFinite(difference) ? numeral(difference).format('+0,0[.]0[000]') : '--'; + const { value: prettyValue, unit: prettyUnit } = unit?.isDefined ? getPrettifiedValue(difference, { unitType: unit?.unitType, abbrev: unit?.abbrev }) : { value: difference, unit: { abbrev: '' } }; + const { value: previousPretty, unit: previousPrettyUnit } = unit?.isDefined ? getPrettifiedValue(previous, { unitType: unit?.unitType, abbrev: unit?.abbrev }) : { value: previous, unit: { abbrev: '' } }; + + const absoluteDifference = Number.isFinite(prettyValue) ? `${numeral(prettyValue).format('+0,0[.]0[000]')} ${prettyUnit.abbrev}` : '--'; const relativeDifference = Number.isFinite(differencePercent) ? numeral(differencePercent).format('+0[.]0[0]%') : '--'; return ( - {absoluteDifference} / {relativeDifference} + {absoluteDifference} / {relativeDifference} ); diff --git a/graylog2-web-interface/src/views/components/visualizations/utils/unitConverters.ts b/graylog2-web-interface/src/views/components/visualizations/utils/unitConverters.ts index a8caa7ea4087..2772c2437a37 100644 --- a/graylog2-web-interface/src/views/components/visualizations/utils/unitConverters.ts +++ b/graylog2-web-interface/src/views/components/visualizations/utils/unitConverters.ts @@ -130,20 +130,28 @@ const _convertValueToUnit = (units: FieldUnitTypes, value: number, fromParams: C export const _getPrettifiedValue = (units: FieldUnitTypes, initValue: number | string, params: ConversionParams): ConvertedResult => { const currentUnit = units?.[params?.unitType] ?? null; - const value = initValue === null ? null : toNumber(initValue); - if (!(value && currentUnit)) return ({ value, unit: currentUnit ? currentUnit.find(({ abbrev }) => abbrev === params.abbrev) : null }); + const _value = initValue === null ? null : toNumber(initValue); + if (!(_value && currentUnit)) return ({ value: _value, unit: currentUnit ? currentUnit.find(({ abbrev }) => abbrev === params.abbrev) : null }); - const allConvertedValues = Object.values(currentUnit).map((unit: Unit) => _convertValueToUnit(units, value, params, { abbrev: unit.abbrev, unitType: unit.unitType })); + const sign = Math.sign(_value); + const absolutValue = Math.abs(_value); + + const allConvertedValues = Object.values(currentUnit).map((unit: Unit) => _convertValueToUnit(units, absolutValue, params, { abbrev: unit.abbrev, unitType: unit.unitType })); const filtratedValues = allConvertedValues.filter(({ value: val, unit }) => val >= 1 && unit.useInPrettier); + let result: ConvertedResult; + if (filtratedValues.length > 0) { - return minBy(filtratedValues, ({ value: val }) => val); + result = minBy(filtratedValues, ({ value: val }) => val); + } else { + const filtratedValuesLower = allConvertedValues.filter(({ value: val, unit }) => val < 1 && unit.useInPrettier); + result = maxBy(filtratedValuesLower, ({ value: val }) => val); } - const filtratedValuesLower = allConvertedValues.filter(({ value: val, unit }) => val < 1 && unit.useInPrettier); + result.value *= sign; - return maxBy(filtratedValuesLower, ({ value: val }) => val); + return result; }; export type ConvertValueToUnit = (value: number, fromParams: ConversionParams, toParams: ConversionParams) => ConvertedResult From 1e1d7887d86ae989750139e768010bf82338ee5b Mon Sep 17 00:00:00 2001 From: Maksym Yadlovskyi Date: Fri, 17 Jan 2025 15:59:30 +0100 Subject: [PATCH 5/8] Show current value unit --- .../visualizations/number/Trend.tsx | 46 ++++++++++++++++--- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/graylog2-web-interface/src/views/components/visualizations/number/Trend.tsx b/graylog2-web-interface/src/views/components/visualizations/number/Trend.tsx index f7e897045926..df38697233eb 100644 --- a/graylog2-web-interface/src/views/components/visualizations/number/Trend.tsx +++ b/graylog2-web-interface/src/views/components/visualizations/number/Trend.tsx @@ -22,7 +22,12 @@ import numeral from 'numeral'; import Icon from 'components/common/Icon'; import type { TrendPreference } from 'views/logic/aggregationbuilder/visualizations/NumberVisualizationConfig'; import type FieldUnit from 'views/logic/aggregationbuilder/FieldUnit'; -import { getPrettifiedValue } from 'views/components/visualizations/utils/unitConverters'; +import { + getPrettifiedValue, + convertValueToUnit, +} from 'views/components/visualizations/utils/unitConverters'; +import formatValueWithUnitLabel from 'views/components/visualizations/utils/formatValueWithUnitLabel'; +import getUnitTextLabel from 'views/components/visualizations/utils/getUnitTextLabel'; type TrendDirection = 'good' | 'bad' | 'neutral'; @@ -101,22 +106,49 @@ const diff = (current: number | undefined, previous: number | undefined): [numbe return [NaN, NaN]; }; -const Trend = React.forwardRef(({ current, previous, trendPreference, unit }: Props, ref) => { +const getTrendConvertedValues = (current: number, previous: number, fieldUNit: FieldUnit) => { const [difference, differencePercent] = diff(current, previous); + if (!fieldUNit?.isDefined) { + return ({ + previousConverted: previous, + differenceConverted: difference, + differencePercent, + unitAbbrevString: '', + }); + } + + const originalParams = { unitType: fieldUNit?.unitType, abbrev: fieldUNit?.abbrev }; + const { unit: currentPrettyUnit } = getPrettifiedValue(current, originalParams); + const currentPrettyParams = { unitType: currentPrettyUnit?.unitType, abbrev: currentPrettyUnit?.abbrev }; + const { value: prettyDiff } = convertValueToUnit(difference, originalParams, currentPrettyParams); + const { value: previousPretty } = convertValueToUnit(previous, originalParams, currentPrettyParams); + + return ({ + previousConverted: `${formatValueWithUnitLabel(previousPretty, currentPrettyUnit.abbrev)} (${previous})`, + unitAbbrevString: ` ${getUnitTextLabel(currentPrettyUnit.abbrev)}`, + differenceConverted: prettyDiff, + differencePercent, + difference, + }); +}; + +const Trend = React.forwardRef(({ current, previous, trendPreference, unit }: Props, ref) => { + const { differenceConverted, difference, differencePercent, unitAbbrevString, previousConverted } = getTrendConvertedValues(current, previous, unit); + const backgroundTrend = _trendDirection(difference, trendPreference); const trendIcon = _trendIcon(difference); - const { value: prettyValue, unit: prettyUnit } = unit?.isDefined ? getPrettifiedValue(difference, { unitType: unit?.unitType, abbrev: unit?.abbrev }) : { value: difference, unit: { abbrev: '' } }; - const { value: previousPretty, unit: previousPrettyUnit } = unit?.isDefined ? getPrettifiedValue(previous, { unitType: unit?.unitType, abbrev: unit?.abbrev }) : { value: previous, unit: { abbrev: '' } }; - - const absoluteDifference = Number.isFinite(prettyValue) ? `${numeral(prettyValue).format('+0,0[.]0[000]')} ${prettyUnit.abbrev}` : '--'; + const absoluteDifference = Number.isFinite(differenceConverted) ? `${numeral(differenceConverted).format('+0,0[.]0[000]')}${unitAbbrevString}` : '--'; const relativeDifference = Number.isFinite(differencePercent) ? numeral(differencePercent).format('+0[.]0[0]%') : '--'; return ( - {absoluteDifference} / {relativeDifference} + {' '} + + {absoluteDifference} / {relativeDifference} + ); From 3875ea2e80d664070bf2f5e7bdd160a45bec7ddd Mon Sep 17 00:00:00 2001 From: Maksym Yadlovskyi Date: Mon, 20 Jan 2025 09:41:04 +0100 Subject: [PATCH 6/8] fix tests. remove unnecessary variable --- .../components/visualizations/number/Trend.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/graylog2-web-interface/src/views/components/visualizations/number/Trend.tsx b/graylog2-web-interface/src/views/components/visualizations/number/Trend.tsx index df38697233eb..747d83859b7f 100644 --- a/graylog2-web-interface/src/views/components/visualizations/number/Trend.tsx +++ b/graylog2-web-interface/src/views/components/visualizations/number/Trend.tsx @@ -106,7 +106,12 @@ const diff = (current: number | undefined, previous: number | undefined): [numbe return [NaN, NaN]; }; -const getTrendConvertedValues = (current: number, previous: number, fieldUNit: FieldUnit) => { +const getTrendConvertedValues = (current: number, previous: number, fieldUNit: FieldUnit): { + previousConverted: number | string, + differenceConverted: number, + differencePercent: number, + unitAbbrevString: string, +} => { const [difference, differencePercent] = diff(current, previous); if (!fieldUNit?.isDefined) { @@ -129,15 +134,14 @@ const getTrendConvertedValues = (current: number, previous: number, fieldUNit: F unitAbbrevString: ` ${getUnitTextLabel(currentPrettyUnit.abbrev)}`, differenceConverted: prettyDiff, differencePercent, - difference, }); }; const Trend = React.forwardRef(({ current, previous, trendPreference, unit }: Props, ref) => { - const { differenceConverted, difference, differencePercent, unitAbbrevString, previousConverted } = getTrendConvertedValues(current, previous, unit); + const { differenceConverted, differencePercent, unitAbbrevString, previousConverted } = getTrendConvertedValues(current, previous, unit); - const backgroundTrend = _trendDirection(difference, trendPreference); - const trendIcon = _trendIcon(difference); + const backgroundTrend = _trendDirection(differenceConverted, trendPreference); + const trendIcon = _trendIcon(differenceConverted); const absoluteDifference = Number.isFinite(differenceConverted) ? `${numeral(differenceConverted).format('+0,0[.]0[000]')}${unitAbbrevString}` : '--'; const relativeDifference = Number.isFinite(differencePercent) ? numeral(differencePercent).format('+0[.]0[0]%') : '--'; From 7e1060a71aa220dab81dce7d08f05358905929c4 Mon Sep 17 00:00:00 2001 From: Maksym Yadlovskyi Date: Mon, 20 Jan 2025 09:45:03 +0100 Subject: [PATCH 7/8] add changelog --- changelog/unreleased/issue-20965.toml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelog/unreleased/issue-20965.toml diff --git a/changelog/unreleased/issue-20965.toml b/changelog/unreleased/issue-20965.toml new file mode 100644 index 000000000000..945c311ad85e --- /dev/null +++ b/changelog/unreleased/issue-20965.toml @@ -0,0 +1,5 @@ +type = "a" +message = "Add unit handling to a single number widget" + +issues = ["20965"] +pulls = ["21339"] From 7e723b6a79fce3c7431273c1d34e285854797a51 Mon Sep 17 00:00:00 2001 From: Maksym Yadlovskyi Date: Mon, 20 Jan 2025 10:07:52 +0100 Subject: [PATCH 8/8] fix linter error --- .../src/views/components/visualizations/number/Trend.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graylog2-web-interface/src/views/components/visualizations/number/Trend.tsx b/graylog2-web-interface/src/views/components/visualizations/number/Trend.tsx index 747d83859b7f..c632d99b32f4 100644 --- a/graylog2-web-interface/src/views/components/visualizations/number/Trend.tsx +++ b/graylog2-web-interface/src/views/components/visualizations/number/Trend.tsx @@ -137,7 +137,7 @@ const getTrendConvertedValues = (current: number, previous: number, fieldUNit: F }); }; -const Trend = React.forwardRef(({ current, previous, trendPreference, unit }: Props, ref) => { +const Trend = React.forwardRef(({ current, previous, trendPreference, unit = undefined }: Props, ref) => { const { differenceConverted, differencePercent, unitAbbrevString, previousConverted } = getTrendConvertedValues(current, previous, unit); const backgroundTrend = _trendDirection(differenceConverted, trendPreference);