diff --git a/web/ui/mantine-ui/src/App.tsx b/web/ui/mantine-ui/src/App.tsx
index 02160e460b..2ca61761fa 100644
--- a/web/ui/mantine-ui/src/App.tsx
+++ b/web/ui/mantine-ui/src/App.tsx
@@ -20,6 +20,7 @@ import {
} from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import {
+ IconBell,
IconBellFilled,
IconChevronDown,
IconChevronRight,
@@ -62,6 +63,7 @@ import ReadinessWrapper from "./components/ReadinessWrapper";
import { QueryParamProvider } from "use-query-params";
import { ReactRouter6Adapter } from "use-query-params/adapters/react-router-6";
import ServiceDiscoveryPage from "./pages/service-discovery/ServiceDiscoveryPage";
+import AlertmanagerDiscoveryPage from "./pages/AlertmanagerDiscoveryPage";
const queryClient = new QueryClient();
@@ -106,6 +108,13 @@ const monitoringStatusPages = [
element: ,
inAgentMode: true,
},
+ {
+ title: "Alertmanager discovery",
+ path: "/discovered-alertmanagers",
+ icon: ,
+ element: ,
+ inAgentMode: false,
+ },
];
const serverStatusPages = [
diff --git a/web/ui/mantine-ui/src/api/responseTypes/alertmanagers.ts b/web/ui/mantine-ui/src/api/responseTypes/alertmanagers.ts
new file mode 100644
index 0000000000..2fcb8c23a3
--- /dev/null
+++ b/web/ui/mantine-ui/src/api/responseTypes/alertmanagers.ts
@@ -0,0 +1,10 @@
+export type AlertmanagerTarget = {
+ url: string;
+};
+
+// Result type for /api/v1/alertmanagers endpoint.
+// See: https://prometheus.io/docs/prometheus/latest/querying/api/#alertmanagers
+export type AlertmanagersResult = {
+ activeAlertmanagers: AlertmanagerTarget[];
+ droppedAlertmanagers: AlertmanagerTarget[];
+};
diff --git a/web/ui/mantine-ui/src/pages/AlertmanagerDiscoveryPage.tsx b/web/ui/mantine-ui/src/pages/AlertmanagerDiscoveryPage.tsx
new file mode 100644
index 0000000000..8fd12511ee
--- /dev/null
+++ b/web/ui/mantine-ui/src/pages/AlertmanagerDiscoveryPage.tsx
@@ -0,0 +1,115 @@
+import {
+ ActionIcon,
+ Alert,
+ Box,
+ Card,
+ Group,
+ Select,
+ Skeleton,
+ Stack,
+ Table,
+ Text,
+} from "@mantine/core";
+import {
+ IconBell,
+ IconBellOff,
+ IconInfoCircle,
+ IconLayoutNavbarCollapse,
+ IconLayoutNavbarExpand,
+ IconSearch,
+} from "@tabler/icons-react";
+import { Suspense } from "react";
+import { useAppDispatch, useAppSelector } from "../state/hooks";
+
+import {
+ ArrayParam,
+ StringParam,
+ useQueryParam,
+ withDefault,
+} from "use-query-params";
+import ErrorBoundary from "../components/ErrorBoundary";
+import ScrapePoolList from "./ServiceDiscoveryPoolsList";
+import { useSuspenseAPIQuery } from "../api/api";
+import { ScrapePoolsResult } from "../api/responseTypes/scrapePools";
+import {
+ setCollapsedPools,
+ setShowLimitAlert,
+} from "../state/serviceDiscoveryPageSlice";
+import { StateMultiSelect } from "../components/StateMultiSelect";
+import badgeClasses from "../Badge.module.css";
+import { AlertmanagersResult } from "../api/responseTypes/alertmanagers";
+import EndpointLink from "../components/EndpointLink";
+
+export const targetPoolDisplayLimit = 20;
+
+export default function AlertmanagerDiscoveryPage() {
+ // Load the list of all available scrape pools.
+ const {
+ data: {
+ data: { activeAlertmanagers, droppedAlertmanagers },
+ },
+ } = useSuspenseAPIQuery({
+ path: `/alertmanagers`,
+ });
+
+ return (
+
+
+
+
+
+ Active Alertmanagers
+
+
+ {activeAlertmanagers.length === 0 ? (
+ }>
+ No active alertmanagers found.
+
+ ) : (
+
+
+ {activeAlertmanagers.map((alertmanager) => (
+
+
+
+
+
+ ))}
+
+
+ )}
+
+
+
+
+
+ Dropped Alertmanagers
+
+
+ {droppedAlertmanagers.length === 0 ? (
+ }>
+ No dropped alertmanagers found.
+
+ ) : (
+
+
+ {droppedAlertmanagers.map((alertmanager) => (
+
+
+
+
+
+ ))}
+
+
+ )}
+
+
+ );
+}
diff --git a/web/ui/mantine-ui/src/pages/service-discovery/ServiceDiscoveryPoolsList.tsx b/web/ui/mantine-ui/src/pages/service-discovery/ServiceDiscoveryPoolsList.tsx
index 537468944a..d29808d99a 100644
--- a/web/ui/mantine-ui/src/pages/service-discovery/ServiceDiscoveryPoolsList.tsx
+++ b/web/ui/mantine-ui/src/pages/service-discovery/ServiceDiscoveryPoolsList.tsx
@@ -25,7 +25,6 @@ import {
} from "../../state/serviceDiscoveryPageSlice";
import CustomInfiniteScroll from "../../components/CustomInfiniteScroll";
-import TargetLabels from "./TargetLabels";
import { useDebouncedValue } from "@mantine/hooks";
import { targetPoolDisplayLimit } from "./ServiceDiscoveryPage";
import { BooleanParam, useQueryParam, withDefault } from "use-query-params";
diff --git a/web/ui/mantine-ui/src/pages/service-discovery/TargetLabels.tsx b/web/ui/mantine-ui/src/pages/service-discovery/TargetLabels.tsx
deleted file mode 100644
index 54e944f8cf..0000000000
--- a/web/ui/mantine-ui/src/pages/service-discovery/TargetLabels.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import { FC } from "react";
-import { Labels } from "../../api/responseTypes/targets";
-import { LabelBadges } from "../../components/LabelBadges";
-import { ActionIcon, Collapse, Group, Stack, Text } from "@mantine/core";
-import { useDisclosure } from "@mantine/hooks";
-import { IconChevronDown, IconChevronUp } from "@tabler/icons-react";
-
-type TargetLabelsProps = {
- labels: Labels;
- discoveredLabels: Labels;
-};
-
-const TargetLabels: FC = ({ discoveredLabels, labels }) => {
- const [showDiscovered, { toggle: toggleDiscovered }] = useDisclosure(false);
-
- return (
-
-
-
-
-
- {showDiscovered ? (
-
- ) : (
-
- )}
-
-
-
-
-
- Discovered labels:
-
-
-
-
- );
-};
-
-export default TargetLabels;