Compare commits

..

1 Commits

Author SHA1 Message Date
Nikhil Soni
d2d2ee01d4 fix(apm): use dynamic stepInterval for API monitoring time range on endpoints page
Replace hardcoded stepInterval: 300 (5 min) with the computed step value in
all four onViewAPIMonitoringPopupClick calls in External.tsx. Also fix the
endTime formula in util.ts — endTime should be `timestamp` (bucket end), not
`timestamp + stepInterval`, so the destination time range covers exactly the
clicked bar bucket [bucket_start, bucket_end].

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 16:58:43 +05:30
81 changed files with 318 additions and 574 deletions

View File

@@ -5686,6 +5686,12 @@ components:
type: string type: string
rootServiceName: rootServiceName:
type: string type: string
serviceNameToTotalDurationMap:
additionalProperties:
minimum: 0
type: integer
nullable: true
type: object
spans: spans:
items: items:
$ref: '#/components/schemas/SpantypesWaterfallSpan' $ref: '#/components/schemas/SpantypesWaterfallSpan'

View File

@@ -6743,6 +6743,15 @@ export interface SpantypesGettableSpanMapperGroupsDTO {
items: SpantypesSpanMapperGroupDTO[]; items: SpantypesSpanMapperGroupDTO[];
} }
export type SpantypesGettableWaterfallTraceDTOServiceNameToTotalDurationMapAnyOf =
{ [key: string]: number };
/**
* @nullable
*/
export type SpantypesGettableWaterfallTraceDTOServiceNameToTotalDurationMap =
SpantypesGettableWaterfallTraceDTOServiceNameToTotalDurationMapAnyOf | null;
export enum SpantypesSpanAggregationTypeDTO { export enum SpantypesSpanAggregationTypeDTO {
span_count = 'span_count', span_count = 'span_count',
execution_time_percentage = 'execution_time_percentage', execution_time_percentage = 'execution_time_percentage',
@@ -6931,6 +6940,10 @@ export interface SpantypesGettableWaterfallTraceDTO {
* @type string * @type string
*/ */
rootServiceName?: string; rootServiceName?: string;
/**
* @type object,null
*/
serviceNameToTotalDurationMap?: SpantypesGettableWaterfallTraceDTOServiceNameToTotalDurationMap;
/** /**
* @type array,null * @type array,null
*/ */

View File

@@ -41,22 +41,14 @@ $item-spacing: 8px;
width: 100%; width: 100%;
background: transparent; background: transparent;
border: none; border: none;
border-radius: 0;
box-shadow: none;
outline: none; outline: none;
height: auto;
color: var(--l1-foreground); color: var(--l1-foreground);
font-size: 14px; font-size: 14px;
line-height: 20px; line-height: 20px;
letter-spacing: -0.07px; letter-spacing: -0.07px;
padding: 0; padding: 0;
&.ant-input:focus {
&:focus,
&:focus-visible,
&:hover {
border: none;
box-shadow: none; box-shadow: none;
outline: none;
} }
&::placeholder { &::placeholder {

View File

@@ -6,7 +6,7 @@ import {
useState, useState,
} from 'react'; } from 'react';
import { Color } from '@signozhq/design-tokens'; import { Color } from '@signozhq/design-tokens';
import { Input } from '@signozhq/ui/input'; import { Input } from 'antd';
import logEvent from 'api/common/logEvent'; import logEvent from 'api/common/logEvent';
import cx from 'classnames'; import cx from 'classnames';
import { TimezonePickerShortcuts } from 'constants/shortcuts/TimezonePickerShortcuts'; import { TimezonePickerShortcuts } from 'constants/shortcuts/TimezonePickerShortcuts';

View File

@@ -1,6 +1,5 @@
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Input } from '@signozhq/ui/input'; import { Card, Form, Input } from 'antd';
import { Card, Form } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import { PANEL_TYPES } from 'constants/queryBuilder'; import { PANEL_TYPES } from 'constants/queryBuilder';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';

View File

@@ -1,6 +1,5 @@
import { useState } from 'react'; import { useState } from 'react';
import { Input } from '@signozhq/ui/input'; import { Button, Input } from 'antd';
import { Button } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import cx from 'classnames'; import cx from 'classnames';
import { X } from '@signozhq/icons'; import { X } from '@signozhq/icons';

View File

@@ -1,6 +1,5 @@
import { useCallback, useEffect, useRef, useState } from 'react'; import { useCallback, useEffect, useRef, useState } from 'react';
import { Input } from '@signozhq/ui/input'; import { Button, Input, InputNumber, Popover, Tooltip } from 'antd';
import { Button, InputNumber, Popover, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import type { DefaultOptionType } from 'antd/es/select'; import type { DefaultOptionType } from 'antd/es/select';
import cx from 'classnames'; import cx from 'classnames';

View File

@@ -259,14 +259,6 @@
border-left: transparent; border-left: transparent;
border-top-left-radius: 0px; border-top-left-radius: 0px;
border-bottom-left-radius: 0px; border-bottom-left-radius: 0px;
&:focus:not(:focus-visible),
&.ant-btn:focus:not(:focus-visible) {
border-color: var(--l2-border);
border-left-color: transparent;
outline: none;
box-shadow: none;
}
} }
} }
} }
@@ -292,21 +284,5 @@
.cm-placeholder { .cm-placeholder {
font-size: 12px !important; font-size: 12px !important;
} }
$add-on-row-height: 38px;
.periscope-input-with-label {
.input {
.ant-select {
height: $add-on-row-height;
}
}
}
.input-with-label {
.input {
height: $add-on-row-height;
}
}
} }
} }

View File

@@ -4,23 +4,6 @@
padding: 12px; padding: 12px;
gap: 12px; gap: 12px;
border-bottom: 1px solid var(--l1-border); border-bottom: 1px solid var(--l1-border);
.search {
input {
--input-background: var(--l2-background);
--input-hover-background: var(--l2-background);
--input-focus-background: var(--l2-background);
&::placeholder {
color: var(--l3-foreground);
}
--input-font-size: 14px;
--input-border-color: var(--l1-border);
--input-focus-border-color: var(--primary-background);
--input-focus-outline-width: 0;
--input-focus-outline-offset: 0;
}
}
.filter-header-checkbox { .filter-header-checkbox {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@@ -1,7 +1,6 @@
/* eslint-disable sonarjs/no-identical-functions */ /* eslint-disable sonarjs/no-identical-functions */
import { Fragment, useMemo, useState } from 'react'; import { Fragment, useMemo, useState } from 'react';
import { Input } from '@signozhq/ui/input'; import { Button, Checkbox, Input, Skeleton } from 'antd';
import { Button, Checkbox, Skeleton } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import cx from 'classnames'; import cx from 'classnames';
import { removeKeysFromExpression } from 'components/QueryBuilderV2/utils'; import { removeKeysFromExpression } from 'components/QueryBuilderV2/utils';

View File

@@ -1,6 +1,5 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { Input } from '@signozhq/ui/input'; import { Button, Input } from 'antd';
import { Button } from 'antd';
import { Check, TableColumnsSplit, X } from '@signozhq/icons'; import { Check, TableColumnsSplit, X } from '@signozhq/icons';
import { Filter as FilterType } from 'types/api/quickFilters/getCustomFilters'; import { Filter as FilterType } from 'types/api/quickFilters/getCustomFilters';

View File

@@ -1,6 +1,5 @@
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import { Input } from '@signozhq/ui/input'; import { Button, Input, Select, Tooltip } from 'antd';
import { Button, Select, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import { CircleX, Trash } from '@signozhq/icons'; import { CircleX, Trash } from '@signozhq/icons';
import { useAppContext } from 'providers/App/App'; import { useAppContext } from 'providers/App/App';

View File

@@ -1,5 +1,4 @@
import { Input } from '@signozhq/ui/input'; import { Collapse, Input } from 'antd';
import { Collapse } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import { useCreateAlertState } from '../context'; import { useCreateAlertState } from '../context';

View File

@@ -1,6 +1,5 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { Input } from '@signozhq/ui/input'; import { Input, Select } from 'antd';
import { Select } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import { ADVANCED_OPTIONS_TIME_UNIT_OPTIONS } from '../../context/constants'; import { ADVANCED_OPTIONS_TIME_UNIT_OPTIONS } from '../../context/constants';

View File

@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Input } from '@signozhq/ui/input'; import { Input } from 'antd';
import './TimeInput.scss'; import './TimeInput.scss';
export interface TimeInputProps { export interface TimeInputProps {

View File

@@ -1,5 +1,4 @@
import { Input } from '@signozhq/ui/input'; import { Input, Select } from 'antd';
import { Select } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import { useCreateAlertState } from '../context'; import { useCreateAlertState } from '../context';

View File

@@ -91,6 +91,7 @@ function ChartPreview({
const renderQBChartPreview = (): JSX.Element => ( const renderQBChartPreview = (): JSX.Element => (
<ChartPreviewComponent <ChartPreviewComponent
headline={headline} headline={headline}
name=""
query={stagedQuery} query={stagedQuery}
selectedInterval={globalSelectedInterval} selectedInterval={globalSelectedInterval}
alertDef={alertDef} alertDef={alertDef}
@@ -106,6 +107,7 @@ function ChartPreview({
const renderPromAndChQueryChartPreview = (): JSX.Element => ( const renderPromAndChQueryChartPreview = (): JSX.Element => (
<ChartPreviewComponent <ChartPreviewComponent
headline={headline} headline={headline}
name="Chart Preview"
query={stagedQuery} query={stagedQuery}
alertDef={alertDef} alertDef={alertDef}
selectedInterval={globalSelectedInterval} selectedInterval={globalSelectedInterval}

View File

@@ -17,6 +17,7 @@ import { CreateAlertProvider } from '../../context';
import ChartPreview from '../ChartPreview/ChartPreview'; import ChartPreview from '../ChartPreview/ChartPreview';
const REQUESTS_PER_SEC = 'requests/sec'; const REQUESTS_PER_SEC = 'requests/sec';
const CHART_PREVIEW_NAME = 'Chart Preview';
const QUERY_TYPE_TEST_ID = 'query-type'; const QUERY_TYPE_TEST_ID = 'query-type';
const GRAPH_TYPE_TEST_ID = 'graph-type'; const GRAPH_TYPE_TEST_ID = 'graph-type';
const CHART_PREVIEW_COMPONENT_TEST_ID = 'chart-preview-component'; const CHART_PREVIEW_COMPONENT_TEST_ID = 'chart-preview-component';
@@ -33,6 +34,7 @@ jest.mock(
return ( return (
<div data-testid={CHART_PREVIEW_COMPONENT_TEST_ID}> <div data-testid={CHART_PREVIEW_COMPONENT_TEST_ID}>
<div data-testid="headline">{props.headline}</div> <div data-testid="headline">{props.headline}</div>
<div data-testid="name">{props.name}</div>
<div data-testid={QUERY_TYPE_TEST_ID}>{props.query?.queryType}</div> <div data-testid={QUERY_TYPE_TEST_ID}>{props.query?.queryType}</div>
<div data-testid="selected-interval"> <div data-testid="selected-interval">
{props.selectedInterval?.startTime} {props.selectedInterval?.startTime}
@@ -173,6 +175,12 @@ describe('ChartPreview', () => {
); );
}); });
it('renders QueryBuilder chart preview with empty name when query type is QUERY_BUILDER', () => {
renderChartPreview();
expect(screen.getByTestId('name')).toHaveTextContent('');
});
it('renders QueryBuilder chart preview with correct props', () => { it('renders QueryBuilder chart preview with correct props', () => {
renderChartPreview(); renderChartPreview();
@@ -183,6 +191,7 @@ describe('ChartPreview', () => {
expect(screen.getByTestId(GRAPH_TYPE_TEST_ID)).toHaveTextContent( expect(screen.getByTestId(GRAPH_TYPE_TEST_ID)).toHaveTextContent(
PANEL_TYPES.TIME_SERIES, PANEL_TYPES.TIME_SERIES,
); );
expect(screen.getByTestId('name')).toHaveTextContent('');
expect(screen.getByTestId('headline')).toBeInTheDocument(); expect(screen.getByTestId('headline')).toBeInTheDocument();
expect(screen.getByTestId('selected-interval')).toBeInTheDocument(); expect(screen.getByTestId('selected-interval')).toBeInTheDocument();
}); });
@@ -205,6 +214,7 @@ describe('ChartPreview', () => {
expect( expect(
screen.getByTestId(CHART_PREVIEW_COMPONENT_TEST_ID), screen.getByTestId(CHART_PREVIEW_COMPONENT_TEST_ID),
).toBeInTheDocument(); ).toBeInTheDocument();
expect(screen.getByTestId('name')).toHaveTextContent(CHART_PREVIEW_NAME);
expect(screen.getByTestId(QUERY_TYPE_TEST_ID)).toHaveTextContent( expect(screen.getByTestId(QUERY_TYPE_TEST_ID)).toHaveTextContent(
EQueryType.PROM, EQueryType.PROM,
); );
@@ -228,6 +238,7 @@ describe('ChartPreview', () => {
expect( expect(
screen.getByTestId(CHART_PREVIEW_COMPONENT_TEST_ID), screen.getByTestId(CHART_PREVIEW_COMPONENT_TEST_ID),
).toBeInTheDocument(); ).toBeInTheDocument();
expect(screen.getByTestId('name')).toHaveTextContent(CHART_PREVIEW_NAME);
expect(screen.getByTestId(QUERY_TYPE_TEST_ID)).toHaveTextContent( expect(screen.getByTestId(QUERY_TYPE_TEST_ID)).toHaveTextContent(
EQueryType.CLICKHOUSE, EQueryType.CLICKHOUSE,
); );

View File

@@ -16,8 +16,7 @@ import {
Plus, Plus,
X, X,
} from '@signozhq/icons'; } from '@signozhq/icons';
import { Input } from '@signozhq/ui/input'; import { Button, Card, Input, Modal, Popover, Tag, Tooltip } from 'antd';
import { Button, Card, Modal, Popover, Tag, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent'; import logEvent from 'api/common/logEvent';
import ConfigureIcon from 'assets/Integrations/ConfigureIcon'; import ConfigureIcon from 'assets/Integrations/ConfigureIcon';

View File

@@ -1,6 +1,5 @@
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { Input } from '@signozhq/ui/input'; import { Button, Input } from 'antd';
import { Button } from 'antd';
import { PrecisionOption, PrecisionOptionsEnum } from 'components/Graph/types'; import { PrecisionOption, PrecisionOptionsEnum } from 'components/Graph/types';
import { ResizeTable } from 'components/ResizeTable'; import { ResizeTable } from 'components/ResizeTable';
import { useNotifications } from 'hooks/useNotifications'; import { useNotifications } from 'hooks/useNotifications';

View File

@@ -17,11 +17,10 @@ import { getTimeRange } from 'utils/getTimeRange';
import BarChart from '../../charts/BarChart/BarChart'; import BarChart from '../../charts/BarChart/BarChart';
import ChartManager from '../../components/ChartManager/ChartManager'; import ChartManager from '../../components/ChartManager/ChartManager';
import { usePanelContextMenu } from '../../hooks/usePanelContextMenu'; import { usePanelContextMenu } from '../../hooks/usePanelContextMenu';
import { prepareBarPanelConfig } from './utils'; import { prepareBarPanelConfig, prepareBarPanelData } from './utils';
import '../Panel.styles.scss'; import '../Panel.styles.scss';
import TooltipFooter from '../components/TooltipFooter'; import TooltipFooter from '../components/TooltipFooter';
import { prepareChartData } from 'lib/uPlotV2/utils/dataUtils';
function BarPanel(props: PanelWrapperProps): JSX.Element { function BarPanel(props: PanelWrapperProps): JSX.Element {
const { const {
@@ -100,7 +99,7 @@ function BarPanel(props: PanelWrapperProps): JSX.Element {
if (!queryResponse?.data?.payload) { if (!queryResponse?.data?.payload) {
return []; return [];
} }
return prepareChartData(queryResponse?.data?.payload); return prepareBarPanelData(queryResponse?.data?.payload);
}, [queryResponse?.data?.payload]); }, [queryResponse?.data?.payload]);
const layoutChildren = useMemo(() => { const layoutChildren = useMemo(() => {

View File

@@ -11,10 +11,21 @@ import { get } from 'lodash-es';
import { Widgets } from 'types/api/dashboard/getAll'; import { Widgets } from 'types/api/dashboard/getAll';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { Query } from 'types/api/queryBuilder/queryBuilderData'; import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { AlignedData } from 'uplot';
import { PanelMode } from '../types'; import { PanelMode } from '../types';
import { fillMissingXAxisTimestamps, getXAxisTimestamps } from '../utils';
import { buildBaseConfig } from '../utils/baseConfigBuilder'; import { buildBaseConfig } from '../utils/baseConfigBuilder';
export function prepareBarPanelData(
apiResponse: MetricRangePayloadProps,
): AlignedData {
const seriesList = apiResponse?.data?.result || [];
const timestampArr = getXAxisTimestamps(seriesList);
const yAxisValuesArr = fillMissingXAxisTimestamps(timestampArr, seriesList);
return [timestampArr, ...yAxisValuesArr];
}
export function prepareBarPanelConfig({ export function prepareBarPanelConfig({
widget, widget,
isDarkMode, isDarkMode,

View File

@@ -17,11 +17,10 @@ import { useTimezone } from 'providers/Timezone';
import uPlot from 'uplot'; import uPlot from 'uplot';
import { getTimeRange } from 'utils/getTimeRange'; import { getTimeRange } from 'utils/getTimeRange';
import { prepareUPlotConfig } from '../TimeSeriesPanel/utils'; import { prepareChartData, prepareUPlotConfig } from '../TimeSeriesPanel/utils';
import '../Panel.styles.scss'; import '../Panel.styles.scss';
import TooltipFooter from '../components/TooltipFooter'; import TooltipFooter from '../components/TooltipFooter';
import { prepareChartData } from 'lib/uPlotV2/utils/dataUtils';
function TimeSeriesPanel(props: PanelWrapperProps): JSX.Element { function TimeSeriesPanel(props: PanelWrapperProps): JSX.Element {
const { const {

View File

@@ -6,8 +6,7 @@ import {
import { Query } from 'types/api/queryBuilder/queryBuilderData'; import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { PanelMode } from '../../types'; import { PanelMode } from '../../types';
import { prepareUPlotConfig } from '../utils'; import { prepareChartData, prepareUPlotConfig } from '../utils';
import { prepareChartData } from 'lib/uPlotV2/utils/dataUtils';
jest.mock( jest.mock(
'container/DashboardContainer/visualization/panels/utils/legendVisibilityUtils', 'container/DashboardContainer/visualization/panels/utils/legendVisibilityUtils',

View File

@@ -1,6 +1,10 @@
import { ExecStats } from 'api/v5/v5'; import { ExecStats } from 'api/v5/v5';
import { Timezone } from 'components/CustomTimePicker/timezoneUtils'; import { Timezone } from 'components/CustomTimePicker/timezoneUtils';
import { PANEL_TYPES } from 'constants/queryBuilder'; import { PANEL_TYPES } from 'constants/queryBuilder';
import {
fillMissingXAxisTimestamps,
getXAxisTimestamps,
} from 'container/DashboardContainer/visualization/panels/utils';
import { getLegend } from 'lib/dashboard/getQueryResults'; import { getLegend } from 'lib/dashboard/getQueryResults';
import getLabelName from 'lib/getLabelName'; import getLabelName from 'lib/getLabelName';
import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin'; import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
@@ -11,15 +15,42 @@ import {
LineStyle, LineStyle,
} from 'lib/uPlotV2/config/types'; } from 'lib/uPlotV2/config/types';
import { UPlotConfigBuilder } from 'lib/uPlotV2/config/UPlotConfigBuilder'; import { UPlotConfigBuilder } from 'lib/uPlotV2/config/UPlotConfigBuilder';
import { hasSingleVisiblePoint } from 'lib/uPlotV2/utils/dataUtils'; import { isInvalidPlotValue } from 'lib/uPlotV2/utils/dataUtils';
import get from 'lodash-es/get'; import get from 'lodash-es/get';
import { Widgets } from 'types/api/dashboard/getAll'; import { Widgets } from 'types/api/dashboard/getAll';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { Query } from 'types/api/queryBuilder/queryBuilderData'; import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { QueryData } from 'types/api/widgets/getQuery';
import { PanelMode } from '../types'; import { PanelMode } from '../types';
import { buildBaseConfig } from '../utils/baseConfigBuilder'; import { buildBaseConfig } from '../utils/baseConfigBuilder';
export const prepareChartData = (
apiResponse: MetricRangePayloadProps,
): uPlot.AlignedData => {
const seriesList = apiResponse?.data?.result || [];
const timestampArr = getXAxisTimestamps(seriesList);
const yAxisValuesArr = fillMissingXAxisTimestamps(timestampArr, seriesList);
return [timestampArr, ...yAxisValuesArr];
};
function hasSingleVisiblePointForSeries(series: QueryData): boolean {
const rawValues = series.values ?? [];
let validPointCount = 0;
for (const [, rawValue] of rawValues) {
if (!isInvalidPlotValue(rawValue)) {
validPointCount += 1;
if (validPointCount > 1) {
return false;
}
}
}
return true;
}
export const prepareUPlotConfig = ({ export const prepareUPlotConfig = ({
widget, widget,
isDarkMode, isDarkMode,
@@ -76,7 +107,7 @@ export const prepareUPlotConfig = ({
} }
apiResponse.data.result.forEach((series) => { apiResponse.data.result.forEach((series) => {
const hasSingleValidPoint = hasSingleVisiblePoint(series.values); const hasSingleValidPoint = hasSingleVisiblePointForSeries(series);
const baseLabelName = getLabelName( const baseLabelName = getLabelName(
series.metric, series.metric,
series.queryName || '', // query series.queryName || '', // query

View File

@@ -19,11 +19,11 @@ import {
Info, Info,
} from '@signozhq/icons'; } from '@signozhq/icons';
import { Color } from '@signozhq/design-tokens'; import { Color } from '@signozhq/design-tokens';
import { Input } from '@signozhq/ui/input';
import { import {
Button, Button,
ColorPicker, ColorPicker,
Divider, Divider,
Input,
Modal, Modal,
RefSelectProps, RefSelectProps,
Select, Select,

View File

@@ -1,7 +1,7 @@
import { Dispatch, SetStateAction } from 'react'; import { Dispatch, SetStateAction } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Input } from '@signozhq/ui/input'; import { Form, Input } from 'antd';
import { Form } from 'antd';
import { EmailChannel } from '../../CreateAlertChannels/config'; import { EmailChannel } from '../../CreateAlertChannels/config';
function EmailForm({ setSelectedConfig }: EmailFormProps): JSX.Element { function EmailForm({ setSelectedConfig }: EmailFormProps): JSX.Element {

View File

@@ -1,7 +1,6 @@
import { Dispatch, SetStateAction } from 'react'; import { Dispatch, SetStateAction } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Input } from '@signozhq/ui/input'; import { Form, Input } from 'antd';
import { Form } from 'antd';
import { MarkdownRenderer } from 'components/MarkdownRenderer/MarkdownRenderer'; import { MarkdownRenderer } from 'components/MarkdownRenderer/MarkdownRenderer';
import { WebhookChannel } from '../../CreateAlertChannels/config'; import { WebhookChannel } from '../../CreateAlertChannels/config';

View File

@@ -1,8 +1,7 @@
import { Dispatch, ReactElement, SetStateAction } from 'react'; import { Dispatch, ReactElement, SetStateAction } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Input } from '@signozhq/ui/input'; import { Form, FormInstance, Input, Select } from 'antd';
import { Switch } from '@signozhq/ui/switch'; import { Switch } from '@signozhq/ui/switch';
import { Form, FormInstance, Select } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import type { Store } from 'antd/lib/form/interface'; import type { Store } from 'antd/lib/form/interface';
import ROUTES from 'constants/routes'; import ROUTES from 'constants/routes';

View File

@@ -1,118 +0,0 @@
import { useMemo } from 'react';
import { Timezone } from 'components/CustomTimePicker/timezoneUtils';
import { PANEL_TYPES } from 'constants/queryBuilder';
import BarChart from 'container/DashboardContainer/visualization/charts/BarChart/BarChart';
import TimeSeries from 'container/DashboardContainer/visualization/charts/TimeSeries/TimeSeries';
import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types';
import { LegendPosition } from 'lib/uPlotV2/components/types';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
import uPlot from 'uplot';
import {
AlertChartPanelType,
buildAlertChartConfig,
buildChartId,
} from './utils';
// Panel types that render through the UPlotConfigBuilder pipeline.
// To support a new modern-chart panel type, add an entry here and extend
// `AlertChartPanelType` / `buildAlertChartConfig` to handle its series setup.
const SUPPORTED_CHARTS: Record<
AlertChartPanelType,
typeof TimeSeries | typeof BarChart
> = {
[PANEL_TYPES.TIME_SERIES]: TimeSeries,
[PANEL_TYPES.BAR]: BarChart,
};
const isSupportedPanelType = (
panelType: PANEL_TYPES,
): panelType is AlertChartPanelType => panelType in SUPPORTED_CHARTS;
export interface ChartContentProps {
panelType: PANEL_TYPES;
alertId?: string;
query: Query;
apiResponse?: MetricRangePayloadProps;
data: uPlot.AlignedData;
thresholds: ThresholdProps[];
yAxisUnit: string;
legendPosition: LegendPosition;
isDarkMode: boolean;
timezone: Timezone;
width: number;
height: number;
minTimeScale?: number;
maxTimeScale?: number;
onDragSelect: (start: number, end: number) => void;
}
export default function ChartContent({
panelType,
alertId,
query,
thresholds,
apiResponse,
data,
yAxisUnit,
isDarkMode,
timezone,
minTimeScale,
maxTimeScale,
onDragSelect,
width,
height,
legendPosition,
}: ChartContentProps): JSX.Element | null {
const supported = isSupportedPanelType(panelType);
const config = useMemo(
() =>
buildAlertChartConfig({
id: buildChartId(alertId),
panelType: panelType as AlertChartPanelType,
query,
thresholds,
apiResponse,
yAxisUnit,
isDarkMode,
timezone,
minTimeScale,
maxTimeScale,
onDragSelect,
}),
[
alertId,
panelType,
query,
thresholds,
apiResponse,
yAxisUnit,
isDarkMode,
timezone,
minTimeScale,
maxTimeScale,
onDragSelect,
],
);
if (!supported) {
return null;
}
const Component = SUPPORTED_CHARTS[panelType];
return (
<Component
config={config}
data={data}
width={width}
height={height}
legendConfig={{ position: legendPosition }}
canPinTooltip
yAxisUnit={yAxisUnit}
timezone={timezone}
/>
);
}

View File

@@ -15,6 +15,8 @@ import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import AnomalyAlertEvaluationView from 'container/AnomalyAlertEvaluationView'; import AnomalyAlertEvaluationView from 'container/AnomalyAlertEvaluationView';
import { INITIAL_CRITICAL_THRESHOLD } from 'container/CreateAlertV2/context/constants'; import { INITIAL_CRITICAL_THRESHOLD } from 'container/CreateAlertV2/context/constants';
import { Threshold } from 'container/CreateAlertV2/context/types'; import { Threshold } from 'container/CreateAlertV2/context/types';
import { getLocalStorageGraphVisibilityState } from 'container/GridCardLayout/GridCard/utils';
import GridPanelSwitch from 'container/GridPanelSwitch';
import { populateMultipleResults } from 'container/NewWidget/LeftContainer/WidgetGraph/util'; import { populateMultipleResults } from 'container/NewWidget/LeftContainer/WidgetGraph/util';
import { getFormatNameByOptionId } from 'container/NewWidget/RightContainer/alertFomatCategories'; import { getFormatNameByOptionId } from 'container/NewWidget/RightContainer/alertFomatCategories';
import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems'; import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems';
@@ -30,7 +32,8 @@ import useUrlQuery from 'hooks/useUrlQuery';
import GetMinMax from 'lib/getMinMax'; import GetMinMax from 'lib/getMinMax';
import getTimeString from 'lib/getTimeString'; import getTimeString from 'lib/getTimeString';
import history from 'lib/history'; import history from 'lib/history';
import { LegendPosition } from 'lib/uPlotV2/components/types'; import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions';
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData';
import { isEmpty } from 'lodash-es'; import { isEmpty } from 'lodash-es';
import { useAppContext } from 'providers/App/App'; import { useAppContext } from 'providers/App/App';
import { useTimezone } from 'providers/Timezone'; import { useTimezone } from 'providers/Timezone';
@@ -38,27 +41,24 @@ import { UpdateTimeInterval } from 'store/actions';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
import { Warning } from 'types/api'; import { Warning } from 'types/api';
import { AlertDef } from 'types/api/alerts/def'; import { AlertDef } from 'types/api/alerts/def';
import { LegendPosition } from 'types/api/dashboard/getAll';
import APIError from 'types/api/error'; import APIError from 'types/api/error';
import { Query } from 'types/api/queryBuilder/queryBuilderData'; import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { EQueryType } from 'types/common/dashboard'; import { EQueryType } from 'types/common/dashboard';
import { GlobalReducer } from 'types/reducer/globalTime'; import { GlobalReducer } from 'types/reducer/globalTime';
import uPlot from 'uplot';
import { getGraphType } from 'utils/getGraphType'; import { getGraphType } from 'utils/getGraphType';
import { getSortedSeriesData } from 'utils/getSortedSeriesData'; import { getSortedSeriesData } from 'utils/getSortedSeriesData';
import { getTimeRange } from 'utils/getTimeRange'; import { getTimeRange } from 'utils/getTimeRange';
import { AlertDetectionTypes } from '..'; import { AlertDetectionTypes } from '..';
import ChartContent from './ChartContent';
import { ChartContainer } from './styles'; import { ChartContainer } from './styles';
import { getThresholds } from './utils'; import { getThresholds } from './utils';
import './ChartPreview.styles.scss'; import './ChartPreview.styles.scss';
import { prepareChartData } from 'lib/uPlotV2/utils/dataUtils';
// Height reserved for the `.chart-preview-header` strip rendered above the chart.
const CHART_PREVIEW_HEADER_HEIGHT = 48;
const CHART_PREVIEW_CONTAINER_PADDING = 16;
export interface ChartPreviewProps { export interface ChartPreviewProps {
name: string;
query: Query | null; query: Query | null;
graphType?: PANEL_TYPES; graphType?: PANEL_TYPES;
selectedTime?: timePreferenceType; selectedTime?: timePreferenceType;
@@ -77,6 +77,7 @@ export interface ChartPreviewProps {
// eslint-disable-next-line sonarjs/cognitive-complexity // eslint-disable-next-line sonarjs/cognitive-complexity
function ChartPreview({ function ChartPreview({
name,
query, query,
graphType = PANEL_TYPES.TIME_SERIES, graphType = PANEL_TYPES.TIME_SERIES,
selectedTime = 'GLOBAL_TIME', selectedTime = 'GLOBAL_TIME',
@@ -112,6 +113,14 @@ function ChartPreview({
const [minTimeScale, setMinTimeScale] = useState<number>(); const [minTimeScale, setMinTimeScale] = useState<number>();
const [maxTimeScale, setMaxTimeScale] = useState<number>(); const [maxTimeScale, setMaxTimeScale] = useState<number>();
const [graphVisibility, setGraphVisibility] = useState<boolean[]>([]);
const legendScrollPositionRef = useRef<{
scrollTop: number;
scrollLeft: number;
}>({
scrollTop: 0,
scrollLeft: 0,
});
const { currentQuery } = useQueryBuilder(); const { currentQuery } = useQueryBuilder();
const { const {
@@ -210,6 +219,18 @@ function ChartPreview({
setMaxTimeScale(endTime); setMaxTimeScale(endTime);
}, [maxTime, minTime, globalSelectedInterval, queryResponse, setQueryStatus]); }, [maxTime, minTime, globalSelectedInterval, queryResponse, setQueryStatus]);
// Initialize graph visibility from localStorage
useEffect(() => {
if (queryResponse?.data?.payload?.data?.result) {
const { graphVisibilityStates: localStoredVisibilityState } =
getLocalStorageGraphVisibilityState({
apiResponse: queryResponse.data.payload.data.result,
name: 'alert-chart-preview',
});
setGraphVisibility(localStoredVisibilityState);
}
}, [queryResponse?.data?.payload?.data?.result]);
if (queryResponse.data && graphType === PANEL_TYPES.BAR) { if (queryResponse.data && graphType === PANEL_TYPES.BAR) {
const sortedSeriesData = getSortedSeriesData( const sortedSeriesData = getSortedSeriesData(
queryResponse.data?.payload.data.result, queryResponse.data?.payload.data.result,
@@ -267,17 +288,62 @@ function ChartPreview({
return LegendPosition.RIGHT; return LegendPosition.RIGHT;
}, [queryResponse?.data?.payload?.data?.result?.length, showSideLegend]); }, [queryResponse?.data?.payload?.data?.result?.length, showSideLegend]);
const resolvedThresholds = useMemo( const options = useMemo(
() => getThresholds(thresholds, t, optionName, yAxisUnit), () =>
[thresholds, t, optionName, yAxisUnit], getUPlotChartOptions({
id: 'alert_legend_widget',
yAxisUnit,
apiResponse: queryResponse?.data?.payload,
dimensions: {
height: containerDimensions?.height ? containerDimensions.height - 48 : 0,
width: containerDimensions?.width,
},
minTimeScale,
maxTimeScale,
isDarkMode,
onDragSelect,
thresholds: getThresholds(thresholds, t, optionName, yAxisUnit),
softMax: null,
softMin: null,
panelType: graphType,
tzDate: (timestamp: number) =>
uPlot.tzDate(new Date(timestamp * 1e3), timezone.value),
timezone: timezone.value,
currentQuery,
query: query || currentQuery,
graphsVisibilityStates: graphVisibility,
setGraphsVisibilityStates: setGraphVisibility,
enhancedLegend: true,
legendPosition,
legendScrollPosition: legendScrollPositionRef.current,
setLegendScrollPosition: (position: {
scrollTop: number;
scrollLeft: number;
}) => {
legendScrollPositionRef.current = position;
},
}),
[
yAxisUnit,
queryResponse?.data?.payload,
containerDimensions,
minTimeScale,
maxTimeScale,
isDarkMode,
onDragSelect,
thresholds,
t,
optionName,
graphType,
timezone.value,
currentQuery,
query,
graphVisibility,
legendPosition,
],
); );
const chartData = useMemo(() => { const chartData = getUPlotChartData(queryResponse?.data?.payload);
if (!queryResponse?.data?.payload) {
return [];
}
return prepareChartData(queryResponse?.data?.payload);
}, [queryResponse?.data?.payload]);
const hasResultData = !!queryResponse?.data?.payload?.data?.result?.length; const hasResultData = !!queryResponse?.data?.payload?.data?.result?.length;
@@ -295,14 +361,6 @@ function ChartPreview({
?.active || false; ?.active || false;
const isWarning = !isEmpty(queryResponse.data?.warning); const isWarning = !isEmpty(queryResponse.data?.warning);
const chartWidth = containerDimensions?.width
? containerDimensions.width - CHART_PREVIEW_CONTAINER_PADDING
: 0;
const chartHeight = containerDimensions?.height
? containerDimensions.height - CHART_PREVIEW_HEADER_HEIGHT
: 0;
return ( return (
<div className="alert-chart-container" ref={graphRef}> <div className="alert-chart-container" ref={graphRef}>
<ChartContainer> <ChartContainer>
@@ -326,22 +384,16 @@ function ChartPreview({
)} )}
{chartDataAvailable && !isAnomalyDetectionAlert && ( {chartDataAvailable && !isAnomalyDetectionAlert && (
<ChartContent <GridPanelSwitch
options={options}
panelType={graphType} panelType={graphType}
alertId={alertDef?.id}
query={query || currentQuery}
apiResponse={queryResponse.data?.payload}
data={chartData} data={chartData}
thresholds={resolvedThresholds} name={name || 'Chart Preview'}
panelData={
queryResponse.data?.payload?.data?.newResult?.data?.result || []
}
query={query || initialQueriesMap.metrics}
yAxisUnit={yAxisUnit} yAxisUnit={yAxisUnit}
legendPosition={legendPosition}
isDarkMode={isDarkMode}
timezone={timezone}
width={chartWidth}
height={chartHeight}
minTimeScale={minTimeScale}
maxTimeScale={maxTimeScale}
onDragSelect={onDragSelect}
/> />
)} )}

View File

@@ -1,10 +1,6 @@
import { Color } from '@signozhq/design-tokens'; import { Color } from '@signozhq/design-tokens';
import { ExecStats } from 'api/v5/v5';
import { Timezone } from 'components/CustomTimePicker/timezoneUtils';
import { PANEL_TYPES } from 'constants/queryBuilder'; import { PANEL_TYPES } from 'constants/queryBuilder';
import { Threshold } from 'container/CreateAlertV2/context/types'; import { Threshold } from 'container/CreateAlertV2/context/types';
import { PanelMode } from 'container/DashboardContainer/visualization/panels/types';
import { buildBaseConfig } from 'container/DashboardContainer/visualization/panels/utils/baseConfigBuilder';
import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types'; import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types';
import { import {
BooleanFormats, BooleanFormats,
@@ -15,20 +11,6 @@ import {
TimeFormats, TimeFormats,
} from 'container/NewWidget/RightContainer/types'; } from 'container/NewWidget/RightContainer/types';
import { TFunction } from 'i18next'; import { TFunction } from 'i18next';
import { getLegend } from 'lib/dashboard/getQueryResults';
import getLabelName from 'lib/getLabelName';
import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
import {
DrawStyle,
FillMode,
LineInterpolation,
LineStyle,
} from 'lib/uPlotV2/config/types';
import { UPlotConfigBuilder } from 'lib/uPlotV2/config/UPlotConfigBuilder';
import { hasSingleVisiblePoint } from 'lib/uPlotV2/utils/dataUtils';
import { get } from 'lodash-es';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { import {
dataFormatConfig, dataFormatConfig,
@@ -38,8 +20,6 @@ import {
timeUnitsConfig, timeUnitsConfig,
} from './config'; } from './config';
const CHART_ID_PREFIX = 'alert_legend_widget';
export function covertIntoDataFormats({ export function covertIntoDataFormats({
value, value,
sourceUnit, sourceUnit,
@@ -162,110 +142,3 @@ export const getThresholds = (
}); });
return thresholdsToReturn; return thresholdsToReturn;
}; };
export type AlertChartPanelType = PANEL_TYPES.TIME_SERIES | PANEL_TYPES.BAR;
export interface BuildAlertChartConfigParams {
id: string;
panelType: AlertChartPanelType;
query: Query;
thresholds: ThresholdProps[];
apiResponse?: MetricRangePayloadProps;
yAxisUnit?: string;
isDarkMode: boolean;
timezone: Timezone;
minTimeScale?: number;
maxTimeScale?: number;
onDragSelect: (startTime: number, endTime: number) => void;
onClick?: OnClickPluginOpts['onClick'];
}
export const buildAlertChartConfig = ({
id,
panelType,
query,
thresholds,
apiResponse,
yAxisUnit,
isDarkMode,
timezone,
minTimeScale,
maxTimeScale,
onDragSelect,
onClick,
}: BuildAlertChartConfigParams): UPlotConfigBuilder => {
const stepIntervals: ExecStats['stepIntervals'] = get(
apiResponse,
'data.newResult.meta.stepIntervals',
{},
);
const stepIntervalValues = Object.values(stepIntervals);
const minStepInterval = stepIntervalValues.length
? Math.min(...stepIntervalValues)
: undefined;
const builder = buildBaseConfig({
id,
panelType,
panelMode: PanelMode.DASHBOARD_VIEW,
thresholds,
apiResponse,
yAxisUnit,
isDarkMode,
timezone,
minTimeScale,
maxTimeScale,
stepInterval: minStepInterval,
onDragSelect,
onClick,
});
const seriesList = apiResponse?.data?.result;
if (!seriesList?.length) {
return builder;
}
const isBar = panelType === PANEL_TYPES.BAR;
seriesList.forEach((series) => {
const baseLabelName = getLabelName(
series.metric,
series.queryName || '',
series.legend || '',
);
const label = query ? getLegend(series, query, baseLabelName) : baseLabelName;
if (isBar) {
builder.addSeries({
scaleKey: 'y',
drawStyle: DrawStyle.Bar,
label,
colorMapping: {},
isDarkMode,
stepInterval: get(stepIntervals, series.queryName, undefined),
});
return;
}
const hasSingleValidPoint = hasSingleVisiblePoint(series.values);
builder.addSeries({
scaleKey: 'y',
drawStyle: hasSingleValidPoint ? DrawStyle.Points : DrawStyle.Line,
label,
colorMapping: {},
spanGaps: true,
lineStyle: LineStyle.Solid,
lineInterpolation: LineInterpolation.Spline,
showPoints: hasSingleValidPoint,
pointSize: 5,
fillMode: FillMode.None,
isDarkMode,
metric: series.metric,
});
});
return builder;
};
export const buildChartId = (id?: string): string =>
id ? `${CHART_ID_PREFIX}_${id}` : CHART_ID_PREFIX;

View File

@@ -719,6 +719,7 @@ function FormAlertRules({
panelType={panelType || PANEL_TYPES.TIME_SERIES} panelType={panelType || PANEL_TYPES.TIME_SERIES}
/> />
} }
name=""
query={stagedQuery} query={stagedQuery}
selectedInterval={globalSelectedInterval} selectedInterval={globalSelectedInterval}
alertDef={alertDef} alertDef={alertDef}
@@ -738,6 +739,7 @@ function FormAlertRules({
panelType={panelType || PANEL_TYPES.TIME_SERIES} panelType={panelType || PANEL_TYPES.TIME_SERIES}
/> />
} }
name="Chart Preview"
query={stagedQuery} query={stagedQuery}
alertDef={alertDef} alertDef={alertDef}
selectedInterval={globalSelectedInterval} selectedInterval={globalSelectedInterval}

View File

@@ -6,8 +6,7 @@ import { useIsFetching } from 'react-query';
import { useDispatch } from 'react-redux'; import { useDispatch } from 'react-redux';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { Color } from '@signozhq/design-tokens'; import { Color } from '@signozhq/design-tokens';
import { Input } from '@signozhq/ui/input'; import { Button, Form, Input, Modal } from 'antd';
import { Button, Form, Modal } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent'; import logEvent from 'api/common/logEvent';
import cx from 'classnames'; import cx from 'classnames';

View File

@@ -5,12 +5,12 @@ import { useCopyToClipboard } from 'react-use';
import { Color } from '@signozhq/design-tokens'; import { Color } from '@signozhq/design-tokens';
import { Badge } from '@signozhq/ui/badge'; import { Badge } from '@signozhq/ui/badge';
import { Button } from '@signozhq/ui/button'; import { Button } from '@signozhq/ui/button';
import { Input } from '@signozhq/ui/input';
import { import {
Col, Col,
Collapse, Collapse,
DatePicker, DatePicker,
Form, Form,
Input,
InputNumber, InputNumber,
Modal, Modal,
Row, Row,

View File

@@ -1,5 +1,4 @@
import { Input } from '@signozhq/ui/input'; import { Form, Input } from 'antd';
import { Form } from 'antd';
import { CloudintegrationtypesCredentialsDTO } from 'api/generated/services/sigNoz.schemas'; import { CloudintegrationtypesCredentialsDTO } from 'api/generated/services/sigNoz.schemas';
function RenderConnectionFields({ function RenderConnectionFields({

View File

@@ -1,7 +1,6 @@
import { useState } from 'react'; import { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Input } from '@signozhq/ui/input'; import { Button, Form, Input } from 'antd';
import { Button, Form } from 'antd';
import apply from 'api/v3/licenses/post'; import apply from 'api/v3/licenses/post';
import { useNotifications } from 'hooks/useNotifications'; import { useNotifications } from 'hooks/useNotifications';
import APIError from 'types/api/error'; import APIError from 'types/api/error';

View File

@@ -1,7 +1,6 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-function-return-type */
import { ChangeEvent, useState } from 'react'; import { ChangeEvent, useState } from 'react';
import { Input } from '@signozhq/ui/input'; import { Button, Input, Modal } from 'antd';
import { Button, Modal } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import ApacheIcon from 'assets/CustomIcons/ApacheIcon'; import ApacheIcon from 'assets/CustomIcons/ApacheIcon';
import DockerIcon from 'assets/CustomIcons/DockerIcon'; import DockerIcon from 'assets/CustomIcons/DockerIcon';

View File

@@ -12,11 +12,11 @@ import { useTranslation } from 'react-i18next';
import { generatePath } from 'react-router-dom'; import { generatePath } from 'react-router-dom';
import { useCopyToClipboard } from 'react-use'; import { useCopyToClipboard } from 'react-use';
import { Color } from '@signozhq/design-tokens'; import { Color } from '@signozhq/design-tokens';
import { Input } from '@signozhq/ui/input';
import { import {
Button, Button,
Dropdown, Dropdown,
Flex, Flex,
Input,
MenuProps, MenuProps,
Modal, Modal,
Popover, Popover,

View File

@@ -1,8 +1,7 @@
import { useState } from 'react'; import { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { LoaderCircle, Check } from '@signozhq/icons'; import { LoaderCircle, Check } from '@signozhq/icons';
import { Input } from '@signozhq/ui/input'; import { Button, Input, Space } from 'antd';
import { Button, Space } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent'; import logEvent from 'api/common/logEvent';
import { useNotifications } from 'hooks/useNotifications'; import { useNotifications } from 'hooks/useNotifications';

View File

@@ -2,9 +2,8 @@ import { ReactNode, useState } from 'react';
import MEditor, { EditorProps, Monaco } from '@monaco-editor/react'; import MEditor, { EditorProps, Monaco } from '@monaco-editor/react';
import { Color } from '@signozhq/design-tokens'; import { Color } from '@signozhq/design-tokens';
import { Button } from '@signozhq/ui/button'; import { Button } from '@signozhq/ui/button';
import { Input } from '@signozhq/ui/input';
import { Switch } from '@signozhq/ui/switch'; import { Switch } from '@signozhq/ui/switch';
import { Collapse, Divider, Tag } from 'antd'; import { Collapse, Divider, Input, Tag } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import { AddToQueryHOCProps } from 'components/Logs/AddToQueryHOC'; import { AddToQueryHOCProps } from 'components/Logs/AddToQueryHOC';
import { ChangeViewFunctionType } from 'container/ExplorerOptions/types'; import { ChangeViewFunctionType } from 'container/ExplorerOptions/types';

View File

@@ -2,8 +2,7 @@ import { ChangeEvent, useCallback, useState } from 'react';
// eslint-disable-next-line no-restricted-imports // eslint-disable-next-line no-restricted-imports
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { CirclePlus, X } from '@signozhq/icons'; import { CirclePlus, X } from '@signozhq/icons';
import { Input } from '@signozhq/ui/input'; import { Col, Input } from 'antd';
import { Col } from 'antd';
import CategoryHeading from 'components/Logs/CategoryHeading'; import CategoryHeading from 'components/Logs/CategoryHeading';
import { fieldSearchFilter } from 'lib/logs/fieldSearch'; import { fieldSearchFilter } from 'lib/logs/fieldSearch';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';

View File

@@ -2,8 +2,7 @@ import { useCallback, useMemo, useState } from 'react';
// eslint-disable-next-line no-restricted-imports // eslint-disable-next-line no-restricted-imports
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { SquareX, X } from '@signozhq/icons'; import { SquareX, X } from '@signozhq/icons';
import { Input } from '@signozhq/ui/input'; import { Button, Input, Select } from 'antd';
import { Button, Select } from 'antd';
import CategoryHeading from 'components/Logs/CategoryHeading'; import CategoryHeading from 'components/Logs/CategoryHeading';
import { import {
ConditionalOperators, ConditionalOperators,

View File

@@ -263,7 +263,7 @@ function External(): JSX.Element {
timestamp: selectedTimeStamp, timestamp: selectedTimeStamp,
domainName: selectedData?.address || '', domainName: selectedData?.address || '',
isError: true, isError: true,
stepInterval: 300, stepInterval,
safeNavigate, safeNavigate,
})} })}
/> />
@@ -306,7 +306,7 @@ function External(): JSX.Element {
timestamp: selectedTimeStamp, timestamp: selectedTimeStamp,
domainName: selectedData?.address, domainName: selectedData?.address,
isError: false, isError: false,
stepInterval: 300, stepInterval,
safeNavigate, safeNavigate,
})} })}
/> />
@@ -352,7 +352,7 @@ function External(): JSX.Element {
timestamp: selectedTimeStamp, timestamp: selectedTimeStamp,
domainName: selectedData?.address, domainName: selectedData?.address,
isError: false, isError: false,
stepInterval: 300, stepInterval,
safeNavigate, safeNavigate,
})} })}
/> />
@@ -395,7 +395,7 @@ function External(): JSX.Element {
timestamp: selectedTimeStamp, timestamp: selectedTimeStamp,
domainName: selectedData?.address, domainName: selectedData?.address,
isError: false, isError: false,
stepInterval: 300, stepInterval,
safeNavigate, safeNavigate,
})} })}
/> />

View File

@@ -151,7 +151,7 @@ export function onViewAPIMonitoringPopupClick({
safeNavigate, safeNavigate,
}: OnViewAPIMonitoringPopupClickProps): (e?: React.MouseEvent) => void { }: OnViewAPIMonitoringPopupClickProps): (e?: React.MouseEvent) => void {
return (e?: React.MouseEvent): void => { return (e?: React.MouseEvent): void => {
const endTime = timestamp + (stepInterval || 60); const endTime = timestamp;
const startTime = timestamp - (stepInterval || 60); const startTime = timestamp - (stepInterval || 60);
const filters = { const filters = {
items: [ items: [

View File

@@ -1,7 +1,6 @@
import { Input } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
// TODO(@signozhq/ui-input): migrate this <Input> once @signozhq/ui Input import { Select } from 'antd';
// supports the `onWheel` handler (used to blur on scroll for number inputs).
import { Input, Select } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import { TIME_AGGREGATION_OPTIONS } from './constants'; import { TIME_AGGREGATION_OPTIONS } from './constants';

View File

@@ -1,8 +1,7 @@
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { useQueryClient } from 'react-query'; import { useQueryClient } from 'react-query';
import type { TableColumnsType as ColumnsType } from 'antd'; import type { TableColumnsType as ColumnsType } from 'antd';
import { Input } from '@signozhq/ui/input'; import { Button, Collapse, Input, Select, Spin } from 'antd';
import { Button, Collapse, Select, Spin } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent'; import logEvent from 'api/common/logEvent';
import { import {

View File

@@ -7,8 +7,7 @@ import {
DropResult, DropResult,
} from 'react-beautiful-dnd'; } from 'react-beautiful-dnd';
import { Color } from '@signozhq/design-tokens'; import { Color } from '@signozhq/design-tokens';
import { Input } from '@signozhq/ui/input'; import { Button, Divider, Dropdown, Input, MenuProps, Tooltip } from 'antd';
import { Button, Divider, Dropdown, MenuProps, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import { FieldDataType } from 'api/v5/v5'; import { FieldDataType } from 'api/v5/v5';
import { SOMETHING_WENT_WRONG } from 'constants/api'; import { SOMETHING_WENT_WRONG } from 'constants/api';

View File

@@ -1,7 +1,5 @@
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
// TODO(@signozhq/ui-input): migrate <Input> once @signozhq/ui Input import { Button, Col, Form, Input as AntInput, Input, Row } from 'antd';
// supports the `spellCheck` prop on the URL input below.
import { Button, Col, Form, Input, Input as AntInput, Row } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import { CONTEXT_LINK_FIELDS } from 'container/NewWidget/RightContainer/ContextLinks/constants'; import { CONTEXT_LINK_FIELDS } from 'container/NewWidget/RightContainer/ContextLinks/constants';
import { import {

View File

@@ -1,8 +1,7 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Blocks, Check, LoaderCircle } from '@signozhq/icons'; import { Blocks, Check, LoaderCircle } from '@signozhq/icons';
import { Input } from '@signozhq/ui/input'; import { Button, Card, Form, Input, Select, Space } from 'antd';
import { Button, Card, Form, Select, Space } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent'; import logEvent from 'api/common/logEvent';
import cx from 'classnames'; import cx from 'classnames';

View File

@@ -1,8 +1,7 @@
import { useState } from 'react'; import { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Check, Server, LoaderCircle } from '@signozhq/icons'; import { Check, Server, LoaderCircle } from '@signozhq/icons';
import { Input } from '@signozhq/ui/input'; import { Button, Card, Form, Input, Space } from 'antd';
import { Button, Card, Form, Space } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent'; import logEvent from 'api/common/logEvent';
import cx from 'classnames'; import cx from 'classnames';

View File

@@ -1,7 +1,6 @@
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Plus, Trash2 } from '@signozhq/icons'; import { Plus, Trash2 } from '@signozhq/icons';
import { Input } from '@signozhq/ui/input'; import { Button, Form, FormInstance, Input, Select, Space } from 'antd';
import { Button, Form, FormInstance, Select, Space } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import { requireErrorMessage } from 'utils/form/requireErrorMessage'; import { requireErrorMessage } from 'utils/form/requireErrorMessage';

View File

@@ -1,6 +1,6 @@
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Input } from '@signozhq/ui/input'; import { Form, Input } from 'antd';
import { Form } from 'antd';
import { ProcessorFormField } from '../../AddNewProcessor/config'; import { ProcessorFormField } from '../../AddNewProcessor/config';
import { formValidationRules } from '../../config'; import { formValidationRules } from '../../config';

View File

@@ -1,6 +1,4 @@
import { ChangeEventHandler, useState } from 'react'; import { ChangeEventHandler, useState } from 'react';
// TODO(@signozhq/ui-input): migrate to @signozhq/ui Input once the antd
// `InputProps` spread (`size`, etc.) is no longer needed on this wrapper.
import { Input, InputProps } from 'antd'; import { Input, InputProps } from 'antd';
function CSVInput({ value, onChange, ...otherProps }: InputProps): JSX.Element { function CSVInput({ value, onChange, ...otherProps }: InputProps): JSX.Element {

View File

@@ -1,8 +1,7 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Info } from '@signozhq/icons'; import { Info } from '@signozhq/icons';
import { Input } from '@signozhq/ui/input';
import { Switch } from '@signozhq/ui/switch'; import { Switch } from '@signozhq/ui/switch';
import { Flex, Form, Space, Tooltip } from 'antd'; import { Flex, Form, Input, Space, Tooltip } from 'antd';
import { ProcessorData } from 'types/api/pipeline/def'; import { ProcessorData } from 'types/api/pipeline/def';
import { PREDEFINED_MAPPING } from '../config'; import { PREDEFINED_MAPPING } from '../config';

View File

@@ -1,7 +1,6 @@
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Input } from '@signozhq/ui/input'; import { Form, Input, Select, Space } from 'antd';
import { Switch } from '@signozhq/ui/switch'; import { Switch } from '@signozhq/ui/switch';
import { Form, Select, Space } from 'antd';
import { ModalFooterTitle } from 'container/PipelinePage/styles'; import { ModalFooterTitle } from 'container/PipelinePage/styles';
import { ProcessorData } from 'types/api/pipeline/def'; import { ProcessorData } from 'types/api/pipeline/def';

View File

@@ -2,8 +2,7 @@ import React, { ChangeEvent, useEffect, useState } from 'react';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { Plus, Search } from '@signozhq/icons'; import { Plus, Search } from '@signozhq/icons';
import { Color } from '@signozhq/design-tokens'; import { Color } from '@signozhq/design-tokens';
import { Input } from '@signozhq/ui/input'; import { Button, Flex, Form, Input, Tooltip } from 'antd';
import { Button, Flex, Form, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import { import {
useDeleteDowntimeScheduleByID, useDeleteDowntimeScheduleByID,

View File

@@ -8,16 +8,4 @@
grid-template-columns: 60% 35%; grid-template-columns: 60% 35%;
justify-content: space-between; justify-content: space-between;
gap: 16px; gap: 16px;
.input-with-label {
.label {
box-sizing: border-box;
height: 36px;
}
.input {
box-sizing: border-box;
height: 36px;
}
}
} }

View File

@@ -1,7 +1,6 @@
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { useQuery } from 'react-query'; import { useQuery } from 'react-query';
import { Input } from '@signozhq/ui/input'; import { Input, Skeleton } from 'antd';
import { Skeleton } from 'antd';
import { getKeySuggestions } from 'api/querySuggestions/getKeySuggestions'; import { getKeySuggestions } from 'api/querySuggestions/getKeySuggestions';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar'; import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import { QUERY_BUILDER_KEY_TYPES } from 'constants/antlrQueryConstants'; import { QUERY_BUILDER_KEY_TYPES } from 'constants/antlrQueryConstants';

View File

@@ -1,8 +1,7 @@
import { ChangeEvent, useMemo } from 'react'; import { ChangeEvent, useMemo } from 'react';
import { Plus, Search } from '@signozhq/icons'; import { Plus, Search } from '@signozhq/icons';
import { Color } from '@signozhq/design-tokens'; import { Color } from '@signozhq/design-tokens';
import { Input } from '@signozhq/ui/input'; import { Button, Flex, Input, Tooltip } from 'antd';
import { Button, Flex, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import { useAppContext } from 'providers/App/App'; import { useAppContext } from 'providers/App/App';
import { USER_ROLES } from 'types/roles'; import { USER_ROLES } from 'types/roles';

View File

@@ -1,5 +1,5 @@
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { Input } from '@signozhq/ui/input'; import { Input } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import cx from 'classnames'; import cx from 'classnames';
import CopyClipboardHOC from 'components/Logs/CopyClipboardHOC'; import CopyClipboardHOC from 'components/Logs/CopyClipboardHOC';

View File

@@ -1,6 +1,5 @@
import { useState } from 'react'; import { useState } from 'react';
import { Input } from '@signozhq/ui/input'; import { Collapse, Input, Modal } from 'antd';
import { Collapse, Modal } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import { getYAxisFormattedValue } from 'components/Graph/yAxisConfig'; import { getYAxisFormattedValue } from 'components/Graph/yAxisConfig';
import { Diamond } from '@signozhq/icons'; import { Diamond } from '@signozhq/icons';

View File

@@ -9,10 +9,10 @@ import {
useState, useState,
} from 'react'; } from 'react';
import { useMutation, useQuery } from 'react-query'; import { useMutation, useQuery } from 'react-query';
import { Input } from '@signozhq/ui/input';
import { import {
Button, Button,
Checkbox, Checkbox,
Input,
Modal, Modal,
Select, Select,
Skeleton, Skeleton,

View File

@@ -1,7 +1,5 @@
// TODO(@signozhq/ui-input): migrate this styled(Input) once @signozhq/ui
// Input supports `addonAfter` (the consumer renders `<InputComponent addonAfter="ms">`).
import { Typography } from '@signozhq/ui/typography';
import { Input } from 'antd'; import { Input } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import styled from 'styled-components'; import styled from 'styled-components';
export const DurationText = styled.div` export const DurationText = styled.div`

View File

@@ -8,8 +8,7 @@ import {
import { useQuery } from 'react-query'; import { useQuery } from 'react-query';
// eslint-disable-next-line no-restricted-imports // eslint-disable-next-line no-restricted-imports
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { Input } from '@signozhq/ui/input'; import { AutoComplete, Input } from 'antd';
import { AutoComplete } from 'antd';
import getTagFilters from 'api/trace/getTagFilter'; import getTagFilters from 'api/trace/getTagFilter';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
import { GlobalReducer } from 'types/reducer/globalTime'; import { GlobalReducer } from 'types/reducer/globalTime';

View File

@@ -2,8 +2,7 @@ import { useMemo, useState } from 'react';
import { useQuery } from 'react-query'; import { useQuery } from 'react-query';
// eslint-disable-next-line no-restricted-imports // eslint-disable-next-line no-restricted-imports
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { Input } from '@signozhq/ui/input'; import { AutoComplete, Input, Space } from 'antd';
import { AutoComplete, Space } from 'antd';
import getTagFilters from 'api/trace/getTagFilter'; import getTagFilters from 'api/trace/getTagFilter';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
import { GlobalReducer } from 'types/reducer/globalTime'; import { GlobalReducer } from 'types/reducer/globalTime';

View File

@@ -1,7 +1,6 @@
import { ChangeEvent, useEffect, useMemo, useState } from 'react'; import { ChangeEvent, useEffect, useMemo, useState } from 'react';
import { ArrowLeft, Check, Loader, Plus, Search } from '@signozhq/icons'; import { ArrowLeft, Check, Loader, Plus, Search } from '@signozhq/icons';
import { Input } from '@signozhq/ui/input'; import { Button, Input, Spin } from 'antd';
import { Button, Spin } from 'antd';
import cx from 'classnames'; import cx from 'classnames';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar'; import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import SignozModal from 'components/SignozModal/SignozModal'; import SignozModal from 'components/SignozModal/SignozModal';

View File

@@ -1,4 +1,4 @@
import { Input } from '@signozhq/ui/input'; import { Input } from 'antd';
import styled from 'styled-components'; import styled from 'styled-components';
export const InputComponent = styled(Input)` export const InputComponent = styled(Input)`

View File

@@ -1,9 +1,3 @@
import {
fillMissingXAxisTimestamps,
getXAxisTimestamps,
} from 'container/DashboardContainer/visualization/panels/utils';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
/** /**
* Checks if a value is invalid for plotting * Checks if a value is invalid for plotting
* *
@@ -58,28 +52,6 @@ export function normalizePlotValue(
return value as number; return value as number;
} }
/**
* Returns true if at most one entry in `values` is a valid plot value.
*
* Used to decide whether a series should render as a single point (drawStyle:
* Points) vs a line — a continuous line with only one visible sample is
* invisible to the user.
*/
export function hasSingleVisiblePoint(
values: ReadonlyArray<readonly [unknown, unknown]> | undefined,
): boolean {
let validPointCount = 0;
for (const [, rawValue] of values ?? []) {
if (!isInvalidPlotValue(rawValue)) {
validPointCount += 1;
if (validPointCount > 1) {
return false;
}
}
}
return true;
}
export interface SeriesSpanGapsOption { export interface SeriesSpanGapsOption {
spanGaps?: boolean | number; spanGaps?: boolean | number;
} }
@@ -254,21 +226,3 @@ export function applySpanGapsToAlignedData(
return [newX, ...transformedSeries] as uPlot.AlignedData; return [newX, ...transformedSeries] as uPlot.AlignedData;
} }
/** * Transforms raw API response into aligned data format expected by uPlot.
*
* The API response contains multiple series of time-value pairs, each with its
* own set of timestamps. uPlot requires a single shared x-axis (timestamps)
* and separate y-value arrays for each series, aligned by index. This function
* extracts the unique sorted timestamps across all series and fills in missing
* values with null to maintain alignment.
*/
export const prepareChartData = (
apiResponse: MetricRangePayloadProps,
): uPlot.AlignedData => {
const seriesList = apiResponse?.data?.result || [];
const timestampArr = getXAxisTimestamps(seriesList);
const yAxisValuesArr = fillMissingXAxisTimestamps(timestampArr, seriesList);
return [timestampArr, ...yAxisValuesArr];
};

View File

@@ -1,6 +1,5 @@
import { Dispatch, SetStateAction, useEffect, useState } from 'react'; import { Dispatch, SetStateAction, useEffect, useState } from 'react';
import { Input } from '@signozhq/ui/input'; import { Input, Select } from 'antd';
import { Select } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import './DropRateView.styles.scss'; import './DropRateView.styles.scss';

View File

@@ -28,10 +28,6 @@
.learn-more { .learn-more {
font-size: 14px; font-size: 14px;
} }
.search-input-container {
margin-top: 16px;
margin-bottom: 8px;
}
.ant-input-affix-wrapper { .ant-input-affix-wrapper {
margin-top: 16px; margin-top: 16px;

View File

@@ -3,8 +3,7 @@ import { useTranslation } from 'react-i18next';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { Color } from '@signozhq/design-tokens'; import { Color } from '@signozhq/design-tokens';
import { Button } from '@signozhq/ui/button'; import { Button } from '@signozhq/ui/button';
import { Input } from '@signozhq/ui/input'; import { ColorPicker, Input, Modal, Table, TableProps } from 'antd';
import { ColorPicker, Modal, Table, TableProps } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent'; import logEvent from 'api/common/logEvent';
import { import {
@@ -312,15 +311,12 @@ function SaveView(): JSX.Element {
Learn more Learn more
</Typography.Link> </Typography.Link>
</Typography.Text> </Typography.Text>
<div className="search-input-container"> <Input
<Input placeholder="Search for views..."
placeholder="Search for views..." prefix={<Search size={12} color={Color.BG_VANILLA_400} />}
prefix={<Search size={12} color={Color.BG_VANILLA_400} />} value={searchValue}
value={searchValue} onChange={handleSearch}
onChange={handleSearch} />
className="search-input"
/>
</div>
<Table <Table
columns={columns} columns={columns}

View File

@@ -1,5 +1,4 @@
import { Input } from '@signozhq/ui/input'; import { Checkbox, Input, Select, Skeleton } from 'antd';
import { Checkbox, Select, Skeleton } from 'antd';
import { Button } from '@signozhq/ui/button'; import { Button } from '@signozhq/ui/button';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import cx from 'classnames'; import cx from 'classnames';

View File

@@ -1,7 +1,6 @@
import { ChangeEvent, useEffect, useMemo, useState } from 'react'; import { ChangeEvent, useEffect, useMemo, useState } from 'react';
import { Button } from '@signozhq/ui/button'; import { Button } from '@signozhq/ui/button';
import { Input } from '@signozhq/ui/input'; import { Input, Spin } from 'antd';
import { Spin } from 'antd';
import cx from 'classnames'; import cx from 'classnames';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar'; import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import SignozModal from 'components/SignozModal/SignozModal'; import SignozModal from 'components/SignozModal/SignozModal';

View File

@@ -1,6 +1,6 @@
import { useState } from 'react'; import { useState } from 'react';
import { useQueryClient } from 'react-query'; import { useQueryClient } from 'react-query';
import { Input } from '@signozhq/ui/input'; import { Input } from 'antd';
import SignozModal from 'components/SignozModal/SignozModal'; import SignozModal from 'components/SignozModal/SignozModal';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys'; import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import { useRenameFunnel } from 'hooks/TracesFunnels/useFunnels'; import { useRenameFunnel } from 'hooks/TracesFunnels/useFunnels';

View File

@@ -9,10 +9,10 @@
.ant-input-prefix { .ant-input-prefix {
margin-inline-end: 6px; margin-inline-end: 6px;
} }
&, &,
input { input {
font-family: Inter; font-family: Inter;
background: var(--l2-background);
font-size: 14px; font-size: 14px;
line-height: 18px; line-height: 18px;
font-style: normal; font-style: normal;

View File

@@ -1,7 +1,6 @@
import { ChangeEvent } from 'react'; import { ChangeEvent } from 'react';
import { Color } from '@signozhq/design-tokens'; import { Color } from '@signozhq/design-tokens';
import { Input } from '@signozhq/ui/input'; import { Button, Input, Popover, Tooltip } from 'antd';
import { Button, Popover, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography'; import { Typography } from '@signozhq/ui/typography';
import { ArrowDownWideNarrow, Check, Plus, Search } from '@signozhq/icons'; import { ArrowDownWideNarrow, Check, Plus, Search } from '@signozhq/icons';
import { useAppContext } from 'providers/App/App'; import { useAppContext } from 'providers/App/App';

View File

@@ -204,12 +204,5 @@
background: var(--l2-background); background: var(--l2-background);
height: 38px; height: 38px;
width: 38px; width: 38px;
&:focus:not(:focus-visible),
&.ant-btn:focus:not(:focus-visible) {
border-color: var(--l2-border);
outline: none;
box-shadow: none;
}
} }
} }

View File

@@ -97,7 +97,7 @@ func makeChain(n int) (*spantypes.WaterfallSpan, map[string]*spantypes.Waterfall
} }
func getWaterfallTrace(roots []*spantypes.WaterfallSpan, spanMap map[string]*spantypes.WaterfallSpan) *spantypes.WaterfallTrace { func getWaterfallTrace(roots []*spantypes.WaterfallSpan, spanMap map[string]*spantypes.WaterfallSpan) *spantypes.WaterfallTrace {
return spantypes.NewWaterfallTrace(0, 0, uint64(len(spanMap)), 0, spanMap, roots, false) return spantypes.NewWaterfallTrace(0, 0, uint64(len(spanMap)), 0, spanMap, nil, roots, false)
} }
// ───────────────────────────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────────────────────────

View File

@@ -33,7 +33,7 @@ func buildTraceFromSpans(spans ...*WaterfallSpan) *WaterfallTrace {
endTime = end endTime = end
} }
} }
return NewWaterfallTrace(startTime, endTime, uint64(len(spanMap)), 0, spanMap, nil, false) return NewWaterfallTrace(startTime, endTime, uint64(len(spanMap)), 0, spanMap, nil, nil, false)
} }
var ( var (

View File

@@ -20,45 +20,50 @@ type TraceSummary struct {
// WaterfallTrace holds processed trace data with childern populated in spans. // WaterfallTrace holds processed trace data with childern populated in spans.
type WaterfallTrace struct { type WaterfallTrace struct {
StartTime uint64 `json:"startTime"` StartTime uint64 `json:"startTime"`
EndTime uint64 `json:"endTime"` EndTime uint64 `json:"endTime"`
TotalSpans uint64 `json:"totalSpans"` TotalSpans uint64 `json:"totalSpans"`
TotalErrorSpans uint64 `json:"totalErrorSpans"` TotalErrorSpans uint64 `json:"totalErrorSpans"`
SpanIDToSpanNodeMap map[string]*WaterfallSpan `json:"spanIdToSpanNodeMap"` ServiceNameToTotalDurationMap map[string]uint64 `json:"serviceNameToTotalDurationMap"`
TraceRoots []*WaterfallSpan `json:"traceRoots"` SpanIDToSpanNodeMap map[string]*WaterfallSpan `json:"spanIdToSpanNodeMap"`
HasMissingSpans bool `json:"hasMissingSpans"` TraceRoots []*WaterfallSpan `json:"traceRoots"`
HasMissingSpans bool `json:"hasMissingSpans"`
} }
// GettableWaterfallTrace is the response for the v3 waterfall API. // GettableWaterfallTrace is the response for the v3 waterfall API.
type GettableWaterfallTrace struct { type GettableWaterfallTrace struct {
StartTimestampMillis uint64 `json:"startTimestampMillis"` StartTimestampMillis uint64 `json:"startTimestampMillis"`
EndTimestampMillis uint64 `json:"endTimestampMillis"` EndTimestampMillis uint64 `json:"endTimestampMillis"`
RootServiceName string `json:"rootServiceName"` RootServiceName string `json:"rootServiceName"`
RootServiceEntryPoint string `json:"rootServiceEntryPoint"` RootServiceEntryPoint string `json:"rootServiceEntryPoint"`
TotalSpansCount uint64 `json:"totalSpansCount"` TotalSpansCount uint64 `json:"totalSpansCount"`
TotalErrorSpansCount uint64 `json:"totalErrorSpansCount"` TotalErrorSpansCount uint64 `json:"totalErrorSpansCount"`
Spans []*WaterfallSpan `json:"spans"` // Deprecated: use Aggregations with SpanAggregationExecutionTimePercentage on the service.name field instead.
HasMissingSpans bool `json:"hasMissingSpans"` ServiceNameToTotalDurationMap map[string]uint64 `json:"serviceNameToTotalDurationMap"`
UncollapsedSpans []string `json:"uncollapsedSpans"` Spans []*WaterfallSpan `json:"spans"`
HasMore bool `json:"hasMore"` HasMissingSpans bool `json:"hasMissingSpans"`
Aggregations []SpanAggregationResult `json:"aggregations"` UncollapsedSpans []string `json:"uncollapsedSpans"`
HasMore bool `json:"hasMore"`
Aggregations []SpanAggregationResult `json:"aggregations"`
} }
// NewWaterfallTrace constructs a WaterfallTrace from processed span data. // NewWaterfallTrace constructs a WaterfallTrace from processed span data.
func NewWaterfallTrace( func NewWaterfallTrace(
startTime, endTime, totalSpans, totalErrorSpans uint64, startTime, endTime, totalSpans, totalErrorSpans uint64,
spanIDToSpanNodeMap map[string]*WaterfallSpan, spanIDToSpanNodeMap map[string]*WaterfallSpan,
serviceNameToTotalDurationMap map[string]uint64,
traceRoots []*WaterfallSpan, traceRoots []*WaterfallSpan,
hasMissingSpans bool, hasMissingSpans bool,
) *WaterfallTrace { ) *WaterfallTrace {
return &WaterfallTrace{ return &WaterfallTrace{
StartTime: startTime, StartTime: startTime,
EndTime: endTime, EndTime: endTime,
TotalSpans: totalSpans, TotalSpans: totalSpans,
TotalErrorSpans: totalErrorSpans, TotalErrorSpans: totalErrorSpans,
SpanIDToSpanNodeMap: spanIDToSpanNodeMap, SpanIDToSpanNodeMap: spanIDToSpanNodeMap,
TraceRoots: traceRoots, ServiceNameToTotalDurationMap: serviceNameToTotalDurationMap,
HasMissingSpans: hasMissingSpans, TraceRoots: traceRoots,
HasMissingSpans: hasMissingSpans,
} }
} }
@@ -119,6 +124,7 @@ func NewWaterfallTraceFromSpans(spans []StorableSpan) *WaterfallTrace {
uint64(len(spans)), uint64(len(spans)),
totalErrorSpans, totalErrorSpans,
spanIDToSpanNodeMap, spanIDToSpanNodeMap,
calculateServiceTime(spanIDToSpanNodeMap),
traceRoots, traceRoots,
hasMissingSpans, hasMissingSpans,
) )
@@ -200,19 +206,23 @@ func (wt *WaterfallTrace) CalculateUncollapsedSpanIDs(uncollapsedSpanIDs []strin
} }
func (wt *WaterfallTrace) Clone() cachetypes.Cacheable { func (wt *WaterfallTrace) Clone() cachetypes.Cacheable {
copyOfServiceNameToTotalDurationMap := make(map[string]uint64)
maps.Copy(copyOfServiceNameToTotalDurationMap, wt.ServiceNameToTotalDurationMap)
copyOfSpanIDToSpanNodeMap := make(map[string]*WaterfallSpan) copyOfSpanIDToSpanNodeMap := make(map[string]*WaterfallSpan)
maps.Copy(copyOfSpanIDToSpanNodeMap, wt.SpanIDToSpanNodeMap) maps.Copy(copyOfSpanIDToSpanNodeMap, wt.SpanIDToSpanNodeMap)
copyOfTraceRoots := make([]*WaterfallSpan, len(wt.TraceRoots)) copyOfTraceRoots := make([]*WaterfallSpan, len(wt.TraceRoots))
copy(copyOfTraceRoots, wt.TraceRoots) copy(copyOfTraceRoots, wt.TraceRoots)
return &WaterfallTrace{ return &WaterfallTrace{
StartTime: wt.StartTime, StartTime: wt.StartTime,
EndTime: wt.EndTime, EndTime: wt.EndTime,
TotalSpans: wt.TotalSpans, TotalSpans: wt.TotalSpans,
TotalErrorSpans: wt.TotalErrorSpans, TotalErrorSpans: wt.TotalErrorSpans,
SpanIDToSpanNodeMap: copyOfSpanIDToSpanNodeMap, ServiceNameToTotalDurationMap: copyOfServiceNameToTotalDurationMap,
TraceRoots: copyOfTraceRoots, SpanIDToSpanNodeMap: copyOfSpanIDToSpanNodeMap,
HasMissingSpans: wt.HasMissingSpans, TraceRoots: copyOfTraceRoots,
HasMissingSpans: wt.HasMissingSpans,
} }
} }
@@ -247,6 +257,11 @@ func NewGettableWaterfallTrace(
rootServiceEntryPoint = traceData.TraceRoots[0].Name rootServiceEntryPoint = traceData.TraceRoots[0].Name
} }
serviceDurationsMillis := make(map[string]uint64, len(traceData.ServiceNameToTotalDurationMap))
for svc, dur := range traceData.ServiceNameToTotalDurationMap {
serviceDurationsMillis[svc] = dur / 1_000_000
}
// convert start timestamp to millis because client is expecting it in millis // convert start timestamp to millis because client is expecting it in millis
for _, span := range selectedSpans { for _, span := range selectedSpans {
span.TimeUnix = span.TimeUnix / 1_000_000 span.TimeUnix = span.TimeUnix / 1_000_000
@@ -262,17 +277,18 @@ func NewGettableWaterfallTrace(
} }
return &GettableWaterfallTrace{ return &GettableWaterfallTrace{
Spans: selectedSpans, Spans: selectedSpans,
UncollapsedSpans: uncollapsedSpans, UncollapsedSpans: uncollapsedSpans,
StartTimestampMillis: traceData.StartTime / 1_000_000, StartTimestampMillis: traceData.StartTime / 1_000_000,
EndTimestampMillis: traceData.EndTime / 1_000_000, EndTimestampMillis: traceData.EndTime / 1_000_000,
TotalSpansCount: traceData.TotalSpans, TotalSpansCount: traceData.TotalSpans,
TotalErrorSpansCount: traceData.TotalErrorSpans, TotalErrorSpansCount: traceData.TotalErrorSpans,
RootServiceName: rootServiceName, RootServiceName: rootServiceName,
RootServiceEntryPoint: rootServiceEntryPoint, RootServiceEntryPoint: rootServiceEntryPoint,
HasMissingSpans: traceData.HasMissingSpans, ServiceNameToTotalDurationMap: serviceDurationsMillis,
HasMore: !selectAllSpans, HasMissingSpans: traceData.HasMissingSpans,
Aggregations: aggregations, HasMore: !selectAllSpans,
Aggregations: aggregations,
} }
} }
@@ -295,6 +311,21 @@ func windowAroundIndex(selectedIndex, total int, spanLimitPerRequest float64) (s
return return
} }
func calculateServiceTime(spanIDToSpanNodeMap map[string]*WaterfallSpan) map[string]uint64 {
serviceSpans := make(map[string][]*WaterfallSpan)
for _, span := range spanIDToSpanNodeMap {
if span.ServiceName != "" {
serviceSpans[span.ServiceName] = append(serviceSpans[span.ServiceName], span)
}
}
totalTimes := make(map[string]uint64)
for service, spans := range serviceSpans {
totalTimes[service] = mergeSpanIntervals(spans)
}
return totalTimes
}
// mergeSpanIntervals computes non-overlapping execution time for a set of spans. // mergeSpanIntervals computes non-overlapping execution time for a set of spans.
func mergeSpanIntervals(spans []*WaterfallSpan) uint64 { func mergeSpanIntervals(spans []*WaterfallSpan) uint64 {
if len(spans) == 0 { if len(spans) == 0 {