Show warning when edit AM-based resolve templates (#3764)
# What this PR does show autoresolve template for ALL integrations when autoresolve is ON show modal on edit button click for alertmanager based integrations <img width="557" alt="Screenshot 2024-01-29 at 13 37 08" src="https://github.com/grafana/oncall/assets/20116910/64569912-4601-4be1-b51e-b040906a3ffb"> ## Which issue(s) this PR fixes Frontend part of https://github.com/grafana/oncall-private/issues/2260 ## Checklist - [ ] Unit, integration, and e2e (if applicable) tests updated - [ ] Documentation added (or `pr:no public docs` PR label added if not required) - [ ] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not required) --------- Co-authored-by: Vadim Stepanov <vadimkerr@gmail.com>
This commit is contained in:
parent
bb7ce3d133
commit
29dadcc07f
4 changed files with 52 additions and 46 deletions
|
|
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
### Added
|
||||
|
||||
- Improved logging during plugin sync and install with Grafana @mderynck ([#3730](https://github.com/grafana/oncall/pull/3730))
|
||||
- Add a modal for autoresolve and grouping templates for Alertmanager-based integrations ([#3764](https://github.com/grafana/oncall/pull/3764))
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import React from 'react';
|
||||
|
||||
import { Button, InlineLabel, LoadingPlaceholder, Tooltip } from '@grafana/ui';
|
||||
import { Button, InlineLabel, LoadingPlaceholder } from '@grafana/ui';
|
||||
import cn from 'classnames/bind';
|
||||
|
||||
import WithConfirm from 'components/WithConfirm/WithConfirm';
|
||||
import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip';
|
||||
import { UserActions } from 'utils/authorization';
|
||||
|
||||
|
|
@ -17,6 +18,7 @@ interface IntegrationTemplateBlockProps {
|
|||
renderInput: () => React.ReactNode;
|
||||
showHelp?: boolean;
|
||||
isLoading?: boolean;
|
||||
warningOnEdit?: string;
|
||||
|
||||
onEdit: (templateName) => void;
|
||||
onRemove?: () => void;
|
||||
|
|
@ -31,6 +33,7 @@ const IntegrationTemplateBlock: React.FC<IntegrationTemplateBlockProps> = ({
|
|||
onEdit,
|
||||
onRemove,
|
||||
isLoading,
|
||||
warningOnEdit,
|
||||
}) => {
|
||||
let tooltip = labelTooltip;
|
||||
let inlineLabelProps = { tooltip };
|
||||
|
|
@ -48,14 +51,24 @@ const IntegrationTemplateBlock: React.FC<IntegrationTemplateBlockProps> = ({
|
|||
{isTemplateEditable && (
|
||||
<>
|
||||
<WithPermissionControlTooltip userAction={UserActions.IntegrationsWrite}>
|
||||
<Tooltip content={'Edit'}>
|
||||
<Button variant={'secondary'} icon={'edit'} tooltip="Edit" size={'md'} onClick={onEdit} />
|
||||
</Tooltip>
|
||||
<WithConfirm skip={!warningOnEdit} title="" body={warningOnEdit} confirmText="Edit">
|
||||
<Button variant={'secondary'} icon="edit" tooltip="Edit" size="md" onClick={onEdit} />
|
||||
</WithConfirm>
|
||||
</WithPermissionControlTooltip>
|
||||
<WithPermissionControlTooltip userAction={UserActions.IntegrationsWrite}>
|
||||
<Tooltip content={'Reset template to default'}>
|
||||
<Button variant={'secondary'} icon={'times'} size={'md'} onClick={onRemove} />
|
||||
</Tooltip>
|
||||
<WithConfirm
|
||||
title=""
|
||||
body={`Are you sure you want to reset the ${label} template to its default state?`}
|
||||
confirmText="Reset"
|
||||
>
|
||||
<Button
|
||||
variant="secondary"
|
||||
icon="times"
|
||||
size="md"
|
||||
tooltip="Reset template to default"
|
||||
onClick={onRemove}
|
||||
/>
|
||||
</WithConfirm>
|
||||
</WithPermissionControlTooltip>
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { ConfirmModal, ConfirmModalProps } from '@grafana/ui';
|
|||
type WithConfirmProps = Partial<ConfirmModalProps> & {
|
||||
children: ReactElement;
|
||||
disabled?: boolean;
|
||||
skip?: boolean;
|
||||
};
|
||||
|
||||
const WithConfirm: React.FC<WithConfirmProps> = ({
|
||||
|
|
@ -15,14 +16,23 @@ const WithConfirm: React.FC<WithConfirmProps> = ({
|
|||
confirmationText,
|
||||
children,
|
||||
disabled,
|
||||
skip = false,
|
||||
}) => {
|
||||
const [showConfirmation, setShowConfirmation] = useState<boolean>(false);
|
||||
|
||||
const onClickCallback = useCallback((event) => {
|
||||
event.stopPropagation();
|
||||
const onClickCallback = useCallback(
|
||||
(event) => {
|
||||
if (skip) {
|
||||
children.props.onClick?.();
|
||||
return;
|
||||
}
|
||||
|
||||
setShowConfirmation(true);
|
||||
}, []);
|
||||
event.stopPropagation();
|
||||
|
||||
setShowConfirmation(true);
|
||||
},
|
||||
[skip]
|
||||
);
|
||||
|
||||
const onConfirmCallback = useCallback(() => {
|
||||
if (children.props.onClick) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useState, useCallback } from 'react';
|
||||
|
||||
import { ConfirmModal, InlineSwitch, Tooltip } from '@grafana/ui';
|
||||
import { InlineSwitch, Tooltip } from '@grafana/ui';
|
||||
import cn from 'classnames/bind';
|
||||
import { observer } from 'mobx-react';
|
||||
|
||||
|
|
@ -39,7 +39,6 @@ const IntegrationTemplateList: React.FC<IntegrationTemplateListProps> = observer
|
|||
const { alertReceiveChannelStore, features } = useStore();
|
||||
const [isRestoringTemplate, setIsRestoringTemplate] = useState<boolean>(false);
|
||||
const [templateRestoreName, setTemplateRestoreName] = useState<string>(undefined);
|
||||
const [showConfirmModal, setShowConfirmModal] = useState(false);
|
||||
const [autoresolveValue, setAutoresolveValue] = useState<boolean>(alertReceiveChannelAllowSourceBasedResolving);
|
||||
|
||||
const handleSaveClick = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
|
|
@ -57,19 +56,6 @@ const IntegrationTemplateList: React.FC<IntegrationTemplateListProps> = observer
|
|||
|
||||
return (
|
||||
<div className={cx('integration__templates')}>
|
||||
{showConfirmModal && (
|
||||
<ConfirmModal
|
||||
isOpen={true}
|
||||
title={undefined}
|
||||
confirmText={'Reset'}
|
||||
dismissText="Cancel"
|
||||
body={'Are you sure you want to reset Slack Title template to default state?'}
|
||||
description={undefined}
|
||||
confirmationText={undefined}
|
||||
onConfirm={() => onResetTemplate(templateRestoreName)}
|
||||
onDismiss={() => onDismiss()}
|
||||
/>
|
||||
)}
|
||||
{templatesToRender.map((template, key) => (
|
||||
<IntegrationBlockItem key={key}>
|
||||
<VerticalBlock>
|
||||
|
|
@ -78,10 +64,18 @@ const IntegrationTemplateList: React.FC<IntegrationTemplateListProps> = observer
|
|||
<IntegrationTemplateBlock
|
||||
key={innerKey}
|
||||
isLoading={isRestoringTemplate && templateRestoreName === contents.name}
|
||||
onRemove={() => onShowConfirmModal(contents.name)}
|
||||
onRemove={() => onResetTemplate(contents.name)}
|
||||
label={contents.label}
|
||||
labelTooltip={contents.labelTooltip}
|
||||
isTemplateEditable={isResolveConditionTemplateEditable(contents.name)}
|
||||
isTemplateEditable={isTemplateEditable(contents.name)}
|
||||
warningOnEdit={
|
||||
alertReceiveChannelIsBasedOnAlertManager &&
|
||||
(isGroupingIdTemplate(contents.name) || isResolveConditionTemplate(contents.name))
|
||||
? 'Caution: Changing this template can lead to unexpected alert behavior, ' +
|
||||
'especially if grouping is enabled in AlertManager/Grafana Alerting. ' +
|
||||
'Please proceed only if you are completely sure of the modifications you are about to make.'
|
||||
: undefined
|
||||
}
|
||||
renderInput={() => (
|
||||
<>
|
||||
{isResolveConditionTemplate(contents.name) && (
|
||||
|
|
@ -90,10 +84,11 @@ const IntegrationTemplateList: React.FC<IntegrationTemplateListProps> = observer
|
|||
value={autoresolveValue}
|
||||
onChange={handleSaveClick}
|
||||
className={cx('inline-switch')}
|
||||
transparent
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
{isResolveConditionTemplateEditable(contents.name) && (
|
||||
{isTemplateEditable(contents.name) && (
|
||||
<div
|
||||
className={cx('input', { 'input-with-toggle': isResolveConditionTemplate(contents.name) })}
|
||||
>
|
||||
|
|
@ -121,25 +116,16 @@ const IntegrationTemplateList: React.FC<IntegrationTemplateListProps> = observer
|
|||
</div>
|
||||
);
|
||||
|
||||
function isResolveConditionTemplateEditable(templateName: string) {
|
||||
return (
|
||||
!(alertReceiveChannelIsBasedOnAlertManager && isResolveConditionTemplate(templateName)) &&
|
||||
(alertReceiveChannelAllowSourceBasedResolving || !isResolveConditionTemplate(templateName))
|
||||
);
|
||||
function isTemplateEditable(templateName: string) {
|
||||
return alertReceiveChannelAllowSourceBasedResolving || !isResolveConditionTemplate(templateName);
|
||||
}
|
||||
|
||||
function isResolveConditionTemplate(templateName: string) {
|
||||
return templateName === 'resolve_condition_template';
|
||||
}
|
||||
|
||||
function onShowConfirmModal(templateName: string) {
|
||||
setTemplateRestoreName(templateName);
|
||||
setShowConfirmModal(true);
|
||||
}
|
||||
|
||||
function onDismiss() {
|
||||
setTemplateRestoreName(undefined);
|
||||
setShowConfirmModal(false);
|
||||
function isGroupingIdTemplate(templateName: string) {
|
||||
return templateName === 'grouping_id_template';
|
||||
}
|
||||
|
||||
function onResetTemplate(templateName: string) {
|
||||
|
|
@ -157,10 +143,6 @@ const IntegrationTemplateList: React.FC<IntegrationTemplateListProps> = observer
|
|||
} else {
|
||||
openErrorNotification(err.message);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setIsRestoringTemplate(false);
|
||||
setShowConfirmModal(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue