mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-08 00:39:50 +03:00
new linting, make sure code is type safe
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
import { type FC, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import AccessTimeIcon from '@mui/icons-material/AccessTime';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import CastIcon from '@mui/icons-material/Cast';
|
||||
@@ -10,18 +13,18 @@ import SettingsBackupRestoreIcon from '@mui/icons-material/SettingsBackupRestore
|
||||
import SettingsEthernetIcon from '@mui/icons-material/SettingsEthernet';
|
||||
import SettingsInputAntennaIcon from '@mui/icons-material/SettingsInputAntenna';
|
||||
import TuneIcon from '@mui/icons-material/Tune';
|
||||
import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, List } from '@mui/material';
|
||||
|
||||
import { List, Button, Dialog, DialogActions, DialogContent, DialogTitle, Box } from '@mui/material';
|
||||
import { useRequest } from 'alova';
|
||||
import { useState, type FC } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import RestartMonitor from './system/RestartMonitor';
|
||||
import { dialogStyle } from 'CustomTheme';
|
||||
import * as SystemApi from 'api/system';
|
||||
|
||||
import { dialogStyle } from 'CustomTheme';
|
||||
import { useRequest } from 'alova';
|
||||
import { ButtonRow, SectionContent, useLayoutTitle } from 'components';
|
||||
import ListMenuItem from 'components/layout/ListMenuItem';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
import RestartMonitor from './system/RestartMonitor';
|
||||
|
||||
const Settings: FC = () => {
|
||||
const { LL } = useI18nContext();
|
||||
useLayoutTitle(LL.SETTINGS(0));
|
||||
@@ -49,8 +52,8 @@ const Settings: FC = () => {
|
||||
.then(() => {
|
||||
setRestarting(true);
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(err.message);
|
||||
.catch((error: Error) => {
|
||||
toast.error(error.message);
|
||||
})
|
||||
.finally(() => {
|
||||
setConfirmRestart(false);
|
||||
@@ -64,8 +67,8 @@ const Settings: FC = () => {
|
||||
.then(() => {
|
||||
setRestarting(true);
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(err.message);
|
||||
.catch((error: Error) => {
|
||||
toast.error(error.message);
|
||||
})
|
||||
.finally(() => {
|
||||
setConfirmFactoryReset(false);
|
||||
@@ -79,8 +82,8 @@ const Settings: FC = () => {
|
||||
.then(() => {
|
||||
setRestarting(true);
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(err.message);
|
||||
.catch((error: Error) => {
|
||||
toast.error(error.message);
|
||||
})
|
||||
.finally(() => {
|
||||
setConfirmRestart(false);
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
import { useState } from 'react';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import WarningIcon from '@mui/icons-material/Warning';
|
||||
import { Button, Checkbox, MenuItem } from '@mui/material';
|
||||
import { range } from 'lodash-es';
|
||||
import { useState } from 'react';
|
||||
import type { ValidateFieldsError } from 'async-validator';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import type { APSettingsType } from 'types';
|
||||
import * as APApi from 'api/ap';
|
||||
|
||||
import type { ValidateFieldsError } from 'async-validator';
|
||||
import {
|
||||
BlockFormControlLabel,
|
||||
BlockNavigation,
|
||||
ButtonRow,
|
||||
FormLoader,
|
||||
SectionContent,
|
||||
ValidatedPasswordField,
|
||||
ValidatedTextField,
|
||||
BlockNavigation
|
||||
ValidatedTextField
|
||||
} from 'components';
|
||||
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import { range } from 'lodash-es';
|
||||
import type { APSettingsType } from 'types';
|
||||
import { APProvisionMode } from 'types';
|
||||
import { numberValue, updateValueDirty, useRest } from 'utils';
|
||||
|
||||
import { createAPSettingsValidator, validate } from 'validators';
|
||||
|
||||
export const isAPEnabled = ({ provision_mode }: APSettingsType) =>
|
||||
@@ -60,8 +60,8 @@ const APSettings: FC = () => {
|
||||
setFieldErrors(undefined);
|
||||
await validate(createAPSettingsValidator(data), data);
|
||||
await saveData();
|
||||
} catch (errors: any) {
|
||||
setFieldErrors(errors);
|
||||
} catch (error) {
|
||||
setFieldErrors(error as ValidateFieldsError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import type { FC } from 'react';
|
||||
|
||||
import ComputerIcon from '@mui/icons-material/Computer';
|
||||
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
|
||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||
import SettingsInputAntennaIcon from '@mui/icons-material/SettingsInputAntenna';
|
||||
import { Avatar, Button, Divider, List, ListItem, ListItemAvatar, ListItemText, useTheme } from '@mui/material';
|
||||
import { useRequest } from 'alova';
|
||||
import type { Theme } from '@mui/material';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import type { APStatusType } from 'types';
|
||||
import * as APApi from 'api/ap';
|
||||
import { ButtonRow, FormLoader, SectionContent } from 'components';
|
||||
|
||||
import { useRequest } from 'alova';
|
||||
import { ButtonRow, FormLoader, SectionContent } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { APStatusType } from 'types';
|
||||
import { APNetworkStatus } from 'types';
|
||||
|
||||
export const apStatusHighlight = ({ status }: APStatusType, theme: Theme) => {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import type { FC } from 'react';
|
||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||
|
||||
import { Tab } from '@mui/material';
|
||||
import { Navigate, Routes, Route } from 'react-router-dom';
|
||||
|
||||
import { RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
import APSettings from './APSettings';
|
||||
import APStatus from './APStatus';
|
||||
import type { FC } from 'react';
|
||||
import { RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
||||
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
const AccessPoint: FC = () => {
|
||||
const { LL } = useI18nContext();
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { Tab } from '@mui/material';
|
||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||
import MqttSettings from './MqttSettings';
|
||||
import MqttStatus from './MqttStatus';
|
||||
import type { FC } from 'react';
|
||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||
|
||||
import { Tab } from '@mui/material';
|
||||
|
||||
import { RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
||||
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
import MqttSettings from './MqttSettings';
|
||||
import MqttStatus from './MqttStatus';
|
||||
|
||||
const Mqtt: FC = () => {
|
||||
const { LL } = useI18nContext();
|
||||
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import WarningIcon from '@mui/icons-material/Warning';
|
||||
import { Button, Checkbox, MenuItem, Grid, Typography, InputAdornment, TextField } from '@mui/material';
|
||||
import { useState } from 'react';
|
||||
import type { ValidateFieldsError } from 'async-validator';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import type { MqttSettingsType } from 'types';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import WarningIcon from '@mui/icons-material/Warning';
|
||||
import { Button, Checkbox, Grid, InputAdornment, MenuItem, TextField, Typography } from '@mui/material';
|
||||
|
||||
import * as MqttApi from 'api/mqtt';
|
||||
|
||||
import type { ValidateFieldsError } from 'async-validator';
|
||||
import {
|
||||
BlockFormControlLabel,
|
||||
BlockNavigation,
|
||||
ButtonRow,
|
||||
FormLoader,
|
||||
SectionContent,
|
||||
ValidatedPasswordField,
|
||||
ValidatedTextField,
|
||||
BlockNavigation
|
||||
ValidatedTextField
|
||||
} from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { MqttSettingsType } from 'types';
|
||||
import { numberValue, updateValueDirty, useRest } from 'utils';
|
||||
|
||||
import { createMqttSettingsValidator, validate } from 'validators';
|
||||
|
||||
const MqttSettings: FC = () => {
|
||||
@@ -54,8 +55,8 @@ const MqttSettings: FC = () => {
|
||||
setFieldErrors(undefined);
|
||||
await validate(createMqttSettingsValidator(data), data);
|
||||
await saveData();
|
||||
} catch (errors: any) {
|
||||
setFieldErrors(errors);
|
||||
} catch (error) {
|
||||
setFieldErrors(error as ValidateFieldsError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import type { FC } from 'react';
|
||||
|
||||
import AutoAwesomeMotionIcon from '@mui/icons-material/AutoAwesomeMotion';
|
||||
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
|
||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||
import ReportIcon from '@mui/icons-material/Report';
|
||||
import SpeakerNotesOffIcon from '@mui/icons-material/SpeakerNotesOff';
|
||||
import { Avatar, Button, Divider, List, ListItem, ListItemAvatar, ListItemText, useTheme } from '@mui/material';
|
||||
import { useRequest } from 'alova';
|
||||
import type { Theme } from '@mui/material';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import type { MqttStatusType } from 'types';
|
||||
import * as MqttApi from 'api/mqtt';
|
||||
|
||||
import { useRequest } from 'alova';
|
||||
import { ButtonRow, FormLoader, SectionContent } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { MqttStatusType } from 'types';
|
||||
import { MqttDisconnectReason } from 'types';
|
||||
|
||||
export const mqttStatusHighlight = ({ enabled, connected }: MqttStatusType, theme: Theme) => {
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { Tab } from '@mui/material';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { Navigate, Routes, Route, useNavigate } from 'react-router-dom';
|
||||
import type { FC } from 'react';
|
||||
import { Navigate, Route, Routes, useNavigate } from 'react-router-dom';
|
||||
|
||||
import { Tab } from '@mui/material';
|
||||
|
||||
import { RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { WiFiNetwork } from 'types';
|
||||
|
||||
import NetworkSettings from './NetworkSettings';
|
||||
import NetworkStatus from './NetworkStatus';
|
||||
import { WiFiConnectionContext } from './WiFiConnectionContext';
|
||||
import WiFiNetworkScanner from './WiFiNetworkScanner';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import type { WiFiNetwork } from 'types';
|
||||
import { RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
const Network: FC = () => {
|
||||
const { LL } = useI18nContext();
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import type { FC } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import LockIcon from '@mui/icons-material/Lock';
|
||||
@@ -14,39 +18,35 @@ import {
|
||||
ListItemAvatar,
|
||||
ListItemSecondaryAction,
|
||||
ListItemText,
|
||||
Typography,
|
||||
MenuItem,
|
||||
TextField,
|
||||
MenuItem
|
||||
Typography
|
||||
} from '@mui/material';
|
||||
// eslint-disable-next-line import/named
|
||||
|
||||
import * as NetworkApi from 'api/network';
|
||||
import * as SystemApi from 'api/system';
|
||||
|
||||
import { updateState, useRequest } from 'alova';
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import type { ValidateFieldsError } from 'async-validator';
|
||||
import {
|
||||
BlockFormControlLabel,
|
||||
BlockNavigation,
|
||||
ButtonRow,
|
||||
FormLoader,
|
||||
MessageBox,
|
||||
SectionContent,
|
||||
ValidatedPasswordField,
|
||||
ValidatedTextField
|
||||
} from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { NetworkSettingsType } from 'types';
|
||||
import { updateValueDirty, useRest } from 'utils';
|
||||
import { validate } from 'validators';
|
||||
import { createNetworkSettingsValidator } from 'validators/network';
|
||||
|
||||
import RestartMonitor from '../system/RestartMonitor';
|
||||
import { WiFiConnectionContext } from './WiFiConnectionContext';
|
||||
import { isNetworkOpen, networkSecurityMode } from './WiFiNetworkSelector';
|
||||
import type { ValidateFieldsError } from 'async-validator';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import type { NetworkSettingsType } from 'types';
|
||||
import * as NetworkApi from 'api/network';
|
||||
import * as SystemApi from 'api/system';
|
||||
import {
|
||||
BlockFormControlLabel,
|
||||
ButtonRow,
|
||||
FormLoader,
|
||||
SectionContent,
|
||||
ValidatedPasswordField,
|
||||
ValidatedTextField,
|
||||
MessageBox,
|
||||
BlockNavigation
|
||||
} from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
import { updateValueDirty, useRest } from 'utils';
|
||||
|
||||
import { validate } from 'validators';
|
||||
import { createNetworkSettingsValidator } from 'validators/network';
|
||||
|
||||
const NetworkSettings: FC = () => {
|
||||
const { LL } = useI18nContext();
|
||||
@@ -80,7 +80,7 @@ const NetworkSettings: FC = () => {
|
||||
useEffect(() => {
|
||||
if (!initialized && data) {
|
||||
if (selectedNetwork) {
|
||||
updateState('networkSettings', (current_data) => ({
|
||||
updateState('networkSettings', (current_data: NetworkSettingsType) => ({
|
||||
ssid: selectedNetwork.ssid,
|
||||
bssid: selectedNetwork.bssid,
|
||||
password: current_data ? current_data.password : '',
|
||||
@@ -115,8 +115,8 @@ const NetworkSettings: FC = () => {
|
||||
setFieldErrors(undefined);
|
||||
await validate(createNetworkSettingsValidator(data), data);
|
||||
await saveData();
|
||||
} catch (errors: any) {
|
||||
setFieldErrors(errors);
|
||||
} catch (error) {
|
||||
setFieldErrors(error as ValidateFieldsError);
|
||||
}
|
||||
deselectNetwork();
|
||||
};
|
||||
@@ -127,7 +127,7 @@ const NetworkSettings: FC = () => {
|
||||
};
|
||||
|
||||
const restart = async () => {
|
||||
await restartCommand().catch((error) => {
|
||||
await restartCommand().catch((error: Error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
setRestarting(true);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { FC } from 'react';
|
||||
|
||||
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
|
||||
import DnsIcon from '@mui/icons-material/Dns';
|
||||
import GiteIcon from '@mui/icons-material/Gite';
|
||||
@@ -7,15 +9,14 @@ import SettingsInputAntennaIcon from '@mui/icons-material/SettingsInputAntenna';
|
||||
import SettingsInputComponentIcon from '@mui/icons-material/SettingsInputComponent';
|
||||
import WifiIcon from '@mui/icons-material/Wifi';
|
||||
import { Avatar, Button, Divider, List, ListItem, ListItemAvatar, ListItemText, useTheme } from '@mui/material';
|
||||
import { useRequest } from 'alova';
|
||||
import type { Theme } from '@mui/material';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import type { NetworkStatusType } from 'types';
|
||||
import * as NetworkApi from 'api/network';
|
||||
import { ButtonRow, FormLoader, SectionContent } from 'components';
|
||||
|
||||
import { useRequest } from 'alova';
|
||||
import { ButtonRow, FormLoader, SectionContent } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { NetworkStatusType } from 'types';
|
||||
import { NetworkConnectionStatus } from 'types';
|
||||
|
||||
const isConnected = ({ status }: NetworkStatusType) =>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
import type { WiFiNetwork } from 'types';
|
||||
|
||||
export interface WiFiConnectionContextValue {
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { useRef, useState } from 'react';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import PermScanWifiIcon from '@mui/icons-material/PermScanWifi';
|
||||
import { Button } from '@mui/material';
|
||||
// eslint-disable-next-line import/named
|
||||
|
||||
import * as NetworkApi from 'api/network';
|
||||
|
||||
import { updateState, useRequest } from 'alova';
|
||||
import { useState, useRef } from 'react';
|
||||
import { ButtonRow, FormLoader, SectionContent } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
import WiFiNetworkSelector from './WiFiNetworkSelector';
|
||||
import type { FC } from 'react';
|
||||
import * as NetworkApi from 'api/network';
|
||||
import { ButtonRow, FormLoader, SectionContent } from 'components';
|
||||
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
const NUM_POLLS = 10;
|
||||
const POLLING_FREQUENCY = 1000;
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { useContext } from 'react';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import LockIcon from '@mui/icons-material/Lock';
|
||||
import LockOpenIcon from '@mui/icons-material/LockOpen';
|
||||
import WifiIcon from '@mui/icons-material/Wifi';
|
||||
import { Avatar, Badge, List, ListItem, ListItemAvatar, ListItemIcon, ListItemText, useTheme } from '@mui/material';
|
||||
import { useContext } from 'react';
|
||||
import type { Theme } from '@mui/material';
|
||||
|
||||
import { MessageBox } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { WiFiNetwork, WiFiNetworkList } from 'types';
|
||||
import { WiFiEncryptionType } from 'types';
|
||||
|
||||
import { WiFiConnectionContext } from './WiFiConnectionContext';
|
||||
import type { Theme } from '@mui/material';
|
||||
import type { FC } from 'react';
|
||||
import type { WiFiNetwork, WiFiNetworkList } from 'types';
|
||||
import { MessageBox } from 'components';
|
||||
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import { WiFiEncryptionType } from 'types';
|
||||
|
||||
interface WiFiNetworkSelectorProps {
|
||||
networkList: WiFiNetworkList;
|
||||
@@ -39,7 +40,7 @@ export const networkSecurityMode = ({ encryption_type }: WiFiNetwork) => {
|
||||
case WiFiEncryptionType.WIFI_AUTH_WPA2_WPA3_PSK:
|
||||
return 'WPA2/WPA3';
|
||||
default:
|
||||
return 'Unknown: ' + encryption_type;
|
||||
return 'Unknown: ' + String(encryption_type);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,28 +1,30 @@
|
||||
import { useState } from 'react';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import WarningIcon from '@mui/icons-material/Warning';
|
||||
import { Button, Checkbox, MenuItem } from '@mui/material';
|
||||
// eslint-disable-next-line import/named
|
||||
import { updateState } from 'alova';
|
||||
import { useState } from 'react';
|
||||
import { selectedTimeZone, timeZoneSelectItems, TIME_ZONES } from './TZ';
|
||||
import type { ValidateFieldsError } from 'async-validator';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import type { NTPSettingsType } from 'types';
|
||||
import * as NTPApi from 'api/ntp';
|
||||
|
||||
import { updateState } from 'alova';
|
||||
import type { ValidateFieldsError } from 'async-validator';
|
||||
import {
|
||||
BlockFormControlLabel,
|
||||
BlockNavigation,
|
||||
ButtonRow,
|
||||
FormLoader,
|
||||
SectionContent,
|
||||
ValidatedTextField,
|
||||
BlockNavigation
|
||||
ValidatedTextField
|
||||
} from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { NTPSettingsType } from 'types';
|
||||
import { updateValueDirty, useRest } from 'utils';
|
||||
import { validate } from 'validators';
|
||||
import { NTP_SETTINGS_VALIDATOR } from 'validators/ntp';
|
||||
|
||||
import { TIME_ZONES, selectedTimeZone, timeZoneSelectItems } from './TZ';
|
||||
|
||||
const NTPSettings: FC = () => {
|
||||
const {
|
||||
loadData,
|
||||
@@ -56,15 +58,15 @@ const NTPSettings: FC = () => {
|
||||
setFieldErrors(undefined);
|
||||
await validate(NTP_SETTINGS_VALIDATOR, data);
|
||||
await saveData();
|
||||
} catch (errors: any) {
|
||||
setFieldErrors(errors);
|
||||
} catch (error) {
|
||||
setFieldErrors(error as ValidateFieldsError);
|
||||
}
|
||||
};
|
||||
|
||||
const changeTimeZone = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateFormValue(event);
|
||||
|
||||
updateState('ntpSettings', (settings) => ({
|
||||
updateState('ntpSettings', (settings: NTPSettingsType) => ({
|
||||
...settings,
|
||||
tz_label: event.target.value,
|
||||
tz_format: TIME_ZONES[event.target.value]
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import type { FC } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import AccessTimeIcon from '@mui/icons-material/AccessTime';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import DnsIcon from '@mui/icons-material/Dns';
|
||||
@@ -18,21 +22,18 @@ import {
|
||||
ListItemAvatar,
|
||||
ListItemText,
|
||||
TextField,
|
||||
useTheme,
|
||||
Typography
|
||||
Typography,
|
||||
useTheme
|
||||
} from '@mui/material';
|
||||
import { useRequest } from 'alova';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import type { Theme } from '@mui/material';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import type { NTPStatusType } from 'types';
|
||||
import { dialogStyle } from 'CustomTheme';
|
||||
import * as NTPApi from 'api/ntp';
|
||||
import { ButtonRow, FormLoader, SectionContent } from 'components';
|
||||
|
||||
import { dialogStyle } from 'CustomTheme';
|
||||
import { useRequest } from 'alova';
|
||||
import { ButtonRow, FormLoader, SectionContent } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { NTPStatusType, Time } from 'types';
|
||||
import { NTPSyncStatus } from 'types';
|
||||
import { formatDateTime, formatLocalDateTime } from 'utils';
|
||||
|
||||
@@ -45,7 +46,7 @@ const NTPStatus: FC = () => {
|
||||
|
||||
const { LL } = useI18nContext();
|
||||
|
||||
const { send: updateTime } = useRequest((local_time) => NTPApi.updateTime(local_time), {
|
||||
const { send: updateTime } = useRequest((local_time: Time) => NTPApi.updateTime(local_time), {
|
||||
immediate: false
|
||||
});
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { Tab } from '@mui/material';
|
||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||
import NTPSettings from './NTPSettings';
|
||||
import NTPStatus from './NTPStatus';
|
||||
import type { FC } from 'react';
|
||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||
|
||||
import { Tab } from '@mui/material';
|
||||
|
||||
import { RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
||||
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
import NTPSettings from './NTPSettings';
|
||||
import NTPStatus from './NTPStatus';
|
||||
|
||||
const NetworkTime: FC = () => {
|
||||
const { LL } = useI18nContext();
|
||||
useLayoutTitle('NTP');
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { MenuItem } from '@mui/material';
|
||||
|
||||
type TimeZones = {
|
||||
[name: string]: string;
|
||||
};
|
||||
type TimeZones = Record<string, string>;
|
||||
|
||||
export const TIME_ZONES: TimeZones = {
|
||||
'Africa/Abidjan': 'GMT0',
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
import { useState } from 'react';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import WarningIcon from '@mui/icons-material/Warning';
|
||||
import { Button, Checkbox } from '@mui/material';
|
||||
import { useState } from 'react';
|
||||
import type { ValidateFieldsError } from 'async-validator';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import type { OTASettingsType } from 'types';
|
||||
import * as SystemApi from 'api/system';
|
||||
|
||||
import type { ValidateFieldsError } from 'async-validator';
|
||||
import {
|
||||
BlockFormControlLabel,
|
||||
BlockNavigation,
|
||||
ButtonRow,
|
||||
FormLoader,
|
||||
SectionContent,
|
||||
ValidatedPasswordField,
|
||||
ValidatedTextField,
|
||||
BlockNavigation,
|
||||
useLayoutTitle
|
||||
} from 'components';
|
||||
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { OTASettingsType } from 'types';
|
||||
import { numberValue, updateValueDirty, useRest } from 'utils';
|
||||
|
||||
import { validate } from 'validators';
|
||||
import { OTA_SETTINGS_VALIDATOR } from 'validators/system';
|
||||
|
||||
@@ -57,8 +57,8 @@ const OTASettings: FC = () => {
|
||||
setFieldErrors(undefined);
|
||||
await validate(OTA_SETTINGS_VALIDATOR, data);
|
||||
await saveData();
|
||||
} catch (errors: any) {
|
||||
setFieldErrors(errors);
|
||||
} catch (error) {
|
||||
setFieldErrors(error as ValidateFieldsError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
import { useEffect } from 'react';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import {
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Box,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
LinearProgress,
|
||||
Typography,
|
||||
TextField,
|
||||
Button
|
||||
Typography
|
||||
} from '@mui/material';
|
||||
import { useRequest } from 'alova';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import type { FC } from 'react';
|
||||
import { dialogStyle } from 'CustomTheme';
|
||||
import * as SecurityApi from 'api/security';
|
||||
import { MessageBox } from 'components';
|
||||
|
||||
import { dialogStyle } from 'CustomTheme';
|
||||
import { useRequest } from 'alova';
|
||||
import { MessageBox } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
interface GenerateTokenProps {
|
||||
@@ -37,7 +38,6 @@ const GenerateToken: FC<GenerateTokenProps> = ({ username, onClose }) => {
|
||||
if (open) {
|
||||
void generateToken();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [open]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import { useContext, useState } from 'react';
|
||||
import type { FC } from 'react';
|
||||
import { useBlocker } from 'react-router-dom';
|
||||
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import CheckIcon from '@mui/icons-material/Check';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
@@ -6,24 +10,22 @@ import EditIcon from '@mui/icons-material/Edit';
|
||||
import PersonAddIcon from '@mui/icons-material/PersonAdd';
|
||||
import VpnKeyIcon from '@mui/icons-material/VpnKey';
|
||||
import WarningIcon from '@mui/icons-material/Warning';
|
||||
import { Button, IconButton, Box } from '@mui/material';
|
||||
import { Box, Button, IconButton } from '@mui/material';
|
||||
|
||||
import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table';
|
||||
import { useTheme } from '@table-library/react-table-library/theme';
|
||||
import { useContext, useState } from 'react';
|
||||
|
||||
import { useBlocker } from 'react-router-dom';
|
||||
import GenerateToken from './GenerateToken';
|
||||
import User from './User';
|
||||
import type { FC } from 'react';
|
||||
import type { SecuritySettingsType, UserType } from 'types';
|
||||
import * as SecurityApi from 'api/security';
|
||||
import { ButtonRow, FormLoader, MessageBox, SectionContent, BlockNavigation } from 'components';
|
||||
|
||||
import { Body, Cell, Header, HeaderCell, HeaderRow, Row, Table } from '@table-library/react-table-library/table';
|
||||
import { useTheme } from '@table-library/react-table-library/theme';
|
||||
import { BlockNavigation, ButtonRow, FormLoader, MessageBox, SectionContent } from 'components';
|
||||
import { AuthenticatedContext } from 'contexts/authentication';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { SecuritySettingsType, UserType } from 'types';
|
||||
import { useRest } from 'utils';
|
||||
import { createUserValidator } from 'validators';
|
||||
|
||||
import GenerateToken from './GenerateToken';
|
||||
import User from './User';
|
||||
|
||||
const ManageUsers: FC = () => {
|
||||
const { loadData, saveData, saving, data, updateDataValue, errorMessage } = useRest<SecuritySettingsType>({
|
||||
read: SecurityApi.readSecuritySettings,
|
||||
@@ -138,12 +140,20 @@ const ManageUsers: FC = () => {
|
||||
setChanged(0);
|
||||
};
|
||||
|
||||
const user_table = data.users.map((u) => ({ ...u, id: u.username }));
|
||||
interface UserType2 {
|
||||
id: string;
|
||||
username: string;
|
||||
password: string;
|
||||
admin: boolean;
|
||||
}
|
||||
|
||||
// add id to the type, needed for the table
|
||||
const user_table = data.users.map((u) => ({ ...u, id: u.username })) as UserType2[];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Table data={{ nodes: user_table }} theme={table_theme} layout={{ custom: true }}>
|
||||
{(tableList: any) => (
|
||||
{(tableList: UserType2[]) => (
|
||||
<>
|
||||
<Header>
|
||||
<HeaderRow>
|
||||
@@ -153,7 +163,7 @@ const ManageUsers: FC = () => {
|
||||
</HeaderRow>
|
||||
</Header>
|
||||
<Body>
|
||||
{tableList.map((u: any) => (
|
||||
{tableList.map((u: UserType2) => (
|
||||
<Row key={u.id} item={u}>
|
||||
<Cell>{u.username}</Cell>
|
||||
<Cell stiff>{u.admin ? <CheckIcon /> : <CloseIcon />}</Cell>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import type { FC } from 'react';
|
||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||
|
||||
import { Tab } from '@mui/material';
|
||||
import { Navigate, Routes, Route } from 'react-router-dom';
|
||||
|
||||
import { RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
import ManageUsers from './ManageUsers';
|
||||
import SecuritySettings from './SecuritySettings';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import { RouterTabs, useRouterTab, useLayoutTitle } from 'components';
|
||||
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
const Security: FC = () => {
|
||||
const { LL } = useI18nContext();
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { useContext, useState } from 'react';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import WarningIcon from '@mui/icons-material/Warning';
|
||||
import { Button } from '@mui/material';
|
||||
import { useContext, useState } from 'react';
|
||||
import type { ValidateFieldsError } from 'async-validator';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import type { SecuritySettingsType } from 'types';
|
||||
import * as SecurityApi from 'api/security';
|
||||
import { ButtonRow, FormLoader, MessageBox, SectionContent, ValidatedPasswordField, BlockNavigation } from 'components';
|
||||
|
||||
import type { ValidateFieldsError } from 'async-validator';
|
||||
import { BlockNavigation, ButtonRow, FormLoader, MessageBox, SectionContent, ValidatedPasswordField } from 'components';
|
||||
import { AuthenticatedContext } from 'contexts/authentication';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { SecuritySettingsType } from 'types';
|
||||
import { updateValueDirty, useRest } from 'utils';
|
||||
import { SECURITY_SETTINGS_VALIDATOR, validate } from 'validators';
|
||||
|
||||
@@ -49,8 +50,8 @@ const SecuritySettings: FC = () => {
|
||||
await validate(SECURITY_SETTINGS_VALIDATOR, data);
|
||||
await saveData();
|
||||
await authenticatedContext.refresh();
|
||||
} catch (errors: any) {
|
||||
setFieldErrors(errors);
|
||||
} catch (error) {
|
||||
setFieldErrors(error as ValidateFieldsError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import PersonAddIcon from '@mui/icons-material/PersonAdd';
|
||||
import SaveIcon from '@mui/icons-material/Save';
|
||||
|
||||
import { Button, Checkbox, Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material';
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
import { dialogStyle } from 'CustomTheme';
|
||||
import type Schema from 'async-validator';
|
||||
import type { ValidateFieldsError } from 'async-validator';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import type { UserType } from 'types';
|
||||
import { dialogStyle } from 'CustomTheme';
|
||||
import { BlockFormControlLabel, ValidatedPasswordField, ValidatedTextField } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { UserType } from 'types';
|
||||
import { updateValue } from 'utils';
|
||||
import { validate } from 'validators';
|
||||
|
||||
@@ -45,8 +45,8 @@ const User: FC<UserFormProps> = ({ creating, validator, user, setUser, onDoneEdi
|
||||
setFieldErrors(undefined);
|
||||
await validate(validator, user);
|
||||
onDoneEditing();
|
||||
} catch (errors: any) {
|
||||
setFieldErrors(errors);
|
||||
} catch (error) {
|
||||
setFieldErrors(error as ValidateFieldsError);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { FC } from 'react';
|
||||
|
||||
import AppsIcon from '@mui/icons-material/Apps';
|
||||
import DeveloperBoardIcon from '@mui/icons-material/DeveloperBoard';
|
||||
import DevicesIcon from '@mui/icons-material/Devices';
|
||||
@@ -8,10 +10,9 @@ import SdCardAlertIcon from '@mui/icons-material/SdCardAlert';
|
||||
import SdStorageIcon from '@mui/icons-material/SdStorage';
|
||||
import { Avatar, Box, Button, Divider, List, ListItem, ListItemAvatar, ListItemText } from '@mui/material';
|
||||
|
||||
import { useRequest } from 'alova';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import * as SystemApi from 'api/system';
|
||||
|
||||
import { useRequest } from 'alova';
|
||||
import { ButtonRow, FormLoader, SectionContent, useLayoutTitle } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useRequest } from 'alova';
|
||||
import { useRef, useState, useEffect } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import * as SystemApi from 'api/system';
|
||||
import { FormLoader } from 'components';
|
||||
|
||||
import { useRequest } from 'alova';
|
||||
import { FormLoader } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
const RESTART_TIMEOUT = 2 * 60 * 1000;
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { type FC, useContext } from 'react';
|
||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||
|
||||
import { Tab } from '@mui/material';
|
||||
import { useContext, type FC } from 'react';
|
||||
import { Navigate, Routes, Route } from 'react-router-dom';
|
||||
import SystemLog from './SystemLog';
|
||||
import SystemStatus from './SystemStatus';
|
||||
|
||||
import { useRouterTab, RouterTabs, useLayoutTitle, RequireAdmin } from 'components';
|
||||
|
||||
import { RequireAdmin, RouterTabs, useLayoutTitle, useRouterTab } from 'components';
|
||||
import { AuthenticatedContext } from 'contexts/authentication';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import SystemActivity from 'project/SystemActivity';
|
||||
|
||||
import SystemLog from './SystemLog';
|
||||
import SystemStatus from './SystemStatus';
|
||||
|
||||
const System: FC = () => {
|
||||
const { LL } = useI18nContext();
|
||||
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import type { FC } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import DownloadIcon from '@mui/icons-material/GetApp';
|
||||
import WarningIcon from '@mui/icons-material/Warning';
|
||||
import { Box, styled, Button, Checkbox, MenuItem, Grid, TextField } from '@mui/material';
|
||||
import { useRequest } from 'alova';
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import type { FC } from 'react';
|
||||
import { Box, Button, Checkbox, Grid, MenuItem, TextField, styled } from '@mui/material';
|
||||
|
||||
import type { LogSettings, LogEntry } from 'types';
|
||||
import { addAccessTokenParameter } from 'api/authentication';
|
||||
import { EVENT_SOURCE_ROOT } from 'api/endpoints';
|
||||
import * as SystemApi from 'api/system';
|
||||
import { fetchLogES } from 'api/system';
|
||||
|
||||
import { SectionContent, FormLoader, BlockFormControlLabel, BlockNavigation, useLayoutTitle } from 'components';
|
||||
|
||||
import { useSSE } from '@alova/scene-react';
|
||||
import { useRequest } from 'alova';
|
||||
import { BlockFormControlLabel, BlockNavigation, FormLoader, SectionContent, useLayoutTitle } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { LogEntry, LogSettings } from 'types';
|
||||
import { LogLevel } from 'types';
|
||||
import { updateValueDirty, useRest } from 'utils';
|
||||
|
||||
export const LOG_EVENTSOURCE_URL = EVENT_SOURCE_ROOT + 'log';
|
||||
|
||||
const LogEntryLine = styled('div')(() => ({
|
||||
color: '#bbbbbb',
|
||||
fontFamily: 'monospace',
|
||||
@@ -58,13 +56,34 @@ const SystemLog: FC = () => {
|
||||
update: SystemApi.updateLogSettings
|
||||
});
|
||||
|
||||
// called on page load to reset pointer and fetch all log entries
|
||||
useRequest(SystemApi.fetchLog());
|
||||
const [logEntries, setLogEntries] = useState<LogEntry[]>([]);
|
||||
const [lastIndex, setLastIndex] = useState<number>(0);
|
||||
|
||||
const updateFormValue = updateValueDirty(origData, dirtyFlags, setDirtyFlags, updateDataValue);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
const { onMessage, onError } = useSSE(fetchLogES, {
|
||||
immediate: true,
|
||||
// withCredentials: true,
|
||||
interceptByGlobalResponded: false
|
||||
});
|
||||
|
||||
onMessage((message: { id: number; data: string }) => {
|
||||
const rawData = message.data;
|
||||
const logentry = JSON.parse(rawData) as LogEntry;
|
||||
if (logentry.i > lastIndex) {
|
||||
setLastIndex(logentry.i);
|
||||
setLogEntries((log) => [...log, logentry]);
|
||||
}
|
||||
});
|
||||
|
||||
onError(() => {
|
||||
toast.error('No connection to Log server');
|
||||
});
|
||||
|
||||
// called on page load to reset pointer and fetch all log entries
|
||||
useRequest(SystemApi.fetchLog());
|
||||
|
||||
const paddedLevelLabel = (level: LogLevel) => {
|
||||
const label = levelLabel(level);
|
||||
return data?.compact ? ' ' + label[0] : label.padStart(8, '\xa0');
|
||||
@@ -97,8 +116,8 @@ const SystemLog: FC = () => {
|
||||
await saveData();
|
||||
};
|
||||
|
||||
// handle scrolling
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (logEntries.length) {
|
||||
ref.current?.scrollIntoView({
|
||||
@@ -108,29 +127,6 @@ const SystemLog: FC = () => {
|
||||
}
|
||||
}, [logEntries.length]);
|
||||
|
||||
useEffect(() => {
|
||||
const es = new EventSource(addAccessTokenParameter(LOG_EVENTSOURCE_URL));
|
||||
es.onmessage = (event: MessageEvent) => {
|
||||
const rawData = event.data;
|
||||
if (typeof rawData === 'string' || rawData instanceof String) {
|
||||
const logentry = JSON.parse(rawData as string) as LogEntry;
|
||||
if (logentry.i > lastIndex) {
|
||||
setLastIndex(logentry.i);
|
||||
setLogEntries((log) => [...log, logentry]);
|
||||
}
|
||||
}
|
||||
};
|
||||
es.onerror = () => {
|
||||
es.close();
|
||||
toast.error('No connection to Log server');
|
||||
};
|
||||
|
||||
return () => {
|
||||
es.close();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const content = () => {
|
||||
if (!data) {
|
||||
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { type FC, useContext, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import AccessTimeIcon from '@mui/icons-material/AccessTime';
|
||||
import BuildIcon from '@mui/icons-material/Build';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
@@ -9,7 +12,6 @@ import PermScanWifiIcon from '@mui/icons-material/PermScanWifi';
|
||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||
import SettingsInputAntennaIcon from '@mui/icons-material/SettingsInputAntenna';
|
||||
import TimerIcon from '@mui/icons-material/Timer';
|
||||
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
@@ -26,17 +28,15 @@ import {
|
||||
useTheme
|
||||
} from '@mui/material';
|
||||
|
||||
import { useRequest } from 'alova';
|
||||
import { useContext, type FC, useState } from 'react';
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
import { dialogStyle } from 'CustomTheme';
|
||||
import * as SystemApi from 'api/system';
|
||||
|
||||
import * as EMSESP from 'project/api';
|
||||
import { dialogStyle } from 'CustomTheme';
|
||||
import { useRequest } from 'alova';
|
||||
import { FormLoader, SectionContent, useLayoutTitle } from 'components';
|
||||
import ListMenuItem from 'components/layout/ListMenuItem';
|
||||
import { AuthenticatedContext } from 'contexts/authentication';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import * as EMSESP from 'project/api';
|
||||
import { busConnectionStatus } from 'project/types';
|
||||
import { NTPSyncStatus } from 'types';
|
||||
|
||||
@@ -141,8 +141,8 @@ const SystemStatus: FC = () => {
|
||||
.then(() => {
|
||||
toast.info(LL.SCANNING() + '...');
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(err.message);
|
||||
.catch((error: Error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
setConfirmScan(false);
|
||||
};
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import DownloadIcon from '@mui/icons-material/GetApp';
|
||||
import { Typography, Button, Box, Link } from '@mui/material';
|
||||
import { useRequest } from 'alova';
|
||||
import { useState, type FC } from 'react';
|
||||
import { type FC, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import RestartMonitor from './RestartMonitor';
|
||||
|
||||
import DownloadIcon from '@mui/icons-material/GetApp';
|
||||
import { Box, Button, Link, Typography } from '@mui/material';
|
||||
|
||||
import * as SystemApi from 'api/system';
|
||||
import { FormLoader, SectionContent, SingleUpload, useLayoutTitle } from 'components';
|
||||
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import * as EMSESP from 'project/api';
|
||||
import { useRequest } from 'alova';
|
||||
import { FormLoader, SectionContent, SingleUpload, useLayoutTitle } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { APIcall } from 'project/types';
|
||||
|
||||
import RestartMonitor from './RestartMonitor';
|
||||
|
||||
const UploadDownload: FC = () => {
|
||||
const { LL } = useI18nContext();
|
||||
@@ -28,7 +31,7 @@ const UploadDownload: FC = () => {
|
||||
const { send: getSchedule, onSuccess: onSuccessGetSchedule } = useRequest(EMSESP.getSchedule(), {
|
||||
immediate: false
|
||||
});
|
||||
const { send: getAPI, onSuccess: onGetAPI } = useRequest((data) => EMSESP.API(data), {
|
||||
const { send: getAPI, onSuccess: onGetAPI } = useRequest((data: APIcall) => EMSESP.API(data), {
|
||||
immediate: false
|
||||
});
|
||||
|
||||
@@ -64,8 +67,9 @@ const UploadDownload: FC = () => {
|
||||
force: true
|
||||
});
|
||||
|
||||
onSuccessUpload(({ data }: any) => {
|
||||
onSuccessUpload(({ data }) => {
|
||||
if (data) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
setMd5(data.md5);
|
||||
toast.success(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL());
|
||||
} else {
|
||||
@@ -74,18 +78,18 @@ const UploadDownload: FC = () => {
|
||||
});
|
||||
|
||||
const startUpload = async (files: File[]) => {
|
||||
await sendUpload(files[0]).catch((err) => {
|
||||
if (err.message === 'The user aborted a request') {
|
||||
await sendUpload(files[0]).catch((error: Error) => {
|
||||
if (error.message === 'The user aborted a request') {
|
||||
toast.warning(LL.UPLOAD() + ' ' + LL.ABORTED());
|
||||
} else if (err.message === 'Network Error') {
|
||||
} else if (error.message === 'Network Error') {
|
||||
toast.warning('Invalid file extension or incompatible bin file');
|
||||
} else {
|
||||
toast.error(err.message);
|
||||
toast.error(error.message);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const saveFile = (json: any, endpoint: string) => {
|
||||
const saveFile = (json: unknown, endpoint: string) => {
|
||||
const anchor = document.createElement('a');
|
||||
anchor.href = URL.createObjectURL(
|
||||
new Blob([JSON.stringify(json, null, 2)], {
|
||||
@@ -111,30 +115,31 @@ const UploadDownload: FC = () => {
|
||||
saveFile(event.data, 'schedule.json');
|
||||
});
|
||||
onGetAPI((event) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
saveFile(event.data, event.sendArgs[0].device + '_' + event.sendArgs[0].entity + '.txt');
|
||||
});
|
||||
|
||||
const downloadSettings = async () => {
|
||||
await getSettings().catch((error) => {
|
||||
await getSettings().catch((error: Error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
};
|
||||
|
||||
const downloadCustomizations = async () => {
|
||||
await getCustomizations().catch((error) => {
|
||||
await getCustomizations().catch((error: Error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
};
|
||||
|
||||
const downloadEntities = async () => {
|
||||
await getEntities().catch((error) => {
|
||||
await getEntities().catch((error: Error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
};
|
||||
|
||||
const downloadSchedule = async () => {
|
||||
await getSchedule()
|
||||
.catch((error) => {
|
||||
.catch((error: Error) => {
|
||||
toast.error(error.message);
|
||||
})
|
||||
.finally(() => {
|
||||
@@ -143,7 +148,7 @@ const UploadDownload: FC = () => {
|
||||
};
|
||||
|
||||
const callAPI = async (device: string, entity: string) => {
|
||||
await getAPI({ device, entity, id: 0 }).catch((error) => {
|
||||
await getAPI({ device, entity, id: 0 }).catch((error: Error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
};
|
||||
@@ -173,7 +178,7 @@ const UploadDownload: FC = () => {
|
||||
) (
|
||||
<Link
|
||||
target="_blank"
|
||||
href={STABLE_URL + 'v' + latestVersion + '/' + getBinURL(latestVersion)}
|
||||
href={STABLE_URL + 'v' + latestVersion + '/' + getBinURL(latestVersion as string)}
|
||||
color="primary"
|
||||
>
|
||||
{LL.DOWNLOAD(1)}
|
||||
@@ -190,7 +195,7 @@ const UploadDownload: FC = () => {
|
||||
{LL.RELEASE_NOTES()}
|
||||
</Link>
|
||||
) (
|
||||
<Link target="_blank" href={DEV_URL + getBinURL(latestDevVersion)} color="primary">
|
||||
<Link target="_blank" href={DEV_URL + getBinURL(latestDevVersion as string)} color="primary">
|
||||
{LL.DOWNLOAD(1)}
|
||||
</Link>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user