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"
|
query_vrf: StrictStr = "Routing Table"
|
||||||
fqdn_tooltip: StrictStr = "Use {protocol}" # Formatted by Javascript
|
fqdn_tooltip: StrictStr = "Use {protocol}" # Formatted by Javascript
|
||||||
cache_prefix: StrictStr = "Results cached for "
|
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
|
complete_time: StrictStr = "Completed in {seconds}" # Formatted by Javascript
|
||||||
|
|
||||||
@validator("title_mode")
|
@validator("title_mode")
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
__name__ = "hyperglass"
|
__name__ = "hyperglass"
|
||||||
__version__ = "1.0.0-beta.26"
|
__version__ = "1.0.0-beta.27"
|
||||||
__author__ = "Matt Love"
|
__author__ = "Matt Love"
|
||||||
__copyright__ = f"Copyright {datetime.now().year} Matthew Love"
|
__copyright__ = f"Copyright {datetime.now().year} Matthew Love"
|
||||||
__license__ = "BSD 3-Clause Clear License"
|
__license__ = "BSD 3-Clause Clear License"
|
||||||
|
@@ -1,16 +1,16 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
AccordionItem,
|
AccordionItem,
|
||||||
AccordionHeader,
|
AccordionHeader,
|
||||||
AccordionPanel,
|
AccordionPanel,
|
||||||
Alert,
|
Alert,
|
||||||
Box,
|
Box,
|
||||||
ButtonGroup,
|
ButtonGroup,
|
||||||
css,
|
css,
|
||||||
Flex,
|
Flex,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Text,
|
Text,
|
||||||
useColorMode,
|
useColorMode
|
||||||
} from "@chakra-ui/core";
|
} from "@chakra-ui/core";
|
||||||
import styled from "@emotion/styled";
|
import styled from "@emotion/styled";
|
||||||
import LightningBolt from "~/components/icons/LightningBolt";
|
import LightningBolt from "~/components/icons/LightningBolt";
|
||||||
@@ -28,230 +28,301 @@ import CacheTimeout from "~/components/CacheTimeout";
|
|||||||
format.extend(String.prototype, {});
|
format.extend(String.prototype, {});
|
||||||
|
|
||||||
const FormattedError = ({ keywords, message }) => {
|
const FormattedError = ({ keywords, message }) => {
|
||||||
const patternStr = keywords.map((kw) => `(${kw})`).join("|");
|
const patternStr = keywords.map(kw => `(${kw})`).join("|");
|
||||||
const pattern = new RegExp(patternStr, "gi");
|
const pattern = new RegExp(patternStr, "gi");
|
||||||
let errorFmt;
|
let errorFmt;
|
||||||
try {
|
try {
|
||||||
errorFmt = strReplace(message, pattern, (match) => (
|
errorFmt = strReplace(message, pattern, match => (
|
||||||
<Text key={match} as="strong">
|
<Text key={match} as="strong">
|
||||||
{match}
|
{match}
|
||||||
</Text>
|
</Text>
|
||||||
));
|
));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
errorFmt = <Text as="span">{message}</Text>;
|
errorFmt = <Text as="span">{message}</Text>;
|
||||||
}
|
}
|
||||||
return <Text as="span">{keywords.length !== 0 ? errorFmt : message}</Text>;
|
return <Text as="span">{keywords.length !== 0 ? errorFmt : message}</Text>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AccordionHeaderWrapper = styled(Flex)`
|
const AccordionHeaderWrapper = styled(Flex)`
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: ${(props) => props.hoverBg};
|
background-color: ${props => props.hoverBg};
|
||||||
}
|
}
|
||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: "outline";
|
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 bg = { dark: "gray.800", light: "blackAlpha.100" };
|
||||||
const color = { dark: "white", light: "black" };
|
const color = { dark: "white", light: "black" };
|
||||||
const selectionBg = { dark: "white", light: "black" };
|
const selectionBg = { dark: "white", light: "black" };
|
||||||
const selectionColor = { dark: "black", light: "white" };
|
const selectionColor = { dark: "black", light: "white" };
|
||||||
|
|
||||||
const Result = React.forwardRef(
|
const Result = React.forwardRef(
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
device,
|
device,
|
||||||
timeout,
|
timeout,
|
||||||
queryLocation,
|
queryLocation,
|
||||||
queryType,
|
queryType,
|
||||||
queryVrf,
|
queryVrf,
|
||||||
queryTarget,
|
queryTarget,
|
||||||
index,
|
index,
|
||||||
resultsComplete,
|
resultsComplete,
|
||||||
setComplete,
|
setComplete
|
||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const { isSm } = useMedia();
|
const { isSm } = useMedia();
|
||||||
const { colorMode } = useColorMode();
|
const { colorMode } = useColorMode();
|
||||||
const [{ data, loading, error }, refetch] = useAxios({
|
const [{ data, loading, error }, refetch] = useAxios({
|
||||||
url: "/api/query/",
|
url: "/api/query/",
|
||||||
method: "post",
|
method: "post",
|
||||||
data: {
|
data: {
|
||||||
query_location: queryLocation,
|
query_location: queryLocation,
|
||||||
query_type: queryType,
|
query_type: queryType,
|
||||||
query_vrf: queryVrf,
|
query_vrf: queryVrf,
|
||||||
query_target: queryTarget,
|
query_target: queryTarget
|
||||||
},
|
},
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
useCache: false,
|
useCache: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const [isOpen, setOpen] = useState(false);
|
const [isOpen, setOpen] = useState(false);
|
||||||
const [hasOverride, setOverride] = useState(false);
|
const [hasOverride, setOverride] = useState(false);
|
||||||
|
|
||||||
const handleToggle = () => {
|
const handleToggle = () => {
|
||||||
setOpen(!isOpen);
|
setOpen(!isOpen);
|
||||||
setOverride(true);
|
setOverride(true);
|
||||||
};
|
};
|
||||||
const cleanOutput =
|
const cleanOutput =
|
||||||
data &&
|
data &&
|
||||||
data.output
|
data.output
|
||||||
.split("\\n")
|
.split("\\n")
|
||||||
.join("\n")
|
.join("\n")
|
||||||
.replace(/\n\n/g, "\n");
|
.replace(/\n\n/g, "\n");
|
||||||
|
|
||||||
const errorKw = (error && error.response?.data?.keywords) || [];
|
const errorKw = (error && error.response?.data?.keywords) || [];
|
||||||
|
|
||||||
let errorMsg;
|
let errorMsg;
|
||||||
if (error && error.response?.data?.output) {
|
if (error && error.response?.data?.output) {
|
||||||
errorMsg = error.response.data.output;
|
errorMsg = error.response.data.output;
|
||||||
} else if (error && error.message.startsWith("timeout")) {
|
} else if (error && error.message.startsWith("timeout")) {
|
||||||
errorMsg = config.messages.request_timeout;
|
errorMsg = config.messages.request_timeout;
|
||||||
} else if (error?.response?.statusText) {
|
} else if (error?.response?.statusText) {
|
||||||
errorMsg = startCase(error.response.statusText);
|
errorMsg = startCase(error.response.statusText);
|
||||||
} else if (error && error.message) {
|
} else if (error && error.message) {
|
||||||
errorMsg = startCase(error.message);
|
errorMsg = startCase(error.message);
|
||||||
} else {
|
} else {
|
||||||
errorMsg = config.messages.general;
|
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>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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";
|
Result.displayName = "HyperglassQueryResult";
|
||||||
|
@@ -1,55 +1,70 @@
|
|||||||
import React from "react";
|
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 format from "string-format";
|
||||||
import useConfig from "~/components/HyperglassProvider";
|
import useConfig from "~/components/HyperglassProvider";
|
||||||
|
|
||||||
format.extend(String.prototype, {});
|
format.extend(String.prototype, {});
|
||||||
|
|
||||||
const runtimeText = (runtime, text) => {
|
const runtimeText = (runtime, text) => {
|
||||||
let unit;
|
let unit;
|
||||||
if (runtime === 1) {
|
if (runtime === 1) {
|
||||||
unit = "seconds";
|
unit = "second";
|
||||||
} else {
|
} else {
|
||||||
unit = "second";
|
unit = "seconds";
|
||||||
}
|
}
|
||||||
const fmt = text.format({ seconds: runtime });
|
const fmt = text.format({ seconds: runtime });
|
||||||
return `${fmt} ${unit}`;
|
return `${fmt} ${unit}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const statusColor = { dark: "primary.300", light: "primary.500" };
|
const statusColor = { dark: "primary.300", light: "primary.500" };
|
||||||
const warningColor = { dark: 300, light: 500 };
|
const warningColor = { dark: 300, light: 500 };
|
||||||
const defaultStatusColor = {
|
const defaultStatusColor = {
|
||||||
dark: "success.300",
|
dark: "success.300",
|
||||||
light: "success.500",
|
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 { colorMode } = useColorMode();
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
return (
|
return (
|
||||||
<Stack ref={ref} isInline alignItems="center" w="100%">
|
<Stack ref={ref} isInline alignItems="center" w="100%">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<Spinner size="sm" mr={4} color={statusColor[colorMode]} />
|
<Spinner size="sm" mr={4} color={statusColor[colorMode]} />
|
||||||
) : error ? (
|
) : error ? (
|
||||||
<Tooltip hasArrow label={errorMsg} placement="top">
|
<Tooltip hasArrow label={errorMsg} placement="top">
|
||||||
<Icon
|
<Icon
|
||||||
name="warning"
|
name="warning"
|
||||||
color={`${errorLevel}.${warningColor[colorMode]}`}
|
color={`${errorLevel}.${warningColor[colorMode]}`}
|
||||||
mr={4}
|
mr={4}
|
||||||
size={6}
|
size={6}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
) : (
|
) : (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
hasArrow
|
hasArrow
|
||||||
label={runtimeText(runtime, config.web.text.complete_time)}
|
label={runtimeText(runtime, config.web.text.complete_time)}
|
||||||
placement="top"
|
placement="top"
|
||||||
>
|
>
|
||||||
<Icon name="check" color={defaultStatusColor[colorMode]} mr={4} size={6} />
|
<Icon
|
||||||
</Tooltip>
|
name="check"
|
||||||
)}
|
color={defaultStatusColor[colorMode]}
|
||||||
<Text fontSize="lg">{title}</Text>
|
mr={4}
|
||||||
<AccordionIcon ml="auto" />
|
size={6}
|
||||||
</Stack>
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
<Text fontSize="lg">{title}</Text>
|
||||||
|
<AccordionIcon ml="auto" />
|
||||||
|
</Stack>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
HYPERGLASS_VERSION="1.0.0b26"
|
HYPERGLASS_VERSION="1.0.0b27"
|
||||||
|
|
||||||
MIN_PYTHON_MAJOR="3"
|
MIN_PYTHON_MAJOR="3"
|
||||||
MIN_PYTHON_MINOR="6"
|
MIN_PYTHON_MINOR="6"
|
||||||
|
@@ -4,7 +4,7 @@ build-backend = "poetry.masonry.api"
|
|||||||
|
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "hyperglass"
|
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."
|
description = "hyperglass is the modern network looking glass that tries to make the internet better."
|
||||||
authors = ["Matt Love <matt@hyperglass.io>"]
|
authors = ["Matt Love <matt@hyperglass.io>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
Reference in New Issue
Block a user