mirror of
https://github.com/thatmattlove/hyperglass.git
synced 2026-04-20 14:58:29 +00:00
continue typescript & chakra v1 migrations [skip ci]
This commit is contained in:
parent
59e767e501
commit
8c454cf0ae
20 changed files with 119 additions and 157 deletions
|
|
@ -19,7 +19,7 @@ export const ResetButton = forwardRef<HTMLButtonElement, ButtonProps>((props, re
|
||||||
aria-label="Reset Form"
|
aria-label="Reset Form"
|
||||||
opacity={isSubmitting.value ? 1 : 0}
|
opacity={isSubmitting.value ? 1 : 0}
|
||||||
{...props}>
|
{...props}>
|
||||||
<Icon as={ChevronLeft} boxSize={24} />
|
<Icon as={ChevronLeft} boxSize={8} />
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { useMemo } from 'react';
|
||||||
import { Select } from '~/components';
|
import { Select } from '~/components';
|
||||||
import { useConfig } from '~/context';
|
import { useConfig } from '~/context';
|
||||||
|
|
||||||
import type { TNetwork, TSelectOption } from '~/types';
|
import type { TNetwork, TSelectOptionMulti } from '~/types';
|
||||||
import type { TQuerySelectField } from './types';
|
import type { TQuerySelectField } from './types';
|
||||||
|
|
||||||
function buildOptions(networks: TNetwork[]) {
|
function buildOptions(networks: TNetwork[]) {
|
||||||
|
|
@ -23,11 +23,12 @@ export const QueryLocation = (props: TQuerySelectField) => {
|
||||||
const { networks } = useConfig();
|
const { networks } = useConfig();
|
||||||
const options = useMemo(() => buildOptions(networks), [networks.length]);
|
const options = useMemo(() => buildOptions(networks), [networks.length]);
|
||||||
|
|
||||||
function handleChange(e: TSelectOption): void {
|
function handleChange(e: TSelectOptionMulti): void {
|
||||||
if (Array.isArray(e?.value) && e !== null) {
|
if (e === null) {
|
||||||
const value = e.value.map(sel => sel);
|
e = [];
|
||||||
onChange({ field: 'query_location', value });
|
|
||||||
}
|
}
|
||||||
|
const value = e.map(sel => sel.value);
|
||||||
|
onChange({ field: 'query_location', value });
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,9 @@ export const Frame = (props: TFrame) => {
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
<Footer />
|
<Footer />
|
||||||
{/* <If c={developer_mode}>
|
<If c={developer_mode}>
|
||||||
<Debugger />
|
<Debugger />
|
||||||
</If> */}
|
</If>
|
||||||
</Flex>
|
</Flex>
|
||||||
<If c={web.greeting.enable && !greetingAck}>
|
<If c={web.greeting.enable && !greetingAck}>
|
||||||
<Greeting onClickThrough={setGreetingAck} />
|
<Greeting onClickThrough={setGreetingAck} />
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { Flex } from '@chakra-ui/react';
|
import { Flex } from '@chakra-ui/react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { uniqWith } from 'lodash';
|
import { intersectionWith } from 'lodash';
|
||||||
import * as yup from 'yup';
|
import * as yup from 'yup';
|
||||||
import {
|
import {
|
||||||
|
If,
|
||||||
AnimatedForm,
|
AnimatedForm,
|
||||||
FormRow,
|
FormRow,
|
||||||
QueryVrf,
|
QueryVrf,
|
||||||
|
|
@ -17,30 +18,17 @@ import {
|
||||||
CommunitySelect,
|
CommunitySelect,
|
||||||
} from '~/components';
|
} from '~/components';
|
||||||
import { useConfig, useGlobalState } from '~/context';
|
import { useConfig, useGlobalState } from '~/context';
|
||||||
import { useStrf, useGreeting } from '~/hooks';
|
import { useStrf, useGreeting, useDevice } from '~/hooks';
|
||||||
|
import { isQueryType, isString } from '~/types';
|
||||||
|
|
||||||
import type { Families, TFormData, TDeviceVrf, TQueryTypes, OnChangeArgs } from '~/types';
|
import type { Families, TFormData, TDeviceVrf, TQueryTypes, OnChangeArgs } from '~/types';
|
||||||
|
|
||||||
function isString(a: any): a is string {
|
|
||||||
return typeof a === 'string';
|
|
||||||
}
|
|
||||||
|
|
||||||
function isQueryType(q: any): q is TQueryTypes {
|
|
||||||
let result = false;
|
|
||||||
if (
|
|
||||||
typeof q === 'string' &&
|
|
||||||
['bgp_route', 'bgp_community', 'bgp_aspath', 'ping', 'traceroute'].includes(q)
|
|
||||||
) {
|
|
||||||
result = true;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const HyperglassForm = () => {
|
export const HyperglassForm = () => {
|
||||||
const { web, content, devices, messages, networks, queries } = useConfig();
|
const { web, content, messages, queries } = useConfig();
|
||||||
|
|
||||||
const { formData, isSubmitting } = useGlobalState();
|
const { formData, isSubmitting } = useGlobalState();
|
||||||
const [greetingAck, setGreetingAck] = useGreeting();
|
const [greetingAck, setGreetingAck] = useGreeting();
|
||||||
|
const getDevice = useDevice();
|
||||||
|
|
||||||
const noQueryType = useStrf(messages.no_input, { field: web.text.query_type });
|
const noQueryType = useStrf(messages.no_input, { field: web.text.query_type });
|
||||||
const noQueryLoc = useStrf(messages.no_input, { field: web.text.query_location });
|
const noQueryLoc = useStrf(messages.no_input, { field: web.text.query_location });
|
||||||
|
|
@ -77,105 +65,23 @@ export const HyperglassForm = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
function handleLocChange(locations: string[]): void {
|
||||||
const handleLocChange = locObj => {
|
const allVrfs = [] as TDeviceVrf[][];
|
||||||
setQueryLocation(locObj.value);
|
|
||||||
const allVrfs = [];
|
|
||||||
const deviceVrfs = [];
|
|
||||||
locObj.value.map(loc => {
|
|
||||||
const locVrfs = [];
|
|
||||||
config.devices[loc].vrfs.map(vrf => {
|
|
||||||
locVrfs.push({
|
|
||||||
label: vrf.display_name,
|
|
||||||
value: vrf.id,
|
|
||||||
});
|
|
||||||
deviceVrfs.push([{ id: vrf.id, ipv4: vrf.ipv4, ipv6: vrf.ipv6 }]);
|
|
||||||
});
|
|
||||||
allVrfs.push(locVrfs);
|
|
||||||
});
|
|
||||||
|
|
||||||
deviceVrfs.length !== 0 &&
|
setQueryLocation(locations);
|
||||||
intersecting.length !== 0 &&
|
|
||||||
deviceVrfs
|
|
||||||
.filter(v => intersecting.every(i => i.id === v.id))
|
|
||||||
.reduce((a, b) => a.concat(b))
|
|
||||||
.filter(v => v.id === 'default')
|
|
||||||
.map(v => {
|
|
||||||
v.ipv4 === true && ipv4++;
|
|
||||||
v.ipv6 === true && ipv6++;
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
|
||||||
// function handleLocChange(locObj: TSelectOption) {
|
// Create an array of each device's VRFs.
|
||||||
// const allVrfs = [] as TDeviceVrf[][];
|
for (const loc of locations) {
|
||||||
// const deviceVrfs = [] as TDeviceVrf[][];
|
const device = getDevice(loc);
|
||||||
|
allVrfs.push(device.vrfs);
|
||||||
// if (Array.isArray(locObj.value)) {
|
|
||||||
// setQueryLocation(locObj.value);
|
|
||||||
// for (const loc of locObj.value) {
|
|
||||||
// const locVrfs = [] as TDeviceVrf[];
|
|
||||||
// for (const vrf of devices.filter(dev => dev.name === loc)[0].vrfs) {
|
|
||||||
// locVrfs.push(vrf);
|
|
||||||
// deviceVrfs.push([vrf]);
|
|
||||||
// }
|
|
||||||
// allVrfs.push(locVrfs);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Use _.intersectionWith to create an array of VRFs common to all selected locations.
|
|
||||||
// const intersecting: TDeviceVrf[] = intersectionWith(...allVrfs, isEqual);
|
|
||||||
// setAvailVrfs(intersecting);
|
|
||||||
|
|
||||||
// // If there are no intersecting VRFs, use the default VRF.
|
|
||||||
// if (intersecting.filter(i => i.id === queryVrf).length === 0 && queryVrf !== 'default') {
|
|
||||||
// setQueryVrf('default');
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let ipv4 = 0;
|
|
||||||
// let ipv6 = 0;
|
|
||||||
|
|
||||||
// if (deviceVrfs.length !== 0 && intersecting.length !== 0) {
|
|
||||||
// const matching = deviceVrfs
|
|
||||||
// // Select intersecting VRFs
|
|
||||||
// .filter(v => intersecting.every(i => i.id === v.id))
|
|
||||||
// .reduce((a, b) => a.concat(b))
|
|
||||||
// .filter(v => v.id === 'default');
|
|
||||||
|
|
||||||
// for (const match of matching) {
|
|
||||||
// if (match.ipv4) {
|
|
||||||
// ipv4++;
|
|
||||||
// }
|
|
||||||
// if (match.ipv6) {
|
|
||||||
// ipv6++;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (ipv4 !== 0 && ipv4 === ipv6) {
|
|
||||||
// setFamilies([4, 6]);
|
|
||||||
// } else if (ipv4 > ipv6) {
|
|
||||||
// setFamilies([4]);
|
|
||||||
// } else if (ipv4 < ipv6) {
|
|
||||||
// setFamilies([6]);
|
|
||||||
// } else {
|
|
||||||
// setFamilies([]);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
function handleLocChange(locations: string | string[]): void {
|
|
||||||
const allVrfs = [] as TDeviceVrf[];
|
|
||||||
|
|
||||||
if (Array.isArray(locations)) {
|
|
||||||
setQueryLocation(locations);
|
|
||||||
for (const loc of locations) {
|
|
||||||
for (const vrf of devices.filter(dev => dev.name === loc)[0].vrfs) {
|
|
||||||
allVrfs.push(vrf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use _.intersectionWith to create an array of VRFs common to all selected locations.
|
// Use _.intersectionWith to create an array of VRFs common to all selected locations.
|
||||||
const intersecting = uniqWith<TDeviceVrf>(allVrfs, (a, b) => a.id === b.id);
|
const intersecting = intersectionWith(
|
||||||
|
...allVrfs,
|
||||||
|
(a: TDeviceVrf, b: TDeviceVrf) => a.id === b.id,
|
||||||
|
);
|
||||||
|
|
||||||
setAvailVrfs(intersecting);
|
setAvailVrfs(intersecting);
|
||||||
|
|
||||||
// If there are no intersecting VRFs, use the default VRF.
|
// If there are no intersecting VRFs, use the default VRF.
|
||||||
|
|
@ -211,7 +117,7 @@ export const HyperglassForm = () => {
|
||||||
function handleChange(e: OnChangeArgs): void {
|
function handleChange(e: OnChangeArgs): void {
|
||||||
setValue(e.field, e.value);
|
setValue(e.field, e.value);
|
||||||
|
|
||||||
if (e.field === 'query_location') {
|
if (e.field === 'query_location' && Array.isArray(e.value)) {
|
||||||
handleLocChange(e.value);
|
handleLocChange(e.value);
|
||||||
} else if (e.field === 'query_type' && isQueryType(e.value)) {
|
} else if (e.field === 'query_type' && isQueryType(e.value)) {
|
||||||
setQueryType(e.value);
|
setQueryType(e.value);
|
||||||
|
|
@ -277,11 +183,11 @@ export const HyperglassForm = () => {
|
||||||
</FormField>
|
</FormField>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
{availVrfs.length > 1 && (
|
<If c={availVrfs.length > 1}>
|
||||||
<FormField label={web.text.query_vrf} name="query_vrf" errors={errors.query_vrf}>
|
<FormField label={web.text.query_vrf} name="query_vrf" errors={errors.query_vrf}>
|
||||||
<QueryVrf label={web.text.query_vrf} vrfs={availVrfs} onChange={handleChange} />
|
<QueryVrf label={web.text.query_vrf} vrfs={availVrfs} onChange={handleChange} />
|
||||||
</FormField>
|
</FormField>
|
||||||
)}
|
</If>
|
||||||
<FormField
|
<FormField
|
||||||
name="query_target"
|
name="query_target"
|
||||||
errors={errors.query_target}
|
errors={errors.query_target}
|
||||||
|
|
@ -298,7 +204,7 @@ export const HyperglassForm = () => {
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}>
|
}>
|
||||||
{queryType === 'bgp_community' && queries.bgp_community.mode === 'select' ? (
|
<If c={queryType === 'bgp_community' && queries.bgp_community.mode === 'select'}>
|
||||||
<CommunitySelect
|
<CommunitySelect
|
||||||
name="query_target"
|
name="query_target"
|
||||||
register={register}
|
register={register}
|
||||||
|
|
@ -306,7 +212,8 @@ export const HyperglassForm = () => {
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
communities={queries.bgp_community.communities}
|
communities={queries.bgp_community.communities}
|
||||||
/>
|
/>
|
||||||
) : (
|
</If>
|
||||||
|
<If c={!(queryType === 'bgp_community' && queries.bgp_community.mode === 'select')}>
|
||||||
<QueryTarget
|
<QueryTarget
|
||||||
name="query_target"
|
name="query_target"
|
||||||
register={register}
|
register={register}
|
||||||
|
|
@ -319,7 +226,7 @@ export const HyperglassForm = () => {
|
||||||
setDisplayValue={setDisplayTarget}
|
setDisplayValue={setDisplayTarget}
|
||||||
placeholder={web.text.query_target}
|
placeholder={web.text.query_target}
|
||||||
/>
|
/>
|
||||||
)}
|
</If>
|
||||||
</FormField>
|
</FormField>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow mt={0} justifyContent="flex-end">
|
<FormRow mt={0} justifyContent="flex-end">
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,11 @@ export interface TCommunities {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TRPKIState {
|
export interface TRPKIState {
|
||||||
state: 0 | 1 | 2 | 3;
|
state:
|
||||||
|
| 0 // Invalid
|
||||||
|
| 1 // Valid
|
||||||
|
| 2 // Unknown
|
||||||
|
| 3; // Unverified
|
||||||
active: boolean;
|
active: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,16 @@ import { Accordion, Box, Stack, useToken } from '@chakra-ui/react';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { AnimatedDiv, Label } from '~/components';
|
import { AnimatedDiv, Label } from '~/components';
|
||||||
import { useConfig, useBreakpointValue } from '~/context';
|
import { useConfig, useBreakpointValue } from '~/context';
|
||||||
|
import { useDevice } from '~/hooks';
|
||||||
|
import { isQueryType } from '~/types';
|
||||||
import { Result } from './individual';
|
import { Result } from './individual';
|
||||||
|
|
||||||
import type { TResults } from './types';
|
import type { TResults } from './types';
|
||||||
|
|
||||||
export const Results = (props: TResults) => {
|
export const Results = (props: TResults) => {
|
||||||
const { queryLocation, queryType, queryVrf, queryTarget, ...rest } = props;
|
const { queryLocation, queryType, queryVrf, queryTarget, ...rest } = props;
|
||||||
const { request_timeout, devices, queries, vrfs, web } = useConfig();
|
const { request_timeout, queries, vrfs, web } = useConfig();
|
||||||
|
const getDevice = useDevice();
|
||||||
const targetBg = useToken('colors', 'teal.600');
|
const targetBg = useToken('colors', 'teal.600');
|
||||||
const queryBg = useToken('colors', 'cyan.500');
|
const queryBg = useToken('colors', 'cyan.500');
|
||||||
const vrfBg = useToken('colors', 'blue.500');
|
const vrfBg = useToken('colors', 'blue.500');
|
||||||
|
|
@ -61,6 +64,11 @@ export const Results = (props: TResults) => {
|
||||||
const matchedVrf =
|
const matchedVrf =
|
||||||
vrfs.filter(v => v.id === queryVrf)[0] ?? vrfs.filter(v => v.id === 'default')[0];
|
vrfs.filter(v => v.id === queryVrf)[0] ?? vrfs.filter(v => v.id === 'default')[0];
|
||||||
|
|
||||||
|
let queryTypeLabel = '';
|
||||||
|
if (isQueryType(queryType)) {
|
||||||
|
queryTypeLabel = queries[queryType].display_name;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box
|
<Box
|
||||||
|
|
@ -84,7 +92,7 @@ export const Results = (props: TResults) => {
|
||||||
bg={queryBg}
|
bg={queryBg}
|
||||||
label={web.text.query_type}
|
label={web.text.query_type}
|
||||||
fontSize={{ base: 'xs', md: 'sm' }}
|
fontSize={{ base: 'xs', md: 'sm' }}
|
||||||
value={queries[queryType].display_name}
|
value={queryTypeLabel}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|
@ -134,7 +142,7 @@ export const Results = (props: TResults) => {
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{queryLocation &&
|
{queryLocation &&
|
||||||
queryLocation.map((loc, i) => {
|
queryLocation.map((loc, i) => {
|
||||||
const device = devices.filter(d => d.name === loc)[0];
|
const device = getDevice(loc);
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
import dynamic from 'next/dynamic';
|
|
||||||
import { forwardRef, useMemo } from 'react';
|
import { forwardRef, useMemo } from 'react';
|
||||||
import { AccordionIcon, Icon, Spinner, Stack, Text, Tooltip } from '@chakra-ui/react';
|
import { AccordionIcon, Box, Spinner, Stack, Text, Tooltip } from '@chakra-ui/react';
|
||||||
|
import { BisError as Warning } from '@meronex/icons/bi';
|
||||||
|
import { FaCheckCircle as Check } from '@meronex/icons/fa';
|
||||||
import { useConfig, useColorValue } from '~/context';
|
import { useConfig, useColorValue } from '~/context';
|
||||||
import { useStrf } from '~/hooks';
|
import { useStrf } from '~/hooks';
|
||||||
|
|
||||||
import type { TResultHeader } from './types';
|
import type { TResultHeader } from './types';
|
||||||
|
|
||||||
const Check = dynamic<MeronexIcon>(() => import('@meronex/icons/fa').then(i => i.FaCheckCircle));
|
|
||||||
const Warning = dynamic<MeronexIcon>(() => import('@meronex/icons/bi').then(i => i.BisError));
|
|
||||||
|
|
||||||
const runtimeText = (runtime: number, text: string): string => {
|
const runtimeText = (runtime: number, text: string): string => {
|
||||||
let unit = 'seconds';
|
let unit = 'seconds';
|
||||||
if (runtime === 1) {
|
if (runtime === 1) {
|
||||||
|
|
@ -34,11 +32,11 @@ export const ResultHeader = forwardRef<HTMLDivElement, TResultHeader>((props, re
|
||||||
<Spinner size="sm" mr={4} color={status} />
|
<Spinner size="sm" mr={4} color={status} />
|
||||||
) : error ? (
|
) : error ? (
|
||||||
<Tooltip hasArrow label={errorMsg} placement="top">
|
<Tooltip hasArrow label={errorMsg} placement="top">
|
||||||
<Icon as={Warning} color={warning} mr={4} boxSize={6} />
|
<Box as={Warning} color={warning} mr={4} boxSize={6} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
) : (
|
) : (
|
||||||
<Tooltip hasArrow label={label} placement="top">
|
<Tooltip hasArrow label={label} placement="top">
|
||||||
<Icon as={Check} color={defaultStatus} mr={4} boxSize={6} />
|
<Box as={Check} color={defaultStatus} mr={4} boxSize={6} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
<Text fontSize="lg">{title}</Text>
|
<Text fontSize="lg">{title}</Text>
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ export const Result = forwardRef<HTMLDivElement, TResult>((props, ref) => {
|
||||||
|
|
||||||
let copyValue = data?.output;
|
let copyValue = data?.output;
|
||||||
|
|
||||||
const formatData = useTableToString(queryTarget, data, [data.format]);
|
const formatData = useTableToString(queryTarget, data, [data?.format]);
|
||||||
|
|
||||||
if (data?.format === 'application/json') {
|
if (data?.format === 'application/json') {
|
||||||
copyValue = formatData();
|
copyValue = formatData();
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,14 @@ import {
|
||||||
} from './styles';
|
} from './styles';
|
||||||
|
|
||||||
import type { TSelectOption } from '~/types';
|
import type { TSelectOption } from '~/types';
|
||||||
import type { TSelect, TSelectContext, TBoxAsReactSelect } from './types';
|
import type { TSelectBase, TSelectContext, TBoxAsReactSelect } from './types';
|
||||||
|
|
||||||
const SelectContext = createContext<TSelectContext>(Object());
|
const SelectContext = createContext<TSelectContext>(Object());
|
||||||
export const useSelectContext = () => useContext(SelectContext);
|
export const useSelectContext = () => useContext(SelectContext);
|
||||||
|
|
||||||
const ReactSelectAsBox = (props: TBoxAsReactSelect) => <Box as={ReactSelect} {...props} />;
|
const ReactSelectAsBox = (props: TBoxAsReactSelect) => <Box as={ReactSelect} {...props} />;
|
||||||
|
|
||||||
export const Select = (props: TSelect) => {
|
export const Select = (props: TSelectBase) => {
|
||||||
const { ctl, options, multi, onSelect, ...rest } = props;
|
const { ctl, options, multi, onSelect, ...rest } = props;
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ export interface TSelectBase extends TBoxAsReactSelect {
|
||||||
multi?: boolean;
|
multi?: boolean;
|
||||||
options: TOptions;
|
options: TOptions;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
onSelect?: (s: TSelectOption) => void;
|
onSelect?: (s: TSelectOption[]) => void;
|
||||||
onChange?: (c: TSelectOption) => void;
|
onChange?: (c: TSelectOption) => void;
|
||||||
colorScheme?: ColorNames;
|
colorScheme?: ColorNames;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
export * from './useBooleanValue';
|
export * from './useBooleanValue';
|
||||||
|
export * from './useDevice';
|
||||||
export * from './useGreeting';
|
export * from './useGreeting';
|
||||||
export * from './useOpposingColor';
|
export * from './useOpposingColor';
|
||||||
export * from './useSessionStorage';
|
export * from './useSessionStorage';
|
||||||
|
|
|
||||||
15
hyperglass/ui/hooks/useDevice.ts
Normal file
15
hyperglass/ui/hooks/useDevice.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { useCallback, useMemo } from 'react';
|
||||||
|
import { useConfig } from '~/context';
|
||||||
|
import { flatten } from '~/util';
|
||||||
|
|
||||||
|
import type { TDevice } from '~/types';
|
||||||
|
|
||||||
|
export function useDevice(): (i: string) => TDevice {
|
||||||
|
const { networks } = useConfig();
|
||||||
|
const devices = useMemo(() => flatten<TDevice>(networks.map(n => n.locations)), []);
|
||||||
|
|
||||||
|
function getDevice(id: string): TDevice {
|
||||||
|
return devices.filter(dev => dev.name === id)[0];
|
||||||
|
}
|
||||||
|
return useCallback(getDevice, []);
|
||||||
|
}
|
||||||
4
hyperglass/ui/package.json
vendored
4
hyperglass/ui/package.json
vendored
|
|
@ -37,7 +37,7 @@
|
||||||
"react-hook-form": "^5.7",
|
"react-hook-form": "^5.7",
|
||||||
"react-markdown": "^4.3.1",
|
"react-markdown": "^4.3.1",
|
||||||
"react-query": "^2.26.4",
|
"react-query": "^2.26.4",
|
||||||
"react-select": "^3.0.8",
|
"react-select": "^3.1.1",
|
||||||
"react-string-replace": "^0.4.4",
|
"react-string-replace": "^0.4.4",
|
||||||
"react-table": "^7.6.2",
|
"react-table": "^7.6.2",
|
||||||
"string-format": "^2.0.0",
|
"string-format": "^2.0.0",
|
||||||
|
|
@ -47,7 +47,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^14.11.10",
|
"@types/node": "^14.11.10",
|
||||||
"@types/react-select": "^3.0.22",
|
"@types/react-select": "^3.0.28",
|
||||||
"@types/react-table": "^7.0.25",
|
"@types/react-table": "^7.0.25",
|
||||||
"@types/string-format": "^2.0.0",
|
"@types/string-format": "^2.0.0",
|
||||||
"@types/yup": "^0.29.9",
|
"@types/yup": "^0.29.9",
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
export type TSelectOption = {
|
export type TSelectOptionBase = {
|
||||||
label: string;
|
label: string;
|
||||||
value: string | string[];
|
value: string;
|
||||||
group?: string;
|
group?: string;
|
||||||
} | null;
|
};
|
||||||
|
|
||||||
|
export type TSelectOption = TSelectOptionBase | null;
|
||||||
|
|
||||||
|
export type TSelectOptionMulti = TSelectOptionBase[] | null;
|
||||||
|
|
||||||
export type TSelectOptionGroup = {
|
export type TSelectOptionGroup = {
|
||||||
label: string;
|
label: string;
|
||||||
|
|
|
||||||
|
|
@ -121,12 +121,12 @@ export interface TDevice extends TDeviceBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TNetworkLocation extends TDeviceBase {
|
export interface TNetworkLocation extends TDeviceBase {
|
||||||
vrfs: TDeviceVrfBase[];
|
vrfs: TDeviceVrf[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TNetwork {
|
export interface TNetwork {
|
||||||
display_name: string;
|
display_name: string;
|
||||||
locations: TNetworkLocation[];
|
locations: TDevice[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TParsedDataField = [string, keyof TRoute, 'left' | 'right' | 'center' | null];
|
export type TParsedDataField = [string, keyof TRoute, 'left' | 'right' | 'center' | null];
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
export type TQueryTypes = '' | 'bgp_route' | 'bgp_community' | 'bgp_aspath' | 'ping' | 'traceroute';
|
export type TQueryTypes = '' | TValidQueryTypes;
|
||||||
|
export type TValidQueryTypes = 'bgp_route' | 'bgp_community' | 'bgp_aspath' | 'ping' | 'traceroute';
|
||||||
|
|
||||||
export interface TFormData {
|
export interface TFormData {
|
||||||
query_location: string[];
|
query_location: string[];
|
||||||
|
|
|
||||||
16
hyperglass/ui/types/guards.ts
Normal file
16
hyperglass/ui/types/guards.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { TValidQueryTypes } from './data';
|
||||||
|
|
||||||
|
export function isQueryType(q: any): q is TValidQueryTypes {
|
||||||
|
let result = false;
|
||||||
|
if (
|
||||||
|
typeof q === 'string' &&
|
||||||
|
['bgp_route', 'bgp_community', 'bgp_aspath', 'ping', 'traceroute'].includes(q)
|
||||||
|
) {
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isString(a: any): a is string {
|
||||||
|
return typeof a === 'string';
|
||||||
|
}
|
||||||
|
|
@ -2,5 +2,6 @@ export * from './common';
|
||||||
export * from './config';
|
export * from './config';
|
||||||
export * from './data';
|
export * from './data';
|
||||||
export * from './dns-over-https';
|
export * from './dns-over-https';
|
||||||
|
export * from './guards';
|
||||||
export * from './table';
|
export * from './table';
|
||||||
export * from './theme';
|
export * from './theme';
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,9 @@ export function all(...iter: any[]) {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function flatten<T extends unknown>(arr: any[][]): T[] {
|
||||||
|
return arr.reduce(function (flat, toFlatten) {
|
||||||
|
return flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten);
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
|
||||||
16
hyperglass/ui/yarn.lock
vendored
16
hyperglass/ui/yarn.lock
vendored
|
|
@ -1055,10 +1055,10 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
|
|
||||||
"@types/react-select@^3.0.22":
|
"@types/react-select@^3.0.28":
|
||||||
version "3.0.22"
|
version "3.0.28"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react-select/-/react-select-3.0.22.tgz#b88306365e99fa86809a5c0ce0f1b4e8d0b626bf"
|
resolved "https://registry.yarnpkg.com/@types/react-select/-/react-select-3.0.28.tgz#b9adb98421926321b81c4cfe6a40121c96a421f0"
|
||||||
integrity sha512-fqgmC979JPr/6476Pau6QnmI9zVV664R7Q92Ld1rgTn+umtUXT5X3+PO/x6O4imCZnh7XCqZcouabWAlAQJNpQ==
|
integrity sha512-Gfg3a/EPLyUQkfezcCQkmLW1Vz6+ziclJhn8dpBUEYJF3IUoxS81ToAi3ky2xtnAyk2wJFMXLvE73KiUd56yTA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
"@types/react-dom" "*"
|
"@types/react-dom" "*"
|
||||||
|
|
@ -5549,10 +5549,10 @@ react-remove-scroll@2.4.0:
|
||||||
use-callback-ref "^1.2.3"
|
use-callback-ref "^1.2.3"
|
||||||
use-sidecar "^1.0.1"
|
use-sidecar "^1.0.1"
|
||||||
|
|
||||||
react-select@^3.0.8:
|
react-select@^3.1.1:
|
||||||
version "3.1.0"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-select/-/react-select-3.1.0.tgz#ab098720b2e9fe275047c993f0d0caf5ded17c27"
|
resolved "https://registry.yarnpkg.com/react-select/-/react-select-3.1.1.tgz#156a5b4a6c22b1e3d62a919cb1fd827adb4060bc"
|
||||||
integrity sha512-wBFVblBH1iuCBprtpyGtd1dGMadsG36W5/t2Aj8OE6WbByDg5jIFyT7X5gT+l0qmT5TqWhxX+VsKJvCEl2uL9g==
|
integrity sha512-HjC6jT2BhUxbIbxMZWqVcDibrEpdUJCfGicN0MMV+BQyKtCaPTgFekKWiOizSCy4jdsLMGjLqcFGJMhVGWB0Dg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.4.4"
|
"@babel/runtime" "^7.4.4"
|
||||||
"@emotion/cache" "^10.0.9"
|
"@emotion/cache" "^10.0.9"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue