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);