diff --git a/public/app/features/alerting/unified/Receivers.tsx b/public/app/features/alerting/unified/Receivers.tsx
index 68cebae6705..0b5b6979e16 100644
--- a/public/app/features/alerting/unified/Receivers.tsx
+++ b/public/app/features/alerting/unified/Receivers.tsx
@@ -1,222 +1,24 @@
-import { css } from '@emotion/css';
-import pluralize from 'pluralize';
-import React, { useEffect } from 'react';
-import { Redirect, Route, RouteChildrenProps, Switch, useLocation, useParams } from 'react-router-dom';
+import React from 'react';
+import { Disable, Enable } from 'react-enable';
-import { GrafanaTheme2, NavModelItem } from '@grafana/data';
-import { Stack } from '@grafana/experimental';
-import { Alert, Icon, LoadingPlaceholder, useStyles2, withErrorBoundary } from '@grafana/ui';
-import { useDispatch } from 'app/types';
+import { withErrorBoundary } from '@grafana/ui';
+const ContactPointsV1 = SafeDynamicImport(() => import('./components/contact-points/ContactPoints.v1'));
+const ContactPointsV2 = SafeDynamicImport(() => import('./components/contact-points/ContactPoints.v2'));
+import { SafeDynamicImport } from 'app/core/components/DynamicImports/SafeDynamicImport';
+import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
-import { ContactPointsState } from '../../../types';
-
-import { useGetContactPointsState } from './api/receiversApi';
-import { AlertManagerPicker } from './components/AlertManagerPicker';
import { AlertingPageWrapper } from './components/AlertingPageWrapper';
-import { GrafanaAlertmanagerDeliveryWarning } from './components/GrafanaAlertmanagerDeliveryWarning';
-import { NoAlertManagerWarning } from './components/NoAlertManagerWarning';
-import { DuplicateTemplateView } from './components/receivers/DuplicateTemplateView';
-import { EditReceiverView } from './components/receivers/EditReceiverView';
-import { EditTemplateView } from './components/receivers/EditTemplateView';
-import { GlobalConfigForm } from './components/receivers/GlobalConfigForm';
-import { NewReceiverView } from './components/receivers/NewReceiverView';
-import { NewTemplateView } from './components/receivers/NewTemplateView';
-import { ReceiversAndTemplatesView } from './components/receivers/ReceiversAndTemplatesView';
-import { isDuplicating } from './components/receivers/TemplateForm';
-import { useAlertManagerSourceName } from './hooks/useAlertManagerSourceName';
-import { useAlertManagersByPermission } from './hooks/useAlertManagerSources';
-import { useUnifiedAlertingSelector } from './hooks/useUnifiedAlertingSelector';
-import { fetchAlertManagerConfigAction, fetchGrafanaNotifiersAction } from './state/actions';
-import { GRAFANA_RULES_SOURCE_NAME } from './utils/datasource';
-import { initialAsyncRequestState } from './utils/redux';
+import { AlertingFeature } from './features';
+// TODO add pagenav back in – what are we missing if we don't specify it?
+const ContactPoints = (props: GrafanaRouteComponentProps): JSX.Element => (
+
+
+
+
+
+
+
+
+);
-export interface NotificationErrorProps {
- errorCount: number;
-}
-
-function NotificationError({ errorCount }: NotificationErrorProps) {
- const styles = useStyles2(getStyles);
-
- return (
-
-
-
-
- {`${errorCount} ${pluralize('error', errorCount)} with contact points`}
-
- {'Some alert notifications might not be delivered'}
-
-
- );
-}
-
-type PageType = 'receivers' | 'templates' | 'global-config';
-
-const Receivers = () => {
- const alertManagers = useAlertManagersByPermission('notification');
- const [alertManagerSourceName, setAlertManagerSourceName] = useAlertManagerSourceName(alertManagers);
- const dispatch = useDispatch();
- const styles = useStyles2(getStyles);
-
- const { id, type } = useParams<{ id?: string; type?: PageType }>();
- const location = useLocation();
- const isRoot = location.pathname.endsWith('/alerting/notifications');
- const isduplicatingTemplate = isDuplicating(location);
- const configRequests = useUnifiedAlertingSelector((state) => state.amConfigs);
-
- const {
- result: config,
- loading,
- error,
- } = (alertManagerSourceName && configRequests[alertManagerSourceName]) || initialAsyncRequestState;
-
- const receiverTypes = useUnifiedAlertingSelector((state) => state.grafanaNotifiers);
-
- const shouldLoadConfig = isRoot || !config;
- const shouldRenderNotificationStatus = isRoot;
-
- useEffect(() => {
- if (alertManagerSourceName && shouldLoadConfig) {
- dispatch(fetchAlertManagerConfigAction(alertManagerSourceName));
- }
- }, [alertManagerSourceName, dispatch, shouldLoadConfig]);
-
- useEffect(() => {
- if (
- alertManagerSourceName === GRAFANA_RULES_SOURCE_NAME &&
- !(receiverTypes.result || receiverTypes.loading || receiverTypes.error)
- ) {
- dispatch(fetchGrafanaNotifiersAction());
- }
- }, [alertManagerSourceName, dispatch, receiverTypes]);
-
- const contactPointsState: ContactPointsState = useGetContactPointsState(alertManagerSourceName ?? '');
- const integrationsErrorCount = contactPointsState?.errorCount ?? 0;
-
- const disableAmSelect = !isRoot;
- let pageNav = getPageNavigationModel(type, id ? decodeURIComponent(id) : undefined, isduplicatingTemplate);
-
- if (!alertManagerSourceName) {
- return isRoot ? (
-
-
-
- ) : (
-
- );
- }
-
- return (
-
-
-
- {shouldRenderNotificationStatus && integrationsErrorCount > 0 && (
-
- )}
-
- {error && !loading && (
-
- {error.message || 'Unknown error.'}
-
- )}
-
- {loading && !config && }
- {config && !error && (
-
-
-
-
-
-
-
-
- {({ match }: RouteChildrenProps<{ name: string }>) =>
- match?.params.name && (
-
- )
- }
-
-
- {({ match }: RouteChildrenProps<{ name: string }>) =>
- match?.params.name && (
-
- )
- }
-
-
-
-
-
- {({ match }: RouteChildrenProps<{ name: string }>) =>
- match?.params.name && (
-
- )
- }
-
-
-
-
-
- )}
-
- );
-};
-
-function getPageNavigationModel(type: PageType | undefined, id: string | undefined, isDuplicatingTemplates: boolean) {
- let pageNav: NavModelItem | undefined;
- if (isDuplicatingTemplates) {
- return {
- text: `New template`,
- subTitle: `Create a new template for your notifications`,
- };
- }
- if (type === 'receivers' || type === 'templates') {
- const objectText = type === 'receivers' ? 'contact point' : 'notification template';
- if (id) {
- pageNav = {
- text: id,
- subTitle: `Edit the settings for a specific ${objectText}`,
- };
- } else {
- pageNav = {
- text: `New ${objectText}`,
- subTitle: `Create a new ${objectText} for your notifications`,
- };
- }
- } else if (type === 'global-config') {
- pageNav = {
- text: 'Global config',
- subTitle: 'Manage your global configuration',
- };
- }
- return pageNav;
-}
-
-export default withErrorBoundary(Receivers, { style: 'page' });
-
-const getStyles = (theme: GrafanaTheme2) => ({
- error: css`
- color: ${theme.colors.error.text};
- `,
- headingContainer: css`
- display: flex;
- justify-content: space-between;
- `,
-});
+export default withErrorBoundary(ContactPoints, { style: 'page' });
diff --git a/public/app/features/alerting/unified/Receivers.test.tsx b/public/app/features/alerting/unified/components/contact-points/ContactPoints.v1.test.tsx
similarity index 96%
rename from public/app/features/alerting/unified/Receivers.test.tsx
rename to public/app/features/alerting/unified/components/contact-points/ContactPoints.v1.test.tsx
index b311da63e17..be44912898a 100644
--- a/public/app/features/alerting/unified/Receivers.test.tsx
+++ b/public/app/features/alerting/unified/components/contact-points/ContactPoints.v1.test.tsx
@@ -20,30 +20,31 @@ import { AccessControlAction, ContactPointsState } from 'app/types';
import 'whatwg-fetch';
-import Receivers from './Receivers';
-import { fetchAlertManagerConfig, fetchStatus, testReceivers, updateAlertManagerConfig } from './api/alertmanager';
-import { AlertmanagersChoiceResponse } from './api/alertmanagerApi';
-import { discoverAlertmanagerFeatures } from './api/buildInfo';
-import { fetchNotifiers } from './api/grafana';
-import * as receiversApi from './api/receiversApi';
-import * as grafanaApp from './components/receivers/grafanaAppReceivers/grafanaApp';
+import { fetchAlertManagerConfig, fetchStatus, testReceivers, updateAlertManagerConfig } from '../../api/alertmanager';
+import { AlertmanagersChoiceResponse } from '../../api/alertmanagerApi';
+import { discoverAlertmanagerFeatures } from '../../api/buildInfo';
+import { fetchNotifiers } from '../../api/grafana';
+import * as receiversApi from '../../api/receiversApi';
+import * as grafanaApp from '../../components/receivers/grafanaAppReceivers/grafanaApp';
import {
mockDataSource,
MockDataSourceSrv,
someCloudAlertManagerConfig,
someCloudAlertManagerStatus,
someGrafanaAlertManagerConfig,
-} from './mocks';
-import { mockAlertmanagerChoiceResponse } from './mocks/alertmanagerApi';
-import { grafanaNotifiersMock } from './mocks/grafana-notifiers';
-import { getAllDataSources } from './utils/config';
-import { ALERTMANAGER_NAME_LOCAL_STORAGE_KEY, ALERTMANAGER_NAME_QUERY_KEY } from './utils/constants';
-import { DataSourceType, GRAFANA_RULES_SOURCE_NAME } from './utils/datasource';
-jest.mock('./api/alertmanager');
-jest.mock('./api/grafana');
-jest.mock('./utils/config');
+} from '../../mocks';
+import { mockAlertmanagerChoiceResponse } from '../../mocks/alertmanagerApi';
+import { grafanaNotifiersMock } from '../../mocks/grafana-notifiers';
+import { getAllDataSources } from '../../utils/config';
+import { ALERTMANAGER_NAME_LOCAL_STORAGE_KEY, ALERTMANAGER_NAME_QUERY_KEY } from '../../utils/constants';
+import { DataSourceType, GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
+
+import Receivers from './ContactPoints.v1';
+jest.mock('../../api/alertmanager');
+jest.mock('../../api/grafana');
+jest.mock('../../utils/config');
jest.mock('app/core/services/context_srv');
-jest.mock('./api/buildInfo');
+jest.mock('../../api/buildInfo');
const mocks = {
getAllDataSources: jest.mocked(getAllDataSources),
diff --git a/public/app/features/alerting/unified/components/contact-points/ContactPoints.v1.tsx b/public/app/features/alerting/unified/components/contact-points/ContactPoints.v1.tsx
new file mode 100644
index 00000000000..fb003777276
--- /dev/null
+++ b/public/app/features/alerting/unified/components/contact-points/ContactPoints.v1.tsx
@@ -0,0 +1,181 @@
+import { css } from '@emotion/css';
+import pluralize from 'pluralize';
+import React, { useEffect } from 'react';
+import { Redirect, Route, RouteChildrenProps, Switch, useLocation } from 'react-router-dom';
+
+import { GrafanaTheme2 } from '@grafana/data';
+import { Stack } from '@grafana/experimental';
+import { Alert, Icon, LoadingPlaceholder, useStyles2 } from '@grafana/ui';
+import { ContactPointsState, useDispatch } from 'app/types';
+
+import { useGetContactPointsState } from '../../api/receiversApi';
+import { useAlertManagerSourceName } from '../../hooks/useAlertManagerSourceName';
+import { useAlertManagersByPermission } from '../../hooks/useAlertManagerSources';
+import { useUnifiedAlertingSelector } from '../../hooks/useUnifiedAlertingSelector';
+import { fetchAlertManagerConfigAction, fetchGrafanaNotifiersAction } from '../../state/actions';
+import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
+import { initialAsyncRequestState } from '../../utils/redux';
+import { AlertManagerPicker } from '../AlertManagerPicker';
+import { GrafanaAlertmanagerDeliveryWarning } from '../GrafanaAlertmanagerDeliveryWarning';
+import { NoAlertManagerWarning } from '../NoAlertManagerWarning';
+import { DuplicateTemplateView } from '../receivers/DuplicateTemplateView';
+import { EditReceiverView } from '../receivers/EditReceiverView';
+import { EditTemplateView } from '../receivers/EditTemplateView';
+import { GlobalConfigForm } from '../receivers/GlobalConfigForm';
+import { NewReceiverView } from '../receivers/NewReceiverView';
+import { NewTemplateView } from '../receivers/NewTemplateView';
+import { ReceiversAndTemplatesView } from '../receivers/ReceiversAndTemplatesView';
+
+export interface NotificationErrorProps {
+ errorCount: number;
+}
+
+function NotificationError({ errorCount }: NotificationErrorProps) {
+ const styles = useStyles2(getStyles);
+
+ return (
+
+
+
+
+ {`${errorCount} ${pluralize('error', errorCount)} with contact points`}
+
+ {'Some alert notifications might not be delivered'}
+
+
+ );
+}
+
+const Receivers = () => {
+ const alertManagers = useAlertManagersByPermission('notification');
+ const [alertManagerSourceName, setAlertManagerSourceName] = useAlertManagerSourceName(alertManagers);
+ const dispatch = useDispatch();
+ const styles = useStyles2(getStyles);
+
+ const location = useLocation();
+ const isRoot = location.pathname.endsWith('/alerting/notifications');
+ const configRequests = useUnifiedAlertingSelector((state) => state.amConfigs);
+
+ const {
+ result: config,
+ loading,
+ error,
+ } = (alertManagerSourceName && configRequests[alertManagerSourceName]) || initialAsyncRequestState;
+
+ const receiverTypes = useUnifiedAlertingSelector((state) => state.grafanaNotifiers);
+
+ const shouldLoadConfig = isRoot || !config;
+ const shouldRenderNotificationStatus = isRoot;
+
+ useEffect(() => {
+ if (alertManagerSourceName && shouldLoadConfig) {
+ dispatch(fetchAlertManagerConfigAction(alertManagerSourceName));
+ }
+ }, [alertManagerSourceName, dispatch, shouldLoadConfig]);
+
+ useEffect(() => {
+ if (
+ alertManagerSourceName === GRAFANA_RULES_SOURCE_NAME &&
+ !(receiverTypes.result || receiverTypes.loading || receiverTypes.error)
+ ) {
+ dispatch(fetchGrafanaNotifiersAction());
+ }
+ }, [alertManagerSourceName, dispatch, receiverTypes]);
+
+ const contactPointsState: ContactPointsState = useGetContactPointsState(alertManagerSourceName ?? '');
+ const integrationsErrorCount = contactPointsState?.errorCount ?? 0;
+
+ const disableAmSelect = !isRoot;
+
+ if (!alertManagerSourceName) {
+ return isRoot ? (
+
+ ) : (
+
+ );
+ }
+
+ return (
+ <>
+
+
+ {shouldRenderNotificationStatus && integrationsErrorCount > 0 && (
+
+ )}
+
+ {error && !loading && (
+
+ {error.message || 'Unknown error.'}
+
+ )}
+
+ {loading && !config && }
+ {config && !error && (
+
+
+
+
+
+
+
+
+ {({ match }: RouteChildrenProps<{ name: string }>) =>
+ match?.params.name && (
+
+ )
+ }
+
+
+ {({ match }: RouteChildrenProps<{ name: string }>) =>
+ match?.params.name && (
+
+ )
+ }
+
+
+
+
+
+ {({ match }: RouteChildrenProps<{ name: string }>) =>
+ match?.params.name && (
+
+ )
+ }
+
+
+
+
+
+ )}
+ >
+ );
+};
+
+export default Receivers;
+
+const getStyles = (theme: GrafanaTheme2) => ({
+ error: css`
+ color: ${theme.colors.error.text};
+ `,
+ headingContainer: css`
+ display: flex;
+ justify-content: space-between;
+ `,
+});
diff --git a/public/app/features/alerting/unified/components/contact-points/ContactPoints.v2.tsx b/public/app/features/alerting/unified/components/contact-points/ContactPoints.v2.tsx
new file mode 100644
index 00000000000..016efeb9330
--- /dev/null
+++ b/public/app/features/alerting/unified/components/contact-points/ContactPoints.v2.tsx
@@ -0,0 +1,5 @@
+import React from 'react';
+
+const ContactPoints = () => <>Hello, contact points v2!>;
+
+export default ContactPoints;
diff --git a/public/app/features/alerting/unified/features.ts b/public/app/features/alerting/unified/features.ts
index 5fb013934da..faa7b8d4d3d 100644
--- a/public/app/features/alerting/unified/features.ts
+++ b/public/app/features/alerting/unified/features.ts
@@ -4,6 +4,7 @@ import { config } from '@grafana/runtime';
export enum AlertingFeature {
NotificationPoliciesV2MatchingInstances = 'notification-policies.v2.matching-instances',
+ ContactPointsV2 = 'contact-points.v2',
}
const FEATURES: FeatureDescription[] = [
@@ -11,5 +12,9 @@ const FEATURES: FeatureDescription[] = [
name: AlertingFeature.NotificationPoliciesV2MatchingInstances,
defaultValue: config.featureToggles.alertingNotificationsPoliciesMatchingInstances,
},
+ {
+ name: AlertingFeature.ContactPointsV2,
+ defaultValue: false,
+ },
];
export default FEATURES;