mirror of
				https://github.com/checktheroads/hyperglass
				synced 2024-05-11 05:55:08 +00:00 
			
		
		
		
	fix cache UI issue
This commit is contained in:
		@@ -139,7 +139,7 @@ class Text(HyperglassModel):
 | 
			
		||||
    query_vrf: StrictStr = "Routing Table"
 | 
			
		||||
    fqdn_tooltip: StrictStr = "Use {protocol}"  # Formatted by Javascript
 | 
			
		||||
    cache_prefix: StrictStr = "Results cached for "
 | 
			
		||||
    cache_icon: StrictStr = "Cached Response from {time}"  # Formatted by Javascript
 | 
			
		||||
    cache_icon: StrictStr = "Cached Response from {time} UTC"  # Formatted by Javascript
 | 
			
		||||
    complete_time: StrictStr = "Completed in {seconds}"  # Formatted by Javascript
 | 
			
		||||
 | 
			
		||||
    @validator("title_mode")
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
 | 
			
		||||
__name__ = "hyperglass"
 | 
			
		||||
__version__ = "1.0.0-beta.26"
 | 
			
		||||
__version__ = "1.0.0-beta.27"
 | 
			
		||||
__author__ = "Matt Love"
 | 
			
		||||
__copyright__ = f"Copyright {datetime.now().year} Matthew Love"
 | 
			
		||||
__license__ = "BSD 3-Clause Clear License"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,16 @@
 | 
			
		||||
import React, { useEffect, useState } from "react";
 | 
			
		||||
import {
 | 
			
		||||
    AccordionItem,
 | 
			
		||||
    AccordionHeader,
 | 
			
		||||
    AccordionPanel,
 | 
			
		||||
    Alert,
 | 
			
		||||
    Box,
 | 
			
		||||
    ButtonGroup,
 | 
			
		||||
    css,
 | 
			
		||||
    Flex,
 | 
			
		||||
    Tooltip,
 | 
			
		||||
    Text,
 | 
			
		||||
    useColorMode,
 | 
			
		||||
  AccordionItem,
 | 
			
		||||
  AccordionHeader,
 | 
			
		||||
  AccordionPanel,
 | 
			
		||||
  Alert,
 | 
			
		||||
  Box,
 | 
			
		||||
  ButtonGroup,
 | 
			
		||||
  css,
 | 
			
		||||
  Flex,
 | 
			
		||||
  Tooltip,
 | 
			
		||||
  Text,
 | 
			
		||||
  useColorMode
 | 
			
		||||
} from "@chakra-ui/core";
 | 
			
		||||
import styled from "@emotion/styled";
 | 
			
		||||
import LightningBolt from "~/components/icons/LightningBolt";
 | 
			
		||||
@@ -28,230 +28,301 @@ import CacheTimeout from "~/components/CacheTimeout";
 | 
			
		||||
format.extend(String.prototype, {});
 | 
			
		||||
 | 
			
		||||
const FormattedError = ({ keywords, message }) => {
 | 
			
		||||
    const patternStr = keywords.map((kw) => `(${kw})`).join("|");
 | 
			
		||||
    const pattern = new RegExp(patternStr, "gi");
 | 
			
		||||
    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>;
 | 
			
		||||
  const patternStr = keywords.map(kw => `(${kw})`).join("|");
 | 
			
		||||
  const pattern = new RegExp(patternStr, "gi");
 | 
			
		||||
  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>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const AccordionHeaderWrapper = styled(Flex)`
 | 
			
		||||
    justify-content: space-between;
 | 
			
		||||
    &:hover {
 | 
			
		||||
        background-color: ${(props) => props.hoverBg};
 | 
			
		||||
    }
 | 
			
		||||
    &:focus {
 | 
			
		||||
        box-shadow: "outline";
 | 
			
		||||
    }
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
  &:hover {
 | 
			
		||||
    background-color: ${props => props.hoverBg};
 | 
			
		||||
  }
 | 
			
		||||
  &:focus {
 | 
			
		||||
    box-shadow: "outline";
 | 
			
		||||
  }
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
const statusMap = { success: "success", warning: "warning", error: "warning", danger: "error" };
 | 
			
		||||
const statusMap = {
 | 
			
		||||
  success: "success",
 | 
			
		||||
  warning: "warning",
 | 
			
		||||
  error: "warning",
 | 
			
		||||
  danger: "error"
 | 
			
		||||
};
 | 
			
		||||
const bg = { dark: "gray.800", light: "blackAlpha.100" };
 | 
			
		||||
const color = { dark: "white", light: "black" };
 | 
			
		||||
const selectionBg = { dark: "white", light: "black" };
 | 
			
		||||
const selectionColor = { dark: "black", light: "white" };
 | 
			
		||||
 | 
			
		||||
const Result = React.forwardRef(
 | 
			
		||||
    (
 | 
			
		||||
        {
 | 
			
		||||
            device,
 | 
			
		||||
            timeout,
 | 
			
		||||
            queryLocation,
 | 
			
		||||
            queryType,
 | 
			
		||||
            queryVrf,
 | 
			
		||||
            queryTarget,
 | 
			
		||||
            index,
 | 
			
		||||
            resultsComplete,
 | 
			
		||||
            setComplete,
 | 
			
		||||
        },
 | 
			
		||||
        ref
 | 
			
		||||
    ) => {
 | 
			
		||||
        const config = useConfig();
 | 
			
		||||
        const { isSm } = useMedia();
 | 
			
		||||
        const { colorMode } = useColorMode();
 | 
			
		||||
        const [{ data, loading, error }, refetch] = useAxios({
 | 
			
		||||
            url: "/api/query/",
 | 
			
		||||
            method: "post",
 | 
			
		||||
            data: {
 | 
			
		||||
                query_location: queryLocation,
 | 
			
		||||
                query_type: queryType,
 | 
			
		||||
                query_vrf: queryVrf,
 | 
			
		||||
                query_target: queryTarget,
 | 
			
		||||
            },
 | 
			
		||||
            timeout: timeout,
 | 
			
		||||
            useCache: false,
 | 
			
		||||
        });
 | 
			
		||||
  (
 | 
			
		||||
    {
 | 
			
		||||
      device,
 | 
			
		||||
      timeout,
 | 
			
		||||
      queryLocation,
 | 
			
		||||
      queryType,
 | 
			
		||||
      queryVrf,
 | 
			
		||||
      queryTarget,
 | 
			
		||||
      index,
 | 
			
		||||
      resultsComplete,
 | 
			
		||||
      setComplete
 | 
			
		||||
    },
 | 
			
		||||
    ref
 | 
			
		||||
  ) => {
 | 
			
		||||
    const config = useConfig();
 | 
			
		||||
    const { isSm } = useMedia();
 | 
			
		||||
    const { colorMode } = useColorMode();
 | 
			
		||||
    const [{ data, loading, error }, refetch] = useAxios({
 | 
			
		||||
      url: "/api/query/",
 | 
			
		||||
      method: "post",
 | 
			
		||||
      data: {
 | 
			
		||||
        query_location: queryLocation,
 | 
			
		||||
        query_type: queryType,
 | 
			
		||||
        query_vrf: queryVrf,
 | 
			
		||||
        query_target: queryTarget
 | 
			
		||||
      },
 | 
			
		||||
      timeout: timeout,
 | 
			
		||||
      useCache: false
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
        const [isOpen, setOpen] = useState(false);
 | 
			
		||||
        const [hasOverride, setOverride] = useState(false);
 | 
			
		||||
    const [isOpen, setOpen] = useState(false);
 | 
			
		||||
    const [hasOverride, setOverride] = useState(false);
 | 
			
		||||
 | 
			
		||||
        const handleToggle = () => {
 | 
			
		||||
            setOpen(!isOpen);
 | 
			
		||||
            setOverride(true);
 | 
			
		||||
        };
 | 
			
		||||
        const cleanOutput =
 | 
			
		||||
            data &&
 | 
			
		||||
            data.output
 | 
			
		||||
                .split("\\n")
 | 
			
		||||
                .join("\n")
 | 
			
		||||
                .replace(/\n\n/g, "\n");
 | 
			
		||||
    const handleToggle = () => {
 | 
			
		||||
      setOpen(!isOpen);
 | 
			
		||||
      setOverride(true);
 | 
			
		||||
    };
 | 
			
		||||
    const cleanOutput =
 | 
			
		||||
      data &&
 | 
			
		||||
      data.output
 | 
			
		||||
        .split("\\n")
 | 
			
		||||
        .join("\n")
 | 
			
		||||
        .replace(/\n\n/g, "\n");
 | 
			
		||||
 | 
			
		||||
        const errorKw = (error && error.response?.data?.keywords) || [];
 | 
			
		||||
    const errorKw = (error && error.response?.data?.keywords) || [];
 | 
			
		||||
 | 
			
		||||
        let errorMsg;
 | 
			
		||||
        if (error && error.response?.data?.output) {
 | 
			
		||||
            errorMsg = error.response.data.output;
 | 
			
		||||
        } else if (error && error.message.startsWith("timeout")) {
 | 
			
		||||
            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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        error && console.dir(error);
 | 
			
		||||
 | 
			
		||||
        const errorLevel =
 | 
			
		||||
            (error?.response?.data?.level && statusMap[error.response?.data?.level]) ?? "error";
 | 
			
		||||
 | 
			
		||||
        const cacheLg = (
 | 
			
		||||
            <>
 | 
			
		||||
                <CacheTimeout timeout={config.cache.timeout} text={config.web.text.cache_prefix} />
 | 
			
		||||
                {data?.cached && (
 | 
			
		||||
                    <Tooltip
 | 
			
		||||
                        hasArrow
 | 
			
		||||
                        label={config.web.text.cache_icon.format({ time: data?.timestamp })}
 | 
			
		||||
                        placement="top"
 | 
			
		||||
                    >
 | 
			
		||||
                        <Box ml={1}>
 | 
			
		||||
                            <LightningBolt color={color[colorMode]} />
 | 
			
		||||
                        </Box>
 | 
			
		||||
                    </Tooltip>
 | 
			
		||||
                )}
 | 
			
		||||
            </>
 | 
			
		||||
        );
 | 
			
		||||
        const cacheSm = (
 | 
			
		||||
            <>
 | 
			
		||||
                {data?.cached && (
 | 
			
		||||
                    <Tooltip
 | 
			
		||||
                        hasArrow
 | 
			
		||||
                        label={config.web.text.cache_icon.format({ time: data?.timestamp })}
 | 
			
		||||
                        placement="top"
 | 
			
		||||
                    >
 | 
			
		||||
                        <Box mr={1}>
 | 
			
		||||
                            <LightningBolt color={color[colorMode]} />
 | 
			
		||||
                        </Box>
 | 
			
		||||
                    </Tooltip>
 | 
			
		||||
                )}
 | 
			
		||||
                <CacheTimeout timeout={config.cache.timeout} text={config.web.text.cache_prefix} />
 | 
			
		||||
            </>
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const cacheData = isSm ? cacheSm : cacheLg;
 | 
			
		||||
 | 
			
		||||
        useEffect(() => {
 | 
			
		||||
            !loading && resultsComplete === null && setComplete(index);
 | 
			
		||||
        }, [loading, resultsComplete]);
 | 
			
		||||
 | 
			
		||||
        useEffect(() => {
 | 
			
		||||
            resultsComplete === index && !hasOverride && setOpen(true);
 | 
			
		||||
        }, [resultsComplete, index]);
 | 
			
		||||
        return (
 | 
			
		||||
            <AccordionItem
 | 
			
		||||
                isOpen={isOpen}
 | 
			
		||||
                isDisabled={loading}
 | 
			
		||||
                ref={ref}
 | 
			
		||||
                css={css({
 | 
			
		||||
                    "&:last-of-type": { borderBottom: "none" },
 | 
			
		||||
                    "&:first-of-type": { borderTop: "none" },
 | 
			
		||||
                })}
 | 
			
		||||
            >
 | 
			
		||||
                <AccordionHeaderWrapper hoverBg="blackAlpha.50">
 | 
			
		||||
                    <AccordionHeader
 | 
			
		||||
                        flex="1 0 auto"
 | 
			
		||||
                        py={2}
 | 
			
		||||
                        _hover={{}}
 | 
			
		||||
                        _focus={{}}
 | 
			
		||||
                        w="unset"
 | 
			
		||||
                        onClick={handleToggle}
 | 
			
		||||
                    >
 | 
			
		||||
                        <ResultHeader
 | 
			
		||||
                            title={device.display_name}
 | 
			
		||||
                            loading={loading}
 | 
			
		||||
                            error={error}
 | 
			
		||||
                            errorMsg={errorMsg}
 | 
			
		||||
                            errorLevel={errorLevel}
 | 
			
		||||
                            runtime={data?.runtime}
 | 
			
		||||
                        />
 | 
			
		||||
                    </AccordionHeader>
 | 
			
		||||
                    <ButtonGroup px={3} py={2}>
 | 
			
		||||
                        <CopyButton copyValue={cleanOutput} variant="ghost" isDisabled={loading} />
 | 
			
		||||
                        <RequeryButton requery={refetch} variant="ghost" isDisabled={loading} />
 | 
			
		||||
                    </ButtonGroup>
 | 
			
		||||
                </AccordionHeaderWrapper>
 | 
			
		||||
                <AccordionPanel
 | 
			
		||||
                    pb={4}
 | 
			
		||||
                    overflowX="auto"
 | 
			
		||||
                    css={css({ WebkitOverflowScrolling: "touch" })}
 | 
			
		||||
                >
 | 
			
		||||
                    <Flex direction="row" flexWrap="wrap">
 | 
			
		||||
                        <Flex direction="column" flex="1 0 auto" maxW={error ? "100%" : null}>
 | 
			
		||||
                            {data && !error && (
 | 
			
		||||
                                <Box
 | 
			
		||||
                                    fontFamily="mono"
 | 
			
		||||
                                    mt={5}
 | 
			
		||||
                                    mx={2}
 | 
			
		||||
                                    p={3}
 | 
			
		||||
                                    border="1px"
 | 
			
		||||
                                    borderColor="inherit"
 | 
			
		||||
                                    rounded="md"
 | 
			
		||||
                                    bg={bg[colorMode]}
 | 
			
		||||
                                    color={color[colorMode]}
 | 
			
		||||
                                    fontSize="sm"
 | 
			
		||||
                                    whiteSpace="pre-wrap"
 | 
			
		||||
                                    as="pre"
 | 
			
		||||
                                    css={css({
 | 
			
		||||
                                        "&::selection": {
 | 
			
		||||
                                            backgroundColor: selectionBg[colorMode],
 | 
			
		||||
                                            color: selectionColor[colorMode],
 | 
			
		||||
                                        },
 | 
			
		||||
                                    })}
 | 
			
		||||
                                >
 | 
			
		||||
                                    {cleanOutput}
 | 
			
		||||
                                </Box>
 | 
			
		||||
                            )}
 | 
			
		||||
                            {error && (
 | 
			
		||||
                                <Alert rounded="lg" my={2} py={4} status={errorLevel}>
 | 
			
		||||
                                    <FormattedError keywords={errorKw} message={errorMsg} />
 | 
			
		||||
                                </Alert>
 | 
			
		||||
                            )}
 | 
			
		||||
                        </Flex>
 | 
			
		||||
                    </Flex>
 | 
			
		||||
 | 
			
		||||
                    <Flex direction="row" flexWrap="wrap">
 | 
			
		||||
                        <Flex
 | 
			
		||||
                            px={3}
 | 
			
		||||
                            mt={2}
 | 
			
		||||
                            justifyContent={["flex-start", "flex-start", "flex-end", "flex-end"]}
 | 
			
		||||
                            flex="1 0 auto"
 | 
			
		||||
                        >
 | 
			
		||||
                            {data && !error && config.cache.show_text && cacheData}
 | 
			
		||||
                        </Flex>
 | 
			
		||||
                    </Flex>
 | 
			
		||||
                </AccordionPanel>
 | 
			
		||||
            </AccordionItem>
 | 
			
		||||
        );
 | 
			
		||||
    let errorMsg;
 | 
			
		||||
    if (error && error.response?.data?.output) {
 | 
			
		||||
      errorMsg = error.response.data.output;
 | 
			
		||||
    } else if (error && error.message.startsWith("timeout")) {
 | 
			
		||||
      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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    error && console.dir(error);
 | 
			
		||||
 | 
			
		||||
    const errorLevel =
 | 
			
		||||
      (error?.response?.data?.level &&
 | 
			
		||||
        statusMap[error.response?.data?.level]) ??
 | 
			
		||||
      "error";
 | 
			
		||||
 | 
			
		||||
    const cacheLg = (
 | 
			
		||||
      <>
 | 
			
		||||
        <CacheTimeout
 | 
			
		||||
          timeout={config.cache.timeout}
 | 
			
		||||
          text={config.web.text.cache_prefix}
 | 
			
		||||
        />
 | 
			
		||||
        {data?.cached && (
 | 
			
		||||
          <Tooltip
 | 
			
		||||
            hasArrow
 | 
			
		||||
            label={config.web.text.cache_icon.format({ time: data?.timestamp })}
 | 
			
		||||
            placement="top"
 | 
			
		||||
          >
 | 
			
		||||
            <Box ml={1}>
 | 
			
		||||
              <LightningBolt color={color[colorMode]} />
 | 
			
		||||
            </Box>
 | 
			
		||||
          </Tooltip>
 | 
			
		||||
        )}
 | 
			
		||||
      </>
 | 
			
		||||
    );
 | 
			
		||||
    const cacheSm = (
 | 
			
		||||
      <>
 | 
			
		||||
        {data?.cached && (
 | 
			
		||||
          <Tooltip
 | 
			
		||||
            hasArrow
 | 
			
		||||
            label={config.web.text.cache_icon.format({ time: data?.timestamp })}
 | 
			
		||||
            placement="top"
 | 
			
		||||
          >
 | 
			
		||||
            <Box mr={1}>
 | 
			
		||||
              <LightningBolt color={color[colorMode]} />
 | 
			
		||||
            </Box>
 | 
			
		||||
          </Tooltip>
 | 
			
		||||
        )}
 | 
			
		||||
        <CacheTimeout
 | 
			
		||||
          timeout={config.cache.timeout}
 | 
			
		||||
          text={config.web.text.cache_prefix}
 | 
			
		||||
        />
 | 
			
		||||
      </>
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const cacheData = isSm ? cacheSm : cacheLg;
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
      !loading && resultsComplete === null && setComplete(index);
 | 
			
		||||
    }, [loading, resultsComplete]);
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
      resultsComplete === index && !hasOverride && setOpen(true);
 | 
			
		||||
    }, [resultsComplete, index]);
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
      data && console.log(data);
 | 
			
		||||
    });
 | 
			
		||||
    return (
 | 
			
		||||
      <AccordionItem
 | 
			
		||||
        isOpen={isOpen}
 | 
			
		||||
        isDisabled={loading}
 | 
			
		||||
        ref={ref}
 | 
			
		||||
        css={css({
 | 
			
		||||
          "&:last-of-type": { borderBottom: "none" },
 | 
			
		||||
          "&:first-of-type": { borderTop: "none" }
 | 
			
		||||
        })}
 | 
			
		||||
      >
 | 
			
		||||
        <AccordionHeaderWrapper hoverBg="blackAlpha.50">
 | 
			
		||||
          <AccordionHeader
 | 
			
		||||
            flex="1 0 auto"
 | 
			
		||||
            py={2}
 | 
			
		||||
            _hover={{}}
 | 
			
		||||
            _focus={{}}
 | 
			
		||||
            w="unset"
 | 
			
		||||
            onClick={handleToggle}
 | 
			
		||||
          >
 | 
			
		||||
            <ResultHeader
 | 
			
		||||
              title={device.display_name}
 | 
			
		||||
              loading={loading}
 | 
			
		||||
              error={error}
 | 
			
		||||
              errorMsg={errorMsg}
 | 
			
		||||
              errorLevel={errorLevel}
 | 
			
		||||
              runtime={data?.runtime}
 | 
			
		||||
            />
 | 
			
		||||
          </AccordionHeader>
 | 
			
		||||
          <ButtonGroup px={3} py={2}>
 | 
			
		||||
            <CopyButton
 | 
			
		||||
              copyValue={cleanOutput}
 | 
			
		||||
              variant="ghost"
 | 
			
		||||
              isDisabled={loading}
 | 
			
		||||
            />
 | 
			
		||||
            <RequeryButton
 | 
			
		||||
              requery={refetch}
 | 
			
		||||
              variant="ghost"
 | 
			
		||||
              isDisabled={loading}
 | 
			
		||||
            />
 | 
			
		||||
          </ButtonGroup>
 | 
			
		||||
        </AccordionHeaderWrapper>
 | 
			
		||||
        <AccordionPanel
 | 
			
		||||
          pb={4}
 | 
			
		||||
          overflowX="auto"
 | 
			
		||||
          css={css({ WebkitOverflowScrolling: "touch" })}
 | 
			
		||||
        >
 | 
			
		||||
          <Flex direction="row" flexWrap="wrap">
 | 
			
		||||
            <Flex
 | 
			
		||||
              direction="column"
 | 
			
		||||
              flex="1 0 auto"
 | 
			
		||||
              maxW={error ? "100%" : null}
 | 
			
		||||
            >
 | 
			
		||||
              {data && !error && (
 | 
			
		||||
                <Box
 | 
			
		||||
                  fontFamily="mono"
 | 
			
		||||
                  mt={5}
 | 
			
		||||
                  mx={2}
 | 
			
		||||
                  p={3}
 | 
			
		||||
                  border="1px"
 | 
			
		||||
                  borderColor="inherit"
 | 
			
		||||
                  rounded="md"
 | 
			
		||||
                  bg={bg[colorMode]}
 | 
			
		||||
                  color={color[colorMode]}
 | 
			
		||||
                  fontSize="sm"
 | 
			
		||||
                  whiteSpace="pre-wrap"
 | 
			
		||||
                  as="pre"
 | 
			
		||||
                  css={css({
 | 
			
		||||
                    "&::selection": {
 | 
			
		||||
                      backgroundColor: selectionBg[colorMode],
 | 
			
		||||
                      color: selectionColor[colorMode]
 | 
			
		||||
                    }
 | 
			
		||||
                  })}
 | 
			
		||||
                >
 | 
			
		||||
                  {cleanOutput}
 | 
			
		||||
                </Box>
 | 
			
		||||
              )}
 | 
			
		||||
              {error && (
 | 
			
		||||
                <Alert rounded="lg" my={2} py={4} status={errorLevel}>
 | 
			
		||||
                  <FormattedError keywords={errorKw} message={errorMsg} />
 | 
			
		||||
                </Alert>
 | 
			
		||||
              )}
 | 
			
		||||
            </Flex>
 | 
			
		||||
          </Flex>
 | 
			
		||||
 | 
			
		||||
          <Flex direction="row" flexWrap="wrap">
 | 
			
		||||
            <Flex
 | 
			
		||||
              px={3}
 | 
			
		||||
              mt={2}
 | 
			
		||||
              justifyContent={[
 | 
			
		||||
                "flex-start",
 | 
			
		||||
                "flex-start",
 | 
			
		||||
                "flex-end",
 | 
			
		||||
                "flex-end"
 | 
			
		||||
              ]}
 | 
			
		||||
              flex="1 0 auto"
 | 
			
		||||
            >
 | 
			
		||||
              {data && !error && config.cache.show_text && isSm ? (
 | 
			
		||||
                <>
 | 
			
		||||
                  <Tooltip
 | 
			
		||||
                    display={data?.cached ? null : "none"}
 | 
			
		||||
                    hasArrow
 | 
			
		||||
                    label={config.web.text.cache_icon.format({
 | 
			
		||||
                      time: data?.timestamp
 | 
			
		||||
                    })}
 | 
			
		||||
                    placement="top"
 | 
			
		||||
                  >
 | 
			
		||||
                    <Box mr={1} display={data?.cached ? null : "none"}>
 | 
			
		||||
                      <LightningBolt color={color[colorMode]} />
 | 
			
		||||
                    </Box>
 | 
			
		||||
                  </Tooltip>
 | 
			
		||||
                  <CacheTimeout
 | 
			
		||||
                    timeout={config.cache.timeout}
 | 
			
		||||
                    text={config.web.text.cache_prefix}
 | 
			
		||||
                  />
 | 
			
		||||
                </>
 | 
			
		||||
              ) : data && !error && config.cache.show_text ? (
 | 
			
		||||
                <>
 | 
			
		||||
                  <CacheTimeout
 | 
			
		||||
                    timeout={config.cache.timeout}
 | 
			
		||||
                    text={config.web.text.cache_prefix}
 | 
			
		||||
                  />
 | 
			
		||||
                  <Tooltip
 | 
			
		||||
                    display={data?.cached ? null : "none"}
 | 
			
		||||
                    hasArrow
 | 
			
		||||
                    label={config.web.text.cache_icon.format({
 | 
			
		||||
                      time: data?.timestamp
 | 
			
		||||
                    })}
 | 
			
		||||
                    placement="top"
 | 
			
		||||
                  >
 | 
			
		||||
                    <Box ml={1} display={data?.cached ? null : "none"}>
 | 
			
		||||
                      <LightningBolt color={color[colorMode]} />
 | 
			
		||||
                    </Box>
 | 
			
		||||
                  </Tooltip>
 | 
			
		||||
                </>
 | 
			
		||||
              ) : null}
 | 
			
		||||
            </Flex>
 | 
			
		||||
          </Flex>
 | 
			
		||||
        </AccordionPanel>
 | 
			
		||||
      </AccordionItem>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
Result.displayName = "HyperglassQueryResult";
 | 
			
		||||
 
 | 
			
		||||
@@ -1,55 +1,70 @@
 | 
			
		||||
import React from "react";
 | 
			
		||||
import { AccordionIcon, Icon, Spinner, Stack, Text, Tooltip, useColorMode } from "@chakra-ui/core";
 | 
			
		||||
import {
 | 
			
		||||
  AccordionIcon,
 | 
			
		||||
  Icon,
 | 
			
		||||
  Spinner,
 | 
			
		||||
  Stack,
 | 
			
		||||
  Text,
 | 
			
		||||
  Tooltip,
 | 
			
		||||
  useColorMode
 | 
			
		||||
} from "@chakra-ui/core";
 | 
			
		||||
import format from "string-format";
 | 
			
		||||
import useConfig from "~/components/HyperglassProvider";
 | 
			
		||||
 | 
			
		||||
format.extend(String.prototype, {});
 | 
			
		||||
 | 
			
		||||
const runtimeText = (runtime, text) => {
 | 
			
		||||
    let unit;
 | 
			
		||||
    if (runtime === 1) {
 | 
			
		||||
        unit = "seconds";
 | 
			
		||||
    } else {
 | 
			
		||||
        unit = "second";
 | 
			
		||||
    }
 | 
			
		||||
    const fmt = text.format({ seconds: runtime });
 | 
			
		||||
    return `${fmt} ${unit}`;
 | 
			
		||||
  let unit;
 | 
			
		||||
  if (runtime === 1) {
 | 
			
		||||
    unit = "second";
 | 
			
		||||
  } else {
 | 
			
		||||
    unit = "seconds";
 | 
			
		||||
  }
 | 
			
		||||
  const fmt = text.format({ seconds: runtime });
 | 
			
		||||
  return `${fmt} ${unit}`;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const statusColor = { dark: "primary.300", light: "primary.500" };
 | 
			
		||||
const warningColor = { dark: 300, light: 500 };
 | 
			
		||||
const defaultStatusColor = {
 | 
			
		||||
    dark: "success.300",
 | 
			
		||||
    light: "success.500",
 | 
			
		||||
  dark: "success.300",
 | 
			
		||||
  light: "success.500"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default React.forwardRef(({ title, loading, error, errorMsg, errorLevel, runtime }, ref) => {
 | 
			
		||||
export default React.forwardRef(
 | 
			
		||||
  ({ title, loading, error, errorMsg, errorLevel, runtime }, ref) => {
 | 
			
		||||
    const { colorMode } = useColorMode();
 | 
			
		||||
    const config = useConfig();
 | 
			
		||||
    return (
 | 
			
		||||
        <Stack ref={ref} isInline alignItems="center" w="100%">
 | 
			
		||||
            {loading ? (
 | 
			
		||||
                <Spinner size="sm" mr={4} color={statusColor[colorMode]} />
 | 
			
		||||
            ) : error ? (
 | 
			
		||||
                <Tooltip hasArrow label={errorMsg} placement="top">
 | 
			
		||||
                    <Icon
 | 
			
		||||
                        name="warning"
 | 
			
		||||
                        color={`${errorLevel}.${warningColor[colorMode]}`}
 | 
			
		||||
                        mr={4}
 | 
			
		||||
                        size={6}
 | 
			
		||||
                    />
 | 
			
		||||
                </Tooltip>
 | 
			
		||||
            ) : (
 | 
			
		||||
                <Tooltip
 | 
			
		||||
                    hasArrow
 | 
			
		||||
                    label={runtimeText(runtime, config.web.text.complete_time)}
 | 
			
		||||
                    placement="top"
 | 
			
		||||
                >
 | 
			
		||||
                    <Icon name="check" color={defaultStatusColor[colorMode]} mr={4} size={6} />
 | 
			
		||||
                </Tooltip>
 | 
			
		||||
            )}
 | 
			
		||||
            <Text fontSize="lg">{title}</Text>
 | 
			
		||||
            <AccordionIcon ml="auto" />
 | 
			
		||||
        </Stack>
 | 
			
		||||
      <Stack ref={ref} isInline alignItems="center" w="100%">
 | 
			
		||||
        {loading ? (
 | 
			
		||||
          <Spinner size="sm" mr={4} color={statusColor[colorMode]} />
 | 
			
		||||
        ) : error ? (
 | 
			
		||||
          <Tooltip hasArrow label={errorMsg} placement="top">
 | 
			
		||||
            <Icon
 | 
			
		||||
              name="warning"
 | 
			
		||||
              color={`${errorLevel}.${warningColor[colorMode]}`}
 | 
			
		||||
              mr={4}
 | 
			
		||||
              size={6}
 | 
			
		||||
            />
 | 
			
		||||
          </Tooltip>
 | 
			
		||||
        ) : (
 | 
			
		||||
          <Tooltip
 | 
			
		||||
            hasArrow
 | 
			
		||||
            label={runtimeText(runtime, config.web.text.complete_time)}
 | 
			
		||||
            placement="top"
 | 
			
		||||
          >
 | 
			
		||||
            <Icon
 | 
			
		||||
              name="check"
 | 
			
		||||
              color={defaultStatusColor[colorMode]}
 | 
			
		||||
              mr={4}
 | 
			
		||||
              size={6}
 | 
			
		||||
            />
 | 
			
		||||
          </Tooltip>
 | 
			
		||||
        )}
 | 
			
		||||
        <Text fontSize="lg">{title}</Text>
 | 
			
		||||
        <AccordionIcon ml="auto" />
 | 
			
		||||
      </Stack>
 | 
			
		||||
    );
 | 
			
		||||
});
 | 
			
		||||
  }
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
set -e
 | 
			
		||||
 | 
			
		||||
HYPERGLASS_VERSION="1.0.0b26"
 | 
			
		||||
HYPERGLASS_VERSION="1.0.0b27"
 | 
			
		||||
 | 
			
		||||
MIN_PYTHON_MAJOR="3"
 | 
			
		||||
MIN_PYTHON_MINOR="6"
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ build-backend = "poetry.masonry.api"
 | 
			
		||||
 | 
			
		||||
[tool.poetry]
 | 
			
		||||
name = "hyperglass"
 | 
			
		||||
version = "1.0.0-beta.26"
 | 
			
		||||
version = "1.0.0-beta.27"
 | 
			
		||||
description = "hyperglass is the modern network looking glass that tries to make the internet better."
 | 
			
		||||
authors = ["Matt Love <matt@hyperglass.io>"]
 | 
			
		||||
readme = "README.md"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user