From 1708410f215dc9893184809515755064ddf32893 Mon Sep 17 00:00:00 2001 From: Yulia Shanyrova Date: Mon, 12 Sep 2022 14:28:46 +0200 Subject: [PATCH 1/8] WIP slack changes --- .../SlackIntegrationButton/SlackIntegrationButton.tsx | 6 +++--- .../chat-ops/parts/tabs/SlackSettings/SlackSettings.tsx | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/grafana-plugin/src/containers/SlackIntegrationButton/SlackIntegrationButton.tsx b/grafana-plugin/src/containers/SlackIntegrationButton/SlackIntegrationButton.tsx index 2909b2f4..b414fce5 100644 --- a/grafana-plugin/src/containers/SlackIntegrationButton/SlackIntegrationButton.tsx +++ b/grafana-plugin/src/containers/SlackIntegrationButton/SlackIntegrationButton.tsx @@ -64,7 +64,7 @@ const SlackIntegrationButton = observer((props: { className: string; disabled?: disabled={disabled} onClick={onInstallModalCallback} > - Install Slack integration + Connect Slack {showModal && } @@ -81,8 +81,8 @@ const SlackModal = (props: SlackModalProps) => { const { onHide, onConfirm } = props; return ( - -
+ +
You can view your Slack Workspace at the top-right corner after you are redirected. It should be a Workspace with App Bot installed:
diff --git a/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.tsx b/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.tsx index 67abb7d5..8d88622b 100644 --- a/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.tsx +++ b/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.tsx @@ -144,7 +144,8 @@ class SlackSettings extends Component { step={TutorialStep.Slack} title={ - + Connect your Slack workspace + Bring the whole incident lifecycle to Slack, from alerts, monitoring, escalations to resolution notes and reports. From c103464981d5adadc53adc8da684d9b300a41ff1 Mon Sep 17 00:00:00 2001 From: Yulia Shanyrova Date: Wed, 28 Sep 2022 10:20:18 +0200 Subject: [PATCH 2/8] WIP. Slack redesign work --- .../SlackInstructions.module.css | 11 +++ .../SlackInstructions/SlackInstructions.tsx | 69 ++++++++++++++++++ grafana-plugin/src/icons/index.tsx | 38 ++++++++++ .../SlackSettings/SlackSettings.module.css | 5 ++ .../tabs/SlackSettings/SlackSettings.tsx | 71 +++++++++++++------ 5 files changed, 174 insertions(+), 20 deletions(-) create mode 100644 grafana-plugin/src/containers/SlackInstructions/SlackInstructions.module.css create mode 100644 grafana-plugin/src/containers/SlackInstructions/SlackInstructions.tsx diff --git a/grafana-plugin/src/containers/SlackInstructions/SlackInstructions.module.css b/grafana-plugin/src/containers/SlackInstructions/SlackInstructions.module.css new file mode 100644 index 00000000..2f95b63e --- /dev/null +++ b/grafana-plugin/src/containers/SlackInstructions/SlackInstructions.module.css @@ -0,0 +1,11 @@ +.slack-infoblock { + width: 725px; +} + +.slack-infoblock input{ + color: var(--primary-text-link) +} + +.slack-icon { + width: 60px; +} \ No newline at end of file diff --git a/grafana-plugin/src/containers/SlackInstructions/SlackInstructions.tsx b/grafana-plugin/src/containers/SlackInstructions/SlackInstructions.tsx new file mode 100644 index 00000000..54baf756 --- /dev/null +++ b/grafana-plugin/src/containers/SlackInstructions/SlackInstructions.tsx @@ -0,0 +1,69 @@ +import React, { useCallback, useState, FC } from 'react'; +import { SlackNewIcon } from '../../icons'; +import { Button, VerticalGroup, Icon, Field, Input } from '@grafana/ui'; +import { observer } from 'mobx-react'; +import cn from 'classnames/bind'; + +import Text from 'components/Text/Text'; +import Block from 'components/GBlock/Block'; + +import styles from './SlackInstructions.module.css'; + +const cx = cn.bind(styles); + +interface SlackInstructionsProps {} + +const SlackInstructions: FC = observer((props) => { + return ( +
+ + {/* + Setup Slack workspace + + + You can manage incidents in your Slack workspace. + Before start you need to connect your Slack bot to Grafana OnCall. + + For bot creating instructions and additional information please read{' '} + + our documentation + + {' '} + + + Setup environment + + Create OnCall Slack bot using{' '} + + our instructions + {' '} + and fill out app credentials below. + +
+ + {}} defaultValue={'appId'} /> + + + {}} defaultValue={'clientsecret'} /> + + + {}} defaultValue={'signingsecret'} /> + + + {}} defaultValue={'https://'} /> + +
+ + + Your host to Slack must start with “https://” and be publicly available (meaning + that it can be reached by Slack servers). If your host is private or local, you can use redirecting services + like Ngrok. + + + +
*/} +
+ ); +}); + +export default SlackInstructions; diff --git a/grafana-plugin/src/icons/index.tsx b/grafana-plugin/src/icons/index.tsx index ffc6f75c..e54c5b56 100644 --- a/grafana-plugin/src/icons/index.tsx +++ b/grafana-plugin/src/icons/index.tsx @@ -269,3 +269,41 @@ export const IsOncallIcon = (props: IsOncallIconProps) => { ); }; + +export const SlackNewIcon = (props: IconProps) => ( +
Test
+ // + // + // + // + // + // + // + // + // + // +); diff --git a/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.module.css b/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.module.css index b9ab506c..c1a97c95 100644 --- a/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.module.css +++ b/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.module.css @@ -15,3 +15,8 @@ margin-bottom: 20px; border-bottom: 1px solid rgba(204, 204, 220, 0.25); } + +.slack-infoblock { + text-align: center; + width: 725px; +} \ No newline at end of file diff --git a/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.tsx b/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.tsx index 8d88622b..b1f91acf 100644 --- a/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.tsx +++ b/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.tsx @@ -1,6 +1,6 @@ import React, { Component } from 'react'; -import { Field, HorizontalGroup, LoadingPlaceholder, VerticalGroup } from '@grafana/ui'; +import { Field, HorizontalGroup, LoadingPlaceholder, VerticalGroup, Icon, Button } from '@grafana/ui'; import cn from 'classnames/bind'; import { observer } from 'mobx-react'; @@ -8,10 +8,12 @@ import PluginLink from 'components/PluginLink/PluginLink'; import Text from 'components/Text/Text'; import Tutorial from 'components/Tutorial/Tutorial'; import { TutorialStep } from 'components/Tutorial/Tutorial.types'; +import Block from 'components/GBlock/Block'; import GSelect from 'containers/GSelect/GSelect'; import RemoteSelect from 'containers/RemoteSelect/RemoteSelect'; import SlackIntegrationButton from 'containers/SlackIntegrationButton/SlackIntegrationButton'; import { WithPermissionControl } from 'containers/WithPermissionControl/WithPermissionControl'; +import SlackInstructions from 'containers/SlackInstructions/SlackInstructions'; import { PRIVATE_CHANNEL_NAME } from 'models/slack_channel/slack_channel.config'; import { SlackChannel } from 'models/slack_channel/slack_channel.types'; import { AppFeature } from 'state/features'; @@ -35,6 +37,11 @@ class SlackSettings extends Component { this.update(); } + handleOpenSlackInstructions = () => { + const { store } = this.props; + store.slackStore.installSlackIntegration(); + }; + update = () => { const { store } = this.props; @@ -140,27 +147,51 @@ class SlackSettings extends Component { const { store } = this.props; return ( - - Connect your Slack workspace - - Bring the whole incident lifecycle to Slack, from alerts, monitoring, escalations to resolution notes and - reports. - + // + // Connect your Slack workspace + // + // Bring the whole incident lifecycle to Slack, from alerts, monitoring, escalations to resolution notes and + // reports. + // - + // - {store.hasFeature(AppFeature.LiveSettings) && ( - - Before installing check ENV variables related - to Slack please - - )} -
- } - /> + // {store.hasFeature(AppFeature.LiveSettings) && ( + // + // Before installing check ENV variables related + // to Slack please + // + // )} + // + // } + // /> + + // + // Connect Slack workspace + // + // + // + // Slack connection will allow you to manage incidents in your team Slack workspace. + // + // After a basic workspace connection, your team members need to connect their personal Slack accounts in + // order to be allowed to manage incidents. + // + // + // + // + // + // + <> + + ); }; } From 06a5b5570a0c2932f7788c7d94cb2273d2626ee4 Mon Sep 17 00:00:00 2001 From: Yulia Shanyrova Date: Tue, 11 Oct 2022 11:35:51 +0200 Subject: [PATCH 3/8] Slack integration redesign. Changed the message from backend as well --- engine/apps/slack/views.py | 4 +- .../SlackInstructions.module.css | 10 +- .../SlackInstructions/SlackInstructions.tsx | 22 +- .../parts/tabs/SlackTab/SlackTab.module.css | 10 + .../parts/tabs/SlackTab/SlackTab.tsx | 44 ++-- grafana-plugin/src/icons/index.tsx | 69 +++-- grafana-plugin/src/img/slack_instructions.png | Bin 0 -> 54320 bytes .../SlackSettings/SlackSettings.module.css | 9 + .../tabs/SlackSettings/SlackSettings.tsx | 237 ++++++++++++------ 9 files changed, 257 insertions(+), 148 deletions(-) create mode 100644 grafana-plugin/src/img/slack_instructions.png diff --git a/engine/apps/slack/views.py b/engine/apps/slack/views.py index 217b0f7c..8d4ca3c1 100644 --- a/engine/apps/slack/views.py +++ b/engine/apps/slack/views.py @@ -501,8 +501,8 @@ class SlackEventApiEndpointView(APIView): return text = ( - "Your Grafana account is not connected to your Slack account. :flushed:\n" - "That's very easy to fix. Please go to the *Grafana* -> *OnCall* -> *Users*, " + "The information in workspace is read-only. To be able to intercat with OnCall alert groups you need to connect a personal account.\n" + "Please go to the *Grafana* -> *OnCall* -> *Users*, " "choose *your profile* and click the *connect* button.\n" ":rocket: :rocket: :rocket:" ) diff --git a/grafana-plugin/src/containers/SlackInstructions/SlackInstructions.module.css b/grafana-plugin/src/containers/SlackInstructions/SlackInstructions.module.css index 2f95b63e..0083dc6f 100644 --- a/grafana-plugin/src/containers/SlackInstructions/SlackInstructions.module.css +++ b/grafana-plugin/src/containers/SlackInstructions/SlackInstructions.module.css @@ -1,11 +1,11 @@ .slack-infoblock { - width: 725px; + width: 725px; } -.slack-infoblock input{ - color: var(--primary-text-link) +.slack-infoblock input { + color: var(--primary-text-link); } .slack-icon { - width: 60px; -} \ No newline at end of file + width: 60px; +} diff --git a/grafana-plugin/src/containers/SlackInstructions/SlackInstructions.tsx b/grafana-plugin/src/containers/SlackInstructions/SlackInstructions.tsx index 54baf756..9b4638f1 100644 --- a/grafana-plugin/src/containers/SlackInstructions/SlackInstructions.tsx +++ b/grafana-plugin/src/containers/SlackInstructions/SlackInstructions.tsx @@ -1,26 +1,28 @@ import React, { useCallback, useState, FC } from 'react'; -import { SlackNewIcon } from '../../icons'; -import { Button, VerticalGroup, Icon, Field, Input } from '@grafana/ui'; -import { observer } from 'mobx-react'; -import cn from 'classnames/bind'; -import Text from 'components/Text/Text'; +import { Button, VerticalGroup, Icon, Field, Input } from '@grafana/ui'; +import cn from 'classnames/bind'; +import { observer } from 'mobx-react'; + +import { SlackNewIcon } from 'icons'; import Block from 'components/GBlock/Block'; +import Text from 'components/Text/Text'; import styles from './SlackInstructions.module.css'; const cx = cn.bind(styles); interface SlackInstructionsProps {} - +/* This component will be used when we will work on moving ENV variables to chat-ops, but we need to do work on backend side first */ const SlackInstructions: FC = observer((props) => { return (
- - {/* - Setup Slack workspace + + Connect Slack workspace + + You can manage incidents in your Slack workspace. Before start you need to connect your Slack bot to Grafana OnCall. @@ -61,7 +63,7 @@ const SlackInstructions: FC = observer((props) => { - */} +
); }); diff --git a/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.module.css b/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.module.css index ce2afbc6..53d1112f 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.module.css +++ b/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.module.css @@ -2,3 +2,13 @@ display: flex; justify-content: flex-end; } + +.slack-infoblock { + text-align: center; + width: 725px; +} + +.external-link-style { + margin-right: 4px; + align-self: baseline; +} \ No newline at end of file diff --git a/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.tsx b/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.tsx index 4edd5ab5..27d44d37 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.tsx @@ -1,9 +1,11 @@ import React, { useCallback } from 'react'; -import { Button, VerticalGroup } from '@grafana/ui'; +import { Button, VerticalGroup, Icon } from '@grafana/ui'; import cn from 'classnames/bind'; import Text from 'components/Text/Text'; +import Block from 'components/GBlock/Block'; +import { SlackNewIcon } from 'icons'; import { useStore } from 'state/useStore'; import styles from './SlackTab.module.css'; @@ -18,20 +20,32 @@ export const SlackTab = () => { }, [slackStore]); return ( - - - You can view your Slack Workspace at the top-right corner after you are redirected. It should be a Workspace - with App Bot installed: - - -
- -
+ + + + + + Personal Slack connection will allow you to manage incidents in your connected team Internal Slack + workspace. + + To setup personal Slack click the button below, choose workspace and click Allow. + + + More details in{' '} + + our documentation + + + + + + + ); }; diff --git a/grafana-plugin/src/icons/index.tsx b/grafana-plugin/src/icons/index.tsx index 38630531..41239fa4 100644 --- a/grafana-plugin/src/icons/index.tsx +++ b/grafana-plugin/src/icons/index.tsx @@ -271,39 +271,38 @@ export const IsOncallIcon = (props: IsOncallIconProps) => { }; export const SlackNewIcon = (props: IconProps) => ( -
Test
- // - // - // - // - // - // - // - // - // - // + + + + + + + + + + ); diff --git a/grafana-plugin/src/img/slack_instructions.png b/grafana-plugin/src/img/slack_instructions.png new file mode 100644 index 0000000000000000000000000000000000000000..0093b533296040529a162fdb8f8a08d4ab213c3f GIT binary patch literal 54320 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 { - state: SlackState = {}; + state: SlackState = { + showENVVariablesButton: false, + }; componentDidMount() { + this.getSlackLiveSettings(); this.update(); } @@ -49,6 +52,32 @@ class SlackSettings extends Component { store.slackStore.updateSlackSettings(); }; + getSlackLiveSettings = async () => { + const { store } = this.props; + const slackClientOAUTH = await store.globalSettingStore.getGlobalSettingItemByName('SLACK_CLIENT_OAUTH_ID'); + const slackClientOAUTHSecret = await store.globalSettingStore.getGlobalSettingItemByName( + 'SLACK_CLIENT_OAUTH_SECRET' + ); + const slackRedirectHost = await store.globalSettingStore.getGlobalSettingItemByName( + 'SLACK_INSTALL_RETURN_REDIRECT_HOST' + ); + const slackSigningSecret = await store.globalSettingStore.getGlobalSettingItemByName('SLACK_SIGNING_SECRET'); + + console.log('slackClientOAUTH', slackClientOAUTH?.error); + console.log('slackClientOAUTHSecret', slackClientOAUTHSecret?.error); + console.log('slackRedirectHost', slackRedirectHost?.error); + console.log('slackSigningSecret', slackSigningSecret?.error); + if ( + slackClientOAUTH?.error || + slackClientOAUTHSecret?.error || + slackRedirectHost?.error || + slackSigningSecret?.error + ) { + console.log('BLA BLA'); + this.setState({ showENVVariablesButton: true }); + } + }; + render() { const { store } = this.props; const { teamStore } = store; @@ -66,34 +95,47 @@ class SlackSettings extends Component { return (
- - Slack - -
- - - - - +
+ Slack
- + + + +
+ {store.teamStore.currentTeam.slack_team_identity?.cached_name} +
+
+ + + + + +
+ + + + + +
+
+
+ Additional settings - + {
- - Remove integration - -
); }; + renderSlackWorkspace = () => { + const { store } = this.props; + return {store.teamStore.currentTeam.slack_team_identity?.cached_name}; + }; + + renderSlackChannels = () => { + const { store } = this.props; + return ( + + + + ); + }; + + renderActionButtons = () => { + + + + + ; + }; + + removeSlackIntegration = () => { + const { store } = this.props; + store.slackStore.removeSlackIntegration().then(() => { + store.teamStore.loadCurrentTeam(); + }); + }; + getSlackSettingsChangeHandler = (field: string) => { const { store } = this.props; const { slackStore } = store; @@ -145,53 +224,49 @@ class SlackSettings extends Component { renderSlackStub = () => { const { store } = this.props; - + const { showENVVariablesButton } = this.state; + const isLiveSettingAvailable = store.hasFeature(AppFeature.LiveSettings) && showENVVariablesButton; return ( - // - // Connect your Slack workspace - // - // Bring the whole incident lifecycle to Slack, from alerts, monitoring, escalations to resolution notes and - // reports. - // - - // - - // {store.hasFeature(AppFeature.LiveSettings) && ( - // - // Before installing check ENV variables related - // to Slack please - // - // )} - // - // } - // /> - - // - // Connect Slack workspace - // - // - // - // Slack connection will allow you to manage incidents in your team Slack workspace. - // - // After a basic workspace connection, your team members need to connect their personal Slack accounts in - // order to be allowed to manage incidents. - // - // - // - // - // - // - <> - - + + Connect Slack workspace + + + + + Slack connection will allow you to manage incidents in your team Slack workspace. +
+ After a basic workspace connection, your team members need to connect their personal Slack accounts in + order to be allowed to manage incidents. +
+ {isLiveSettingAvailable && ( + + For bot creating instructions and additional information please read{' '} + + our documentation + + + )} + +
+
+ {isLiveSettingAvailable ? ( + + + + ) : ( + + + + + + + )} +
); }; } From 6aac3f16e8c4d5ab530f855e785fac404dc04501 Mon Sep 17 00:00:00 2001 From: Yulia Shanyrova Date: Tue, 18 Oct 2022 17:09:15 +0200 Subject: [PATCH 4/8] Telegram redesign --- .../TelegramIntegrationButton.module.css | 10 ++ .../TelegramIntegrationButton.tsx | 170 +++++++----------- .../tabs/TelegramInfo/TelegramInfo.module.css | 11 ++ .../parts/tabs/TelegramInfo/TelegramInfo.tsx | 99 ++++++---- grafana-plugin/src/icons/index.tsx | 20 +++ .../TelegramSettings.module.css | 9 + .../TelegramSettings/TelegramSettings.tsx | 94 ++++++---- 7 files changed, 244 insertions(+), 169 deletions(-) diff --git a/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.module.css b/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.module.css index 59d60c4e..818c6064 100644 --- a/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.module.css +++ b/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.module.css @@ -19,3 +19,13 @@ .telegram-instruction-cancel { margin-top: 24px; } + +.telegram-block { + width: 100%; +} + +.field-command { + margin-top: 8px; + width: 100%; + display: inline-block; +} diff --git a/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.tsx b/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.tsx index 4e5524ae..0a2e5f2a 100644 --- a/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.tsx +++ b/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.tsx @@ -1,12 +1,13 @@ import React, { useCallback, useState, useEffect } from 'react'; -import { Button, Modal, Icon, HorizontalGroup } from '@grafana/ui'; +import { Button, Modal, Icon, HorizontalGroup, VerticalGroup, Field, Input } from '@grafana/ui'; import cn from 'classnames/bind'; import { observer } from 'mobx-react'; import CopyToClipboard from 'react-copy-to-clipboard'; import Text from 'components/Text/Text'; import WithConfirm from 'components/WithConfirm/WithConfirm'; +import Block from 'components/GBlock/Block'; import { WithPermissionControl } from 'containers/WithPermissionControl/WithPermissionControl'; import { useStore } from 'state/useStore'; import { UserAction } from 'state/userAction'; @@ -47,7 +48,7 @@ const TelegramIntegrationButton = observer((props: TelegramIntegrationProps) => <> {showModal && } @@ -76,110 +77,77 @@ const TelegramModal = (props: TelegramModalProps) => { }, []); return ( - -
- Follow these steps to create and connect to a dedicated OnCall channel. -
- -
- - If you already have a dedicated channel to use with OnCall, you can use the following activation code:{' '} - {verificationCode} - - { - openNotification('Verification code copied'); - }} - > - - - + + + + + If you already have a private channel to work with OnCall, use the following activation code: + + + { + openNotification('Code is copied'); + }} + > + + + } + /> + + + Setup new channel. + + 1. Open Telegram, create a new Private Channel and enable{' '} + Sign Messages in settings. -
- -
- 1. Create a New Channel, and set it to Private. -
- -
- - 2. In Manage Channel, make sure Sign messages is enabled. + + 2. Create a new Discussion group. This group handles alert actions and comments.{' '} -
- -
- 3. Create a new discussion group. This group handles alert actions and comments. -
- -
- - 4. Add the discussion group to the channel. In Manage Channel, click Discussion to find and add - the new group. + + 3. Connect the discussion group with the channel. In Manage Channel, click{' '} + Discussion to find and add your group.{' '} -
- -
- - 5. Click{' '} - - {botLink} - {' '} - to add the OnCall bot to your contacts. Add the bot to your channel as an Admin. Allow it to{' '} - Post Messages. + + 4. Go to {botLink} to add the OnCall bot to your contacts. Then add the bot to your + channel as an Admin and allow it to Post Messages. -
- -
- 6. Add the bot to the discussion group. -
- -
- - 7. Send the verification code, {verificationCode} - - { - openNotification('Verification code copied'); - }} - > - - - {' '} - , to the channel and wait for the confirmation message. + 5. Add the bot to the discussion group. + + 6. Send this verification code to the channel and wait for the confirmation message: + + { + openNotification('Code is copied'); + }} + > + + + } + /> + -
- -
- 8. Make sure users connect their Telegram accounts in their OnCall user profile. -
- -
- 9. Done! Now you can manage alerts in your Telegram workspace. -
- -
- - Each alert group notification is assigned a dedicated discussion. Users can perform notification actions - (acknowledge, resolve, silence) and discuss alerts in the comments section of the discussions. - - -
- -
- - - - -
+ 7. Start to manage alerts in your team Telegram workspace. +
+ + + + +
+
); }; diff --git a/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.module.css b/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.module.css index b5c7e681..29eec363 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.module.css +++ b/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.module.css @@ -6,3 +6,14 @@ display: flex; justify-content: space-between; } + +.automatic-connect-telegram-block { + width: 100%; + display: flex; + justify-content: space-between; +} + +.field-command { + width: 100%; + display: inline-block; +} \ No newline at end of file diff --git a/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx b/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx index 633c9d8b..b191f4dd 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx @@ -1,15 +1,17 @@ import React, { HTMLAttributes, useEffect, useState } from 'react'; -import { Alert, Button, HorizontalGroup, Icon, VerticalGroup } from '@grafana/ui'; +import { Alert, Button, HorizontalGroup, Icon, VerticalGroup, Field, Input } from '@grafana/ui'; import cn from 'classnames/bind'; import { observer } from 'mobx-react'; import CopyToClipboard from 'react-copy-to-clipboard'; import PluginLink from 'components/PluginLink/PluginLink'; import Text from 'components/Text/Text'; +import Block from 'components/GBlock/Block'; import { AppFeature } from 'state/features'; import { useStore } from 'state/useStore'; import { openNotification } from 'utils'; +import { TelegramColorIcon } from 'icons'; import styles from './TelegramInfo.module.css'; @@ -37,45 +39,66 @@ const TelegramInfo = observer((props: TelegramInfoProps) => { <> {telegramConfigured || !store.hasFeature(AppFeature.LiveSettings) ? ( - - - - Or add bot manually: - - - 1) Go to{' '} - - {botLink} - - - - - 2) Send - {verificationCode} - { - openNotification('Verification code copied'); - }} - > - - - to telegram bot - + Connect personal Telegram + + Connect Telegram automatically + + + + + Manual connection: + + + 1. Go to{' '} + + {botLink} + + + + + 2. Send this verification code to the bot and wait for the confirmation message: + + + { + openNotification('Code is copied'); + }} + > + + + } + /> + + 3. Refresh the page and start to manage alerts in your personal Telegram. ) : ( - - Can't connect Telegram. Check ENV variables{' '} - related to Telegram. - - } - /> + + Connect Telegram workspace + + + + You can manage incidents in your team Telegram channel or from personal direct messages. + + + To connect channel setup Telegram environment first, which includes connection to your bot and host URL. + + + More details in{' '} + + our documentation + + + + + + + + )} ); diff --git a/grafana-plugin/src/icons/index.tsx b/grafana-plugin/src/icons/index.tsx index ff0afb08..aca2e3d3 100644 --- a/grafana-plugin/src/icons/index.tsx +++ b/grafana-plugin/src/icons/index.tsx @@ -269,3 +269,23 @@ export const IsOncallIcon = (props: IsOncallIconProps) => { ); }; + +export const TelegramColorIcon = () => { + return ( + + + + + + + ); +}; diff --git a/grafana-plugin/src/pages/chat-ops/parts/tabs/TelegramSettings/TelegramSettings.module.css b/grafana-plugin/src/pages/chat-ops/parts/tabs/TelegramSettings/TelegramSettings.module.css index 837f409b..b84ff65e 100644 --- a/grafana-plugin/src/pages/chat-ops/parts/tabs/TelegramSettings/TelegramSettings.module.css +++ b/grafana-plugin/src/pages/chat-ops/parts/tabs/TelegramSettings/TelegramSettings.module.css @@ -6,3 +6,12 @@ display: flex; justify-content: space-between; } + +.telegram-infoblock { + text-align: center; + width: 725px; +} + +ul { + margin: 20px 30px; +} \ No newline at end of file diff --git a/grafana-plugin/src/pages/chat-ops/parts/tabs/TelegramSettings/TelegramSettings.tsx b/grafana-plugin/src/pages/chat-ops/parts/tabs/TelegramSettings/TelegramSettings.tsx index d8b694c6..37eb8165 100644 --- a/grafana-plugin/src/pages/chat-ops/parts/tabs/TelegramSettings/TelegramSettings.tsx +++ b/grafana-plugin/src/pages/chat-ops/parts/tabs/TelegramSettings/TelegramSettings.tsx @@ -1,12 +1,13 @@ import React, { Component } from 'react'; -import { Alert, Button, HorizontalGroup, Icon, LoadingPlaceholder, VerticalGroup } from '@grafana/ui'; +import { Alert, Badge, Button, HorizontalGroup, Icon, LoadingPlaceholder, VerticalGroup } from '@grafana/ui'; import cn from 'classnames/bind'; import { observer } from 'mobx-react'; import GTable from 'components/GTable/GTable'; import PluginLink from 'components/PluginLink/PluginLink'; import Text from 'components/Text/Text'; +import Block from 'components/GBlock/Block'; import Tutorial from 'components/Tutorial/Tutorial'; import { TutorialStep } from 'components/Tutorial/Tutorial.types'; import WithConfirm from 'components/WithConfirm/WithConfirm'; @@ -15,6 +16,7 @@ import { TelegramChannel } from 'models/telegram_channel/telegram_channel.types' import { AppFeature } from 'state/features'; import { WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; +import { TelegramColorIcon } from 'icons'; import styles from './TelegramSettings.module.css'; @@ -47,16 +49,28 @@ class TelegramSettings extends Component { if (!telegramConfigured && store.hasFeature(AppFeature.LiveSettings)) { return ( - - Can't connect Telegram. Check ENV variables{' '} - related to Telegram. - - } - /> + + Connect Telegram workspace + + + + You can manage incidents in your team Telegram channel or from personal direct messages. + + + To connect channel setup Telegram environment first, which includes connection to your bot and host URL. + + + More details in{' '} + + our documentation + + + + + + + + ); } @@ -66,38 +80,51 @@ class TelegramSettings extends Component { if (!connectedChannels.length) { return ( - + Connect Telegram workspace + + + You can manage incidents in your team Telegram channel or from personal direct messages. - Bring the whole incident lifecycle into your chat workspace. Everything from alerts, monitoring, and - escalations to reports. + More details in{' '} + + our documentation + - - } - /> + + + Features +
    +
  • perform actions (acknowledge, resolve, silence)
  • +
  • discuss alerts in comments
  • +
  • notifications to users accounts will be served as links to the main channel
  • +
+ Make sure your team connects Telegram in their OnCall user profiles too or they cannot manage incidents. +
+ + + + + + + ); } const columns = [ { - width: '30%', - title: 'Channel name', - dataIndex: 'channel_name', + width: '35%', + title: 'Channel', + key: 'name', + render: this.renderChannelName, }, { - width: '30%', - title: 'Discussion group name', + width: '35%', + title: 'Discussion group', dataIndex: 'discussion_group_name', }, - { - width: '10%', - title: 'Is default channel', - dataIndex: 'is_default_channel', - render: this.renderDefaultChannel, - }, { width: '30%', key: 'action', @@ -126,6 +153,13 @@ class TelegramSettings extends Component { ); } + renderChannelName = (record: TelegramChannel) => { + return ( + <> + {record.channel_name} {record.is_default_channel && } + + ); + }; renderDefaultChannel = (isDefault: boolean) => { return <>{isDefault && }; }; From 68afc87a0ecaf4ff9805286326eb40b723d9a648 Mon Sep 17 00:00:00 2001 From: Yulia Shanyrova Date: Fri, 21 Oct 2022 14:59:21 +0200 Subject: [PATCH 5/8] small UX changes. SLACK and TELEGRAM MERGED in this branch --- .../src/components/GBlock/Block.module.css | 4 +-- .../src/components/Text/Text.module.scss | 1 - .../TelegramIntegrationButton.module.css | 5 ++++ .../TelegramIntegrationButton.tsx | 20 ++++++------- .../tabs/TelegramInfo/TelegramInfo.module.css | 1 + .../parts/tabs/TelegramInfo/TelegramInfo.tsx | 2 +- .../SlackSettings/SlackSettings.module.css | 12 +++++++- .../tabs/SlackSettings/SlackSettings.tsx | 21 +++++++------- .../TelegramSettings.module.css | 10 +++++++ .../TelegramSettings/TelegramSettings.tsx | 28 ++++++++++++------- 10 files changed, 67 insertions(+), 37 deletions(-) diff --git a/grafana-plugin/src/components/GBlock/Block.module.css b/grafana-plugin/src/components/GBlock/Block.module.css index cb56c069..49f4ed2d 100644 --- a/grafana-plugin/src/components/GBlock/Block.module.css +++ b/grafana-plugin/src/components/GBlock/Block.module.css @@ -4,11 +4,11 @@ } :global(.theme-dark) .root_bordered { - border: var(--border); + border: var(--border-weak); } :global(.theme-light) .root_bordered { - border: var(--border); + border: var(--border-weak); } :global(.theme-dark) .root_shadowed { diff --git a/grafana-plugin/src/components/Text/Text.module.scss b/grafana-plugin/src/components/Text/Text.module.scss index 411ffd33..a2ea3832 100644 --- a/grafana-plugin/src/components/Text/Text.module.scss +++ b/grafana-plugin/src/components/Text/Text.module.scss @@ -17,7 +17,6 @@ } &--link { color: var(--primary-text-link); - text-decoration: underline; } &--success { color: var(--green-5); diff --git a/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.module.css b/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.module.css index 818c6064..a9a45ed3 100644 --- a/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.module.css +++ b/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.module.css @@ -29,3 +29,8 @@ width: 100%; display: inline-block; } + +.infoblock-text { + margin-left: 48px; + margin-right: 48px; +} \ No newline at end of file diff --git a/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.tsx b/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.tsx index 0a2e5f2a..dc539d95 100644 --- a/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.tsx +++ b/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.tsx @@ -100,7 +100,7 @@ const TelegramModal = (props: TelegramModalProps) => { /> - Setup new channel. + Setup new channel 1. Open Telegram, create a new Private Channel and enable{' '} Sign Messages in settings. @@ -137,16 +137,14 @@ const TelegramModal = (props: TelegramModalProps) => { 7. Start to manage alerts in your team Telegram workspace. -
- - - - -
+ + + + ); diff --git a/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.module.css b/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.module.css index 29eec363..6f0a61b7 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.module.css +++ b/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.module.css @@ -11,6 +11,7 @@ width: 100%; display: flex; justify-content: space-between; + margin-bottom: 24px; } .field-command { diff --git a/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx b/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx index b191f4dd..52bc5fe4 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx @@ -46,7 +46,7 @@ const TelegramInfo = observer((props: TelegramInfoProps) => { - Manual connection: + Manual connection 1. Go to{' '} diff --git a/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.module.css b/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.module.css index fe7c1bef..49111eb0 100644 --- a/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.module.css +++ b/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.module.css @@ -28,4 +28,14 @@ .team_workspace { height: 30px; -} \ No newline at end of file +} + +.infoblock-text { + margin-left: 48px; + margin-right: 48px; + margin-top: 24px; +} + +.infoblock-icon { + margin-top: 24px; +} diff --git a/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.tsx b/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.tsx index b372c33e..b03905bd 100644 --- a/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.tsx +++ b/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.tsx @@ -63,17 +63,12 @@ class SlackSettings extends Component { ); const slackSigningSecret = await store.globalSettingStore.getGlobalSettingItemByName('SLACK_SIGNING_SECRET'); - console.log('slackClientOAUTH', slackClientOAUTH?.error); - console.log('slackClientOAUTHSecret', slackClientOAUTHSecret?.error); - console.log('slackRedirectHost', slackRedirectHost?.error); - console.log('slackSigningSecret', slackSigningSecret?.error); if ( slackClientOAUTH?.error || slackClientOAUTHSecret?.error || slackRedirectHost?.error || slackSigningSecret?.error ) { - console.log('BLA BLA'); this.setState({ showENVVariablesButton: true }); } }; @@ -230,22 +225,26 @@ class SlackSettings extends Component { Connect Slack workspace - - - + +
+ +
+ Slack connection will allow you to manage incidents in your team Slack workspace. -
- After a basic workspace connection, your team members need to connect their personal Slack accounts in +
+ + After a basic workspace connection your team members need to connect their personal Slack accounts in order to be allowed to manage incidents. {isLiveSettingAvailable && ( - + For bot creating instructions and additional information please read{' '} our documentation )} + { Connect Telegram workspace - - - You can manage incidents in your team Telegram channel or from personal direct messages. + +
+ +
+ + You can manage incidents in your team Telegram channel or from personal direct messages.{' '} + - + To connect channel setup Telegram environment first, which includes connection to your bot and host URL. - + More details in{' '} our documentation @@ -83,10 +87,14 @@ class TelegramSettings extends Component { Connect Telegram workspace - - - You can manage incidents in your team Telegram channel or from personal direct messages. - + +
+ +
+ + You can manage incidents in your team Telegram channel or from personal direct messages.{' '} + + More details in{' '}
our documentation @@ -95,7 +103,7 @@ class TelegramSettings extends Component {
- Features + Features
  • perform actions (acknowledge, resolve, silence)
  • discuss alerts in comments
  • From f2a780c4fa20ca7b38733b711b70cac3215044f5 Mon Sep 17 00:00:00 2001 From: Yulia Shanyrova Date: Wed, 26 Oct 2022 14:13:26 +0200 Subject: [PATCH 6/8] Changes after review and change incident word --- .../SlackInstructions/SlackInstructions.tsx | 2 +- .../parts/tabs/SlackTab/SlackTab.tsx | 2 +- .../parts/tabs/TelegramInfo/TelegramInfo.tsx | 2 +- .../tabs/SlackSettings/SlackSettings.tsx | 24 ++++++++++--------- .../TelegramSettings/TelegramSettings.tsx | 6 ++--- 5 files changed, 19 insertions(+), 17 deletions(-) diff --git a/grafana-plugin/src/containers/SlackInstructions/SlackInstructions.tsx b/grafana-plugin/src/containers/SlackInstructions/SlackInstructions.tsx index 9b4638f1..e4465c0e 100644 --- a/grafana-plugin/src/containers/SlackInstructions/SlackInstructions.tsx +++ b/grafana-plugin/src/containers/SlackInstructions/SlackInstructions.tsx @@ -23,7 +23,7 @@ const SlackInstructions: FC = observer((props) => { - You can manage incidents in your Slack workspace. + You can manage alert groups in your Slack workspace. Before start you need to connect your Slack bot to Grafana OnCall. For bot creating instructions and additional information please read{' '} diff --git a/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.tsx b/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.tsx index 27d44d37..e64d750d 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.tsx @@ -25,7 +25,7 @@ export const SlackTab = () => { - Personal Slack connection will allow you to manage incidents in your connected team Internal Slack + Personal Slack connection will allow you to manage alert grouops in your connected team Internal Slack workspace. To setup personal Slack click the button below, choose workspace and click Allow. diff --git a/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx b/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx index 52bc5fe4..b06f8418 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx @@ -82,7 +82,7 @@ const TelegramInfo = observer((props: TelegramInfoProps) => { - You can manage incidents in your team Telegram channel or from personal direct messages. + You can manage alert groups in your team Telegram channel or from personal direct messages. To connect channel setup Telegram environment first, which includes connection to your bot and host URL. diff --git a/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.tsx b/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.tsx index b03905bd..92fac740 100644 --- a/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.tsx +++ b/grafana-plugin/src/pages/chat-ops/parts/tabs/SlackSettings/SlackSettings.tsx @@ -36,8 +36,9 @@ class SlackSettings extends Component { }; componentDidMount() { - this.getSlackLiveSettings(); - this.update(); + this.getSlackLiveSettings().then(() => { + this.update(); + }); } handleOpenSlackInstructions = () => { @@ -54,14 +55,14 @@ class SlackSettings extends Component { getSlackLiveSettings = async () => { const { store } = this.props; - const slackClientOAUTH = await store.globalSettingStore.getGlobalSettingItemByName('SLACK_CLIENT_OAUTH_ID'); - const slackClientOAUTHSecret = await store.globalSettingStore.getGlobalSettingItemByName( - 'SLACK_CLIENT_OAUTH_SECRET' + const results = await store.globalSettingStore.getAll(); + + const slackClientOAUTH = results.find((element: { name: string }) => element.name === 'SLACK_CLIENT_OAUTH_ID'); + const slackClientOAUTHSecret = results.find( + (element: { name: string }) => element.name === 'SLACK_CLIENT_OAUTH_SECRET' ); - const slackRedirectHost = await store.globalSettingStore.getGlobalSettingItemByName( - 'SLACK_INSTALL_RETURN_REDIRECT_HOST' - ); - const slackSigningSecret = await store.globalSettingStore.getGlobalSettingItemByName('SLACK_SIGNING_SECRET'); + const slackRedirectHost = results.find((element: { name: string }) => element.name === 'SLACK_CLIENT_OAUTH_ID'); + const slackSigningSecret = results.find((element: { name: string }) => element.name === 'SLACK_SIGNING_SECRET'); if ( slackClientOAUTH?.error || @@ -221,6 +222,7 @@ class SlackSettings extends Component { const { store } = this.props; const { showENVVariablesButton } = this.state; const isLiveSettingAvailable = store.hasFeature(AppFeature.LiveSettings) && showENVVariablesButton; + return ( Connect Slack workspace @@ -230,11 +232,11 @@ class SlackSettings extends Component {
- Slack connection will allow you to manage incidents in your team Slack workspace. + Slack connection will allow you to manage alert groups in your team Slack workspace. After a basic workspace connection your team members need to connect their personal Slack accounts in - order to be allowed to manage incidents. + order to be allowed to manage alert groups. {isLiveSettingAvailable && ( diff --git a/grafana-plugin/src/pages/chat-ops/parts/tabs/TelegramSettings/TelegramSettings.tsx b/grafana-plugin/src/pages/chat-ops/parts/tabs/TelegramSettings/TelegramSettings.tsx index 1bb0ce37..d4215b35 100644 --- a/grafana-plugin/src/pages/chat-ops/parts/tabs/TelegramSettings/TelegramSettings.tsx +++ b/grafana-plugin/src/pages/chat-ops/parts/tabs/TelegramSettings/TelegramSettings.tsx @@ -57,7 +57,7 @@ class TelegramSettings extends Component {
- You can manage incidents in your team Telegram channel or from personal direct messages.{' '} + You can manage alert groups in your team Telegram channel or from personal direct messages.{' '} @@ -92,7 +92,7 @@ class TelegramSettings extends Component { - You can manage incidents in your team Telegram channel or from personal direct messages.{' '} + You can manage alert groups in your team Telegram channel or from personal direct messages.{' '} More details in{' '} @@ -109,7 +109,7 @@ class TelegramSettings extends Component {
  • discuss alerts in comments
  • notifications to users accounts will be served as links to the main channel
  • - Make sure your team connects Telegram in their OnCall user profiles too or they cannot manage incidents. + Make sure your team connects Telegram in their OnCall user profiles too or they cannot manage alert groups.
    From 85c3fbc8b8ce5d02b1c75b2e62cd63bb2e33a05b Mon Sep 17 00:00:00 2001 From: Yulia Shanyrova Date: Mon, 31 Oct 2022 12:30:04 +0100 Subject: [PATCH 7/8] linting fixes --- .../containers/SlackInstructions/SlackInstructions.tsx | 6 +++--- .../TelegramIntegrationButton.tsx | 2 +- .../UserSettings/parts/tabs/SlackTab/SlackTab.tsx | 2 +- .../UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx | 6 +++--- grafana-plugin/src/icons/index.tsx | 2 +- .../chat-ops/parts/tabs/SlackSettings/SlackSettings.tsx | 2 +- .../parts/tabs/TelegramSettings/TelegramSettings.tsx | 8 +++----- 7 files changed, 13 insertions(+), 15 deletions(-) diff --git a/grafana-plugin/src/containers/SlackInstructions/SlackInstructions.tsx b/grafana-plugin/src/containers/SlackInstructions/SlackInstructions.tsx index e4465c0e..edd7a21b 100644 --- a/grafana-plugin/src/containers/SlackInstructions/SlackInstructions.tsx +++ b/grafana-plugin/src/containers/SlackInstructions/SlackInstructions.tsx @@ -1,12 +1,12 @@ -import React, { useCallback, useState, FC } from 'react'; +import React, { FC } from 'react'; import { Button, VerticalGroup, Icon, Field, Input } from '@grafana/ui'; import cn from 'classnames/bind'; import { observer } from 'mobx-react'; -import { SlackNewIcon } from 'icons'; import Block from 'components/GBlock/Block'; import Text from 'components/Text/Text'; +import { SlackNewIcon } from 'icons'; import styles from './SlackInstructions.module.css'; @@ -14,7 +14,7 @@ const cx = cn.bind(styles); interface SlackInstructionsProps {} /* This component will be used when we will work on moving ENV variables to chat-ops, but we need to do work on backend side first */ -const SlackInstructions: FC = observer((props) => { +const SlackInstructions: FC = observer(() => { return (
    diff --git a/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.tsx b/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.tsx index 19a28911..4c307180 100644 --- a/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.tsx +++ b/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.tsx @@ -5,8 +5,8 @@ import cn from 'classnames/bind'; import { observer } from 'mobx-react'; import CopyToClipboard from 'react-copy-to-clipboard'; -import Text from 'components/Text/Text'; import Block from 'components/GBlock/Block'; +import Text from 'components/Text/Text'; import { WithPermissionControl } from 'containers/WithPermissionControl/WithPermissionControl'; import { useStore } from 'state/useStore'; import { UserAction } from 'state/userAction'; diff --git a/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.tsx b/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.tsx index e64d750d..3a305949 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.tsx @@ -3,8 +3,8 @@ import React, { useCallback } from 'react'; import { Button, VerticalGroup, Icon } from '@grafana/ui'; import cn from 'classnames/bind'; -import Text from 'components/Text/Text'; import Block from 'components/GBlock/Block'; +import Text from 'components/Text/Text'; import { SlackNewIcon } from 'icons'; import { useStore } from 'state/useStore'; diff --git a/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx b/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx index 59cb8840..f0750886 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.tsx @@ -1,17 +1,17 @@ import React, { HTMLAttributes, useEffect, useState } from 'react'; -import { Alert, Button, HorizontalGroup, Icon, VerticalGroup, Field, Input } from '@grafana/ui'; +import { Button, Icon, VerticalGroup, Field, Input } from '@grafana/ui'; import cn from 'classnames/bind'; import { observer } from 'mobx-react'; import CopyToClipboard from 'react-copy-to-clipboard'; +import Block from 'components/GBlock/Block'; import PluginLink from 'components/PluginLink/PluginLink'; import Text from 'components/Text/Text'; -import Block from 'components/GBlock/Block'; +import { TelegramColorIcon } from 'icons'; import { AppFeature } from 'state/features'; import { useStore } from 'state/useStore'; import { openNotification } from 'utils'; -import { TelegramColorIcon } from 'icons'; import styles from './TelegramInfo.module.css'; diff --git a/grafana-plugin/src/icons/index.tsx b/grafana-plugin/src/icons/index.tsx index 0de9316c..c5675c28 100644 --- a/grafana-plugin/src/icons/index.tsx +++ b/grafana-plugin/src/icons/index.tsx @@ -293,7 +293,7 @@ export const TelegramColorIcon = () => { ); }; -export const SlackNewIcon = (props: IconProps) => ( +export const SlackNewIcon = () => ( Date: Mon, 31 Oct 2022 12:43:25 +0100 Subject: [PATCH 8/8] prettier fix --- .../TelegramIntegrationButton.module.css | 2 +- .../UserSettings/parts/tabs/SlackTab/SlackTab.module.css | 2 +- .../parts/tabs/TelegramInfo/TelegramInfo.module.css | 2 +- .../parts/tabs/TelegramSettings/TelegramSettings.module.css | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.module.css b/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.module.css index a9a45ed3..8ec18f73 100644 --- a/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.module.css +++ b/grafana-plugin/src/containers/TelegramIntegrationButton/TelegramIntegrationButton.module.css @@ -33,4 +33,4 @@ .infoblock-text { margin-left: 48px; margin-right: 48px; -} \ No newline at end of file +} diff --git a/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.module.css b/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.module.css index 53d1112f..51473abb 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.module.css +++ b/grafana-plugin/src/containers/UserSettings/parts/tabs/SlackTab/SlackTab.module.css @@ -11,4 +11,4 @@ .external-link-style { margin-right: 4px; align-self: baseline; -} \ No newline at end of file +} diff --git a/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.module.css b/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.module.css index 6f0a61b7..fea6889e 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.module.css +++ b/grafana-plugin/src/containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo.module.css @@ -17,4 +17,4 @@ .field-command { width: 100%; display: inline-block; -} \ No newline at end of file +} diff --git a/grafana-plugin/src/pages/chat-ops/parts/tabs/TelegramSettings/TelegramSettings.module.css b/grafana-plugin/src/pages/chat-ops/parts/tabs/TelegramSettings/TelegramSettings.module.css index 4aef28a6..ecd192db 100644 --- a/grafana-plugin/src/pages/chat-ops/parts/tabs/TelegramSettings/TelegramSettings.module.css +++ b/grafana-plugin/src/pages/chat-ops/parts/tabs/TelegramSettings/TelegramSettings.module.css @@ -24,4 +24,4 @@ ul { .infoblock-icon { margin-top: 24px; -} \ No newline at end of file +}