CEC-6016: add trim to fleet (#523)
* CEC-6016: add trim to fleet * update snapshot
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import PropTypes from "prop-types";
|
||||
import {
|
||||
Checkbox,
|
||||
@@ -17,9 +16,9 @@ import { useStatusContext } from "../../Contexts/StatusContext";
|
||||
import { LocalDateTimeString } from "../../../utils/dates";
|
||||
import TableHeaderSortable from "../../Table/HeaderSortable";
|
||||
import { logger } from "../../../services/monitoring";
|
||||
import ConnectedIcon from "../../Controls/ConnectedIcon";
|
||||
import ECUList from "../../Controls/ECUList";
|
||||
import { useLocalStorage } from "../../useLocalStorage";
|
||||
import { VehicleTeaser } from "../../VehicleTeaser";
|
||||
|
||||
const tableColumns = [
|
||||
{
|
||||
@@ -164,12 +163,11 @@ const CarSelectionTable = (props) => {
|
||||
</TableCell>
|
||||
)}
|
||||
<TableCell align="center">
|
||||
<ConnectedIcon
|
||||
connected={row.connected}
|
||||
connectedHMI={row.connectedHMI}
|
||||
style={{ marginRight: 3 }}
|
||||
<VehicleTeaser
|
||||
vin={row.vin}
|
||||
trex={row.connected}
|
||||
icc={row.connectedHMI}
|
||||
/>
|
||||
<Link to={`/vehicle-status/${row.vin}`}>{row.vin}</Link>
|
||||
{row.ecu_list && (
|
||||
<>
|
||||
<br />
|
||||
|
||||
@@ -1,36 +1,78 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import CheckCircleIcon from "@material-ui/icons/CheckCircle";
|
||||
import CheckBoxIcon from "@material-ui/icons/CheckBox";
|
||||
import DoneAllIcon from "@material-ui/icons/DoneAll";
|
||||
import ClearIcon from "@material-ui/icons/Clear";
|
||||
import DoneIcon from "@material-ui/icons/Done";
|
||||
import { Tooltip } from "@material-ui/core";
|
||||
|
||||
const ConnectedIcon = (props) => {
|
||||
if (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>
|
||||
);
|
||||
}
|
||||
let title = tooltip(props.connected, props.connectedHMI);
|
||||
|
||||
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 = {
|
||||
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;
|
||||
|
||||
@@ -191,7 +191,7 @@ exports[`FleetVehiclesTable Render 1`] = `
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
VIN
|
||||
Vehicle
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="MuiSvgIcon-root MuiTableSortLabel-icon MuiTableSortLabel-iconDirectionAsc"
|
||||
|
||||
@@ -23,18 +23,19 @@ import {
|
||||
} from "../../../../Contexts/FleetContext";
|
||||
import { useStatusContext } from "../../../../Contexts/StatusContext";
|
||||
import { useUserContext } from "../../../../Contexts/UserContext";
|
||||
import { VehicleProvider } from "../../../../Contexts/VehicleContext";
|
||||
import SearchField from "../../../../Controls/SearchField";
|
||||
import TableHeaderSortable from "../../../../Table/HeaderSortable";
|
||||
import { useLocalStorage } from "../../../../useLocalStorage";
|
||||
import ConnectedIcon from "../../../../Controls/ConnectedIcon";
|
||||
import BulkActions from "../../../../BulkActions";
|
||||
import Battery from "../../../../Battery";
|
||||
import { VehicleTeaserController as VehicleTeaser } from "../../../../VehicleTeaser";
|
||||
import useStyles from "../../../../useStyles";
|
||||
|
||||
const tableColumns = [
|
||||
{
|
||||
id: "vin",
|
||||
label: "VIN",
|
||||
id: "vehicle",
|
||||
label: "Vehicle",
|
||||
},
|
||||
{
|
||||
id: "trex_version",
|
||||
@@ -213,54 +214,53 @@ const MainForm = ({ name }) => {
|
||||
selectCount={selected.length}
|
||||
rowCount={fleetVehicles.length}
|
||||
/>
|
||||
<TableBody>
|
||||
{fleetVehicles && fleetVehicles.map((car) => {
|
||||
const isSelected = selected.includes(car.vin);
|
||||
return (car.vin && <TableRow key={"row" + car.vin}>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
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 }}
|
||||
<VehicleProvider>
|
||||
<TableBody>
|
||||
{fleetVehicles && fleetVehicles.map((car) => {
|
||||
const isSelected = selected.includes(car.vin);
|
||||
return (car.vin && <TableRow key={"row" + car.vin}>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
onChange={() => handleSelect(car.vin, !isSelected)}
|
||||
/>
|
||||
}
|
||||
<Link key={"link" + car.vin} to={`/vehicle-status/${car.vin}`}>{car.vin}</Link>
|
||||
</TableCell>
|
||||
<TableCell key={"cell2" + car.vin} align="center">{car.trex_version}</TableCell>
|
||||
<TableCell key={"cell3" + car.car_update_name}>
|
||||
<Tooltip key={"cell3tooltip"} title={`${car.car_update_id} / ${car.car_update_type}`}>
|
||||
<Link to={`/vehicle-status/${car.vin}/${car.car_update_id}`} className={classes.truncateCell}>
|
||||
{car.car_update_name}
|
||||
</Link>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
<TableCell key={"cell4" + car.vin}>
|
||||
{car.car_update_status}
|
||||
{car.car_update_progress > -1 && (
|
||||
<LinearProgress variant="determinate" value={car.car_update_progress} />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell key={"cell5" + car.vin} align="left">
|
||||
<Battery percent={car.charge} charge={car.charge_type} />
|
||||
</TableCell>
|
||||
<TableCell key={"cell6" + car.vin}>
|
||||
{car.voltage > 0 && `${car.voltage}V`}
|
||||
</TableCell>
|
||||
<TableCell key={"cell7" + car.vin}>
|
||||
{car.park ? "Yes" : "No"}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
})}
|
||||
</TableBody>
|
||||
</TableCell>
|
||||
<TableCell key={"cell" + car.vin} align="center">
|
||||
<VehicleTeaser
|
||||
vin={car.vin}
|
||||
icc={car.connectedHMI}
|
||||
trex={car.connected}
|
||||
token={token}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell key={"cell2" + car.vin} align="center">{car.trex_version}</TableCell>
|
||||
<TableCell key={"cell3" + car.car_update_name}>
|
||||
<Tooltip key={"cell3tooltip"} title={`${car.car_update_id} / ${car.car_update_type}`}>
|
||||
<Link to={`/vehicle-status/${car.vin}/${car.car_update_id}`} className={classes.truncateCell}>
|
||||
{car.car_update_name}
|
||||
</Link>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
<TableCell key={"cell4" + car.vin}>
|
||||
{car.car_update_status}
|
||||
{car.car_update_progress > -1 && (
|
||||
<LinearProgress variant="determinate" value={car.car_update_progress} />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell key={"cell5" + car.vin} align="left">
|
||||
<Battery percent={car.charge} charge={car.charge_type} />
|
||||
</TableCell>
|
||||
<TableCell key={"cell6" + car.vin}>
|
||||
{car.voltage > 0 && `${car.voltage}V`}
|
||||
</TableCell>
|
||||
<TableCell key={"cell7" + car.vin}>
|
||||
{car.park ? "Yes" : "No"}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
})}
|
||||
</TableBody>
|
||||
</VehicleProvider>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
|
||||
@@ -190,7 +190,7 @@ exports[`VehiclesTab Render 1`] = `
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
VIN
|
||||
Vehicle
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
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 { 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;
|
||||
}
|
||||
Reference in New Issue
Block a user