Created by:
diff --git a/grafana-plugin/src/pages/integrations_2/Integrations2.module.scss b/grafana-plugin/src/pages/integrations_2/Integrations2.module.scss
index 003100ea..4f86de25 100644
--- a/grafana-plugin/src/pages/integrations_2/Integrations2.module.scss
+++ b/grafana-plugin/src/pages/integrations_2/Integrations2.module.scss
@@ -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);
+ }
+}
\ No newline at end of file
diff --git a/grafana-plugin/src/pages/integrations_2/Integrations2.tsx b/grafana-plugin/src/pages/integrations_2/Integrations2.tsx
index 4750cf75..1de27eb1 100644
--- a/grafana-plugin/src/pages/integrations_2/Integrations2.tsx
+++ b/grafana-plugin/src/pages/integrations_2/Integrations2.tsx
@@ -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
integrationsFilters: { searchTerm: '' },
errorData: initErrorDataState(),
page: 1,
+ confirmationModal: undefined,
};
async componentDidMount() {
@@ -110,7 +126,7 @@ class Integrations extends React.Component
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
id={alertReceiveChannelId}
/>
)}
+
+ {confirmationModal && (
+
+ this.setState({
+ confirmationModal: undefined,
+ })
+ }
+ />
+ )}
>
);
}
@@ -231,9 +265,13 @@ class Integrations extends React.Component
);
}
- renderName(item: AlertReceiveChannel) {
+ renderName = (item: AlertReceiveChannel) => {
+ const {
+ query: { p },
+ } = this.props;
+
return (
-
+
);
- }
+ };
renderDatasource(item: AlertReceiveChannel, alertReceiveChannelStore) {
const alertReceiveChannel = alertReceiveChannelStore.items[item.id];
@@ -354,29 +392,66 @@ class Integrations extends React.Component
renderButtons = (item: AlertReceiveChannel) => {
return (
-
-
- this.onIntegrationEditClick(item.id)} />
-
-
-
-
-
- }
- >
- this.handleDeleteAlertReceiveChannel(item.id)}
- />
-
-
-
+ (
+
+
+ this.onIntegrationEditClick(item.id)}>
+ Integration settings
+
+
+
+
openNotification('Integration ID is copied')}>
+
+
+
+
+ UID: {item.id}
+
+
+
+
+
+
+
+ <>
+
+
{
+ this.setState({
+ confirmationModal: {
+ isOpen: true,
+ confirmText: 'Delete',
+ dismissText: 'Cancel',
+ onConfirm: () => this.handleDeleteAlertReceiveChannel(item.id),
+ title: 'Delete integration',
+ body: (
+
+ Are you sure you want to delete integration?
+
+ ),
+ },
+ });
+ }}
+ style={{ width: '100%' }}
+ >
+
+
+
+ Delete Integration
+
+
+
+
+ >
+
+
+ )}
+ >
+ {({ openMenu }) => (
+
+ )}
+
);
};
@@ -390,6 +465,7 @@ class Integrations extends React.Component
const { alertReceiveChannelStore } = store;
alertReceiveChannelStore.deleteAlertReceiveChannel(alertReceiveChannelId).then(this.applyFilters);
+ this.setState({ confirmationModal: undefined });
};
handleIntegrationsFiltersChange = (integrationsFilters: Filters) => {
diff --git a/grafana-plugin/src/plugin.json b/grafana-plugin/src/plugin.json
index 6250bbe8..a3a4c287 100644
--- a/grafana-plugin/src/plugin.json
+++ b/grafana-plugin/src/plugin.json
@@ -40,7 +40,7 @@
},
{
"type": "page",
- "name": "Alert Groups",
+ "name": "Alert groups",
"path": "/a/grafana-oncall-app/alert-groups",
"role": "Viewer",
"action": "grafana-oncall-app.alert-groups:read",
@@ -64,7 +64,7 @@
},
{
"type": "page",
- "name": "Escalation Chains",
+ "name": "Escalation chains",
"path": "/a/grafana-oncall-app/escalations",
"role": "Viewer",
"action": "grafana-oncall-app.escalation-chains:read",
@@ -80,7 +80,7 @@
},
{
"type": "page",
- "name": "Outgoing Webhooks",
+ "name": "Outgoing webhooks",
"path": "/a/grafana-oncall-app/outgoing_webhooks",
"role": "Viewer",
"action": "grafana-oncall-app.outgoing-webhooks:read",
@@ -88,7 +88,7 @@
},
{
"type": "page",
- "name": "Outgoing Webhooks 2",
+ "name": "Outgoing webhooks 2",
"path": "/a/grafana-oncall-app/outgoing_webhooks_2",
"role": "Viewer",
"action": "grafana-oncall-app.outgoing-webhooks:read",