mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Split out components for notification policies (#97191)
This commit is contained in:
parent
89774f3c8d
commit
6df7d1fbc0
@ -1406,12 +1406,9 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "13"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "14"]
|
||||
],
|
||||
"public/app/features/alerting/unified/NotificationPolicies.tsx:5381": [
|
||||
"public/app/features/alerting/unified/NotificationPoliciesPage.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 in text props. Wrap text with <Trans /> or use t()", "1"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "2"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "3"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "4"]
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"]
|
||||
],
|
||||
"public/app/features/alerting/unified/PanelAlertTabContent.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
@ -1711,10 +1708,8 @@ exports[`better eslint`] = {
|
||||
],
|
||||
"public/app/features/alerting/unified/components/mute-timings/MuteTimingsTable.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"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "2"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "3"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "4"]
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "2"]
|
||||
],
|
||||
"public/app/features/alerting/unified/components/notification-policies/ContactPointSelector.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
@ -1760,18 +1755,18 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "18"]
|
||||
],
|
||||
"public/app/features/alerting/unified/components/notification-policies/Filters.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 in text props. Wrap text with <Trans /> or use t()", "3"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "4"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "5"]
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"]
|
||||
],
|
||||
"public/app/features/alerting/unified/components/notification-policies/Modals.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 in text props. Wrap text with <Trans /> or use t()", "1"],
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "2"]
|
||||
],
|
||||
"public/app/features/alerting/unified/components/notification-policies/NotificationPoliciesList.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 in text props. Wrap text with <Trans /> or use t()", "1"]
|
||||
],
|
||||
"public/app/features/alerting/unified/components/notification-policies/Policy.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 in text props. Wrap text with <Trans /> or use t()", "1"],
|
||||
@ -2579,9 +2574,7 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Do not use any type assertions.", "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 in text props. Wrap text with <Trans /> or use t()", "4"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "5"]
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "3"]
|
||||
],
|
||||
"public/app/features/alerting/unified/components/silences/SilencesTable.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
|
@ -47,7 +47,10 @@ export function getAlertingRoutes(cfg = config): RouteDescriptor[] {
|
||||
...PERMISSIONS_TIME_INTERVALS_MODIFY,
|
||||
]),
|
||||
component: importAlertingComponent(
|
||||
() => import(/* webpackChunkName: "AlertAmRoutes" */ 'app/features/alerting/unified/NotificationPolicies')
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "NotificationPoliciesPage" */ 'app/features/alerting/unified/NotificationPoliciesPage'
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
|
@ -32,7 +32,8 @@ import {
|
||||
} from 'app/plugins/datasource/alertmanager/types';
|
||||
import { AccessControlAction } from 'app/types';
|
||||
|
||||
import NotificationPolicies, { findRoutesMatchingFilters } from './NotificationPolicies';
|
||||
import NotificationPolicies from './NotificationPoliciesPage';
|
||||
import { findRoutesMatchingFilters } from './components/notification-policies/NotificationPoliciesList';
|
||||
import {
|
||||
grantUserPermissions,
|
||||
mockDataSource,
|
@ -0,0 +1,113 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { GrafanaTheme2, UrlQueryMap } from '@grafana/data';
|
||||
import { Tab, TabContent, TabsBar, useStyles2, withErrorBoundary } from '@grafana/ui';
|
||||
import { useQueryParams } from 'app/core/hooks/useQueryParams';
|
||||
import { useMuteTimings } from 'app/features/alerting/unified/components/mute-timings/useMuteTimings';
|
||||
import { NotificationPoliciesList } from 'app/features/alerting/unified/components/notification-policies/NotificationPoliciesList';
|
||||
import { AlertmanagerAction, useAlertmanagerAbility } from 'app/features/alerting/unified/hooks/useAbilities';
|
||||
|
||||
import { AlertmanagerPageWrapper } from './components/AlertingPageWrapper';
|
||||
import { GrafanaAlertmanagerDeliveryWarning } from './components/GrafanaAlertmanagerDeliveryWarning';
|
||||
import { MuteTimingsTable } from './components/mute-timings/MuteTimingsTable';
|
||||
import { useAlertmanager } from './state/AlertmanagerContext';
|
||||
|
||||
enum ActiveTab {
|
||||
NotificationPolicies = 'notification_policies',
|
||||
MuteTimings = 'mute_timings',
|
||||
}
|
||||
|
||||
const NotificationPoliciesTabs = () => {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
// Alertmanager logic and data hooks
|
||||
const { selectedAlertmanager = '' } = useAlertmanager();
|
||||
const [policiesSupported, canSeePoliciesTab] = useAlertmanagerAbility(AlertmanagerAction.ViewNotificationPolicyTree);
|
||||
const [timingsSupported, canSeeTimingsTab] = useAlertmanagerAbility(AlertmanagerAction.ViewMuteTiming);
|
||||
const availableTabs = [
|
||||
canSeePoliciesTab && ActiveTab.NotificationPolicies,
|
||||
canSeeTimingsTab && ActiveTab.MuteTimings,
|
||||
].filter((tab) => !!tab);
|
||||
const { data: muteTimings = [] } = useMuteTimings({
|
||||
alertmanager: selectedAlertmanager,
|
||||
skip: !canSeeTimingsTab,
|
||||
});
|
||||
|
||||
// Tab state management
|
||||
const [queryParams, setQueryParams] = useQueryParams();
|
||||
const { tab } = getActiveTabFromUrl(queryParams, availableTabs[0]);
|
||||
const [activeTab, setActiveTab] = useState<ActiveTab>(tab);
|
||||
|
||||
const muteTimingsTabActive = activeTab === ActiveTab.MuteTimings;
|
||||
const policyTreeTabActive = activeTab === ActiveTab.NotificationPolicies;
|
||||
|
||||
const numberOfMuteTimings = muteTimings.length;
|
||||
|
||||
return (
|
||||
<>
|
||||
<GrafanaAlertmanagerDeliveryWarning currentAlertmanager={selectedAlertmanager} />
|
||||
<TabsBar>
|
||||
{policiesSupported && canSeePoliciesTab && (
|
||||
<Tab
|
||||
label={'Notification Policies'}
|
||||
active={policyTreeTabActive}
|
||||
onChangeTab={() => {
|
||||
setActiveTab(ActiveTab.NotificationPolicies);
|
||||
setQueryParams({ tab: ActiveTab.NotificationPolicies });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{timingsSupported && canSeeTimingsTab && (
|
||||
<Tab
|
||||
label={'Mute Timings'}
|
||||
active={muteTimingsTabActive}
|
||||
counter={numberOfMuteTimings}
|
||||
onChangeTab={() => {
|
||||
setActiveTab(ActiveTab.MuteTimings);
|
||||
setQueryParams({ tab: ActiveTab.MuteTimings });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</TabsBar>
|
||||
<TabContent className={styles.tabContent}>
|
||||
{policyTreeTabActive && <NotificationPoliciesList />}
|
||||
{muteTimingsTabActive && <MuteTimingsTable />}
|
||||
</TabContent>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
tabContent: css({
|
||||
marginTop: theme.spacing(2),
|
||||
}),
|
||||
});
|
||||
|
||||
interface QueryParamValues {
|
||||
tab: ActiveTab;
|
||||
}
|
||||
|
||||
function getActiveTabFromUrl(queryParams: UrlQueryMap, defaultTab: ActiveTab): QueryParamValues {
|
||||
let tab = defaultTab;
|
||||
|
||||
if (queryParams.tab === ActiveTab.NotificationPolicies) {
|
||||
tab = ActiveTab.NotificationPolicies;
|
||||
}
|
||||
|
||||
if (queryParams.tab === ActiveTab.MuteTimings) {
|
||||
tab = ActiveTab.MuteTimings;
|
||||
}
|
||||
|
||||
return {
|
||||
tab,
|
||||
};
|
||||
}
|
||||
|
||||
const NotificationPoliciesPage = () => (
|
||||
<AlertmanagerPageWrapper navId="am-routes" accessType="notification">
|
||||
<NotificationPoliciesTabs />
|
||||
</AlertmanagerPageWrapper>
|
||||
);
|
||||
|
||||
export default withErrorBoundary(NotificationPoliciesPage, { style: 'page' });
|
@ -19,7 +19,7 @@ import { MuteTimingsTable } from './MuteTimingsTable';
|
||||
const renderWithProvider = (alertManagerSource?: string) => {
|
||||
return render(
|
||||
<AlertmanagerProvider accessType={'notification'} alertmanagerSourceName={alertManagerSource}>
|
||||
<MuteTimingsTable alertManagerSourceName={alertManagerSource ?? GRAFANA_RULES_SOURCE_NAME} />
|
||||
<MuteTimingsTable />
|
||||
</AlertmanagerProvider>
|
||||
);
|
||||
};
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
ALL_MUTE_TIMINGS,
|
||||
useExportMuteTimingsDrawer,
|
||||
} from 'app/features/alerting/unified/components/mute-timings/useExportMuteTimingsDrawer';
|
||||
import { useAlertmanager } from 'app/features/alerting/unified/state/AlertmanagerContext';
|
||||
import { PROVENANCE_ANNOTATION } from 'app/features/alerting/unified/utils/k8s/constants';
|
||||
|
||||
import { Authorize } from '../../components/Authorize';
|
||||
@ -22,21 +23,18 @@ import { Spacer } from '../Spacer';
|
||||
import { MuteTiming, useMuteTimings } from './useMuteTimings';
|
||||
import { renderTimeIntervals } from './util';
|
||||
|
||||
interface MuteTimingsTableProps {
|
||||
alertManagerSourceName: string;
|
||||
hideActions?: boolean;
|
||||
}
|
||||
|
||||
type TableItem = {
|
||||
id: string;
|
||||
data: MuteTiming;
|
||||
};
|
||||
|
||||
export const MuteTimingsTable = ({ alertManagerSourceName, hideActions }: MuteTimingsTableProps) => {
|
||||
export const MuteTimingsTable = () => {
|
||||
const { selectedAlertmanager: alertManagerSourceName = '', hasConfigurationAPI } = useAlertmanager();
|
||||
const hideActions = !hasConfigurationAPI;
|
||||
const styles = useStyles2(getStyles);
|
||||
const [ExportAllDrawer, showExportAllDrawer] = useExportMuteTimingsDrawer();
|
||||
|
||||
const { data, isLoading, error } = useMuteTimings({ alertmanager: alertManagerSourceName });
|
||||
const { data, isLoading, error } = useMuteTimings({ alertmanager: alertManagerSourceName ?? '' });
|
||||
|
||||
const items = useMemo((): TableItem[] => {
|
||||
const muteTimings = data || [];
|
||||
@ -73,10 +71,10 @@ export const MuteTimingsTable = ({ alertManagerSourceName, hideActions }: MuteTi
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Stack direction="row" alignItems="center">
|
||||
<span>
|
||||
<Trans i18nKey="alerting.mute-timings.description">
|
||||
Enter specific time intervals when not to send notifications or freeze notifications for recurring periods of
|
||||
time.
|
||||
</span>
|
||||
</Trans>
|
||||
<Spacer />
|
||||
{!hideActions && items.length > 0 && (
|
||||
<Authorize actions={[AlertmanagerAction.CreateMuteTiming]}>
|
||||
@ -86,7 +84,7 @@ export const MuteTimingsTable = ({ alertManagerSourceName, hideActions }: MuteTi
|
||||
variant="primary"
|
||||
href={makeAMLink('alerting/routes/mute-timing/new', alertManagerSourceName)}
|
||||
>
|
||||
Add mute timing
|
||||
<Trans i18nKey="alerting.mute-timings.add-mute-timing">Add mute timing</Trans>
|
||||
</LinkButton>
|
||||
</Authorize>
|
||||
)}
|
||||
|
@ -3,7 +3,9 @@ import { debounce, isEqual } from 'lodash';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
|
||||
import { Button, Field, Icon, Input, Label, Stack, Text, Tooltip, useStyles2 } from '@grafana/ui';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
import { ContactPointSelector } from 'app/features/alerting/unified/components/notification-policies/ContactPointSelector';
|
||||
import { AlertmanagerAction, useAlertmanagerAbility } from 'app/features/alerting/unified/hooks/useAbilities';
|
||||
import { ObjectMatcher, RouteWithID } from 'app/plugins/datasource/alertmanager/types';
|
||||
|
||||
import { useURLSearchParams } from '../../hooks/useURLSearchParams';
|
||||
@ -25,6 +27,7 @@ const NotificationPoliciesFilter = ({
|
||||
onChangeMatchers,
|
||||
matchingCount,
|
||||
}: NotificationPoliciesFilterProps) => {
|
||||
const [contactPointsSupported, canSeeContactPoints] = useAlertmanagerAbility(AlertmanagerAction.ViewContactPoint);
|
||||
const [searchParams, setSearchParams] = useURLSearchParams();
|
||||
const searchInputRef = useRef<HTMLInputElement | null>(null);
|
||||
const { queryString, contactPoint } = getNotificationPoliciesFilters(searchParams);
|
||||
@ -68,13 +71,13 @@ const NotificationPoliciesFilter = ({
|
||||
label={
|
||||
<Label>
|
||||
<Stack gap={0.5}>
|
||||
<span>Search by matchers</span>
|
||||
<Trans i18nKey="alerting.common.search-by-matchers">Search by matchers</Trans>
|
||||
<Tooltip
|
||||
content={
|
||||
<div>
|
||||
<Trans i18nKey="alerting.policies.filter-description">
|
||||
Filter notification policies by using a comma separated list of matchers, e.g.:
|
||||
<pre>severity=critical, region=EMEA</pre>
|
||||
</div>
|
||||
</Trans>
|
||||
}
|
||||
>
|
||||
<Icon name="info-circle" size="sm" />
|
||||
@ -97,6 +100,7 @@ const NotificationPoliciesFilter = ({
|
||||
defaultValue={queryString}
|
||||
/>
|
||||
</Field>
|
||||
{contactPointsSupported && canSeeContactPoints && (
|
||||
<Field label="Search by contact point" style={{ marginBottom: 0 }}>
|
||||
<ContactPointSelector
|
||||
selectProps={{
|
||||
@ -111,10 +115,11 @@ const NotificationPoliciesFilter = ({
|
||||
selectedContactPointName={searchParams.get('contactPoint') ?? undefined}
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
{hasFilters && (
|
||||
<Stack alignItems="center">
|
||||
<Button variant="secondary" icon="times" onClick={clearFilters}>
|
||||
Clear filters
|
||||
<Trans i18nKey="alerting.common.clear-filters">Clear filters</Trans>
|
||||
</Button>
|
||||
<Text variant="bodySmall" color="secondary">
|
||||
{matchingCount === 0 && 'No policies matching filters.'}
|
||||
|
@ -1,102 +1,50 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useAsyncFn } from 'react-use';
|
||||
|
||||
import { GrafanaTheme2, UrlQueryMap } from '@grafana/data';
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
LoadingPlaceholder,
|
||||
Stack,
|
||||
Tab,
|
||||
TabContent,
|
||||
TabsBar,
|
||||
useStyles2,
|
||||
withErrorBoundary,
|
||||
} from '@grafana/ui';
|
||||
import { Alert, Button, Stack } from '@grafana/ui';
|
||||
import { useAppNotification } from 'app/core/copy/appNotification';
|
||||
import { useQueryParams } from 'app/core/hooks/useQueryParams';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
import { useContactPointsWithStatus } from 'app/features/alerting/unified/components/contact-points/useContactPoints';
|
||||
import { useMuteTimings } from 'app/features/alerting/unified/components/mute-timings/useMuteTimings';
|
||||
import { AlertmanagerAction, useAlertmanagerAbility } from 'app/features/alerting/unified/hooks/useAbilities';
|
||||
import { FormAmRoute } from 'app/features/alerting/unified/types/amroutes';
|
||||
import { addUniqueIdentifierToRoute } from 'app/features/alerting/unified/utils/amroutes';
|
||||
import { ERROR_NEWER_CONFIGURATION } from 'app/features/alerting/unified/utils/k8s/errors';
|
||||
import { isErrorMatchingCode, stringifyErrorLike } from 'app/features/alerting/unified/utils/misc';
|
||||
import { computeInheritedTree } from 'app/features/alerting/unified/utils/notification-policies';
|
||||
import { ObjectMatcher, Route, RouteWithID } from 'app/plugins/datasource/alertmanager/types';
|
||||
|
||||
import { alertmanagerApi } from './api/alertmanagerApi';
|
||||
import { useGetContactPointsState } from './api/receiversApi';
|
||||
import { AlertmanagerPageWrapper } from './components/AlertingPageWrapper';
|
||||
import { GrafanaAlertmanagerDeliveryWarning } from './components/GrafanaAlertmanagerDeliveryWarning';
|
||||
import { MuteTimingsTable } from './components/mute-timings/MuteTimingsTable';
|
||||
import {
|
||||
NotificationPoliciesFilter,
|
||||
findRoutesByMatchers,
|
||||
findRoutesMatchingPredicate,
|
||||
} from './components/notification-policies/Filters';
|
||||
import {
|
||||
useAddPolicyModal,
|
||||
useAlertGroupsModal,
|
||||
useDeletePolicyModal,
|
||||
useEditPolicyModal,
|
||||
} from './components/notification-policies/Modals';
|
||||
import { Policy } from './components/notification-policies/Policy';
|
||||
import {
|
||||
useNotificationPolicyRoute,
|
||||
useUpdateNotificationPolicyRoute,
|
||||
} from './components/notification-policies/useNotificationPolicyRoute';
|
||||
import { isLoading as isPending, useAsync } from './hooks/useAsync';
|
||||
import { useAlertmanager } from './state/AlertmanagerContext';
|
||||
import { FormAmRoute } from './types/amroutes';
|
||||
import { useRouteGroupsMatcher } from './useRouteGroupsMatcher';
|
||||
import { addUniqueIdentifierToRoute } from './utils/amroutes';
|
||||
import { ERROR_NEWER_CONFIGURATION } from './utils/k8s/errors';
|
||||
import { isErrorMatchingCode, stringifyErrorLike } from './utils/misc';
|
||||
import { computeInheritedTree } from './utils/notification-policies';
|
||||
import { useAlertmanager } from '../../state/AlertmanagerContext';
|
||||
|
||||
import { alertmanagerApi } from './../../api/alertmanagerApi';
|
||||
import { useGetContactPointsState } from './../../api/receiversApi';
|
||||
import { isLoading as isPending, useAsync } from './../../hooks/useAsync';
|
||||
import { useRouteGroupsMatcher } from './../../useRouteGroupsMatcher';
|
||||
import {
|
||||
InsertPosition,
|
||||
addRouteToReferenceRoute,
|
||||
cleanRouteIDs,
|
||||
mergePartialAmRouteWithRouteTree,
|
||||
omitRouteFromRouteTree,
|
||||
} from './utils/routeTree';
|
||||
} from './../../utils/routeTree';
|
||||
import { NotificationPoliciesFilter, findRoutesByMatchers, findRoutesMatchingPredicate } from './Filters';
|
||||
import { useAddPolicyModal, useAlertGroupsModal, useDeletePolicyModal, useEditPolicyModal } from './Modals';
|
||||
import { Policy } from './Policy';
|
||||
import { useNotificationPolicyRoute, useUpdateNotificationPolicyRoute } from './useNotificationPolicyRoute';
|
||||
|
||||
enum ActiveTab {
|
||||
NotificationPolicies = 'notification_policies',
|
||||
MuteTimings = 'mute_timings',
|
||||
}
|
||||
|
||||
const AmRoutes = () => {
|
||||
const styles = useStyles2(getStyles);
|
||||
export const NotificationPoliciesList = () => {
|
||||
const appNotification = useAppNotification();
|
||||
const [policiesSupported, canSeePoliciesTab] = useAlertmanagerAbility(AlertmanagerAction.ViewNotificationPolicyTree);
|
||||
const [timingsSupported, canSeeTimingsTab] = useAlertmanagerAbility(AlertmanagerAction.ViewMuteTiming);
|
||||
const [contactPointsSupported, canSeeContactPointsStatus] = useAlertmanagerAbility(
|
||||
AlertmanagerAction.ViewContactPoint
|
||||
);
|
||||
const availableTabs = [
|
||||
canSeePoliciesTab && ActiveTab.NotificationPolicies,
|
||||
canSeeTimingsTab && ActiveTab.MuteTimings,
|
||||
].filter((tab) => !!tab);
|
||||
const [contactPointsSupported, canSeeContactPoints] = useAlertmanagerAbility(AlertmanagerAction.ViewContactPoint);
|
||||
|
||||
const [_, canSeeAlertGroups] = useAlertmanagerAbility(AlertmanagerAction.ViewAlertGroups);
|
||||
const { useGetAlertmanagerAlertGroupsQuery } = alertmanagerApi;
|
||||
|
||||
const [queryParams, setQueryParams] = useQueryParams();
|
||||
const { tab } = getActiveTabFromUrl(queryParams, availableTabs[0]);
|
||||
|
||||
const [activeTab, setActiveTab] = useState<ActiveTab>(tab);
|
||||
|
||||
const [contactPointFilter, setContactPointFilter] = useState<string | undefined>();
|
||||
const [labelMatchersFilter, setLabelMatchersFilter] = useState<ObjectMatcher[]>([]);
|
||||
|
||||
const { selectedAlertmanager, hasConfigurationAPI, isGrafanaAlertmanager } = useAlertmanager();
|
||||
const { getRouteGroupsMap } = useRouteGroupsMatcher();
|
||||
const { data: muteTimings = [] } = useMuteTimings({
|
||||
alertmanager: selectedAlertmanager ?? '',
|
||||
skip: !canSeeTimingsTab,
|
||||
});
|
||||
|
||||
const shouldFetchContactPoints =
|
||||
policiesSupported && canSeePoliciesTab && contactPointsSupported && canSeeContactPointsStatus;
|
||||
|
||||
const shouldFetchContactPoints = contactPointsSupported && canSeeContactPoints;
|
||||
const contactPointsState = useGetContactPointsState(
|
||||
// Workaround to not try and call this API when we don't have access to the policies tab
|
||||
shouldFetchContactPoints ? (selectedAlertmanager ?? '') : ''
|
||||
@ -107,7 +55,7 @@ const AmRoutes = () => {
|
||||
isLoading,
|
||||
error: resultError,
|
||||
refetch: refetchNotificationPolicyRoute,
|
||||
} = useNotificationPolicyRoute({ alertmanager: selectedAlertmanager ?? '' }, { skip: !canSeePoliciesTab });
|
||||
} = useNotificationPolicyRoute({ alertmanager: selectedAlertmanager ?? '' });
|
||||
|
||||
// We make the assumption that the first policy is the default one
|
||||
// At the time of writing, this will be always the case for the AM config response, and the K8S API
|
||||
@ -118,14 +66,14 @@ const AmRoutes = () => {
|
||||
|
||||
const { currentData: alertGroups, refetch: refetchAlertGroups } = useGetAlertmanagerAlertGroupsQuery(
|
||||
{ amSourceName: selectedAlertmanager ?? '' },
|
||||
{ skip: !canSeePoliciesTab || !canSeeAlertGroups || !selectedAlertmanager }
|
||||
{ skip: !canSeeAlertGroups || !selectedAlertmanager }
|
||||
);
|
||||
|
||||
const { contactPoints: receivers } = useContactPointsWithStatus({
|
||||
alertmanager: selectedAlertmanager ?? '',
|
||||
fetchPolicies: false,
|
||||
fetchStatuses: canSeeContactPointsStatus,
|
||||
skip: !canSeePoliciesTab,
|
||||
fetchStatuses: true,
|
||||
skip: !shouldFetchContactPoints,
|
||||
});
|
||||
|
||||
const rootRoute = useMemo(() => {
|
||||
@ -244,43 +192,10 @@ const AmRoutes = () => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const numberOfMuteTimings = muteTimings.length;
|
||||
const hasPoliciesData = rootRoute && !resultError && !isLoading;
|
||||
const hasPoliciesError = !!resultError && !isLoading;
|
||||
|
||||
const muteTimingsTabActive = activeTab === ActiveTab.MuteTimings;
|
||||
const policyTreeTabActive = activeTab === ActiveTab.NotificationPolicies;
|
||||
|
||||
return (
|
||||
<>
|
||||
<GrafanaAlertmanagerDeliveryWarning currentAlertmanager={selectedAlertmanager} />
|
||||
<TabsBar>
|
||||
{policiesSupported && canSeePoliciesTab && (
|
||||
<Tab
|
||||
label={'Notification Policies'}
|
||||
active={policyTreeTabActive}
|
||||
onChangeTab={() => {
|
||||
setActiveTab(ActiveTab.NotificationPolicies);
|
||||
setQueryParams({ tab: ActiveTab.NotificationPolicies });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{timingsSupported && canSeeTimingsTab && (
|
||||
<Tab
|
||||
label={'Mute Timings'}
|
||||
active={muteTimingsTabActive}
|
||||
counter={numberOfMuteTimings}
|
||||
onChangeTab={() => {
|
||||
setActiveTab(ActiveTab.MuteTimings);
|
||||
setQueryParams({ tab: ActiveTab.MuteTimings });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</TabsBar>
|
||||
<TabContent className={styles.tabContent}>
|
||||
{isLoading && <LoadingPlaceholder text="Loading notification policies..." />}
|
||||
|
||||
{policyTreeTabActive && (
|
||||
<>
|
||||
{hasPoliciesError && (
|
||||
<Alert severity="error" title="Error loading Alertmanager config">
|
||||
@ -337,12 +252,6 @@ const AmRoutes = () => {
|
||||
{deleteModal}
|
||||
{alertInstancesModal}
|
||||
</>
|
||||
)}
|
||||
{muteTimingsTabActive && (
|
||||
<MuteTimingsTable alertManagerSourceName={selectedAlertmanager} hideActions={!hasConfigurationAPI} />
|
||||
)}
|
||||
</TabContent>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -429,37 +338,3 @@ function findMapIntersection(...matchingRoutes: FilterResult[]): FilterResult {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
tabContent: css({
|
||||
marginTop: theme.spacing(2),
|
||||
}),
|
||||
});
|
||||
|
||||
interface QueryParamValues {
|
||||
tab: ActiveTab;
|
||||
}
|
||||
|
||||
function getActiveTabFromUrl(queryParams: UrlQueryMap, defaultTab: ActiveTab): QueryParamValues {
|
||||
let tab = defaultTab;
|
||||
|
||||
if (queryParams.tab === ActiveTab.NotificationPolicies) {
|
||||
tab = ActiveTab.NotificationPolicies;
|
||||
}
|
||||
|
||||
if (queryParams.tab === ActiveTab.MuteTimings) {
|
||||
tab = ActiveTab.MuteTimings;
|
||||
}
|
||||
|
||||
return {
|
||||
tab,
|
||||
};
|
||||
}
|
||||
|
||||
const NotificationPoliciesPage = () => (
|
||||
<AlertmanagerPageWrapper navId="am-routes" accessType="notification">
|
||||
<AmRoutes />
|
||||
</AlertmanagerPageWrapper>
|
||||
);
|
||||
|
||||
export default withErrorBoundary(NotificationPoliciesPage, { style: 'page' });
|
@ -35,7 +35,6 @@ import {
|
||||
} from 'app/plugins/datasource/alertmanager/types';
|
||||
import { ReceiversState } from 'app/types';
|
||||
|
||||
import { RoutesMatchingFilters } from '../../NotificationPolicies';
|
||||
import { AlertmanagerAction, useAlertmanagerAbilities, useAlertmanagerAbility } from '../../hooks/useAbilities';
|
||||
import { getAmMatcherFormatter } from '../../utils/alertmanager';
|
||||
import { MatcherFormatter, normalizeMatchers } from '../../utils/matchers';
|
||||
@ -51,6 +50,7 @@ import { Spacer } from '../Spacer';
|
||||
import { GrafanaPoliciesExporter } from '../export/GrafanaPoliciesExporter';
|
||||
|
||||
import { Matchers } from './Matchers';
|
||||
import { RoutesMatchingFilters } from './NotificationPoliciesList';
|
||||
import { TIMING_OPTIONS_DEFAULTS, TimingOptions } from './timingOptions';
|
||||
|
||||
interface PolicyComponentProps {
|
||||
@ -742,7 +742,7 @@ const TimeIntervals: FC<{ timings: string[]; alertManagerSourceName: string }> =
|
||||
href={createMuteTimingLink(timing, alertManagerSourceName)}
|
||||
color={canSeeMuteTimings ? 'primary' : 'secondary'}
|
||||
variant="bodySmall"
|
||||
inline={false}
|
||||
inline={canSeeMuteTimings ? false : undefined}
|
||||
>
|
||||
{timing}
|
||||
</Wrapper>
|
||||
|
@ -5,6 +5,7 @@ import { FormEvent, useState } from 'react';
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Button, Field, Icon, Input, Label, Stack, Tooltip, useStyles2 } from '@grafana/ui';
|
||||
import { useQueryParams } from 'app/core/hooks/useQueryParams';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
|
||||
import { parsePromQLStyleMatcherLoose } from '../../utils/matchers';
|
||||
import { getSilenceFiltersFromUrlParams } from '../../utils/misc';
|
||||
@ -48,7 +49,7 @@ export const SilencesFilter = () => {
|
||||
label={
|
||||
<Label>
|
||||
<Stack gap={0.5}>
|
||||
<span>Search by matchers</span>
|
||||
<Trans i18nKey="alerting.common.search-by-matchers">Search by matchers</Trans>
|
||||
<Tooltip
|
||||
content={
|
||||
<div>
|
||||
@ -79,7 +80,7 @@ export const SilencesFilter = () => {
|
||||
{queryString && (
|
||||
<div className={styles.rowChild}>
|
||||
<Button variant="secondary" icon="times" onClick={clearFilters}>
|
||||
Clear filters
|
||||
<Trans i18nKey="alerting.common.clear-filters">Clear filters</Trans>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
@ -241,11 +241,13 @@
|
||||
},
|
||||
"common": {
|
||||
"cancel": "Cancel",
|
||||
"clear-filters": "Clear filters",
|
||||
"delete": "Delete",
|
||||
"edit": "Edit",
|
||||
"export": "Export",
|
||||
"export-all": "Export all",
|
||||
"loading": "Loading...",
|
||||
"search-by-matchers": "Search by matchers",
|
||||
"view": "View"
|
||||
},
|
||||
"contact-points": {
|
||||
@ -327,6 +329,8 @@
|
||||
}
|
||||
},
|
||||
"mute-timings": {
|
||||
"add-mute-timing": "Add mute timing",
|
||||
"description": "Enter specific time intervals when not to send notifications or freeze notifications for recurring periods of time.",
|
||||
"save": "Save mute timing",
|
||||
"saving": "Saving mute timing"
|
||||
},
|
||||
@ -349,6 +353,7 @@
|
||||
"warning-1": "Deleting this notification policy will permanently remove it.",
|
||||
"warning-2": "Are you sure you want to delete this policy?"
|
||||
},
|
||||
"filter-description": "Filter notification policies by using a comma separated list of matchers, e.g.:<1>severity=critical, region=EMEA</1>",
|
||||
"generated-policies": "Auto-generated policies",
|
||||
"matchers": "Matchers",
|
||||
"metadata": {
|
||||
|
@ -241,11 +241,13 @@
|
||||
},
|
||||
"common": {
|
||||
"cancel": "Cäʼnčęľ",
|
||||
"clear-filters": "Cľęäř ƒįľŧęřş",
|
||||
"delete": "Đęľęŧę",
|
||||
"edit": "Ēđįŧ",
|
||||
"export": "Ēχpőřŧ",
|
||||
"export-all": "Ēχpőřŧ äľľ",
|
||||
"loading": "Ŀőäđįʼnģ...",
|
||||
"search-by-matchers": "Ŝęäřčĥ þy mäŧčĥęřş",
|
||||
"view": "Vįęŵ"
|
||||
},
|
||||
"contact-points": {
|
||||
@ -327,6 +329,8 @@
|
||||
}
|
||||
},
|
||||
"mute-timings": {
|
||||
"add-mute-timing": "Åđđ mūŧę ŧįmįʼnģ",
|
||||
"description": "Ēʼnŧęř şpęčįƒįč ŧįmę įʼnŧęřväľş ŵĥęʼn ʼnőŧ ŧő şęʼnđ ʼnőŧįƒįčäŧįőʼnş őř ƒřęęžę ʼnőŧįƒįčäŧįőʼnş ƒőř řęčūřřįʼnģ pęřįőđş őƒ ŧįmę.",
|
||||
"save": "Ŝävę mūŧę ŧįmįʼnģ",
|
||||
"saving": "Ŝävįʼnģ mūŧę ŧįmįʼnģ"
|
||||
},
|
||||
@ -349,6 +353,7 @@
|
||||
"warning-1": "Đęľęŧįʼnģ ŧĥįş ʼnőŧįƒįčäŧįőʼn pőľįčy ŵįľľ pęřmäʼnęʼnŧľy řęmővę įŧ.",
|
||||
"warning-2": "Åřę yőū şūřę yőū ŵäʼnŧ ŧő đęľęŧę ŧĥįş pőľįčy?"
|
||||
},
|
||||
"filter-description": "Fįľŧęř ʼnőŧįƒįčäŧįőʼn pőľįčįęş þy ūşįʼnģ ä čőmmä şępäřäŧęđ ľįşŧ őƒ mäŧčĥęřş, ę.ģ.:<1>şęvęřįŧy=čřįŧįčäľ, řęģįőʼn=ĒMĒÅ</1>",
|
||||
"generated-policies": "Åūŧő-ģęʼnęřäŧęđ pőľįčįęş",
|
||||
"matchers": "Mäŧčĥęřş",
|
||||
"metadata": {
|
||||
|
Loading…
Reference in New Issue
Block a user