diff --git a/public/app/features/alerting/unified/components/contact-points/ContactPoints.tsx b/public/app/features/alerting/unified/components/contact-points/ContactPoints.tsx index c73d19f7952..f8888a043f6 100644 --- a/public/app/features/alerting/unified/components/contact-points/ContactPoints.tsx +++ b/public/app/features/alerting/unified/components/contact-points/ContactPoints.tsx @@ -32,6 +32,7 @@ import { ContactPoint } from './ContactPoint'; import { NotificationTemplates } from './NotificationTemplates'; import { ContactPointsFilter } from './components/ContactPointsFilter'; import { GlobalConfigAlert } from './components/GlobalConfigAlert'; +import { ContactPointsFilterState, useContactPointsFilter } from './components/useContactPointsFilter'; import { useContactPointsWithStatus } from './useContactPoints'; import { useContactPointsSearch } from './useContactPointsSearch'; import { ALL_CONTACT_POINTS, useExportContactPoint } from './useExportContactPoint'; @@ -46,7 +47,8 @@ const DEFAULT_PAGE_SIZE = 10; const ContactPointsTab = () => { const { selectedAlertmanager } = useAlertmanager(); - const [queryParams] = useURLSearchParams(); + const { filters } = useContactPointsFilter(); + console.log(filters); // If we're using the K8S API, then we don't need to fetch the policies info within the hook, // as we get metadata about this from the API @@ -69,8 +71,6 @@ const ContactPointsTab = () => { const [ExportDrawer, showExportDrawer] = useExportContactPoint(); - const search = queryParams.get('search'); - if (isLoading) { return ; } @@ -129,7 +129,7 @@ const ContactPointsTab = () => { {error && {stringifyErrorLike(error)}} - {!error && } + {!error && } {/* Grafana manager Alertmanager does not support global config, Mimir and Cortex do */} {!isGrafanaManagedAlertmanager && } {ExportDrawer} @@ -235,12 +235,12 @@ export const ContactPointsPageContents = () => { interface ContactPointsListProps { contactPoints: ContactPointWithMetadata[]; - search?: string | null; + filters?: ContactPointsFilterState; pageSize?: number; } -const ContactPointsList = ({ contactPoints, search, pageSize = DEFAULT_PAGE_SIZE }: ContactPointsListProps) => { - const searchResults = useContactPointsSearch(contactPoints, search); +const ContactPointsList = ({ contactPoints, filters, pageSize = DEFAULT_PAGE_SIZE }: ContactPointsListProps) => { + const searchResults = useContactPointsSearch(contactPoints, filters); const { page, pageItems, numberOfPages, onPageChange } = usePagination(searchResults, 1, pageSize); if (pageItems.length === 0) { diff --git a/public/app/features/alerting/unified/components/contact-points/components/ContactPointsFilter.tsx b/public/app/features/alerting/unified/components/contact-points/components/ContactPointsFilter.tsx index 61f0e07d402..7e3154efab0 100644 --- a/public/app/features/alerting/unified/components/contact-points/components/ContactPointsFilter.tsx +++ b/public/app/features/alerting/unified/components/contact-points/components/ContactPointsFilter.tsx @@ -1,54 +1,36 @@ import { css } from '@emotion/css'; -import { useCallback, useDeferredValue, useEffect, useMemo, useState } from 'react'; +import { useMemo } from 'react'; import { SelectableValue } from '@grafana/data'; -import { Stack } from '@grafana/experimental'; -import { Button, Field, Icon, Input, Select, SelectCommonProps, Text, useStyles2 } from '@grafana/ui'; +import { Button, Field, Icon, Input, Select, SelectCommonProps, Stack, Text, useStyles2 } from '@grafana/ui'; import { alertmanagerApi } from '../../../api/alertmanagerApi'; -import { useURLSearchParams } from '../../../hooks/useURLSearchParams'; import { useAlertmanager } from '../../../state/AlertmanagerContext'; import { stringifyErrorLike } from '../../../utils/misc'; +import { useContactPointsFilter } from './useContactPointsFilter'; + const useGrafanaNotifiers = alertmanagerApi.endpoints.grafanaNotifiers.useQuery; const ContactPointsFilter = () => { const styles = useStyles2(getStyles); const { isGrafanaAlertmanager } = useAlertmanager(); - - const [searchParams, setSearchParams] = useURLSearchParams(); - const [filters, setFilters] = useState({ - search: searchParams.get('search') ?? undefined, - type: searchParams.get('type') ?? undefined, - }); - const filtersDeferred = useDeferredValue(filters); - - const clear = useCallback(() => { - setFilters({ search: '', type: '' }); - }, []); - - // when the filters are updated (deferred) update the search params - useEffect(() => { - setSearchParams(filtersDeferred, true); - }, [filtersDeferred, setSearchParams]); + const { filters, updateFilter, clear } = useContactPointsFilter(); const hasFilters = Object.values(filters).some(Boolean); return ( - + } onChange={(event) => { - setFilters({ - ...filters, - search: event.currentTarget.value, - }); + updateFilter({ search: event.currentTarget.value }); }} - value={filtersDeferred.search} + value={filters.search} /> {/* I don't feel like doing this for non-Grafana Alertmanagers right now */} @@ -56,12 +38,9 @@ const ContactPointsFilter = () => { { - setFilters({ - ...filters, - type: type.value, - }); + updateFilter({ type: type.value }); }} /> diff --git a/public/app/features/alerting/unified/components/contact-points/components/useContactPointsFilter.tsx b/public/app/features/alerting/unified/components/contact-points/components/useContactPointsFilter.tsx new file mode 100644 index 00000000000..e5f18d54e45 --- /dev/null +++ b/public/app/features/alerting/unified/components/contact-points/components/useContactPointsFilter.tsx @@ -0,0 +1,44 @@ +import { useCallback, useDeferredValue, useEffect, useState } from 'react'; + +import { useURLSearchParams } from '../../../hooks/useURLSearchParams'; + +export type ContactPointsFilterState = { + search?: string; + type?: string; +}; + +export function useContactPointsFilter() { + const [searchParams, setSearchParams] = useURLSearchParams(); + const [filters, setFilters] = useState({ + search: searchParams.get('search') ?? undefined, + type: searchParams.get('type') ?? undefined, + }); + const filtersDeferred = useDeferredValue(filters); + + const clear = useCallback(() => { + setFilters({ search: '', type: '' }); + }, []); + + const updateFilter = useCallback((update: Partial) => { + setFilters((filters) => ({ ...filters, ...update })); + }, []); + + // // when the filters are updated (deferred) update the search params + useEffect(() => { + setSearchParams(filtersDeferred, true); + }, [filtersDeferred, setSearchParams]); + + // When URL params change, update filters + useEffect(() => { + setFilters({ + search: searchParams.get('search') ?? undefined, + type: searchParams.get('type') ?? undefined, + }); + }, [searchParams]); + + return { + clear, + filters: filtersDeferred, + updateFilter, + }; +} diff --git a/public/app/features/alerting/unified/components/contact-points/useContactPointsSearch.tsx b/public/app/features/alerting/unified/components/contact-points/useContactPointsSearch.tsx index 7c09430fc4a..6cb207e0858 100644 --- a/public/app/features/alerting/unified/components/contact-points/useContactPointsSearch.tsx +++ b/public/app/features/alerting/unified/components/contact-points/useContactPointsSearch.tsx @@ -5,6 +5,8 @@ import { useMemo } from 'react'; import { RECEIVER_META_KEY } from 'app/features/alerting/unified/components/contact-points/constants'; import { ContactPointWithMetadata } from 'app/features/alerting/unified/components/contact-points/utils'; +import { ContactPointsFilterState } from './components/useContactPointsFilter'; + const fuzzyFinder = new uFuzzy({ intraMode: 1, intraIns: 1, @@ -16,8 +18,11 @@ const fuzzyFinder = new uFuzzy({ // let's search in two different haystacks, the name of the contact point and the type of the receiver(s) export const useContactPointsSearch = ( contactPoints: ContactPointWithMetadata[], - search?: string | null + filters: ContactPointsFilterState = {} ): ContactPointWithMetadata[] => { + const hasFilters = Object.values(filters).some(Boolean); + const { search, type } = filters; + const nameHaystack = useMemo(() => { return contactPoints.map((contactPoint) => contactPoint.name); }, [contactPoints]); @@ -29,14 +34,23 @@ export const useContactPointsSearch = ( ); }, [contactPoints]); - if (!search) { + if (!hasFilters) { return contactPoints; } - const nameHits = fuzzyFinder.filter(nameHaystack, search) ?? []; - const typeHits = fuzzyFinder.filter(typeHaystack, search) ?? []; + const results: ContactPointWithMetadata[] = []; - const hits = [...nameHits, ...typeHits]; + if (search) { + fuzzyFinder.filter(nameHaystack, search)?.forEach((id) => { + results.push(contactPoints[id]); + }); + } + + if (type) { + fuzzyFinder.filter(typeHaystack, type)?.forEach((id) => { + results.push(contactPoints[id]); + }); + } - return uniq(hits).map((id) => contactPoints[id]) ?? []; + return uniq(results); };