Files
wlan-cloud-ucentralgw-ui/src/components/Modals/TelemetryModal/index.tsx
2024-06-04 09:06:16 -04:00

279 lines
8.2 KiB
TypeScript

import * as React from 'react';
import {
Alert,
AlertDescription,
AlertIcon,
AlertTitle,
Box,
Button,
Center,
Checkbox,
FormLabel,
Heading,
Select,
SimpleGrid,
Slider,
SliderFilledTrack,
SliderMark,
SliderThumb,
SliderTrack,
Spinner,
Stack,
useClipboard,
} from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
import { Modal } from '../Modal';
import { lowercaseFirstLetter } from 'helpers/stringHelper';
import { useTelemetry } from 'hooks/Network/Telemetry';
import { secondsDuration } from 'helpers/dateFormatting';
export type TelemetryModalProps = {
serialNumber: string;
modalProps: {
isOpen: boolean;
onClose: () => void;
};
};
type FormData = {
serialNumber: string;
interval: number;
lifetime: number;
kafka: boolean;
types: string[];
};
const _TelemetryModal = ({ serialNumber, modalProps }: TelemetryModalProps) => {
const { t } = useTranslation();
const [form, setForm] = React.useState<FormData>({
serialNumber,
interval: 3,
lifetime: 60 * 5,
kafka: false,
types: ['wifi-frames', 'dhcp-snooping', 'state'],
});
const [lastDate, setLastDate] = React.useState<Date>(new Date());
const telemetry = useTelemetry();
const { hasCopied, onCopy, setValue } = useClipboard(JSON.stringify(telemetry.lastMessage?.msg ?? {}, null, 2));
const onStart = () => {
telemetry.startTelemetry(form, modalProps.onClose);
};
const tick = () => {
setLastDate(new Date());
};
const secondsAgo = React.useMemo(
() =>
telemetry.lastMessage?.timestamp
? Math.max(0, Math.floor((lastDate.getTime() - telemetry.lastMessage.timestamp.getTime()) / 1000))
: ' X ',
[lastDate, telemetry.lastMessage?.timestamp],
);
const onToggle = (value: string) => (e: { target: { checked: boolean } }) => {
if (e.target.checked) {
setForm({
...form,
types: [...form.types, value],
});
} else {
setForm({
...form,
types: form.types.filter((type) => type !== value),
});
}
};
React.useEffect(() => {
telemetry.closeSocket();
}, [modalProps.isOpen]);
React.useEffect(() => {
setValue(JSON.stringify(telemetry.lastMessage?.msg ?? {}, null, 2));
}, [telemetry.lastMessage?.msg]);
React.useEffect(() => {
const timerID = setTimeout(() => tick(), 1000);
return () => {
clearTimeout(timerID);
};
}, [lastDate]);
React.useEffect(() => {
setForm({
...form,
serialNumber,
});
}, [serialNumber]);
return (
<Modal
{...modalProps}
title={t('controller.telemetry.title')}
topRightButtons={
<Button
onClick={onStart}
isDisabled={telemetry.isOpen || form.types.length === 0}
isLoading={telemetry.startRequest.isLoading}
colorScheme="blue"
>
{t('common.start')}
</Button>
}
>
<>
{telemetry.startRequest.isLoading && (
<Center my="100px">
<Spinner size="xl" />
</Center>
)}
{telemetry.startRequest.error && (
<Alert status="error" my="100px">
<AlertIcon />
<Box>
<AlertTitle>{t('common.error')}</AlertTitle>
{
// @ts-ignore
<AlertDescription>{telemetry.error?.response?.data?.ErrorDescription}</AlertDescription>
}
</Box>
</Alert>
)}
{telemetry.startRequest.data ? (
<>
<p>
{t('controller.telemetry.interval')}: {form.interval} {lowercaseFirstLetter(t('common.seconds'))}
</p>
<p>
{t('controller.telemetry.duration')}: {secondsDuration(form.lifetime, t)}
</p>
<p>
{t('controller.telemetry.types')}: {form.types.join(', ')}
</p>
<p>
{t('controller.telemetry.last_update')}: {telemetry.lastMessage?.timestamp.toLocaleString()}
</p>
<Heading size="sm">{t('controller.telemetry.seconds_ago', { seconds: secondsAgo })}</Heading>
<Box textAlign="right" mb={2}>
<Button onClick={onCopy} size="md" colorScheme="teal">
{hasCopied ? `${t('common.copied')}!` : t('common.copy')}
</Button>
</Box>
<Box maxH="calc(100vh - 380px)" minH="300px" overflowY="auto">
<pre>{JSON.stringify(telemetry.lastMessage?.msg ?? {}, null, 2)}</pre>
</Box>
</>
) : (
<>
<FormLabel>{t('controller.telemetry.interval')}</FormLabel>
<Slider
step={1}
value={form.interval}
max={120}
onChange={(value) => setForm({ ...form, interval: value })}
focusThumbOnChange={false}
>
<SliderMark
value={form.interval}
textAlign="center"
bg="blue.500"
color="white"
ml="6"
mt="-3"
w="12"
zIndex={2}
>
{form.interval}s
</SliderMark>
<SliderTrack>
<SliderFilledTrack />
</SliderTrack>
<SliderThumb />
</Slider>
<FormLabel mt={4}>{t('controller.telemetry.duration')}</FormLabel>
<Slider
step={1}
value={form.lifetime / 60}
max={120}
onChange={(value) => setForm({ ...form, lifetime: value * 60 })}
focusThumbOnChange={false}
>
<SliderMark
value={form.lifetime / 60}
textAlign="center"
bg="blue.500"
color="white"
ml="6"
mt="-3"
w="12"
zIndex={2}
>
{form.lifetime / 60}m
</SliderMark>
<SliderTrack>
<SliderFilledTrack />
</SliderTrack>
<SliderThumb />
</Slider>
<SimpleGrid minChildWidth="320px" spacing={2} mt={4}>
<Box>
<FormLabel>{t('controller.telemetry.output')}</FormLabel>
<Select
value={form.kafka ? 'kafka' : ''}
onChange={(e) =>
setForm({
...form,
kafka: e.target.value === 'kafka',
})
}
w="160px"
>
<option value="">{t('controller.telemetry.websocket')}</option>
<option value="kafka">{t('controller.telemetry.kafka')}</option>
</Select>
</Box>
<Box>
<FormLabel>{t('controller.telemetry.types')}</FormLabel>
<Stack spacing={5} direction="row" mt={4}>
<Checkbox
colorScheme="blue"
isChecked={form.types.includes('wifi-frames')}
onChange={onToggle('wifi-frames')}
>
WiFi Frames
</Checkbox>
<Checkbox
colorScheme="green"
isChecked={form.types.includes('dhcp-snooping')}
onChange={onToggle('dhcp-snooping')}
>
DHCP Snooping
</Checkbox>
<Checkbox colorScheme="purple" isChecked={form.types.includes('state')} onChange={onToggle('state')}>
State
</Checkbox>
</Stack>
</Box>
</SimpleGrid>
{form.types.length === 0 && (
<Center>
<Alert status="error" mt={4} w="unset">
<AlertIcon />
<Box>
<AlertTitle>{t('common.error')}</AlertTitle>
<AlertDescription>{t('controller.telemetry.need_types')}</AlertDescription>
</Box>
</Alert>
</Center>
)}
</>
)}
</>
</Modal>
);
};
export const TelemetryModal = React.memo(_TelemetryModal);