Yunus M 2180118094
feat: anomaly detection UI (#5916)
* feat: anamoly detection - initial ui

* feat: anamoly detection - oct 10

* feat: use antd checkbox

* feat: handle multiple series

* feat: handle chart height on switch btwn threshold / anomaly

* feat: do not update url on detection type change

* chore: pr clean up

* feat: remove chart container background
2024-10-14 10:31:02 +05:30

256 lines
6.6 KiB
TypeScript

import './FormAlertRules.styles.scss';
import { PlusOutlined } from '@ant-design/icons';
import { Button, Form, Select, Switch, Tooltip } from 'antd';
import getChannels from 'api/channels/getAll';
import logEvent from 'api/common/logEvent';
import { ALERTS_DATA_SOURCE_MAP } from 'constants/alerts';
import ROUTES from 'constants/routes';
import useComponentPermission from 'hooks/useComponentPermission';
import useFetch from 'hooks/useFetch';
import { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import { AlertTypes } from 'types/api/alerts/alertTypes';
import { AlertDef, Labels } from 'types/api/alerts/def';
import AppReducer from 'types/reducer/app';
import { requireErrorMessage } from 'utils/form/requireErrorMessage';
import { popupContainer } from 'utils/selectPopupContainer';
import ChannelSelect from './ChannelSelect';
import LabelSelect from './labels';
import {
FormContainer,
FormItemMedium,
InputSmall,
SeveritySelect,
StepHeading,
TextareaMedium,
} from './styles';
const { Option } = Select;
interface BasicInfoProps {
isNewRule: boolean;
alertDef: AlertDef;
setAlertDef: (a: AlertDef) => void;
}
function BasicInfo({
isNewRule,
alertDef,
setAlertDef,
}: BasicInfoProps): JSX.Element {
const { t } = useTranslation('alerts');
const channels = useFetch(getChannels);
const { role } = useSelector<AppState, AppReducer>((state) => state.app);
const [addNewChannelPermission] = useComponentPermission(
['add_new_channel'],
role,
);
const [
shouldBroadCastToAllChannels,
setShouldBroadCastToAllChannels,
] = useState(false);
useEffect(() => {
const hasPreferredChannels =
(alertDef.preferredChannels && alertDef.preferredChannels.length > 0) ||
isNewRule;
setShouldBroadCastToAllChannels(!hasPreferredChannels);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const handleBroadcastToAllChannels = (shouldBroadcast: boolean): void => {
setShouldBroadCastToAllChannels(shouldBroadcast);
setAlertDef({
...alertDef,
broadcastToAll: shouldBroadcast,
});
};
const noChannels = channels.payload?.length === 0;
const handleCreateNewChannels = useCallback(() => {
logEvent('Alert: Create notification channel button clicked', {
dataSource: ALERTS_DATA_SOURCE_MAP[alertDef?.alertType as AlertTypes],
ruleId: isNewRule ? 0 : alertDef?.id,
});
window.open(ROUTES.CHANNELS_NEW, '_blank');
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (!channels.loading && isNewRule) {
logEvent('Alert: New alert creation page visited', {
dataSource: ALERTS_DATA_SOURCE_MAP[alertDef?.alertType as AlertTypes],
numberOfChannels: channels?.payload?.length,
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [channels.payload, channels.loading]);
return (
<>
<StepHeading> {t('alert_form_step4')} </StepHeading>
<FormContainer>
<Form.Item
label={t('field_severity')}
labelAlign="left"
name={['labels', 'severity']}
>
<SeveritySelect
getPopupContainer={popupContainer}
defaultValue="critical"
onChange={(value: unknown | string): void => {
const s = (value as string) || 'critical';
setAlertDef({
...alertDef,
labels: {
...alertDef.labels,
severity: s,
},
});
}}
>
<Option value="critical">{t('option_critical')}</Option>
<Option value="error">{t('option_error')}</Option>
<Option value="warning">{t('option_warning')}</Option>
<Option value="info">{t('option_info')}</Option>
</SeveritySelect>
</Form.Item>
<Form.Item
required
name="alert"
labelAlign="left"
label={t('field_alert_name')}
rules={[
{ required: true, message: requireErrorMessage(t('field_alert_name')) },
]}
>
<InputSmall
onChange={(e): void => {
setAlertDef({
...alertDef,
alert: e.target.value,
});
}}
/>
</Form.Item>
<Form.Item
label={t('field_alert_desc')}
labelAlign="left"
name={['annotations', 'description']}
>
<TextareaMedium
onChange={(e): void => {
setAlertDef({
...alertDef,
annotations: {
...alertDef.annotations,
description: e.target.value,
},
});
}}
/>
</Form.Item>
<FormItemMedium label={t('field_labels')}>
<LabelSelect
onSetLabels={(l: Labels): void => {
setAlertDef({
...alertDef,
labels: {
...l,
},
});
}}
initialValues={alertDef.labels}
/>
</FormItemMedium>
<FormItemMedium
name="alert_all_configured_channels"
label="Alert all the configured channels"
>
<Tooltip
title={
noChannels
? 'No channels. Ask an admin to create a notification channel'
: undefined
}
placement="right"
>
<Switch
checked={shouldBroadCastToAllChannels}
onChange={handleBroadcastToAllChannels}
disabled={noChannels || !!channels.loading}
data-testid="alert-broadcast-to-all-channels"
/>
</Tooltip>
</FormItemMedium>
{!shouldBroadCastToAllChannels && (
<Tooltip
title={
noChannels
? 'No channels. Ask an admin to create a notification channel'
: undefined
}
placement="right"
>
<FormItemMedium
label="Notification Channels"
name="notification_channels"
required
rules={[
{ required: true, message: requireErrorMessage(t('field_alert_name')) },
]}
>
<ChannelSelect
disabled={
shouldBroadCastToAllChannels || noChannels || !!channels.loading
}
currentValue={alertDef.preferredChannels}
channels={channels}
onSelectChannels={(preferredChannels): void => {
setAlertDef({
...alertDef,
preferredChannels,
});
}}
/>
</FormItemMedium>
</Tooltip>
)}
{noChannels && (
<Tooltip
title={
!addNewChannelPermission
? 'Ask an admin to create a notification channel'
: undefined
}
placement="right"
>
<Button
onClick={handleCreateNewChannels}
icon={<PlusOutlined />}
className="create-notification-btn"
disabled={!addNewChannelPermission}
>
Create a notification channel
</Button>
</Tooltip>
)}
</FormContainer>
</>
);
}
export default BasicInfo;