From 136e179cc4c03f2a0a28d68131579a81c6b6479f Mon Sep 17 00:00:00 2001 From: Oliver Date: Mon, 24 Mar 2025 20:45:34 +1100 Subject: [PATCH] [UI] Alerts (#9365) * remove TODO (now implemented) * Add Mantine tooltips * Add "alerts" to header * Add more alert types --- .../src/components/buttons/ScanButton.tsx | 18 +-- .../components/buttons/SpotlightButton.tsx | 19 +-- src/frontend/src/components/nav/Header.tsx | 126 +++++++++++++++--- 3 files changed, 128 insertions(+), 35 deletions(-) diff --git a/src/frontend/src/components/buttons/ScanButton.tsx b/src/frontend/src/components/buttons/ScanButton.tsx index 608663da62..68aa0d39db 100644 --- a/src/frontend/src/components/buttons/ScanButton.tsx +++ b/src/frontend/src/components/buttons/ScanButton.tsx @@ -1,5 +1,5 @@ import { t } from '@lingui/macro'; -import { ActionIcon } from '@mantine/core'; +import { ActionIcon, Tooltip } from '@mantine/core'; import { useDisclosure } from '@mantine/hooks'; import { IconQrcode } from '@tabler/icons-react'; import BarcodeScanDialog from '../barcodes/BarcodeScanDialog'; @@ -12,13 +12,15 @@ export function ScanButton() { return ( <> - - - + + + + + ); diff --git a/src/frontend/src/components/buttons/SpotlightButton.tsx b/src/frontend/src/components/buttons/SpotlightButton.tsx index 275650133b..93e706bb43 100644 --- a/src/frontend/src/components/buttons/SpotlightButton.tsx +++ b/src/frontend/src/components/buttons/SpotlightButton.tsx @@ -1,5 +1,5 @@ import { t } from '@lingui/macro'; -import { ActionIcon } from '@mantine/core'; +import { ActionIcon, Tooltip } from '@mantine/core'; import { IconCommand } from '@tabler/icons-react'; import { firstSpotlight } from '../nav/Layout'; @@ -9,13 +9,14 @@ import { firstSpotlight } from '../nav/Layout'; */ export function SpotlightButton() { return ( - firstSpotlight.open()} - title={t`Open spotlight`} - variant='transparent' - aria-label='open-spotlight' - > - - + + firstSpotlight.open()} + variant='transparent' + aria-label='open-spotlight' + > + + + ); } diff --git a/src/frontend/src/components/nav/Header.tsx b/src/frontend/src/components/nav/Header.tsx index cfb830eb49..b4f3106a40 100644 --- a/src/frontend/src/components/nav/Header.tsx +++ b/src/frontend/src/components/nav/Header.tsx @@ -1,17 +1,25 @@ import { ActionIcon, + Alert, Container, Group, Indicator, + Menu, Tabs, - Text + Text, + Tooltip } from '@mantine/core'; import { useDisclosure } from '@mantine/hooks'; -import { IconBell, IconSearch } from '@tabler/icons-react'; +import { + IconBell, + IconExclamationCircle, + IconSearch +} from '@tabler/icons-react'; import { useQuery } from '@tanstack/react-query'; import { type ReactNode, useEffect, useMemo, useState } from 'react'; import { useMatch, useNavigate } from 'react-router-dom'; +import { t } from '@lingui/macro'; import { api } from '../../App'; import { getNavTabs } from '../../defaults/links'; import { ApiEndpoints } from '../../enums/ApiEndpoints'; @@ -32,7 +40,15 @@ import { NavigationDrawer } from './NavigationDrawer'; import { NotificationDrawer } from './NotificationDrawer'; import { SearchDrawer } from './SearchDrawer'; +interface AlertInfo { + key: string; + title: string; + message: string; +} + export function Header() { + const user = useUserState(); + const [setNavigationOpen, navigationOpen] = useLocalState((state) => [ state.setNavigationOpen, state.navigationOpen @@ -58,6 +74,49 @@ export function Header() { return server.customize?.navbar_message; }, [server.customize]); + const [dismissed, setDismissed] = useState([]); + + const alerts: AlertInfo[] = useMemo(() => { + const _alerts: AlertInfo[] = []; + + if (server?.debug_mode) { + _alerts.push({ + key: 'debug', + title: t`Debug Mode`, + message: t`The server is running in debug mode.` + }); + } + + if (server?.worker_running == false) { + _alerts.push({ + key: 'worker', + title: t`Background Worker`, + message: t`The background worker process is not running.` + }); + } + + if (globalSettings.isSet('SERVER_RESTART_REQUIRED')) { + _alerts.push({ + key: 'restart', + title: t`Server Restart`, + message: t`The server requires a restart to apply changes.` + }); + } + + const n_migrations = + Number.parseInt(globalSettings.getSetting('_PENDING_MIGRATIONS')) ?? 0; + + if (n_migrations > 0) { + _alerts.push({ + key: 'migrations', + title: t`Database Migrations`, + message: t`There are pending database migrations.` + }); + } + + return _alerts.filter((alert) => !dismissed.includes(alert.key)); + }, [server, dismissed, globalSettings]); + // Fetch number of notifications for the current user const notifications = useQuery({ queryKey: ['notification-count'], @@ -125,13 +184,15 @@ export function Header() { )} - - - + + + + + {globalSettings.isSet('BARCODE_ENABLE') && } - - - + + + + + + {user.isStaff() && alerts.length > 0 && ( + + + + + + + + + + {alerts.map((alert) => ( + + setDismissed([...dismissed, alert.key])} + > + {alert.message} + + + ))} + + + )} @@ -179,8 +271,6 @@ function NavTabs() { return; } - // TODO: Hide icons if user does not wish to display them! - _tabs.push(