1
0
mirror of https://github.com/checktheroads/hyperglass synced 2024-05-11 05:55:08 +00:00
Files
checktheroads-hyperglass/hyperglass/ui/components/Result.js

263 lines
7.4 KiB
JavaScript
Raw Normal View History

2020-05-29 17:47:53 -07:00
/** @jsx jsx */
2020-10-07 09:41:58 -07:00
import { jsx } from '@emotion/core';
import { forwardRef, useEffect, useState } from 'react';
2020-01-17 02:50:57 -07:00
import {
2020-04-18 12:09:16 -07:00
AccordionItem,
AccordionHeader,
AccordionPanel,
Alert,
Box,
ButtonGroup,
css,
Flex,
Tooltip,
Text,
2020-05-29 17:47:53 -07:00
useColorMode,
2020-10-07 09:41:58 -07:00
useTheme,
} from '@chakra-ui/core';
import { BsLightningFill } from '@meronex/icons/bs';
import styled from '@emotion/styled';
import useAxios from 'axios-hooks';
import strReplace from 'react-string-replace';
import format from 'string-format';
import { startCase } from 'lodash';
import { useConfig, useMedia } from 'app/context';
import {
BGPTable,
CacheTimeout,
CopyButton,
RequeryButton,
ResultHeader,
2020-10-07 09:41:58 -07:00
TextOutput,
} from 'app/components';
import { tableToString } from 'app/util';
2020-01-17 02:50:57 -07:00
2020-04-18 07:58:46 -07:00
format.extend(String.prototype, {});
2020-01-17 02:50:57 -07:00
const FormattedError = ({ keywords, message }) => {
2020-10-07 09:41:58 -07:00
const patternStr = keywords.map(kw => `(${kw})`).join('|');
const pattern = new RegExp(patternStr, 'gi');
2020-04-18 12:09:16 -07:00
let errorFmt;
try {
errorFmt = strReplace(message, pattern, match => (
<Text key={match} as="strong">
{match}
</Text>
));
} catch (err) {
errorFmt = <Text as="span">{message}</Text>;
}
return <Text as="span">{keywords.length !== 0 ? errorFmt : message}</Text>;
2020-01-17 02:50:57 -07:00
};
2020-01-20 00:37:04 -07:00
const AccordionHeaderWrapper = styled(Flex)`
2020-04-18 12:09:16 -07:00
justify-content: space-between;
&:hover {
background-color: ${props => props.hoverBg};
}
&:focus {
2020-10-07 09:41:58 -07:00
box-shadow: 'outline';
2020-04-18 12:09:16 -07:00
}
2020-01-20 00:37:04 -07:00
`;
2020-04-18 12:09:16 -07:00
const statusMap = {
2020-10-07 09:41:58 -07:00
success: 'success',
warning: 'warning',
error: 'warning',
danger: 'error',
2020-04-18 12:09:16 -07:00
};
2020-05-29 17:47:53 -07:00
2020-10-07 09:41:58 -07:00
const color = { dark: 'white', light: 'black' };
const scrollbar = { dark: 'whiteAlpha.300', light: 'blackAlpha.300' };
const scrollbarHover = { dark: 'whiteAlpha.400', light: 'blackAlpha.400' };
const scrollbarBg = { dark: 'whiteAlpha.50', light: 'blackAlpha.50' };
2020-01-26 02:24:12 -07:00
export const Result = forwardRef(
2020-04-18 12:09:16 -07:00
(
{
device,
timeout,
queryLocation,
queryType,
queryVrf,
queryTarget,
index,
resultsComplete,
2020-10-07 09:41:58 -07:00
setComplete,
2020-04-18 12:09:16 -07:00
},
2020-10-07 09:41:58 -07:00
ref,
2020-04-18 12:09:16 -07:00
) => {
const config = useConfig();
2020-05-29 17:47:53 -07:00
const theme = useTheme();
2020-04-18 12:09:16 -07:00
const { isSm } = useMedia();
const { colorMode } = useColorMode();
2020-04-19 09:50:31 -07:00
let [{ data, loading, error }, refetch] = useAxios({
2020-10-07 09:41:58 -07:00
url: '/api/query/',
method: 'post',
2020-04-18 12:09:16 -07:00
data: {
query_location: queryLocation,
query_type: queryType,
query_vrf: queryVrf,
2020-10-07 09:41:58 -07:00
query_target: queryTarget,
2020-04-18 12:09:16 -07:00
},
timeout: timeout,
2020-10-07 09:41:58 -07:00
useCache: false,
2020-04-18 12:09:16 -07:00
});
2020-03-22 11:43:14 -07:00
2020-04-18 12:09:16 -07:00
const [isOpen, setOpen] = useState(false);
const [hasOverride, setOverride] = useState(false);
2020-03-22 11:43:14 -07:00
2020-04-18 12:09:16 -07:00
const handleToggle = () => {
setOpen(!isOpen);
setOverride(true);
};
2020-01-17 02:50:57 -07:00
2020-04-18 12:09:16 -07:00
const errorKw = (error && error.response?.data?.keywords) || [];
2020-01-26 02:24:12 -07:00
2020-04-18 12:09:16 -07:00
let errorMsg;
if (error && error.response?.data?.output) {
errorMsg = error.response.data.output;
2020-10-07 09:41:58 -07:00
} else if (error && error.message.startsWith('timeout')) {
2020-04-18 12:09:16 -07:00
errorMsg = config.messages.request_timeout;
} else if (error?.response?.statusText) {
errorMsg = startCase(error.response.statusText);
} else if (error && error.message) {
errorMsg = startCase(error.message);
} else {
errorMsg = config.messages.general;
}
2020-01-26 02:24:12 -07:00
2020-04-19 09:50:31 -07:00
error && console.error(error);
2020-01-27 21:28:27 -07:00
2020-04-18 12:09:16 -07:00
const errorLevel =
2020-10-07 09:41:58 -07:00
(error?.response?.data?.level && statusMap[error.response?.data?.level]) ?? 'error';
2020-01-26 02:24:12 -07:00
const structuredDataComponent = {
bgp_route: BGPTable,
bgp_aspath: BGPTable,
bgp_community: BGPTable,
ping: TextOutput,
2020-10-07 09:41:58 -07:00
traceroute: TextOutput,
};
2020-05-29 17:47:53 -07:00
let Output = TextOutput;
let copyValue = data?.output;
2020-10-07 09:41:58 -07:00
if (data?.format === 'application/json') {
2020-05-29 17:47:53 -07:00
Output = structuredDataComponent[queryType];
copyValue = tableToString(queryTarget, data, config);
2020-05-29 17:47:53 -07:00
}
2020-07-23 09:20:15 -07:00
if (error) {
copyValue = errorMsg;
}
2020-04-18 12:09:16 -07:00
useEffect(() => {
!loading && resultsComplete === null && setComplete(index);
}, [loading, resultsComplete]);
2020-03-22 11:43:14 -07:00
2020-04-18 12:09:16 -07:00
useEffect(() => {
resultsComplete === index && !hasOverride && setOpen(true);
}, [resultsComplete, index]);
2020-04-19 09:50:31 -07:00
2020-04-18 12:09:16 -07:00
return (
<AccordionItem
isOpen={isOpen}
isDisabled={loading}
ref={ref}
css={css({
2020-10-07 09:41:58 -07:00
'&:last-of-type': { borderBottom: 'none' },
'&:first-of-type': { borderTop: 'none' },
})(theme)}>
2020-04-18 12:09:16 -07:00
<AccordionHeaderWrapper hoverBg="blackAlpha.50">
<AccordionHeader
flex="1 0 auto"
py={2}
_hover={{}}
_focus={{}}
w="unset"
2020-10-07 09:41:58 -07:00
onClick={handleToggle}>
2020-04-18 12:09:16 -07:00
<ResultHeader
title={device.display_name}
loading={loading}
error={error}
errorMsg={errorMsg}
errorLevel={errorLevel}
runtime={data?.runtime}
/>
</AccordionHeader>
2020-05-29 17:47:53 -07:00
<ButtonGroup px={[1, 1, 3, 3]} py={2}>
2020-10-07 09:41:58 -07:00
<CopyButton copyValue={copyValue} variant="ghost" isDisabled={loading} />
<RequeryButton requery={refetch} variant="ghost" isDisabled={loading} />
2020-04-18 12:09:16 -07:00
</ButtonGroup>
</AccordionHeaderWrapper>
<AccordionPanel
pb={4}
overflowX="auto"
2020-05-29 17:47:53 -07:00
css={css({
2020-10-07 09:41:58 -07:00
WebkitOverflowScrolling: 'touch',
'&::-webkit-scrollbar': { height: '5px' },
'&::-webkit-scrollbar-track': {
backgroundColor: scrollbarBg[colorMode],
2020-05-29 17:47:53 -07:00
},
2020-10-07 09:41:58 -07:00
'&::-webkit-scrollbar-thumb': {
backgroundColor: scrollbar[colorMode],
2020-05-29 17:47:53 -07:00
},
2020-10-07 09:41:58 -07:00
'&::-webkit-scrollbar-thumb:hover': {
backgroundColor: scrollbarHover[colorMode],
2020-05-29 17:47:53 -07:00
},
2020-10-07 09:41:58 -07:00
'-ms-overflow-style': { display: 'none' },
})(theme)}>
2020-06-06 01:25:42 -07:00
<Flex direction="column" flexWrap="wrap">
2020-10-07 09:41:58 -07:00
<Flex direction="column" flex="1 0 auto" maxW={error ? '100%' : null}>
2020-05-29 17:47:53 -07:00
{!error && data && <Output>{data?.output}</Output>}
2020-04-18 12:09:16 -07:00
{error && (
<Alert rounded="lg" my={2} py={4} status={errorLevel}>
<FormattedError keywords={errorKw} message={errorMsg} />
</Alert>
)}
</Flex>
</Flex>
2020-03-23 01:10:27 -07:00
2020-04-18 12:09:16 -07:00
<Flex direction="row" flexWrap="wrap">
<Flex
px={3}
mt={2}
2020-10-07 09:41:58 -07:00
justifyContent={['flex-start', 'flex-start', 'flex-end', 'flex-end']}
flex="1 0 auto">
2020-04-19 09:50:31 -07:00
{config.cache.show_text && data && !error && (
<>
{!isSm && (
<CacheTimeout
timeout={config.cache.timeout}
text={config.web.text.cache_prefix}
/>
)}
<Tooltip
2020-10-07 09:41:58 -07:00
display={data?.cached ? null : 'none'}
2020-04-19 09:50:31 -07:00
hasArrow
label={config.web.text.cache_icon.format({
2020-10-07 09:41:58 -07:00
time: data?.timestamp,
2020-04-19 09:50:31 -07:00
})}
2020-10-07 09:41:58 -07:00
placement="top">
<Box ml={1} display={data?.cached ? 'block' : 'none'}>
<BsLightningFill color={color[colorMode]} />
2020-04-19 09:50:31 -07:00
</Box>
</Tooltip>
{isSm && (
<CacheTimeout
timeout={config.cache.timeout}
text={config.web.text.cache_prefix}
/>
)}
</>
)}
2020-04-18 12:09:16 -07:00
</Flex>
</Flex>
</AccordionPanel>
</AccordionItem>
);
2020-10-07 09:41:58 -07:00
},
2020-01-17 02:50:57 -07:00
);