Merge branch '1223-oncall-cloud-tab' into grafana_cloud_notifications
This commit is contained in:
commit
e263e3f270
14 changed files with 488 additions and 7 deletions
|
|
@ -117,7 +117,7 @@ steps:
|
|||
- name: Build and Push Engine Docker Image Backend to GCR
|
||||
image: plugins/docker
|
||||
settings:
|
||||
repo: us.gcr.io/kubernetes-dev/oncall-engine
|
||||
repo: us.gcr.io/kubernetes-dev/oncall
|
||||
dockerfile: engine/Dockerfile
|
||||
context: engine/
|
||||
config:
|
||||
|
|
|
|||
|
|
@ -100,9 +100,11 @@ export const Root = observer((props: AppRootProps) => {
|
|||
const style = document.createElement('style');
|
||||
document.head.appendChild(style);
|
||||
const index = style.sheet.insertRule('.page-body {max-width: unset !important}');
|
||||
const index2 = style.sheet.insertRule('.page-container {max-width: unset !important}');
|
||||
|
||||
return () => {
|
||||
style.sheet.removeRule(index);
|
||||
style.sheet.removeRule(index2);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ import cn from 'classnames/bind';
|
|||
import { observer } from 'mobx-react';
|
||||
import { useMediaQuery } from 'react-responsive';
|
||||
|
||||
import { Tabs, TabsContent } from 'containers/UserSettings/parts';
|
||||
import { User as UserType } from 'models/user/user.types';
|
||||
import { AppFeature } from 'state/features';
|
||||
import { useStore } from 'state/useStore';
|
||||
|
||||
import { UserSettingsTab } from './UserSettings.types';
|
||||
import { Tabs, TabsContent } from './parts';
|
||||
|
||||
import styles from './UserSettings.module.css';
|
||||
|
||||
|
|
@ -58,7 +58,8 @@ const UserSettings = observer((props: UserFormProps) => {
|
|||
setActiveTab(tab);
|
||||
}, []);
|
||||
|
||||
const isModalWide = activeTab === UserSettingsTab.UserInfo && isDesktopOrLaptop;
|
||||
const isModalWide =
|
||||
(activeTab === UserSettingsTab.UserInfo && isDesktopOrLaptop) || activeTab === UserSettingsTab.PhoneVerification;
|
||||
|
||||
const [showNotificationSettingsTab, showSlackConnectionTab, showTelegramConnectionTab, showMobileAppVerificationTab] =
|
||||
[
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
|
||||
import { Tab, TabContent, TabsBar } from '@grafana/ui';
|
||||
import cn from 'classnames/bind';
|
||||
|
|
@ -7,6 +7,7 @@ import Block from 'components/GBlock/Block';
|
|||
import MobileAppVerification from 'containers/MobileAppVerification/MobileAppVerification';
|
||||
import { UserSettingsTab } from 'containers/UserSettings/UserSettings.types';
|
||||
import { SlackTab } from 'containers/UserSettings/parts/tabs//SlackTab/SlackTab';
|
||||
import CloudPhoneSettings from 'containers/UserSettings/parts/tabs/CloudPhoneSettings/CloudPhoneSettings';
|
||||
import { NotificationSettingsTab } from 'containers/UserSettings/parts/tabs/NotificationSettingsTab';
|
||||
import PhoneVerification from 'containers/UserSettings/parts/tabs/PhoneVerification/PhoneVerification';
|
||||
import TelegramInfo from 'containers/UserSettings/parts/tabs/TelegramInfo/TelegramInfo';
|
||||
|
|
@ -105,6 +106,7 @@ export const TabsContent = (props: TabsContentProps) => {
|
|||
|
||||
const store = useStore();
|
||||
const { userStore } = store;
|
||||
const [isPhoneEnabled, setIsPhoneEnabled] = useState<boolean>(false);
|
||||
|
||||
const storeUser = userStore.items[id];
|
||||
|
||||
|
|
@ -124,9 +126,12 @@ export const TabsContent = (props: TabsContentProps) => {
|
|||
<UserInfoTab id={id} onTabChange={onTabChange} />
|
||||
))}
|
||||
{activeTab === UserSettingsTab.NotificationSettings && <NotificationSettingsTab id={id} />}
|
||||
{activeTab === UserSettingsTab.PhoneVerification && (
|
||||
<PhoneVerification userPk={id} phone={storeUser.unverified_phone_number || '+'} />
|
||||
)}
|
||||
{activeTab === UserSettingsTab.PhoneVerification &&
|
||||
(isPhoneEnabled ? (
|
||||
<CloudPhoneSettings />
|
||||
) : (
|
||||
<PhoneVerification userPk={id} phone={storeUser.unverified_phone_number || '+'} />
|
||||
))}
|
||||
{activeTab === UserSettingsTab.MobileAppVerification && (
|
||||
<MobileAppVerification userPk={id} phone={storeUser.unverified_phone_number || '+'} />
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
.test {
|
||||
color: grey;
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
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 cn from 'classnames/bind';
|
||||
|
||||
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 WithConfirm from 'components/WithConfirm/WithConfirm';
|
||||
import { User as UserType } from 'models/user/user.types';
|
||||
import { WithStoreProps } from 'state/types';
|
||||
import { withMobXProviderContext } from 'state/withStore';
|
||||
|
||||
import styles from './CloudPhoneSettings.module.css';
|
||||
|
||||
const cx = cn.bind(styles);
|
||||
|
||||
interface CloudPhoneSettingsProps extends WithStoreProps {}
|
||||
|
||||
const CloudPhoneSettings = (props: CloudPhoneSettingsProps) => {
|
||||
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 });
|
||||
};
|
||||
|
||||
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')}>
|
||||
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>
|
||||
)}
|
||||
</VerticalGroup>
|
||||
);
|
||||
};
|
||||
|
||||
export default withMobXProviderContext(CloudPhoneSettings);
|
||||
77
grafana-plugin/src/models/cloud/cloud.ts
Normal file
77
grafana-plugin/src/models/cloud/cloud.ts
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import { get } from 'lodash-es';
|
||||
import { action, computed, observable } from 'mobx';
|
||||
|
||||
import BaseStore from 'models/base_store';
|
||||
import { NotificationPolicyType } from 'models/notification_policy';
|
||||
import { makeRequest } from 'network';
|
||||
import { Mixpanel } from 'services/mixpanel';
|
||||
import { RootStore } from 'state';
|
||||
import { move } from 'state/helpers';
|
||||
|
||||
import { Cloud } from './cloud.types';
|
||||
|
||||
export class CloudStore extends BaseStore {
|
||||
@observable.shallow
|
||||
searchResult: { count?: number; results?: Array<Cloud['id']> } = {};
|
||||
|
||||
@observable.shallow
|
||||
items: { [id: string]: Cloud } = {};
|
||||
|
||||
constructor(rootStore: RootStore) {
|
||||
super(rootStore);
|
||||
|
||||
this.path = '/cloud_users/';
|
||||
}
|
||||
|
||||
@action
|
||||
async updateItems(f: any = { searchTerm: '' }, page = 1) {
|
||||
const filters = typeof f === 'string' ? { searchTerm: f } : f; // for GSelect compatibility
|
||||
const { searchTerm: search } = filters;
|
||||
const { count, results } = await makeRequest(this.path, {
|
||||
params: { search, page },
|
||||
});
|
||||
|
||||
this.items = {
|
||||
...this.items,
|
||||
...results.reduce(
|
||||
(acc: { [key: number]: Cloud }, item: Cloud) => ({
|
||||
...acc,
|
||||
[item.id]: item,
|
||||
}),
|
||||
{}
|
||||
),
|
||||
};
|
||||
|
||||
this.searchResult = {
|
||||
count,
|
||||
results: results.map((item: Cloud) => item.id),
|
||||
};
|
||||
}
|
||||
|
||||
getSearchResult() {
|
||||
return {
|
||||
count: this.searchResult.count,
|
||||
results:
|
||||
this.searchResult.results &&
|
||||
this.searchResult.results.map((cloud_user_id: Cloud['id']) => this.items?.[cloud_user_id]),
|
||||
};
|
||||
}
|
||||
|
||||
async syncCloudUsers() {
|
||||
return await makeRequest(`${this.path}sync_with_cloud`, { method: 'POST' });
|
||||
}
|
||||
|
||||
async getCloudConnectionStatus() {
|
||||
return await makeRequest(`/cloud_connection/`, { method: 'GET' });
|
||||
}
|
||||
|
||||
@action
|
||||
async connectToCloud(token: string) {
|
||||
return await makeRequest(`/live_settings/`, { method: 'PUT', params: { token } });
|
||||
}
|
||||
|
||||
@action
|
||||
async disconnectToCloud() {
|
||||
return await makeRequest(`/live_settings/`, { method: 'DELETE' });
|
||||
}
|
||||
}
|
||||
6
grafana-plugin/src/models/cloud/cloud.types.ts
Normal file
6
grafana-plugin/src/models/cloud/cloud.types.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export interface Cloud {
|
||||
id: string;
|
||||
username: string;
|
||||
cloud_sync_status?: number;
|
||||
link?: string;
|
||||
}
|
||||
|
|
@ -50,4 +50,6 @@ export interface User {
|
|||
permissions: UserAction[];
|
||||
trigger_video_call?: boolean;
|
||||
export_url?: string;
|
||||
status?: number;
|
||||
link?: string;
|
||||
}
|
||||
|
|
|
|||
28
grafana-plugin/src/pages/cloud/CloudPage.module.css
Normal file
28
grafana-plugin/src/pages/cloud/CloudPage.module.css
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
.info-block {
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.warning-message {
|
||||
color: var(--warning-text-color);
|
||||
}
|
||||
|
||||
.success-message {
|
||||
color: var(--success-text-color);
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: var(--error-text-color);
|
||||
}
|
||||
|
||||
.user-table {
|
||||
margin-top: 24px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cloud-page-title {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.cloud-oncall-name {
|
||||
color: #f55f3e;
|
||||
}
|
||||
261
grafana-plugin/src/pages/cloud/CloudPage.tsx
Normal file
261
grafana-plugin/src/pages/cloud/CloudPage.tsx
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
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 cn from 'classnames/bind';
|
||||
|
||||
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 WithConfirm from 'components/WithConfirm/WithConfirm';
|
||||
import { HeartGreenIcon, HeartRedIcon } from 'icons';
|
||||
import { Cloud } from 'models/cloud/cloud.types';
|
||||
import { WithStoreProps } from 'state/types';
|
||||
import { useStore } from 'state/useStore';
|
||||
import { withMobXProviderContext } from 'state/withStore';
|
||||
|
||||
import styles from './CloudPage.module.css';
|
||||
|
||||
const cx = cn.bind(styles);
|
||||
|
||||
interface CloudPageProps extends WithStoreProps {}
|
||||
|
||||
const CloudPage = (props: CloudPageProps) => {
|
||||
const store = useStore();
|
||||
const [cloudApiKey, setCloudApiKey] = useState<string>('');
|
||||
const [cloudIsConnected, setCloudIsConnected] = useState<boolean>(true);
|
||||
const [showConfirmationModal, setShowConfirmationModal] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
store.cloudStore.updateItems();
|
||||
}, []);
|
||||
|
||||
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 },
|
||||
];
|
||||
|
||||
// const data = store.cloudStore.getSearchResult();
|
||||
const handleChangeCloudApiKey = useCallback((e) => {
|
||||
setCloudApiKey(e.target.value);
|
||||
}, []);
|
||||
|
||||
const saveKeyAndConnect = () => {
|
||||
setShowConfirmationModal(true);
|
||||
};
|
||||
|
||||
const disconnectCloudOncall = () => {
|
||||
console.log('disconnected');
|
||||
setCloudIsConnected(false);
|
||||
store.cloudStore.disconnectToCloud();
|
||||
};
|
||||
|
||||
const connectToCloud = () => {
|
||||
setCloudIsConnected(true);
|
||||
setShowConfirmationModal(false);
|
||||
store.cloudStore.connectToCloud(cloudApiKey);
|
||||
};
|
||||
|
||||
const syncUsers = () => {
|
||||
console.log('Sync Users');
|
||||
};
|
||||
|
||||
const handleLinkClick = (link: string) => {
|
||||
getLocationSrv().update({ partial: false, path: link });
|
||||
};
|
||||
|
||||
const renderButtons = (user: Cloud) => {
|
||||
switch (user.cloud_sync_status) {
|
||||
case 0:
|
||||
return null;
|
||||
case 1:
|
||||
return (
|
||||
<Button variant="secondary" icon="external-link-alt" onClick={() => handleLinkClick(user.link)}>
|
||||
Configure notifications
|
||||
</Button>
|
||||
);
|
||||
case 2:
|
||||
return (
|
||||
<Button variant="secondary" icon="external-link-alt" onClick={() => handleLinkClick(user.link)}>
|
||||
Open profile in Cloud
|
||||
</Button>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const renderStatus = (user: Cloud) => {
|
||||
switch (user.cloud_sync_status) {
|
||||
case 0:
|
||||
return <Text className={cx('error-message')}>User not found in the Grafana Cloud</Text>;
|
||||
case 1:
|
||||
return <Text type="success">Phone number verified</Text>;
|
||||
|
||||
case 2:
|
||||
return <Text type="warning">Phone number is not verified in Grafana Cloud</Text>;
|
||||
default:
|
||||
return <Text className={cx('error-message')}>User not found in Grafana Cloud</Text>;
|
||||
}
|
||||
};
|
||||
|
||||
const renderStatusIcon = (user: Cloud) => {
|
||||
switch (user.cloud_sync_status) {
|
||||
case 0:
|
||||
return <Icon className={cx('error-message')} name="times" />;
|
||||
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" />;
|
||||
}
|
||||
};
|
||||
|
||||
const renderEmail = (user: Cloud) => {
|
||||
return <Text type="primary">{user.username}</Text>;
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
width: '5%',
|
||||
render: renderStatusIcon,
|
||||
key: 'statusIcon',
|
||||
},
|
||||
{
|
||||
width: '30%',
|
||||
render: renderEmail,
|
||||
key: 'email',
|
||||
},
|
||||
{
|
||||
width: '35%',
|
||||
render: renderStatus,
|
||||
key: 'status',
|
||||
},
|
||||
{
|
||||
width: '30%',
|
||||
render: renderButtons,
|
||||
key: 'buttons',
|
||||
align: 'actions',
|
||||
},
|
||||
];
|
||||
|
||||
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('heart-icon')} /> 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">
|
||||
Disconnect
|
||||
</Button>
|
||||
</WithConfirm>
|
||||
</VerticalGroup>
|
||||
) : (
|
||||
<VerticalGroup>
|
||||
<Text.Title level={4}>
|
||||
<Icon name="sync" className={cx('heart-icon')} /> 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>
|
||||
|
||||
{showConfirmationModal && (
|
||||
<Modal
|
||||
isOpen
|
||||
title="Are you sure you want to connect to cloud?"
|
||||
onDismiss={() => setShowConfirmationModal(false)}
|
||||
>
|
||||
<HorizontalGroup>
|
||||
<Button variant="primary" onClick={connectToCloud}>
|
||||
Continue
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={() => setShowConfirmationModal(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
</Modal>
|
||||
)}
|
||||
|
||||
<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
|
||||
</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"
|
||||
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" /> 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')}
|
||||
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>
|
||||
)}
|
||||
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);
|
||||
|
|
@ -3,6 +3,7 @@ import React from 'react';
|
|||
import { AppRootProps } from '@grafana/data';
|
||||
|
||||
import ChatOpsPage from 'pages/chat-ops/ChatOps';
|
||||
import CloudPage from 'pages/cloud/CloudPage';
|
||||
import EscalationsChainsPage from 'pages/escalation-chains/EscalationChains';
|
||||
import IncidentPage2 from 'pages/incident/Incident';
|
||||
import IncidentsPage2 from 'pages/incidents/Incidents';
|
||||
|
|
@ -116,6 +117,12 @@ export const pages: PageDefinition[] = [
|
|||
text: 'Migrate From Amixr.IO',
|
||||
hideFromTabs: true,
|
||||
},
|
||||
{
|
||||
component: CloudPage,
|
||||
icon: 'cloud',
|
||||
id: 'cloud',
|
||||
text: 'Cloud',
|
||||
},
|
||||
{
|
||||
component: Test,
|
||||
icon: 'cog',
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { AlertReceiveChannel } from 'models/alert_receive_channel/alert_receive_
|
|||
import { AlertReceiveChannelFiltersStore } from 'models/alert_receive_channel_filters/alert_receive_channel_filters';
|
||||
import { AlertGroupStore } from 'models/alertgroup/alertgroup';
|
||||
import { ApiTokenStore } from 'models/api_token/api_token';
|
||||
import { CloudStore } from 'models/cloud/cloud';
|
||||
import { EscalationChainStore } from 'models/escalation_chain/escalation_chain';
|
||||
import { EscalationPolicyStore } from 'models/escalation_policy/escalation_policy';
|
||||
import { GlobalSettingStore } from 'models/global_setting/global_setting';
|
||||
|
|
@ -81,6 +82,7 @@ export class RootBaseStore {
|
|||
// --------------------------
|
||||
|
||||
userStore: UserStore = new UserStore(this);
|
||||
cloudStore: CloudStore = new CloudStore(this);
|
||||
grafanaTeamStore: GrafanaTeamStore = new GrafanaTeamStore(this);
|
||||
alertReceiveChannelStore: AlertReceiveChannelStore = new AlertReceiveChannelStore(this);
|
||||
outgoingWebhookStore: OutgoingWebhookStore = new OutgoingWebhookStore(this);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@
|
|||
--secondary-text-color: rgba(36, 41, 46, 0.75);
|
||||
--disabled-text-color: rgba(36, 41, 46, 0.5);
|
||||
--warning-text-color: #8a6c00;
|
||||
--success-text-color: rgb(10, 118, 78);
|
||||
--error-text-color: rgb(207, 14, 91);
|
||||
--primary-text-link: #1f62e0;
|
||||
--timeline-icon-background: rgba(70, 76, 84, 0);
|
||||
--timeline-icon-background-resolution-note: rgba(50, 116, 217, 0);
|
||||
|
|
@ -38,6 +40,8 @@
|
|||
--secondary-text-color: rgba(204, 204, 220, 0.65);
|
||||
--disabled-text-color: rgba(204, 204, 220, 0.4);
|
||||
--warning-text-color: #f8d06b;
|
||||
--success-text-color: rgb(108, 207, 142);
|
||||
--error-text-color: rgb(255, 82, 134);
|
||||
--primary-text-link: #6e9fff;
|
||||
--timeline-icon-background: rgba(70, 76, 84, 1);
|
||||
--timeline-icon-background-resolution-note: rgba(50, 116, 217, 1);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue