diff --git a/public/app/features/alerting/unified/Templates.tsx b/public/app/features/alerting/unified/Templates.tsx index f3b8b011f36..74b23cfcc1f 100644 --- a/public/app/features/alerting/unified/Templates.tsx +++ b/public/app/features/alerting/unified/Templates.tsx @@ -1,6 +1,5 @@ import { Route, Routes } from 'react-router-dom-v5-compat'; -import { AlertmanagerPageWrapper } from './components/AlertingPageWrapper'; import DuplicateMessageTemplate from './components/contact-points/DuplicateMessageTemplate'; import EditMessageTemplate from './components/contact-points/EditMessageTemplate'; import NewMessageTemplate from './components/contact-points/NewMessageTemplate'; @@ -8,21 +7,11 @@ import { withPageErrorBoundary } from './withPageErrorBoundary'; function NotificationTemplates() { return ( - - - } /> - } /> - } /> - - + + } /> + } /> + } /> + ); } diff --git a/public/app/features/alerting/unified/components/contact-points/DuplicateMessageTemplate.tsx b/public/app/features/alerting/unified/components/contact-points/DuplicateMessageTemplate.tsx index bc89effea98..f94bdb31fe5 100644 --- a/public/app/features/alerting/unified/components/contact-points/DuplicateMessageTemplate.tsx +++ b/public/app/features/alerting/unified/components/contact-points/DuplicateMessageTemplate.tsx @@ -2,16 +2,19 @@ import { useParams } from 'react-router-dom-v5-compat'; import { Alert, LoadingPlaceholder } from '@grafana/ui'; import { EntityNotFound } from 'app/core/components/PageNotFound/EntityNotFound'; +import { t } from 'app/core/internationalization'; import { isNotFoundError } from '../../api/util'; import { useAlertmanager } from '../../state/AlertmanagerContext'; import { generateCopiedName } from '../../utils/duplicate'; import { stringifyErrorLike } from '../../utils/misc'; import { updateDefinesWithUniqueValue } from '../../utils/templates'; +import { createRelativeUrl } from '../../utils/url'; import { withPageErrorBoundary } from '../../withPageErrorBoundary'; import { AlertmanagerPageWrapper } from '../AlertingPageWrapper'; import { TemplateForm } from '../receivers/TemplateForm'; +import { ActiveTab } from './ContactPoints'; import { useGetNotificationTemplate, useNotificationTemplates } from './useNotificationTemplates'; const notFoundComponent = ; @@ -23,16 +26,19 @@ const DuplicateMessageTemplateComponent = () => { const { currentData: template, - isLoading, - error, + isLoading: isLoadingTemplate, + error: templateFetchError, } = useGetNotificationTemplate({ alertmanager: selectedAlertmanager ?? '', uid: templateUid ?? '' }); const { currentData: templates, isLoading: templatesLoading, - error: templatesError, + error: templatesFetchError, } = useNotificationTemplates({ alertmanager: selectedAlertmanager ?? '' }); + const isLoading = isLoadingTemplate || templatesLoading; + const error = templateFetchError || templatesFetchError; + if (!selectedAlertmanager) { return ; } @@ -41,11 +47,11 @@ const DuplicateMessageTemplateComponent = () => { return ; } - if (isLoading || templatesLoading) { + if (isLoading) { return ; } - if (error || templatesError || !template || !templates) { + if (error) { return isNotFoundError(error) ? ( notFoundComponent ) : ( @@ -55,6 +61,10 @@ const DuplicateMessageTemplateComponent = () => { ); } + if (!template) { + return notFoundComponent; + } + const duplicatedName = generateCopiedName(template.title, templates?.map((t) => t.title) ?? []); return ( @@ -67,7 +77,24 @@ const DuplicateMessageTemplateComponent = () => { function DuplicateMessageTemplate() { return ( - + ); diff --git a/public/app/features/alerting/unified/components/contact-points/EditMessageTemplate.tsx b/public/app/features/alerting/unified/components/contact-points/EditMessageTemplate.tsx index 926e1c5404b..d762d727c66 100644 --- a/public/app/features/alerting/unified/components/contact-points/EditMessageTemplate.tsx +++ b/public/app/features/alerting/unified/components/contact-points/EditMessageTemplate.tsx @@ -2,14 +2,17 @@ import { useParams } from 'react-router-dom-v5-compat'; import { Alert, LoadingPlaceholder } from '@grafana/ui'; import { EntityNotFound } from 'app/core/components/PageNotFound/EntityNotFound'; +import { t } from 'app/core/internationalization'; import { isNotFoundError } from '../../api/util'; import { useAlertmanager } from '../../state/AlertmanagerContext'; import { stringifyErrorLike } from '../../utils/misc'; +import { createRelativeUrl } from '../../utils/url'; import { withPageErrorBoundary } from '../../withPageErrorBoundary'; import { AlertmanagerPageWrapper } from '../AlertingPageWrapper'; import { TemplateForm } from '../receivers/TemplateForm'; +import { ActiveTab } from './ContactPoints'; import { useGetNotificationTemplate } from './useNotificationTemplates'; const notFoundComponent = ; @@ -19,7 +22,7 @@ const EditMessageTemplateComponent = () => { const templateUid = name ? decodeURIComponent(name) : undefined; const { selectedAlertmanager } = useAlertmanager(); - const { currentData, isLoading, error } = useGetNotificationTemplate({ + const { currentData, isLoading, error, isUninitialized } = useGetNotificationTemplate({ alertmanager: selectedAlertmanager ?? '', uid: templateUid ?? '', }); @@ -28,7 +31,7 @@ const EditMessageTemplateComponent = () => { return ; } - if (isLoading) { + if (isLoading || isUninitialized) { return ; } @@ -51,7 +54,21 @@ const EditMessageTemplateComponent = () => { function EditMessageTemplate() { return ( - + ); diff --git a/public/app/features/alerting/unified/components/contact-points/NewMessageTemplate.tsx b/public/app/features/alerting/unified/components/contact-points/NewMessageTemplate.tsx index 7cdd2286be1..43f1246f05c 100644 --- a/public/app/features/alerting/unified/components/contact-points/NewMessageTemplate.tsx +++ b/public/app/features/alerting/unified/components/contact-points/NewMessageTemplate.tsx @@ -1,16 +1,38 @@ +import { t } from 'app/core/internationalization'; + import { useAlertmanager } from '../../state/AlertmanagerContext'; +import { createRelativeUrl } from '../../utils/url'; import { withPageErrorBoundary } from '../../withPageErrorBoundary'; import { AlertmanagerPageWrapper } from '../AlertingPageWrapper'; import { TemplateForm } from '../receivers/TemplateForm'; -function NewMessageTemplate() { - const { selectedAlertmanager } = useAlertmanager(); +import { ActiveTab } from './ContactPoints'; +function NewMessageTemplatePage() { return ( - - + + ); } -export default withPageErrorBoundary(NewMessageTemplate); +function NewMessageTemplate() { + const { selectedAlertmanager } = useAlertmanager(); + return ; +} + +export default withPageErrorBoundary(NewMessageTemplatePage); diff --git a/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx b/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx index 96ca2dedd46..29186ded954 100644 --- a/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx +++ b/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx @@ -25,17 +25,14 @@ import { useStyles2, } from '@grafana/ui'; import { useAppNotification } from 'app/core/copy/appNotification'; -import { useCleanup } from 'app/core/hooks/useCleanup'; import { Trans, t } from 'app/core/internationalization'; import { ActiveTab as ContactPointsActiveTabs } from 'app/features/alerting/unified/components/contact-points/ContactPoints'; import { TestTemplateAlert } from 'app/plugins/datasource/alertmanager/types'; -import { AppChromeUpdate } from '../../../../../core/components/AppChrome/AppChromeUpdate'; -import { useUnifiedAlertingSelector } from '../../hooks/useUnifiedAlertingSelector'; import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource'; import { makeAMLink, stringifyErrorLike } from '../../utils/misc'; -import { initialAsyncRequestState } from '../../utils/redux'; import { ProvisionedResource, ProvisioningAlert } from '../Provisioning'; +import { Spacer } from '../Spacer'; import { EditorColumnHeader } from '../contact-points/templates/EditorColumnHeader'; import { NotificationTemplate, @@ -95,15 +92,14 @@ export const TemplateForm = ({ originalTemplate, prefill, alertmanager }: Props) const appNotification = useAppNotification(); - const [createNewTemplate] = useCreateNotificationTemplate({ alertmanager }); - const [updateTemplate] = useUpdateNotificationTemplate({ alertmanager }); + const [createNewTemplate, { error: createTemplateError }] = useCreateNotificationTemplate({ alertmanager }); + const [updateTemplate, { error: updateTemplateError }] = useUpdateNotificationTemplate({ alertmanager }); const { titleIsUnique } = useValidateNotificationTemplate({ alertmanager, originalTemplate }); - useCleanup((state) => (state.unifiedAlerting.saveAMConfig = initialAsyncRequestState)); const formRef = useRef(null); const isGrafanaAlertManager = alertmanager === GRAFANA_RULES_SOURCE_NAME; - const { error } = useUnifiedAlertingSelector((state) => state.saveAMConfig); + const error = updateTemplateError ?? createTemplateError; const [cheatsheetOpened, toggleCheatsheetOpened] = useToggle(false); @@ -168,28 +164,9 @@ export const TemplateForm = ({ originalTemplate, prefill, alertmanager }: Props) setValue('content', newValue); }; - const actionButtons = ( - - - - Cancel - - - ); - return ( <> -
{/* error message */} {error && ( @@ -206,136 +183,155 @@ export const TemplateForm = ({ originalTemplate, prefill, alertmanager }: Props) {/* name field for the template */}
- - - + + {/* name and save buttons */} + + + + + + + + + Cancel + + + - {/* editor layout */} -
-
- {/* template content and payload editor column – full height and half-width */} -
- {/* template editor */} -
- {/* primaryProps will set "minHeight: min-content;" so we have to make sure to apply minHeight to the child */} -
-
- - {/* examples dropdown – only available for Grafana Alertmanager */} - {isGrafanaAlertManager && ( - - {GlobalTemplateDataExamples.map((item, index) => ( + {/* editor layout */} +
+
+ {/* template content and payload editor column – full height and half-width */} +
+ {/* template editor */} +
+ {/* primaryProps will set "minHeight: min-content;" so we have to make sure to apply minHeight to the child */} +
+
+ + {/* examples dropdown – only available for Grafana Alertmanager */} + {isGrafanaAlertManager && ( + + {GlobalTemplateDataExamples.map((item, index) => ( + appendExample(item.example)} + /> + ))} + appendExample(item.example)} + label={'Examples documentation'} + url="https://grafana.com/docs/grafana/latest/alerting/configure-notifications/template-notifications/examples/" + target="_blank" + icon="external-link-alt" /> - ))} - - - - } + + } + > + + + )} + - - )} - - - } - /> -
- - - {({ width, height }) => ( - setValue('content', value)} - containerStyles={styles.editorContainer} - width={width} - height={height} - /> - )} - - -
-
- {/* payload editor – only available for Grafana Alertmanager */} - {isGrafanaAlertManager && ( - <> -
-
-
- Help + + + } />
+ + + {({ width, height }) => ( + setValue('content', value)} + containerStyles={styles.editorContainer} + width={width} + height={height} + /> + )} + +
- - )} -
-
- {/* preview column – full height and half-width */} - {isGrafanaAlertManager && ( - <> -
-
- +
+ {/* payload editor – only available for Grafana Alertmanager */} + {isGrafanaAlertManager && ( + <> +
+
+
+ +
+
+ + )}
- - )} -
+
+ {/* preview column – full height and half-width */} + {isGrafanaAlertManager && ( + <> +
+
+ +
+ + )} +
+
@@ -439,9 +435,6 @@ export const getStyles = (theme: GrafanaTheme2) => { label: css({ margin: 0, }), - nameField: css({ - marginBottom: theme.spacing(1), - }), contentContainer: css({ flex: 1, display: 'flex', diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 8849f1b3a69..c2e79b5c104 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -295,6 +295,9 @@ "export-all": "Export all", "loading": "Loading...", "search-by-matchers": "Search by matchers", + "titles": { + "notification-templates": "Notification Templates" + }, "view": "View" }, "contact-points": { @@ -404,6 +407,20 @@ "title": "Alert instance routing preview", "uninitialized": "When you have your folder selected and your query and labels are configured, click \"Preview routing\" to see the results here." }, + "notification-templates": { + "duplicate": { + "subTitle": "Duplicate a group of notification templates", + "title": "Duplicate notification template group" + }, + "edit": { + "subTitle": "Edit a group of notification templates", + "title": "Edit notification template group" + }, + "new": { + "subTitle": "Create a new group of notification templates", + "title": "New notification template group" + } + }, "policies": { "default-policy": { "description": "All alert instances will be handled by the default policy if no other matching policies are found.", diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json index fd11d47872d..41644d694cb 100644 --- a/public/locales/pseudo-LOCALE/grafana.json +++ b/public/locales/pseudo-LOCALE/grafana.json @@ -295,6 +295,9 @@ "export-all": "Ēχpőřŧ äľľ", "loading": "Ŀőäđįʼnģ...", "search-by-matchers": "Ŝęäřčĥ þy mäŧčĥęřş", + "titles": { + "notification-templates": "Ńőŧįƒįčäŧįőʼn Ŧęmpľäŧęş" + }, "view": "Vįęŵ" }, "contact-points": { @@ -404,6 +407,20 @@ "title": "Åľęřŧ įʼnşŧäʼnčę řőūŧįʼnģ přęvįęŵ", "uninitialized": "Ŵĥęʼn yőū ĥävę yőūř ƒőľđęř şęľęčŧęđ äʼnđ yőūř qūęřy äʼnđ ľäþęľş äřę čőʼnƒįģūřęđ, čľįčĸ \"Přęvįęŵ řőūŧįʼnģ\" ŧő şęę ŧĥę řęşūľŧş ĥęřę." }, + "notification-templates": { + "duplicate": { + "subTitle": "Đūpľįčäŧę ä ģřőūp őƒ ʼnőŧįƒįčäŧįőʼn ŧęmpľäŧęş", + "title": "Đūpľįčäŧę ʼnőŧįƒįčäŧįőʼn ŧęmpľäŧę ģřőūp" + }, + "edit": { + "subTitle": "Ēđįŧ ä ģřőūp őƒ ʼnőŧįƒįčäŧįőʼn ŧęmpľäŧęş", + "title": "Ēđįŧ ʼnőŧįƒįčäŧįőʼn ŧęmpľäŧę ģřőūp" + }, + "new": { + "subTitle": "Cřęäŧę ä ʼnęŵ ģřőūp őƒ ʼnőŧįƒįčäŧįőʼn ŧęmpľäŧęş", + "title": "Ńęŵ ʼnőŧįƒįčäŧįőʼn ŧęmpľäŧę ģřőūp" + } + }, "policies": { "default-policy": { "description": "Åľľ äľęřŧ įʼnşŧäʼnčęş ŵįľľ þę ĥäʼnđľęđ þy ŧĥę đęƒäūľŧ pőľįčy įƒ ʼnő őŧĥęř mäŧčĥįʼnģ pőľįčįęş äřę ƒőūʼnđ.",