This commit is contained in:
MichaelDvP
2022-05-01 18:15:34 +02:00
32 changed files with 562 additions and 307 deletions

View File

@@ -9,6 +9,8 @@ import * as AuthenticationApi from './api/authentication';
import { PROJECT_NAME } from './api/env';
import { AuthenticationContext } from './contexts/authentication';
import { AxiosError } from 'axios';
import { extractErrorMessage, onEnterCallback, updateValue } from './utils';
import { SignInRequest } from './types';
import { ValidatedTextField } from './components';
@@ -42,9 +44,11 @@ const SignIn: FC = () => {
try {
const { data: loginResponse } = await AuthenticationApi.signIn(signInRequest);
authenticationContext.signIn(loginResponse.access_token);
} catch (error: any) {
if (error.response?.status === 401) {
enqueueSnackbar('Invalid login details', { variant: 'warning' });
} catch (error: unknown) {
if (error instanceof AxiosError) {
if (error.response?.status === 401) {
enqueueSnackbar('Invalid login details', { variant: 'warning' });
}
} else {
enqueueSnackbar(extractErrorMessage(error, 'Unexpected error, please try again'), { variant: 'error' });
}

View File

@@ -24,13 +24,19 @@ const getBorderColor = (theme: Theme, props: DropzoneState) => {
export interface SingleUploadProps {
onDrop: (acceptedFiles: File[]) => void;
onCancel: () => void;
accept?: string | string[];
uploading: boolean;
progress?: ProgressEvent;
}
const SingleUpload: FC<SingleUploadProps> = ({ onDrop, onCancel, accept, uploading, progress }) => {
const dropzoneState = useDropzone({ onDrop, accept, disabled: uploading, multiple: false });
const SingleUpload: FC<SingleUploadProps> = ({ onDrop, onCancel, uploading, progress }) => {
const dropzoneState = useDropzone({
onDrop,
accept: {
'application/octet-stream': ['.bin']
},
disabled: uploading,
multiple: false
});
const { getRootProps, getInputProps } = dropzoneState;
const theme = useTheme();

View File

@@ -43,7 +43,7 @@ const useFileUpload = ({ upload }: MediaUploadOptions) => {
});
resetUploadingStates();
enqueueSnackbar('Upload successful', { variant: 'success' });
} catch (error: any) {
} catch (error: unknown) {
if (axios.isCancel(error)) {
enqueueSnackbar('Upload aborted', { variant: 'warning' });
} else {

View File

@@ -23,9 +23,9 @@ const Authentication: FC = ({ children }) => {
const decodedMe = AuthenticationApi.decodeMeJWT(accessToken);
setMe(decodedMe);
enqueueSnackbar(`Logged in as ${decodedMe.username}`, { variant: 'success' });
} catch (error: any) {
} catch (error: unknown) {
setMe(undefined);
throw new Error('Failed to parse JWT ' + error.message);
throw new Error('Failed to parse JWT');
}
};
@@ -49,7 +49,7 @@ const Authentication: FC = ({ children }) => {
await AuthenticationApi.verifyAuthorization();
setMe(AuthenticationApi.decodeMeJWT(accessToken));
setInitialized(true);
} catch (error: any) {
} catch (error: unknown) {
setMe(undefined);
setInitialized(true);
}

View File

@@ -16,7 +16,7 @@ const FeaturesLoader: FC = (props) => {
try {
const response = await FeaturesApi.readFeatures();
setFeatures(response.data);
} catch (error: any) {
} catch (error: unknown) {
setErrorMessage(extractErrorMessage(error, 'Failed to fetch application details.'));
}
}, []);

View File

@@ -1,13 +1,14 @@
import { useEffect, FC, useState, useCallback, useRef } from 'react';
import { useSnackbar } from 'notistack';
import { AxiosError } from 'axios';
import { Button } from '@mui/material';
import PermScanWifiIcon from '@mui/icons-material/PermScanWifi';
import * as NetworkApi from '../../api/network';
import { WiFiNetwork, WiFiNetworkList } from '../../types';
import { ButtonRow, FormLoader, SectionContent } from '../../components';
import { extractErrorMessage } from '../../utils';
import WiFiNetworkSelector from './WiFiNetworkSelector';
@@ -52,8 +53,12 @@ const WiFiNetworkScanner: FC = () => {
newNetworkList.networks.sort(compareNetworks);
setNetworkList(newNetworkList);
}
} catch (error: any) {
finishedWithError(extractErrorMessage(error, 'Problem listing WiFi networks'));
} catch (error: unknown) {
if (error instanceof AxiosError) {
finishedWithError('Problem listing WiFi networks ' + error.response?.data.message);
} else {
finishedWithError('Problem listing WiFi networks');
}
}
}, [finishedWithError]);
@@ -64,8 +69,12 @@ const WiFiNetworkScanner: FC = () => {
try {
await NetworkApi.scanNetworks();
setTimeout(pollNetworkList, POLLING_FREQUENCY);
} catch (error: any) {
finishedWithError(extractErrorMessage(error, 'Problem scanning for WiFi networks'));
} catch (error: unknown) {
if (error instanceof AxiosError) {
finishedWithError('Problem scanning for WiFi networks ' + error.response?.data.message);
} else {
finishedWithError('Problem scanning for WiFi networks');
}
}
}, [finishedWithError, pollNetworkList]);

View File

@@ -86,7 +86,7 @@ const NTPStatusForm: FC = () => {
enqueueSnackbar('Time set', { variant: 'success' });
setSettingTime(false);
loadData();
} catch (error: any) {
} catch (error: unknown) {
enqueueSnackbar(extractErrorMessage(error, 'Problem updating time'), { variant: 'error' });
} finally {
setProcessing(false);

View File

@@ -33,7 +33,7 @@ const GenerateToken: FC<GenerateTokenProps> = ({ username, onClose }) => {
const getToken = useCallback(async () => {
try {
setToken((await SecurityApi.generateToken(username)).data);
} catch (error: any) {
} catch (error: unknown) {
enqueueSnackbar(extractErrorMessage(error, 'Problem generating token'), { variant: 'error' });
}
}, [username, enqueueSnackbar]);

View File

@@ -21,7 +21,6 @@ const FirmwareFileUpload: FC<UploadFirmwareProps> = ({ uploadFirmware }) => {
/>
)}
<SingleUpload
accept=".bin"
onDrop={uploadFile}
onCancel={cancelUpload}
uploading={uploading}

View File

@@ -17,7 +17,7 @@ const FirmwareRestartMonitor: FC = () => {
try {
await SystemApi.readSystemStatus(POLL_TIMEOUT);
document.location.href = '/firmwareUpdated';
} catch (error: any) {
} catch (error: unknown) {
if (new Date().getTime() < timeoutAt.current) {
setTimeoutId(setTimeout(poll.current, POLL_INTERVAL));
} else {

View File

@@ -106,7 +106,7 @@ const SystemLog: FC = () => {
if (response.status !== 200) {
enqueueSnackbar('Problem applying log settings', { variant: 'error' });
}
} catch (error: any) {
} catch (error: unknown) {
enqueueSnackbar(extractErrorMessage(error, 'Problem applying log settings'), { variant: 'error' });
}
}
@@ -158,7 +158,7 @@ const SystemLog: FC = () => {
const fetchLog = useCallback(async () => {
try {
setLogEntries((await SystemApi.readLogEntries()).data);
} catch (error: any) {
} catch (error: unknown) {
setErrorMessage(extractErrorMessage(error, 'Failed to fetch log'));
}
}, []);

View File

@@ -81,7 +81,7 @@ const SystemStatusForm: FC = () => {
try {
await SystemApi.restart();
enqueueSnackbar('EMS-ESP is restarting...', { variant: 'info' });
} catch (error: any) {
} catch (error: unknown) {
enqueueSnackbar(extractErrorMessage(error, 'Problem restarting device'), { variant: 'error' });
} finally {
setConfirmRestart(false);
@@ -179,7 +179,7 @@ const SystemStatusForm: FC = () => {
try {
await SystemApi.factoryReset();
enqueueSnackbar('Device has been factory reset and will now restart', { variant: 'info' });
} catch (error: any) {
} catch (error: unknown) {
enqueueSnackbar(extractErrorMessage(error, 'Problem factory resetting the device'), { variant: 'error' });
} finally {
setConfirmFactoryReset(false);

View File

@@ -138,6 +138,7 @@ const DashboardData: FC = () => {
border-right: 1px solid transparent;
border-bottom: 1px solid transparent;
&:nth-of-type(1) {
padding-left: 8px;
min-width: 42px;
width: 42px;
div {
@@ -197,21 +198,36 @@ const DashboardData: FC = () => {
border-top: 1px solid #177ac9;
border-bottom: 1px solid #177ac9;
color: white;
cursor: 'pointer',
}
`,
BaseCell: `
padding-left: 8px;
padding-left: 16px;
cursor: pointer;
border-top: 1px solid transparent;
border-right: 1px solid transparent;
border-bottom: 1px solid transparent;
&:nth-of-type(1) {
padding-left: 16px;
}
&:nth-of-type(4) {
padding-left: 16px;
}
width: 124px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&:last-of-type {
text-align: right;
min-width: 64px;
}
`,
HeaderCell: `
padding-left: 0px;
padding-left: 8px;
&:nth-of-type(1) {
padding-left: 8px;
}
&:nth-of-type(4) {
padding-left: 16px;
}
&:not(:last-of-type) {
border-right: 1px solid #565656;
}
@@ -237,6 +253,7 @@ const DashboardData: FC = () => {
iconUp: <KeyboardArrowUpOutlinedIcon />,
iconDown: <KeyboardArrowDownOutlinedIcon />
},
sortToggleType: SortToggleType.AlternateWithReset,
sortFns: {
GPIO: (array) => array.sort((a, b) => a.g - b.g),
NAME: (array) => array.sort((a, b) => a.n.localeCompare(b.n)),
@@ -254,6 +271,7 @@ const DashboardData: FC = () => {
iconUp: <KeyboardArrowUpOutlinedIcon />,
iconDown: <KeyboardArrowDownOutlinedIcon />
},
sortToggleType: SortToggleType.AlternateWithReset,
sortFns: {
NAME: (array) => array.sort((a, b) => a.n.localeCompare(b.n)),
TEMPERATURE: (array) => array.sort((a, b) => a.t - b.t)
@@ -272,7 +290,8 @@ const DashboardData: FC = () => {
},
sortToggleType: SortToggleType.AlternateWithReset,
sortFns: {
NAME: (array) => array.sort((a, b) => a.id.slice(2).localeCompare(b.id.slice(2)))
NAME: (array) => array.sort((a, b) => a.id.slice(2).localeCompare(b.id.slice(2))),
VALUE: (array) => array.sort((a, b) => a.v.toString().localeCompare(b.v.toString()))
}
}
);
@@ -360,7 +379,7 @@ const DashboardData: FC = () => {
const fetchCoreData = useCallback(async () => {
try {
setCoreData((await EMSESP.readCoreData()).data);
} catch (error: any) {
} catch (error: unknown) {
enqueueSnackbar(extractErrorMessage(error, 'Failed to fetch core data'), { variant: 'error' });
}
}, [enqueueSnackbar]);
@@ -381,7 +400,7 @@ const DashboardData: FC = () => {
const unique_id = parseInt(id);
try {
setDeviceData((await EMSESP.readDeviceData({ id: unique_id })).data);
} catch (error: any) {
} catch (error: unknown) {
enqueueSnackbar(extractErrorMessage(error, 'Problem fetching device data'), { variant: 'error' });
}
};
@@ -389,7 +408,7 @@ const DashboardData: FC = () => {
const fetchSensorData = async () => {
try {
setSensorData((await EMSESP.readSensorData()).data);
} catch (error: any) {
} catch (error: unknown) {
enqueueSnackbar(extractErrorMessage(error, 'Problem fetching sensor data'), { variant: 'error' });
}
};
@@ -442,7 +461,7 @@ const DashboardData: FC = () => {
enqueueSnackbar('Write command sent', { variant: 'success' });
}
setDeviceValue(undefined);
} catch (error: any) {
} catch (error: unknown) {
enqueueSnackbar(extractErrorMessage(error, 'Problem writing value'), { variant: 'error' });
} finally {
refreshData();
@@ -533,7 +552,7 @@ const DashboardData: FC = () => {
enqueueSnackbar('Sensor updated', { variant: 'success' });
}
setSensor(undefined);
} catch (error: any) {
} catch (error: unknown) {
enqueueSnackbar(extractErrorMessage(error, 'Problem updating sensor'), { variant: 'error' });
} finally {
setSensor(undefined);
@@ -741,6 +760,7 @@ const DashboardData: FC = () => {
}}
theme={data_theme}
sort={dv_sort}
// layout={{ custom: true }}
>
{(tableList: any) => (
<>
@@ -756,7 +776,16 @@ const DashboardData: FC = () => {
ENTITY NAME
</Button>
</HeaderCell>
<HeaderCell resize>VALUE</HeaderCell>
<HeaderCell resize>
<Button
fullWidth
style={{ fontSize: '14px', justifyContent: 'flex-start' }}
endIcon={getSortIcon(dv_sort.state, 'VALUE')}
onClick={() => dv_sort.fns.onToggleSort({ sortKey: 'VALUE' })}
>
VALUE
</Button>
</HeaderCell>
<HeaderCell />
</HeaderRow>
</Header>
@@ -940,7 +969,7 @@ const DashboardData: FC = () => {
} else {
enqueueSnackbar('Analog sensor removed', { variant: 'success' });
}
} catch (error: any) {
} catch (error: unknown) {
enqueueSnackbar(extractErrorMessage(error, 'Problem updating analog sensor'), { variant: 'error' });
} finally {
setAnalog(undefined);
@@ -968,7 +997,7 @@ const DashboardData: FC = () => {
} else {
enqueueSnackbar('Analog sensor updated', { variant: 'success' });
}
} catch (error: any) {
} catch (error: unknown) {
enqueueSnackbar(extractErrorMessage(error, 'Problem updating analog'), { variant: 'error' });
} finally {
setAnalog(undefined);

View File

@@ -91,6 +91,7 @@ const DashboardStatus: FC = () => {
BaseRow: `
font-size: 14px;
color: white;
height: 32px;
`,
HeaderRow: `
text-transform: uppercase;
@@ -123,9 +124,29 @@ const DashboardStatus: FC = () => {
border-top: 1px solid transparent;
border-right: 1px solid transparent;
border-bottom: 1px solid transparent;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&:nth-of-type(1) {
padding-left: 8px;
flex: 1;
}
&:nth-of-type(2) {
min-width: 15%;
width: 15%;
text-align: right;
}
&:nth-of-type(3) {
min-width: 15%;
width: 15%;
text-align: right;
}
&:last-of-type {
min-width: 15%;
width: 15%;
text-align: right;
padding-right: 8px;
}
`
});
@@ -141,7 +162,7 @@ const DashboardStatus: FC = () => {
try {
await EMSESP.scanDevices();
enqueueSnackbar('Scanning for devices...', { variant: 'info' });
} catch (error: any) {
} catch (error: unknown) {
enqueueSnackbar(extractErrorMessage(error, 'Problem initiating scan'), { variant: 'error' });
} finally {
setConfirmScan(false);

View File

@@ -48,7 +48,7 @@ const HelpInformation: FC = () => {
document.body.removeChild(a);
enqueueSnackbar('File downloaded', { variant: 'info' });
}
} catch (error: any) {
} catch (error: unknown) {
enqueueSnackbar(extractErrorMessage(error, 'Problem with downloading'), { variant: 'error' });
}
};

View File

@@ -64,7 +64,7 @@ const SettingsApplication: FC = () => {
eth_clock_mode: response.data.eth_clock_mode
});
}
} catch (error: any) {
} catch (error: unknown) {
enqueueSnackbar(extractErrorMessage(error, 'Problem fetching board profile'), { variant: 'error' });
} finally {
setProcessingBoard(false);
@@ -103,7 +103,7 @@ const SettingsApplication: FC = () => {
try {
await EMSESP.restart();
enqueueSnackbar('EMS-ESP is restarting...', { variant: 'info' });
} catch (error: any) {
} catch (error: unknown) {
enqueueSnackbar(extractErrorMessage(error, 'Problem restarting device'), { variant: 'error' });
}
};

View File

@@ -62,6 +62,7 @@ const SettingsCustomization: FC = () => {
BaseRow: `
font-size: 14px;
color: white;
height: 32px;
min-height: 32px;
`,
HeaderRow: `
@@ -103,12 +104,14 @@ const SettingsCustomization: FC = () => {
border-top: 1px solid transparent;
border-right: 1px solid transparent;
border-bottom: 1px solid transparent;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&:nth-of-type(1) {
left: 0px;
min-width: 124px;
width: 124px;
padding-left: 0px;
}
&:nth-of-type(2) {
min-width: 70%;
@@ -116,12 +119,14 @@ const SettingsCustomization: FC = () => {
}
`,
HeaderCell: `
padding-left: 0px;
&:nth-of-type(1) {
padding-left: 24px;
}
&:nth-of-type(3) {
border-left: 1px solid #565656;
&:nth-of-type(2) {
padding-left: 0px;
}
&:not(:last-of-type) {
border-right: 1px solid #565656;
}
`
});
@@ -155,7 +160,7 @@ const SettingsCustomization: FC = () => {
const fetchDevices = useCallback(async () => {
try {
setDevices((await EMSESP.readDevices()).data);
} catch (error: any) {
} catch (error: unknown) {
setErrorMessage(extractErrorMessage(error, 'Failed to fetch device list'));
}
}, []);
@@ -168,7 +173,7 @@ const SettingsCustomization: FC = () => {
try {
const data = (await EMSESP.readDeviceEntities({ id: unique_id })).data;
setInitialMask(data);
} catch (error: any) {
} catch (error: unknown) {
setErrorMessage(extractErrorMessage(error, 'Problem fetching device entities'));
}
};
@@ -250,7 +255,7 @@ const SettingsCustomization: FC = () => {
try {
await EMSESP.resetCustomizations();
enqueueSnackbar('All customizations have been removed. Restarting...', { variant: 'info' });
} catch (error: any) {
} catch (error: unknown) {
enqueueSnackbar(extractErrorMessage(error, 'Problem resetting customizations'), { variant: 'error' });
} finally {
setConfirmReset(false);
@@ -278,7 +283,7 @@ const SettingsCustomization: FC = () => {
} else {
enqueueSnackbar('Customization save failed', { variant: 'error' });
}
} catch (error: any) {
} catch (error: unknown) {
enqueueSnackbar(extractErrorMessage(error, 'Problem sending entity list'), { variant: 'error' });
}
setInitialMask(deviceEntities);
@@ -414,17 +419,12 @@ const SettingsCustomization: FC = () => {
</Button>
</Grid>
</Grid>
<Table
data={{ nodes: shown_data }}
theme={entities_theme}
sort={entity_sort}
layout={{ custom: true, horizontalScroll: true }}
>
<Table data={{ nodes: shown_data }} theme={entities_theme} sort={entity_sort} layout={{ custom: true }}>
{(tableList: any) => (
<>
<Header>
<HeaderRow>
<HeaderCell pinLeft>OPTIONS</HeaderCell>
<HeaderCell>OPTIONS</HeaderCell>
<HeaderCell resize>
<Button
fullWidth
@@ -436,7 +436,6 @@ const SettingsCustomization: FC = () => {
</Button>
</HeaderCell>
<HeaderCell resize>VALUE</HeaderCell>
<HeaderCell />
</HeaderRow>
</Header>
<Body>

View File

@@ -1,4 +1,10 @@
import { AxiosError } from 'axios';
export const extractErrorMessage = (error: AxiosError, defaultMessage: string) =>
(error.response && error.response.data ? error.response.data.message : error.message) || defaultMessage;
export const extractErrorMessage = (error: unknown, defaultMessage: string) => {
if (error instanceof AxiosError) {
return error.response && error.response.data && error?.response?.data?.message;
} else if (error instanceof Error) {
return error.message;
}
return defaultMessage;
};

View File

@@ -22,7 +22,7 @@ export const useRest = <D>({ read, update }: RestRequestOptions<D>) => {
setErrorMessage(undefined);
try {
setData((await read()).data);
} catch (error: any) {
} catch (error: unknown) {
const message = extractErrorMessage(error, 'Problem loading data');
enqueueSnackbar(message, { variant: 'error' });
setErrorMessage(message);
@@ -45,7 +45,7 @@ export const useRest = <D>({ read, update }: RestRequestOptions<D>) => {
} else {
enqueueSnackbar('Settings saved', { variant: 'success' });
}
} catch (error: any) {
} catch (error: unknown) {
const message = extractErrorMessage(error, 'Problem saving data');
enqueueSnackbar(message, { variant: 'error' });
setErrorMessage(message);