diff --git a/frontend/src/constants/events.ts b/frontend/src/constants/events.ts
index db4ce63d7a..1b6a0e6feb 100644
--- a/frontend/src/constants/events.ts
+++ b/frontend/src/constants/events.ts
@@ -1,4 +1,5 @@
export enum Events {
UPDATE_GRAPH_VISIBILITY_STATE = 'UPDATE_GRAPH_VISIBILITY_STATE',
UPDATE_GRAPH_MANAGER_TABLE = 'UPDATE_GRAPH_MANAGER_TABLE',
+ TABLE_COLUMNS_DATA = 'TABLE_COLUMNS_DATA',
}
diff --git a/frontend/src/container/GridPanelSwitch/index.tsx b/frontend/src/container/GridPanelSwitch/index.tsx
index 511bf449e9..f054fabb5e 100644
--- a/frontend/src/container/GridPanelSwitch/index.tsx
+++ b/frontend/src/container/GridPanelSwitch/index.tsx
@@ -26,7 +26,12 @@ const GridPanelSwitch = forwardRef<
yAxisUnit,
thresholds,
},
- [PANEL_TYPES.TABLE]: { ...GRID_TABLE_CONFIG, data: panelData, query },
+ [PANEL_TYPES.TABLE]: {
+ ...GRID_TABLE_CONFIG,
+ data: panelData,
+ query,
+ thresholds,
+ },
[PANEL_TYPES.LIST]: null,
[PANEL_TYPES.TRACE]: null,
[PANEL_TYPES.EMPTY_WIDGET]: null,
diff --git a/frontend/src/container/GridTableComponent/index.tsx b/frontend/src/container/GridTableComponent/index.tsx
index e3659c1c95..4b1fab5f8f 100644
--- a/frontend/src/container/GridTableComponent/index.tsx
+++ b/frontend/src/container/GridTableComponent/index.tsx
@@ -1,20 +1,86 @@
+import { ExclamationCircleFilled } from '@ant-design/icons';
+import { Space, Tooltip } from 'antd';
+import { Events } from 'constants/events';
import { QueryTable } from 'container/QueryTable';
-import { memo } from 'react';
+import { createTableColumnsFromQuery } from 'lib/query/createTableColumnsFromQuery';
+import { memo, ReactNode, useEffect, useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { eventEmitter } from 'utils/getEventEmitter';
import { WrapperStyled } from './styles';
import { GridTableComponentProps } from './types';
+import { findMatchingThreshold } from './utils';
function GridTableComponent({
data,
query,
+ thresholds,
...props
}: GridTableComponentProps): JSX.Element {
+ const { t } = useTranslation(['valueGraph']);
+ const { columns, dataSource } = useMemo(
+ () =>
+ createTableColumnsFromQuery({
+ query,
+ queryTableData: data,
+ }),
+ [data, query],
+ );
+
+ const newColumnData = columns.map((e) => ({
+ ...e,
+ render: (text: string): ReactNode => {
+ const isNumber = !Number.isNaN(Number(text));
+ if (thresholds && isNumber) {
+ const { hasMultipleMatches, threshold } = findMatchingThreshold(
+ thresholds,
+ e.title as string,
+ Number(text),
+ );
+
+ const idx = thresholds.findIndex(
+ (t) => t.thresholdTableOptions === e.title,
+ );
+ if (idx !== -1) {
+ return (
+
+
+ {text}
+ {hasMultipleMatches && (
+
+
+
+ )}
+
+
+ );
+ }
+ }
+ return {text}
;
+ },
+ }));
+
+ useEffect(() => {
+ eventEmitter.emit(Events.TABLE_COLUMNS_DATA, {
+ columns: newColumnData,
+ dataSource,
+ });
+ }, [dataSource, newColumnData]);
+
return (
diff --git a/frontend/src/container/GridTableComponent/types.ts b/frontend/src/container/GridTableComponent/types.ts
index cd8e446822..4f210eca18 100644
--- a/frontend/src/container/GridTableComponent/types.ts
+++ b/frontend/src/container/GridTableComponent/types.ts
@@ -1,10 +1,23 @@
import { TableProps } from 'antd';
import { LogsExplorerTableProps } from 'container/LogsExplorerTable/LogsExplorerTable.interfaces';
+import {
+ ThresholdOperators,
+ ThresholdProps,
+} from 'container/NewWidget/RightContainer/Threshold/types';
import { RowData } from 'lib/query/createTableColumnsFromQuery';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
-export type GridTableComponentProps = { query: Query } & Pick<
- LogsExplorerTableProps,
- 'data'
-> &
+export type GridTableComponentProps = {
+ query: Query;
+ thresholds?: ThresholdProps[];
+} & Pick &
Omit, 'columns' | 'dataSource'>;
+
+export type RequiredThresholdProps = Omit<
+ ThresholdProps,
+ 'thresholdTableOptions' | 'thresholdOperator' | 'thresholdValue'
+> & {
+ thresholdTableOptions: string;
+ thresholdOperator: ThresholdOperators;
+ thresholdValue: number;
+};
diff --git a/frontend/src/container/GridTableComponent/utils.ts b/frontend/src/container/GridTableComponent/utils.ts
new file mode 100644
index 0000000000..e60cac6a24
--- /dev/null
+++ b/frontend/src/container/GridTableComponent/utils.ts
@@ -0,0 +1,58 @@
+import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types';
+
+// Helper function to evaluate the condition based on the operator
+function evaluateCondition(
+ operator: string | undefined,
+ value: number,
+ thresholdValue: number,
+): boolean {
+ switch (operator) {
+ case '>':
+ return value > thresholdValue;
+ case '<':
+ return value < thresholdValue;
+ case '>=':
+ return value >= thresholdValue;
+ case '<=':
+ return value <= thresholdValue;
+ case '==':
+ return value === thresholdValue;
+ default:
+ return false;
+ }
+}
+
+export function findMatchingThreshold(
+ thresholds: ThresholdProps[],
+ label: string,
+ value: number,
+): {
+ threshold: ThresholdProps;
+ hasMultipleMatches: boolean;
+} {
+ const matchingThresholds: ThresholdProps[] = [];
+ let hasMultipleMatches = false;
+
+ thresholds.forEach((threshold) => {
+ if (
+ threshold.thresholdValue !== undefined &&
+ threshold.thresholdTableOptions === label &&
+ evaluateCondition(
+ threshold.thresholdOperator,
+ value,
+ threshold.thresholdValue,
+ )
+ ) {
+ matchingThresholds.push(threshold);
+ }
+ });
+
+ if (matchingThresholds.length > 1) {
+ hasMultipleMatches = true;
+ }
+
+ return {
+ threshold: matchingThresholds[0],
+ hasMultipleMatches,
+ };
+}
diff --git a/frontend/src/container/NewWidget/RightContainer/Threshold/ShowCaseValue.styles.scss b/frontend/src/container/NewWidget/RightContainer/Threshold/ShowCaseValue.styles.scss
index 360387c333..05d5233e52 100644
--- a/frontend/src/container/NewWidget/RightContainer/Threshold/ShowCaseValue.styles.scss
+++ b/frontend/src/container/NewWidget/RightContainer/Threshold/ShowCaseValue.styles.scss
@@ -1,6 +1,7 @@
.show-case-container {
padding: 5px 15px;
border-radius: 5px;
+ display: inline-block;
}
.show-case-dark {
diff --git a/frontend/src/container/NewWidget/RightContainer/Threshold/Threshold.tsx b/frontend/src/container/NewWidget/RightContainer/Threshold/Threshold.tsx
index 1a2347c9d6..b64c5b26e6 100644
--- a/frontend/src/container/NewWidget/RightContainer/Threshold/Threshold.tsx
+++ b/frontend/src/container/NewWidget/RightContainer/Threshold/Threshold.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable sonarjs/cognitive-complexity */
import './Threshold.styles.scss';
import { CheckOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons';
@@ -40,6 +41,8 @@ function Threshold({
moveThreshold,
selectedGraph,
thresholdLabel = '',
+ tableOptions,
+ thresholdTableOptions = '',
}: ThresholdProps): JSX.Element {
const [isEditMode, setIsEditMode] = useState(isEditEnabled);
const [operator, setOperator] = useState(
@@ -52,6 +55,9 @@ function Threshold({
thresholdFormat,
);
const [label, setLabel] = useState(thresholdLabel);
+ const [tableSelectedOption, setTableSelectedOption] = useState(
+ thresholdTableOptions,
+ );
const isDarkMode = useIsDarkMode();
@@ -72,6 +78,7 @@ function Threshold({
thresholdUnit: unit,
thresholdValue: value,
thresholdLabel: label,
+ thresholdTableOptions: tableSelectedOption,
};
}
return threshold;
@@ -104,6 +111,10 @@ function Threshold({
setFormat(value);
};
+ const handleTableOptionsChange = (value: string): void => {
+ setTableSelectedOption(value);
+ };
+
const deleteHandler = (): void => {
if (thresholdDeleteHandler) {
thresholdDeleteHandler(index);
@@ -203,7 +214,11 @@ function Threshold({
/>
-
+
{selectedGraph === PANEL_TYPES.TIME_SERIES && (
<>
Label
@@ -219,19 +234,49 @@ function Threshold({
)}
>
)}
- {selectedGraph === PANEL_TYPES.VALUE && (
+ {(selectedGraph === PANEL_TYPES.VALUE ||
+ selectedGraph === PANEL_TYPES.TABLE) && (
<>
- If value is
+
+ If value {selectedGraph === PANEL_TYPES.TABLE ? 'in' : 'is'}
+
{isEditMode ? (
-
+ <>
+ {selectedGraph === PANEL_TYPES.TABLE && (
+
+
+ is
+
+ )}
+
+ >
) : (
-
+ <>
+ {selectedGraph === PANEL_TYPES.TABLE && (
+
+
+ is
+
+ )}
+
+ >
)}
>
)}
@@ -280,7 +325,7 @@ function Threshold({
>
) : (
<>
- } />
+ } />
>
)}
diff --git a/frontend/src/container/NewWidget/RightContainer/Threshold/ThresholdSelector.tsx b/frontend/src/container/NewWidget/RightContainer/Threshold/ThresholdSelector.tsx
index 1874d41f65..a257c6d951 100644
--- a/frontend/src/container/NewWidget/RightContainer/Threshold/ThresholdSelector.tsx
+++ b/frontend/src/container/NewWidget/RightContainer/Threshold/ThresholdSelector.tsx
@@ -1,9 +1,13 @@
import './ThresholdSelector.styles.scss';
import { Button, Typography } from 'antd';
-import { useCallback } from 'react';
+import { ColumnsType } from 'antd/es/table';
+import { Events } from 'constants/events';
+import { RowData } from 'lib/query/createTableColumnsFromQuery';
+import { useCallback, useEffect, useState } from 'react';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
+import { eventEmitter } from 'utils/getEventEmitter';
import { v4 as uuid } from 'uuid';
import Threshold from './Threshold';
@@ -15,6 +19,22 @@ function ThresholdSelector({
yAxisUnit,
selectedGraph,
}: ThresholdSelectorProps): JSX.Element {
+ const [tableOptions, setTableOptions] = useState<
+ Array<{ value: string; label: string }>
+ >([]);
+ useEffect(() => {
+ eventEmitter.on(
+ Events.TABLE_COLUMNS_DATA,
+ (data: { columns: ColumnsType; dataSource: RowData[] }) => {
+ const newTableOptions = data.columns.map((e) => ({
+ value: e.title as string,
+ label: e.title as string,
+ }));
+ setTableOptions([...newTableOptions]);
+ },
+ );
+ }, []);
+
const moveThreshold = useCallback(
(dragIndex: number, hoverIndex: number) => {
setThresholds((prevCards) => {
@@ -44,6 +64,7 @@ function ThresholdSelector({
moveThreshold,
keyIndex: thresholds.length,
selectedGraph,
+ thresholdTableOptions: tableOptions[0]?.value || '',
},
]);
};
@@ -75,6 +96,8 @@ function ThresholdSelector({
moveThreshold={moveThreshold}
selectedGraph={selectedGraph}
thresholdLabel={threshold.thresholdLabel}
+ tableOptions={tableOptions}
+ thresholdTableOptions={threshold.thresholdTableOptions}
/>
))}