[Alerting] - Display scenes using SceneAppPage for caching (#74767)

* Display scenes using SceneAppPage for caching

Also, reorganizing them in different tabs per Torkel's suggestion

* Fix lint

* Alerting home app poc

* Fix imports

* fix routing

* remove width

* Apply feature flags back

* Add comment regarding enabling the rule group query variable

* Upgrade scenes lib

* Add query variables back

---------

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
This commit is contained in:
Virginia Cepeda 2023-09-19 09:17:56 -03:00 committed by GitHub
parent 7209e84cd2
commit 0d3c47da99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 165 additions and 128 deletions

View File

@ -251,7 +251,7 @@
"@grafana/lezer-traceql": "0.0.6",
"@grafana/monaco-logql": "^0.0.7",
"@grafana/runtime": "workspace:*",
"@grafana/scenes": "^1.1.1",
"@grafana/scenes": "^1.3.1",
"@grafana/schema": "workspace:*",
"@grafana/ui": "workspace:*",
"@kusto/monaco-kusto": "^7.4.0",

View File

@ -93,6 +93,13 @@ const unifiedRoutes: RouteDescriptor[] = [
() => import(/* webpackChunkName: "AlertingHome" */ 'app/features/alerting/unified/home/Home')
),
},
{
path: '/alerting/home',
exact: false,
component: SafeDynamicImport(
() => import(/* webpackChunkName: "AlertingHome" */ 'app/features/alerting/unified/home/Home')
),
},
{
path: '/alerting/list',
roles: evaluateAccess([AccessControlAction.AlertingRuleRead, AccessControlAction.AlertingRuleExternalRead]),

View File

@ -4,8 +4,23 @@ import SVG from 'react-inlinesvg';
import { GrafanaTheme2 } from '@grafana/data';
import { Stack } from '@grafana/experimental';
import { EmbeddedScene, SceneFlexLayout, SceneFlexItem, SceneReactObject } from '@grafana/scenes';
import { Icon, useStyles2, useTheme2 } from '@grafana/ui';
export const getOverviewScene = () => {
return new EmbeddedScene({
body: new SceneFlexLayout({
children: [
new SceneFlexItem({
body: new SceneReactObject({
component: GettingStarted,
}),
}),
],
}),
});
};
export default function GettingStarted({ showWelcomeHeader }: { showWelcomeHeader?: boolean }) {
const theme = useTheme2();
const styles = useStyles2(getWelcomePageStyles);
@ -130,32 +145,43 @@ export 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 met 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>
<div className={styles.welcomeHeaderWrapper}>
<div className={styles.subtitle}>Learn about problems in your systems moments after they occur</div>
<ContentBox className={cx(styles.ctaContainer, className)}>
<WelcomeCTABox
title="Alert rules"
description="Define the condition that must be met 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>
</div>
);
}
const getWelcomeHeaderStyles = (theme: GrafanaTheme2) => ({
welcomeHeaderWrapper: css({
color: theme.colors.text.primary,
}),
subtitle: css({
color: theme.colors.text.secondary,
paddingBottom: theme.spacing(2),
}),
ctaContainer: css`
padding: ${theme.spacing(4, 2)};
display: flex;

View File

@ -1,50 +1,71 @@
import React, { useState } from 'react';
import { config } from '@grafana/runtime';
import { Tab, TabContent, TabsBar } from '@grafana/ui';
import { SceneApp, SceneAppPage } from '@grafana/scenes';
import { usePageNav } from 'app/core/components/Page/usePageNav';
import { PluginPageContext, PluginPageContextType } from 'app/features/plugins/components/PluginPageContext';
import { AlertingPageWrapper } from '../components/AlertingPageWrapper';
import { getOverviewScene, WelcomeHeader } from './GettingStarted';
import { getGrafanaScenes } from './Insights';
import GettingStarted, { WelcomeHeader } from './GettingStarted';
import Insights from './Insights';
let homeApp: SceneApp | undefined;
type HomeTabs = 'insights' | 'gettingStarted';
export function getHomeApp(insightsEnabled: boolean) {
if (homeApp) {
return homeApp;
}
if (insightsEnabled) {
homeApp = new SceneApp({
pages: [
new SceneAppPage({
title: 'Alerting',
subTitle: <WelcomeHeader />,
url: '/alerting',
hideFromBreadcrumbs: true,
tabs: [
new SceneAppPage({
title: 'Grafana',
url: '/alerting/home/insights',
getScene: getGrafanaScenes,
}),
new SceneAppPage({
title: 'Overview',
url: '/alerting/home/overview',
getScene: getOverviewScene,
}),
],
}),
],
});
} else {
homeApp = new SceneApp({
pages: [
new SceneAppPage({
title: 'Alerting',
subTitle: <WelcomeHeader />,
url: '/alerting',
hideFromBreadcrumbs: true,
getScene: getOverviewScene,
}),
],
});
}
return homeApp;
}
export default function Home() {
const [activeTab, setActiveTab] = useState<HomeTabs>('insights');
const insightsEnabled = !!config.featureToggles.alertingInsights;
const appScene = getHomeApp(insightsEnabled);
const sectionNav = usePageNav('alerting')!;
const [pluginContext] = useState<PluginPageContextType>({ sectionNav });
const alertingInsightsEnabled = config.featureToggles.alertingInsights;
return (
<AlertingPageWrapper pageId={'alerting'}>
{alertingInsightsEnabled && (
<>
<WelcomeHeader />
<TabsBar>
<Tab
key={'insights'}
label={'Insights'}
active={activeTab === 'insights'}
onChangeTab={() => {
setActiveTab('insights');
}}
/>
<Tab
key={'gettingStarted'}
label={'Overview'}
active={activeTab === 'gettingStarted'}
onChangeTab={() => {
setActiveTab('gettingStarted');
}}
/>
</TabsBar>
<TabContent>
{activeTab === 'insights' && <Insights />}
{activeTab === 'gettingStarted' && <GettingStarted />}
</TabContent>
</>
)}
{!alertingInsightsEnabled && <GettingStarted showWelcomeHeader={true} />}
</AlertingPageWrapper>
<PluginPageContext.Provider value={pluginContext}>
<appScene.Component model={appScene} />
</PluginPageContext.Provider>
);
}

View File

@ -1,16 +1,13 @@
import { css } from '@emotion/css';
import React from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import {
EmbeddedScene,
NestedScene,
QueryVariable,
SceneFlexItem,
SceneFlexLayout,
SceneTimeRange,
SceneVariableSet,
VariableValueSelectors,
} from '@grafana/scenes';
import { useStyles2, CollapsableSection } from '@grafana/ui';
import { getFiringAlertsScene } from '../insights/grafana/FiringAlertsPercentage';
import { getFiringAlertsRateScene } from '../insights/grafana/FiringAlertsRate';
@ -49,31 +46,50 @@ const grafanaCloudPromDs = {
const THIS_WEEK_TIME_RANGE = new SceneTimeRange({ from: 'now-1w', to: 'now' });
const LAST_WEEK_TIME_RANGE = new SceneTimeRange({ from: 'now-2w', to: 'now-1w' });
function getGrafanaScenes() {
export function getGrafanaScenes() {
return new EmbeddedScene({
body: new SceneFlexLayout({
wrap: 'wrap',
direction: 'column',
children: [
getMostFiredInstancesScene(THIS_WEEK_TIME_RANGE, ashDs, 'Top 10 firing instances this week'),
getFiringAlertsRateScene(THIS_WEEK_TIME_RANGE, ashDs, 'Alerts firing per minute'),
getFiringAlertsScene(THIS_WEEK_TIME_RANGE, ashDs, 'Firing alerts this week'),
getFiringAlertsScene(LAST_WEEK_TIME_RANGE, ashDs, 'Firing alerts last week'),
new SceneFlexLayout({
children: [
getMostFiredInstancesScene(THIS_WEEK_TIME_RANGE, ashDs, 'Top 10 firing instances this week'),
getFiringAlertsRateScene(THIS_WEEK_TIME_RANGE, ashDs, 'Alerts firing per minute'),
],
}),
new SceneFlexLayout({
children: [
getFiringAlertsScene(THIS_WEEK_TIME_RANGE, ashDs, 'Firing alerts this week'),
getFiringAlertsScene(LAST_WEEK_TIME_RANGE, ashDs, 'Firing alerts last week'),
],
}),
new SceneFlexItem({
ySizing: 'content',
body: getCloudScenes(),
}),
new SceneFlexItem({
ySizing: 'content',
body: getMimirManagedRulesScenes(),
}),
new SceneFlexItem({
ySizing: 'content',
body: getMimirManagedRulesPerGroupScenes(),
}),
],
}),
});
}
function getCloudScenes() {
return new EmbeddedScene({
return new NestedScene({
title: 'Mimir alertmanager',
canCollapse: true,
isCollapsed: true,
body: new SceneFlexLayout({
wrap: 'wrap',
children: [
getAlertsByStateScene(THIS_WEEK_TIME_RANGE, cloudUsageDs, 'Alerts by State'),
getNotificationsScene(THIS_WEEK_TIME_RANGE, cloudUsageDs, 'Notifications'),
getSilencesScene(LAST_WEEK_TIME_RANGE, cloudUsageDs, 'Silences'),
getInvalidConfigScene(THIS_WEEK_TIME_RANGE, cloudUsageDs, 'Invalid configuration'),
],
@ -82,7 +98,10 @@ function getCloudScenes() {
}
function getMimirManagedRulesScenes() {
return new EmbeddedScene({
return new NestedScene({
title: 'Mimir-managed rules',
canCollapse: true,
isCollapsed: true,
body: new SceneFlexLayout({
wrap: 'wrap',
children: [
@ -108,7 +127,10 @@ function getMimirManagedRulesPerGroupScenes() {
query: 'label_values(grafanacloud_instance_rule_group_rules,rule_group)',
});
return new EmbeddedScene({
return new NestedScene({
title: 'Mimir-managed Rules - Per Rule Group',
canCollapse: true,
isCollapsed: true,
body: new SceneFlexLayout({
wrap: 'wrap',
children: [
@ -124,37 +146,3 @@ function getMimirManagedRulesPerGroupScenes() {
controls: [new VariableValueSelectors({})],
});
}
export default function Insights() {
const styles = useStyles2(getStyles);
const grafanaScenes = getGrafanaScenes();
const cloudScenes = getCloudScenes();
const mimirRulesScenes = getMimirManagedRulesScenes();
const mimirRulesPerGroupScenes = getMimirManagedRulesPerGroupScenes();
return (
<div className={styles.container}>
<CollapsableSection label="Grafana" isOpen={true}>
<grafanaScenes.Component model={grafanaScenes} />
</CollapsableSection>
<CollapsableSection label="Mimir Alertmanager" isOpen={true}>
<cloudScenes.Component model={cloudScenes} />
</CollapsableSection>
<CollapsableSection label="Mimir-managed Rules" isOpen={true}>
<mimirRulesScenes.Component model={mimirRulesScenes} />
</CollapsableSection>
<CollapsableSection label="Mimir-managed Rules - Per Rule Group" isOpen={true}>
<mimirRulesPerGroupScenes.Component model={mimirRulesPerGroupScenes} />
</CollapsableSection>
</div>
);
}
const getStyles = (theme: GrafanaTheme2) => ({
container: css({
padding: '10px 0 10px 0',
}),
});

View File

@ -61,8 +61,7 @@ export function getFiringAlertsScene(timeRange: SceneTimeRange, datasource: Data
});
return new SceneFlexItem({
width: 'calc(50% - 4px)',
height: 300,
minHeight: 300,
body: PanelBuilders.stat().setTitle(panelTitle).setData(transformation).setUnit('percent').build(),
});
}

View File

@ -18,8 +18,7 @@ export function getFiringAlertsRateScene(timeRange: SceneTimeRange, datasource:
});
return new SceneFlexItem({
width: 'calc(50% - 4px)',
height: 300,
minHeight: 300,
body: PanelBuilders.timeseries()
.setTitle(panelTitle)
.setData(query)

View File

@ -103,8 +103,7 @@ export function getMostFiredInstancesScene(timeRange: SceneTimeRange, datasource
});
return new SceneFlexItem({
width: 'calc(50% - 4px)',
height: 300,
minHeight: 300,
body: PanelBuilders.table().setTitle(panelTitle).setData(transformation).build(),
});
}

View File

@ -54,8 +54,7 @@ export function getMostFiredRulesScene(timeRange: SceneTimeRange, datasource: Da
});
return new SceneFlexItem({
width: 'calc(50% - 4px)',
height: 300,
minHeight: 300,
body: PanelBuilders.table().setTitle(panelTitle).setData(transformation).build(),
});
}

View File

@ -49,8 +49,7 @@ export function getMostFiredLabelsScene(timeRange: SceneTimeRange, datasource: D
});
return new SceneFlexItem({
width: 'calc(50% - 8px)',
height: 300,
minHeight: 300,
body: PanelBuilders.table().setTitle(panelTitle).setData(query).build(),
});
}

View File

@ -4010,9 +4010,9 @@ __metadata:
languageName: unknown
linkType: soft
"@grafana/scenes@npm:^1.1.1":
version: 1.1.1
resolution: "@grafana/scenes@npm:1.1.1"
"@grafana/scenes@npm:^1.3.1":
version: 1.3.1
resolution: "@grafana/scenes@npm:1.3.1"
dependencies:
"@grafana/e2e-selectors": 10.0.2
react-grid-layout: 1.3.4
@ -4024,7 +4024,7 @@ __metadata:
"@grafana/runtime": 10.0.3
"@grafana/schema": 10.0.3
"@grafana/ui": 10.0.3
checksum: 6405998a40e38f088443f5d4b1f5ea1f73e5bc0d08216e4aaccf8ff0b68ec4c3d691430857f357d3eed335dee0dc2e24c41c5c2f286fc7fdd32375382ad3eafe
checksum: fe348e4eaaa3604d0d1ec745b14ae1c0752ce1aa481e5f05a10cdf9392448386e600c2a94f2ae23af83f59daf34cd11fb7a336da863b8624b98c8d3c2732b3c3
languageName: node
linkType: hard
@ -19702,7 +19702,7 @@ __metadata:
"@grafana/lezer-traceql": 0.0.6
"@grafana/monaco-logql": ^0.0.7
"@grafana/runtime": "workspace:*"
"@grafana/scenes": ^1.1.1
"@grafana/scenes": ^1.3.1
"@grafana/schema": "workspace:*"
"@grafana/tsconfig": ^1.3.0-rc1
"@grafana/ui": "workspace:*"