mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 21:58:59 +08:00
fix: fixed threshold for columns with units (#6079)
* fix: fixed threshold for columns with units * fix: added interunit and category conversion for handling threshold across different unit types * fix: added invalid comparison text and removed unsupported unit categories * fix: cleanup and multiple threshold and state change handling * fix: restrict category select to only columnUnit group * fix: restricted column name from threshold option * fix: removed console log * fix: resolved comments and some refactoring * fix: resolved comments and some refactoring
This commit is contained in:
parent
28818fbaac
commit
503ed45a99
@ -97,13 +97,19 @@ function GridTableComponent({
|
|||||||
|
|
||||||
const newColumnData = columns.map((e) => ({
|
const newColumnData = columns.map((e) => ({
|
||||||
...e,
|
...e,
|
||||||
render: (text: string): ReactNode => {
|
render: (text: string, ...rest: any): ReactNode => {
|
||||||
const isNumber = !Number.isNaN(Number(text));
|
let textForThreshold = text;
|
||||||
|
if (columnUnits && columnUnits?.[e.title as string]) {
|
||||||
|
textForThreshold = rest[0][`${e.title}_without_unit`];
|
||||||
|
}
|
||||||
|
const isNumber = !Number.isNaN(Number(textForThreshold));
|
||||||
|
|
||||||
if (thresholds && isNumber) {
|
if (thresholds && isNumber) {
|
||||||
const { hasMultipleMatches, threshold } = findMatchingThreshold(
|
const { hasMultipleMatches, threshold } = findMatchingThreshold(
|
||||||
thresholds,
|
thresholds,
|
||||||
e.title as string,
|
e.title as string,
|
||||||
Number(text),
|
Number(textForThreshold),
|
||||||
|
columnUnits?.[e.title as string],
|
||||||
);
|
);
|
||||||
|
|
||||||
const idx = thresholds.findIndex(
|
const idx = thresholds.findIndex(
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/* eslint-disable sonarjs/cognitive-complexity */
|
/* eslint-disable sonarjs/cognitive-complexity */
|
||||||
import { ColumnsType, ColumnType } from 'antd/es/table';
|
import { ColumnsType, ColumnType } from 'antd/es/table';
|
||||||
|
import { convertUnit } from 'container/NewWidget/RightContainer/dataFormatCategories';
|
||||||
import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types';
|
import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types';
|
||||||
import { QUERY_TABLE_CONFIG } from 'container/QueryTable/config';
|
import { QUERY_TABLE_CONFIG } from 'container/QueryTable/config';
|
||||||
import { QueryTableProps } from 'container/QueryTable/QueryTable.intefaces';
|
import { QueryTableProps } from 'container/QueryTable/QueryTable.intefaces';
|
||||||
@ -30,10 +31,39 @@ function evaluateCondition(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates whether a given value meets a specified threshold condition.
|
||||||
|
* It first converts the value to the appropriate unit if a threshold unit is provided,
|
||||||
|
* and then checks the condition using the specified operator.
|
||||||
|
*
|
||||||
|
* @param value - The value to be evaluated.
|
||||||
|
* @param thresholdValue - The threshold value to compare against.
|
||||||
|
* @param thresholdOperator - The operator used for comparison (e.g., '>', '<', '==').
|
||||||
|
* @param thresholdUnit - The unit to which the value should be converted.
|
||||||
|
* @param columnUnit - The current unit of the value.
|
||||||
|
* @returns A boolean indicating whether the value meets the threshold condition.
|
||||||
|
*/
|
||||||
|
function evaluateThresholdWithConvertedValue(
|
||||||
|
value: number,
|
||||||
|
thresholdValue: number,
|
||||||
|
thresholdOperator?: string,
|
||||||
|
thresholdUnit?: string,
|
||||||
|
columnUnit?: string,
|
||||||
|
): boolean {
|
||||||
|
const convertedValue = convertUnit(value, columnUnit, thresholdUnit);
|
||||||
|
|
||||||
|
if (convertedValue) {
|
||||||
|
return evaluateCondition(thresholdOperator, convertedValue, thresholdValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return evaluateCondition(thresholdOperator, value, thresholdValue);
|
||||||
|
}
|
||||||
|
|
||||||
export function findMatchingThreshold(
|
export function findMatchingThreshold(
|
||||||
thresholds: ThresholdProps[],
|
thresholds: ThresholdProps[],
|
||||||
label: string,
|
label: string,
|
||||||
value: number,
|
value: number,
|
||||||
|
columnUnit?: string,
|
||||||
): {
|
): {
|
||||||
threshold: ThresholdProps;
|
threshold: ThresholdProps;
|
||||||
hasMultipleMatches: boolean;
|
hasMultipleMatches: boolean;
|
||||||
@ -45,10 +75,12 @@ export function findMatchingThreshold(
|
|||||||
if (
|
if (
|
||||||
threshold.thresholdValue !== undefined &&
|
threshold.thresholdValue !== undefined &&
|
||||||
threshold.thresholdTableOptions === label &&
|
threshold.thresholdTableOptions === label &&
|
||||||
evaluateCondition(
|
evaluateThresholdWithConvertedValue(
|
||||||
threshold.thresholdOperator,
|
|
||||||
value,
|
value,
|
||||||
threshold.thresholdValue,
|
threshold?.thresholdValue,
|
||||||
|
threshold.thresholdOperator,
|
||||||
|
threshold.thresholdUnit,
|
||||||
|
columnUnit,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
matchingThresholds.push(threshold);
|
matchingThresholds.push(threshold);
|
||||||
|
@ -297,6 +297,16 @@
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.invalid-unit {
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
font-family: 'Giest Mono';
|
||||||
|
font-size: 11px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 16px;
|
||||||
|
letter-spacing: 0.48px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.threshold-card-container:hover {
|
.threshold-card-container:hover {
|
||||||
|
@ -3,17 +3,18 @@ import './Threshold.styles.scss';
|
|||||||
|
|
||||||
import { Button, Input, InputNumber, Select, Space, Typography } from 'antd';
|
import { Button, Input, InputNumber, Select, Space, Typography } from 'antd';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
|
import { unitOptions } from 'container/NewWidget/utils';
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
import { Check, Pencil, Trash2, X } from 'lucide-react';
|
import { Check, Pencil, Trash2, X } from 'lucide-react';
|
||||||
import { useRef, useState } from 'react';
|
import { useMemo, useRef, useState } from 'react';
|
||||||
import { useDrag, useDrop, XYCoord } from 'react-dnd';
|
import { useDrag, useDrop, XYCoord } from 'react-dnd';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
operatorOptions,
|
operatorOptions,
|
||||||
panelTypeVsDragAndDrop,
|
panelTypeVsDragAndDrop,
|
||||||
showAsOptions,
|
showAsOptions,
|
||||||
unitOptions,
|
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
|
import { convertUnit } from '../dataFormatCategories';
|
||||||
import ColorSelector from './ColorSelector';
|
import ColorSelector from './ColorSelector';
|
||||||
import CustomColor from './CustomColor';
|
import CustomColor from './CustomColor';
|
||||||
import ShowCaseValue from './ShowCaseValue';
|
import ShowCaseValue from './ShowCaseValue';
|
||||||
@ -40,6 +41,7 @@ function Threshold({
|
|||||||
thresholdLabel = '',
|
thresholdLabel = '',
|
||||||
tableOptions,
|
tableOptions,
|
||||||
thresholdTableOptions = '',
|
thresholdTableOptions = '',
|
||||||
|
columnUnits,
|
||||||
}: ThresholdProps): JSX.Element {
|
}: ThresholdProps): JSX.Element {
|
||||||
const [isEditMode, setIsEditMode] = useState<boolean>(isEditEnabled);
|
const [isEditMode, setIsEditMode] = useState<boolean>(isEditEnabled);
|
||||||
const [operator, setOperator] = useState<string | number>(
|
const [operator, setOperator] = useState<string | number>(
|
||||||
@ -192,6 +194,13 @@ function Threshold({
|
|||||||
|
|
||||||
const allowDragAndDrop = panelTypeVsDragAndDrop[selectedGraph];
|
const allowDragAndDrop = panelTypeVsDragAndDrop[selectedGraph];
|
||||||
|
|
||||||
|
const isInvalidUnitComparison = useMemo(
|
||||||
|
() =>
|
||||||
|
unit !== 'none' &&
|
||||||
|
convertUnit(value, unit, columnUnits?.[tableSelectedOption]) === null,
|
||||||
|
[unit, value, columnUnits, tableSelectedOption],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={allowDragAndDrop ? ref : null}
|
ref={allowDragAndDrop ? ref : null}
|
||||||
@ -303,7 +312,7 @@ function Threshold({
|
|||||||
{isEditMode ? (
|
{isEditMode ? (
|
||||||
<Select
|
<Select
|
||||||
defaultValue={unit}
|
defaultValue={unit}
|
||||||
options={unitOptions}
|
options={unitOptions(columnUnits?.[tableSelectedOption] || '')}
|
||||||
onChange={handleUnitChange}
|
onChange={handleUnitChange}
|
||||||
showSearch
|
showSearch
|
||||||
className="unit-selection"
|
className="unit-selection"
|
||||||
@ -339,6 +348,12 @@ function Threshold({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{isInvalidUnitComparison && (
|
||||||
|
<Typography.Text className="invalid-unit">
|
||||||
|
Threshold unit ({unit}) is not valid in comparison with the column unit (
|
||||||
|
{columnUnits?.[tableSelectedOption] || 'none'})
|
||||||
|
</Typography.Text>
|
||||||
|
)}
|
||||||
{isEditMode && (
|
{isEditMode && (
|
||||||
<div className="threshold-action-button">
|
<div className="threshold-action-button">
|
||||||
<Button
|
<Button
|
||||||
|
@ -3,14 +3,12 @@
|
|||||||
import './ThresholdSelector.styles.scss';
|
import './ThresholdSelector.styles.scss';
|
||||||
|
|
||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
import { ColumnsType } from 'antd/es/table';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
import { Events } from 'constants/events';
|
|
||||||
import { RowData } from 'lib/query/createTableColumnsFromQuery';
|
|
||||||
import { Antenna, Plus } from 'lucide-react';
|
import { Antenna, Plus } from 'lucide-react';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { DndProvider } from 'react-dnd';
|
import { DndProvider } from 'react-dnd';
|
||||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||||
import { eventEmitter } from 'utils/getEventEmitter';
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
import Threshold from './Threshold';
|
import Threshold from './Threshold';
|
||||||
@ -21,22 +19,23 @@ function ThresholdSelector({
|
|||||||
setThresholds,
|
setThresholds,
|
||||||
yAxisUnit,
|
yAxisUnit,
|
||||||
selectedGraph,
|
selectedGraph,
|
||||||
|
columnUnits,
|
||||||
}: ThresholdSelectorProps): JSX.Element {
|
}: ThresholdSelectorProps): JSX.Element {
|
||||||
const [tableOptions, setTableOptions] = useState<
|
const { currentQuery } = useQueryBuilder();
|
||||||
Array<{ value: string; label: string }>
|
|
||||||
>([]);
|
function getAggregateColumnsNamesAndLabels(): string[] {
|
||||||
useEffect(() => {
|
if (currentQuery.queryType === EQueryType.QUERY_BUILDER) {
|
||||||
eventEmitter.on(
|
const queries = currentQuery.builder.queryData.map((q) => q.queryName);
|
||||||
Events.TABLE_COLUMNS_DATA,
|
const formulas = currentQuery.builder.queryFormulas.map((q) => q.queryName);
|
||||||
(data: { columns: ColumnsType<RowData>; dataSource: RowData[] }) => {
|
return [...queries, ...formulas];
|
||||||
const newTableOptions = data.columns.map((e) => ({
|
}
|
||||||
value: e.title as string,
|
if (currentQuery.queryType === EQueryType.CLICKHOUSE) {
|
||||||
label: e.title as string,
|
return currentQuery.clickhouse_sql.map((q) => q.name);
|
||||||
}));
|
}
|
||||||
setTableOptions([...newTableOptions]);
|
return currentQuery.promql.map((q) => q.name);
|
||||||
},
|
}
|
||||||
);
|
|
||||||
}, []);
|
const aggregationQueries = getAggregateColumnsNamesAndLabels();
|
||||||
|
|
||||||
const moveThreshold = useCallback(
|
const moveThreshold = useCallback(
|
||||||
(dragIndex: number, hoverIndex: number) => {
|
(dragIndex: number, hoverIndex: number) => {
|
||||||
@ -66,7 +65,7 @@ function ThresholdSelector({
|
|||||||
moveThreshold,
|
moveThreshold,
|
||||||
keyIndex: thresholds.length,
|
keyIndex: thresholds.length,
|
||||||
selectedGraph,
|
selectedGraph,
|
||||||
thresholdTableOptions: tableOptions[0]?.value || '',
|
thresholdTableOptions: aggregationQueries[0] || '',
|
||||||
},
|
},
|
||||||
...thresholds,
|
...thresholds,
|
||||||
]);
|
]);
|
||||||
@ -105,8 +104,12 @@ function ThresholdSelector({
|
|||||||
moveThreshold={moveThreshold}
|
moveThreshold={moveThreshold}
|
||||||
selectedGraph={selectedGraph}
|
selectedGraph={selectedGraph}
|
||||||
thresholdLabel={threshold.thresholdLabel}
|
thresholdLabel={threshold.thresholdLabel}
|
||||||
tableOptions={tableOptions}
|
tableOptions={aggregationQueries.map((query) => ({
|
||||||
|
value: query,
|
||||||
|
label: query,
|
||||||
|
}))}
|
||||||
thresholdTableOptions={threshold.thresholdTableOptions}
|
thresholdTableOptions={threshold.thresholdTableOptions}
|
||||||
|
columnUnits={columnUnits}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { Dispatch, ReactNode, SetStateAction } from 'react';
|
import { Dispatch, ReactNode, SetStateAction } from 'react';
|
||||||
|
import { ColumnUnit } from 'types/api/dashboard/getAll';
|
||||||
|
|
||||||
export type ThresholdOperators = '>' | '<' | '>=' | '<=' | '=';
|
export type ThresholdOperators = '>' | '<' | '>=' | '<=' | '=';
|
||||||
|
|
||||||
@ -19,6 +20,7 @@ export type ThresholdProps = {
|
|||||||
moveThreshold: (dragIndex: number, hoverIndex: number) => void;
|
moveThreshold: (dragIndex: number, hoverIndex: number) => void;
|
||||||
selectedGraph: PANEL_TYPES;
|
selectedGraph: PANEL_TYPES;
|
||||||
tableOptions?: Array<{ value: string; label: string }>;
|
tableOptions?: Array<{ value: string; label: string }>;
|
||||||
|
columnUnits?: ColumnUnit;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ShowCaseValueProps = {
|
export type ShowCaseValueProps = {
|
||||||
@ -36,4 +38,5 @@ export type ThresholdSelectorProps = {
|
|||||||
thresholds: ThresholdProps[];
|
thresholds: ThresholdProps[];
|
||||||
setThresholds: Dispatch<SetStateAction<ThresholdProps[]>>;
|
setThresholds: Dispatch<SetStateAction<ThresholdProps[]>>;
|
||||||
selectedGraph: PANEL_TYPES;
|
selectedGraph: PANEL_TYPES;
|
||||||
|
columnUnits: ColumnUnit;
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import { DefaultOptionType } from 'antd/es/select';
|
import { DefaultOptionType } from 'antd/es/select';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { categoryToSupport } from 'container/QueryBuilder/filters/BuilderUnitsFilter/config';
|
|
||||||
|
|
||||||
import { getCategorySelectOptionByName } from './alertFomatCategories';
|
|
||||||
|
|
||||||
export const operatorOptions: DefaultOptionType[] = [
|
export const operatorOptions: DefaultOptionType[] = [
|
||||||
{ value: '>', label: '>' },
|
{ value: '>', label: '>' },
|
||||||
@ -11,11 +8,6 @@ export const operatorOptions: DefaultOptionType[] = [
|
|||||||
{ value: '<=', label: '<=' },
|
{ value: '<=', label: '<=' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const unitOptions = categoryToSupport.map((category) => ({
|
|
||||||
label: category,
|
|
||||||
options: getCategorySelectOptionByName(category),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const showAsOptions: DefaultOptionType[] = [
|
export const showAsOptions: DefaultOptionType[] = [
|
||||||
{ value: 'Text', label: 'Text' },
|
{ value: 'Text', label: 'Text' },
|
||||||
{ value: 'Background', label: 'Background' },
|
{ value: 'Background', label: 'Background' },
|
||||||
|
@ -438,3 +438,168 @@ export const dataTypeCategories: DataTypeCategories = [
|
|||||||
export const flattenedCategories = flattenDeep(
|
export const flattenedCategories = flattenDeep(
|
||||||
dataTypeCategories.map((category) => category.formats),
|
dataTypeCategories.map((category) => category.formats),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
type ConversionFactors = {
|
||||||
|
[key: string]: {
|
||||||
|
[key: string]: number | null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Object containing conversion factors for various categories and formats
|
||||||
|
const conversionFactors: ConversionFactors = {
|
||||||
|
[CategoryNames.Time]: {
|
||||||
|
[TimeFormats.Hertz]: 1,
|
||||||
|
[TimeFormats.Nanoseconds]: 1e-9,
|
||||||
|
[TimeFormats.Microseconds]: 1e-6,
|
||||||
|
[TimeFormats.Milliseconds]: 1e-3,
|
||||||
|
[TimeFormats.Seconds]: 1,
|
||||||
|
[TimeFormats.Minutes]: 60,
|
||||||
|
[TimeFormats.Hours]: 3600,
|
||||||
|
[TimeFormats.Days]: 86400,
|
||||||
|
[TimeFormats.DurationMs]: 1e-3,
|
||||||
|
[TimeFormats.DurationS]: 1,
|
||||||
|
[TimeFormats.DurationHms]: null, // Requires special handling
|
||||||
|
[TimeFormats.DurationDhms]: null, // Requires special handling
|
||||||
|
[TimeFormats.Timeticks]: null, // Requires special handling
|
||||||
|
[TimeFormats.ClockMs]: 1e-3,
|
||||||
|
[TimeFormats.ClockS]: 1,
|
||||||
|
},
|
||||||
|
[CategoryNames.Throughput]: {
|
||||||
|
[ThroughputFormats.CountsPerSec]: 1,
|
||||||
|
[ThroughputFormats.OpsPerSec]: 1,
|
||||||
|
[ThroughputFormats.RequestsPerSec]: 1,
|
||||||
|
[ThroughputFormats.ReadsPerSec]: 1,
|
||||||
|
[ThroughputFormats.WritesPerSec]: 1,
|
||||||
|
[ThroughputFormats.IOOpsPerSec]: 1,
|
||||||
|
[ThroughputFormats.CountsPerMin]: 1 / 60,
|
||||||
|
[ThroughputFormats.OpsPerMin]: 1 / 60,
|
||||||
|
[ThroughputFormats.ReadsPerMin]: 1 / 60,
|
||||||
|
[ThroughputFormats.WritesPerMin]: 1 / 60,
|
||||||
|
},
|
||||||
|
[CategoryNames.Data]: {
|
||||||
|
[DataFormats.BytesIEC]: 1,
|
||||||
|
[DataFormats.BytesSI]: 1,
|
||||||
|
[DataFormats.BitsIEC]: 0.125,
|
||||||
|
[DataFormats.BitsSI]: 0.125,
|
||||||
|
[DataFormats.KibiBytes]: 1024,
|
||||||
|
[DataFormats.KiloBytes]: 1000,
|
||||||
|
[DataFormats.MebiBytes]: 1048576,
|
||||||
|
[DataFormats.MegaBytes]: 1000000,
|
||||||
|
[DataFormats.GibiBytes]: 1073741824,
|
||||||
|
[DataFormats.GigaBytes]: 1000000000,
|
||||||
|
[DataFormats.TebiBytes]: 1099511627776,
|
||||||
|
[DataFormats.TeraBytes]: 1000000000000,
|
||||||
|
[DataFormats.PebiBytes]: 1125899906842624,
|
||||||
|
[DataFormats.PetaBytes]: 1000000000000000,
|
||||||
|
},
|
||||||
|
[CategoryNames.DataRate]: {
|
||||||
|
[DataRateFormats.PacketsPerSec]: null, // Cannot convert directly to other data rates
|
||||||
|
[DataRateFormats.BytesPerSecIEC]: 1,
|
||||||
|
[DataRateFormats.BytesPerSecSI]: 1,
|
||||||
|
[DataRateFormats.BitsPerSecIEC]: 0.125,
|
||||||
|
[DataRateFormats.BitsPerSecSI]: 0.125,
|
||||||
|
[DataRateFormats.KibiBytesPerSec]: 1024,
|
||||||
|
[DataRateFormats.KibiBitsPerSec]: 128,
|
||||||
|
[DataRateFormats.KiloBytesPerSec]: 1000,
|
||||||
|
[DataRateFormats.KiloBitsPerSec]: 125,
|
||||||
|
[DataRateFormats.MebiBytesPerSec]: 1048576,
|
||||||
|
[DataRateFormats.MebiBitsPerSec]: 131072,
|
||||||
|
[DataRateFormats.MegaBytesPerSec]: 1000000,
|
||||||
|
[DataRateFormats.MegaBitsPerSec]: 125000,
|
||||||
|
[DataRateFormats.GibiBytesPerSec]: 1073741824,
|
||||||
|
[DataRateFormats.GibiBitsPerSec]: 134217728,
|
||||||
|
[DataRateFormats.GigaBytesPerSec]: 1000000000,
|
||||||
|
[DataRateFormats.GigaBitsPerSec]: 125000000,
|
||||||
|
[DataRateFormats.TebiBytesPerSec]: 1099511627776,
|
||||||
|
[DataRateFormats.TebiBitsPerSec]: 137438953472,
|
||||||
|
[DataRateFormats.TeraBytesPerSec]: 1000000000000,
|
||||||
|
[DataRateFormats.TeraBitsPerSec]: 125000000000,
|
||||||
|
[DataRateFormats.PebiBytesPerSec]: 1125899906842624,
|
||||||
|
[DataRateFormats.PebiBitsPerSec]: 140737488355328,
|
||||||
|
[DataRateFormats.PetaBytesPerSec]: 1000000000000000,
|
||||||
|
[DataRateFormats.PetaBitsPerSec]: 125000000000000,
|
||||||
|
},
|
||||||
|
[CategoryNames.Miscellaneous]: {
|
||||||
|
[MiscellaneousFormats.None]: null,
|
||||||
|
[MiscellaneousFormats.String]: null,
|
||||||
|
[MiscellaneousFormats.Short]: null,
|
||||||
|
[MiscellaneousFormats.Percent]: 1,
|
||||||
|
[MiscellaneousFormats.PercentUnit]: 100,
|
||||||
|
[MiscellaneousFormats.Humidity]: 1,
|
||||||
|
[MiscellaneousFormats.Decibel]: null,
|
||||||
|
[MiscellaneousFormats.Hexadecimal0x]: null,
|
||||||
|
[MiscellaneousFormats.Hexadecimal]: null,
|
||||||
|
[MiscellaneousFormats.ScientificNotation]: null,
|
||||||
|
[MiscellaneousFormats.LocaleFormat]: null,
|
||||||
|
[MiscellaneousFormats.Pixels]: null,
|
||||||
|
},
|
||||||
|
[CategoryNames.Boolean]: {
|
||||||
|
[BooleanFormats.TRUE_FALSE]: null, // Not convertible
|
||||||
|
[BooleanFormats.YES_NO]: null, // Not convertible
|
||||||
|
[BooleanFormats.ON_OFF]: null, // Not convertible
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to get the conversion factor between two units in a specific category
|
||||||
|
function getConversionFactor(
|
||||||
|
fromUnit: string,
|
||||||
|
toUnit: string,
|
||||||
|
category: CategoryNames,
|
||||||
|
): number | null {
|
||||||
|
// Retrieves the conversion factors for the specified category
|
||||||
|
const categoryFactors = conversionFactors[category];
|
||||||
|
if (!categoryFactors) {
|
||||||
|
return null; // Returns null if the category does not exist
|
||||||
|
}
|
||||||
|
const fromFactor = categoryFactors[fromUnit];
|
||||||
|
const toFactor = categoryFactors[toUnit];
|
||||||
|
if (
|
||||||
|
fromFactor === undefined ||
|
||||||
|
toFactor === undefined ||
|
||||||
|
fromFactor === null ||
|
||||||
|
toFactor === null
|
||||||
|
) {
|
||||||
|
return null; // Returns null if either unit does not exist or is not convertible
|
||||||
|
}
|
||||||
|
return fromFactor / toFactor; // Returns the conversion factor ratio
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to convert a value from one unit to another
|
||||||
|
export function convertUnit(
|
||||||
|
value: number,
|
||||||
|
fromUnitId?: string,
|
||||||
|
toUnitId?: string,
|
||||||
|
): number | null {
|
||||||
|
let fromUnit: string | undefined;
|
||||||
|
let toUnit: string | undefined;
|
||||||
|
|
||||||
|
// Finds the category that contains the specified units and extracts fromUnit and toUnit using array methods
|
||||||
|
const category = dataTypeCategories.find((category) =>
|
||||||
|
category.formats.some((format) => {
|
||||||
|
if (format.id === fromUnitId) fromUnit = format.id;
|
||||||
|
if (format.id === toUnitId) toUnit = format.id;
|
||||||
|
return fromUnit && toUnit; // Break out early if both units are found
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!category || !fromUnit || !toUnit) return null; // Return null if category or units are not found
|
||||||
|
|
||||||
|
// Gets the conversion factor for the specified units
|
||||||
|
const conversionFactor = getConversionFactor(
|
||||||
|
fromUnit,
|
||||||
|
toUnit,
|
||||||
|
category.name as any,
|
||||||
|
);
|
||||||
|
if (conversionFactor === null) return null; // Return null if conversion is not possible
|
||||||
|
|
||||||
|
return value * conversionFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to get the category name for a given unit ID
|
||||||
|
export const getCategoryName = (unitId: string): CategoryNames | null => {
|
||||||
|
// Finds the category that contains the specified unit ID
|
||||||
|
const foundCategory = dataTypeCategories.find((category) =>
|
||||||
|
category.formats.some((format) => format.id === unitId),
|
||||||
|
);
|
||||||
|
return foundCategory ? (foundCategory.name as CategoryNames) : null;
|
||||||
|
};
|
||||||
|
@ -311,6 +311,7 @@ function RightContainer({
|
|||||||
setThresholds={setThresholds}
|
setThresholds={setThresholds}
|
||||||
yAxisUnit={yAxisUnit}
|
yAxisUnit={yAxisUnit}
|
||||||
selectedGraph={selectedGraph}
|
selectedGraph={selectedGraph}
|
||||||
|
columnUnits={columnUnits}
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { DefaultOptionType } from 'antd/es/select';
|
||||||
import { omitIdFromQuery } from 'components/ExplorerCard/utils';
|
import { omitIdFromQuery } from 'components/ExplorerCard/utils';
|
||||||
import {
|
import {
|
||||||
initialQueryBuilderFormValuesMap,
|
initialQueryBuilderFormValuesMap,
|
||||||
@ -8,12 +9,19 @@ import {
|
|||||||
listViewInitialTraceQuery,
|
listViewInitialTraceQuery,
|
||||||
PANEL_TYPES_INITIAL_QUERY,
|
PANEL_TYPES_INITIAL_QUERY,
|
||||||
} from 'container/NewDashboard/ComponentsSlider/constants';
|
} from 'container/NewDashboard/ComponentsSlider/constants';
|
||||||
import { cloneDeep, isEqual, set, unset } from 'lodash-es';
|
import { categoryToSupport } from 'container/QueryBuilder/filters/BuilderUnitsFilter/config';
|
||||||
|
import { cloneDeep, isEmpty, isEqual, set, unset } from 'lodash-es';
|
||||||
import { Widgets } from 'types/api/dashboard/getAll';
|
import { Widgets } from 'types/api/dashboard/getAll';
|
||||||
import { IBuilderQuery, Query } from 'types/api/queryBuilder/queryBuilderData';
|
import { IBuilderQuery, Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
import { EQueryType } from 'types/common/dashboard';
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
import { DataSource } from 'types/common/queryBuilder';
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
|
import {
|
||||||
|
dataTypeCategories,
|
||||||
|
getCategoryName,
|
||||||
|
} from './RightContainer/dataFormatCategories';
|
||||||
|
import { CategoryNames } from './RightContainer/types';
|
||||||
|
|
||||||
export const getIsQueryModified = (
|
export const getIsQueryModified = (
|
||||||
currentQuery: Query,
|
currentQuery: Query,
|
||||||
stagedQuery: Query | null,
|
stagedQuery: Query | null,
|
||||||
@ -529,3 +537,41 @@ export const PANEL_TYPE_TO_QUERY_TYPES: Record<PANEL_TYPES, EQueryType[]> = {
|
|||||||
EQueryType.PROM,
|
EQueryType.PROM,
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a list of category select options based on the provided category name.
|
||||||
|
* If the category is found, it maps the formats to an array of objects containing
|
||||||
|
* the label and value for each format.
|
||||||
|
*/
|
||||||
|
export const getCategorySelectOptionByName = (
|
||||||
|
name?: CategoryNames | string,
|
||||||
|
): DefaultOptionType[] =>
|
||||||
|
dataTypeCategories
|
||||||
|
.find((category) => category.name === name)
|
||||||
|
?.formats.map((format) => ({
|
||||||
|
label: format.name,
|
||||||
|
value: format.id,
|
||||||
|
})) || [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates unit options based on the provided column unit.
|
||||||
|
* It first retrieves the category name associated with the column unit.
|
||||||
|
* If the category is empty, it maps all supported categories to their respective
|
||||||
|
* select options. If a valid category is found, it filters the supported categories
|
||||||
|
* to return only the options for the matched category.
|
||||||
|
*/
|
||||||
|
export const unitOptions = (columnUnit: string): DefaultOptionType[] => {
|
||||||
|
const category = getCategoryName(columnUnit);
|
||||||
|
if (isEmpty(category)) {
|
||||||
|
return categoryToSupport.map((category) => ({
|
||||||
|
label: category,
|
||||||
|
options: getCategorySelectOptionByName(category),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return categoryToSupport
|
||||||
|
.filter((supportedCategory) => supportedCategory === category)
|
||||||
|
.map((filteredCategory) => ({
|
||||||
|
label: filteredCategory,
|
||||||
|
options: getCategorySelectOptionByName(filteredCategory),
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user