The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/public/app/features/alerting/unified/Home.tsx

273 lines
7.8 KiB

import { css, cx } from '@emotion/css';
import React from 'react';
import SVG from 'react-inlinesvg';
import { GrafanaTheme2 } from '@grafana/data';
import { Stack } from '@grafana/experimental';
import { config } from '@grafana/runtime';
import { Icon, useStyles2, useTheme2 } from '@grafana/ui';
import { AlertingPageWrapper } from './components/AlertingPageWrapper';
export default function Home() {
const theme = useTheme2();
const styles = useStyles2(getWelcomePageStyles);
return (
<AlertingPageWrapper pageId={config.featureToggles.topnav ? 'alerting' : 'alert-home'}>
<div className={styles.grid}>
<WelcomeHeader className={styles.ctaContainer} />
<ContentBox className={styles.flowBlock}>
<div>
<h3>How it works</h3>
<ul className={styles.list}>
<li>
Grafana alerting periodically queries data sources and evaluates the condition defined in the alert rule
</li>
<li>If the condition is breached, an alert instance fires</li>
<li>Firing instances are routed to notification policies based on matching labels</li>
<li>Notifications are sent out to the contact points specified in the notification policy</li>
</ul>
</div>
<SVG
src={`public/img/alerting/at_a_glance_${theme.name.toLowerCase()}.svg`}
width={undefined}
height={undefined}
/>
</ContentBox>
<ContentBox className={styles.gettingStartedBlock}>
<h3>Get started</h3>
<Stack direction="column" alignItems="space-between">
<ul className={styles.list}>
<li>
<strong>Create an alert rule</strong> by adding queries and expressions from multiple data sources.
</li>
<li>
<strong>Add labels</strong> to your alert rules{' '}
<strong>to connect them to notification policies</strong>
</li>
<li>
<strong>Configure contact points</strong> to define where to send your notifications to.
</li>
<li>
<strong>Configure notification policies</strong> to route your alert instances to contact points.
</li>
</ul>
<div>
<ArrowLink href="https://grafana.com/docs/grafana/latest/alerting/" title="Read more in the Docs" />
</div>
</Stack>
</ContentBox>
<ContentBox className={styles.videoBlock}>
<iframe
title="Alerting - Introductory video"
src="https://player.vimeo.com/video/720001629?h=c6c1732f92"
width="960"
height="540"
allow="autoplay; fullscreen"
allowFullScreen
frameBorder="0"
// This is necessary because color-scheme defined on :root has impact on iframe elements
// More about how color-scheme works for iframes https://github.com/w3c/csswg-drafts/issues/4772
// Summary: If the color scheme of an iframe differs from embedding document iframe gets an opaque canvas bg appropriate to its color scheme
style={{ colorScheme: 'light dark' }}
></iframe>
</ContentBox>
</div>
</AlertingPageWrapper>
);
}
const getWelcomePageStyles = (theme: GrafanaTheme2) => ({
grid: css`
display: grid;
grid-template-rows: min-content auto auto;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
gap: ${theme.spacing(2)};
`,
ctaContainer: css`
grid-column: 1 / span 5;
`,
flowBlock: css`
grid-column: 1 / span 5;
display: flex;
flex-wrap: wrap;
gap: ${theme.spacing(1)};
& > div {
flex: 2;
min-width: 350px;
}
& > svg {
flex: 3;
min-width: 500px;
}
`,
videoBlock: css`
grid-column: 3 / span 3;
grid-row: 3 / span 1;
// Video required
position: relative;
padding: 56.25% 0 0 0; /* 16:9 */
iframe {
position: absolute;
top: ${theme.spacing(2)};
left: ${theme.spacing(2)};
width: calc(100% - ${theme.spacing(4)});
height: calc(100% - ${theme.spacing(4)});
border: none;
}
`,
gettingStartedBlock: css`
grid-column: 1 / span 2;
justify-content: space-between;
`,
list: css`
margin: ${theme.spacing(0, 2)};
& > li {
margin-bottom: ${theme.spacing(1)};
}
`,
});
function WelcomeHeader({ className }: { className?: string }) {
const styles = useStyles2(getWelcomeHeaderStyles);
return (
<ContentBox className={cx(styles.ctaContainer, className)}>
<WelcomeCTABox
title="Alert rules"
description="Define the condition that must be me before an alert rule fires"
href="/alerting/list"
hrefText="Manage alert rules"
/>
<div className={styles.separator} />
<WelcomeCTABox
title="Contact points"
description="Configure who receives notifications and how they are sent"
href="/alerting/notifications"
hrefText="Manage contact points"
/>
<div className={styles.separator} />
<WelcomeCTABox
title="Notification policies"
description="Configure how firing alert instances are routed to contact points"
href="/alerting/routes"
hrefText="Manage notification policies"
/>
</ContentBox>
);
}
const getWelcomeHeaderStyles = (theme: GrafanaTheme2) => ({
ctaContainer: css`
padding: ${theme.spacing(4, 2)};
display: flex;
gap: ${theme.spacing(4)};
justify-content: space-between;
flex-wrap: wrap;
${theme.breakpoints.down('lg')} {
flex-direction: column;
}
`,
separator: css`
width: 1px;
background-color: ${theme.colors.border.medium};
${theme.breakpoints.down('lg')} {
display: none;
}
`,
});
interface WelcomeCTABoxProps {
title: string;
description: string;
href: string;
hrefText: string;
}
function WelcomeCTABox({ title, description, href, hrefText }: WelcomeCTABoxProps) {
const styles = useStyles2(getWelcomeCTAButtonStyles);
return (
<div className={styles.container}>
<h3 className={styles.title}>{title}</h3>
<div className={styles.desc}>{description}</div>
<div className={styles.actionRow}>
<a href={href} className={styles.link}>
{hrefText}
</a>
</div>
</div>
);
}
const getWelcomeCTAButtonStyles = (theme: GrafanaTheme2) => ({
container: css`
flex: 1;
min-width: 240px;
display: grid;
gap: ${theme.spacing(1)};
grid-template-columns: min-content 1fr 1fr 1fr;
grid-template-rows: min-content auto min-content;
`,
title: css`
margin-bottom: 0;
grid-column: 2 / span 3;
grid-row: 1;
`,
desc: css`
grid-column: 2 / span 3;
grid-row: 2;
`,
actionRow: css`
grid-column: 2 / span 3;
grid-row: 3;
max-width: 240px;
`,
link: css`
color: ${theme.colors.text.link};
`,
});
function ContentBox({ children, className }: React.PropsWithChildren<{ className?: string }>) {
const styles = useStyles2(getContentBoxStyles);
return <div className={cx(styles.box, className)}>{children}</div>;
}
const getContentBoxStyles = (theme: GrafanaTheme2) => ({
box: css`
padding: ${theme.spacing(2)};
background-color: ${theme.colors.background.secondary};
border-radius: 3px;
outline: 1px solid ${theme.colors.border.strong};
`,
});
function ArrowLink({ href, title }: { href: string; title: string }) {
const styles = useStyles2(getArrowLinkStyles);
return (
<a href={href} className={styles.link} rel="noreferrer">
{title} <Icon name="angle-right" size="xl" />
</a>
);
}
const getArrowLinkStyles = (theme: GrafanaTheme2) => ({
link: css`
display: block;
color: ${theme.colors.text.link};
`,
});