diff --git a/CHANGELOG.md b/CHANGELOG.md index 23b0e04119..4f6d300b19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Changed + +- UI Updates for the integrations page ([#2310](https://github.com/grafana/oncall/pull/2310)) + ## v1.2.46 (2023-06-22) ### Added diff --git a/grafana-plugin/integration-tests/utils/integrations.ts b/grafana-plugin/integration-tests/utils/integrations.ts index 8e54eaa1da..c903f3adb5 100644 --- a/grafana-plugin/integration-tests/utils/integrations.ts +++ b/grafana-plugin/integration-tests/utils/integrations.ts @@ -1,5 +1,5 @@ import { Page } from '@playwright/test'; -import { clickButton, fillInInput, selectDropdownValue } from './forms'; +import { clickButton } from './forms'; import { goToOnCallPage } from './navigation'; const CREATE_INTEGRATION_MODAL_TEST_ID_SELECTOR = 'div[data-testid="create-integration-modal"]'; @@ -9,7 +9,7 @@ export const openCreateIntegrationModal = async (page: Page): Promise => { await goToOnCallPage(page, 'integrations'); // open the create integration modal - (await page.waitForSelector('text=New integration to receive alerts')).click(); + (await page.waitForSelector('text=New integration')).click(); // wait for it to pop up await page.waitForSelector(CREATE_INTEGRATION_MODAL_TEST_ID_SELECTOR); @@ -18,32 +18,34 @@ export const openCreateIntegrationModal = async (page: Page): Promise => { export const createIntegrationAndSendDemoAlert = async ( page: Page, integrationName: string, - escalationChainName: string + _escalationChainName: string ): Promise => { await openCreateIntegrationModal(page); // create a webhook integration (await page.waitForSelector(`${CREATE_INTEGRATION_MODAL_TEST_ID_SELECTOR} >> text=Webhook`)).click(); - // wait for the integrations settings modal to open up... and then close it - await clickButton({ page, buttonText: 'Open Escalations Settings' }); + // fill in the required inputs + (await page.waitForSelector('input[name="verbal_name"]', { state: 'attached' })).fill(integrationName); + (await page.waitForSelector('textarea[name="description_short"]', { state: 'attached' })).fill("Here goes your integration description"); - // update the integration name - await (await page.waitForSelector('div[data-testid="integration-header"] >> h4 >> button')).click(); - await fillInInput(page, 'div[data-testid="edit-integration-name-modal"] >> input', integrationName); - await clickButton({ page, buttonText: 'Update' }); + const grafanaUpdateBtn = page.getByTestId("update-integration-button"); + await grafanaUpdateBtn.click(); - const integrationSettingsElement = page.getByTestId('integration-settings'); + /* + * TODO: This is slightly more complicated now, change this in next iteration */ + // const integrationSettingsElement = page.getByTestId('integration-settings'); - // assign the escalation chain to the integration - await selectDropdownValue({ - page, - selectType: 'grafanaSelect', - placeholderText: 'Select Escalation Chain', - value: escalationChainName, - startingLocator: integrationSettingsElement, - }); + // // assign the escalation chain to the integration + // await selectDropdownValue({ + // page, + // selectType: 'grafanaSelect', + // placeholderText: 'Select Escalation Chain', + // value: escalationChainName, + // startingLocator: integrationSettingsElement, + // }); // send demo alert await clickButton({ page, buttonText: 'Send demo alert', dataTestId: 'send-demo-alert' }); + await clickButton({ page, buttonText: 'Send Alert', dataTestId: "submit-send-alert" }) }; diff --git a/grafana-plugin/src/components/AlertTemplates/CommonAlertTemplatesForm.config.ts b/grafana-plugin/src/components/AlertTemplates/CommonAlertTemplatesForm.config.ts index 8c65c6fb5b..1fc1ff932a 100644 --- a/grafana-plugin/src/components/AlertTemplates/CommonAlertTemplatesForm.config.ts +++ b/grafana-plugin/src/components/AlertTemplates/CommonAlertTemplatesForm.config.ts @@ -1,4 +1,4 @@ -import { TemplateOptions } from 'pages/integration_2/Integration2.config'; +import { TemplateOptions } from 'pages/integration/Integration.config'; export interface Template { name: string; diff --git a/grafana-plugin/src/components/GForm/GForm.tsx b/grafana-plugin/src/components/GForm/GForm.tsx index 4990c28244..a7e6b1454f 100644 --- a/grafana-plugin/src/components/GForm/GForm.tsx +++ b/grafana-plugin/src/components/GForm/GForm.tsx @@ -151,9 +151,11 @@ class GForm extends React.Component { return ( <> {openFields.map(renderField)} - - {collapsedfields.map(renderField)} - + {collapsedfields.length > 0 && ( + + {collapsedfields.map(renderField)} + + )} ); }} diff --git a/grafana-plugin/src/containers/AlertRules/AlertRules.helpers.ts b/grafana-plugin/src/containers/AlertRules/AlertRules.helpers.ts deleted file mode 100644 index c6220d480c..0000000000 --- a/grafana-plugin/src/containers/AlertRules/AlertRules.helpers.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { toArray } from 'react-emoji-render'; - -export const parseEmojis = (value: any) => { - const emojisArray = toArray(value); - - // toArray outputs React elements for emojis and strings for other - const newValue = emojisArray.reduce((previous, current) => { - if (typeof current === 'string') { - return previous + current; - } - //@ts-ignore - return previous + current.props.children; - }, ''); - - return newValue; -}; diff --git a/grafana-plugin/src/containers/AlertRules/AlertRules.module.css b/grafana-plugin/src/containers/AlertRules/AlertRules.module.css deleted file mode 100644 index 317ae25bfa..0000000000 --- a/grafana-plugin/src/containers/AlertRules/AlertRules.module.css +++ /dev/null @@ -1,144 +0,0 @@ -.root { - display: flex; - gap: 24px; - flex-direction: column; -} - -.route { - margin-bottom: 10px; -} - -.route-content { - margin-left: 16px; -} - -.route-header { - margin-bottom: 10px; -} - -.verbal-name { - font-weight: 500; -} - -.headerBlock { - background-color: var(--secondary-background); -} - -.title { - font-size: 16px; -} - -.root .timeline { - margin-left: 20px; -} - -.root .steps { - margin: 10px 0 0 0; -} - -.root .select { - flex-shrink: 0; - min-width: 100px; -} - -.create-channel-filter { - margin-left: 40px; - margin-top: 10px; -} - -.channel-filter-header { - display: flex; - flex-wrap: wrap-reverse; -} - -.channel-filter-header-left, -.channel-filter-header-right { - flex-grow: 1; - margin-bottom: 16px; -} - -.channel-filter-header-right { - display: flex; - justify-content: flex-end; -} - -.channel-filter-header-title { - display: flex; - align-items: center; - flex-wrap: wrap; - column-gap: 4px; - row-gap: 8px; -} - -.root .channel mark { - padding: 2px 5px; -} - -.channel_disabled { - text-decoration: line-through; -} - -.alertRulesActions { - display: flex; - justify-content: space-between; - margin-bottom: 24px; -} - -.alertRulesContent { - flex-shrink: 0; - overflow: auto; - max-height: 65vh; -} - -.slackLink { - font-size: 12px; -} - -.buttons { - display: flex; - align-items: baseline; -} - -.buttons > * { - margin-left: 8px; -} - -.slack-channel-step { - border-radius: 2px; - background: var(--secondary-background); - padding: 2px 2px 2px 12px; - flex-wrap: wrap; -} - -.icons-container { - align-self: center; -} - -.slack-channel-switch { - margin-left: -8px; -} - -.description-style a { - color: var(--primary-text-link); -} - -.integration__heading-text { - display: flex; - gap: 8px; -} - -.integration__heading-container { - display: flex; - flex-wrap: wrap-reverse; -} - -.integration__heading-container-left, -.integration__heading-container-right { - flex-grow: 1; - margin-bottom: 12px; -} - -.integration__heading-container-right { - display: flex; - justify-content: flex-end; -} diff --git a/grafana-plugin/src/containers/AlertRules/AlertRules.tsx b/grafana-plugin/src/containers/AlertRules/AlertRules.tsx deleted file mode 100644 index 4bb4a088ab..0000000000 --- a/grafana-plugin/src/containers/AlertRules/AlertRules.tsx +++ /dev/null @@ -1,922 +0,0 @@ -import React, { SyntheticEvent } from 'react'; - -import { SelectableValue } from '@grafana/data'; -import { - Alert, - Button, - ConfirmModal, - Field, - HorizontalGroup, - Icon, - IconButton, - Input, - LoadingPlaceholder, - Modal, - Tooltip, - VerticalGroup, -} from '@grafana/ui'; -import cn from 'classnames/bind'; -import { observer } from 'mobx-react'; -import CopyToClipboard from 'react-copy-to-clipboard'; -import Emoji from 'react-emoji-render'; - -import Collapse from 'components/Collapse/Collapse'; -import Block from 'components/GBlock/Block'; -import PluginLink from 'components/PluginLink/PluginLink'; -import SourceCode from 'components/SourceCode/SourceCode'; -import Text from 'components/Text/Text'; -import WithConfirm from 'components/WithConfirm/WithConfirm'; -import { parseEmojis } from 'containers/AlertRules/AlertRules.helpers'; -import { ChatOpsConnectors } from 'containers/AlertRules/parts'; -import ChannelFilterForm from 'containers/ChannelFilterForm/ChannelFilterForm'; -import EscalationChainForm, { EscalationChainFormMode } from 'containers/EscalationChainForm/EscalationChainForm'; -import EscalationChainSteps from 'containers/EscalationChainSteps/EscalationChainSteps'; -import GSelect from 'containers/GSelect/GSelect'; -import { IntegrationSettingsTab } from 'containers/IntegrationSettings/IntegrationSettings.types'; -import TeamName from 'containers/TeamName/TeamName'; -import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip'; -import { AlertReceiveChannel, MaintenanceMode } from 'models/alert_receive_channel/alert_receive_channel.types'; -import { ChannelFilter } from 'models/channel_filter/channel_filter.types'; -import { EscalationChain } from 'models/escalation_chain/escalation_chain.types'; -import { EscalationPolicyOption } from 'models/escalation_policy/escalation_policy.types'; -import { MaintenanceType } from 'models/maintenance/maintenance.types'; -import { AppFeature } from 'state/features'; -import { WithStoreProps } from 'state/types'; -import { withMobXProviderContext } from 'state/withStore'; -import { openNotification } from 'utils'; -import { isUserActionAllowed, UserActions } from 'utils/authorization'; -import sanitize from 'utils/sanitize'; - -import styles from './AlertRules.module.css'; -const cx = cn.bind(styles); - -interface AlertRulesProps extends WithStoreProps { - alertReceiveChannelId: AlertReceiveChannel['id']; - onDelete: (id: AlertReceiveChannel['id']) => void; - onEditAlertReceiveChannelTemplates: () => void; - onShowSettings: (tab?: IntegrationSettingsTab) => void; -} - -interface AlertRulesState { - alertReceiveChannelIdToEditTemplates?: AlertReceiveChannel['id']; - alertReceiveChannelIdToCreateChannelFilter?: AlertReceiveChannel['id']; - channelFilterToEdit?: ChannelFilter; - expandedRoutes: Array; - settingsVisible?: boolean; - routeToDelete?: ChannelFilter['id']; - escalationChainIdToCopy?: EscalationChain['id']; - channelFilterIdToCopyEscalationChain?: ChannelFilter['id']; - editIntegrationName?: string; -} - -const Notification: React.FC = () => ( -
- Demo alert was generated. Find it on the - "Alert Groups" - page and make sure it didn't freak out your colleagues 😉 -
-); - -@observer -class AlertRules extends React.Component { - state: AlertRulesState = { - expandedRoutes: [], - }; - - newAlertReceiveChannelName?: string; - - componentDidMount() { - if (this.props.alertReceiveChannelId) { - this.update(); - } - } - - componentDidUpdate(prevProps: Readonly, _prevState: Readonly, _snapshot?: any) { - if (this.props.alertReceiveChannelId && prevProps.alertReceiveChannelId !== this.props.alertReceiveChannelId) { - if (prevProps.alertReceiveChannelId) { - this.setState({ - expandedRoutes: [], - channelFilterToEdit: undefined, - channelFilterIdToCopyEscalationChain: undefined, - alertReceiveChannelIdToCreateChannelFilter: undefined, - alertReceiveChannelIdToEditTemplates: undefined, - editIntegrationName: undefined, - }); - } - - this.update(); - } - } - - async update() { - const { store, alertReceiveChannelId } = this.props; - const { alertReceiveChannelStore, telegramChannelStore } = store; - - store.alertReceiveChannelStore.updateItem(alertReceiveChannelId); - - await store.alertReceiveChannelStore.updateChannelFilters(alertReceiveChannelId); - await store.escalationChainStore.updateItems(); - await telegramChannelStore.updateTelegramChannels(); - - const channelFilterIds = store.alertReceiveChannelStore.channelFilterIds[alertReceiveChannelId]; - - store.alertReceiveChannelStore.updateCustomButtons(alertReceiveChannelId); - - const expandedRoutes: Array = []; - channelFilterIds - .filter((channelFilterId: ChannelFilter['id']) => { - const channelFilter = alertReceiveChannelStore.channelFilters[channelFilterId]; - - return channelFilter.is_default; - }) - .forEach((channelFilterId: ChannelFilter['id']) => { - const channelFilter = alertReceiveChannelStore.channelFilters[channelFilterId]; - store.escalationPolicyStore.updateEscalationPolicies(channelFilter.escalation_chain); - - expandedRoutes.push(channelFilterId); - }); - - this.setState({ expandedRoutes }); - } - - render() { - const { - alertReceiveChannelIdToCreateChannelFilter, - routeToDelete, - escalationChainIdToCopy, - channelFilterIdToCopyEscalationChain, - editIntegrationName, - } = this.state; - const { store, alertReceiveChannelId, onShowSettings } = this.props; - const { alertReceiveChannelStore } = store; - - const alertReceiveChannel = alertReceiveChannelStore.items[alertReceiveChannelId]; - - if (!alertReceiveChannel) { - return ; - } - - const isIntegrationNameempty = editIntegrationName === ''; - const maintenanceMode = alertReceiveChannel.maintenance_mode; - return ( - <> -
- -
-
- -
-
{parseEmojis(alertReceiveChannel?.verbal_name || '')}
- - - -
-
-
- -
-
- - - - -
- {maintenanceMode === MaintenanceMode.Debug || maintenanceMode === MaintenanceMode.Maintenance ? ( - -
-
-
- - {editIntegrationName !== undefined && ( - this.setState({ editIntegrationName: undefined })} - > -
- - this.setState({ editIntegrationName: e.target.value })} - /> - - - - - -
-
- )} -
-
- - {alertReceiveChannel.description && ( -
-
} - severity="info" - /> -
- )} -
-
- - {!alertReceiveChannelIdToCreateChannelFilter && ( - - - - )} -
- {alertReceiveChannelIdToCreateChannelFilter && ( - { - this.setState({ - alertReceiveChannelIdToCreateChannelFilter: undefined, - }); - }} - onUpdate={this.handleCreateChannelFilter} - /> - )} - {routeToDelete && ( - this.setState({ routeToDelete: undefined })} - /> - )} - {channelFilterIdToCopyEscalationChain && ( - { - this.setState({ - escalationChainIdToCopy: undefined, - channelFilterIdToCopyEscalationChain: undefined, - }); - }} - onUpdate={this.handleEscalationChainCreate} - /> - )} - {this.renderRoutes(alertReceiveChannelId)} -
- - - ); - } - - getDeleteRouteClickHandler = (routeId: ChannelFilter['id']) => { - return (event: SyntheticEvent) => { - event.stopPropagation(); - this.setState({ routeToDelete: routeId }); - }; - }; - - handleDeleteRoute = async () => { - const { routeToDelete } = this.state; - const { store } = this.props; - - this.setState({ routeToDelete: undefined }); - - const { alertReceiveChannelStore } = store; - - await alertReceiveChannelStore.deleteChannelFilter(routeToDelete); - - this.update(); - }; - - handleEscalationChainCreate = async (id: EscalationChain['id']) => { - const { store } = this.props; - const { alertReceiveChannelStore } = store; - const { channelFilterIdToCopyEscalationChain } = this.state; - - await alertReceiveChannelStore - .saveChannelFilter(channelFilterIdToCopyEscalationChain, { escalation_chain: id }) - .then(() => { - store.escalationPolicyStore.updateEscalationPolicies(id); - }); - - store.escalationChainStore.updateItems(); - }; - - handleDeleteAlertReceiveChannel = () => { - const { alertReceiveChannelId, onDelete } = this.props; - onDelete(alertReceiveChannelId); - }; - - handleStopMaintenance = () => { - const { store, alertReceiveChannelId } = this.props; - const { maintenanceStore, alertReceiveChannelStore } = store; - - maintenanceStore.stopMaintenanceMode(MaintenanceType.alert_receive_channel, alertReceiveChannelId).then(() => { - alertReceiveChannelStore.updateItem(alertReceiveChannelId); - }); - }; - - getChangeIntegrationNameHandler = (name: string) => { - return () => { - this.newAlertReceiveChannelName = name; - this.setState({ editIntegrationName: name }); - }; - }; - - handleChangeAlertReceiveChannelName = () => { - const { store, alertReceiveChannelId } = this.props; - const { editIntegrationName } = this.state; - - store.alertReceiveChannelStore - .saveAlertReceiveChannel(alertReceiveChannelId, { verbal_name: editIntegrationName }) - .then(() => { - store.alertReceiveChannelStore.updateItem(alertReceiveChannelId); - }); - this.setState({ editIntegrationName: undefined }); - this.update(); - }; - - handleCreateChannelFilter = (id: ChannelFilter['id']) => { - const { store } = this.props; - const { alertReceiveChannelIdToCreateChannelFilter, expandedRoutes } = this.state; - - if (!expandedRoutes.includes(id)) { - this.setState({ - expandedRoutes: [...expandedRoutes, id], - }); - } - - store.alertReceiveChannelStore.updateChannelFilters(alertReceiveChannelIdToCreateChannelFilter).then(() => { - const channelFilter = store.alertReceiveChannelStore.channelFilters[id]; - - store.escalationPolicyStore.updateEscalationPolicies(channelFilter.escalation_chain); - }); - }; - - getCreateChannelFilterClickHandler = (alertReceiveChannelId: AlertReceiveChannel['id']) => { - return () => { - this.setState({ - alertReceiveChannelIdToCreateChannelFilter: alertReceiveChannelId, - }); - }; - }; - - renderRoutes = (alertReceiveChannelId: AlertReceiveChannel['id']) => { - const { expandedRoutes, channelFilterToEdit } = this.state; - const { store } = this.props; - - const channelFilterIds = store.alertReceiveChannelStore.channelFilterIds[alertReceiveChannelId]; - - if (!channelFilterIds) { - return ; - } - - return ( -
- {channelFilterIds.map((channelFilterId: ChannelFilter['id']) => { - const channelFilter = store.alertReceiveChannelStore.channelFilters[channelFilterId]; - - if (channelFilterId === channelFilterToEdit?.id) { - return ( - { - this.setState({ - channelFilterToEdit: undefined, - }); - }} - onUpdate={() => { - store.alertReceiveChannelStore.updateChannelFilters(channelFilterToEdit.alert_receive_channel); - }} - /> - ); - } - - const escalationChain = store.escalationChainStore.items[channelFilter.escalation_chain]; - - let warningAboutModifyingEscalationChain = null; - const otherRoutes = escalationChain?.number_of_routes - 1; - const otherIntegrations = escalationChain?.number_of_integrations - 1; - - if (otherRoutes > 0 || otherIntegrations > 0) { - warningAboutModifyingEscalationChain = ( - <> - Modifying this will affect{' '} - {otherRoutes > 0 && ( - - {otherRoutes} other route{otherRoutes === 1 ? '' : 's'} - - )} - {otherRoutes > 0 && otherIntegrations > 0 && ' and '} - {otherIntegrations > 0 && ( - - {otherIntegrations} other integration{otherIntegrations === 1 ? '' : 's'} - - )} - .{' '} - - ); - } - - return ( - -
- {escalationChain ? ( - <> -
- - - - {escalationChain?.name} - - {' '} - escalation chain - -
- - {warningAboutModifyingEscalationChain} - You can{' '} - - - {' '} - of the current chain or{' '} - - - - -
- {this._renderEscalationPolicies(channelFilter.id)} - - ) : ( - - Select Escalation Chain ↑ or - {' '} - - )} -
-
- ); - })} -
- ); - }; - - getChannelFilterToggleHandler = (channelFilterId: ChannelFilter['id']) => { - return (isOpen: boolean) => { - const { expandedRoutes } = this.state; - - if (!isOpen && expandedRoutes.includes(channelFilterId)) { - const index = expandedRoutes.indexOf(channelFilterId); - const newExpandedRoutes = [...expandedRoutes]; - newExpandedRoutes.splice(index, 1); - this.setState({ expandedRoutes: newExpandedRoutes }); - } else if (isOpen && !expandedRoutes.includes(channelFilterId)) { - this.setState({ expandedRoutes: [...expandedRoutes, channelFilterId] }); - } - }; - }; - - renderChannelFilterButtons = (channelFilterId: ChannelFilter['id'], index: number) => { - const { store, alertReceiveChannelId } = this.props; - - const { alertReceiveChannelStore } = store; - - const channelFilterIds = alertReceiveChannelStore.channelFilterIds[alertReceiveChannelId]; - - const channelFilter = alertReceiveChannelStore.channelFilters[channelFilterId]; - - return ( - - {Boolean(index > 0 && !channelFilter.is_default) && ( - - { - e.stopPropagation(); - alertReceiveChannelStore.moveChannelFilterToPosition(alertReceiveChannelId, index, index - 1); - }} - tooltip="Move up" - tooltipPlacement="top" - /> - - )} - - {Boolean(index < channelFilterIds.length - 2 && !channelFilter.is_default) && ( - - { - e.stopPropagation(); - alertReceiveChannelStore.moveChannelFilterToPosition(alertReceiveChannelId, index, index + 1); - }} - tooltip="Move down" - tooltipPlacement="top" - /> - - )} - {!channelFilter.is_default && ( - - - - )} - {!channelFilter.is_default && ( - - { - event.stopPropagation(); - this.setState({ - channelFilterToEdit: channelFilter, - }); - }} - tooltip="Edit" - tooltipPlacement="top" - /> - - )} - {store.hasFeature(AppFeature.Webhooks2) && ( - - - ID {channelFilter.id} -
- (click to copy ID to clipboard) - - } - tooltipPlacement="top" - name="info-circle" - /> -
- )} - - - -
- ); - }; - - renderChannelFilterTitle = ( - alertReceiveChannelId: AlertReceiveChannel['id'], - channelFilterId: ChannelFilter['id'] - ) => { - const { store } = this.props; - const channelFilterIds = store.alertReceiveChannelStore.channelFilterIds[alertReceiveChannelId]; - const channelFilter = store.alertReceiveChannelStore.channelFilters[channelFilterId]; - - const index = channelFilterIds.indexOf(channelFilterId); - return ( - <> -
-
-
- {channelFilter.is_default ? ( - <> - {channelFilterIds.length > 1 && ELSE} - route to escalation chain: - - { - return ( - <> - {item.label} - - - ); - }} - /> - - - ) : ( - <> - {index === 0 ? 'IF' : 'ELSE IF'} - {channelFilter.filtering_term_type === 0 ? ( - <> - - regular expression - - - - - - ) : ( - jinja2 expression - )} - is - {'True'} - {'for new Alert Group:'} - - )} -
-
-
-
e.stopPropagation()}>{this.renderChannelFilterButtons(channelFilterId, index)}
-
-
- {!channelFilter.is_default && ( - - - {!channelFilter.is_default && ( - <> - {channelFilter.filtering_term_type === 0 ? ( - - {'payload =~ "' + channelFilter.filtering_term + '"'} - - ) : ( - {channelFilter.filtering_term} - )} - - )} - - - {'route to escalation chain: '} - -
e.stopPropagation()}> - { - return ( - <> - {item.label} - - - ); - }} - /> -
-
-
-
- )} - - ); - }; - - getEscalationChainChangeHandler = (channelFilterId: ChannelFilter['id']) => { - const { store } = this.props; - return (value: EscalationChain['id']) => { - store.alertReceiveChannelStore - .saveChannelFilter(channelFilterId, { - escalation_chain: value, - }) - .then(() => { - store.escalationChainStore.updateItems(); // to update number_of_integrations and number_of_routes - store.escalationPolicyStore.updateEscalationPolicies(value); - }); - }; - }; - - getEditChannelFilterClickHandler = (channelFilter: ChannelFilter) => { - return (event: MouseEvent) => { - event.stopPropagation(); - this.setState({ - channelFilterToEdit: channelFilter, - }); - }; - }; - - _renderEscalationPolicies = (channelFilterId: ChannelFilter['id']) => { - const { store } = this.props; - const channelFilter = store.alertReceiveChannelStore.channelFilters[channelFilterId]; - const escalationChainId = channelFilter.escalation_chain; - - return ( - } - id={escalationChainId} - /> - ); - }; - - getCreateEscalationPolicyClickHandler = (escalationChainId: EscalationChain['id']) => { - const { store } = this.props; - const { escalationPolicyStore } = store; - - return async (option: EscalationPolicyOption) => { - await escalationPolicyStore.createEscalationPolicy(escalationChainId, { - step: option.value, - }); - - escalationPolicyStore.updateEscalationPolicies(escalationChainId); - }; - }; - - getEscalationPoliciesSortEndHandler = (escalationChainId: EscalationChain['id']) => { - const { store } = this.props; - const { escalationPolicyStore } = store; - - return ({ oldIndex, newIndex }: any) => { - escalationPolicyStore.moveEscalationPolicyToPosition(oldIndex, newIndex, escalationChainId); - }; - }; - - getSendDemoAlertClickHandler = (id: AlertReceiveChannel['id']) => { - const { - store: { alertReceiveChannelStore }, - } = this.props; - return () => { - alertReceiveChannelStore.sendDemoAlert(id).then(() => { - alertReceiveChannelStore.updateCounters(); - openNotification(); - }); - }; - }; - - getSendDemoAlertToParticularRoute = (id: ChannelFilter['id']) => { - const { - store: { alertReceiveChannelStore }, - } = this.props; - return () => { - alertReceiveChannelStore.sendDemoAlertToParticularRoute(id).then(() => { - openNotification(); - }); - }; - }; -} - -export default withMobXProviderContext(AlertRules); diff --git a/grafana-plugin/src/containers/IntegrationContainers/CollapsedIntegrationRouteDisplay/CollapsedIntegrationRouteDisplay.tsx b/grafana-plugin/src/containers/IntegrationContainers/CollapsedIntegrationRouteDisplay/CollapsedIntegrationRouteDisplay.tsx index af06e384b7..f249bccf6e 100644 --- a/grafana-plugin/src/containers/IntegrationContainers/CollapsedIntegrationRouteDisplay/CollapsedIntegrationRouteDisplay.tsx +++ b/grafana-plugin/src/containers/IntegrationContainers/CollapsedIntegrationRouteDisplay/CollapsedIntegrationRouteDisplay.tsx @@ -12,8 +12,8 @@ import styles from 'containers/IntegrationContainers/CollapsedIntegrationRouteDi import { RouteButtonsDisplay } from 'containers/IntegrationContainers/ExpandedIntegrationRouteDisplay/ExpandedIntegrationRouteDisplay'; import { AlertReceiveChannel } from 'models/alert_receive_channel/alert_receive_channel.types'; import { ChannelFilter } from 'models/channel_filter'; -import CommonIntegrationHelper from 'pages/integration_2/CommonIntegration2.helper'; -import IntegrationHelper from 'pages/integration_2/Integration2.helper'; +import CommonIntegrationHelper from 'pages/integration/CommonIntegration.helper'; +import IntegrationHelper from 'pages/integration/Integration.helper'; import { useStore } from 'state/useStore'; import { openNotification } from 'utils'; diff --git a/grafana-plugin/src/containers/IntegrationContainers/ExpandedIntegrationRouteDisplay/ExpandedIntegrationRouteDisplay.tsx b/grafana-plugin/src/containers/IntegrationContainers/ExpandedIntegrationRouteDisplay/ExpandedIntegrationRouteDisplay.tsx index 889d78ac72..ebb86236a3 100644 --- a/grafana-plugin/src/containers/IntegrationContainers/ExpandedIntegrationRouteDisplay/ExpandedIntegrationRouteDisplay.tsx +++ b/grafana-plugin/src/containers/IntegrationContainers/ExpandedIntegrationRouteDisplay/ExpandedIntegrationRouteDisplay.tsx @@ -33,9 +33,9 @@ import { AlertReceiveChannel } from 'models/alert_receive_channel/alert_receive_ import { AlertTemplatesDTO } from 'models/alert_templates'; import { ChannelFilter } from 'models/channel_filter/channel_filter.types'; import { EscalationChain } from 'models/escalation_chain/escalation_chain.types'; -import CommonIntegrationHelper from 'pages/integration_2/CommonIntegration2.helper'; -import IntegrationHelper from 'pages/integration_2/Integration2.helper'; -import { MONACO_INPUT_HEIGHT_SMALL, MONACO_OPTIONS } from 'pages/integration_2/Integration2Common.config'; +import CommonIntegrationHelper from 'pages/integration/CommonIntegration.helper'; +import IntegrationHelper from 'pages/integration/Integration.helper'; +import { MONACO_INPUT_HEIGHT_SMALL, MONACO_OPTIONS } from 'pages/integration/IntegrationCommon.config'; import { useStore } from 'state/useStore'; import { openNotification } from 'utils'; import { UserActions } from 'utils/authorization'; diff --git a/grafana-plugin/src/containers/IntegrationContainers/IntegrationCommonTemplatesList.config.ts b/grafana-plugin/src/containers/IntegrationContainers/IntegrationCommonTemplatesList.config.ts index 1cadbe6896..16667ede8d 100644 --- a/grafana-plugin/src/containers/IntegrationContainers/IntegrationCommonTemplatesList.config.ts +++ b/grafana-plugin/src/containers/IntegrationContainers/IntegrationCommonTemplatesList.config.ts @@ -1,10 +1,9 @@ -import { MONACO_INPUT_HEIGHT_SMALL, MONACO_INPUT_HEIGHT_TALL } from 'pages/integration_2/Integration2Common.config'; +import { MONACO_INPUT_HEIGHT_SMALL, MONACO_INPUT_HEIGHT_TALL } from 'pages/integration/IntegrationCommon.config'; interface TemplateToRender { name: string; label: string; height: string; - type: 'plain' | 'html' | 'image' | 'boolean'; labelTooltip?: string; } @@ -23,7 +22,6 @@ export const commonTemplatesToRender: TemplateBlock[] = [ labelTooltip: 'The Grouping Template is applied to every incoming alert payload after the Routing Template. It can be based on time, or alert content, or both. If the resulting grouping key matches an existing non-resolved alert group, the alert will be grouped accordingly. Otherwise, a new alert group will be created', height: MONACO_INPUT_HEIGHT_SMALL, - type: 'plain', }, { name: 'resolve_condition_template', @@ -31,7 +29,6 @@ export const commonTemplatesToRender: TemplateBlock[] = [ labelTooltip: 'If Autoresolution Template is True, the alert will resolve its group as "resolved by source". If the group is already resolved, the alert will be added to that group', height: MONACO_INPUT_HEIGHT_SMALL, - type: 'boolean', }, ], }, @@ -42,19 +39,16 @@ export const commonTemplatesToRender: TemplateBlock[] = [ name: 'web_title_template', label: 'Title', height: MONACO_INPUT_HEIGHT_SMALL, - type: 'html', }, { name: 'web_message_template', label: 'Message', height: MONACO_INPUT_HEIGHT_TALL, - type: 'html', }, { name: 'web_image_url_template', label: 'Image', height: MONACO_INPUT_HEIGHT_SMALL, - type: 'image', }, ], }, @@ -65,13 +59,11 @@ export const commonTemplatesToRender: TemplateBlock[] = [ name: 'acknowledge_condition_template', label: 'Auto acknowledge', height: MONACO_INPUT_HEIGHT_SMALL, - type: 'boolean', }, { name: 'source_link_template', label: 'Source link', height: MONACO_INPUT_HEIGHT_SMALL, - type: 'plain', }, ], }, @@ -82,13 +74,11 @@ export const commonTemplatesToRender: TemplateBlock[] = [ name: 'phone_call_title_template', label: 'Phone Call', height: MONACO_INPUT_HEIGHT_SMALL, - type: 'plain', }, { name: 'sms_title_template', label: 'SMS', height: MONACO_INPUT_HEIGHT_SMALL, - type: 'plain', }, ], }, @@ -99,19 +89,16 @@ export const commonTemplatesToRender: TemplateBlock[] = [ name: 'slack_title_template', label: 'Title', height: MONACO_INPUT_HEIGHT_SMALL, - type: 'plain', }, { name: 'slack_message_template', label: 'Message', height: MONACO_INPUT_HEIGHT_TALL, - type: 'plain', }, { name: 'slack_image_url_template', label: 'Image', height: MONACO_INPUT_HEIGHT_SMALL, - type: 'image', }, ], }, @@ -122,19 +109,16 @@ export const commonTemplatesToRender: TemplateBlock[] = [ name: 'telegram_title_template', label: 'Title', height: MONACO_INPUT_HEIGHT_SMALL, - type: 'plain', }, { name: 'telegram_message_template', label: 'Message', height: MONACO_INPUT_HEIGHT_TALL, - type: 'plain', }, { name: 'telegram_image_url_template', label: 'Image', height: MONACO_INPUT_HEIGHT_SMALL, - type: 'image', }, ], }, @@ -145,9 +129,8 @@ export const commonTemplatesToRender: TemplateBlock[] = [ name: 'email_title_template', label: 'Title', height: MONACO_INPUT_HEIGHT_SMALL, - type: 'plain', }, - { name: 'email_message_template', label: 'Message', height: MONACO_INPUT_HEIGHT_TALL, type: 'plain' }, + { name: 'email_message_template', label: 'Message', height: MONACO_INPUT_HEIGHT_TALL }, ], }, ]; diff --git a/grafana-plugin/src/containers/IntegrationContainers/Integration2HearbeatForm/Integration2HeartbeatForm.tsx b/grafana-plugin/src/containers/IntegrationContainers/IntegrationHearbeatForm/IntegrationHeartbeatForm.tsx similarity index 93% rename from grafana-plugin/src/containers/IntegrationContainers/Integration2HearbeatForm/Integration2HeartbeatForm.tsx rename to grafana-plugin/src/containers/IntegrationContainers/IntegrationHearbeatForm/IntegrationHeartbeatForm.tsx index c0faf12ca8..94a29df96c 100644 --- a/grafana-plugin/src/containers/IntegrationContainers/Integration2HearbeatForm/Integration2HeartbeatForm.tsx +++ b/grafana-plugin/src/containers/IntegrationContainers/IntegrationHearbeatForm/IntegrationHeartbeatForm.tsx @@ -16,12 +16,12 @@ import { UserActions } from 'utils/authorization'; const cx = cn.bind({}); -interface Integration2HearbeatFormProps { +interface IntegrationHearbeatFormProps { alertReceveChannelId: AlertReceiveChannel['id']; onClose?: () => void; } -const Integration2HearbeatForm = observer(({ alertReceveChannelId, onClose }: Integration2HearbeatFormProps) => { +const IntegrationHearbeatForm = observer(({ alertReceveChannelId, onClose }: IntegrationHearbeatFormProps) => { const [interval, setInterval] = useState(undefined); const { heartbeatStore, alertReceiveChannelStore } = useStore(); @@ -112,6 +112,6 @@ const Integration2HearbeatForm = observer(({ alertReceveChannelId, onClose }: In } }); -export default withMobXProviderContext(Integration2HearbeatForm) as ({ +export default withMobXProviderContext(IntegrationHearbeatForm) as ({ alertReceveChannelId, -}: Integration2HearbeatFormProps) => JSX.Element; +}: IntegrationHearbeatFormProps) => JSX.Element; diff --git a/grafana-plugin/src/containers/IntegrationContainers/IntegrationTemplatesList.tsx b/grafana-plugin/src/containers/IntegrationContainers/IntegrationTemplatesList.tsx index f3e16ccd02..43d35e8e16 100644 --- a/grafana-plugin/src/containers/IntegrationContainers/IntegrationTemplatesList.tsx +++ b/grafana-plugin/src/containers/IntegrationContainers/IntegrationTemplatesList.tsx @@ -10,9 +10,9 @@ import Text from 'components/Text/Text'; import { templatesToRender } from 'containers/IntegrationContainers/IntegrationTemplatesList.config'; import { AlertReceiveChannel } from 'models/alert_receive_channel/alert_receive_channel.types'; import { AlertTemplatesDTO } from 'models/alert_templates'; -import IntegrationHelper from 'pages/integration_2/Integration2.helper'; -import styles from 'pages/integration_2/Integration2.module.scss'; -import { MONACO_INPUT_HEIGHT_TALL, MONACO_OPTIONS } from 'pages/integration_2/Integration2Common.config'; +import IntegrationHelper from 'pages/integration/Integration.helper'; +import styles from 'pages/integration/Integration.module.scss'; +import { MONACO_INPUT_HEIGHT_TALL, MONACO_OPTIONS } from 'pages/integration/IntegrationCommon.config'; import { useStore } from 'state/useStore'; import { openErrorNotification, openNotification } from 'utils'; diff --git a/grafana-plugin/src/containers/IntegrationForm/IntegrationForm2.config.ts b/grafana-plugin/src/containers/IntegrationForm/IntegrationForm.config.ts similarity index 100% rename from grafana-plugin/src/containers/IntegrationForm/IntegrationForm2.config.ts rename to grafana-plugin/src/containers/IntegrationForm/IntegrationForm.config.ts diff --git a/grafana-plugin/src/containers/IntegrationForm/IntegrationForm2.helpers.ts b/grafana-plugin/src/containers/IntegrationForm/IntegrationForm.helpers.ts similarity index 100% rename from grafana-plugin/src/containers/IntegrationForm/IntegrationForm2.helpers.ts rename to grafana-plugin/src/containers/IntegrationForm/IntegrationForm.helpers.ts diff --git a/grafana-plugin/src/containers/IntegrationForm/IntegrationForm2.module.css b/grafana-plugin/src/containers/IntegrationForm/IntegrationForm.module.css similarity index 100% rename from grafana-plugin/src/containers/IntegrationForm/IntegrationForm2.module.css rename to grafana-plugin/src/containers/IntegrationForm/IntegrationForm.module.css diff --git a/grafana-plugin/src/containers/IntegrationForm/IntegrationForm2.tsx b/grafana-plugin/src/containers/IntegrationForm/IntegrationForm.tsx similarity index 95% rename from grafana-plugin/src/containers/IntegrationForm/IntegrationForm2.tsx rename to grafana-plugin/src/containers/IntegrationForm/IntegrationForm.tsx index 0b658b18cc..d550d6c946 100644 --- a/grafana-plugin/src/containers/IntegrationForm/IntegrationForm2.tsx +++ b/grafana-plugin/src/containers/IntegrationForm/IntegrationForm.tsx @@ -20,10 +20,10 @@ import { openErrorNotification } from 'utils'; import { UserActions } from 'utils/authorization'; import { PLUGIN_ROOT } from 'utils/consts'; -import { form } from './IntegrationForm2.config'; -import { prepareForEdit } from './IntegrationForm2.helpers'; +import { form } from './IntegrationForm.config'; +import { prepareForEdit } from './IntegrationForm.helpers'; -import styles from './IntegrationForm2.module.css'; +import styles from './IntegrationForm.module.css'; const cx = cn.bind(styles); @@ -34,7 +34,7 @@ interface IntegrationFormProps { onUpdate: () => void; } -const IntegrationForm2 = observer((props: IntegrationFormProps) => { +const IntegrationForm = observer((props: IntegrationFormProps) => { const { id, onHide, onUpdate, isTableView = true } = props; const store = useStore(); @@ -60,7 +60,7 @@ const IntegrationForm2 = observer((props: IntegrationFormProps) => { ? alertReceiveChannelStore .create(data) .then((response) => { - history.push(`${PLUGIN_ROOT}/integrations_2/${response.id}`); + history.push(`${PLUGIN_ROOT}/integrations/${response.id}`); }) .catch(() => { openErrorNotification('Something went wrong, please try again later.'); @@ -210,7 +210,7 @@ const IntegrationForm2 = observer((props: IntegrationFormProps) => { )} - @@ -230,4 +230,4 @@ const IntegrationForm2 = observer((props: IntegrationFormProps) => { } }); -export default IntegrationForm2; +export default IntegrationForm; diff --git a/grafana-plugin/src/containers/IntegrationSettings/IntegrationSettings.module.css b/grafana-plugin/src/containers/IntegrationSettings/IntegrationSettings.module.css deleted file mode 100644 index 865fbed683..0000000000 --- a/grafana-plugin/src/containers/IntegrationSettings/IntegrationSettings.module.css +++ /dev/null @@ -1,32 +0,0 @@ -.root { - display: block; -} - -.title { - padding: 24px; - display: flex; - gap: 10px; - align-items: flex-start; -} - -.title-column { - display: flex; - flex-direction: column; -} - -.content { - margin-top: 10px; -} - -.content ul { - list-style-type: none; -} - -.content ol { - list-style-type: none; -} - -.settings-header-buttons { - right: 0; - position: absolute; -} diff --git a/grafana-plugin/src/containers/IntegrationSettings/IntegrationSettings.tsx b/grafana-plugin/src/containers/IntegrationSettings/IntegrationSettings.tsx deleted file mode 100644 index aa6d7ec878..0000000000 --- a/grafana-plugin/src/containers/IntegrationSettings/IntegrationSettings.tsx +++ /dev/null @@ -1,173 +0,0 @@ -import React, { useCallback, useEffect, useState } from 'react'; - -import { Drawer, Tab, TabContent, TabsBar, Button, VerticalGroup, Input } from '@grafana/ui'; -import cn from 'classnames/bind'; -import { observer } from 'mobx-react'; -import CopyToClipboard from 'react-copy-to-clipboard'; -import Emoji from 'react-emoji-render'; - -import IntegrationLogo from 'components/IntegrationLogo/IntegrationLogo'; -import Text from 'components/Text/Text'; -import AlertTemplatesFormContainer from 'containers/AlertTemplatesFormContainer/AlertTemplatesFormContainer'; -import HeartbeatForm from 'containers/HeartbeatModal/HeartbeatForm'; -import { AlertReceiveChannel } from 'models/alert_receive_channel/alert_receive_channel.types'; -import { Alert } from 'models/alertgroup/alertgroup.types'; -import { useStore } from 'state/useStore'; -import { openNotification } from 'utils'; -import LocationHelper from 'utils/LocationHelper'; - -import { IntegrationSettingsTab } from './IntegrationSettings.types'; -import Autoresolve from './parts/Autoresolve'; - -import styles from 'containers/IntegrationSettings/IntegrationSettings.module.css'; - -const cx = cn.bind(styles); - -interface IntegrationSettingsProps { - id: AlertReceiveChannel['id']; - alertGroupId?: Alert['pk']; - startTab?: IntegrationSettingsTab; - onHide: () => void; - onUpdate?: () => void; - onUpdateTemplates?: () => void; -} - -const IntegrationSettings = observer((props: IntegrationSettingsProps) => { - const { id, onHide, onUpdate, onUpdateTemplates, startTab, alertGroupId } = props; - const [activeTab, setActiveTab] = useState(startTab || IntegrationSettingsTab.Templates); - const [selectedTemplate, setSelectedTemplate] = useState(''); - - const store = useStore(); - - const { alertReceiveChannelStore } = store; - - const alertReceiveChannel = alertReceiveChannelStore.items[id]; - - const getTabClickHandler = useCallback((tab: IntegrationSettingsTab) => { - return () => { - setActiveTab(tab); - LocationHelper.update({ tab }, 'partial'); - }; - }, []); - - useEffect(() => { - alertReceiveChannelStore.updateItem(id); - }, []); - - const integration = alertReceiveChannelStore.getIntegration(alertReceiveChannel); - - const [expanded, _setExpanded] = useState(false); - - const handleSwitchToTemplate = (templateName: string) => { - setActiveTab(IntegrationSettingsTab.Templates); - setSelectedTemplate(templateName); - }; - - return ( - - {integration && } -
- {alertReceiveChannel && ( - - settings - - )} - {integration && Type: {integration.display_name}} -
- - } - width={expanded ? '100%' : '70%'} - onClose={onHide} - closeOnMaskClick={false} - > - - - - {alertReceiveChannel?.is_available_for_integration_heartbeat && ( - - )} - - - - {activeTab === IntegrationSettingsTab.Templates && ( - - )} - {activeTab === IntegrationSettingsTab.Heartbeat && ( -
- -
- )} - {activeTab === IntegrationSettingsTab.Autoresolve && ( - - )} - {activeTab === IntegrationSettingsTab.HowToConnect && ( -
- - {alertReceiveChannel.integration_url && ( -
-

This is the unique webhook URL for the integration:

-
- { - openNotification('Unique webhook URL copied'); - }} - > -
-
- )} -
- - -
- )} - - - ); -}); - -export default IntegrationSettings; diff --git a/grafana-plugin/src/containers/IntegrationSettings/IntegrationSettings.types.ts b/grafana-plugin/src/containers/IntegrationSettings/IntegrationSettings.types.ts deleted file mode 100644 index edd380851f..0000000000 --- a/grafana-plugin/src/containers/IntegrationSettings/IntegrationSettings.types.ts +++ /dev/null @@ -1,7 +0,0 @@ -export enum IntegrationSettingsTab { - Templates = 'Templates', - Heartbeat = 'Heartbeat', - Autoresolve = 'Autoresolve', - LiveLogs = 'LiveLogs', - HowToConnect = 'HowToConnect', -} diff --git a/grafana-plugin/src/containers/IntegrationSettings/parts/Autoresolve.module.css b/grafana-plugin/src/containers/IntegrationSettings/parts/Autoresolve.module.css deleted file mode 100644 index 0614492ed1..0000000000 --- a/grafana-plugin/src/containers/IntegrationSettings/parts/Autoresolve.module.css +++ /dev/null @@ -1,47 +0,0 @@ -.border-container { - margin-bottom: 32px; - padding-bottom: 32px; - border-bottom: var(--border); -} - -.root ul { - margin-left: 24px; -} - -.settings-label { - display: grid; -} - -.team-select { - width: 520px; -} - -.team-select-actionbuttons { - margin-top: 24px; -} - -.confirmation-buttons { - display: flex; - flex-direction: row-reverse; -} - -.confirmation-buttons .save-team-button { - margin-left: 8px; -} - -.autoresolve-block { - height: 32px; - padding: 4px 8px; - margin-top: 8px; - min-width: 500px; - width: 620px; -} - -.autoresolve-div { - display: flex; - align-items: baseline; -} - -.warning-icon-color { - color: var(--warning-text-color); -} diff --git a/grafana-plugin/src/containers/IntegrationSettings/parts/Autoresolve.tsx b/grafana-plugin/src/containers/IntegrationSettings/parts/Autoresolve.tsx deleted file mode 100644 index 6b4e8c8382..0000000000 --- a/grafana-plugin/src/containers/IntegrationSettings/parts/Autoresolve.tsx +++ /dev/null @@ -1,236 +0,0 @@ -import React, { useCallback, useState, useEffect } from 'react'; - -import { Alert, Button, Icon, Label, Modal, Select } from '@grafana/ui'; -import cn from 'classnames/bind'; -import { get } from 'lodash-es'; - -import Block from 'components/GBlock/Block'; -import Text from 'components/Text/Text'; -import GSelect from 'containers/GSelect/GSelect'; -import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip'; -import { AlertReceiveChannel } from 'models/alert_receive_channel/alert_receive_channel.types'; -import { Alert as AlertType } from 'models/alertgroup/alertgroup.types'; -import { Team } from 'models/team/team.types'; -import { useStore } from 'state/useStore'; -import { openErrorNotification, openNotification } from 'utils'; -import LocationHelper from 'utils/LocationHelper'; -import { UserActions } from 'utils/authorization'; - -import styles from 'containers/IntegrationSettings/parts/Autoresolve.module.css'; - -const cx = cn.bind(styles); - -interface AutoresolveProps { - alertReceiveChannelId: AlertReceiveChannel['id']; - alertGroupId?: AlertType['pk']; - onSwitchToTemplate?: (templateName: string) => void; -} - -const Autoresolve = ({ alertReceiveChannelId, onSwitchToTemplate, alertGroupId }: AutoresolveProps) => { - const store = useStore(); - const { alertReceiveChannelStore, grafanaTeamStore, userStore } = store; - - const currentTeam = userStore.currentUser?.current_team; - - const alertReceiveChannel = alertReceiveChannelStore.items[alertReceiveChannelId]; - - const [teamId, setTeamId] = useState(alertReceiveChannel.team); - const [showSaveConfirmationModal, setShowSaveConfirmationModal] = useState(false); - const [autoresolveChanged, setAutoresolveChanged] = useState(false); - const [autoresolveValue, setAutoresolveValue] = useState(alertReceiveChannel?.allow_source_based_resolving); - const [showErrorOnTeamSelect, setShowErrorOnTeamSelect] = useState(false); - const [autoresolveSelected, setAutoresolveSelected] = useState( - alertReceiveChannel?.allow_source_based_resolving - ); - const [autoresolveConditionInvalid, setAutoresolveConditionInvalid] = useState(false); - - useEffect(() => { - store.alertReceiveChannelStore.updateItem(alertReceiveChannelId); - store.alertReceiveChannelStore.updateTemplates(alertReceiveChannelId, alertGroupId); - }, [alertGroupId, alertReceiveChannelId, store]); - - useEffect(() => { - const autoresolveCondition = get( - store.alertReceiveChannelStore.templates[alertReceiveChannelId], - 'resolve_condition_template' - ); - if (autoresolveCondition === 'invalid template') { - setAutoresolveConditionInvalid(true); - } - }, [store.alertReceiveChannelStore.templates[alertReceiveChannelId]]); - - const handleAutoresolveSelected = useCallback( - (autoresolveSelectedOption) => { - setAutoresolveChanged(true); - setAutoresolveValue(autoresolveSelectedOption?.value); - if (autoresolveSelectedOption?.value === 'true') { - setAutoresolveSelected(true); - } - if (autoresolveSelectedOption?.value === 'false') { - setAutoresolveSelected(false); - } - }, - [autoresolveChanged] - ); - - const handleChangeTeam = useCallback( - (value: Team['pk']) => { - setTeamId(value); - }, - [teamId] - ); - - const handleSaveTeam = () => { - store.alertReceiveChannelStore - .changeTeam(alertReceiveChannelId, teamId) - .then(async () => { - await alertReceiveChannelStore.updateItems(); - setShowSaveConfirmationModal(false); - openNotification( - `Integration moved from ${grafanaTeamStore.items[currentTeam]?.name} to ${grafanaTeamStore.items[teamId]?.name}` - ); - if (alertReceiveChannelId === store.selectedAlertReceiveChannel) { - const searchResult = alertReceiveChannelStore.getSearchResult(); - - store.selectedAlertReceiveChannel = searchResult && searchResult[0] && searchResult[0].id; - } - }) - .catch((error) => { - openErrorNotification(get(error, 'response.data.detail')); - setShowSaveConfirmationModal(false); - setShowErrorOnTeamSelect(true); - }); - }; - - const handleSaveClick = () => { - if (teamId !== currentTeam) { - setShowSaveConfirmationModal(true); - } - if (autoresolveChanged) { - store.alertReceiveChannelStore.saveAlertReceiveChannel(alertReceiveChannelId, { - allow_source_based_resolving: autoresolveValue, - }); - } - }; - - const handleGoToTemplateSettingsCllick = () => { - LocationHelper.update({ tab: 'Templates' }, 'partial'); - onSwitchToTemplate('resolve_condition_template'); - }; - - return ( - <> - -
- - - - -
-
- -
- -