diff --git a/docs/docs/ui/text.mdx b/docs/docs/ui/text.mdx
index c9eff8f..fc41528 100644
--- a/docs/docs/ui/text.mdx
+++ b/docs/docs/ui/text.mdx
@@ -8,19 +8,21 @@ 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. |
-| `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. |
-| `query_vrf` | String | `'Routing Table'` | Query VRF form label. |
-| `subtitle` | String | `'Network Looking Glass'` | Subtitle text. value. |
-| `title` | String | `'hyperglass'` | Title text. |
-| `title_mode` | String | `'text_only'` | Set the title mode. Must be text_only, logo_only, logo_subtitle, or all |
+| 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_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. |
+| `query_vrf` | String | `'Routing Table'` | Query VRF form label. |
+| `subtitle` | String | `'Network Looking Glass'` | Subtitle text. value. |
+| `title` | String | `'hyperglass'` | Title text. |
+| `title_mode` | String | `'text_only'` | Set the title mode. Must be text_only, logo_only, logo_subtitle, or all |
### Title Mode
diff --git a/hyperglass/models/config/web.py b/hyperglass/models/config/web.py
index 85b432f..8a72884 100644
--- a/hyperglass/models/config/web.py
+++ b/hyperglass/models/config/web.py
@@ -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
diff --git a/hyperglass/ui/components/form/resolvedTarget.tsx b/hyperglass/ui/components/form/resolvedTarget.tsx
index 569b370..305f55c 100644
--- a/hyperglass/ui/components/form/resolvedTarget.tsx
+++ b/hyperglass/ui/components/form/resolvedTarget.tsx
@@ -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(() => import('@meronex/icons/fa').then(i => i.FaArrowCircleRight)),
+);
+
+const LeftArrow = chakra(
+ dynamic(() => 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,40 +77,60 @@ export const ResolvedTarget = (props: TResolvedTarget) => {
return (
-
- {messageStart}
-
- {`${displayTarget.value}`.toLowerCase()}
+ {(answer4 || answer6) && (
+
+ {messageStart}
+
+ {`${displayTarget.value}`.toLowerCase()}
+
+ {messageEnd}
- {messageEnd}
-
+ )}
- {!isLoading4 && !isError4 && query4 && findAnswer(data4) && (
+ {!isLoading4 && !isError4 && query4 && answer4 && (
}
title={tooltip4}
fontFamily="mono"
- onClick={() => selectTarget(findAnswer(data4))}>
- {findAnswer(data4)}
+ colorScheme="primary"
+ justifyContent="space-between"
+ onClick={() => selectTarget(answer4)}
+ rightIcon={}>
+ {answer4}
)}
- {!isLoading6 && !isError6 && query6 && findAnswer(data6) && (
+ {!isLoading6 && !isError6 && query6 && answer6 && (
}
title={tooltip6}
fontFamily="mono"
- onClick={() => selectTarget(findAnswer(data6))}>
- {findAnswer(data6)}
+ colorScheme="secondary"
+ justifyContent="space-between"
+ onClick={() => selectTarget(answer6)}
+ rightIcon={}>
+ {answer6}
)}
+ {!answer4 && !answer6 && (
+ <>
+
+ {errorStart}
+
+ {`${displayTarget.value}`.toLowerCase()}
+
+ {errorEnd}
+
+ }>
+ {web.text.fqdn_error_button}
+
+ >
+ )}
);
diff --git a/hyperglass/ui/components/form/types.ts b/hyperglass/ui/components/form/types.ts
index 2d0070c..b85526b 100644
--- a/hyperglass/ui/components/form/types.ts
+++ b/hyperglass/ui/components/form/types.ts
@@ -40,4 +40,5 @@ export interface TQueryTarget {
export interface TResolvedTarget {
setTarget(e: OnChangeArgs): void;
+ errorClose(): void;
}
diff --git a/hyperglass/ui/components/submit/submit.tsx b/hyperglass/ui/components/submit/submit.tsx
index 179b881..5f02bb0 100644
--- a/hyperglass/ui/components/submit/submit.tsx
+++ b/hyperglass/ui/components/submit/submit.tsx
@@ -63,7 +63,7 @@ const MSubmitButton = (props: TRSubmitButton) => {
- {isOpen && }
+ {isOpen && }
@@ -83,7 +83,9 @@ const DSubmitButton = (props: TRSubmitButton) => {
- {isOpen && }
+
+ {isOpen && }
+
);
diff --git a/hyperglass/ui/types/config.ts b/hyperglass/ui/types/config.ts
index 2730ba8..38493bf 100644
--- a/hyperglass/ui/types/config.ts
+++ b/hyperglass/ui/types/config.ts
@@ -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;