mirror of
https://github.com/optim-enterprises-bv/OptimCloud-gw-ui.git
synced 2025-11-02 19:27:45 +00:00
Merge pull request #168 from stephb9959/main
[WIFI-12413] Added toast on download trace/script result error
This commit is contained in:
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "ucentral-client",
|
"name": "ucentral-client",
|
||||||
"version": "2.9.0(18)",
|
"version": "2.9.0(22)",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "ucentral-client",
|
"name": "ucentral-client",
|
||||||
"version": "2.9.0(18)",
|
"version": "2.9.0(22)",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chakra-ui/icons": "^2.0.11",
|
"@chakra-ui/icons": "^2.0.11",
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "ucentral-client",
|
"name": "ucentral-client",
|
||||||
"version": "2.9.0(18)",
|
"version": "2.9.0(22)",
|
||||||
"description": "",
|
"description": "",
|
||||||
"private": true,
|
"private": true,
|
||||||
"main": "index.tsx",
|
"main": "index.tsx",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"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'",
|
"analyze": "source-map-explorer 'build/static/js/*.js'",
|
||||||
"lint": "TIMING=1 eslint \"src/**/*.{ts,tsx,js,jsx}\" --fix",
|
"lint": "TIMING=1 eslint \"src/**/*.{ts,tsx,js,jsx}\" --fix",
|
||||||
"clean": "rm -rf node_modules && rm -rf build"
|
"clean": "rm -rf node_modules && rm -rf build"
|
||||||
|
|||||||
@@ -12,7 +12,14 @@ export interface AlertButtonProps extends ThemeProps {
|
|||||||
label?: string;
|
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 { t } = useTranslation();
|
||||||
const breakpoint = useBreakpoint();
|
const breakpoint = useBreakpoint();
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,14 @@ export interface CreateButtonProps extends SpaceProps {
|
|||||||
label?: string;
|
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 { t } = useTranslation();
|
||||||
const breakpoint = useBreakpoint();
|
const breakpoint = useBreakpoint();
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const _DeleteButton: React.FC<DeleteButtonProps> = ({
|
|||||||
onClick,
|
onClick,
|
||||||
isDisabled,
|
isDisabled,
|
||||||
isLoading,
|
isLoading,
|
||||||
isCompact,
|
isCompact = true,
|
||||||
label,
|
label,
|
||||||
ml,
|
ml,
|
||||||
...props
|
...props
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ const DeviceActionDropdown = ({
|
|||||||
onOpenScriptModal,
|
onOpenScriptModal,
|
||||||
onOpenRebootModal,
|
onOpenRebootModal,
|
||||||
size,
|
size,
|
||||||
isCompact,
|
isCompact = true,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
@@ -163,7 +163,7 @@ const DeviceActionDropdown = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu>
|
<Menu>
|
||||||
<Tooltip label={t('commands.other')}>
|
<Tooltip label={t('common.actions')}>
|
||||||
{size === undefined || isCompact ? (
|
{size === undefined || isCompact ? (
|
||||||
<MenuButton
|
<MenuButton
|
||||||
as={IconButton}
|
as={IconButton}
|
||||||
@@ -182,7 +182,7 @@ const DeviceActionDropdown = ({
|
|||||||
isDisabled={isDisabled}
|
isDisabled={isDisabled}
|
||||||
ml={2}
|
ml={2}
|
||||||
>
|
>
|
||||||
{t('commands.other')}
|
{t('common.actions')}
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
)}
|
)}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { IconButton, Button, Tooltip, useBreakpoint } from '@chakra-ui/react';
|
import { IconButton, Button, Tooltip, useBreakpoint } from '@chakra-ui/react';
|
||||||
import { Pen } from 'phosphor-react';
|
import { Pen } from 'phosphor-react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
export interface EditButtonProps {
|
export interface EditButtonProps {
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
@@ -11,7 +12,15 @@ export interface EditButtonProps {
|
|||||||
ml?: string | number;
|
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();
|
const breakpoint = useBreakpoint();
|
||||||
|
|
||||||
if (!isCompact && breakpoint !== 'base' && breakpoint !== 'sm') {
|
if (!isCompact && breakpoint !== 'base' && breakpoint !== 'sm') {
|
||||||
@@ -24,12 +33,12 @@ const _EditButton: React.FC<EditButtonProps> = ({ onClick, label, isDisabled, is
|
|||||||
isDisabled={isDisabled}
|
isDisabled={isDisabled}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{label}
|
{label ?? t('common.edit')}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Tooltip label={label}>
|
<Tooltip label={label ?? t('common.edit')} hasArrow>
|
||||||
<IconButton
|
<IconButton
|
||||||
aria-label="edit"
|
aria-label="edit"
|
||||||
colorScheme="gray"
|
colorScheme="gray"
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const _RefreshButton: React.FC<RefreshButtonProps> = ({
|
|||||||
onClick,
|
onClick,
|
||||||
isDisabled,
|
isDisabled,
|
||||||
isFetching,
|
isFetching,
|
||||||
isCompact,
|
isCompact = true,
|
||||||
ml,
|
ml,
|
||||||
size,
|
size,
|
||||||
...props
|
...props
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const _ResponsiveButton: React.FC<ResponsiveButtonProps> = ({
|
|||||||
onClick,
|
onClick,
|
||||||
isDisabled,
|
isDisabled,
|
||||||
isLoading,
|
isLoading,
|
||||||
isCompact,
|
isCompact = true,
|
||||||
color,
|
color,
|
||||||
label,
|
label,
|
||||||
icon,
|
icon,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const _SaveButton: React.FC<SaveButtonProps> = ({
|
|||||||
onClick,
|
onClick,
|
||||||
isDisabled,
|
isDisabled,
|
||||||
isLoading,
|
isLoading,
|
||||||
isCompact,
|
isCompact = true,
|
||||||
isDirty,
|
isDirty,
|
||||||
dirtyCheck,
|
dirtyCheck,
|
||||||
...props
|
...props
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const _ToggleEditButton: React.FC<ToggleEditButtonProps> = ({
|
|||||||
isDirty,
|
isDirty,
|
||||||
isDisabled,
|
isDisabled,
|
||||||
isLoading,
|
isLoading,
|
||||||
isCompact,
|
isCompact = true,
|
||||||
ml,
|
ml,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const _WarningButton: React.FC<WarningButtonProps> = ({
|
|||||||
onClick,
|
onClick,
|
||||||
isDisabled,
|
isDisabled,
|
||||||
isLoading,
|
isLoading,
|
||||||
isCompact,
|
isCompact = true,
|
||||||
label,
|
label,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export const ColumnPicker = ({
|
|||||||
hiddenColumns,
|
hiddenColumns,
|
||||||
setHiddenColumns,
|
setHiddenColumns,
|
||||||
size,
|
size,
|
||||||
isCompact,
|
isCompact = true,
|
||||||
}: ColumnPickerProps) => {
|
}: ColumnPickerProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { getPref, setPref } = useAuth();
|
const { getPref, setPref } = useAuth();
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export const WifiScanModal = ({ modalProps: { isOpen, onClose }, serialNumber }:
|
|||||||
if (isOpen) resetData();
|
if (isOpen) resetData();
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
return (
|
return (
|
||||||
(<Modal onClose={closeModal} isOpen={isOpen} size="xl" scrollBehavior="inside">
|
<Modal onClose={closeModal} isOpen={isOpen} size="xl" scrollBehavior="inside">
|
||||||
<ModalOverlay />
|
<ModalOverlay />
|
||||||
<ModalContent maxWidth={{ sm: '600px', md: '700px', lg: '800px', xl: '50%' }}>
|
<ModalContent maxWidth={{ sm: '600px', md: '700px', lg: '800px', xl: '50%' }}>
|
||||||
<ModalHeader
|
<ModalHeader
|
||||||
@@ -66,7 +66,7 @@ export const WifiScanModal = ({ modalProps: { isOpen, onClose }, serialNumber }:
|
|||||||
<>
|
<>
|
||||||
{csvData ? (
|
{csvData ? (
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
(<CSVLink
|
<CSVLink
|
||||||
filename={`wifi_scan_${serialNumber}_${dateForFilename(new Date().getTime() / 1000)}.csv`}
|
filename={`wifi_scan_${serialNumber}_${dateForFilename(new Date().getTime() / 1000)}.csv`}
|
||||||
data={csvData as object[]}
|
data={csvData as object[]}
|
||||||
>
|
>
|
||||||
@@ -77,7 +77,7 @@ export const WifiScanModal = ({ modalProps: { isOpen, onClose }, serialNumber }:
|
|||||||
label={t('common.download')}
|
label={t('common.download')}
|
||||||
onClick={() => {}}
|
onClick={() => {}}
|
||||||
/>
|
/>
|
||||||
</CSVLink>)
|
</CSVLink>
|
||||||
) : (
|
) : (
|
||||||
<ResponsiveButton
|
<ResponsiveButton
|
||||||
color="gray"
|
color="gray"
|
||||||
@@ -118,6 +118,6 @@ export const WifiScanModal = ({ modalProps: { isOpen, onClose }, serialNumber }:
|
|||||||
confirm={closeCancelAndForm}
|
confirm={closeCancelAndForm}
|
||||||
cancel={closeConfirm}
|
cancel={closeConfirm}
|
||||||
/>
|
/>
|
||||||
</Modal>)
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -276,8 +276,11 @@ export const useDeviceScript = ({ serialNumber }: { serialNumber: string }) => {
|
|||||||
const downloadScript = (serialNumber: string, commandId: string) =>
|
const downloadScript = (serialNumber: string, commandId: string) =>
|
||||||
axiosGw.get(`file/${commandId}?serialNumber=${serialNumber}`, { responseType: 'arraybuffer' });
|
axiosGw.get(`file/${commandId}?serialNumber=${serialNumber}`, { responseType: 'arraybuffer' });
|
||||||
|
|
||||||
export const useDownloadScriptResult = ({ serialNumber, commandId }: { serialNumber: string; commandId: string }) =>
|
export const useDownloadScriptResult = ({ serialNumber, commandId }: { serialNumber: string; commandId: string }) => {
|
||||||
useQuery(['download-script', serialNumber, commandId], () => downloadScript(serialNumber, commandId), {
|
const { t } = useTranslation();
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
|
return useQuery(['download-script', serialNumber, commandId], () => downloadScript(serialNumber, commandId), {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
onSuccess: (response) => {
|
onSuccess: (response) => {
|
||||||
const blob = new Blob([response.data], { type: 'application/octet-stream' });
|
const blob = new Blob([response.data], { type: 'application/octet-stream' });
|
||||||
@@ -290,4 +293,27 @@ export const useDownloadScriptResult = ({ serialNumber, commandId }: { serialNum
|
|||||||
link.download = filename;
|
link.download = filename;
|
||||||
link.click();
|
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 { useToast } from '@chakra-ui/react';
|
||||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
|
import axios from 'axios';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { axiosGw } from 'constants/axiosInstances';
|
import { axiosGw } from 'constants/axiosInstances';
|
||||||
|
|
||||||
@@ -85,8 +86,11 @@ export const useTrace = ({ serialNumber, alertOnCompletion }: { serialNumber: st
|
|||||||
export const downloadTrace = (serialNumber: string, commandId: string) =>
|
export const downloadTrace = (serialNumber: string, commandId: string) =>
|
||||||
axiosGw.get(`file/${commandId}?serialNumber=${serialNumber}`, { responseType: 'arraybuffer' });
|
axiosGw.get(`file/${commandId}?serialNumber=${serialNumber}`, { responseType: 'arraybuffer' });
|
||||||
|
|
||||||
export const useDownloadTrace = ({ serialNumber, commandId }: { serialNumber: string; commandId: string }) =>
|
export const useDownloadTrace = ({ serialNumber, commandId }: { serialNumber: string; commandId: string }) => {
|
||||||
useQuery(['download-trace', serialNumber, commandId], () => downloadTrace(serialNumber, commandId), {
|
const { t } = useTranslation();
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
|
return useQuery(['download-trace', serialNumber, commandId], () => downloadTrace(serialNumber, commandId), {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
onSuccess: (response) => {
|
onSuccess: (response) => {
|
||||||
const blob = new Blob([response.data], { type: 'application/octet-stream' });
|
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.download = filename;
|
||||||
link.click();
|
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">
|
<Box textAlign="right" display="flex">
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<HStack>
|
<HStack>
|
||||||
<HistoryDatePickers defaults={time} setTime={setNewTime} onClear={onClear} />
|
|
||||||
<ColumnPicker
|
<ColumnPicker
|
||||||
columns={columns as Column<unknown>[]}
|
columns={columns as Column<unknown>[]}
|
||||||
hiddenColumns={hiddenColumns}
|
hiddenColumns={hiddenColumns}
|
||||||
setHiddenColumns={setHiddenColumns}
|
setHiddenColumns={setHiddenColumns}
|
||||||
preference="gateway.device.commandshistory.hiddenColumns"
|
preference="gateway.device.commandshistory.hiddenColumns"
|
||||||
/>
|
/>
|
||||||
|
<HistoryDatePickers defaults={time} setTime={setNewTime} onClear={onClear} />
|
||||||
<RefreshButton
|
<RefreshButton
|
||||||
isCompact
|
isCompact
|
||||||
isFetching={getCommands.isFetching}
|
isFetching={getCommands.isFetching}
|
||||||
|
|||||||
@@ -99,16 +99,6 @@ const useCommandHistoryTable = ({ serialNumber, limit }: Props) => {
|
|||||||
const actionCell = React.useCallback(
|
const actionCell = React.useCallback(
|
||||||
(command: DeviceCommandHistory) => (
|
(command: DeviceCommandHistory) => (
|
||||||
<HStack>
|
<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')}>
|
<Tooltip label={t('crud.delete')}>
|
||||||
<IconButton
|
<IconButton
|
||||||
aria-label={t('crud.delete')}
|
aria-label={t('crud.delete')}
|
||||||
@@ -119,6 +109,16 @@ const useCommandHistoryTable = ({ serialNumber, limit }: Props) => {
|
|||||||
isLoading={loadingDeleteSerial === command.UUID}
|
isLoading={loadingDeleteSerial === command.UUID}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</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>
|
</HStack>
|
||||||
),
|
),
|
||||||
[loadingDeleteSerial],
|
[loadingDeleteSerial],
|
||||||
|
|||||||
@@ -52,8 +52,6 @@ const DeviceStatisticsCard = ({ serialNumber }: Props) => {
|
|||||||
<Heading size="md">{t('configurations.statistics')}</Heading>
|
<Heading size="md">{t('configurations.statistics')}</Heading>
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<HStack>
|
<HStack>
|
||||||
<ViewLastStatsModal serialNumber={serialNumber} />
|
|
||||||
<StatisticsCardDatePickers defaults={time} setTime={setNewTime} onClear={onClear} />
|
|
||||||
<Select value={selected} onChange={onSelectInterface}>
|
<Select value={selected} onChange={onSelectInterface}>
|
||||||
{parsedData?.interfaces
|
{parsedData?.interfaces
|
||||||
? Object.keys(parsedData.interfaces).map((v) => (
|
? Object.keys(parsedData.interfaces).map((v) => (
|
||||||
@@ -64,6 +62,8 @@ const DeviceStatisticsCard = ({ serialNumber }: Props) => {
|
|||||||
: null}
|
: null}
|
||||||
<option value="memory">{t('statistics.memory')}</option>
|
<option value="memory">{t('statistics.memory')}</option>
|
||||||
</Select>
|
</Select>
|
||||||
|
<StatisticsCardDatePickers defaults={time} setTime={setNewTime} onClear={onClear} />
|
||||||
|
<ViewLastStatsModal serialNumber={serialNumber} />
|
||||||
<RefreshButton
|
<RefreshButton
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={refresh}
|
onClick={refresh}
|
||||||
|
|||||||
@@ -9,12 +9,15 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
Center,
|
Center,
|
||||||
Heading,
|
Heading,
|
||||||
|
IconButton,
|
||||||
Spinner,
|
Spinner,
|
||||||
|
Tooltip,
|
||||||
useClipboard,
|
useClipboard,
|
||||||
useColorMode,
|
useColorMode,
|
||||||
useDisclosure,
|
useDisclosure,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { JsonViewer } from '@textea/json-viewer';
|
import { JsonViewer } from '@textea/json-viewer';
|
||||||
|
import { ListDashes } from 'phosphor-react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { RefreshButton } from 'components/Buttons/RefreshButton';
|
import { RefreshButton } from 'components/Buttons/RefreshButton';
|
||||||
import FormattedDate from 'components/InformationDisplays/FormattedDate';
|
import FormattedDate from 'components/InformationDisplays/FormattedDate';
|
||||||
@@ -43,9 +46,15 @@ const ViewCapabilitiesModal = ({ serialNumber }: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button onClick={onOpen} colorScheme="pink" mr={2}>
|
<Tooltip label={t('controller.devices.capabilities')} hasArrow>
|
||||||
{t('controller.devices.capabilities')}
|
<IconButton
|
||||||
</Button>
|
aria-label={t('controller.devices.capabilities')}
|
||||||
|
icon={<ListDashes size={20} />}
|
||||||
|
onClick={onOpen}
|
||||||
|
colorScheme="pink"
|
||||||
|
mr={2}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
title={t('controller.devices.capabilities')}
|
title={t('controller.devices.capabilities')}
|
||||||
|
|||||||
@@ -7,11 +7,14 @@ import {
|
|||||||
AccordionPanel,
|
AccordionPanel,
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
|
IconButton,
|
||||||
|
Tooltip,
|
||||||
useClipboard,
|
useClipboard,
|
||||||
useColorMode,
|
useColorMode,
|
||||||
useDisclosure,
|
useDisclosure,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { JsonViewer } from '@textea/json-viewer';
|
import { JsonViewer } from '@textea/json-viewer';
|
||||||
|
import { Barcode } from 'phosphor-react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Modal } from 'components/Modals/Modal';
|
import { Modal } from 'components/Modals/Modal';
|
||||||
import { DeviceConfiguration } from 'models/Device';
|
import { DeviceConfiguration } from 'models/Device';
|
||||||
@@ -30,9 +33,14 @@ const ViewConfigurationModal = ({ configuration }: { configuration?: DeviceConfi
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button onClick={onOpen} isDisabled={!configuration} colorScheme="purple">
|
<Tooltip label={t('configurations.one')} hasArrow>
|
||||||
{t('configurations.one')}
|
<IconButton
|
||||||
</Button>
|
aria-label={t('configurations.one')}
|
||||||
|
icon={<Barcode size={20} />}
|
||||||
|
onClick={onOpen}
|
||||||
|
colorScheme="purple"
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
title={t('configurations.one')}
|
title={t('configurations.one')}
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {
|
||||||
|
AlertDialog,
|
||||||
|
AlertDialogBody,
|
||||||
|
AlertDialogContent,
|
||||||
|
AlertDialogFooter,
|
||||||
|
AlertDialogHeader,
|
||||||
|
AlertDialogOverlay,
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Center,
|
|
||||||
Heading,
|
Heading,
|
||||||
HStack,
|
HStack,
|
||||||
Popover,
|
|
||||||
PopoverArrow,
|
|
||||||
PopoverBody,
|
|
||||||
PopoverCloseButton,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverFooter,
|
|
||||||
PopoverHeader,
|
|
||||||
PopoverTrigger,
|
|
||||||
Portal,
|
Portal,
|
||||||
Spacer,
|
Spacer,
|
||||||
Tag,
|
Tag,
|
||||||
@@ -62,6 +59,7 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
const breakpoint = useBreakpoint();
|
const breakpoint = useBreakpoint();
|
||||||
|
const cancelRef = React.useRef(null);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { mutateAsync: deleteDevice, isLoading: isDeleting } = useDeleteDevice({
|
const { mutateAsync: deleteDevice, isLoading: isDeleting } = useDeleteDevice({
|
||||||
serialNumber,
|
serialNumber,
|
||||||
@@ -197,7 +195,7 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
|
|||||||
<Spacer />
|
<Spacer />
|
||||||
<HStack spacing={2}>
|
<HStack spacing={2}>
|
||||||
{breakpoint !== 'base' && breakpoint !== 'md' && <DeviceSearchBar />}
|
{breakpoint !== 'base' && breakpoint !== 'md' && <DeviceSearchBar />}
|
||||||
|
<DeleteButton isCompact onClick={onDeleteOpen} />
|
||||||
{getDevice?.data && (
|
{getDevice?.data && (
|
||||||
<DeviceActionDropdown
|
<DeviceActionDropdown
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -216,33 +214,6 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
|
|||||||
isCompact
|
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
|
<RefreshButton
|
||||||
onClick={refresh}
|
onClick={refresh}
|
||||||
isFetching={getDevice.isFetching || getHealth.isFetching || getStatus.isFetching}
|
isFetching={getDevice.isFetching || getHealth.isFetching || getStatus.isFetching}
|
||||||
@@ -274,6 +245,7 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
|
|||||||
<Spacer />
|
<Spacer />
|
||||||
<HStack spacing={2}>
|
<HStack spacing={2}>
|
||||||
<DeviceSearchBar />
|
<DeviceSearchBar />
|
||||||
|
<DeleteButton isCompact onClick={onDeleteOpen} />
|
||||||
{getDevice?.data && (
|
{getDevice?.data && (
|
||||||
<DeviceActionDropdown
|
<DeviceActionDropdown
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -289,35 +261,9 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
|
|||||||
onOpenRebootModal={rebootModalProps.onOpen}
|
onOpenRebootModal={rebootModalProps.onOpen}
|
||||||
onOpenScriptModal={scriptModal.openModal}
|
onOpenScriptModal={scriptModal.openModal}
|
||||||
size="md"
|
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
|
<RefreshButton
|
||||||
onClick={refresh}
|
onClick={refresh}
|
||||||
isFetching={getDevice.isFetching || getHealth.isFetching || getStatus.isFetching}
|
isFetching={getDevice.isFetching || getHealth.isFetching || getStatus.isFetching}
|
||||||
@@ -330,6 +276,24 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
|
|||||||
</Card>
|
</Card>
|
||||||
</Portal>
|
</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} />
|
<WifiScanModal modalProps={scanModalProps} serialNumber={serialNumber} />
|
||||||
<FirmwareUpgradeModal modalProps={upgradeModalProps} serialNumber={serialNumber} />
|
<FirmwareUpgradeModal modalProps={upgradeModalProps} serialNumber={serialNumber} />
|
||||||
<FactoryResetModal modalProps={resetModalProps} serialNumber={serialNumber} />
|
<FactoryResetModal modalProps={resetModalProps} serialNumber={serialNumber} />
|
||||||
|
|||||||
@@ -7,9 +7,11 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Center,
|
Center,
|
||||||
|
IconButton,
|
||||||
Tag,
|
Tag,
|
||||||
TagLabel,
|
TagLabel,
|
||||||
Text,
|
Text,
|
||||||
|
Tooltip,
|
||||||
useDisclosure,
|
useDisclosure,
|
||||||
useToast,
|
useToast,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
@@ -58,9 +60,14 @@ const UpdateDbButton = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button colorScheme="teal" leftIcon={<Database size={20} />} onClick={onOpen}>
|
<Tooltip label={t('firmware.last_db_update_title')}>
|
||||||
{t('firmware.last_db_update_title')}
|
<IconButton
|
||||||
</Button>
|
aria-label={t('firmware.last_db_update_title')}
|
||||||
|
colorScheme="teal"
|
||||||
|
icon={<Database size={20} />}
|
||||||
|
onClick={onOpen}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
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 { Download } from 'phosphor-react';
|
||||||
import { CSVLink } from 'react-csv';
|
import { CSVLink } from 'react-csv';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@@ -127,9 +127,9 @@ const LogsCard = () => {
|
|||||||
filename={`logs_${dateForFilename(new Date().getTime() / 1000)}.csv`}
|
filename={`logs_${dateForFilename(new Date().getTime() / 1000)}.csv`}
|
||||||
data={downloadableLogs as object[]}
|
data={downloadableLogs as object[]}
|
||||||
>
|
>
|
||||||
<Button onClick={() => {}} colorScheme="blue" leftIcon={<Download />}>
|
<Tooltip label={t('logs.export')} hasArrow>
|
||||||
{t('logs.export')}
|
<IconButton aria-label={t('logs.export')} icon={<Download />} colorScheme="blue" />
|
||||||
</Button>
|
</Tooltip>
|
||||||
</CSVLink>
|
</CSVLink>
|
||||||
</HStack>
|
</HStack>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|||||||
@@ -1,5 +1,19 @@
|
|||||||
import * as React from 'react';
|
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 { Download } from 'phosphor-react';
|
||||||
import { CSVLink } from 'react-csv';
|
import { CSVLink } from 'react-csv';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@@ -128,9 +142,9 @@ const FmsLogsCard = () => {
|
|||||||
filename={`logs_${dateForFilename(new Date().getTime() / 1000)}.csv`}
|
filename={`logs_${dateForFilename(new Date().getTime() / 1000)}.csv`}
|
||||||
data={downloadableLogs as object[]}
|
data={downloadableLogs as object[]}
|
||||||
>
|
>
|
||||||
<Button onClick={() => {}} colorScheme="blue" leftIcon={<Download />}>
|
<Tooltip label={t('logs.export')} hasArrow>
|
||||||
{t('logs.export')}
|
<IconButton aria-label={t('logs.export')} icon={<Download />} colorScheme="blue" />
|
||||||
</Button>
|
</Tooltip>
|
||||||
</CSVLink>
|
</CSVLink>
|
||||||
</HStack>
|
</HStack>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|||||||
@@ -1,5 +1,19 @@
|
|||||||
import * as React from 'react';
|
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 { Download } from 'phosphor-react';
|
||||||
import { CSVLink } from 'react-csv';
|
import { CSVLink } from 'react-csv';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@@ -128,9 +142,9 @@ const GeneralLogsCard = () => {
|
|||||||
filename={`logs_${dateForFilename(new Date().getTime() / 1000)}.csv`}
|
filename={`logs_${dateForFilename(new Date().getTime() / 1000)}.csv`}
|
||||||
data={downloadableLogs as object[]}
|
data={downloadableLogs as object[]}
|
||||||
>
|
>
|
||||||
<Button onClick={() => {}} colorScheme="blue" leftIcon={<Download />}>
|
<Tooltip label={t('logs.export')} hasArrow>
|
||||||
{t('logs.export')}
|
<IconButton aria-label={t('logs.export')} icon={<Download />} colorScheme="blue" />
|
||||||
</Button>
|
</Tooltip>
|
||||||
</CSVLink>
|
</CSVLink>
|
||||||
</HStack>
|
</HStack>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|||||||
@@ -1,5 +1,19 @@
|
|||||||
import * as React from 'react';
|
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 { Download } from 'phosphor-react';
|
||||||
import { CSVLink } from 'react-csv';
|
import { CSVLink } from 'react-csv';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@@ -128,9 +142,9 @@ const SecLogsCard = () => {
|
|||||||
filename={`logs_${dateForFilename(new Date().getTime() / 1000)}.csv`}
|
filename={`logs_${dateForFilename(new Date().getTime() / 1000)}.csv`}
|
||||||
data={downloadableLogs as object[]}
|
data={downloadableLogs as object[]}
|
||||||
>
|
>
|
||||||
<Button onClick={() => {}} colorScheme="blue" leftIcon={<Download />}>
|
<Tooltip label={t('logs.export')} hasArrow>
|
||||||
{t('logs.export')}
|
<IconButton aria-label={t('logs.export')} icon={<Download />} colorScheme="blue" />
|
||||||
</Button>
|
</Tooltip>
|
||||||
</CSVLink>
|
</CSVLink>
|
||||||
</HStack>
|
</HStack>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { Box } from '@chakra-ui/react';
|
||||||
import ApiKeyTable from './Table';
|
import ApiKeyTable from './Table';
|
||||||
import { Card } from 'components/Containers/Card';
|
import { Card } from 'components/Containers/Card';
|
||||||
import { CardBody } from 'components/Containers/Card/CardBody';
|
import { CardBody } from 'components/Containers/Card/CardBody';
|
||||||
@@ -10,7 +11,9 @@ const ApiKeysCard = () => {
|
|||||||
return (
|
return (
|
||||||
<Card p={4}>
|
<Card p={4}>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<ApiKeyTable userId={user?.id ?? ''} />
|
<Box w="100%">
|
||||||
|
<ApiKeyTable userId={user?.id ?? ''} />
|
||||||
|
</Box>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import * as React from 'react';
|
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 { useTranslation } from 'react-i18next';
|
||||||
import SystemLoggingModal from './Modal';
|
import SystemLoggingModal from './Modal';
|
||||||
import { EndpointApiResponse } from 'hooks/Network/Endpoints';
|
import { EndpointApiResponse } from 'hooks/Network/Endpoints';
|
||||||
@@ -15,9 +16,17 @@ const SystemLoggingButton = ({ endpoint, token }: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button colorScheme="teal" onClick={modalProps.onOpen} mr={2} my="auto">
|
<Tooltip label={t('system.logging')} hasArrow>
|
||||||
{t('system.logging')}
|
<IconButton
|
||||||
</Button>
|
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} />
|
<SystemLoggingModal modalProps={modalProps} endpoint={endpoint.uri} token={token} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import FormattedDate from '../../../components/InformationDisplays/FormattedDate';
|
import FormattedDate from '../../../components/InformationDisplays/FormattedDate';
|
||||||
import SystemLoggingButton from './LoggingButton';
|
import SystemLoggingButton from './LoggingButton';
|
||||||
import SystemCertificatesTable from './SystemCertificatesTable';
|
import SystemCertificatesTable from './SystemCertificatesTable';
|
||||||
|
import { RefreshButton } from 'components/Buttons/RefreshButton';
|
||||||
import { Card } from 'components/Containers/Card';
|
import { Card } from 'components/Containers/Card';
|
||||||
import { CardBody } from 'components/Containers/Card/CardBody';
|
import { CardBody } from 'components/Containers/Card/CardBody';
|
||||||
import { compactSecondsToDetailed } from 'helpers/dateFormatting';
|
import { compactSecondsToDetailed } from 'helpers/dateFormatting';
|
||||||
@@ -70,16 +71,7 @@ const SystemTile = ({ endpoint, token }: Props) => {
|
|||||||
<Heading pt={0}>{endpoint.type}</Heading>
|
<Heading pt={0}>{endpoint.type}</Heading>
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<SystemLoggingButton endpoint={endpoint} token={token} />
|
<SystemLoggingButton endpoint={endpoint} token={token} />
|
||||||
<Button
|
<RefreshButton onClick={refresh} isFetching={isFetchingSystem || isFetchingSubsystems} />
|
||||||
mt={1}
|
|
||||||
minWidth="112px"
|
|
||||||
colorScheme="blue"
|
|
||||||
rightIcon={<ArrowsClockwise />}
|
|
||||||
onClick={refresh}
|
|
||||||
isLoading={isFetchingSystem || isFetchingSubsystems}
|
|
||||||
>
|
|
||||||
{t('common.refresh')}
|
|
||||||
</Button>
|
|
||||||
</Box>
|
</Box>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<VStack w="100%">
|
<VStack w="100%">
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ const UserActions = ({ id, isSuspended, isWaitingForCheck, refresh, size = 'sm',
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu>
|
<Menu>
|
||||||
<Tooltip label={t('commands.other')}>
|
<Tooltip label={t('common.actions')}>
|
||||||
<MenuButton
|
<MenuButton
|
||||||
as={IconButton}
|
as={IconButton}
|
||||||
aria-label="Commands"
|
aria-label="Commands"
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { AddIcon } from '@chakra-ui/icons';
|
import { useDisclosure } from '@chakra-ui/react';
|
||||||
import { Button, useDisclosure } from '@chakra-ui/react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { SaveButton } from '../../../../components/Buttons/SaveButton';
|
import { SaveButton } from '../../../../components/Buttons/SaveButton';
|
||||||
import { ConfirmCloseAlertModal } from '../../../../components/Modals/ConfirmCloseAlert';
|
import { ConfirmCloseAlertModal } from '../../../../components/Modals/ConfirmCloseAlert';
|
||||||
import { Modal } from '../../../../components/Modals/Modal';
|
import { Modal } from '../../../../components/Modals/Modal';
|
||||||
import CreateUserForm, { CreateUserFormValues } from './Form';
|
import CreateUserForm, { CreateUserFormValues } from './Form';
|
||||||
|
import { CreateButton } from 'components/Buttons/CreateButton';
|
||||||
import { useAuth } from 'contexts/AuthProvider';
|
import { useAuth } from 'contexts/AuthProvider';
|
||||||
import { useFormRef } from 'hooks/useFormRef';
|
import { useFormRef } from 'hooks/useFormRef';
|
||||||
|
|
||||||
@@ -25,16 +25,7 @@ const CreateUserModal = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button
|
{user?.userRole === 'CSR' ? null : <CreateButton onClick={onOpen} ml={2} />}
|
||||||
hidden={user?.userRole === 'csr'}
|
|
||||||
alignItems="center"
|
|
||||||
colorScheme="blue"
|
|
||||||
rightIcon={<AddIcon />}
|
|
||||||
onClick={onOpen}
|
|
||||||
ml={2}
|
|
||||||
>
|
|
||||||
{t('crud.create')}
|
|
||||||
</Button>
|
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onClose={closeModal}
|
onClose={closeModal}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import { Avatar, Box, Button, Flex, useDisclosure } from '@chakra-ui/react';
|
import { Avatar, Box, Flex, useDisclosure } from '@chakra-ui/react';
|
||||||
import { ArrowsClockwise } from 'phosphor-react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import { ColumnPicker } from '../../../components/DataTables/ColumnPicker';
|
import { ColumnPicker } from '../../../components/DataTables/ColumnPicker';
|
||||||
@@ -9,6 +8,7 @@ import FormattedDate from '../../../components/InformationDisplays/FormattedDate
|
|||||||
import CreateUserModal from './CreateUserModal';
|
import CreateUserModal from './CreateUserModal';
|
||||||
import EditUserModal from './EditUserModal';
|
import EditUserModal from './EditUserModal';
|
||||||
import UserActions from './UserActions';
|
import UserActions from './UserActions';
|
||||||
|
import { RefreshButton } from 'components/Buttons/RefreshButton';
|
||||||
import { Card } from 'components/Containers/Card';
|
import { Card } from 'components/Containers/Card';
|
||||||
import { CardBody } from 'components/Containers/Card/CardBody';
|
import { CardBody } from 'components/Containers/Card/CardBody';
|
||||||
import { CardHeader } from 'components/Containers/Card/CardHeader';
|
import { CardHeader } from 'components/Containers/Card/CardHeader';
|
||||||
@@ -125,15 +125,7 @@ const UserTable = () => {
|
|||||||
preference="provisioning.userTable.hiddenColumns"
|
preference="provisioning.userTable.hiddenColumns"
|
||||||
/>
|
/>
|
||||||
<CreateUserModal />
|
<CreateUserModal />
|
||||||
<Button
|
<RefreshButton onClick={refreshUsers} isFetching={isFetching} ml={2} />
|
||||||
colorScheme="gray"
|
|
||||||
onClick={() => refreshUsers()}
|
|
||||||
rightIcon={<ArrowsClockwise />}
|
|
||||||
ml={2}
|
|
||||||
isLoading={isFetching}
|
|
||||||
>
|
|
||||||
{t('common.refresh')}
|
|
||||||
</Button>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
</CardHeader>
|
</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 CardComponent from './additions/card/Card';
|
||||||
import CardBodyComponent from './additions/card/CardBody';
|
import CardBodyComponent from './additions/card/CardBody';
|
||||||
import CardHeaderComponent from './additions/card/CardHeader';
|
import CardHeaderComponent from './additions/card/CardHeader';
|
||||||
@@ -37,4 +37,6 @@ const theme = extendTheme({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Tooltip.defaultProps = { ...Tooltip.defaultProps, hasArrow: true };
|
||||||
|
|
||||||
export default theme;
|
export default theme;
|
||||||
|
|||||||
@@ -1,49 +1,9 @@
|
|||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||||
import { VitePWA } from 'vite-plugin-pwa';
|
|
||||||
import react from '@vitejs/plugin-react';
|
import react from '@vitejs/plugin-react';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [tsconfigPaths(), react()],
|
||||||
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',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
build: {
|
build: {
|
||||||
outDir: './build',
|
outDir: './build',
|
||||||
chunkSizeWarningLimit: 1000,
|
chunkSizeWarningLimit: 1000,
|
||||||
|
|||||||
Reference in New Issue
Block a user