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:
Maxim Mordasov 2024-01-29 17:15:56 +03:00 committed by GitHub
parent bb7ce3d133
commit 29dadcc07f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 52 additions and 46 deletions

View file

@ -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

View file

@ -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>
</>
)}

View file

@ -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) {

View file

@ -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);
});
}
}