From 6ccd9add1e023f84589d1ca2e18b8c5256bf47a0 Mon Sep 17 00:00:00 2001 From: leonnicolas Date: Fri, 10 Jan 2025 23:32:05 +0100 Subject: [PATCH] Make "hide empty rules persistent" It can be a bit annoying to always press "hide empty rules". This commit uses the session storage of the browser to make it persistent. Signed-off-by: leonnicolas --- .../src/components/SettingsMenu.tsx | 54 +++++++++------ web/ui/mantine-ui/src/pages/AlertsPage.tsx | 69 +++++++++++-------- .../src/state/localStorageMiddleware.ts | 5 +- web/ui/mantine-ui/src/state/settingsSlice.ts | 14 ++-- 4 files changed, 89 insertions(+), 53 deletions(-) diff --git a/web/ui/mantine-ui/src/components/SettingsMenu.tsx b/web/ui/mantine-ui/src/components/SettingsMenu.tsx index aae38909d0..79d9c201cc 100644 --- a/web/ui/mantine-ui/src/components/SettingsMenu.tsx +++ b/web/ui/mantine-ui/src/components/SettingsMenu.tsx @@ -5,7 +5,7 @@ import { Checkbox, Stack, Group, - NumberInput, + NumberInput } from "@mantine/core"; import { IconSettings } from "@tabler/icons-react"; import { FC } from "react"; @@ -21,8 +21,9 @@ const SettingsMenu: FC = () => { enableSyntaxHighlighting, enableLinter, showAnnotations, + hideEmptyGroups, ruleGroupsPerPage, - alertGroupsPerPage, + alertGroupsPerPage } = useSettings(); const dispatch = useAppDispatch(); @@ -48,7 +49,7 @@ const SettingsMenu: FC = () => { onChange={(event) => dispatch( updateSettings({ - useLocalTime: event.currentTarget.checked, + useLocalTime: event.currentTarget.checked }) ) } @@ -63,7 +64,7 @@ const SettingsMenu: FC = () => { onChange={(event) => dispatch( updateSettings({ - enableQueryHistory: event.currentTarget.checked, + enableQueryHistory: event.currentTarget.checked }) ) } @@ -74,7 +75,7 @@ const SettingsMenu: FC = () => { onChange={(event) => dispatch( updateSettings({ - enableAutocomplete: event.currentTarget.checked, + enableAutocomplete: event.currentTarget.checked }) ) } @@ -85,7 +86,7 @@ const SettingsMenu: FC = () => { onChange={(event) => dispatch( updateSettings({ - enableSyntaxHighlighting: event.currentTarget.checked, + enableSyntaxHighlighting: event.currentTarget.checked }) ) } @@ -96,7 +97,7 @@ const SettingsMenu: FC = () => { onChange={(event) => dispatch( updateSettings({ - enableLinter: event.currentTarget.checked, + enableLinter: event.currentTarget.checked }) ) } @@ -107,17 +108,30 @@ const SettingsMenu: FC = () => {
- - dispatch( - updateSettings({ - showAnnotations: event.currentTarget.checked, - }) - ) - } - /> + + + dispatch( + updateSettings({ + showAnnotations: event.currentTarget.checked + }) + ) + } + /> + + dispatch( + updateSettings({ + hideEmptyGroups: event.currentTarget.checked + }) + ) + } + /> +
{ dispatch( updateSettings({ - alertGroupsPerPage: value, + alertGroupsPerPage: value }) ); }} @@ -151,7 +165,7 @@ const SettingsMenu: FC = () => { dispatch( updateSettings({ - ruleGroupsPerPage: value, + ruleGroupsPerPage: value }) ); }} diff --git a/web/ui/mantine-ui/src/pages/AlertsPage.tsx b/web/ui/mantine-ui/src/pages/AlertsPage.tsx index 3143f0b41d..33e170556d 100644 --- a/web/ui/mantine-ui/src/pages/AlertsPage.tsx +++ b/web/ui/mantine-ui/src/pages/AlertsPage.tsx @@ -11,7 +11,7 @@ import { Alert, TextInput, Anchor, - Pagination, + Pagination } from "@mantine/core"; import { useSuspenseAPIQuery } from "../api/api"; import { AlertingRule, AlertingRulesResult } from "../api/responseTypes/rules"; @@ -23,14 +23,14 @@ import { Fragment, useEffect, useMemo } from "react"; import { StateMultiSelect } from "../components/StateMultiSelect"; import { IconInfoCircle, IconSearch } from "@tabler/icons-react"; import { LabelBadges } from "../components/LabelBadges"; -import { useSettings } from "../state/settingsSlice"; +import { useSettings, updateSettings } from "../state/settingsSlice"; +import { useAppDispatch } from "../state/hooks"; import { ArrayParam, - BooleanParam, NumberParam, StringParam, useQueryParam, - withDefault, + withDefault } from "use-query-params"; import { useDebouncedValue } from "@mantine/hooks"; import { KVSearch } from "@nexucis/kvsearch"; @@ -67,7 +67,7 @@ type AlertsPageData = { const kvSearch = new KVSearch({ shouldSort: true, - indexedKeys: ["name", "labels", ["labels", /.*/]], + indexedKeys: ["name", "labels", ["labels", /.*/]] }); const buildAlertsPageData = ( @@ -79,9 +79,9 @@ const buildAlertsPageData = ( globalCounts: { inactive: 0, pending: 0, - firing: 0, + firing: 0 }, - groups: [], + groups: [] }; for (const group of data.groups) { @@ -89,7 +89,7 @@ const buildAlertsPageData = ( total: 0, inactive: 0, pending: 0, - firing: 0, + firing: 0 }; for (const r of group.rules) { @@ -126,9 +126,9 @@ const buildAlertsPageData = ( rule: r, counts: { firing: r.alerts.filter((a) => a.state === "firing").length, - pending: r.alerts.filter((a) => a.state === "pending").length, - }, - })), + pending: r.alerts.filter((a) => a.state === "pending").length + } + })) }); } @@ -146,11 +146,12 @@ export default function AlertsPage() { const { data } = useSuspenseAPIQuery({ path: `/rules`, params: { - type: "alert", - }, + type: "alert" + } }); - const { showAnnotations } = useSettings(); + const { hideEmptyGroups, showAnnotations } = useSettings(); + const dispatch = useAppDispatch(); // Define URL query params. const [stateFilter, setStateFilter] = useQueryParam( @@ -162,10 +163,6 @@ export default function AlertsPage() { withDefault(StringParam, "") ); const [debouncedSearch] = useDebouncedValue(searchFilter.trim(), 250); - const [showEmptyGroups, setShowEmptyGroups] = useQueryParam( - "showEmptyGroups", - withDefault(BooleanParam, true) - ); const { alertGroupsPerPage } = useSettings(); const [activePage, setActivePage] = useQueryParam( @@ -181,10 +178,10 @@ export default function AlertsPage() { const shownGroups = useMemo( () => - showEmptyGroups + !hideEmptyGroups ? alertsPageData.groups : alertsPageData.groups.filter((g) => g.rules.length > 0), - [alertsPageData.groups, showEmptyGroups] + [alertsPageData.groups, hideEmptyGroups] ); // If we were e.g. on page 10 and the number of total pages decreases to 5 (due to filtering @@ -255,7 +252,13 @@ export default function AlertsPage() { setShowEmptyGroups(false)} + onClick={() => { + dispatch( + updateSettings({ + hideEmptyGroups: true + }) + ); + }} > Hide empty groups @@ -267,7 +270,13 @@ export default function AlertsPage() { setShowEmptyGroups(false)} + onClick={() => { + dispatch( + updateSettings({ + hideEmptyGroups: true + }) + ); + }} > Hide empty groups @@ -286,8 +295,8 @@ export default function AlertsPage() { // have a different background color than their surrounding group card in dark mode, // but it would be better to use CSS to override the light/dark colors for // collapsed/expanded accordion items. - backgroundColor: "#c0c0c015", - }, + backgroundColor: "#c0c0c015" + } }} key={j} value={j.toString()} @@ -403,7 +412,7 @@ export default function AlertsPage() { )} )), - [currentPageGroups, showAnnotations, setShowEmptyGroups] + [currentPageGroups, showAnnotations, dispatch] ); return ( @@ -442,7 +451,7 @@ export default function AlertsPage() { No rules found. ) : ( - !showEmptyGroups && + hideEmptyGroups && alertsPageData.groups.length !== shownGroups.length && ( Hiding {alertsPageData.groups.length - shownGroups.length} empty groups due to filters or no rules. - setShowEmptyGroups(true)}> + + dispatch(updateSettings({ hideEmptyGroups: false })) + } + > Show empty groups diff --git a/web/ui/mantine-ui/src/state/localStorageMiddleware.ts b/web/ui/mantine-ui/src/state/localStorageMiddleware.ts index 79baa5ac64..b3dc70662b 100644 --- a/web/ui/mantine-ui/src/state/localStorageMiddleware.ts +++ b/web/ui/mantine-ui/src/state/localStorageMiddleware.ts @@ -37,7 +37,7 @@ startAppListening({ effect: ({ payload }) => { persistToLocalStorage( localStorageKeyServiceDiscoveryPageCollapsedPools, - payload + payload, ); }, }); @@ -47,7 +47,7 @@ startAppListening({ effect: (_, { getState }) => { persistToLocalStorage( localStorageKeyQueryHistory, - getState().queryPage.queryHistory + getState().queryPage.queryHistory, ); }, }); @@ -62,6 +62,7 @@ startAppListening({ case "enableAutocomplete": case "enableSyntaxHighlighting": case "enableLinter": + case "hideEmptyGroups": case "showAnnotations": case "ruleGroupsPerPage": return persistToLocalStorage(`settings.${key}`, value); diff --git a/web/ui/mantine-ui/src/state/settingsSlice.ts b/web/ui/mantine-ui/src/state/settingsSlice.ts index c4154b7253..da1c1546bb 100644 --- a/web/ui/mantine-ui/src/state/settingsSlice.ts +++ b/web/ui/mantine-ui/src/state/settingsSlice.ts @@ -13,6 +13,7 @@ interface Settings { enableAutocomplete: boolean; enableSyntaxHighlighting: boolean; enableLinter: boolean; + hideEmptyGroups: boolean; showAnnotations: boolean; ruleGroupsPerPage: number; alertGroupsPerPage: number; @@ -30,6 +31,7 @@ export const localStorageKeyEnableAutocomplete = "settings.enableAutocomplete"; export const localStorageKeyEnableSyntaxHighlighting = "settings.enableSyntaxHighlighting"; export const localStorageKeyEnableLinter = "settings.enableLinter"; +export const localStorageKeyHideEmptyGroups = "settings.hideEmptyGroups"; export const localStorageKeyShowAnnotations = "settings.showAnnotations"; export const localStorageKeyRuleGroupsPerPage = "settings.ruleGroupsPerPage"; export const localStorageKeyAlertGroupsPerPage = "settings.alertGroupsPerPage"; @@ -53,7 +55,7 @@ const getPathPrefix = (path: string) => { "/flags", "/config", "/alertmanager-discovery", - "/agent", + "/agent" ]; const pagePath = pagePaths.find((p) => path.endsWith(p)); @@ -95,6 +97,10 @@ export const initialState: Settings = { localStorageKeyEnableLinter, true ), + hideEmptyGroups: initializeFromLocalStorage( + localStorageKeyHideEmptyGroups, + false + ), showAnnotations: initializeFromLocalStorage( localStorageKeyShowAnnotations, true @@ -106,7 +112,7 @@ export const initialState: Settings = { alertGroupsPerPage: initializeFromLocalStorage( localStorageKeyAlertGroupsPerPage, 10 - ), + ) }; export const settingsSlice = createSlice({ @@ -115,8 +121,8 @@ export const settingsSlice = createSlice({ reducers: { updateSettings: (state, { payload }: PayloadAction>) => { Object.assign(state, payload); - }, - }, + } + } }); export const { updateSettings } = settingsSlice.actions;