import { css } from '@emotion/css'; import React, { FC, useState } from 'react'; import { useDebounce } from 'react-use'; import { GrafanaTheme2 } from '@grafana/data'; import { Button, Icon, Input, Label, useStyles2 } from '@grafana/ui'; import { contextSrv } from 'app/core/services/context_srv'; import { Authorize } from '../../components/Authorize'; import { useURLSearchParams } from '../../hooks/useURLSearchParams'; import { AmRouteReceiver, FormAmRoute } from '../../types/amroutes'; import { getNotificationsPermissions } from '../../utils/access-control'; import { emptyArrayFieldMatcher, emptyRoute } from '../../utils/amroutes'; import { getNotificationPoliciesFilters } from '../../utils/misc'; import { EmptyArea } from '../EmptyArea'; import { EmptyAreaWithCTA } from '../EmptyAreaWithCTA'; import { MatcherFilter } from '../alert-groups/MatcherFilter'; import { AmRoutesTable } from './AmRoutesTable'; export interface AmSpecificRoutingProps { alertManagerSourceName: string; onChange: (routes: FormAmRoute) => void; onRootRouteEdit: () => void; receivers: AmRouteReceiver[]; routes: FormAmRoute; readOnly?: boolean; } interface Filters { queryString?: string; contactPoint?: string; } export const AmSpecificRouting: FC = ({ alertManagerSourceName, onChange, onRootRouteEdit, receivers, routes, readOnly = false, }) => { const [actualRoutes, setActualRoutes] = useState([...routes.routes]); const [isAddMode, setIsAddMode] = useState(false); const permissions = getNotificationsPermissions(alertManagerSourceName); const canCreateNotifications = contextSrv.hasPermission(permissions.create); const [searchParams, setSearchParams] = useURLSearchParams(); const { queryString, contactPoint } = getNotificationPoliciesFilters(searchParams); const [filters, setFilters] = useState({ queryString, contactPoint }); useDebounce( () => { setSearchParams({ queryString: filters.queryString, contactPoint: filters.contactPoint }); }, 400, [filters] ); const styles = useStyles2(getStyles); const clearFilters = () => { setFilters({ queryString: undefined, contactPoint: undefined }); setSearchParams({ queryString: undefined, contactPoint: undefined }); }; const addNewRoute = () => { clearFilters(); setIsAddMode(true); setActualRoutes(() => [ ...routes.routes, { ...emptyRoute, matchers: [emptyArrayFieldMatcher], }, ]); }; const onCancelAdd = () => { setIsAddMode(false); setActualRoutes([...routes.routes]); }; const onTableRouteChange = (newRoutes: FormAmRoute[]): void => { onChange({ ...routes, routes: newRoutes, }); if (isAddMode) { setIsAddMode(false); } }; return (
Specific routing

Send specific alerts to chosen contact points, based on matching criteria

{!routes.receiver ? ( readOnly ? (

There is no default contact point configured for the root route.

) : ( ) ) : actualRoutes.length > 0 ? ( <>
{!isAddMode && (
setFilters((currentFilters) => ({ ...currentFilters, queryString: filter })) } queryString={filters.queryString ?? ''} className={styles.filterInput} />
setFilters((currentFilters) => ({ ...currentFilters, contactPoint: currentTarget.value })) } value={filters.contactPoint ?? ''} placeholder="Search by contact point" data-testid="search-query-input" prefix={} />
{(queryString || contactPoint) && ( )}
)} {!isAddMode && !readOnly && (
)}
) : readOnly ? (

There are no specific policies configured.

) : ( )}
); }; const getStyles = (theme: GrafanaTheme2) => { return { container: css` display: flex; flex-flow: column wrap; `, searchContainer: css` display: flex; flex-flow: row nowrap; padding-bottom: ${theme.spacing(2)}; border-bottom: 1px solid ${theme.colors.border.strong}; `, clearFilterBtn: css` align-self: flex-end; margin-left: ${theme.spacing(1)}; `, filterInput: css` width: 340px; & + & { margin-left: ${theme.spacing(1)}; } `, addMatcherBtnRow: css` display: flex; flex-flow: column nowrap; padding: ${theme.spacing(2)} 0; `, addMatcherBtn: css` align-self: flex-end; `, }; };