github.com/thanos-io/thanos@v0.32.5/pkg/ui/react-app/src/pages/targets/ScrapePoolList.tsx (about)

     1  import React, { ChangeEvent, FC, useEffect, useState } from 'react';
     2  import Filter, { Expanded } from './Filter';
     3  import { useFetch } from '../../hooks/useFetch';
     4  import { groupTargets, ScrapePool, ScrapePools, Target } from './target';
     5  import PathPrefixProps from '../../types/PathPrefixProps';
     6  import { withStatusIndicator } from '../../components/withStatusIndicator';
     7  import { useLocalStorage } from '../../hooks/useLocalStorage';
     8  import { ToggleMoreLess } from '../../components/ToggleMoreLess';
     9  import { KVSearch } from '@nexucis/kvsearch';
    10  import styles from './ScrapePoolPanel.module.css';
    11  import { Col, Collapse, Row } from 'reactstrap';
    12  import SearchBar from '../../components/SearchBar';
    13  import { ScrapePoolContent } from './ScrapePoolContent';
    14  
    15  interface ScrapePoolListProps {
    16    activeTargets: Target[];
    17  }
    18  
    19  const kvSearch = new KVSearch<Target>({
    20    shouldSort: true,
    21    indexedKeys: ['labels', 'scrapePool', ['labels', /.*/]],
    22  });
    23  
    24  interface PanelProps {
    25    scrapePool: string;
    26    targetGroup: ScrapePool;
    27    expanded: boolean;
    28    toggleExpanded: () => void;
    29  }
    30  
    31  export const ScrapePoolPanel: FC<PanelProps> = (props: PanelProps) => {
    32    const modifier = props.targetGroup.upCount < props.targetGroup.targets.length ? 'danger' : 'normal';
    33    const id = `pool-${props.scrapePool}`;
    34    const anchorProps = {
    35      href: `#${id}`,
    36      id,
    37    };
    38    return (
    39      <div>
    40        <ToggleMoreLess event={props.toggleExpanded} showMore={props.expanded}>
    41          <a className={styles[modifier]} {...anchorProps}>
    42            {`${props.scrapePool} (${props.targetGroup.upCount}/${props.targetGroup.targets.length} up)`}
    43          </a>
    44        </ToggleMoreLess>
    45        <Collapse isOpen={props.expanded}>
    46          <ScrapePoolContent targets={props.targetGroup.targets} />
    47        </Collapse>
    48      </div>
    49    );
    50  };
    51  
    52  export const ScrapePoolListContent: FC<ScrapePoolListProps> = ({ activeTargets }) => {
    53    const initialPoolList = groupTargets(activeTargets);
    54    const [poolList, setPoolList] = useState<ScrapePools>(initialPoolList);
    55    const [targetList, setTargetList] = useState(activeTargets);
    56    const [filter, setFilter] = useLocalStorage('targets-page-filter', { showHealthy: true, showUnhealthy: true });
    57  
    58    const initialExpanded: Expanded = Object.keys(initialPoolList).reduce(
    59      (acc: { [scrapePool: string]: boolean }, scrapePool: string) => ({
    60        ...acc,
    61        [scrapePool]: true,
    62      }),
    63      {}
    64    );
    65    const [expanded, setExpanded] = useLocalStorage('targets-page-expansion-state', initialExpanded);
    66    const { showHealthy, showUnhealthy } = filter;
    67  
    68    const handleSearchChange = (e: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
    69      if (e.target.value !== '') {
    70        const result = kvSearch.filter(e.target.value.trim(), activeTargets);
    71        setTargetList(result.map((value) => value.original));
    72      } else {
    73        setTargetList(activeTargets);
    74      }
    75    };
    76  
    77    useEffect(() => {
    78      const list = targetList.filter((t) => showHealthy || t.health.toLowerCase() !== 'up');
    79      setPoolList(groupTargets(list));
    80    }, [showHealthy, targetList]);
    81  
    82    return (
    83      <>
    84        <Row xs="4" className="align-items-center">
    85          <Col>
    86            <Filter filter={filter} setFilter={setFilter} expanded={expanded} setExpanded={setExpanded} />
    87          </Col>
    88          <Col xs="6">
    89            <SearchBar handleChange={handleSearchChange} placeholder="Filter by endpoint or labels" />
    90          </Col>
    91        </Row>
    92        {Object.keys(poolList)
    93          .filter((scrapePool) => {
    94            const targetGroup = poolList[scrapePool];
    95            const isHealthy = targetGroup.upCount === targetGroup.targets.length;
    96            return (isHealthy && showHealthy) || (!isHealthy && showUnhealthy);
    97          })
    98          .map<JSX.Element>((scrapePool) => (
    99            <ScrapePoolPanel
   100              key={scrapePool}
   101              scrapePool={scrapePool}
   102              targetGroup={poolList[scrapePool]}
   103              expanded={expanded[scrapePool]}
   104              toggleExpanded={(): void => setExpanded({ ...expanded, [scrapePool]: !expanded[scrapePool] })}
   105            />
   106          ))}
   107      </>
   108    );
   109  };
   110  ScrapePoolListContent.displayName = 'ScrapePoolListContent';
   111  
   112  const ScrapePoolListWithStatusIndicator = withStatusIndicator(ScrapePoolListContent);
   113  
   114  const ScrapePoolList: FC<PathPrefixProps> = ({ pathPrefix }) => {
   115    const { response, error, isLoading } = useFetch<ScrapePoolListProps>(`${pathPrefix}/api/v1/targets?state=active`);
   116    const { status: responseStatus } = response;
   117    const badResponse = responseStatus !== 'success' && responseStatus !== 'start fetching';
   118    return (
   119      <ScrapePoolListWithStatusIndicator
   120        {...response.data}
   121        error={badResponse ? new Error(responseStatus) : error}
   122        isLoading={isLoading}
   123        componentTitle="Targets information"
   124      />
   125    );
   126  };
   127  
   128  export default ScrapePoolList;