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:
@@ -750,7 +750,7 @@ exports[`App Route /carupdate-deploy authenticated 1`] = `
|
||||
class="MuiGrid-root MuiGrid-item MuiGrid-grid-md-10"
|
||||
>
|
||||
<div
|
||||
class="MuiFormControl-root makeStyles-margin-1195 makeStyles-textField-1196"
|
||||
class="MuiFormControl-root makeStyles-margin-1195 makeStyles-fullWidth-1218"
|
||||
>
|
||||
<label
|
||||
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"
|
||||
>
|
||||
<div
|
||||
class="MuiGrid-root MuiGrid-item MuiGrid-grid-md-2"
|
||||
class="MuiGrid-root MuiGrid-item MuiGrid-grid-md-4"
|
||||
>
|
||||
<div
|
||||
class="makeStyles-labelInline-1459"
|
||||
@@ -4242,10 +4242,10 @@ exports[`App Route /package-deploy authenticated 1`] = `
|
||||
</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
|
||||
class="MuiFormControl-root makeStyles-margin-1478 makeStyles-textField-1479"
|
||||
class="MuiFormControl-root makeStyles-margin-1478 makeStyles-fullWidth-1501"
|
||||
>
|
||||
<label
|
||||
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated"
|
||||
@@ -4296,10 +4296,10 @@ exports[`App Route /package-deploy authenticated 1`] = `
|
||||
</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
|
||||
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=""
|
||||
tabindex="-1"
|
||||
type="submit"
|
||||
@@ -5595,7 +5595,7 @@ exports[`App Route /packages authenticated 1`] = `
|
||||
class="MuiGrid-root makeStyles-textCenterAlign-1389 MuiGrid-item MuiGrid-grid-md-4"
|
||||
>
|
||||
<div
|
||||
class="MuiFormControl-root makeStyles-margin-1368 makeStyles-textField-1369"
|
||||
class="MuiFormControl-root makeStyles-margin-1368 makeStyles-fullWidth-1391"
|
||||
>
|
||||
<label
|
||||
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated"
|
||||
@@ -7300,11 +7300,15 @@ exports[`App Route /vehicle-status authenticated 1`] = `
|
||||
data-testid="mocked-vehicleprovider"
|
||||
>
|
||||
<div
|
||||
data-testid="mocked-updatesprovider"
|
||||
class="makeStyles-paper-1005 makeStyles-tableSize-1056"
|
||||
>
|
||||
<h6
|
||||
class="MuiTypography-root MuiTypography-h6"
|
||||
>
|
||||
Car Updates
|
||||
</h6>
|
||||
<div
|
||||
class="makeStyles-paper-1005"
|
||||
style="height: 700px; width: 100%;"
|
||||
data-testid="mocked-updatesprovider"
|
||||
>
|
||||
<table
|
||||
class="MuiTable-root"
|
||||
@@ -7568,6 +7572,529 @@ exports[`App Route /vehicle-status authenticated 1`] = `
|
||||
</tfoot>
|
||||
</table>
|
||||
</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>
|
||||
</main>
|
||||
@@ -7942,7 +8469,7 @@ exports[`App Route /vehicles authenticated 1`] = `
|
||||
class="MuiGrid-root makeStyles-textCenterAlign-988 MuiGrid-item MuiGrid-grid-md-4"
|
||||
>
|
||||
<div
|
||||
class="MuiFormControl-root makeStyles-margin-967 makeStyles-textField-968"
|
||||
class="MuiFormControl-root makeStyles-margin-967 makeStyles-fullWidth-990"
|
||||
>
|
||||
<label
|
||||
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated"
|
||||
@@ -8038,6 +8565,11 @@ exports[`App Route /vehicles authenticated 1`] = `
|
||||
>
|
||||
Close window
|
||||
</option>
|
||||
<option
|
||||
value="ecu"
|
||||
>
|
||||
ECU Versions
|
||||
</option>
|
||||
<option
|
||||
value="log"
|
||||
>
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
TablePagination,
|
||||
TableRow,
|
||||
} from "@material-ui/core";
|
||||
import clsx from "clsx";
|
||||
|
||||
import {
|
||||
UpdatesProvider,
|
||||
@@ -105,7 +106,7 @@ const MainForm = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`${classes.paper} ${classes.tableSize}`}>
|
||||
<div className={clsx(classes.paper, classes.tableSize)}>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
|
||||
175
src/components/Cars/CarECUs/index.jsx
Normal file
175
src/components/Cars/CarECUs/index.jsx
Normal 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;
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
TablePagination,
|
||||
TableRow,
|
||||
} from "@material-ui/core";
|
||||
import clsx from "clsx";
|
||||
|
||||
import { useVehicleContext } from "../../Contexts/VehicleContext";
|
||||
import { useStatusContext } from "../../Contexts/StatusContext";
|
||||
@@ -114,7 +115,7 @@ const CarSelectionTable = (props) => {
|
||||
}, [pageIndex, pageSize, orderBy, order, search, token]);
|
||||
|
||||
return (
|
||||
<div className={`${classes.paper} ${classes.tableSize}`}>
|
||||
<div className={clsx(classes.paper, classes.tableSize)}>
|
||||
<Table>
|
||||
<TableHeaderSortable
|
||||
classes={classes}
|
||||
@@ -147,7 +148,11 @@ const CarSelectionTable = (props) => {
|
||||
{row.ecu_list && (
|
||||
<>
|
||||
<br />
|
||||
<ECUList list={row.ecu_list} search={searchTerm} />
|
||||
<ECUList
|
||||
list={row.ecu_list}
|
||||
search={searchTerm}
|
||||
searchedOnly={true}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</TableCell>
|
||||
|
||||
159
src/components/Cars/CarUpdates/index.jsx
Normal file
159
src/components/Cars/CarUpdates/index.jsx
Normal 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;
|
||||
@@ -75,7 +75,7 @@ const SendCommand = ({ vins }) => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={`${classes.form}`} style={{ marginTop: 20 }}>
|
||||
<div className={classes.form} style={{ marginTop: 20 }}>
|
||||
<FormControl
|
||||
className={classes.formControlInline}
|
||||
variant="outlined"
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react";
|
||||
import { Grid } from "@material-ui/core";
|
||||
import AddCircleIcon from "@material-ui/icons/AddCircle";
|
||||
import { Link } from "react-router-dom";
|
||||
import clsx from "clsx";
|
||||
|
||||
import { VehicleProvider } from "../../Contexts/VehicleContext";
|
||||
import { useUserContext } from "../../Contexts/UserContext";
|
||||
@@ -54,7 +55,7 @@ const MainForm = () => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={`${classes.paper} ${classes.tableSize}`}>
|
||||
<div className={clsx(classes.paper, classes.tableSize)}>
|
||||
<Grid container className={classes.root} spacing={2}>
|
||||
<Grid item md={4} className={classes.textJustifyAlign}>
|
||||
<Link to="/vehicle-add">
|
||||
|
||||
@@ -1,63 +1,38 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import { useParams } from "react-router";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableFooter,
|
||||
TablePagination,
|
||||
TableRow,
|
||||
} from "@material-ui/core";
|
||||
import clsx from "clsx";
|
||||
import { Button, Grid, Typography } from "@material-ui/core";
|
||||
|
||||
import CarECUs from "../CarECUs";
|
||||
import CarUpdates from "../CarUpdates";
|
||||
import {
|
||||
UpdatesProvider,
|
||||
useUpdatesContext,
|
||||
} from "../../Contexts/UpdatesContext";
|
||||
import { VehicleProvider } from "../../Contexts/VehicleContext";
|
||||
VehicleProvider,
|
||||
useVehicleContext,
|
||||
} from "../../Contexts/VehicleContext";
|
||||
import { useUserContext } from "../../Contexts/UserContext";
|
||||
import { useStatusContext } from "../../Contexts/StatusContext";
|
||||
import useStyles from "../../useStyles";
|
||||
import { LocalDateTimeString } from "../../../utils/dates";
|
||||
import TableHeaderSortable from "../../Table/HeaderSortable";
|
||||
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 { vin } = useParams();
|
||||
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, setTitle, setSitePath } = useStatusContext();
|
||||
const { setTitle, setSitePath, setMessage } = useStatusContext();
|
||||
const { busy, sendCommand } = useVehicleContext();
|
||||
const {
|
||||
token: {
|
||||
idToken: { jwtToken: token },
|
||||
},
|
||||
} = 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(() => {
|
||||
const title = `Vehicle ${vin} Details`;
|
||||
@@ -74,113 +49,39 @@ const MainForm = () => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [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 (
|
||||
<div className={classes.paper} style={{ height: 700, width: "100%" }}>
|
||||
<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>
|
||||
<div className={clsx(classes.paper, classes.tableSize)}>
|
||||
<Typography variant="h6">Car Updates</Typography>
|
||||
<CarUpdates vin={vin} token={token} />
|
||||
<Grid container className={classes.root} spacing={2}>
|
||||
<Grid item md={4} className={classes.textJustifyAlign}></Grid>
|
||||
<Grid item md={4} className={classes.textCenterAlign}>
|
||||
<Typography variant="h6" className={classes.labelInline}>
|
||||
Car ECUs
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item md={4} className={classes.textRightAlign}>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={busy}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
className={clsx(classes.formControl, classes.textField)}
|
||||
onClick={updateHandler}
|
||||
>
|
||||
{busy ? "Sending..." : "Update"}
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<CarECUs vin={vin} token={token} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const CarUpdates = () => (
|
||||
const CarStatus = () => (
|
||||
<VehicleProvider>
|
||||
<UpdatesProvider>
|
||||
<MainForm />
|
||||
</UpdatesProvider>
|
||||
</VehicleProvider>
|
||||
);
|
||||
|
||||
export default CarUpdates;
|
||||
export default CarStatus;
|
||||
|
||||
@@ -33,21 +33,19 @@ export const VehicleProvider = ({ children }) => {
|
||||
const [models, setModels] = useState([]);
|
||||
const [years, setYears] = useState([]);
|
||||
|
||||
const getVehicles = async (search, token) => {
|
||||
const addConnections = async (cars, token) => {
|
||||
try {
|
||||
setBusy(true);
|
||||
const result = await api.getVehicles(search, token);
|
||||
const vins = cars.map((car) => car.vin);
|
||||
const result = await api.getConnections(vins, token);
|
||||
|
||||
if (result.error) {
|
||||
setVehicles([]);
|
||||
throw new Error(`Get vehicles error. ${result.message}`);
|
||||
throw new Error(`Add connections error. ${result.message}`);
|
||||
}
|
||||
await addConnections(result.data, token);
|
||||
setVehicles(result.data);
|
||||
if (result.total) {
|
||||
setTotalVehicles(result.total);
|
||||
}
|
||||
} finally {
|
||||
setBusy(false);
|
||||
cars.forEach((car) => {
|
||||
car.connected = result[car.vin] || false;
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error(e.stack);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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) => {
|
||||
try {
|
||||
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) => {
|
||||
try {
|
||||
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 (
|
||||
<VehicleContext.Provider
|
||||
value={{
|
||||
busy,
|
||||
vehicles,
|
||||
totalVehicles,
|
||||
models,
|
||||
years,
|
||||
getVehicles,
|
||||
totalVehicles,
|
||||
addVehicle,
|
||||
getModels,
|
||||
getYears,
|
||||
sendCommand,
|
||||
getConnections,
|
||||
getECUs,
|
||||
getLocations,
|
||||
getModels,
|
||||
getState,
|
||||
getYears,
|
||||
getVehicles,
|
||||
sendCommand,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -17,19 +17,7 @@ export const useVehicleContext = () => ({
|
||||
totalVehicles,
|
||||
models,
|
||||
years,
|
||||
getVehicles: jest.fn(() => vehicles),
|
||||
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) => {
|
||||
const result = {};
|
||||
|
||||
@@ -39,9 +27,54 @@ export const useVehicleContext = () => ({
|
||||
|
||||
return result;
|
||||
}),
|
||||
getLocations: jest.fn().mockResolvedValue([
|
||||
{ "altitude": 5, "longitude": 10, "latitude": 15, "vin": "TESTVIN123" },
|
||||
])
|
||||
getECUs: jest.fn(() => {
|
||||
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) => {
|
||||
|
||||
@@ -2,16 +2,17 @@ import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Chip } from "@material-ui/core";
|
||||
|
||||
const ECUList = ({ list, delimiter, search }) => {
|
||||
const ECUList = ({ list, delimiter, search, searchedOnly }) => {
|
||||
if (!list) return null;
|
||||
if (!delimiter) delimiter = ",";
|
||||
|
||||
const items = list.split(delimiter);
|
||||
|
||||
return items.map((item, index) => {
|
||||
const match = search
|
||||
? item.toLowerCase().split(" ").indexOf(search.toLowerCase())
|
||||
: -1;
|
||||
|
||||
if (searchedOnly && match === -1) return null;
|
||||
return (
|
||||
<Chip
|
||||
key={index}
|
||||
@@ -29,6 +30,7 @@ ECUList.propTypes = {
|
||||
list: PropTypes.string,
|
||||
delimiter: PropTypes.string,
|
||||
search: PropTypes.string,
|
||||
searchedOnly: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default ECUList;
|
||||
|
||||
@@ -30,7 +30,7 @@ const SearchField = (props) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<FormControl className={clsx(classes.margin, classes.textField)}>
|
||||
<FormControl className={clsx(classes.margin, classes.fullWidth)}>
|
||||
<InputLabel htmlFor="search">Search</InputLabel>
|
||||
<Input
|
||||
id="search"
|
||||
|
||||
@@ -52,9 +52,7 @@ export default function MenuDrawer({ children }) {
|
||||
paper: classes.drawerPaper,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={`${classes.drawerHeader} ${classes.drawerHeaderLogo}`}
|
||||
>
|
||||
<div className={clsx(classes.drawerHeader, classes.drawerHeaderLogo)}>
|
||||
<img
|
||||
src={logo}
|
||||
alt="Fisker Admin Portal"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useParams, Redirect } from "react-router";
|
||||
import { Button, Grid, Typography } from "@material-ui/core";
|
||||
import clsx from "clsx";
|
||||
|
||||
import {
|
||||
ManifestsProvider,
|
||||
@@ -126,21 +127,21 @@ const MainForm = () => {
|
||||
<form className={classes.form} noValidate action="{onSubmit}">
|
||||
<Typography variant="body2">Created {createDate}.</Typography>
|
||||
<Grid container className={classes.root} spacing={2}>
|
||||
<Grid item md={2}>
|
||||
<Grid item md={4}>
|
||||
<div
|
||||
className={classes.labelInline}
|
||||
>{`${selected.length} Selected`}</div>
|
||||
</Grid>
|
||||
<Grid item md={8} className={classes.textCenterAlign}>
|
||||
<Grid item md={4} className={classes.textCenterAlign}>
|
||||
<SearchField classes={classes} onSearch={handleSearch} />
|
||||
</Grid>
|
||||
<Grid item md={2} className={classes.textRightAlign}>
|
||||
<Grid item md={4} className={classes.textRightAlign}>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={busy || selected.length === 0}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
className={classes.formControl}
|
||||
className={clsx(classes.formControl, classes.textField)}
|
||||
onClick={onSubmit}
|
||||
>
|
||||
{busy ? "Deploying..." : "Deploy"}
|
||||
|
||||
@@ -14,6 +14,7 @@ import AddCircleIcon from "@material-ui/icons/AddCircle";
|
||||
import SendIcon from "@material-ui/icons/Send";
|
||||
import VisibilityIcon from "@material-ui/icons/Visibility";
|
||||
import DeleteIcon from "@material-ui/icons/Delete";
|
||||
import clsx from "clsx";
|
||||
|
||||
import {
|
||||
useManifestsContext,
|
||||
@@ -184,7 +185,7 @@ const MainForm = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`${classes.paper} ${classes.tableSize}`}>
|
||||
<div className={clsx(classes.paper, classes.tableSize)}>
|
||||
<Grid container className={classes.root} spacing={2}>
|
||||
<Grid item md={4} className={classes.textJustifyAlign}>
|
||||
<Link to="/package-create" className={classes.labelInline}>
|
||||
@@ -213,7 +214,11 @@ const MainForm = () => {
|
||||
{row.ecu_list && (
|
||||
<>
|
||||
<br />
|
||||
<ECUList list={row.ecu_list} search={search} />
|
||||
<ECUList
|
||||
list={row.ecu_list}
|
||||
search={search}
|
||||
searchedOnly={true}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</TableCell>
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
TablePagination,
|
||||
TableRow,
|
||||
} from "@material-ui/core";
|
||||
import clsx from "clsx";
|
||||
|
||||
import {
|
||||
ManifestsProvider,
|
||||
@@ -119,7 +120,7 @@ const MainForm = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`${classes.paper} ${classes.tableSize}`}>
|
||||
<div className={clsx(classes.paper, classes.tableSize)}>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { Button } from "@material-ui/core";
|
||||
import clsx from "clsx";
|
||||
|
||||
import { useUserContext } from "../Contexts/UserContext";
|
||||
import useStyles from "../useStyles";
|
||||
|
||||
@@ -21,7 +23,7 @@ export default function SignInForm() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={`${classes.paper} ${classes.textJustifyAlign}`}>
|
||||
<div className={clsx(classes.paper, classes.textJustifyAlign)}>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
|
||||
@@ -9,27 +9,37 @@ const data = [
|
||||
{ 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 = {
|
||||
getVehicles: async (search, token) => { return { data }; },
|
||||
addVehicle: async (vehicle, token) => {
|
||||
data.push(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) => {
|
||||
const result = {};
|
||||
|
||||
@@ -39,9 +49,28 @@ const vehiclesAPI = {
|
||||
|
||||
return result;
|
||||
},
|
||||
getECUs: async (vin, token) => {
|
||||
return { data: ecusData, total: ecusData.length};
|
||||
},
|
||||
getModels: async (token) => {
|
||||
return {
|
||||
data: ["Ocean", "Pear"],
|
||||
};
|
||||
},
|
||||
getLocations: jest.fn().mockResolvedValue([
|
||||
{ "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;
|
||||
|
||||
@@ -51,7 +51,12 @@ const Commands = [{
|
||||
value: "close",
|
||||
label: "Close window",
|
||||
parameters: Windows,
|
||||
},{
|
||||
},
|
||||
{
|
||||
value: "ecu",
|
||||
label: "ECU Versions",
|
||||
},
|
||||
{
|
||||
value: "log",
|
||||
label: "Log level",
|
||||
parameters: [
|
||||
|
||||
@@ -10,8 +10,17 @@ const vehiclesAPI = {
|
||||
})
|
||||
.then(fetchRespHandler),
|
||||
|
||||
getVehicles: async (search, token) => {
|
||||
const u = addQueryParams(`${API_ENDPOINT}/vehicles`, search);
|
||||
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)
|
||||
},
|
||||
|
||||
getECUs: async (search, token) => {
|
||||
const u = addQueryParams(`${API_ENDPOINT}/vehicleecus`, search);
|
||||
return fetch(u, {
|
||||
method: "GET",
|
||||
headers: Object.assign({ "Content-Type": "application/json" }, getAuthHeaderOptions(token)),
|
||||
@@ -25,6 +34,27 @@ const vehiclesAPI = {
|
||||
})
|
||||
.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`, {
|
||||
method: "GET",
|
||||
headers: Object.assign({ "Content-Type": "application/json" }, getAuthHeaderOptions(token)),
|
||||
@@ -39,27 +69,6 @@ const vehiclesAPI = {
|
||||
}),
|
||||
})
|
||||
.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;
|
||||
|
||||
Reference in New Issue
Block a user