From eb0ae4e7ec58ecbd48a0a471acee71f934c2d11c Mon Sep 17 00:00:00 2001 From: Pawel Lebioda Date: Fri, 28 Aug 2026 12:55:42 +0200 Subject: [PATCH 1/2] PMM-15360: Gate OpenManager's nav and page on the switch Adds the settings flag as a second gate alongside the MongoDB-monitored check for OpenManager's nav entry, reusing the existing pattern for hiding MongoDB-only nav entries rather than inventing a second mechanism. OmPage now fails closed on the settings flag too, not just isPMMAdmin: a saved or shared link has to answer "switched off" rather than the API's raw FailedPrecondition or the unauthorized card, which would misreport a disabled feature as a permissions problem to an admin who has every right to be here. Also adds the technical-preview banner (dismissible per browser via localStorage, not a setting), with an explicit color override on its close button -- @percona/peak-ui's MuiAlert theme colors the icon/message slots via theme.palette[severity].contrastText but not .MuiAlert-action, so the default close button was rendering almost invisibly against the warning background. Stacks on PMM-15326-om-ui-nav (needs OmPage/navigation.provider.tsx) and this ticket's own settings-flag PR, merged in here since neither is available on a single common upstream branch yet. Signed-off-by: Pawel Lebioda --- .../navigation/navigation.provider.tsx | 7 +- ui/apps/pmm/src/om/OmPage.tsx | 71 ++++++++++++++++++- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/ui/apps/pmm/src/contexts/navigation/navigation.provider.tsx b/ui/apps/pmm/src/contexts/navigation/navigation.provider.tsx index e5183f252e..100a0f5b4e 100644 --- a/ui/apps/pmm/src/contexts/navigation/navigation.provider.tsx +++ b/ui/apps/pmm/src/contexts/navigation/navigation.provider.tsx @@ -95,8 +95,11 @@ export const NavigationProvider: FC = ({ children }) => { // is established; role/flag gating comes with real auth (Option B). items.push(...addSepApps()); - // Served by pmm-managed, so it is not gated with the SEP group. - items.push(...addOm()); + // Served by pmm-managed, so it is not gated with the SEP group -- gated + // on the settings flag alone. + if (settings.omEnabled) { + items.push(...addOm()); + } if (settings.backupManagementEnabled) { items.push(NAV_BACKUPS); diff --git a/ui/apps/pmm/src/om/OmPage.tsx b/ui/apps/pmm/src/om/OmPage.tsx index 53c4d9ef5b..a6c30261c8 100644 --- a/ui/apps/pmm/src/om/OmPage.tsx +++ b/ui/apps/pmm/src/om/OmPage.tsx @@ -16,11 +16,27 @@ */ import { FC, PropsWithChildren } from 'react'; +import Alert from '@mui/material/Alert'; +import Card from '@mui/material/Card'; +import CircularProgress from '@mui/material/CircularProgress'; import Stack from '@mui/material/Stack'; +import Typography from '@mui/material/Typography'; import { Page } from 'components/page'; import { useUser } from 'contexts/user'; +import { useReadonlySettings } from 'hooks/api/useSettings'; +import { useLocalStorage } from 'hooks/utils/useLocalStorage'; import { OrgRole } from 'types/user.types'; +const TECHNICAL_PREVIEW_DISMISSED_KEY = 'pmm-ui.om.technicalPreviewDismissed'; + +const Messages = { + switchedOff: + 'OpenManager is switched off. A PMM admin can turn it on in Configuration → Settings → Advanced Settings.', + technicalPreview: 'Technical preview', + technicalPreviewBody: + 'OpenManager is a technical preview. It is still under development and may change.', +}; + /** * Host chrome for the OM page. * @@ -37,9 +53,31 @@ import { OrgRole } from 'types/user.types'; * `isGrafanaAdmin || orgRole === Admin`, and `roles` (org-role only) cannot express the * Grafana-admin half on its own, so it gates the remaining case and `Page` renders its * standard unauthorized card. + * + * The settings gate is enforced here for the same reason: a saved or shared link to + * this page has to answer "switched off", not the API's raw FailedPrecondition, and + * not the unauthorized card above, which would misreport a disabled feature as a + * permissions problem to an admin who has every right to be here (PMM-15360 AC1/AC2/AC7). + * + * The technical-preview banner is dismissible, and remembered per browser rather than + * per PMM account or installation -- it is a "you've seen this" acknowledgement, not a + * setting with a right answer for every viewer, so localStorage is enough and needs no + * round trip to pmm-managed. + * + * The close button's `sx` override exists because `@percona/peak-ui`'s MuiAlert theme + * (`styleOverrides.icon`/`.message`) sets `color: theme.palette[severity].contrastText` + * on the icon and message slots, but not on `.MuiAlert-action` -- so the close button + * MUI renders for `onClose` falls back to the alert root's own `color`, which this + * theme leaves close to the warning background itself. Nothing else in this app uses a + * dismissible Alert, which is presumably why that gap was never hit before. */ export const OmPage: FC = ({ children }) => { const { user } = useUser(); + const { data: settings, isLoading } = useReadonlySettings(); + const [previewDismissed, setPreviewDismissed] = useLocalStorage( + TECHNICAL_PREVIEW_DISMISSED_KEY, + false + ); return ( = ({ children }) => { roles={user?.isPMMAdmin ? undefined : [OrgRole.Admin]} > -
{children}
+ {isLoading ? ( + + + + ) : settings?.omEnabled ? ( + <> + {!previewDismissed && ( + setPreviewDismissed(true)} + data-testid="om-technical-preview" + sx={{ + '& .MuiAlert-action': { + color: (theme) => theme.palette.warning.contrastText, + }, + }} + > + + {Messages.technicalPreview}{' '} + {Messages.technicalPreviewBody} + + + )} +
{children}
+ + ) : ( + + + {Messages.switchedOff} + + + )}
); From c17476c8893d97cced495441b547b9dc099ffe3a Mon Sep 17 00:00:00 2001 From: Pawel Lebioda Date: Tue, 1 Sep 2026 19:38:35 +0200 Subject: [PATCH 2/2] PMM-15360 Fix two type errors the merge with main surfaced navigation.provider.tsx: settings.omEnabled -> settings?.omEnabled, matching the optional-chaining the sibling backupManagementEnabled check already uses -- settings can be null before the first fetch resolves. settings.provider.tsx: the anonymous-user fallback CombinedSettings literal was missing omEnabled, now required by the type PMM-15360- om-switch-flag added. Defaults to false, matching every other flag in that fallback -- an anonymous session is never a PMM admin, so it would never see OM's nav entry regardless. Signed-off-by: Pawel Lebioda --- ui/apps/pmm/src/contexts/navigation/navigation.provider.tsx | 2 +- ui/apps/pmm/src/contexts/settings/settings.provider.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/apps/pmm/src/contexts/navigation/navigation.provider.tsx b/ui/apps/pmm/src/contexts/navigation/navigation.provider.tsx index 92227d70ef..90e57967ac 100644 --- a/ui/apps/pmm/src/contexts/navigation/navigation.provider.tsx +++ b/ui/apps/pmm/src/contexts/navigation/navigation.provider.tsx @@ -103,7 +103,7 @@ export const NavigationProvider: FC = ({ children }) => { // Served by pmm-managed, so it is not gated with the SEP group -- gated // on the settings flag alone. - if (settings.omEnabled) { + if (settings?.omEnabled) { items.push(...addOm()); } diff --git a/ui/apps/pmm/src/contexts/settings/settings.provider.tsx b/ui/apps/pmm/src/contexts/settings/settings.provider.tsx index 2926b7ff38..662fa19700 100644 --- a/ui/apps/pmm/src/contexts/settings/settings.provider.tsx +++ b/ui/apps/pmm/src/contexts/settings/settings.provider.tsx @@ -38,6 +38,7 @@ export const SettingsProvider: FC = ({ children }) => { backupManagementEnabled: false, azurediscoverEnabled: false, enableAccessControl: false, + omEnabled: false, frontend: frontendSettings.data, // check if pmm-compat-app plugin is enabled newUIEnabled: frontendSettings.data.apps['pmm-compat-app']?.preload,