Repository: prometheus/prometheus
File: web/ui/mantine-ui/src/pages/RulesPage.tsx (TypeScript)

Code before:
                    variant="transparent"
                    className={badgeClasses.statsBadge}
                    styles={{ label: { textTransform: "none" } }}
                    leftSection={<IconRepeat style={badgeIconStyle} />}
                  >
                    every {humanizeDuration(parseFloat(g.interval) * 1000)}{" "}
                  </Badge>
                </Tooltip>
              </Group>
            </Group>
            {g.rules.length === 0 && (
              <Alert title="No rules" icon={<IconInfoCircle />}>
                No rules in rule group.
              </Alert>
            )}
            <CustomInfiniteScroll
              allItems={g.rules}
              child={({ items }) => (
                <Accordion multiple variant="separated">
                  {items.map((r, j) => (
                    <Accordion.Item
                      mt={rem(5)}
                      styles={{
                        item: {
                          // TODO: This transparency hack is an OK workaround to make the collapsed items
                          // have a different background color than their surrounding group card in dark mode,
                          // but it would be better to use CSS to override the light/dark colors for
                          // collapsed/expanded accordion items.
                          backgroundColor: "#c0c0c015",
                        },
                      }}
                      key={j}
                      value={j.toString()}
                      style={{
                        borderLeft:
                          r.health === "err"
                            ? "5px solid var(--mantine-color-red-4)"
                            : r.health === "unknown"
                              ? "5px solid var(--mantine-color-gray-5)"
                              : "5px solid var(--mantine-color-green-4)",
                      }}
                    >
                      <Accordion.Control
                        styles={{ label: { paddingBlock: rem(10) } }}
                      >
                        <Group justify="space-between" mr="lg">
                          <Group gap="xs" wrap="nowrap">
                            {r.type === "alerting" ? (
                              <Tooltip label="Alerting rule" withArrow>
                                <IconBell
                                  style={{ width: rem(15), height: rem(15) }}

Review comment (suggestion):
```suggestion
  // We memorize the actual rendering of the page items to avoid re-rendering
```

Code after:
        ? rulesPageData.groups
        : rulesPageData.groups.filter((g) => g.rules.length > 0),
    [rulesPageData.groups, showEmptyGroups]
  );

  // If we were e.g. on page 10 and the number of total pages decreases to 5 (due to filtering
  // or changing the max number of items per page), go to the largest possible page.
  const totalPageCount = Math.ceil(shownGroups.length / ruleGroupsPerPage);
  const effectiveActivePage = Math.max(1, Math.min(activePage, totalPageCount));

  useEffect(() => {
    if (effectiveActivePage !== activePage) {
      setActivePage(effectiveActivePage);
    }
  }, [effectiveActivePage, activePage, setActivePage]);

  const currentPageGroups = useMemo(
    () =>
      shownGroups.slice(
        (effectiveActivePage - 1) * ruleGroupsPerPage,
        effectiveActivePage * ruleGroupsPerPage
      ),
    [shownGroups, effectiveActivePage, ruleGroupsPerPage]
  );

  // We memoize the actual rendering of the page items to avoid re-rendering
  // them on every state change. This is especially important when the user
  // types into the search box, as the search filter changes on every keystroke,
  // even before debouncing takes place (extracting the filters and results list
  // into separate components would be an alternative to this, but it's kinda
  // convenient to have in the same file IMO).
  const renderedPageItems = useMemo(
    () =>
      currentPageGroups.map((g) => (
        <Card shadow="xs" withBorder p="md" key={`${g.file}-${g.name}`}>
          <Group mb="sm" justify="space-between">
            <Group align="baseline">
              <Text fz="xl" fw={600} c="var(--mantine-primary-color-filled)">
                {g.name}
              </Text>
              <Text fz="sm" c="gray.6">
                {g.file}
              </Text>
            </Group>
            <Group>
              <Tooltip label="Last group evaluation" withArrow>
                <Badge
                  variant="light"
                  className={badgeClasses.statsBadge}
                  styles={{ label: { textTransform: "none" } }}
                  leftSection={<IconRefresh style={badgeIconStyle} />}