mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-26 22:19:40 -08:00
Style cleanups, mostly for web notifications and startup alert
Some of the changes are a bit unreadable because the previous files were not saved with the project's linter / auto-formatter settings applied. But it's basically: * For icons that are not Mantine-native components, use the rem() function for computing their size, so they scale correctly with the root font size. See https://mantine.dev/styles/rem/. * Try a different icon for the notifications tray, since the bell icon was already used for Prometheus alerts. Other candidates from https://tabler.io/icons would be IconExclamationCircle or IconDeviceDesktopExclamation or IconMessageCircleExclamation. * The server startup alert looked a bit cramped, introduced a Stack to add spacing between the text and the progress bar. * Added a bit of spacing between notification text and date. Things looked cramped. To make things look ok with that, I also top-aligned the notification text and icon. Signed-off-by: Julius Volz <julius.volz@gmail.com>
This commit is contained in:
parent
023146e918
commit
3d2194f561
|
@ -1,61 +1,105 @@
|
||||||
import { ActionIcon, Indicator, Popover, Card, Text, Stack, ScrollArea, Group } from "@mantine/core";
|
import {
|
||||||
import { IconBell, IconAlertTriangle, IconNetworkOff } from "@tabler/icons-react";
|
ActionIcon,
|
||||||
import { useNotifications } from '../state/useNotifications';
|
Indicator,
|
||||||
|
Popover,
|
||||||
|
Card,
|
||||||
|
Text,
|
||||||
|
Stack,
|
||||||
|
ScrollArea,
|
||||||
|
Group,
|
||||||
|
rem,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import {
|
||||||
|
IconAlertTriangle,
|
||||||
|
IconNetworkOff,
|
||||||
|
IconMessageExclamation,
|
||||||
|
} from "@tabler/icons-react";
|
||||||
|
import { useNotifications } from "../state/useNotifications";
|
||||||
import { actionIconStyle } from "../styles";
|
import { actionIconStyle } from "../styles";
|
||||||
import { useSettings } from '../state/settingsSlice';
|
import { useSettings } from "../state/settingsSlice";
|
||||||
import { formatTimestamp } from "../lib/formatTime";
|
import { formatTimestamp } from "../lib/formatTime";
|
||||||
|
|
||||||
const NotificationsIcon = () => {
|
const NotificationsIcon = () => {
|
||||||
const { notifications, isConnectionError } = useNotifications();
|
const { notifications, isConnectionError } = useNotifications();
|
||||||
const { useLocalTime } = useSettings();
|
const { useLocalTime } = useSettings();
|
||||||
|
|
||||||
return (
|
return notifications.length === 0 && !isConnectionError ? null : (
|
||||||
(notifications.length === 0 && !isConnectionError) ? null : (
|
<Indicator
|
||||||
<Indicator
|
color={"red"}
|
||||||
color={"red"}
|
size={16}
|
||||||
size={16}
|
label={isConnectionError ? "!" : notifications.length}
|
||||||
label={isConnectionError ? "!" : notifications.length}
|
>
|
||||||
>
|
<Popover position="bottom-end" shadow="md" withArrow>
|
||||||
<Popover position="bottom-end" shadow="md" withArrow>
|
<Popover.Target>
|
||||||
<Popover.Target>
|
<ActionIcon
|
||||||
<ActionIcon color="gray" title="Notifications" aria-label="Notifications" size={32}>
|
color="gray"
|
||||||
<IconBell style={actionIconStyle}/>
|
title="Notifications"
|
||||||
</ActionIcon>
|
aria-label="Notifications"
|
||||||
</Popover.Target>
|
size={32}
|
||||||
|
>
|
||||||
|
<IconMessageExclamation style={actionIconStyle} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Popover.Target>
|
||||||
|
|
||||||
<Popover.Dropdown>
|
<Popover.Dropdown>
|
||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
<Text fw={700} size="xs" color="dimmed" ta="center">Notifications</Text>
|
<Text fw={700} size="xs" c="dimmed" ta="center">
|
||||||
<ScrollArea.Autosize mah={200}>
|
Notifications
|
||||||
{ isConnectionError ? (
|
</Text>
|
||||||
<Card p="xs" color="red">
|
<ScrollArea.Autosize mah={200}>
|
||||||
<Group wrap="nowrap">
|
{isConnectionError ? (
|
||||||
<IconNetworkOff color="red" size={20} />
|
<Card p="xs" color="red">
|
||||||
<Stack gap="0">
|
<Group wrap="nowrap">
|
||||||
<Text size="sm" fw={500}>Real-time notifications interrupted.</Text>
|
<IconNetworkOff
|
||||||
<Text size="xs" color="dimmed">Please refresh the page or check your connection.</Text>
|
color="red"
|
||||||
</Stack>
|
style={{ width: rem(20), height: rem(20) }}
|
||||||
</Group>
|
/>
|
||||||
</Card>
|
<Stack gap="0">
|
||||||
) : notifications.length === 0 ? (
|
<Text size="sm" fw={500}>
|
||||||
<Text ta="center" color="dimmed">No notifications</Text>
|
Real-time notifications interrupted.
|
||||||
) : (notifications.map((notification, index) => (
|
</Text>
|
||||||
|
<Text size="xs" c="dimmed">
|
||||||
|
Please refresh the page or check your connection.
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
) : notifications.length === 0 ? (
|
||||||
|
<Text ta="center" c="dimmed">
|
||||||
|
No notifications
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
|
notifications.map((notification, index) => (
|
||||||
<Card key={index} p="xs">
|
<Card key={index} p="xs">
|
||||||
<Group wrap="nowrap">
|
<Group wrap="nowrap" align="flex-start">
|
||||||
<IconAlertTriangle color="red" size={20} />
|
<IconAlertTriangle
|
||||||
<Stack style={{ maxWidth: 250 }} gap={0}>
|
color="red"
|
||||||
<Text size="sm" fw={500}>{notification.text}</Text>
|
style={{
|
||||||
<Text size="xs" color="dimmed">{formatTimestamp(new Date(notification.date).valueOf() / 1000, useLocalTime)}</Text>
|
width: rem(20),
|
||||||
|
height: rem(20),
|
||||||
|
marginTop: rem(3),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Stack maw={250} gap={5}>
|
||||||
|
<Text size="sm" fw={500}>
|
||||||
|
{notification.text}
|
||||||
|
</Text>
|
||||||
|
<Text size="xs" c="dimmed">
|
||||||
|
{formatTimestamp(
|
||||||
|
new Date(notification.date).valueOf() / 1000,
|
||||||
|
useLocalTime
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Group>
|
</Group>
|
||||||
</Card>
|
</Card>
|
||||||
)))}
|
))
|
||||||
</ScrollArea.Autosize>
|
)}
|
||||||
</Stack>
|
</ScrollArea.Autosize>
|
||||||
</Popover.Dropdown>
|
</Stack>
|
||||||
</Popover>
|
</Popover.Dropdown>
|
||||||
</Indicator>
|
</Popover>
|
||||||
)
|
</Indicator>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { useAppDispatch } from "../state/hooks";
|
||||||
import { updateSettings, useSettings } from "../state/settingsSlice";
|
import { updateSettings, useSettings } from "../state/settingsSlice";
|
||||||
import { useSuspenseAPIQuery } from "../api/api";
|
import { useSuspenseAPIQuery } from "../api/api";
|
||||||
import { WALReplayStatus } from "../api/responseTypes/walreplay";
|
import { WALReplayStatus } from "../api/responseTypes/walreplay";
|
||||||
import { Progress, Alert } from "@mantine/core";
|
import { Progress, Alert, Stack } from "@mantine/core";
|
||||||
import { useSuspenseQuery } from "@tanstack/react-query";
|
import { useSuspenseQuery } from "@tanstack/react-query";
|
||||||
|
|
||||||
const STATUS_STARTING = "is starting up...";
|
const STATUS_STARTING = "is starting up...";
|
||||||
|
@ -57,14 +57,12 @@ const ReadinessLoader: FC = () => {
|
||||||
// Only call WAL replay status API if the service is starting up.
|
// Only call WAL replay status API if the service is starting up.
|
||||||
const shouldQueryWALReplay = statusMessage === STATUS_STARTING;
|
const shouldQueryWALReplay = statusMessage === STATUS_STARTING;
|
||||||
|
|
||||||
const {
|
const { data: walData, isSuccess: walSuccess } =
|
||||||
data: walData,
|
useSuspenseAPIQuery<WALReplayStatus>({
|
||||||
isSuccess: walSuccess,
|
path: "/status/walreplay",
|
||||||
} = useSuspenseAPIQuery<WALReplayStatus>({
|
key: ["walreplay", queryKey],
|
||||||
path: "/status/walreplay",
|
enabled: shouldQueryWALReplay, // Only enabled when service is starting up.
|
||||||
key: ["walreplay", queryKey],
|
});
|
||||||
enabled: shouldQueryWALReplay, // Only enabled when service is starting up.
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (ready) {
|
if (ready) {
|
||||||
|
@ -80,14 +78,18 @@ const ReadinessLoader: FC = () => {
|
||||||
return (
|
return (
|
||||||
<Alert
|
<Alert
|
||||||
color="yellow"
|
color="yellow"
|
||||||
title={"Prometheus " + (agentMode && "Agent "||"") + (statusMessage || STATUS_LOADING)}
|
title={
|
||||||
icon={<IconAlertTriangle/>}
|
"Prometheus " +
|
||||||
|
((agentMode && "Agent ") || "") +
|
||||||
|
(statusMessage || STATUS_LOADING)
|
||||||
|
}
|
||||||
|
icon={<IconAlertTriangle />}
|
||||||
maw={500}
|
maw={500}
|
||||||
mx="auto"
|
mx="auto"
|
||||||
mt="lg"
|
mt="lg"
|
||||||
>
|
>
|
||||||
{shouldQueryWALReplay && walSuccess && walData && (
|
{shouldQueryWALReplay && walSuccess && walData && (
|
||||||
<>
|
<Stack>
|
||||||
<strong>
|
<strong>
|
||||||
Replaying WAL ({walData.data.current}/{walData.data.max})
|
Replaying WAL ({walData.data.current}/{walData.data.max})
|
||||||
</strong>
|
</strong>
|
||||||
|
@ -95,9 +97,13 @@ const ReadinessLoader: FC = () => {
|
||||||
size="xl"
|
size="xl"
|
||||||
animated
|
animated
|
||||||
color="yellow"
|
color="yellow"
|
||||||
value={((walData.data.current - walData.data.min + 1) / (walData.data.max - walData.data.min + 1)) * 100}
|
value={
|
||||||
|
((walData.data.current - walData.data.min + 1) /
|
||||||
|
(walData.data.max - walData.data.min + 1)) *
|
||||||
|
100
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
</Alert>
|
</Alert>
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
Badge,
|
Badge,
|
||||||
Card,
|
Card,
|
||||||
Group,
|
Group,
|
||||||
|
rem,
|
||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
|
@ -135,11 +136,15 @@ export default function RulesPage() {
|
||||||
<Group gap="xs" wrap="nowrap">
|
<Group gap="xs" wrap="nowrap">
|
||||||
{r.type === "alerting" ? (
|
{r.type === "alerting" ? (
|
||||||
<Tooltip label="Alerting rule" withArrow>
|
<Tooltip label="Alerting rule" withArrow>
|
||||||
<IconBell size={15} />
|
<IconBell
|
||||||
|
style={{ width: rem(15), height: rem(15) }}
|
||||||
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
) : (
|
) : (
|
||||||
<Tooltip label="Recording rule" withArrow>
|
<Tooltip label="Recording rule" withArrow>
|
||||||
<IconTimeline size={15} />
|
<IconTimeline
|
||||||
|
style={{ width: rem(15), height: rem(15) }}
|
||||||
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
<Text>{r.name}</Text>
|
<Text>{r.name}</Text>
|
||||||
|
|
|
@ -21,6 +21,7 @@ import {
|
||||||
Skeleton,
|
Skeleton,
|
||||||
Stack,
|
Stack,
|
||||||
Table,
|
Table,
|
||||||
|
rem,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { escapeString } from "../../../lib/escapeString";
|
import { escapeString } from "../../../lib/escapeString";
|
||||||
import serializeNode from "../../../promql/serialize";
|
import serializeNode from "../../../promql/serialize";
|
||||||
|
@ -326,7 +327,9 @@ const LabelsExplorer: FC<LabelsExplorerProps> = ({
|
||||||
title="Cancel"
|
title="Cancel"
|
||||||
style={{ flexShrink: 0 }}
|
style={{ flexShrink: 0 }}
|
||||||
>
|
>
|
||||||
<IconX size={18} />
|
<IconX
|
||||||
|
style={{ width: rem(18), height: rem(18) }}
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
) : (
|
) : (
|
||||||
|
|
Loading…
Reference in a new issue