diff --git a/grafana-plugin/e2e-tests/insights/insights.test.ts b/grafana-plugin/e2e-tests/insights/insights.test.ts index 355037b4..6700b684 100644 --- a/grafana-plugin/e2e-tests/insights/insights.test.ts +++ b/grafana-plugin/e2e-tests/insights/insights.test.ts @@ -43,14 +43,13 @@ test.describe('Insights', () => { test('Viewer can see all the panels in OnCall insights', async ({ viewerRolePage: { page } }) => { await goToOnCallPage(page, 'insights'); [ - 'Total alert groups', - 'Total alert groups by state', - 'New alert groups for selected period', - 'Mean time to respond \\(MTTR\\)', - 'MTTR changed for period', - 'New alert groups during time period', + 'New alert groups', + 'Mean time to respond \\(MTTR\\) average', 'Alert groups by Integration', 'Mean time to respond \\(MTTR\\) by Integration', + 'Alert groups by Team', + 'Mean time to respond \\(MTTR\\) by Team', + 'New alert groups notifications', ].forEach(async (panelTitle) => { await expect(page.getByRole('heading', { name: new RegExp(`^${panelTitle}$`) }).first()).toBeVisible(); }); @@ -58,7 +57,7 @@ test.describe('Insights', () => { test('There is no panel that misses data', async ({ adminRolePage: { page } }) => { await goToOnCallPage(page, 'insights'); - await page.getByText('Last 7 days').click(); + await page.getByText('Last 24 hours').click(); await page.getByText('Last 1 hour').click(); await page.waitForTimeout(2000); await expect(page.getByText('No data')).toBeHidden(); diff --git a/grafana-plugin/src/models/alertgroup/alertgroup.ts b/grafana-plugin/src/models/alertgroup/alertgroup.ts index 41e05b92..29cca3f7 100644 --- a/grafana-plugin/src/models/alertgroup/alertgroup.ts +++ b/grafana-plugin/src/models/alertgroup/alertgroup.ts @@ -282,7 +282,7 @@ export class AlertGroupStore extends BaseStore { this.updateAlertGroups(); } - @action + @action.bound async updateAlertGroups() { this.alertGroupsLoading = true; diff --git a/grafana-plugin/src/models/loader/loader.ts b/grafana-plugin/src/models/loader/loader.ts index 0338f435..49fff8b1 100644 --- a/grafana-plugin/src/models/loader/loader.ts +++ b/grafana-plugin/src/models/loader/loader.ts @@ -12,14 +12,14 @@ class LoaderStoreClass { makeObservable(this); } - @action + @action.bound setLoadingAction(actionKey: string, isLoading: boolean) { this.items[actionKey] = isLoading; } - isLoading(actionKey: string): boolean { + isLoading = (actionKey: string): boolean => { return !!this.items[actionKey]; - } + }; } export const LoaderStore = new LoaderStoreClass(); diff --git a/grafana-plugin/src/pages/insights/Insights.hooks.ts b/grafana-plugin/src/pages/insights/Insights.hooks.ts new file mode 100644 index 00000000..1d9b5ee0 --- /dev/null +++ b/grafana-plugin/src/pages/insights/Insights.hooks.ts @@ -0,0 +1,35 @@ +import { useEffect, useState } from 'react'; + +import { useStore } from 'state/useStore'; + +const TWENTY_SECS = 20_000; +const FIVE_SECS = 5_000; + +export const useAlertCreationChecker = () => { + const { + alertGroupStore: { updateAlertGroups, alerts }, + } = useStore(); + const [isFirstAlertCheckDone, setIsFirstAlertCheckDone] = useState(false); + + const isAnyAlertCreatedMoreThan20SecsAgo = Array.from(alerts).some(([_key, alert]) => { + const alertTime = new Date(alert.started_at).getTime(); + const nowTime = new Date().getTime(); + return nowTime - alertTime > TWENTY_SECS; + }); + + useEffect(() => { + const fetch = async () => { + if (!isAnyAlertCreatedMoreThan20SecsAgo) { + await updateAlertGroups(); + } + setIsFirstAlertCheckDone(true); + }; + fetch(); + const interval = setInterval(() => { + fetch(); + }, FIVE_SECS); + return () => clearInterval(interval); + }, [isAnyAlertCreatedMoreThan20SecsAgo]); + + return { isAnyAlertCreatedMoreThan20SecsAgo, isFirstAlertCheckDone }; +}; diff --git a/grafana-plugin/src/pages/insights/Insights.module.scss b/grafana-plugin/src/pages/insights/Insights.module.scss index e5503077..eec1969a 100644 --- a/grafana-plugin/src/pages/insights/Insights.module.scss +++ b/grafana-plugin/src/pages/insights/Insights.module.scss @@ -6,6 +6,6 @@ margin: 0; } -.alertBox { +.spaceTop { margin-top: 16px; } diff --git a/grafana-plugin/src/pages/insights/Insights.tsx b/grafana-plugin/src/pages/insights/Insights.tsx index ce3b143f..c2de19c4 100644 --- a/grafana-plugin/src/pages/insights/Insights.tsx +++ b/grafana-plugin/src/pages/insights/Insights.tsx @@ -14,29 +14,28 @@ import { SceneAppPage, useSceneApp, } from '@grafana/scenes'; -import { Alert } from '@grafana/ui'; +import { Alert, LoadingPlaceholder, VerticalGroup } from '@grafana/ui'; import { observer } from 'mobx-react'; import { Text } from 'components/Text/Text'; +import { Tutorial } from 'components/Tutorial/Tutorial'; +import { TutorialStep } from 'components/Tutorial/Tutorial.types'; import { useStore } from 'state/useStore'; -import { DOCS_ROOT } from 'utils/consts'; +import { DOCS_ROOT, PLUGIN_ROOT } from 'utils/consts'; +import { useAlertCreationChecker } from './Insights.hooks'; import styles from './Insights.module.scss'; import { InsightsConfig } from './Insights.types'; -import getAlertGroupsByIntegrationScene from './scenes/AlertGroupsByIntegration'; -import getAlertGroupsByTeamScene from './scenes/AlertGroupsByTeam'; -import getMTTRScene from './scenes/MTTR'; -import getMTTRByIntegrationScene from './scenes/MTTRByIntegration'; -import getMTTRByTeamScene from './scenes/MTTRByTeam'; -import getMTTRChangedForPeriodStatScene from './scenes/MTTRChangedForPeriodStat'; -import getMTTRChangedForPeriodTimeseriesScene from './scenes/MTTRChangedForPeriodTimeseries'; -import getNewAlertGroupsDuringTimePeriodScene from './scenes/NewAlertGroupsDuringTimePeriod'; -import getNewAlertGroupsForSelectedPeriodScene from './scenes/NewAlertGroupsForSelectedPeriod'; -import getNewAlertGroupsNotificationsDuringTimePeriodScene from './scenes/NewAlertGroupsNotificationsDuringTimePeriod'; -import getNewAlertGroupsNotificationsForPeriodTableScene from './scenes/NewAlertGroupsNotificationsForPeriodTable'; -import getNewAlertGroupsNotificationsInTotalScene from './scenes/NewAlertGroupsNotificationsInTotal'; -import getTotalAlertGroupsScene from './scenes/TotalAlertGroups'; -import getTotalAlertGroupsByStateScene from './scenes/TotalAlertGroupsByState'; +import { getAlertGroupsByIntegrationScene } from './scenes/AlertGroupsByIntegration'; +import { getAlertGroupsByTeamScene } from './scenes/AlertGroupsByTeam'; +import { getMTTRAverage } from './scenes/MTTRAverageStat'; +import { getMTTRByIntegrationScene } from './scenes/MTTRByIntegration'; +import { getMTTRByTeamScene } from './scenes/MTTRByTeam'; +import { getMTTRChangedTimeseriesScene } from './scenes/MTTRChangedTimeseries'; +import { getNewAlertGroupsScene } from './scenes/NewAlertGroups'; +import { getNewAlertGroupsNotificationsTableScene } from './scenes/NewAlertGroupsNotificationsTable'; +import { getNewAlertGroupsNotificationsTimeseriesScene } from './scenes/NewAlertGroupsNotificationsTimeseries'; +import { getNewAlertGroupsTimeseriesScene } from './scenes/NewAlertGroupsTimeseries'; import getVariables from './variables'; export const Insights = observer(() => { @@ -45,8 +44,8 @@ export const Insights = observer(() => { insightsDatasource, organizationStore: { currentOrganization }, } = useStore(); - const [showAllStackInfo, setShowAllStackInfo] = useState(false); const [datasource, setDatasource] = useState(); + const { isAnyAlertCreatedMoreThan20SecsAgo, isFirstAlertCheckDone } = useAlertCreationChecker(); const config = useMemo( () => ({ @@ -54,7 +53,7 @@ export const Insights = observer(() => { datasource: { uid: isOpenSource ? '$datasource' : insightsDatasource }, stack: currentOrganization?.stack_slug, }), - [] + [isOpenSource, currentOrganization?.stack_slug] ); const variables = useMemo(() => getVariables(config), [config]); @@ -64,26 +63,33 @@ export const Insights = observer(() => { const appScene = useSceneApp(getAppScene); useEffect(() => { - const stackListener = variables.stack.subscribeToState(({ text }) => { - setShowAllStackInfo((text as string[]).includes('All')); - }); + if (!isAnyAlertCreatedMoreThan20SecsAgo) { + return undefined; + } const dataSourceListener = isOpenSource && variables.datasource.subscribeToState(({ text }) => { setDatasource(`${text}`); }); return () => { - stackListener?.unsubscribe?.(); dataSourceListener?.unsubscribe?.(); }; - }, []); + }, [isAnyAlertCreatedMoreThan20SecsAgo]); + if (!isFirstAlertCheckDone) { + return ; + } return (
- {showAllStackInfo && } - {isOpenSource && !datasource && } - + {isAnyAlertCreatedMoreThan20SecsAgo ? ( + <> + {isOpenSource && !datasource && } + + + ) : ( + + )}
); }); @@ -97,15 +103,23 @@ const InsightsGeneralInfo = () => { return Find out more about OnCall Insights and Metrics in our {docsLink}.; }; -const AllStacksSelectedWarning = () => { - const [alertVisible, setAlertVisible] = useState(true); - - return alertVisible ? ( - setAlertVisible(false)} severity="warning" title="" className={styles.alertBox}> - Retrieving insights from multiple stacks has performance impact and loading data might take significantly more - time. We recommend to select only specific stacks. - - ) : null; +const NoAlertCreatedTutorial = () => { + return ( +
+ + + Your OnCall stack doesn’t have any alerts to visualise insights. +
+ Make sure that you setup OnCall configuration to start monitoring. +
+ + } + /> +
+ ); }; const NoDatasourceWarning = () => { @@ -128,10 +142,10 @@ const getRootScene = (config: InsightsConfig, variables: ReturnType new EmbeddedScene({ - $timeRange: new SceneTimeRange({ from: 'now-7d', to: 'now' }), + $timeRange: new SceneTimeRange({ from: 'now-24h', to: 'now' }), $variables: new SceneVariableSet({ variables: Object.values(variables), }), @@ -152,22 +166,12 @@ const getRootScene = (config: InsightsConfig, variables: ReturnType=0)`, format: 'table', instant: true, legendFormat: '__auto', @@ -74,6 +74,7 @@ export default function getAlertGroupsByIntegrationScene({ datasource }: Insight }, ], }, + decimals: 0, }, overrides: [ { @@ -97,7 +98,7 @@ export default function getAlertGroupsByIntegrationScene({ datasource }: Insight ], }, options: { - cellHeight: 'sm', + cellHeight: 'md', footer: { countRows: false, fields: '', diff --git a/grafana-plugin/src/pages/insights/scenes/AlertGroupsByTeam.tsx b/grafana-plugin/src/pages/insights/scenes/AlertGroupsByTeam.tsx index 07c12692..e93fd9c3 100644 --- a/grafana-plugin/src/pages/insights/scenes/AlertGroupsByTeam.tsx +++ b/grafana-plugin/src/pages/insights/scenes/AlertGroupsByTeam.tsx @@ -3,14 +3,14 @@ import { SceneDataTransformer, SceneFlexItem, SceneQueryRunner, VizPanel } from import { InsightsConfig } from 'pages/insights/Insights.types'; -export default function getAlertGroupsByTeamScene({ datasource }: InsightsConfig) { +export function getAlertGroupsByTeamScene({ datasource, stack }: InsightsConfig) { const query = new SceneQueryRunner({ datasource, queries: [ { editorMode: 'code', exemplar: false, - expr: 'sort_desc(max_over_time(sum by(team) (avg without(pod, instance)($alert_groups_total{slug=~"$stack", team=~"$team", integration=~"$integration"}))[1d:]))', + expr: `sort_desc(delta(max_over_time(sum by(team) (avg without(pod, instance)($alert_groups_total{slug=~"${stack}", team=~"$team", integration=~"$integration"}))[1h:])[$__range:])>=0)`, format: 'table', instant: true, legendFormat: '__auto', @@ -74,6 +74,7 @@ export default function getAlertGroupsByTeamScene({ datasource }: InsightsConfig }, ], }, + decimals: 0, }, overrides: [ { @@ -97,7 +98,7 @@ export default function getAlertGroupsByTeamScene({ datasource }: InsightsConfig ], }, options: { - cellHeight: 'sm', + cellHeight: 'md', footer: { countRows: false, fields: '', diff --git a/grafana-plugin/src/pages/insights/scenes/MTTR.tsx b/grafana-plugin/src/pages/insights/scenes/MTTR.tsx deleted file mode 100644 index 0dbec58d..00000000 --- a/grafana-plugin/src/pages/insights/scenes/MTTR.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { ThresholdsMode } from '@grafana/data'; -import { SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; - -import { InsightsConfig } from 'pages/insights/Insights.types'; - -export default function getMTTRScene({ datasource }: InsightsConfig) { - const query = new SceneQueryRunner({ - datasource, - queries: [ - { - editorMode: 'code', - exemplar: false, - expr: 'avg_over_time((sum($alert_groups_response_time_seconds_sum{slug=~"$stack", team=~"$team", integration=~"$integration"}) / sum($alert_groups_response_time_seconds_count{slug=~"$stack", team=~"$team", integration=~"$integration"}))[$__range:])', - instant: true, - legendFormat: '__auto', - range: false, - refId: 'A', - }, - ], - }); - - return new SceneFlexItem({ - $data: query, - body: new VizPanel({ - title: 'Mean time to respond (MTTR)', - description: 'Mean time between the start and first action of all alert groups for the last 7 days', - pluginId: 'stat', - fieldConfig: { - defaults: { - color: { - mode: 'thresholds', - }, - mappings: [], - thresholds: { - mode: ThresholdsMode.Absolute, - steps: [ - { - color: 'text', - value: null, - }, - ], - }, - unit: 's', - }, - overrides: [ - { - matcher: { - id: 'byName', - options: 'Value', - }, - properties: [ - { - id: 'displayName', - value: 'MTTR', - }, - ], - }, - ], - }, - options: { - colorMode: 'value', - graphMode: 'none', - justifyMode: 'center', - orientation: 'auto', - reduceOptions: { - calcs: ['lastNotNull'], - fields: '', - values: false, - }, - textMode: 'auto', - }, - }), - }); -} diff --git a/grafana-plugin/src/pages/insights/scenes/MTTRChangedForPeriodStat.tsx b/grafana-plugin/src/pages/insights/scenes/MTTRAverageStat.tsx similarity index 74% rename from grafana-plugin/src/pages/insights/scenes/MTTRChangedForPeriodStat.tsx rename to grafana-plugin/src/pages/insights/scenes/MTTRAverageStat.tsx index 77569394..47f4c274 100644 --- a/grafana-plugin/src/pages/insights/scenes/MTTRChangedForPeriodStat.tsx +++ b/grafana-plugin/src/pages/insights/scenes/MTTRAverageStat.tsx @@ -3,17 +3,17 @@ import { SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; import { InsightsConfig } from 'pages/insights/Insights.types'; -export default function getMTTRChangedForPeriodStatScene({ datasource }: InsightsConfig) { +export function getMTTRAverage({ datasource, stack }: InsightsConfig) { const query = new SceneQueryRunner({ datasource, queries: [ { editorMode: 'code', exemplar: false, - expr: 'avg(sum($alert_groups_response_time_seconds_sum{slug=~"$stack", team=~"$team", integration=~"$integration"}) / sum($alert_groups_response_time_seconds_count{slug=~"$stack", team=~"$team", integration=~"$integration"}))', - instant: false, + expr: `avg_over_time((sum($alert_groups_response_time_seconds_sum{slug=~"${stack}", team=~"$team", integration=~"$integration"}) / sum($alert_groups_response_time_seconds_count{slug=~"${stack}", team=~"$team", integration=~"$integration"}))[$__range:])`, + instant: true, legendFormat: '__auto', - range: true, + range: false, refId: 'A', }, ], @@ -22,7 +22,7 @@ export default function getMTTRChangedForPeriodStatScene({ datasource }: Insight return new SceneFlexItem({ $data: query, body: new VizPanel({ - title: 'MTTR changed for period', + title: 'Mean time to respond (MTTR) average', pluginId: 'stat', fieldConfig: { defaults: { @@ -57,7 +57,7 @@ export default function getMTTRChangedForPeriodStatScene({ datasource }: Insight justifyMode: 'center', orientation: 'auto', reduceOptions: { - calcs: ['diff'], + calcs: ['lastNotNull'], fields: '', values: false, }, diff --git a/grafana-plugin/src/pages/insights/scenes/MTTRByIntegration.tsx b/grafana-plugin/src/pages/insights/scenes/MTTRByIntegration.tsx index 0f93aa7c..a5cfbb9f 100644 --- a/grafana-plugin/src/pages/insights/scenes/MTTRByIntegration.tsx +++ b/grafana-plugin/src/pages/insights/scenes/MTTRByIntegration.tsx @@ -3,14 +3,14 @@ import { SceneDataTransformer, SceneFlexItem, SceneQueryRunner, VizPanel } from import { InsightsConfig } from 'pages/insights/Insights.types'; -export default function getMTTRByIntegrationScene({ datasource }: InsightsConfig) { +export function getMTTRByIntegrationScene({ datasource, stack }: InsightsConfig) { const query = new SceneQueryRunner({ datasource, queries: [ { editorMode: 'code', exemplar: false, - expr: 'sort_desc(avg_over_time((sum by (integration)($alert_groups_response_time_seconds_sum{slug=~"$stack", team=~"$team", integration=~"$integration"}) / sum by (integration)($alert_groups_response_time_seconds_count{slug=~"$stack", team=~"$team", integration=~"$integration"}))[$__range:]))', + expr: `sort_desc(avg_over_time((sum by (integration)($alert_groups_response_time_seconds_sum{slug=~"${stack}", team=~"$team", integration=~"$integration"}) / sum by (integration)($alert_groups_response_time_seconds_count{slug=~"${stack}", team=~"$team", integration=~"$integration"}))[$__range:]))`, format: 'table', instant: true, legendFormat: '__auto', @@ -105,14 +105,14 @@ export default function getMTTRByIntegrationScene({ datasource }: InsightsConfig }, { id: 'custom.width', - value: 300, + value: 200, }, ], }, ], }, options: { - cellHeight: 'sm', + cellHeight: 'md', footer: { countRows: false, fields: '', diff --git a/grafana-plugin/src/pages/insights/scenes/MTTRByTeam.tsx b/grafana-plugin/src/pages/insights/scenes/MTTRByTeam.tsx index 2196e9e2..bc870360 100644 --- a/grafana-plugin/src/pages/insights/scenes/MTTRByTeam.tsx +++ b/grafana-plugin/src/pages/insights/scenes/MTTRByTeam.tsx @@ -3,14 +3,14 @@ import { SceneDataTransformer, SceneFlexItem, SceneQueryRunner, VizPanel } from import { InsightsConfig } from 'pages/insights/Insights.types'; -export default function getMTTRByTeamScene({ datasource }: InsightsConfig) { +export function getMTTRByTeamScene({ datasource, stack }: InsightsConfig) { const query = new SceneQueryRunner({ datasource, queries: [ { editorMode: 'code', exemplar: false, - expr: 'sort_desc(avg_over_time((sum by(team) ($alert_groups_response_time_seconds_sum{slug=~"$stack", team=~"$team", integration=~"$integration"}) / sum by(team)($alert_groups_response_time_seconds_count{slug=~"$stack", team=~"$team", integration=~"$integration"}))[$__range:]))', + expr: `sort_desc(avg_over_time((sum by(team) ($alert_groups_response_time_seconds_sum{slug=~"${stack}", team=~"$team", integration=~"$integration"}) / sum by(team)($alert_groups_response_time_seconds_count{slug=~"${stack}", team=~"$team", integration=~"$integration"}))[$__range:]))`, format: 'table', instant: true, legendFormat: '__auto', @@ -47,7 +47,7 @@ export default function getMTTRByTeamScene({ datasource }: InsightsConfig) { return new SceneFlexItem({ $data: transformedData, body: new VizPanel({ - title: 'Mean time to respond by Team (MTTR)', + title: 'Mean time to respond (MTTR) by Team', pluginId: 'table', fieldConfig: { defaults: { @@ -102,7 +102,7 @@ export default function getMTTRByTeamScene({ datasource }: InsightsConfig) { ], }, options: { - cellHeight: 'sm', + cellHeight: 'md', footer: { countRows: false, fields: '', diff --git a/grafana-plugin/src/pages/insights/scenes/MTTRChangedForPeriodTimeseries.tsx b/grafana-plugin/src/pages/insights/scenes/MTTRChangedTimeseries.tsx similarity index 86% rename from grafana-plugin/src/pages/insights/scenes/MTTRChangedForPeriodTimeseries.tsx rename to grafana-plugin/src/pages/insights/scenes/MTTRChangedTimeseries.tsx index a581efa5..9b3dd434 100644 --- a/grafana-plugin/src/pages/insights/scenes/MTTRChangedForPeriodTimeseries.tsx +++ b/grafana-plugin/src/pages/insights/scenes/MTTRChangedTimeseries.tsx @@ -3,14 +3,14 @@ import { SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; import { InsightsConfig } from 'pages/insights/Insights.types'; -export default function getMTTRChangedForPeriodTimeseriesScene({ datasource }: InsightsConfig) { +export function getMTTRChangedTimeseriesScene({ datasource, stack }: InsightsConfig) { const query = new SceneQueryRunner({ datasource, queries: [ { editorMode: 'code', exemplar: false, - expr: 'avg(sum($alert_groups_response_time_seconds_sum{slug=~"$stack", team=~"$team", integration=~"$integration"}) / sum($alert_groups_response_time_seconds_count{slug=~"$stack", team=~"$team", integration=~"$integration"}))', + expr: `avg(sum($alert_groups_response_time_seconds_sum{slug=~"${stack}", team=~"$team", integration=~"$integration"}) / sum($alert_groups_response_time_seconds_count{slug=~"${stack}", team=~"$team", integration=~"$integration"}))`, instant: false, legendFormat: '__auto', range: true, @@ -22,7 +22,7 @@ export default function getMTTRChangedForPeriodTimeseriesScene({ datasource }: I return new SceneFlexItem({ $data: query, body: new VizPanel({ - title: 'MTTR changed for period', + title: 'Mean time to respond (MTTR) changed', pluginId: 'timeseries', fieldConfig: { defaults: { diff --git a/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsForSelectedPeriod.tsx b/grafana-plugin/src/pages/insights/scenes/NewAlertGroups.tsx similarity index 84% rename from grafana-plugin/src/pages/insights/scenes/NewAlertGroupsForSelectedPeriod.tsx rename to grafana-plugin/src/pages/insights/scenes/NewAlertGroups.tsx index 11edb35b..1b7a278a 100644 --- a/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsForSelectedPeriod.tsx +++ b/grafana-plugin/src/pages/insights/scenes/NewAlertGroups.tsx @@ -3,7 +3,7 @@ import { SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; import { InsightsConfig } from 'pages/insights/Insights.types'; -export default function getNewAlertGroupsForSelectedPeriodScene({ datasource }: InsightsConfig) { +export function getNewAlertGroupsScene({ datasource, stack }: InsightsConfig) { const query = new SceneQueryRunner({ datasource, queries: [ @@ -12,7 +12,7 @@ export default function getNewAlertGroupsForSelectedPeriodScene({ datasource }: editorMode: 'code', excludeNullMetadata: false, exemplar: false, - expr: 'increase(max_over_time(sum(avg without(pod, instance) ($alert_groups_total{slug=~"$stack", team=~"$team", integration=~"$integration"}))[1d:])[$__range:])', + expr: `delta(max_over_time(sum(avg without(pod, instance) ($alert_groups_total{slug=~"${stack}", team=~"$team", integration=~"$integration"}))[30m:])[$__range:]) >= 0`, format: 'time_series', fullMetaSearch: false, includeNullMetadata: true, @@ -28,7 +28,7 @@ export default function getNewAlertGroupsForSelectedPeriodScene({ datasource }: return new SceneFlexItem({ $data: query, body: new VizPanel({ - title: 'New alert groups for selected period', + title: 'New alert groups', pluginId: 'stat', fieldConfig: { defaults: { diff --git a/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsInTotal.tsx b/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsInTotal.tsx deleted file mode 100644 index d757b233..00000000 --- a/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsInTotal.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { ThresholdsMode } from '@grafana/data'; -import { SceneDataTransformer, SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; - -import { InsightsConfig } from 'pages/insights/Insights.types'; - -export default function getNewAlertGroupsNotificationsInTotalScene({ datasource }: InsightsConfig) { - const query = new SceneQueryRunner({ - datasource, - queries: [ - { - editorMode: 'code', - exemplar: false, - expr: 'sort_desc(max_over_time(sum by(username) (avg without(pod, instance)($user_was_notified_of_alert_groups_total{slug=~"$stack"}))[1d:]))', - format: 'table', - instant: true, - legendFormat: '__auto', - range: false, - refId: 'A', - }, - ], - }); - - const transformedData = new SceneDataTransformer({ - $data: query, - transformations: [ - { - id: 'seriesToRows', - options: {}, - }, - { - id: 'organize', - options: { - excludeByName: { - Time: true, - username: false, - }, - indexByName: {}, - renameByName: { - Metric: 'Integration', - Value: 'Alert groups', - team: 'Team', - username: 'Username', - }, - }, - }, - ], - }); - - return new SceneFlexItem({ - $data: transformedData, - body: new VizPanel({ - title: 'New alert groups notifications in total', - pluginId: 'table', - fieldConfig: { - defaults: { - color: { - mode: 'thresholds', - }, - custom: { - align: 'auto', - cellOptions: { - mode: 'gradient', - type: 'gauge', - valueDisplayMode: 'color', - }, - filterable: false, - inspect: false, - }, - mappings: [], - thresholds: { - mode: ThresholdsMode.Absolute, - steps: [ - { - color: 'green', - value: null, - }, - ], - }, - }, - overrides: [ - { - matcher: { - id: 'byName', - options: 'Username', - }, - properties: [ - { - id: 'custom.cellOptions', - value: { - type: 'auto', - }, - }, - { - id: 'custom.width', - value: 300, - }, - ], - }, - ], - }, - options: { - cellHeight: 'sm', - footer: { - countRows: false, - fields: '', - reducer: ['sum'], - show: false, - }, - showHeader: true, - }, - }), - }); -} diff --git a/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsForPeriodTable.tsx b/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsTable.tsx similarity index 86% rename from grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsForPeriodTable.tsx rename to grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsTable.tsx index 76f6f3c5..0a616347 100644 --- a/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsForPeriodTable.tsx +++ b/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsTable.tsx @@ -3,14 +3,14 @@ import { SceneDataTransformer, SceneFlexItem, SceneQueryRunner, VizPanel } from import { InsightsConfig } from 'pages/insights/Insights.types'; -export default function getNewAlertGroupsNotificationsForPeriodTableScene({ datasource }: InsightsConfig) { +export function getNewAlertGroupsNotificationsTableScene({ datasource, stack }: InsightsConfig) { const query = new SceneQueryRunner({ datasource, queries: [ { editorMode: 'code', exemplar: false, - expr: 'sort_desc(increase(max_over_time(sum by (username) (avg without(pod, instance) ($user_was_notified_of_alert_groups_total{slug=~"$stack"}))[1h:])[$__range:]))', + expr: `sort_desc(delta(max_over_time(sum by (username) (avg without(pod, instance) ($user_was_notified_of_alert_groups_total{slug=~"${stack}"}))[1h:])[$__range:])>=0)`, format: 'table', instant: true, legendFormat: '__auto', @@ -49,7 +49,7 @@ export default function getNewAlertGroupsNotificationsForPeriodTableScene({ data return new SceneFlexItem({ $data: transformedData, body: new VizPanel({ - title: 'New alert groups notifications for period', + title: 'New alert groups notifications', pluginId: 'table', fieldConfig: { defaults: { @@ -99,7 +99,7 @@ export default function getNewAlertGroupsNotificationsForPeriodTableScene({ data ], }, options: { - cellHeight: 'sm', + cellHeight: 'md', footer: { countRows: false, fields: '', diff --git a/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsDuringTimePeriod.tsx b/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsTimeseries.tsx similarity index 89% rename from grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsDuringTimePeriod.tsx rename to grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsTimeseries.tsx index e2f65107..457ff270 100644 --- a/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsDuringTimePeriod.tsx +++ b/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsNotificationsTimeseries.tsx @@ -3,7 +3,7 @@ import { SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; import { InsightsConfig } from 'pages/insights/Insights.types'; -export default function getNewAlertGroupsNotificationsDuringTimePeriodScene({ datasource }: InsightsConfig) { +export function getNewAlertGroupsNotificationsTimeseriesScene({ datasource, stack }: InsightsConfig) { const query = new SceneQueryRunner({ datasource, queries: [ @@ -12,7 +12,7 @@ export default function getNewAlertGroupsNotificationsDuringTimePeriodScene({ da editorMode: 'code', excludeNullMetadata: false, exemplar: false, - expr: 'increase(max_over_time(sum by (username) (avg without(pod, instance) ($user_was_notified_of_alert_groups_total{slug=~"$stack"}))[30m:])[1h:])', + expr: `delta(max_over_time(sum by (username) (avg without(pod, instance) ($user_was_notified_of_alert_groups_total{slug=~"${stack}"}))[30m:])[1h:]) >= 0`, fullMetaSearch: false, instant: false, legendFormat: '__auto', @@ -26,7 +26,7 @@ export default function getNewAlertGroupsNotificationsDuringTimePeriodScene({ da return new SceneFlexItem({ $data: query, body: new VizPanel({ - title: 'New alert groups notifications during time period', + title: 'New alert groups notifications', pluginId: 'timeseries', fieldConfig: { defaults: { diff --git a/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsDuringTimePeriod.tsx b/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsTimeseries.tsx similarity index 89% rename from grafana-plugin/src/pages/insights/scenes/NewAlertGroupsDuringTimePeriod.tsx rename to grafana-plugin/src/pages/insights/scenes/NewAlertGroupsTimeseries.tsx index c4b22fb5..1d0e238c 100644 --- a/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsDuringTimePeriod.tsx +++ b/grafana-plugin/src/pages/insights/scenes/NewAlertGroupsTimeseries.tsx @@ -3,7 +3,7 @@ import { SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; import { InsightsConfig } from 'pages/insights/Insights.types'; -export default function getNewAlertGroupsDuringTimePeriodScene({ datasource }: InsightsConfig) { +export function getNewAlertGroupsTimeseriesScene({ datasource, stack }: InsightsConfig) { const query = new SceneQueryRunner({ datasource, queries: [ @@ -12,7 +12,7 @@ export default function getNewAlertGroupsDuringTimePeriodScene({ datasource }: I editorMode: 'code', excludeNullMetadata: false, exemplar: false, - expr: 'increase(max_over_time(sum by (integration) (avg without(pod, instance) ($alert_groups_total{slug=~"$stack", team=~"$team", integration=~"$integration"}))[30m:])[1h:])', + expr: `delta(max_over_time(sum by (integration) (avg without(pod, instance) ($alert_groups_total{slug=~"${stack}", team=~"$team", integration=~"$integration"}))[30m:])[1h:]) >= 0`, fullMetaSearch: false, instant: false, legendFormat: '__auto', @@ -26,7 +26,7 @@ export default function getNewAlertGroupsDuringTimePeriodScene({ datasource }: I return new SceneFlexItem({ $data: query, body: new VizPanel({ - title: 'New alert groups during time period', + title: 'New alert groups', pluginId: 'timeseries', fieldConfig: { defaults: { diff --git a/grafana-plugin/src/pages/insights/scenes/TotalAlertGroups.tsx b/grafana-plugin/src/pages/insights/scenes/TotalAlertGroups.tsx deleted file mode 100644 index 09236080..00000000 --- a/grafana-plugin/src/pages/insights/scenes/TotalAlertGroups.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { ThresholdsMode } from '@grafana/data'; -import { SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; - -import { InsightsConfig } from 'pages/insights/Insights.types'; - -export default function getTotalAlertGroupsScene({ datasource }: InsightsConfig) { - const query = new SceneQueryRunner({ - datasource, - queries: [ - { - disableTextWrap: false, - editorMode: 'code', - excludeNullMetadata: false, - exemplar: false, - expr: 'max_over_time(sum(avg without(pod, instance) ($alert_groups_total{slug=~"$stack", team=~"$team", integration=~"$integration"}))[1d:])', - format: 'time_series', - fullMetaSearch: false, - instant: false, - legendFormat: '__auto', - range: true, - refId: 'A', - useBackend: false, - }, - ], - }); - - return new SceneFlexItem({ - $data: query, - body: new VizPanel({ - pluginId: 'stat', - fieldConfig: { - defaults: { - color: { - mode: 'thresholds', - }, - mappings: [], - thresholds: { - mode: ThresholdsMode.Absolute, - steps: [ - { - color: 'text', - value: null, - }, - ], - }, - unit: 'none', - }, - overrides: [ - { - matcher: { - id: 'byName', - options: 'Value', - }, - properties: [ - { - id: 'displayName', - value: 'Total alert groups', - }, - ], - }, - ], - }, - options: { - colorMode: 'value', - graphMode: 'none', - justifyMode: 'center', - orientation: 'auto', - reduceOptions: { - calcs: ['lastNotNull'], - fields: '', - values: false, - }, - textMode: 'auto', - }, - pluginVersion: '9.5.2', - title: 'Total alert groups', - }), - }); -} diff --git a/grafana-plugin/src/pages/insights/scenes/TotalAlertGroupsByState.tsx b/grafana-plugin/src/pages/insights/scenes/TotalAlertGroupsByState.tsx deleted file mode 100644 index 8b490f10..00000000 --- a/grafana-plugin/src/pages/insights/scenes/TotalAlertGroupsByState.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import { ThresholdsMode } from '@grafana/data'; -import { SceneDataTransformer, SceneFlexItem, SceneQueryRunner, VizPanel } from '@grafana/scenes'; - -import { InsightsConfig } from 'pages/insights/Insights.types'; - -export default function getTotalAlertGroupsByStateScene({ datasource }: InsightsConfig) { - const query = new SceneQueryRunner({ - datasource, - queries: [ - { - disableTextWrap: false, - editorMode: 'code', - excludeNullMetadata: false, - expr: 'sum by (state) (avg without(pod, instance) ($alert_groups_total{slug=~"$stack", team=~"$team", integration=~"$integration"}))', - fullMetaSearch: false, - legendFormat: '__auto', - range: true, - refId: 'A', - useBackend: false, - }, - ], - }); - - const transformedData = new SceneDataTransformer({ - $data: query, - transformations: [ - { - id: 'joinByLabels', - options: { - value: 'state', - }, - }, - { - id: 'organize', - options: { - excludeByName: {}, - indexByName: { - acknowledged: 1, - firing: 0, - resolved: 2, - silenced: 3, - }, - renameByName: {}, - }, - }, - ], - }); - - return new SceneFlexItem({ - $data: transformedData, - body: new VizPanel({ - title: 'Total alert groups by state', - pluginId: 'bargauge', - fieldConfig: { - defaults: { - color: { - mode: 'thresholds', - }, - mappings: [], - thresholds: { - mode: ThresholdsMode.Absolute, - steps: [ - { - color: 'green', - value: null, - }, - ], - }, - }, - overrides: [ - { - matcher: { - id: 'byName', - options: 'firing', - }, - properties: [ - { - id: 'color', - value: { - fixedColor: 'red', - mode: 'fixed', - }, - }, - ], - }, - { - matcher: { - id: 'byName', - options: 'acknowledged', - }, - properties: [ - { - id: 'color', - value: { - fixedColor: 'dark-yellow', - mode: 'fixed', - }, - }, - ], - }, - { - matcher: { - id: 'byName', - options: 'silenced', - }, - properties: [ - { - id: 'color', - value: { - mode: 'fixed', - }, - }, - ], - }, - ], - }, - options: { - displayMode: 'gradient', - minVizHeight: 10, - minVizWidth: 0, - orientation: 'vertical', - reduceOptions: { - calcs: ['lastNotNull'], - fields: '', - values: false, - }, - showUnfilled: true, - valueMode: 'color', - }, - pluginVersion: '9.5.2', - }), - }); -} diff --git a/grafana-plugin/src/pages/insights/variables.ts b/grafana-plugin/src/pages/insights/variables.ts index f11338ba..a1ba585d 100644 --- a/grafana-plugin/src/pages/insights/variables.ts +++ b/grafana-plugin/src/pages/insights/variables.ts @@ -26,18 +26,6 @@ const getVariables = ({ isOpenSource, datasource, stack }: InsightsConfig) => ({ }), } : {}), - stack: new QueryVariable({ - ...DEFAULT_VARIABLE_CONFIG, - name: 'stack', - label: 'Stack', - value: stack, - datasource, - definition: 'label_values(${alert_groups_total},slug)', - query: { - query: 'label_values(${alert_groups_total},slug)', - refId: 'PrometheusVariableQueryEditor-VariableQuery', - }, - }), team: new QueryVariable({ ...DEFAULT_VARIABLE_CONFIG, name: 'team', @@ -45,9 +33,9 @@ const getVariables = ({ isOpenSource, datasource, stack }: InsightsConfig) => ({ text: ['All'], value: ['$__all'], datasource, - definition: 'label_values(${alert_groups_total}{slug=~"$stack"},team)', + definition: `label_values(\${alert_groups_total}{slug=~"${stack}"},team)`, query: { - query: 'label_values(${alert_groups_total}{slug=~"$stack"},team)', + query: `label_values(\${alert_groups_total}{slug=~"${stack}"},team)`, refId: 'PrometheusVariableQueryEditor-VariableQuery', }, refresh: 2, @@ -59,9 +47,9 @@ const getVariables = ({ isOpenSource, datasource, stack }: InsightsConfig) => ({ text: ['All'], value: ['$__all'], datasource, - definition: 'label_values(${alert_groups_total}{team=~"$team",slug=~"$stack"},integration)', + definition: `label_values(\${alert_groups_total}{team=~"$team",slug=~"${stack}"},integration)`, query: { - query: 'label_values(${alert_groups_total}{team=~"$team",slug=~"$stack"},integration)', + query: `label_values(\${alert_groups_total}{team=~"$team",slug=~"${stack}"},integration)`, refId: 'PrometheusVariableQueryEditor-VariableQuery', }, refresh: 2, @@ -77,8 +65,8 @@ const getVariables = ({ isOpenSource, datasource, stack }: InsightsConfig) => ({ query: 'metrics(alert_groups_total)', refId: 'PrometheusVariableQueryEditor-VariableQuery', }, - text: ['oncall_alert_groups_total', 'grafanacloud_oncall_stack_alert_groups_total'], - value: ['oncall_alert_groups_total', 'grafanacloud_oncall_stack_alert_groups_total'], + text: ['oncall_alert_groups_total', 'grafanacloud_oncall_instance_alert_groups_total'], + value: ['oncall_alert_groups_total', 'grafanacloud_oncall_instance_alert_groups_total'], definition: 'metrics(alert_groups_total)', hide: 2, includeAll: false, diff --git a/grafana-plugin/src/utils/hooks.tsx b/grafana-plugin/src/utils/hooks.tsx index e16c3182..1b35842a 100644 --- a/grafana-plugin/src/utils/hooks.tsx +++ b/grafana-plugin/src/utils/hooks.tsx @@ -2,6 +2,9 @@ import React, { useEffect, useRef, useState } from 'react'; import { useLocation } from 'react-router-dom'; +import { ActionKey } from 'models/loader/action-keys'; +import { useStore } from 'state/useStore'; + export function useForceUpdate() { const [, setValue] = useState(0); return () => setValue((value) => value + 1); @@ -69,3 +72,10 @@ export function useDebouncedCallback(callback: (...args: A) => }, wait); }; } + +export const useIsLoading = (actionKey: ActionKey) => { + const { + loaderStore: { isLoading }, + } = useStore(); + return isLoading(actionKey); +};