Compare commits

..

1 Commits

Author SHA1 Message Date
Yunus M
0f0cceb927 refactor: replace antd Tabs with @signozhq/ui Tabs
Migrates Tabs usage from antd to the @signozhq/ui Tabs component across
dashboard settings, integration details, metrics application, pipelines,
trace detail, and workspace-locked pages. API differences (activeKey →
value, defaultActiveKey → defaultValue, TabsProps['items'] → TabItemProps[])
are updated to match the new component.
2026-05-22 14:33:12 +05:30
112 changed files with 19929 additions and 1965 deletions

7
.github/CODEOWNERS vendored
View File

@@ -118,9 +118,6 @@ go.mod @therealpandey
/tests/integration/ @therealpandey
# e2e tests
/tests/e2e/ @AshwinBhatkal
# Flagger Owners
/pkg/flagger/ @therealpandey
@@ -165,7 +162,3 @@ go.mod @therealpandey
/frontend/src/lib/dashboard/ @SigNoz/pulse-frontend
/frontend/src/lib/dashboardVariables/ @SigNoz/pulse-frontend
/frontend/src/components/NewSelect/ @SigNoz/pulse-frontend
## Dashboard V2
/frontend/src/pages/DashboardPageV2/ @SigNoz/pulse-frontend
/frontend/src/pages/DashboardsListPageV2/ @SigNoz/pulse-frontend

View File

@@ -18,7 +18,6 @@ const BANNED_COMPONENTS = {
'Use @signozhq/ui/typography Typography instead of antd Typography.',
Switch: 'Use @signozhq/ui/switch Switch instead of antd Switch.',
Badge: 'Use @signozhq/ui/badge instead of antd Badge.',
Progress: 'Use @signozhq/ui/progress instead of antd Progress.',
};
export default {

View File

@@ -51,6 +51,13 @@
background: var(--l1-background);
}
.progress-container {
.ant-progress-bg {
height: 8px !important;
border-radius: 4px;
}
}
.ant-table-tbody > tr:hover > td {
background: color-mix(in srgb, var(--l1-foreground) 4%, transparent);
}

View File

@@ -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';
@@ -59,7 +59,7 @@ function ProgressRender(item: string | number): JSX.Element {
<Progress
percent={percent}
strokeLinecap="butt"
showInfo
size="small"
strokeColor={((): string => {
const cpuPercent = percent;
if (cpuPercent >= 90) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -45,10 +45,6 @@
.contributors-row {
height: 80px;
}
.top-contributors-progress {
--progress-background: transparent;
}
&__content {
.ant-table {
&-cell {

View File

@@ -1,7 +1,6 @@
import { HTMLAttributes } from 'react';
import { Color } from '@signozhq/design-tokens';
import { Table, TableColumnsType as ColumnsType } from 'antd';
import { Progress } from '@signozhq/ui/progress';
import { Progress, Table, TableColumnsType as ColumnsType } from 'antd';
import logEvent from 'api/common/logEvent';
import { ConditionalAlertPopover } from 'container/AlertHistory/AlertPopover/AlertPopover';
import AlertLabels from 'pages/AlertDetails/AlertHeader/AlertLabels/AlertLabels';
@@ -52,8 +51,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>
),

View File

@@ -141,9 +141,12 @@
.progress-container {
width: 158px;
.ant-progress {
margin: 0;
span {
font-weight: 600;
.ant-progress-text {
font-weight: 600;
}
}
}

View File

@@ -1,8 +1,7 @@
import { useMemo } from 'react';
import { useQueries } from 'react-query';
import { Color } from '@signozhq/design-tokens';
import { Skeleton, Tooltip } from 'antd';
import { Progress } from '@signozhq/ui/progress';
import { Progress, Skeleton, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { ENTITY_VERSION_V5 } from 'constants/app';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
@@ -137,11 +136,12 @@ function DomainMetrics({
<Tooltip title={formattedDomainMetricsData.errorRate}>
{formattedDomainMetricsData.errorRate !== '-' ? (
<Progress
status="active"
percent={Number(
Number(formattedDomainMetricsData.errorRate).toFixed(2),
)}
strokeLinecap="butt"
showInfo
size="small"
strokeColor={((): string => {
const errorRatePercent = Number(
Number(formattedDomainMetricsData.errorRate).toFixed(2),

View File

@@ -1,8 +1,7 @@
import { useMemo } from 'react';
import { UseQueryResult } from 'react-query';
import { Color } from '@signozhq/design-tokens';
import { Skeleton, Tooltip } from 'antd';
import { Progress } from '@signozhq/ui/progress';
import { Progress, Skeleton, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import {
getDisplayValue,
@@ -81,9 +80,10 @@ function EndPointMetrics({
<Tooltip title={metricsData?.errorRate}>
{metricsData?.errorRate !== '-' ? (
<Progress
status="active"
percent={Number(Number(metricsData?.errorRate ?? 0).toFixed(2))}
strokeLinecap="butt"
showInfo
size="small"
strokeColor={((): string => {
const errorRatePercent = Number(
Number(metricsData?.errorRate ?? 0).toFixed(2),

View File

@@ -1,7 +1,6 @@
import { ReactNode } from 'react';
import { Color } from '@signozhq/design-tokens';
import { TableColumnType as ColumnType, Tag, Tooltip } from 'antd';
import { Progress } from '@signozhq/ui/progress';
import { Progress, TableColumnType as ColumnType, Tag, Tooltip } from 'antd';
import { convertFiltersToExpressionWithExistingQuery } from 'components/QueryBuilderV2/utils';
import {
FiltersType,
@@ -258,9 +257,10 @@ export const columnsConfig: ColumnType<APIDomainsRowData>[] = [
errorRate === 'n/a' || errorRate === '-' ? 0 : errorRate;
return (
<Progress
status="active"
percent={Number((errorRateValue as number).toFixed(2))}
strokeLinecap="butt"
showInfo
size="small"
strokeColor={((): string => {
const errorRatePercent = Number((errorRateValue as number).toFixed(2));
if (errorRatePercent >= 90) {
@@ -1022,13 +1022,14 @@ export const getEndPointsColumnsConfig = (
className: `column`,
render: (errorRate: number | string): React.ReactNode => (
<Progress
status="active"
percent={Number(
(
(errorRate === 'n/a' || errorRate === '-' ? 0 : errorRate) as number
).toFixed(1),
)}
strokeLinecap="butt"
showInfo
size="small"
strokeColor={((): string => {
const errorRatePercent = Number((errorRate as number).toFixed(1));
if (errorRatePercent >= 90) {
@@ -2513,9 +2514,10 @@ export const dependentServicesColumns: ColumnType<DependentServicesData>[] = [
render: (errorPercentage: number | string): React.ReactNode =>
errorPercentage !== '-' ? (
<Progress
status="active"
percent={Number((errorPercentage as number).toFixed(2))}
strokeLinecap="butt"
showInfo
size="small"
strokeColor={((): string => {
const errorPercentagePercent = Number(
(errorPercentage as number).toFixed(2),
@@ -3020,13 +3022,14 @@ export const getAllEndpointsWidgetData = (
),
F1: (errorRate: any): ReactNode => (
<Progress
status="active"
percent={Number(
(
(errorRate === 'n/a' || errorRate === '-' ? 0 : errorRate) as number
).toFixed(2),
)}
strokeLinecap="butt"
showInfo
size="small"
strokeColor={((): string => {
const errorRatePercent = Number(
(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
import { Button, Tabs, Tooltip } from 'antd';
import { Button, Tooltip } from 'antd';
import { Tabs } from '@signozhq/ui/tabs';
import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
import { Braces, Globe, Table } from '@signozhq/icons';
import { useAppContext } from 'providers/App/App';
@@ -73,7 +74,7 @@ function DashboardSettings({
...(enablePublicDashboard ? [publicDashboardItem] : []),
];
return <Tabs items={items} animated className="settings-tabs" />;
return <Tabs items={items} className="settings-tabs" />;
}
export default DashboardSettings;

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,7 @@
import { Dispatch, ReactElement, SetStateAction } from 'react';
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 { Form, FormInstance, Select } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import type { Store } from 'antd/lib/form/interface';
import ROUTES from 'constants/routes';

View File

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

View File

@@ -39,5 +39,7 @@
width: 100% !important;
--progress-width: 100%;
.ant-progress-steps-outer {
width: 100% !important;
}
}

View File

@@ -1,4 +1,4 @@
import { Progress } from '@signozhq/ui/progress';
import { Progress } from 'antd';
import { ChecklistItem } from '../HomeChecklist/HomeChecklist';
@@ -15,7 +15,9 @@ function StepsProgress({
const totalChecklistItems = checklistItems.length;
const progress = (completedChecklistItems.length / totalChecklistItems) * 100;
const progress = Math.round(
(completedChecklistItems.length / totalChecklistItems) * 100,
);
return (
<div className="steps-progress-container">

View File

@@ -1,7 +1,6 @@
import React from 'react';
import { Color } from '@signozhq/design-tokens';
import { Tag } from 'antd';
import { Progress } from '@signozhq/ui/progress';
import { Progress, Tag } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import {
getHostLists,
@@ -80,8 +79,8 @@ export const hostDetailsMetadataConfig: K8sDetailsMetadataConfig<HostData>[] = [
render: (value): React.ReactNode => (
<Progress
percent={Number(Number(value).toFixed(1))}
size="small"
strokeColor={getProgressColor(Number(value))}
showInfo
/>
),
},
@@ -91,8 +90,8 @@ export const hostDetailsMetadataConfig: K8sDetailsMetadataConfig<HostData>[] = [
render: (value): React.ReactNode => (
<Progress
percent={Number(Number(value).toFixed(1))}
size="small"
strokeColor={getMemoryProgressColor(Number(value))}
showInfo
/>
),
},

View File

@@ -60,6 +60,11 @@
& > div {
width: 100%;
}
:global(.ant-progress-bg) {
height: 8px !important;
border-radius: 4px;
}
}
.progressBar {

View File

@@ -103,8 +103,12 @@
.progress-container {
width: 158px;
span {
font-weight: 600;
.ant-progress {
margin: 0;
.ant-progress-text {
font-weight: 600;
}
}
}
@@ -288,6 +292,10 @@
}
.progress-container {
.ant-progress-bg {
height: 8px !important;
border-radius: 4px;
}
}
.ant-table-tbody > tr:hover > td {

View File

@@ -1,4 +1,4 @@
import { Progress } from '@signozhq/ui/progress';
import { Progress } from 'antd';
import TanStackTable from 'components/TanStackTableView';
import {
getMemoryProgressColor,
@@ -53,6 +53,7 @@ export function EntityProgressBar({
<Progress
percent={percentage}
strokeLinecap="butt"
size="small"
status="normal"
strokeColor={getStrokeColor(type, value)}
className={styles.progressBar}

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
import { Button, Tabs, TabsProps } from 'antd';
import { Button } from 'antd';
import { Tabs, TabItemProps } from '@signozhq/ui/tabs';
import { Typography } from '@signozhq/ui/typography';
import ConfigureIcon from 'assets/Integrations/ConfigureIcon';
import { CableCar, Group } from '@signozhq/icons';
@@ -22,7 +23,7 @@ function IntegrationDetailContent(
): JSX.Element {
const { activeDetailTab, integrationData, integrationId, setActiveDetailTab } =
props;
const items: TabsProps['items'] = [
const items: TabItemProps[] = [
{
key: 'overview',
label: (
@@ -81,11 +82,7 @@ function IntegrationDetailContent(
];
return (
<div className="integration-detail-container">
<Tabs
activeKey={activeDetailTab}
items={items}
onChange={setActiveDetailTab}
/>
<Tabs value={activeDetailTab} items={items} onChange={setActiveDetailTab} />
</div>
);
}

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,7 @@
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { LoaderCircle, Check } from '@signozhq/icons';
import { Input } from '@signozhq/ui/input';
import { Button, Space } from 'antd';
import { Button, Input, Space } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent';
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 { Color } from '@signozhq/design-tokens';
import { Button } from '@signozhq/ui/button';
import { Input } from '@signozhq/ui/input';
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 { AddToQueryHOCProps } from 'components/Logs/AddToQueryHOC';
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
import { useSelector } from 'react-redux';
import { CirclePlus, X } from '@signozhq/icons';
import { Input } from '@signozhq/ui/input';
import { Col } from 'antd';
import { Col, Input } from 'antd';
import CategoryHeading from 'components/Logs/CategoryHeading';
import { fieldSearchFilter } from 'lib/logs/fieldSearch';
import { AppState } from 'store/reducers';

View File

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

View File

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

View File

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

View File

@@ -142,6 +142,13 @@
}
}
.progress-container {
.ant-progress-bg {
height: 8px !important;
border-radius: 4px;
}
}
.ant-table-tbody > tr:hover > td {
background: color-mix(in srgb, var(--l1-foreground) 4%, transparent);
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -58,26 +58,6 @@ describe('AuthDomain', () => {
});
});
it('reflects ssoEnabled state from nested config in each row toggle', async () => {
server.use(
rest.get(AUTH_DOMAINS_LIST_ENDPOINT, (_, res, ctx) =>
res(ctx.status(200), ctx.json(mockDomainsListResponse)),
),
);
render(<AuthDomain />);
// mockDomainsListResponse rows:
// [0] signoz.io → config.ssoEnabled: true
// [1] example.com → config.ssoEnabled: false
// [2] corp.io → config.ssoEnabled: true
const switches = await screen.findAllByRole('switch');
expect(switches).toHaveLength(3);
expect(switches[0]).toBeChecked();
expect(switches[1]).not.toBeChecked();
expect(switches[2]).toBeChecked();
});
it('renders empty state when no domains exist', async () => {
server.use(
rest.get(AUTH_DOMAINS_LIST_ENDPOINT, (_, res, ctx) =>

View File

@@ -121,14 +121,14 @@ function AuthDomain(): JSX.Element {
},
{
title: 'Enforce SSO',
dataIndex: ['config', 'ssoEnabled'],
dataIndex: 'ssoEnabled',
key: 'ssoEnabled',
width: 80,
render: (
value: boolean,
record: AuthtypesGettableAuthDomainDTO,
): JSX.Element => (
<SSOEnforcementToggle isDefaultChecked={!!value} record={record} />
<SSOEnforcementToggle isDefaultChecked={value} record={record} />
),
},
{

View File

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

View File

@@ -87,7 +87,12 @@
.service-progress-indicator {
width: fit-content;
--progress-width: 30px;
margin-inline-end: 0px !important;
margin-bottom: 0px !important;
.ant-progress-inner {
width: 30px;
}
}
.percent-value {

View File

@@ -1,7 +1,6 @@
import { useEffect, useMemo, useState } from 'react';
import { useParams } from 'react-router-dom';
import { Skeleton, Tooltip } from 'antd';
import { Progress } from '@signozhq/ui/progress';
import { Progress, Skeleton, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { AxiosError } from 'axios';
import Spinner from 'components/Spinner';

View File

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

View File

@@ -1,6 +1,4 @@
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';
function CSVInput({ value, onChange, ...otherProps }: InputProps): JSX.Element {

View File

@@ -1,8 +1,7 @@
import { useEffect, useState } from 'react';
import { Info } from '@signozhq/icons';
import { Input } from '@signozhq/ui/input';
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 { PREDEFINED_MAPPING } from '../config';

View File

@@ -1,7 +1,6 @@
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 { Form, Select, Space } from 'antd';
import { ModalFooterTitle } from 'container/PipelinePage/styles';
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 { Plus, Search } from '@signozhq/icons';
import { Color } from '@signozhq/design-tokens';
import { Input } from '@signozhq/ui/input';
import { Button, Flex, Form, Tooltip } from 'antd';
import { Button, Flex, Form, Input, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import {
useDeleteDowntimeScheduleByID,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,258 +0,0 @@
import {
act,
fireEvent,
render,
screen,
waitFor,
} from '@testing-library/react';
import DateTimeSelection from '../index';
import {
__resetSearchParamsGetter,
__setSearchParamsGetterForTest,
} from '../utils/getUnstableCurrentSearchParams';
import { queryClient, TestWrapper } from './testUtils';
const mockSafeNavigate = jest.fn();
jest.mock('hooks/useSafeNavigate', () => ({
useSafeNavigate: (): { safeNavigate: jest.Mock } => ({
safeNavigate: mockSafeNavigate,
}),
}));
jest.mock('container/NewExplorerCTA', () => ({
__esModule: true,
default: (): null => null,
}));
jest.mock('components/CustomTimePicker/CustomTimePicker', () => ({
__esModule: true,
default: ({
onSelect,
}: {
onSelect: (value: string) => void;
}): JSX.Element => (
<div data-testid="custom-time-picker">
<button
type="button"
data-testid="select-15m"
onClick={(): void => onSelect('15m')}
>
15m
</button>
<button
type="button"
data-testid="select-1h"
onClick={(): void => onSelect('1h')}
>
1h
</button>
<button
type="button"
data-testid="select-6h"
onClick={(): void => onSelect('6h')}
>
6h
</button>
<button
type="button"
data-testid="select-custom"
onClick={(): void => onSelect('custom')}
>
Custom
</button>
</div>
),
}));
describe('DateTimeSelectionV2 - Edge Cases', () => {
let currentSearchParams: URLSearchParams;
beforeEach(() => {
jest.clearAllMocks();
mockSafeNavigate.mockClear();
queryClient.clear();
});
afterEach(() => {
__resetSearchParamsGetter();
});
describe('Fresh Params at Navigation Time (Core Fix)', () => {
it('should read params at navigation time, not render time', async () => {
currentSearchParams = new URLSearchParams('relativeTime=30m');
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper
initialSearchParams="relativeTime=30m"
onUrlUpdate={(event): void => {
currentSearchParams = event.searchParams;
}}
>
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
currentSearchParams = new URLSearchParams(
'relativeTime=30m&externalParam=addedLater',
);
mockSafeNavigate.mockClear();
act(() => {
fireEvent.click(screen.getByTestId('select-1h'));
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('relativeTime=1h');
expect(navigatedUrl).toContain('externalParam=addedLater');
});
it('should preserve multiple externally added params', async () => {
currentSearchParams = new URLSearchParams('relativeTime=30m');
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper initialSearchParams="relativeTime=30m">
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
currentSearchParams = new URLSearchParams(
'relativeTime=30m&yAxisUnit=bytes&groupBy=host&view=table',
);
mockSafeNavigate.mockClear();
act(() => {
fireEvent.click(screen.getByTestId('select-6h'));
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('relativeTime=6h');
expect(navigatedUrl).toContain('yAxisUnit=bytes');
expect(navigatedUrl).toContain('groupBy=host');
expect(navigatedUrl).toContain('view=table');
});
});
describe('Empty and Special Values', () => {
it('should handle empty URL params gracefully', async () => {
currentSearchParams = new URLSearchParams('');
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper initialSearchParams="">
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
mockSafeNavigate.mockClear();
act(() => {
fireEvent.click(screen.getByTestId('select-15m'));
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('relativeTime=15m');
});
it('should handle special characters in preserved params', async () => {
currentSearchParams = new URLSearchParams(
'relativeTime=30m&filter=name%3D%22test%22',
);
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper initialSearchParams="relativeTime=30m&filter=name%3D%22test%22">
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
mockSafeNavigate.mockClear();
act(() => {
fireEvent.click(screen.getByTestId('select-1h'));
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('relativeTime=1h');
expect(navigatedUrl).toContain('filter=');
});
it('should not navigate when selecting custom (opens picker instead)', async () => {
currentSearchParams = new URLSearchParams('relativeTime=30m');
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper initialSearchParams="relativeTime=30m">
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
mockSafeNavigate.mockClear();
act(() => {
fireEvent.click(screen.getByTestId('select-custom'));
});
await new Promise((resolve) => setTimeout(resolve, 100));
const customNavigationCalls = mockSafeNavigate.mock.calls.filter((call) => {
const url = call[0] as string;
return url.includes('startTime=') || url.includes('endTime=');
});
expect(customNavigationCalls).toHaveLength(0);
});
});
});

View File

@@ -1,180 +0,0 @@
import {
act,
fireEvent,
render,
screen,
waitFor,
} from '@testing-library/react';
import DateTimeSelection from '../index';
import {
__resetSearchParamsGetter,
__setSearchParamsGetterForTest,
} from '../utils/getUnstableCurrentSearchParams';
import { queryClient, TestWrapper } from './testUtils';
const mockSafeNavigate = jest.fn();
jest.mock('hooks/useSafeNavigate', () => ({
useSafeNavigate: (): { safeNavigate: jest.Mock } => ({
safeNavigate: mockSafeNavigate,
}),
}));
jest.mock('container/NewExplorerCTA', () => ({
__esModule: true,
default: (): null => null,
}));
let mockOnCustomDateHandler: ((range: [unknown, unknown]) => void) | null =
null;
let mockOnValidCustomDateChange: ((data: { timeStr: string }) => void) | null =
null;
jest.mock('components/CustomTimePicker/CustomTimePicker', () => ({
__esModule: true,
default: ({
onSelect,
onCustomDateHandler,
onValidCustomDateChange,
}: {
onSelect: (value: string) => void;
onCustomDateHandler?: (range: [unknown, unknown]) => void;
onValidCustomDateChange?: (data: { timeStr: string }) => void;
}): JSX.Element => {
mockOnCustomDateHandler = onCustomDateHandler || null;
mockOnValidCustomDateChange = onValidCustomDateChange || null;
return (
<div data-testid="custom-time-picker">
<button
type="button"
data-testid="select-1h"
onClick={(): void => onSelect('1h')}
>
1h
</button>
</div>
);
},
}));
describe('DateTimeSelectionV2 - Modal Mode', () => {
let currentSearchParams: URLSearchParams;
beforeEach(() => {
jest.clearAllMocks();
mockSafeNavigate.mockClear();
queryClient.clear();
mockOnCustomDateHandler = null;
mockOnValidCustomDateChange = null;
});
afterEach(() => {
__resetSearchParamsGetter();
});
it('should call onTimeChange instead of navigating for relative time', async () => {
currentSearchParams = new URLSearchParams('relativeTime=30m');
__setSearchParamsGetterForTest(() => currentSearchParams);
const mockOnTimeChange = jest.fn();
render(
<TestWrapper initialSearchParams="relativeTime=30m">
<DateTimeSelection
showAutoRefresh
isModalTimeSelection
onTimeChange={mockOnTimeChange}
/>
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
mockSafeNavigate.mockClear();
act(() => {
fireEvent.click(screen.getByTestId('select-1h'));
});
await waitFor(() => {
expect(mockOnTimeChange).toHaveBeenCalledWith('1h');
});
expect(mockSafeNavigate).not.toHaveBeenCalled();
});
it('should call onTimeChange with custom and timestamps for custom date', async () => {
currentSearchParams = new URLSearchParams('relativeTime=30m');
__setSearchParamsGetterForTest(() => currentSearchParams);
const mockOnTimeChange = jest.fn();
render(
<TestWrapper initialSearchParams="relativeTime=30m">
<DateTimeSelection
showAutoRefresh
isModalTimeSelection
onTimeChange={mockOnTimeChange}
/>
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
const startMoment = { toDate: (): Date => new Date(1700000000000) };
const endMoment = { toDate: (): Date => new Date(1700003600000) };
mockSafeNavigate.mockClear();
act(() => {
mockOnCustomDateHandler?.([startMoment, endMoment]);
});
await waitFor(() => {
expect(mockOnTimeChange).toHaveBeenCalledWith(
'custom',
[1700000000000, 1700003600000],
);
});
expect(mockSafeNavigate).not.toHaveBeenCalled();
});
it('should call onTimeChange for valid custom date string in modal', async () => {
currentSearchParams = new URLSearchParams('relativeTime=30m');
__setSearchParamsGetterForTest(() => currentSearchParams);
const mockOnTimeChange = jest.fn();
render(
<TestWrapper initialSearchParams="relativeTime=30m">
<DateTimeSelection
showAutoRefresh
isModalTimeSelection
onTimeChange={mockOnTimeChange}
/>
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
mockSafeNavigate.mockClear();
act(() => {
mockOnValidCustomDateChange?.({ timeStr: '4h' });
});
await waitFor(() => {
expect(mockOnTimeChange).toHaveBeenCalledWith('4h');
});
expect(mockSafeNavigate).not.toHaveBeenCalled();
});
});

View File

@@ -1,207 +0,0 @@
// eslint-disable-next-line no-restricted-imports
import { Provider } from 'react-redux';
import { MemoryRouter, Route } from 'react-router-dom';
import { NuqsTestingAdapter } from 'nuqs/adapters/testing';
import { QueryClient, QueryClientProvider } from 'react-query';
import {
act,
fireEvent,
render,
screen,
waitFor,
} from '@testing-library/react';
import { parseAsString, useQueryState } from 'nuqs';
import { AppContext } from 'providers/App/App';
import TimezoneProvider from 'providers/Timezone';
import { QueryBuilderProvider } from 'providers/QueryBuilder';
import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import store from 'store';
import { getAppContextMock } from 'tests/test-utils';
import { CompatRouter } from 'react-router-dom-v5-compat';
import DateTimeSelection from '../index';
import {
__resetSearchParamsGetter,
__setSearchParamsGetterForTest,
} from '../utils/getUnstableCurrentSearchParams';
const queryClient = new QueryClient({
defaultOptions: {
queries: { refetchOnWindowFocus: false, retry: false },
mutations: { retry: false },
},
});
const mockStore = configureStore([thunk]);
const mockSafeNavigate = jest.fn();
jest.mock('hooks/useSafeNavigate', () => ({
useSafeNavigate: (): { safeNavigate: jest.Mock } => ({
safeNavigate: mockSafeNavigate,
}),
}));
jest.mock('components/CustomTimePicker/CustomTimePicker', () => ({
__esModule: true,
default: ({
onSelect,
}: {
onSelect: (value: string) => void;
}): JSX.Element => (
<div data-testid="custom-time-picker">
<button
type="button"
data-testid="select-1h"
onClick={(): void => onSelect('1h')}
>
1h
</button>
</div>
),
}));
jest.mock('container/NewExplorerCTA', () => ({
__esModule: true,
default: (): null => null,
}));
function NuqsParamSetter({ paramValue }: { paramValue: string }): JSX.Element {
const [, setYAxisUnit] = useQueryState(
'yAxisUnit',
parseAsString.withDefault(''),
);
return (
<button
type="button"
data-testid="set-nuqs-param"
onClick={(): void => {
setYAxisUnit(paramValue);
}}
>
Set yAxisUnit
</button>
);
}
interface WrapperProps {
children: React.ReactNode;
initialSearchParams?: string;
initialPath?: string;
onUrlUpdate?: (event: { searchParams: URLSearchParams }) => void;
}
function TestWrapper({
children,
initialSearchParams = '',
initialPath = '/services',
onUrlUpdate,
}: WrapperProps): JSX.Element {
const initialEntry = initialSearchParams
? `${initialPath}?${initialSearchParams}`
: initialPath;
const mockedStore = mockStore({
...store.getState(),
app: {
...store.getState().app,
role: 'ADMIN',
user: {
userId: 'test-user-id',
email: 'test@signoz.io',
name: 'TestUser',
profilePictureURL: '',
accessJwt: '',
refreshJwt: '',
},
isLoggedIn: true,
},
});
return (
<MemoryRouter initialEntries={[initialEntry]}>
<CompatRouter>
<NuqsTestingAdapter
searchParams={initialSearchParams}
onUrlUpdate={onUrlUpdate}
>
<QueryClientProvider client={queryClient}>
<Provider store={mockedStore}>
<AppContext.Provider value={getAppContextMock('ADMIN')}>
<TimezoneProvider>
<QueryBuilderProvider>
<Route path="*">{children}</Route>
</QueryBuilderProvider>
</TimezoneProvider>
</AppContext.Provider>
</Provider>
</QueryClientProvider>
</NuqsTestingAdapter>
</CompatRouter>
</MemoryRouter>
);
}
describe('REGRESSION: DateTimeSelectionV2 preserves nuqs query params on time change', () => {
let currentSearchParams: URLSearchParams;
beforeEach(() => {
jest.clearAllMocks();
mockSafeNavigate.mockClear();
queryClient.clear();
// Initialize with test's initial search params
currentSearchParams = new URLSearchParams('relativeTime=30m');
__setSearchParamsGetterForTest(() => currentSearchParams);
});
afterEach(() => {
__resetSearchParamsGetter();
});
it('should preserve yAxisUnit param set via nuqs when changing time selection', async () => {
render(
<TestWrapper
initialSearchParams="relativeTime=30m"
onUrlUpdate={(event): void => {
// Sync nuqs URL updates to our mock getter
// This simulates how window.location.search would be updated in real browser
currentSearchParams = event.searchParams;
}}
>
<NuqsParamSetter paramValue="bytes" />
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
act(() => {
fireEvent.click(screen.getByTestId('set-nuqs-param'));
});
await waitFor(() => {
expect(currentSearchParams.get('yAxisUnit')).toBe('bytes');
});
mockSafeNavigate.mockClear();
act(() => {
fireEvent.click(screen.getByTestId('select-1h'));
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('relativeTime=1h');
expect(navigatedUrl).toContain('yAxisUnit=bytes');
});
});

View File

@@ -1,133 +0,0 @@
import { render, screen, waitFor } from '@testing-library/react';
import ROUTES from 'constants/routes';
import DateTimeSelection from '../index';
import {
__resetSearchParamsGetter,
__setSearchParamsGetterForTest,
} from '../utils/getUnstableCurrentSearchParams';
import { queryClient, TestWrapper } from './testUtils';
const mockSafeNavigate = jest.fn();
jest.mock('hooks/useSafeNavigate', () => ({
useSafeNavigate: (): { safeNavigate: jest.Mock } => ({
safeNavigate: mockSafeNavigate,
}),
}));
jest.mock('container/NewExplorerCTA', () => ({
__esModule: true,
default: (): null => null,
}));
jest.mock('components/CustomTimePicker/CustomTimePicker', () => ({
__esModule: true,
default: (): JSX.Element => <div data-testid="custom-time-picker" />,
}));
describe('DateTimeSelectionV2 - Route-Specific Behavior', () => {
let currentSearchParams: URLSearchParams;
beforeEach(() => {
jest.clearAllMocks();
mockSafeNavigate.mockClear();
queryClient.clear();
});
afterEach(() => {
__resetSearchParamsGetter();
});
describe('Alert Pages', () => {
it('should set default time for alert overview when no time params', async () => {
currentSearchParams = new URLSearchParams('otherParam=value');
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper
initialSearchParams="otherParam=value"
initialPath={ROUTES.ALERT_OVERVIEW}
>
<DateTimeSelection showAutoRefresh defaultRelativeTime="6h" />
</TestWrapper>,
);
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[0][0] as string;
expect(navigatedUrl).toContain('relativeTime=6h');
expect(navigatedUrl).toContain('otherParam=value');
});
it('should set default time for alert history when no time params', async () => {
currentSearchParams = new URLSearchParams('filter=active');
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper
initialSearchParams="filter=active"
initialPath={ROUTES.ALERT_HISTORY}
>
<DateTimeSelection showAutoRefresh defaultRelativeTime="6h" />
</TestWrapper>,
);
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[0][0] as string;
expect(navigatedUrl).toContain('relativeTime=6h');
});
it('should NOT override existing time params on alert pages', async () => {
currentSearchParams = new URLSearchParams('relativeTime=1h&filter=active');
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper
initialSearchParams="relativeTime=1h&filter=active"
initialPath={ROUTES.ALERT_OVERVIEW}
>
<DateTimeSelection showAutoRefresh defaultRelativeTime="6h" />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
const calls = mockSafeNavigate.mock.calls;
if (calls.length > 0) {
const lastUrl = calls[calls.length - 1][0] as string;
expect(lastUrl).toContain('relativeTime=1h');
}
});
});
describe('disableUrlSync Behavior', () => {
it('should not sync URL on mount when disableUrlSync is true', async () => {
currentSearchParams = new URLSearchParams('');
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper initialSearchParams="" initialPath="/services">
<DateTimeSelection showAutoRefresh disableUrlSync />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
const syncCalls = mockSafeNavigate.mock.calls.filter((call) => {
const url = call[0] as string;
return url.includes('relativeTime=') && !url.includes('services?');
});
expect(syncCalls).toHaveLength(0);
});
});
});

View File

@@ -1,353 +0,0 @@
import {
act,
fireEvent,
render,
screen,
waitFor,
} from '@testing-library/react';
import DateTimeSelection from '../index';
import {
__resetSearchParamsGetter,
__setSearchParamsGetterForTest,
} from '../utils/getUnstableCurrentSearchParams';
import { queryClient, TestWrapper, createMockMoment } from './testUtils';
const mockSafeNavigate = jest.fn();
jest.mock('hooks/useSafeNavigate', () => ({
useSafeNavigate: (): { safeNavigate: jest.Mock } => ({
safeNavigate: mockSafeNavigate,
}),
}));
jest.mock('container/NewExplorerCTA', () => ({
__esModule: true,
default: (): null => null,
}));
let mockOnCustomDateHandler: ((range: [unknown, unknown]) => void) | null =
null;
let mockOnValidCustomDateChange: ((data: { timeStr: string }) => void) | null =
null;
jest.mock('components/CustomTimePicker/CustomTimePicker', () => ({
__esModule: true,
default: ({
onSelect,
onCustomDateHandler,
onValidCustomDateChange,
}: {
onSelect: (value: string) => void;
onCustomDateHandler?: (range: [unknown, unknown]) => void;
onValidCustomDateChange?: (data: { timeStr: string }) => void;
}): JSX.Element => {
mockOnCustomDateHandler = onCustomDateHandler || null;
mockOnValidCustomDateChange = onValidCustomDateChange || null;
return (
<div data-testid="custom-time-picker">
<button
type="button"
data-testid="select-15m"
onClick={(): void => onSelect('15m')}
>
15m
</button>
<button
type="button"
data-testid="select-1h"
onClick={(): void => onSelect('1h')}
>
1h
</button>
<button
type="button"
data-testid="select-6h"
onClick={(): void => onSelect('6h')}
>
6h
</button>
</div>
);
},
}));
describe('DateTimeSelectionV2 - Time Selection', () => {
let currentSearchParams: URLSearchParams;
beforeEach(() => {
jest.clearAllMocks();
mockSafeNavigate.mockClear();
queryClient.clear();
mockOnCustomDateHandler = null;
mockOnValidCustomDateChange = null;
});
afterEach(() => {
__resetSearchParamsGetter();
});
describe('Relative Time', () => {
it('should update relativeTime and remove startTime/endTime', async () => {
currentSearchParams = new URLSearchParams(
'startTime=1000&endTime=2000&otherParam=keep',
);
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper initialSearchParams="startTime=1000&endTime=2000&otherParam=keep">
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
mockSafeNavigate.mockClear();
act(() => {
fireEvent.click(screen.getByTestId('select-15m'));
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('relativeTime=15m');
expect(navigatedUrl).not.toContain('startTime=');
expect(navigatedUrl).not.toContain('endTime=');
expect(navigatedUrl).toContain('otherParam=keep');
});
it('should remove activeLogId param on time change', async () => {
currentSearchParams = new URLSearchParams(
'relativeTime=30m&activeLogId=log123',
);
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper initialSearchParams="relativeTime=30m&activeLogId=log123">
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
mockSafeNavigate.mockClear();
act(() => {
fireEvent.click(screen.getByTestId('select-1h'));
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('relativeTime=1h');
expect(navigatedUrl).not.toContain('activeLogId');
});
it('should update compositeQuery with new ID when present', async () => {
const compositeQuery = encodeURIComponent(
JSON.stringify({ id: 'old-id', builder: { queryData: [] } }),
);
currentSearchParams = new URLSearchParams(
`relativeTime=30m&compositeQuery=${compositeQuery}`,
);
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper
initialSearchParams={`relativeTime=30m&compositeQuery=${compositeQuery}`}
>
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
mockSafeNavigate.mockClear();
act(() => {
fireEvent.click(screen.getByTestId('select-6h'));
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('compositeQuery=');
expect(navigatedUrl).not.toContain('old-id');
});
it('should preserve all non-time URL params', async () => {
currentSearchParams = new URLSearchParams(
'relativeTime=30m&param1=a&param2=b&param3=c',
);
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper initialSearchParams="relativeTime=30m&param1=a&param2=b&param3=c">
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
mockSafeNavigate.mockClear();
act(() => {
fireEvent.click(screen.getByTestId('select-1h'));
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('relativeTime=1h');
expect(navigatedUrl).toContain('param1=a');
expect(navigatedUrl).toContain('param2=b');
expect(navigatedUrl).toContain('param3=c');
});
});
describe('Custom Date Range', () => {
it('should set startTime/endTime and remove relativeTime', async () => {
currentSearchParams = new URLSearchParams('relativeTime=30m&keepThis=yes');
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper initialSearchParams="relativeTime=30m&keepThis=yes">
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
const startMoment = createMockMoment(1700000000000);
const endMoment = createMockMoment(1700003600000);
mockSafeNavigate.mockClear();
act(() => {
mockOnCustomDateHandler?.([startMoment, endMoment]);
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('startTime=1700000000000');
expect(navigatedUrl).toContain('endTime=1700003600000');
expect(navigatedUrl).not.toContain('relativeTime=');
expect(navigatedUrl).toContain('keepThis=yes');
});
it('should update compositeQuery when present for custom date', async () => {
const compositeQuery = encodeURIComponent(
JSON.stringify({ id: 'old-id', builder: { queryData: [] } }),
);
currentSearchParams = new URLSearchParams(
`relativeTime=30m&compositeQuery=${compositeQuery}`,
);
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper
initialSearchParams={`relativeTime=30m&compositeQuery=${compositeQuery}`}
>
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
const startMoment = createMockMoment(1700000000000);
const endMoment = createMockMoment(1700003600000);
mockSafeNavigate.mockClear();
act(() => {
mockOnCustomDateHandler?.([startMoment, endMoment]);
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('compositeQuery=');
expect(navigatedUrl).not.toContain('old-id');
});
});
describe('Valid Custom Date String', () => {
it('should handle shorthand date format and preserve params', async () => {
currentSearchParams = new URLSearchParams('relativeTime=30m&filter=active');
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper initialSearchParams="relativeTime=30m&filter=active">
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
mockSafeNavigate.mockClear();
act(() => {
mockOnValidCustomDateChange?.({ timeStr: '2h' });
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('relativeTime=2h');
expect(navigatedUrl).toContain('filter=active');
expect(navigatedUrl).not.toContain('startTime=');
expect(navigatedUrl).not.toContain('endTime=');
});
});
});

View File

@@ -1,95 +0,0 @@
// eslint-disable-next-line no-restricted-imports
import { Provider } from 'react-redux';
import { MemoryRouter, Route } from 'react-router-dom';
import { NuqsTestingAdapter } from 'nuqs/adapters/testing';
import { QueryClient, QueryClientProvider } from 'react-query';
import { AppContext } from 'providers/App/App';
import TimezoneProvider from 'providers/Timezone';
import { QueryBuilderProvider } from 'providers/QueryBuilder';
import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import store from 'store';
import { getAppContextMock } from 'tests/test-utils';
import { CompatRouter } from 'react-router-dom-v5-compat';
export const queryClient = new QueryClient({
defaultOptions: {
queries: { refetchOnWindowFocus: false, retry: false },
mutations: { retry: false },
},
});
export const mockStore = configureStore([thunk]);
interface WrapperProps {
children: React.ReactNode;
initialSearchParams?: string;
initialPath?: string;
onUrlUpdate?: (event: { searchParams: URLSearchParams }) => void;
}
export function TestWrapper({
children,
initialSearchParams = '',
initialPath = '/services',
onUrlUpdate,
}: WrapperProps): JSX.Element {
const initialEntry = initialSearchParams
? `${initialPath}?${initialSearchParams}`
: initialPath;
const mockedStore = mockStore({
...store.getState(),
app: {
...store.getState().app,
role: 'ADMIN',
user: {
userId: 'test-user-id',
email: 'test@signoz.io',
name: 'TestUser',
profilePictureURL: '',
accessJwt: '',
refreshJwt: '',
},
isLoggedIn: true,
},
});
return (
<MemoryRouter initialEntries={[initialEntry]}>
<CompatRouter>
<NuqsTestingAdapter
searchParams={initialSearchParams}
onUrlUpdate={onUrlUpdate}
>
<QueryClientProvider client={queryClient}>
<Provider store={mockedStore}>
<AppContext.Provider value={getAppContextMock('ADMIN')}>
<TimezoneProvider>
<QueryBuilderProvider>
<Route path="*">{children}</Route>
</QueryBuilderProvider>
</TimezoneProvider>
</AppContext.Provider>
</Provider>
</QueryClientProvider>
</NuqsTestingAdapter>
</CompatRouter>
</MemoryRouter>
);
}
export function createMockMoment(timestamp: number): {
toDate: () => Date;
toISOString: () => string;
format: () => string;
toString: () => string;
} {
const date = new Date(timestamp);
return {
toDate: (): Date => date,
toISOString: (): string => date.toISOString(),
format: (): string => date.toISOString(),
toString: (): string => date.toString(),
};
}

View File

@@ -2,7 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
// eslint-disable-next-line no-restricted-imports
import { connect, useDispatch, useSelector } from 'react-redux';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import { useNavigationType } from 'react-router-dom-v5-compat';
import { useNavigationType, useSearchParams } from 'react-router-dom-v5-compat';
import { RefreshCw, Undo } from '@signozhq/icons';
import { Button } from 'antd';
import getLocalStorageKey from 'api/browser/localstorage/get';
@@ -20,6 +20,7 @@ import {
} from 'store/globalTime';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useSafeNavigate } from 'hooks/useSafeNavigate';
import useUrlQuery from 'hooks/useUrlQuery';
import { isValidShortHandDateTimeFormat } from 'lib/getMinMax';
import getTimeString from 'lib/getTimeString';
import { cloneDeep, isObject } from 'lodash-es';
@@ -53,7 +54,6 @@ import {
Time,
TimeRange,
} from './types';
import { getUnstableCurrentSearchParams } from './utils/getUnstableCurrentSearchParams';
import './DateTimeSelectionV2.styles.scss';
@@ -90,12 +90,10 @@ function DateTimeSelection({
const [hasSelectedTimeError, setHasSelectedTimeError] = useState(false);
const [isOpen, setIsOpen] = useState<boolean>(false);
const currentSearchParams = getUnstableCurrentSearchParams();
const searchStartTime = currentSearchParams.get(QueryParams.startTime);
const searchEndTime = currentSearchParams.get(QueryParams.endTime);
const relativeTimeFromUrl = currentSearchParams.get(QueryParams.relativeTime);
const hasTimeParamsInUrl =
(searchStartTime && searchEndTime) || relativeTimeFromUrl;
const urlQuery = useUrlQuery();
const searchStartTime = urlQuery.get('startTime');
const searchEndTime = urlQuery.get('endTime');
const relativeTimeFromUrl = urlQuery.get(QueryParams.relativeTime);
// Prioritize props for initial modal time, fallback to URL params
let initialModalStartTime = 0;
@@ -117,6 +115,8 @@ function DateTimeSelection({
);
const [modalEndTime, setModalEndTime] = useState<number>(initialModalEndTime);
const [searchParams] = useSearchParams();
// Effect to update modal time state when props change
useEffect(() => {
if (modalInitialStartTime !== undefined) {
@@ -323,15 +323,14 @@ function DateTimeSelection({
return;
}
const urlQuery = getUnstableCurrentSearchParams();
urlQuery.delete(QueryParams.startTime);
urlQuery.delete(QueryParams.endTime);
urlQuery.delete('startTime');
urlQuery.delete('endTime');
urlQuery.set(QueryParams.relativeTime, value);
// Remove Hidden Filters from URL query parameters on time change
urlQuery.delete(QueryParams.activeLogId);
if (urlQuery.has(QueryParams.compositeQuery)) {
if (searchParams.has(QueryParams.compositeQuery)) {
const updatedCompositeQuery = getUpdatedCompositeQuery();
urlQuery.set(QueryParams.compositeQuery, updatedCompositeQuery);
}
@@ -350,6 +349,8 @@ function DateTimeSelection({
getUpdatedCompositeQuery,
updateLocalStorageForRoutes,
updateTimeInterval,
urlQuery,
searchParams,
],
);
@@ -413,7 +414,6 @@ function DateTimeSelection({
updateLocalStorageForRoutes(JSON.stringify({ startTime, endTime }));
const urlQuery = getUnstableCurrentSearchParams();
urlQuery.set(
QueryParams.startTime,
startTime?.toDate().getTime().toString(),
@@ -421,7 +421,7 @@ function DateTimeSelection({
urlQuery.set(QueryParams.endTime, endTime?.toDate().getTime().toString());
urlQuery.delete(QueryParams.relativeTime);
if (urlQuery.has(QueryParams.compositeQuery)) {
if (searchParams.has(QueryParams.compositeQuery)) {
const updatedCompositeQuery = getUpdatedCompositeQuery();
urlQuery.set(QueryParams.compositeQuery, updatedCompositeQuery);
}
@@ -441,9 +441,8 @@ function DateTimeSelection({
updateTimeInterval(dateTimeStr);
updateLocalStorageForRoutes(dateTimeStr);
const urlQuery = getUnstableCurrentSearchParams();
urlQuery.delete(QueryParams.startTime);
urlQuery.delete(QueryParams.endTime);
urlQuery.delete('startTime');
urlQuery.delete('endTime');
urlQuery.set(QueryParams.relativeTime, dateTimeStr);
@@ -596,12 +595,13 @@ function DateTimeSelection({
// set the default relative time for alert history and overview pages if relative time is not specified
if (
!hasTimeParamsInUrl &&
(!urlQuery.has(QueryParams.startTime) ||
!urlQuery.has(QueryParams.endTime)) &&
!urlQuery.has(QueryParams.relativeTime) &&
(currentRoute === ROUTES.ALERT_OVERVIEW ||
currentRoute === ROUTES.ALERT_HISTORY)
) {
updateTimeInterval(defaultRelativeTime);
const urlQuery = getUnstableCurrentSearchParams();
urlQuery.set(QueryParams.relativeTime, defaultRelativeTime);
const generatedUrl = `${location.pathname}?${urlQuery.toString()}`;
safeNavigate(generatedUrl);
@@ -625,10 +625,9 @@ function DateTimeSelection({
updateTimeInterval(updatedTime, [preStartTime, preEndTime]);
}
const urlQuery = getUnstableCurrentSearchParams();
if (updatedTime !== 'custom') {
urlQuery.delete(QueryParams.startTime);
urlQuery.delete(QueryParams.endTime);
urlQuery.delete('startTime');
urlQuery.delete('endTime');
urlQuery.set(QueryParams.relativeTime, updatedTime);
} else {
const startTime = preStartTime.toString();

View File

@@ -1,34 +0,0 @@
/**
* This was introduced to fix a sync bug between Nuqs and react-router-dom
*
* We are using the wrong adapter for nuqs because the correct one only supports v6/v7,
* and we are at version v5. This causes the nuqs/react-router-dom to be out of sync.
*
* We can revert this commit once we migrate react-router-dom to v6, or once we migrate
* to DateTimeSelectionV3
*/
/**
* This was created to help testing the regression introduced between nuqs/react-router-dom
*/
type SearchParamsGetter = () => URLSearchParams;
let getter: SearchParamsGetter = (): URLSearchParams =>
new URLSearchParams(window.location.search);
/**
* This function will return a fresh instance of URLSearchParams every time it's called.
*
* DO NOT USE IT FOR useEffect/useCallback dependencies, use Nuqs instead.
*/
export function getUnstableCurrentSearchParams(): URLSearchParams {
return getter();
}
// Testing helpers
export function __setSearchParamsGetterForTest(fn: SearchParamsGetter): void {
getter = fn;
}
export function __resetSearchParamsGetter(): void {
getter = (): URLSearchParams => new URLSearchParams(window.location.search);
}

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 { Typography } from '@signozhq/ui/typography';
import styled from 'styled-components';
export const DurationText = styled.div`

View File

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

View File

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

View File

@@ -1,7 +1,6 @@
import { ChangeEvent, useEffect, useMemo, useState } from 'react';
import { ArrowLeft, Check, Loader, Plus, Search } from '@signozhq/icons';
import { Input } from '@signozhq/ui/input';
import { Button, Spin } from 'antd';
import { Button, Input, Spin } from 'antd';
import cx from 'classnames';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
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';
export const InputComponent = styled(Input)`

View File

@@ -1,8 +0,0 @@
function DashboardPageV2(): JSX.Element {
return (
<div>
<h1>Dashboard Page V2</h1>
</div>
);
}
export default DashboardPageV2;

View File

@@ -1,9 +0,0 @@
function DashboardsListPageV2(): JSX.Element {
return (
<div>
<h1>Dashboards List Page V2</h1>
</div>
);
}
export default DashboardsListPageV2;

View File

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

View File

@@ -1,5 +1,5 @@
import { useParams } from 'react-router-dom';
import { Tabs, TabsProps } from 'antd';
import { Tabs, TabItemProps } from '@signozhq/ui/tabs';
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: TabsProps['items'] = [
const items: TabItemProps[] = [
{
label: TAB_KEY_VS_LABEL[MetricsApplicationTab.OVER_METRICS],
key: MetricsApplicationTab.OVER_METRICS,
@@ -53,9 +53,8 @@ function MetricsApplication(): JSX.Element {
<ApDexApplication />
<Tabs
items={items}
activeKey={activeKey}
value={activeKey}
className="service-route-tab"
destroyInactiveTabPane
onChange={onTabChange}
/>
</div>

View File

@@ -2,8 +2,7 @@ import { useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useQuery } from 'react-query';
import * as Sentry from '@sentry/react';
import type { TabsProps } from 'antd';
import { Tabs } from 'antd';
import { Tabs, TabItemProps } from '@signozhq/ui/tabs';
import getPipeline from 'api/pipeline/get';
import Spinner from 'components/Spinner';
import ChangeHistory from 'container/PipelinePage/Layouts/ChangeHistory';
@@ -46,7 +45,7 @@ function Pipelines(): JSX.Element {
refetchInterval: pipelineRefetchInterval,
});
const tabItems: TabsProps['items'] = useMemo(
const tabItems: TabItemProps[] = useMemo(
() => [
{
key: 'pipelines',
@@ -83,11 +82,7 @@ function Pipelines(): JSX.Element {
return (
<Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}>
<Tabs
className="pipeline-tabs"
defaultActiveKey="pipelines"
items={tabItems}
/>
<Tabs className="pipeline-tabs" defaultValue="pipelines" items={tabItems} />
</Sentry.ErrorBoundary>
);
}

View File

@@ -3,8 +3,7 @@ import { useTranslation } from 'react-i18next';
import { useLocation } from 'react-router-dom';
import { Color } from '@signozhq/design-tokens';
import { Button } from '@signozhq/ui/button';
import { Input } from '@signozhq/ui/input';
import { ColorPicker, Modal, Table, TableProps } from 'antd';
import { ColorPicker, Input, Modal, Table, TableProps } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent';
import {

View File

@@ -5,7 +5,8 @@ import {
ResizablePanel,
ResizablePanelGroup,
} from '@signozhq/resizable';
import { Button, Tabs } from 'antd';
import { Button } from 'antd';
import { Tabs } from '@signozhq/ui/tabs';
import FlamegraphImg from 'assets/TraceDetail/Flamegraph';
import cx from 'classnames';
import TraceFlamegraph from 'container/PaginatedTraceFlamegraph/PaginatedTraceFlamegraph';
@@ -146,7 +147,7 @@ function TraceDetailsV2(): JSX.Element {
notFound={noData}
/>
{!noData ? (
<Tabs items={items} animated className="trace-visualisation-tabs" />
<Tabs items={items} className="trace-visualisation-tabs" />
) : (
<NoData />
)}

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,7 @@
import React, { useCallback, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { useMutation } from 'react-query';
import type { TabsProps } from 'antd';
import type { TabItemProps } from '@signozhq/ui/tabs';
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: TabsProps['items'] = [
const tabItems: TabItemProps[] = [
{
key: 'whyChooseSignoz',
label: t('whyChooseSignoz'),
@@ -398,8 +398,8 @@ export default function WorkspaceBlocked(): JSX.Element {
<div className="workspace-locked__tabs">
<Tabs
items={tabItems}
defaultActiveKey="youAreInGoodCompany"
onTabClick={handleTabClick}
defaultValue="youAreInGoodCompany"
onChange={handleTabClick}
/>
</div>
</>

View File

@@ -1,50 +0,0 @@
package impllogspipeline
import (
"context"
"github.com/SigNoz/signoz/pkg/modules/logspipeline"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/valuer"
)
const elementType = "log_pipelines"
type module struct {
sqlStore sqlstore.SQLStore
}
func NewModule(sqlStore sqlstore.SQLStore) logspipeline.Module {
return &module{sqlStore: sqlStore}
}
func (m *module) Collect(ctx context.Context, orgID valuer.UUID) (map[string]any, error) {
subq := m.sqlStore.BunDB().NewSelect().
TableExpr("agent_config_version").
ColumnExpr("MAX(version)").
Where("org_id = ?", orgID).
Where("element_type = ?", elementType)
var result struct {
Total int `bun:"total"`
Enabled int `bun:"enabled_count"`
}
err := m.sqlStore.BunDB().NewSelect().
TableExpr("agent_config_element AS e").
Join("JOIN agent_config_version AS v ON v.id = e.version_id").
Join("JOIN pipelines AS p ON p.id = e.element_id").
Where("v.org_id = ?", orgID).
Where("v.element_type = ?", elementType).
Where("v.version = (?)", subq).
ColumnExpr("COUNT(*) AS total").
ColumnExpr("SUM(CASE WHEN p.enabled THEN 1 ELSE 0 END) AS enabled_count").
Scan(ctx, &result)
if err != nil {
return nil, err
}
return map[string]any{
"logs_pipeline.total.count": result.Total,
"logs_pipeline.enabled.count": result.Enabled,
}, nil
}

View File

@@ -1,7 +0,0 @@
package logspipeline
import "github.com/SigNoz/signoz/pkg/statsreporter"
type Module interface {
statsreporter.StatsCollector
}

View File

@@ -7,6 +7,8 @@ import (
"strings"
"unicode"
"github.com/SigNoz/signoz/pkg/query-service/constants"
"encoding/base64"
"encoding/json"
"fmt"
@@ -176,6 +178,27 @@ func HydrateFileUris(spec interface{}, fs embed.FS, basedir string) (interface{}
if specMap, ok := spec.(map[string]interface{}); ok {
result := map[string]interface{}{}
for k, v := range specMap {
// Check if this is a dashboards slice and if dot metrics are enabled
if k == "dashboards" && constants.IsDotMetricsEnabled {
if dashboards, ok := v.([]interface{}); ok {
for i, dashboard := range dashboards {
if dashboardUri, ok := dashboard.(string); ok {
if strings.HasPrefix(dashboardUri, "file://") {
dashboards[i] = strings.Replace(dashboardUri, ".json", "_dot.json", 1)
}
} else if dashBoardMap, ok := dashboard.(map[string]interface{}); ok {
if dashboardUri, ok := dashBoardMap["definition"].(string); ok {
if strings.HasPrefix(dashboardUri, "file://") {
dashboardUri = strings.Replace(dashboardUri, ".json", "_dot.json", 1)
}
dashBoardMap["definition"] = dashboardUri
}
dashboards[i] = dashBoardMap
}
}
v = dashboards
}
}
hydrated, err := HydrateFileUris(v, fs, basedir)
if err != nil {
return nil, err

View File

@@ -367,9 +367,9 @@
"id": "31d3f13b-27d5-4291-9fb3-d5d5708a72f3",
"modificationUUID": "e4a9edf1-acd0-48b7-8a35-1b4bb668408d",
"multiSelect": false,
"name": "mysql.instance.endpoint",
"name": "mysql_instance_endpoint",
"order": 0,
"queryValue": "SELECT JSONExtractString(labels, 'mysql.instance.endpoint') as `mysql.instance.endpoint`\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'mysql.uptime'\nGROUP BY `mysql.instance.endpoint`",
"queryValue": "SELECT JSONExtractString(labels, 'mysql_instance_endpoint') as mysql_instance_endpoint\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'mysql_uptime'\nGROUP BY mysql_instance_endpoint",
"selectedValue": "",
"showALLOption": false,
"sort": "ASC",
@@ -393,7 +393,7 @@
{
"aggregations": [
{
"metricName": "mysql.buffer_pool.pages",
"metricName": "mysql_buffer_pool_usage",
"reduceTo": "avg",
"spaceAggregation": "avg",
"temporality": null,
@@ -404,7 +404,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
},
"functions": [],
"groupBy": [
@@ -529,7 +529,7 @@
{
"aggregations": [
{
"metricName": "mysql.buffer_pool.pages",
"metricName": "mysql_buffer_pool_pages",
"reduceTo": "avg",
"spaceAggregation": "sum",
"temporality": null,
@@ -540,7 +540,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
},
"functions": [],
"groupBy": [
@@ -658,7 +658,7 @@
{
"aggregations": [
{
"metricName": "mysql.buffer_pool.data_pages",
"metricName": "mysql_buffer_pool_data_pages",
"reduceTo": "avg",
"spaceAggregation": "sum",
"temporality": null,
@@ -669,7 +669,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
},
"functions": [],
"groupBy": [
@@ -788,7 +788,7 @@
{
"aggregations": [
{
"metricName": "mysql.buffer_pool.page_flushes",
"metricName": "mysql_buffer_pool_page_flushes",
"reduceTo": "avg",
"spaceAggregation": "sum",
"temporality": null,
@@ -799,7 +799,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
},
"functions": [],
"groupBy": [],
@@ -915,7 +915,7 @@
{
"aggregations": [
{
"metricName": "mysql.table.io.wait.count",
"metricName": "mysql_table_io_wait_count",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -926,7 +926,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
},
"functions": [],
"groupBy": [
@@ -1045,7 +1045,7 @@
{
"aggregations": [
{
"metricName": "mysql.table.io.wait.count",
"metricName": "mysql_table_io_wait_count",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -1056,7 +1056,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
},
"functions": [],
"groupBy": [
@@ -1175,7 +1175,7 @@
{
"aggregations": [
{
"metricName": "mysql.table.io.wait.count",
"metricName": "mysql_table_io_wait_count",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -1186,7 +1186,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
},
"functions": [],
"groupBy": [
@@ -1305,7 +1305,7 @@
{
"aggregations": [
{
"metricName": "mysql.handlers",
"metricName": "mysql_handlers",
"reduceTo": "avg",
"spaceAggregation": "sum",
"temporality": null,
@@ -1316,7 +1316,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
},
"functions": [],
"groupBy": [
@@ -1329,7 +1329,7 @@
}
],
"having": {
"expression": "sum(mysql.handlers) \u003e 0"
"expression": "sum(mysql_handlers) \u003e 0"
},
"legend": "{{kind}}",
"limit": null,
@@ -1434,7 +1434,7 @@
{
"aggregations": [
{
"metricName": "mysql.locks",
"metricName": "mysql_locks",
"reduceTo": "avg",
"spaceAggregation": "sum",
"temporality": null,
@@ -1445,7 +1445,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
},
"functions": [],
"groupBy": [
@@ -1563,7 +1563,7 @@
{
"aggregations": [
{
"metricName": "mysql.log_operations",
"metricName": "mysql_log_operations",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -1574,7 +1574,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
},
"functions": [],
"groupBy": [
@@ -1693,7 +1693,7 @@
{
"aggregations": [
{
"metricName": "mysql.connection.count",
"metricName": "mysql_connection_count",
"reduceTo": "avg",
"spaceAggregation": "sum",
"temporality": null,
@@ -1704,7 +1704,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
},
"functions": [],
"groupBy": [],
@@ -1720,7 +1720,7 @@
{
"aggregations": [
{
"metricName": "mysql.connection.errors",
"metricName": "mysql_connection_errors",
"reduceTo": "avg",
"spaceAggregation": "sum",
"temporality": null,
@@ -1731,7 +1731,7 @@
"disabled": false,
"expression": "B",
"filter": {
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
},
"functions": [],
"groupBy": [
@@ -1745,7 +1745,7 @@
}
],
"having": {
"expression": "sum(mysql.connection.errors) \u003e 0"
"expression": "sum(mysql_connection_errors) \u003e 0"
},
"legend": "Error count: {{error}}",
"limit": null,
@@ -1850,7 +1850,7 @@
{
"aggregations": [
{
"metricName": "mysql.opened_resources",
"metricName": "mysql_opened_resources",
"reduceTo": "avg",
"spaceAggregation": "sum",
"temporality": null,
@@ -1861,7 +1861,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
},
"functions": [],
"groupBy": [
@@ -1874,7 +1874,7 @@
}
],
"having": {
"expression": "sum(mysql.opened_resources) \u003e 0"
"expression": "sum(mysql_opened_resources) \u003e 0"
},
"legend": "{{kind}}",
"limit": null,
@@ -1979,7 +1979,7 @@
{
"aggregations": [
{
"metricName": "mysql.operations",
"metricName": "mysql_operations",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -1990,7 +1990,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
},
"functions": [],
"groupBy": [
@@ -2109,7 +2109,7 @@
{
"aggregations": [
{
"metricName": "mysql.row_locks",
"metricName": "mysql_row_locks",
"reduceTo": "avg",
"spaceAggregation": "sum",
"temporality": null,
@@ -2120,7 +2120,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
},
"functions": [],
"groupBy": [
@@ -2238,7 +2238,7 @@
{
"aggregations": [
{
"metricName": "mysql.threads",
"metricName": "mysql_threads",
"reduceTo": "avg",
"spaceAggregation": "sum",
"temporality": null,
@@ -2249,7 +2249,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
},
"functions": [],
"groupBy": [
@@ -2367,7 +2367,7 @@
{
"aggregations": [
{
"metricName": "mysql.row_operations",
"metricName": "mysql_row_operations",
"reduceTo": "avg",
"spaceAggregation": "sum",
"temporality": null,
@@ -2378,7 +2378,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
},
"functions": [],
"groupBy": [
@@ -2497,7 +2497,7 @@
{
"aggregations": [
{
"metricName": "mysql.prepared_statements",
"metricName": "mysql_prepared_statements",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -2508,7 +2508,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
},
"functions": [],
"groupBy": [

View File

@@ -287,7 +287,7 @@
"multiSelect": true,
"name": "table_name",
"order": 0,
"queryValue": "SELECT JSONExtractString(labels, 'postgresql.table.name') AS table_name\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'postgresql.operations' AND JSONExtractString(labels, 'postgresql.database.name') IN {{.db_name}}\nGROUP BY table_name",
"queryValue": "SELECT JSONExtractString(labels, 'postgresql_table_name') AS table_name\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'postgresql_operations' AND JSONExtractString(labels, 'postgresql_database_name') IN {{.db_name}}\nGROUP BY table_name",
"selectedValue": [
"public.pgbench_accounts",
"public.pgbench_branches",
@@ -309,7 +309,7 @@
"multiSelect": true,
"name": "db_name",
"order": 0,
"queryValue": "SELECT JSONExtractString(labels, 'postgresql.database.name') AS db_name\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'postgresql.operations'\nGROUP BY db_name",
"queryValue": "SELECT JSONExtractString(labels, 'postgresql_database_name') AS db_name\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'postgresql_operations'\nGROUP BY db_name",
"selectedValue": [
"pgtestdb"
],
@@ -335,7 +335,7 @@
{
"aggregations": [
{
"metricName": "postgresql.operations",
"metricName": "postgresql_operations",
"reduceTo": "sum",
"spaceAggregation": "avg",
"temporality": null,
@@ -346,22 +346,22 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "(operation = 'ins' AND postgresql.database.name IN $db_name)"
"expression": "(operation = 'ins' AND postgresql_database_name IN $db_name)"
},
"groupBy": [
{
"dataType": "string",
"id": "postgresql.database.name--string--tag--false",
"id": "postgresql_database_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql.database.name",
"key": "postgresql_database_name",
"type": "tag"
}
],
"having": {
"expression": ""
},
"legend": "{{postgresql.database.name}}",
"legend": "{{postgresql_database_name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
@@ -410,7 +410,7 @@
{
"aggregations": [
{
"metricName": "postgresql.operations",
"metricName": "postgresql_operations",
"reduceTo": "sum",
"spaceAggregation": "avg",
"temporality": null,
@@ -421,22 +421,22 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "(operation = 'upd' AND postgresql.database.name IN $db_name)"
"expression": "(operation = 'upd' AND postgresql_database_name IN $db_name)"
},
"groupBy": [
{
"dataType": "string",
"id": "postgresql.database.name--string--tag--false",
"id": "postgresql_database_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql.database.name",
"key": "postgresql_database_name",
"type": "tag"
}
],
"having": {
"expression": ""
},
"legend": "{{postgresql.database.name}}",
"legend": "{{postgresql_database_name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
@@ -485,7 +485,7 @@
{
"aggregations": [
{
"metricName": "postgresql.operations",
"metricName": "postgresql_operations",
"reduceTo": "sum",
"spaceAggregation": "avg",
"temporality": null,
@@ -496,22 +496,22 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "(operation = 'del' AND postgresql.database.name IN $db_name)"
"expression": "(operation = 'del' AND postgresql_database_name IN $db_name)"
},
"groupBy": [
{
"dataType": "string",
"id": "postgresql.database.name--string--tag--false",
"id": "postgresql_database_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql.database.name",
"key": "postgresql_database_name",
"type": "tag"
}
],
"having": {
"expression": ""
},
"legend": "{{postgresql.database.name}}",
"legend": "{{postgresql_database_name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
@@ -560,7 +560,7 @@
{
"aggregations": [
{
"metricName": "postgresql.operations",
"metricName": "postgresql_operations",
"reduceTo": "sum",
"spaceAggregation": "avg",
"temporality": null,
@@ -571,22 +571,22 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "(operation = 'hot_upd' AND postgresql.database.name IN $db_name)"
"expression": "(operation = 'hot_upd' AND postgresql_database_name IN $db_name)"
},
"groupBy": [
{
"dataType": "string",
"id": "postgresql.database.name--string--tag--false",
"id": "postgresql_database_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql.database.name",
"key": "postgresql_database_name",
"type": "tag"
}
],
"having": {
"expression": ""
},
"legend": "{{postgresql.database.name}}",
"legend": "{{postgresql_database_name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
@@ -635,7 +635,7 @@
{
"aggregations": [
{
"metricName": "postgresql.operations",
"metricName": "postgresql_operations",
"reduceTo": "avg",
"spaceAggregation": "avg",
"temporality": null,
@@ -646,15 +646,15 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "(operation = 'ins' AND postgresql.database.name IN $db_name)"
"expression": "(operation = 'ins' AND postgresql_database_name IN $db_name)"
},
"groupBy": [
{
"dataType": "string",
"id": "postgresql.database.name--string--tag--false",
"id": "postgresql_database_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql.database.name",
"key": "postgresql_database_name",
"type": "tag"
}
],
@@ -670,7 +670,7 @@
{
"aggregations": [
{
"metricName": "postgresql.operations",
"metricName": "postgresql_operations",
"reduceTo": "avg",
"spaceAggregation": "avg",
"temporality": null,
@@ -681,15 +681,15 @@
"disabled": false,
"expression": "B",
"filter": {
"expression": "(operation = 'upd' AND postgresql.database.name IN $db_name)"
"expression": "(operation = 'upd' AND postgresql_database_name IN $db_name)"
},
"groupBy": [
{
"dataType": "string",
"id": "postgresql.database.name--string--tag--false",
"id": "postgresql_database_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql.database.name",
"key": "postgresql_database_name",
"type": "tag"
}
],
@@ -705,7 +705,7 @@
{
"aggregations": [
{
"metricName": "postgresql.operations",
"metricName": "postgresql_operations",
"reduceTo": "avg",
"spaceAggregation": "avg",
"temporality": null,
@@ -716,15 +716,15 @@
"disabled": false,
"expression": "C",
"filter": {
"expression": "(operation = 'del' AND postgresql.database.name IN $db_name)"
"expression": "(operation = 'del' AND postgresql_database_name IN $db_name)"
},
"groupBy": [
{
"dataType": "string",
"id": "postgresql.database.name--string--tag--false",
"id": "postgresql_database_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql.database.name",
"key": "postgresql_database_name",
"type": "tag"
}
],
@@ -780,7 +780,7 @@
{
"aggregations": [
{
"metricName": "postgresql.database.locks",
"metricName": "postgresql_database_locks",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -852,7 +852,7 @@
{
"aggregations": [
{
"metricName": "postgresql.deadlocks",
"metricName": "postgresql_deadlocks",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -863,22 +863,22 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "postgresql.database.name IN $db_name"
"expression": "postgresql_database_name IN $db_name"
},
"groupBy": [
{
"dataType": "string",
"id": "postgresql.database.name--string--tag--false",
"id": "postgresql_database_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql.database.name",
"key": "postgresql_database_name",
"type": "tag"
}
],
"having": {
"expression": ""
},
"legend": "{{postgresql.database.name}}",
"legend": "{{postgresql_database_name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
@@ -927,7 +927,7 @@
{
"aggregations": [
{
"metricName": "postgresql.backends",
"metricName": "postgresql_backends",
"reduceTo": "sum",
"spaceAggregation": "avg",
"temporality": null,
@@ -938,22 +938,22 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "postgresql.database.name IN $db_name"
"expression": "postgresql_database_name IN $db_name"
},
"groupBy": [
{
"dataType": "string",
"id": "postgresql.database.name--string--tag--false",
"id": "postgresql_database_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql.database.name",
"key": "postgresql_database_name",
"type": "tag"
}
],
"having": {
"expression": ""
},
"legend": "{{postgresql.database.name}}",
"legend": "{{postgresql_database_name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
@@ -1002,7 +1002,7 @@
{
"aggregations": [
{
"metricName": "postgresql.rows",
"metricName": "postgresql_rows",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -1013,7 +1013,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "(state = 'dead' AND postgresql.database.name IN $db_name AND postgresql.table.name IN $table_name)"
"expression": "(state = 'dead' AND postgresql_database_name IN $db_name AND postgresql_table_name IN $table_name)"
},
"groupBy": [],
"having": {
@@ -1068,7 +1068,7 @@
{
"aggregations": [
{
"metricName": "postgresql.index.scans",
"metricName": "postgresql_index_scans",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -1079,22 +1079,22 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "(postgresql.database.name IN $db_name AND postgresql.table.name IN $table_name)"
"expression": "(postgresql_database_name IN $db_name AND postgresql_table_name IN $table_name)"
},
"groupBy": [
{
"dataType": "string",
"id": "postgresql.index.name--string--tag--false",
"id": "postgresql_index_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql.index.name",
"key": "postgresql_index_name",
"type": "tag"
}
],
"having": {
"expression": ""
},
"legend": "{{postgresql.index.name}}",
"legend": "{{postgresql_index_name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
@@ -1143,7 +1143,7 @@
{
"aggregations": [
{
"metricName": "postgresql.rows",
"metricName": "postgresql_rows",
"reduceTo": "avg",
"spaceAggregation": "avg",
"temporality": null,
@@ -1154,16 +1154,16 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "(state = 'dead' AND postgresql.database.name IN $db_name AND postgresql.table.name IN $table_name)"
"expression": "(state = 'dead' AND postgresql_database_name IN $db_name AND postgresql_table_name IN $table_name)"
},
"functions": [],
"groupBy": [
{
"dataType": "string",
"id": "postgresql.table.name--string--tag--false",
"id": "postgresql_table_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql.table.name",
"key": "postgresql_table_name",
"type": "tag"
}
],
@@ -1179,7 +1179,7 @@
{
"aggregations": [
{
"metricName": "postgresql.rows",
"metricName": "postgresql_rows",
"reduceTo": "avg",
"spaceAggregation": "avg",
"temporality": null,
@@ -1190,16 +1190,16 @@
"disabled": false,
"expression": "B",
"filter": {
"expression": "(state = 'live' AND postgresql.database.name IN $db_name AND postgresql.table.name IN $table_name)"
"expression": "(state = 'live' AND postgresql_database_name IN $db_name AND postgresql_table_name IN $table_name)"
},
"functions": [],
"groupBy": [
{
"dataType": "string",
"id": "postgresql.table.name--string--tag--false",
"id": "postgresql_table_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql.table.name",
"key": "postgresql_table_name",
"type": "tag"
}
],
@@ -1215,7 +1215,7 @@
{
"aggregations": [
{
"metricName": "postgresql.index.scans",
"metricName": "postgresql_index_scans",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -1226,16 +1226,16 @@
"disabled": false,
"expression": "C",
"filter": {
"expression": "(postgresql.database.name IN $db_name AND postgresql.table.name IN $table_name)"
"expression": "(postgresql_database_name IN $db_name AND postgresql_table_name IN $table_name)"
},
"functions": [],
"groupBy": [
{
"dataType": "string",
"id": "postgresql.table.name--string--tag--false",
"id": "postgresql_table_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql.table.name",
"key": "postgresql_table_name",
"type": "tag"
}
],
@@ -1251,7 +1251,7 @@
{
"aggregations": [
{
"metricName": "postgresql.table.size",
"metricName": "postgresql_table_size",
"reduceTo": "avg",
"spaceAggregation": "avg",
"temporality": null,
@@ -1262,16 +1262,16 @@
"disabled": true,
"expression": "D",
"filter": {
"expression": "(postgresql.database.name IN $db_name AND postgresql.table.name IN $table_name)"
"expression": "(postgresql_database_name IN $db_name AND postgresql_table_name IN $table_name)"
},
"functions": [],
"groupBy": [
{
"dataType": "string",
"id": "postgresql.table.name--string--tag--false",
"id": "postgresql_table_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql.table.name",
"key": "postgresql_table_name",
"type": "tag"
}
],

Some files were not shown because too many files have changed in this diff Show More