Merge branch 'main' into CEC-5542
This commit is contained in:
@@ -34,6 +34,7 @@ export const VehicleProvider = ({ children }) => {
|
|||||||
const [totalFlashpacks, setTotalFlashpacks] = useState(0);
|
const [totalFlashpacks, setTotalFlashpacks] = useState(0);
|
||||||
const [flashpackECUMappings, setFlashpackECUMappings] = useState([])
|
const [flashpackECUMappings, setFlashpackECUMappings] = useState([])
|
||||||
const [totalFlashpackECUMappings, setTotalFlashpackECUMappings] = useState(0)
|
const [totalFlashpackECUMappings, setTotalFlashpackECUMappings] = useState(0)
|
||||||
|
const [osVersions, setOSVersions] = useState([{ "value": "", "label": "None" }])
|
||||||
|
|
||||||
const addConnections = async (cars, token) => {
|
const addConnections = async (cars, token) => {
|
||||||
try {
|
try {
|
||||||
@@ -331,7 +332,7 @@ export const VehicleProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const addFlashpackVersion = async (model, trim, year, flashpack, carFlashpackVersions, token) => {
|
const addFlashpackVersion = async (model, trim, year, flashpack, osVersion, carFlashpackVersions, token) => {
|
||||||
try {
|
try {
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
|
|
||||||
@@ -340,6 +341,7 @@ export const VehicleProvider = ({ children }) => {
|
|||||||
"car_trim": trim,
|
"car_trim": trim,
|
||||||
"car_year": year,
|
"car_year": year,
|
||||||
"flashpack": flashpack,
|
"flashpack": flashpack,
|
||||||
|
"os_version": osVersion,
|
||||||
"ecu_versions": carFlashpackVersions,
|
"ecu_versions": carFlashpackVersions,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,6 +426,30 @@ export const VehicleProvider = ({ children }) => {
|
|||||||
} finally {
|
} finally {
|
||||||
setBusy(false);
|
setBusy(false);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getOSVersions = async (token) => {
|
||||||
|
try {
|
||||||
|
setBusy(true);
|
||||||
|
|
||||||
|
const result = await api.getOSVersions(token);
|
||||||
|
if (result.error) {
|
||||||
|
throw new Error(`Get OS versions error. ${result.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = [{ "value": "", "label": "None" }]
|
||||||
|
|
||||||
|
for (let i = 0; i < result.data.length; i++) {
|
||||||
|
data.push({
|
||||||
|
"value": result.data[i],
|
||||||
|
"label": result.data[i]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setOSVersions(data);
|
||||||
|
} finally {
|
||||||
|
setBusy(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -466,6 +492,8 @@ export const VehicleProvider = ({ children }) => {
|
|||||||
deleteFlashpackVersion,
|
deleteFlashpackVersion,
|
||||||
deleteFlashpackVersionECUMapping,
|
deleteFlashpackVersionECUMapping,
|
||||||
getCarFlashpackVersionInfo,
|
getCarFlashpackVersionInfo,
|
||||||
|
osVersions,
|
||||||
|
getOSVersions,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import {
|
import {
|
||||||
Checkbox,
|
Checkbox,
|
||||||
@@ -17,9 +16,9 @@ import { useStatusContext } from "../../Contexts/StatusContext";
|
|||||||
import { LocalDateTimeString } from "../../../utils/dates";
|
import { LocalDateTimeString } from "../../../utils/dates";
|
||||||
import TableHeaderSortable from "../../Table/HeaderSortable";
|
import TableHeaderSortable from "../../Table/HeaderSortable";
|
||||||
import { logger } from "../../../services/monitoring";
|
import { logger } from "../../../services/monitoring";
|
||||||
import ConnectedIcon from "../../Controls/ConnectedIcon";
|
|
||||||
import ECUList from "../../Controls/ECUList";
|
import ECUList from "../../Controls/ECUList";
|
||||||
import { useLocalStorage } from "../../useLocalStorage";
|
import { useLocalStorage } from "../../useLocalStorage";
|
||||||
|
import { VehicleTeaser } from "../../VehicleTeaser";
|
||||||
|
|
||||||
const tableColumns = [
|
const tableColumns = [
|
||||||
{
|
{
|
||||||
@@ -164,12 +163,11 @@ const CarSelectionTable = (props) => {
|
|||||||
</TableCell>
|
</TableCell>
|
||||||
)}
|
)}
|
||||||
<TableCell align="center">
|
<TableCell align="center">
|
||||||
<ConnectedIcon
|
<VehicleTeaser
|
||||||
connected={row.connected}
|
vin={row.vin}
|
||||||
connectedHMI={row.connectedHMI}
|
trex={row.connected}
|
||||||
style={{ marginRight: 3 }}
|
icc={row.connectedHMI}
|
||||||
/>
|
/>
|
||||||
<Link to={`/vehicle-status/${row.vin}`}>{row.vin}</Link>
|
|
||||||
{row.ecu_list && (
|
{row.ecu_list && (
|
||||||
<>
|
<>
|
||||||
<br />
|
<br />
|
||||||
|
|||||||
@@ -1,36 +1,78 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import CheckCircleIcon from "@material-ui/icons/CheckCircle";
|
import DoneAllIcon from "@material-ui/icons/DoneAll";
|
||||||
import CheckBoxIcon from "@material-ui/icons/CheckBox";
|
import ClearIcon from "@material-ui/icons/Clear";
|
||||||
|
import DoneIcon from "@material-ui/icons/Done";
|
||||||
import { Tooltip } from "@material-ui/core";
|
import { Tooltip } from "@material-ui/core";
|
||||||
|
|
||||||
const ConnectedIcon = (props) => {
|
const ConnectedIcon = (props) => {
|
||||||
if (props.connected || props.connectedHMI) {
|
let title = tooltip(props.connected, props.connectedHMI);
|
||||||
return (
|
|
||||||
<span style={props.style}>
|
|
||||||
{props.connected && (
|
|
||||||
<Tooltip title="TBOX">
|
|
||||||
<CheckCircleIcon
|
|
||||||
style={{ color: "Green", fontSize: 12, marginRight: 3 }}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
{props.connectedHMI && (
|
|
||||||
<Tooltip title="ICC">
|
|
||||||
<CheckBoxIcon
|
|
||||||
style={{ color: "Blue", fontSize: 12, marginRight: 3 }}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
const content = () => {
|
||||||
|
if (props.connected && props.connectedHMI) {
|
||||||
|
return (
|
||||||
|
<Tooltip title={title}>
|
||||||
|
<DoneAllIcon
|
||||||
|
fontSize="small"
|
||||||
|
style={{ color: "green" }}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.connected || props.connectedHMI) {
|
||||||
|
let color = "blue";
|
||||||
|
if (props.connected) {
|
||||||
|
color = "green"
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip title={title}>
|
||||||
|
<DoneIcon
|
||||||
|
fontSize="small"
|
||||||
|
style={{ color }}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip title={title}>
|
||||||
|
<ClearIcon
|
||||||
|
fontSize="small"
|
||||||
|
style={{ color: "red" }}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span style={props.style}>
|
||||||
|
{content()}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
ConnectedIcon.propTypes = {
|
ConnectedIcon.propTypes = {
|
||||||
connected: PropTypes.bool.isRequired,
|
connected: PropTypes.bool.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function tooltip(trex, icc) {
|
||||||
|
const status = [];
|
||||||
|
|
||||||
|
if (trex) {
|
||||||
|
status.push("TREX");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (icc) {
|
||||||
|
status.push("ICC");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!status.length) {
|
||||||
|
return "OFFLINE";
|
||||||
|
}
|
||||||
|
|
||||||
|
return status.join(" & ");
|
||||||
|
}
|
||||||
|
|
||||||
export default ConnectedIcon;
|
export default ConnectedIcon;
|
||||||
|
|||||||
@@ -91,11 +91,6 @@ exports[`FlashpackAdd Render 1`] = `
|
|||||||
class="MuiSelect-root MuiSelect-select MuiSelect-outlined MuiInputBase-input MuiOutlinedInput-input"
|
class="MuiSelect-root MuiSelect-select MuiSelect-outlined MuiInputBase-input MuiOutlinedInput-input"
|
||||||
required=""
|
required=""
|
||||||
>
|
>
|
||||||
<option
|
|
||||||
value="Base"
|
|
||||||
>
|
|
||||||
Base
|
|
||||||
</option>
|
|
||||||
<option
|
<option
|
||||||
value="Sport"
|
value="Sport"
|
||||||
>
|
>
|
||||||
@@ -244,6 +239,50 @@ exports[`FlashpackAdd Render 1`] = `
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="MuiFormControl-root MuiFormControl-marginNormal MuiFormControl-fullWidth"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="MuiFormLabel-root MuiInputLabel-root makeStyles-whiteBackground-0 MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined"
|
||||||
|
data-shrink="false"
|
||||||
|
>
|
||||||
|
OS Version
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-formControl"
|
||||||
|
required=""
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
aria-invalid="false"
|
||||||
|
class="MuiSelect-root MuiSelect-select MuiSelect-outlined MuiInputBase-input MuiOutlinedInput-input"
|
||||||
|
required=""
|
||||||
|
/>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="MuiSvgIcon-root MuiSelect-icon MuiSelect-iconOutlined"
|
||||||
|
focusable="false"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M7 10l5 5 5-5z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<fieldset
|
||||||
|
aria-hidden="true"
|
||||||
|
class="PrivateNotchedOutline-root-0 MuiOutlinedInput-notchedOutline"
|
||||||
|
style="padding-left: 8px;"
|
||||||
|
>
|
||||||
|
<legend
|
||||||
|
class="PrivateNotchedOutline-legend-0"
|
||||||
|
style="width: 0.01px;"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
|
||||||
|
</span>
|
||||||
|
</legend>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
class="container"
|
class="container"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -27,14 +27,17 @@ const MainForm = () => {
|
|||||||
const [redirect, setRedirect] = useState(null);
|
const [redirect, setRedirect] = useState(null);
|
||||||
const { setMessage, setTitle, setSitePath } = useStatusContext();
|
const { setMessage, setTitle, setSitePath } = useStatusContext();
|
||||||
const [carModel, setCarModel] = useLocalStorage("FLASHPACK_ADD_MODEL", "Ocean");
|
const [carModel, setCarModel] = useLocalStorage("FLASHPACK_ADD_MODEL", "Ocean");
|
||||||
const [carTrim, setCarTrim] = useLocalStorage("FLASHPACK_ADD_TRIM", "Base");
|
const [carTrim, setCarTrim] = useLocalStorage("FLASHPACK_ADD_TRIM", "Sport");
|
||||||
const [carYear, setCarYear] = useLocalStorage("FLASHPACK_ADD_YEAR", 2024);
|
const [carYear, setCarYear] = useLocalStorage("FLASHPACK_ADD_YEAR", 2024);
|
||||||
const [trims, setTrims] = useLocalStorage("FLASHPACK_ADD_TRIMS", modelsTrimsYears.oceanTrims);
|
const [trims, setTrims] = useLocalStorage("FLASHPACK_ADD_TRIMS", modelsTrimsYears.oceanTrims);
|
||||||
const [years, setYears] = useLocalStorage("FLASHPACK_ADD_YEARS", modelsTrimsYears.oceanYears);
|
const [years, setYears] = useLocalStorage("FLASHPACK_ADD_YEARS", modelsTrimsYears.oceanYears);
|
||||||
const [flashpack, setFlashpack] = useState();
|
const [flashpack, setFlashpack] = useState("");
|
||||||
|
const [osVersion, setOSVersion] = useState("");
|
||||||
const [mappingInputs, setMappingInputs] = useState([{ ecuName: "", ecuVersion: "" }]);
|
const [mappingInputs, setMappingInputs] = useState([{ ecuName: "", ecuVersion: "" }]);
|
||||||
const {
|
const {
|
||||||
addFlashpackVersion,
|
addFlashpackVersion,
|
||||||
|
getOSVersions,
|
||||||
|
osVersions,
|
||||||
busy,
|
busy,
|
||||||
} = useVehicleContext();
|
} = useVehicleContext();
|
||||||
|
|
||||||
@@ -56,6 +59,19 @@ const MainForm = () => {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
if (!token) return;
|
||||||
|
await getOSVersions(token);
|
||||||
|
} catch (e) {
|
||||||
|
setMessage(e.message);
|
||||||
|
logger.warn(e.stack);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [token]);
|
||||||
|
|
||||||
const onCarModelChange = (event) => {
|
const onCarModelChange = (event) => {
|
||||||
let newModel = event.target.value
|
let newModel = event.target.value
|
||||||
|
|
||||||
@@ -87,6 +103,10 @@ const MainForm = () => {
|
|||||||
setFlashpack(event.target.value);
|
setFlashpack(event.target.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onOSVersionChange = (event) => {
|
||||||
|
setOSVersion(event.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
const onSubmit = async (event) => {
|
const onSubmit = async (event) => {
|
||||||
try {
|
try {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -100,7 +120,7 @@ const MainForm = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await addFlashpackVersion(carModel, carTrim, parseInt(carYear), flashpack, carFlashpackVersions, token);
|
const result = await addFlashpackVersion(carModel, carTrim, parseInt(carYear), flashpack, osVersion, carFlashpackVersions, token);
|
||||||
if (!result || result.error) return;
|
if (!result || result.error) return;
|
||||||
|
|
||||||
setMessage(`Added ${carYear} ${carModel} ${carTrim} ${flashpack}`);
|
setMessage(`Added ${carYear} ${carModel} ${carTrim} ${flashpack}`);
|
||||||
@@ -157,6 +177,7 @@ const MainForm = () => {
|
|||||||
onChange={onFlashpackChange}
|
onChange={onFlashpackChange}
|
||||||
type="number"
|
type="number"
|
||||||
/>
|
/>
|
||||||
|
<DropDownList fullWidth required label="OS Version" data={osVersions} classes={classes} onChange={onOSVersionChange} value={osVersion} />
|
||||||
<div className="container">
|
<div className="container">
|
||||||
{mappingInputs.map((item, index) => (
|
{mappingInputs.map((item, index) => (
|
||||||
<div className="input_container" key={index}>
|
<div className="input_container" key={index}>
|
||||||
|
|||||||
@@ -79,11 +79,6 @@ exports[`Flashpack Render 1`] = `
|
|||||||
aria-invalid="false"
|
aria-invalid="false"
|
||||||
class="MuiSelect-root MuiSelect-select MuiSelect-outlined MuiInputBase-input MuiOutlinedInput-input"
|
class="MuiSelect-root MuiSelect-select MuiSelect-outlined MuiInputBase-input MuiOutlinedInput-input"
|
||||||
>
|
>
|
||||||
<option
|
|
||||||
value="Base"
|
|
||||||
>
|
|
||||||
Base
|
|
||||||
</option>
|
|
||||||
<option
|
<option
|
||||||
value="Sport"
|
value="Sport"
|
||||||
>
|
>
|
||||||
@@ -245,6 +240,29 @@ exports[`Flashpack Render 1`] = `
|
|||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
</th>
|
</th>
|
||||||
|
<th
|
||||||
|
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
|
||||||
|
scope="col"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-disabled="false"
|
||||||
|
class="MuiButtonBase-root MuiTableSortLabel-root"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
Part of OS 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
|
<th
|
||||||
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
|
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
|
||||||
scope="col"
|
scope="col"
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ const tableColumns = [
|
|||||||
id: "flashpack",
|
id: "flashpack",
|
||||||
label: "Flashpack Number",
|
label: "Flashpack Number",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "os_version",
|
||||||
|
label: "Part of OS Version",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "car_model",
|
id: "car_model",
|
||||||
label: "Model",
|
label: "Model",
|
||||||
@@ -58,7 +62,7 @@ const MainForm = () => {
|
|||||||
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||||
const [rowToDelete, setRowToDelete] = useState({});
|
const [rowToDelete, setRowToDelete] = useState({});
|
||||||
const [model, setModel] = useLocalStorage("FLASHPACKS_MODEL", "Ocean");
|
const [model, setModel] = useLocalStorage("FLASHPACKS_MODEL", "Ocean");
|
||||||
const [trim, setTrim] = useLocalStorage("FLASHPACKS_TRIM", "Base");
|
const [trim, setTrim] = useLocalStorage("FLASHPACKS_TRIM", "Sport");
|
||||||
const [year, setYear] = useLocalStorage("FLASHPACKS_YEAR", 2024);
|
const [year, setYear] = useLocalStorage("FLASHPACKS_YEAR", 2024);
|
||||||
const [trims, setTrims] = useLocalStorage("FLASHPACKS_TRIMS", modelsTrimsYears.oceanTrims);
|
const [trims, setTrims] = useLocalStorage("FLASHPACKS_TRIMS", modelsTrimsYears.oceanTrims);
|
||||||
const [years, setYears] = useLocalStorage("FLASHPACKS_YEARS", modelsTrimsYears.oceanYears);
|
const [years, setYears] = useLocalStorage("FLASHPACKS_YEARS", modelsTrimsYears.oceanYears);
|
||||||
@@ -210,6 +214,9 @@ const MainForm = () => {
|
|||||||
{row.flashpack}
|
{row.flashpack}
|
||||||
</Link>
|
</Link>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell align="center">
|
||||||
|
{row.os_version}
|
||||||
|
</TableCell>
|
||||||
<TableCell align="center">
|
<TableCell align="center">
|
||||||
{row.car_model}
|
{row.car_model}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
@@ -245,7 +252,7 @@ const MainForm = () => {
|
|||||||
) : (
|
) : (
|
||||||
<TablePagination
|
<TablePagination
|
||||||
rowsPerPageOptions={[5, 10, 25, 100]}
|
rowsPerPageOptions={[5, 10, 25, 100]}
|
||||||
colSpan={5}
|
colSpan={6}
|
||||||
count={totalFlashpacks}
|
count={totalFlashpacks}
|
||||||
rowsPerPage={pageSize}
|
rowsPerPage={pageSize}
|
||||||
page={pageIndex}
|
page={pageIndex}
|
||||||
|
|||||||
@@ -6,10 +6,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"oceanTrims": [
|
"oceanTrims": [
|
||||||
{
|
|
||||||
"value": "Base",
|
|
||||||
"label": "Base"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"value": "Sport",
|
"value": "Sport",
|
||||||
"label": "Sport"
|
"label": "Sport"
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ exports[`FleetVehiclesTable Render 1`] = `
|
|||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
VIN
|
Vehicle
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
class="MuiSvgIcon-root MuiTableSortLabel-icon MuiTableSortLabel-iconDirectionAsc"
|
class="MuiSvgIcon-root MuiTableSortLabel-icon MuiTableSortLabel-iconDirectionAsc"
|
||||||
|
|||||||
@@ -23,18 +23,19 @@ import {
|
|||||||
} from "../../../../Contexts/FleetContext";
|
} from "../../../../Contexts/FleetContext";
|
||||||
import { useStatusContext } from "../../../../Contexts/StatusContext";
|
import { useStatusContext } from "../../../../Contexts/StatusContext";
|
||||||
import { useUserContext } from "../../../../Contexts/UserContext";
|
import { useUserContext } from "../../../../Contexts/UserContext";
|
||||||
|
import { VehicleProvider } from "../../../../Contexts/VehicleContext";
|
||||||
import SearchField from "../../../../Controls/SearchField";
|
import SearchField from "../../../../Controls/SearchField";
|
||||||
import TableHeaderSortable from "../../../../Table/HeaderSortable";
|
import TableHeaderSortable from "../../../../Table/HeaderSortable";
|
||||||
import { useLocalStorage } from "../../../../useLocalStorage";
|
import { useLocalStorage } from "../../../../useLocalStorage";
|
||||||
import ConnectedIcon from "../../../../Controls/ConnectedIcon";
|
|
||||||
import BulkActions from "../../../../BulkActions";
|
import BulkActions from "../../../../BulkActions";
|
||||||
import Battery from "../../../../Battery";
|
import Battery from "../../../../Battery";
|
||||||
|
import { VehicleTeaserController as VehicleTeaser } from "../../../../VehicleTeaser";
|
||||||
import useStyles from "../../../../useStyles";
|
import useStyles from "../../../../useStyles";
|
||||||
|
|
||||||
const tableColumns = [
|
const tableColumns = [
|
||||||
{
|
{
|
||||||
id: "vin",
|
id: "vehicle",
|
||||||
label: "VIN",
|
label: "Vehicle",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "trex_version",
|
id: "trex_version",
|
||||||
@@ -213,54 +214,53 @@ const MainForm = ({ name }) => {
|
|||||||
selectCount={selected.length}
|
selectCount={selected.length}
|
||||||
rowCount={fleetVehicles.length}
|
rowCount={fleetVehicles.length}
|
||||||
/>
|
/>
|
||||||
<TableBody>
|
<VehicleProvider>
|
||||||
{fleetVehicles && fleetVehicles.map((car) => {
|
<TableBody>
|
||||||
const isSelected = selected.includes(car.vin);
|
{fleetVehicles && fleetVehicles.map((car) => {
|
||||||
return (car.vin && <TableRow key={"row" + car.vin}>
|
const isSelected = selected.includes(car.vin);
|
||||||
<TableCell padding="checkbox">
|
return (car.vin && <TableRow key={"row" + car.vin}>
|
||||||
<Checkbox
|
<TableCell padding="checkbox">
|
||||||
checked={isSelected}
|
<Checkbox
|
||||||
onChange={() => handleSelect(car.vin, !isSelected)}
|
checked={isSelected}
|
||||||
/>
|
onChange={() => handleSelect(car.vin, !isSelected)}
|
||||||
</TableCell>
|
|
||||||
<TableCell key={"cell" + car.vin} align="center">
|
|
||||||
{(car.connected || car.connectedHMI) &&
|
|
||||||
<ConnectedIcon
|
|
||||||
key={"icon" + car.vin}
|
|
||||||
connected={car.connected}
|
|
||||||
connectedHMI={car.connectedHMI}
|
|
||||||
style={{ marginRight: 3 }}
|
|
||||||
/>
|
/>
|
||||||
}
|
</TableCell>
|
||||||
<Link key={"link" + car.vin} to={`/vehicle-status/${car.vin}`}>{car.vin}</Link>
|
<TableCell key={"cell" + car.vin} align="center">
|
||||||
</TableCell>
|
<VehicleTeaser
|
||||||
<TableCell key={"cell2" + car.vin} align="center">{car.trex_version}</TableCell>
|
vin={car.vin}
|
||||||
<TableCell key={"cell3" + car.car_update_name}>
|
icc={car.connectedHMI}
|
||||||
<Tooltip key={"cell3tooltip"} title={`${car.car_update_id} / ${car.car_update_type}`}>
|
trex={car.connected}
|
||||||
<Link to={`/vehicle-status/${car.vin}/${car.car_update_id}`} className={classes.truncateCell}>
|
token={token}
|
||||||
{car.car_update_name}
|
/>
|
||||||
</Link>
|
</TableCell>
|
||||||
</Tooltip>
|
<TableCell key={"cell2" + car.vin} align="center">{car.trex_version}</TableCell>
|
||||||
</TableCell>
|
<TableCell key={"cell3" + car.car_update_name}>
|
||||||
<TableCell key={"cell4" + car.vin}>
|
<Tooltip key={"cell3tooltip"} title={`${car.car_update_id} / ${car.car_update_type}`}>
|
||||||
{car.car_update_status}
|
<Link to={`/vehicle-status/${car.vin}/${car.car_update_id}`} className={classes.truncateCell}>
|
||||||
{car.car_update_progress > -1 && (
|
{car.car_update_name}
|
||||||
<LinearProgress variant="determinate" value={car.car_update_progress} />
|
</Link>
|
||||||
)}
|
</Tooltip>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell key={"cell5" + car.vin} align="left">
|
<TableCell key={"cell4" + car.vin}>
|
||||||
<Battery percent={car.charge} charge={car.charge_type} />
|
{car.car_update_status}
|
||||||
</TableCell>
|
{car.car_update_progress > -1 && (
|
||||||
<TableCell key={"cell6" + car.vin}>
|
<LinearProgress variant="determinate" value={car.car_update_progress} />
|
||||||
{car.voltage > 0 && `${car.voltage}V`}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell key={"cell7" + car.vin}>
|
<TableCell key={"cell5" + car.vin} align="left">
|
||||||
{car.park ? "Yes" : "No"}
|
<Battery percent={car.charge} charge={car.charge_type} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
<TableCell key={"cell6" + car.vin}>
|
||||||
)
|
{car.voltage > 0 && `${car.voltage}V`}
|
||||||
})}
|
</TableCell>
|
||||||
</TableBody>
|
<TableCell key={"cell7" + car.vin}>
|
||||||
|
{car.park ? "Yes" : "No"}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</VehicleProvider>
|
||||||
<TableFooter>
|
<TableFooter>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ exports[`VehiclesTab Render 1`] = `
|
|||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
VIN
|
Vehicle
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
class="MuiSvgIcon-root MuiTableSortLabel-icon MuiTableSortLabel-iconDirectionAsc"
|
class="MuiSvgIcon-root MuiTableSortLabel-icon MuiTableSortLabel-iconDirectionAsc"
|
||||||
|
|||||||
72
src/components/VehicleTeaser/index.jsx
Normal file
72
src/components/VehicleTeaser/index.jsx
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import { useRef, useState, useEffect } from "react";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import Chip from '@mui/material/Chip';
|
||||||
|
import Stack from '@mui/material/Stack';
|
||||||
|
import ConnectedIcon from "../Controls/ConnectedIcon";
|
||||||
|
import { useVehicleContext } from "../Contexts/VehicleContext";
|
||||||
|
import useStyles from "../useStyles";
|
||||||
|
import { useIntersectObserver } from "../../hooks";
|
||||||
|
|
||||||
|
// Prevent fetching missing data by not including `token` prop
|
||||||
|
|
||||||
|
export function VehicleTeaserController(props) {
|
||||||
|
const el = useRef(null);
|
||||||
|
const isVisible = useIntersectObserver(el, "0px", true);
|
||||||
|
const [isMissingData, setIsMissingData] = useState(false);
|
||||||
|
|
||||||
|
const { getVehicle, vehicle } = useVehicleContext();
|
||||||
|
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.trim === undefined) {
|
||||||
|
setIsMissingData(true);
|
||||||
|
}
|
||||||
|
}, [isVisible, props.trim]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isVisible && props.token) {
|
||||||
|
getVehicle(props.vin, props.token)
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [isMissingData, isVisible, props.vin, props.token]);
|
||||||
|
|
||||||
|
if (!props.vin) {
|
||||||
|
return (
|
||||||
|
<div className={classes.alignBaseline}>
|
||||||
|
Missing VIN
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={el}>
|
||||||
|
<VehicleTeaser
|
||||||
|
vin={props.vin}
|
||||||
|
trex={props.trex}
|
||||||
|
icc={props.icc}
|
||||||
|
trim={props.trim || vehicle.trim}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function VehicleTeaser(props) {
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.alignBaseline}>
|
||||||
|
<ConnectedIcon
|
||||||
|
connected={props.trex}
|
||||||
|
connectedHMI={props.icc}
|
||||||
|
style={{ display: "flex", marginRight: 3 }}
|
||||||
|
/>
|
||||||
|
<Link to={`/vehicle-status/${props.vin}`}>
|
||||||
|
{props.vin}
|
||||||
|
</Link>
|
||||||
|
<Stack direction="row" spacing={1} style={{ marginLeft: 3 }}>
|
||||||
|
{props.trim && <Chip label={props.trim} size="small" />}
|
||||||
|
</Stack>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,2 +1,4 @@
|
|||||||
export { useTimeoutState } from "./useTimeoutState";
|
export { useTimeoutState } from "./useTimeoutState";
|
||||||
export { useUpdateManifest } from "./useUpdateManifest";
|
export { useUpdateManifest } from "./useUpdateManifest";
|
||||||
|
export { useIntersectObserver } from "./useIntersectObserver";
|
||||||
|
|
||||||
|
|||||||
21
src/hooks/useIntersectObserver.js
Normal file
21
src/hooks/useIntersectObserver.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
export function useIntersectObserver(element, offset, once) {
|
||||||
|
const [isInViewport, setIsInViewport] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const current = element?.current;
|
||||||
|
const observer = new IntersectionObserver(([entry]) => {
|
||||||
|
setIsInViewport(entry.isIntersecting);
|
||||||
|
if (entry.isIntersecting && once) {
|
||||||
|
observer.unobserve(current);
|
||||||
|
}
|
||||||
|
}, { rootMargin: offset });
|
||||||
|
|
||||||
|
current && observer.observe(current);
|
||||||
|
|
||||||
|
return () => current && observer.unobserve(current);
|
||||||
|
}, [element, offset, once]);
|
||||||
|
|
||||||
|
return isInViewport;
|
||||||
|
}
|
||||||
@@ -360,6 +360,17 @@ const vehiclesAPI = {
|
|||||||
}).then(fetchRespHandler)
|
}).then(fetchRespHandler)
|
||||||
.catch(errorHandler)
|
.catch(errorHandler)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getOSVersions: async (token) => {
|
||||||
|
return fetch(`${API_ENDPOINT}/manifests_active_os_versions`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: Object.assign(
|
||||||
|
{ "Content-Type": "application/json" },
|
||||||
|
getAuthHeaderOptions(token)
|
||||||
|
),
|
||||||
|
}).then(fetchRespHandler)
|
||||||
|
.catch(errorHandler)
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default vehiclesAPI;
|
export default vehiclesAPI;
|
||||||
|
|||||||
Reference in New Issue
Block a user