enable toggling between manual routing

This commit is contained in:
laurenashleigh
2026-01-12 11:41:26 +00:00
parent 1430698a8b
commit ba59c5a7ee
3 changed files with 121 additions and 49 deletions
@@ -1,15 +1,35 @@
import { css } from '@emotion/css';
import { useMemo, useState } from 'react';
import { useMemo } from 'react';
import { useFormContext } from 'react-hook-form';
import { notificationsAPIv0alpha1 } from '@grafana/alerting/unstable';
import type { GrafanaTheme2 } from '@grafana/data';
import { Trans, t } from '@grafana/i18n';
import { Button, Combobox, ComboboxOption, Field, Input, Stack, TextArea, TextLink, useStyles2 } from '@grafana/ui';
import {
Button,
Combobox,
ComboboxOption,
Field,
Input,
Stack,
Switch,
Text,
TextArea,
TextLink,
useStyles2,
} from '@grafana/ui';
import { RuleFormValues } from '../types/rule-form';
import { NeedHelpInfoForNotificationPolicy } from './rule-editor/NotificationsStep';
export function RuleNotificationSection() {
const styles = useStyles2(getStyles);
const [contactPoint, setContactPoint] = useState<ComboboxOption<string> | null>(null);
const { watch, setValue } = useFormContext<RuleFormValues>();
const manualRouting = watch('manualRouting');
const useNotificationPolicy = !manualRouting;
const selectedContactPoint = watch('contactPoints.grafana.selectedContactPoint');
// Fetch contact points from Alerting API v0alpha1
const { currentData, status, refetch } = notificationsAPIv0alpha1.endpoints.listReceiver.useQuery({});
const options = useMemo<Array<ComboboxOption<string>>>(
@@ -35,49 +55,95 @@ export function RuleNotificationSection() {
<div className={styles.contentIndented}>
<Stack direction="column" gap={2}>
<Field
label={t('alerting.simplified.notification.contact-point.label', 'Contact point')}
description={t(
'alerting.simplified.notification.contact-point.description',
'Select who should receive a notification when the alert rule fires'
)}
label={
<Stack direction="row" alignItems="center" justifyContent="space-between" gap={1}>
<span>{t('alerting.simplified.notification.recipient.label', 'Recipient')}</span>
<Stack direction="row" alignItems="center" gap={0.5}>
<Text variant="bodySmall" color="secondary">
{t('alerting.simplified.notification.manual-routing.label', 'Manual routing')}
</Text>
<Switch
value={manualRouting}
onChange={(e) => {
const next = e.currentTarget.checked;
setValue('manualRouting', next, { shouldDirty: true });
setValue('editorSettings.simplifiedNotificationEditor', next, { shouldDirty: true });
}}
aria-label={t('alerting.simplified.notification.manual-routing.aria', 'Toggle manual routing')}
/>
</Stack>
</Stack>
}
noMargin
>
<Stack direction="row" gap={1} alignItems="center">
<Combobox<ComboboxOption<string>['value']>
options={options}
value={contactPoint}
onChange={(opt) => setContactPoint(opt)}
width={30}
placeholder={t('alerting.simplified.notification.select-contact-point', 'Select a contact point...')}
isClearable
data-testid="contact-point"
loading={status === 'pending'}
/>
<Button
icon="sync"
variant="secondary"
fill="text"
size="sm"
aria-label={t('alerting.common.refresh', 'Refresh')}
onClick={() => {
if (refetch) {
refetch();
}
}}
/>
<TextLink
external
href={'/alerting/notifications'}
aria-label={t(
'alerting.link-to-contact-points.aria-label-view-or-create-contact-points',
'View or create contact points'
)}
>
<Trans i18nKey="alerting.link-to-contact-points.view-or-create-contact-points">
View or create contact points
</Trans>
</TextLink>
</Stack>
{useNotificationPolicy ? (
<div className={styles.contentTopSpacer}>
<Stack direction="column" gap={1} alignItems="start">
<Text variant="bodySmall" color="secondary">
<Trans i18nKey="alerting.simplified.notification.policy-selected">
Notifications for firing alerts are routed to contact points based on matching labels and the
notification policy tree.
</Trans>
</Text>
<NeedHelpInfoForNotificationPolicy />
</Stack>
</div>
) : (
<div className={styles.contentTopSpacer}>
<Stack direction="column" gap={1}>
<Text variant="bodySmall" color="secondary">
<Trans i18nKey="alerting.simplified.notification.contact-point-selected">
Notifications for firing alerts are routed to a selected contact point.
</Trans>
</Text>
<Stack direction="row" gap={1} alignItems="center">
<Combobox<ComboboxOption<string>['value']>
options={options}
value={
selectedContactPoint ? (options.find((o) => o.value === selectedContactPoint) ?? null) : null
}
onChange={(opt) =>
setValue('contactPoints.grafana.selectedContactPoint', opt?.value ?? '', {
shouldDirty: true,
})
}
width={30}
placeholder={t(
'alerting.simplified.notification.select-contact-point',
'Select a contact point...'
)}
isClearable
data-testid="contact-point"
loading={status === 'pending'}
/>
<Button
icon="sync"
variant="secondary"
fill="text"
size="sm"
aria-label={t('alerting.common.refresh', 'Refresh')}
onClick={() => {
if (refetch) {
refetch();
}
}}
/>
<TextLink
external
href={'/alerting/notifications'}
aria-label={t(
'alerting.link-to-contact-points.aria-label-view-or-create-contact-points',
'View or create contact points'
)}
>
<Trans i18nKey="alerting.link-to-contact-points.view-or-create-contact-points">
View or create contact points
</Trans>
</TextLink>
</Stack>
</Stack>
</div>
)}
</Field>
<Field label={t('alerting.simplified.notification.summary.label', 'Summary (optional)')} noMargin>
@@ -139,5 +205,6 @@ function getStyles(theme: GrafanaTheme2) {
fontWeight: 600,
}),
contentIndented: css({ marginLeft: `calc(20px + ${theme.spacing(1)})` }),
contentTopSpacer: css({ marginTop: theme.spacing(0.5) }),
};
}
@@ -250,7 +250,7 @@ function AutomaticRooting({ alertUid }: AutomaticRootingProps) {
}
// Auxiliar components to build the texts and descriptions in the NotificationsStep
function NeedHelpInfoForNotificationPolicy() {
export function NeedHelpInfoForNotificationPolicy() {
return (
<NeedHelpInfo
contentText={
+9 -4
View File
@@ -2824,14 +2824,19 @@
"immediate-warning": "Immediate firing might lead to unnecessary alerts being sent for temporary issues"
},
"notification": {
"contact-point": {
"description": "Select who should receive a notification when the alert rule fires",
"label": "Contact point"
},
"contact-point-selected": "Notifications for firing alerts are routed to a selected contact point.",
"description": {
"label": "Description (optional)",
"placeholder": "Enter a description of what the alert rule does…"
},
"manual-routing": {
"aria": "Toggle manual routing",
"label": "Manual routing"
},
"policy-selected": "Notifications for firing alerts are routed to contact points based on matching labels and the notification policy tree.",
"recipient": {
"label": "Recipient"
},
"runbook-url": {
"label": "Runbook URL (optional)",
"placeholder": "Enter the webpage where you keep your runbook for the alert…"