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

improve DNS resolution UX [skip ci]

This commit is contained in:
checktheroads
2021-01-01 01:39:20 -07:00
parent 472a382f76
commit f787b95073
6 changed files with 85 additions and 41 deletions

View File

@@ -9,11 +9,13 @@ description: Customize the text used in the web UI
## `text`
| Parameter | Type | Default | Description |
| :--------------- | :----: | :------------------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| :--------------- | :----: | :-------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `cache_prefix` | String | `'Results cached for '` | Text displayed with the cache timeout countdown. |
| `cache_icon` | String | `'Cached Response from {time}'` | Text displayed when a user hovers over the lightning bolt icon, which is displayed when a response from the server was a cached response. `{time}` is replaced with the _original_ query's timestamp. |
| `completed_time` | String | `'Completed in {seconds}'` | Text displayed when a user hovers over the success icon for a query result. `{seconds}` will be replaced with 'n seconds' where n is the time a query took to complete. |
| `fqdn_tooltip` | String | `'Use {protocol}'` | Text displayed when a user hovers over the IPv4 or IPv6 button on an FQDN target resolved by DNS. `{protocol}` is replaced with the relevant IP protocol. |
| `fqdn_message` | String | `'Your browser has resolved {fqdn} to'` | Text displayed when prompting a user to select a resolve IPv4 or IPv6 address for an FQDN query. |
| `fqdn_error` | String | `'Unable to resolve {fqdn}'` | Text displayed when an FQDN is not resolvable. |
| `fqdn_button` | String | `'Try Again'` | Button text used when an FQDN is not resolvable. |
| `query_location` | String | `'Location'` | Query Location (router) form label. |
| `query_target` | String | `'Target'` | Query Target (IP/hostname/community/AS Path) form label. |
| `query_type` | String | `'Query Type'` | Query Type (BGP Route, Ping, Traceroute, etc.) form label. |

View File

@@ -127,6 +127,8 @@ class Text(HyperglassModel):
query_vrf: StrictStr = "Routing Table"
fqdn_tooltip: StrictStr = "Use {protocol}" # Formatted by Javascript
fqdn_message: StrictStr = "Your browser has resolved {fqdn} to" # Formatted by Javascript
fqdn_error: StrictStr = "Unable to resolve {fqdn}" # Formatted by Javascript
fqdn_error_button: StrictStr = "Try Again"
cache_prefix: StrictStr = "Results cached for "
cache_icon: StrictStr = "Cached from {time} UTC" # Formatted by Javascript
complete_time: StrictStr = "Completed in {seconds}" # Formatted by Javascript

View File

@@ -1,9 +1,17 @@
import { useEffect, useMemo } from 'react';
import { Button, Stack, Text, VStack } from '@chakra-ui/react';
import { FiArrowRightCircle as RightArrow } from '@meronex/icons/fi';
import dynamic from 'next/dynamic';
import { Button, chakra, Icon, Stack, Text, VStack } from '@chakra-ui/react';
import { useConfig, useColorValue } from '~/context';
import { useStrf, useLGState, useDNSQuery } from '~/hooks';
const RightArrow = chakra(
dynamic<MeronexIcon>(() => import('@meronex/icons/fa').then(i => i.FaArrowCircleRight)),
);
const LeftArrow = chakra(
dynamic<MeronexIcon>(() => import('@meronex/icons/fa').then(i => i.FaArrowCircleLeft)),
);
import type { DnsOverHttps } from '~/types';
import type { TResolvedTarget } from './types';
@@ -17,31 +25,38 @@ function findAnswer(data: DnsOverHttps.Response | undefined): string {
}
export const ResolvedTarget = (props: TResolvedTarget) => {
const { setTarget } = props;
const { setTarget, errorClose } = props;
const { web } = useConfig();
const { displayTarget, isSubmitting, families, queryTarget } = useLGState();
const color = useColorValue('secondary.500', 'secondary.300');
const errorColor = useColorValue('red.500', 'red.300');
const query4 = Array.from(families.value).includes(4);
const query6 = Array.from(families.value).includes(6);
const tooltip4 = useStrf(web.text.fqdn_tooltip, { protocol: 'IPv4' });
const tooltip6 = useStrf(web.text.fqdn_tooltip, { protocol: 'IPv6' });
const [messageStart, messageEnd] = useMemo(() => web.text.fqdn_message.split('{fqdn}'), [
web.text.fqdn_message,
]);
const { data: data4, isLoading: isLoading4, isError: isError4 } = useDNSQuery(
const [messageStart, messageEnd] = web.text.fqdn_message.split('{fqdn}');
const [errorStart, errorEnd] = web.text.fqdn_error.split('{fqdn}');
const { data: data4, isLoading: isLoading4, isError: isError4, error: error4 } = useDNSQuery(
displayTarget.value,
4,
);
const { data: data6, isLoading: isLoading6, isError: isError6 } = useDNSQuery(
const { data: data6, isLoading: isLoading6, isError: isError6, error: error6 } = useDNSQuery(
displayTarget.value,
6,
);
isError4 && console.error(error4);
isError6 && console.error(error6);
const answer4 = useMemo(() => findAnswer(data4), [data4]);
const answer6 = useMemo(() => findAnswer(data6), [data6]);
function handleOverride(value: string): void {
setTarget({ field: 'query_target', value });
}
@@ -62,6 +77,7 @@ export const ResolvedTarget = (props: TResolvedTarget) => {
return (
<VStack w="100%" spacing={4} justify="center">
{(answer4 || answer6) && (
<Text fontSize="sm" textAlign="center">
{messageStart}
<Text as="span" fontSize="sm" fontWeight="bold" color={color}>
@@ -69,33 +85,52 @@ export const ResolvedTarget = (props: TResolvedTarget) => {
</Text>
{messageEnd}
</Text>
)}
<Stack spacing={2}>
{!isLoading4 && !isError4 && query4 && findAnswer(data4) && (
{!isLoading4 && !isError4 && query4 && answer4 && (
<Button
size="sm"
fontSize="xs"
colorScheme="primary"
justifyContent="space-between"
rightIcon={<RightArrow size="18px" />}
title={tooltip4}
fontFamily="mono"
onClick={() => selectTarget(findAnswer(data4))}>
{findAnswer(data4)}
colorScheme="primary"
justifyContent="space-between"
onClick={() => selectTarget(answer4)}
rightIcon={<RightArrow boxSize="18px" />}>
{answer4}
</Button>
)}
{!isLoading6 && !isError6 && query6 && findAnswer(data6) && (
{!isLoading6 && !isError6 && query6 && answer6 && (
<Button
size="sm"
fontSize="xs"
colorScheme="secondary"
justifyContent="space-between"
rightIcon={<RightArrow size="18px" />}
title={tooltip6}
fontFamily="mono"
onClick={() => selectTarget(findAnswer(data6))}>
{findAnswer(data6)}
colorScheme="secondary"
justifyContent="space-between"
onClick={() => selectTarget(answer6)}
rightIcon={<RightArrow boxSize="18px" />}>
{answer6}
</Button>
)}
{!answer4 && !answer6 && (
<>
<Text fontSize="sm" textAlign="center" color={errorColor}>
{errorStart}
<Text as="span" fontSize="sm" fontWeight="bold">
{`${displayTarget.value}`.toLowerCase()}
</Text>
{errorEnd}
</Text>
<Button
colorScheme="red"
variant="outline"
onClick={errorClose}
leftIcon={<LeftArrow />}>
{web.text.fqdn_error_button}
</Button>
</>
)}
</Stack>
</VStack>
);

View File

@@ -40,4 +40,5 @@ export interface TQueryTarget {
export interface TResolvedTarget {
setTarget(e: OnChangeArgs): void;
errorClose(): void;
}

View File

@@ -63,7 +63,7 @@ const MSubmitButton = (props: TRSubmitButton) => {
<ModalContent bg={bg}>
<ModalCloseButton />
<ModalBody px={4} py={10}>
{isOpen && <ResolvedTarget setTarget={onChange} />}
{isOpen && <ResolvedTarget setTarget={onChange} errorClose={onClose} />}
</ModalBody>
</ModalContent>
</Modal>
@@ -83,7 +83,9 @@ const DSubmitButton = (props: TRSubmitButton) => {
<PopoverContent bg={bg}>
<PopoverArrow bg={bg} />
<PopoverCloseButton />
<PopoverBody p={6}>{isOpen && <ResolvedTarget setTarget={onChange} />}</PopoverBody>
<PopoverBody p={6}>
{isOpen && <ResolvedTarget setTarget={onChange} errorClose={onClose} />}
</PopoverBody>
</PopoverContent>
</Popover>
);

View File

@@ -36,6 +36,8 @@ export interface IConfigWebText {
query_vrf: string;
fqdn_tooltip: string;
fqdn_message: string;
fqdn_error: string;
fqdn_error_button: string;
cache_prefix: string;
cache_icon: string;
complete_time: string;