import { useMemo } from 'react'; import { Alert, Button, EmptyState, LinkButton, LoadingPlaceholder, Pagination, Stack, Tab, TabContent, TabsBar, Text, } from '@grafana/ui'; import { contextSrv } from 'app/core/core'; import { Trans, t } from 'app/core/internationalization'; import { shouldUseK8sApi } from 'app/features/alerting/unified/utils/k8s/utils'; import { makeAMLink, stringifyErrorLike } from 'app/features/alerting/unified/utils/misc'; import { AccessControlAction } from 'app/types'; import { AlertmanagerAction, useAlertmanagerAbility } from '../../hooks/useAbilities'; import { usePagination } from '../../hooks/usePagination'; import { useURLSearchParams } from '../../hooks/useURLSearchParams'; import { useAlertmanager } from '../../state/AlertmanagerContext'; import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource'; import { withPageErrorBoundary } from '../../withPageErrorBoundary'; import { AlertmanagerPageWrapper } from '../AlertingPageWrapper'; import { GrafanaAlertmanagerDeliveryWarning } from '../GrafanaAlertmanagerDeliveryWarning'; import { ContactPoint } from './ContactPoint'; import { NotificationTemplates } from './NotificationTemplates'; import { ContactPointsFilter } from './components/ContactPointsFilter'; import { GlobalConfigAlert } from './components/GlobalConfigAlert'; import { useContactPointsWithStatus } from './useContactPoints'; import { useContactPointsSearch } from './useContactPointsSearch'; import { ALL_CONTACT_POINTS, useExportContactPoint } from './useExportContactPoint'; import { ContactPointWithMetadata } from './utils'; export enum ActiveTab { ContactPoints = 'contact_points', NotificationTemplates = 'templates', } const DEFAULT_PAGE_SIZE = 10; const ContactPointsTab = () => { const { selectedAlertmanager } = useAlertmanager(); const [queryParams] = useURLSearchParams(); // 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 const fetchPolicies = !shouldUseK8sApi(selectedAlertmanager!); // User may have access to list contact points, but not permission to fetch the status endpoint const fetchStatuses = contextSrv.hasPermission(AccessControlAction.AlertingNotificationsRead); const { isLoading, error, contactPoints } = useContactPointsWithStatus({ alertmanager: selectedAlertmanager!, fetchPolicies, fetchStatuses, }); const [addContactPointSupported, addContactPointAllowed] = useAlertmanagerAbility( AlertmanagerAction.CreateContactPoint ); const [exportContactPointsSupported, exportContactPointsAllowed] = useAlertmanagerAbility( AlertmanagerAction.ExportContactPoint ); const [ExportDrawer, showExportDrawer] = useExportContactPoint(); const search = queryParams.get('search'); if (isLoading) { return ; } const isGrafanaManagedAlertmanager = selectedAlertmanager === GRAFANA_RULES_SOURCE_NAME; if (contactPoints.length === 0) { return ( Create contact point ) } message={t('alerting.contact-points.empty-state.title', "You don't have any contact points yet")} /> ); } return ( <> {/* TODO we can add some additional info here with a ToggleTip */} {addContactPointSupported && ( Create contact point )} {exportContactPointsSupported && ( )} {error ? ( {stringifyErrorLike(error)} ) : ( )} {/* Grafana manager Alertmanager does not support global config, Mimir and Cortex do */} {!isGrafanaManagedAlertmanager && } {ExportDrawer} ); }; const NotificationTemplatesTab = () => { const [createTemplateSupported, createTemplateAllowed] = useAlertmanagerAbility( AlertmanagerAction.CreateNotificationTemplate ); return ( <> Create notification templates to customize your notifications. {createTemplateSupported && ( Add notification template group )} ); }; const useTabQueryParam = (defaultTab: ActiveTab) => { const [queryParams, setQueryParams] = useURLSearchParams(); const param = useMemo(() => { const queryParam = queryParams.get('tab'); if (!queryParam || !Object.values(ActiveTab).map(String).includes(queryParam)) { return defaultTab; } return queryParam || defaultTab; }, [defaultTab, queryParams]); const setParam = (tab: ActiveTab) => setQueryParams({ tab }); return [param, setParam] as const; }; export const ContactPointsPageContents = () => { const { selectedAlertmanager } = useAlertmanager(); const [, showContactPointsTab] = useAlertmanagerAbility(AlertmanagerAction.ViewContactPoint); const [, showTemplatesTab] = useAlertmanagerAbility(AlertmanagerAction.ViewNotificationTemplate); // Depending on permissions, user may not have access to all tabs, // but we can default to picking the first one that they definitely _do_ have access to const defaultTab = [ showContactPointsTab && ActiveTab.ContactPoints, showTemplatesTab && ActiveTab.NotificationTemplates, ].filter((tab) => !!tab)[0]; const [activeTab, setActiveTab] = useTabQueryParam(defaultTab); const { contactPoints } = useContactPointsWithStatus({ alertmanager: selectedAlertmanager!, }); const showingContactPoints = activeTab === ActiveTab.ContactPoints; const showNotificationTemplates = activeTab === ActiveTab.NotificationTemplates; return ( <> {showContactPointsTab && ( setActiveTab(ActiveTab.ContactPoints)} /> )} {showTemplatesTab && ( setActiveTab(ActiveTab.NotificationTemplates)} /> )} {showingContactPoints && } {showNotificationTemplates && } ); }; interface ContactPointsListProps { contactPoints: ContactPointWithMetadata[]; search?: string | null; pageSize?: number; } const ContactPointsList = ({ contactPoints, search, pageSize = DEFAULT_PAGE_SIZE }: ContactPointsListProps) => { const searchResults = useContactPointsSearch(contactPoints, search); const { page, pageItems, numberOfPages, onPageChange } = usePagination(searchResults, 1, pageSize); if (pageItems.length === 0) { const emptyMessage = t('alerting.contact-points.no-contact-points-found', 'No contact points found'); return ; } return ( <> {pageItems.map((contactPoint, index) => { const key = `${contactPoint.name}-${index}`; return ; })} ); }; function ContactPointsPage() { return ( ); } export default withPageErrorBoundary(ContactPointsPage);