Alerting: Update design of rule details tab and add updated by (#99895)
This commit is contained in:
+1
-20
@@ -1637,10 +1637,6 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "4"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "5"]
|
||||
],
|
||||
"public/app/features/alerting/unified/components/InfoPausedRule.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "1"]
|
||||
],
|
||||
"public/app/features/alerting/unified/components/InvalidIntervalWarning.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "1"],
|
||||
@@ -2238,9 +2234,7 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "8"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "9"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "10"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "11"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "12"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "13"]
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "11"]
|
||||
],
|
||||
"public/app/features/alerting/unified/components/rule-editor/GrafanaFolderAndLabelsStep.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"]
|
||||
@@ -2486,19 +2480,6 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "3"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "4"]
|
||||
],
|
||||
"public/app/features/alerting/unified/components/rule-viewer/tabs/Details.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "1"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "2"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "3"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "4"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "5"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "6"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "7"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "8"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "9"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "10"]
|
||||
],
|
||||
"public/app/features/alerting/unified/components/rule-viewer/tabs/Query.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "1"]
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as React from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
|
||||
import { Trans } from '../../../src/utils/i18n';
|
||||
import { t } from '../../../src/utils/i18n';
|
||||
import { useStyles2 } from '../../themes';
|
||||
import { Button, ButtonProps } from '../Button';
|
||||
import { Icon } from '../Icon/Icon';
|
||||
@@ -60,11 +60,12 @@ export function ClipboardButton({
|
||||
}
|
||||
}, [getText, onClipboardCopy, onClipboardError]);
|
||||
|
||||
const copiedText = t('clipboard-button.inline-toast.success', 'Copied');
|
||||
return (
|
||||
<>
|
||||
{showCopySuccess && (
|
||||
<InlineToast placement="top" referenceElement={buttonRef.current}>
|
||||
<Trans i18nKey="clipboard-button.inline-toast.success">Copied</Trans>
|
||||
{copiedText}
|
||||
</InlineToast>
|
||||
)}
|
||||
|
||||
@@ -72,7 +73,7 @@ export function ClipboardButton({
|
||||
onClick={copyTextCallback}
|
||||
icon={icon}
|
||||
variant={showCopySuccess ? 'success' : variant}
|
||||
aria-label={showCopySuccess ? 'Copied' : undefined}
|
||||
aria-label={showCopySuccess ? copiedText : undefined}
|
||||
{...buttonProps}
|
||||
className={cx(styles.button, showCopySuccess && styles.successButton, buttonProps.className)}
|
||||
ref={buttonRef}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { Alert } from '@grafana/ui';
|
||||
import { Trans, t } from 'app/core/internationalization';
|
||||
|
||||
const InfoPausedRule = () => {
|
||||
return (
|
||||
<Alert severity="info" title="Alert evaluation currently paused">
|
||||
Notifications for this rule will not fire and no alert instances will be created until the rule is un-paused.
|
||||
<Alert severity="info" title={t('alerting.alert.evaluation-paused', 'Alert evaluation currently paused')}>
|
||||
<Trans i18nKey="alerting.alert.evaluation-paused-description">
|
||||
Notifications for this rule will not fire and no alert instances will be created until the rule is un-paused.
|
||||
</Trans>
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import { Box, ClipboardButton, Stack, Text, Tooltip } from '@grafana/ui';
|
||||
import { t } from 'app/core/internationalization';
|
||||
|
||||
import ConditionalWrap from '../ConditionalWrap';
|
||||
|
||||
type DetailTextProps = {
|
||||
id: string;
|
||||
label: string;
|
||||
value: string | JSX.Element | null;
|
||||
/** Should the value be displayed using monospace font family? */
|
||||
monospace?: boolean;
|
||||
/** Optional string to display in a tooltip on hover of the value */
|
||||
tooltipValue?: string;
|
||||
} & ConditionalProps;
|
||||
|
||||
type ConditionalProps =
|
||||
// Require either both copy props or neither
|
||||
| {
|
||||
/** Should we show a button for copying the value to clipboard? */
|
||||
showCopyButton: boolean;
|
||||
/**
|
||||
* Value to use for copying to clipboard, when enabled.
|
||||
* Needed as the value could be an element
|
||||
*/
|
||||
copyValue: string;
|
||||
}
|
||||
| { showCopyButton?: never; copyValue?: never };
|
||||
|
||||
export const DetailText = ({
|
||||
id,
|
||||
label,
|
||||
value,
|
||||
monospace,
|
||||
showCopyButton,
|
||||
copyValue,
|
||||
tooltipValue,
|
||||
}: DetailTextProps) => {
|
||||
const copyToClipboardLabel = t('alerting.copy-to-clipboard', 'Copy "{{label}}" to clipboard', { label });
|
||||
return (
|
||||
<Box>
|
||||
<Stack direction="column" gap={0}>
|
||||
<Text color="secondary" id={id}>
|
||||
{label}
|
||||
</Text>
|
||||
<Text aria-labelledby={id} color="primary" variant={monospace ? 'code' : 'body'}>
|
||||
<ConditionalWrap
|
||||
shouldWrap={Boolean(tooltipValue)}
|
||||
wrap={(children) => <Tooltip content={tooltipValue!}>{children}</Tooltip>}
|
||||
>
|
||||
<span>{value}</span>
|
||||
</ConditionalWrap>
|
||||
{showCopyButton && (
|
||||
<ClipboardButton
|
||||
aria-label={copyToClipboardLabel}
|
||||
fill="text"
|
||||
variant="secondary"
|
||||
icon="copy"
|
||||
size="sm"
|
||||
getText={() => copyValue}
|
||||
/>
|
||||
)}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
+8
-2
@@ -363,7 +363,10 @@ export function GrafanaEvaluationBehaviorStep({
|
||||
{showErrorHandling && (
|
||||
<>
|
||||
<NeedHelpInfoForConfigureNoDataError />
|
||||
<Field htmlFor="no-data-state-input" label="Alert state if no data or all values are null">
|
||||
<Field
|
||||
htmlFor="no-data-state-input"
|
||||
label={t('alerting.alert.state-no-data', 'Alert state if no data or all values are null')}
|
||||
>
|
||||
<Controller
|
||||
render={({ field: { onChange, ref, ...field } }) => (
|
||||
<GrafanaAlertStatePicker
|
||||
@@ -378,7 +381,10 @@ export function GrafanaEvaluationBehaviorStep({
|
||||
name="noDataState"
|
||||
/>
|
||||
</Field>
|
||||
<Field htmlFor="exec-err-state-input" label="Alert state if execution error or timeout">
|
||||
<Field
|
||||
htmlFor="exec-err-state-input"
|
||||
label={t('alerting.alert.state-error-timeout', 'Alert state if execution error or timeout')}
|
||||
>
|
||||
<Controller
|
||||
render={({ field: { onChange, ref, ...field } }) => (
|
||||
<GrafanaAlertStatePicker
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { within } from '@testing-library/react';
|
||||
import { render, screen, userEvent, waitFor } from 'test/test-utils';
|
||||
import { byRole, byText } from 'testing-library-selector';
|
||||
import { byLabelText, byRole, byText } from 'testing-library-selector';
|
||||
|
||||
import { setPluginLinksHook } from '@grafana/runtime';
|
||||
import { setupMswServer } from 'app/features/alerting/unified/mockApi';
|
||||
@@ -40,7 +40,7 @@ const ELEMENTS = {
|
||||
label: ([key, value]: [string, string]) => byRole('listitem', { name: `${key}: ${value}` }),
|
||||
},
|
||||
details: {
|
||||
pendingPeriod: byText(/Pending period/i),
|
||||
pendingPeriod: byLabelText(/Pending period/i),
|
||||
},
|
||||
actions: {
|
||||
edit: byRole('link', { name: 'Edit' }),
|
||||
@@ -107,6 +107,10 @@ const dataSources = {
|
||||
};
|
||||
|
||||
describe('RuleViewer', () => {
|
||||
beforeEach(() => {
|
||||
setupDataSources(...Object.values(dataSources));
|
||||
});
|
||||
|
||||
describe('Grafana managed alert rule', () => {
|
||||
const mockRule = getGrafanaRule(
|
||||
{
|
||||
@@ -211,10 +215,6 @@ describe('RuleViewer', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
setupDataSources(...Object.values(dataSources));
|
||||
});
|
||||
|
||||
it('should render a data source managed alert rule', () => {
|
||||
renderRuleViewer(mockRule, mockRuleIdentifier);
|
||||
|
||||
@@ -291,7 +291,7 @@ describe('RuleViewer', () => {
|
||||
// One summary is rendered by the Title component, and the other by the DetailsTab component
|
||||
expect(ELEMENTS.metadata.summary(mockRule.annotations[Annotation.summary]).getAll()).toHaveLength(2);
|
||||
|
||||
expect(within(ELEMENTS.details.pendingPeriod.get()).getByText(/15m/i)).toBeInTheDocument();
|
||||
expect(ELEMENTS.details.pendingPeriod.get()).toHaveTextContent(/15m/i);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { formatDistanceToNowStrict } from 'date-fns';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { ClipboardButton, Stack, Text, TextLink, useStyles2 } from '@grafana/ui';
|
||||
import { GrafanaTheme2, dateTimeFormat, dateTimeFormatTimeAgo } from '@grafana/data';
|
||||
import { Icon, Stack, Text, TextLink, useStyles2 } from '@grafana/ui';
|
||||
import { Trans, t } from 'app/core/internationalization';
|
||||
import { CombinedRule } from 'app/types/unified-alerting';
|
||||
|
||||
import { usePendingPeriod } from '../../../hooks/rules/usePendingPeriod';
|
||||
import { getAnnotations, isGrafanaRecordingRule, isGrafanaRulerRule, isRecordingRulerRule } from '../../../utils/rules';
|
||||
import { MetaText } from '../../MetaText';
|
||||
import {
|
||||
getAnnotations,
|
||||
isGrafanaAlertingRule,
|
||||
isGrafanaRecordingRule,
|
||||
isGrafanaRulerRule,
|
||||
isRecordingRulerRule,
|
||||
} from '../../../utils/rules';
|
||||
import { isNullDate } from '../../../utils/time';
|
||||
import { Tokenize } from '../../Tokenize';
|
||||
|
||||
interface DetailsProps {
|
||||
rule: CombinedRule;
|
||||
}
|
||||
import { DetailText } from '../../common/DetailText';
|
||||
|
||||
enum RuleType {
|
||||
GrafanaManagedAlertRule = 'Grafana-managed alert rule',
|
||||
@@ -22,7 +25,22 @@ enum RuleType {
|
||||
CloudRecordingRule = 'Cloud recording rule',
|
||||
}
|
||||
|
||||
const Details = ({ rule }: DetailsProps) => {
|
||||
const DetailGroup = ({ title, children }: { title: string; children: React.ReactNode }) => {
|
||||
return (
|
||||
<Stack direction="column" gap={1}>
|
||||
<Text variant="h4">{title}</Text>
|
||||
<Stack direction="column" gap={2}>
|
||||
{children}
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
interface DetailsProps {
|
||||
rule: CombinedRule;
|
||||
}
|
||||
|
||||
export const Details = ({ rule }: DetailsProps) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
let ruleType: RuleType;
|
||||
@@ -43,103 +61,136 @@ const Details = ({ rule }: DetailsProps) => {
|
||||
const evaluationDuration = rule.promRule?.evaluationTime;
|
||||
const evaluationTimestamp = rule.promRule?.lastEvaluation;
|
||||
|
||||
const copyRuleUID = useCallback(() => {
|
||||
if (isGrafanaRulerRule(rule.rulerRule)) {
|
||||
return rule.rulerRule.grafana_alert.uid;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}, [rule.rulerRule]);
|
||||
|
||||
const annotations = getAnnotations(rule);
|
||||
|
||||
const hasEvaluationDuration = Number.isFinite(evaluationDuration);
|
||||
|
||||
return (
|
||||
<Stack direction="column" gap={3}>
|
||||
<div className={styles.metadataWrapper}>
|
||||
{/* type and identifier (optional) */}
|
||||
<MetaText direction="column">
|
||||
Rule type
|
||||
<Text color="primary">{ruleType}</Text>
|
||||
</MetaText>
|
||||
<MetaText direction="column">
|
||||
{isGrafanaRulerRule(rule.rulerRule) && (
|
||||
<>
|
||||
Rule Identifier
|
||||
<Stack direction="row" alignItems="center" gap={0.5}>
|
||||
<Text color="primary">
|
||||
{rule.rulerRule.grafana_alert.uid}
|
||||
<ClipboardButton fill="text" variant="secondary" icon="copy" size="sm" getText={copyRuleUID} />
|
||||
</Text>
|
||||
</Stack>
|
||||
</>
|
||||
)}
|
||||
</MetaText>
|
||||
const lastUpdatedBy = (() => {
|
||||
if (!isGrafanaRulerRule(rule.rulerRule)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
{/* evaluation duration and pending period */}
|
||||
<MetaText direction="column">
|
||||
{hasEvaluationDuration && (
|
||||
<>
|
||||
Last evaluation
|
||||
{evaluationTimestamp && evaluationDuration ? (
|
||||
<span>
|
||||
<Text color="primary">{formatDistanceToNowStrict(new Date(evaluationTimestamp))} ago</Text>, took{' '}
|
||||
<Text color="primary">{evaluationDuration}ms</Text>
|
||||
</span>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</MetaText>
|
||||
<MetaText direction="column">
|
||||
{pendingPeriod && (
|
||||
<>
|
||||
Pending period
|
||||
<Text color="primary">{pendingPeriod}</Text>
|
||||
</>
|
||||
)}
|
||||
</MetaText>
|
||||
return rule.rulerRule.grafana_alert.updated_by?.name || `User ID: ${rule.rulerRule.grafana_alert.updated_by?.uid}`;
|
||||
})();
|
||||
|
||||
{/* nodata and execution error state mapping */}
|
||||
{isGrafanaRulerRule(rule.rulerRule) &&
|
||||
// grafana recording rules don't have these fields
|
||||
rule.rulerRule.grafana_alert.no_data_state &&
|
||||
rule.rulerRule.grafana_alert.exec_err_state && (
|
||||
<>
|
||||
<MetaText direction="column">
|
||||
Alert state if no data or all values are null
|
||||
<Text color="primary">{rule.rulerRule.grafana_alert.no_data_state}</Text>
|
||||
</MetaText>
|
||||
<MetaText direction="column">
|
||||
Alert state if execution error or timeout
|
||||
<Text color="primary">{rule.rulerRule.grafana_alert.exec_err_state}</Text>
|
||||
</MetaText>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* annotations go here */}
|
||||
{annotations && (
|
||||
<>
|
||||
<Text variant="h4">Annotations</Text>
|
||||
{Object.keys(annotations).length === 0 ? (
|
||||
<Text variant="bodySmall" color="secondary" italic>
|
||||
No annotations
|
||||
</Text>
|
||||
) : (
|
||||
<div className={styles.metadataWrapper}>
|
||||
{Object.entries(annotations).map(([name, value]) => (
|
||||
<MetaText direction="column" key={name}>
|
||||
{name}
|
||||
<AnnotationValue value={value} />
|
||||
</MetaText>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
const updated = isGrafanaRulerRule(rule.rulerRule) ? rule.rulerRule.grafana_alert.updated : undefined;
|
||||
const isPaused = isGrafanaAlertingRule(rule.rulerRule) && rule.rulerRule.grafana_alert.is_paused;
|
||||
const pausedIcon = (
|
||||
<Stack>
|
||||
<Text color="warning">
|
||||
<Icon name="pause-circle" />
|
||||
</Text>
|
||||
<Text>
|
||||
<Trans i18nKey="alerting.alert.evaluation-paused">Alert evaluation currently paused</Trans>
|
||||
</Text>
|
||||
</Stack>
|
||||
);
|
||||
return (
|
||||
<div className={styles.metadata}>
|
||||
<DetailGroup title={t('alerting.alert.rule', 'Rule')}>
|
||||
<DetailText id="rule-type" label={t('alerting.alert.rule-type', 'Rule type')} value={ruleType} />
|
||||
{isGrafanaRulerRule(rule.rulerRule) && (
|
||||
<>
|
||||
<DetailText
|
||||
id="rule-type"
|
||||
label={t('alerting.alert.rule-identifier', 'Rule identifier')}
|
||||
value={rule.rulerRule.grafana_alert.uid}
|
||||
monospace
|
||||
showCopyButton
|
||||
copyValue={rule.rulerRule.grafana_alert.uid}
|
||||
/>
|
||||
<DetailText
|
||||
id="last-updated-by"
|
||||
label={t('alerting.alert.last-updated-by', 'Last updated by')}
|
||||
value={lastUpdatedBy}
|
||||
/>
|
||||
{updated && (
|
||||
<DetailText
|
||||
id="date-of-last-update"
|
||||
label={t('alerting.alert.last-updated-at', 'Last updated at')}
|
||||
value={dateTimeFormat(updated) + ` (${dateTimeFormatTimeAgo(updated)})`}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</DetailGroup>
|
||||
|
||||
<DetailGroup title={t('alerting.alert.evaluation', 'Evaluation')}>
|
||||
{isPaused ? (
|
||||
pausedIcon
|
||||
) : (
|
||||
<>
|
||||
{hasEvaluationDuration && evaluationTimestamp && (
|
||||
<DetailText
|
||||
id="last-evaluated"
|
||||
label={t('alerting.alert.last-evaluated', 'Last evaluated')}
|
||||
value={
|
||||
!isNullDate(evaluationTimestamp)
|
||||
? formatDistanceToNowStrict(new Date(evaluationTimestamp), { addSuffix: true })
|
||||
: '-'
|
||||
}
|
||||
tooltipValue={!isNullDate(evaluationTimestamp) ? dateTimeFormat(evaluationTimestamp) : undefined}
|
||||
/>
|
||||
)}
|
||||
{hasEvaluationDuration && (
|
||||
<DetailText
|
||||
id="last-evaluation-duration"
|
||||
label={t('alerting.alert.last-evaluation-duration', 'Last evaluation duration')}
|
||||
value={isPaused ? pausedIcon : `${evaluationDuration} ms`}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{pendingPeriod && (
|
||||
<DetailText
|
||||
id="pending-period"
|
||||
label={t('alerting.alert.pending-period', 'Pending period')}
|
||||
value={pendingPeriod}
|
||||
/>
|
||||
)}
|
||||
</DetailGroup>
|
||||
|
||||
{isGrafanaRulerRule(rule.rulerRule) &&
|
||||
// grafana recording rules don't have these fields
|
||||
rule.rulerRule.grafana_alert.no_data_state &&
|
||||
rule.rulerRule.grafana_alert.exec_err_state && (
|
||||
<DetailGroup title={t('alerting.alert.alert-state', 'Alert state')}>
|
||||
{hasEvaluationDuration && (
|
||||
<DetailText
|
||||
id="alert-state-no-data"
|
||||
label={t('alerting.alert.state-no-data', 'Alert state if no data or all values are null')}
|
||||
value={rule.rulerRule.grafana_alert.no_data_state}
|
||||
/>
|
||||
)}
|
||||
{pendingPeriod && (
|
||||
<DetailText
|
||||
id="alert-state-exec-err"
|
||||
label={t('alerting.alert.state-error-timeout', 'Alert state if execution error or timeout')}
|
||||
value={rule.rulerRule.grafana_alert.exec_err_state}
|
||||
/>
|
||||
)}
|
||||
</DetailGroup>
|
||||
)}
|
||||
|
||||
{annotations && (
|
||||
<DetailGroup title={t('alerting.alert.annotations', 'Annotations')}>
|
||||
{Object.keys(annotations).length === 0 ? (
|
||||
<div>
|
||||
<Text color="secondary" italic>
|
||||
<Trans i18nKey="alerting.alert.no-annotations">No annotations</Trans>
|
||||
</Text>
|
||||
</div>
|
||||
) : (
|
||||
Object.entries(annotations).map(([name, value]) => {
|
||||
const id = `annotation-${name.replace(/\s/g, '-')}`;
|
||||
return <DetailText id={id} key={name} label={name} value={<AnnotationValue value={value} />} />;
|
||||
})
|
||||
)}
|
||||
</DetailGroup>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface AnnotationValueProps {
|
||||
@@ -152,7 +203,7 @@ export function AnnotationValue({ value }: AnnotationValueProps) {
|
||||
|
||||
if (needsExternalLink) {
|
||||
return (
|
||||
<TextLink variant="bodySmall" href={value} external>
|
||||
<TextLink href={value} external>
|
||||
{value}
|
||||
</TextLink>
|
||||
);
|
||||
@@ -162,12 +213,16 @@ export function AnnotationValue({ value }: AnnotationValueProps) {
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
metadataWrapper: css({
|
||||
metadata: css({
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'auto auto',
|
||||
rowGap: theme.spacing(3),
|
||||
columnGap: theme.spacing(12),
|
||||
gap: theme.spacing(4),
|
||||
gridTemplateColumns: '1fr 1fr 1fr',
|
||||
|
||||
[theme.breakpoints.down('lg')]: {
|
||||
gridTemplateColumns: '1fr 1fr',
|
||||
},
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
gridTemplateColumns: '1fr',
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
export { Details };
|
||||
|
||||
@@ -265,6 +265,11 @@ export interface GrafanaRuleDefinition extends PostableGrafanaRuleDefinition {
|
||||
namespace_uid: string;
|
||||
rule_group: string;
|
||||
provenance?: string;
|
||||
updated_by?: {
|
||||
uid: string;
|
||||
name?: string;
|
||||
};
|
||||
updated?: string;
|
||||
}
|
||||
|
||||
export interface RulerGrafanaRuleDTO<T = GrafanaRuleDefinition> {
|
||||
|
||||
@@ -174,6 +174,24 @@
|
||||
}
|
||||
},
|
||||
"alerting": {
|
||||
"alert": {
|
||||
"alert-state": "Alert state",
|
||||
"annotations": "Annotations",
|
||||
"evaluation": "Evaluation",
|
||||
"evaluation-paused": "Alert evaluation currently paused",
|
||||
"evaluation-paused-description": "Notifications for this rule will not fire and no alert instances will be created until the rule is un-paused.",
|
||||
"last-evaluated": "Last evaluated",
|
||||
"last-evaluation-duration": "Last evaluation duration",
|
||||
"last-updated-at": "Last updated at",
|
||||
"last-updated-by": "Last updated by",
|
||||
"no-annotations": "No annotations",
|
||||
"pending-period": "Pending period",
|
||||
"rule": "Rule",
|
||||
"rule-identifier": "Rule identifier",
|
||||
"rule-type": "Rule type",
|
||||
"state-error-timeout": "Alert state if execution error or timeout",
|
||||
"state-no-data": "Alert state if no data or all values are null"
|
||||
},
|
||||
"alert-recording-rule-form": {
|
||||
"evaluation-behaviour": {
|
||||
"description": {
|
||||
@@ -283,6 +301,7 @@
|
||||
"contactPointFilter": {
|
||||
"label": "Contact point"
|
||||
},
|
||||
"copy-to-clipboard": "Copy \"{{label}}\" to clipboard",
|
||||
"export": {
|
||||
"subtitle": {
|
||||
"formats": "Select the format and download the file or copy the contents to clipboard",
|
||||
|
||||
@@ -174,6 +174,24 @@
|
||||
}
|
||||
},
|
||||
"alerting": {
|
||||
"alert": {
|
||||
"alert-state": "Åľęřŧ şŧäŧę",
|
||||
"annotations": "Åʼnʼnőŧäŧįőʼnş",
|
||||
"evaluation": "Ēväľūäŧįőʼn",
|
||||
"evaluation-paused": "Åľęřŧ ęväľūäŧįőʼn čūřřęʼnŧľy päūşęđ",
|
||||
"evaluation-paused-description": "Ńőŧįƒįčäŧįőʼnş ƒőř ŧĥįş řūľę ŵįľľ ʼnőŧ ƒįřę äʼnđ ʼnő äľęřŧ įʼnşŧäʼnčęş ŵįľľ þę čřęäŧęđ ūʼnŧįľ ŧĥę řūľę įş ūʼn-päūşęđ.",
|
||||
"last-evaluated": "Ŀäşŧ ęväľūäŧęđ",
|
||||
"last-evaluation-duration": "Ŀäşŧ ęväľūäŧįőʼn đūřäŧįőʼn",
|
||||
"last-updated-at": "Ŀäşŧ ūpđäŧęđ äŧ",
|
||||
"last-updated-by": "Ŀäşŧ ūpđäŧęđ þy",
|
||||
"no-annotations": "Ńő äʼnʼnőŧäŧįőʼnş",
|
||||
"pending-period": "Pęʼnđįʼnģ pęřįőđ",
|
||||
"rule": "Ŗūľę",
|
||||
"rule-identifier": "Ŗūľę įđęʼnŧįƒįęř",
|
||||
"rule-type": "Ŗūľę ŧypę",
|
||||
"state-error-timeout": "Åľęřŧ şŧäŧę įƒ ęχęčūŧįőʼn ęřřőř őř ŧįmęőūŧ",
|
||||
"state-no-data": "Åľęřŧ şŧäŧę įƒ ʼnő đäŧä őř äľľ väľūęş äřę ʼnūľľ"
|
||||
},
|
||||
"alert-recording-rule-form": {
|
||||
"evaluation-behaviour": {
|
||||
"description": {
|
||||
@@ -283,6 +301,7 @@
|
||||
"contactPointFilter": {
|
||||
"label": "Cőʼnŧäčŧ pőįʼnŧ"
|
||||
},
|
||||
"copy-to-clipboard": "Cőpy \"{{label}}\" ŧő čľįpþőäřđ",
|
||||
"export": {
|
||||
"subtitle": {
|
||||
"formats": "Ŝęľęčŧ ŧĥę ƒőřmäŧ äʼnđ đőŵʼnľőäđ ŧĥę ƒįľę őř čőpy ŧĥę čőʼnŧęʼnŧş ŧő čľįpþőäřđ",
|
||||
|
||||
Reference in New Issue
Block a user