Cloud notifications enpoints
This commit is contained in:
parent
cd796d4160
commit
82ec0c9324
8 changed files with 359 additions and 187 deletions
|
|
@ -2,6 +2,7 @@ import React, { useCallback, useState } from 'react';
|
|||
|
||||
import { Tab, TabContent, TabsBar } from '@grafana/ui';
|
||||
import cn from 'classnames/bind';
|
||||
import { observer } from 'mobx-react';
|
||||
|
||||
import Block from 'components/GBlock/Block';
|
||||
import MobileAppVerification from 'containers/MobileAppVerification/MobileAppVerification';
|
||||
|
|
@ -13,6 +14,7 @@ import PhoneVerification from 'containers/UserSettings/parts/tabs/PhoneVerificat
|
|||
import TelegramInfo from 'containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo';
|
||||
import { UserInfoTab } from 'containers/UserSettings/parts/tabs/UserInfoTab/UserInfoTab';
|
||||
import { User } from 'models/user/user.types';
|
||||
import { AppFeature } from 'state/features';
|
||||
import { useStore } from 'state/useStore';
|
||||
|
||||
import styles from 'containers/UserSettings/parts/index.module.css';
|
||||
|
|
@ -101,12 +103,11 @@ interface TabsContentProps {
|
|||
isDesktopOrLaptop: boolean;
|
||||
}
|
||||
|
||||
export const TabsContent = (props: TabsContentProps) => {
|
||||
export const TabsContent = observer((props: TabsContentProps) => {
|
||||
const { id, activeTab, onTabChange, isDesktopOrLaptop } = props;
|
||||
|
||||
const store = useStore();
|
||||
const { userStore } = store;
|
||||
const [isPhoneEnabled, setIsPhoneEnabled] = useState<boolean>(false);
|
||||
|
||||
const storeUser = userStore.items[id];
|
||||
|
||||
|
|
@ -127,7 +128,7 @@ export const TabsContent = (props: TabsContentProps) => {
|
|||
))}
|
||||
{activeTab === UserSettingsTab.NotificationSettings && <NotificationSettingsTab id={id} />}
|
||||
{activeTab === UserSettingsTab.PhoneVerification &&
|
||||
(isPhoneEnabled ? (
|
||||
(store.hasFeature(AppFeature.CloudNotifications) ? (
|
||||
<CloudPhoneSettings />
|
||||
) : (
|
||||
<PhoneVerification userPk={id} phone={storeUser.unverified_phone_number || '+'} />
|
||||
|
|
@ -139,4 +140,4 @@ export const TabsContent = (props: TabsContentProps) => {
|
|||
{activeTab === UserSettingsTab.TelegramInfo && <TelegramInfo />}
|
||||
</TabContent>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,8 +1,20 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import { getLocationSrv, LocationUpdate } from '@grafana/runtime';
|
||||
import { Field, Input, Button, Modal, HorizontalGroup, Alert, Icon, VerticalGroup, Table } from '@grafana/ui';
|
||||
import {
|
||||
Field,
|
||||
Input,
|
||||
Button,
|
||||
Modal,
|
||||
HorizontalGroup,
|
||||
Alert,
|
||||
Icon,
|
||||
VerticalGroup,
|
||||
Table,
|
||||
LoadingPlaceholder,
|
||||
} 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';
|
||||
|
|
@ -20,66 +32,106 @@ const cx = cn.bind(styles);
|
|||
|
||||
interface CloudPhoneSettingsProps extends WithStoreProps {}
|
||||
|
||||
const CloudPhoneSettings = (props: CloudPhoneSettingsProps) => {
|
||||
const CloudPhoneSettings = observer((props: CloudPhoneSettingsProps) => {
|
||||
const store = useStore();
|
||||
const [isAccountMatched, setIsAccountMatched] = useState<boolean>(true);
|
||||
const [isPhoneVerified, setIsPhoneVerified] = useState<boolean>(true);
|
||||
const [userStatus, setUserStatus] = useState<number>(0);
|
||||
const [userLink, setUserLink] = useState<string>(null);
|
||||
|
||||
const signUpGrafanaCloud = () => {
|
||||
console.log('Sign UP');
|
||||
useEffect(() => {
|
||||
getCloudUserInfo();
|
||||
}, []);
|
||||
|
||||
const handleLinkClick = (link: string) => {
|
||||
getLocationSrv().update({ partial: false, path: link });
|
||||
};
|
||||
const handleLinkClick = () => {
|
||||
|
||||
const syncUser = () => {
|
||||
store.cloudStore.syncCloudUser(store.userStore.currentUserPk);
|
||||
};
|
||||
|
||||
const getCloudUserInfo = async () => {
|
||||
await store.cloudStore.updateItems();
|
||||
const { count, results } = await store.cloudStore.getSearchResult();
|
||||
console.log('RES', results);
|
||||
const cloudUser =
|
||||
results && (await results.find((element: { id: string }) => element.id === store.userStore.currentUserPk));
|
||||
console.log('CLOUD USER', cloudUser);
|
||||
setUserStatus(cloudUser?.cloud_data?.status);
|
||||
setUserLink(cloudUser?.cloud_data?.link);
|
||||
};
|
||||
|
||||
const UserCloudStatus = () => {
|
||||
switch (userStatus) {
|
||||
case 0:
|
||||
return (
|
||||
<VerticalGroup spacing="lg">
|
||||
<Text>Grafana Cloud is not synced</Text>
|
||||
</VerticalGroup>
|
||||
);
|
||||
case 1:
|
||||
return (
|
||||
<VerticalGroup spacing="lg">
|
||||
<Text>
|
||||
{
|
||||
'We can’t find a matching account in the connected Grafana Cloud instance (matching happens by e-mail). '
|
||||
}
|
||||
</Text>
|
||||
<Button variant="primary" onClick={() => handleLinkClick(userLink)}>
|
||||
Sign up in Grafana Cloud
|
||||
</Button>
|
||||
</VerticalGroup>
|
||||
);
|
||||
case 2:
|
||||
return (
|
||||
<VerticalGroup spacing="lg">
|
||||
<Text>
|
||||
Your account successfully matched with the Grafana Cloud account. Please verify your phone number.{' '}
|
||||
</Text>
|
||||
<Button variant="secondary" icon="external-link-alt" onClick={() => handleLinkClick(userLink)}>
|
||||
Verify phone number in Grafana Cloud
|
||||
</Button>
|
||||
</VerticalGroup>
|
||||
);
|
||||
case 3:
|
||||
return (
|
||||
<VerticalGroup spacing="lg">
|
||||
<Text>
|
||||
Your account successfully matched with the Grafana Cloud account. Your phone number is verified.{' '}
|
||||
</Text>
|
||||
<Button variant="secondary" icon="external-link-alt" onClick={() => handleLinkClick(userLink)}>
|
||||
Open account in Grafana Cloud
|
||||
</Button>
|
||||
</VerticalGroup>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<VerticalGroup spacing="lg">
|
||||
<Text>
|
||||
{
|
||||
'We can’t find a matching account in the connected Grafana Cloud instance (matching happens by e-mail). '
|
||||
}
|
||||
</Text>
|
||||
<Button variant="primary" onClick={() => handleLinkClick(userLink)}>
|
||||
Sign up in Grafana Cloud
|
||||
</Button>
|
||||
</VerticalGroup>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<VerticalGroup spacing="lg">
|
||||
<HorizontalGroup justify="space-between">
|
||||
<Text.Title level={3}>OnCall use Grafana Cloud for SMS and phone call notifications</Text.Title>
|
||||
<Button variant="secondary" icon="sync" onClick={handleLinkClick}>
|
||||
<Button variant="secondary" icon="sync" onClick={syncUser}>
|
||||
Update
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
{isAccountMatched ? (
|
||||
isPhoneVerified ? (
|
||||
<VerticalGroup spacing="lg">
|
||||
<Text>
|
||||
Your account successfully matched with the Grafana Cloud account. Please verify your phone number.{' '}
|
||||
</Text>
|
||||
<Button
|
||||
variant="secondary"
|
||||
icon="external-link-alt"
|
||||
onClick={() => handleLinkClick('fillmewithcorrectlink')}
|
||||
>
|
||||
Verify phone number in Grafana Cloud
|
||||
</Button>
|
||||
</VerticalGroup>
|
||||
) : (
|
||||
<VerticalGroup spacing="lg">
|
||||
<Text>
|
||||
Your account successfully matched with the Grafana Cloud account. Your phone number is verified.
|
||||
</Text>
|
||||
<Button
|
||||
variant="secondary"
|
||||
icon="external-link-alt"
|
||||
onClick={() => handleLinkClick('fillmewithcorrectlink')}
|
||||
>
|
||||
Open account in Grafana Cloud
|
||||
</Button>
|
||||
</VerticalGroup>
|
||||
)
|
||||
) : (
|
||||
<VerticalGroup spacing="lg">
|
||||
<Text>
|
||||
{'We can’t find a matching account in the connected Grafana Cloud instance (matching happens by e-mail). '}
|
||||
</Text>
|
||||
<Button variant="primary" onClick={signUpGrafanaCloud}>
|
||||
Sign up in Grafana Cloud
|
||||
</Button>
|
||||
</VerticalGroup>
|
||||
)}
|
||||
{userStatus ? <UserCloudStatus /> : <LoadingPlaceholder text="Loading..." />}
|
||||
</VerticalGroup>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default withMobXProviderContext(CloudPhoneSettings);
|
||||
|
|
|
|||
|
|
@ -59,18 +59,15 @@ export class CloudStore extends BaseStore {
|
|||
}
|
||||
|
||||
async syncCloudUser(id: string) {
|
||||
return await makeRequest(`${this.path}${id}/sync_with_cloud/`, { method: 'POST' });
|
||||
return await makeRequest(`${this.path}${id}/sync/`, { method: 'POST' });
|
||||
}
|
||||
|
||||
async getCloudConnectionStatus() {
|
||||
return await makeRequest(`/cloud_connection/`, { method: 'GET' });
|
||||
}
|
||||
|
||||
@action
|
||||
async connectToCloud(token: string) {}
|
||||
|
||||
@action
|
||||
async disconnectToCloud() {
|
||||
return await makeRequest(`/live_settings/`, { method: 'DELETE' });
|
||||
return await makeRequest(`/cloud_connection/`, { method: 'DELETE' });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,4 +60,9 @@ export class GlobalSettingStore extends BaseStore {
|
|||
|
||||
return this.searchResult[query].map((globalSettingId: GlobalSetting['id']) => this.items[globalSettingId]);
|
||||
}
|
||||
|
||||
async getGlobalSettingItemByName(name: string) {
|
||||
const results = await this.getAll();
|
||||
return results.find((element: { name: string }) => element.name === name);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,22 @@
|
|||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
display: inline-block;
|
||||
white-space: break-spaces;
|
||||
line-height: 20px;
|
||||
color: var(--error-text-color);
|
||||
}
|
||||
|
||||
.error-icon svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.heart-icon {
|
||||
color: var(--secondary-text-color);
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.block-button {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,20 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import { getLocationSrv, LocationUpdate } from '@grafana/runtime';
|
||||
import { Field, Input, Button, Modal, HorizontalGroup, Alert, Icon, VerticalGroup, Table } from '@grafana/ui';
|
||||
import {
|
||||
Field,
|
||||
Input,
|
||||
Button,
|
||||
Modal,
|
||||
HorizontalGroup,
|
||||
Alert,
|
||||
Icon,
|
||||
VerticalGroup,
|
||||
Table,
|
||||
LoadingPlaceholder,
|
||||
} 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';
|
||||
|
|
@ -14,36 +26,45 @@ import { Cloud } from 'models/cloud/cloud.types';
|
|||
import { WithStoreProps } from 'state/types';
|
||||
import { useStore } from 'state/useStore';
|
||||
import { withMobXProviderContext } from 'state/withStore';
|
||||
import { openErrorNotification } from 'utils';
|
||||
|
||||
import styles from './CloudPage.module.css';
|
||||
|
||||
const cx = cn.bind(styles);
|
||||
|
||||
interface CloudPageProps extends WithStoreProps {}
|
||||
const ITEMS_PER_PAGE = 1;
|
||||
|
||||
const CloudPage = (props: CloudPageProps) => {
|
||||
const CloudPage = observer((props: CloudPageProps) => {
|
||||
const store = useStore();
|
||||
const [page, setPage] = useState<number>(1);
|
||||
const [cloudApiKey, setCloudApiKey] = useState<string>('');
|
||||
const [cloudIsConnected, setCloudIsConnected] = useState<boolean>(true);
|
||||
const [apiKeyError, setApiKeyError] = useState<boolean>(false);
|
||||
const [cloudIsConnected, setCloudIsConnected] = useState<boolean>(undefined);
|
||||
const [heartbitLink, setHeartbitLink] = useState<string>(null);
|
||||
const [heartbitStatus, setHeartbitStatus] = useState<boolean>(false);
|
||||
const [showConfirmationModal, setShowConfirmationModal] = useState<boolean>(false);
|
||||
const [syncingUsers, setSyncingUsers] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
store.cloudStore.updateItems();
|
||||
store.cloudStore.updateItems(page);
|
||||
store.cloudStore.getCloudConnectionStatus().then((cloudStatus) => {
|
||||
setCloudIsConnected(cloudStatus.cloud_connection_status);
|
||||
setHeartbitStatus(cloudStatus.cloud_heartbeat_enabled);
|
||||
setHeartbitLink(cloudStatus.cloud_heartbeat_link);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const data = [
|
||||
{ id: 'yshanyrova', email: 'y.shanyrova@grafana.com', cloud_data: { status: 2, link: '/test/abc' } },
|
||||
{ id: 'amixradmin', email: 'amixr-admin@grafana.com', cloud_data: { status: 1, link: '/test/abc' } },
|
||||
{ id: 'amixr', email: 'amixr@grafana.com', cloud_data: { status: undefined, link: '/test/abc' } },
|
||||
];
|
||||
const { count, results } = store.cloudStore.getSearchResult();
|
||||
|
||||
// const { count, results } = store.cloudStore.getSearchResult();
|
||||
const handleChangePage = (page: number) => {
|
||||
setPage(page);
|
||||
store.cloudStore.updateItems(page);
|
||||
};
|
||||
|
||||
const handleChangeCloudApiKey = useCallback((e) => {
|
||||
setCloudApiKey(e.target.value);
|
||||
setApiKeyError(false);
|
||||
}, []);
|
||||
|
||||
const saveKeyAndConnect = () => {
|
||||
|
|
@ -51,20 +72,32 @@ const CloudPage = (props: CloudPageProps) => {
|
|||
};
|
||||
|
||||
const disconnectCloudOncall = () => {
|
||||
console.log('disconnected');
|
||||
setCloudIsConnected(false);
|
||||
store.cloudStore.disconnectToCloud();
|
||||
};
|
||||
|
||||
const connectToCloud = () => {
|
||||
setCloudIsConnected(true);
|
||||
const connectToCloud = async () => {
|
||||
setShowConfirmationModal(false);
|
||||
// store.cloudStore.update('')
|
||||
store.cloudStore.connectToCloud(cloudApiKey);
|
||||
const globalSettingItem = await store.globalSettingStore.getGlobalSettingItemByName('GRAFANA_CLOUD_ONCALL_TOKEN');
|
||||
store.globalSettingStore
|
||||
.update(globalSettingItem?.id, { name: 'GRAFANA_CLOUD_ONCALL_TOKEN', value: cloudApiKey })
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
setCloudIsConnected(false);
|
||||
setApiKeyError(true);
|
||||
openErrorNotification(response.error);
|
||||
} else {
|
||||
setCloudIsConnected(true);
|
||||
syncUsers();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const syncUsers = () => {
|
||||
store.cloudStore.syncCloudUsers();
|
||||
const syncUsers = async () => {
|
||||
setSyncingUsers(true);
|
||||
await store.cloudStore.syncCloudUsers();
|
||||
await store.cloudStore.updateItems();
|
||||
setSyncingUsers(false);
|
||||
};
|
||||
|
||||
const handleLinkClick = (link: string) => {
|
||||
|
|
@ -76,17 +109,7 @@ const CloudPage = (props: CloudPageProps) => {
|
|||
case 0:
|
||||
return null;
|
||||
case 1:
|
||||
return (
|
||||
<Button
|
||||
variant="secondary"
|
||||
icon="external-link-alt"
|
||||
size="sm"
|
||||
className={cx('table-button')}
|
||||
onClick={() => handleLinkClick(user?.cloud_data?.link)}
|
||||
>
|
||||
Configure notifications
|
||||
</Button>
|
||||
);
|
||||
return null;
|
||||
case 2:
|
||||
return (
|
||||
<Button
|
||||
|
|
@ -99,6 +122,18 @@ const CloudPage = (props: CloudPageProps) => {
|
|||
Open profile in Cloud
|
||||
</Button>
|
||||
);
|
||||
case 3:
|
||||
return (
|
||||
<Button
|
||||
variant="secondary"
|
||||
icon="external-link-alt"
|
||||
size="sm"
|
||||
className={cx('table-button')}
|
||||
onClick={() => handleLinkClick(user?.cloud_data?.link)}
|
||||
>
|
||||
Configure notifications
|
||||
</Button>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
@ -107,12 +142,14 @@ const CloudPage = (props: CloudPageProps) => {
|
|||
const renderStatus = (user: Cloud) => {
|
||||
switch (user?.cloud_data?.status) {
|
||||
case 0:
|
||||
return <Text className={cx('error-message')}>User not found in the Grafana Cloud</Text>;
|
||||
return <Text className={cx('error-message')}>Grafana Cloud is not synced</Text>;
|
||||
case 1:
|
||||
return <Text type="success">Phone number verified</Text>;
|
||||
|
||||
return <Text className={cx('error-message')}>User not found in Grafana Cloud</Text>;
|
||||
case 2:
|
||||
return <Text type="warning">Phone number is not verified in Grafana Cloud</Text>;
|
||||
case 3:
|
||||
return <Text type="success">Phone number verified</Text>;
|
||||
|
||||
default:
|
||||
return <Text className={cx('error-message')}>User not found in Grafana Cloud</Text>;
|
||||
}
|
||||
|
|
@ -122,20 +159,26 @@ const CloudPage = (props: CloudPageProps) => {
|
|||
switch (user?.cloud_data?.status) {
|
||||
case 0:
|
||||
return (
|
||||
<span className={cx('error-message')}>
|
||||
<div className={cx('error-icon')}>
|
||||
<CrossCircleIcon />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
case 1:
|
||||
return <Icon className={cx('success-message')} name="check-circle" />;
|
||||
return (
|
||||
<div className={cx('error-icon')}>
|
||||
<CrossCircleIcon />
|
||||
</div>
|
||||
);
|
||||
|
||||
case 2:
|
||||
return <Icon className={cx('warning-message')} name="exclamation-triangle" />;
|
||||
case 3:
|
||||
return <Icon className={cx('success-message')} name="check-circle" />;
|
||||
default:
|
||||
return (
|
||||
<span className={cx('error-message')}>
|
||||
<div className={cx('error-message')}>
|
||||
<CrossCircleIcon />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
@ -168,40 +211,158 @@ const CloudPage = (props: CloudPageProps) => {
|
|||
},
|
||||
];
|
||||
|
||||
const ConnectedBlock = (
|
||||
<VerticalGroup spacing="lg">
|
||||
<Block withBackground bordered className={cx('info-block')}>
|
||||
<VerticalGroup>
|
||||
<Text.Title level={4}>
|
||||
<Icon name="check" className={cx('block-icon')} size="lg" /> Cloud OnCall API key
|
||||
</Text.Title>
|
||||
<Text type="secondary">Cloud OnCall is sucessfully connected.</Text>
|
||||
|
||||
<WithConfirm title="Are you sure to disconnect Cloud OnCall?" confirmText="Disconnect">
|
||||
<Button variant="destructive" onClick={disconnectCloudOncall} size="md" className={cx('block-button')}>
|
||||
Disconnect
|
||||
</Button>
|
||||
</WithConfirm>
|
||||
</VerticalGroup>
|
||||
</Block>
|
||||
<Block bordered withBackground className={cx('info-block')}>
|
||||
<VerticalGroup>
|
||||
<Text.Title level={4}>
|
||||
<span className={cx('heart-icon')}>
|
||||
<HeartIcon />
|
||||
</span>
|
||||
Monitor cloud instance with heartbeat
|
||||
</Text.Title>
|
||||
<Text type="secondary">
|
||||
Once connected, current OnCall instance will send heartbeats every 3 minutes to the cloud Instance. If no
|
||||
heartbeat will be received in 10 minutes, cloud instance will issue an alert.
|
||||
</Text>
|
||||
{heartbitStatus && heartbitLink && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
icon="external-link-alt"
|
||||
className={cx('block-button')}
|
||||
onClick={() => handleLinkClick(heartbitLink)}
|
||||
>
|
||||
Configure escalations in Cloud OnCall
|
||||
</Button>
|
||||
)}
|
||||
</VerticalGroup>
|
||||
</Block>
|
||||
<Block bordered withBackground className={cx('info-block')}>
|
||||
<VerticalGroup>
|
||||
<Text.Title level={4}>
|
||||
<Icon name="bell" className={cx('block-icon')} size="lg" /> SMS and phone call notifications
|
||||
</Text.Title>
|
||||
|
||||
<div style={{ width: '100%' }}>
|
||||
<Text type="secondary">
|
||||
{
|
||||
'Ask your users to sign up in Grafana Cloud, verify phone number and feel free to set up SMS & phone call notificaitons in personal settings!'
|
||||
}
|
||||
</Text>
|
||||
|
||||
<GTable
|
||||
className={cx('user-table')}
|
||||
rowClassName={cx('user-row')}
|
||||
showHeader={false}
|
||||
emptyText={results ? 'No variables found' : 'Loading...'}
|
||||
title={() => (
|
||||
<div className={cx('table-title')}>
|
||||
<HorizontalGroup justify="space-between">
|
||||
<Text type="secondary">
|
||||
{count ? count : 0}
|
||||
{` users matched between OSS and Cloud OnCall`}
|
||||
</Text>
|
||||
{syncingUsers ? (
|
||||
<Button variant="primary" onClick={syncUsers} icon="sync" disabled>
|
||||
Syncing...
|
||||
</Button>
|
||||
) : (
|
||||
<Button variant="primary" onClick={syncUsers} icon="sync">
|
||||
Sync users
|
||||
</Button>
|
||||
)}
|
||||
</HorizontalGroup>
|
||||
</div>
|
||||
)}
|
||||
rowKey="id"
|
||||
// @ts-ignore
|
||||
columns={columns}
|
||||
data={results}
|
||||
pagination={{
|
||||
page,
|
||||
total: Math.ceil((count || 0) / ITEMS_PER_PAGE),
|
||||
onChange: handleChangePage,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</VerticalGroup>
|
||||
</Block>
|
||||
</VerticalGroup>
|
||||
);
|
||||
|
||||
const DisconnectedBlock = (
|
||||
<VerticalGroup spacing="lg">
|
||||
<Block withBackground bordered className={cx('info-block')}>
|
||||
<VerticalGroup>
|
||||
<Text.Title level={4}>
|
||||
<Icon name="sync" className={cx('block-icon')} size="lg" /> Cloud OnCall API key
|
||||
</Text.Title>
|
||||
<Field
|
||||
label=""
|
||||
description="Find it in you Cloud OnCall -> Settings page"
|
||||
style={{ width: '100%' }}
|
||||
invalid={apiKeyError}
|
||||
>
|
||||
<Input id="cloudApiKey" onChange={handleChangeCloudApiKey} />
|
||||
</Field>
|
||||
<Button variant="primary" onClick={saveKeyAndConnect} disabled={!cloudApiKey} size="md">
|
||||
Save key and connect
|
||||
</Button>
|
||||
</VerticalGroup>
|
||||
</Block>
|
||||
<Block bordered withBackground className={cx('info-block')}>
|
||||
<VerticalGroup>
|
||||
<Text.Title level={4}>
|
||||
<span className={cx('block-icon')}>
|
||||
<HeartIcon />
|
||||
</span>{' '}
|
||||
Monitor cloud instance with heartbeat
|
||||
</Text.Title>
|
||||
<Text type="secondary">
|
||||
Once connected, current OnCall instance will send heartbeats every 3 minutes to the cloud Instance. If no
|
||||
heartbeat will be received in 10 minutes, cloud instance will issue an alert.
|
||||
</Text>
|
||||
</VerticalGroup>
|
||||
</Block>
|
||||
<Block bordered withBackground className={cx('info-block')}>
|
||||
<VerticalGroup>
|
||||
<Text.Title level={4}>
|
||||
<Icon name="bell" className={cx('block-icon')} size="lg" /> SMS and phone call notifications
|
||||
</Text.Title>
|
||||
|
||||
<Text type="secondary">Users matched between OSS and Cloud OnCall currently unavialable.</Text>
|
||||
</VerticalGroup>
|
||||
</Block>
|
||||
</VerticalGroup>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={cx('root')}>
|
||||
<VerticalGroup spacing="lg">
|
||||
<Text.Title level={3} className={cx('cloud-page-title')}>
|
||||
Connect Open Source OnCall and <Text className={cx('cloud-oncall-name')}>Cloud OnCall</Text>
|
||||
</Text.Title>
|
||||
<Block withBackground bordered className={cx('info-block')}>
|
||||
{cloudIsConnected ? (
|
||||
<VerticalGroup>
|
||||
<Text.Title level={4}>
|
||||
<Icon name="check" className={cx('block-icon')} size="lg" /> Cloud OnCall API key
|
||||
</Text.Title>
|
||||
<Text type="secondary">Cloud OnCall is sucessfully connected.</Text>
|
||||
|
||||
<WithConfirm title="Are you sure to disconnect Cloud OnCall?" confirmText="Disconnect">
|
||||
<Button variant="destructive" onClick={disconnectCloudOncall} size="md" className={cx('block-button')}>
|
||||
Disconnect
|
||||
</Button>
|
||||
</WithConfirm>
|
||||
</VerticalGroup>
|
||||
) : (
|
||||
<VerticalGroup>
|
||||
<Text.Title level={4}>
|
||||
<Icon name="sync" className={cx('block-icon')} size="lg" /> Cloud OnCall API key
|
||||
</Text.Title>
|
||||
<Field label="" description="Find it in you Cloud OnCall -> Settings page" style={{ width: '100%' }}>
|
||||
<Input id="cloudApiKey" onChange={handleChangeCloudApiKey} />
|
||||
</Field>
|
||||
<Button variant="primary" onClick={saveKeyAndConnect} disabled={!cloudApiKey} size="md">
|
||||
Save key and connect
|
||||
</Button>
|
||||
</VerticalGroup>
|
||||
)}
|
||||
</Block>
|
||||
{cloudIsConnected === undefined ? (
|
||||
<LoadingPlaceholder text="Loading..." />
|
||||
) : cloudIsConnected ? (
|
||||
ConnectedBlock
|
||||
) : (
|
||||
DisconnectedBlock
|
||||
)}
|
||||
|
||||
{showConfirmationModal && (
|
||||
<Modal
|
||||
|
|
@ -219,77 +380,9 @@ const CloudPage = (props: CloudPageProps) => {
|
|||
</HorizontalGroup>
|
||||
</Modal>
|
||||
)}
|
||||
|
||||
<Block bordered withBackground className={cx('info-block')}>
|
||||
<VerticalGroup>
|
||||
<Text.Title level={4}>
|
||||
<span className={cx('block-icon')}>
|
||||
<HeartIcon />
|
||||
</span>{' '}
|
||||
Monitor cloud instance with heartbeat
|
||||
</Text.Title>
|
||||
<Text type="secondary">
|
||||
Once connected, current OnCall instance will send heartbeats every 3 minutes to the cloud Instance. If no
|
||||
heartbeat will be received in 10 minutes, cloud instance will issue an alert.
|
||||
</Text>
|
||||
{cloudIsConnected && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
icon="external-link-alt"
|
||||
className={cx('block-button')}
|
||||
onClick={() => handleLinkClick('fillmewithcorrectlink')}
|
||||
>
|
||||
Configure escalations in Cloud OnCall
|
||||
</Button>
|
||||
)}
|
||||
</VerticalGroup>
|
||||
</Block>
|
||||
|
||||
<Block bordered withBackground className={cx('info-block')}>
|
||||
<VerticalGroup>
|
||||
<Text.Title level={4}>
|
||||
<Icon name="bell" className={cx('block-icon')} size="lg" /> SMS and phone call notifications
|
||||
</Text.Title>
|
||||
{cloudIsConnected ? (
|
||||
<div style={{ width: '100%' }}>
|
||||
<Text type="secondary">
|
||||
{
|
||||
'Ask your users to sign up in Grafana Cloud, verify phone number and feel free to set up SMS & phone call notificaitons in personal settings!'
|
||||
}
|
||||
</Text>
|
||||
|
||||
<GTable
|
||||
className={cx('user-table')}
|
||||
rowClassName={cx('user-row')}
|
||||
showHeader={false}
|
||||
emptyText={data ? 'No variables found' : 'Loading...'}
|
||||
title={() => (
|
||||
<div className={cx('table-title')}>
|
||||
<HorizontalGroup justify="space-between">
|
||||
<Text type="secondary">
|
||||
{/* {count ? count : 0} */}
|
||||
{`3 users matched between OSS and Cloud OnCall`}
|
||||
</Text>
|
||||
<Button variant="primary" onClick={syncUsers} icon="sync">
|
||||
Sync users
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
</div>
|
||||
)}
|
||||
rowKey="id"
|
||||
// @ts-ignore
|
||||
columns={columns}
|
||||
data={data}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<Text type="secondary">Users matched between OSS and Cloud OnCall currently unavialable.</Text>
|
||||
)}
|
||||
</VerticalGroup>
|
||||
</Block>
|
||||
</VerticalGroup>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default withMobXProviderContext(CloudPage);
|
||||
|
|
|
|||
|
|
@ -98,6 +98,13 @@
|
|||
"path": "/a/grafana-oncall-app/?page=outgoing_webhooks",
|
||||
"role": "Viewer",
|
||||
"addToNav": true
|
||||
},
|
||||
{
|
||||
"type": "page",
|
||||
"name": "Cloud",
|
||||
"path": "/a/grafana-oncall-app/?page=cloud",
|
||||
"role": "Editor",
|
||||
"addToNav": true
|
||||
}
|
||||
],
|
||||
"routes": [
|
||||
|
|
|
|||
|
|
@ -3,4 +3,5 @@ export enum AppFeature {
|
|||
Telegram = 'telegram',
|
||||
LiveSettings = 'live_settings',
|
||||
MobileApp = 'mobile_app',
|
||||
CloudNotifications = 'grafana_cloud_notifications',
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue