all of the linting

main
Cameron Clough 2022-01-10 01:40:47 +00:00
parent df44a9f49e
commit 0e6b7c7d3a
23 changed files with 519 additions and 364 deletions

View File

@ -4,9 +4,9 @@ import React, { useState } from 'react';
import Login from './components/views/login';
import UserAdmin from './components/views/useradmin';
import GlobalSnack from './components/widgets/globalSnack';
import DeviceStore from './context/devices';
import ToastStore from './context/toast';
import { UserProvider } from './context/users';
import DevicesProvider from './context/devices';
import ToastProvider from './context/toast';
import UserProvider from './context/users';
import * as authenticationController from './controllers/authentication';
// Connection opened
@ -34,16 +34,15 @@ function App() {
<CssBaseline />
<UserProvider>
<DeviceStore>
<ToastStore>
<DevicesProvider>
<ToastProvider>
<GlobalSnack />
{session ? <UserAdmin /> : <Login />}
</ToastStore>
</DeviceStore>
</ToastProvider>
</DevicesProvider>
</UserProvider>
</ThemeProvider>

View File

@ -1,13 +1,14 @@
import React from 'react';
import PropTypes from 'prop-types';
import Grid from '@mui/material/Grid';
import GoogleMapReact from 'google-map-react';
import React from 'react';
import { Scrollbars } from 'react-custom-scrollbars';
import DrivesTable from './tabPane';
function DeviceLastSeenMap() {
return (
<div style={{ height: '500px', width: 'calc(100%)' }}>
<GoogleMapReact
height="100px"
bootstrapURLKeys={{ key: process.env.REACT_APP_GMAPS_API_KEY }}
@ -103,14 +104,16 @@ function DeviceLastSeenMap() {
);
}
export default function SignIn(props) {
return (
<div style={{
height: '100%',
width: '100%',
}}
>
function DeviceData(props) {
const { device } = props;
return (
<div
style={{
height: '100%',
width: '100%',
}}
>
<Scrollbars autoHeightMin="100%" autoHeightMax="100%">
<Grid container style={{ padding: 30 }}>
<Grid item xs={12}>
@ -118,12 +121,18 @@ export default function SignIn(props) {
</Grid>
<Grid item xs={12}>
<DrivesTable dongleId={props.device.dongle_id} />
<DrivesTable dongleId={device.dongle_id} />
</Grid>
</Grid>
</Scrollbars>
</div>
);
}
DeviceData.propTypes = {
device: PropTypes.shape({
dongle_id: PropTypes.string.isRequired,
}).isRequired,
};
export default DeviceData;

View File

@ -1,9 +1,10 @@
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import ButtonBase from '@mui/material/ButtonBase';
import Chip from '@mui/material/Chip';
import Divider from '@mui/material/Divider';
import Grid from '@mui/material/Grid';
import Typography from '@mui/material/Typography';
import React, { useEffect } from 'react';
const stylezz = {
margin: '0px 2px 0px 0px',
@ -22,10 +23,11 @@ function timeSince(date) {
return 'just now';
}
export default function SignIn(props) {
const [state, setState] = React.useState({ count: 0, last_seen: 0 });
function DeviceOverview(props) {
const { device } = props;
const [state, setState] = React.useState({ count: 0, last_seen: 0 });
// Reloads component to update X time ago
// TODO prevent X time ago from being refreshed when the device has been
// updated to show offline.
@ -72,7 +74,6 @@ export default function SignIn(props) {
</Typography>
<div>
{device.online
? <Chip style={{ background: '#004d40', ...stylezz }} label="Online" size="small" variant="outlined" />
: <Chip style={{ background: '#b71c1c', ...stylezz }} label={`Offline ${deviceLastSeen}`} size="small" variant="outlined" />}
@ -84,6 +85,15 @@ export default function SignIn(props) {
</ButtonBase>
<Divider />
</div>
);
}
DeviceOverview.propTypes = {
device: PropTypes.shape({
dongle_id: PropTypes.string.isRequired,
last_seen: PropTypes.string.isRequired,
online: PropTypes.bool.isRequired,
}).isRequired,
};
export default DeviceOverview;

View File

@ -1,6 +1,5 @@
import DeleteIcon from '@mui/icons-material/Delete';
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
import React, { useContext, useEffect } from 'react';
import PropTypes from 'prop-types';
import { Skeleton } from '@mui/material';
import Box from '@mui/material/Box';
import IconButton from '@mui/material/IconButton';
@ -12,9 +11,12 @@ import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Tooltip from '@mui/material/Tooltip';
import React, { useContext, useEffect } from 'react';
import { context as DeviceContext } from '../../../context/devices';
import { context as SnackbarContext } from '../../../context/toast';
import DeleteIcon from '@mui/icons-material/Delete';
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
import { DevicesContext } from '../../../context/devices';
import { ToastContext } from '../../../context/toast';
import * as deviceController from '../../../controllers/devices';
import * as helpers from '../../../controllers/helpers';
@ -29,20 +31,26 @@ function loading() {
);
}
export default function EnhancedTable(props) {
const [state, dispatch] = useContext(DeviceContext);
function BootLogsTable(props) {
const { dongleId } = props;
const [, notifDispatch] = useContext(SnackbarContext);
const [state, dispatch] = useContext(DevicesContext);
const [, notifDispatch] = useContext(ToastContext);
useEffect(() => {
deviceController.getBootlogs(props.dongleId).then((res) => {
deviceController.getBootlogs(dongleId).then((res) => {
// TODO: why set timeout 1?
setTimeout(() => {
dispatch({ type: 'update_dongle_bootlogs', dongle_id: props.dongleId, bootlogs: res.data });
dispatch({
type: 'update_dongle_bootlogs',
dongle_id: dongleId,
bootlogs: res.data,
});
}, 1);
}).catch(() => {
notifDispatch({ type: 'NEW_TOAST', msg: 'Failed to load bootlogs' });
});
}, [dispatch, notifDispatch, props.dongleId]);
}, [dispatch, notifDispatch, dongleId]);
return (
<Box sx={{ width: '100%' }}>
@ -65,14 +73,13 @@ export default function EnhancedTable(props) {
<TableBody>
{/* if you don't need to support IE11, you can replace the `stableSort` call with:
rows.slice().sort(getComparator(order, orderBy)) */}
{state.dongles[props.dongleId].boot ? state.dongles[props.dongleId].boot.map((row) => (
{state.dongles[dongleId].boot ? state.dongles[dongleId].boot.map((row) => (
<TableRow hover>
<TableCell>{helpers.formatDate(row.date)}</TableCell>
<TableCell>{row.name}</TableCell>
<TableCell>{`${Math.round(row.size / 1024)} MiB`}</TableCell>
<TableCell>
<Tooltip title="Open in new window">
<IconButton size="small" onClick={() => window.open(row.permalink, '_blank')}>
<OpenInNewIcon fontSize="inherit" />
</IconButton>
@ -100,3 +107,9 @@ export default function EnhancedTable(props) {
</Box>
);
}
BootLogsTable.propTypes = {
dongleId: PropTypes.string.isRequired,
};
export default BootLogsTable;

View File

@ -54,31 +54,26 @@ function RichContent(props) {
</AccordionDetails>
</Accordion>
)
}
}
}
}
*/
export default function Console() {
function ConsoleTable() {
return (
<Box sx={{ width: '100%' }}>
<Paper sx={{ width: '100%', mb: 2 }}>
{/*
Object.keys(state).map(key => {
console.log(key, state[key])
return (<RichContent content={state[key]} key1={key} />)
}
)
*/}
</Paper>
</Box>
);
}
export default ConsoleTable;

View File

@ -1,8 +1,5 @@
// eslint-disable-next-line react-hooks/exhaustive-deps
import DeleteIcon from '@mui/icons-material/Delete';
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
import React, { useContext, useEffect } from 'react';
import PropTypes from 'prop-types';
import { Skeleton } from '@mui/material';
import Box from '@mui/material/Box';
import IconButton from '@mui/material/IconButton';
@ -14,17 +11,17 @@ import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Tooltip from '@mui/material/Tooltip';
import React, { useContext, useEffect } from 'react';
import { context as DeviceContext } from '../../../context/devices';
import DeleteIcon from '@mui/icons-material/Delete';
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
import { ACTIONS, DevicesContext } from '../../../context/devices';
import * as deviceController from '../../../controllers/devices';
import * as helpers from '../../../controllers/helpers';
function buildContent(row) {
return (
<TableRow
hover
>
<TableRow hover>
<TableCell>{helpers.formatDate(row.date)}</TableCell>
<TableCell>{row.name}</TableCell>
<TableCell>{`${Math.round(row.size / 1024)} MiB`}</TableCell>
@ -47,7 +44,6 @@ function buildContent(row) {
<DeleteIcon fontSize="inherit" />
</IconButton>
</Tooltip>
</TableCell>
</TableRow>
);
@ -64,17 +60,23 @@ function loading() {
);
}
export default function EnhancedTable(props) {
// eslint-disable-next-line no-unused-vars
const [state, dispatch] = useContext(DeviceContext);
useEffect(() => {
deviceController.getCrashlogs(props.dongleId).then((res) => {
dispatch({ type: 'update_dongle_bootlogs', dongle_id: props.dongleId, bootlogs: res.data });
});
}, [dispatch, props.dongleId]);
function CrashLogsTable(props) {
const { dongleId } = props;
console.log('drives', state.dongles[props.dongleId]);
console.log('drives', typeof state.dongles[props.dongleId]);
// eslint-disable-next-line no-unused-vars
const [state, dispatch] = useContext(DevicesContext);
useEffect(() => {
deviceController.getCrashlogs(dongleId).then((res) => {
dispatch({
type: ACTIONS.UPDATE_DONGLE_BOOTLOGS,
dongle_id: dongleId,
bootlogs: res.data,
});
});
}, [dispatch, dongleId]);
console.log('drives', state.dongles[dongleId]);
console.log('drives', typeof state.dongles[dongleId]);
return (
<Box sx={{ width: '100%' }}>
@ -95,10 +97,11 @@ export default function EnhancedTable(props) {
</TableHead>
<TableBody>
{state.dongles[props.dongleId].crash
? state.dongles[props.dongleId].crash.length > 0 ? state.dongles[props.dongleId].crash.map(buildContent) : <p> No drives </p>
{state.dongles[dongleId].crash
? (state.dongles[dongleId].crash.length > 0
? state.dongles[dongleId].crash.map(buildContent)
: <p> No drives </p>)
: [1, 1, 1, 1, 1].map(loading)}
</TableBody>
</Table>
</TableContainer>
@ -107,3 +110,9 @@ export default function EnhancedTable(props) {
</Box>
);
}
CrashLogsTable.propTypes = {
dongleId: PropTypes.string.isRequired,
};
export default CrashLogsTable;

View File

@ -1,37 +1,43 @@
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import Grid from '@mui/material/Grid';
import IconButton from '@mui/material/IconButton';
import Skeleton from '@mui/material/Skeleton';
import Tooltip from '@mui/material/Tooltip';
import Typography from '@mui/material/Typography';
import React, { useContext } from 'react';
import { context as DeviceContext } from '../../../context/devices';
import { context as SnackbarContext } from '../../../context/toast';
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import { DevicesContext } from '../../../context/devices';
import { ACTIONS as ToastActions, ToastContext } from '../../../context/toast';
import * as helpers from '../../../controllers/helpers';
export default function SignIn(props) {
const [state] = useContext(DeviceContext);
const [, notifDispatch] = useContext(SnackbarContext);
function DeviceInfo(props) {
const { dongleId } = props;
const [devicesState] = useContext(DevicesContext);
const [, toastDispatch] = useContext(ToastContext);
if (!devicesState.dongles[dongleId]) {
return (<p>no</p>);
}
function pubKeyClipboard(newClip) {
navigator.clipboard.writeText(newClip).then(() => {
notifDispatch({
type: 'NEW_TOAST',
toastDispatch({
type: ToastActions.NEW_TOAST,
open: true,
msg: 'Successfully copied to clipboard!',
});
}, () => {
notifDispatch({
type: 'NEW_TOAST',
toastDispatch({
type: ToastActions.NEW_TOAST,
open: true,
msg: 'Failed to write to clipboard!',
});
});
}
if (!state.dongles[props.dongleId]) { return (<p>no</p>); }
const dongle = state.dongles[props.dongleId];
const dongle = devicesState.dongles[dongleId];
if (!dongle) {
return (
<Grid container>
@ -40,15 +46,13 @@ export default function SignIn(props) {
<Skeleton animation="wave" />
<Skeleton animation="wave" />
</Grid>
</Grid>
);
}
return (
<div className="wrapper" style={{ marginTop: '10px' }}>
<Typography variant="body1">{state.dongles[props.dongleId].dongle_id}</Typography>
<Typography variant="body1">{devicesState.dongles[dongleId].dongle_id}</Typography>
<Grid container>
<Grid item xs={3}>
@ -97,3 +101,9 @@ export default function SignIn(props) {
</div>
);
}
DeviceInfo.propTypes = {
dongleId: PropTypes.string.isRequired,
};
export default DeviceInfo;

View File

@ -1,6 +1,5 @@
import DeleteIcon from '@mui/icons-material/Delete';
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
import React, { useContext, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { Skeleton } from '@mui/material';
import Box from '@mui/material/Box';
import IconButton from '@mui/material/IconButton';
@ -12,27 +11,32 @@ import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Tooltip from '@mui/material/Tooltip';
import React, { useContext, useEffect, useState } from 'react';
import { context as DeviceContext } from '../../../context/devices';
import { context as SnackbarContext } from '../../../context/toast';
import DeleteIcon from '@mui/icons-material/Delete';
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
import { DevicesContext } from '../../../context/devices';
import { ToastContext } from '../../../context/toast';
import * as deviceController from '../../../controllers/devices';
import * as helpers from '../../../controllers/helpers';
import ViewDrive from './view_drive';
export default function EnhancedTable(props) {
const [deviceState, dispatch] = useContext(DeviceContext);
const [, notifDispatch] = useContext(SnackbarContext);
function DrivesLogTable(props) {
const { dongleId } = props;
const [devicesState, dispatch] = useContext(DevicesContext);
const [, notifDispatch] = useContext(ToastContext);
const [state, setState] = useState({ selectedSegment: null });
useEffect(() => {
deviceController.getDrives(props.dongleId).then((res) => {
deviceController.getDrives(dongleId).then((res) => {
setTimeout(() => {
dispatch({ type: 'update_dongle_drive', dongle_id: props.dongleId, drives: res.data });
dispatch({ type: 'update_dongle_drive', dongle_id: dongleId, drives: res.data });
}, 1);
}).catch(() => {
notifDispatch({ type: 'NEW_TOAST', msg: 'Failed to load drives' });
});
}, [dispatch, notifDispatch, props.dongleId]);
}, [dispatch, notifDispatch, dongleId]);
return (
<Box sx={{ width: '100%' }}>
@ -61,57 +65,58 @@ export default function EnhancedTable(props) {
<TableBody>
{/* if you don't need to support IE11, you can replace the `stableSort` call with:
rows.slice().sort(getComparator(order, orderBy)) */}
{deviceState.dongles[props.dongleId].drives ? deviceState.dongles[props.dongleId].drives.map((row, index) => {
let metadata;
{devicesState.dongles[dongleId].drives
? devicesState.dongles[dongleId].drives.map((row, index) => {
let metadata;
try {
metadata = JSON.parse(row.metadata);
} catch (err) { metadata = {}; }
return (
<TableRow
hover
onClick={() => { state.selectedSegment === index ? setState({ ...state, selectedSegment: null }) : setState({ ...state, selectedSegment: index }); }}
>
<TableCell
scope="row"
try {
metadata = JSON.parse(row.metadata);
} catch (err) { metadata = {}; }
return (
<TableRow
hover
onClick={() => (state.selectedSegment === index
? setState({ ...state, selectedSegment: null })
: setState({ ...state, selectedSegment: index }))}
>
{row.identifier}
</TableCell>
<TableCell scope="row">
{row.identifier}
</TableCell>
<TableCell>{metadata.hasOwnProperty('CarParams1') ? metadata.CarParams.CarName : 'Glorious Skoda'}</TableCell>
<TableCell>{metadata.hasOwnProperty('InitData1') ? metadata.InitData.Version : 'Lemon boy'}</TableCell>
<TableCell>{`${Math.round(row.filesize / 1024)} MiB`}</TableCell>
<TableCell>{helpers.formatDuration(row.duration)}</TableCell>
<TableCell>{Math.round(row.distance_meters / 1000)}</TableCell>
<TableCell>{row.upload_complete.toString()}</TableCell>
<TableCell>{row.is_processed.toString()}</TableCell>
<TableCell>{helpers.formatDate(row.drive_date)}</TableCell>
<TableCell>{metadata.CarParams ? metadata.CarParams.CarName : 'Glorious Skoda'}</TableCell>
<TableCell>{metadata.InitData ? metadata.InitData.Version : 'Lemon boy'}</TableCell>
<TableCell>{`${Math.round(row.filesize / 1024)} MiB`}</TableCell>
<TableCell>{helpers.formatDuration(row.duration)}</TableCell>
<TableCell>{Math.round(row.distance_meters / 1000)}</TableCell>
<TableCell>{row.upload_complete.toString()}</TableCell>
<TableCell>{row.is_processed.toString()}</TableCell>
<TableCell>{helpers.formatDate(row.drive_date)}</TableCell>
<TableCell>
<Tooltip title="Open in new window">
<IconButton size="small">
<OpenInNewIcon fontSize="inherit" />
</IconButton>
</Tooltip>
<TableCell>
<Tooltip title="Open in new window">
<IconButton size="small">
<OpenInNewIcon fontSize="inherit" />
</IconButton>
</Tooltip>
<Tooltip title="Preserve">
<IconButton size="small">
<FavoriteBorderIcon fontSize="inherit" />
</IconButton>
</Tooltip>
<Tooltip title="Preserve">
<IconButton size="small">
<FavoriteBorderIcon fontSize="inherit" />
</IconButton>
</Tooltip>
<Tooltip title="Delete">
<IconButton size="small">
<DeleteIcon fontSize="inherit" />
</IconButton>
</Tooltip>
<Tooltip title="Delete">
<IconButton size="small">
<DeleteIcon fontSize="inherit" />
</IconButton>
</Tooltip>
</TableCell>
</TableRow>
);
})
</TableCell>
</TableRow>
);
})
: [1, 1, 1, 1, 1].map((v) => (
: [1, 1, 1, 1, 1].map(() => (
<TableRow>
<TableCell padding="checkbox">
<Skeleton animation="wave" />
@ -138,7 +143,15 @@ export default function EnhancedTable(props) {
</TableContainer>
</Paper>
{state.selectedSegment ? <ViewDrive dongleId={props.dongleId} drive={state.selectedSegment} /> : null }
{state.selectedSegment
? <ViewDrive dongleId={dongleId} drive={state.selectedSegment} />
: null }
</Box>
);
}
DrivesLogTable.propTypes = {
dongleId: PropTypes.string.isRequired,
};
export default DrivesLogTable;

View File

@ -1,16 +1,20 @@
import React from 'react';
import PropTypes from 'prop-types';
import Box from '@mui/material/Box';
import Tab from '@mui/material/Tab';
import Tabs from '@mui/material/Tabs';
import React from 'react';
import BootLogsTable from './boot';
import Console from './console';
import ConsoleTable from './console';
import CrashLogsTable from './crash';
import DeviceInfo from './device';
import DrivesLogTable from './drives';
function TabPanel(props) {
const {
children, value, index, ...other
children,
value,
index,
} = props;
return (
@ -19,7 +23,6 @@ function TabPanel(props) {
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
{...other}
>
{value === index && (
<div style={{ padding: '5px' }}>
@ -30,7 +33,15 @@ function TabPanel(props) {
);
}
export default function SignIn(props) {
TabPanel.propTypes = {
children: PropTypes.node.isRequired,
index: PropTypes.number.isRequired,
value: PropTypes.number.isRequired,
};
function TabPane(props) {
const { dongleId } = props;
const [value, setValue] = React.useState(0);
const handleChange = (event, newValue) => {
@ -39,7 +50,6 @@ export default function SignIn(props) {
return (
<div className="wrapper">
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs
value={value}
@ -56,24 +66,26 @@ export default function SignIn(props) {
</Tabs>
</Box>
<TabPanel value={value} index={0}>
<DeviceInfo dongleId={props.dongleId} />
<DeviceInfo dongleId={dongleId} />
</TabPanel>
<TabPanel value={value} index={1}>
<DrivesLogTable dongleId={props.dongleId} />
<DrivesLogTable dongleId={dongleId} />
</TabPanel>
<TabPanel value={value} index={2}>
<CrashLogsTable dongleId={props.dongleId} />
<CrashLogsTable dongleId={dongleId} />
</TabPanel>
<TabPanel value={value} index={3}>
<BootLogsTable dongleId={props.dongleId} />
<BootLogsTable dongleId={dongleId} />
</TabPanel>
<TabPanel value={value} index={4}>
<Console dongleId={props.dongleId} />
<ConsoleTable dongleId={dongleId} />
</TabPanel>
{
}
</div>
);
}
TabPane.propTypes = {
dongleId: PropTypes.string.isRequired,
};
export default TabPane;

View File

@ -1,3 +1,5 @@
import React, { useContext, useState } from 'react';
import PropTypes from 'prop-types';
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
import Box from '@mui/material/Box';
import IconButton from '@mui/material/IconButton';
@ -9,38 +11,41 @@ import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Tooltip from '@mui/material/Tooltip';
import React, { useContext, useState } from 'react';
import Typography from '@mui/material/Typography';
import { context as DeviceContext } from '../../../context/devices';
import { DevicesContext } from '../../../context/devices';
import * as deviceController from '../../../controllers/devices';
export default function EnhancedTable(props) {
const [deviceState] = useContext(DeviceContext);
function ViewDrive(props) {
const {
dongleId,
drive: driveId,
} = props;
const [deviceState] = useContext(DevicesContext);
const [state, setState] = useState({
loading: true, firstReqSent: false, segment: null, drive: null,
loading: true, firstReqSent: false, segment: null, driveId: null,
});
if (state.drive === null) {
setState({ ...state, drive: props.drive });
setState({ ...state, driveId });
}
if (props.drive !== state.drive) {
if (driveId !== state.drive) {
setState({
...state, loading: true, firstReqSent: false, segment: null, drive: props.drive,
...state, loading: true, firstReqSent: false, segment: null, driveId,
});
}
const dongle_id = props.dongleId;
const drive_id = props.drive;
const dongle = deviceState.dongles[dongle_id];
const dongle = deviceState.dongles[dongleId];
console.log('view drive', dongle);
console.log('drives', dongle.drives);
if (!dongle || !dongle.drives) return (<p>loading</p>);
if (state.segment === null) {
// TODO Make this not run multiple times
deviceController.getDriveSegments(dongle_id, dongle.drives[drive_id].identifier).then((res) => {
deviceController.getDriveSegments(dongleId, dongle.drives[driveId].identifier).then((res) => {
console.log('my res', res.data);
if (res.data === null) {
setState({
@ -56,7 +61,7 @@ export default function EnhancedTable(props) {
// test
const drive = dongle.drives[drive_id];
const drive = dongle.drives[driveId];
let vehicle = '';
let version = '';
@ -76,8 +81,12 @@ export default function EnhancedTable(props) {
}
if (metadata.CarParams) {
if (metadata.CarParams.CarName !== undefined) vehicle += `${metadata.CarParams.CarName.toUpperCase()} `;
if (metadata.CarParams.CarFingerprint !== undefined) vehicle += (metadata.CarParams.CarFingerprint.toUpperCase());
if (metadata.CarParams.CarName !== undefined) {
vehicle += `${metadata.CarParams.CarName.toUpperCase()} `;
}
if (metadata.CarParams.CarFingerprint !== undefined) {
vehicle += (metadata.CarParams.CarFingerprint.toUpperCase());
}
}
} catch (exception) { console.log(exception); }
@ -99,7 +108,6 @@ export default function EnhancedTable(props) {
url: `${driveUrl}/${segment}/${directoryTree.children[i].children[c].name}`,
name: directoryTree.children[i].children[c].name,
fileSize: directoryTree.children[i].children[c].size,
};
}
@ -112,7 +120,6 @@ export default function EnhancedTable(props) {
return (
<Box sx={{ width: '100%' }}>
<Paper sx={{ width: '100%', mb: 2, padding: '20px' }}>
<Typography variant="body1">
<b>Vehicle:</b>
{' '}
@ -157,33 +164,41 @@ export default function EnhancedTable(props) {
</TableHead>
<TableBody>
{
directorySegments ? Object.keys(directorySegments).map((key, index) => Object.keys(directorySegments[key]).map((key1, index1) => (
<TableRow hover>
<TableCell>{key}</TableCell>
<TableCell>{directorySegments[key][key1].name}</TableCell>
<TableCell>{`${Math.round(directorySegments[key][key1].fileSize / 1024)} MiB`}</TableCell>
<TableCell>
<Tooltip title="Open in new window">
<IconButton size="small" onClick={() => window.open(directorySegments[key][key1].url, '_blank')}>
<OpenInNewIcon fontSize="inherit" />
</IconButton>
</Tooltip>
</TableCell>
</TableRow>
))) : null
}
directorySegments
? Object.keys(directorySegments)
.map((key) => Object.keys(directorySegments[key])
.map((key1) => (
<TableRow hover>
<TableCell>{key}</TableCell>
<TableCell>{directorySegments[key][key1].name}</TableCell>
<TableCell>{`${Math.round(directorySegments[key][key1].fileSize / 1024)} MiB`}</TableCell>
<TableCell>
<Tooltip title="Open in new window">
<IconButton size="small" onClick={() => window.open(directorySegments[key][key1].url, '_blank')}>
<OpenInNewIcon fontSize="inherit" />
</IconButton>
</Tooltip>
</TableCell>
</TableRow>
)))
: null
}
</TableBody>
</Table>
</TableContainer>
</Paper>
</Box>
);
}
ViewDrive.propTypes = {
dongleId: PropTypes.string.isRequired,
drive: PropTypes.string.isRequired,
};
export default ViewDrive;
/*
var dongleIdHash = crypto.createHmac('sha256', config.applicationSalt).update(device.dongle_id).digest('hex');

View File

@ -1,28 +1,10 @@
import { createTheme, ThemeProvider } from '@mui/material/styles';
import React, { useContext, useState } from 'react';
import React from 'react';
import { Link } from 'react-router-dom';
import { UserContext } from '../../context/users';
import { createTheme, ThemeProvider } from '@mui/material/styles';
const theme = createTheme();
export default function SignIn() {
const [loading, setLoading] = useState(false);
const [state, dispatch] = useContext(UserContext);
console.log('component', state);
const handleSubmit = (event) => {
dispatch({ type: 'toggle_button' });
event.preventDefault();
const data = new FormData(event.currentTarget);
// eslint-disable-next-line no-console
console.log({
email: data.get('email'),
password: data.get('password'),
});
setLoading(true);
};
function Home() {
return (
<ThemeProvider theme={theme}>
<p>hello</p>
@ -32,3 +14,5 @@ export default function SignIn() {
</ThemeProvider>
);
}
export default Home;

View File

@ -1,3 +1,4 @@
import React, { useContext, useState } from 'react';
import LoadingButton from '@mui/lab/LoadingButton';
import Box from '@mui/material/Box';
import Container from '@mui/material/Container';
@ -6,7 +7,7 @@ import Link from '@mui/material/Link';
import Paper from '@mui/material/Paper';
import TextField from '@mui/material/TextField';
import Typography from '@mui/material/Typography';
import React, { useContext, useState } from 'react';
import { UserContext } from '../../context/users';
export default function SignIn() {

View File

@ -1,17 +1,17 @@
import React, { useContext } from 'react';
import Grid from '@mui/material/Grid';
import Paper from '@mui/material/Paper';
import { Scrollbars } from 'rc-scrollbars';
import React, { useContext } from 'react';
import { context as DeviceContext } from '../../context/devices';
import { DevicesContext } from '../../context/devices';
import DeviceData from '../device/deviceData';
import DeviceOverview from '../device/overview';
export default function SignIn() {
const [deviceState] = useContext(DeviceContext);
const [deviceState] = useContext(DevicesContext);
return (
<div className="wrapper">
<Grid
container
spacing={0}
@ -23,18 +23,17 @@ export default function SignIn() {
<Paper style={{ minHeight: '100%', maxHeight: '100%', margin: '0' }}>
<Scrollbars autoHeight autoHeightMin="calc(100vh - 14px)" autoHeightMax="calc(100% - 14px)">
<div style={{ padding: '5px' }}>
{deviceState ? Object.keys(deviceState.dongles).map((key) => <DeviceOverview device={deviceState.dongles[key]} />) : <p>no</p>}
{deviceState
? (Object.keys(deviceState.dongles)
.map((key) => <DeviceOverview device={deviceState.dongles[key]} />))
: <p>no</p>}
</div>
</Scrollbars>
</Paper>
</Grid>
<Grid item xs={12} md={8} lg={9} sm={6} xl={10}>
{deviceState.dongles['53331425'] ? <DeviceData device={deviceState.dongles['53331425']} /> : <p>no</p>}
</Grid>
</Grid>
</div>

View File

@ -1,12 +1,13 @@
import Snackbar from '@mui/material/Snackbar';
import React, { useContext } from 'react';
import { context as DeviceContext } from '../../context/toast';
import Snackbar from '@mui/material/Snackbar';
export default function Toast(props) {
const [state, dispatch] = useContext(DeviceContext);
import { ACTIONS, ToastContext } from '../../context/toast';
function Toast() {
const [state, dispatch] = useContext(ToastContext);
const handleClose = () => {
dispatch({ type: 'CLOSE_TOAST' });
dispatch({ type: ACTIONS.CLOSE_TOAST });
};
return (
@ -19,3 +20,5 @@ export default function Toast(props) {
/>
);
}
export default Toast;

View File

@ -0,0 +1,10 @@
const ACTIONS = {
ADD_DATA: 'add_data',
FETCH_ALL_DONGLES: 'fetch_all_dongles',
UPDATE_DONGLE_DRIVE: 'update_dongle_drive',
UPDATE_DONGLE_BOOTLOGS: 'update_dongle_bootlogs',
UPDATE_DONGLE_CRASHLOGS: 'update_dongle_crashlogs',
USER_AUTHENTICATION: 'user_authentication',
};
export default ACTIONS;

View File

@ -1,125 +1,68 @@
import React, { createContext, useEffect, useReducer } from 'react';
import React, {
createContext,
useEffect,
useMemo,
useReducer,
} from 'react';
import PropTypes from 'prop-types';
import * as deviceController from '../../controllers/devices';
function process(state, action) {
if (action.type !== 'ADD_DATA') { return state; }
switch (action.data.command) {
case 'dongle_status':
return {
...state,
dongles: {
...state.dongles,
[action.data.data.dongle_id]: {
...state.dongles[action.data.data.dongle_id],
online: action.data.data.online,
last_seen: action.data.data.time,
dongle_id: action.data.data.dongle_id,
},
},
};
default:
return state;
}
}
export const Reducer = (state, action) => {
console.log('input', state, action);
switch (action.type) {
case 'ADD_DATA':
return process(state, action);
case 'fetch_all_dongles':
console.log('fetch', action);
return {
...state,
dongles: action.data,
};
case 'update_dongle_drive':
return {
...state,
dongles: {
...state.dongles,
[action.dongle_id]: {
...state.dongles[action.dongle_id],
drives: action.drives,
},
},
};
case 'update_dongle_bootlogs':
return {
...state,
dongles: {
...state.dongles,
[action.dongle_id]: {
...state.dongles[action.dongle_id],
boot: action.bootlogs,
},
},
};
case 'update_dongle_crashlogs':
return {
...state,
dongles: {
...state.dongles,
[action.dongle_id]: {
...state.dongles[action.dongle_id],
crash: action.crashlogs,
},
},
};
case 'user_authentication':
return {
...state,
user: action.user,
};
default:
return state;
}
};
import ACTIONS from './actions';
import reducer from './reducer';
const initialState = {
dongles: {},
};
function Store({ children }) {
console.log('STORE HAS BEEN RERENDERED');
const [state, dispatch] = useReducer(Reducer, initialState);
const DevicesContext = createContext(initialState);
function DevicesProvider({ children }) {
const [state, dispatch] = useReducer(reducer, initialState);
useEffect(() => {
const ws = new WebSocket('ws://localhost:81');
ws.onmessage = ({ data }) => {
data = JSON.parse(data);
console.log('Message');
if (data.id) {
dispatch({ type: 'ADD_DATA', id: data.id, data });
const payload = JSON.parse(data);
console.log('devices onmessage', payload);
if (!payload.id) {
dispatch({ type: ACTIONS.ADD_DATA, data: payload });
}
};
deviceController.getAllDevices().then((devices) => {
console.log('store', devices);
console.log('devices store', devices);
dispatch({ type: 'fetch_all_dongles', data: devices });
dispatch({ type: ACTIONS.FETCH_ALL_DONGLES, data: devices });
});
return () => {
// Clean up the websocket
try {
ws.close();
} catch (e) { }
} catch (e) {
// do nothing
}
};
}, []);
const contextValue = useMemo(() => ([state, dispatch]), [state, dispatch]);
return (
<context.Provider value={[state, dispatch]}>
{children}
</context.Provider>
<DevicesContext.Provider value={contextValue}>
{ children }
</DevicesContext.Provider>
);
}
export const context = createContext(initialState);
export default Store;
DevicesProvider.propTypes = {
children: PropTypes.node.isRequired,
};
export default DevicesProvider;
export {
ACTIONS,
DevicesContext,
};

View File

@ -0,0 +1,87 @@
import ACTIONS from './actions';
function process(state, action) {
if (action.type !== ACTIONS.ADD_DATA) {
return state;
}
switch (action.data.command) {
case 'dongle_status':
return {
...state,
dongles: {
...state.dongles,
[action.data.data.dongle_id]: {
...state.dongles[action.data.data.dongle_id],
online: action.data.data.online,
last_seen: action.data.data.time,
dongle_id: action.data.data.dongle_id,
},
},
};
default:
return state;
}
}
function reducer(state, action) {
console.log('input', state, action);
switch (action.type) {
case ACTIONS.ADD_DATA:
return process(state, action);
case ACTIONS.FETCH_ALL_DONGLES:
console.log('fetch', action);
return {
...state,
dongles: action.data,
};
case ACTIONS.UPDATE_DONGLE_DRIVE:
return {
...state,
dongles: {
...state.dongles,
[action.dongle_id]: {
...state.dongles[action.dongle_id],
drives: action.drives,
},
},
};
case ACTIONS.UPDATE_DONGLE_BOOTLOGS:
return {
...state,
dongles: {
...state.dongles,
[action.dongle_id]: {
...state.dongles[action.dongle_id],
boot: action.bootlogs,
},
},
};
case ACTIONS.UPDATE_DONGLE_CRASHLOGS:
return {
...state,
dongles: {
...state.dongles,
[action.dongle_id]: {
...state.dongles[action.dongle_id],
crash: action.crashlogs,
},
},
};
case ACTIONS.USER_AUTHENTICATION:
return {
...state,
user: action.user,
};
default:
return state;
}
}
export default reducer;

View File

@ -0,0 +1,6 @@
const ACTIONS = {
NEW_TOAST: 'new_toast',
CLOSE_TOAST: 'close_toast',
};
export default ACTIONS;

View File

@ -1,19 +1,34 @@
import React, { createContext, useReducer } from 'react';
import Reducer from './reducer';
import React, { createContext, useMemo, useReducer } from 'react';
import PropTypes from 'prop-types';
import ACTIONS from './actions';
import reducer from './reducer';
const initialState = {
open: false,
message: null,
};
function Store({ children }) {
const [state, dispatch] = useReducer(Reducer, initialState);
const ToastContext = createContext(initialState);
function ToastProvider({ children }) {
const [state, dispatch] = useReducer(reducer, initialState);
const contextValue = useMemo(() => ([state, dispatch]), [state, dispatch]);
return (
<context.Provider value={[state, dispatch]}>
<ToastContext.Provider value={contextValue}>
{children}
</context.Provider>
</ToastContext.Provider>
);
}
export const context = createContext(initialState);
export default Store;
ToastProvider.propTypes = {
children: PropTypes.node.isRequired,
};
export default ToastProvider;
export {
ACTIONS,
ToastContext,
};

View File

@ -1,12 +1,14 @@
const Reducer = (state, action) => {
import ACTIONS from './actions';
function reducer(state, action) {
switch (action.type) {
case 'NEW_TOAST':
case ACTIONS.NEW_TOAST:
return {
...state,
open: action.open,
msg: action.message,
};
case 'CLOSE_TOAST':
case ACTIONS.CLOSE_TOAST:
return {
...state,
open: false,
@ -15,6 +17,6 @@ const Reducer = (state, action) => {
default:
return state;
}
};
}
export default Reducer;
export default reducer;

View File

@ -0,0 +1,5 @@
const ACTIONS = {
SIGN_OUT: 'sign_out',
};
export default ACTIONS;

View File

@ -1,17 +1,38 @@
import React from 'react';
import { reducer, initialState } from './reducer';
import React, { createContext, useMemo, useReducer } from 'react';
import PropTypes from 'prop-types';
export const UserContext = React.createContext({
state: initialState,
dispatch: () => null,
});
import ACTIONS from './actions';
import reducer from './reducer';
export function UserProvider({ children }) {
const [state, dispatch] = React.useReducer(reducer, initialState);
const initialState = {
signedIn: false,
user: {
id: null,
username: null,
JWT: null,
},
};
const UserContext = createContext(initialState);
function UserProvider({ children }) {
const [state, dispatch] = useReducer(reducer, initialState);
const contextValue = useMemo(() => ([state, dispatch]), [state, dispatch]);
return (
<UserContext.Provider value={[state, dispatch]}>
<UserContext.Provider value={contextValue}>
{ children }
</UserContext.Provider>
);
}
UserProvider.propTypes = {
children: PropTypes.node.isRequired,
};
export default UserProvider;
export {
ACTIONS,
UserContext,
};

View File

@ -1,7 +1,8 @@
export const reducer = (state, action) => {
switch (action.type) {
case 'sign_out':
import ACTIONS from './actions';
function reducer(state, action) {
switch (action.type) {
case ACTIONS.SIGN_OUT:
return {
...state,
active: !state.active,
@ -10,13 +11,6 @@ export const reducer = (state, action) => {
default:
return state;
}
};
}
export const initialState = {
signedIn: false,
user: {
id: null,
username: null,
JWT: null,
},
};
export default reducer;