CEC-371 Update car ECUs display (#78)

* Clean up className styles
Update car status page to show update and ECUs

* Add update ecu version button
Show all ECUs on car status page
Only show car ecus for search
This commit is contained in:
John Wu
2021-08-18 09:14:13 -07:00
committed by GitHub
parent 3e66959521
commit d1815e2ff9
20 changed files with 1169 additions and 299 deletions

View File

@@ -750,7 +750,7 @@ exports[`App Route /carupdate-deploy authenticated 1`] = `
class="MuiGrid-root MuiGrid-item MuiGrid-grid-md-10" class="MuiGrid-root MuiGrid-item MuiGrid-grid-md-10"
> >
<div <div
class="MuiFormControl-root makeStyles-margin-1195 makeStyles-textField-1196" class="MuiFormControl-root makeStyles-margin-1195 makeStyles-fullWidth-1218"
> >
<label <label
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated" class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated"
@@ -4233,7 +4233,7 @@ exports[`App Route /package-deploy authenticated 1`] = `
class="MuiGrid-root makeStyles-root-1464 MuiGrid-container MuiGrid-spacing-xs-2" class="MuiGrid-root makeStyles-root-1464 MuiGrid-container MuiGrid-spacing-xs-2"
> >
<div <div
class="MuiGrid-root MuiGrid-item MuiGrid-grid-md-2" class="MuiGrid-root MuiGrid-item MuiGrid-grid-md-4"
> >
<div <div
class="makeStyles-labelInline-1459" class="makeStyles-labelInline-1459"
@@ -4242,10 +4242,10 @@ exports[`App Route /package-deploy authenticated 1`] = `
</div> </div>
</div> </div>
<div <div
class="MuiGrid-root makeStyles-textCenterAlign-1499 MuiGrid-item MuiGrid-grid-md-8" class="MuiGrid-root makeStyles-textCenterAlign-1499 MuiGrid-item MuiGrid-grid-md-4"
> >
<div <div
class="MuiFormControl-root makeStyles-margin-1478 makeStyles-textField-1479" class="MuiFormControl-root makeStyles-margin-1478 makeStyles-fullWidth-1501"
> >
<label <label
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated" class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated"
@@ -4296,10 +4296,10 @@ exports[`App Route /package-deploy authenticated 1`] = `
</div> </div>
</div> </div>
<div <div
class="MuiGrid-root makeStyles-textRightAlign-1500 MuiGrid-item MuiGrid-grid-md-2" class="MuiGrid-root makeStyles-textRightAlign-1500 MuiGrid-item MuiGrid-grid-md-4"
> >
<button <button
class="MuiButtonBase-root MuiButton-root MuiButton-contained makeStyles-formControl-1457 MuiButton-containedPrimary Mui-disabled Mui-disabled" class="MuiButtonBase-root MuiButton-root MuiButton-contained makeStyles-formControl-1457 makeStyles-textField-1479 MuiButton-containedPrimary Mui-disabled Mui-disabled"
disabled="" disabled=""
tabindex="-1" tabindex="-1"
type="submit" type="submit"
@@ -5595,7 +5595,7 @@ exports[`App Route /packages authenticated 1`] = `
class="MuiGrid-root makeStyles-textCenterAlign-1389 MuiGrid-item MuiGrid-grid-md-4" class="MuiGrid-root makeStyles-textCenterAlign-1389 MuiGrid-item MuiGrid-grid-md-4"
> >
<div <div
class="MuiFormControl-root makeStyles-margin-1368 makeStyles-textField-1369" class="MuiFormControl-root makeStyles-margin-1368 makeStyles-fullWidth-1391"
> >
<label <label
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated" class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated"
@@ -7300,11 +7300,15 @@ exports[`App Route /vehicle-status authenticated 1`] = `
data-testid="mocked-vehicleprovider" data-testid="mocked-vehicleprovider"
> >
<div <div
data-testid="mocked-updatesprovider" class="makeStyles-paper-1005 makeStyles-tableSize-1056"
> >
<h6
class="MuiTypography-root MuiTypography-h6"
>
Car Updates
</h6>
<div <div
class="makeStyles-paper-1005" data-testid="mocked-updatesprovider"
style="height: 700px; width: 100%;"
> >
<table <table
class="MuiTable-root" class="MuiTable-root"
@@ -7568,6 +7572,529 @@ exports[`App Route /vehicle-status authenticated 1`] = `
</tfoot> </tfoot>
</table> </table>
</div> </div>
<div
class="MuiGrid-root makeStyles-root-1016 MuiGrid-container MuiGrid-spacing-xs-2"
>
<div
class="MuiGrid-root makeStyles-textJustifyAlign-1050 MuiGrid-item MuiGrid-grid-md-4"
/>
<div
class="MuiGrid-root makeStyles-textCenterAlign-1051 MuiGrid-item MuiGrid-grid-md-4"
>
<h6
class="MuiTypography-root makeStyles-labelInline-1011 MuiTypography-h6"
>
Car ECUs
</h6>
</div>
<div
class="MuiGrid-root makeStyles-textRightAlign-1052 MuiGrid-item MuiGrid-grid-md-4"
>
<button
class="MuiButtonBase-root MuiButton-root MuiButton-contained makeStyles-formControl-1009 makeStyles-textField-1031 MuiButton-containedPrimary"
tabindex="0"
type="submit"
>
<span
class="MuiButton-label"
>
Update
</span>
<span
class="MuiTouchRipple-root"
/>
</button>
</div>
</div>
<div
class="makeStyles-paper-1005 makeStyles-tableSize-1056"
>
<table
class="MuiTable-root"
>
<thead
class="MuiTableHead-root"
>
<tr
class="MuiTableRow-root MuiTableRow-head"
>
<th
aria-sort="descending"
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
scope="col"
>
<span
aria-disabled="false"
class="MuiButtonBase-root MuiTableSortLabel-root MuiTableSortLabel-active"
role="button"
tabindex="0"
>
ECU
<span
class="makeStyles-hiddenSortSpan-1029"
>
sorted descending
</span>
<svg
aria-hidden="true"
class="MuiSvgIcon-root MuiTableSortLabel-icon MuiTableSortLabel-iconDirectionDesc"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"
/>
</svg>
</span>
</th>
<th
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
scope="col"
>
<span
aria-disabled="false"
class="MuiButtonBase-root MuiTableSortLabel-root"
role="button"
tabindex="0"
>
SW Version
<svg
aria-hidden="true"
class="MuiSvgIcon-root MuiTableSortLabel-icon MuiTableSortLabel-iconDirectionAsc"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"
/>
</svg>
</span>
</th>
<th
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
scope="col"
>
<span
aria-disabled="false"
class="MuiButtonBase-root MuiTableSortLabel-root"
role="button"
tabindex="0"
>
BL Version
<svg
aria-hidden="true"
class="MuiSvgIcon-root MuiTableSortLabel-icon MuiTableSortLabel-iconDirectionAsc"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"
/>
</svg>
</span>
</th>
<th
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
scope="col"
>
<span
aria-disabled="false"
class="MuiButtonBase-root MuiTableSortLabel-root"
role="button"
tabindex="0"
>
HW Version
<svg
aria-hidden="true"
class="MuiSvgIcon-root MuiTableSortLabel-icon MuiTableSortLabel-iconDirectionAsc"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"
/>
</svg>
</span>
</th>
<th
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
scope="col"
>
<span
aria-disabled="false"
class="MuiButtonBase-root MuiTableSortLabel-root"
role="button"
tabindex="0"
>
Vendor
<svg
aria-hidden="true"
class="MuiSvgIcon-root MuiTableSortLabel-icon MuiTableSortLabel-iconDirectionAsc"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"
/>
</svg>
</span>
</th>
<th
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
scope="col"
>
<span
aria-disabled="false"
class="MuiButtonBase-root MuiTableSortLabel-root"
role="button"
tabindex="0"
>
Config
<svg
aria-hidden="true"
class="MuiSvgIcon-root MuiTableSortLabel-icon MuiTableSortLabel-iconDirectionAsc"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"
/>
</svg>
</span>
</th>
<th
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
scope="col"
>
<span
aria-disabled="false"
class="MuiButtonBase-root MuiTableSortLabel-root"
role="button"
tabindex="0"
>
Fingerprint
<svg
aria-hidden="true"
class="MuiSvgIcon-root MuiTableSortLabel-icon MuiTableSortLabel-iconDirectionAsc"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"
/>
</svg>
</span>
</th>
<th
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
scope="col"
>
<span
aria-disabled="false"
class="MuiButtonBase-root MuiTableSortLabel-root"
role="button"
tabindex="0"
>
Serial
<svg
aria-hidden="true"
class="MuiSvgIcon-root MuiTableSortLabel-icon MuiTableSortLabel-iconDirectionAsc"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"
/>
</svg>
</span>
</th>
<th
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
scope="col"
>
<span
aria-disabled="false"
class="MuiButtonBase-root MuiTableSortLabel-root"
role="button"
tabindex="0"
>
Created
<svg
aria-hidden="true"
class="MuiSvgIcon-root MuiTableSortLabel-icon MuiTableSortLabel-iconDirectionAsc"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"
/>
</svg>
</span>
</th>
<th
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
scope="col"
>
<span
aria-disabled="false"
class="MuiButtonBase-root MuiTableSortLabel-root"
role="button"
tabindex="0"
>
Updated
<svg
aria-hidden="true"
class="MuiSvgIcon-root MuiTableSortLabel-icon MuiTableSortLabel-iconDirectionAsc"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"
/>
</svg>
</span>
</th>
</tr>
</thead>
<tbody
class="MuiTableBody-root"
>
<tr
class="MuiTableRow-root"
>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
ECUA
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
SWVERSION
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
BLVERSION
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
HWVERSION
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
VENDOR
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
CONFIG
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
FINGERPRINT
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
SERIAL
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
7/14/2021 8:09:40 PM
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
7/14/2021 8:09:40 PM
</td>
</tr>
<tr
class="MuiTableRow-root"
>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
ECUB
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
SWVERSION
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
BLVERSION
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
HWVERSION
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
VENDOR
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
CONFIG
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
FINGERPRINT
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
SERIAL
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
7/14/2021 8:09:40 PM
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
7/14/2021 8:09:40 PM
</td>
</tr>
</tbody>
<tfoot
class="MuiTableFooter-root"
>
<tr
class="MuiTableRow-root MuiTableRow-footer"
>
<td
class="MuiTableCell-root MuiTableCell-footer MuiTablePagination-root"
colspan="10"
>
<div
class="MuiToolbar-root MuiToolbar-regular MuiTablePagination-toolbar MuiToolbar-gutters"
>
<div
class="MuiTablePagination-spacer"
/>
<p
class="MuiTypography-root MuiTablePagination-caption MuiTypography-body2 MuiTypography-colorInherit"
id="mui-00000"
>
Rows per page:
</p>
<div
class="MuiInputBase-root MuiTablePagination-input MuiTablePagination-selectRoot"
>
<select
aria-label="rows per page"
class="MuiSelect-root MuiSelect-select MuiTablePagination-select MuiInputBase-input"
id="mui-00000"
>
<option
class="MuiTablePagination-menuItem"
value="5"
>
5
</option>
<option
class="MuiTablePagination-menuItem"
value="10"
>
10
</option>
<option
class="MuiTablePagination-menuItem"
value="25"
>
25
</option>
<option
class="MuiTablePagination-menuItem"
value="100"
>
100
</option>
</select>
<svg
aria-hidden="true"
class="MuiSvgIcon-root MuiSelect-icon MuiTablePagination-selectIcon"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M7 10l5 5 5-5z"
/>
</svg>
</div>
<p
class="MuiTypography-root MuiTablePagination-caption MuiTypography-body2 MuiTypography-colorInherit"
>
1-2 of 2
</p>
<div
class="MuiTablePagination-actions"
>
<button
aria-label="Previous page"
class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorInherit Mui-disabled Mui-disabled"
disabled=""
tabindex="-1"
title="Previous page"
type="button"
>
<span
class="MuiIconButton-label"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z"
/>
</svg>
</span>
</button>
<button
aria-label="Next page"
class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorInherit Mui-disabled Mui-disabled"
disabled=""
tabindex="-1"
title="Next page"
type="button"
>
<span
class="MuiIconButton-label"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"
/>
</svg>
</span>
</button>
</div>
</div>
</td>
</tr>
</tfoot>
</table>
</div>
</div> </div>
</div> </div>
</main> </main>
@@ -7942,7 +8469,7 @@ exports[`App Route /vehicles authenticated 1`] = `
class="MuiGrid-root makeStyles-textCenterAlign-988 MuiGrid-item MuiGrid-grid-md-4" class="MuiGrid-root makeStyles-textCenterAlign-988 MuiGrid-item MuiGrid-grid-md-4"
> >
<div <div
class="MuiFormControl-root makeStyles-margin-967 makeStyles-textField-968" class="MuiFormControl-root makeStyles-margin-967 makeStyles-fullWidth-990"
> >
<label <label
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated" class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated"
@@ -8038,6 +8565,11 @@ exports[`App Route /vehicles authenticated 1`] = `
> >
Close window Close window
</option> </option>
<option
value="ecu"
>
ECU Versions
</option>
<option <option
value="log" value="log"
> >

View File

@@ -11,6 +11,7 @@ import {
TablePagination, TablePagination,
TableRow, TableRow,
} from "@material-ui/core"; } from "@material-ui/core";
import clsx from "clsx";
import { import {
UpdatesProvider, UpdatesProvider,
@@ -105,7 +106,7 @@ const MainForm = () => {
}; };
return ( return (
<div className={`${classes.paper} ${classes.tableSize}`}> <div className={clsx(classes.paper, classes.tableSize)}>
<Table> <Table>
<TableHead> <TableHead>
<TableRow> <TableRow>

View File

@@ -0,0 +1,175 @@
import React, { useEffect, useState } from "react";
import {
Table,
TableBody,
TableCell,
TableFooter,
TablePagination,
TableRow,
} from "@material-ui/core";
import clsx from "clsx";
import { LocalDateTimeString } from "../../../utils/dates";
import TableHeaderSortable from "../../Table/HeaderSortable";
import { useVehicleContext } from "../../Contexts/VehicleContext";
import { useStatusContext } from "../../Contexts/StatusContext";
import useStyles from "../../useStyles";
import { logger } from "../../../services/monitoring";
const tableColumns = [
{
id: "ecu",
label: "ECU",
},
{
id: "sw_version",
label: "SW Version",
},
{
id: "boot_loader_version",
label: "BL Version",
},
{
id: "hw_version",
label: "HW Version",
},
{
id: "vendor",
label: "Vendor",
},
{
id: "config",
label: "Config",
},
{
id: "fingerprint",
label: "Fingerprint",
},
{
id: "serial_number",
label: "Serial",
},
{
id: "created_at",
label: "Created",
},
{
id: "updated_at",
label: "Updated",
},
];
const CarECUs = ({ vin, token }) => {
const [ecus, setECUs] = useState([]);
const [total, setTotal] = useState(0);
const classes = useStyles();
const [pageSize, setPageSize] = useState(10);
const [pageIndex, setPageIndex] = useState(0);
const [orderBy, setOrderBy] = useState("ecu");
const [order, setOrder] = useState("desc");
const { getECUs } = useVehicleContext();
const { setMessage } = useStatusContext();
useEffect(() => {
(async () => {
try {
if (!vin || !token) return;
const result = await getECUs(
{
vin,
limit: pageSize,
offset: pageSize * pageIndex,
order: `${orderBy} ${order}`,
},
token
);
setECUs(result.data);
if (result.total > -1) setTotal(result.total);
} catch (e) {
console.log(e);
setMessage(e.message);
logger.warn(e.stack);
}
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [vin, token, pageIndex, pageSize, orderBy, order]);
const handleChangePageIndex = (event, newIndex) => {
setPageIndex(newIndex);
};
const handleChangePageSize = (event) => {
setPageSize(parseInt(event.target.value, 10));
setPageIndex(0);
};
const handleSort = (event, property) => {
try {
if (property === orderBy) {
if (order === "asc") {
setOrder("desc");
} else {
setOrder("asc");
}
} else {
setOrderBy(property);
setOrder("asc");
}
} catch (e) {
logger.warn(e.stack);
}
};
return (
<div className={clsx(classes.paper, classes.tableSize)}>
<Table>
<TableHeaderSortable
classes={classes}
orderBy={orderBy}
order={order}
columnData={tableColumns}
onSortRequest={handleSort}
/>
<TableBody>
{ecus.map((row) => (
<TableRow key={row.ecu}>
<TableCell align="center">{row.ecu}</TableCell>
<TableCell align="center">{row.sw_version}</TableCell>
<TableCell align="center">{row.boot_loader_version}</TableCell>
<TableCell align="center">{row.hw_version}</TableCell>
<TableCell align="center">{row.vendor}</TableCell>
<TableCell align="center">{row.config}</TableCell>
<TableCell align="center">{row.fingerprint}</TableCell>
<TableCell align="center">{row.serial_number}</TableCell>
<TableCell align="center">
{LocalDateTimeString(row.created)}
</TableCell>
<TableCell align="center">
{LocalDateTimeString(row.updated)}
</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={[5, 10, 25, 100]}
colSpan={10}
count={total}
rowsPerPage={pageSize}
page={pageIndex}
SelectProps={{
inputProps: { "aria-label": "rows per page" },
native: true,
}}
onPageChange={handleChangePageIndex}
onRowsPerPageChange={handleChangePageSize}
/>
</TableRow>
</TableFooter>
</Table>
</div>
);
};
export default CarECUs;

View File

@@ -10,6 +10,7 @@ import {
TablePagination, TablePagination,
TableRow, TableRow,
} from "@material-ui/core"; } from "@material-ui/core";
import clsx from "clsx";
import { useVehicleContext } from "../../Contexts/VehicleContext"; import { useVehicleContext } from "../../Contexts/VehicleContext";
import { useStatusContext } from "../../Contexts/StatusContext"; import { useStatusContext } from "../../Contexts/StatusContext";
@@ -114,7 +115,7 @@ const CarSelectionTable = (props) => {
}, [pageIndex, pageSize, orderBy, order, search, token]); }, [pageIndex, pageSize, orderBy, order, search, token]);
return ( return (
<div className={`${classes.paper} ${classes.tableSize}`}> <div className={clsx(classes.paper, classes.tableSize)}>
<Table> <Table>
<TableHeaderSortable <TableHeaderSortable
classes={classes} classes={classes}
@@ -147,7 +148,11 @@ const CarSelectionTable = (props) => {
{row.ecu_list && ( {row.ecu_list && (
<> <>
<br /> <br />
<ECUList list={row.ecu_list} search={searchTerm} /> <ECUList
list={row.ecu_list}
search={searchTerm}
searchedOnly={true}
/>
</> </>
)} )}
</TableCell> </TableCell>

View File

@@ -0,0 +1,159 @@
import React, { useEffect, useState } from "react";
import {
Table,
TableBody,
TableCell,
TableFooter,
TablePagination,
TableRow,
} from "@material-ui/core";
import { LocalDateTimeString } from "../../../utils/dates";
import TableHeaderSortable from "../../Table/HeaderSortable";
import {
UpdatesProvider,
useUpdatesContext,
} from "../../Contexts/UpdatesContext";
import { useStatusContext } from "../../Contexts/StatusContext";
import useStyles from "../../useStyles";
import { logger } from "../../../services/monitoring";
const tableColumns = [
{
id: "id",
label: "ID",
},
{
id: "update_package_id",
label: "Name",
},
{
id: "status",
label: "Status",
},
{
id: "created_at",
label: "Created",
},
{
id: "updated_at",
label: "Updated",
},
];
const MainForm = ({ vin, token }) => {
const classes = useStyles();
const [pageSize, setPageSize] = useState(10);
const [pageIndex, setPageIndex] = useState(0);
const [orderBy, setOrderBy] = useState("id");
const [order, setOrder] = useState("desc");
const { getCarUpdates, carUpdates, totalCarUpdates } = useUpdatesContext();
const { setMessage } = useStatusContext();
useEffect(() => {
(async () => {
try {
if (!vin || !token) return;
await getCarUpdates(
{
vin,
limit: pageSize,
offset: pageSize * pageIndex,
order: `${orderBy} ${order}`,
},
token
);
} catch (e) {
setMessage(e.message);
logger.warn(e.stack);
}
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [vin, token, pageIndex, pageSize, orderBy, order]);
const handleChangePageIndex = (event, newIndex) => {
setPageIndex(newIndex);
};
const handleChangePageSize = (event) => {
setPageSize(parseInt(event.target.value, 10));
setPageIndex(0);
};
const handleSort = (event, property) => {
try {
if (property === orderBy) {
if (order === "asc") {
setOrder("desc");
} else {
setOrder("asc");
}
} else {
setOrderBy(property);
setOrder("asc");
}
} catch (e) {
logger.warn(e.stack);
}
};
const updateName = (row) => {
if (row.updatepackage)
return `${row.updatepackage.package_name} ${row.updatepackage.version}`;
if (row.updatemanifest)
return `${row.updatemanifest.name} ${row.updatemanifest.version}`;
return "None";
};
return (
<Table>
<TableHeaderSortable
classes={classes}
orderBy={orderBy}
order={order}
columnData={tableColumns}
onSortRequest={handleSort}
/>
<TableBody>
{carUpdates.map((row) => (
<TableRow key={row.id}>
<TableCell align="center">{row.id}</TableCell>
<TableCell align="center">{updateName(row)}</TableCell>
<TableCell align="center">{row.status}</TableCell>
<TableCell align="center">
{LocalDateTimeString(row.created)}
</TableCell>
<TableCell align="center">
{LocalDateTimeString(row.updated)}
</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={[5, 10, 25, 100]}
colSpan={5}
count={totalCarUpdates}
rowsPerPage={pageSize}
page={pageIndex}
SelectProps={{
inputProps: { "aria-label": "rows per page" },
native: true,
}}
onPageChange={handleChangePageIndex}
onRowsPerPageChange={handleChangePageSize}
/>
</TableRow>
</TableFooter>
</Table>
);
};
const CarUpdates = (props) => (
<UpdatesProvider>
<MainForm {...props} />
</UpdatesProvider>
);
export default CarUpdates;

View File

@@ -75,7 +75,7 @@ const SendCommand = ({ vins }) => {
}, []); }, []);
return ( return (
<div className={`${classes.form}`} style={{ marginTop: 20 }}> <div className={classes.form} style={{ marginTop: 20 }}>
<FormControl <FormControl
className={classes.formControlInline} className={classes.formControlInline}
variant="outlined" variant="outlined"

View File

@@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react";
import { Grid } from "@material-ui/core"; import { Grid } from "@material-ui/core";
import AddCircleIcon from "@material-ui/icons/AddCircle"; import AddCircleIcon from "@material-ui/icons/AddCircle";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import clsx from "clsx";
import { VehicleProvider } from "../../Contexts/VehicleContext"; import { VehicleProvider } from "../../Contexts/VehicleContext";
import { useUserContext } from "../../Contexts/UserContext"; import { useUserContext } from "../../Contexts/UserContext";
@@ -54,7 +55,7 @@ const MainForm = () => {
}, []); }, []);
return ( return (
<div className={`${classes.paper} ${classes.tableSize}`}> <div className={clsx(classes.paper, classes.tableSize)}>
<Grid container className={classes.root} spacing={2}> <Grid container className={classes.root} spacing={2}>
<Grid item md={4} className={classes.textJustifyAlign}> <Grid item md={4} className={classes.textJustifyAlign}>
<Link to="/vehicle-add"> <Link to="/vehicle-add">

View File

@@ -1,63 +1,38 @@
import React, { useEffect, useState } from "react"; import React, { useEffect } from "react";
import { useParams } from "react-router"; import { useParams } from "react-router";
import { import clsx from "clsx";
Table, import { Button, Grid, Typography } from "@material-ui/core";
TableBody,
TableCell,
TableFooter,
TablePagination,
TableRow,
} from "@material-ui/core";
import CarECUs from "../CarECUs";
import CarUpdates from "../CarUpdates";
import { import {
UpdatesProvider, VehicleProvider,
useUpdatesContext, useVehicleContext,
} from "../../Contexts/UpdatesContext"; } from "../../Contexts/VehicleContext";
import { VehicleProvider } from "../../Contexts/VehicleContext";
import { useUserContext } from "../../Contexts/UserContext"; import { useUserContext } from "../../Contexts/UserContext";
import { useStatusContext } from "../../Contexts/StatusContext"; import { useStatusContext } from "../../Contexts/StatusContext";
import useStyles from "../../useStyles"; import useStyles from "../../useStyles";
import { LocalDateTimeString } from "../../../utils/dates";
import TableHeaderSortable from "../../Table/HeaderSortable";
import { logger } from "../../../services/monitoring"; import { logger } from "../../../services/monitoring";
const tableColumns = [
{
id: "id",
label: "ID",
},
{
id: "update_package_id",
label: "Name",
},
{
id: "status",
label: "Status",
},
{
id: "created_at",
label: "Created",
},
{
id: "updated_at",
label: "Updated",
},
];
const MainForm = () => { const MainForm = () => {
const { vin } = useParams(); const { vin } = useParams();
const classes = useStyles(); const classes = useStyles();
const [pageSize, setPageSize] = useState(10); const { setTitle, setSitePath, setMessage } = useStatusContext();
const [pageIndex, setPageIndex] = useState(0); const { busy, sendCommand } = useVehicleContext();
const [orderBy, setOrderBy] = useState("id");
const [order, setOrder] = useState("desc");
const { getCarUpdates, carUpdates, totalCarUpdates } = useUpdatesContext();
const { setMessage, setTitle, setSitePath } = useStatusContext();
const { const {
token: { token: {
idToken: { jwtToken: token }, idToken: { jwtToken: token },
}, },
} = useUserContext(); } = useUserContext();
const updateHandler = async (e) => {
try {
await sendCommand([vin], "ecu", "", token);
setMessage(`Sent command to ${vin}`);
} catch (e) {
setMessage(e.message);
logger.error(e.stack);
}
};
useEffect(() => { useEffect(() => {
const title = `Vehicle ${vin} Details`; const title = `Vehicle ${vin} Details`;
@@ -74,113 +49,39 @@ const MainForm = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [vin]); }, [vin]);
useEffect(() => {
(async () => {
try {
await getCarUpdates(
{
vin,
limit: pageSize,
offset: pageSize * pageIndex,
order: `${orderBy} ${order}`,
},
token
);
} catch (e) {
setMessage(e.message);
logger.warn(e.stack);
}
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pageIndex, pageSize, token, orderBy, order]);
const handleChangePageIndex = (event, newIndex) => {
setPageIndex(newIndex);
};
const handleChangePageSize = (event) => {
setPageSize(parseInt(event.target.value, 10));
setPageIndex(0);
};
const handleSort = (event, property) => {
try {
if (property === orderBy) {
if (order === "asc") {
setOrder("desc");
} else {
setOrder("asc");
}
} else {
setOrderBy(property);
setOrder("asc");
}
} catch (e) {
logger.warn(e.stack);
}
};
const updateName = (row) => {
if (row.updatepackage)
return `${row.updatepackage.package_name} ${row.updatepackage.version}`;
if (row.updatemanifest)
return `${row.updatemanifest.name} ${row.updatemanifest.version}`;
return "None";
};
return ( return (
<div className={classes.paper} style={{ height: 700, width: "100%" }}> <div className={clsx(classes.paper, classes.tableSize)}>
<Table> <Typography variant="h6">Car Updates</Typography>
<TableHeaderSortable <CarUpdates vin={vin} token={token} />
classes={classes} <Grid container className={classes.root} spacing={2}>
orderBy={orderBy} <Grid item md={4} className={classes.textJustifyAlign}></Grid>
order={order} <Grid item md={4} className={classes.textCenterAlign}>
columnData={tableColumns} <Typography variant="h6" className={classes.labelInline}>
onSortRequest={handleSort} Car ECUs
/> </Typography>
<TableBody> </Grid>
{carUpdates.map((row) => ( <Grid item md={4} className={classes.textRightAlign}>
<TableRow key={row.id}> <Button
<TableCell align="center">{row.id}</TableCell> type="submit"
<TableCell align="center">{updateName(row)}</TableCell> disabled={busy}
<TableCell align="center">{row.status}</TableCell> variant="contained"
<TableCell align="center"> color="primary"
{LocalDateTimeString(row.created)} className={clsx(classes.formControl, classes.textField)}
</TableCell> onClick={updateHandler}
<TableCell align="center"> >
{LocalDateTimeString(row.updated)} {busy ? "Sending..." : "Update"}
</TableCell> </Button>
</TableRow> </Grid>
))} </Grid>
</TableBody> <CarECUs vin={vin} token={token} />
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={[5, 10, 25, 100]}
colSpan={5}
count={totalCarUpdates}
rowsPerPage={pageSize}
page={pageIndex}
SelectProps={{
inputProps: { "aria-label": "rows per page" },
native: true,
}}
onPageChange={handleChangePageIndex}
onRowsPerPageChange={handleChangePageSize}
/>
</TableRow>
</TableFooter>
</Table>
</div> </div>
); );
}; };
const CarUpdates = () => ( const CarStatus = () => (
<VehicleProvider> <VehicleProvider>
<UpdatesProvider>
<MainForm /> <MainForm />
</UpdatesProvider>
</VehicleProvider> </VehicleProvider>
); );
export default CarUpdates; export default CarStatus;

View File

@@ -33,21 +33,19 @@ export const VehicleProvider = ({ children }) => {
const [models, setModels] = useState([]); const [models, setModels] = useState([]);
const [years, setYears] = useState([]); const [years, setYears] = useState([]);
const getVehicles = async (search, token) => { const addConnections = async (cars, token) => {
try { try {
setBusy(true); const vins = cars.map((car) => car.vin);
const result = await api.getVehicles(search, token); const result = await api.getConnections(vins, token);
if (result.error) { if (result.error) {
setVehicles([]); throw new Error(`Add connections error. ${result.message}`);
throw new Error(`Get vehicles error. ${result.message}`);
} }
await addConnections(result.data, token); cars.forEach((car) => {
setVehicles(result.data); car.connected = result[car.vin] || false;
if (result.total) { });
setTotalVehicles(result.total); } catch (e) {
} logger.error(e.stack);
} finally {
setBusy(false);
} }
}; };
@@ -63,6 +61,41 @@ export const VehicleProvider = ({ children }) => {
} }
}; };
const getConnections = async (vins, token) => {
try {
setBusy(true);
const result = await api.getConnections(vins, token);
if (result.error)
throw new Error(`Get connections error. ${result.message}`);
return result;
} finally {
setBusy(false);
}
};
const getECUs = async (search, token) => {
try {
setBusy(true);
const result = await api.getECUs(search, token);
if (result.error) throw new Error(`Get ECUs error. ${result.message}`);
return result;
} finally {
setBusy(false);
}
};
const getLocations = async (token) => {
try {
setBusy(true);
const result = await api.getLocations(token);
if (result.error)
throw new Error(`Get locations error. ${result.message}`);
return result;
} finally {
setBusy(false);
}
};
const getModels = async (token) => { const getModels = async (token) => {
try { try {
setBusy(true); setBusy(true);
@@ -74,6 +107,35 @@ export const VehicleProvider = ({ children }) => {
} }
}; };
const getState = async (token, vin) => {
try {
setBusy(true);
const result = await api.getState(token, vin);
if (result.error) throw new Error(`Get state error. ${result.message}`);
return result;
} finally {
setBusy(false);
}
};
const getVehicles = async (search, token) => {
try {
setBusy(true);
const result = await api.getVehicles(search, token);
if (result.error) {
setVehicles([]);
throw new Error(`Get vehicles error. ${result.message}`);
}
await addConnections(result.data, token);
setVehicles(result.data);
if (result.total) {
setTotalVehicles(result.total);
}
} finally {
setBusy(false);
}
};
const getYears = async (token) => { const getYears = async (token) => {
try { try {
setBusy(true); setBusy(true);
@@ -97,74 +159,23 @@ export const VehicleProvider = ({ children }) => {
} }
}; };
const addConnections = async (cars, token) => {
try {
const vins = cars.map((car) => car.vin);
const result = await api.getConnections(vins, token);
if (result.error) {
throw new Error(`Add connections error. ${result.message}`);
}
cars.forEach((car) => {
car.connected = result[car.vin] || false;
});
} catch (e) {
logger.error(e.stack);
}
};
const getConnections = async (vins, token) => {
try {
setBusy(true);
const result = await api.getConnections(vins, token);
if (result.error)
throw new Error(`Get connections error. ${result.message}`);
return result;
} finally {
setBusy(false);
}
};
const getLocations = async (token) => {
try {
setBusy(true);
const result = await api.getLocations(token);
if (result.error)
throw new Error(`Get locations error. ${result.message}`);
return result;
} finally {
setBusy(false);
}
};
const getState = async (token, vin) => {
try {
setBusy(true);
const result = await api.getState(token, vin);
if (result.error)
throw new Error(`Get state error. ${result.message}`);
return result;
} finally {
setBusy(false);
}
};
return ( return (
<VehicleContext.Provider <VehicleContext.Provider
value={{ value={{
busy, busy,
vehicles, vehicles,
totalVehicles,
models, models,
years, years,
getVehicles, totalVehicles,
addVehicle, addVehicle,
getModels,
getYears,
sendCommand,
getConnections, getConnections,
getECUs,
getLocations, getLocations,
getModels,
getState, getState,
getYears,
getVehicles,
sendCommand,
}} }}
> >
{children} {children}

View File

@@ -17,19 +17,7 @@ export const useVehicleContext = () => ({
totalVehicles, totalVehicles,
models, models,
years, years,
getVehicles: jest.fn(() => vehicles),
addVehicle: jest.fn(), addVehicle: jest.fn(),
getModels: jest.fn(() => {
models = ["Ocean", "PEAR"];
}),
getYears: jest.fn(() => {
years = [2023, 2024];
}),
sendCommand: jest.fn((vins, command, parameters, token) => ({
vins,
command,
parameters,
})),
getConnections: jest.fn((vins, token) => { getConnections: jest.fn((vins, token) => {
const result = {}; const result = {};
@@ -39,9 +27,54 @@ export const useVehicleContext = () => ({
return result; return result;
}), }),
getLocations: jest.fn().mockResolvedValue([ getECUs: jest.fn(() => {
{ "altitude": 5, "longitude": 10, "latitude": 15, "vin": "TESTVIN123" }, return {
]) data: [
{
boot_loader_version: "BLVERSION",
config: "CONFIG",
created: "2021-07-14T20:09:40.98187Z",
ecu: "ECUA",
fingerprint: "FINGERPRINT",
hw_version: "HWVERSION",
serial_number: "SERIAL",
sw_version: "SWVERSION",
updated: "2021-07-14T20:09:40.98187Z",
vendor: "VENDOR",
},
{
boot_loader_version: "BLVERSION",
config: "CONFIG",
created: "2021-07-14T20:09:40.98187Z",
ecu: "ECUB",
fingerprint: "FINGERPRINT",
hw_version: "HWVERSION",
serial_number: "SERIAL",
sw_version: "SWVERSION",
updated: "2021-07-14T20:09:40.98187Z",
vendor: "VENDOR",
},
],
total: 2,
};
}),
getModels: jest.fn(() => {
models = ["Ocean", "PEAR"];
}),
getLocations: jest
.fn()
.mockResolvedValue([
{ altitude: 5, longitude: 10, latitude: 15, vin: "TESTVIN123" },
]),
getVehicles: jest.fn(() => vehicles),
getYears: jest.fn(() => {
years = [2023, 2024];
}),
sendCommand: jest.fn((vins, command, parameters, token) => ({
vins,
command,
parameters,
})),
}); });
export const setBusy = (val) => { export const setBusy = (val) => {

View File

@@ -2,16 +2,17 @@ import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Chip } from "@material-ui/core"; import { Chip } from "@material-ui/core";
const ECUList = ({ list, delimiter, search }) => { const ECUList = ({ list, delimiter, search, searchedOnly }) => {
if (!list) return null; if (!list) return null;
if (!delimiter) delimiter = ","; if (!delimiter) delimiter = ",";
const items = list.split(delimiter); const items = list.split(delimiter);
return items.map((item, index) => { return items.map((item, index) => {
const match = search const match = search
? item.toLowerCase().split(" ").indexOf(search.toLowerCase()) ? item.toLowerCase().split(" ").indexOf(search.toLowerCase())
: -1; : -1;
if (searchedOnly && match === -1) return null;
return ( return (
<Chip <Chip
key={index} key={index}
@@ -29,6 +30,7 @@ ECUList.propTypes = {
list: PropTypes.string, list: PropTypes.string,
delimiter: PropTypes.string, delimiter: PropTypes.string,
search: PropTypes.string, search: PropTypes.string,
searchedOnly: PropTypes.bool,
}; };
export default ECUList; export default ECUList;

View File

@@ -30,7 +30,7 @@ const SearchField = (props) => {
}; };
return ( return (
<FormControl className={clsx(classes.margin, classes.textField)}> <FormControl className={clsx(classes.margin, classes.fullWidth)}>
<InputLabel htmlFor="search">Search</InputLabel> <InputLabel htmlFor="search">Search</InputLabel>
<Input <Input
id="search" id="search"

View File

@@ -52,9 +52,7 @@ export default function MenuDrawer({ children }) {
paper: classes.drawerPaper, paper: classes.drawerPaper,
}} }}
> >
<div <div className={clsx(classes.drawerHeader, classes.drawerHeaderLogo)}>
className={`${classes.drawerHeader} ${classes.drawerHeaderLogo}`}
>
<img <img
src={logo} src={logo}
alt="Fisker Admin Portal" alt="Fisker Admin Portal"

View File

@@ -1,6 +1,7 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useParams, Redirect } from "react-router"; import { useParams, Redirect } from "react-router";
import { Button, Grid, Typography } from "@material-ui/core"; import { Button, Grid, Typography } from "@material-ui/core";
import clsx from "clsx";
import { import {
ManifestsProvider, ManifestsProvider,
@@ -126,21 +127,21 @@ const MainForm = () => {
<form className={classes.form} noValidate action="{onSubmit}"> <form className={classes.form} noValidate action="{onSubmit}">
<Typography variant="body2">Created {createDate}.</Typography> <Typography variant="body2">Created {createDate}.</Typography>
<Grid container className={classes.root} spacing={2}> <Grid container className={classes.root} spacing={2}>
<Grid item md={2}> <Grid item md={4}>
<div <div
className={classes.labelInline} className={classes.labelInline}
>{`${selected.length} Selected`}</div> >{`${selected.length} Selected`}</div>
</Grid> </Grid>
<Grid item md={8} className={classes.textCenterAlign}> <Grid item md={4} className={classes.textCenterAlign}>
<SearchField classes={classes} onSearch={handleSearch} /> <SearchField classes={classes} onSearch={handleSearch} />
</Grid> </Grid>
<Grid item md={2} className={classes.textRightAlign}> <Grid item md={4} className={classes.textRightAlign}>
<Button <Button
type="submit" type="submit"
disabled={busy || selected.length === 0} disabled={busy || selected.length === 0}
variant="contained" variant="contained"
color="primary" color="primary"
className={classes.formControl} className={clsx(classes.formControl, classes.textField)}
onClick={onSubmit} onClick={onSubmit}
> >
{busy ? "Deploying..." : "Deploy"} {busy ? "Deploying..." : "Deploy"}

View File

@@ -14,6 +14,7 @@ import AddCircleIcon from "@material-ui/icons/AddCircle";
import SendIcon from "@material-ui/icons/Send"; import SendIcon from "@material-ui/icons/Send";
import VisibilityIcon from "@material-ui/icons/Visibility"; import VisibilityIcon from "@material-ui/icons/Visibility";
import DeleteIcon from "@material-ui/icons/Delete"; import DeleteIcon from "@material-ui/icons/Delete";
import clsx from "clsx";
import { import {
useManifestsContext, useManifestsContext,
@@ -184,7 +185,7 @@ const MainForm = () => {
}; };
return ( return (
<div className={`${classes.paper} ${classes.tableSize}`}> <div className={clsx(classes.paper, classes.tableSize)}>
<Grid container className={classes.root} spacing={2}> <Grid container className={classes.root} spacing={2}>
<Grid item md={4} className={classes.textJustifyAlign}> <Grid item md={4} className={classes.textJustifyAlign}>
<Link to="/package-create" className={classes.labelInline}> <Link to="/package-create" className={classes.labelInline}>
@@ -213,7 +214,11 @@ const MainForm = () => {
{row.ecu_list && ( {row.ecu_list && (
<> <>
<br /> <br />
<ECUList list={row.ecu_list} search={search} /> <ECUList
list={row.ecu_list}
search={search}
searchedOnly={true}
/>
</> </>
)} )}
</TableCell> </TableCell>

View File

@@ -11,6 +11,7 @@ import {
TablePagination, TablePagination,
TableRow, TableRow,
} from "@material-ui/core"; } from "@material-ui/core";
import clsx from "clsx";
import { import {
ManifestsProvider, ManifestsProvider,
@@ -119,7 +120,7 @@ const MainForm = () => {
}; };
return ( return (
<div className={`${classes.paper} ${classes.tableSize}`}> <div className={clsx(classes.paper, classes.tableSize)}>
<Table> <Table>
<TableHead> <TableHead>
<TableRow> <TableRow>

View File

@@ -1,5 +1,7 @@
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { Button } from "@material-ui/core"; import { Button } from "@material-ui/core";
import clsx from "clsx";
import { useUserContext } from "../Contexts/UserContext"; import { useUserContext } from "../Contexts/UserContext";
import useStyles from "../useStyles"; import useStyles from "../useStyles";
@@ -21,7 +23,7 @@ export default function SignInForm() {
}, []); }, []);
return ( return (
<div className={`${classes.paper} ${classes.textJustifyAlign}`}> <div className={clsx(classes.paper, classes.textJustifyAlign)}>
<Button <Button
type="submit" type="submit"
variant="contained" variant="contained"

View File

@@ -9,27 +9,37 @@ const data = [
{ vin: "1G11C5SL9FF153507", year: 2021, model: "Ocean", trim: "Basic" }, { vin: "1G11C5SL9FF153507", year: 2021, model: "Ocean", trim: "Basic" },
]; ];
const ecusData = [{
"boot_loader_version": "BLVERSION",
"config": "CONFIG",
"created": "2021-07-14T20:09:40.98187Z",
"ecu": "ECUA",
"fingerprint": "FINGERPRINT",
"hw_version": "HWVERSION",
"serial_number": "SERIAL",
"sw_version": "SWVERSION",
"updated": "2021-07-14T20:09:40.98187Z",
"vendor": "VENDOR",
},
{
"boot_loader_version": "BLVERSION",
"config": "CONFIG",
"created": "2021-07-14T20:09:40.98187Z",
"ecu": "ECUB",
"fingerprint": "FINGERPRINT",
"hw_version": "HWVERSION",
"serial_number": "SERIAL",
"sw_version": "SWVERSION",
"updated": "2021-07-14T20:09:40.98187Z",
"vendor": "VENDOR",
}
];
const vehiclesAPI = { const vehiclesAPI = {
getVehicles: async (search, token) => { return { data }; },
addVehicle: async (vehicle, token) => { addVehicle: async (vehicle, token) => {
data.push(vehicle); data.push(vehicle);
return vehicle; return vehicle;
}, },
getModels: async (token) => {
return {
data: ["Ocean", "Pear"],
};
},
getYears: async (token) => {
return {
data: [2021, 2022],
};
},
sendCommand: async (vin, command, parameters, token) => {
return {
vin, command, parameters
}
},
getConnections: async (vins, token) => { getConnections: async (vins, token) => {
const result = {}; const result = {};
@@ -39,9 +49,28 @@ const vehiclesAPI = {
return result; return result;
}, },
getECUs: async (vin, token) => {
return { data: ecusData, total: ecusData.length};
},
getModels: async (token) => {
return {
data: ["Ocean", "Pear"],
};
},
getLocations: jest.fn().mockResolvedValue([ getLocations: jest.fn().mockResolvedValue([
{ "altitude": 5, "longitude": 10, "latitude": 15, "vin": "TESTVIN123" }, { "altitude": 5, "longitude": 10, "latitude": 15, "vin": "TESTVIN123" },
]), ]),
getVehicles: async (search, token) => { return { data }; },
getYears: async (token) => {
return {
data: [2021, 2022],
};
},
sendCommand: async (vin, command, parameters, token) => {
return {
vin, command, parameters
}
},
}; };
export default vehiclesAPI; export default vehiclesAPI;

View File

@@ -51,7 +51,12 @@ const Commands = [{
value: "close", value: "close",
label: "Close window", label: "Close window",
parameters: Windows, parameters: Windows,
},{ },
{
value: "ecu",
label: "ECU Versions",
},
{
value: "log", value: "log",
label: "Log level", label: "Log level",
parameters: [ parameters: [

View File

@@ -10,8 +10,17 @@ const vehiclesAPI = {
}) })
.then(fetchRespHandler), .then(fetchRespHandler),
getVehicles: async (search, token) => { getConnections: async (vins, token) => {
const u = addQueryParams(`${API_ENDPOINT}/vehicles`, search); const u = `${API_ENDPOINT}/carsconnected?vins=${vins.join(",")}`;
return fetch(u, {
method: "GET",
headers: Object.assign({ "Content-Type": "application/json" }, getAuthHeaderOptions(token)),
})
.then(fetchRespHandler)
},
getECUs: async (search, token) => {
const u = addQueryParams(`${API_ENDPOINT}/vehicleecus`, search);
return fetch(u, { return fetch(u, {
method: "GET", method: "GET",
headers: Object.assign({ "Content-Type": "application/json" }, getAuthHeaderOptions(token)), headers: Object.assign({ "Content-Type": "application/json" }, getAuthHeaderOptions(token)),
@@ -25,6 +34,27 @@ const vehiclesAPI = {
}) })
.then(fetchRespHandler), .then(fetchRespHandler),
getLocations: async (token) => fetch(`${API_ENDPOINT}/carslocations`, {
method: "GET",
headers: Object.assign({ "Content-Type": "application/json" }, getAuthHeaderOptions(token)),
})
.then(fetchRespHandler),
getState: async (token, vin) => fetch(`${API_ENDPOINT}/carstate?vin=${vin}`, {
method: "GET",
headers: Object.assign({ "Content-Type": "application/json" }, getAuthHeaderOptions(token)),
})
.then(fetchRespHandler),
getVehicles: async (search, token) => {
const u = addQueryParams(`${API_ENDPOINT}/vehicles`, search);
return fetch(u, {
method: "GET",
headers: Object.assign({ "Content-Type": "application/json" }, getAuthHeaderOptions(token)),
})
.then(fetchRespHandler)
},
getYears: async (token) => fetch(`${API_ENDPOINT}/vehicleyears`, { getYears: async (token) => fetch(`${API_ENDPOINT}/vehicleyears`, {
method: "GET", method: "GET",
headers: Object.assign({ "Content-Type": "application/json" }, getAuthHeaderOptions(token)), headers: Object.assign({ "Content-Type": "application/json" }, getAuthHeaderOptions(token)),
@@ -39,27 +69,6 @@ const vehiclesAPI = {
}), }),
}) })
.then(fetchRespHandler), .then(fetchRespHandler),
getConnections: async (vins, token) => {
const u = `${API_ENDPOINT}/carsconnected?vins=${vins.join(",")}`
return fetch(u, {
method: "GET",
headers: Object.assign({ "Content-Type": "application/json" }, getAuthHeaderOptions(token)),
})
.then(fetchRespHandler)
},
getLocations: async (token) => fetch(`${API_ENDPOINT}/carslocations`, {
method: "GET",
headers: Object.assign({ "Content-Type": "application/json" }, getAuthHeaderOptions(token)),
})
.then(fetchRespHandler),
getState: async (token, vin) => fetch(`${API_ENDPOINT}/carstate?vin=${vin}`, {
method: "GET",
headers: Object.assign({ "Content-Type": "application/json" }, getAuthHeaderOptions(token)),
})
.then(fetchRespHandler),
}; };
export default vehiclesAPI; export default vehiclesAPI;