UID has been added to Integrations table, Integration page and routes (#2112)
# What this PR does UID has been added to Integrations table, Integration page and routes
This commit is contained in:
parent
0a78b99fd8
commit
ed7da1953f
8 changed files with 286 additions and 72 deletions
|
|
@ -0,0 +1,19 @@
|
|||
.hamburger-menu {
|
||||
cursor: pointer;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
||||
.hamburger-menu-withBackground {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
vertical-align: middle;
|
||||
justify-content: center;
|
||||
background-color: rgba(204, 204, 220, 0.16);
|
||||
border: 1px solid transparent;
|
||||
height: 32px;
|
||||
width: 30px;
|
||||
padding: 4px;
|
||||
cursor: pointer;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
import React, { useRef } from 'react';
|
||||
|
||||
import { Icon } from '@grafana/ui';
|
||||
import cn from 'classnames/bind';
|
||||
|
||||
import styles from './HamburgerMenu.module.css';
|
||||
|
||||
interface HamburgerMenuProps {
|
||||
openMenu: React.MouseEventHandler<HTMLElement>;
|
||||
listWidth: number;
|
||||
listBorder: number;
|
||||
withBackground?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const cx = cn.bind(styles);
|
||||
|
||||
const HamburgerMenu: React.FC<HamburgerMenuProps> = (props) => {
|
||||
const ref = useRef<HTMLDivElement>();
|
||||
const { openMenu, listBorder, listWidth, withBackground, className } = props;
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={withBackground ? cx('hamburger-menu-withBackground') : cx('hamburger-menu', className)}
|
||||
onClick={() => {
|
||||
const boundingRect = ref.current.getBoundingClientRect();
|
||||
|
||||
openMenu({
|
||||
pageX: boundingRect.right - listWidth + listBorder * 2,
|
||||
pageY: boundingRect.top + boundingRect.height,
|
||||
} as any);
|
||||
}}
|
||||
>
|
||||
<Icon size="sm" name="ellipsis-v" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HamburgerMenu;
|
||||
|
|
@ -6,3 +6,45 @@
|
|||
width: 700px;
|
||||
}
|
||||
}
|
||||
|
||||
.integrations-actionsList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 200px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.integrations-actionItem {
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
flex-shrink: 0;
|
||||
white-space: nowrap;
|
||||
border-left: 2px solid transparent;
|
||||
cursor: pointer;
|
||||
min-width: 84px;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-direction: row;
|
||||
|
||||
&:hover {
|
||||
background: var(--gray-9);
|
||||
}
|
||||
}
|
||||
|
||||
.hamburgerMenu-small {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
vertical-align: middle;
|
||||
justify-content: center;
|
||||
background-color: rgba(204, 204, 220, 0.16);
|
||||
color: var(--secondary-background);
|
||||
border: 1px solid transparent;
|
||||
height: 24px;
|
||||
width: 22px;
|
||||
padding: 4px;
|
||||
cursor: pointer;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,13 +14,16 @@ import {
|
|||
} from '@grafana/ui';
|
||||
import cn from 'classnames/bind';
|
||||
import { observer } from 'mobx-react';
|
||||
import CopyToClipboard from 'react-copy-to-clipboard';
|
||||
|
||||
import HamburgerMenu from 'components/HamburgerMenu/HamburgerMenu';
|
||||
import IntegrationBlock from 'components/Integrations/IntegrationBlock';
|
||||
import IntegrationBlockItem from 'components/Integrations/IntegrationBlockItem';
|
||||
import MonacoEditor from 'components/MonacoEditor/MonacoEditor';
|
||||
import PluginLink from 'components/PluginLink/PluginLink';
|
||||
import Text from 'components/Text/Text';
|
||||
import TooltipBadge from 'components/TooltipBadge/TooltipBadge';
|
||||
import { WithContextMenu } from 'components/WithContextMenu/WithContextMenu';
|
||||
import { ChatOpsConnectors } from 'containers/AlertRules/parts';
|
||||
import EscalationChainSteps from 'containers/EscalationChainSteps/EscalationChainSteps';
|
||||
import styles from 'containers/IntegrationContainers/ExpandedIntegrationRouteDisplay/ExpandedIntegrationRouteDisplay.module.scss';
|
||||
|
|
@ -33,6 +36,7 @@ import { EscalationChain } from 'models/escalation_chain/escalation_chain.types'
|
|||
import { MONACO_INPUT_HEIGHT_SMALL, MONACO_OPTIONS } from 'pages/integration_2/Integration2.config';
|
||||
import IntegrationHelper from 'pages/integration_2/Integration2.helper';
|
||||
import { useStore } from 'state/useStore';
|
||||
import { openNotification } from 'utils';
|
||||
import { UserActions } from 'utils/authorization';
|
||||
|
||||
const cx = cn.bind(styles);
|
||||
|
|
@ -341,11 +345,38 @@ export const RouteButtonsDisplay: React.FC<RouteButtonsDisplayProps> = ({
|
|||
)}
|
||||
|
||||
{!channelFilter.is_default && (
|
||||
<WithPermissionControlTooltip userAction={UserActions.IntegrationsWrite}>
|
||||
<Tooltip placement="top" content={'Delete'}>
|
||||
<Button variant={'secondary'} icon={'trash-alt'} size={'sm'} onClick={onDelete} />
|
||||
</Tooltip>
|
||||
</WithPermissionControlTooltip>
|
||||
<WithContextMenu
|
||||
renderMenuItems={() => (
|
||||
<div className={cx('integrations-actionsList')}>
|
||||
<CopyToClipboard text={channelFilter.id} onCopy={() => openNotification('Route ID is copied')}>
|
||||
<div className={cx('integrations-actionItem')}>
|
||||
<HorizontalGroup spacing={'xs'}>
|
||||
<Icon name="copy" />
|
||||
|
||||
<Text type="primary">UID: {channelFilter.id}</Text>
|
||||
</HorizontalGroup>
|
||||
</div>
|
||||
</CopyToClipboard>
|
||||
|
||||
<div className="thin-line-break" />
|
||||
|
||||
<WithPermissionControlTooltip key="delete" userAction={UserActions.IntegrationsWrite}>
|
||||
<div className={cx('integrations-actionItem')} onClick={onDelete}>
|
||||
<Text type="danger">
|
||||
<HorizontalGroup spacing={'xs'}>
|
||||
<Icon name="trash-alt" />
|
||||
<span>Delete Route</span>
|
||||
</HorizontalGroup>
|
||||
</Text>
|
||||
</div>
|
||||
</WithPermissionControlTooltip>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
{({ openMenu }) => (
|
||||
<HamburgerMenu openMenu={openMenu} listBorder={2} listWidth={200} className={cx('hamburgerMenu-small')} />
|
||||
)}
|
||||
</WithContextMenu>
|
||||
)}
|
||||
</HorizontalGroup>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ $LARGE-MARGIN: 24px;
|
|||
&__actionsList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 160px;
|
||||
width: 200px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
|
|
@ -85,22 +85,6 @@ $LARGE-MARGIN: 24px;
|
|||
}
|
||||
}
|
||||
|
||||
.hamburger-menu {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
vertical-align: middle;
|
||||
justify-content: center;
|
||||
background-color: rgba(204, 204, 220, 0.16);
|
||||
color: var(--secondary-background);
|
||||
border: 1px solid transparent;
|
||||
height: 32px;
|
||||
width: 30px;
|
||||
padding: 4px;
|
||||
cursor: pointer;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
||||
.loadingPlaceholder {
|
||||
margin-bottom: 0;
|
||||
margin-right: 4px;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useRef, useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import {
|
||||
Button,
|
||||
|
|
@ -23,6 +23,7 @@ import { RouteComponentProps, useHistory, withRouter } from 'react-router-dom';
|
|||
import { debounce } from 'throttle-debounce';
|
||||
|
||||
import { TemplateForEdit, templateForEdit } from 'components/AlertTemplates/AlertTemplatesForm.config';
|
||||
import HamburgerMenu from 'components/HamburgerMenu/HamburgerMenu';
|
||||
import IntegrationCollapsibleTreeView, {
|
||||
IntegrationCollapsibleItem,
|
||||
} from 'components/IntegrationCollapsibleTreeView/IntegrationCollapsibleTreeView';
|
||||
|
|
@ -83,7 +84,7 @@ interface Integration2State extends PageBaseState {
|
|||
isAddingRoute: boolean;
|
||||
}
|
||||
|
||||
const ACTIONS_LIST_WIDTH = 160;
|
||||
const ACTIONS_LIST_WIDTH = 200;
|
||||
const ACTIONS_LIST_BORDER = 2;
|
||||
const NEW_ROUTE_DEFAULT = '{{ (payload.severity == "foo" and "bar" in payload.region) or True }}';
|
||||
|
||||
|
|
@ -586,27 +587,6 @@ const DemoNotification: React.FC = () => {
|
|||
);
|
||||
};
|
||||
|
||||
const HamburgerMenu: React.FC<{ openMenu: React.MouseEventHandler<HTMLElement> }> = ({ openMenu }) => {
|
||||
const ref = useRef<HTMLDivElement>();
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cx('hamburger-menu')}
|
||||
onClick={() => {
|
||||
const boundingRect = ref.current.getBoundingClientRect();
|
||||
|
||||
openMenu({
|
||||
pageX: boundingRect.right - ACTIONS_LIST_WIDTH + ACTIONS_LIST_BORDER * 2,
|
||||
pageY: boundingRect.top + boundingRect.height,
|
||||
} as any);
|
||||
}}
|
||||
>
|
||||
<Icon size="sm" name="ellipsis-v" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface IntegrationSendDemoPayloadModalProps {
|
||||
isOpen: boolean;
|
||||
alertReceiveChannel: AlertReceiveChannel;
|
||||
|
|
@ -839,6 +819,19 @@ const IntegrationActions: React.FC<IntegrationActionsProps> = ({ alertReceiveCha
|
|||
</WithPermissionControlTooltip>
|
||||
)}
|
||||
|
||||
<CopyToClipboard
|
||||
text={alertReceiveChannel.id}
|
||||
onCopy={() => openNotification('Integration ID is copied')}
|
||||
>
|
||||
<div className={cx('integration__actionItem')}>
|
||||
<HorizontalGroup spacing={'xs'}>
|
||||
<Icon name="copy" />
|
||||
|
||||
<Text type="primary">UID: {alertReceiveChannel.id}</Text>
|
||||
</HorizontalGroup>
|
||||
</div>
|
||||
</CopyToClipboard>
|
||||
|
||||
<div className="thin-line-break" />
|
||||
|
||||
<WithPermissionControlTooltip userAction={UserActions.IntegrationsWrite}>
|
||||
|
|
@ -859,6 +852,7 @@ const IntegrationActions: React.FC<IntegrationActionsProps> = ({ alertReceiveCha
|
|||
confirmText: 'Delete',
|
||||
});
|
||||
}}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
<Text type="danger">
|
||||
<HorizontalGroup spacing={'xs'}>
|
||||
|
|
@ -872,7 +866,14 @@ const IntegrationActions: React.FC<IntegrationActionsProps> = ({ alertReceiveCha
|
|||
</div>
|
||||
)}
|
||||
>
|
||||
{({ openMenu }) => <HamburgerMenu openMenu={openMenu} />}
|
||||
{({ openMenu }) => (
|
||||
<HamburgerMenu
|
||||
openMenu={openMenu}
|
||||
listBorder={ACTIONS_LIST_BORDER}
|
||||
listWidth={ACTIONS_LIST_WIDTH}
|
||||
withBackground
|
||||
/>
|
||||
)}
|
||||
</WithContextMenu>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -19,3 +19,29 @@
|
|||
padding: 4px 10px;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.integrations-actionsList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 200px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.integrations-actionItem {
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
flex-shrink: 0;
|
||||
white-space: nowrap;
|
||||
border-left: 2px solid transparent;
|
||||
cursor: pointer;
|
||||
min-width: 84px;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-direction: row;
|
||||
|
||||
&:hover {
|
||||
background: var(--gray-9);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,15 @@
|
|||
import React from 'react';
|
||||
|
||||
import { HorizontalGroup, Button, IconButton, VerticalGroup } from '@grafana/ui';
|
||||
import { HorizontalGroup, Button, VerticalGroup, Icon, ConfirmModal } from '@grafana/ui';
|
||||
import cn from 'classnames/bind';
|
||||
import { debounce } from 'lodash-es';
|
||||
import { observer } from 'mobx-react';
|
||||
import CopyToClipboard from 'react-copy-to-clipboard';
|
||||
import Emoji from 'react-emoji-render';
|
||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
|
||||
import GTable from 'components/GTable/GTable';
|
||||
import HamburgerMenu from 'components/HamburgerMenu/HamburgerMenu';
|
||||
import IntegrationLogo from 'components/IntegrationLogo/IntegrationLogo';
|
||||
import { Filters } from 'components/IntegrationsFilters/IntegrationsFilters';
|
||||
import { PageBaseState } from 'components/PageErrorHandlingWrapper/PageErrorHandlingWrapper';
|
||||
|
|
@ -18,7 +20,7 @@ import {
|
|||
import PluginLink from 'components/PluginLink/PluginLink';
|
||||
import Text from 'components/Text/Text';
|
||||
import TooltipBadge from 'components/TooltipBadge/TooltipBadge';
|
||||
import WithConfirm from 'components/WithConfirm/WithConfirm';
|
||||
import { WithContextMenu } from 'components/WithContextMenu/WithContextMenu';
|
||||
import IntegrationForm2 from 'containers/IntegrationForm/IntegrationForm2';
|
||||
import RemoteFilters from 'containers/RemoteFilters/RemoteFilters';
|
||||
import TeamName from 'containers/TeamName/TeamName';
|
||||
|
|
@ -28,6 +30,7 @@ import { AlertReceiveChannel, MaintenanceMode } from 'models/alert_receive_chann
|
|||
import IntegrationHelper from 'pages/integration_2/Integration2.helper';
|
||||
import { PageProps, WithStoreProps } from 'state/types';
|
||||
import { withMobXProviderContext } from 'state/withStore';
|
||||
import { openNotification } from 'utils';
|
||||
import LocationHelper from 'utils/LocationHelper';
|
||||
import { UserActions } from 'utils/authorization';
|
||||
|
||||
|
|
@ -37,11 +40,23 @@ const cx = cn.bind(styles);
|
|||
const FILTERS_DEBOUNCE_MS = 500;
|
||||
const ITEMS_PER_PAGE = 15;
|
||||
const MAX_LINE_LENGTH = 40;
|
||||
const ACTIONS_LIST_WIDTH = 200;
|
||||
const ACTIONS_LIST_BORDER = 2;
|
||||
|
||||
interface IntegrationsState extends PageBaseState {
|
||||
integrationsFilters: Filters;
|
||||
alertReceiveChannelId?: AlertReceiveChannel['id'] | 'new';
|
||||
page: number;
|
||||
confirmationModal: {
|
||||
isOpen: boolean;
|
||||
title: any;
|
||||
dismissText: string;
|
||||
confirmText: string;
|
||||
body?: React.ReactNode;
|
||||
description?: string;
|
||||
confirmationText?: string;
|
||||
onConfirm: () => void;
|
||||
};
|
||||
}
|
||||
|
||||
interface IntegrationsProps extends WithStoreProps, PageProps, RouteComponentProps<{ id: string }> {}
|
||||
|
|
@ -52,6 +67,7 @@ class Integrations extends React.Component<IntegrationsProps, IntegrationsState>
|
|||
integrationsFilters: { searchTerm: '' },
|
||||
errorData: initErrorDataState(),
|
||||
page: 1,
|
||||
confirmationModal: undefined,
|
||||
};
|
||||
|
||||
async componentDidMount() {
|
||||
|
|
@ -110,7 +126,7 @@ class Integrations extends React.Component<IntegrationsProps, IntegrationsState>
|
|||
|
||||
render() {
|
||||
const { store, query } = this.props;
|
||||
const { alertReceiveChannelId, page } = this.state;
|
||||
const { alertReceiveChannelId, page, confirmationModal } = this.state;
|
||||
const { grafanaTeamStore, alertReceiveChannelStore, heartbeatStore } = store;
|
||||
|
||||
const { count, results } = alertReceiveChannelStore.getPaginatedSearchResult();
|
||||
|
|
@ -215,6 +231,24 @@ class Integrations extends React.Component<IntegrationsProps, IntegrationsState>
|
|||
id={alertReceiveChannelId}
|
||||
/>
|
||||
)}
|
||||
|
||||
{confirmationModal && (
|
||||
<ConfirmModal
|
||||
isOpen={confirmationModal.isOpen}
|
||||
title={confirmationModal.title}
|
||||
confirmText={confirmationModal.confirmText}
|
||||
dismissText="Cancel"
|
||||
body={confirmationModal.body}
|
||||
description={confirmationModal.description}
|
||||
confirmationText={confirmationModal.confirmationText}
|
||||
onConfirm={confirmationModal.onConfirm}
|
||||
onDismiss={() =>
|
||||
this.setState({
|
||||
confirmationModal: undefined,
|
||||
})
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -354,29 +388,66 @@ class Integrations extends React.Component<IntegrationsProps, IntegrationsState>
|
|||
|
||||
renderButtons = (item: AlertReceiveChannel) => {
|
||||
return (
|
||||
<HorizontalGroup justify="flex-end">
|
||||
<WithPermissionControlTooltip key="edit" userAction={UserActions.IntegrationsWrite}>
|
||||
<IconButton tooltip="Settings" name="cog" onClick={() => this.onIntegrationEditClick(item.id)} />
|
||||
</WithPermissionControlTooltip>
|
||||
<WithPermissionControlTooltip key="edit" userAction={UserActions.IntegrationsWrite}>
|
||||
<WithConfirm
|
||||
description={
|
||||
<Text>
|
||||
<Emoji
|
||||
className={cx('title')}
|
||||
text={`Are you sure you want to delete ${item.verbal_name} integration?`}
|
||||
/>
|
||||
</Text>
|
||||
}
|
||||
>
|
||||
<IconButton
|
||||
tooltip="Delete"
|
||||
name="trash-alt"
|
||||
onClick={() => this.handleDeleteAlertReceiveChannel(item.id)}
|
||||
/>
|
||||
</WithConfirm>
|
||||
</WithPermissionControlTooltip>
|
||||
</HorizontalGroup>
|
||||
<WithContextMenu
|
||||
renderMenuItems={() => (
|
||||
<div className={cx('integrations-actionsList')}>
|
||||
<WithPermissionControlTooltip key="edit" userAction={UserActions.IntegrationsWrite}>
|
||||
<div className={cx('integrations-actionItem')} onClick={() => this.onIntegrationEditClick(item.id)}>
|
||||
<Text type="primary">Integration settings</Text>
|
||||
</div>
|
||||
</WithPermissionControlTooltip>
|
||||
|
||||
<CopyToClipboard text={item.id} onCopy={() => openNotification('Integration ID is copied')}>
|
||||
<div className={cx('integrations-actionItem')}>
|
||||
<HorizontalGroup spacing={'xs'}>
|
||||
<Icon name="copy" />
|
||||
|
||||
<Text type="primary">UID: {item.id}</Text>
|
||||
</HorizontalGroup>
|
||||
</div>
|
||||
</CopyToClipboard>
|
||||
|
||||
<div className="thin-line-break" />
|
||||
|
||||
<WithPermissionControlTooltip key="delete" userAction={UserActions.IntegrationsWrite}>
|
||||
<>
|
||||
<div className={cx('integrations-actionItem')}>
|
||||
<div
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
confirmationModal: {
|
||||
isOpen: true,
|
||||
confirmText: 'Delete',
|
||||
dismissText: 'Cancel',
|
||||
onConfirm: () => this.handleDeleteAlertReceiveChannel(item.id),
|
||||
title: 'Delete integration',
|
||||
body: (
|
||||
<Text type="primary">
|
||||
Are you sure you want to delete <Emoji text={item.verbal_name} /> integration?
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
});
|
||||
}}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
<Text type="danger">
|
||||
<HorizontalGroup spacing={'xs'}>
|
||||
<Icon name="trash-alt" />
|
||||
<span>Delete Integration</span>
|
||||
</HorizontalGroup>
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</WithPermissionControlTooltip>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
{({ openMenu }) => (
|
||||
<HamburgerMenu openMenu={openMenu} listBorder={ACTIONS_LIST_BORDER} listWidth={ACTIONS_LIST_WIDTH} />
|
||||
)}
|
||||
</WithContextMenu>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -390,6 +461,7 @@ class Integrations extends React.Component<IntegrationsProps, IntegrationsState>
|
|||
const { alertReceiveChannelStore } = store;
|
||||
|
||||
alertReceiveChannelStore.deleteAlertReceiveChannel(alertReceiveChannelId).then(this.applyFilters);
|
||||
this.setState({ confirmationModal: undefined });
|
||||
};
|
||||
|
||||
handleIntegrationsFiltersChange = (integrationsFilters: Filters) => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue