mirror of https://github.com/grafana/grafana
Alerting: receivers page + template list (#33112)
parent
382cab6406
commit
60b469f836
@ -0,0 +1,7 @@ |
|||||||
|
import { config } from '@grafana/runtime'; |
||||||
|
import NotificationsListPage from './NotificationsListPage'; |
||||||
|
import Receivers from './unified/Receivers'; |
||||||
|
|
||||||
|
// route between unified and "old" alerting pages based on feature flag
|
||||||
|
|
||||||
|
export default config.featureToggles.ngalert ? Receivers : NotificationsListPage; |
@ -0,0 +1,40 @@ |
|||||||
|
import { Field, InfoBox, LoadingPlaceholder } from '@grafana/ui'; |
||||||
|
import React, { FC, useEffect } from 'react'; |
||||||
|
import { useDispatch } from 'react-redux'; |
||||||
|
import { AlertingPageWrapper } from './components/AlertingPageWrapper'; |
||||||
|
import { AlertManagerPicker } from './components/AlertManagerPicker'; |
||||||
|
import { TemplatesTable } from './components/receivers/TemplatesTable'; |
||||||
|
import { useAlertManagerSourceName } from './hooks/useAlertManagerSourceName'; |
||||||
|
import { useUnifiedAlertingSelector } from './hooks/useUnifiedAlertingSelector'; |
||||||
|
import { fetchAlertManagerConfigAction } from './state/actions'; |
||||||
|
import { initialAsyncRequestState } from './utils/redux'; |
||||||
|
|
||||||
|
const Receivers: FC = () => { |
||||||
|
const [alertManagerSourceName, setAlertManagerSourceName] = useAlertManagerSourceName(); |
||||||
|
const dispatch = useDispatch(); |
||||||
|
|
||||||
|
const config = useUnifiedAlertingSelector((state) => state.amConfigs); |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
dispatch(fetchAlertManagerConfigAction(alertManagerSourceName)); |
||||||
|
}, [alertManagerSourceName, dispatch]); |
||||||
|
|
||||||
|
const { result, loading, error } = config[alertManagerSourceName] || initialAsyncRequestState; |
||||||
|
|
||||||
|
return ( |
||||||
|
<AlertingPageWrapper pageId="receivers"> |
||||||
|
<Field label="Choose alert manager"> |
||||||
|
<AlertManagerPicker current={alertManagerSourceName} onChange={setAlertManagerSourceName} /> |
||||||
|
</Field> |
||||||
|
{error && !loading && ( |
||||||
|
<InfoBox severity="error" title={<h4>Error loading alert manager config</h4>}> |
||||||
|
{error.message || 'Unknown error.'} |
||||||
|
</InfoBox> |
||||||
|
)} |
||||||
|
{loading && <LoadingPlaceholder text="loading receivers..." />} |
||||||
|
{result && !loading && !error && <TemplatesTable config={result} />} |
||||||
|
</AlertingPageWrapper> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
export default Receivers; |
@ -0,0 +1,36 @@ |
|||||||
|
import { css } from '@emotion/css'; |
||||||
|
import { GrafanaTheme } from '@grafana/data'; |
||||||
|
import { Button, useStyles } from '@grafana/ui'; |
||||||
|
import React, { FC } from 'react'; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
title: string; |
||||||
|
description: string; |
||||||
|
addButtonLabel: string; |
||||||
|
} |
||||||
|
|
||||||
|
export const ReceiversSection: FC<Props> = ({ title, description, addButtonLabel, children }) => { |
||||||
|
const styles = useStyles(getStyles); |
||||||
|
return ( |
||||||
|
<> |
||||||
|
<div className={styles.heading}> |
||||||
|
<div> |
||||||
|
<h4>{title}</h4> |
||||||
|
<p className={styles.description}>{description}</p> |
||||||
|
</div> |
||||||
|
<Button icon="plus">{addButtonLabel}</Button> |
||||||
|
</div> |
||||||
|
{children} |
||||||
|
</> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
const getStyles = (theme: GrafanaTheme) => ({ |
||||||
|
heading: css` |
||||||
|
display: flex; |
||||||
|
justify-content: space-between; |
||||||
|
`,
|
||||||
|
description: css` |
||||||
|
color: ${theme.colors.textSemiWeak}; |
||||||
|
`,
|
||||||
|
}); |
@ -0,0 +1,80 @@ |
|||||||
|
import { useStyles } from '@grafana/ui'; |
||||||
|
import { AlertManagerCortexConfig } from 'app/plugins/datasource/alertmanager/types'; |
||||||
|
import React, { FC, Fragment, useMemo, useState } from 'react'; |
||||||
|
import { getAlertTableStyles } from '../../styles/table'; |
||||||
|
import { CollapseToggle } from '../CollapseToggle'; |
||||||
|
import { DetailsField } from '../DetailsField'; |
||||||
|
import { ActionButton } from '../rules/ActionButton'; |
||||||
|
import { ActionIcon } from '../rules/ActionIcon'; |
||||||
|
import { ReceiversSection } from './ReceiversSection'; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
config: AlertManagerCortexConfig; |
||||||
|
} |
||||||
|
|
||||||
|
export const TemplatesTable: FC<Props> = ({ config }) => { |
||||||
|
const [expandedTemplates, setExpandedTemplates] = useState<Record<string, boolean>>({}); |
||||||
|
const tableStyles = useStyles(getAlertTableStyles); |
||||||
|
|
||||||
|
const templateRows = useMemo(() => Object.entries(config.template_files), [config]); |
||||||
|
|
||||||
|
return ( |
||||||
|
<ReceiversSection |
||||||
|
title="Message templates" |
||||||
|
description="Templates construct the messages that get sent to the contact points." |
||||||
|
addButtonLabel="New templates" |
||||||
|
> |
||||||
|
<table className={tableStyles.table}> |
||||||
|
<colgroup> |
||||||
|
<col className={tableStyles.colExpand} /> |
||||||
|
<col /> |
||||||
|
<col /> |
||||||
|
</colgroup> |
||||||
|
<thead> |
||||||
|
<tr> |
||||||
|
<th></th> |
||||||
|
<th>Template</th> |
||||||
|
<th>Actions</th> |
||||||
|
</tr> |
||||||
|
</thead> |
||||||
|
<tbody> |
||||||
|
{!templateRows.length && ( |
||||||
|
<tr className={tableStyles.evenRow}> |
||||||
|
<td colSpan={3}>No templates defined.</td> |
||||||
|
</tr> |
||||||
|
)} |
||||||
|
{templateRows.map(([name, content], idx) => { |
||||||
|
const isExpanded = !!expandedTemplates[name]; |
||||||
|
return ( |
||||||
|
<Fragment key={name}> |
||||||
|
<tr key={name} className={idx % 2 === 0 ? tableStyles.evenRow : undefined}> |
||||||
|
<td> |
||||||
|
<CollapseToggle |
||||||
|
isCollapsed={!expandedTemplates[name]} |
||||||
|
onToggle={() => setExpandedTemplates({ ...expandedTemplates, [name]: !isExpanded })} |
||||||
|
/> |
||||||
|
</td> |
||||||
|
<td>{name}</td> |
||||||
|
<td className={tableStyles.actionsCell}> |
||||||
|
<ActionButton icon="pen">Edit</ActionButton> |
||||||
|
<ActionIcon tooltip="delete template" icon="trash-alt" /> |
||||||
|
</td> |
||||||
|
</tr> |
||||||
|
{isExpanded && ( |
||||||
|
<tr className={idx % 2 === 0 ? tableStyles.evenRow : undefined}> |
||||||
|
<td></td> |
||||||
|
<td colSpan={2}> |
||||||
|
<DetailsField label="Description" horizontal={true}> |
||||||
|
<pre>{content}</pre> |
||||||
|
</DetailsField> |
||||||
|
</td> |
||||||
|
</tr> |
||||||
|
)} |
||||||
|
</Fragment> |
||||||
|
); |
||||||
|
})} |
||||||
|
</tbody> |
||||||
|
</table> |
||||||
|
</ReceiversSection> |
||||||
|
); |
||||||
|
}; |
Loading…
Reference in new issue