mirror of
https://github.com/optim-enterprises-bv/OptimCloud-gw-ui.git
synced 2025-10-29 09:22:21 +00:00
[WIFI-12413] Added toast on download trace/script result error
Signed-off-by: Charles <charles.bourque96@gmail.com>
This commit is contained in:
4
package-lock.json
generated
4
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ const _DeleteButton: React.FC<DeleteButtonProps> = ({
|
||||
onClick,
|
||||
isDisabled,
|
||||
isLoading,
|
||||
isCompact,
|
||||
isCompact = true,
|
||||
label,
|
||||
ml,
|
||||
...props
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -17,7 +17,7 @@ const _RefreshButton: React.FC<RefreshButtonProps> = ({
|
||||
onClick,
|
||||
isDisabled,
|
||||
isFetching,
|
||||
isCompact,
|
||||
isCompact = true,
|
||||
ml,
|
||||
size,
|
||||
...props
|
||||
|
||||
@@ -15,7 +15,7 @@ const _ResponsiveButton: React.FC<ResponsiveButtonProps> = ({
|
||||
onClick,
|
||||
isDisabled,
|
||||
isLoading,
|
||||
isCompact,
|
||||
isCompact = true,
|
||||
color,
|
||||
label,
|
||||
icon,
|
||||
|
||||
@@ -18,7 +18,7 @@ const _SaveButton: React.FC<SaveButtonProps> = ({
|
||||
onClick,
|
||||
isDisabled,
|
||||
isLoading,
|
||||
isCompact,
|
||||
isCompact = true,
|
||||
isDirty,
|
||||
dirtyCheck,
|
||||
...props
|
||||
|
||||
@@ -20,7 +20,7 @@ const _ToggleEditButton: React.FC<ToggleEditButtonProps> = ({
|
||||
isDirty,
|
||||
isDisabled,
|
||||
isLoading,
|
||||
isCompact,
|
||||
isCompact = true,
|
||||
ml,
|
||||
...props
|
||||
}) => {
|
||||
|
||||
@@ -16,7 +16,7 @@ const _WarningButton: React.FC<WarningButtonProps> = ({
|
||||
onClick,
|
||||
isDisabled,
|
||||
isLoading,
|
||||
isCompact,
|
||||
isCompact = true,
|
||||
label,
|
||||
...props
|
||||
}) => {
|
||||
|
||||
@@ -21,7 +21,7 @@ export const ColumnPicker = ({
|
||||
hiddenColumns,
|
||||
setHiddenColumns,
|
||||
size,
|
||||
isCompact,
|
||||
isCompact = true,
|
||||
}: ColumnPickerProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { getPref, setPref } = useAuth();
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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',
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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',
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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')}
|
||||
|
||||
@@ -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')}
|
||||
|
||||
@@ -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} />
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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} />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -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%">
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user