1
import React, { ChangeEvent, FC, useState } from 'react';
2
import { Input, InputGroup, Table } from 'reactstrap';
3
import { withStatusIndicator } from '../../components/withStatusIndicator';
4
import { useFetch } from '../../hooks/useFetch';
5
import { usePathPrefix } from '../../contexts/PathPrefixContext';
6
import { API_PATH } from '../../constants/constants';
7
import { faSort, faSortDown, faSortUp } from '@fortawesome/free-solid-svg-icons';
8
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
9
import { IconDefinition } from '@fortawesome/fontawesome-common-types';
10
import sanitizeHTML from 'sanitize-html';
11
import { Fuzzy, FuzzyResult } from '@nexucis/fuzzy';
13
const fuz = new Fuzzy({ pre: '<strong>', post: '</strong>', shouldSort: true });
14
const flagSeparator = '||';
17
[key: string]: string;
25
(keys: boolean, reverse: boolean) =>
26
([k1, v1]: [string, string], [k2, v2]: [string, string]): number => {
27
const a = keys ? k1 : v1;
28
const b = keys ? k2 : v2;
29
const reverser = reverse ? -1 : 1;
30
return reverser * a.localeCompare(b);
33
const getSortIcon = (b: boolean | undefined): IconDefinition => {
34
if (b === undefined) {
49
export const FlagsContent: FC<FlagsProps> = ({ data = {} }) => {
50
const initialSearch = '';
51
const [searchState, setSearchState] = useState(initialSearch);
52
const initialSort: SortState = {
57
const [sortState, setSortState] = useState(initialSort);
58
const searchable = Object.entries(data)
59
.sort(compareAlphaFn(sortState.name === 'Flag', !sortState.alpha))
60
.map(([flag, value]) => `--${flag}${flagSeparator}${value}`);
61
let filtered = searchable;
62
if (searchState.length > 0) {
63
filtered = fuz.filter(searchState, searchable).map((value: FuzzyResult) => value.rendered);
67
<h2>Command-Line Flags</h2>
71
placeholder="Filter by flag name or value..."
74
onChange={({ target }: ChangeEvent<HTMLInputElement>): void => {
75
setSearchState(target.value);
79
<Table bordered size="sm" striped hover>
82
{['Flag', 'Value'].map((col: string) => (
85
className={`px-4 ${col}`}
86
style={{ width: '50%' }}
91
alpha: sortState.name === col ? !sortState.alpha : true,
95
<span className="mr-2">{col}</span>
96
<FontAwesomeIcon icon={getSortIcon(sortState.name !== col ? undefined : sortState.alpha)} />
102
{filtered.map((result: string) => {
103
const [flagMatchStr, valueMatchStr] = result.split(flagSeparator);
104
const sanitizeOpts = { allowedTags: ['strong'] };
106
<tr key={flagMatchStr}>
107
<td className="flag-item">
108
<span dangerouslySetInnerHTML={{ __html: sanitizeHTML(flagMatchStr, sanitizeOpts) }} />
110
<td className="flag-value">
111
<span dangerouslySetInnerHTML={{ __html: sanitizeHTML(valueMatchStr, sanitizeOpts) }} />
121
const FlagsWithStatusIndicator = withStatusIndicator(FlagsContent);
123
FlagsContent.displayName = 'Flags';
125
const Flags: FC = () => {
126
const pathPrefix = usePathPrefix();
127
const { response, error, isLoading } = useFetch<FlagMap>(`${pathPrefix}/${API_PATH}/status/flags`);
128
return <FlagsWithStatusIndicator data={response.data} error={error} isLoading={isLoading} />;