+
30 ? title : ''}>
- {' '}
+ {' '}
{title}
diff --git a/frontend/src/container/NewDashboard/DashboardVariablesSelection/DashboardVariableSelection.styles.scss b/frontend/src/container/NewDashboard/DashboardVariablesSelection/DashboardVariableSelection.styles.scss
index f7fcb83a53..6df3e79906 100644
--- a/frontend/src/container/NewDashboard/DashboardVariablesSelection/DashboardVariableSelection.styles.scss
+++ b/frontend/src/container/NewDashboard/DashboardVariablesSelection/DashboardVariableSelection.styles.scss
@@ -43,6 +43,15 @@
.ant-select-item {
display: flex;
align-items: center;
+ gap: 8px;
+ }
+
+ .rc-virtual-list-holder {
+ [data-testid='option-ALL'] {
+ border-bottom: 1px solid var(--bg-slate-400);
+ padding-bottom: 12px;
+ margin-bottom: 8px;
+ }
}
.all-label {
@@ -56,28 +65,25 @@
}
.dropdown-value {
- display: flex;
- justify-content: space-between;
- align-items: center;
+ display: grid;
+ grid-template-columns: 1fr max-content;
.option-text {
- max-width: 180px;
padding: 0 8px;
}
.toggle-tag-label {
padding-left: 8px;
right: 40px;
- font-weight: normal;
- position: absolute;
+ font-weight: 500;
}
}
}
}
.dropdown-styles {
- min-width: 300px;
- max-width: 350px;
+ min-width: 400px;
+ max-width: 500px;
}
.lightMode {
diff --git a/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.test.tsx b/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.test.tsx
index 0c8fbd51ae..1cb89d6b95 100644
--- a/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.test.tsx
+++ b/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.test.tsx
@@ -1,14 +1,8 @@
import '@testing-library/jest-dom/extend-expect';
-import {
- act,
- fireEvent,
- render,
- screen,
- waitFor,
-} from '@testing-library/react';
import MockQueryClientProvider from 'providers/test/MockQueryClientProvider';
import React, { useEffect } from 'react';
+import { act, fireEvent, render, screen, waitFor } from 'tests/test-utils';
import { IDashboardVariable } from 'types/api/dashboard/getAll';
import VariableItem from './VariableItem';
diff --git a/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.tsx b/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.tsx
index baa8228b3c..a0a444a715 100644
--- a/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.tsx
+++ b/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable sonarjs/cognitive-complexity */
/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-static-element-interactions */
/* eslint-disable @typescript-eslint/no-explicit-any */
@@ -25,8 +26,11 @@ import { debounce, isArray, isString } from 'lodash-es';
import map from 'lodash-es/map';
import { ChangeEvent, memo, useEffect, useMemo, useState } from 'react';
import { useQuery } from 'react-query';
+import { useSelector } from 'react-redux';
+import { AppState } from 'store/reducers';
import { IDashboardVariable } from 'types/api/dashboard/getAll';
import { VariableResponseProps } from 'types/api/dashboard/variables/query';
+import { GlobalReducer } from 'types/reducer/globalTime';
import { popupContainer } from 'utils/selectPopupContainer';
import { variablePropsToPayloadVariables } from '../utils';
@@ -58,14 +62,14 @@ interface VariableItemProps {
const getSelectValue = (
selectedValue: IDashboardVariable['selectedValue'],
variableData: IDashboardVariable,
-): string | string[] => {
+): string | string[] | undefined => {
if (Array.isArray(selectedValue)) {
if (!variableData.multiSelect && selectedValue.length === 1) {
- return selectedValue[0]?.toString() || '';
+ return selectedValue[0]?.toString();
}
return selectedValue.map((item) => item.toString());
}
- return selectedValue?.toString() || '';
+ return selectedValue?.toString();
};
// eslint-disable-next-line sonarjs/cognitive-complexity
@@ -80,6 +84,23 @@ function VariableItem({
[],
);
+ const { maxTime, minTime } = useSelector
(
+ (state) => state.globalTime,
+ );
+
+ useEffect(() => {
+ if (variableData.allSelected && variableData.type === 'QUERY') {
+ setVariablesToGetUpdated((prev) => {
+ const variablesQueue = [...prev.filter((v) => v !== variableData.name)];
+ if (variableData.name) {
+ variablesQueue.push(variableData.name);
+ }
+ return variablesQueue;
+ });
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [minTime, maxTime]);
+
const [errorMessage, setErrorMessage] = useState(null);
const getDependentVariables = (queryValue: string): string[] => {
@@ -111,7 +132,14 @@ function VariableItem({
const variableKey = dependentVariablesStr.replace(/\s/g, '');
- return [REACT_QUERY_KEY.DASHBOARD_BY_ID, variableName, variableKey];
+ // added this time dependency for variables query as API respects the passed time range now
+ return [
+ REACT_QUERY_KEY.DASHBOARD_BY_ID,
+ variableName,
+ variableKey,
+ `${minTime}`,
+ `${maxTime}`,
+ ];
};
// eslint-disable-next-line sonarjs/cognitive-complexity
@@ -151,10 +179,14 @@ function VariableItem({
valueNotInList = true;
}
}
+ // variablesData.allSelected is added for the case where on change of options we need to update the
+ // local storage
if (
variableData.type === 'QUERY' &&
variableData.name &&
- (variablesToGetUpdated.includes(variableData.name) || valueNotInList)
+ (variablesToGetUpdated.includes(variableData.name) ||
+ valueNotInList ||
+ variableData.allSelected)
) {
let value = variableData.selectedValue;
let allSelected = false;
@@ -268,7 +300,7 @@ function VariableItem({
e.stopPropagation();
e.preventDefault();
const isChecked =
- variableData.allSelected || selectValue.includes(ALL_SELECT_VALUE);
+ variableData.allSelected || selectValue?.includes(ALL_SELECT_VALUE);
if (isChecked) {
handleChange([]);
@@ -338,8 +370,8 @@ function VariableItem({
(Array.isArray(selectValue) && selectValue?.includes(option.toString()));
if (isChecked) {
- if (mode === ToggleTagValue.Only) {
- handleChange(option.toString());
+ if (mode === ToggleTagValue.Only && variableData.multiSelect) {
+ handleChange([option.toString()]);
} else if (!variableData.multiSelect) {
handleChange(option.toString());
} else {
@@ -430,6 +462,7 @@ function VariableItem({
+ {omittedValues.length}
)}
+ allowClear
>
{enableSelectAll && (
@@ -468,11 +501,17 @@ function VariableItem({
{...retProps(option as string)}
onClick={(e): void => handleToggle(e as any, option as string)}
>
-
-
- {option.toString()}
-
-
+
+ {option.toString()}
+
{variableData.multiSelect &&
optionState.tag === option.toString() &&
diff --git a/frontend/src/container/NewWidget/RightContainer/constants.ts b/frontend/src/container/NewWidget/RightContainer/constants.ts
index 171f6b81d3..03cee96d21 100644
--- a/frontend/src/container/NewWidget/RightContainer/constants.ts
+++ b/frontend/src/container/NewWidget/RightContainer/constants.ts
@@ -74,7 +74,7 @@ export const panelTypeVsYAxisUnit: { [key in PANEL_TYPES]: boolean } = {
[PANEL_TYPES.VALUE]: true,
[PANEL_TYPES.TABLE]: false,
[PANEL_TYPES.LIST]: false,
- [PANEL_TYPES.PIE]: false,
+ [PANEL_TYPES.PIE]: true,
[PANEL_TYPES.BAR]: true,
[PANEL_TYPES.HISTOGRAM]: false,
[PANEL_TYPES.TRACE]: false,
diff --git a/frontend/src/container/NewWidget/RightContainer/index.tsx b/frontend/src/container/NewWidget/RightContainer/index.tsx
index 84400737d4..43e3b5611d 100644
--- a/frontend/src/container/NewWidget/RightContainer/index.tsx
+++ b/frontend/src/container/NewWidget/RightContainer/index.tsx
@@ -211,7 +211,11 @@ function RightContainer({
)}
{allowSoftMinMax && (
diff --git a/frontend/src/container/PanelWrapper/PanelWrapper.tsx b/frontend/src/container/PanelWrapper/PanelWrapper.tsx
index ed105b3948..2f5b35485e 100644
--- a/frontend/src/container/PanelWrapper/PanelWrapper.tsx
+++ b/frontend/src/container/PanelWrapper/PanelWrapper.tsx
@@ -16,6 +16,7 @@ function PanelWrapper({
selectedGraph,
tableProcessedDataRef,
customTooltipElement,
+ searchTerm,
}: PanelWrapperProps): JSX.Element {
const Component = PanelTypeVsPanelWrapper[
selectedGraph || widget.panelTypes
@@ -39,6 +40,7 @@ function PanelWrapper({
selectedGraph={selectedGraph}
tableProcessedDataRef={tableProcessedDataRef}
customTooltipElement={customTooltipElement}
+ searchTerm={searchTerm}
/>
);
}
diff --git a/frontend/src/container/PanelWrapper/PiePanelWrapper.tsx b/frontend/src/container/PanelWrapper/PiePanelWrapper.tsx
index a176247781..dce84ad78d 100644
--- a/frontend/src/container/PanelWrapper/PiePanelWrapper.tsx
+++ b/frontend/src/container/PanelWrapper/PiePanelWrapper.tsx
@@ -4,6 +4,7 @@ import { Color } from '@signozhq/design-tokens';
import { Group } from '@visx/group';
import { Pie } from '@visx/shape';
import { useTooltip, useTooltipInPortal } from '@visx/tooltip';
+import { getYAxisFormattedValue } from 'components/Graph/yAxisConfig';
import { themeColors } from 'constants/theme';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { generateColor } from 'lib/uPlotLib/utils/generateColor';
@@ -129,7 +130,12 @@ function PiePanelWrapper({
showTooltip({
tooltipData: {
label,
- value: arc.data.value,
+ // do not update the unit in the data as the arc allotment is based on value
+ // and treats 4K smaller than 40
+ value: getYAxisFormattedValue(
+ arc.data.value,
+ widget?.yAxisUnit || 'none',
+ ),
color: arc.data.color,
key: label,
},
diff --git a/frontend/src/container/PanelWrapper/TablePanelWrapper.tsx b/frontend/src/container/PanelWrapper/TablePanelWrapper.tsx
index 0eab4143a2..c5222e8d53 100644
--- a/frontend/src/container/PanelWrapper/TablePanelWrapper.tsx
+++ b/frontend/src/container/PanelWrapper/TablePanelWrapper.tsx
@@ -8,6 +8,7 @@ function TablePanelWrapper({
widget,
queryResponse,
tableProcessedDataRef,
+ searchTerm,
}: PanelWrapperProps): JSX.Element {
const panelData =
(queryResponse.data?.payload?.data?.result?.[0] as any)?.table || [];
@@ -20,6 +21,7 @@ function TablePanelWrapper({
columnUnits={widget.columnUnits}
tableProcessedDataRef={tableProcessedDataRef}
sticky={widget.panelTypes === PANEL_TYPES.TABLE}
+ searchTerm={searchTerm}
// eslint-disable-next-line react/jsx-props-no-spreading
{...GRID_TABLE_CONFIG}
/>
diff --git a/frontend/src/container/PanelWrapper/panelWrapper.types.ts b/frontend/src/container/PanelWrapper/panelWrapper.types.ts
index 7d5e3122e8..4778ffdb97 100644
--- a/frontend/src/container/PanelWrapper/panelWrapper.types.ts
+++ b/frontend/src/container/PanelWrapper/panelWrapper.types.ts
@@ -23,6 +23,7 @@ export type PanelWrapperProps = {
onDragSelect: (start: number, end: number) => void;
selectedGraph?: PANEL_TYPES;
tableProcessedDataRef?: React.MutableRefObject;
+ searchTerm?: string;
customTooltipElement?: HTMLDivElement;
};
diff --git a/frontend/src/container/PipelinePage/Layouts/ChangeHistory/tests/ChangeHistory.test.tsx b/frontend/src/container/PipelinePage/Layouts/ChangeHistory/tests/ChangeHistory.test.tsx
index 194acbea0a..88fdb5d594 100644
--- a/frontend/src/container/PipelinePage/Layouts/ChangeHistory/tests/ChangeHistory.test.tsx
+++ b/frontend/src/container/PipelinePage/Layouts/ChangeHistory/tests/ChangeHistory.test.tsx
@@ -9,6 +9,20 @@ import store from 'store';
import ChangeHistory from '../index';
import { pipelineData, pipelineDataHistory } from './testUtils';
+jest.mock('uplot', () => {
+ const paths = {
+ spline: jest.fn(),
+ bars: jest.fn(),
+ };
+ const uplotMock = jest.fn(() => ({
+ paths,
+ }));
+ return {
+ paths,
+ default: uplotMock,
+ };
+});
+
const queryClient = new QueryClient({
defaultOptions: {
queries: {
diff --git a/frontend/src/container/PipelinePage/tests/AddNewPipeline.test.tsx b/frontend/src/container/PipelinePage/tests/AddNewPipeline.test.tsx
index 8990ffa4e7..360a7c5925 100644
--- a/frontend/src/container/PipelinePage/tests/AddNewPipeline.test.tsx
+++ b/frontend/src/container/PipelinePage/tests/AddNewPipeline.test.tsx
@@ -9,6 +9,20 @@ import store from 'store';
import { pipelineMockData } from '../mocks/pipeline';
import AddNewPipeline from '../PipelineListsView/AddNewPipeline';
+jest.mock('uplot', () => {
+ const paths = {
+ spline: jest.fn(),
+ bars: jest.fn(),
+ };
+ const uplotMock = jest.fn(() => ({
+ paths,
+ }));
+ return {
+ paths,
+ default: uplotMock,
+ };
+});
+
export function matchMedia(): void {
Object.defineProperty(window, 'matchMedia', {
writable: true,
diff --git a/frontend/src/container/PipelinePage/tests/AddNewProcessor.test.tsx b/frontend/src/container/PipelinePage/tests/AddNewProcessor.test.tsx
index a4d2e680a4..d3f236437f 100644
--- a/frontend/src/container/PipelinePage/tests/AddNewProcessor.test.tsx
+++ b/frontend/src/container/PipelinePage/tests/AddNewProcessor.test.tsx
@@ -9,6 +9,20 @@ import { pipelineMockData } from '../mocks/pipeline';
import AddNewProcessor from '../PipelineListsView/AddNewProcessor';
import { matchMedia } from './AddNewPipeline.test';
+jest.mock('uplot', () => {
+ const paths = {
+ spline: jest.fn(),
+ bars: jest.fn(),
+ };
+ const uplotMock = jest.fn(() => ({
+ paths,
+ }));
+ return {
+ paths,
+ default: uplotMock,
+ };
+});
+
beforeAll(() => {
matchMedia();
});
diff --git a/frontend/src/container/PipelinePage/tests/DeleteAction.test.tsx b/frontend/src/container/PipelinePage/tests/DeleteAction.test.tsx
index 451ef8807f..3b2fdfeb34 100644
--- a/frontend/src/container/PipelinePage/tests/DeleteAction.test.tsx
+++ b/frontend/src/container/PipelinePage/tests/DeleteAction.test.tsx
@@ -6,6 +6,20 @@ import { MemoryRouter } from 'react-router-dom';
import i18n from 'ReactI18';
import store from 'store';
+jest.mock('uplot', () => {
+ const paths = {
+ spline: jest.fn(),
+ bars: jest.fn(),
+ };
+ const uplotMock = jest.fn(() => ({
+ paths,
+ }));
+ return {
+ paths,
+ default: uplotMock,
+ };
+});
+
describe('PipelinePage container test', () => {
it('should render DeleteAction section', () => {
const { asFragment } = render(
diff --git a/frontend/src/container/PipelinePage/tests/DragAction.test.tsx b/frontend/src/container/PipelinePage/tests/DragAction.test.tsx
index 168b3f042f..9f64714072 100644
--- a/frontend/src/container/PipelinePage/tests/DragAction.test.tsx
+++ b/frontend/src/container/PipelinePage/tests/DragAction.test.tsx
@@ -6,6 +6,20 @@ import { MemoryRouter } from 'react-router-dom';
import i18n from 'ReactI18';
import store from 'store';
+jest.mock('uplot', () => {
+ const paths = {
+ spline: jest.fn(),
+ bars: jest.fn(),
+ };
+ const uplotMock = jest.fn(() => ({
+ paths,
+ }));
+ return {
+ paths,
+ default: uplotMock,
+ };
+});
+
describe('PipelinePage container test', () => {
it('should render DragAction section', () => {
const { asFragment } = render(
diff --git a/frontend/src/container/PipelinePage/tests/EditAction.test.tsx b/frontend/src/container/PipelinePage/tests/EditAction.test.tsx
index c52991bf6d..56dd779600 100644
--- a/frontend/src/container/PipelinePage/tests/EditAction.test.tsx
+++ b/frontend/src/container/PipelinePage/tests/EditAction.test.tsx
@@ -6,6 +6,20 @@ import { MemoryRouter } from 'react-router-dom';
import i18n from 'ReactI18';
import store from 'store';
+jest.mock('uplot', () => {
+ const paths = {
+ spline: jest.fn(),
+ bars: jest.fn(),
+ };
+ const uplotMock = jest.fn(() => ({
+ paths,
+ }));
+ return {
+ paths,
+ default: uplotMock,
+ };
+});
+
describe('PipelinePage container test', () => {
it('should render EditAction section', () => {
const { asFragment } = render(
diff --git a/frontend/src/container/PipelinePage/tests/PipelineActions.test.tsx b/frontend/src/container/PipelinePage/tests/PipelineActions.test.tsx
index 83f503107b..d472f4745c 100644
--- a/frontend/src/container/PipelinePage/tests/PipelineActions.test.tsx
+++ b/frontend/src/container/PipelinePage/tests/PipelineActions.test.tsx
@@ -8,6 +8,20 @@ import store from 'store';
import { pipelineMockData } from '../mocks/pipeline';
import PipelineActions from '../PipelineListsView/TableComponents/PipelineActions';
+jest.mock('uplot', () => {
+ const paths = {
+ spline: jest.fn(),
+ bars: jest.fn(),
+ };
+ const uplotMock = jest.fn(() => ({
+ paths,
+ }));
+ return {
+ paths,
+ default: uplotMock,
+ };
+});
+
describe('PipelinePage container test', () => {
it('should render PipelineActions section', () => {
const { asFragment } = render(
diff --git a/frontend/src/container/PipelinePage/tests/PipelineExpandView.test.tsx b/frontend/src/container/PipelinePage/tests/PipelineExpandView.test.tsx
index 6875d11259..b9c78091dd 100644
--- a/frontend/src/container/PipelinePage/tests/PipelineExpandView.test.tsx
+++ b/frontend/src/container/PipelinePage/tests/PipelineExpandView.test.tsx
@@ -9,6 +9,20 @@ import { pipelineMockData } from '../mocks/pipeline';
import PipelineExpandView from '../PipelineListsView/PipelineExpandView';
import { matchMedia } from './AddNewPipeline.test';
+jest.mock('uplot', () => {
+ const paths = {
+ spline: jest.fn(),
+ bars: jest.fn(),
+ };
+ const uplotMock = jest.fn(() => ({
+ paths,
+ }));
+ return {
+ paths,
+ default: uplotMock,
+ };
+});
+
beforeAll(() => {
matchMedia();
});
diff --git a/frontend/src/container/PipelinePage/tests/PipelineListsView.test.tsx b/frontend/src/container/PipelinePage/tests/PipelineListsView.test.tsx
index 74f1f125e0..517e623ebe 100644
--- a/frontend/src/container/PipelinePage/tests/PipelineListsView.test.tsx
+++ b/frontend/src/container/PipelinePage/tests/PipelineListsView.test.tsx
@@ -11,6 +11,20 @@ import store from 'store';
import { pipelineApiResponseMockData } from '../mocks/pipeline';
import PipelineListsView from '../PipelineListsView';
+jest.mock('uplot', () => {
+ const paths = {
+ spline: jest.fn(),
+ bars: jest.fn(),
+ };
+ const uplotMock = jest.fn(() => ({
+ paths,
+ }));
+ return {
+ paths,
+ default: uplotMock,
+ };
+});
+
const samplePipelinePreviewResponse = {
isLoading: false,
logs: [
diff --git a/frontend/src/container/PipelinePage/tests/PipelinePageLayout.test.tsx b/frontend/src/container/PipelinePage/tests/PipelinePageLayout.test.tsx
index 91d5dfe244..a71bc1266d 100644
--- a/frontend/src/container/PipelinePage/tests/PipelinePageLayout.test.tsx
+++ b/frontend/src/container/PipelinePage/tests/PipelinePageLayout.test.tsx
@@ -11,6 +11,20 @@ import { v4 } from 'uuid';
import PipelinePageLayout from '../Layouts/Pipeline';
import { matchMedia } from './AddNewPipeline.test';
+jest.mock('uplot', () => {
+ const paths = {
+ spline: jest.fn(),
+ bars: jest.fn(),
+ };
+ const uplotMock = jest.fn(() => ({
+ paths,
+ }));
+ return {
+ paths,
+ default: uplotMock,
+ };
+});
+
beforeAll(() => {
matchMedia();
});
diff --git a/frontend/src/container/PipelinePage/tests/TagInput.test.tsx b/frontend/src/container/PipelinePage/tests/TagInput.test.tsx
index 24cedc2eb0..e95efb6715 100644
--- a/frontend/src/container/PipelinePage/tests/TagInput.test.tsx
+++ b/frontend/src/container/PipelinePage/tests/TagInput.test.tsx
@@ -7,6 +7,20 @@ import store from 'store';
import TagInput from '../components/TagInput';
+jest.mock('uplot', () => {
+ const paths = {
+ spline: jest.fn(),
+ bars: jest.fn(),
+ };
+ const uplotMock = jest.fn(() => ({
+ paths,
+ }));
+ return {
+ paths,
+ default: uplotMock,
+ };
+});
+
describe('Pipeline Page', () => {
it('should render TagInput section', () => {
const { asFragment } = render(
diff --git a/frontend/src/container/PipelinePage/tests/utils.test.ts b/frontend/src/container/PipelinePage/tests/utils.test.ts
index c21e8c5a4b..707ad06c2d 100644
--- a/frontend/src/container/PipelinePage/tests/utils.test.ts
+++ b/frontend/src/container/PipelinePage/tests/utils.test.ts
@@ -11,6 +11,20 @@ import {
getTableColumn,
} from '../PipelineListsView/utils';
+jest.mock('uplot', () => {
+ const paths = {
+ spline: jest.fn(),
+ bars: jest.fn(),
+ };
+ const uplotMock = jest.fn(() => ({
+ paths,
+ }));
+ return {
+ paths,
+ default: uplotMock,
+ };
+});
+
describe('Utils testing of Pipeline Page', () => {
test('it should be check form field of add pipeline', () => {
expect(pipelineFields.length).toBe(3);
diff --git a/frontend/src/container/QueryBuilder/components/Query/Query.tsx b/frontend/src/container/QueryBuilder/components/Query/Query.tsx
index 747198abfb..453cf063f8 100644
--- a/frontend/src/container/QueryBuilder/components/Query/Query.tsx
+++ b/frontend/src/container/QueryBuilder/components/Query/Query.tsx
@@ -23,6 +23,7 @@ import {
import AggregateEveryFilter from 'container/QueryBuilder/filters/AggregateEveryFilter';
import LimitFilter from 'container/QueryBuilder/filters/LimitFilter/LimitFilter';
import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch';
+import QueryBuilderSearchV2 from 'container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations';
// ** Hooks
@@ -81,6 +82,10 @@ export const Query = memo(function Query({
entityVersion: version,
});
+ const isLogsExplorerPage = useMemo(() => pathname === ROUTES.LOGS_EXPLORER, [
+ pathname,
+ ]);
+
const handleChangeAggregateEvery = useCallback(
(value: IBuilderQuery['stepInterval']) => {
handleChangeQueryData('stepInterval', value);
@@ -452,11 +457,19 @@ export const Query = memo(function Query({
)}
-
+ {isLogsExplorerPage ? (
+
+ ) : (
+
+ )}
diff --git a/frontend/src/container/QueryBuilder/filters/HavingFilter/HavingFilter.tsx b/frontend/src/container/QueryBuilder/filters/HavingFilter/HavingFilter.tsx
index 7d11d018cc..3eab3e50ee 100644
--- a/frontend/src/container/QueryBuilder/filters/HavingFilter/HavingFilter.tsx
+++ b/frontend/src/container/QueryBuilder/filters/HavingFilter/HavingFilter.tsx
@@ -1,3 +1,4 @@
+import { Color } from '@signozhq/design-tokens';
import { Select } from 'antd';
import { ENTITY_VERSION_V4 } from 'constants/app';
// ** Constants
@@ -34,6 +35,7 @@ export function HavingFilter({
const [currentFormValue, setCurrentFormValue] = useState(
initialHavingValues,
);
+ const [errorMessage, setErrorMessage] = useState(null);
const { isMulti } = useTagValidation(
currentFormValue.op,
@@ -198,6 +200,29 @@ export function HavingFilter({
resetChanges();
};
+ const handleFocus = useCallback(() => {
+ setErrorMessage(null);
+ }, []);
+
+ const handleBlur = useCallback((): void => {
+ if (searchText) {
+ const { columnName, op, value } = getHavingObject(searchText);
+ const isCompleteHavingClause =
+ columnName && op && value.every((v) => v !== '');
+
+ if (isCompleteHavingClause && isValidHavingValue(searchText)) {
+ setLocalValues((prev) => {
+ const updatedValues = [...prev, searchText];
+ onChange(updatedValues.map(transformFromStringToHaving));
+ return updatedValues;
+ });
+ setSearchText('');
+ } else {
+ setErrorMessage('Invalid HAVING clause');
+ }
+ }
+ }, [searchText, onChange]);
+
useEffect(() => {
parseSearchText(searchText);
}, [searchText, parseSearchText]);
@@ -209,28 +234,36 @@ export function HavingFilter({
const isMetricsDataSource = query.dataSource === DataSource.METRICS;
return (
-
+ <>
+
+ {errorMessage && (
+ {errorMessage}
+ )}
+ >
);
}
diff --git a/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2.styles.scss b/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2.styles.scss
index 1ca8bd7529..624546fed5 100644
--- a/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2.styles.scss
+++ b/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2.styles.scss
@@ -2,6 +2,10 @@
display: flex;
gap: 4px;
+ .ant-select-dropdown {
+ padding: 0px;
+ }
+
.show-all-filters {
.content {
.rc-virtual-list-holder {
@@ -231,16 +235,16 @@
}
&.resource {
- border: 1px solid rgba(242, 71, 105, 0.2);
+ border: 1px solid #4bcff920;
.ant-typography {
- color: var(--bg-sakura-400);
- background: rgba(245, 108, 135, 0.1);
+ color: var(--bg-aqua-400);
+ background: #4bcff910;
font-size: 14px;
}
.ant-tag-close-icon {
- background: rgba(245, 108, 135, 0.1);
+ background: #4bcff910;
}
}
&.tag {
@@ -259,3 +263,110 @@
}
}
}
+
+.lightMode {
+ .query-builder-search-v2 {
+ .content {
+ .operator-for {
+ .operator-for-text {
+ color: var(--bg-ink-200);
+ }
+
+ .operator-for-value {
+ background: rgba(255, 255, 255, 0.1);
+ color: var(--bg-ink-200);
+ }
+ }
+
+ .value-for {
+ .value-for-text {
+ color: var(--bg-ink-200);
+ }
+
+ .value-for-value {
+ background: rgba(255, 255, 255, 0.1);
+ color: var(--bg-ink-400);
+ }
+ }
+ .example-queries {
+ cursor: default;
+ .heading {
+ color: var(--bg-slate-50);
+ }
+
+ .query-container {
+ .example-query {
+ background: var(--bg-vanilla-200);
+ color: var(--bg-ink-400);
+ }
+
+ .example-query:hover {
+ color: var(--bg-ink-100);
+ }
+ }
+ }
+ }
+
+ .keyboard-shortcuts {
+ border: 1px solid var(--bg-vanilla-300);
+ background: var(--bg-vanilla-200);
+
+ .icons {
+ border-top: 1.143px solid var(--bg-ink-200);
+ border-right: 1.143px solid var(--bg-ink-200);
+ border-bottom: 2.286px solid var(--bg-ink-200);
+ border-left: 1.143px solid var(--bg-ink-200);
+ background: var(--bg-vanilla-300);
+ }
+
+ .keyboard-text {
+ color: var(--bg-ink-400);
+ }
+
+ .navigate {
+ border-right: 1px solid #1d212d;
+ }
+
+ .show-all-filter-items {
+ border-left: 1px solid #1d212d;
+ }
+ }
+
+ .qb-search-bar-tokenised-tags {
+ .ant-tag {
+ border: 1px solid var(--bg-slate-100);
+ background: var(--bg-vanilla-300);
+ box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
+
+ .ant-typography {
+ color: var(--bg-ink-100);
+ }
+
+ &.resource {
+ border: 1px solid #4bcff920;
+
+ .ant-typography {
+ color: var(--bg-aqua-400);
+ background: #4bcff910;
+ }
+
+ .ant-tag-close-icon {
+ background: #4bcff910;
+ }
+ }
+ &.tag {
+ border: 1px solid rgba(189, 153, 121, 0.2);
+
+ .ant-typography {
+ color: var(--bg-sienna-400);
+ background: rgba(189, 153, 121, 0.1);
+ }
+
+ .ant-tag-close-icon {
+ background: rgba(189, 153, 121, 0.1);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2.tsx b/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2.tsx
index 3ddeef85bc..0925c10d97 100644
--- a/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2.tsx
+++ b/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2.tsx
@@ -286,16 +286,62 @@ function QueryBuilderSearchV2(
parsedValue = value;
}
if (currentState === DropdownState.ATTRIBUTE_KEY) {
- setCurrentFilterItem((prev) => ({
- ...prev,
- key: parsedValue as BaseAutocompleteData,
- op: '',
- value: '',
- }));
- setCurrentState(DropdownState.OPERATOR);
- setSearchValue((parsedValue as BaseAutocompleteData)?.key);
+ // Case - convert abc def ghi type attribute keys directly to body contains abc def ghi
+ if (
+ isObject(parsedValue) &&
+ parsedValue?.key &&
+ parsedValue?.key?.split(' ').length > 1
+ ) {
+ setTags((prev) => [
+ ...prev,
+ {
+ key: {
+ key: 'body',
+ dataType: DataTypes.String,
+ type: '',
+ isColumn: true,
+ isJSON: false,
+ // eslint-disable-next-line sonarjs/no-duplicate-string
+ id: 'body--string----true',
+ },
+ op: OPERATORS.CONTAINS,
+ value: (parsedValue as BaseAutocompleteData)?.key,
+ },
+ ]);
+ setCurrentFilterItem(undefined);
+ setSearchValue('');
+ setCurrentState(DropdownState.ATTRIBUTE_KEY);
+ } else {
+ setCurrentFilterItem((prev) => ({
+ ...prev,
+ key: parsedValue as BaseAutocompleteData,
+ op: '',
+ value: '',
+ }));
+ setCurrentState(DropdownState.OPERATOR);
+ setSearchValue((parsedValue as BaseAutocompleteData)?.key);
+ }
} else if (currentState === DropdownState.OPERATOR) {
- if (value === OPERATORS.EXISTS || value === OPERATORS.NOT_EXISTS) {
+ if (isEmpty(value) && currentFilterItem?.key?.key) {
+ setTags((prev) => [
+ ...prev,
+ {
+ key: {
+ key: 'body',
+ dataType: DataTypes.String,
+ type: '',
+ isColumn: true,
+ isJSON: false,
+ id: 'body--string----true',
+ },
+ op: OPERATORS.CONTAINS,
+ value: currentFilterItem?.key?.key,
+ },
+ ]);
+ setCurrentFilterItem(undefined);
+ setSearchValue('');
+ setCurrentState(DropdownState.ATTRIBUTE_KEY);
+ } else if (value === OPERATORS.EXISTS || value === OPERATORS.NOT_EXISTS) {
setTags((prev) => [
...prev,
{
@@ -399,6 +445,7 @@ function QueryBuilderSearchV2(
whereClauseConfig?.customKey === 'body' &&
whereClauseConfig?.customOp === OPERATORS.CONTAINS
) {
+ // eslint-disable-next-line sonarjs/no-identical-functions
setTags((prev) => [
...prev,
{
@@ -519,19 +566,20 @@ function QueryBuilderSearchV2(
setCurrentState(DropdownState.OPERATOR);
}
}
- if (suggestionsData?.payload?.attributes?.length === 0) {
+ // again let's not auto select anything for the user
+ if (tagOperator) {
setCurrentFilterItem({
key: {
- key: tagKey.split(' ')[0],
+ key: tagKey,
dataType: DataTypes.EMPTY,
type: '',
isColumn: false,
isJSON: false,
},
- op: '',
+ op: tagOperator,
value: '',
});
- setCurrentState(DropdownState.OPERATOR);
+ setCurrentState(DropdownState.ATTRIBUTE_VALUE);
}
} else if (
// Case 2 - if key is defined but the search text doesn't match with the set key,
@@ -607,13 +655,32 @@ function QueryBuilderSearchV2(
// the useEffect takes care of setting the dropdown values correctly on change of the current state
useEffect(() => {
if (currentState === DropdownState.ATTRIBUTE_KEY) {
+ const { tagKey } = getTagToken(searchValue);
if (isLogsExplorerPage) {
- setDropdownOptions(
- suggestionsData?.payload?.attributes?.map((key) => ({
+ // add the user typed option in the dropdown to select that and move ahead irrespective of the matches and all
+ setDropdownOptions([
+ ...(!isEmpty(tagKey) &&
+ !suggestionsData?.payload?.attributes?.some((val) =>
+ isEqual(val.key, tagKey),
+ )
+ ? [
+ {
+ label: tagKey,
+ value: {
+ key: tagKey,
+ dataType: DataTypes.EMPTY,
+ type: '',
+ isColumn: false,
+ isJSON: false,
+ },
+ },
+ ]
+ : []),
+ ...(suggestionsData?.payload?.attributes?.map((key) => ({
label: key.key,
value: key,
- })) || [],
- );
+ })) || []),
+ ]);
} else {
setDropdownOptions(
data?.payload?.attributeKeys?.map((key) => ({
@@ -643,12 +710,14 @@ function QueryBuilderSearchV2(
op.label.startsWith(partialOperator.toLocaleUpperCase()),
);
}
+ operatorOptions = [{ label: '', value: '' }, ...operatorOptions];
setDropdownOptions(operatorOptions);
} else if (strippedKey.endsWith('[*]') && strippedKey.startsWith('body.')) {
operatorOptions = [OPERATORS.HAS, OPERATORS.NHAS].map((operator) => ({
label: operator,
value: operator,
}));
+ operatorOptions = [{ label: '', value: '' }, ...operatorOptions];
setDropdownOptions(operatorOptions);
} else {
operatorOptions = QUERY_BUILDER_OPERATORS_BY_TYPES.universal.map(
@@ -663,6 +732,7 @@ function QueryBuilderSearchV2(
op.label.startsWith(partialOperator.toLocaleUpperCase()),
);
}
+ operatorOptions = [{ label: '', value: '' }, ...operatorOptions];
setDropdownOptions(operatorOptions);
}
}
@@ -729,7 +799,8 @@ function QueryBuilderSearchV2(
}, [tags]);
useEffect(() => {
- if (!isEqual(query.filters.items, tags)) {
+ // convert the query and tags to same format before comparison
+ if (!isEqual(getInitTags(query), tags)) {
setTags(getInitTags(query));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -769,7 +840,7 @@ function QueryBuilderSearchV2(
);
const queryTags = useMemo(
- () => tags.map((tag) => `${tag.key.key} ${tag.op} ${tag.value}`),
+ () => tags.map((tag) => `${tag.key?.key} ${tag.op} ${tag.value}`),
[tags],
);
diff --git a/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/Suggestions.styles.scss b/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/Suggestions.styles.scss
index bd7ad36a5a..153f32e5ee 100644
--- a/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/Suggestions.styles.scss
+++ b/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/Suggestions.styles.scss
@@ -77,14 +77,14 @@
&.resource {
border-radius: 50px;
- background: rgba(245, 108, 135, 0.1) !important;
- color: var(--bg-sakura-400) !important;
+ background: #4bcff910 !important;
+ color: var(--bg-aqua-400) !important;
.dot {
- background-color: var(--bg-sakura-400);
+ background-color: var(--bg-aqua-400);
}
.text {
- color: var(--bg-sakura-400);
+ color: var(--bg-aqua-400);
font-family: Inter;
font-size: 12px;
font-style: normal;
@@ -168,3 +168,59 @@
}
}
}
+
+.lightMode {
+ .text {
+ color: var(--bg-ink-400);
+ }
+
+ .option {
+ .container {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+
+ .right-section {
+ .data-type {
+ background: var(--bg-vanilla-300);
+ }
+ }
+ .option-meta-data-container {
+ display: flex;
+ gap: 8px;
+ }
+ }
+
+ .container-without-tag {
+ .left {
+ .OPERATOR {
+ color: var(--bg-ink-400);
+ }
+
+ .VALUE {
+ color: var(--bg-ink-400);
+ }
+ }
+
+ .right {
+ .data-type {
+ background: var(--bg-vanilla-300);
+ }
+ }
+ }
+ }
+ .option:hover {
+ .container {
+ .left-section {
+ .value {
+ color: var(--bg-ink-100);
+ }
+ }
+ }
+ .container-without-tag {
+ .value {
+ color: var(--bg-ink-100);
+ }
+ }
+ }
+}
diff --git a/frontend/src/container/QueryTable/QueryTable.intefaces.ts b/frontend/src/container/QueryTable/QueryTable.intefaces.ts
index 7576d796ec..254e4885e7 100644
--- a/frontend/src/container/QueryTable/QueryTable.intefaces.ts
+++ b/frontend/src/container/QueryTable/QueryTable.intefaces.ts
@@ -19,4 +19,5 @@ export type QueryTableProps = Omit<
columns?: ColumnsType;
dataSource?: RowData[];
sticky?: TableProps['sticky'];
+ searchTerm?: string;
};
diff --git a/frontend/src/container/QueryTable/QueryTable.tsx b/frontend/src/container/QueryTable/QueryTable.tsx
index 1786e5d4e3..e438070173 100644
--- a/frontend/src/container/QueryTable/QueryTable.tsx
+++ b/frontend/src/container/QueryTable/QueryTable.tsx
@@ -3,8 +3,11 @@ import './QueryTable.styles.scss';
import { ResizeTable } from 'components/ResizeTable';
import Download from 'container/Download/Download';
import { IServiceName } from 'container/MetricsApplication/Tabs/types';
-import { createTableColumnsFromQuery } from 'lib/query/createTableColumnsFromQuery';
-import { useMemo } from 'react';
+import {
+ createTableColumnsFromQuery,
+ RowData,
+} from 'lib/query/createTableColumnsFromQuery';
+import { useCallback, useEffect, useMemo, useState } from 'react';
import { useParams } from 'react-router-dom';
import { QueryTableProps } from './QueryTable.intefaces';
@@ -20,6 +23,7 @@ export function QueryTable({
columns,
dataSource,
sticky,
+ searchTerm,
...props
}: QueryTableProps): JSX.Element {
const { isDownloadEnabled = false, fileName = '' } = downloadOption || {};
@@ -55,6 +59,27 @@ export function QueryTable({
hideOnSinglePage: true,
};
+ const [filterTable, setFilterTable] = useState(null);
+
+ const onTableSearch = useCallback(
+ (value?: string): void => {
+ const filterTable = newDataSource.filter((o) =>
+ Object.keys(o).some((k) =>
+ String(o[k])
+ .toLowerCase()
+ .includes(value?.toLowerCase() || ''),
+ ),
+ );
+
+ setFilterTable(filterTable);
+ },
+ [newDataSource],
+ );
+
+ useEffect(() => {
+ onTableSearch(searchTerm);
+ }, [newDataSource, onTableSearch, searchTerm]);
+
return (
{isDownloadEnabled && (
@@ -69,7 +94,7 @@ export function QueryTable({
({
+ ...jest.requireActual('react-router-dom'),
+ useLocation: (): { pathname: string } => ({
+ pathname: ``,
+ }),
+}));
+
+// Mock useDashabord hook
+jest.mock('providers/Dashboard/Dashboard', () => ({
+ useDashboard: (): any => ({
+ selectedDashboard: {
+ data: {
+ variables: [],
+ },
+ },
+ }),
+}));
+
+describe('QueryTable -', () => {
+ it('should render correctly with all the data rows', () => {
+ const { container } = render();
+ const tableRows = container.querySelectorAll('tr.ant-table-row');
+ expect(tableRows.length).toBe(QueryTableProps.queryTableData.rows.length);
+ });
+
+ it('should render correctly with searchTerm', () => {
+ const { container } = render(
+ ,
+ );
+ const tableRows = container.querySelectorAll('tr.ant-table-row');
+ expect(tableRows.length).toBe(3);
+ });
+});
+
+const setSearchTerm = jest.fn();
+describe('WidgetHeader -', () => {
+ it('global search option should be working', () => {
+ const { getByText, getByTestId } = render(
+ ,
+ );
+ expect(getByText('Table - Panel')).toBeInTheDocument();
+ const searchWidget = getByTestId('widget-header-search');
+ expect(searchWidget).toBeInTheDocument();
+ // click and open the search input
+ fireEvent.click(searchWidget);
+ // check if input is opened
+ const searchInput = getByTestId('widget-header-search-input');
+ expect(searchInput).toBeInTheDocument();
+
+ // enter search term
+ fireEvent.change(searchInput, { target: { value: 'frontend' } });
+ // check if search term is set
+ expect(setSearchTerm).toHaveBeenCalledWith('frontend');
+ expect(searchInput).toHaveValue('frontend');
+ });
+
+ it('global search should not be present for non-table panel', () => {
+ const { queryByTestId } = render(
+ ,
+ );
+ expect(queryByTestId('widget-header-search')).not.toBeInTheDocument();
+ });
+});
diff --git a/frontend/src/container/QueryTable/__test__/mocks.ts b/frontend/src/container/QueryTable/__test__/mocks.ts
new file mode 100644
index 0000000000..abdb7bcfe3
--- /dev/null
+++ b/frontend/src/container/QueryTable/__test__/mocks.ts
@@ -0,0 +1,797 @@
+/* eslint-disable sonarjs/no-duplicate-string */
+export const QueryTableProps: any = {
+ props: {
+ loading: false,
+ size: 'small',
+ },
+ queryTableData: {
+ columns: [
+ {
+ name: 'resource_host_name',
+ queryName: '',
+ isValueColumn: false,
+ },
+ {
+ name: 'service_name',
+ queryName: '',
+ isValueColumn: false,
+ },
+ {
+ name: 'operation',
+ queryName: '',
+ isValueColumn: false,
+ },
+ {
+ name: 'A',
+ queryName: 'A',
+ isValueColumn: true,
+ },
+ ],
+ rows: [
+ {
+ data: {
+ A: 11.5,
+ operation: 'GetDriver',
+ resource_host_name: 'test-hs-name',
+ service_name: 'redis',
+ },
+ },
+ {
+ data: {
+ A: 10.13,
+ operation: 'HTTP GET',
+ resource_host_name: 'test-hs-name',
+ service_name: 'frontend',
+ },
+ },
+ {
+ data: {
+ A: 9.21,
+ operation: 'HTTP GET /route',
+ resource_host_name: 'test-hs-name',
+ service_name: 'route',
+ },
+ },
+ {
+ data: {
+ A: 9.21,
+ operation: 'HTTP GET: /route',
+ resource_host_name: 'test-hs-name',
+ service_name: 'frontend',
+ },
+ },
+ {
+ data: {
+ A: 0.92,
+ operation: 'HTTP GET: /customer',
+ resource_host_name: 'test-hs-name',
+ service_name: 'frontend',
+ },
+ },
+ {
+ data: {
+ A: 0.92,
+ operation: 'SQL SELECT',
+ resource_host_name: 'test-hs-name',
+ service_name: 'mysql',
+ },
+ },
+ {
+ data: {
+ A: 0.92,
+ operation: 'HTTP GET /customer',
+ resource_host_name: 'test-hs-name',
+ service_name: 'customer',
+ },
+ },
+ ],
+ },
+ query: {
+ builder: {
+ queryData: [
+ {
+ aggregateAttribute: {
+ dataType: 'float64',
+ id: 'signoz_calls_total--float64--Sum--true',
+ isColumn: true,
+ isJSON: false,
+ key: 'signoz_calls_total',
+ type: 'Sum',
+ },
+ aggregateOperator: 'rate',
+ dataSource: 'metrics',
+ disabled: false,
+ expression: 'A',
+ filters: {
+ items: [],
+ op: 'AND',
+ },
+ functions: [],
+ groupBy: [
+ {
+ dataType: 'string',
+ id: 'resource_host_name--string--tag--false',
+ isColumn: false,
+ isJSON: false,
+ key: 'resource_host_name',
+ type: 'tag',
+ },
+ {
+ dataType: 'string',
+ id: 'service_name--string--tag--false',
+ isColumn: false,
+ isJSON: false,
+ key: 'service_name',
+ type: 'tag',
+ },
+ {
+ dataType: 'string',
+ id: 'operation--string--tag--false',
+ isColumn: false,
+ isJSON: false,
+ key: 'operation',
+ type: 'tag',
+ },
+ ],
+ having: [],
+ legend: '',
+ limit: null,
+ orderBy: [],
+ queryName: 'A',
+ reduceTo: 'avg',
+ spaceAggregation: 'sum',
+ stepInterval: 60,
+ timeAggregation: 'rate',
+ },
+ ],
+ queryFormulas: [],
+ },
+ clickhouse_sql: [
+ {
+ disabled: false,
+ legend: '',
+ name: 'A',
+ query: '',
+ },
+ ],
+ id: '1e08128f-c6a3-42ff-8033-4e38d291cf0a',
+ promql: [
+ {
+ disabled: false,
+ legend: '',
+ name: 'A',
+ query: '',
+ },
+ ],
+ queryType: 'builder',
+ },
+ columns: [
+ {
+ dataIndex: 'resource_host_name',
+ title: 'resource_host_name',
+ width: 145,
+ },
+ {
+ dataIndex: 'service_name',
+ title: 'service_name',
+ width: 145,
+ },
+ {
+ dataIndex: 'operation',
+ title: 'operation',
+ width: 145,
+ },
+ {
+ dataIndex: 'A',
+ title: 'A',
+ width: 145,
+ },
+ ],
+ dataSource: [
+ {
+ A: 11.5,
+ operation: 'GetDriver',
+ resource_host_name: 'test-hs-name',
+ service_name: 'redis',
+ },
+ {
+ A: 10.13,
+ operation: 'HTTP GET',
+ resource_host_name: 'test-hs-name',
+ service_name: 'frontend',
+ },
+ {
+ A: 9.21,
+ operation: 'HTTP GET /route',
+ resource_host_name: 'test-hs-name',
+ service_name: 'route',
+ },
+ {
+ A: 9.21,
+ operation: 'HTTP GET: /route',
+ resource_host_name: 'test-hs-name',
+ service_name: 'frontend',
+ },
+ {
+ A: 0.92,
+ operation: 'HTTP GET: /customer',
+ resource_host_name: 'test-hs-name',
+ service_name: 'frontend',
+ },
+ {
+ A: 0.92,
+ operation: 'SQL SELECT',
+ resource_host_name: 'test-hs-name',
+ service_name: 'mysql',
+ },
+ {
+ A: 0.92,
+ operation: 'HTTP GET /customer',
+ resource_host_name: 'test-hs-name',
+ service_name: 'customer',
+ },
+ ],
+ sticky: true,
+ searchTerm: '',
+};
+
+export const WidgetHeaderProps: any = {
+ title: 'Table - Panel',
+ widget: {
+ bucketCount: 30,
+ bucketWidth: 0,
+ columnUnits: {},
+ description: '',
+ fillSpans: false,
+ id: 'add65f0d-7662-4024-af51-da567759235d',
+ isStacked: false,
+ mergeAllActiveQueries: false,
+ nullZeroValues: 'zero',
+ opacity: '1',
+ panelTypes: 'table',
+ query: {
+ builder: {
+ queryData: [
+ {
+ aggregateAttribute: {
+ dataType: 'float64',
+ id: 'signoz_calls_total--float64--Sum--true',
+ isColumn: true,
+ isJSON: false,
+ key: 'signoz_calls_total',
+ type: 'Sum',
+ },
+ aggregateOperator: 'rate',
+ dataSource: 'metrics',
+ disabled: false,
+ expression: 'A',
+ filters: {
+ items: [],
+ op: 'AND',
+ },
+ functions: [],
+ groupBy: [
+ {
+ dataType: 'string',
+ id: 'resource_host_name--string--tag--false',
+ isColumn: false,
+ isJSON: false,
+ key: 'resource_host_name',
+ type: 'tag',
+ },
+ {
+ dataType: 'string',
+ id: 'service_name--string--tag--false',
+ isColumn: false,
+ isJSON: false,
+ key: 'service_name',
+ type: 'tag',
+ },
+ {
+ dataType: 'string',
+ id: 'operation--string--tag--false',
+ isColumn: false,
+ isJSON: false,
+ key: 'operation',
+ type: 'tag',
+ },
+ ],
+ having: [],
+ legend: '',
+ limit: null,
+ orderBy: [],
+ queryName: 'A',
+ reduceTo: 'avg',
+ spaceAggregation: 'sum',
+ stepInterval: 60,
+ timeAggregation: 'rate',
+ },
+ ],
+ queryFormulas: [],
+ },
+ clickhouse_sql: [
+ {
+ disabled: false,
+ legend: '',
+ name: 'A',
+ query: '',
+ },
+ ],
+ id: '1e08128f-c6a3-42ff-8033-4e38d291cf0a',
+ promql: [
+ {
+ disabled: false,
+ legend: '',
+ name: 'A',
+ query: '',
+ },
+ ],
+ queryType: 'builder',
+ },
+ selectedLogFields: [
+ {
+ dataType: 'string',
+ name: 'body',
+ type: '',
+ },
+ {
+ dataType: 'string',
+ name: 'timestamp',
+ type: '',
+ },
+ ],
+ selectedTracesFields: [
+ {
+ dataType: 'string',
+ id: 'serviceName--string--tag--true',
+ isColumn: true,
+ isJSON: false,
+ key: 'serviceName',
+ type: 'tag',
+ },
+ {
+ dataType: 'string',
+ id: 'name--string--tag--true',
+ isColumn: true,
+ isJSON: false,
+ key: 'name',
+ type: 'tag',
+ },
+ {
+ dataType: 'float64',
+ id: 'durationNano--float64--tag--true',
+ isColumn: true,
+ isJSON: false,
+ key: 'durationNano',
+ type: 'tag',
+ },
+ {
+ dataType: 'string',
+ id: 'httpMethod--string--tag--true',
+ isColumn: true,
+ isJSON: false,
+ key: 'httpMethod',
+ type: 'tag',
+ },
+ {
+ dataType: 'string',
+ id: 'responseStatusCode--string--tag--true',
+ isColumn: true,
+ isJSON: false,
+ key: 'responseStatusCode',
+ type: 'tag',
+ },
+ ],
+ softMax: 0,
+ softMin: 0,
+ stackedBarChart: false,
+ thresholds: [],
+ timePreferance: 'GLOBAL_TIME',
+ title: 'Table - Panel',
+ yAxisUnit: 'none',
+ },
+ parentHover: false,
+ queryResponse: {
+ status: 'success',
+ isLoading: false,
+ isSuccess: true,
+ isError: false,
+ isIdle: false,
+ data: {
+ statusCode: 200,
+ error: null,
+ message: 'success',
+ payload: {
+ status: 'success',
+ data: {
+ resultType: '',
+ result: [
+ {
+ table: {
+ columns: [
+ {
+ name: 'resource_host_name',
+ queryName: '',
+ isValueColumn: false,
+ },
+ {
+ name: 'service_name',
+ queryName: '',
+ isValueColumn: false,
+ },
+ {
+ name: 'operation',
+ queryName: '',
+ isValueColumn: false,
+ },
+ {
+ name: 'A',
+ queryName: 'A',
+ isValueColumn: true,
+ },
+ ],
+ rows: [
+ {
+ data: {
+ A: 11.67,
+ operation: 'GetDriver',
+ resource_host_name: '4f6ec470feea',
+ service_name: 'redis',
+ },
+ },
+ {
+ data: {
+ A: 10.26,
+ operation: 'HTTP GET',
+ resource_host_name: '4f6ec470feea',
+ service_name: 'frontend',
+ },
+ },
+ {
+ data: {
+ A: 9.33,
+ operation: 'HTTP GET: /route',
+ resource_host_name: '4f6ec470feea',
+ service_name: 'frontend',
+ },
+ },
+ {
+ data: {
+ A: 9.33,
+ operation: 'HTTP GET /route',
+ resource_host_name: '4f6ec470feea',
+ service_name: 'route',
+ },
+ },
+ {
+ data: {
+ A: 0.93,
+ operation: 'FindDriverIDs',
+ resource_host_name: '4f6ec470feea',
+ service_name: 'redis',
+ },
+ },
+ {
+ data: {
+ A: 0.93,
+ operation: 'HTTP GET: /customer',
+ resource_host_name: '4f6ec470feea',
+ service_name: 'frontend',
+ },
+ },
+ {
+ data: {
+ A: 0.93,
+ operation: '/driver.DriverService/FindNearest',
+ resource_host_name: '4f6ec470feea',
+ service_name: 'driver',
+ },
+ },
+ {
+ data: {
+ A: 0.93,
+ operation: '/driver.DriverService/FindNearest',
+ resource_host_name: '4f6ec470feea',
+ service_name: 'frontend',
+ },
+ },
+ {
+ data: {
+ A: 0.93,
+ operation: 'SQL SELECT',
+ resource_host_name: '4f6ec470feea',
+ service_name: 'mysql',
+ },
+ },
+ {
+ data: {
+ A: 0.93,
+ operation: 'HTTP GET /customer',
+ resource_host_name: '4f6ec470feea',
+ service_name: 'customer',
+ },
+ },
+ {
+ data: {
+ A: 0.93,
+ operation: 'HTTP GET /dispatch',
+ resource_host_name: '4f6ec470feea',
+ service_name: 'frontend',
+ },
+ },
+ {
+ data: {
+ A: 0.21,
+ operation: 'check_request limit',
+ resource_host_name: '',
+ service_name: 'demo-app',
+ },
+ },
+ {
+ data: {
+ A: 0.21,
+ operation: 'authenticate_check_cache',
+ resource_host_name: '',
+ service_name: 'demo-app',
+ },
+ },
+ {
+ data: {
+ A: 0.21,
+ operation: 'authenticate_check_db',
+ resource_host_name: '',
+ service_name: 'demo-app',
+ },
+ },
+ {
+ data: {
+ A: 0.21,
+ operation: 'authenticate',
+ resource_host_name: '',
+ service_name: 'demo-app',
+ },
+ },
+ {
+ data: {
+ A: 0.21,
+ operation: 'check cart in cache',
+ resource_host_name: '',
+ service_name: 'demo-app',
+ },
+ },
+ {
+ data: {
+ A: 0.2,
+ operation: 'get_cart',
+ resource_host_name: '',
+ service_name: 'demo-app',
+ },
+ },
+ {
+ data: {
+ A: 0.2,
+ operation: 'check cart in db',
+ resource_host_name: '',
+ service_name: 'demo-app',
+ },
+ },
+ {
+ data: {
+ A: 0.2,
+ operation: 'home',
+ resource_host_name: '',
+ service_name: 'demo-app',
+ },
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ params: {
+ start: 1726669030000,
+ end: 1726670830000,
+ step: 60,
+ variables: {},
+ formatForWeb: true,
+ compositeQuery: {
+ queryType: 'builder',
+ panelType: 'table',
+ fillGaps: false,
+ builderQueries: {
+ A: {
+ aggregateAttribute: {
+ dataType: 'float64',
+ id: 'signoz_calls_total--float64--Sum--true',
+ isColumn: true,
+ isJSON: false,
+ key: 'signoz_calls_total',
+ type: 'Sum',
+ },
+ aggregateOperator: 'rate',
+ dataSource: 'metrics',
+ disabled: false,
+ expression: 'A',
+ filters: {
+ items: [],
+ op: 'AND',
+ },
+ functions: [],
+ groupBy: [
+ {
+ dataType: 'string',
+ id: 'resource_host_name--string--tag--false',
+ isColumn: false,
+ isJSON: false,
+ key: 'resource_host_name',
+ type: 'tag',
+ },
+ {
+ dataType: 'string',
+ id: 'service_name--string--tag--false',
+ isColumn: false,
+ isJSON: false,
+ key: 'service_name',
+ type: 'tag',
+ },
+ {
+ dataType: 'string',
+ id: 'operation--string--tag--false',
+ isColumn: false,
+ isJSON: false,
+ key: 'operation',
+ type: 'tag',
+ },
+ ],
+ having: [],
+ legend: '',
+ limit: null,
+ orderBy: [],
+ queryName: 'A',
+ reduceTo: 'avg',
+ spaceAggregation: 'sum',
+ stepInterval: 60,
+ timeAggregation: 'rate',
+ },
+ },
+ },
+ },
+ },
+ dataUpdatedAt: 1726670830710,
+ error: null,
+ errorUpdatedAt: 0,
+ failureCount: 0,
+ errorUpdateCount: 0,
+ isFetched: true,
+ isFetchedAfterMount: true,
+ isFetching: false,
+ isRefetching: false,
+ isLoadingError: false,
+ isPlaceholderData: false,
+ isPreviousData: false,
+ isRefetchError: false,
+ isStale: true,
+ },
+ headerMenuList: ['view', 'clone', 'delete', 'edit'],
+ isWarning: false,
+ isFetchingResponse: false,
+ tableProcessedDataRef: {
+ current: [
+ {
+ resource_host_name: '4f6ec470feea',
+ service_name: 'redis',
+ operation: 'GetDriver',
+ A: 11.67,
+ },
+ {
+ resource_host_name: '4f6ec470feea',
+ service_name: 'frontend',
+ operation: 'HTTP GET',
+ A: 10.26,
+ },
+ {
+ resource_host_name: '4f6ec470feea',
+ service_name: 'frontend',
+ operation: 'HTTP GET: /route',
+ A: 9.33,
+ },
+ {
+ resource_host_name: '4f6ec470feea',
+ service_name: 'route',
+ operation: 'HTTP GET /route',
+ A: 9.33,
+ },
+ {
+ resource_host_name: '4f6ec470feea',
+ service_name: 'redis',
+ operation: 'FindDriverIDs',
+ A: 0.93,
+ },
+ {
+ resource_host_name: '4f6ec470feea',
+ service_name: 'frontend',
+ operation: 'HTTP GET: /customer',
+ A: 0.93,
+ },
+ {
+ resource_host_name: '4f6ec470feea',
+ service_name: 'driver',
+ operation: '/driver.DriverService/FindNearest',
+ A: 0.93,
+ },
+ {
+ resource_host_name: '4f6ec470feea',
+ service_name: 'frontend',
+ operation: '/driver.DriverService/FindNearest',
+ A: 0.93,
+ },
+ {
+ resource_host_name: '4f6ec470feea',
+ service_name: 'mysql',
+ operation: 'SQL SELECT',
+ A: 0.93,
+ },
+ {
+ resource_host_name: '4f6ec470feea',
+ service_name: 'customer',
+ operation: 'HTTP GET /customer',
+ A: 0.93,
+ },
+ {
+ resource_host_name: '4f6ec470feea',
+ service_name: 'frontend',
+ operation: 'HTTP GET /dispatch',
+ A: 0.93,
+ },
+ {
+ resource_host_name: '',
+ service_name: 'demo-app',
+ operation: 'check_request limit',
+ A: 0.21,
+ },
+ {
+ resource_host_name: '',
+ service_name: 'demo-app',
+ operation: 'authenticate_check_cache',
+ A: 0.21,
+ },
+ {
+ resource_host_name: '',
+ service_name: 'demo-app',
+ operation: 'authenticate_check_db',
+ A: 0.21,
+ },
+ {
+ resource_host_name: '',
+ service_name: 'demo-app',
+ operation: 'authenticate',
+ A: 0.21,
+ },
+ {
+ resource_host_name: '',
+ service_name: 'demo-app',
+ operation: 'check cart in cache',
+ A: 0.21,
+ },
+ {
+ resource_host_name: '',
+ service_name: 'demo-app',
+ operation: 'get_cart',
+ A: 0.2,
+ },
+ {
+ resource_host_name: '',
+ service_name: 'demo-app',
+ operation: 'check cart in db',
+ A: 0.2,
+ },
+ {
+ resource_host_name: '',
+ service_name: 'demo-app',
+ operation: 'home',
+ A: 0.2,
+ },
+ ],
+ },
+};
diff --git a/frontend/src/container/ServiceApplication/ServiceMetrics/ServiceMetricTable.tsx b/frontend/src/container/ServiceApplication/ServiceMetrics/ServiceMetricTable.tsx
index 6430cc9c8f..1a3b99d6dd 100644
--- a/frontend/src/container/ServiceApplication/ServiceMetrics/ServiceMetricTable.tsx
+++ b/frontend/src/container/ServiceApplication/ServiceMetrics/ServiceMetricTable.tsx
@@ -92,9 +92,10 @@ function ServiceMetricTable({
return (
<>
{RPS > MAX_RPS_LIMIT && (
-
+
{getText('rps_over_100')}
+ email
)}
diff --git a/frontend/src/container/ServiceApplication/ServiceTraces/ServiceTracesTable.tsx b/frontend/src/container/ServiceApplication/ServiceTraces/ServiceTracesTable.tsx
index 6633b7a1aa..42d22e8980 100644
--- a/frontend/src/container/ServiceApplication/ServiceTraces/ServiceTracesTable.tsx
+++ b/frontend/src/container/ServiceApplication/ServiceTraces/ServiceTracesTable.tsx
@@ -49,10 +49,11 @@ function ServiceTraceTable({
return (
<>
{RPS > MAX_RPS_LIMIT && (
-
-
+
+
{getText('rps_over_100')}
-
+ email
+
)}
diff --git a/frontend/src/container/SideNav/menuItems.tsx b/frontend/src/container/SideNav/menuItems.tsx
index be694227a1..6d24b74c53 100644
--- a/frontend/src/container/SideNav/menuItems.tsx
+++ b/frontend/src/container/SideNav/menuItems.tsx
@@ -1,7 +1,6 @@
import { RocketOutlined } from '@ant-design/icons';
import ROUTES from 'constants/routes';
import {
- AreaChart,
BarChart2,
BellDot,
BugIcon,
@@ -114,11 +113,6 @@ const menuItems: SidebarItem[] = [
icon: ,
isBeta: true,
},
- {
- key: ROUTES.USAGE_EXPLORER,
- label: 'Usage Explorer',
- icon: ,
- },
{
key: ROUTES.BILLING,
label: 'Billing',
diff --git a/frontend/src/container/TracesExplorer/ListView/index.tsx b/frontend/src/container/TracesExplorer/ListView/index.tsx
index 810ffb8241..c22623772b 100644
--- a/frontend/src/container/TracesExplorer/ListView/index.tsx
+++ b/frontend/src/container/TracesExplorer/ListView/index.tsx
@@ -14,9 +14,8 @@ import { Pagination } from 'hooks/queryPagination';
import useDragColumns from 'hooks/useDragColumns';
import { getDraggedColumns } from 'hooks/useDragColumns/utils';
import useUrlQueryData from 'hooks/useUrlQueryData';
-import history from 'lib/history';
import { RowData } from 'lib/query/createTableColumnsFromQuery';
-import { HTMLAttributes, memo, useCallback, useMemo } from 'react';
+import { memo, useCallback, useMemo } from 'react';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import { DataSource } from 'types/common/queryBuilder';
@@ -25,7 +24,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { TracesLoading } from '../TraceLoading/TraceLoading';
import { defaultSelectedColumns, PER_PAGE_OPTIONS } from './configs';
import { Container, ErrorText, tableStyles } from './styles';
-import { getListColumns, getTraceLink, transformDataWithDate } from './utils';
+import { getListColumns, transformDataWithDate } from './utils';
interface ListViewProps {
isFilterApplied: boolean;
@@ -108,21 +107,6 @@ function ListView({ isFilterApplied }: ListViewProps): JSX.Element {
[queryTableData],
);
- const handleRow = useCallback(
- (record: RowData): HTMLAttributes => ({
- onClick: (event): void => {
- event.preventDefault();
- event.stopPropagation();
- if (event.metaKey || event.ctrlKey) {
- window.open(getTraceLink(record), '_blank');
- } else {
- history.push(getTraceLink(record));
- }
- },
- }),
- [],
- );
-
const handleDragColumn = useCallback(
(fromIndex: number, toIndex: number) =>
onDragColumns(columns, fromIndex, toIndex),
@@ -169,7 +153,6 @@ function ListView({ isFilterApplied }: ListViewProps): JSX.Element {
style={tableStyles}
dataSource={transformedQueryTableData}
columns={columns}
- onRow={handleRow}
onDragColumn={handleDragColumn}
/>
)}
diff --git a/frontend/src/container/TracesExplorer/ListView/utils.tsx b/frontend/src/container/TracesExplorer/ListView/utils.tsx
index a6201436d1..dc0e3808ae 100644
--- a/frontend/src/container/TracesExplorer/ListView/utils.tsx
+++ b/frontend/src/container/TracesExplorer/ListView/utils.tsx
@@ -47,11 +47,11 @@ export const getListColumns = (
key: 'date',
title: 'Timestamp',
width: 145,
- render: (item): JSX.Element => {
+ render: (value, item): JSX.Element => {
const date =
- typeof item === 'string'
- ? dayjs(item).format('YYYY-MM-DD HH:mm:ss.SSS')
- : dayjs(item / 1e6).format('YYYY-MM-DD HH:mm:ss.SSS');
+ typeof value === 'string'
+ ? dayjs(value).format('YYYY-MM-DD HH:mm:ss.SSS')
+ : dayjs(value / 1e6).format('YYYY-MM-DD HH:mm:ss.SSS');
return (
{date}
@@ -67,10 +67,10 @@ export const getListColumns = (
dataIndex: key,
key: `${key}-${dataType}-${type}`,
width: 145,
- render: (value): JSX.Element => {
+ render: (value, item): JSX.Element => {
if (value === '') {
return (
-
+
N/A
);
@@ -78,7 +78,7 @@ export const getListColumns = (
if (key === 'httpMethod' || key === 'responseStatusCode') {
return (
-
+
{value}
@@ -88,14 +88,14 @@ export const getListColumns = (
if (key === 'durationNano') {
return (
-
+
{getMs(value)}ms
);
}
return (
-
+
{value}
);
diff --git a/frontend/src/container/TracesTableComponent/TracesTableComponent.styles.scss b/frontend/src/container/TracesTableComponent/TracesTableComponent.styles.scss
index 74e80f8764..c59bf3c5ad 100644
--- a/frontend/src/container/TracesTableComponent/TracesTableComponent.styles.scss
+++ b/frontend/src/container/TracesTableComponent/TracesTableComponent.styles.scss
@@ -52,6 +52,8 @@
height: 40px;
justify-content: end;
padding: 0 8px;
+ margin-top: 12px;
+ margin-bottom: 2px;
}
}
diff --git a/frontend/src/hooks/logs/useActiveLog.ts b/frontend/src/hooks/logs/useActiveLog.ts
index 0a968c4650..d7cc498f9f 100644
--- a/frontend/src/hooks/logs/useActiveLog.ts
+++ b/frontend/src/hooks/logs/useActiveLog.ts
@@ -1,6 +1,6 @@
import { getAggregateKeys } from 'api/queryBuilder/getAttributeKeys';
import { SOMETHING_WENT_WRONG } from 'constants/api';
-import { QueryBuilderKeys } from 'constants/queryBuilder';
+import { OPERATORS, QueryBuilderKeys } from 'constants/queryBuilder';
import ROUTES from 'constants/routes';
import { getOperatorValue } from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
@@ -24,6 +24,16 @@ import { v4 as uuid } from 'uuid';
import { UseActiveLog } from './types';
+export function getOldLogsOperatorFromNew(operator: string): string {
+ switch (operator) {
+ case OPERATORS['=']:
+ return OPERATORS.IN;
+ case OPERATORS['!=']:
+ return OPERATORS.NIN;
+ default:
+ return operator;
+ }
+}
export const useActiveLog = (): UseActiveLog => {
const dispatch = useDispatch();
@@ -178,10 +188,11 @@ export const useActiveLog = (): UseActiveLog => {
);
const onAddToQueryLogs = useCallback(
(fieldKey: string, fieldValue: string, operator: string) => {
+ const newOperator = getOldLogsOperatorFromNew(operator);
const updatedQueryString = getGeneratedFilterQueryString(
fieldKey,
fieldValue,
- operator,
+ newOperator,
queryString,
);
diff --git a/frontend/src/hooks/queryBuilder/useFetchKeysAndValues.ts b/frontend/src/hooks/queryBuilder/useFetchKeysAndValues.ts
index 6fd42175ad..7b99b9d250 100644
--- a/frontend/src/hooks/queryBuilder/useFetchKeysAndValues.ts
+++ b/frontend/src/hooks/queryBuilder/useFetchKeysAndValues.ts
@@ -70,7 +70,7 @@ export const useFetchKeysAndValues = (
const queryFiltersWithoutId = useMemo(
() => ({
...query.filters,
- items: query.filters.items.map((item) => {
+ items: query.filters?.items?.map((item) => {
const filterWithoutId = cloneDeep(item);
unset(filterWithoutId, 'id');
return filterWithoutId;
diff --git a/frontend/src/hooks/queryBuilder/useGetCompositeQueryParam.ts b/frontend/src/hooks/queryBuilder/useGetCompositeQueryParam.ts
index 442531a15b..efef00022a 100644
--- a/frontend/src/hooks/queryBuilder/useGetCompositeQueryParam.ts
+++ b/frontend/src/hooks/queryBuilder/useGetCompositeQueryParam.ts
@@ -13,7 +13,11 @@ export const useGetCompositeQueryParam = (): Query | null => {
try {
if (!compositeQuery) return null;
- parsedCompositeQuery = JSON.parse(decodeURIComponent(compositeQuery));
+ // MDN reference - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#decoding_query_parameters_from_a_url
+ // MDN reference to support + characters using encoding - https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams#preserving_plus_signs add later
+ parsedCompositeQuery = JSON.parse(
+ decodeURIComponent(compositeQuery.replace(/\+/g, ' ')),
+ );
} catch (e) {
parsedCompositeQuery = null;
}
diff --git a/frontend/src/index.html.ejs b/frontend/src/index.html.ejs
index 8a4e407ec5..f77e50f2b2 100644
--- a/frontend/src/index.html.ejs
+++ b/frontend/src/index.html.ejs
@@ -114,25 +114,6 @@
})();
-
-