[WIFI-12413] Added toast on download trace/script result error

Signed-off-by: Charles <charles.bourque96@gmail.com>
This commit is contained in:
Charles
2023-03-17 10:23:33 +01:00
parent 65e9e64cb4
commit 09184b0402
35 changed files with 265 additions and 210 deletions

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "ucentral-client",
"version": "2.9.0(18)",
"version": "2.9.0(22)",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "ucentral-client",
"version": "2.9.0(18)",
"version": "2.9.0(22)",
"license": "ISC",
"dependencies": {
"@chakra-ui/icons": "^2.0.11",

View File

@@ -1,13 +1,13 @@
{
"name": "ucentral-client",
"version": "2.9.0(18)",
"version": "2.9.0(22)",
"description": "",
"private": true,
"main": "index.tsx",
"scripts": {
"dev": "vite",
"build": "vite build",
"format": "prettier --write \"src/**/*.js\"",
"format": "prettier --write \"src/**/*x.{ts,tsx,js,jsx}\"",
"analyze": "source-map-explorer 'build/static/js/*.js'",
"lint": "TIMING=1 eslint \"src/**/*.{ts,tsx,js,jsx}\" --fix",
"clean": "rm -rf node_modules && rm -rf build"

View File

@@ -12,7 +12,14 @@ export interface AlertButtonProps extends ThemeProps {
label?: string;
}
const _AlertButton: React.FC<AlertButtonProps> = ({ onClick, isDisabled, isLoading, isCompact, label, ...props }) => {
const _AlertButton: React.FC<AlertButtonProps> = ({
onClick,
isDisabled,
isLoading,
isCompact = true,
label,
...props
}) => {
const { t } = useTranslation();
const breakpoint = useBreakpoint();

View File

@@ -11,7 +11,14 @@ export interface CreateButtonProps extends SpaceProps {
label?: string;
}
const _CreateButton: React.FC<CreateButtonProps> = ({ onClick, isDisabled, isLoading, isCompact, label, ...props }) => {
const _CreateButton: React.FC<CreateButtonProps> = ({
onClick,
isDisabled,
isLoading,
isCompact = true,
label,
...props
}) => {
const { t } = useTranslation();
const breakpoint = useBreakpoint();

View File

@@ -16,7 +16,7 @@ const _DeleteButton: React.FC<DeleteButtonProps> = ({
onClick,
isDisabled,
isLoading,
isCompact,
isCompact = true,
label,
ml,
...props

View File

@@ -51,7 +51,7 @@ const DeviceActionDropdown = ({
onOpenScriptModal,
onOpenRebootModal,
size,
isCompact,
isCompact = true,
}: Props) => {
const { t } = useTranslation();
const toast = useToast();
@@ -163,7 +163,7 @@ const DeviceActionDropdown = ({
return (
<Menu>
<Tooltip label={t('commands.other')}>
<Tooltip label={t('common.actions')}>
{size === undefined || isCompact ? (
<MenuButton
as={IconButton}
@@ -182,7 +182,7 @@ const DeviceActionDropdown = ({
isDisabled={isDisabled}
ml={2}
>
{t('commands.other')}
{t('common.actions')}
</MenuButton>
)}
</Tooltip>

View File

@@ -1,6 +1,7 @@
import React from 'react';
import { IconButton, Button, Tooltip, useBreakpoint } from '@chakra-ui/react';
import { Pen } from 'phosphor-react';
import { useTranslation } from 'react-i18next';
export interface EditButtonProps {
onClick: () => void;
@@ -11,7 +12,15 @@ export interface EditButtonProps {
ml?: string | number;
}
const _EditButton: React.FC<EditButtonProps> = ({ onClick, label, isDisabled, isLoading, isCompact, ...props }) => {
const _EditButton: React.FC<EditButtonProps> = ({
onClick,
label,
isDisabled,
isLoading,
isCompact = true,
...props
}) => {
const { t } = useTranslation();
const breakpoint = useBreakpoint();
if (!isCompact && breakpoint !== 'base' && breakpoint !== 'sm') {
@@ -24,12 +33,12 @@ const _EditButton: React.FC<EditButtonProps> = ({ onClick, label, isDisabled, is
isDisabled={isDisabled}
{...props}
>
{label}
{label ?? t('common.edit')}
</Button>
);
}
return (
<Tooltip label={label}>
<Tooltip label={label ?? t('common.edit')} hasArrow>
<IconButton
aria-label="edit"
colorScheme="gray"

View File

@@ -17,7 +17,7 @@ const _RefreshButton: React.FC<RefreshButtonProps> = ({
onClick,
isDisabled,
isFetching,
isCompact,
isCompact = true,
ml,
size,
...props

View File

@@ -15,7 +15,7 @@ const _ResponsiveButton: React.FC<ResponsiveButtonProps> = ({
onClick,
isDisabled,
isLoading,
isCompact,
isCompact = true,
color,
label,
icon,

View File

@@ -18,7 +18,7 @@ const _SaveButton: React.FC<SaveButtonProps> = ({
onClick,
isDisabled,
isLoading,
isCompact,
isCompact = true,
isDirty,
dirtyCheck,
...props

View File

@@ -20,7 +20,7 @@ const _ToggleEditButton: React.FC<ToggleEditButtonProps> = ({
isDirty,
isDisabled,
isLoading,
isCompact,
isCompact = true,
ml,
...props
}) => {

View File

@@ -16,7 +16,7 @@ const _WarningButton: React.FC<WarningButtonProps> = ({
onClick,
isDisabled,
isLoading,
isCompact,
isCompact = true,
label,
...props
}) => {

View File

@@ -21,7 +21,7 @@ export const ColumnPicker = ({
hiddenColumns,
setHiddenColumns,
size,
isCompact,
isCompact = true,
}: ColumnPickerProps) => {
const { t } = useTranslation();
const { getPref, setPref } = useAuth();

View File

@@ -57,7 +57,7 @@ export const WifiScanModal = ({ modalProps: { isOpen, onClose }, serialNumber }:
if (isOpen) resetData();
}, [isOpen]);
return (
(<Modal onClose={closeModal} isOpen={isOpen} size="xl" scrollBehavior="inside">
<Modal onClose={closeModal} isOpen={isOpen} size="xl" scrollBehavior="inside">
<ModalOverlay />
<ModalContent maxWidth={{ sm: '600px', md: '700px', lg: '800px', xl: '50%' }}>
<ModalHeader
@@ -66,7 +66,7 @@ export const WifiScanModal = ({ modalProps: { isOpen, onClose }, serialNumber }:
<>
{csvData ? (
// @ts-ignore
(<CSVLink
<CSVLink
filename={`wifi_scan_${serialNumber}_${dateForFilename(new Date().getTime() / 1000)}.csv`}
data={csvData as object[]}
>
@@ -77,7 +77,7 @@ export const WifiScanModal = ({ modalProps: { isOpen, onClose }, serialNumber }:
label={t('common.download')}
onClick={() => {}}
/>
</CSVLink>)
</CSVLink>
) : (
<ResponsiveButton
color="gray"
@@ -118,6 +118,6 @@ export const WifiScanModal = ({ modalProps: { isOpen, onClose }, serialNumber }:
confirm={closeCancelAndForm}
cancel={closeConfirm}
/>
</Modal>)
</Modal>
);
};

View File

@@ -276,8 +276,11 @@ export const useDeviceScript = ({ serialNumber }: { serialNumber: string }) => {
const downloadScript = (serialNumber: string, commandId: string) =>
axiosGw.get(`file/${commandId}?serialNumber=${serialNumber}`, { responseType: 'arraybuffer' });
export const useDownloadScriptResult = ({ serialNumber, commandId }: { serialNumber: string; commandId: string }) =>
useQuery(['download-script', serialNumber, commandId], () => downloadScript(serialNumber, commandId), {
export const useDownloadScriptResult = ({ serialNumber, commandId }: { serialNumber: string; commandId: string }) => {
const { t } = useTranslation();
const toast = useToast();
return useQuery(['download-script', serialNumber, commandId], () => downloadScript(serialNumber, commandId), {
enabled: false,
onSuccess: (response) => {
const blob = new Blob([response.data], { type: 'application/octet-stream' });
@@ -290,4 +293,27 @@ export const useDownloadScriptResult = ({ serialNumber, commandId }: { serialNum
link.download = filename;
link.click();
},
onError: (e) => {
if (axios.isAxiosError(e)) {
const bufferResponse = e.response?.data;
let errorMessage = '';
// If the response is a buffer, parse to JSON object
if (bufferResponse instanceof ArrayBuffer) {
const decoder = new TextDecoder('utf-8');
const json = JSON.parse(decoder.decode(bufferResponse));
errorMessage = json.ErrorDescription;
}
toast({
id: `script-download-error-${serialNumber}`,
title: t('common.error'),
description: errorMessage,
status: 'error',
duration: 5000,
isClosable: true,
position: 'top-right',
});
}
},
});
};

View File

@@ -1,5 +1,6 @@
import { useToast } from '@chakra-ui/react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';
import { useTranslation } from 'react-i18next';
import { axiosGw } from 'constants/axiosInstances';
@@ -85,8 +86,11 @@ export const useTrace = ({ serialNumber, alertOnCompletion }: { serialNumber: st
export const downloadTrace = (serialNumber: string, commandId: string) =>
axiosGw.get(`file/${commandId}?serialNumber=${serialNumber}`, { responseType: 'arraybuffer' });
export const useDownloadTrace = ({ serialNumber, commandId }: { serialNumber: string; commandId: string }) =>
useQuery(['download-trace', serialNumber, commandId], () => downloadTrace(serialNumber, commandId), {
export const useDownloadTrace = ({ serialNumber, commandId }: { serialNumber: string; commandId: string }) => {
const { t } = useTranslation();
const toast = useToast();
return useQuery(['download-trace', serialNumber, commandId], () => downloadTrace(serialNumber, commandId), {
enabled: false,
onSuccess: (response) => {
const blob = new Blob([response.data], { type: 'application/octet-stream' });
@@ -99,4 +103,27 @@ export const useDownloadTrace = ({ serialNumber, commandId }: { serialNumber: st
link.download = filename;
link.click();
},
onError: (e) => {
if (axios.isAxiosError(e)) {
const bufferResponse = e.response?.data;
let errorMessage = '';
// If the response is a buffer, parse to JSON object
if (bufferResponse instanceof ArrayBuffer) {
const decoder = new TextDecoder('utf-8');
const json = JSON.parse(decoder.decode(bufferResponse));
errorMessage = json.ErrorDescription;
}
toast({
id: `trace-download-error-${serialNumber}`,
title: t('common.error'),
description: errorMessage,
status: 'error',
duration: 5000,
isClosable: true,
position: 'top-right',
});
}
},
});
};

View File

@@ -44,13 +44,13 @@ const CommandHistory = ({ serialNumber }: Props) => {
<Box textAlign="right" display="flex">
<Spacer />
<HStack>
<HistoryDatePickers defaults={time} setTime={setNewTime} onClear={onClear} />
<ColumnPicker
columns={columns as Column<unknown>[]}
hiddenColumns={hiddenColumns}
setHiddenColumns={setHiddenColumns}
preference="gateway.device.commandshistory.hiddenColumns"
/>
<HistoryDatePickers defaults={time} setTime={setNewTime} onClear={onClear} />
<RefreshButton
isCompact
isFetching={getCommands.isFetching}

View File

@@ -99,16 +99,6 @@ const useCommandHistoryTable = ({ serialNumber, limit }: Props) => {
const actionCell = React.useCallback(
(command: DeviceCommandHistory) => (
<HStack>
<Tooltip label={t('common.view_details')}>
<IconButton
aria-label={t('common.view_details')}
onClick={onOpenDetails(command)}
colorScheme="blue"
icon={<MagnifyingGlass size={20} />}
size="sm"
isLoading={loadingDeleteSerial === command.UUID}
/>
</Tooltip>
<Tooltip label={t('crud.delete')}>
<IconButton
aria-label={t('crud.delete')}
@@ -119,6 +109,16 @@ const useCommandHistoryTable = ({ serialNumber, limit }: Props) => {
isLoading={loadingDeleteSerial === command.UUID}
/>
</Tooltip>
<Tooltip label={t('common.view_details')}>
<IconButton
aria-label={t('common.view_details')}
onClick={onOpenDetails(command)}
colorScheme="blue"
icon={<MagnifyingGlass size={20} />}
size="sm"
isLoading={loadingDeleteSerial === command.UUID}
/>
</Tooltip>
</HStack>
),
[loadingDeleteSerial],

View File

@@ -52,8 +52,6 @@ const DeviceStatisticsCard = ({ serialNumber }: Props) => {
<Heading size="md">{t('configurations.statistics')}</Heading>
<Spacer />
<HStack>
<ViewLastStatsModal serialNumber={serialNumber} />
<StatisticsCardDatePickers defaults={time} setTime={setNewTime} onClear={onClear} />
<Select value={selected} onChange={onSelectInterface}>
{parsedData?.interfaces
? Object.keys(parsedData.interfaces).map((v) => (
@@ -64,6 +62,8 @@ const DeviceStatisticsCard = ({ serialNumber }: Props) => {
: null}
<option value="memory">{t('statistics.memory')}</option>
</Select>
<StatisticsCardDatePickers defaults={time} setTime={setNewTime} onClear={onClear} />
<ViewLastStatsModal serialNumber={serialNumber} />
<RefreshButton
size="sm"
onClick={refresh}

View File

@@ -9,12 +9,15 @@ import {
Button,
Center,
Heading,
IconButton,
Spinner,
Tooltip,
useClipboard,
useColorMode,
useDisclosure,
} from '@chakra-ui/react';
import { JsonViewer } from '@textea/json-viewer';
import { ListDashes } from 'phosphor-react';
import { useTranslation } from 'react-i18next';
import { RefreshButton } from 'components/Buttons/RefreshButton';
import FormattedDate from 'components/InformationDisplays/FormattedDate';
@@ -43,9 +46,15 @@ const ViewCapabilitiesModal = ({ serialNumber }: Props) => {
return (
<>
<Button onClick={onOpen} colorScheme="pink" mr={2}>
{t('controller.devices.capabilities')}
</Button>
<Tooltip label={t('controller.devices.capabilities')} hasArrow>
<IconButton
aria-label={t('controller.devices.capabilities')}
icon={<ListDashes size={20} />}
onClick={onOpen}
colorScheme="pink"
mr={2}
/>
</Tooltip>
<Modal
isOpen={isOpen}
title={t('controller.devices.capabilities')}

View File

@@ -7,11 +7,14 @@ import {
AccordionPanel,
Box,
Button,
IconButton,
Tooltip,
useClipboard,
useColorMode,
useDisclosure,
} from '@chakra-ui/react';
import { JsonViewer } from '@textea/json-viewer';
import { Barcode } from 'phosphor-react';
import { useTranslation } from 'react-i18next';
import { Modal } from 'components/Modals/Modal';
import { DeviceConfiguration } from 'models/Device';
@@ -30,9 +33,14 @@ const ViewConfigurationModal = ({ configuration }: { configuration?: DeviceConfi
return (
<>
<Button onClick={onOpen} isDisabled={!configuration} colorScheme="purple">
{t('configurations.one')}
</Button>
<Tooltip label={t('configurations.one')} hasArrow>
<IconButton
aria-label={t('configurations.one')}
icon={<Barcode size={20} />}
onClick={onOpen}
colorScheme="purple"
/>
</Tooltip>
<Modal
isOpen={isOpen}
title={t('configurations.one')}

View File

@@ -1,18 +1,15 @@
import * as React from 'react';
import {
AlertDialog,
AlertDialogBody,
AlertDialogContent,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogOverlay,
Box,
Button,
Center,
Heading,
HStack,
Popover,
PopoverArrow,
PopoverBody,
PopoverCloseButton,
PopoverContent,
PopoverFooter,
PopoverHeader,
PopoverTrigger,
Portal,
Spacer,
Tag,
@@ -62,6 +59,7 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
const { t } = useTranslation();
const toast = useToast();
const breakpoint = useBreakpoint();
const cancelRef = React.useRef(null);
const navigate = useNavigate();
const { mutateAsync: deleteDevice, isLoading: isDeleting } = useDeleteDevice({
serialNumber,
@@ -197,7 +195,7 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
<Spacer />
<HStack spacing={2}>
{breakpoint !== 'base' && breakpoint !== 'md' && <DeviceSearchBar />}
<DeleteButton isCompact onClick={onDeleteOpen} />
{getDevice?.data && (
<DeviceActionDropdown
// @ts-ignore
@@ -216,33 +214,6 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
isCompact
/>
)}
<Popover isOpen={isDeleteOpen} onOpen={onDeleteOpen} onClose={onDeleteClose}>
<Tooltip hasArrow label={t('crud.delete')} placement="top" isDisabled={isDeleteOpen}>
<Box>
<PopoverTrigger>
<DeleteButton isCompact onClick={() => {}} />
</PopoverTrigger>
</Box>
</Tooltip>
<PopoverContent>
<PopoverArrow />
<PopoverCloseButton />
<PopoverHeader>
{t('crud.delete')} {serialNumber}
</PopoverHeader>
<PopoverBody>{t('crud.delete_confirm', { obj: t('devices.one') })}</PopoverBody>
<PopoverFooter>
<Center>
<Button colorScheme="gray" mr="1" onClick={onDeleteClose}>
{t('common.cancel')}
</Button>
<Button colorScheme="red" ml="1" onClick={handleDeleteClick} isLoading={isDeleting}>
{t('common.yes')}
</Button>
</Center>
</PopoverFooter>
</PopoverContent>
</Popover>
<RefreshButton
onClick={refresh}
isFetching={getDevice.isFetching || getHealth.isFetching || getStatus.isFetching}
@@ -274,6 +245,7 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
<Spacer />
<HStack spacing={2}>
<DeviceSearchBar />
<DeleteButton isCompact onClick={onDeleteOpen} />
{getDevice?.data && (
<DeviceActionDropdown
// @ts-ignore
@@ -289,35 +261,9 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
onOpenRebootModal={rebootModalProps.onOpen}
onOpenScriptModal={scriptModal.openModal}
size="md"
isCompact
/>
)}
<Popover isOpen={isDeleteOpen} onOpen={onDeleteOpen} onClose={onDeleteClose}>
<Tooltip hasArrow label={t('crud.delete')} placement="top" isDisabled={isDeleteOpen}>
<Box>
<PopoverTrigger>
<DeleteButton isCompact onClick={() => {}} />
</PopoverTrigger>
</Box>
</Tooltip>
<PopoverContent>
<PopoverArrow />
<PopoverCloseButton />
<PopoverHeader>
{t('crud.delete')} {serialNumber}
</PopoverHeader>
<PopoverBody>{t('crud.delete_confirm', { obj: t('devices.one') })}</PopoverBody>
<PopoverFooter>
<Center>
<Button colorScheme="gray" mr="1" onClick={onDeleteClose}>
{t('common.cancel')}
</Button>
<Button colorScheme="red" ml="1" onClick={handleDeleteClick} isLoading={isDeleting}>
{t('common.yes')}
</Button>
</Center>
</PopoverFooter>
</PopoverContent>
</Popover>
<RefreshButton
onClick={refresh}
isFetching={getDevice.isFetching || getHealth.isFetching || getStatus.isFetching}
@@ -330,6 +276,24 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
</Card>
</Portal>
)}
<AlertDialog isOpen={isDeleteOpen} leastDestructiveRef={cancelRef} onClose={onDeleteClose}>
<AlertDialogOverlay>
<AlertDialogContent>
<AlertDialogHeader fontSize="lg" fontWeight="bold">
{t('crud.delete')} {serialNumber}
</AlertDialogHeader>
<AlertDialogBody>{t('crud.delete_confirm', { obj: t('devices.one') })}</AlertDialogBody>
<AlertDialogFooter>
<Button colorScheme="gray" mr="1" onClick={onDeleteClose} ref={cancelRef}>
{t('common.cancel')}
</Button>
<Button colorScheme="red" ml="1" onClick={handleDeleteClick} isLoading={isDeleting}>
{t('common.yes')}
</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialogOverlay>
</AlertDialog>
<WifiScanModal modalProps={scanModalProps} serialNumber={serialNumber} />
<FirmwareUpgradeModal modalProps={upgradeModalProps} serialNumber={serialNumber} />
<FactoryResetModal modalProps={resetModalProps} serialNumber={serialNumber} />

View File

@@ -7,9 +7,11 @@ import {
Box,
Button,
Center,
IconButton,
Tag,
TagLabel,
Text,
Tooltip,
useDisclosure,
useToast,
} from '@chakra-ui/react';
@@ -58,9 +60,14 @@ const UpdateDbButton = () => {
return (
<>
<Button colorScheme="teal" leftIcon={<Database size={20} />} onClick={onOpen}>
{t('firmware.last_db_update_title')}
</Button>
<Tooltip label={t('firmware.last_db_update_title')}>
<IconButton
aria-label={t('firmware.last_db_update_title')}
colorScheme="teal"
icon={<Database size={20} />}
onClick={onOpen}
/>
</Tooltip>
<Modal
isOpen={isOpen}
onClose={onClose}

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import { Box, Button, Flex, HStack, Select, Spacer, Table, Text, Th, Thead, Tr } from '@chakra-ui/react';
import { Box, Flex, HStack, IconButton, Select, Spacer, Table, Text, Th, Thead, Tooltip, Tr } from '@chakra-ui/react';
import { Download } from 'phosphor-react';
import { CSVLink } from 'react-csv';
import { useTranslation } from 'react-i18next';
@@ -127,9 +127,9 @@ const LogsCard = () => {
filename={`logs_${dateForFilename(new Date().getTime() / 1000)}.csv`}
data={downloadableLogs as object[]}
>
<Button onClick={() => {}} colorScheme="blue" leftIcon={<Download />}>
{t('logs.export')}
</Button>
<Tooltip label={t('logs.export')} hasArrow>
<IconButton aria-label={t('logs.export')} icon={<Download />} colorScheme="blue" />
</Tooltip>
</CSVLink>
</HStack>
</CardHeader>

View File

@@ -1,5 +1,19 @@
import * as React from 'react';
import { Badge, Box, Button, Flex, HStack, Select, Spacer, Table, Text, Th, Thead, Tr } from '@chakra-ui/react';
import {
Badge,
Box,
Flex,
HStack,
IconButton,
Select,
Spacer,
Table,
Text,
Th,
Thead,
Tooltip,
Tr,
} from '@chakra-ui/react';
import { Download } from 'phosphor-react';
import { CSVLink } from 'react-csv';
import { useTranslation } from 'react-i18next';
@@ -128,9 +142,9 @@ const FmsLogsCard = () => {
filename={`logs_${dateForFilename(new Date().getTime() / 1000)}.csv`}
data={downloadableLogs as object[]}
>
<Button onClick={() => {}} colorScheme="blue" leftIcon={<Download />}>
{t('logs.export')}
</Button>
<Tooltip label={t('logs.export')} hasArrow>
<IconButton aria-label={t('logs.export')} icon={<Download />} colorScheme="blue" />
</Tooltip>
</CSVLink>
</HStack>
</CardHeader>

View File

@@ -1,5 +1,19 @@
import * as React from 'react';
import { Badge, Box, Button, Flex, HStack, Select, Spacer, Table, Text, Th, Thead, Tr } from '@chakra-ui/react';
import {
Badge,
Box,
Flex,
HStack,
IconButton,
Select,
Spacer,
Table,
Text,
Th,
Thead,
Tooltip,
Tr,
} from '@chakra-ui/react';
import { Download } from 'phosphor-react';
import { CSVLink } from 'react-csv';
import { useTranslation } from 'react-i18next';
@@ -128,9 +142,9 @@ const GeneralLogsCard = () => {
filename={`logs_${dateForFilename(new Date().getTime() / 1000)}.csv`}
data={downloadableLogs as object[]}
>
<Button onClick={() => {}} colorScheme="blue" leftIcon={<Download />}>
{t('logs.export')}
</Button>
<Tooltip label={t('logs.export')} hasArrow>
<IconButton aria-label={t('logs.export')} icon={<Download />} colorScheme="blue" />
</Tooltip>
</CSVLink>
</HStack>
</CardHeader>

View File

@@ -1,5 +1,19 @@
import * as React from 'react';
import { Badge, Box, Button, Flex, HStack, Select, Spacer, Table, Text, Th, Thead, Tr } from '@chakra-ui/react';
import {
Badge,
Box,
Flex,
HStack,
IconButton,
Select,
Spacer,
Table,
Text,
Th,
Thead,
Tooltip,
Tr,
} from '@chakra-ui/react';
import { Download } from 'phosphor-react';
import { CSVLink } from 'react-csv';
import { useTranslation } from 'react-i18next';
@@ -128,9 +142,9 @@ const SecLogsCard = () => {
filename={`logs_${dateForFilename(new Date().getTime() / 1000)}.csv`}
data={downloadableLogs as object[]}
>
<Button onClick={() => {}} colorScheme="blue" leftIcon={<Download />}>
{t('logs.export')}
</Button>
<Tooltip label={t('logs.export')} hasArrow>
<IconButton aria-label={t('logs.export')} icon={<Download />} colorScheme="blue" />
</Tooltip>
</CSVLink>
</HStack>
</CardHeader>

View File

@@ -1,4 +1,5 @@
import * as React from 'react';
import { Box } from '@chakra-ui/react';
import ApiKeyTable from './Table';
import { Card } from 'components/Containers/Card';
import { CardBody } from 'components/Containers/Card/CardBody';
@@ -10,7 +11,9 @@ const ApiKeysCard = () => {
return (
<Card p={4}>
<CardBody>
<ApiKeyTable userId={user?.id ?? ''} />
<Box w="100%">
<ApiKeyTable userId={user?.id ?? ''} />
</Box>
</CardBody>
</Card>
);

View File

@@ -1,5 +1,6 @@
import * as React from 'react';
import { Button, useDisclosure } from '@chakra-ui/react';
import { IconButton, Tooltip, useDisclosure } from '@chakra-ui/react';
import { Article } from 'phosphor-react';
import { useTranslation } from 'react-i18next';
import SystemLoggingModal from './Modal';
import { EndpointApiResponse } from 'hooks/Network/Endpoints';
@@ -15,9 +16,17 @@ const SystemLoggingButton = ({ endpoint, token }: Props) => {
return (
<>
<Button colorScheme="teal" onClick={modalProps.onOpen} mr={2} my="auto">
{t('system.logging')}
</Button>
<Tooltip label={t('system.logging')} hasArrow>
<IconButton
aria-label={t('system.logging')}
colorScheme="teal"
type="button"
my="auto"
onClick={modalProps.onOpen}
icon={<Article size={20} />}
mr={2}
/>
</Tooltip>
<SystemLoggingModal modalProps={modalProps} endpoint={endpoint.uri} token={token} />
</>
);

View File

@@ -22,6 +22,7 @@ import { useTranslation } from 'react-i18next';
import FormattedDate from '../../../components/InformationDisplays/FormattedDate';
import SystemLoggingButton from './LoggingButton';
import SystemCertificatesTable from './SystemCertificatesTable';
import { RefreshButton } from 'components/Buttons/RefreshButton';
import { Card } from 'components/Containers/Card';
import { CardBody } from 'components/Containers/Card/CardBody';
import { compactSecondsToDetailed } from 'helpers/dateFormatting';
@@ -70,16 +71,7 @@ const SystemTile = ({ endpoint, token }: Props) => {
<Heading pt={0}>{endpoint.type}</Heading>
<Spacer />
<SystemLoggingButton endpoint={endpoint} token={token} />
<Button
mt={1}
minWidth="112px"
colorScheme="blue"
rightIcon={<ArrowsClockwise />}
onClick={refresh}
isLoading={isFetchingSystem || isFetchingSubsystems}
>
{t('common.refresh')}
</Button>
<RefreshButton onClick={refresh} isFetching={isFetchingSystem || isFetchingSubsystems} />
</Box>
<CardBody>
<VStack w="100%">

View File

@@ -77,7 +77,7 @@ const UserActions = ({ id, isSuspended, isWaitingForCheck, refresh, size = 'sm',
return (
<Menu>
<Tooltip label={t('commands.other')}>
<Tooltip label={t('common.actions')}>
<MenuButton
as={IconButton}
aria-label="Commands"

View File

@@ -1,11 +1,11 @@
import * as React from 'react';
import { AddIcon } from '@chakra-ui/icons';
import { Button, useDisclosure } from '@chakra-ui/react';
import { useDisclosure } from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
import { SaveButton } from '../../../../components/Buttons/SaveButton';
import { ConfirmCloseAlertModal } from '../../../../components/Modals/ConfirmCloseAlert';
import { Modal } from '../../../../components/Modals/Modal';
import CreateUserForm, { CreateUserFormValues } from './Form';
import { CreateButton } from 'components/Buttons/CreateButton';
import { useAuth } from 'contexts/AuthProvider';
import { useFormRef } from 'hooks/useFormRef';
@@ -25,16 +25,7 @@ const CreateUserModal = () => {
return (
<>
<Button
hidden={user?.userRole === 'csr'}
alignItems="center"
colorScheme="blue"
rightIcon={<AddIcon />}
onClick={onOpen}
ml={2}
>
{t('crud.create')}
</Button>
{user?.userRole === 'CSR' ? null : <CreateButton onClick={onOpen} ml={2} />}
<Modal
isOpen={isOpen}
onClose={closeModal}

View File

@@ -1,6 +1,5 @@
import React, { useCallback, useState } from 'react';
import { Avatar, Box, Button, Flex, useDisclosure } from '@chakra-ui/react';
import { ArrowsClockwise } from 'phosphor-react';
import { Avatar, Box, Flex, useDisclosure } from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
import { v4 as uuid } from 'uuid';
import { ColumnPicker } from '../../../components/DataTables/ColumnPicker';
@@ -9,6 +8,7 @@ import FormattedDate from '../../../components/InformationDisplays/FormattedDate
import CreateUserModal from './CreateUserModal';
import EditUserModal from './EditUserModal';
import UserActions from './UserActions';
import { RefreshButton } from 'components/Buttons/RefreshButton';
import { Card } from 'components/Containers/Card';
import { CardBody } from 'components/Containers/Card/CardBody';
import { CardHeader } from 'components/Containers/Card/CardHeader';
@@ -125,15 +125,7 @@ const UserTable = () => {
preference="provisioning.userTable.hiddenColumns"
/>
<CreateUserModal />
<Button
colorScheme="gray"
onClick={() => refreshUsers()}
rightIcon={<ArrowsClockwise />}
ml={2}
isLoading={isFetching}
>
{t('common.refresh')}
</Button>
<RefreshButton onClick={refreshUsers} isFetching={isFetching} ml={2} />
</Box>
</Flex>
</CardHeader>

View File

@@ -1,4 +1,4 @@
import { extendTheme, type ThemeConfig } from '@chakra-ui/react';
import { extendTheme, Tooltip, type ThemeConfig } from '@chakra-ui/react';
import CardComponent from './additions/card/Card';
import CardBodyComponent from './additions/card/CardBody';
import CardHeaderComponent from './additions/card/CardHeader';
@@ -37,4 +37,6 @@ const theme = extendTheme({
},
});
Tooltip.defaultProps = { ...Tooltip.defaultProps, hasArrow: true };
export default theme;

View File

@@ -1,49 +1,9 @@
import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';
import { VitePWA } from 'vite-plugin-pwa';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [
tsconfigPaths(),
react(),
VitePWA({
registerType: 'autoUpdate',
devOptions: {
enabled: true,
/* other options */
},
manifest: {
name: 'OpenWiFi Controller App',
short_name: 'OpenWiFiController',
description: 'OpenWiFi Controller App',
theme_color: '#000000',
icons: [
{
src: 'android-chrome-192x192.png',
sizes: '192x192',
type: 'image/png',
},
{
src: 'android-chrome-384x384.png',
sizes: '384x384',
type: 'image/png',
},
{
src: 'android-chrome-512x512.png',
sizes: '512x512',
type: 'image/png',
},
{
src: 'android-chrome-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'any maskable',
},
],
},
}),
],
plugins: [tsconfigPaths(), react()],
build: {
outDir: './build',
chunkSizeWarningLimit: 1000,