mirror of
https://github.com/SigNoz/signoz.git
synced 2026-05-21 17:30:32 +01:00
Compare commits
2 Commits
feat/tabs-
...
chore/migr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a69f1fab56 | ||
|
|
5ddcf33811 |
@@ -52,10 +52,8 @@
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
.ant-progress-bg {
|
||||
height: 8px !important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
--progress-height: 8px;
|
||||
--progress-border-radius: 4px;
|
||||
}
|
||||
|
||||
.ant-table-tbody > tr:hover > td {
|
||||
|
||||
@@ -9,13 +9,13 @@ import {
|
||||
Flex,
|
||||
Input,
|
||||
InputRef,
|
||||
Progress,
|
||||
Space,
|
||||
Spin,
|
||||
TableColumnsType,
|
||||
TableColumnType,
|
||||
Tooltip,
|
||||
} from 'antd';
|
||||
import { Progress } from '@signozhq/ui/progress';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import type { FilterDropdownProps } from 'antd/lib/table/interface';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
@@ -60,6 +60,7 @@ function ProgressRender(item: string | number): JSX.Element {
|
||||
percent={percent}
|
||||
strokeLinecap="butt"
|
||||
size="small"
|
||||
showInfo
|
||||
strokeColor={((): string => {
|
||||
const cpuPercent = percent;
|
||||
if (cpuPercent >= 90) {
|
||||
|
||||
@@ -45,6 +45,10 @@
|
||||
.contributors-row {
|
||||
height: 80px;
|
||||
}
|
||||
.top-contributors-progress {
|
||||
--progress-background: transparent;
|
||||
}
|
||||
|
||||
&__content {
|
||||
.ant-table {
|
||||
&-cell {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { HTMLAttributes } from 'react';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Progress, Table, TableColumnsType as ColumnsType } from 'antd';
|
||||
import { Table, TableColumnsType as ColumnsType } from 'antd';
|
||||
import { Progress } from '@signozhq/ui/progress';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { ConditionalAlertPopover } from 'container/AlertHistory/AlertPopover/AlertPopover';
|
||||
import AlertLabels from 'pages/AlertDetails/AlertHeader/AlertLabels/AlertLabels';
|
||||
@@ -51,8 +52,8 @@ function TopContributorsRows({
|
||||
<Progress
|
||||
percent={(count / totalCurrentTriggers) * 100}
|
||||
showInfo={false}
|
||||
trailColor="rgba(255, 255, 255, 0)"
|
||||
strokeColor={Color.BG_ROBIN_500}
|
||||
className="top-contributors-progress"
|
||||
/>
|
||||
</ConditionalAlertPopover>
|
||||
),
|
||||
|
||||
@@ -141,12 +141,9 @@
|
||||
|
||||
.progress-container {
|
||||
width: 158px;
|
||||
.ant-progress {
|
||||
margin: 0;
|
||||
|
||||
.ant-progress-text {
|
||||
font-weight: 600;
|
||||
}
|
||||
span {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useQueries } from 'react-query';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Progress, Skeleton, Tooltip } from 'antd';
|
||||
import { Skeleton, Tooltip } from 'antd';
|
||||
import { Progress } from '@signozhq/ui/progress';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import { ENTITY_VERSION_V5 } from 'constants/app';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
@@ -142,6 +143,7 @@ function DomainMetrics({
|
||||
)}
|
||||
strokeLinecap="butt"
|
||||
size="small"
|
||||
showInfo
|
||||
strokeColor={((): string => {
|
||||
const errorRatePercent = Number(
|
||||
Number(formattedDomainMetricsData.errorRate).toFixed(2),
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useMemo } from 'react';
|
||||
import { UseQueryResult } from 'react-query';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Progress, Skeleton, Tooltip } from 'antd';
|
||||
import { Skeleton, Tooltip } from 'antd';
|
||||
import { Progress } from '@signozhq/ui/progress';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import {
|
||||
getDisplayValue,
|
||||
@@ -84,6 +85,7 @@ function EndPointMetrics({
|
||||
percent={Number(Number(metricsData?.errorRate ?? 0).toFixed(2))}
|
||||
strokeLinecap="butt"
|
||||
size="small"
|
||||
showInfo
|
||||
strokeColor={((): string => {
|
||||
const errorRatePercent = Number(
|
||||
Number(metricsData?.errorRate ?? 0).toFixed(2),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Progress, TableColumnType as ColumnType, Tag, Tooltip } from 'antd';
|
||||
import { TableColumnType as ColumnType, Tag, Tooltip } from 'antd';
|
||||
import { Progress } from '@signozhq/ui/progress';
|
||||
import { convertFiltersToExpressionWithExistingQuery } from 'components/QueryBuilderV2/utils';
|
||||
import {
|
||||
FiltersType,
|
||||
@@ -261,6 +262,7 @@ export const columnsConfig: ColumnType<APIDomainsRowData>[] = [
|
||||
percent={Number((errorRateValue as number).toFixed(2))}
|
||||
strokeLinecap="butt"
|
||||
size="small"
|
||||
showInfo
|
||||
strokeColor={((): string => {
|
||||
const errorRatePercent = Number((errorRateValue as number).toFixed(2));
|
||||
if (errorRatePercent >= 90) {
|
||||
@@ -1030,6 +1032,7 @@ export const getEndPointsColumnsConfig = (
|
||||
)}
|
||||
strokeLinecap="butt"
|
||||
size="small"
|
||||
showInfo
|
||||
strokeColor={((): string => {
|
||||
const errorRatePercent = Number((errorRate as number).toFixed(1));
|
||||
if (errorRatePercent >= 90) {
|
||||
@@ -2518,6 +2521,7 @@ export const dependentServicesColumns: ColumnType<DependentServicesData>[] = [
|
||||
percent={Number((errorPercentage as number).toFixed(2))}
|
||||
strokeLinecap="butt"
|
||||
size="small"
|
||||
showInfo
|
||||
strokeColor={((): string => {
|
||||
const errorPercentagePercent = Number(
|
||||
(errorPercentage as number).toFixed(2),
|
||||
@@ -3030,6 +3034,7 @@ export const getAllEndpointsWidgetData = (
|
||||
)}
|
||||
strokeLinecap="butt"
|
||||
size="small"
|
||||
showInfo
|
||||
strokeColor={((): string => {
|
||||
const errorRatePercent = Number(
|
||||
(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Button, Tooltip } from 'antd';
|
||||
import { Tabs } from '@signozhq/ui/tabs';
|
||||
import { Button, Tabs, Tooltip } from 'antd';
|
||||
import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
|
||||
import { Braces, Globe, Table } from '@signozhq/icons';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
@@ -74,7 +73,7 @@ function DashboardSettings({
|
||||
...(enablePublicDashboard ? [publicDashboardItem] : []),
|
||||
];
|
||||
|
||||
return <Tabs items={items} className="settings-tabs" />;
|
||||
return <Tabs items={items} animated className="settings-tabs" />;
|
||||
}
|
||||
|
||||
export default DashboardSettings;
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { UseQueryResult } from 'react-query';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
|
||||
import { usePanelContextMenu } from '../usePanelContextMenu';
|
||||
|
||||
// The hook composes `useCoordinates` (popover state) and `useGraphContextMenu`
|
||||
// (menu items). We mock both so the test focuses on the `enableDrillDown` gate
|
||||
// rather than the implementation of the menu wiring itself.
|
||||
const onClickMock = jest.fn();
|
||||
jest.mock('periscope/components/ContextMenu', () => ({
|
||||
useCoordinates: (): unknown => ({
|
||||
coordinates: null,
|
||||
popoverPosition: null,
|
||||
clickedData: null,
|
||||
onClose: jest.fn(),
|
||||
subMenu: null,
|
||||
onClick: onClickMock,
|
||||
setSubMenu: jest.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('container/QueryTable/Drilldown/useGraphContextMenu', () => ({
|
||||
__esModule: true,
|
||||
default: (): { menuItemsConfig: { header: string; items: string } } => ({
|
||||
menuItemsConfig: { header: 'menu-header', items: 'menu-items' },
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('container/QueryTable/Drilldown/drilldownUtils', () => ({
|
||||
getUplotClickData: jest.fn(() => ({
|
||||
coord: { x: 1, y: 2 },
|
||||
record: { queryName: 'A', filters: [] },
|
||||
label: 'lbl',
|
||||
seriesColor: '#abc',
|
||||
})),
|
||||
}));
|
||||
|
||||
jest.mock('container/PanelWrapper/utils', () => ({
|
||||
isApmMetric: jest.fn(() => false),
|
||||
getTimeRangeFromStepInterval: jest.fn(() => ({ start: 0, end: 0 })),
|
||||
}));
|
||||
|
||||
const mockWidget = { id: 'w-1', query: {} } as unknown as Widgets;
|
||||
const mockQueryResponse = {
|
||||
data: undefined,
|
||||
isLoading: false,
|
||||
} as unknown as UseQueryResult<
|
||||
SuccessResponse<MetricRangePayloadProps, unknown>,
|
||||
Error
|
||||
>;
|
||||
|
||||
describe('usePanelContextMenu', () => {
|
||||
beforeEach(() => {
|
||||
onClickMock.mockClear();
|
||||
});
|
||||
|
||||
it('returns empty menuItemsConfig when enableDrillDown is false', () => {
|
||||
const { result } = renderHook(() =>
|
||||
usePanelContextMenu({
|
||||
widget: mockWidget,
|
||||
queryResponse: mockQueryResponse,
|
||||
enableDrillDown: false,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.current.menuItemsConfig).toStrictEqual({});
|
||||
});
|
||||
|
||||
it('returns wired menuItemsConfig when enableDrillDown is true', () => {
|
||||
const { result } = renderHook(() =>
|
||||
usePanelContextMenu({
|
||||
widget: mockWidget,
|
||||
queryResponse: mockQueryResponse,
|
||||
enableDrillDown: true,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.current.menuItemsConfig).toStrictEqual({
|
||||
header: 'menu-header',
|
||||
items: 'menu-items',
|
||||
});
|
||||
});
|
||||
|
||||
it('clickHandlerWithContextMenu is a no-op when enableDrillDown is false', () => {
|
||||
const { result } = renderHook(() =>
|
||||
usePanelContextMenu({
|
||||
widget: mockWidget,
|
||||
queryResponse: mockQueryResponse,
|
||||
enableDrillDown: false,
|
||||
}),
|
||||
);
|
||||
|
||||
result.current.clickHandlerWithContextMenu(
|
||||
100, // xValue
|
||||
200, // yValue
|
||||
0, // mouseX
|
||||
0, // mouseY
|
||||
{ serviceName: 'svc' }, // metric
|
||||
{ queryName: 'A', inFocusOrNot: true }, // queryData
|
||||
10, // absoluteMouseX
|
||||
20, // absoluteMouseY
|
||||
{}, // axesData
|
||||
{ seriesIndex: 0, seriesName: 'A', value: 1, color: '#abc' }, // focusedSeries
|
||||
);
|
||||
|
||||
expect(onClickMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('clickHandlerWithContextMenu opens popover when enableDrillDown is true', () => {
|
||||
const { result } = renderHook(() =>
|
||||
usePanelContextMenu({
|
||||
widget: mockWidget,
|
||||
queryResponse: mockQueryResponse,
|
||||
enableDrillDown: true,
|
||||
}),
|
||||
);
|
||||
|
||||
result.current.clickHandlerWithContextMenu(
|
||||
100,
|
||||
200,
|
||||
0,
|
||||
0,
|
||||
{ serviceName: 'svc' },
|
||||
{ queryName: 'A', inFocusOrNot: true },
|
||||
10,
|
||||
20,
|
||||
{},
|
||||
{ seriesIndex: 0, seriesName: 'A', value: 1, color: '#abc' },
|
||||
);
|
||||
|
||||
expect(onClickMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('defaults to disabled when enableDrillDown is not provided', () => {
|
||||
const { result } = renderHook(() =>
|
||||
usePanelContextMenu({
|
||||
widget: mockWidget,
|
||||
queryResponse: mockQueryResponse,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.current.menuItemsConfig).toStrictEqual({});
|
||||
});
|
||||
});
|
||||
@@ -21,11 +21,13 @@ interface UseTimeSeriesContextMenuParams {
|
||||
SuccessResponse<MetricRangePayloadProps, unknown>,
|
||||
Error
|
||||
>;
|
||||
enableDrillDown?: boolean;
|
||||
}
|
||||
|
||||
export const usePanelContextMenu = ({
|
||||
widget,
|
||||
queryResponse,
|
||||
enableDrillDown = false,
|
||||
}: UseTimeSeriesContextMenuParams): {
|
||||
coordinates: { x: number; y: number } | null;
|
||||
popoverPosition: PopoverPosition | null;
|
||||
@@ -61,6 +63,9 @@ export const usePanelContextMenu = ({
|
||||
|
||||
const clickHandlerWithContextMenu = useCallback(
|
||||
(...args: any[]) => {
|
||||
if (!enableDrillDown) {
|
||||
return;
|
||||
}
|
||||
const [
|
||||
xValue,
|
||||
_yvalue,
|
||||
@@ -112,14 +117,14 @@ export const usePanelContextMenu = ({
|
||||
});
|
||||
}
|
||||
},
|
||||
[onClick, queryResponse],
|
||||
[enableDrillDown, onClick, queryResponse],
|
||||
);
|
||||
|
||||
return {
|
||||
coordinates,
|
||||
popoverPosition,
|
||||
onClose,
|
||||
menuItemsConfig,
|
||||
menuItemsConfig: enableDrillDown ? menuItemsConfig : {},
|
||||
clickHandlerWithContextMenu,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -31,6 +31,7 @@ function BarPanel(props: PanelWrapperProps): JSX.Element {
|
||||
isFullViewMode,
|
||||
onToggleModelHandler,
|
||||
groupByPerQuery,
|
||||
enableDrillDown = false,
|
||||
} = props;
|
||||
const uPlotRef = useRef<uPlot | null>(null);
|
||||
const graphRef = useRef<HTMLDivElement>(null);
|
||||
@@ -61,6 +62,7 @@ function BarPanel(props: PanelWrapperProps): JSX.Element {
|
||||
} = usePanelContextMenu({
|
||||
widget,
|
||||
queryResponse,
|
||||
enableDrillDown,
|
||||
});
|
||||
|
||||
const config = useMemo(() => {
|
||||
|
||||
@@ -31,6 +31,7 @@ function TimeSeriesPanel(props: PanelWrapperProps): JSX.Element {
|
||||
isFullViewMode,
|
||||
onToggleModelHandler,
|
||||
groupByPerQuery,
|
||||
enableDrillDown = false,
|
||||
} = props;
|
||||
const graphRef = useRef<HTMLDivElement>(null);
|
||||
const [minTimeScale, setMinTimeScale] = useState<number>();
|
||||
@@ -60,6 +61,7 @@ function TimeSeriesPanel(props: PanelWrapperProps): JSX.Element {
|
||||
} = usePanelContextMenu({
|
||||
widget,
|
||||
queryResponse,
|
||||
enableDrillDown,
|
||||
});
|
||||
|
||||
const chartData = useMemo(() => {
|
||||
|
||||
@@ -292,6 +292,8 @@ function FullView({
|
||||
return <Spinner height="100%" size="large" tip="Loading..." />;
|
||||
}
|
||||
|
||||
const showEditBtn = editWidget && dashboardEditView;
|
||||
|
||||
return (
|
||||
<div className="full-view-container">
|
||||
<OverlayScrollbar>
|
||||
@@ -306,7 +308,7 @@ function FullView({
|
||||
Reset Query
|
||||
</Button>
|
||||
)}
|
||||
{editWidget && (
|
||||
{showEditBtn && (
|
||||
<Button
|
||||
className="switch-edit-btn"
|
||||
disabled={response.isFetching || response.isLoading}
|
||||
|
||||
@@ -39,7 +39,5 @@
|
||||
|
||||
width: 100% !important;
|
||||
|
||||
.ant-progress-steps-outer {
|
||||
width: 100% !important;
|
||||
}
|
||||
--progress-width: 100%;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Progress } from 'antd';
|
||||
import { Progress } from '@signozhq/ui/progress';
|
||||
|
||||
import { ChecklistItem } from '../HomeChecklist/HomeChecklist';
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Progress, Tag } from 'antd';
|
||||
import { Tag } from 'antd';
|
||||
import { Progress } from '@signozhq/ui/progress';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import {
|
||||
getHostLists,
|
||||
@@ -81,6 +82,7 @@ export const hostDetailsMetadataConfig: K8sDetailsMetadataConfig<HostData>[] = [
|
||||
percent={Number(Number(value).toFixed(1))}
|
||||
size="small"
|
||||
strokeColor={getProgressColor(Number(value))}
|
||||
showInfo
|
||||
/>
|
||||
),
|
||||
},
|
||||
@@ -92,6 +94,7 @@ export const hostDetailsMetadataConfig: K8sDetailsMetadataConfig<HostData>[] = [
|
||||
percent={Number(Number(value).toFixed(1))}
|
||||
size="small"
|
||||
strokeColor={getMemoryProgressColor(Number(value))}
|
||||
showInfo
|
||||
/>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -61,10 +61,8 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:global(.ant-progress-bg) {
|
||||
height: 8px !important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
--progress-height: 8px;
|
||||
--progress-border-radius: 4px;
|
||||
}
|
||||
|
||||
.progressBar {
|
||||
|
||||
@@ -103,12 +103,8 @@
|
||||
.progress-container {
|
||||
width: 158px;
|
||||
|
||||
.ant-progress {
|
||||
margin: 0;
|
||||
|
||||
.ant-progress-text {
|
||||
font-weight: 600;
|
||||
}
|
||||
span {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,10 +288,8 @@
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
.ant-progress-bg {
|
||||
height: 8px !important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
--progress-height: 8px;
|
||||
--progress-border-radius: 4px;
|
||||
}
|
||||
|
||||
.ant-table-tbody > tr:hover > td {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Progress } from 'antd';
|
||||
import { Progress } from '@signozhq/ui/progress';
|
||||
import TanStackTable from 'components/TanStackTableView';
|
||||
import {
|
||||
getMemoryProgressColor,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Button } from 'antd';
|
||||
import { Tabs, TabItemProps } from '@signozhq/ui/tabs';
|
||||
import { Button, Tabs, TabsProps } from 'antd';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import ConfigureIcon from 'assets/Integrations/ConfigureIcon';
|
||||
import { CableCar, Group } from '@signozhq/icons';
|
||||
@@ -23,7 +22,7 @@ function IntegrationDetailContent(
|
||||
): JSX.Element {
|
||||
const { activeDetailTab, integrationData, integrationId, setActiveDetailTab } =
|
||||
props;
|
||||
const items: TabItemProps[] = [
|
||||
const items: TabsProps['items'] = [
|
||||
{
|
||||
key: 'overview',
|
||||
label: (
|
||||
@@ -82,7 +81,11 @@ function IntegrationDetailContent(
|
||||
];
|
||||
return (
|
||||
<div className="integration-detail-container">
|
||||
<Tabs value={activeDetailTab} items={items} onChange={setActiveDetailTab} />
|
||||
<Tabs
|
||||
activeKey={activeDetailTab}
|
||||
items={items}
|
||||
onChange={setActiveDetailTab}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,12 @@ import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { FeatureKeys } from '../../../constants/features';
|
||||
import { useAppContext } from '../../../providers/App/App';
|
||||
import { GraphTitle, MENU_ITEMS, SERVICE_CHART_ID } from '../constant';
|
||||
import {
|
||||
GraphTitle,
|
||||
MENU_ITEMS,
|
||||
SERVICE_CHART_ID,
|
||||
SERVICE_DETAIL_DRILLDOWN_ENABLED,
|
||||
} from '../constant';
|
||||
import { getWidgetQueryBuilder } from '../MetricsApplication.factory';
|
||||
import { Card, GraphContainer, Row } from '../styles';
|
||||
import { Button } from './styles';
|
||||
@@ -206,6 +211,7 @@ function DBCall(): JSX.Element {
|
||||
}}
|
||||
onDragSelect={onDragSelect}
|
||||
version={ENTITY_VERSION_V4}
|
||||
enableDrillDown={SERVICE_DETAIL_DRILLDOWN_ENABLED}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
@@ -244,6 +250,7 @@ function DBCall(): JSX.Element {
|
||||
}}
|
||||
onDragSelect={onDragSelect}
|
||||
version={ENTITY_VERSION_V4}
|
||||
enableDrillDown={SERVICE_DETAIL_DRILLDOWN_ENABLED}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
|
||||
@@ -32,7 +32,12 @@ import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { FeatureKeys } from '../../../constants/features';
|
||||
import { useAppContext } from '../../../providers/App/App';
|
||||
import { GraphTitle, legend, MENU_ITEMS } from '../constant';
|
||||
import {
|
||||
GraphTitle,
|
||||
legend,
|
||||
MENU_ITEMS,
|
||||
SERVICE_DETAIL_DRILLDOWN_ENABLED,
|
||||
} from '../constant';
|
||||
import { getWidgetQueryBuilder } from '../MetricsApplication.factory';
|
||||
import { Card, GraphContainer, Row } from '../styles';
|
||||
import GraphControlsPanel from './Overview/GraphControlsPanel/GraphControlsPanel';
|
||||
@@ -279,6 +284,7 @@ function External(): JSX.Element {
|
||||
}}
|
||||
onDragSelect={onDragSelect}
|
||||
version={ENTITY_VERSION_V4}
|
||||
enableDrillDown={SERVICE_DETAIL_DRILLDOWN_ENABLED}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
@@ -322,6 +328,7 @@ function External(): JSX.Element {
|
||||
}}
|
||||
onDragSelect={onDragSelect}
|
||||
version={ENTITY_VERSION_V4}
|
||||
enableDrillDown={SERVICE_DETAIL_DRILLDOWN_ENABLED}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
@@ -366,6 +373,7 @@ function External(): JSX.Element {
|
||||
}
|
||||
onDragSelect={onDragSelect}
|
||||
version={ENTITY_VERSION_V4}
|
||||
enableDrillDown={SERVICE_DETAIL_DRILLDOWN_ENABLED}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
@@ -409,6 +417,7 @@ function External(): JSX.Element {
|
||||
}}
|
||||
onDragSelect={onDragSelect}
|
||||
version={ENTITY_VERSION_V4}
|
||||
enableDrillDown={SERVICE_DETAIL_DRILLDOWN_ENABLED}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
|
||||
@@ -15,6 +15,7 @@ import DisplayThreshold from 'container/GridCardLayout/WidgetHeader/DisplayThres
|
||||
import {
|
||||
GraphTitle,
|
||||
SERVICE_CHART_ID,
|
||||
SERVICE_DETAIL_DRILLDOWN_ENABLED,
|
||||
} from 'container/MetricsApplication/constant';
|
||||
import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsApplication.factory';
|
||||
import { apDexMetricsQueryBuilderQueries } from 'container/MetricsApplication/MetricsPageQueries/OverviewQueries';
|
||||
@@ -105,6 +106,7 @@ function ApDexMetrics({
|
||||
threshold={threshold}
|
||||
isQueryEnabled={isQueryEnabled}
|
||||
version={ENTITY_VERSION_V4}
|
||||
enableDrillDown={SERVICE_DETAIL_DRILLDOWN_ENABLED}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import Graph from 'container/GridCardLayout/GridCard';
|
||||
import {
|
||||
GraphTitle,
|
||||
SERVICE_CHART_ID,
|
||||
SERVICE_DETAIL_DRILLDOWN_ENABLED,
|
||||
} from 'container/MetricsApplication/constant';
|
||||
import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsApplication.factory';
|
||||
import { latency } from 'container/MetricsApplication/MetricsPageQueries/OverviewQueries';
|
||||
@@ -138,6 +139,7 @@ function ServiceOverview({
|
||||
onClickHandler={handleGraphClick('Service')}
|
||||
isQueryEnabled={isQueryEnabled}
|
||||
version={ENTITY_VERSION_V4}
|
||||
enableDrillDown={SERVICE_DETAIL_DRILLDOWN_ENABLED}
|
||||
/>
|
||||
)}
|
||||
</GraphContainer>
|
||||
|
||||
@@ -4,6 +4,7 @@ import axios from 'axios';
|
||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
import { ENTITY_VERSION_V4 } from 'constants/app';
|
||||
import Graph from 'container/GridCardLayout/GridCard';
|
||||
import { SERVICE_DETAIL_DRILLDOWN_ENABLED } from 'container/MetricsApplication/constant';
|
||||
import { Card, GraphContainer } from 'container/MetricsApplication/styles';
|
||||
import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
@@ -43,6 +44,7 @@ function TopLevelOperation({
|
||||
onDragSelect={onDragSelect}
|
||||
isQueryEnabled={!topLevelOperationsIsLoading}
|
||||
version={ENTITY_VERSION_V4}
|
||||
enableDrillDown={SERVICE_DETAIL_DRILLDOWN_ENABLED}
|
||||
/>
|
||||
)}
|
||||
</GraphContainer>
|
||||
|
||||
@@ -25,6 +25,8 @@ export const OPERATION_LEGENDS = ['Operations'];
|
||||
|
||||
export const MENU_ITEMS = [MenuItemKeys.View, MenuItemKeys.CreateAlerts];
|
||||
|
||||
export const SERVICE_DETAIL_DRILLDOWN_ENABLED = true;
|
||||
|
||||
export enum FORMULA {
|
||||
ERROR_PERCENTAGE = 'A*100/B',
|
||||
DATABASE_CALLS_AVG_DURATION = 'A/B',
|
||||
|
||||
@@ -143,10 +143,8 @@
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
.ant-progress-bg {
|
||||
height: 8px !important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
--progress-height: 8px;
|
||||
--progress-border-radius: 4px;
|
||||
}
|
||||
|
||||
.ant-table-tbody > tr:hover > td {
|
||||
|
||||
@@ -87,12 +87,7 @@
|
||||
|
||||
.service-progress-indicator {
|
||||
width: fit-content;
|
||||
margin-inline-end: 0px !important;
|
||||
margin-bottom: 0px !important;
|
||||
|
||||
.ant-progress-inner {
|
||||
width: 30px;
|
||||
}
|
||||
--progress-width: 30px;
|
||||
}
|
||||
|
||||
.percent-value {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { Progress, Skeleton, Tooltip } from 'antd';
|
||||
import { Skeleton, Tooltip } from 'antd';
|
||||
import { Progress } from '@signozhq/ui/progress';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import { AxiosError } from 'axios';
|
||||
import Spinner from 'components/Spinner';
|
||||
|
||||
@@ -169,9 +169,10 @@ describe('drilldownUtils', () => {
|
||||
|
||||
// Verify transformations were applied
|
||||
if (filterExpression) {
|
||||
// Rule 2: operation → name
|
||||
expect(filterExpression).toContain(`name = 'GET'`);
|
||||
// `operation` rewrites to `name` via source-side pass, then `name`
|
||||
// is dropped by the logs target-side pass (logs has no span-name).
|
||||
expect(filterExpression).not.toContain(`operation = 'GET'`);
|
||||
expect(filterExpression).not.toContain(`name = 'GET'`);
|
||||
|
||||
// Rule 3: span.kind → kind
|
||||
expect(filterExpression).toContain(`${spanKindKey} = '${spanKindServer}'`);
|
||||
@@ -262,8 +263,9 @@ describe('drilldownUtils', () => {
|
||||
const filterExpression = result?.builder.queryData[0]?.filter?.expression;
|
||||
|
||||
if (filterExpression) {
|
||||
// All transformations should be applied
|
||||
expect(filterExpression).toContain(`name = 'POST'`);
|
||||
// `operation` rewrites to `name` then drops for logs target.
|
||||
expect(filterExpression).not.toContain(`operation = 'POST'`);
|
||||
expect(filterExpression).not.toContain(`name = 'POST'`);
|
||||
expect(filterExpression).toContain(`${spanKindKey} = '${spanKindClient}'`);
|
||||
expect(filterExpression).toContain(`status_code_string = 'Error'`);
|
||||
expect(filterExpression).toContain(`http.status_code = 500`);
|
||||
@@ -410,8 +412,9 @@ describe('drilldownUtils', () => {
|
||||
const filterExpression = result?.builder.queryData[0]?.filter?.expression;
|
||||
|
||||
if (filterExpression) {
|
||||
// Transformed attributes
|
||||
expect(filterExpression).toContain(`name = 'GET'`);
|
||||
// `operation` rewrites to `name` then drops for logs target.
|
||||
expect(filterExpression).not.toContain(`operation = 'GET'`);
|
||||
expect(filterExpression).not.toContain(`name = 'GET'`);
|
||||
expect(filterExpression).toContain(`${spanKindKey} = '${spanKindServer}'`);
|
||||
|
||||
// Preserved non-metric attributes
|
||||
@@ -499,4 +502,189 @@ describe('drilldownUtils', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getViewQuery target-aware sanitisation (serviceName / name)', () => {
|
||||
const makeQuery = (
|
||||
expression: string,
|
||||
dataSource: 'traces' | 'logs' | 'metrics' = 'traces',
|
||||
): Query => ({
|
||||
id: 'src-query',
|
||||
queryType: 'builder' as any,
|
||||
builder: {
|
||||
queryData: [
|
||||
{
|
||||
queryName: 'src',
|
||||
dataSource: dataSource as any,
|
||||
aggregations: [{ metricName: 'non_apm_metric' }] as any,
|
||||
groupBy: [],
|
||||
expression: '',
|
||||
disabled: false,
|
||||
functions: [],
|
||||
legend: '',
|
||||
having: [],
|
||||
limit: null,
|
||||
stepInterval: undefined,
|
||||
orderBy: [],
|
||||
filter: { expression },
|
||||
},
|
||||
],
|
||||
queryFormulas: [],
|
||||
queryTraceOperator: [],
|
||||
},
|
||||
promql: [],
|
||||
clickhouse_sql: [],
|
||||
});
|
||||
|
||||
it('rewrites serviceName -> service.name when drilling to logs', () => {
|
||||
const result = getViewQuery(
|
||||
makeQuery(`serviceName = 'svc'`),
|
||||
[],
|
||||
'view_logs',
|
||||
'src',
|
||||
);
|
||||
const expr = result?.builder.queryData[0]?.filter?.expression || '';
|
||||
expect(expr).toContain(`service.name = 'svc'`);
|
||||
expect(expr).not.toContain('serviceName');
|
||||
});
|
||||
|
||||
it('rewrites serviceName -> service.name when drilling to traces', () => {
|
||||
const result = getViewQuery(
|
||||
makeQuery(`serviceName = 'svc'`),
|
||||
[],
|
||||
'view_traces',
|
||||
'src',
|
||||
);
|
||||
const expr = result?.builder.queryData[0]?.filter?.expression || '';
|
||||
expect(expr).toContain(`service.name = 'svc'`);
|
||||
expect(expr).not.toContain('serviceName');
|
||||
});
|
||||
|
||||
it('drops `name` clause when drilling to logs', () => {
|
||||
const result = getViewQuery(
|
||||
makeQuery(`name = 'GET /api'`),
|
||||
[],
|
||||
'view_logs',
|
||||
'src',
|
||||
);
|
||||
const expr = result?.builder.queryData[0]?.filter?.expression || '';
|
||||
expect(expr).not.toContain(`name = 'GET /api'`);
|
||||
});
|
||||
|
||||
it('keeps `name` clause when drilling to traces', () => {
|
||||
const result = getViewQuery(
|
||||
makeQuery(`name = 'GET /api'`),
|
||||
[],
|
||||
'view_traces',
|
||||
'src',
|
||||
);
|
||||
const expr = result?.builder.queryData[0]?.filter?.expression || '';
|
||||
expect(expr).toContain(`name = 'GET /api'`);
|
||||
});
|
||||
|
||||
it('combined: drilling to logs rewrites serviceName and drops name', () => {
|
||||
const result = getViewQuery(
|
||||
makeQuery(`serviceName = 'svc' AND name = 'GET /api'`),
|
||||
[],
|
||||
'view_logs',
|
||||
'src',
|
||||
);
|
||||
const expr = result?.builder.queryData[0]?.filter?.expression || '';
|
||||
expect(expr).toContain(`service.name = 'svc'`);
|
||||
expect(expr).not.toContain('serviceName');
|
||||
expect(expr).not.toContain(`name = 'GET /api'`);
|
||||
});
|
||||
|
||||
it('combined: drilling to traces rewrites serviceName and keeps name', () => {
|
||||
const result = getViewQuery(
|
||||
makeQuery(`serviceName = 'svc' AND name = 'GET /api'`),
|
||||
[],
|
||||
'view_traces',
|
||||
'src',
|
||||
);
|
||||
const expr = result?.builder.queryData[0]?.filter?.expression || '';
|
||||
expect(expr).toContain(`service.name = 'svc'`);
|
||||
expect(expr).toContain(`name = 'GET /api'`);
|
||||
expect(expr).not.toContain('serviceName');
|
||||
});
|
||||
|
||||
it('metric-APM source -> traces target preserves existing operation -> name rewrite', () => {
|
||||
const metricsQuery: Query = {
|
||||
id: 'apm-metrics',
|
||||
queryType: 'builder' as any,
|
||||
builder: {
|
||||
queryData: [
|
||||
{
|
||||
queryName: 'm',
|
||||
dataSource: 'metrics' as any,
|
||||
aggregations: [{ metricName: 'signoz_calls_total' }] as any,
|
||||
groupBy: [],
|
||||
expression: '',
|
||||
disabled: false,
|
||||
functions: [],
|
||||
legend: '',
|
||||
having: [],
|
||||
limit: null,
|
||||
stepInterval: undefined,
|
||||
orderBy: [],
|
||||
filter: { expression: `operation = 'GET'` },
|
||||
},
|
||||
],
|
||||
queryFormulas: [],
|
||||
queryTraceOperator: [],
|
||||
},
|
||||
promql: [],
|
||||
clickhouse_sql: [],
|
||||
};
|
||||
const result = getViewQuery(metricsQuery, [], 'view_traces', 'm');
|
||||
const expr = result?.builder.queryData[0]?.filter?.expression || '';
|
||||
expect(expr).toContain(`name = 'GET'`);
|
||||
expect(expr).not.toContain(`operation = 'GET'`);
|
||||
});
|
||||
|
||||
it('metric-APM source -> logs target: operation rewrites to name, then dropped', () => {
|
||||
const metricsQuery: Query = {
|
||||
id: 'apm-metrics',
|
||||
queryType: 'builder' as any,
|
||||
builder: {
|
||||
queryData: [
|
||||
{
|
||||
queryName: 'm',
|
||||
dataSource: 'metrics' as any,
|
||||
aggregations: [{ metricName: 'signoz_calls_total' }] as any,
|
||||
groupBy: [],
|
||||
expression: '',
|
||||
disabled: false,
|
||||
functions: [],
|
||||
legend: '',
|
||||
having: [],
|
||||
limit: null,
|
||||
stepInterval: undefined,
|
||||
orderBy: [],
|
||||
filter: { expression: `operation = 'GET'` },
|
||||
},
|
||||
],
|
||||
queryFormulas: [],
|
||||
queryTraceOperator: [],
|
||||
},
|
||||
promql: [],
|
||||
clickhouse_sql: [],
|
||||
};
|
||||
const result = getViewQuery(metricsQuery, [], 'view_logs', 'm');
|
||||
const expr = result?.builder.queryData[0]?.filter?.expression || '';
|
||||
expect(expr).not.toContain(`operation = 'GET'`);
|
||||
expect(expr).not.toContain(`name = 'GET'`);
|
||||
});
|
||||
|
||||
it('drilling to metrics does not apply target-side sanitisation', () => {
|
||||
const result = getViewQuery(
|
||||
makeQuery(`serviceName = 'svc' AND name = 'GET /api'`),
|
||||
[],
|
||||
'view_metrics',
|
||||
'src',
|
||||
);
|
||||
const expr = result?.builder.queryData[0]?.filter?.expression || '';
|
||||
expect(expr).toContain('serviceName');
|
||||
expect(expr).toContain(`name = 'GET /api'`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,8 +7,10 @@ import {
|
||||
import ROUTES from 'constants/routes';
|
||||
import { isApmMetric } from 'container/PanelWrapper/utils';
|
||||
import {
|
||||
applyMappingsToExpression,
|
||||
DRILLDOWN_TO_LOGS_MAPPINGS,
|
||||
DRILLDOWN_TO_TRACES_MAPPINGS,
|
||||
METRIC_TO_LOGS_TRACES_MAPPINGS,
|
||||
replaceKeysAndValuesInExpression,
|
||||
} from 'container/QueryTable/Drilldown/metricsCorrelationUtils';
|
||||
import cloneDeep from 'lodash-es/cloneDeep';
|
||||
import {
|
||||
@@ -347,27 +349,41 @@ export const getViewQuery = (
|
||||
newQuery.builder.queryData[0].filter = newFilterExpression;
|
||||
|
||||
try {
|
||||
// ===========================================
|
||||
// TEMP LOGIC - TO BE REMOVED LATER
|
||||
// ===========================================
|
||||
// Apply metric-to-logs/traces transformations
|
||||
// Drill-down filter sanitisation. Two stages:
|
||||
// 1. Source-side: rewrite metric-APM-specific keys (operation, span.kind,
|
||||
// status.code) so they map onto trace/log columns.
|
||||
// 2. Target-side: normalise legacy keys to OTel-canonical (`serviceName`
|
||||
// -> `service.name`) and drop keys with no equivalent in the target
|
||||
// datasource (e.g. `name` for logs).
|
||||
let expression = newFilterExpression?.expression || '';
|
||||
|
||||
const specificQuery = getQueryData(query, queryName);
|
||||
const isMetricQuery = specificQuery?.dataSource === 'metrics';
|
||||
const metricName = (specificQuery?.aggregations?.[0] as MetricAggregation)
|
||||
?.metricName;
|
||||
|
||||
if (isMetricQuery && isApmMetric(metricName || '')) {
|
||||
const transformedExpression = replaceKeysAndValuesInExpression(
|
||||
newFilterExpression?.expression || '',
|
||||
expression = applyMappingsToExpression(
|
||||
expression,
|
||||
METRIC_TO_LOGS_TRACES_MAPPINGS,
|
||||
);
|
||||
newQuery.builder.queryData[0].filter = {
|
||||
expression: transformedExpression || '',
|
||||
};
|
||||
}
|
||||
// ===========================================
|
||||
|
||||
if (key === 'view_logs') {
|
||||
expression = applyMappingsToExpression(
|
||||
expression,
|
||||
DRILLDOWN_TO_LOGS_MAPPINGS,
|
||||
);
|
||||
} else if (key === 'view_traces') {
|
||||
expression = applyMappingsToExpression(
|
||||
expression,
|
||||
DRILLDOWN_TO_TRACES_MAPPINGS,
|
||||
);
|
||||
}
|
||||
|
||||
newQuery.builder.queryData[0].filter = { expression };
|
||||
} catch (error) {
|
||||
console.error('Error transforming metrics to logs/traces:', error);
|
||||
console.error('Error sanitising drilldown filter expression:', error);
|
||||
}
|
||||
|
||||
return newQuery;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
/* eslint-disable sonarjs/cognitive-complexity */
|
||||
import { formatValueForExpression } from 'components/QueryBuilderV2/utils';
|
||||
import {
|
||||
formatValueForExpression,
|
||||
removeKeysFromExpression,
|
||||
} from 'components/QueryBuilderV2/utils';
|
||||
import { getOperatorValue } from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
||||
import { IQueryPair } from 'types/antlrQueryTypes';
|
||||
import { extractQueryPairs } from 'utils/queryContextUtils';
|
||||
@@ -8,7 +11,7 @@ import { isFunctionOperator, isNonValueOperator } from 'utils/tokenUtils';
|
||||
|
||||
type KeyValueMapping = {
|
||||
attribute: string;
|
||||
newAttribute: string;
|
||||
newAttribute: string | null;
|
||||
valueMappings: Record<string, string>;
|
||||
};
|
||||
|
||||
@@ -40,8 +43,33 @@ export const METRIC_TO_LOGS_TRACES_MAPPINGS: KeyValueMapping[] = [
|
||||
},
|
||||
];
|
||||
|
||||
export const DRILLDOWN_TO_LOGS_MAPPINGS: KeyValueMapping[] = [
|
||||
{
|
||||
attribute: 'serviceName',
|
||||
newAttribute: 'service.name',
|
||||
valueMappings: {},
|
||||
},
|
||||
{
|
||||
attribute: 'name',
|
||||
newAttribute: null,
|
||||
valueMappings: {},
|
||||
},
|
||||
];
|
||||
|
||||
export const DRILLDOWN_TO_TRACES_MAPPINGS: KeyValueMapping[] = [
|
||||
{
|
||||
attribute: 'serviceName',
|
||||
newAttribute: 'service.name',
|
||||
valueMappings: {},
|
||||
},
|
||||
];
|
||||
|
||||
// Logic for rewriting key/values in an expression using provided mappings.
|
||||
function modifyKeyVal(pair: IQueryPair, mapping: KeyValueMapping): string {
|
||||
// Callers must pre-filter mappings to ensure newAttribute is non-null.
|
||||
function modifyKeyVal(
|
||||
pair: IQueryPair,
|
||||
mapping: KeyValueMapping & { newAttribute: string },
|
||||
): string {
|
||||
const newKey = mapping.newAttribute;
|
||||
const op = pair.operator;
|
||||
|
||||
@@ -107,8 +135,18 @@ export function replaceKeysAndValuesInExpression(
|
||||
return expression;
|
||||
}
|
||||
|
||||
const attributeToMapping = new Map<string, KeyValueMapping>(
|
||||
mappingList.map((m) => [m.attribute.trim().toLowerCase(), m]),
|
||||
// Only rewrite mappings (newAttribute non-null) are processed here.
|
||||
// Drops are handled separately by applyMappingsToExpression via removeKeysFromExpression.
|
||||
const attributeToMapping = new Map<
|
||||
string,
|
||||
KeyValueMapping & { newAttribute: string }
|
||||
>(
|
||||
mappingList
|
||||
.filter(
|
||||
(m): m is KeyValueMapping & { newAttribute: string } =>
|
||||
m.newAttribute !== null,
|
||||
)
|
||||
.map((m) => [m.attribute.trim().toLowerCase(), m]),
|
||||
);
|
||||
|
||||
const pairs: IQueryPair[] = extractQueryPairs(expression);
|
||||
@@ -179,3 +217,26 @@ export function replaceKeysAndValuesInExpression(
|
||||
|
||||
return resultParts.join('');
|
||||
}
|
||||
|
||||
// Apply a list of mappings to a filter expression. Rewrites are applied first
|
||||
// (newAttribute is a string), then drops (newAttribute is null) via the
|
||||
// ANTLR-parser-based removeKeysFromExpression which handles AND/OR/NOT/paren
|
||||
// elision correctly.
|
||||
export function applyMappingsToExpression(
|
||||
expression: string,
|
||||
mappings: KeyValueMapping[],
|
||||
): string {
|
||||
if (!expression || !mappings || mappings.length === 0) {
|
||||
return expression;
|
||||
}
|
||||
|
||||
const dropKeys = mappings
|
||||
.filter((m) => m.newAttribute === null)
|
||||
.map((m) => m.attribute);
|
||||
|
||||
let result = replaceKeysAndValuesInExpression(expression, mappings);
|
||||
if (dropKeys.length > 0) {
|
||||
result = removeKeysFromExpression(result, dropKeys);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { Tabs, TabItemProps } from '@signozhq/ui/tabs';
|
||||
import { Tabs, TabsProps } from 'antd';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import DBCall from 'container/MetricsApplication/Tabs/DBCall';
|
||||
import External from 'container/MetricsApplication/Tabs/External';
|
||||
@@ -24,7 +24,7 @@ function MetricsApplication(): JSX.Element {
|
||||
const urlQuery = useUrlQuery();
|
||||
const { safeNavigate } = useSafeNavigate();
|
||||
|
||||
const items: TabItemProps[] = [
|
||||
const items: TabsProps['items'] = [
|
||||
{
|
||||
label: TAB_KEY_VS_LABEL[MetricsApplicationTab.OVER_METRICS],
|
||||
key: MetricsApplicationTab.OVER_METRICS,
|
||||
@@ -53,8 +53,9 @@ function MetricsApplication(): JSX.Element {
|
||||
<ApDexApplication />
|
||||
<Tabs
|
||||
items={items}
|
||||
value={activeKey}
|
||||
activeKey={activeKey}
|
||||
className="service-route-tab"
|
||||
destroyInactiveTabPane
|
||||
onChange={onTabChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,8 @@ import { useEffect, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useQuery } from 'react-query';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { Tabs, TabItemProps } from '@signozhq/ui/tabs';
|
||||
import type { TabsProps } from 'antd';
|
||||
import { Tabs } from 'antd';
|
||||
import getPipeline from 'api/pipeline/get';
|
||||
import Spinner from 'components/Spinner';
|
||||
import ChangeHistory from 'container/PipelinePage/Layouts/ChangeHistory';
|
||||
@@ -45,7 +46,7 @@ function Pipelines(): JSX.Element {
|
||||
refetchInterval: pipelineRefetchInterval,
|
||||
});
|
||||
|
||||
const tabItems: TabItemProps[] = useMemo(
|
||||
const tabItems: TabsProps['items'] = useMemo(
|
||||
() => [
|
||||
{
|
||||
key: 'pipelines',
|
||||
@@ -82,7 +83,11 @@ function Pipelines(): JSX.Element {
|
||||
|
||||
return (
|
||||
<Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}>
|
||||
<Tabs className="pipeline-tabs" defaultValue="pipelines" items={tabItems} />
|
||||
<Tabs
|
||||
className="pipeline-tabs"
|
||||
defaultActiveKey="pipelines"
|
||||
items={tabItems}
|
||||
/>
|
||||
</Sentry.ErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,8 +5,7 @@ import {
|
||||
ResizablePanel,
|
||||
ResizablePanelGroup,
|
||||
} from '@signozhq/resizable';
|
||||
import { Button } from 'antd';
|
||||
import { Tabs } from '@signozhq/ui/tabs';
|
||||
import { Button, Tabs } from 'antd';
|
||||
import FlamegraphImg from 'assets/TraceDetail/Flamegraph';
|
||||
import cx from 'classnames';
|
||||
import TraceFlamegraph from 'container/PaginatedTraceFlamegraph/PaginatedTraceFlamegraph';
|
||||
@@ -147,7 +146,7 @@ function TraceDetailsV2(): JSX.Element {
|
||||
notFound={noData}
|
||||
/>
|
||||
{!noData ? (
|
||||
<Tabs items={items} className="trace-visualisation-tabs" />
|
||||
<Tabs items={items} animated className="trace-visualisation-tabs" />
|
||||
) : (
|
||||
<NoData />
|
||||
)}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useMutation } from 'react-query';
|
||||
import type { TabItemProps } from '@signozhq/ui/tabs';
|
||||
import type { TabsProps } from 'antd';
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
@@ -14,8 +14,8 @@ import {
|
||||
Row,
|
||||
Skeleton,
|
||||
Space,
|
||||
Tabs,
|
||||
} from 'antd';
|
||||
import { Tabs } from '@signozhq/ui/tabs';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import updateCreditCardApi from 'api/v1/checkout/create';
|
||||
@@ -154,7 +154,7 @@ export default function WorkspaceBlocked(): JSX.Element {
|
||||
/>
|
||||
));
|
||||
|
||||
const tabItems: TabItemProps[] = [
|
||||
const tabItems: TabsProps['items'] = [
|
||||
{
|
||||
key: 'whyChooseSignoz',
|
||||
label: t('whyChooseSignoz'),
|
||||
@@ -398,8 +398,8 @@ export default function WorkspaceBlocked(): JSX.Element {
|
||||
<div className="workspace-locked__tabs">
|
||||
<Tabs
|
||||
items={tabItems}
|
||||
defaultValue="youAreInGoodCompany"
|
||||
onChange={handleTabClick}
|
||||
defaultActiveKey="youAreInGoodCompany"
|
||||
onTabClick={handleTabClick}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user