update rule definition section UI
This commit is contained in:
@@ -13,7 +13,7 @@ import { isCloudRecordingRuleByType, isGrafanaManagedRuleByType, isRecordingRule
|
||||
|
||||
import { FolderSelectorV2 } from './rule-editor/FolderSelectorV2';
|
||||
import { LabelsEditorModal } from './rule-editor/labels/LabelsEditorModal';
|
||||
import { LabelsFieldInForm } from './rule-editor/labels/LabelsFieldInForm';
|
||||
import { LabelsFieldInFormV2 } from './rule-editor/labels/LabelsFieldInFormV2';
|
||||
|
||||
export function RuleDefinitionSection({ type }: { type: RuleFormType }) {
|
||||
const styles = useStyles2(getStyles);
|
||||
@@ -43,7 +43,11 @@ export function RuleDefinitionSection({ type }: { type: RuleFormType }) {
|
||||
<Stack direction="column" gap={2}>
|
||||
<Field
|
||||
noMargin
|
||||
label={<Trans i18nKey="alerting.alert-rule-name-and-metric.label-name">Name</Trans>}
|
||||
label={
|
||||
<span className={styles.nameLabel}>
|
||||
<Trans i18nKey="alerting.alert-rule-name-and-metric.label-name">Name</Trans>
|
||||
</span>
|
||||
}
|
||||
error={errors?.name?.message}
|
||||
invalid={!!errors.name?.message}
|
||||
>
|
||||
@@ -78,7 +82,7 @@ export function RuleDefinitionSection({ type }: { type: RuleFormType }) {
|
||||
{isGrafanaManagedRuleByType(type) && (
|
||||
<>
|
||||
<FolderSelectorV2 />
|
||||
<LabelsFieldInForm showHelpTooltip onEditClick={() => setShowLabelsEditor(true)} labelVariant="small" />
|
||||
<LabelsFieldInFormV2 onEditClick={() => setShowLabelsEditor(true)} />
|
||||
<LabelsEditorModal
|
||||
isOpen={showLabelsEditor}
|
||||
onClose={(labelsToUpdate) => {
|
||||
@@ -128,5 +132,9 @@ function getStyles(theme: GrafanaTheme2) {
|
||||
fontWeight: 600,
|
||||
}),
|
||||
contentIndented: css({ marginLeft: `calc(20px + ${theme.spacing(1)})` }),
|
||||
nameLabel: css({
|
||||
fontSize: theme.typography.bodySmall.fontSize,
|
||||
fontWeight: 500,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@ import { useMemo, useState } from 'react';
|
||||
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, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { LinkToContactPoints } from './rule-editor/alert-rule-form/simplifiedRouting/contactPoint/ContactPointSelector';
|
||||
import { Button, Combobox, ComboboxOption, Field, Input, Stack, TextArea, TextLink, useStyles2 } from '@grafana/ui';
|
||||
|
||||
export function RuleNotificationSection() {
|
||||
const styles = useStyles2(getStyles);
|
||||
@@ -37,7 +35,7 @@ export function RuleNotificationSection() {
|
||||
<div className={styles.contentIndented}>
|
||||
<Stack direction="column" gap={2}>
|
||||
<Field
|
||||
label={t('alerting.simplified.notification.contact-point', 'Contact point')}
|
||||
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'
|
||||
@@ -67,11 +65,22 @@ export function RuleNotificationSection() {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<LinkToContactPoints />
|
||||
<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>
|
||||
</Field>
|
||||
|
||||
<Field label={t('alerting.simplified.notification.summary', 'Summary (optional)')} noMargin>
|
||||
<Field label={t('alerting.simplified.notification.summary.label', 'Summary (optional)')} noMargin>
|
||||
<TextArea
|
||||
placeholder={t(
|
||||
'alerting.simplified.notification.summary.placeholder',
|
||||
@@ -80,7 +89,7 @@ export function RuleNotificationSection() {
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<Field label={t('alerting.simplified.notification.description', 'Description (optional)')} noMargin>
|
||||
<Field label={t('alerting.simplified.notification.description.label', 'Description (optional)')} noMargin>
|
||||
<TextArea
|
||||
placeholder={t(
|
||||
'alerting.simplified.notification.description.placeholder',
|
||||
@@ -89,7 +98,7 @@ export function RuleNotificationSection() {
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<Field label={t('alerting.simplified.notification.runbook-url', 'Runbook URL (optional)')} noMargin>
|
||||
<Field label={t('alerting.simplified.notification.runbook-url.label', 'Runbook URL (optional)')} noMargin>
|
||||
<Input
|
||||
placeholder={t(
|
||||
'alerting.simplified.notification.runbook-url.placeholder',
|
||||
|
||||
@@ -55,7 +55,7 @@ export function FolderSelectorV2() {
|
||||
error={errors.folder?.message}
|
||||
data-testid="folder-picker"
|
||||
>
|
||||
<Stack direction="column" alignItems="flex-start" gap={0.5}>
|
||||
<Stack direction="column" alignItems="flex-start" gap={1}>
|
||||
<Controller
|
||||
render={({ field: { ref, ...field } }) => (
|
||||
<div style={{ width: 420 }}>
|
||||
|
||||
+7
-49
@@ -1,7 +1,7 @@
|
||||
import { useFormContext, useWatch } from 'react-hook-form';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { Button, Icon, Stack, Text, Tooltip } from '@grafana/ui';
|
||||
import { Button, Stack, Text } from '@grafana/ui';
|
||||
|
||||
import { AIImproveLabelsButtonComponent } from '../../../enterprise-components/AI/AIGenImproveLabelsButton/addAIImproveLabelsButton';
|
||||
import { RuleFormValues } from '../../../types/rule-form';
|
||||
@@ -12,18 +12,11 @@ import { LabelsInRule } from './LabelsField';
|
||||
|
||||
interface LabelsFieldInFormProps {
|
||||
onEditClick: () => void;
|
||||
labelVariant?: 'small' | 'default';
|
||||
showHelpTooltip?: boolean;
|
||||
}
|
||||
export function LabelsFieldInForm({
|
||||
onEditClick,
|
||||
labelVariant = 'default',
|
||||
showHelpTooltip = false,
|
||||
}: LabelsFieldInFormProps) {
|
||||
const { control, watch } = useFormContext<RuleFormValues>();
|
||||
export function LabelsFieldInForm({ onEditClick }: LabelsFieldInFormProps) {
|
||||
const { watch } = useFormContext<RuleFormValues>();
|
||||
|
||||
// Subscribe to label changes so UI updates when modal saves
|
||||
const labels = useWatch({ control, name: 'labels' }) ?? [];
|
||||
const labels = watch('labels');
|
||||
const type = watch('type');
|
||||
|
||||
const isRecordingRule = type ? isRecordingRuleByType(type) : false;
|
||||
@@ -36,48 +29,13 @@ export function LabelsFieldInForm({
|
||||
'Add labels to your rule for searching, silencing, or routing to a notification policy.'
|
||||
);
|
||||
|
||||
const hasLabels = Array.isArray(labels) && labels.length > 0 && labels.some((label) => label?.key || label?.value);
|
||||
const hasLabels = Object.keys(labels).length > 0 && labels.some((label) => label.key || label.value);
|
||||
|
||||
return (
|
||||
<Stack direction="column" gap={2}>
|
||||
<Stack direction="column" gap={1}>
|
||||
<Text element="h5">
|
||||
<Stack direction="row" alignItems="center" gap={0.5}>
|
||||
{labelVariant === 'small' ? (
|
||||
<Text variant="bodySmall">
|
||||
<Trans i18nKey="alerting.labels-field-in-form.labels">Labels</Trans>
|
||||
</Text>
|
||||
) : (
|
||||
<Text element="h5">
|
||||
<Trans i18nKey="alerting.labels-field-in-form.labels">Labels</Trans>
|
||||
</Text>
|
||||
)}
|
||||
<Text variant="bodySmall" color="secondary">
|
||||
{t('alerting.common.optional', '(optional)')}
|
||||
</Text>
|
||||
{showHelpTooltip && (
|
||||
<Tooltip
|
||||
content={
|
||||
<div>
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
{t(
|
||||
'alerting.labels-field-in-form.tooltip-text',
|
||||
'Labels are used to differentiate an alert from all other alerts.You can use them for searching, silencing, and routing notifications.'
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
{t(
|
||||
'alerting.labels-field-in-form.tooltip-text-2',
|
||||
'The dropdown only displays labels that you have previously used for alerts. Select a label from the options below or type in a new one.'
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Icon name="info-circle" size="sm" />
|
||||
</Tooltip>
|
||||
)}
|
||||
</Stack>
|
||||
<Trans i18nKey="alerting.labels-field-in-form.labels">Labels</Trans>
|
||||
</Text>
|
||||
<Stack direction={'column'} gap={1}>
|
||||
<Stack direction={'row'} gap={1}>
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
import { useFormContext, useWatch } from 'react-hook-form';
|
||||
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { Button, Field, Stack, Text } from '@grafana/ui';
|
||||
|
||||
import { AIImproveLabelsButtonComponent } from '../../../enterprise-components/AI/AIGenImproveLabelsButton/addAIImproveLabelsButton';
|
||||
import { RuleFormValues } from '../../../types/rule-form';
|
||||
import { isGrafanaManagedRuleByType, isRecordingRuleByType } from '../../../utils/rules';
|
||||
|
||||
import { LabelsInRule } from './LabelsField';
|
||||
|
||||
interface LabelsFieldInFormProps {
|
||||
onEditClick: () => void;
|
||||
}
|
||||
export function LabelsFieldInFormV2({ onEditClick }: LabelsFieldInFormProps) {
|
||||
const { control, watch } = useFormContext<RuleFormValues>();
|
||||
|
||||
// Subscribe to label changes so UI updates when modal saves
|
||||
const labels = useWatch({ control, name: 'labels' }) ?? [];
|
||||
const type = watch('type');
|
||||
|
||||
const isRecordingRule = type ? isRecordingRuleByType(type) : false;
|
||||
const isGrafanaManaged = type ? isGrafanaManagedRuleByType(type) : false;
|
||||
|
||||
const text = isRecordingRule
|
||||
? t('alerting.alertform.labels.recording', 'Add labels to your rule.')
|
||||
: t(
|
||||
'alerting.alertform.labels.alerting',
|
||||
'Add labels to your rule for searching, silencing, or routing to a notification policy.'
|
||||
);
|
||||
|
||||
const hasLabels = Array.isArray(labels) && labels.length > 0 && labels.some((label) => label?.key || label?.value);
|
||||
|
||||
return (
|
||||
<Field
|
||||
noMargin
|
||||
label={
|
||||
<Stack direction="row" alignItems="center" gap={0.5}>
|
||||
<Text variant="bodySmall">
|
||||
<Trans i18nKey="alerting.labels-field-in-form.labels">Labels</Trans>
|
||||
</Text>
|
||||
<Text variant="bodySmall" color="secondary">
|
||||
{t('alerting.common.optional', '(optional)')}
|
||||
</Text>
|
||||
</Stack>
|
||||
}
|
||||
>
|
||||
<Stack direction={'column'} gap={2}>
|
||||
<Stack direction={'column'} gap={1}>
|
||||
<Stack direction={'row'} gap={1}>
|
||||
<Text variant="bodySmall" color="secondary">
|
||||
{text}
|
||||
</Text>
|
||||
</Stack>
|
||||
{isGrafanaManaged && <AIImproveLabelsButtonComponent />}
|
||||
</Stack>
|
||||
<Stack>
|
||||
{hasLabels ? (
|
||||
<Stack direction="row" gap={1} alignItems="center">
|
||||
<LabelsInRule labels={labels} />
|
||||
<Button variant="secondary" type="button" onClick={onEditClick} size="sm">
|
||||
<Trans i18nKey="alerting.labels-field-in-form.edit-labels">Edit labels</Trans>
|
||||
</Button>
|
||||
</Stack>
|
||||
) : (
|
||||
<Stack direction="column" gap={0.5} alignItems="start">
|
||||
<Text color="secondary">
|
||||
<Trans i18nKey="alerting.labels-field-in-form.no-labels-selected">No labels selected</Trans>
|
||||
</Text>
|
||||
<Button
|
||||
icon="plus"
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={onEditClick}
|
||||
size="sm"
|
||||
data-testid="add-labels-button"
|
||||
>
|
||||
<Trans i18nKey="alerting.labels-field-in-form.add-labels">Add labels</Trans>
|
||||
</Button>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
@@ -523,6 +523,7 @@
|
||||
"metric-aria-label-metric": "metric",
|
||||
"metric-placeholder-recorded-metric": "Give the name of the new recorded metric",
|
||||
"placeholder-name": "Give your {{namePlaceholder}} a name",
|
||||
"recording-rule-pattern": "Recording rule name must be valid metric name. It may only contain letters, numbers, and colons. It may not contain whitespace.",
|
||||
"title-section": "Enter {{entityName}} name"
|
||||
},
|
||||
"alert-rules": {
|
||||
@@ -863,6 +864,8 @@
|
||||
"export-all": "Export all",
|
||||
"learn-more": "Learn more",
|
||||
"loading": "Loading...",
|
||||
"optional": "(optional)",
|
||||
"refresh": "Refresh",
|
||||
"search-by-matchers": "Search by matchers",
|
||||
"titles": {
|
||||
"notification-templates": "Notification Templates"
|
||||
@@ -1076,6 +1079,9 @@
|
||||
"text-loading-notification-template": "Loading notification template",
|
||||
"title-error-loading-notification-template": "Error loading notification template"
|
||||
},
|
||||
"duration": {
|
||||
"immediately": "immediately"
|
||||
},
|
||||
"duration-quick-pick": {
|
||||
"none": "None"
|
||||
},
|
||||
@@ -2811,6 +2817,47 @@
|
||||
"simpleCondition": {
|
||||
"alertCondition": "Alert condition"
|
||||
},
|
||||
"simplified": {
|
||||
"condition": {
|
||||
"title": "Condition"
|
||||
},
|
||||
"continue-in-alerting": "Continue in Alerting",
|
||||
"create": "Create",
|
||||
"evaluation": {
|
||||
"after-breached": "after the condition is initially breached.",
|
||||
"custom-delay": "with custom delay of",
|
||||
"custom-delay-invalid": "The delay must be a multiple of the evaluation frequency.",
|
||||
"every": "Evaluate the rule every",
|
||||
"fire": "Fire the alert rule",
|
||||
"immediate-warning": "Immediate firing might lead to unnecessary alerts being sent for temporary issues",
|
||||
"wait-prefix": "Wait",
|
||||
"wait-suffix": "after the condition is breached before firing."
|
||||
},
|
||||
"notification": {
|
||||
"contact-point": {
|
||||
"description": "Select who should receive a notification when the alert rule fires",
|
||||
"label": "Contact point"
|
||||
},
|
||||
"description": {
|
||||
"label": "Description (optional)",
|
||||
"placeholder": "Enter a description of what the alert rule does…"
|
||||
},
|
||||
"runbook-url": {
|
||||
"label": "Runbook URL (optional)",
|
||||
"placeholder": "Enter the webpage where you keep your runbook for the alert…"
|
||||
},
|
||||
"select-contact-point": "Select a contact point...",
|
||||
"summary": {
|
||||
"label": "Summary (optional)",
|
||||
"placeholder": "Enter a summary of what happened and why…"
|
||||
},
|
||||
"title": "Notification"
|
||||
},
|
||||
"rule-definition": "Rule Definition",
|
||||
"step-number-one": "1",
|
||||
"step-number-three": "3",
|
||||
"step-number-two": "2"
|
||||
},
|
||||
"smart-alert-type-detector": {
|
||||
"data-source-managed": "Data source-managed",
|
||||
"data-sourcemanaged-alert-rules": "Data source-managed alert rules",
|
||||
|
||||
Reference in New Issue
Block a user