import { forwardRef, useEffect, useMemo, useState } from 'react'; import { Box, Flex, Alert, Tooltip, ButtonGroup, AccordionItem, AccordionPanel, AccordionButton, } from '@chakra-ui/react'; import { motion } from 'framer-motion'; import { BsLightningFill } from '@meronex/icons/bs'; import { startCase } from 'lodash'; import { BGPTable, Countdown, CopyButton, RequeryButton, TextOutput, If } from '~/components'; import { useColorValue, useConfig, useMobile } from '~/context'; import { useStrf, useLGQuery, useTableToString } from '~/hooks'; import { isStructuredOutput, isStringOutput } from '~/types'; import { FormattedError } from './error'; import { ResultHeader } from './header'; import { isStackError, isFetchError, isLGError } from './guards'; import type { TAccordionHeaderWrapper, TResult, TErrorLevels } from './types'; const AnimatedAccordionItem = motion.custom(AccordionItem); const AccordionHeaderWrapper = (props: TAccordionHeaderWrapper) => { const { hoverBg, ...rest } = props; return ( ); }; export const Result = forwardRef((props, ref) => { const { index, device, queryVrf, queryType, queryTarget, setComplete, queryLocation, resultsComplete, } = props; const { web, cache, messages } = useConfig(); const isMobile = useMobile(); const color = useColorValue('black', 'white'); const scrollbar = useColorValue('blackAlpha.300', 'whiteAlpha.300'); const scrollbarHover = useColorValue('blackAlpha.400', 'whiteAlpha.400'); const scrollbarBg = useColorValue('blackAlpha.50', 'whiteAlpha.50'); const { data, error, isError, isLoading, refetch } = useLGQuery({ queryLocation, queryTarget, queryType, queryVrf, }); const cacheLabel = useStrf(web.text.cache_icon, { time: data?.timestamp }, [data?.timestamp]); const [isOpen, setOpen] = useState(false); const [hasOverride, setOverride] = useState(false); const handleToggle = () => { setOpen(!isOpen); setOverride(true); }; const errorKeywords = useMemo(() => { let kw = [] as string[]; if (isLGError(error)) { kw = error.keywords; } return kw; }, [isError]); let errorMsg; if (isLGError(error)) { errorMsg = error.output as string; } else if (isFetchError(error)) { errorMsg = startCase(error.statusText); } else if (isStackError(error) && error.message.toLowerCase().startsWith('timeout')) { errorMsg = messages.request_timeout; } else if (isStackError(error)) { errorMsg = startCase(error.message); } else { errorMsg = messages.general; } error && console.error(error); const errorLevel = useMemo(() => { const statusMap = { success: 'success', warning: 'warning', error: 'warning', danger: 'error', } as { [k in TResponseLevel]: 'success' | 'warning' | 'error' }; let e: TErrorLevels = 'error'; if (isLGError(error)) { const idx = error.level as TResponseLevel; e = statusMap[idx]; } return e; }, [error]); const tableComponent = useMemo(() => { let result = false; if (typeof queryType.match(/^bgp_\w+$/) !== null && data?.format === 'application/json') { result = true; } return result; }, [queryType, data?.format]); let copyValue = data?.output as string; const formatData = useTableToString(queryTarget, data, [data?.format]); if (data?.format === 'application/json') { copyValue = formatData(); } if (error) { copyValue = errorMsg; } useEffect(() => { if (isLoading && resultsComplete === null) { setComplete(index); } }, [isLoading, resultsComplete]); useEffect(() => { if (resultsComplete === index && !hasOverride) { setOpen(true); } }, [resultsComplete, index]); return ( {!isError && typeof data !== 'undefined' && ( <> {isStructuredOutput(data) && data.level === 'success' && tableComponent ? ( {data.output} ) : isStringOutput(data) && data.level === 'success' && !tableComponent ? ( {data.output} ) : isStringOutput(data) && data.level !== 'success' ? ( ) : null} )} ); });