When removing Slack ChatOps integration, warn the user of the implications (#1192)

# What this PR does
- When removing Slack ChatOps integration, warn the user of the
implications of doing so + make them confirm the deletion by having to
type `DELETE`:
![Screenshot 2023-01-23 at 15 01
27](https://user-images.githubusercontent.com/9406895/214060105-1af61170-3141-488c-8977-2809edb04faa.png)
- remove
`grafana-plugin/src/containers/SlackIntegrationButton/SlackIntegrationButton.tsx`
component as it is not referenced anywhere + remove
`grafana-plugin/src/img/slack_workspace_choose_attention.png` as this
was only referenced in `SlackIntegrationButton.tsx`

## Which issue(s) this PR fixes
https://github.com/grafana/oncall-private/issues/1588

## Checklist

- [ ] Tests updated (N/A)
- [ ] Documentation added (N/A)
- [x] `CHANGELOG.md` updated
This commit is contained in:
Joey Orlando 2023-01-24 11:21:11 +01:00 committed by GitHub
parent 46b39b2c87
commit 3bc593cdb2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 40 additions and 119 deletions

View file

@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Allow users with `viewer` role to fetch cloud connection status using the internal API ([#1181](https://github.com/grafana/oncall/pull/1181))
- When removing the Slack ChatOps integration, make it more explicit to the user what the implications of doing so are
### Fixed

View file

@ -1,18 +1,21 @@
import React, { ReactElement, useCallback, useState } from 'react';
import { ConfirmModal } from '@grafana/ui';
import { ConfirmModal, ConfirmModalProps } from '@grafana/ui';
interface WithConfirmProps {
type WithConfirmProps = Partial<ConfirmModalProps> & {
children: ReactElement;
title?: string;
body?: React.ReactNode;
confirmText?: string;
disabled?: boolean;
}
const WithConfirm = (props: WithConfirmProps) => {
const { children, title = 'Are you sure to delete?', body, confirmText = 'Delete', disabled } = props;
};
const WithConfirm: React.FC<WithConfirmProps> = ({
title = 'Are you sure to delete?',
confirmText = 'Delete',
body,
description,
confirmationText,
children,
disabled,
}) => {
const [showConfirmation, setShowConfirmation] = useState<boolean>(false);
const onClickCallback = useCallback((event) => {
@ -39,6 +42,8 @@ const WithConfirm = (props: WithConfirmProps) => {
dismissText="Cancel"
onConfirm={onConfirmCallback}
body={body}
description={description}
confirmationText={confirmationText}
onDismiss={() => {
setShowConfirmation(false);
}}

View file

@ -1,97 +0,0 @@
import React, { useCallback, useState } from 'react';
import { Button, Modal } from '@grafana/ui';
import { observer } from 'mobx-react';
import WithConfirm from 'components/WithConfirm/WithConfirm';
import { WithPermissionControl } from 'containers/WithPermissionControl/WithPermissionControl';
import { useStore } from 'state/useStore';
import { UserActions } from 'utils/authorization';
const SlackIntegrationButton = observer((props: { className: string; disabled?: boolean }) => {
const { className, disabled } = props;
const [showModal, setShowModal] = useState<boolean>(false);
const store = useStore();
const onInstallModalCallback = useCallback(() => {
setShowModal(true);
}, []);
const onInstallModalHideCallback = useCallback(() => {
setShowModal(false);
}, []);
const onRemoveClickCallback = useCallback(() => {
store.slackStore.removeSlackIntegration().then(() => {
store.teamStore.loadCurrentTeam();
});
}, []);
const onInstallClickCallback = useCallback(() => {
store.slackStore.installSlackIntegration();
}, []);
if (store.teamStore.currentTeam?.slack_team_identity) {
return (
<WithPermissionControl userAction={UserActions.IntegrationsWrite}>
<WithConfirm title="Are you sure to delete this Slack Integration?">
<Button
variant="destructive"
size="md"
icon="slack"
className={className}
disabled={disabled}
onClick={onRemoveClickCallback}
>
Remove Slack Integration ({store.teamStore.currentTeam.slack_team_identity?.cached_name})
</Button>
</WithConfirm>
</WithPermissionControl>
);
}
return (
<>
<WithPermissionControl userAction={UserActions.IntegrationsWrite}>
<Button
size="lg"
variant="primary"
icon="slack"
className={className}
disabled={disabled}
onClick={onInstallModalCallback}
>
Connect Slack
</Button>
</WithPermissionControl>
{showModal && <SlackModal onHide={onInstallModalHideCallback} onConfirm={onInstallClickCallback} />}
</>
);
});
interface SlackModalProps {
onHide: () => void;
onConfirm: () => void;
}
const SlackModal = (props: SlackModalProps) => {
const { onHide, onConfirm } = props;
return (
<Modal title="Slack connection" closeOnEscape isOpen onDismiss={onHide}>
<div style={{ textAlign: 'left' }}>
You can view your Slack Workspace at the top-right corner after you are redirected. It should be a Workspace
with App Bot installed:
</div>
<img
style={{ height: '350px', display: 'block', margin: '0 auto' }}
src="public/plugins/grafana-oncall-app/img/slack_workspace_choose_attention.png"
/>
<Button onClick={onConfirm}>I'll check! Proceed to Slack...</Button>
</Modal>
);
};
export default SlackIntegrationButton;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

View file

@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { Field, HorizontalGroup, LoadingPlaceholder, VerticalGroup, Icon, Button } from '@grafana/ui';
import { Alert, Field, HorizontalGroup, LoadingPlaceholder, VerticalGroup, Icon, Button } from '@grafana/ui';
import cn from 'classnames/bind';
import { observer } from 'mobx-react';
@ -124,8 +124,30 @@ class SlackSettings extends Component<SlackProps, SlackState> {
</WithPermissionControl>
</Field>
</HorizontalGroup>
<WithPermissionControl userAction={UserActions.ChatOpsWrite}>
<WithConfirm title="Are you sure to delete this Slack Integration?">
<WithPermissionControl userAction={UserActions.ChatOpsUpdateSettings}>
<WithConfirm
title="Remove Slack Integration for all of OnCall"
description={
<Alert severity="error" title="WARNING">
<p>Are you sure to delete this Slack Integration?</p>
<p>
Removing the integration will also irreverisbly remove the following data for your OnCall plugin:
</p>
<ul style={{ marginLeft: '20px' }}>
<li>default organization Slack channel</li>
<li>default Slack channels for OnCall Integrations</li>
<li>Slack channels & Slack user groups for OnCall Schedules</li>
<li>linked Slack usernames for OnCall Users</li>
</ul>
<br />
<p>
If you would like to instead remove your linked Slack username, please head{' '}
<PluginLink query={{ page: 'users/me' }}>here</PluginLink>.
</p>
</Alert>
}
confirmationText="DELETE"
>
<Button variant="destructive" size="sm" onClick={() => this.removeSlackIntegration()}>
Disconnect
</Button>
@ -189,16 +211,6 @@ class SlackSettings extends Component<SlackProps, SlackState> {
);
};
renderActionButtons = () => {
<WithPermissionControl userAction={UserActions.ChatOpsUpdateSettings}>
<WithConfirm title="Are you sure to delete this Slack Integration?">
<Button variant="destructive" size="sm" onClick={() => this.removeSlackIntegration()}>
Disconnect
</Button>
</WithConfirm>
</WithPermissionControl>;
};
removeSlackIntegration = () => {
const { store } = this.props;
store.slackStore.removeSlackIntegration().then(() => {