Merge pull request #663 from grafana/637-telegram-installation-redesign
Telegram redesign
This commit is contained in:
commit
3f424cb0a0
18 changed files with 607 additions and 239 deletions
|
|
@ -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:"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
}
|
||||
&--link {
|
||||
color: var(--primary-text-link);
|
||||
text-decoration: underline;
|
||||
}
|
||||
&--success {
|
||||
color: var(--green-5);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
.slack-infoblock {
|
||||
width: 725px;
|
||||
}
|
||||
|
||||
.slack-infoblock input {
|
||||
color: var(--primary-text-link);
|
||||
}
|
||||
|
||||
.slack-icon {
|
||||
width: 60px;
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
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 Block from 'components/GBlock/Block';
|
||||
import Text from 'components/Text/Text';
|
||||
import { SlackNewIcon } from 'icons';
|
||||
|
||||
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<SlackInstructionsProps> = observer(() => {
|
||||
return (
|
||||
<div>
|
||||
<VerticalGroup spacing="lg">
|
||||
<Text.Title level={2}>Connect Slack workspace</Text.Title>
|
||||
|
||||
<Block bordered withBackground className={cx('slack-infoblock')}>
|
||||
<VerticalGroup align="center" spacing="lg">
|
||||
<SlackNewIcon />
|
||||
<Text>You can manage alert groups in your Slack workspace. </Text>
|
||||
<Text>Before start you need to connect your Slack bot to Grafana OnCall.</Text>
|
||||
<Text type="secondary">
|
||||
For bot creating instructions and additional information please read{' '}
|
||||
<a href="https://grafana.com/docs/grafana-cloud/oncall/open-source/#slack-setup">
|
||||
<Text type="link">our documentation</Text>
|
||||
</a>
|
||||
</Text>{' '}
|
||||
</VerticalGroup>
|
||||
</Block>
|
||||
<Text>Setup environment</Text>
|
||||
<Text>
|
||||
Create OnCall Slack bot using{' '}
|
||||
<a href="https://grafana.com/docs/grafana-cloud/oncall/open-source/#slack-setup">
|
||||
<Text type="link">our instructions</Text>
|
||||
</a>{' '}
|
||||
and fill out app credentials below.
|
||||
</Text>
|
||||
<div className={cx('slack-infoblock')}>
|
||||
<Field label="App ID">
|
||||
<Input id="appId" onChange={() => {}} defaultValue={'appId'} />
|
||||
</Field>
|
||||
<Field label="Client secret">
|
||||
<Input id="clientsecret" onChange={() => {}} defaultValue={'clientsecret'} />
|
||||
</Field>
|
||||
<Field label="Signing secret">
|
||||
<Input id="signingsecret" onChange={() => {}} defaultValue={'signingsecret'} />
|
||||
</Field>
|
||||
<Field label="Redirect host">
|
||||
<Input id="host" onChange={() => {}} defaultValue={'https://'} />
|
||||
</Field>
|
||||
</div>
|
||||
<Block bordered withBackground className={cx('slack-infoblock')}>
|
||||
<Text type="secondary">
|
||||
<Icon name="info-circle" /> 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.
|
||||
</Text>
|
||||
</Block>
|
||||
<Button onClick={() => {}}>Save environment</Button>
|
||||
</VerticalGroup>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default SlackInstructions;
|
||||
|
|
@ -63,7 +63,7 @@ const SlackIntegrationButton = observer((props: { className: string; disabled?:
|
|||
disabled={disabled}
|
||||
onClick={onInstallModalCallback}
|
||||
>
|
||||
Install Slack integration
|
||||
Connect Slack
|
||||
</Button>
|
||||
</WithPermissionControl>
|
||||
{showModal && <SlackModal onHide={onInstallModalHideCallback} onConfirm={onInstallClickCallback} />}
|
||||
|
|
@ -80,8 +80,8 @@ const SlackModal = (props: SlackModalProps) => {
|
|||
const { onHide, onConfirm } = props;
|
||||
|
||||
return (
|
||||
<Modal title="One more thing..." closeOnEscape isOpen onDismiss={onHide}>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -19,3 +19,18 @@
|
|||
.telegram-instruction-cancel {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.telegram-block {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.field-command {
|
||||
margin-top: 8px;
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.infoblock-text {
|
||||
margin-left: 48px;
|
||||
margin-right: 48px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
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 Block from 'components/GBlock/Block';
|
||||
import Text from 'components/Text/Text';
|
||||
import { WithPermissionControl } from 'containers/WithPermissionControl/WithPermissionControl';
|
||||
import { useStore } from 'state/useStore';
|
||||
|
|
@ -44,7 +45,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} />}
|
||||
|
|
@ -73,101 +74,66 @@ 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')}>
|
||||
<Text type="secondary">7. Start to manage alerts in your team Telegram workspace.</Text>
|
||||
<HorizontalGroup justify="flex-end">
|
||||
<Button variant="secondary" onClick={onHide}>
|
||||
Cancel
|
||||
|
|
@ -176,7 +142,7 @@ const TelegramModal = (props: TelegramModalProps) => {
|
|||
Done
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
</div>
|
||||
</VerticalGroup>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 Block from 'components/GBlock/Block';
|
||||
import Text from 'components/Text/Text';
|
||||
import { SlackNewIcon } from 'icons';
|
||||
import { useStore } from 'state/useStore';
|
||||
|
||||
import styles from './SlackTab.module.css';
|
||||
|
|
@ -18,20 +20,32 @@ export const SlackTab = () => {
|
|||
}, [slackStore]);
|
||||
|
||||
return (
|
||||
<VerticalGroup>
|
||||
<Text>
|
||||
You can view your Slack Workspace at the top-right corner after you are redirected. It should be a Workspace
|
||||
with App Bot installed:
|
||||
</Text>
|
||||
<img
|
||||
style={{ height: '350px', display: 'block', margin: '0 auto' }}
|
||||
src="public/plugins/grafana-oncall-app/img/slack_workspace_choose_attention.png"
|
||||
/>
|
||||
<div className={cx('footer')}>
|
||||
<Button key="back" onClick={handleClickConnectSlackAccount}>
|
||||
I'll check! Proceed to Slack...
|
||||
</Button>
|
||||
</div>
|
||||
<VerticalGroup spacing="lg">
|
||||
<Block bordered withBackground className={cx('slack-infoblock', 'personal-slack-infoblock')}>
|
||||
<VerticalGroup align="center" spacing="lg">
|
||||
<SlackNewIcon />
|
||||
<Text>
|
||||
Personal Slack connection will allow you to manage alert grouops in your connected team Internal Slack
|
||||
workspace.
|
||||
</Text>
|
||||
<Text>To setup personal Slack click the button below, choose workspace and click Allow.</Text>
|
||||
|
||||
<Text type="secondary">
|
||||
More details in{' '}
|
||||
<a href="https://grafana.com/docs/grafana-cloud/oncall/open-source/#slack-setup">
|
||||
<Text type="link">our documentation</Text>
|
||||
</a>
|
||||
</Text>
|
||||
|
||||
<img
|
||||
style={{ height: '350px', display: 'block', margin: '0 auto' }}
|
||||
src="public/plugins/grafana-oncall-app/img/slack_instructions.png"
|
||||
/>
|
||||
</VerticalGroup>
|
||||
</Block>
|
||||
<Button onClick={handleClickConnectSlackAccount}>
|
||||
<Icon name="external-link-alt" className={cx('external-link-style')} /> Open Slack connection page
|
||||
</Button>
|
||||
</VerticalGroup>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,3 +6,15 @@
|
|||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.automatic-connect-telegram-block {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.field-command {
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import React, { HTMLAttributes, useEffect, useState } from 'react';
|
||||
|
||||
import { Alert, Button, HorizontalGroup, Icon, VerticalGroup } 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 { TelegramColorIcon } from 'icons';
|
||||
import { AppFeature } from 'state/features';
|
||||
import { useStore } from 'state/useStore';
|
||||
import { openNotification } from 'utils';
|
||||
|
|
@ -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.Title level={5}>Manual connection</Text.Title>
|
||||
|
||||
<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 alert groups 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>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -273,3 +273,59 @@ 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>
|
||||
);
|
||||
};
|
||||
export const SlackNewIcon = () => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48px" height="48px">
|
||||
<path
|
||||
fill="#33d375"
|
||||
d="M33,8c0-2.209-1.791-4-4-4s-4,1.791-4,4c0,1.254,0,9.741,0,11c0,2.209,1.791,4,4,4s4-1.791,4-4 C33,17.741,33,9.254,33,8z"
|
||||
/>
|
||||
<path
|
||||
fill="#33d375"
|
||||
d="M43,19c0,2.209-1.791,4-4,4c-1.195,0-4,0-4,0s0-2.986,0-4c0-2.209,1.791-4,4-4S43,16.791,43,19z"
|
||||
/>
|
||||
<path
|
||||
fill="#40c4ff"
|
||||
d="M8,14c-2.209,0-4,1.791-4,4s1.791,4,4,4c1.254,0,9.741,0,11,0c2.209,0,4-1.791,4-4s-1.791-4-4-4 C17.741,14,9.254,14,8,14z"
|
||||
/>
|
||||
<path
|
||||
fill="#40c4ff"
|
||||
d="M19,4c2.209,0,4,1.791,4,4c0,1.195,0,4,0,4s-2.986,0-4,0c-2.209,0-4-1.791-4-4S16.791,4,19,4z"
|
||||
/>
|
||||
<path
|
||||
fill="#e91e63"
|
||||
d="M14,39.006C14,41.212,15.791,43,18,43s4-1.788,4-3.994c0-1.252,0-9.727,0-10.984 c0-2.206-1.791-3.994-4-3.994s-4,1.788-4,3.994C14,29.279,14,37.754,14,39.006z"
|
||||
/>
|
||||
<path
|
||||
fill="#e91e63"
|
||||
d="M4,28.022c0-2.206,1.791-3.994,4-3.994c1.195,0,4,0,4,0s0,2.981,0,3.994c0,2.206-1.791,3.994-4,3.994 S4,30.228,4,28.022z"
|
||||
/>
|
||||
<path
|
||||
fill="#ffc107"
|
||||
d="M39,33c2.209,0,4-1.791,4-4s-1.791-4-4-4c-1.254,0-9.741,0-11,0c-2.209,0-4,1.791-4,4s1.791,4,4,4 C29.258,33,37.746,33,39,33z"
|
||||
/>
|
||||
<path
|
||||
fill="#ffc107"
|
||||
d="M28,43c-2.209,0-4-1.791-4-4c0-1.195,0-4,0-4s2.986,0,4,0c2.209,0,4,1.791,4,4S30.209,43,28,43z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
|
|
|||
BIN
grafana-plugin/src/img/slack_instructions.png
Normal file
BIN
grafana-plugin/src/img/slack_instructions.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
|
|
@ -15,3 +15,27 @@
|
|||
margin-bottom: 20px;
|
||||
border-bottom: 1px solid rgba(204, 204, 220, 0.25);
|
||||
}
|
||||
|
||||
.slack-infoblock {
|
||||
text-align: center;
|
||||
width: 725px;
|
||||
}
|
||||
|
||||
.external-link-style {
|
||||
margin-right: 4px;
|
||||
align-self: baseline;
|
||||
}
|
||||
|
||||
.team_workspace {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.infoblock-text {
|
||||
margin-left: 48px;
|
||||
margin-right: 48px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.infoblock-icon {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
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';
|
||||
|
||||
import Block from 'components/GBlock/Block';
|
||||
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 WithConfirm from 'components/WithConfirm/WithConfirm';
|
||||
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 { SlackNewIcon } from 'icons';
|
||||
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';
|
||||
|
|
@ -25,16 +25,27 @@ const cx = cn.bind(styles);
|
|||
|
||||
interface SlackProps extends WithStoreProps {}
|
||||
|
||||
interface SlackState {}
|
||||
interface SlackState {
|
||||
showENVVariablesButton: boolean;
|
||||
}
|
||||
|
||||
@observer
|
||||
class SlackSettings extends Component<SlackProps, SlackState> {
|
||||
state: SlackState = {};
|
||||
state: SlackState = {
|
||||
showENVVariablesButton: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.update();
|
||||
this.getSlackLiveSettings().then(() => {
|
||||
this.update();
|
||||
});
|
||||
}
|
||||
|
||||
handleOpenSlackInstructions = () => {
|
||||
const { store } = this.props;
|
||||
store.slackStore.installSlackIntegration();
|
||||
};
|
||||
|
||||
update = () => {
|
||||
const { store } = this.props;
|
||||
|
||||
|
|
@ -42,6 +53,27 @@ class SlackSettings extends Component<SlackProps, SlackState> {
|
|||
store.slackStore.updateSlackSettings();
|
||||
};
|
||||
|
||||
getSlackLiveSettings = async () => {
|
||||
const { store } = this.props;
|
||||
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 = 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 ||
|
||||
slackClientOAUTHSecret?.error ||
|
||||
slackRedirectHost?.error ||
|
||||
slackSigningSecret?.error
|
||||
) {
|
||||
this.setState({ showENVVariablesButton: true });
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { store } = this.props;
|
||||
const { teamStore } = store;
|
||||
|
|
@ -59,34 +91,47 @@ class SlackSettings extends Component<SlackProps, SlackState> {
|
|||
|
||||
return (
|
||||
<div className={cx('root')}>
|
||||
<Text.Title level={4} className={cx('title')}>
|
||||
Slack
|
||||
</Text.Title>
|
||||
<div className={cx('slack-settings')}>
|
||||
<Field label="Default channel for Slack notifications">
|
||||
<WithPermissionControl userAction={UserAction.UpdateGeneralLogChannelId}>
|
||||
<GSelect
|
||||
showSearch
|
||||
className={cx('select', 'control')}
|
||||
modelName="slackChannelStore"
|
||||
displayField="display_name"
|
||||
valueField="id"
|
||||
placeholder="Select Slack Channel"
|
||||
value={teamStore.currentTeam?.slack_channel?.id}
|
||||
onChange={this.handleSlackChannelChange}
|
||||
nullItemName={PRIVATE_CHANNEL_NAME}
|
||||
/>
|
||||
</WithPermissionControl>
|
||||
</Field>
|
||||
<div className={cx('title')}>
|
||||
<Text.Title level={3}>Slack</Text.Title>
|
||||
</div>
|
||||
<div className={cx('slack-settings')}>
|
||||
<Text.Title level={4} className={cx('title')}>
|
||||
<HorizontalGroup justify="space-between">
|
||||
<HorizontalGroup align="center">
|
||||
<Field label="Slack Workspace">
|
||||
<div className={cx('select', 'control', 'team_workspace')}>
|
||||
<Text>{store.teamStore.currentTeam.slack_team_identity?.cached_name}</Text>
|
||||
</div>
|
||||
</Field>
|
||||
<Field label="Default channel for Slack notifications">
|
||||
<WithPermissionControl userAction={UserAction.UpdateGeneralLogChannelId}>
|
||||
<GSelect
|
||||
showSearch
|
||||
className={cx('select', 'control')}
|
||||
modelName="slackChannelStore"
|
||||
displayField="display_name"
|
||||
valueField="id"
|
||||
placeholder="Select Slack Channel"
|
||||
value={teamStore.currentTeam?.slack_channel?.id}
|
||||
onChange={this.handleSlackChannelChange}
|
||||
nullItemName={PRIVATE_CHANNEL_NAME}
|
||||
/>
|
||||
</WithPermissionControl>
|
||||
</Field>
|
||||
</HorizontalGroup>
|
||||
<WithPermissionControl userAction={UserAction.UpdateIntegrations}>
|
||||
<WithConfirm title="Are you sure to delete this Slack Integration?">
|
||||
<Button variant="destructive" size="sm" onClick={() => this.removeSlackIntegration()}>
|
||||
Disconnect
|
||||
</Button>
|
||||
</WithConfirm>
|
||||
</WithPermissionControl>
|
||||
</HorizontalGroup>
|
||||
</div>
|
||||
<div className={cx('slack-settings')}>
|
||||
<Text.Title level={5} className={cx('title')}>
|
||||
Additional settings
|
||||
</Text.Title>
|
||||
<Field
|
||||
label="Timeout for acknowledged alerts"
|
||||
description="Set up a reminder and timeout for acknowledged alert to never forget about them"
|
||||
>
|
||||
<Field label="Timeout for acknowledged alerts">
|
||||
<HorizontalGroup>
|
||||
<WithPermissionControl userAction={UserAction.UpdateGeneralLogChannelId}>
|
||||
<RemoteSelect
|
||||
|
|
@ -110,14 +155,51 @@ class SlackSettings extends Component<SlackProps, SlackState> {
|
|||
</HorizontalGroup>
|
||||
</Field>
|
||||
</div>
|
||||
<Text.Title level={4} className={cx('title')}>
|
||||
Remove integration
|
||||
</Text.Title>
|
||||
<SlackIntegrationButton className={cx('slack-button')} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
renderSlackWorkspace = () => {
|
||||
const { store } = this.props;
|
||||
return <Text>{store.teamStore.currentTeam.slack_team_identity?.cached_name}</Text>;
|
||||
};
|
||||
|
||||
renderSlackChannels = () => {
|
||||
const { store } = this.props;
|
||||
return (
|
||||
<WithPermissionControl userAction={UserAction.UpdateGeneralLogChannelId}>
|
||||
<GSelect
|
||||
showSearch
|
||||
className={cx('select', 'control')}
|
||||
modelName="slackChannelStore"
|
||||
displayField="display_name"
|
||||
valueField="id"
|
||||
placeholder="Select Slack Channel"
|
||||
value={store.teamStore.currentTeam?.slack_channel?.id}
|
||||
onChange={this.handleSlackChannelChange}
|
||||
nullItemName={PRIVATE_CHANNEL_NAME}
|
||||
/>
|
||||
</WithPermissionControl>
|
||||
);
|
||||
};
|
||||
|
||||
renderActionButtons = () => {
|
||||
<WithPermissionControl userAction={UserAction.UpdateIntegrations}>
|
||||
<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(() => {
|
||||
store.teamStore.loadCurrentTeam();
|
||||
});
|
||||
};
|
||||
|
||||
getSlackSettingsChangeHandler = (field: string) => {
|
||||
const { store } = this.props;
|
||||
const { slackStore } = store;
|
||||
|
|
@ -138,28 +220,54 @@ class SlackSettings extends Component<SlackProps, SlackState> {
|
|||
|
||||
renderSlackStub = () => {
|
||||
const { store } = this.props;
|
||||
const { showENVVariablesButton } = this.state;
|
||||
const isLiveSettingAvailable = store.hasFeature(AppFeature.LiveSettings) && showENVVariablesButton;
|
||||
|
||||
return (
|
||||
<Tutorial
|
||||
step={TutorialStep.Slack}
|
||||
title={
|
||||
<VerticalGroup align="center" spacing="lg">
|
||||
<Text type="secondary">
|
||||
Bring the whole incident lifecycle to Slack, from alerts, monitoring, escalations to resolution notes and
|
||||
reports.
|
||||
<VerticalGroup spacing="lg">
|
||||
<Text.Title level={2}>Connect Slack workspace</Text.Title>
|
||||
<Block bordered withBackground className={cx('slack-infoblock')}>
|
||||
<VerticalGroup align="center">
|
||||
<div className={cx('infoblock-icon')}>
|
||||
<SlackNewIcon />
|
||||
</div>
|
||||
<Text className={cx('infoblock-text')}>
|
||||
Slack connection will allow you to manage alert groups in your team Slack workspace.
|
||||
</Text>
|
||||
|
||||
<SlackIntegrationButton className={cx('slack-button')} />
|
||||
|
||||
{store.hasFeature(AppFeature.LiveSettings) && (
|
||||
<Text type="secondary">
|
||||
Before installing <PluginLink query={{ page: 'live-settings' }}>check ENV variables</PluginLink> related
|
||||
to Slack please
|
||||
<Text className={cx('infoblock-text')}>
|
||||
After a basic workspace connection your team members need to connect their personal Slack accounts in
|
||||
order to be allowed to manage alert groups.
|
||||
</Text>
|
||||
{isLiveSettingAvailable && (
|
||||
<Text type="secondary" className={cx('infoblock-text')}>
|
||||
For bot creating instructions and additional information please read{' '}
|
||||
<a href="https://grafana.com/docs/grafana-cloud/oncall/open-source/#slack-setup">
|
||||
<Text type="link">our documentation</Text>
|
||||
</a>
|
||||
</Text>
|
||||
)}
|
||||
|
||||
<img
|
||||
style={{ height: '350px', display: 'block', margin: '0 auto' }}
|
||||
src="public/plugins/grafana-oncall-app/img/slack_instructions.png"
|
||||
/>
|
||||
</VerticalGroup>
|
||||
}
|
||||
/>
|
||||
</Block>
|
||||
{isLiveSettingAvailable ? (
|
||||
<PluginLink query={{ page: 'live-settings' }}>
|
||||
<Button variant="primary">Setup ENV Variables</Button>
|
||||
</PluginLink>
|
||||
) : (
|
||||
<HorizontalGroup>
|
||||
<Button onClick={this.handleOpenSlackInstructions}>
|
||||
<Icon name="external-link-alt" className={cx('external-link-style')} /> Open Slack connection page
|
||||
</Button>
|
||||
<PluginLink query={{ page: 'live-settings' }}>
|
||||
<Button variant="secondary">See ENV Variables</Button>
|
||||
</PluginLink>
|
||||
</HorizontalGroup>
|
||||
)}
|
||||
</VerticalGroup>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,3 +6,22 @@
|
|||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.telegram-infoblock {
|
||||
text-align: center;
|
||||
width: 725px;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 20px 30px;
|
||||
}
|
||||
|
||||
.infoblock-text {
|
||||
margin-left: 48px;
|
||||
margin-right: 48px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.infoblock-icon {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
import React, { Component } from 'react';
|
||||
|
||||
import { Alert, Button, HorizontalGroup, Icon, LoadingPlaceholder, VerticalGroup } from '@grafana/ui';
|
||||
import { Badge, Button, HorizontalGroup, Icon, LoadingPlaceholder, VerticalGroup } from '@grafana/ui';
|
||||
import cn from 'classnames/bind';
|
||||
import { observer } from 'mobx-react';
|
||||
|
||||
import Block from 'components/GBlock/Block';
|
||||
import GTable from 'components/GTable/GTable';
|
||||
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 WithConfirm from 'components/WithConfirm/WithConfirm';
|
||||
import TelegramIntegrationButton from 'containers/TelegramIntegrationButton/TelegramIntegrationButton';
|
||||
import { TelegramColorIcon } from 'icons';
|
||||
import { TelegramChannel } from 'models/telegram_channel/telegram_channel.types';
|
||||
import { AppFeature } from 'state/features';
|
||||
import { WithStoreProps } from 'state/types';
|
||||
|
|
@ -47,16 +47,32 @@ 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">
|
||||
<div className={cx('infoblock-icon')}>
|
||||
<TelegramColorIcon />
|
||||
</div>
|
||||
<Text className={cx('infoblock-text')}>
|
||||
You can manage alert groups in your team Telegram channel or from personal direct messages.{' '}
|
||||
</Text>
|
||||
|
||||
<Text className={cx('infoblock-text')}>
|
||||
To connect channel setup Telegram environment first, which includes connection to your bot and host URL.
|
||||
</Text>
|
||||
<Text type="secondary" className={cx('infoblock-text')}>
|
||||
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 +82,55 @@ class TelegramSettings extends Component<TelegramProps, TelegramState> {
|
|||
|
||||
if (!connectedChannels.length) {
|
||||
return (
|
||||
<Tutorial
|
||||
step={TutorialStep.Slack}
|
||||
title={
|
||||
<VerticalGroup align="center" spacing="lg">
|
||||
<Text type="secondary">
|
||||
Bring the whole incident lifecycle into your chat workspace. Everything from alerts, monitoring, and
|
||||
escalations to reports.
|
||||
<VerticalGroup spacing="lg">
|
||||
<Text.Title level={2}>Connect Telegram workspace</Text.Title>
|
||||
<Block bordered withBackground className={cx('telegram-infoblock')}>
|
||||
<VerticalGroup align="center">
|
||||
<div className={cx('infoblock-icon')}>
|
||||
<TelegramColorIcon />
|
||||
</div>
|
||||
<Text className={cx('infoblock-text')}>
|
||||
You can manage alert groups in your team Telegram channel or from personal direct messages.{' '}
|
||||
</Text>
|
||||
<Text type="secondary" className={cx('infoblock-text')}>
|
||||
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>
|
||||
<Text.Title level={4}>Features</Text.Title>
|
||||
<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 alert groups.
|
||||
</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 +159,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" />}</>;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue