diff --git a/public/app/features/alerting/unified/components/contact-points/__snapshots__/useContactPoints.test.tsx.snap b/public/app/features/alerting/unified/components/contact-points/__snapshots__/useContactPoints.test.tsx.snap index dd84a65a6ae..ea4fc89ca90 100644 --- a/public/app/features/alerting/unified/components/contact-points/__snapshots__/useContactPoints.test.tsx.snap +++ b/public/app/features/alerting/unified/components/contact-points/__snapshots__/useContactPoints.test.tsx.snap @@ -144,5 +144,6 @@ exports[`useContactPoints should return contact points with status 1`] = ` ], "error": undefined, "isLoading": false, + "refetchReceivers": [Function], } `; diff --git a/public/app/features/alerting/unified/components/contact-points/useContactPoints.tsx b/public/app/features/alerting/unified/components/contact-points/useContactPoints.tsx index 90557614f0e..d5b3a841ed6 100644 --- a/public/app/features/alerting/unified/components/contact-points/useContactPoints.tsx +++ b/public/app/features/alerting/unified/components/contact-points/useContactPoints.tsx @@ -97,6 +97,7 @@ export function useContactPointsWithStatus() { error, isLoading, contactPoints, + refetchReceivers: fetchAlertmanagerConfiguration.refetch, }; } diff --git a/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/AlertManagerRouting.tsx b/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/AlertManagerRouting.tsx index acd91ad4865..ad8151d4ebe 100644 --- a/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/AlertManagerRouting.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/AlertManagerRouting.tsx @@ -1,8 +1,8 @@ -import { css } from '@emotion/css'; +import { css, cx } from '@emotion/css'; import React, { useState } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; -import { Alert, CollapsableSection, LoadingPlaceholder, Stack, TextLink, useStyles2 } from '@grafana/ui'; +import { Alert, CollapsableSection, IconButton, LoadingPlaceholder, Stack, TextLink, useStyles2 } from '@grafana/ui'; import { AlertManagerDataSource } from 'app/features/alerting/unified/utils/datasource'; import { createUrl } from 'app/features/alerting/unified/utils/url'; @@ -18,15 +18,32 @@ interface AlertManagerManualRoutingProps { alertManager: AlertManagerDataSource; } +const LOADING_SPINNER_DURATION = 1000; + export function AlertManagerManualRouting({ alertManager }: AlertManagerManualRoutingProps) { const styles = useStyles2(getStyles); const alertManagerName = alertManager.name; - const { isLoading, error: errorInContactPointStatus, contactPoints } = useContactPointsWithStatus(); + const { isLoading, error: errorInContactPointStatus, contactPoints, refetchReceivers } = useContactPointsWithStatus(); const [selectedContactPointWithMetadata, setSelectedContactPointWithMetadata] = useState< ContactPointWithMetadata | undefined >(); + // We need to provide a fake loading state for the contact points, because it might be that the response is so fast that the loading spinner is not shown, + // and the user might think that the contact points are not fetched. + // We will show the loading spinner for 1 second, and if the fetching takes more than 1 second, we will show the loading spinner until the fetching is done. + + const [loadingContactPoints, setLoadingContactPoints] = useState(false); + // we need to keep track if the fetching takes more than 1 second, so we can show the loading spinner until the fetching is done + const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + + const onClickRefresh = () => { + setLoadingContactPoints(true); + Promise.all([refetchReceivers(), sleep(LOADING_SPINNER_DURATION)]).finally(() => { + setLoadingContactPoints(false); + }); + }; + if (errorInContactPointStatus) { return ; } @@ -51,6 +68,15 @@ export function AlertManagerManualRouting({ alertManager }: AlertManagerManualRo onSelectContactPoint={setSelectedContactPointWithMetadata} />
+
@@ -123,4 +149,22 @@ const getStyles = (theme: GrafanaTheme2) => ({ gap: theme.spacing(1), marginTop: theme.spacing(1), }), + refreshButton: css({ + color: theme.colors.text.secondary, + cursor: 'pointer', + borderRadius: theme.shape.radius.circle, + overflow: 'hidden', + }), + loading: css({ + pointerEvents: 'none', + animation: 'rotation 2s infinite linear', + '@keyframes rotation': { + from: { + transform: 'rotate(720deg)', + }, + to: { + transform: 'rotate(0deg)', + }, + }, + }), });