data
parent
d0083947e5
commit
9b5ace58c5
|
@ -32,11 +32,13 @@ import CssBaseline from '@mui/material/CssBaseline';
|
|||
function App() {
|
||||
|
||||
|
||||
|
||||
|
||||
const theme = React.useMemo(
|
||||
() =>
|
||||
createTheme({
|
||||
palette: {
|
||||
mode: true ? 'dark' : 'light',
|
||||
mode: window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light',
|
||||
},
|
||||
}),
|
||||
[true],
|
||||
|
|
|
@ -31,7 +31,6 @@ import GoogleMapReact from 'google-map-react';
|
|||
import axios from "axios"
|
||||
|
||||
import {context as SnackbarContext} from "./../../context/toast"
|
||||
import {updateDongleInfo, athenaCommandControl} from "./../../controllers/devices"
|
||||
|
||||
const useStyles = makeStyles({
|
||||
controlsButton: {
|
||||
|
@ -53,18 +52,7 @@ function DeviceControls() {
|
|||
}
|
||||
|
||||
async function athenaReboot() {
|
||||
const data = await athenaCommandControl("c3a5d816", "reboot");
|
||||
console.log(data);
|
||||
if (data.dispatched) {
|
||||
notifdispatch({type: 'NEW_TOAST', message: 'Athena(Reboot) ISSUED COMMAND', open: true})
|
||||
|
||||
} else if (data.hasOwnProperty("connected") && data.connected === false) {
|
||||
notifdispatch({type: 'NEW_TOAST', message: 'Athena(Reboot) NOT CONNECTED', open: true})
|
||||
|
||||
} else {
|
||||
notifdispatch({type: 'NEW_TOAST', message: 'Athena(Reboot) FAILED', open: true})
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -97,18 +85,19 @@ function DeviceControls() {
|
|||
}
|
||||
|
||||
function DeviceLastSeenMap() {
|
||||
console.log("api key", process.env)
|
||||
return (
|
||||
<div style={{ height: "500px", width: 'calc(100%)' }}>
|
||||
|
||||
<GoogleMapReact
|
||||
height="100px"
|
||||
bootstrapURLKeys={{ key: "AIzaSyAYGRgKRk4G9m8SPIjKUfEZG3cqqXH0enc" }}
|
||||
bootstrapURLKeys={{ key: process.env.REACT_APP_GMAPS_API_KEY }}
|
||||
defaultCenter={{
|
||||
lat: 51.501134,
|
||||
lng: -0.142318
|
||||
}}
|
||||
defaultZoom={17}
|
||||
options={{
|
||||
options={{
|
||||
styles: [
|
||||
{ elementType: "geometry", stylers: [{ color: "#242f3e" }] },
|
||||
{ elementType: "labels.text.stroke", stylers: [{ color: "#242f3e" }] },
|
||||
|
@ -116,7 +105,7 @@ function DeviceLastSeenMap() {
|
|||
{
|
||||
featureType: "administrative.locality",
|
||||
elementType: "labels.text.fill",
|
||||
stylers: [{ color: "#d59563" }],
|
||||
stylers: [{ color: "#d59563" }],
|
||||
},
|
||||
{
|
||||
featureType: "poi",
|
||||
|
@ -196,7 +185,7 @@ function DeviceLastSeenMap() {
|
|||
}
|
||||
|
||||
|
||||
export default function SignIn() {
|
||||
export default function SignIn(props) {
|
||||
const classes = useStyles();
|
||||
const [ state, dispatch ] = useContext(DeviceContext)
|
||||
|
||||
|
@ -215,9 +204,7 @@ export default function SignIn() {
|
|||
<Grid container style={{padding: 30}}>
|
||||
|
||||
|
||||
<Grid item xs={12}>
|
||||
<DeviceControls />
|
||||
</Grid>
|
||||
|
||||
|
||||
<Grid item xs={12}>
|
||||
<DeviceLastSeenMap />
|
||||
|
@ -225,7 +212,7 @@ export default function SignIn() {
|
|||
|
||||
|
||||
<Grid item xs={12}>
|
||||
<DrivesTable />
|
||||
<DrivesTable dongleId={props.device.dongle_id}/>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
|
|
@ -34,38 +34,26 @@ const stylezz = {
|
|||
|
||||
function timeSince(date) {
|
||||
|
||||
|
||||
|
||||
var seconds = Math.floor((new Date() - date) / 1000);
|
||||
|
||||
var interval = seconds / 31536000;
|
||||
if (seconds / 86400 > 1) {
|
||||
return Math.floor(seconds / 86400) + `d`;
|
||||
} else if (seconds / 3600 > 1) {
|
||||
return Math.floor(seconds / 3600) + `h`;
|
||||
} else if (seconds / 60 > 1) {
|
||||
return Math.floor(seconds / 60) + `m`;
|
||||
} else {
|
||||
return "just now";
|
||||
}
|
||||
|
||||
if (interval > 1) {
|
||||
return Math.floor(interval) + " years";
|
||||
}
|
||||
interval = seconds / 2592000;
|
||||
if (interval > 1) {
|
||||
return Math.floor(interval) + " months";
|
||||
}
|
||||
interval = seconds / 86400;
|
||||
if (interval > 1) {
|
||||
return Math.floor(interval) + " days";
|
||||
}
|
||||
interval = seconds / 3600;
|
||||
if (interval > 1) {
|
||||
return Math.floor(interval) + " hours";
|
||||
}
|
||||
interval = seconds / 60;
|
||||
if (interval > 1) {
|
||||
return Math.floor(interval) + " minutes";
|
||||
}
|
||||
return Math.floor(seconds) + "s";
|
||||
}
|
||||
var aDay = 24*60*60*1000;
|
||||
console.log(timeSince(new Date(Date.now()-aDay)));
|
||||
console.log(timeSince(new Date(Date.now()-aDay*2)));
|
||||
|
||||
|
||||
export default function SignIn(props) {
|
||||
|
||||
const [state, setState] = React.useState({count: 0, last_seen: 0});
|
||||
const [state, setState] = React.useState({ count: 0, last_seen: 0 });
|
||||
const device = props.device;
|
||||
|
||||
// Reloads component to update X time ago
|
||||
|
@ -74,20 +62,23 @@ export default function SignIn(props) {
|
|||
|
||||
useEffect(() => {
|
||||
setInterval(() => {
|
||||
setState({...state, count: state.count+1})
|
||||
}, 5000)});
|
||||
setState({ ...state, count: state.count + 1 })
|
||||
}, 60 * 1000)
|
||||
});
|
||||
|
||||
|
||||
const deviceLastSeen = timeSince(new Date(device.last_seen));
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ButtonBase style={{ padding: '10px'}}>
|
||||
<Grid container spacing={3}>
|
||||
<ButtonBase style={{ padding: '10px' }}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={4}>
|
||||
<img src="/c3.webp" style={{ width: "100%" }} />
|
||||
</Grid>
|
||||
<Grid item xs={8}>
|
||||
<Grid item xs={8} style={{ textAlign: 'left' }}>
|
||||
{/* <TextField
|
||||
defaultValue={"SuperSkoda"}
|
||||
size="small"
|
||||
|
@ -109,25 +100,27 @@ export default function SignIn(props) {
|
|||
|
||||
|
||||
/>*/}
|
||||
<Typography variant="body2" gutterBottom>{device.dongle_id}</Typography>
|
||||
<Typography variant="body2" align={"left"} gutterBottom>Dongle: {device.dongle_id}</Typography>
|
||||
|
||||
<Typography variant="body2" gutterBottom>Last Seen: {timeSince(new Date(device.last_seen))} ago</Typography>
|
||||
|
||||
|
||||
|
||||
<div>
|
||||
|
||||
{device.online ?
|
||||
<Chip style={{ background: '#004d40', ...stylezz }} label="Online" size="small" variant="outlined" /> :
|
||||
<Chip style={{ background: '#b71c1c', ...stylezz }} label="Offline" size="small" variant="outlined" />
|
||||
{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" />
|
||||
}
|
||||
|
||||
|
||||
<Chip style={{ background: '#dcedc8', ...stylezz }} label="Uploads Enabled" size="small" variant="outlined" />
|
||||
|
||||
|
||||
<Chip style={{ background: '#004d40', ...stylezz }} label="Active" size="small" variant="outlined" />
|
||||
|
||||
</div>
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ButtonBase>
|
||||
<Divider />
|
||||
</div>
|
||||
<Divider />
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,242 @@
|
|||
import React, { useState, useContext, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { alpha } from '@mui/material/styles';
|
||||
import Box from '@mui/material/Box';
|
||||
import Table from '@mui/material/Table';
|
||||
import TableBody from '@mui/material/TableBody';
|
||||
import TableCell from '@mui/material/TableCell';
|
||||
import TableContainer from '@mui/material/TableContainer';
|
||||
import TableHead from '@mui/material/TableHead';
|
||||
import TablePagination from '@mui/material/TablePagination';
|
||||
import TableRow from '@mui/material/TableRow';
|
||||
import TableSortLabel from '@mui/material/TableSortLabel';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import Checkbox from '@mui/material/Checkbox';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import Tooltip from '@mui/material/Tooltip';
|
||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
import Switch from '@mui/material/Switch';
|
||||
import FilterListIcon from '@mui/icons-material/FilterList';
|
||||
import { visuallyHidden } from '@mui/utils';
|
||||
import { context as DeviceContext } from "./../../../context/devices"
|
||||
import * as deviceController from "./../../../controllers/devices"
|
||||
import { Skeleton, ToolTip } from '@mui/material';
|
||||
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
|
||||
import FavoriteIcon from '@mui/icons-material/Favorite';
|
||||
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
||||
|
||||
|
||||
const rows = [
|
||||
|
||||
];
|
||||
|
||||
const headCells = [
|
||||
{
|
||||
id: 'identifier',
|
||||
numeric: false,
|
||||
disablePadding: true,
|
||||
label: 'Identifier',
|
||||
},
|
||||
{
|
||||
id: 'car',
|
||||
numeric: false,
|
||||
disablePadding: false,
|
||||
label: 'Car',
|
||||
},
|
||||
{
|
||||
id: 'version',
|
||||
numeric: false,
|
||||
disablePadding: false,
|
||||
label: 'Version',
|
||||
},
|
||||
{
|
||||
id: 'filesize',
|
||||
numeric: false,
|
||||
disablePadding: false,
|
||||
label: 'Filesize',
|
||||
},
|
||||
{
|
||||
id: 'duration',
|
||||
numeric: false,
|
||||
disablePadding: false,
|
||||
label: 'Duration',
|
||||
},
|
||||
{
|
||||
id: 'distance_meters',
|
||||
numeric: false,
|
||||
disablePadding: false,
|
||||
label: 'Distance',
|
||||
},
|
||||
{
|
||||
id: 'upload_complete',
|
||||
numeric: false,
|
||||
disablePadding: false,
|
||||
label: 'Uploaded',
|
||||
},
|
||||
{
|
||||
id: 'is_processed',
|
||||
numeric: false,
|
||||
disablePadding: false,
|
||||
label: 'Processed',
|
||||
},
|
||||
{
|
||||
id: 'upload_date',
|
||||
numeric: false,
|
||||
disablePadding: false,
|
||||
label: 'Uploaded at',
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
function formatDate(timestampMs) {
|
||||
return new Date(timestampMs).toISOString().replace(/T/, ' ').replace(/\..+/, '');
|
||||
}
|
||||
|
||||
function formatDuration(durationSeconds) {
|
||||
durationSeconds = Math.round(durationSeconds);
|
||||
const secs = durationSeconds % 60;
|
||||
let mins = Math.floor(durationSeconds / 60);
|
||||
let hours = Math.floor(mins / 60);
|
||||
mins = mins % 60;
|
||||
const days = Math.floor(hours / 24);
|
||||
hours = hours % 24;
|
||||
|
||||
let response = '';
|
||||
if (days > 0) response += days + 'd ';
|
||||
if (hours > 0 || days > 0) response += hours + 'h ';
|
||||
if (hours > 0 || days > 0 || mins > 0) response += mins + 'm ';
|
||||
response += secs + 's';
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function loading() {
|
||||
return (
|
||||
<TableRow>
|
||||
<TableCell ><Skeleton animation="wave" /></TableCell>
|
||||
<TableCell ><Skeleton animation="wave" /></TableCell>
|
||||
<TableCell ><Skeleton animation="wave" /></TableCell>
|
||||
<TableCell ><Skeleton animation="wave" /></TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default function EnhancedTable(props) {
|
||||
console.log("HELLO", props.dongleId)
|
||||
const [state, dispatch] = useContext(DeviceContext)
|
||||
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
deviceController.getBootlogs('53331425').then((res) => {
|
||||
console.log("me result:", res)
|
||||
setTimeout(() => {
|
||||
dispatch({ type: "update_dongle_bootlogs", dongle_id: props.dongleId, bootlogs: res.data })
|
||||
}, 1)
|
||||
})
|
||||
|
||||
}, []);
|
||||
|
||||
console.log("drives", state.dongles[props.dongleId])
|
||||
console.log("drives", typeof state.dongles[props.dongleId])
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<Box sx={{ width: '100%' }}>
|
||||
<Paper sx={{ width: '100%', mb: 2 }}>
|
||||
<TableContainer>
|
||||
<Table
|
||||
sx={{ minWidth: 750 }}
|
||||
aria-labelledby="tableTitle"
|
||||
size={'small'}
|
||||
>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell >Date</TableCell>
|
||||
<TableCell >File</TableCell>
|
||||
<TableCell >File size</TableCell>
|
||||
<TableCell >Actions</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
|
||||
<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) => {
|
||||
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
hover
|
||||
>
|
||||
|
||||
|
||||
<TableCell >{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>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="Preserve">
|
||||
<IconButton size="small">
|
||||
<FavoriteBorderIcon fontSize="inherit"/>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="Delete">
|
||||
<IconButton size="small">
|
||||
<DeleteIcon fontSize="inherit"/>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
|
||||
}) :
|
||||
|
||||
[1, 1, 1, 1, 1].map((v) => (
|
||||
<TableRow
|
||||
>
|
||||
|
||||
<TableCell ><Skeleton animation="wave" /></TableCell>
|
||||
<TableCell ><Skeleton animation="wave" /></TableCell>
|
||||
<TableCell ><Skeleton animation="wave" /></TableCell>
|
||||
<TableCell ><Skeleton animation="wave" /></TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import React, {useState, useContext} from 'react';
|
||||
import React, { useState, useContext, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { alpha } from '@mui/material/styles';
|
||||
import Box from '@mui/material/Box';
|
||||
|
@ -14,54 +14,27 @@ import Toolbar from '@mui/material/Toolbar';
|
|||
import Typography from '@mui/material/Typography';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import Checkbox from '@mui/material/Checkbox';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import Tooltip from '@mui/material/Tooltip';
|
||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
import Switch from '@mui/material/Switch';
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import FilterListIcon from '@mui/icons-material/FilterList';
|
||||
import { visuallyHidden } from '@mui/utils';
|
||||
import { context as DeviceContext } from "./../../../context/devices"
|
||||
import * as deviceController from "./../../../controllers/devices"
|
||||
import { Skeleton, ToolTip } from '@mui/material';
|
||||
|
||||
import Tooltip from '@mui/material/Tooltip';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
|
||||
import FavoriteIcon from '@mui/icons-material/Favorite';
|
||||
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
||||
|
||||
function createData(identifier, car, version, file_size, duration, distance_meters, upload_complete, is_processed, upload_date) {
|
||||
return {
|
||||
identifier, car, version, file_size, duration, distance_meters, upload_complete, is_processed, upload_date
|
||||
};
|
||||
}
|
||||
|
||||
const rows = [
|
||||
createData(Math.random(), 'Skoda Rapid', '0.8.10', 1000, 500, 100009, "true", "false", "01-11-2021"),
|
||||
createData(Math.random(), 'Skoda Rapid', '0.8.10', 1000, 500, 100009, "true", "false", "01-11-2022"),
|
||||
|
||||
];
|
||||
|
||||
function descendingComparator(a, b, orderBy) {
|
||||
if (b[orderBy] < a[orderBy]) {
|
||||
return -1;
|
||||
}
|
||||
if (b[orderBy] > a[orderBy]) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function getComparator(order, orderBy) {
|
||||
return order === 'desc'
|
||||
? (a, b) => descendingComparator(a, b, orderBy)
|
||||
: (a, b) => -descendingComparator(a, b, orderBy);
|
||||
}
|
||||
|
||||
// This method is created for cross-browser compatibility, if you don't
|
||||
// need to support IE11, you can use Array.prototype.sort() directly
|
||||
function stableSort(array, comparator) {
|
||||
const stabilizedThis = array.map((el, index) => [el, index]);
|
||||
stabilizedThis.sort((a, b) => {
|
||||
const order = comparator(a[0], b[0]);
|
||||
if (order !== 0) {
|
||||
return order;
|
||||
}
|
||||
return a[1] - b[1];
|
||||
});
|
||||
return stabilizedThis.map((el) => el[0]);
|
||||
}
|
||||
const headCells = [
|
||||
{
|
||||
id: 'identifier',
|
||||
|
@ -117,263 +90,121 @@ const headCells = [
|
|||
disablePadding: false,
|
||||
label: 'Uploaded at',
|
||||
},
|
||||
|
||||
|
||||
];
|
||||
|
||||
function EnhancedTableHead(props) {
|
||||
const { onSelectAllClick, order, orderBy, numSelected, rowCount, onRequestSort } =
|
||||
props;
|
||||
const createSortHandler = (property) => (event) => {
|
||||
onRequestSort(event, property);
|
||||
};
|
||||
|
||||
return (
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
color="primary"
|
||||
indeterminate={numSelected > 0 && numSelected < rowCount}
|
||||
checked={rowCount > 0 && numSelected === rowCount}
|
||||
onChange={onSelectAllClick}
|
||||
inputProps={{
|
||||
'aria-label': 'select all desserts',
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
{headCells.map((headCell) => (
|
||||
<TableCell
|
||||
key={headCell.id}
|
||||
align={headCell.numeric ? 'right' : 'left'}
|
||||
padding={headCell.disablePadding ? 'none' : 'normal'}
|
||||
sortDirection={orderBy === headCell.id ? order : false}
|
||||
>
|
||||
<TableSortLabel
|
||||
active={orderBy === headCell.id}
|
||||
direction={orderBy === headCell.id ? order : 'asc'}
|
||||
onClick={createSortHandler(headCell.id)}
|
||||
>
|
||||
{headCell.label}
|
||||
{orderBy === headCell.id ? (
|
||||
<Box component="span" sx={visuallyHidden}>
|
||||
{order === 'desc' ? 'sorted descending' : 'sorted ascending'}
|
||||
</Box>
|
||||
) : null}
|
||||
</TableSortLabel>
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
);
|
||||
function formatDate(timestampMs) {
|
||||
return new Date(timestampMs).toISOString().replace(/T/, ' ').replace(/\..+/, '');
|
||||
}
|
||||
|
||||
EnhancedTableHead.propTypes = {
|
||||
numSelected: PropTypes.number.isRequired,
|
||||
onRequestSort: PropTypes.func.isRequired,
|
||||
onSelectAllClick: PropTypes.func.isRequired,
|
||||
order: PropTypes.oneOf(['asc', 'desc']).isRequired,
|
||||
orderBy: PropTypes.string.isRequired,
|
||||
rowCount: PropTypes.number.isRequired,
|
||||
};
|
||||
function formatDuration(durationSeconds) {
|
||||
durationSeconds = Math.round(durationSeconds);
|
||||
const secs = durationSeconds % 60;
|
||||
let mins = Math.floor(durationSeconds / 60);
|
||||
let hours = Math.floor(mins / 60);
|
||||
mins = mins % 60;
|
||||
const days = Math.floor(hours / 24);
|
||||
hours = hours % 24;
|
||||
|
||||
const EnhancedTableToolbar = (props) => {
|
||||
const { numSelected } = props;
|
||||
let response = '';
|
||||
if (days > 0) response += days + 'd ';
|
||||
if (hours > 0 || days > 0) response += hours + 'h ';
|
||||
if (hours > 0 || days > 0 || mins > 0) response += mins + 'm ';
|
||||
response += secs + 's';
|
||||
return response;
|
||||
}
|
||||
|
||||
function buildContent(row) {
|
||||
return (
|
||||
<Toolbar
|
||||
sx={{
|
||||
pl: { sm: 2 },
|
||||
pr: { xs: 1, sm: 1 },
|
||||
...(numSelected > 0 && {
|
||||
bgcolor: (theme) =>
|
||||
alpha(theme.palette.primary.main, theme.palette.action.activatedOpacity),
|
||||
}),
|
||||
}}
|
||||
<TableRow
|
||||
hover
|
||||
>
|
||||
{numSelected > 0 ? (
|
||||
<Typography
|
||||
sx={{ flex: '1 1 100%' }}
|
||||
color="inherit"
|
||||
variant="subtitle1"
|
||||
component="div"
|
||||
>
|
||||
{numSelected} selected
|
||||
</Typography>
|
||||
) : (
|
||||
<Typography
|
||||
sx={{ flex: '1 1 100%' }}
|
||||
variant="h6"
|
||||
id="tableTitle"
|
||||
component="div"
|
||||
>
|
||||
Drives
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
{numSelected > 0 ? (
|
||||
|
||||
<TableCell >{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>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="Preserve">
|
||||
<IconButton size="small">
|
||||
<FavoriteBorderIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="Delete">
|
||||
<IconButton>
|
||||
<DeleteIcon />
|
||||
<IconButton size="small">
|
||||
<DeleteIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip title="Filter list">
|
||||
<IconButton>
|
||||
<FilterListIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Toolbar>
|
||||
);
|
||||
};
|
||||
|
||||
EnhancedTableToolbar.propTypes = {
|
||||
numSelected: PropTypes.number.isRequired,
|
||||
};
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
}
|
||||
|
||||
export default function EnhancedTable() {
|
||||
const [order, setOrder] = React.useState('asc');
|
||||
const [orderBy, setOrderBy] = React.useState('calories');
|
||||
const [selected, setSelected] = React.useState([]);
|
||||
const [page, setPage] = React.useState(0);
|
||||
const [rowsPerPage, setRowsPerPage] = React.useState(5);
|
||||
function loading() {
|
||||
return (
|
||||
<TableRow>
|
||||
<TableCell ><Skeleton animation="wave" /></TableCell>
|
||||
<TableCell ><Skeleton animation="wave" /></TableCell>
|
||||
<TableCell ><Skeleton animation="wave" /></TableCell>
|
||||
<TableCell ><Skeleton animation="wave" /></TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
}
|
||||
|
||||
const handleRequestSort = (event, property) => {
|
||||
const isAsc = orderBy === property && order === 'asc';
|
||||
setOrder(isAsc ? 'desc' : 'asc');
|
||||
setOrderBy(property);
|
||||
};
|
||||
export default function EnhancedTable(props) {
|
||||
console.log("HELLO", props.dongleId)
|
||||
const [state, dispatch] = useContext(DeviceContext)
|
||||
|
||||
const handleSelectAllClick = (event) => {
|
||||
if (event.target.checked) {
|
||||
const newSelecteds = rows.map((n) => n.name);
|
||||
setSelected(newSelecteds);
|
||||
return;
|
||||
}
|
||||
setSelected([]);
|
||||
};
|
||||
useEffect(() => {
|
||||
deviceController.getCrashlogs('53331425').then((res) => {
|
||||
dispatch({ type: "update_dongle_bootlogs", dongle_id: props.dongleId, bootlogs: res.data })
|
||||
})
|
||||
|
||||
const handleClick = (event, name) => {
|
||||
const selectedIndex = selected.indexOf(name);
|
||||
let newSelected = [];
|
||||
}, []);
|
||||
|
||||
if (selectedIndex === -1) {
|
||||
newSelected = newSelected.concat(selected, name);
|
||||
} else if (selectedIndex === 0) {
|
||||
newSelected = newSelected.concat(selected.slice(1));
|
||||
} else if (selectedIndex === selected.length - 1) {
|
||||
newSelected = newSelected.concat(selected.slice(0, -1));
|
||||
} else if (selectedIndex > 0) {
|
||||
newSelected = newSelected.concat(
|
||||
selected.slice(0, selectedIndex),
|
||||
selected.slice(selectedIndex + 1),
|
||||
);
|
||||
}
|
||||
|
||||
setSelected(newSelected);
|
||||
};
|
||||
|
||||
const handleChangePage = (event, newPage) => {
|
||||
setPage(newPage);
|
||||
};
|
||||
|
||||
const handleChangeRowsPerPage = (event) => {
|
||||
setRowsPerPage(parseInt(event.target.value, 10));
|
||||
setPage(0);
|
||||
};
|
||||
|
||||
|
||||
const isSelected = (name) => selected.indexOf(name) !== -1;
|
||||
|
||||
// Avoid a layout jump when reaching the last page with empty rows.
|
||||
const emptyRows =
|
||||
page > 0 ? Math.max(0, (1 + page) * rowsPerPage - rows.length) : 0;
|
||||
console.log("drives", state.dongles[props.dongleId])
|
||||
console.log("drives", typeof state.dongles[props.dongleId])
|
||||
|
||||
return (
|
||||
<Box sx={{ width: '100%' }}>
|
||||
<Paper sx={{ width: '100%', mb: 2 }}>
|
||||
<EnhancedTableToolbar numSelected={selected.length} />
|
||||
<TableContainer>
|
||||
<Table
|
||||
sx={{ minWidth: 750 }}
|
||||
aria-labelledby="tableTitle"
|
||||
size={'small'}
|
||||
>
|
||||
<EnhancedTableHead
|
||||
numSelected={selected.length}
|
||||
order={order}
|
||||
orderBy={orderBy}
|
||||
onSelectAllClick={handleSelectAllClick}
|
||||
onRequestSort={handleRequestSort}
|
||||
rowCount={rows.length}
|
||||
/>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell >Date</TableCell>
|
||||
<TableCell >File</TableCell>
|
||||
<TableCell >File size</TableCell>
|
||||
<TableCell >Actions</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
|
||||
<TableBody>
|
||||
{/* if you don't need to support IE11, you can replace the `stableSort` call with:
|
||||
rows.slice().sort(getComparator(order, orderBy)) */}
|
||||
{stableSort(rows, getComparator(order, orderBy))
|
||||
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
|
||||
.map((row, index) => {
|
||||
const isItemSelected = isSelected(row.name);
|
||||
const labelId = `enhanced-table-checkbox-${index}`;
|
||||
|
||||
{state.dongles[props.dongleId].crash ?
|
||||
state.dongles[props.dongleId].crash.length > 0 ? state.dongles[props.dongleId].crash.map(buildContent) : <p> No drives </p>
|
||||
:
|
||||
[1, 1, 1, 1, 1].map(loading)
|
||||
}
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
hover
|
||||
onClick={(event) => handleClick(event, row.name)}
|
||||
role="checkbox"
|
||||
aria-checked={isItemSelected}
|
||||
tabIndex={-1}
|
||||
key={row.name}
|
||||
selected={isItemSelected}
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
color="primary"
|
||||
checked={isItemSelected}
|
||||
inputProps={{
|
||||
'aria-labelledby': labelId,
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell
|
||||
id={labelId}
|
||||
scope="row"
|
||||
>
|
||||
{row.identifier}
|
||||
</TableCell>
|
||||
|
||||
<TableCell >{row.car}</TableCell>
|
||||
<TableCell >{row.version}</TableCell>
|
||||
<TableCell >{row.file_size}</TableCell>
|
||||
<TableCell >{row.duration}</TableCell>
|
||||
<TableCell >{row.distance_meters}</TableCell>
|
||||
<TableCell >{row.upload_complete}</TableCell>
|
||||
<TableCell >{row.is_processed}</TableCell>
|
||||
<TableCell >{row.upload_date}</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
{emptyRows > 0 && (
|
||||
<TableRow
|
||||
style={{
|
||||
height: (33) * emptyRows,
|
||||
}}
|
||||
>
|
||||
<TableCell colSpan={6} />
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<TablePagination
|
||||
rowsPerPageOptions={[5, 10, 25, 10000]}
|
||||
component="div"
|
||||
count={rows.length}
|
||||
rowsPerPage={rowsPerPage}
|
||||
page={page}
|
||||
onPageChange={handleChangePage}
|
||||
onRowsPerPageChange={handleChangeRowsPerPage}
|
||||
/>
|
||||
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
|
|
|
@ -21,24 +21,70 @@ import Tab from '@mui/material/Tab';
|
|||
import DrivesLogTable from "./drives";
|
||||
import CrashLogsTable from "./crash";
|
||||
import BootLogsTable from "./boot";
|
||||
import { context as DeviceContext } from "./../../../context/devices"
|
||||
|
||||
import Tooltip from '@mui/material/Tooltip';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
|
||||
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
|
||||
|
||||
const theme = createTheme();
|
||||
function formatDate(timestampMs) {
|
||||
return new Date(timestampMs).toISOString().replace(/T/, ' ').replace(/\..+/, '');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export default function SignIn() {
|
||||
export default function SignIn(props) {
|
||||
|
||||
const [value, setValue] = React.useState(0);
|
||||
const [state, dispatch] = useContext(DeviceContext)
|
||||
|
||||
const handleChange = (event, newValue) => {
|
||||
setValue(newValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="wrapper">
|
||||
if (!state.dongles[props.dongleId]) { return (<p>no</p>) }
|
||||
const dongle = state.dongles[props.dongleId];
|
||||
// <span style={{maxWidth: "100%", overflow: "scroll", whiteSpace:"nowrap" }}> {dongle.public_key.split(/\r?\n|\r/g).map((key) => (<p style={{margin: 0}}>{key}</p>))}</span>
|
||||
|
||||
sda
|
||||
return (
|
||||
<div className="wrapper" style={{ marginTop: '10px' }}>
|
||||
<Typography variant="body1">{state.dongles[props.dongleId].dongle_id}</Typography>
|
||||
|
||||
<Grid container>
|
||||
<Grid item xs={3}>
|
||||
|
||||
<div style={{ padding: '5px' }}>
|
||||
|
||||
<h3>Device {dongle.dongle_id} </h3>
|
||||
<b>Type:</b> {dongle.device_type}<br></br>
|
||||
<b>Serial:</b> {dongle.serial}<br></br>
|
||||
<b>IMEI:</b> {dongle.imei}<br></br>
|
||||
<b>Registered:</b> {formatDate(dongle.created)}<br></br>
|
||||
<b>Last Ping:</b> {formatDate(dongle.last_ping)}<br></br>
|
||||
<b>Public Key:</b> -----BEGIN PUBLIC KEY-----
|
||||
<Tooltip title="Copy public key">
|
||||
<IconButton onClick={() => console.log("click")}>
|
||||
<ContentCopyIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<br></br>
|
||||
|
||||
|
||||
<b>Stored Drives: </b> ` + drives.length + `<br></br>
|
||||
<b>Quota Storage: </b>{dongle.storage_used} MB / 200000 MB
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</Grid>
|
||||
|
||||
|
||||
</Grid>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, {useState, useContext} from 'react';
|
||||
import React, { useState, useContext, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { alpha } from '@mui/material/styles';
|
||||
import Box from '@mui/material/Box';
|
||||
|
@ -18,50 +18,22 @@ import IconButton from '@mui/material/IconButton';
|
|||
import Tooltip from '@mui/material/Tooltip';
|
||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
import Switch from '@mui/material/Switch';
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import FilterListIcon from '@mui/icons-material/FilterList';
|
||||
import { visuallyHidden } from '@mui/utils';
|
||||
import { context as DeviceContext } from "./../../../context/devices"
|
||||
import * as deviceController from "./../../../controllers/devices"
|
||||
import { Skeleton, ToolTip } from '@mui/material';
|
||||
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
|
||||
import FavoriteIcon from '@mui/icons-material/Favorite';
|
||||
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
||||
|
||||
function createData(identifier, car, version, file_size, duration, distance_meters, upload_complete, is_processed, upload_date) {
|
||||
return {
|
||||
identifier, car, version, file_size, duration, distance_meters, upload_complete, is_processed, upload_date
|
||||
};
|
||||
}
|
||||
|
||||
const rows = [
|
||||
createData(Math.random(), 'Skoda Rapid', '0.8.10', 1000, 500, 100009, "true", "false", "01-11-2021"),
|
||||
createData(Math.random(), 'Skoda Rapid', '0.8.10', 1000, 500, 100009, "true", "false", "01-11-2022"),
|
||||
|
||||
];
|
||||
|
||||
function descendingComparator(a, b, orderBy) {
|
||||
if (b[orderBy] < a[orderBy]) {
|
||||
return -1;
|
||||
}
|
||||
if (b[orderBy] > a[orderBy]) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function getComparator(order, orderBy) {
|
||||
return order === 'desc'
|
||||
? (a, b) => descendingComparator(a, b, orderBy)
|
||||
: (a, b) => -descendingComparator(a, b, orderBy);
|
||||
}
|
||||
|
||||
// This method is created for cross-browser compatibility, if you don't
|
||||
// need to support IE11, you can use Array.prototype.sort() directly
|
||||
function stableSort(array, comparator) {
|
||||
const stabilizedThis = array.map((el, index) => [el, index]);
|
||||
stabilizedThis.sort((a, b) => {
|
||||
const order = comparator(a[0], b[0]);
|
||||
if (order !== 0) {
|
||||
return order;
|
||||
}
|
||||
return a[1] - b[1];
|
||||
});
|
||||
return stabilizedThis.map((el) => el[0]);
|
||||
}
|
||||
const headCells = [
|
||||
{
|
||||
id: 'identifier',
|
||||
|
@ -117,263 +89,166 @@ const headCells = [
|
|||
disablePadding: false,
|
||||
label: 'Uploaded at',
|
||||
},
|
||||
|
||||
|
||||
];
|
||||
|
||||
function EnhancedTableHead(props) {
|
||||
const { onSelectAllClick, order, orderBy, numSelected, rowCount, onRequestSort } =
|
||||
props;
|
||||
const createSortHandler = (property) => (event) => {
|
||||
onRequestSort(event, property);
|
||||
};
|
||||
|
||||
return (
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
color="primary"
|
||||
indeterminate={numSelected > 0 && numSelected < rowCount}
|
||||
checked={rowCount > 0 && numSelected === rowCount}
|
||||
onChange={onSelectAllClick}
|
||||
inputProps={{
|
||||
'aria-label': 'select all desserts',
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
{headCells.map((headCell) => (
|
||||
<TableCell
|
||||
key={headCell.id}
|
||||
align={headCell.numeric ? 'right' : 'left'}
|
||||
padding={headCell.disablePadding ? 'none' : 'normal'}
|
||||
sortDirection={orderBy === headCell.id ? order : false}
|
||||
>
|
||||
<TableSortLabel
|
||||
active={orderBy === headCell.id}
|
||||
direction={orderBy === headCell.id ? order : 'asc'}
|
||||
onClick={createSortHandler(headCell.id)}
|
||||
>
|
||||
{headCell.label}
|
||||
{orderBy === headCell.id ? (
|
||||
<Box component="span" sx={visuallyHidden}>
|
||||
{order === 'desc' ? 'sorted descending' : 'sorted ascending'}
|
||||
</Box>
|
||||
) : null}
|
||||
</TableSortLabel>
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
);
|
||||
function formatDate(timestampMs) {
|
||||
return new Date(timestampMs).toISOString().replace(/T/, ' ').replace(/\..+/, '');
|
||||
}
|
||||
|
||||
EnhancedTableHead.propTypes = {
|
||||
numSelected: PropTypes.number.isRequired,
|
||||
onRequestSort: PropTypes.func.isRequired,
|
||||
onSelectAllClick: PropTypes.func.isRequired,
|
||||
order: PropTypes.oneOf(['asc', 'desc']).isRequired,
|
||||
orderBy: PropTypes.string.isRequired,
|
||||
rowCount: PropTypes.number.isRequired,
|
||||
};
|
||||
function formatDuration(durationSeconds) {
|
||||
durationSeconds = Math.round(durationSeconds);
|
||||
const secs = durationSeconds % 60;
|
||||
let mins = Math.floor(durationSeconds / 60);
|
||||
let hours = Math.floor(mins / 60);
|
||||
mins = mins % 60;
|
||||
const days = Math.floor(hours / 24);
|
||||
hours = hours % 24;
|
||||
|
||||
const EnhancedTableToolbar = (props) => {
|
||||
const { numSelected } = props;
|
||||
|
||||
return (
|
||||
<Toolbar
|
||||
sx={{
|
||||
pl: { sm: 2 },
|
||||
pr: { xs: 1, sm: 1 },
|
||||
...(numSelected > 0 && {
|
||||
bgcolor: (theme) =>
|
||||
alpha(theme.palette.primary.main, theme.palette.action.activatedOpacity),
|
||||
}),
|
||||
}}
|
||||
>
|
||||
{numSelected > 0 ? (
|
||||
<Typography
|
||||
sx={{ flex: '1 1 100%' }}
|
||||
color="inherit"
|
||||
variant="subtitle1"
|
||||
component="div"
|
||||
>
|
||||
{numSelected} selected
|
||||
</Typography>
|
||||
) : (
|
||||
<Typography
|
||||
sx={{ flex: '1 1 100%' }}
|
||||
variant="h6"
|
||||
id="tableTitle"
|
||||
component="div"
|
||||
>
|
||||
Drives
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
{numSelected > 0 ? (
|
||||
<Tooltip title="Delete">
|
||||
<IconButton>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip title="Filter list">
|
||||
<IconButton>
|
||||
<FilterListIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Toolbar>
|
||||
);
|
||||
};
|
||||
|
||||
EnhancedTableToolbar.propTypes = {
|
||||
numSelected: PropTypes.number.isRequired,
|
||||
};
|
||||
|
||||
export default function EnhancedTable() {
|
||||
const [order, setOrder] = React.useState('asc');
|
||||
const [orderBy, setOrderBy] = React.useState('calories');
|
||||
const [selected, setSelected] = React.useState([]);
|
||||
const [page, setPage] = React.useState(0);
|
||||
const [rowsPerPage, setRowsPerPage] = React.useState(5);
|
||||
|
||||
const handleRequestSort = (event, property) => {
|
||||
const isAsc = orderBy === property && order === 'asc';
|
||||
setOrder(isAsc ? 'desc' : 'asc');
|
||||
setOrderBy(property);
|
||||
};
|
||||
|
||||
const handleSelectAllClick = (event) => {
|
||||
if (event.target.checked) {
|
||||
const newSelecteds = rows.map((n) => n.name);
|
||||
setSelected(newSelecteds);
|
||||
return;
|
||||
}
|
||||
setSelected([]);
|
||||
};
|
||||
|
||||
const handleClick = (event, name) => {
|
||||
const selectedIndex = selected.indexOf(name);
|
||||
let newSelected = [];
|
||||
|
||||
if (selectedIndex === -1) {
|
||||
newSelected = newSelected.concat(selected, name);
|
||||
} else if (selectedIndex === 0) {
|
||||
newSelected = newSelected.concat(selected.slice(1));
|
||||
} else if (selectedIndex === selected.length - 1) {
|
||||
newSelected = newSelected.concat(selected.slice(0, -1));
|
||||
} else if (selectedIndex > 0) {
|
||||
newSelected = newSelected.concat(
|
||||
selected.slice(0, selectedIndex),
|
||||
selected.slice(selectedIndex + 1),
|
||||
);
|
||||
}
|
||||
|
||||
setSelected(newSelected);
|
||||
};
|
||||
|
||||
const handleChangePage = (event, newPage) => {
|
||||
setPage(newPage);
|
||||
};
|
||||
|
||||
const handleChangeRowsPerPage = (event) => {
|
||||
setRowsPerPage(parseInt(event.target.value, 10));
|
||||
setPage(0);
|
||||
};
|
||||
let response = '';
|
||||
if (days > 0) response += days + 'd ';
|
||||
if (hours > 0 || days > 0) response += hours + 'h ';
|
||||
if (hours > 0 || days > 0 || mins > 0) response += mins + 'm ';
|
||||
response += secs + 's';
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
const isSelected = (name) => selected.indexOf(name) !== -1;
|
||||
export default function EnhancedTable(props) {
|
||||
console.log("HELLO", props.dongleId)
|
||||
const [state, dispatch] = useContext(DeviceContext)
|
||||
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
deviceController.getDrives('53331425').then((res) => {
|
||||
console.log("me result:", res)
|
||||
setTimeout(() => {
|
||||
dispatch({ type: "update_dongle_drive", dongle_id: props.dongleId, drives: res.data })
|
||||
}, 1)
|
||||
})
|
||||
|
||||
}, []);
|
||||
|
||||
console.log("drives", state.dongles[props.dongleId])
|
||||
console.log("drives", typeof state.dongles[props.dongleId])
|
||||
|
||||
|
||||
// Avoid a layout jump when reaching the last page with empty rows.
|
||||
const emptyRows =
|
||||
page > 0 ? Math.max(0, (1 + page) * rowsPerPage - rows.length) : 0;
|
||||
|
||||
return (
|
||||
<Box sx={{ width: '100%' }}>
|
||||
<Paper sx={{ width: '100%', mb: 2 }}>
|
||||
<EnhancedTableToolbar numSelected={selected.length} />
|
||||
<TableContainer>
|
||||
<Table
|
||||
sx={{ minWidth: 750 }}
|
||||
aria-labelledby="tableTitle"
|
||||
size={'small'}
|
||||
>
|
||||
<EnhancedTableHead
|
||||
numSelected={selected.length}
|
||||
order={order}
|
||||
orderBy={orderBy}
|
||||
onSelectAllClick={handleSelectAllClick}
|
||||
onRequestSort={handleRequestSort}
|
||||
rowCount={rows.length}
|
||||
/>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell >Identifier</TableCell>
|
||||
<TableCell >Car</TableCell>
|
||||
<TableCell >Version</TableCell>
|
||||
<TableCell >File size</TableCell>
|
||||
<TableCell >Duration</TableCell>
|
||||
<TableCell >Distance</TableCell>
|
||||
<TableCell >uploaded</TableCell>
|
||||
<TableCell >Processed</TableCell>
|
||||
<TableCell >Date</TableCell>
|
||||
<TableCell >Actions</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
|
||||
<TableBody>
|
||||
{/* if you don't need to support IE11, you can replace the `stableSort` call with:
|
||||
rows.slice().sort(getComparator(order, orderBy)) */}
|
||||
{stableSort(rows, getComparator(order, orderBy))
|
||||
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
|
||||
.map((row, index) => {
|
||||
const isItemSelected = isSelected(row.name);
|
||||
const labelId = `enhanced-table-checkbox-${index}`;
|
||||
{state.dongles[props.dongleId].drives ? state.dongles[props.dongleId].drives.map((row) => {
|
||||
let metadata;
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
hover
|
||||
onClick={(event) => handleClick(event, row.name)}
|
||||
role="checkbox"
|
||||
aria-checked={isItemSelected}
|
||||
tabIndex={-1}
|
||||
key={row.name}
|
||||
selected={isItemSelected}
|
||||
try {
|
||||
metadata = JSON.parse(row.metadata)
|
||||
} catch (err) { metadata = {} }
|
||||
return (
|
||||
<TableRow
|
||||
hover
|
||||
>
|
||||
|
||||
<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 >{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 >{formatDate(row.drive_date)}</TableCell>
|
||||
|
||||
|
||||
<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="Delete">
|
||||
<IconButton size="small">
|
||||
<DeleteIcon fontSize="inherit"/>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
|
||||
}) :
|
||||
|
||||
[1, 1, 1, 1, 1].map((v) => (
|
||||
<TableRow
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
<Skeleton animation="wave" />
|
||||
</TableCell>
|
||||
<TableCell
|
||||
scope="row"
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
color="primary"
|
||||
checked={isItemSelected}
|
||||
inputProps={{
|
||||
'aria-labelledby': labelId,
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell
|
||||
id={labelId}
|
||||
scope="row"
|
||||
>
|
||||
{row.identifier}
|
||||
</TableCell>
|
||||
<Skeleton animation="wave" />
|
||||
</TableCell>
|
||||
|
||||
<TableCell ><Skeleton animation="wave" /></TableCell>
|
||||
<TableCell ><Skeleton animation="wave" /></TableCell>
|
||||
<TableCell ><Skeleton animation="wave" /></TableCell>
|
||||
<TableCell ><Skeleton animation="wave" /></TableCell>
|
||||
<TableCell ><Skeleton animation="wave" /></TableCell>
|
||||
<TableCell ><Skeleton animation="wave" /></TableCell>
|
||||
<TableCell ><Skeleton animation="wave" /></TableCell>
|
||||
<TableCell ><Skeleton animation="wave" /></TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<TableCell >{row.car}</TableCell>
|
||||
<TableCell >{row.version}</TableCell>
|
||||
<TableCell >{row.file_size}</TableCell>
|
||||
<TableCell >{row.duration}</TableCell>
|
||||
<TableCell >{row.distance_meters}</TableCell>
|
||||
<TableCell >{row.upload_complete}</TableCell>
|
||||
<TableCell >{row.is_processed}</TableCell>
|
||||
<TableCell >{row.upload_date}</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
{emptyRows > 0 && (
|
||||
<TableRow
|
||||
style={{
|
||||
height: (33) * emptyRows,
|
||||
}}
|
||||
>
|
||||
<TableCell colSpan={6} />
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<TablePagination
|
||||
rowsPerPageOptions={[5, 10, 25, 10000]}
|
||||
component="div"
|
||||
count={rows.length}
|
||||
rowsPerPage={rowsPerPage}
|
||||
page={page}
|
||||
onPageChange={handleChangePage}
|
||||
onRowsPerPageChange={handleChangeRowsPerPage}
|
||||
/>
|
||||
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
|
|
|
@ -49,7 +49,7 @@ function TabPanel(props) {
|
|||
|
||||
|
||||
|
||||
export default function SignIn() {
|
||||
export default function SignIn(props) {
|
||||
|
||||
const [value, setValue] = React.useState(0);
|
||||
|
||||
|
@ -70,19 +70,19 @@ export default function SignIn() {
|
|||
</Tabs>
|
||||
</Box>
|
||||
<TabPanel value={value} index={0}>
|
||||
<DeviceInfo />
|
||||
<DeviceInfo dongleId={props.dongleId}/>
|
||||
</TabPanel>
|
||||
<TabPanel value={value} index={1}>
|
||||
<DrivesLogTable />
|
||||
<DrivesLogTable dongleId={props.dongleId}/>
|
||||
</TabPanel>
|
||||
<TabPanel value={value} index={2}>
|
||||
<CrashLogsTable />
|
||||
<CrashLogsTable dongleId={props.dongleId}/>
|
||||
</TabPanel>
|
||||
<TabPanel value={value} index={3}>
|
||||
<BootLogsTable />
|
||||
<BootLogsTable dongleId={props.dongleId}/>
|
||||
</TabPanel>
|
||||
<TabPanel value={value} index={4}>
|
||||
<Console />
|
||||
<Console dongleId={props.dongleId} />
|
||||
</TabPanel>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -24,6 +24,7 @@ import DeviceOverview from "./../device/overview"
|
|||
import { Scrollbars } from 'rc-scrollbars';
|
||||
import { context as DeviceContext } from "./../../context/devices"
|
||||
|
||||
|
||||
const theme = createTheme();
|
||||
|
||||
|
||||
|
@ -38,6 +39,8 @@ export default function SignIn() {
|
|||
};
|
||||
const [deviceState, deviceDispatch] = useContext(DeviceContext);
|
||||
|
||||
console.log("USER ADMIN STATE", deviceState)
|
||||
|
||||
|
||||
let ok = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
|
||||
return (
|
||||
|
@ -48,7 +51,7 @@ export default function SignIn() {
|
|||
<Paper style={{ minHeight: "100%", maxHeight: "100%", margin: "0" }}>
|
||||
<Scrollbars autoHeight={true} autoHeightMin="calc(100vh - 14px)" audoHeightMax="calc(100% - 14px)">
|
||||
<div style={{ padding: '5px' }}>
|
||||
{ deviceState ? Object.keys(deviceState).map(key => <DeviceOverview device={deviceState[key]}/>) : <p>no</p> }
|
||||
{ deviceState ? Object.keys(deviceState.dongles).map(key => <DeviceOverview device={deviceState.dongles[key]}/>) : <p>no</p> }
|
||||
|
||||
|
||||
</div>
|
||||
|
@ -62,7 +65,9 @@ export default function SignIn() {
|
|||
|
||||
</Grid>
|
||||
<Grid item xs={12} md={8} lg={9} sm={6} xl={10}>
|
||||
<DeviceData />
|
||||
{ deviceState.dongles['53331425'] ? <DeviceData device={deviceState.dongles['53331425']} /> : <p>no</p> }
|
||||
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
|
|
|
@ -1,43 +1,131 @@
|
|||
import React, { createContext, useReducer, useEffect, useRef } from "react";
|
||||
import Reducer from './reducer'
|
||||
import * as deviceController from "./../../controllers/devices"
|
||||
|
||||
function process(state, action) {
|
||||
if (action.type !== "ADD_DATA") { return state }
|
||||
|
||||
switch (action.data.command) {
|
||||
|
||||
case "dongle_status":
|
||||
console.log("dongle:", action.data.data.dongle_id)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case "get_drives": {
|
||||
|
||||
|
||||
const initialState = {
|
||||
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const initialState = {
|
||||
dongles: {}
|
||||
};
|
||||
|
||||
const Store = ({ children }) => {
|
||||
console.log("STORE HAS BEEN RERENDERED")
|
||||
const [state, dispatch] = useReducer(Reducer, initialState);
|
||||
|
||||
useEffect(() => {
|
||||
const ws = new WebSocket('ws://localhost:81');
|
||||
|
||||
const ws = new WebSocket('ws://localhost:81');
|
||||
|
||||
ws.onmessage = ({ data }) => {
|
||||
data = JSON.parse(data)
|
||||
console.log("Message")
|
||||
console.log("Message")
|
||||
if (data.id) {
|
||||
dispatch({ type: "ADD_DATA", id: data.id, data: data })
|
||||
}
|
||||
};
|
||||
|
||||
deviceController.getAllDevices().then((devices) => {
|
||||
console.log("store", devices)
|
||||
|
||||
|
||||
dispatch({ type: "fetch_all_dongles", data: devices })
|
||||
})
|
||||
|
||||
return () => {
|
||||
try {
|
||||
ws.close();
|
||||
} catch (e) {}
|
||||
try {
|
||||
ws.close();
|
||||
} catch (e) { }
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}, []);
|
||||
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
function process(state, action) {
|
||||
|
||||
|
||||
if (action.type !== "ADD_DATA") {return state}
|
||||
|
||||
if (action.data.command === "dongle_status") {
|
||||
console.log("dongle:", action.data.data.dongle_id)
|
||||
return {
|
||||
...state,
|
||||
[action.data.data.dongle_id]: {
|
||||
online: action.data.data.online,
|
||||
last_seen: action.data.data.time,
|
||||
dongle_id: action.data.data.dongle_id
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const Reducer = (state, action) => {
|
||||
console.log("input", state, action)
|
||||
switch (action.type) {
|
||||
case 'ADD_DATA':
|
||||
return process(state, action)
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default Reducer;
|
|
@ -1,20 +1,49 @@
|
|||
import { tableBodyClasses } from "@mui/material";
|
||||
import axios from "axios";
|
||||
import {context as DeviceContext} from "./../context/devices"
|
||||
|
||||
|
||||
|
||||
export async function updateDongleInfo(dongleId) {
|
||||
const req = await axios.get('https://localhost/realtime/dongle/c3a5d816/connected', {withCredentials: true});
|
||||
const responseData = req.data;
|
||||
|
||||
return responseData.data
|
||||
|
||||
export async function getDrives(dongleId) {
|
||||
const req = await axios.get(`http://localhost/retropilot/0/device/${dongleId}/drives/false`, {withCredentials: true});
|
||||
return req.data
|
||||
}
|
||||
|
||||
export async function athenaCommandControl(dongleId, command, params) {
|
||||
const req = await axios.get('https://localhost/realtime/dongle/c3a5d816/send/reboot', {withCredentials: true});
|
||||
const responseData = req.data;
|
||||
export async function getBootlogs(dongleId) {
|
||||
const req = await axios.get(`http://localhost/retropilot/0/device/${dongleId}/bootlogs`, {withCredentials: true});
|
||||
return req.data
|
||||
}
|
||||
|
||||
return responseData.data
|
||||
export async function getCrashlogs(dongleId) {
|
||||
const req = await axios.get(`http://localhost/retropilot/0/device/${dongleId}/crashlogs`, {withCredentials: true});
|
||||
return req.data
|
||||
}
|
||||
|
||||
}
|
||||
export async function getAllDevices() {
|
||||
const req = await axios.get(`http://localhost/retropilot/0/devices`, {withCredentials: true});
|
||||
const responseData = req.data
|
||||
|
||||
let dongles = {}
|
||||
|
||||
if (responseData.success === true) {
|
||||
|
||||
responseData.data.map((object)=>{
|
||||
|
||||
dongles = {
|
||||
...dongles,
|
||||
[object.dongle_id]: {
|
||||
...object,
|
||||
online: false,
|
||||
// Show when last connected to api instead Athena by default
|
||||
last_seen: object.last_ping,
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
return dongles;
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
Loading…
Reference in New Issue