ui: Add expandable scrape interval and timeout to targets page

Implements expand/collapse functionality for displaying final scrape
configuration (interval + timeout) in the targets page timing column.
- Add ScrapeDetails component with expand/collapse chevron
- Keep existing "Last Scrape" and "Scrape Duration" badges always visible
- Display "Scrape interval: every \<interval\>" and "Scrape timeout: after \<timeout\>" when expanded
- Use IconRepeat for interval and IconPlugConnectedX for timeout
- Follow TargetLabels.tsx pattern for consistency
- Implement performance optimization with conditional DOM rendering
- Maintain existing hover tooltip functionality

Signed-off-by: ADITYATIWARI342005 <142050150+ADITYATIWARI342005@users.noreply.github.com>
This commit is contained in:
ADITYATIWARI342005 2025-09-28 01:18:26 +05:30
parent c9e0e36701
commit 8eb8758925
2 changed files with 111 additions and 55 deletions

View File

@ -0,0 +1,109 @@
import { FC } from "react";
import { ActionIcon, Badge, Collapse, Group, Stack, Tooltip } from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import {
IconChevronDown,
IconChevronUp,
IconHourglass,
IconPlugConnectedX,
IconRefresh,
IconRepeat,
} from "@tabler/icons-react";
import { humanizeDuration, humanizeDurationRelative, now } from "../../lib/formatTime";
import { Target } from "../../api/responseTypes/targets";
import badgeClasses from "../../Badge.module.css";
import { actionIconStyle, badgeIconStyle } from "../../styles";
type ScrapeDetailsProps = {
target: Target;
};
const ScrapeDetails: FC<ScrapeDetailsProps> = ({ target }) => {
const [showDetails, { toggle: toggleDetails }] = useDisclosure(false);
return (
<Stack gap="xs">
<Group wrap="nowrap" align="flex-start">
<Group gap="xs" wrap="wrap">
<Tooltip label="Last target scrape" withArrow>
<Badge
variant="light"
className={badgeClasses.statsBadge}
styles={{
label: { textTransform: "none" },
}}
leftSection={<IconRefresh style={badgeIconStyle} />}
>
{humanizeDurationRelative(target.lastScrape, now())}
</Badge>
</Tooltip>
<Tooltip
label={
<div style={{ lineHeight: 1.2 }}>
<div>Interval: {target.scrapeInterval}</div>
<div>Timeout: {target.scrapeTimeout}</div>
</div>
}
withArrow
>
<Badge
variant="light"
className={badgeClasses.statsBadge}
styles={{
label: { textTransform: "none" },
}}
leftSection={<IconHourglass style={badgeIconStyle} />}
>
{humanizeDuration(target.lastScrapeDuration * 1000)}
</Badge>
</Tooltip>
</Group>
<ActionIcon
size="xs"
color="gray"
variant="light"
onClick={toggleDetails}
title={`${showDetails ? "Hide" : "Show"} scrape configuration details`}
>
{showDetails ? (
<IconChevronUp style={actionIconStyle} />
) : (
<IconChevronDown style={actionIconStyle} />
)}
</ActionIcon>
</Group>
<Collapse in={showDetails}>
{/* Additionally remove DOM elements when not expanded (helps performance) */}
{showDetails && (
<Group gap="xs" wrap="wrap">
<Tooltip label="Scrape interval" withArrow>
<Badge
variant="light"
className={badgeClasses.statsBadge}
styles={{ label: { textTransform: "none" } }}
leftSection={<IconRepeat style={badgeIconStyle} />}
>
every {target.scrapeInterval}
</Badge>
</Tooltip>
<Tooltip label="Scrape timeout" withArrow>
<Badge
variant="light"
className={badgeClasses.statsBadge}
styles={{ label: { textTransform: "none" } }}
leftSection={<IconPlugConnectedX style={badgeIconStyle} />}
>
after {target.scrapeTimeout}
</Badge>
</Tooltip>
</Group>
)}
</Collapse>
</Stack>
);
};
export default ScrapeDetails;

View File

@ -8,23 +8,15 @@ import {
Stack,
Table,
Text,
Tooltip,
} from "@mantine/core";
import { KVSearch } from "@nexucis/kvsearch";
import {
IconAlertTriangle,
IconHourglass,
IconInfoCircle,
IconRefresh,
} from "@tabler/icons-react";
import { useSuspenseAPIQuery } from "../../api/api";
import { Target, TargetsResult } from "../../api/responseTypes/targets";
import React, { FC, memo, useMemo } from "react";
import {
humanizeDurationRelative,
humanizeDuration,
now,
} from "../../lib/formatTime";
import { useLocalStorage } from "@mantine/hooks";
import { useAppDispatch, useAppSelector } from "../../state/hooks";
import {
@ -37,8 +29,8 @@ import CustomInfiniteScroll from "../../components/CustomInfiniteScroll";
import badgeClasses from "../../Badge.module.css";
import panelClasses from "../../Panel.module.css";
import TargetLabels from "./TargetLabels";
import ScrapeDetails from "./ScrapeDetails";
import { targetPoolDisplayLimit } from "./TargetsPage";
import { badgeIconStyle } from "../../styles";
type ScrapePool = {
targets: Target[];
@ -332,52 +324,7 @@ const ScrapePoolList: FC<ScrapePoolListProp> = memo(
/>
</Table.Td>
<Table.Td valign="top">
<Group gap="xs" wrap="wrap">
<Tooltip
label="Last target scrape"
withArrow
>
<Badge
variant="light"
className={badgeClasses.statsBadge}
styles={{
label: { textTransform: "none" },
}}
leftSection={
<IconRefresh
style={badgeIconStyle}
/>
}
>
{humanizeDurationRelative(
target.lastScrape,
now()
)}
</Badge>
</Tooltip>
<Tooltip
label="Duration of last target scrape"
withArrow
>
<Badge
variant="light"
className={badgeClasses.statsBadge}
styles={{
label: { textTransform: "none" },
}}
leftSection={
<IconHourglass
style={badgeIconStyle}
/>
}
>
{humanizeDuration(
target.lastScrapeDuration * 1000
)}
</Badge>
</Tooltip>
</Group>
<ScrapeDetails target={target} />
</Table.Td>
<Table.Td valign="top">
<Badge