Telegram redesign

This commit is contained in:
Yulia Shanyrova 2022-10-18 17:09:15 +02:00
parent 1c0b51f811
commit 6aac3f16e8
7 changed files with 244 additions and 169 deletions

View file

@ -19,3 +19,13 @@
.telegram-instruction-cancel {
margin-top: 24px;
}
.telegram-block {
width: 100%;
}
.field-command {
margin-top: 8px;
width: 100%;
display: inline-block;
}

View file

@ -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) =>
<>
<WithPermissionControl userAction={UserAction.UpdateIntegrations}>
<Button size={size} variant="primary" icon="plus" disabled={disabled} onClick={onInstallModalCallback}>
Connect Telegram channel
Add Telegram channel
</Button>
</WithPermissionControl>
{showModal && <TelegramModal onHide={onInstallModalHideCallback} onUpdate={onModalUpdateCallback} />}
@ -76,110 +77,77 @@ const TelegramModal = (props: TelegramModalProps) => {
}, []);
return (
<Modal title="Connect Telegram Channel" closeOnEscape isOpen onDismiss={onUpdate}>
<div className={cx('telegram-instruction-container')}>
<Text.Title level={5}>Follow these steps to create and connect to a dedicated OnCall channel.</Text.Title>
</div>
<div className={cx('telegram-instruction-container')}>
<Text>
If you already have a dedicated channel to use with OnCall, you can use the following activation code:{' '}
<Text className={cx('verification-code')}>{verificationCode}</Text>
<span className={cx('copy-icon')}>
<CopyToClipboard
text={verificationCode}
onCopy={() => {
openNotification('Verification code copied');
}}
>
<Icon name="copy" />
</CopyToClipboard>
</span>
<Modal title="Adding Telegram Channel" closeOnEscape isOpen onDismiss={onUpdate}>
<VerticalGroup spacing="md">
<Block withBackground bordered className={cx('telegram-block')}>
<Text type="secondary">
If you already have a private channel to work with OnCall, use the following activation code:
</Text>
<Field className={cx('field-command')}>
<Input
id="telegramVerificationCode"
value={verificationCode}
suffix={
<CopyToClipboard
text={verificationCode}
onCopy={() => {
openNotification('Code is copied');
}}
>
<Icon name="copy" />
</CopyToClipboard>
}
/>
</Field>
</Block>
<Text.Title level={5}>Setup new channel.</Text.Title>
<Text type="secondary">
1. Open Telegram, create a new <Text type="primary">Private Channel</Text> and enable{' '}
<Text type="primary">Sign Messages</Text> in settings.
</Text>
</div>
<div className={cx('telegram-instruction-container')}>
<Text>1. Create a New Channel, and set it to Private.</Text>
</div>
<div className={cx('telegram-instruction-container')}>
<Text>
2. In <b>Manage Channel</b>, make sure <b>Sign messages</b> is enabled.
<Text type="secondary">
2. Create a new <Text type="primary">Discussion group</Text>. This group handles alert actions and comments.{' '}
</Text>
</div>
<div className={cx('telegram-instruction-container')}>
<Text>3. Create a new discussion group. This group handles alert actions and comments.</Text>
</div>
<div className={cx('telegram-instruction-container')}>
<Text>
4. Add the discussion group to the channel. In <b>Manage Channel</b>, click <b>Discussion</b> to find and add
the new group.
<Text type="secondary">
3. Connect the discussion group with the channel. In <Text type="primary">Manage Channel</Text>, click{' '}
<Text type="primary">Discussion</Text> to find and add your group.{' '}
</Text>
</div>
<div className={cx('telegram-instruction-container')}>
<Text>
5. Click{' '}
<a className={cx('telegram-bot')} href={botLink} target="_blank" rel="noreferrer">
{botLink}
</a>{' '}
to add the OnCall bot to your contacts. Add the bot to your channel as an Admin. Allow it to{' '}
<b>Post Messages</b>.
<Text type="secondary">
4. Go to <Text type="link">{botLink}</Text> to add the OnCall bot to your contacts. Then add the bot to your
channel as an <Text type="primary">Admin</Text> and allow it to <Text type="primary">Post Messages</Text>.
</Text>
</div>
<div className={cx('telegram-instruction-container')}>
<Text>6. Add the bot to the discussion group.</Text>
</div>
<div className={cx('telegram-instruction-container')}>
<Text>
7. Send the verification code, <Text className={cx('verification-code')}>{verificationCode}</Text>
<span className={cx('copy-icon')}>
<CopyToClipboard
text={verificationCode}
onCopy={() => {
openNotification('Verification code copied');
}}
>
<Icon name="copy" />
</CopyToClipboard>
</span>{' '}
, to the channel and wait for the confirmation message.
<Text type="secondary">5. Add the bot to the discussion group.</Text>
<Text type="secondary">
6. Send this verification code to the channel and wait for the confirmation message:
<Field className={cx('field-command')}>
<Input
id="telegramVerificationCode"
value={verificationCode}
suffix={
<CopyToClipboard
text={verificationCode}
onCopy={() => {
openNotification('Code is copied');
}}
>
<Icon name="copy" />
</CopyToClipboard>
}
/>
</Field>
</Text>
</div>
<div className={cx('telegram-instruction-container')}>
<Text>8. Make sure users connect their Telegram accounts in their OnCall user profile.</Text>
</div>
<div className={cx('telegram-instruction-container')}>
<Text>9. Done! Now you can manage alerts in your Telegram workspace.</Text>
</div>
<div className={cx('telegram-instruction-container')}>
<Text>
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.
</Text>
<img
style={{ height: '350px', display: 'block', margin: '20px auto' }}
src="public/plugins/grafana-oncall-app/img/telegram_discussion.png"
/>
</div>
<div className={cx('telegram-instruction-cancel')}>
<HorizontalGroup justify="flex-end">
<Button variant="secondary" onClick={onHide}>
Cancel
</Button>
<Button variant="primary" onClick={onUpdate}>
Done
</Button>
</HorizontalGroup>
</div>
<Text type="secondary">7. Start to manage alerts in your team Telegram workspace.</Text>
<div className={cx('telegram-instruction-cancel')}>
<HorizontalGroup justify="flex-end">
<Button variant="secondary" onClick={onHide}>
Cancel
</Button>
<Button variant="primary" onClick={onUpdate}>
Done
</Button>
</HorizontalGroup>
</div>
</VerticalGroup>
</Modal>
);
};

View file

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

View file

@ -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) ? (
<VerticalGroup>
<a href={`${botLink}/?start=${verificationCode}`} target="_blank" rel="noreferrer">
<Button size="sm" fill="outline">
Connect automatically
</Button>
</a>
<Text>Or add bot manually:</Text>
<HorizontalGroup>
<Text>
1) Go to{' '}
<a className={cx('verification-code')} href={botLink} target="_blank" rel="noreferrer">
{botLink}
</a>
</Text>
</HorizontalGroup>
<HorizontalGroup className={cx('verification-code-text')}>
<Text>2) Send </Text>
<Text className={cx('verification-code')}>{verificationCode}</Text>
<CopyToClipboard
text={verificationCode}
onCopy={() => {
openNotification('Verification code copied');
}}
>
<Icon name="copy" />
</CopyToClipboard>
<Text>to telegram bot </Text>
</HorizontalGroup>
<Text.Title level={5}>Connect personal Telegram</Text.Title>
<Block bordered withBackground className={cx('automatic-connect-telegram-block')}>
<Text type="secondary">Connect Telegram automatically</Text>
<a href={`${botLink}/?start=${verificationCode}`} target="_blank" rel="noreferrer">
<Button size="sm">Connect account</Button>
</a>
</Block>
<Text>Manual connection:</Text>
<Text type="secondary">
1. Go to{' '}
<a className={cx('verification-code')} href={botLink} target="_blank" rel="noreferrer">
{botLink}
</a>
</Text>
<Text type="secondary">
2. Send this verification code to the bot and wait for <Text>the confirmation message: </Text>
</Text>
<Field className={cx('field-command')}>
<Input
id="telegramVerificationCode"
value={verificationCode}
suffix={
<CopyToClipboard
text={verificationCode}
onCopy={() => {
openNotification('Code is copied');
}}
>
<Icon name="copy" />
</CopyToClipboard>
}
/>
</Field>
<Text type="secondary">3. Refresh the page and start to manage alerts in your personal Telegram.</Text>
</VerticalGroup>
) : (
<Alert
severity="warning"
// @ts-ignore
title={
<>
Can't connect Telegram. <PluginLink query={{ page: 'live-settings' }}> Check ENV variables</PluginLink>{' '}
related to Telegram.
</>
}
/>
<VerticalGroup spacing="lg">
<Text.Title level={2}>Connect Telegram workspace</Text.Title>
<Block bordered withBackground className={cx('telegram-infoblock')}>
<VerticalGroup align="center" spacing="lg">
<TelegramColorIcon />
<Text>You can manage incidents in your team Telegram channel or from personal direct messages. </Text>
<Text>
To connect channel setup Telegram environment first, which includes connection to your bot and host URL.
</Text>
<Text type="secondary">
More details in{' '}
<a href="https://grafana.com/docs/grafana-cloud/oncall/chat-options/configure-telegram/">
<Text type="link">our documentation</Text>
</a>
</Text>
</VerticalGroup>
</Block>
<PluginLink query={{ page: 'live-settings' }}>
<Button variant="primary">Setup ENV Variables</Button>
</PluginLink>
</VerticalGroup>
)}
</>
);

View file

@ -269,3 +269,23 @@ export const IsOncallIcon = (props: IsOncallIconProps) => {
</svg>
);
};
export const TelegramColorIcon = () => {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48px" height="48px">
<path fill="#29b6f6" d="M24 4A20 20 0 1 0 24 44A20 20 0 1 0 24 4Z" />
<path
fill="#fff"
d="M33.95,15l-3.746,19.126c0,0-0.161,0.874-1.245,0.874c-0.576,0-0.873-0.274-0.873-0.274l-8.114-6.733 l-3.97-2.001l-5.095-1.355c0,0-0.907-0.262-0.907-1.012c0-0.625,0.933-0.923,0.933-0.923l21.316-8.468 c-0.001-0.001,0.651-0.235,1.126-0.234C33.667,14,34,14.125,34,14.5C34,14.75,33.95,15,33.95,15z"
/>
<path
fill="#b0bec5"
d="M23,30.505l-3.426,3.374c0,0-0.149,0.115-0.348,0.12c-0.069,0.002-0.143-0.009-0.219-0.043 l0.964-5.965L23,30.505z"
/>
<path
fill="#cfd8dc"
d="M29.897,18.196c-0.169-0.22-0.481-0.26-0.701-0.093L16,26c0,0,2.106,5.892,2.427,6.912 c0.322,1.021,0.58,1.045,0.58,1.045l0.964-5.965l9.832-9.096C30.023,18.729,30.064,18.416,29.897,18.196z"
/>
</svg>
);
};

View file

@ -6,3 +6,12 @@
display: flex;
justify-content: space-between;
}
.telegram-infoblock {
text-align: center;
width: 725px;
}
ul {
margin: 20px 30px;
}

View file

@ -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<TelegramProps, TelegramState> {
if (!telegramConfigured && store.hasFeature(AppFeature.LiveSettings)) {
return (
<Alert
severity="warning"
// @ts-ignore
title={
<>
Can't connect Telegram. <PluginLink query={{ page: 'live-settings' }}> Check ENV variables</PluginLink>{' '}
related to Telegram.
</>
}
/>
<VerticalGroup spacing="lg">
<Text.Title level={2}>Connect Telegram workspace</Text.Title>
<Block bordered withBackground className={cx('telegram-infoblock')}>
<VerticalGroup align="center" spacing="lg">
<TelegramColorIcon />
<Text>You can manage incidents in your team Telegram channel or from personal direct messages. </Text>
<Text>
To connect channel setup Telegram environment first, which includes connection to your bot and host URL.
</Text>
<Text type="secondary">
More details in{' '}
<a href="https://grafana.com/docs/grafana-cloud/oncall/chat-options/configure-telegram/">
<Text type="link">our documentation</Text>
</a>
</Text>
</VerticalGroup>
</Block>
<PluginLink query={{ page: 'live-settings' }}>
<Button variant="primary">Setup ENV Variables</Button>
</PluginLink>
</VerticalGroup>
);
}
@ -66,38 +80,51 @@ class TelegramSettings extends Component<TelegramProps, TelegramState> {
if (!connectedChannels.length) {
return (
<Tutorial
step={TutorialStep.Slack}
title={
<VerticalGroup spacing="lg">
<Text.Title level={2}>Connect Telegram workspace</Text.Title>
<Block bordered withBackground className={cx('telegram-infoblock')}>
<VerticalGroup align="center" spacing="lg">
<TelegramColorIcon />
<Text>You can manage incidents in your team Telegram channel or from personal direct messages. </Text>
<Text type="secondary">
Bring the whole incident lifecycle into your chat workspace. Everything from alerts, monitoring, and
escalations to reports.
More details in{' '}
<a href="https://grafana.com/docs/grafana-cloud/oncall/chat-options/configure-telegram/">
<Text type="link">our documentation</Text>
</a>
</Text>
<TelegramIntegrationButton size="lg" onUpdate={this.update} />
</VerticalGroup>
}
/>
</Block>
<Text>
Features
<ul>
<li>perform actions (acknowledge, resolve, silence)</li>
<li>discuss alerts in comments</li>
<li>notifications to users accounts will be served as links to the main channel</li>
</ul>
Make sure your team connects Telegram in their OnCall user profiles too or they cannot manage incidents.
</Text>
<HorizontalGroup>
<TelegramIntegrationButton size="md" onUpdate={this.update} />
<PluginLink query={{ page: 'live-settings' }}>
<Button variant="secondary">See ENV Variables</Button>
</PluginLink>
</HorizontalGroup>
</VerticalGroup>
);
}
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<TelegramProps, TelegramState> {
);
}
renderChannelName = (record: TelegramChannel) => {
return (
<>
{record.channel_name} {record.is_default_channel && <Badge text="Default" color="green" />}
</>
);
};
renderDefaultChannel = (isDefault: boolean) => {
return <>{isDefault && <Icon name="check" />}</>;
};