add tabs for snow integration (#3726)

# What this PR does
add incoming / outgoing tabs when integration is service now

## Which issue(s) this PR fixes
https://github.com/grafana/oncall-private/issues/2460

## Checklist

- [x] Unit, integration, and e2e (if applicable) tests updated
- [x] Documentation added (or `pr:no public docs` PR label added if not
required)
- [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not
required)
This commit is contained in:
Dominik Broj 2024-01-22 16:24:54 +01:00 committed by GitHub
parent 32b11196c8
commit fdbccdac99
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 90 additions and 25 deletions

View file

@ -0,0 +1,47 @@
import React, { FC, useState } from 'react';
import { css } from '@emotion/css';
import { Tab, TabsBar, TabContent, useStyles2 } from '@grafana/ui';
import cn from 'classnames';
interface TabConfig {
label: string;
content: React.ReactNode;
}
interface TabsProps {
tabs: TabConfig[];
defaultActiveLabel?: string;
tabContentClassName?: string;
}
const Tabs: FC<TabsProps> = ({ tabs, defaultActiveLabel, tabContentClassName }) => {
const styles = useStyles2(getStyles);
const [activeTabLabel, setActiveTabLabel] = useState(defaultActiveLabel || tabs[0].label);
return (
<>
<TabsBar>
{tabs.map(({ label }) => (
<Tab
label={label}
key={label}
onChangeTab={() => setActiveTabLabel(label)}
active={activeTabLabel === label}
/>
))}
</TabsBar>
<TabContent className={cn(styles.content, tabContentClassName)}>
{tabs.find(({ label }) => label === activeTabLabel)?.content}
</TabContent>
</>
);
};
export const getStyles = () => ({
content: css({
marginTop: '16px',
}),
});
export default Tabs;

View file

@ -125,3 +125,5 @@ const IntegrationHelper = {
};
export default IntegrationHelper;
export const getIsBidirectionalIntegration = ({ integration }: AlertReceiveChannel) => integration === 'servicenow';

View file

@ -34,6 +34,7 @@ import PageErrorHandlingWrapper, { PageBaseState } from 'components/PageErrorHan
import { initErrorDataState } from 'components/PageErrorHandlingWrapper/PageErrorHandlingWrapper.helpers';
import PluginLink from 'components/PluginLink/PluginLink';
import RenderConditionally from 'components/RenderConditionally/RenderConditionally';
import Tabs from 'components/Tabs/Tabs';
import Tag from 'components/Tag/Tag';
import Text from 'components/Text/Text';
import TooltipBadge from 'components/TooltipBadge/TooltipBadge';
@ -57,7 +58,7 @@ import {
} from 'models/alert_receive_channel/alert_receive_channel.types';
import { AlertTemplatesDTO } from 'models/alert_templates/alert_templates';
import { ChannelFilter } from 'models/channel_filter/channel_filter.types';
import IntegrationHelper from 'pages/integration/Integration.helper';
import IntegrationHelper, { getIsBidirectionalIntegration } from 'pages/integration/Integration.helper';
import styles from 'pages/integration/Integration.module.scss';
import { AppFeature } from 'state/features';
import { PageProps, SelectOption, WithStoreProps } from 'state/types';
@ -155,6 +156,36 @@ class Integration extends React.Component<IntegrationProps, IntegrationState> {
const isLegacyIntegration = integration && (integration?.value as string).toLowerCase().startsWith('legacy_');
const contactPoints = alertReceiveChannelStore.connectedContactPoints?.[alertReceiveChannel.id];
const incomingPart = (
<>
<IntegrationCollapsibleTreeView configElements={this.getConfigForTreeComponent(id, templates) as any} />
{isEditTemplateModalOpen && (
<IntegrationTemplate
id={id}
onHide={() => {
this.setState({
isEditTemplateModalOpen: undefined,
});
if (selectedTemplate?.name !== 'route_template') {
this.setState({ isTemplateSettingsOpen: true });
}
LocationHelper.update({ template: undefined, routeId: undefined }, 'partial');
}}
channelFilterId={channelFilterIdForEdit}
onUpdateTemplates={this.onUpdateTemplatesCallback}
onUpdateRoute={this.onUpdateRoutesCallback}
template={selectedTemplate}
templateBody={
selectedTemplate?.name === 'route_template'
? this.getRoutingTemplate(channelFilterIdForEdit)
: templates[selectedTemplate?.name]
}
templates={templates}
/>
)}
</>
);
return (
<PageErrorHandlingWrapper errorData={errorData} objectName="integration" pageName="Integration">
{() => (
@ -223,32 +254,17 @@ class Integration extends React.Component<IntegrationProps, IntegrationState> {
)}
</div>
<IntegrationCollapsibleTreeView configElements={this.getConfigForTreeComponent(id, templates) as any} />
{isEditTemplateModalOpen && (
<IntegrationTemplate
id={id}
onHide={() => {
this.setState({
isEditTemplateModalOpen: undefined,
});
if (selectedTemplate?.name !== 'route_template') {
this.setState({ isTemplateSettingsOpen: true });
}
LocationHelper.update({ template: undefined, routeId: undefined }, 'partial');
}}
channelFilterId={channelFilterIdForEdit}
onUpdateTemplates={this.onUpdateTemplatesCallback}
onUpdateRoute={this.onUpdateRoutesCallback}
template={selectedTemplate}
templateBody={
selectedTemplate?.name === 'route_template'
? this.getRoutingTemplate(channelFilterIdForEdit)
: templates[selectedTemplate?.name]
}
templates={templates}
{getIsBidirectionalIntegration(alertReceiveChannel) ? (
<Tabs
tabs={[
{ label: 'Incoming', content: incomingPart },
{ label: 'Outgoing', content: <div>outgoing tab content</div> },
]}
/>
) : (
<>{incomingPart}</>
)}
{isEditRegexpRouteTemplateModalOpen && (
<EditRegexpRouteTemplateModal
alertReceiveChannelId={id}