mirror of
https://github.com/grafana/grafana.git
synced 2025-02-16 18:34:52 -06:00
[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:
parent
7209e84cd2
commit
0d3c47da99
@ -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",
|
||||
|
@ -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]),
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
@ -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',
|
||||
}),
|
||||
});
|
||||
|
@ -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(),
|
||||
});
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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(),
|
||||
});
|
||||
}
|
||||
|
@ -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(),
|
||||
});
|
||||
}
|
||||
|
@ -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(),
|
||||
});
|
||||
}
|
||||
|
10
yarn.lock
10
yarn.lock
@ -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:*"
|
||||
|
Loading…
Reference in New Issue
Block a user