mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-14 08:55:56 +08:00
feat: create alerts is updated from trace explorer (#2995)
This commit is contained in:
parent
709bfda0cc
commit
cda37b99b4
@ -0,0 +1,27 @@
|
||||
import { TFunction } from 'i18next';
|
||||
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
||||
|
||||
import { OptionType } from './types';
|
||||
|
||||
export const getOptionList = (t: TFunction): OptionType[] => [
|
||||
{
|
||||
title: t('metric_based_alert'),
|
||||
selection: AlertTypes.METRICS_BASED_ALERT,
|
||||
description: t('metric_based_alert_desc'),
|
||||
},
|
||||
{
|
||||
title: t('log_based_alert'),
|
||||
selection: AlertTypes.LOGS_BASED_ALERT,
|
||||
description: t('log_based_alert_desc'),
|
||||
},
|
||||
{
|
||||
title: t('traces_based_alert'),
|
||||
selection: AlertTypes.TRACES_BASED_ALERT,
|
||||
description: t('traces_based_alert_desc'),
|
||||
},
|
||||
{
|
||||
title: t('exceptions_based_alert'),
|
||||
selection: AlertTypes.EXCEPTIONS_BASED_ALERT,
|
||||
description: t('exceptions_based_alert_desc'),
|
||||
},
|
||||
];
|
@ -1,61 +1,40 @@
|
||||
import { Row } from 'antd';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
||||
|
||||
import { getOptionList } from './config';
|
||||
import { AlertTypeCard, SelectTypeContainer } from './styles';
|
||||
|
||||
interface OptionType {
|
||||
title: string;
|
||||
selection: AlertTypes;
|
||||
description: string;
|
||||
}
|
||||
import { OptionType } from './types';
|
||||
|
||||
function SelectAlertType({ onSelect }: SelectAlertTypeProps): JSX.Element {
|
||||
const { t } = useTranslation(['alerts']);
|
||||
|
||||
const renderOptions = (): JSX.Element => {
|
||||
const optionList: OptionType[] = [
|
||||
{
|
||||
title: t('metric_based_alert'),
|
||||
selection: AlertTypes.METRICS_BASED_ALERT,
|
||||
description: t('metric_based_alert_desc'),
|
||||
},
|
||||
{
|
||||
title: t('log_based_alert'),
|
||||
selection: AlertTypes.LOGS_BASED_ALERT,
|
||||
description: t('log_based_alert_desc'),
|
||||
},
|
||||
{
|
||||
title: t('traces_based_alert'),
|
||||
selection: AlertTypes.TRACES_BASED_ALERT,
|
||||
description: t('traces_based_alert_desc'),
|
||||
},
|
||||
{
|
||||
title: t('exceptions_based_alert'),
|
||||
selection: AlertTypes.EXCEPTIONS_BASED_ALERT,
|
||||
description: t('exceptions_based_alert_desc'),
|
||||
},
|
||||
];
|
||||
return (
|
||||
const optionList = getOptionList(t);
|
||||
|
||||
const renderOptions = useMemo(
|
||||
() => (
|
||||
<>
|
||||
{optionList.map((o: OptionType) => (
|
||||
{optionList.map((option: OptionType) => (
|
||||
<AlertTypeCard
|
||||
key={o.selection}
|
||||
title={o.title}
|
||||
key={option.selection}
|
||||
title={option.title}
|
||||
onClick={(): void => {
|
||||
onSelect(o.selection);
|
||||
onSelect(option.selection);
|
||||
}}
|
||||
>
|
||||
{o.description}
|
||||
{option.description}
|
||||
</AlertTypeCard>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
),
|
||||
[onSelect, optionList],
|
||||
);
|
||||
|
||||
return (
|
||||
<SelectTypeContainer>
|
||||
<h3> {t('choose_alert_type')} </h3>
|
||||
<Row>{renderOptions()}</Row>
|
||||
<Row>{renderOptions}</Row>
|
||||
</SelectTypeContainer>
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
||||
|
||||
export interface OptionType {
|
||||
title: string;
|
||||
selection: AlertTypes;
|
||||
description: string;
|
||||
}
|
8
frontend/src/container/CreateAlertRule/config.ts
Normal file
8
frontend/src/container/CreateAlertRule/config.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
export const ALERT_TYPE_VS_SOURCE_MAPPING = {
|
||||
[DataSource.LOGS]: AlertTypes.LOGS_BASED_ALERT,
|
||||
[DataSource.METRICS]: AlertTypes.METRICS_BASED_ALERT,
|
||||
[DataSource.TRACES]: AlertTypes.TRACES_BASED_ALERT,
|
||||
};
|
@ -1,9 +1,11 @@
|
||||
import { Form, Row } from 'antd';
|
||||
import FormAlertRules from 'container/FormAlertRules';
|
||||
import { useState } from 'react';
|
||||
import { useGetCompositeQueryParam } from 'hooks/queryBuilder/useGetCompositeQueryParam';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
||||
import { AlertDef } from 'types/api/alerts/def';
|
||||
|
||||
import { ALERT_TYPE_VS_SOURCE_MAPPING } from './config';
|
||||
import {
|
||||
alertDefaults,
|
||||
exceptionAlertDefaults,
|
||||
@ -17,6 +19,9 @@ function CreateRules(): JSX.Element {
|
||||
const [alertType, setAlertType] = useState<AlertTypes>(
|
||||
AlertTypes.METRICS_BASED_ALERT,
|
||||
);
|
||||
|
||||
const compositeQuery = useGetCompositeQueryParam();
|
||||
|
||||
const [formInstance] = Form.useForm();
|
||||
|
||||
const onSelectType = (typ: AlertTypes): void => {
|
||||
@ -36,6 +41,19 @@ function CreateRules(): JSX.Element {
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!compositeQuery) {
|
||||
return;
|
||||
}
|
||||
const dataSource = compositeQuery?.builder?.queryData[0]?.dataSource;
|
||||
|
||||
const alertType = ALERT_TYPE_VS_SOURCE_MAPPING[dataSource];
|
||||
|
||||
if (alertType) {
|
||||
onSelectType(alertType);
|
||||
}
|
||||
}, [compositeQuery]);
|
||||
|
||||
if (!initValues) {
|
||||
return (
|
||||
<Row wrap={false}>
|
||||
|
@ -1,24 +1,44 @@
|
||||
import { Button, Dropdown, MenuProps, Modal } from 'antd';
|
||||
import { COMPOSITE_QUERY } from 'constants/queryBuilderQueryNames';
|
||||
import ROUTES from 'constants/routes';
|
||||
import history from 'lib/history';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
import { MENU_KEY, MENU_LABEL } from './config';
|
||||
import ExportPanelContainer from './ExportPanel';
|
||||
|
||||
function ExportPanel({ isLoading, onExport }: ExportPanelProps): JSX.Element {
|
||||
function ExportPanel({
|
||||
isLoading,
|
||||
onExport,
|
||||
query,
|
||||
}: ExportPanelProps): JSX.Element {
|
||||
const [isExport, setIsExport] = useState<boolean>(false);
|
||||
|
||||
const onModalToggle = useCallback((value: boolean) => {
|
||||
setIsExport(value);
|
||||
}, []);
|
||||
|
||||
const onCreateAlertsHandler = useCallback(() => {
|
||||
history.push(
|
||||
`${ROUTES.ALERTS_NEW}?${COMPOSITE_QUERY}=${encodeURIComponent(
|
||||
JSON.stringify(query),
|
||||
)}`,
|
||||
);
|
||||
}, [query]);
|
||||
|
||||
const onMenuClickHandler: MenuProps['onClick'] = useCallback(
|
||||
(e: OnClickProps) => {
|
||||
if (e.key === MENU_KEY.EXPORT) {
|
||||
onModalToggle(true);
|
||||
}
|
||||
|
||||
if (e.key === MENU_KEY.CREATE_ALERTS) {
|
||||
onCreateAlertsHandler();
|
||||
}
|
||||
},
|
||||
[onModalToggle],
|
||||
[onModalToggle, onCreateAlertsHandler],
|
||||
);
|
||||
|
||||
const menu: MenuProps = useMemo(
|
||||
@ -54,7 +74,11 @@ function ExportPanel({ isLoading, onExport }: ExportPanelProps): JSX.Element {
|
||||
open={isExport}
|
||||
centered
|
||||
>
|
||||
<ExportPanelContainer isLoading={isLoading} onExport={onExport} />
|
||||
<ExportPanelContainer
|
||||
query={query}
|
||||
isLoading={isLoading}
|
||||
onExport={onExport}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
@ -71,6 +95,7 @@ interface OnClickProps {
|
||||
export interface ExportPanelProps {
|
||||
isLoading?: boolean;
|
||||
onExport: (dashboard: Dashboard | null) => void;
|
||||
query: Query | null;
|
||||
}
|
||||
|
||||
export default ExportPanel;
|
||||
|
@ -64,7 +64,7 @@ function WidgetHeader({
|
||||
history.push(
|
||||
`${window.location.pathname}/new?widgetId=${widgetId}&graphType=${
|
||||
widget.panelTypes
|
||||
}&${COMPOSITE_QUERY}=${JSON.stringify(widget.query)}`,
|
||||
}&${COMPOSITE_QUERY}=${encodeURIComponent(JSON.stringify(widget.query))}`,
|
||||
);
|
||||
}, [widget.id, widget.panelTypes, widget.query]);
|
||||
|
||||
|
@ -77,8 +77,8 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
|
||||
history.push(
|
||||
`${
|
||||
ROUTES.EDIT_ALERTS
|
||||
}?ruleId=${record.id.toString()}&${COMPOSITE_QUERY}=${JSON.stringify(
|
||||
compositeQuery,
|
||||
}?ruleId=${record.id.toString()}&${COMPOSITE_QUERY}=${encodeURIComponent(
|
||||
JSON.stringify(compositeQuery),
|
||||
)}`,
|
||||
);
|
||||
})
|
||||
|
@ -47,7 +47,9 @@ function DashboardGraphSlider({ toggleAddWidget }: Props): JSX.Element {
|
||||
history.push(
|
||||
`${history.location.pathname}/new?graphType=${name}&widgetId=${
|
||||
emptyLayout.i
|
||||
}&${COMPOSITE_QUERY}=${JSON.stringify(initialQueriesMap.metrics)}`,
|
||||
}&${COMPOSITE_QUERY}=${encodeURIComponent(
|
||||
JSON.stringify(initialQueriesMap.metrics),
|
||||
)}`,
|
||||
);
|
||||
} catch (error) {
|
||||
notifications.error({
|
||||
|
@ -8,7 +8,16 @@ export const useGetCompositeQueryParam = (): Query | null => {
|
||||
|
||||
return useMemo(() => {
|
||||
const compositeQuery = urlQuery.get(COMPOSITE_QUERY);
|
||||
let parsedCompositeQuery: Query | null = null;
|
||||
|
||||
return compositeQuery ? JSON.parse(compositeQuery) : null;
|
||||
try {
|
||||
if (!compositeQuery) return null;
|
||||
|
||||
parsedCompositeQuery = JSON.parse(decodeURIComponent(compositeQuery));
|
||||
} catch (e) {
|
||||
parsedCompositeQuery = null;
|
||||
}
|
||||
|
||||
return parsedCompositeQuery;
|
||||
}, [urlQuery]);
|
||||
};
|
||||
|
@ -74,7 +74,9 @@ function TracesExplorer(): JSX.Element {
|
||||
dashboardId: data?.payload?.uuid,
|
||||
})}/new?${QueryParams.graphType}=graph&${
|
||||
QueryParams.widgetId
|
||||
}=empty&${COMPOSITE_QUERY}=${JSON.stringify(exportDefaultQuery)}`;
|
||||
}=empty&${COMPOSITE_QUERY}=${encodeURIComponent(
|
||||
JSON.stringify(exportDefaultQuery),
|
||||
)}`;
|
||||
|
||||
history.push(dashboardEditView);
|
||||
},
|
||||
@ -118,7 +120,11 @@ function TracesExplorer(): JSX.Element {
|
||||
|
||||
<Container>
|
||||
<ActionsWrapper>
|
||||
<ExportPanel isLoading={isLoading} onExport={handleExport} />
|
||||
<ExportPanel
|
||||
query={stagedQuery}
|
||||
isLoading={isLoading}
|
||||
onExport={handleExport}
|
||||
/>
|
||||
</ActionsWrapper>
|
||||
|
||||
<Tabs
|
||||
|
@ -460,7 +460,10 @@ export function QueryBuilderProvider({
|
||||
id: uuid(),
|
||||
};
|
||||
|
||||
urlQuery.set(COMPOSITE_QUERY, JSON.stringify(currentGeneratedQuery));
|
||||
urlQuery.set(
|
||||
COMPOSITE_QUERY,
|
||||
encodeURIComponent(JSON.stringify(currentGeneratedQuery)),
|
||||
);
|
||||
|
||||
if (searchParams) {
|
||||
Object.keys(searchParams).forEach((param) =>
|
||||
|
@ -91,7 +91,11 @@ export const SaveDashboard = ({
|
||||
const compositeQuery = params.get(COMPOSITE_QUERY);
|
||||
const { maxTime, minTime } = store.getState().globalTime;
|
||||
const query = compositeQuery
|
||||
? updateStepInterval(JSON.parse(compositeQuery), maxTime, minTime)
|
||||
? updateStepInterval(
|
||||
JSON.parse(decodeURIComponent(compositeQuery)),
|
||||
maxTime,
|
||||
minTime,
|
||||
)
|
||||
: updateStepInterval(selectedWidget.query, maxTime, minTime);
|
||||
|
||||
const response = await updateDashboardApi({
|
||||
|
Loading…
x
Reference in New Issue
Block a user