Merge pull request #2258 from SigNoz/release/v0.16.1

Release/v0.16.1
This commit is contained in:
Prashant Shahi 2023-02-15 01:47:43 +05:30 committed by GitHub
commit 7c8afc2e1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
65 changed files with 590 additions and 607 deletions

View File

@ -144,6 +144,8 @@ Moreover, SigNoz has few more advanced features wrt Jaeger:
- SigNoz Logs management are based on ClickHouse, a columnar OLAP datastore which makes aggregate log analytics queries much more efficient
- 50% lower resource requirement compared to Elastic during ingestion
We have published benchmarks comparing Elastic with SigNoz. Check it out [here](https://signoz.io/blog/logs-performance-benchmark/?utm_source=github-readme&utm_medium=logs-benchmark)
<p>&nbsp </p>
### SigNoz vs Loki
@ -152,6 +154,8 @@ Moreover, SigNoz has few more advanced features wrt Jaeger:
- SigNoz supports indexes over high cardinality data and has no limitations on the number of indexes, while Loki reaches max streams with a few indexes added to it.
- Searching over a huge volume of data is difficult and slow in Loki compared to SigNoz
We have published benchmarks comparing Loki with SigNoz. Check it out [here](https://signoz.io/blog/logs-performance-benchmark/?utm_source=github-readme&utm_medium=logs-benchmark)
<br /><br />
<img align="left" src="https://signoz-public.s3.us-east-2.amazonaws.com/Contributors.svg" width="50px" />

View File

@ -137,7 +137,7 @@ services:
condition: on-failure
query-service:
image: signoz/query-service:0.16.0
image: signoz/query-service:0.16.1
command: ["-config=/root/config/prometheus.yml"]
# ports:
# - "6060:6060" # pprof port
@ -166,7 +166,7 @@ services:
<<: *clickhouse-depend
frontend:
image: signoz/frontend:0.16.0
image: signoz/frontend:0.16.1
deploy:
restart_policy:
condition: on-failure

View File

@ -153,7 +153,7 @@ services:
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
query-service:
image: signoz/query-service:${DOCKER_TAG:-0.16.0}
image: signoz/query-service:${DOCKER_TAG:-0.16.1}
container_name: query-service
command: ["-config=/root/config/prometheus.yml"]
# ports:
@ -181,7 +181,7 @@ services:
<<: *clickhouse-depend
frontend:
image: signoz/frontend:${DOCKER_TAG:-0.16.0}
image: signoz/frontend:${DOCKER_TAG:-0.16.1}
container_name: frontend
restart: on-failure
depends_on:

View File

@ -228,7 +228,7 @@ wait_for_containers_start() {
# The while loop is important because for-loops don't work for dynamic values
while [[ $timeout -gt 0 ]]; do
status_code="$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3301/api/v1/services/list || true)"
status_code="$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:3301/api/v1/health?live=1" || true)"
if [[ status_code -eq 200 ]]; then
break
else

View File

@ -174,6 +174,7 @@
"lint-staged": "^12.3.7",
"portfinder-sync": "^0.0.2",
"prettier": "2.2.1",
"react-hooks-testing-library": "0.6.0",
"react-hot-loader": "^4.13.0",
"react-resizable": "3.0.4",
"ts-jest": "^27.1.4",

View File

@ -1,11 +1,11 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { notification } from 'antd';
import getLocalStorageApi from 'api/browser/localstorage/get';
import loginApi from 'api/user/login';
import { Logout } from 'api/utils';
import Spinner from 'components/Spinner';
import { LOCALSTORAGE } from 'constants/localStorage';
import ROUTES from 'constants/routes';
import { useNotifications } from 'hooks/useNotifications';
import history from 'lib/history';
import React, { useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
@ -47,7 +47,7 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
const dispatch = useDispatch<Dispatch<AppActions>>();
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const currentRoute = mapRoutes.get('current');
@ -157,12 +157,7 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
// NOTE: disabling this rule as there is no need to have div
// eslint-disable-next-line react/jsx-no-useless-fragment
return (
<>
{NotificationElement}
{children}
</>
);
return <>{children}</>;
}
interface PrivateRouteProps {

View File

@ -3,6 +3,7 @@ import NotFound from 'components/NotFound';
import Spinner from 'components/Spinner';
import AppLayout from 'container/AppLayout';
import { useThemeConfig } from 'hooks/useDarkMode';
import { NotificationProvider } from 'hooks/useNotifications';
import history from 'lib/history';
import React, { Suspense } from 'react';
import { Route, Router, Switch } from 'react-router-dom';
@ -15,26 +16,28 @@ function App(): JSX.Element {
return (
<ConfigProvider theme={themeConfig}>
<Router history={history}>
<PrivateRoute>
<AppLayout>
<Suspense fallback={<Spinner size="large" tip="Loading..." />}>
<Switch>
{routes.map(({ path, component, exact }) => (
<Route
key={`${path}`}
exact={exact}
path={path}
component={component}
/>
))}
<NotificationProvider>
<Router history={history}>
<PrivateRoute>
<AppLayout>
<Suspense fallback={<Spinner size="large" tip="Loading..." />}>
<Switch>
{routes.map(({ path, component, exact }) => (
<Route
key={`${path}`}
exact={exact}
path={path}
component={component}
/>
))}
<Route path="*" component={NotFound} />
</Switch>
</Suspense>
</AppLayout>
</PrivateRoute>
</Router>
<Route path="*" component={NotFound} />
</Switch>
</Suspense>
</AppLayout>
</PrivateRoute>
</Router>
</NotificationProvider>
</ConfigProvider>
);
}

View File

@ -10,7 +10,7 @@ const getSpans = async (
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try {
const updatedSelectedTags = props.selectedTags.map((e) => ({
Key: e.Key[0],
Key: e.Key,
Operator: e.Operator,
StringValues: e.StringValues,
NumberValues: e.NumberValues,

View File

@ -28,7 +28,7 @@ const getSpanAggregate = async (
});
const updatedSelectedTags = props.selectedTags.map((e) => ({
Key: e.Key[0],
Key: e.Key,
Operator: e.Operator,
StringValues: e.StringValues,
NumberValues: e.NumberValues,

View File

@ -1,4 +1,5 @@
import { notification, Popover } from 'antd';
import { Popover } from 'antd';
import { useNotifications } from 'hooks/useNotifications';
import React, { useCallback, useEffect } from 'react';
import { useCopyToClipboard } from 'react-use';
@ -7,7 +8,7 @@ function CopyClipboardHOC({
children,
}: CopyClipboardHOCProps): JSX.Element {
const [value, setCopy] = useCopyToClipboard();
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
useEffect(() => {
if (value.value) {
notifications.success({
@ -22,7 +23,6 @@ function CopyClipboardHOC({
return (
<span onClick={onClick} onKeyDown={onClick} role="button" tabIndex={0}>
{NotificationElement}
<Popover
placement="top"
content={<span style={{ fontSize: '0.9rem' }}>Copy to clipboard</span>}

View File

@ -1,8 +1,9 @@
import { blue, grey, orange } from '@ant-design/colors';
import { CopyFilled, ExpandAltOutlined } from '@ant-design/icons';
import { Button, Divider, notification, Row, Typography } from 'antd';
import { Button, Divider, Row, Typography } from 'antd';
import { map } from 'd3';
import dayjs from 'dayjs';
import { useNotifications } from 'hooks/useNotifications';
import { FlatLogData } from 'lib/logs/flatLogData';
import React, { useCallback, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
@ -79,7 +80,7 @@ function LogItem({ logData }: LogItemProps): JSX.Element {
const dispatch = useDispatch();
const flattenLogData = useMemo(() => FlatLogData(logData), [logData]);
const [, setCopy] = useCopyToClipboard();
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const handleDetailedView = useCallback(() => {
dispatch({
@ -97,7 +98,6 @@ function LogItem({ logData }: LogItemProps): JSX.Element {
return (
<Container>
{NotificationElement}
<div>
<div>
{'{'}

View File

@ -1,9 +1,10 @@
/* eslint-disable react/display-name */
import { Button, notification } from 'antd';
import { Button } from 'antd';
import { ColumnsType } from 'antd/lib/table';
import { ResizeTable } from 'components/ResizeTable';
import ROUTES from 'constants/routes';
import useComponentPermission from 'hooks/useComponentPermission';
import { useNotifications } from 'hooks/useNotifications';
import history from 'lib/history';
import React, { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
@ -17,7 +18,7 @@ import Delete from './Delete';
function AlertChannels({ allChannels }: AlertChannelsProps): JSX.Element {
const { t } = useTranslation(['channels']);
const [notifications, Element] = notification.useNotification();
const { notifications } = useNotifications();
const [channels, setChannels] = useState<Channels[]>(allChannels);
const { role } = useSelector<AppState, AppReducer>((state) => state.app);
const [action] = useComponentPermission(['new_alert_action'], role);
@ -63,12 +64,7 @@ function AlertChannels({ allChannels }: AlertChannelsProps): JSX.Element {
});
}
return (
<>
{Element}
<ResizeTable columns={columns} dataSource={channels} rowKey="id" />
</>
);
return <ResizeTable columns={columns} dataSource={channels} rowKey="id" />;
}
interface AlertChannelsProps {

View File

@ -3,7 +3,6 @@ import {
Button,
Card,
Input,
notification,
Space,
TableProps,
Tooltip,
@ -18,6 +17,7 @@ import getErrorCounts from 'api/errors/getErrorCounts';
import { ResizeTable } from 'components/ResizeTable';
import ROUTES from 'constants/routes';
import dayjs from 'dayjs';
import { useNotifications } from 'hooks/useNotifications';
import useUrlQuery from 'hooks/useUrlQuery';
import createQueryParams from 'lib/createQueryParams';
import history from 'lib/history';
@ -127,7 +127,7 @@ function AllErrors(): JSX.Element {
enabled: !loading,
},
]);
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
useEffect(() => {
if (data?.error) {
@ -386,24 +386,21 @@ function AllErrors(): JSX.Element {
);
return (
<>
{NotificationElement}
<ResizeTable
columns={columns}
tableLayout="fixed"
dataSource={data?.payload as Exception[]}
rowKey="firstSeen"
loading={isLoading || false || errorCountResponse.status === 'loading'}
pagination={{
pageSize: getUpdatedPageSize,
responsive: true,
current: getUpdatedOffset / 10 + 1,
position: ['bottomLeft'],
total: errorCountResponse.data?.payload || 0,
}}
onChange={onChangeHandler}
/>
</>
<ResizeTable
columns={columns}
tableLayout="fixed"
dataSource={data?.payload as Exception[]}
rowKey="firstSeen"
loading={isLoading || false || errorCountResponse.status === 'loading'}
pagination={{
pageSize: getUpdatedPageSize,
responsive: true,
current: getUpdatedOffset / 10 + 1,
position: ['bottomLeft'],
total: errorCountResponse.data?.payload || 0,
}}
onChange={onChangeHandler}
/>
);
}

View File

@ -1,4 +1,3 @@
import { notification } from 'antd';
import getDynamicConfigs from 'api/dynamicConfigs/getDynamicConfigs';
import getFeaturesFlags from 'api/features/getFeatureFlags';
import getUserLatestVersion from 'api/user/getLatestVersion';
@ -6,6 +5,7 @@ import getUserVersion from 'api/user/getVersion';
import Header from 'container/Header';
import SideNav from 'container/SideNav';
import TopNav from 'container/TopNav';
import { useNotifications } from 'hooks/useNotifications';
import React, { ReactNode, useEffect, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { useQueries } from 'react-query';
@ -91,7 +91,7 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
const latestVersionCounter = useRef(0);
const latestConfigCounter = useRef(0);
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
useEffect(() => {
if (
@ -228,7 +228,6 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
return (
<Layout>
{NotificationElement}
{isToDisplayLayout && <Header />}
<Layout>
{isToDisplayLayout && <SideNav />}

View File

@ -1,4 +1,4 @@
import { Form, notification } from 'antd';
import { Form } from 'antd';
import createPagerApi from 'api/channels/createPager';
import createSlackApi from 'api/channels/createSlack';
import createWebhookApi from 'api/channels/createWebhook';
@ -7,6 +7,7 @@ import testSlackApi from 'api/channels/testSlack';
import testWebhookApi from 'api/channels/testWebhook';
import ROUTES from 'constants/routes';
import FormAlertChannels from 'container/FormAlertChannels';
import { useNotifications } from 'hooks/useNotifications';
import history from 'lib/history';
import React, { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
@ -58,7 +59,7 @@ function CreateAlertChannels({
});
const [savingState, setSavingState] = useState<boolean>(false);
const [testingState, setTestingState] = useState<boolean>(false);
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const [type, setType] = useState<ChannelType>(preType);
const onTypeChangeHandler = useCallback(
@ -336,7 +337,6 @@ function CreateAlertChannels({
onSaveHandler,
savingState,
testingState,
NotificationElement,
title: t('page_title_create'),
initialValue: {
type,

View File

@ -1,4 +1,4 @@
import { Form, notification } from 'antd';
import { Form } from 'antd';
import editPagerApi from 'api/channels/editPager';
import editSlackApi from 'api/channels/editSlack';
import editWebhookApi from 'api/channels/editWebhook';
@ -17,6 +17,7 @@ import {
WebhookType,
} from 'container/CreateAlertChannels/config';
import FormAlertChannels from 'container/FormAlertChannels';
import { useNotifications } from 'hooks/useNotifications';
import history from 'lib/history';
import React, { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
@ -36,7 +37,7 @@ function EditAlertChannels({
});
const [savingState, setSavingState] = useState<boolean>(false);
const [testingState, setTestingState] = useState<boolean>(false);
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const { id } = useParams<{ id: string }>();
const [type, setType] = useState<ChannelType>(
@ -281,7 +282,6 @@ function EditAlertChannels({
onSaveHandler,
testingState,
savingState,
NotificationElement,
title: t('page_title_edit'),
initialValue,
editing: true,

View File

@ -1,9 +1,10 @@
import { Button, Divider, notification, Space, Typography } from 'antd';
import { Button, Divider, Space, Typography } from 'antd';
import getNextPrevId from 'api/errors/getNextPrevId';
import Editor from 'components/Editor';
import { ResizeTable } from 'components/ResizeTable';
import { getNanoSeconds } from 'container/AllError/utils';
import dayjs from 'dayjs';
import { useNotifications } from 'hooks/useNotifications';
import history from 'lib/history';
import { urlKey } from 'pages/ErrorDetails/utils';
import React, { useMemo, useState } from 'react';
@ -80,7 +81,7 @@ function ErrorDetails(props: ErrorDetailsProps): JSX.Element {
[],
);
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const onClickErrorIdHandler = async (
id: string,
@ -121,7 +122,6 @@ function ErrorDetails(props: ErrorDetailsProps): JSX.Element {
return (
<>
{NotificationElement}
<Typography>{errorDetail.exceptionType}</Typography>
<Typography>{errorDetail.exceptionMessage}</Typography>
<Divider />

View File

@ -31,7 +31,6 @@ function FormAlertChannels({
onSaveHandler,
savingState,
testingState,
NotificationElement,
title,
initialValue,
editing = false,
@ -53,8 +52,6 @@ function FormAlertChannels({
};
return (
<>
{NotificationElement}
<Title level={3}>{title}</Title>
<Form initialValues={initialValue} layout="vertical" form={formInstance}>
@ -126,10 +123,6 @@ interface FormAlertChannelsProps {
onTestHandler: (props: ChannelType) => void;
testingState: boolean;
savingState: boolean;
NotificationElement: React.ReactElement<
unknown,
string | React.JSXElementConstructor<unknown>
>;
title: string;
initialValue: Store;
// editing indicates if the form is opened in edit mode

View File

@ -1,6 +1,7 @@
import { notification, Select } from 'antd';
import { Select } from 'antd';
import getChannels from 'api/channels/getAll';
import useFetch from 'hooks/useFetch';
import { useNotifications } from 'hooks/useNotifications';
import React from 'react';
import { useTranslation } from 'react-i18next';
@ -20,7 +21,7 @@ function ChannelSelect({
const { loading, payload, error, errorMessage } = useFetch(getChannels);
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const handleChange = (value: string[]): void => {
onSelectChannels(value);
@ -50,22 +51,19 @@ function ChannelSelect({
return children;
};
return (
<>
{NotificationElement}
<StyledSelect
status={error ? 'error' : ''}
mode="multiple"
style={{ width: '100%' }}
placeholder={t('placeholder_channel_select')}
value={currentValue}
onChange={(value): void => {
handleChange(value as string[]);
}}
optionLabelProp="label"
>
{renderOptions()}
</StyledSelect>
</>
<StyledSelect
status={error ? 'error' : ''}
mode="multiple"
style={{ width: '100%' }}
placeholder={t('placeholder_channel_select')}
value={currentValue}
onChange={(value): void => {
handleChange(value as string[]);
}}
optionLabelProp="label"
>
{renderOptions()}
</StyledSelect>
);
}

View File

@ -1,11 +1,12 @@
import { PlusOutlined } from '@ant-design/icons';
import { Button, notification, Tabs } from 'antd';
import { Button, Tabs } from 'antd';
import MetricsBuilderFormula from 'container/NewWidget/LeftContainer/QuerySection/QueryBuilder/queryBuilder/formula';
import MetricsBuilder from 'container/NewWidget/LeftContainer/QuerySection/QueryBuilder/queryBuilder/query';
import {
IQueryBuilderFormulaHandleChange,
IQueryBuilderQueryHandleChange,
} from 'container/NewWidget/LeftContainer/QuerySection/QueryBuilder/queryBuilder/types';
import { useNotifications } from 'hooks/useNotifications';
import React, { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { AlertTypes } from 'types/api/alerts/alertTypes';
@ -163,7 +164,7 @@ function QuerySection({
...allQueries,
});
};
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const addMetricQuery = useCallback(() => {
if (Object.keys(metricQueries).length > 5) {
@ -351,7 +352,6 @@ function QuerySection({
};
return (
<>
{NotificationElement}
<StepHeading> {t('alert_form_step1')}</StepHeading>
<FormContainer>
<div style={{ display: 'flex' }}>{renderTabs(alertType)}</div>

View File

@ -1,10 +1,11 @@
import { ExclamationCircleOutlined, SaveOutlined } from '@ant-design/icons';
import { Col, FormInstance, Modal, notification, Typography } from 'antd';
import { Col, FormInstance, Modal, Typography } from 'antd';
import saveAlertApi from 'api/alerts/save';
import testAlertApi from 'api/alerts/testAlert';
import ROUTES from 'constants/routes';
import QueryTypeTag from 'container/NewWidget/LeftContainer/QueryTypeTag';
import PlotTag from 'container/NewWidget/LeftContainer/WidgetGraph/PlotTag';
import { useNotifications } from 'hooks/useNotifications';
import history from 'lib/history';
import React, { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
@ -190,7 +191,7 @@ function FormAlertRules({
});
}
};
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const validatePromParams = useCallback((): boolean => {
let retval = true;
@ -483,7 +484,6 @@ function FormAlertRules({
);
return (
<>
{NotificationElement}
{Element}
<PanelContainer>
<StyledLeftContainer flex="5 1 600px">

View File

@ -1,19 +1,10 @@
/* eslint-disable sonarjs/no-duplicate-string */
import { LoadingOutlined } from '@ant-design/icons';
import {
Button,
Card,
Col,
Divider,
Modal,
notification,
Row,
Spin,
Typography,
} from 'antd';
import { Button, Card, Col, Divider, Modal, Row, Spin, Typography } from 'antd';
import setRetentionApi from 'api/settings/setRetention';
import TextToolTip from 'components/TextToolTip';
import useComponentPermission from 'hooks/useComponentPermission';
import { useNotifications } from 'hooks/useNotifications';
import find from 'lodash-es/find';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
@ -172,7 +163,7 @@ function GeneralSettings({
logsTtlValuesPayload.status === 'pending' ? 1000 : null,
);
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const onModalToggleHandler = (type: TTTLType): void => {
if (type === 'metrics') setModalMetrics((modal) => !modal);
@ -593,7 +584,6 @@ function GeneralSettings({
return (
<>
{NotificationElement}
{Element}
<Col xs={24} md={22} xl={20} xxl={18} style={{ margin: 'auto' }}>
<ErrorTextContainer>

View File

@ -1,7 +1,8 @@
/* eslint-disable react/no-unstable-nested-components */
import { notification } from 'antd';
import updateDashboardApi from 'api/dashboard/update';
import useComponentPermission from 'hooks/useComponentPermission';
import { useNotifications } from 'hooks/useNotifications';
import React, { useCallback, useEffect, useState } from 'react';
import { Layout } from 'react-grid-layout';
import { useTranslation } from 'react-i18next';
@ -204,7 +205,7 @@ function GridGraph(props: Props): JSX.Element {
[widgets, onDragSelect],
);
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const onEmptyWidgetHandler = useCallback(async () => {
try {
@ -276,21 +277,18 @@ function GridGraph(props: Props): JSX.Element {
}, [layouts, onEmptyWidgetHandler, t, toggleAddWidget, notifications]);
return (
<>
{NotificationElement}
<GraphLayoutContainer
{...{
addPanelLoading,
layouts,
onAddPanelHandler,
onLayoutChangeHandler,
onLayoutSaveHandler,
saveLayoutState,
widgets,
setLayout,
}}
/>
</>
<GraphLayoutContainer
{...{
addPanelLoading,
layouts,
onAddPanelHandler,
onLayoutChangeHandler,
onLayoutSaveHandler,
saveLayoutState,
widgets,
setLayout,
}}
/>
);
}

View File

@ -1,6 +1,7 @@
import { Button, Form, Input, notification } from 'antd';
import { Button, Form, Input } from 'antd';
import getFeaturesFlags from 'api/features/getFeatureFlags';
import apply from 'api/licenses/apply';
import { useNotifications } from 'hooks/useNotifications';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { QueryObserverResult, RefetchOptions, useQuery } from 'react-query';
@ -27,7 +28,7 @@ function ApplyLicenseForm({
enabled: false,
});
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const onFinish = async (values: unknown | { key: string }): Promise<void> => {
const params = values as { key: string };
@ -77,7 +78,6 @@ function ApplyLicenseForm({
return (
<ApplyFormContainer>
{NotificationElement}
<ApplyForm layout="inline" onFinish={onFinish}>
<LicenseInput labelAlign="left" name="key">
<Input

View File

@ -1,12 +1,13 @@
/* eslint-disable react/display-name */
import { PlusOutlined } from '@ant-design/icons';
import { notification, Typography } from 'antd';
import { Typography } from 'antd';
import { ColumnsType } from 'antd/lib/table';
import { ResizeTable } from 'components/ResizeTable';
import TextToolTip from 'components/TextToolTip';
import ROUTES from 'constants/routes';
import useComponentPermission from 'hooks/useComponentPermission';
import useInterval from 'hooks/useInterval';
import { useNotifications } from 'hooks/useNotifications';
import history from 'lib/history';
import React, { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
@ -31,7 +32,7 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
role,
);
const [notificationsApi, NotificationElement] = notification.useNotification();
const { notifications: notificationsApi } = useNotifications();
useInterval(() => {
(async (): Promise<void> => {
@ -51,8 +52,6 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
history.push(ROUTES.ALERTS_NEW);
}, []);
const [notifications, Element] = notification.useNotification();
const onEditHandler = (id: string): void => {
history.push(`${ROUTES.EDIT_ALERTS}?ruleId=${id}`);
};
@ -144,7 +143,7 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
Edit
</ColumnButton>
<DeleteAlert notifications={notifications} setData={setData} id={id} />
<DeleteAlert notifications={notificationsApi} setData={setData} id={id} />
</>
),
});
@ -152,9 +151,6 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
return (
<>
{NotificationElement}
{Element}
<ButtonContainer>
<TextToolTip
{...{

View File

@ -1,6 +1,6 @@
import { notification } from 'antd';
import patchAlert from 'api/alerts/patch';
import { State } from 'hooks/useFetch';
import { useNotifications } from 'hooks/useNotifications';
import React, { useState } from 'react';
import { GettableAlert } from 'types/api/alerts/get';
import { PayloadProps as PatchPayloadProps } from 'types/api/alerts/patch';
@ -20,7 +20,7 @@ function ToggleAlertState({
payload: undefined,
});
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const defaultErrorMessage = 'Something went wrong';
@ -90,17 +90,14 @@ function ToggleAlertState({
};
return (
<>
{NotificationElement}
<ColumnButton
disabled={apiStatus.loading || false}
loading={apiStatus.loading || false}
onClick={(): Promise<void> => onToggleHandler(id, !disabled)}
type="link"
>
{disabled ? 'Enable' : 'Disable'}
</ColumnButton>
</>
<ColumnButton
disabled={apiStatus.loading || false}
loading={apiStatus.loading || false}
onClick={(): Promise<void> => onToggleHandler(id, !disabled)}
type="link"
>
{disabled ? 'Enable' : 'Disable'}
</ColumnButton>
);
}

View File

@ -1,7 +1,8 @@
import { notification, Space } from 'antd';
import { Space } from 'antd';
import getAll from 'api/alerts/getAll';
import ReleaseNote from 'components/ReleaseNote';
import Spinner from 'components/Spinner';
import { useNotifications } from 'hooks/useNotifications';
import React, { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { useQuery } from 'react-query';
@ -17,7 +18,7 @@ function ListAlertRules(): JSX.Element {
cacheTime: 0,
});
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
useEffect(() => {
if (status === 'error' || (status === 'success' && data.statusCode >= 400)) {
@ -29,26 +30,18 @@ function ListAlertRules(): JSX.Element {
// api failed to load the data
if (isError) {
return (
<div>
{NotificationElement}
{data?.error || t('something_went_wrong')}
</div>
);
return <div>{data?.error || t('something_went_wrong')}</div>;
}
// api is successful but error is present
if (status === 'success' && data.statusCode >= 400) {
return (
<>
{NotificationElement}
<ListAlert
{...{
allAlertRules: [],
refetch,
}}
/>
</>
<ListAlert
{...{
allAlertRules: [],
refetch,
}}
/>
);
}
@ -59,7 +52,6 @@ function ListAlertRules(): JSX.Element {
return (
<Space direction="vertical" size="large" style={{ width: '100%' }}>
{NotificationElement}
<ReleaseNote path={location.pathname} />
<ListAlert
{...{

View File

@ -1,17 +1,10 @@
import { red } from '@ant-design/colors';
import { ExclamationCircleTwoTone } from '@ant-design/icons';
import {
Button,
Modal,
notification,
Space,
Typography,
Upload,
UploadProps,
} from 'antd';
import { Button, Modal, Space, Typography, Upload, UploadProps } from 'antd';
import createDashboard from 'api/dashboard/create';
import Editor from 'components/Editor';
import ROUTES from 'constants/routes';
import { useNotifications } from 'hooks/useNotifications';
import history from 'lib/history';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
@ -41,7 +34,7 @@ function ImportJSON({
const [editorValue, setEditorValue] = useState<string>('');
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const onChangeHandler: UploadProps['onChange'] = (info) => {
const { fileList } = info;
@ -132,61 +125,58 @@ function ImportJSON({
);
return (
<>
{NotificationElement}
<Modal
open={isImportJSONModalVisible}
centered
maskClosable
destroyOnClose
width="70vw"
onCancel={onModalHandler}
title={
<>
<Typography.Title level={4}>{t('import_json')}</Typography.Title>
<Typography>{t('import_dashboard_by_pasting')}</Typography>
</>
}
footer={
<FooterContainer>
<Button
disabled={editorValue.length === 0}
onClick={onClickLoadJsonHandler}
loading={dashboardCreating}
>
{t('load_json')}
</Button>
{isCreateDashboardError && getErrorNode(t('error_loading_json'))}
</FooterContainer>
}
>
<div>
<Space direction="horizontal">
<Upload
accept=".json"
showUploadList={false}
multiple={false}
onChange={onChangeHandler}
beforeUpload={(): boolean => false}
action="none"
data={jsonData}
>
<Button type="primary">{t('upload_json_file')}</Button>
</Upload>
{isUploadJSONError && <>{getErrorNode(t('error_upload_json'))}</>}
</Space>
<Modal
open={isImportJSONModalVisible}
centered
maskClosable
destroyOnClose
width="70vw"
onCancel={onModalHandler}
title={
<>
<Typography.Title level={4}>{t('import_json')}</Typography.Title>
<Typography>{t('import_dashboard_by_pasting')}</Typography>
</>
}
footer={
<FooterContainer>
<Button
disabled={editorValue.length === 0}
onClick={onClickLoadJsonHandler}
loading={dashboardCreating}
>
{t('load_json')}
</Button>
{isCreateDashboardError && getErrorNode(t('error_loading_json'))}
</FooterContainer>
}
>
<div>
<Space direction="horizontal">
<Upload
accept=".json"
showUploadList={false}
multiple={false}
onChange={onChangeHandler}
beforeUpload={(): boolean => false}
action="none"
data={jsonData}
>
<Button type="primary">{t('upload_json_file')}</Button>
</Upload>
{isUploadJSONError && <>{getErrorNode(t('error_upload_json'))}</>}
</Space>
<EditorContainer>
<Typography.Paragraph>{t('paste_json_below')}</Typography.Paragraph>
<Editor
onChange={(newValue): void => setEditorValue(newValue)}
value={editorValue}
language="json"
/>
</EditorContainer>
</div>
</Modal>
</>
<EditorContainer>
<Typography.Paragraph>{t('paste_json_below')}</Typography.Paragraph>
<Editor
onChange={(newValue): void => setEditorValue(newValue)}
value={editorValue}
language="json"
/>
</EditorContainer>
</div>
</Modal>
);
}

View File

@ -1,8 +1,9 @@
import { Button, Input, notification, Space, Tooltip, Typography } from 'antd';
import { Button, Input, Space, Tooltip, Typography } from 'antd';
import loginApi from 'api/user/login';
import loginPrecheckApi from 'api/user/loginPrecheck';
import afterLogin from 'AppRoutes/utils';
import ROUTES from 'constants/routes';
import { useNotifications } from 'hooks/useNotifications';
import history from 'lib/history';
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
@ -42,7 +43,7 @@ function Login({
const [precheckInProcess, setPrecheckInProcess] = useState(false);
const [precheckComplete, setPrecheckComplete] = useState(false);
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
useEffect(() => {
if (withPassword === 'Y') {
@ -185,7 +186,6 @@ function Login({
return (
<FormWrapper>
{NotificationElement}
<FormContainer onSubmit={onSubmitHandler}>
<Title level={4}>{t('login_page_title')}</Title>
<ParentContainer>

View File

@ -1,4 +1,4 @@
import { notification } from 'antd';
import { useNotifications } from 'hooks/useNotifications';
import { flatten } from 'lodash-es';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useSelector } from 'react-redux';
@ -36,7 +36,7 @@ function SearchFields({
const keyPrefixRef = useRef(hashCode(JSON.stringify(fieldsQuery)));
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
useEffect(() => {
const updatedFieldsQuery = createParsedQueryStructure([
@ -102,7 +102,6 @@ function SearchFields({
return (
<>
{NotificationElement}
<QueryBuilder
key={keyPrefixRef.current}
keyPrefix={keyPrefixRef.current}

View File

@ -6,7 +6,7 @@ import { Tags } from 'types/reducer/trace';
export const dbSystemTags: Tags[] = [
{
Key: ['db.system.(string)'],
Key: 'db.system.(string)',
StringValues: [''],
NumberValues: [],
BoolValues: [],

View File

@ -1,5 +1,6 @@
import { Button, notification, Space, Typography } from 'antd';
import { Button, Space, Typography } from 'antd';
import changeMyPassword from 'api/user/changeMyPassword';
import { useNotifications } from 'hooks/useNotifications';
import { isPasswordNotValidMessage, isPasswordValid } from 'pages/SignUp/utils';
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
@ -23,7 +24,7 @@ function PasswordContainer(): JSX.Element {
ns: 'settings',
});
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
useEffect(() => {
if (currentPassword && !isPasswordValid(currentPassword)) {
@ -82,7 +83,6 @@ function PasswordContainer(): JSX.Element {
return (
<Space direction="vertical" size="large">
{NotificationElement}
<Typography.Title level={3}>
{t('change_password', {
ns: 'settings',

View File

@ -1,5 +1,6 @@
import { Button, notification, Space, Typography } from 'antd';
import { Button, Space, Typography } from 'antd';
import editUser from 'api/user/editUser';
import { useNotifications } from 'hooks/useNotifications';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
@ -21,7 +22,7 @@ function UpdateName(): JSX.Element {
const [changedName, setChangedName] = useState<string>(user?.name || '');
const [loading, setLoading] = useState<boolean>(false);
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
if (!user || !org) {
return <div />;
@ -72,7 +73,6 @@ function UpdateName(): JSX.Element {
return (
<div>
{NotificationElement}
<Space direction="vertical" size="middle">
<Typography>Name</Typography>
<NameInput

View File

@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { notification } from 'antd';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { useNotifications } from 'hooks/useNotifications';
import history from 'lib/history';
import React, { useCallback } from 'react';
import { connect, useSelector } from 'react-redux';
@ -22,7 +23,7 @@ function DashboardGraphSlider({ toggleAddWidget }: Props): JSX.Element {
(state) => state.dashboards,
);
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const [selectedDashboard] = dashboards;
const { data } = selectedDashboard;
@ -57,7 +58,6 @@ function DashboardGraphSlider({ toggleAddWidget }: Props): JSX.Element {
return (
<Container>
{NotificationElement}
{menuItems.map(({ name, Icon, display }) => (
<Card
onClick={(event): void => {

View File

@ -1,8 +1,9 @@
import { blue, red } from '@ant-design/colors';
import { PlusOutlined } from '@ant-design/icons';
import { Button, Modal, notification, Row, Space, Tag } from 'antd';
import { Button, Modal, Row, Space, Tag } from 'antd';
import { NotificationInstance } from 'antd/es/notification/interface';
import { ResizeTable } from 'components/ResizeTable';
import { useNotifications } from 'hooks/useNotifications';
import React, { useRef, useState } from 'react';
import { connect, useSelector } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux';
@ -26,7 +27,7 @@ function VariablesSetting({
(state) => state.dashboards,
);
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const [selectedDashboard] = dashboards;
@ -143,7 +144,6 @@ function VariablesSetting({
return (
<>
{NotificationElement}
{variableViewMode ? (
<VariableItem
variableData={{ ...variableEditData } as IDashboardVariable}

View File

@ -1,5 +1,6 @@
import { notification, Row } from 'antd';
import { Row } from 'antd';
import { NotificationInstance } from 'antd/es/notification/interface';
import { useNotifications } from 'hooks/useNotifications';
import { map, sortBy } from 'lodash-es';
import React, { useState } from 'react';
import { connect, useSelector } from 'react-redux';
@ -26,7 +27,7 @@ function DashboardVariableSelection({
const [update, setUpdate] = useState<boolean>(false);
const [lastUpdatedVar, setLastUpdatedVar] = useState<string>('');
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const onVarChanged = (name: string): void => {
setLastUpdatedVar(name);
@ -62,7 +63,6 @@ function DashboardVariableSelection({
return (
<Row style={{ gap: '1rem' }}>
{NotificationElement}
{map(sortBy(Object.keys(variables)), (variableName) => (
<VariableItem
key={`${variableName}${variables[variableName].modificationUUID}`}

View File

@ -1,5 +1,6 @@
import { Button, Modal, notification, Typography } from 'antd';
import { Button, Modal, Typography } from 'antd';
import Editor from 'components/Editor';
import { useNotifications } from 'hooks/useNotifications';
import React, { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useCopyToClipboard } from 'react-use';
@ -32,7 +33,7 @@ function ShareModal({
const [isViewJSON, setIsViewJSON] = useState<boolean>(false);
const { t } = useTranslation(['dashboard', 'common']);
const [state, setCopy] = useCopyToClipboard();
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
useEffect(() => {
if (state.error) {
@ -84,34 +85,28 @@ function ShareModal({
}, [isViewJSON, jsonValue, selectedData, selectedDataCleaned, setCopy, t]);
return (
<>
{NotificationElement}
<Modal
open={isJSONModalVisible}
onCancel={(): void => {
onToggleHandler();
setIsViewJSON(false);
}}
width="70vw"
centered
title={t('share', {
ns: 'common',
})}
okText={t('download_json')}
cancelText={t('cancel')}
destroyOnClose
footer={GetFooterComponent}
>
{!isViewJSON ? (
<Typography>{t('export_dashboard')}</Typography>
) : (
<Editor
onChange={(value): void => setJSONValue(value)}
value={jsonValue}
/>
)}
</Modal>
</>
<Modal
open={isJSONModalVisible}
onCancel={(): void => {
onToggleHandler();
setIsViewJSON(false);
}}
width="70vw"
centered
title={t('share', {
ns: 'common',
})}
okText={t('download_json')}
cancelText={t('cancel')}
destroyOnClose
footer={GetFooterComponent}
>
{!isViewJSON ? (
<Typography>{t('export_dashboard')}</Typography>
) : (
<Editor onChange={(value): void => setJSONValue(value)} value={jsonValue} />
)}
</Modal>
);
}

View File

@ -1,10 +1,10 @@
import { PlusOutlined } from '@ant-design/icons';
import { notification } from 'antd';
import {
QueryBuilderFormulaTemplate,
QueryBuilderQueryTemplate,
} from 'constants/dashboard';
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
import { useNotifications } from 'hooks/useNotifications';
import GetFormulaName from 'lib/query/GetFormulaName';
import GetQueryName from 'lib/query/GetQueryName';
import React from 'react';
@ -37,7 +37,7 @@ function QueryBuilderQueryContainer({
metricsBuilderQueries,
selectedGraph,
}: IQueryBuilderQueryContainerProps): JSX.Element | null {
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const handleQueryBuilderQueryChange = ({
queryIndex,
aggregateFunction,
@ -156,7 +156,6 @@ function QueryBuilderQueryContainer({
}
return (
<>
{NotificationElement}
{metricsBuilderQueries.queryBuilder.map((q, idx) => (
<MetricsBuilder
key={q.name}

View File

@ -1,10 +1,11 @@
/* eslint-disable prefer-regex-literals */
import { PlusOutlined } from '@ant-design/icons';
import { Button, Form, Input, Modal, notification, Typography } from 'antd';
import { Button, Form, Input, Modal, Typography } from 'antd';
import { useForm } from 'antd/es/form/Form';
import createDomainApi from 'api/SAML/postDomain';
import { FeatureKeys } from 'constants/featureKeys';
import useFeatureFlag from 'hooks/useFeatureFlag';
import { useNotifications } from 'hooks/useNotifications';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
@ -21,7 +22,7 @@ function AddDomain({ refetch }: Props): JSX.Element {
const { org } = useSelector<AppState, AppReducer>((state) => state.app);
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const onCreateHandler = async (): Promise<void> => {
try {
@ -51,7 +52,6 @@ function AddDomain({ refetch }: Props): JSX.Element {
return (
<>
{NotificationElement}
<Container>
<Typography.Title level={3}>
{t('authenticated_domains', {

View File

@ -1,5 +1,6 @@
import { Button, Form, notification, Space } from 'antd';
import { Button, Form, Space } from 'antd';
import { useForm } from 'antd/lib/form/Form';
import { useNotifications } from 'hooks/useNotifications';
import React, { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { AuthDomain, GOOGLE_AUTH, SAML } from 'types/api/SAML/listDomain';
@ -31,7 +32,7 @@ function EditSSO({
const { t } = useTranslation(['common']);
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const onFinishHandler = useCallback(() => {
form
@ -75,7 +76,6 @@ function EditSSO({
autoComplete="off"
form={form}
>
{NotificationElement}
{renderFormInputs(record)}
<Space
style={{ width: '100%', justifyContent: 'flex-end' }}

View File

@ -1,5 +1,5 @@
import { LockTwoTone } from '@ant-design/icons';
import { Button, Modal, notification, Space, Typography } from 'antd';
import { Button, Modal, Space, Typography } from 'antd';
import { ColumnsType } from 'antd/lib/table';
import deleteDomain from 'api/SAML/deleteDomain';
import listAllDomain from 'api/SAML/listAllDomain';
@ -9,6 +9,7 @@ import TextToolTip from 'components/TextToolTip';
import { SIGNOZ_UPGRADE_PLAN_URL } from 'constants/app';
import { FeatureKeys } from 'constants/featureKeys';
import useFeatureFlag from 'hooks/useFeatureFlag';
import { useNotifications } from 'hooks/useNotifications';
import React, { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useQuery } from 'react-query';
@ -57,7 +58,7 @@ function AuthDomains(): JSX.Element {
enabled: org !== null,
});
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const assignSsoMethod = useCallback(
(typ: AuthDomain['ssoType']): void => {
@ -254,7 +255,6 @@ function AuthDomains(): JSX.Element {
if (!isLoading && data?.payload?.length === 0) {
return (
<Space direction="vertical" size="middle">
{NotificationElement}
<AddDomain refetch={refetch} />
<Modal
@ -286,7 +286,6 @@ function AuthDomains(): JSX.Element {
return (
<>
{NotificationElement}
<Modal
centered
title="Configure Authentication Method"

View File

@ -1,5 +1,6 @@
import { Button, Form, Input, notification } from 'antd';
import { Button, Form, Input } from 'antd';
import editOrg from 'api/user/editOrg';
import { useNotifications } from 'hooks/useNotifications';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
@ -21,7 +22,7 @@ function DisplayName({
const { name } = (org || [])[index];
const [isLoading, setIsLoading] = useState<boolean>(false);
const dispatch = useDispatch<Dispatch<AppActions>>();
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const onSubmit = async ({ name: orgName }: OnSubmitProps): Promise<void> => {
try {
@ -76,7 +77,6 @@ function DisplayName({
onFinish={onSubmit}
autoComplete="off"
>
{NotificationElement}
<Form.Item name="name" label="Display name" rules={[{ required: true }]}>
<Input size="large" placeholder={t('signoz')} disabled={isLoading} />
</Form.Item>

View File

@ -1,7 +1,8 @@
import { CopyOutlined } from '@ant-design/icons';
import { Button, Input, notification, Select, Space, Tooltip } from 'antd';
import { Button, Input, Select, Space, Tooltip } from 'antd';
import getResetPasswordToken from 'api/user/getResetPasswordToken';
import ROUTES from 'constants/routes';
import { useNotifications } from 'hooks/useNotifications';
import React, { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useCopyToClipboard } from 'react-use';
@ -36,7 +37,7 @@ function EditMembersDetails({
[],
);
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
useEffect(() => {
if (state.error) {
@ -91,7 +92,6 @@ function EditMembersDetails({
return (
<Space direction="vertical" size="large">
{NotificationElement}
<Space direction="horizontal">
<Title>Email address</Title>
<Input

View File

@ -1,4 +1,4 @@
import { Button, Modal, notification, Space, Typography } from 'antd';
import { Button, Modal, Space, Typography } from 'antd';
import { ColumnsType } from 'antd/lib/table';
import deleteUser from 'api/user/deleteUser';
import editUserApi from 'api/user/editUser';
@ -6,6 +6,7 @@ import getOrgUser from 'api/user/getOrgUser';
import updateRole from 'api/user/updateRole';
import { ResizeTable } from 'components/ResizeTable';
import dayjs from 'dayjs';
import { useNotifications } from 'hooks/useNotifications';
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useQuery } from 'react-query';
@ -40,7 +41,7 @@ function UserFunction({
const { t } = useTranslation(['common']);
const [isDeleteLoading, setIsDeleteLoading] = useState<boolean>(false);
const [isUpdateLoading, setIsUpdateLoading] = useState<boolean>(false);
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const onUpdateDetailsHandler = (): void => {
setDataSource((data) => {
@ -164,7 +165,6 @@ function UserFunction({
return (
<>
{NotificationElement}
<Space direction="horizontal">
<Typography.Link
onClick={(): void => onModalToggleHandler(setIsModalVisible, true)}

View File

@ -1,5 +1,5 @@
import { PlusOutlined } from '@ant-design/icons';
import { Button, Modal, notification, Space, Typography } from 'antd';
import { Button, Modal, Space, Typography } from 'antd';
import { ColumnsType } from 'antd/lib/table';
import deleteInvite from 'api/user/deleteInvite';
import getPendingInvites from 'api/user/getPendingInvites';
@ -7,6 +7,7 @@ import sendInvite from 'api/user/sendInvite';
import { ResizeTable } from 'components/ResizeTable';
import { INVITE_MEMBERS_HASH } from 'constants/app';
import ROUTES from 'constants/routes';
import { useNotifications } from 'hooks/useNotifications';
import React, { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useQuery } from 'react-query';
@ -26,7 +27,7 @@ function PendingInvitesContainer(): JSX.Element {
const [isInvitingMembers, setIsInvitingMembers] = useState<boolean>(false);
const { t } = useTranslation(['organizationsettings', 'common']);
const [state, setText] = useCopyToClipboard();
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
useEffect(() => {
if (state.error) {
@ -229,7 +230,6 @@ function PendingInvitesContainer(): JSX.Element {
return (
<div>
{NotificationElement}
<Modal
title={t('invite_team_members')}
open={isInviteTeamMemberModalOpen}

View File

@ -1,8 +1,9 @@
import { Button, Input, notification, Typography } from 'antd';
import { Button, Input, Typography } from 'antd';
import resetPasswordApi from 'api/user/resetPassword';
import { Logout } from 'api/utils';
import WelcomeLeftContainer from 'components/WelcomeLeftContainer';
import ROUTES from 'constants/routes';
import { useNotifications } from 'hooks/useNotifications';
import history from 'lib/history';
import { Label } from 'pages/SignUp/styles';
import React, { useEffect, useState } from 'react';
@ -24,7 +25,7 @@ function ResetPassword({ version }: ResetPasswordProps): JSX.Element {
const { search } = useLocation();
const params = new URLSearchParams(search);
const token = params.get('token');
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
useEffect(() => {
if (!token) {
@ -83,73 +84,70 @@ function ResetPassword({ version }: ResetPasswordProps): JSX.Element {
return (
<WelcomeLeftContainer version={version}>
<>
{NotificationElement}
<FormWrapper>
<form onSubmit={handleSubmit}>
<Title level={4}>Reset Your Password</Title>
<FormWrapper>
<form onSubmit={handleSubmit}>
<Title level={4}>Reset Your Password</Title>
<div>
<Label htmlFor="Password">Password</Label>
<Input.Password
value={password}
onChange={(e): void => {
setState(e.target.value, setPassword);
}}
required
id="currentPassword"
/>
</div>
<div>
<Label htmlFor="ConfirmPassword">Confirm Password</Label>
<Input.Password
value={confirmPassword}
onChange={(e): void => {
const updateValue = e.target.value;
setState(updateValue, setConfirmPassword);
if (password !== updateValue) {
setConfirmPasswordError(true);
} else {
setConfirmPasswordError(false);
}
}}
required
id="UpdatePassword"
/>
{confirmPasswordError && (
<Typography.Paragraph
italic
style={{
color: '#D89614',
marginTop: '0.50rem',
}}
>
Passwords dont match. Please try again
</Typography.Paragraph>
)}
</div>
<ButtonContainer>
<Button
type="primary"
htmlType="submit"
data-attr="signup"
loading={loading}
disabled={
loading ||
!password ||
!confirmPassword ||
confirmPasswordError ||
token === null
<div>
<Label htmlFor="Password">Password</Label>
<Input.Password
value={password}
onChange={(e): void => {
setState(e.target.value, setPassword);
}}
required
id="currentPassword"
/>
</div>
<div>
<Label htmlFor="ConfirmPassword">Confirm Password</Label>
<Input.Password
value={confirmPassword}
onChange={(e): void => {
const updateValue = e.target.value;
setState(updateValue, setConfirmPassword);
if (password !== updateValue) {
setConfirmPasswordError(true);
} else {
setConfirmPasswordError(false);
}
}}
required
id="UpdatePassword"
/>
{confirmPasswordError && (
<Typography.Paragraph
italic
style={{
color: '#D89614',
marginTop: '0.50rem',
}}
>
Get Started
</Button>
</ButtonContainer>
</form>
</FormWrapper>
</>
Passwords dont match. Please try again
</Typography.Paragraph>
)}
</div>
<ButtonContainer>
<Button
type="primary"
htmlType="submit"
data-attr="signup"
loading={loading}
disabled={
loading ||
!password ||
!confirmPassword ||
confirmPasswordError ||
token === null
}
>
Get Started
</Button>
</ButtonContainer>
</form>
</FormWrapper>
</WelcomeLeftContainer>
);
}

View File

@ -1,6 +1,7 @@
import { Checkbox, notification, Tooltip, Typography } from 'antd';
import { Checkbox, Tooltip, Typography } from 'antd';
import getFilters from 'api/trace/getFilters';
import { AxiosError } from 'axios';
import { useNotifications } from 'hooks/useNotifications';
import React, { useMemo, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Dispatch } from 'redux';
@ -38,7 +39,7 @@ function CheckBoxComponent(props: CheckBoxProps): JSX.Element {
(userSelectedFilter.get(name) || []).find((e) => e === keyValue) !==
undefined;
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
// eslint-disable-next-line sonarjs/cognitive-complexity
const onCheckHandler = async (): Promise<void> => {
@ -163,7 +164,6 @@ function CheckBoxComponent(props: CheckBoxProps): JSX.Element {
return (
<CheckBoxContainer>
{NotificationElement}
<Checkbox
disabled={isLoading || filterLoading}
onClick={onCheckHandler}

View File

@ -1,6 +1,7 @@
import { Input, notification } from 'antd';
import { Input } from 'antd';
import getFilters from 'api/trace/getFilters';
import { AxiosError } from 'axios';
import { useNotifications } from 'hooks/useNotifications';
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Dispatch } from 'redux';
@ -28,7 +29,7 @@ function TraceID(): JSX.Element {
);
const [isLoading, setIsLoading] = useState(false);
const [userEnteredValue, setUserEnteredValue] = useState<string>('');
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
useEffect(() => {
setUserEnteredValue(selectedFilter.get('traceID')?.[0] || '');
}, [selectedFilter]);
@ -109,7 +110,6 @@ function TraceID(): JSX.Element {
};
return (
<div>
{NotificationElement}
<Search
placeholder="Filter by Trace ID"
onSearch={onSearch}

View File

@ -1,7 +1,8 @@
import { DownOutlined, RightOutlined } from '@ant-design/icons';
import { Card, Divider, notification, Typography } from 'antd';
import { Card, Divider, Typography } from 'antd';
import getFilters from 'api/trace/getFilters';
import { AxiosError } from 'axios';
import { useNotifications } from 'hooks/useNotifications';
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Dispatch } from 'redux';
@ -53,7 +54,7 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element {
const defaultErrorMessage = 'Something went wrong';
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
// eslint-disable-next-line sonarjs/cognitive-complexity
const onExpandHandler: React.MouseEventHandler<HTMLDivElement> = async (e) => {
@ -297,7 +298,6 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element {
return (
<>
{NotificationElement}
{PanelName !== 'duration' && <Divider plain style={{ margin: 0 }} />}
<Card bordered={false}>

View File

@ -16,7 +16,7 @@ function TagsKey(props: TagsKeysProps): JSX.Element {
const { index, setLocalSelectedTags, tag } = props;
const [selectedKey, setSelectedKey] = useState<string>(tag.Key[0] || '');
const [selectedKey, setSelectedKey] = useState<string>(tag.Key || '');
const traces = useSelector<AppState, TraceReducer>((state) => state.traces);

View File

@ -150,12 +150,12 @@ function SingleTags(props: AllTagsProps): JSX.Element {
}
</SelectComponent>
{selectedKey[0] ? (
{selectedKey ? (
<TagValue
index={index}
tag={tag}
setLocalSelectedTags={setLocalSelectedTags}
tagKey={selectedKey[0]}
tagKey={selectedKey}
/>
) : (
<SelectComponent />

View File

@ -72,7 +72,7 @@ export function onTagValueChange(
export function disableTagValue(
selectedOperator: OperatorValues,
setLocalValue: React.Dispatch<React.SetStateAction<TagValueTypes[]>>,
selectedKeys: string[],
selectedKeys: string,
setLocalSelectedTags: React.Dispatch<React.SetStateAction<Tags[]>>,
index: number,
): boolean {
@ -169,9 +169,9 @@ export function selectOptions(
return [];
}
export function mapOperators(selectedKey: string[]): AllMenuProps[] {
export function mapOperators(selectedKey: string): AllMenuProps[] {
return AllMenu.filter((e) =>
e?.supportedTypes?.includes(extractTagType(selectedKey[0])),
e?.supportedTypes?.includes(extractTagType(selectedKey)),
);
}
@ -192,7 +192,7 @@ export function onTagKeySelect(
setLocalSelectedTags((tags) => [
...tags.slice(0, index),
{
Key: [value],
Key: value,
Operator: tag.Operator,
StringValues: tag.StringValues,
NumberValues: tag.NumberValues,

View File

@ -39,7 +39,7 @@ function AllTags({
setLocalSelectedTags((tags) => [
...tags,
{
Key: [],
Key: '',
Operator: 'Equals',
StringValues: [],
NumberValues: [],
@ -94,7 +94,7 @@ function AllTags({
<CurrentTagsContainer>
{localSelectedTags.map((tags, index) => (
<Tags
key={tags.Key.join(',')}
key={tags.Key}
tag={tags}
index={index}
onCloseHandler={(): void => onCloseHandler(index)}

View File

@ -59,7 +59,7 @@ export const parseQueryToTags = (query: string): PayloadProps<Tags> => {
// If the operator is Exists or NotExists, then return the tag object without values
if (operator === 'Exists' || operator === 'NotExists') {
return {
Key: [tagName],
Key: tagName,
StringValues: [],
NumberValues: [],
BoolValues: [],
@ -97,7 +97,7 @@ export const parseQueryToTags = (query: string): PayloadProps<Tags> => {
// Return the tag object
return {
Key: [tagName],
Key: tagName,
StringValues,
NumberValues,
BoolValues,
@ -120,31 +120,31 @@ export const parseTagsToQuery = (tags: Tags): PayloadProps<string> => {
const payload = tags
.map(({ StringValues, NumberValues, BoolValues, Key, Operator }) => {
// Check if the key of the tag is undefined
if (!Key[0]) {
if (!Key) {
isError = true;
}
if (Operator === 'Exists' || Operator === 'NotExists') {
return `${Key[0]} ${Operator}`;
return `${Key} ${Operator}`;
}
// Check if the tag has string values
if (StringValues.length > 0) {
// Format the string values and join them with a ','
const formattedStringValues = formatValues(StringValues);
return `${Key[0]} ${Operator} (${formattedStringValues})`;
return `${Key} ${Operator} (${formattedStringValues})`;
}
// Check if the tag has number values
if (NumberValues.length > 0) {
// Format the number values and join them with a ','
const formattedNumberValues = formatValues(NumberValues);
return `${Key[0]} ${Operator} (${formattedNumberValues})`;
return `${Key} ${Operator} (${formattedNumberValues})`;
}
// Check if the tag has boolean values
if (BoolValues.length > 0) {
// Format the boolean values and join them with a ','
const formattedBoolValues = formatValues(BoolValues);
return `${Key[0]} ${Operator} (${formattedBoolValues})`;
return `${Key} ${Operator} (${formattedBoolValues})`;
}
return '';

View File

@ -1,7 +1,7 @@
import { notification } from 'antd';
import getTriggeredApi from 'api/alerts/getTriggered';
import Spinner from 'components/Spinner';
import { State } from 'hooks/useFetch';
import { useNotifications } from 'hooks/useNotifications';
import React, { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { PayloadProps } from 'types/api/alerts/getTriggered';
@ -17,7 +17,7 @@ function TriggeredAlerts(): JSX.Element {
payload: [],
});
const { t } = useTranslation(['common']);
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
const fetchData = useCallback(async () => {
try {
@ -69,21 +69,11 @@ function TriggeredAlerts(): JSX.Element {
}, [groupState.error, groupState.errorMessage, t, notifications]);
if (groupState.error) {
return (
<>
{NotificationElement}
<TriggerComponent allAlerts={[]} />
</>
);
return <TriggerComponent allAlerts={[]} />;
}
if (groupState.loading || groupState.payload === undefined) {
return (
<>
{NotificationElement}
<Spinner height="75vh" tip="Loading Alerts..." />
</>
);
return <Spinner height="75vh" tip="Loading Alerts..." />;
}
// commented the reduce() call as we no longer use /alerts/groups
@ -95,12 +85,7 @@ function TriggeredAlerts(): JSX.Element {
// return [...acc, ...curr.alerts];
// }, initialAlerts);
return (
<>
{NotificationElement}
<TriggerComponent allAlerts={groupState.payload} />
</>
);
return <TriggerComponent allAlerts={groupState.payload} />;
}
export default TriggeredAlerts;

View File

@ -0,0 +1,43 @@
import { notification } from 'antd';
import { NotificationInstance } from 'antd/es/notification/interface';
import React, { createContext, useContext, useMemo } from 'react';
type Notification = {
notifications: NotificationInstance;
};
const defaultNotification: Notification = {
notifications: {
success: (): void => {},
error: (): void => {},
info: (): void => {},
warning: (): void => {},
open: (): void => {},
destroy: (): void => {},
},
};
export const NotificationContext = createContext<Notification>(
defaultNotification,
);
export function NotificationProvider({
children,
}: {
children: JSX.Element;
}): JSX.Element {
const [notificationApi, NotificationElement] = notification.useNotification();
const notifications = useMemo(() => ({ notifications: notificationApi }), [
notificationApi,
]);
return (
<NotificationContext.Provider value={notifications}>
{NotificationElement}
{children}
</NotificationContext.Provider>
);
}
export const useNotifications = (): Notification =>
useContext(NotificationContext);

View File

@ -37,7 +37,7 @@ export const convertRawQueriesToTraceSelectedTags = (
queries: IResourceAttributeQuery[],
): Tags[] =>
queries.map((query) => ({
Key: [convertMetricKeyToTrace(query.tagKey)],
Key: convertMetricKeyToTrace(query.tagKey),
Operator: convertOperatorLabelToTraceOperator(query.operator),
StringValues: query.tagValue,
NumberValues: [],

View File

@ -1,8 +1,8 @@
import { notification } from 'antd';
import get from 'api/alerts/get';
import Spinner from 'components/Spinner';
import ROUTES from 'constants/routes';
import EditRulesContainer from 'container/EditRules';
import { useNotifications } from 'hooks/useNotifications';
import history from 'lib/history';
import React, { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
@ -26,7 +26,7 @@ function EditRules(): JSX.Element {
enabled: isValidRuleId,
});
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
useEffect(() => {
if (!isValidRuleId) {
@ -42,12 +42,7 @@ function EditRules(): JSX.Element {
ruleId == null ||
(data?.payload?.data === undefined && !isLoading)
) {
return (
<div>
{NotificationElement}
{data?.error || t('something_went_wrong')}
</div>
);
return <div>{data?.error || t('something_went_wrong')}</div>;
}
if (isLoading || !data?.payload) {
@ -55,13 +50,10 @@ function EditRules(): JSX.Element {
}
return (
<>
{NotificationElement}
<EditRulesContainer
ruleId={parseInt(ruleId, 10)}
initialValue={data.payload.data}
/>
</>
<EditRulesContainer
ruleId={parseInt(ruleId, 10)}
initialValue={data.payload.data}
/>
);
}

View File

@ -1,10 +1,11 @@
import { notification, Space } from 'antd';
import { Space } from 'antd';
import getLocalStorageKey from 'api/browser/localstorage/get';
import ReleaseNote from 'components/ReleaseNote';
import Spinner from 'components/Spinner';
import { SKIP_ONBOARDING } from 'constants/onboarding';
import ResourceAttributesFilter from 'container/MetricsApplication/ResourceAttributesFilter';
import MetricTable from 'container/MetricsTable';
import { useNotifications } from 'hooks/useNotifications';
import { convertRawQueriesToTraceSelectedTags } from 'lib/resourceAttributes';
import React, { useEffect, useMemo } from 'react';
import { connect, useSelector } from 'react-redux';
@ -30,7 +31,7 @@ function Metrics({ getService }: MetricsProps): JSX.Element {
error,
errorMessage,
} = useSelector<AppState, MetricReducer>((state) => state.metrics);
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
useEffect(() => {
if (error) {
@ -91,7 +92,6 @@ function Metrics({ getService }: MetricsProps): JSX.Element {
return (
<Space direction="vertical" style={{ width: '100%' }}>
{NotificationElement}
<ReleaseNote path={location.pathname} />
<ResourceAttributesFilter />

View File

@ -1,4 +1,4 @@
import { Button, Input, notification, Space, Switch, Typography } from 'antd';
import { Button, Input, Space, Switch, Typography } from 'antd';
import editOrg from 'api/user/editOrg';
import getInviteDetails from 'api/user/getInviteDetails';
import loginApi from 'api/user/login';
@ -6,6 +6,7 @@ import signUpApi from 'api/user/signup';
import afterLogin from 'AppRoutes/utils';
import WelcomeLeftContainer from 'components/WelcomeLeftContainer';
import ROUTES from 'constants/routes';
import { useNotifications } from 'hooks/useNotifications';
import history from 'lib/history';
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
@ -56,7 +57,7 @@ function SignUp({ version }: SignUpProps): JSX.Element {
enabled: token !== null,
});
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
useEffect(() => {
if (
@ -266,169 +267,164 @@ function SignUp({ version }: SignUpProps): JSX.Element {
return (
<WelcomeLeftContainer version={version}>
<>
{NotificationElement}
<FormWrapper>
<form onSubmit={!precheck.sso ? handleSubmit : handleSubmitSSO}>
<Title level={4}>Create your account</Title>
<FormWrapper>
<form onSubmit={!precheck.sso ? handleSubmit : handleSubmitSSO}>
<Title level={4}>Create your account</Title>
<div>
<Label htmlFor="signupEmail">{t('label_email')}</Label>
<Input
placeholder={t('placeholder_email')}
type="email"
autoFocus
value={email}
onChange={(e): void => {
setState(e.target.value, setEmail);
}}
required
id="signupEmail"
disabled={isDetailsDisable}
/>
</div>
{isNameVisible && (
<div>
<Label htmlFor="signupEmail">{t('label_email')}</Label>
<Label htmlFor="signupFirstName">{t('label_firstname')}</Label>
<Input
placeholder={t('placeholder_email')}
type="email"
autoFocus
value={email}
placeholder={t('placeholder_firstname')}
value={firstName}
onChange={(e): void => {
setState(e.target.value, setEmail);
setState(e.target.value, setFirstName);
}}
required
id="signupEmail"
id="signupFirstName"
disabled={isDetailsDisable}
/>
</div>
)}
{isNameVisible && (
<div>
<Label htmlFor="signupFirstName">{t('label_firstname')}</Label>
<Input
placeholder={t('placeholder_firstname')}
value={firstName}
onChange={(e): void => {
setState(e.target.value, setFirstName);
}}
required
id="signupFirstName"
disabled={isDetailsDisable}
/>
</div>
)}
<div>
<Label htmlFor="organizationName">{t('label_orgname')}</Label>
<Input
placeholder={t('placeholder_orgname')}
value={organizationName}
onChange={(e): void => {
setState(e.target.value, setOrganizationName);
}}
required
id="organizationName"
disabled={isDetailsDisable}
/>
</div>
{!precheck.sso && (
<div>
<Label htmlFor="organizationName">{t('label_orgname')}</Label>
<Input
placeholder={t('placeholder_orgname')}
value={organizationName}
<Label htmlFor="Password">{t('label_password')}</Label>
<Input.Password
value={password}
onChange={(e): void => {
setState(e.target.value, setOrganizationName);
setState(e.target.value, setPassword);
}}
required
id="organizationName"
disabled={isDetailsDisable}
id="currentPassword"
/>
</div>
{!precheck.sso && (
<div>
<Label htmlFor="Password">{t('label_password')}</Label>
<Input.Password
value={password}
onChange={(e): void => {
setState(e.target.value, setPassword);
}}
required
id="currentPassword"
/>
</div>
)}
{!precheck.sso && (
<div>
<Label htmlFor="ConfirmPassword">{t('label_confirm_password')}</Label>
<Input.Password
value={confirmPassword}
onChange={(e): void => {
const updateValue = e.target.value;
setState(updateValue, setConfirmPassword);
}}
required
id="confirmPassword"
/>
{confirmPasswordError && (
<Typography.Paragraph
italic
id="password-confirm-error"
style={{
color: '#D89614',
marginTop: '0.50rem',
}}
>
{t('failed_confirm_password')}
</Typography.Paragraph>
)}
{isPasswordPolicyError && (
<Typography.Paragraph
italic
style={{
color: '#D89614',
marginTop: '0.50rem',
}}
>
{isPasswordNotValidMessage}
</Typography.Paragraph>
)}
</div>
)}
{isPreferenceVisible && (
<>
<MarginTop marginTop="2.4375rem">
<Space>
<Switch
onChange={(value): void =>
onSwitchHandler(value, setHasOptedUpdates)
}
checked={hasOptedUpdates}
/>
<Typography>{t('prompt_keepme_posted')} </Typography>
</Space>
</MarginTop>
<MarginTop marginTop="0.5rem">
<Space>
<Switch
onChange={(value): void => onSwitchHandler(value, setIsAnonymous)}
checked={isAnonymous}
/>
<Typography>{t('prompt_anonymise')}</Typography>
</Space>
</MarginTop>
</>
)}
{isPreferenceVisible && (
<Typography.Paragraph
italic
style={{
color: '#D89614',
marginTop: '0.50rem',
)}
{!precheck.sso && (
<div>
<Label htmlFor="ConfirmPassword">{t('label_confirm_password')}</Label>
<Input.Password
value={confirmPassword}
onChange={(e): void => {
const updateValue = e.target.value;
setState(updateValue, setConfirmPassword);
}}
>
This will create an admin account. If you are not an admin, please ask
your admin for an invite link
</Typography.Paragraph>
)}
required
id="confirmPassword"
/>
<ButtonContainer>
<Button
type="primary"
htmlType="submit"
data-attr="signup"
loading={loading}
disabled={
loading ||
!email ||
!organizationName ||
(!precheck.sso && (!password || !confirmPassword)) ||
(!isDetailsDisable && !firstName) ||
confirmPasswordError ||
isPasswordPolicyError
}
>
{t('button_get_started')}
</Button>
</ButtonContainer>
</form>
</FormWrapper>
</>
{confirmPasswordError && (
<Typography.Paragraph
italic
id="password-confirm-error"
style={{
color: '#D89614',
marginTop: '0.50rem',
}}
>
{t('failed_confirm_password')}
</Typography.Paragraph>
)}
{isPasswordPolicyError && (
<Typography.Paragraph
italic
style={{
color: '#D89614',
marginTop: '0.50rem',
}}
>
{isPasswordNotValidMessage}
</Typography.Paragraph>
)}
</div>
)}
{isPreferenceVisible && (
<>
<MarginTop marginTop="2.4375rem">
<Space>
<Switch
onChange={(value): void => onSwitchHandler(value, setHasOptedUpdates)}
checked={hasOptedUpdates}
/>
<Typography>{t('prompt_keepme_posted')} </Typography>
</Space>
</MarginTop>
<MarginTop marginTop="0.5rem">
<Space>
<Switch
onChange={(value): void => onSwitchHandler(value, setIsAnonymous)}
checked={isAnonymous}
/>
<Typography>{t('prompt_anonymise')}</Typography>
</Space>
</MarginTop>
</>
)}
{isPreferenceVisible && (
<Typography.Paragraph
italic
style={{
color: '#D89614',
marginTop: '0.50rem',
}}
>
This will create an admin account. If you are not an admin, please ask
your admin for an invite link
</Typography.Paragraph>
)}
<ButtonContainer>
<Button
type="primary"
htmlType="submit"
data-attr="signup"
loading={loading}
disabled={
loading ||
!email ||
!organizationName ||
(!precheck.sso && (!password || !confirmPassword)) ||
(!isDetailsDisable && !firstName) ||
confirmPasswordError ||
isPasswordPolicyError
}
>
{t('button_get_started')}
</Button>
</ButtonContainer>
</form>
</FormWrapper>
</WelcomeLeftContainer>
);
}

View File

@ -1,4 +1,4 @@
import { Card, notification } from 'antd';
import { Card } from 'antd';
import { NotificationInstance } from 'antd/es/notification/interface';
import ROUTES from 'constants/routes';
import Filters from 'container/Trace/Filters';
@ -6,6 +6,7 @@ import TraceGraph from 'container/Trace/Graph';
import Search from 'container/Trace/Search';
import TraceGraphFilter from 'container/Trace/TraceGraphFilter';
import TraceTable from 'container/Trace/TraceTable';
import { useNotifications } from 'hooks/useNotifications';
import getStep from 'lib/getStep';
import history from 'lib/history';
import React, { useCallback, useEffect, useState } from 'react';
@ -53,7 +54,7 @@ function Trace({
isFilterExclude,
} = useSelector<AppState, TraceReducer>((state) => state.traces);
const [notifications, NotificationElement] = notification.useNotification();
const { notifications } = useNotifications();
useEffect(() => {
getInitialFilter(minTime, maxTime, notifications);
@ -139,7 +140,6 @@ function Trace({
return (
<>
{NotificationElement}
<Search />
<Container>
<div>

View File

@ -47,7 +47,7 @@ interface SpansAggregateData {
}
export interface Tags {
Key: string[];
Key: string;
Operator: OperatorValues;
StringValues: string[];
NumberValues: number[];

View File

@ -3640,3 +3640,13 @@ func (r *ClickHouseReader) QueryDashboardVars(ctx context.Context, query string)
}
return &result, nil
}
func (r *ClickHouseReader) CheckClickHouse(ctx context.Context) error {
rows, err := r.db.Query(ctx, "SELECT 1")
if err != nil {
return err
}
defer rows.Close()
return nil
}

View File

@ -351,7 +351,7 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router) {
router.HandleFunc("/api/v1/feedback", OpenAccess(aH.submitFeedback)).Methods(http.MethodPost)
// router.HandleFunc("/api/v1/get_percentiles", aH.getApplicationPercentiles).Methods(http.MethodGet)
router.HandleFunc("/api/v1/services", ViewAccess(aH.getServices)).Methods(http.MethodPost)
router.HandleFunc("/api/v1/services/list", aH.getServicesList).Methods(http.MethodGet)
router.HandleFunc("/api/v1/services/list", ViewAccess(aH.getServicesList)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/service/overview", ViewAccess(aH.getServiceOverview)).Methods(http.MethodPost)
router.HandleFunc("/api/v1/service/top_operations", ViewAccess(aH.getTopOperations)).Methods(http.MethodPost)
router.HandleFunc("/api/v1/service/top_level_operations", ViewAccess(aH.getServicesTopLevelOps)).Methods(http.MethodPost)
@ -364,6 +364,7 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router) {
router.HandleFunc("/api/v1/version", OpenAccess(aH.getVersion)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/featureFlags", OpenAccess(aH.getFeatureFlags)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/configs", OpenAccess(aH.getConfigs)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/health", OpenAccess(aH.getHealth)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/getSpanFilters", ViewAccess(aH.getSpanFilters)).Methods(http.MethodPost)
router.HandleFunc("/api/v1/getTagFilters", ViewAccess(aH.getTagFilters)).Methods(http.MethodPost)
@ -1671,6 +1672,22 @@ func (aH *APIHandler) getConfigs(w http.ResponseWriter, r *http.Request) {
aH.Respond(w, configs)
}
// getHealth is used to check the health of the service.
// 'live' query param can be used to check liveliness of
// the service by checking the database connection.
func (aH *APIHandler) getHealth(w http.ResponseWriter, r *http.Request) {
_, ok := r.URL.Query()["live"]
if ok {
err := aH.reader.CheckClickHouse(r.Context())
if err != nil {
aH.HandleError(w, err, http.StatusServiceUnavailable)
return
}
}
aH.WriteJSON(w, r, map[string]string{"status": "ok"})
}
// inviteUser is used to invite a user. It is used by an admin api.
func (aH *APIHandler) inviteUser(w http.ResponseWriter, r *http.Request) {
req, err := parseInviteRequest(r)

View File

@ -77,4 +77,5 @@ type Reader interface {
GetFanoutStorage() *storage.Storage
QueryDashboardVars(ctx context.Context, query string) (*model.DashboardVar, error)
CheckClickHouse(ctx context.Context) error
}