diff --git a/web/ui/mantine-ui/src/App.tsx b/web/ui/mantine-ui/src/App.tsx index 3ed950a9fd..5b6926c90b 100644 --- a/web/ui/mantine-ui/src/App.tsx +++ b/web/ui/mantine-ui/src/App.tsx @@ -46,7 +46,6 @@ import QueryPage from "./pages/query/QueryPage"; import AlertsPage from "./pages/AlertsPage"; import RulesPage from "./pages/RulesPage"; import TargetsPage from "./pages/targets/TargetsPage"; -import ServiceDiscoveryPage from "./pages/ServiceDiscoveryPage"; import StatusPage from "./pages/StatusPage"; import TSDBStatusPage from "./pages/TSDBStatusPage"; import FlagsPage from "./pages/FlagsPage"; @@ -62,6 +61,7 @@ import SettingsMenu from "./components/SettingsMenu"; 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"; const queryClient = new QueryClient(); diff --git a/web/ui/mantine-ui/src/components/LabelBadges.tsx b/web/ui/mantine-ui/src/components/LabelBadges.tsx index 2c2c81efbf..f60a37f032 100644 --- a/web/ui/mantine-ui/src/components/LabelBadges.tsx +++ b/web/ui/mantine-ui/src/components/LabelBadges.tsx @@ -1,4 +1,4 @@ -import { Badge, BadgeVariant, Group, MantineColor } from "@mantine/core"; +import { Badge, BadgeVariant, Group, MantineColor, Stack } from "@mantine/core"; import { FC } from "react"; import { escapeString } from "../lib/escapeString"; import badgeClasses from "../Badge.module.css"; @@ -7,14 +7,16 @@ export interface LabelBadgesProps { labels: Record; variant?: BadgeVariant; color?: MantineColor; + wrapper?: typeof Group | typeof Stack; } export const LabelBadges: FC = ({ labels, variant, color, + wrapper: Wrapper = Group, }) => ( - + {Object.entries(labels).map(([k, v]) => { return ( = ({ ); })} - + ); diff --git a/web/ui/mantine-ui/src/pages/ServiceDiscoveryPage.tsx b/web/ui/mantine-ui/src/pages/ServiceDiscoveryPage.tsx deleted file mode 100644 index e1a7e8c652..0000000000 --- a/web/ui/mantine-ui/src/pages/ServiceDiscoveryPage.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function ServiceDiscoveryPage() { - return <>ServiceDiscovery page; -} diff --git a/web/ui/mantine-ui/src/pages/service-discovery/ServiceDiscoveryPage.tsx b/web/ui/mantine-ui/src/pages/service-discovery/ServiceDiscoveryPage.tsx new file mode 100644 index 0000000000..2b870fb073 --- /dev/null +++ b/web/ui/mantine-ui/src/pages/service-discovery/ServiceDiscoveryPage.tsx @@ -0,0 +1,127 @@ +import { + ActionIcon, + Box, + Group, + Select, + Skeleton, + TextInput, +} from "@mantine/core"; +import { + IconLayoutNavbarCollapse, + IconLayoutNavbarExpand, + IconSearch, +} from "@tabler/icons-react"; +import { Suspense } from "react"; +import { useAppDispatch, useAppSelector } from "../../state/hooks"; + +import { 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"; + +export const targetPoolDisplayLimit = 20; + +export default function ServiceDiscoveryPage() { + // Load the list of all available scrape pools. + const { + data: { + data: { scrapePools }, + }, + } = useSuspenseAPIQuery({ + path: `/scrape_pools`, + }); + + const dispatch = useAppDispatch(); + + const [scrapePool, setScrapePool] = useQueryParam("scrapePool", StringParam); + const [searchFilter, setSearchFilter] = useQueryParam( + "searchFilter", + withDefault(StringParam, "") + ); + + const { collapsedPools, showLimitAlert } = useAppSelector( + (state) => state.serviceDiscoveryPage + ); + + // When we have more than X targets, we want to limit the display by selecting the first + // scrape pool and reflecting that in the URL as well. We also want to show an alert + // about the fact that we are limiting the display, but the tricky bit is that this + // alert should only be shown once, upon the first "redirect" that causes the limiting, + // not again when the page is reloaded with the same URL parameters. That's why we remember + // `showLimitAlert` in Redux (just useState() doesn't work properly, because the component + // for some Suspense-related reasons seems to be mounted/unmounted multiple times, so the + // state cell would get initialized multiple times as well). + const limited = + scrapePools.length > targetPoolDisplayLimit && scrapePool === undefined; + if (limited) { + setScrapePool(scrapePools[0]); + dispatch(setShowLimitAlert(true)); + } + + return ( + <> + +