endpoints WIP

This commit is contained in:
Yulia Shanyrova 2022-06-07 11:23:41 +02:00
parent 273dbd2ec5
commit 7d0f8d4177
8 changed files with 174 additions and 46 deletions

View file

@ -11,6 +11,7 @@ import Text from 'components/Text/Text';
import WithConfirm from 'components/WithConfirm/WithConfirm';
import { User as UserType } from 'models/user/user.types';
import { WithStoreProps } from 'state/types';
import { useStore } from 'state/useStore';
import { withMobXProviderContext } from 'state/withStore';
import styles from './CloudPhoneSettings.module.css';
@ -20,21 +21,22 @@ const cx = cn.bind(styles);
interface CloudPhoneSettingsProps extends WithStoreProps {}
const CloudPhoneSettings = (props: CloudPhoneSettingsProps) => {
const store = useStore();
const [isAccountMatched, setIsAccountMatched] = useState<boolean>(true);
const [isPhoneVerified, setIsPhoneVerified] = useState<boolean>(true);
const signUpGrafanaCloud = () => {
console.log('Sign UP');
};
const handleLinkClick = (link: string) => {
getLocationSrv().update({ partial: false, path: link });
const handleLinkClick = () => {
store.cloudStore.syncCloudUser(store.userStore.currentUserPk);
};
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('fillmewithcorrectlink')}>
<Button variant="secondary" icon="sync" onClick={handleLinkClick}>
Update
</Button>
</HorizontalGroup>

View file

@ -0,0 +1,8 @@
<svg width="15px" height="15px" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M0.877075 7.49988C0.877075 3.84219 3.84222 0.877045 7.49991 0.877045C11.1576 0.877045 14.1227 3.84219 14.1227 7.49988C14.1227 11.1575 11.1576 14.1227 7.49991 14.1227C3.84222 14.1227 0.877075 11.1575 0.877075 7.49988ZM7.49991 1.82704C4.36689 1.82704 1.82708 4.36686 1.82708 7.49988C1.82708 10.6329 4.36689 13.1727 7.49991 13.1727C10.6329 13.1727 13.1727 10.6329 13.1727 7.49988C13.1727 4.36686 10.6329 1.82704 7.49991 1.82704ZM9.85358 5.14644C10.0488 5.3417 10.0488 5.65829 9.85358 5.85355L8.20713 7.49999L9.85358 9.14644C10.0488 9.3417 10.0488 9.65829 9.85358 9.85355C9.65832 10.0488 9.34173 10.0488 9.14647 9.85355L7.50002 8.2071L5.85358 9.85355C5.65832 10.0488 5.34173 10.0488 5.14647 9.85355C4.95121 9.65829 4.95121 9.3417 5.14647 9.14644L6.79292 7.49999L5.14647 5.85355C4.95121 5.65829 4.95121 5.3417 5.14647 5.14644C5.34173 4.95118 5.65832 4.95118 5.85358 5.14644L7.50002 6.79289L9.14647 5.14644C9.34173 4.95118 9.65832 4.95118 9.85358 5.14644Z"
fill="currentColor"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.1"
width="16"
height="16"
id="Capa_1"
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
fill="currentColor"
viewBox="0 0 490.4 490.4"
>
<g>
<path
d="M222.5,453.7c6.1,6.1,14.3,9.5,22.9,9.5c8.5,0,16.9-3.5,22.9-9.5L448,274c27.3-27.3,42.3-63.6,42.4-102.1
c0-38.6-15-74.9-42.3-102.2S384.6,27.4,346,27.4c-37.9,0-73.6,14.5-100.7,40.9c-27.2-26.5-63-41.1-101-41.1
c-38.5,0-74.7,15-102,42.2C15,96.7,0,133,0,171.6c0,38.5,15.1,74.8,42.4,102.1L222.5,453.7z M59.7,86.8
c22.6-22.6,52.7-35.1,84.7-35.1s62.2,12.5,84.9,35.2l7.4,7.4c2.3,2.3,5.4,3.6,8.7,3.6l0,0c3.2,0,6.4-1.3,8.7-3.6l7.2-7.2
c22.7-22.7,52.8-35.2,84.9-35.2c32,0,62.1,12.5,84.7,35.1c22.7,22.7,35.1,52.8,35.1,84.8s-12.5,62.1-35.2,84.8L251,436.4
c-2.9,2.9-8.2,2.9-11.2,0l-180-180c-22.7-22.7-35.2-52.8-35.2-84.8C24.6,139.6,37.1,109.5,59.7,86.8z"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -168,6 +168,42 @@ export const HeartRedIcon = (props: IconProps) => (
</svg>
);
export const HeartIcon = (props: IconProps) => (
<svg
version="1.1"
width="16"
height="16"
id="Capa_1"
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
fill="currentColor"
viewBox="0 0 490.4 490.4"
>
<g>
<path
d="M222.5,453.7c6.1,6.1,14.3,9.5,22.9,9.5c8.5,0,16.9-3.5,22.9-9.5L448,274c27.3-27.3,42.3-63.6,42.4-102.1
c0-38.6-15-74.9-42.3-102.2S384.6,27.4,346,27.4c-37.9,0-73.6,14.5-100.7,40.9c-27.2-26.5-63-41.1-101-41.1
c-38.5,0-74.7,15-102,42.2C15,96.7,0,133,0,171.6c0,38.5,15.1,74.8,42.4,102.1L222.5,453.7z M59.7,86.8
c22.6-22.6,52.7-35.1,84.7-35.1s62.2,12.5,84.9,35.2l7.4,7.4c2.3,2.3,5.4,3.6,8.7,3.6l0,0c3.2,0,6.4-1.3,8.7-3.6l7.2-7.2
c22.7-22.7,52.8-35.2,84.9-35.2c32,0,62.1,12.5,84.7,35.1c22.7,22.7,35.1,52.8,35.1,84.8s-12.5,62.1-35.2,84.8L251,436.4
c-2.9,2.9-8.2,2.9-11.2,0l-180-180c-22.7-22.7-35.2-52.8-35.2-84.8C24.6,139.6,37.1,109.5,59.7,86.8z"
/>
</g>
</svg>
);
export const CrossCircleIcon = (props: IconProps) => (
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M0.877075 7.49988C0.877075 3.84219 3.84222 0.877045 7.49991 0.877045C11.1576 0.877045 14.1227 3.84219 14.1227 7.49988C14.1227 11.1575 11.1576 14.1227 7.49991 14.1227C3.84222 14.1227 0.877075 11.1575 0.877075 7.49988ZM7.49991 1.82704C4.36689 1.82704 1.82708 4.36686 1.82708 7.49988C1.82708 10.6329 4.36689 13.1727 7.49991 13.1727C10.6329 13.1727 13.1727 10.6329 13.1727 7.49988C13.1727 4.36686 10.6329 1.82704 7.49991 1.82704ZM9.85358 5.14644C10.0488 5.3417 10.0488 5.65829 9.85358 5.85355L8.20713 7.49999L9.85358 9.14644C10.0488 9.3417 10.0488 9.65829 9.85358 9.85355C9.65832 10.0488 9.34173 10.0488 9.14647 9.85355L7.50002 8.2071L5.85358 9.85355C5.65832 10.0488 5.34173 10.0488 5.14647 9.85355C4.95121 9.65829 4.95121 9.3417 5.14647 9.14644L6.79292 7.49999L5.14647 5.85355C4.95121 5.65829 4.95121 5.3417 5.14647 5.14644C5.34173 4.95118 5.65832 4.95118 5.85358 5.14644L7.50002 6.79289L9.14647 5.14644C9.34173 4.95118 9.65832 4.95118 9.85358 5.14644Z"
fill="currentColor"
/>
</svg>
);
export const GrafanaIcon = (props: IconProps) => (
<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path

View file

@ -3,6 +3,7 @@ import { action, computed, observable } from 'mobx';
import BaseStore from 'models/base_store';
import { NotificationPolicyType } from 'models/notification_policy';
import { User } from 'models/user/user.types';
import { makeRequest } from 'network';
import { Mixpanel } from 'services/mixpanel';
import { RootStore } from 'state';
@ -24,11 +25,9 @@ export class CloudStore extends BaseStore {
}
@action
async updateItems(f: any = { searchTerm: '' }, page = 1) {
const filters = typeof f === 'string' ? { searchTerm: f } : f; // for GSelect compatibility
const { searchTerm: search } = filters;
async updateItems(page = 1) {
const { count, results } = await makeRequest(this.path, {
params: { search, page },
params: { page },
});
this.items = {
@ -51,14 +50,16 @@ export class CloudStore extends BaseStore {
getSearchResult() {
return {
count: this.searchResult.count,
results:
this.searchResult.results &&
this.searchResult.results.map((cloud_user_id: Cloud['id']) => this.items?.[cloud_user_id]),
results: this.searchResult.results && this.searchResult.results.map((id: Cloud['id']) => this.items?.[id]),
};
}
async syncCloudUsers() {
return await makeRequest(`${this.path}sync_with_cloud`, { method: 'POST' });
return await makeRequest(`${this.path}`, { method: 'POST' });
}
async syncCloudUser(id: string) {
return await makeRequest(`${this.path}${id}/sync_with_cloud/`, { method: 'POST' });
}
async getCloudConnectionStatus() {
@ -66,9 +67,7 @@ export class CloudStore extends BaseStore {
}
@action
async connectToCloud(token: string) {
return await makeRequest(`/live_settings/`, { method: 'PUT', params: { token } });
}
async connectToCloud(token: string) {}
@action
async disconnectToCloud() {

View file

@ -1,6 +1,9 @@
export interface Cloud {
id: string;
username: string;
cloud_sync_status?: number;
link?: string;
email: string;
cloud_data?: {
status?: number;
link?: string;
};
}

View file

@ -1,5 +1,7 @@
.info-block {
width: 70%;
min-width: 1100px;
padding: 24px;
}
.warning-message {
@ -19,6 +21,10 @@
width: 100%;
}
.user-row {
height: 32px;
}
.cloud-page-title {
margin-top: 24px;
}
@ -26,3 +32,19 @@
.cloud-oncall-name {
color: #f55f3e;
}
.block-icon {
color: var(--secondary-text-color);
}
.block-button {
margin-top: 24px;
}
.table-title {
margin-bottom: 16px;
}
.table-button {
float: right;
}

View file

@ -9,7 +9,7 @@ import GTable from 'components/GTable/GTable';
import PluginLink from 'components/PluginLink/PluginLink';
import Text from 'components/Text/Text';
import WithConfirm from 'components/WithConfirm/WithConfirm';
import { HeartGreenIcon, HeartRedIcon } from 'icons';
import { CrossCircleIcon, HeartIcon } from 'icons';
import { Cloud } from 'models/cloud/cloud.types';
import { WithStoreProps } from 'state/types';
import { useStore } from 'state/useStore';
@ -29,16 +29,19 @@ const CloudPage = (props: CloudPageProps) => {
useEffect(() => {
store.cloudStore.updateItems();
store.cloudStore.getCloudConnectionStatus().then((cloudStatus) => {
setCloudIsConnected(cloudStatus.cloud_connection_status);
});
}, []);
const usersCount = 3;
const data = [
{ id: 'yshanyrova', username: 'y.shanyrova@grafana.com', cloud_sync_status: 2, link: '/test/abc' },
{ id: 'amixradmin', username: 'amixr-admin@grafana.com', cloud_sync_status: 1, link: '/test/qwerty' },
{ id: 'amixr', username: 'amixr@grafana.com', cloud_sync_status: undefined, link: undefined },
{ 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 data = store.cloudStore.getSearchResult();
// const { count, results } = store.cloudStore.getSearchResult();
const handleChangeCloudApiKey = useCallback((e) => {
setCloudApiKey(e.target.value);
}, []);
@ -56,11 +59,12 @@ const CloudPage = (props: CloudPageProps) => {
const connectToCloud = () => {
setCloudIsConnected(true);
setShowConfirmationModal(false);
// store.cloudStore.update('')
store.cloudStore.connectToCloud(cloudApiKey);
};
const syncUsers = () => {
console.log('Sync Users');
store.cloudStore.syncCloudUsers();
};
const handleLinkClick = (link: string) => {
@ -68,18 +72,30 @@ const CloudPage = (props: CloudPageProps) => {
};
const renderButtons = (user: Cloud) => {
switch (user.cloud_sync_status) {
switch (user?.cloud_data?.status) {
case 0:
return null;
case 1:
return (
<Button variant="secondary" icon="external-link-alt" onClick={() => handleLinkClick(user.link)}>
<Button
variant="secondary"
icon="external-link-alt"
size="sm"
className={cx('table-button')}
onClick={() => handleLinkClick(user?.cloud_data?.link)}
>
Configure notifications
</Button>
);
case 2:
return (
<Button variant="secondary" icon="external-link-alt" onClick={() => handleLinkClick(user.link)}>
<Button
variant="secondary"
icon="external-link-alt"
size="sm"
className={cx('table-button')}
onClick={() => handleLinkClick(user?.cloud_data?.link)}
>
Open profile in Cloud
</Button>
);
@ -89,7 +105,7 @@ const CloudPage = (props: CloudPageProps) => {
};
const renderStatus = (user: Cloud) => {
switch (user.cloud_sync_status) {
switch (user?.cloud_data?.status) {
case 0:
return <Text className={cx('error-message')}>User not found in the Grafana Cloud</Text>;
case 1:
@ -103,41 +119,49 @@ const CloudPage = (props: CloudPageProps) => {
};
const renderStatusIcon = (user: Cloud) => {
switch (user.cloud_sync_status) {
switch (user?.cloud_data?.status) {
case 0:
return <Icon className={cx('error-message')} name="times" />;
return (
<span className={cx('error-message')}>
<CrossCircleIcon />
</span>
);
case 1:
return <Icon className={cx('success-message')} name="check-circle" />;
case 2:
return <Icon className={cx('warning-message')} name="exclamation-triangle" />;
default:
return <Icon className={cx('error-message')} name="times" />;
return (
<span className={cx('error-message')}>
<CrossCircleIcon />
</span>
);
}
};
const renderEmail = (user: Cloud) => {
return <Text type="primary">{user.username}</Text>;
return <Text type="primary">{user.email}</Text>;
};
const columns = [
{
width: '5%',
width: '2%',
render: renderStatusIcon,
key: 'statusIcon',
},
{
width: '30%',
width: '28%',
render: renderEmail,
key: 'email',
},
{
width: '35%',
width: '50%',
render: renderStatus,
key: 'status',
},
{
width: '30%',
width: '20%',
render: renderButtons,
key: 'buttons',
align: 'actions',
@ -154,12 +178,12 @@ const CloudPage = (props: CloudPageProps) => {
{cloudIsConnected ? (
<VerticalGroup>
<Text.Title level={4}>
<Icon name="check" className={cx('heart-icon')} /> Cloud OnCall API key
<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">
<Button variant="destructive" onClick={disconnectCloudOncall} size="md" className={cx('block-button')}>
Disconnect
</Button>
</WithConfirm>
@ -167,7 +191,7 @@ const CloudPage = (props: CloudPageProps) => {
) : (
<VerticalGroup>
<Text.Title level={4}>
<Icon name="sync" className={cx('heart-icon')} /> Cloud OnCall API key
<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} />
@ -199,7 +223,10 @@ const CloudPage = (props: CloudPageProps) => {
<Block bordered withBackground className={cx('info-block')}>
<VerticalGroup>
<Text.Title level={4}>
<Icon name="heart-break" className={cx('heart-icon')} /> Monitor cloud instance with heartbeat
<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
@ -209,6 +236,7 @@ const CloudPage = (props: CloudPageProps) => {
<Button
variant="secondary"
icon="external-link-alt"
className={cx('block-button')}
onClick={() => handleLinkClick('fillmewithcorrectlink')}
>
Configure escalations in Cloud OnCall
@ -220,7 +248,7 @@ const CloudPage = (props: CloudPageProps) => {
<Block bordered withBackground className={cx('info-block')}>
<VerticalGroup>
<Text.Title level={4}>
<Icon name="bell" /> SMS and phone call notifications
<Icon name="bell" className={cx('block-icon')} size="lg" /> SMS and phone call notifications
</Text.Title>
{cloudIsConnected ? (
<div style={{ width: '100%' }}>
@ -233,14 +261,20 @@ const CloudPage = (props: CloudPageProps) => {
<GTable
className={cx('user-table')}
rowClassName={cx('user-row')}
showHeader={false}
emptyText={data ? 'No variables found' : 'Loading...'}
title={() => (
<HorizontalGroup justify="space-between">
<Text type="secondary">{`${usersCount} users matched between OSS and Cloud OnCall`}</Text>
<Button variant="primary" onClick={syncUsers} icon="sync">
Sync users
</Button>
</HorizontalGroup>
<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