mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-13 14:08:58 +08:00
feat: alert rename interaction (#6208)
* feat: alert rename interaction * feat: add support for enter and escape shortcuts to submit and cancel rename * chore: add missing alert field * chore: update the style similar to dashboard rename * refactor: remove buttonProps * chore: add missing alert property to fix the build --------- Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com>
This commit is contained in:
parent
939e2a3570
commit
577a169508
@ -18,4 +18,5 @@ export const REACT_QUERY_KEY = {
|
|||||||
GET_ALL_ALLERTS: 'GET_ALL_ALLERTS',
|
GET_ALL_ALLERTS: 'GET_ALL_ALLERTS',
|
||||||
REMOVE_ALERT_RULE: 'REMOVE_ALERT_RULE',
|
REMOVE_ALERT_RULE: 'REMOVE_ALERT_RULE',
|
||||||
DUPLICATE_ALERT_RULE: 'DUPLICATE_ALERT_RULE',
|
DUPLICATE_ALERT_RULE: 'DUPLICATE_ALERT_RULE',
|
||||||
|
UPDATE_ALERT_RULE: 'UPDATE_ALERT_RULE',
|
||||||
};
|
};
|
||||||
|
@ -57,6 +57,7 @@ export const alertDefaults: AlertDef = {
|
|||||||
},
|
},
|
||||||
annotations: defaultAnnotations,
|
annotations: defaultAnnotations,
|
||||||
evalWindow: defaultEvalWindow,
|
evalWindow: defaultEvalWindow,
|
||||||
|
alert: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const anamolyAlertDefaults: AlertDef = {
|
export const anamolyAlertDefaults: AlertDef = {
|
||||||
@ -101,6 +102,7 @@ export const anamolyAlertDefaults: AlertDef = {
|
|||||||
},
|
},
|
||||||
annotations: defaultAnnotations,
|
annotations: defaultAnnotations,
|
||||||
evalWindow: defaultEvalWindow,
|
evalWindow: defaultEvalWindow,
|
||||||
|
alert: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const logAlertDefaults: AlertDef = {
|
export const logAlertDefaults: AlertDef = {
|
||||||
@ -132,6 +134,7 @@ export const logAlertDefaults: AlertDef = {
|
|||||||
},
|
},
|
||||||
annotations: defaultAnnotations,
|
annotations: defaultAnnotations,
|
||||||
evalWindow: defaultEvalWindow,
|
evalWindow: defaultEvalWindow,
|
||||||
|
alert: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const traceAlertDefaults: AlertDef = {
|
export const traceAlertDefaults: AlertDef = {
|
||||||
@ -163,6 +166,7 @@ export const traceAlertDefaults: AlertDef = {
|
|||||||
},
|
},
|
||||||
annotations: defaultAnnotations,
|
annotations: defaultAnnotations,
|
||||||
evalWindow: defaultEvalWindow,
|
evalWindow: defaultEvalWindow,
|
||||||
|
alert: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const exceptionAlertDefaults: AlertDef = {
|
export const exceptionAlertDefaults: AlertDef = {
|
||||||
@ -194,6 +198,7 @@ export const exceptionAlertDefaults: AlertDef = {
|
|||||||
},
|
},
|
||||||
annotations: defaultAnnotations,
|
annotations: defaultAnnotations,
|
||||||
evalWindow: defaultEvalWindow,
|
evalWindow: defaultEvalWindow,
|
||||||
|
alert: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ALERTS_VALUES_MAP: Record<AlertTypes, AlertDef> = {
|
export const ALERTS_VALUES_MAP: Record<AlertTypes, AlertDef> = {
|
||||||
|
@ -2,82 +2,90 @@ import './ActionButtons.styles.scss';
|
|||||||
|
|
||||||
import { Color } from '@signozhq/design-tokens';
|
import { Color } from '@signozhq/design-tokens';
|
||||||
import { Divider, Dropdown, MenuProps, Switch, Tooltip } from 'antd';
|
import { Divider, Dropdown, MenuProps, Switch, Tooltip } from 'antd';
|
||||||
import { QueryParams } from 'constants/query';
|
|
||||||
import ROUTES from 'constants/routes';
|
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
import useUrlQuery from 'hooks/useUrlQuery';
|
|
||||||
import history from 'lib/history';
|
|
||||||
import { Copy, Ellipsis, PenLine, Trash2 } from 'lucide-react';
|
import { Copy, Ellipsis, PenLine, Trash2 } from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
useAlertRuleDelete,
|
useAlertRuleDelete,
|
||||||
useAlertRuleDuplicate,
|
useAlertRuleDuplicate,
|
||||||
useAlertRuleStatusToggle,
|
useAlertRuleStatusToggle,
|
||||||
|
useAlertRuleUpdate,
|
||||||
} from 'pages/AlertDetails/hooks';
|
} from 'pages/AlertDetails/hooks';
|
||||||
import CopyToClipboard from 'periscope/components/CopyToClipboard';
|
import CopyToClipboard from 'periscope/components/CopyToClipboard';
|
||||||
import { useAlertRule } from 'providers/Alert';
|
import { useAlertRule } from 'providers/Alert';
|
||||||
import React, { useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { CSSProperties } from 'styled-components';
|
import { CSSProperties } from 'styled-components';
|
||||||
import { AlertDef } from 'types/api/alerts/def';
|
import { AlertDef } from 'types/api/alerts/def';
|
||||||
|
|
||||||
import { AlertHeaderProps } from '../AlertHeader';
|
import { AlertHeaderProps } from '../AlertHeader';
|
||||||
|
import RenameModal from './RenameModal';
|
||||||
|
|
||||||
const menuItemStyle: CSSProperties = {
|
const menuItemStyle: CSSProperties = {
|
||||||
fontSize: '14px',
|
fontSize: '14px',
|
||||||
letterSpacing: '0.14px',
|
letterSpacing: '0.14px',
|
||||||
};
|
};
|
||||||
|
|
||||||
function AlertActionButtons({
|
function AlertActionButtons({
|
||||||
ruleId,
|
ruleId,
|
||||||
alertDetails,
|
alertDetails,
|
||||||
|
setUpdatedName,
|
||||||
}: {
|
}: {
|
||||||
ruleId: string;
|
ruleId: string;
|
||||||
alertDetails: AlertHeaderProps['alertDetails'];
|
alertDetails: AlertHeaderProps['alertDetails'];
|
||||||
|
setUpdatedName: (name: string) => void;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const { alertRuleState, setAlertRuleState } = useAlertRule();
|
const { alertRuleState, setAlertRuleState } = useAlertRule();
|
||||||
const { handleAlertStateToggle } = useAlertRuleStatusToggle({ ruleId });
|
const [intermediateName, setIntermediateName] = useState<string>(
|
||||||
|
alertDetails.alert,
|
||||||
|
);
|
||||||
|
const [isRenameAlertOpen, setIsRenameAlertOpen] = useState<boolean>(false);
|
||||||
|
const isDarkMode = useIsDarkMode();
|
||||||
|
|
||||||
|
const { handleAlertStateToggle } = useAlertRuleStatusToggle({ ruleId });
|
||||||
const { handleAlertDuplicate } = useAlertRuleDuplicate({
|
const { handleAlertDuplicate } = useAlertRuleDuplicate({
|
||||||
alertDetails: (alertDetails as unknown) as AlertDef,
|
alertDetails: (alertDetails as unknown) as AlertDef,
|
||||||
});
|
});
|
||||||
const { handleAlertDelete } = useAlertRuleDelete({ ruleId: Number(ruleId) });
|
const { handleAlertDelete } = useAlertRuleDelete({ ruleId: Number(ruleId) });
|
||||||
|
const { handleAlertUpdate, isLoading } = useAlertRuleUpdate({
|
||||||
|
alertDetails: (alertDetails as unknown) as AlertDef,
|
||||||
|
setUpdatedName,
|
||||||
|
intermediateName,
|
||||||
|
});
|
||||||
|
|
||||||
const params = useUrlQuery();
|
const handleRename = useCallback(() => {
|
||||||
|
setIsRenameAlertOpen(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleRename = React.useCallback(() => {
|
const onNameChangeHandler = useCallback(() => {
|
||||||
params.set(QueryParams.ruleId, String(ruleId));
|
handleAlertUpdate();
|
||||||
history.push(`${ROUTES.ALERT_OVERVIEW}?${params.toString()}`);
|
setIsRenameAlertOpen(false);
|
||||||
}, [params, ruleId]);
|
}, [handleAlertUpdate]);
|
||||||
|
|
||||||
const menu: MenuProps['items'] = React.useMemo(
|
const menuItems: MenuProps['items'] = [
|
||||||
() => [
|
{
|
||||||
{
|
key: 'rename-rule',
|
||||||
key: 'rename-rule',
|
label: 'Rename',
|
||||||
label: 'Rename',
|
icon: <PenLine size={16} color={Color.BG_VANILLA_400} />,
|
||||||
icon: <PenLine size={16} color={Color.BG_VANILLA_400} />,
|
onClick: handleRename,
|
||||||
onClick: (): void => handleRename(),
|
style: menuItemStyle,
|
||||||
style: menuItemStyle,
|
},
|
||||||
|
{
|
||||||
|
key: 'duplicate-rule',
|
||||||
|
label: 'Duplicate',
|
||||||
|
icon: <Copy size={16} color={Color.BG_VANILLA_400} />,
|
||||||
|
onClick: handleAlertDuplicate,
|
||||||
|
style: menuItemStyle,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'delete-rule',
|
||||||
|
label: 'Delete',
|
||||||
|
icon: <Trash2 size={16} color={Color.BG_CHERRY_400} />,
|
||||||
|
onClick: handleAlertDelete,
|
||||||
|
style: {
|
||||||
|
...menuItemStyle,
|
||||||
|
color: Color.BG_CHERRY_400,
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
key: 'duplicate-rule',
|
];
|
||||||
label: 'Duplicate',
|
|
||||||
icon: <Copy size={16} color={Color.BG_VANILLA_400} />,
|
|
||||||
onClick: (): void => handleAlertDuplicate(),
|
|
||||||
style: menuItemStyle,
|
|
||||||
},
|
|
||||||
{ type: 'divider' },
|
|
||||||
{
|
|
||||||
key: 'delete-rule',
|
|
||||||
label: 'Delete',
|
|
||||||
icon: <Trash2 size={16} color={Color.BG_CHERRY_400} />,
|
|
||||||
onClick: (): void => handleAlertDelete(),
|
|
||||||
style: {
|
|
||||||
...menuItemStyle,
|
|
||||||
color: Color.BG_CHERRY_400,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[handleAlertDelete, handleAlertDuplicate, handleRename],
|
|
||||||
);
|
|
||||||
const isDarkMode = useIsDarkMode();
|
|
||||||
|
|
||||||
// state for immediate UI feedback rather than waiting for onSuccess of handleAlertStateTiggle to updating the alertRuleState
|
// state for immediate UI feedback rather than waiting for onSuccess of handleAlertStateTiggle to updating the alertRuleState
|
||||||
const [isAlertRuleDisabled, setIsAlertRuleDisabled] = useState<
|
const [isAlertRuleDisabled, setIsAlertRuleDisabled] = useState<
|
||||||
@ -95,35 +103,48 @@ function AlertActionButtons({
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
useEffect(() => (): void => setAlertRuleState(undefined), []);
|
useEffect(() => (): void => setAlertRuleState(undefined), []);
|
||||||
|
|
||||||
|
const toggleAlertRule = useCallback(() => {
|
||||||
|
setIsAlertRuleDisabled((prev) => !prev);
|
||||||
|
handleAlertStateToggle();
|
||||||
|
}, [handleAlertStateToggle]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="alert-action-buttons">
|
<>
|
||||||
<Tooltip title={alertRuleState ? 'Enable alert' : 'Disable alert'}>
|
<div className="alert-action-buttons">
|
||||||
{isAlertRuleDisabled !== undefined && (
|
<Tooltip title={alertRuleState ? 'Enable alert' : 'Disable alert'}>
|
||||||
<Switch
|
{isAlertRuleDisabled !== undefined && (
|
||||||
size="small"
|
<Switch
|
||||||
onChange={(): void => {
|
size="small"
|
||||||
setIsAlertRuleDisabled((prev) => !prev);
|
onChange={toggleAlertRule}
|
||||||
handleAlertStateToggle();
|
checked={!isAlertRuleDisabled}
|
||||||
}}
|
/>
|
||||||
checked={!isAlertRuleDisabled}
|
)}
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Tooltip>
|
|
||||||
<CopyToClipboard textToCopy={window.location.href} />
|
|
||||||
|
|
||||||
<Divider type="vertical" />
|
|
||||||
|
|
||||||
<Dropdown trigger={['click']} menu={{ items: menu }}>
|
|
||||||
<Tooltip title="More options">
|
|
||||||
<Ellipsis
|
|
||||||
size={16}
|
|
||||||
color={isDarkMode ? Color.BG_VANILLA_400 : Color.BG_INK_400}
|
|
||||||
cursor="pointer"
|
|
||||||
className="dropdown-icon"
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Dropdown>
|
<CopyToClipboard textToCopy={window.location.href} />
|
||||||
</div>
|
|
||||||
|
<Divider type="vertical" />
|
||||||
|
|
||||||
|
<Dropdown trigger={['click']} menu={{ items: menuItems }}>
|
||||||
|
<Tooltip title="More options">
|
||||||
|
<Ellipsis
|
||||||
|
size={16}
|
||||||
|
color={isDarkMode ? Color.BG_VANILLA_400 : Color.BG_INK_400}
|
||||||
|
cursor="pointer"
|
||||||
|
className="dropdown-icon"
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<RenameModal
|
||||||
|
isOpen={isRenameAlertOpen}
|
||||||
|
setIsOpen={setIsRenameAlertOpen}
|
||||||
|
isLoading={isLoading}
|
||||||
|
onNameChangeHandler={onNameChangeHandler}
|
||||||
|
intermediateName={intermediateName}
|
||||||
|
setIntermediateName={setIntermediateName}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,138 @@
|
|||||||
|
.rename-alert {
|
||||||
|
.ant-modal-content {
|
||||||
|
width: 384px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid var(--bg-slate-500);
|
||||||
|
background: var(--bg-ink-400);
|
||||||
|
box-shadow: 0px -4px 16px 2px rgba(0, 0, 0, 0.2);
|
||||||
|
padding: 0px;
|
||||||
|
|
||||||
|
.ant-modal-header {
|
||||||
|
height: 52px;
|
||||||
|
padding: 16px;
|
||||||
|
background: var(--bg-ink-400);
|
||||||
|
border-bottom: 1px solid var(--bg-slate-500);
|
||||||
|
margin-bottom: 0px;
|
||||||
|
.ant-modal-title {
|
||||||
|
color: var(--bg-vanilla-100);
|
||||||
|
font-family: Inter;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px; /* 142.857% */
|
||||||
|
width: 349px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-modal-body {
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
.alert-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.name-text {
|
||||||
|
color: var(--bg-vanilla-100);
|
||||||
|
font-family: Inter;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 20px; /* 142.857% */
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-name-input {
|
||||||
|
display: flex;
|
||||||
|
padding: 6px 6px 6px 8px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
align-self: stretch;
|
||||||
|
border-radius: 0px 2px 2px 0px;
|
||||||
|
border: 1px solid var(--bg-slate-400);
|
||||||
|
background: var(--bg-ink-300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-modal-footer {
|
||||||
|
padding: 16px;
|
||||||
|
margin-top: 0px;
|
||||||
|
.alert-rename {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.cancel-btn {
|
||||||
|
display: flex;
|
||||||
|
padding: 4px 8px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: var(--bg-slate-500);
|
||||||
|
|
||||||
|
.ant-btn-icon {
|
||||||
|
margin-inline-end: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rename-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
padding: 4px 8px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: var(--bg-robin-500);
|
||||||
|
|
||||||
|
.ant-btn-icon {
|
||||||
|
margin-inline-end: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.lightMode {
|
||||||
|
.rename-alert {
|
||||||
|
.ant-modal-content {
|
||||||
|
border: 1px solid var(--bg-vanilla-300);
|
||||||
|
background: var(--bg-vanilla-100);
|
||||||
|
|
||||||
|
.ant-modal-header {
|
||||||
|
background: var(--bg-vanilla-100);
|
||||||
|
border-bottom: 1px solid var(--bg-vanilla-300);
|
||||||
|
|
||||||
|
.ant-modal-title {
|
||||||
|
color: var(--bg-ink-300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-modal-body {
|
||||||
|
.alert-content {
|
||||||
|
.name-text {
|
||||||
|
color: var(--bg-ink-300);
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-name-input {
|
||||||
|
border: 1px solid var(--bg-vanilla-300);
|
||||||
|
background: var(--bg-vanilla-100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-modal-footer {
|
||||||
|
.alert-rename {
|
||||||
|
.cancel-btn {
|
||||||
|
background: var(--bg-vanilla-300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
import './RenameModal.styles.scss';
|
||||||
|
|
||||||
|
import { Button, Input, InputRef, Modal, Typography } from 'antd';
|
||||||
|
import { Check, X } from 'lucide-react';
|
||||||
|
import { useCallback, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
isOpen: boolean;
|
||||||
|
setIsOpen: (isOpen: boolean) => void;
|
||||||
|
onNameChangeHandler: () => void;
|
||||||
|
isLoading: boolean;
|
||||||
|
intermediateName: string;
|
||||||
|
setIntermediateName: (name: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
function RenameModal({
|
||||||
|
isOpen,
|
||||||
|
setIsOpen,
|
||||||
|
onNameChangeHandler,
|
||||||
|
isLoading,
|
||||||
|
intermediateName,
|
||||||
|
setIntermediateName,
|
||||||
|
}: Props): JSX.Element {
|
||||||
|
const inputRef = useRef<InputRef>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isOpen && inputRef.current) {
|
||||||
|
inputRef.current.focus();
|
||||||
|
}
|
||||||
|
}, [isOpen]);
|
||||||
|
|
||||||
|
const handleClose = useCallback((): void => setIsOpen(false), [setIsOpen]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleKeyDown = (e: KeyboardEvent): void => {
|
||||||
|
if (isOpen) {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
onNameChangeHandler();
|
||||||
|
} else if (e.key === 'Escape') {
|
||||||
|
handleClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('keydown', handleKeyDown);
|
||||||
|
|
||||||
|
return (): void => {
|
||||||
|
document.removeEventListener('keydown', handleKeyDown);
|
||||||
|
};
|
||||||
|
}, [isOpen, onNameChangeHandler, handleClose]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open={isOpen}
|
||||||
|
title="Rename Alert"
|
||||||
|
onOk={onNameChangeHandler}
|
||||||
|
onCancel={handleClose}
|
||||||
|
rootClassName="rename-alert"
|
||||||
|
footer={
|
||||||
|
<div className="alert-rename">
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
icon={<Check size={14} />}
|
||||||
|
className="rename-btn"
|
||||||
|
onClick={onNameChangeHandler}
|
||||||
|
disabled={isLoading}
|
||||||
|
>
|
||||||
|
Rename Alert
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
icon={<X size={14} />}
|
||||||
|
className="cancel-btn"
|
||||||
|
onClick={handleClose}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="alert-content">
|
||||||
|
<Typography.Text className="name-text">Enter a new name</Typography.Text>
|
||||||
|
<Input
|
||||||
|
ref={inputRef}
|
||||||
|
data-testid="alert-name"
|
||||||
|
className="alert-name-input"
|
||||||
|
value={intermediateName}
|
||||||
|
onChange={(e): void => setIntermediateName(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RenameModal;
|
@ -2,7 +2,7 @@ import './AlertHeader.styles.scss';
|
|||||||
|
|
||||||
import LineClampedText from 'periscope/components/LineClampedText/LineClampedText';
|
import LineClampedText from 'periscope/components/LineClampedText/LineClampedText';
|
||||||
import { useAlertRule } from 'providers/Alert';
|
import { useAlertRule } from 'providers/Alert';
|
||||||
import { useMemo } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
|
|
||||||
import AlertActionButtons from './ActionButtons/ActionButtons';
|
import AlertActionButtons from './ActionButtons/ActionButtons';
|
||||||
import AlertLabels from './AlertLabels/AlertLabels';
|
import AlertLabels from './AlertLabels/AlertLabels';
|
||||||
@ -19,7 +19,9 @@ export type AlertHeaderProps = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
function AlertHeader({ alertDetails }: AlertHeaderProps): JSX.Element {
|
function AlertHeader({ alertDetails }: AlertHeaderProps): JSX.Element {
|
||||||
const { state, alert, labels } = alertDetails;
|
const { state, alert: alertName, labels } = alertDetails;
|
||||||
|
const { alertRuleState } = useAlertRule();
|
||||||
|
const [updatedName, setUpdatedName] = useState(alertName);
|
||||||
|
|
||||||
const labelsWithoutSeverity = useMemo(
|
const labelsWithoutSeverity = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@ -29,8 +31,6 @@ function AlertHeader({ alertDetails }: AlertHeaderProps): JSX.Element {
|
|||||||
[labels],
|
[labels],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { alertRuleState } = useAlertRule();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="alert-info">
|
<div className="alert-info">
|
||||||
<div className="alert-info__info-wrapper">
|
<div className="alert-info__info-wrapper">
|
||||||
@ -38,7 +38,7 @@ function AlertHeader({ alertDetails }: AlertHeaderProps): JSX.Element {
|
|||||||
<div className="alert-title-wrapper">
|
<div className="alert-title-wrapper">
|
||||||
<AlertState state={alertRuleState ?? state} />
|
<AlertState state={alertRuleState ?? state} />
|
||||||
<div className="alert-title">
|
<div className="alert-title">
|
||||||
<LineClampedText text={alert} />
|
<LineClampedText text={updatedName || alertName} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -54,7 +54,11 @@ function AlertHeader({ alertDetails }: AlertHeaderProps): JSX.Element {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="alert-info__action-buttons">
|
<div className="alert-info__action-buttons">
|
||||||
<AlertActionButtons alertDetails={alertDetails} ruleId={alertDetails.id} />
|
<AlertActionButtons
|
||||||
|
alertDetails={alertDetails}
|
||||||
|
ruleId={alertDetails.id}
|
||||||
|
setUpdatedName={setUpdatedName}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -467,6 +467,44 @@ export const useAlertRuleDuplicate = ({
|
|||||||
|
|
||||||
return { handleAlertDuplicate };
|
return { handleAlertDuplicate };
|
||||||
};
|
};
|
||||||
|
export const useAlertRuleUpdate = ({
|
||||||
|
alertDetails,
|
||||||
|
setUpdatedName,
|
||||||
|
intermediateName,
|
||||||
|
}: {
|
||||||
|
alertDetails: AlertDef;
|
||||||
|
setUpdatedName: (name: string) => void;
|
||||||
|
intermediateName: string;
|
||||||
|
}): {
|
||||||
|
handleAlertUpdate: () => void;
|
||||||
|
isLoading: boolean;
|
||||||
|
} => {
|
||||||
|
const { notifications } = useNotifications();
|
||||||
|
const handleError = useAxiosError();
|
||||||
|
|
||||||
|
const { mutate: updateAlertRule, isLoading } = useMutation(
|
||||||
|
[REACT_QUERY_KEY.UPDATE_ALERT_RULE, alertDetails.id],
|
||||||
|
save,
|
||||||
|
{
|
||||||
|
onMutate: () => setUpdatedName(intermediateName),
|
||||||
|
onSuccess: () =>
|
||||||
|
notifications.success({ message: 'Alert renamed successfully' }),
|
||||||
|
onError: (error) => {
|
||||||
|
setUpdatedName(alertDetails.alert);
|
||||||
|
handleError(error);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleAlertUpdate = (): void => {
|
||||||
|
updateAlertRule({
|
||||||
|
data: { ...alertDetails, alert: intermediateName },
|
||||||
|
id: alertDetails.id,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return { handleAlertUpdate, isLoading };
|
||||||
|
};
|
||||||
|
|
||||||
export const useAlertRuleDelete = ({
|
export const useAlertRuleDelete = ({
|
||||||
ruleId,
|
ruleId,
|
||||||
|
@ -19,7 +19,7 @@ export const defaultSeasonality = 'hourly';
|
|||||||
export interface AlertDef {
|
export interface AlertDef {
|
||||||
id?: number;
|
id?: number;
|
||||||
alertType?: string;
|
alertType?: string;
|
||||||
alert?: string;
|
alert: string;
|
||||||
ruleType?: string;
|
ruleType?: string;
|
||||||
frequency?: string;
|
frequency?: string;
|
||||||
condition: RuleCondition;
|
condition: RuleCondition;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user