Alerting: Contact points v2 – part 1 (#70643)

This commit is contained in:
Gilles De Mey 2023-07-04 12:47:19 +02:00 committed by GitHub
parent 3640bf77ba
commit 368afc211c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 412 additions and 110 deletions

View File

@ -199,6 +199,7 @@ export const availableIconsIndex = {
'square-shape': true,
star: true,
'step-backward': true,
stopwatch: true,
'stopwatch-slash': true,
sync: true,
'sync-slash': true,

View File

@ -120,6 +120,7 @@
"unicons/star",
"unicons/step-backward",
"unicons/sync",
"unicons/stopwatch",
"unicons/table",
"unicons/tag-alt",
"unicons/times",

View File

@ -128,52 +128,53 @@ import u1117 from '../../../../../public/img/icons/unicons/square-shape.svg';
import u1118 from '../../../../../public/img/icons/unicons/star.svg';
import u1119 from '../../../../../public/img/icons/unicons/step-backward.svg';
import u1120 from '../../../../../public/img/icons/unicons/sync.svg';
import u1121 from '../../../../../public/img/icons/unicons/table.svg';
import u1122 from '../../../../../public/img/icons/unicons/tag-alt.svg';
import u1123 from '../../../../../public/img/icons/unicons/times.svg';
import u1124 from '../../../../../public/img/icons/unicons/trash-alt.svg';
import u1125 from '../../../../../public/img/icons/unicons/unlock.svg';
import u1126 from '../../../../../public/img/icons/unicons/upload.svg';
import u1127 from '../../../../../public/img/icons/unicons/user.svg';
import u1128 from '../../../../../public/img/icons/unicons/users-alt.svg';
import u1129 from '../../../../../public/img/icons/unicons/wrap-text.svg';
import u1130 from '../../../../../public/img/icons/unicons/cloud-upload.svg';
import u1131 from '../../../../../public/img/icons/unicons/credit-card.svg';
import u1132 from '../../../../../public/img/icons/unicons/file-copy-alt.svg';
import u1133 from '../../../../../public/img/icons/unicons/fire.svg';
import u1134 from '../../../../../public/img/icons/unicons/hourglass.svg';
import u1135 from '../../../../../public/img/icons/unicons/layer-group.svg';
import u1136 from '../../../../../public/img/icons/unicons/layers-alt.svg';
import u1137 from '../../../../../public/img/icons/unicons/line-alt.svg';
import u1138 from '../../../../../public/img/icons/unicons/list-ui-alt.svg';
import u1139 from '../../../../../public/img/icons/unicons/message.svg';
import u1140 from '../../../../../public/img/icons/unicons/palette.svg';
import u1141 from '../../../../../public/img/icons/unicons/percentage.svg';
import u1142 from '../../../../../public/img/icons/unicons/shield-exclamation.svg';
import u1143 from '../../../../../public/img/icons/unicons/plus-square.svg';
import u1144 from '../../../../../public/img/icons/unicons/x.svg';
import u1145 from '../../../../../public/img/icons/unicons/capture.svg';
import u1146 from '../../../../../public/img/icons/custom/gf-grid.svg';
import u1147 from '../../../../../public/img/icons/custom/gf-landscape.svg';
import u1148 from '../../../../../public/img/icons/custom/gf-layout-simple.svg';
import u1149 from '../../../../../public/img/icons/custom/gf-portrait.svg';
import u1150 from '../../../../../public/img/icons/custom/gf-bar-alignment-after.svg';
import u1151 from '../../../../../public/img/icons/custom/gf-bar-alignment-before.svg';
import u1152 from '../../../../../public/img/icons/custom/gf-bar-alignment-center.svg';
import u1153 from '../../../../../public/img/icons/custom/gf-interpolation-linear.svg';
import u1154 from '../../../../../public/img/icons/custom/gf-interpolation-smooth.svg';
import u1155 from '../../../../../public/img/icons/custom/gf-interpolation-step-after.svg';
import u1156 from '../../../../../public/img/icons/custom/gf-interpolation-step-before.svg';
import u1157 from '../../../../../public/img/icons/custom/gf-logs.svg';
import u1158 from '../../../../../public/img/icons/custom/gf-movepane-left.svg';
import u1159 from '../../../../../public/img/icons/custom/gf-movepane-right.svg';
import u1160 from '../../../../../public/img/icons/mono/favorite.svg';
import u1161 from '../../../../../public/img/icons/mono/grafana.svg';
import u1162 from '../../../../../public/img/icons/mono/heart.svg';
import u1163 from '../../../../../public/img/icons/mono/heart-break.svg';
import u1164 from '../../../../../public/img/icons/mono/panel-add.svg';
import u1165 from '../../../../../public/img/icons/mono/library-panel.svg';
import u1166 from '../../../../../public/img/icons/unicons/record-audio.svg';
import u1121 from '../../../../../public/img/icons/unicons/stopwatch.svg';
import u1122 from '../../../../../public/img/icons/unicons/table.svg';
import u1123 from '../../../../../public/img/icons/unicons/tag-alt.svg';
import u1124 from '../../../../../public/img/icons/unicons/times.svg';
import u1125 from '../../../../../public/img/icons/unicons/trash-alt.svg';
import u1126 from '../../../../../public/img/icons/unicons/unlock.svg';
import u1127 from '../../../../../public/img/icons/unicons/upload.svg';
import u1128 from '../../../../../public/img/icons/unicons/user.svg';
import u1129 from '../../../../../public/img/icons/unicons/users-alt.svg';
import u1130 from '../../../../../public/img/icons/unicons/wrap-text.svg';
import u1131 from '../../../../../public/img/icons/unicons/cloud-upload.svg';
import u1132 from '../../../../../public/img/icons/unicons/credit-card.svg';
import u1133 from '../../../../../public/img/icons/unicons/file-copy-alt.svg';
import u1134 from '../../../../../public/img/icons/unicons/fire.svg';
import u1135 from '../../../../../public/img/icons/unicons/hourglass.svg';
import u1136 from '../../../../../public/img/icons/unicons/layer-group.svg';
import u1137 from '../../../../../public/img/icons/unicons/layers-alt.svg';
import u1138 from '../../../../../public/img/icons/unicons/line-alt.svg';
import u1139 from '../../../../../public/img/icons/unicons/list-ui-alt.svg';
import u1140 from '../../../../../public/img/icons/unicons/message.svg';
import u1141 from '../../../../../public/img/icons/unicons/palette.svg';
import u1142 from '../../../../../public/img/icons/unicons/percentage.svg';
import u1143 from '../../../../../public/img/icons/unicons/shield-exclamation.svg';
import u1144 from '../../../../../public/img/icons/unicons/plus-square.svg';
import u1145 from '../../../../../public/img/icons/unicons/x.svg';
import u1146 from '../../../../../public/img/icons/unicons/capture.svg';
import u1147 from '../../../../../public/img/icons/custom/gf-grid.svg';
import u1148 from '../../../../../public/img/icons/custom/gf-landscape.svg';
import u1149 from '../../../../../public/img/icons/custom/gf-layout-simple.svg';
import u1150 from '../../../../../public/img/icons/custom/gf-portrait.svg';
import u1151 from '../../../../../public/img/icons/custom/gf-bar-alignment-after.svg';
import u1152 from '../../../../../public/img/icons/custom/gf-bar-alignment-before.svg';
import u1153 from '../../../../../public/img/icons/custom/gf-bar-alignment-center.svg';
import u1154 from '../../../../../public/img/icons/custom/gf-interpolation-linear.svg';
import u1155 from '../../../../../public/img/icons/custom/gf-interpolation-smooth.svg';
import u1156 from '../../../../../public/img/icons/custom/gf-interpolation-step-after.svg';
import u1157 from '../../../../../public/img/icons/custom/gf-interpolation-step-before.svg';
import u1158 from '../../../../../public/img/icons/custom/gf-logs.svg';
import u1159 from '../../../../../public/img/icons/custom/gf-movepane-left.svg';
import u1160 from '../../../../../public/img/icons/custom/gf-movepane-right.svg';
import u1161 from '../../../../../public/img/icons/mono/favorite.svg';
import u1162 from '../../../../../public/img/icons/mono/grafana.svg';
import u1163 from '../../../../../public/img/icons/mono/heart.svg';
import u1164 from '../../../../../public/img/icons/mono/heart-break.svg';
import u1165 from '../../../../../public/img/icons/mono/panel-add.svg';
import u1166 from '../../../../../public/img/icons/mono/library-panel.svg';
import u1167 from '../../../../../public/img/icons/unicons/record-audio.svg';
// do not edit this list directly
// the list of icons live here: @grafana/ui/components/Icon/cached.json
@ -317,52 +318,53 @@ export function initIconCache() {
cacheItem(u1118, 'unicons/star.svg');
cacheItem(u1119, 'unicons/step-backward.svg');
cacheItem(u1120, 'unicons/sync.svg');
cacheItem(u1121, 'unicons/table.svg');
cacheItem(u1122, 'unicons/tag-alt.svg');
cacheItem(u1123, 'unicons/times.svg');
cacheItem(u1124, 'unicons/trash-alt.svg');
cacheItem(u1125, 'unicons/unlock.svg');
cacheItem(u1126, 'unicons/upload.svg');
cacheItem(u1127, 'unicons/user.svg');
cacheItem(u1128, 'unicons/users-alt.svg');
cacheItem(u1129, 'unicons/wrap-text.svg');
cacheItem(u1130, 'unicons/cloud-upload.svg');
cacheItem(u1131, 'unicons/credit-card.svg');
cacheItem(u1132, 'unicons/file-copy-alt.svg');
cacheItem(u1133, 'unicons/fire.svg');
cacheItem(u1134, 'unicons/hourglass.svg');
cacheItem(u1135, 'unicons/layer-group.svg');
cacheItem(u1136, 'unicons/layers-alt.svg');
cacheItem(u1137, 'unicons/line-alt.svg');
cacheItem(u1138, 'unicons/list-ui-alt.svg');
cacheItem(u1139, 'unicons/message.svg');
cacheItem(u1140, 'unicons/palette.svg');
cacheItem(u1141, 'unicons/percentage.svg');
cacheItem(u1142, 'unicons/shield-exclamation.svg');
cacheItem(u1143, 'unicons/plus-square.svg');
cacheItem(u1144, 'unicons/x.svg');
cacheItem(u1145, 'unicons/capture.svg');
cacheItem(u1146, 'custom/gf-grid.svg');
cacheItem(u1147, 'custom/gf-landscape.svg');
cacheItem(u1148, 'custom/gf-layout-simple.svg');
cacheItem(u1149, 'custom/gf-portrait.svg');
cacheItem(u1150, 'custom/gf-bar-alignment-after.svg');
cacheItem(u1151, 'custom/gf-bar-alignment-before.svg');
cacheItem(u1152, 'custom/gf-bar-alignment-center.svg');
cacheItem(u1153, 'custom/gf-interpolation-linear.svg');
cacheItem(u1154, 'custom/gf-interpolation-smooth.svg');
cacheItem(u1155, 'custom/gf-interpolation-step-after.svg');
cacheItem(u1156, 'custom/gf-interpolation-step-before.svg');
cacheItem(u1157, 'custom/gf-logs.svg');
cacheItem(u1158, 'custom/gf-movepane-left.svg');
cacheItem(u1159, 'custom/gf-movepane-right.svg');
cacheItem(u1160, 'mono/favorite.svg');
cacheItem(u1161, 'mono/grafana.svg');
cacheItem(u1162, 'mono/heart.svg');
cacheItem(u1163, 'mono/heart-break.svg');
cacheItem(u1164, 'mono/panel-add.svg');
cacheItem(u1165, 'mono/library-panel.svg');
cacheItem(u1166, 'unicons/record-audio.svg');
cacheItem(u1121, 'unicons/stopwatch.svg');
cacheItem(u1122, 'unicons/table.svg');
cacheItem(u1123, 'unicons/tag-alt.svg');
cacheItem(u1124, 'unicons/times.svg');
cacheItem(u1125, 'unicons/trash-alt.svg');
cacheItem(u1126, 'unicons/unlock.svg');
cacheItem(u1127, 'unicons/upload.svg');
cacheItem(u1128, 'unicons/user.svg');
cacheItem(u1129, 'unicons/users-alt.svg');
cacheItem(u1130, 'unicons/wrap-text.svg');
cacheItem(u1131, 'unicons/cloud-upload.svg');
cacheItem(u1132, 'unicons/credit-card.svg');
cacheItem(u1133, 'unicons/file-copy-alt.svg');
cacheItem(u1134, 'unicons/fire.svg');
cacheItem(u1135, 'unicons/hourglass.svg');
cacheItem(u1136, 'unicons/layer-group.svg');
cacheItem(u1137, 'unicons/layers-alt.svg');
cacheItem(u1138, 'unicons/line-alt.svg');
cacheItem(u1139, 'unicons/list-ui-alt.svg');
cacheItem(u1140, 'unicons/message.svg');
cacheItem(u1141, 'unicons/palette.svg');
cacheItem(u1142, 'unicons/percentage.svg');
cacheItem(u1143, 'unicons/shield-exclamation.svg');
cacheItem(u1144, 'unicons/plus-square.svg');
cacheItem(u1145, 'unicons/x.svg');
cacheItem(u1146, 'unicons/capture.svg');
cacheItem(u1147, 'custom/gf-grid.svg');
cacheItem(u1148, 'custom/gf-landscape.svg');
cacheItem(u1149, 'custom/gf-layout-simple.svg');
cacheItem(u1150, 'custom/gf-portrait.svg');
cacheItem(u1151, 'custom/gf-bar-alignment-after.svg');
cacheItem(u1152, 'custom/gf-bar-alignment-before.svg');
cacheItem(u1153, 'custom/gf-bar-alignment-center.svg');
cacheItem(u1154, 'custom/gf-interpolation-linear.svg');
cacheItem(u1155, 'custom/gf-interpolation-smooth.svg');
cacheItem(u1156, 'custom/gf-interpolation-step-after.svg');
cacheItem(u1157, 'custom/gf-interpolation-step-before.svg');
cacheItem(u1158, 'custom/gf-logs.svg');
cacheItem(u1159, 'custom/gf-movepane-left.svg');
cacheItem(u1160, 'custom/gf-movepane-right.svg');
cacheItem(u1161, 'mono/favorite.svg');
cacheItem(u1162, 'mono/grafana.svg');
cacheItem(u1163, 'mono/heart.svg');
cacheItem(u1164, 'mono/heart-break.svg');
cacheItem(u1165, 'mono/panel-add.svg');
cacheItem(u1166, 'mono/library-panel.svg');
cacheItem(u1167, 'unicons/record-audio.svg');
// do not edit this list directly
// the list of icons live here: @grafana/ui/components/Icon/cached.json
}

View File

@ -0,0 +1,12 @@
import React from 'react';
interface ConditionalWrapProps {
shouldWrap: boolean;
children: JSX.Element;
wrap: (children: JSX.Element) => JSX.Element;
}
export const ConditionalWrap = ({ shouldWrap, children, wrap }: ConditionalWrapProps): JSX.Element =>
shouldWrap ? React.cloneElement(wrap(children)) : children;
export default ConditionalWrap;

View File

@ -1,5 +1,267 @@
import { css } from '@emotion/css';
import React from 'react';
const ContactPoints = () => <>Hello, contact points v2!</>;
import { GrafanaTheme2 } from '@grafana/data';
import { Stack } from '@grafana/experimental';
import { Badge, Button, Dropdown, Icon, Menu, Tooltip, useStyles2 } from '@grafana/ui';
import { Span } from '@grafana/ui/src/unstable';
import ConditionalWrap from 'app/features/alerting/components/ConditionalWrap';
import { GrafanaNotifierType } from 'app/types/alerting';
import { INTEGRATION_ICONS } from '../../types/contact-points';
import { MetaText } from '../MetaText';
import { Spacer } from '../Spacer';
import { Strong } from '../Strong';
const ContactPoints = () => {
const styles = useStyles2(getStyles);
return (
<Stack direction="column">
<div className={styles.contactPointWrapper}>
<Stack direction="column" gap={0}>
<ContactPointHeader name={'grafana-default-email'} policies={['', '']} />
<div className={styles.receiversWrapper}>
<ContactPointReceiver type={'email'} description="gilles.demey@grafana.com" />
</div>
</Stack>
</div>
<div className={styles.contactPointWrapper}>
<Stack direction="column" gap={0}>
<ContactPointHeader name={'New school'} provenance={'api'} />
<div className={styles.receiversWrapper}>
<Stack direction="column" gap={0}>
<ContactPointReceiver type={'slack'} description="#test-alerts" sendingResolved={false} />
<ContactPointReceiver type={'discord'} />
</Stack>
</div>
</Stack>
</div>
<div className={styles.contactPointWrapper}>
<Stack direction="column" gap={0}>
<ContactPointHeader name={'Japan 🇯🇵'} />
<div className={styles.receiversWrapper}>
<ContactPointReceiver type={'line'} />
</div>
</Stack>
</div>
<div className={styles.contactPointWrapper}>
<Stack direction="column" gap={0}>
<ContactPointHeader name={'Google Stuff'} />
<div className={styles.receiversWrapper}>
<ContactPointReceiver type={'googlechat'} />
</div>
</Stack>
</div>
<div className={styles.contactPointWrapper}>
<Stack direction="column" gap={0}>
<ContactPointHeader name={'Chinese Contact Points'} />
<div className={styles.receiversWrapper}>
<Stack direction="column" gap={0}>
<ContactPointReceiver type={'dingding'} />
<ContactPointReceiver type={'wecom'} error="403 unauthorized" />
</Stack>
</div>
</Stack>
</div>
<div className={styles.contactPointWrapper}>
<Stack direction="column" gap={0}>
<ContactPointHeader
name={
"This is a very long title to check if we are dealing with it appropriately, it shouldn't cause any layout issues"
}
/>
<div className={styles.receiversWrapper}>
<Stack direction="column" gap={0}>
<ContactPointReceiver type={'dingding'} />
</Stack>
</div>
</Stack>
</div>
</Stack>
);
};
interface ContactPointHeaderProps {
name: string;
provenance?: string;
policies?: string[]; // some array of policies that refer to this contact point
}
const ContactPointHeader = (props: ContactPointHeaderProps) => {
const { name, provenance, policies = [] } = props;
const styles = useStyles2(getStyles);
const isProvisioned = Boolean(provenance);
return (
<div className={styles.headerWrapper}>
<Stack direction="row" alignItems="center" gap={1}>
<Stack alignItems="center" gap={1}>
<Span variant="body">{name}</Span>
</Stack>
{policies.length > 0 ? (
<MetaText>
{/* TODO make this a link to the notification policies page with the filter applied */}
is used by <Strong>{policies.length}</Strong> notification policies
</MetaText>
) : (
<MetaText>is not used</MetaText>
)}
{isProvisioned && <Badge color="purple" text="Provisioned" />}
<Spacer />
<ConditionalWrap
shouldWrap={isProvisioned}
wrap={(children) => (
<Tooltip content="Provisioned items cannot be edited in the UI" placement="top">
{children}
</Tooltip>
)}
>
<Button
variant="secondary"
size="sm"
icon="edit"
type="button"
disabled={isProvisioned}
aria-label="edit-action"
data-testid="edit-action"
>
Edit
</Button>
</ConditionalWrap>
<Dropdown
overlay={
<Menu>
<Menu.Item label="Export" icon="download-alt" />
<Menu.Divider />
<Menu.Item label="Delete" icon="trash-alt" destructive disabled={isProvisioned} />
</Menu>
}
>
<Button
variant="secondary"
size="sm"
icon="ellipsis-h"
type="button"
aria-label="more-actions"
data-testid="more-actions"
/>
</Dropdown>
</Stack>
</div>
);
};
interface ContactPointReceiverProps {
type: GrafanaNotifierType | string;
description?: string;
error?: string;
sendingResolved?: boolean;
}
const ContactPointReceiver = (props: ContactPointReceiverProps) => {
const { type, description, error, sendingResolved = true } = props;
const styles = useStyles2(getStyles);
const iconName = INTEGRATION_ICONS[type];
return (
<div className={styles.integrationWrapper}>
<Stack direction="column" gap={0}>
<div className={styles.receiverDescriptionRow}>
<Stack direction="row" alignItems="center" gap={1}>
<Stack direction="row" alignItems="center" gap={0.5}>
{iconName && <Icon name={iconName} />}
<Span variant="body" color="primary">
{type}
</Span>
</Stack>
{description && (
<Span variant="bodySmall" color="secondary">
{description}
</Span>
)}
</Stack>
</div>
<div className={styles.metadataRow}>
<Stack direction="row" gap={1}>
{error ? (
<>
{/* TODO we might need an error variant for MetaText, dito for success */}
{/* TODO show error details on hover or elsewhere */}
<Span color="error" variant="bodySmall" weight="bold">
<Stack direction="row" alignItems={'center'} gap={0.5}>
<Tooltip
content={
'failed to send notification to email addresses: gilles.demey@grafana.com: dial tcp 192.168.1.21:1025: connect: connection refused'
}
>
<span>
<Icon name="exclamation-circle" /> Last delivery attempt failed
</span>
</Tooltip>
</Stack>
</Span>
</>
) : (
<>
<MetaText icon="clock-nine">
Last delivery attempt <Strong>25 minutes ago</Strong>
</MetaText>
<MetaText icon="stopwatch">
took <Strong>2s</Strong>
</MetaText>
</>
)}
{!sendingResolved && (
<MetaText icon="info-circle">
Delivering <Strong>only firing</Strong> notifications
</MetaText>
)}
</Stack>
</div>
</Stack>
</div>
);
};
const getStyles = (theme: GrafanaTheme2) => ({
contactPointWrapper: css`
border-radius: ${theme.shape.borderRadius()};
border: solid 1px ${theme.colors.border.weak};
border-bottom: none;
`,
integrationWrapper: css`
position: relative;
background: ${theme.colors.background.primary};
border-bottom: solid 1px ${theme.colors.border.weak};
`,
headerWrapper: css`
padding: ${theme.spacing(1)} ${theme.spacing(1.5)};
background: ${theme.colors.background.secondary};
border-bottom: solid 1px ${theme.colors.border.weak};
border-top-left-radius: ${theme.shape.borderRadius()};
border-top-right-radius: ${theme.shape.borderRadius()};
`,
receiverDescriptionRow: css`
padding: ${theme.spacing(1)} ${theme.spacing(1.5)};
`,
metadataRow: css`
padding: 0 ${theme.spacing(1.5)} ${theme.spacing(1.5)} ${theme.spacing(1.5)};
border-bottom-left-radius: ${theme.shape.borderRadius()};
border-bottom-right-radius: ${theme.shape.borderRadius()};
`,
receiversWrapper: css``,
});
export default ContactPoints;

View File

@ -0,0 +1,23 @@
/**
* This hook will combine data from both the Alertmanager config
* and (if available) it will also fetch the status from the Grafana Managed status endpoint
*/
import { NotifierType, NotifierStatus } from 'app/types';
// A Contact Point has 1 or more integrations
// each integration can have additional metadata assigned to it
export interface ContactPoint<T extends Notifier> {
notifiers: T[];
}
interface Notifier {
type: NotifierType;
}
// Grafana Managed contact points have receivers with additional diagnostics
export interface NotifierWithDiagnostics extends Notifier {
status: NotifierStatus;
}
export function useContactPoints(AlertManagerSourceName: string) {}

View File

@ -4,7 +4,7 @@ import pluralize from 'pluralize';
import React, { FC, Fragment, ReactNode } from 'react';
import { Link } from 'react-router-dom';
import { GrafanaTheme2, IconName } from '@grafana/data';
import { GrafanaTheme2 } from '@grafana/data';
import { Stack } from '@grafana/experimental';
import { Badge, Button, Dropdown, getTagColorsFromName, Icon, Menu, Tooltip, useStyles2 } from '@grafana/ui';
import { Span } from '@grafana/ui/src/unstable';
@ -12,6 +12,7 @@ import { contextSrv } from 'app/core/core';
import { RouteWithID, Receiver, ObjectMatcher, AlertmanagerGroup } from 'app/plugins/datasource/alertmanager/types';
import { ReceiversState } from 'app/types';
import { INTEGRATION_ICONS } from '../../types/contact-points';
import { getNotificationsPermissions } from '../../utils/access-control';
import { normalizeMatchers } from '../../utils/matchers';
import { createContactPointLink, createMuteTimingLink } from '../../utils/misc';
@ -130,7 +131,7 @@ const Policy: FC<PolicyComponentProps> = ({
<div className={styles.policyItemWrapper}>
<Stack direction="column" gap={1}>
{/* Matchers and actions */}
<div className={styles.matchersRow}>
<div>
<Stack direction="row" alignItems="center" gap={1}>
{isDefaultPolicy ? (
<DefaultPolicyIndicator />
@ -429,18 +430,6 @@ interface ContactPointDetailsProps {
receivers: Receiver[];
}
const INTEGRATION_ICONS: Record<string, IconName> = {
discord: 'discord',
email: 'envelope',
googlechat: 'google-hangouts-alt',
hipchat: 'hipchat',
line: 'line',
pagerduty: 'pagerduty',
slack: 'slack',
teams: 'microsoft',
telegram: 'telegram-alt',
};
// @TODO make this work for cloud AMs too
const ContactPointsHoverDetails: FC<ContactPointDetailsProps> = ({
alertManagerSourceName,
@ -601,10 +590,9 @@ const getStyles = (theme: GrafanaTheme2) => ({
metadataRow: css`
background: ${theme.colors.background.secondary};
border-bottom-left-radius: ${theme.shape.borderRadius(1)};
border-bottom-right-radius: ${theme.shape.borderRadius(1)};
border-bottom-left-radius: ${theme.shape.borderRadius(2)};
border-bottom-right-radius: ${theme.shape.borderRadius(2)};
`,
matchersRow: css``,
policyWrapper: (hasFocus = false) => css`
flex: 1;
position: relative;

View File

@ -0,0 +1,13 @@
import { IconName } from '@grafana/ui';
export const INTEGRATION_ICONS: Record<string, IconName> = {
discord: 'discord',
email: 'envelope',
googlechat: 'google-hangouts-alt',
hipchat: 'hipchat',
line: 'line',
pagerduty: 'pagerduty',
slack: 'slack',
teams: 'microsoft',
telegram: 'telegram-alt',
};