282 lines
8.2 KiB
JavaScript
282 lines
8.2 KiB
JavaScript
import {
|
|
Checkbox,
|
|
Grid,
|
|
LinearProgress,
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableFooter,
|
|
TablePagination,
|
|
TableRow,
|
|
Tooltip,
|
|
} from "@material-ui/core";
|
|
import AddCircleIcon from "@material-ui/icons/AddCircle";
|
|
import clsx from "clsx";
|
|
import React, { useEffect, useState, useRef } from "react";
|
|
import { Link } from "react-router-dom";
|
|
|
|
import { logger } from "../../../../../services/monitoring";
|
|
import {
|
|
FleetProvider,
|
|
useFleetContext
|
|
} from "../../../../Contexts/FleetContext";
|
|
import { useStatusContext } from "../../../../Contexts/StatusContext";
|
|
import { useUserContext } from "../../../../Contexts/UserContext";
|
|
import SearchField from "../../../../Controls/SearchField";
|
|
import TableHeaderSortable from "../../../../Table/HeaderSortable";
|
|
import { useLocalStorage } from "../../../../useLocalStorage";
|
|
import BulkActions from "../../../../BulkActions";
|
|
import Battery from "../../../../Battery";
|
|
import { VehicleTeaserController as VehicleTeaser } from "../../../../VehicleTeaser";
|
|
import useStyles from "../../../../useStyles";
|
|
|
|
const tableColumns = [
|
|
{
|
|
id: "vehicle",
|
|
label: "Vehicle",
|
|
},
|
|
{
|
|
id: "trex_version",
|
|
label: "T.REX Version",
|
|
},
|
|
{
|
|
id: "car_update",
|
|
label: "Car Update",
|
|
},
|
|
{
|
|
id: "car_update_status",
|
|
label: "Car Update Status",
|
|
},
|
|
{
|
|
id: "battery",
|
|
label: "Battery",
|
|
},
|
|
{
|
|
id: "voltage",
|
|
label: "Voltage",
|
|
},
|
|
{
|
|
id: "park", // TODO: Update to 'gear' when we confirm each possible state
|
|
label: "Park",
|
|
},
|
|
];
|
|
|
|
const PAGE_SIZE = "FLEET_STATUS_VEHICLES_TABLE_PAGE_SIZE";
|
|
|
|
const MainForm = ({ name }) => {
|
|
const [pageSize, setPageSize] = useLocalStorage(PAGE_SIZE, 10);
|
|
const [pageIndex, setPageIndex] = useState(0);
|
|
const [orderBy, setOrderBy] = useState("id");
|
|
const [order, setOrder] = useState("desc");
|
|
const [search, setSearch] = useState("");
|
|
const [selected, setSelected] = useState([]);
|
|
const [selectAll, setSelectAll] = useState(false);
|
|
const [fleetVINs, setFleetVINs] = useState([]);
|
|
const componentMounted = useRef(true);
|
|
const classes = useStyles();
|
|
const { setMessage } = useStatusContext();
|
|
const {
|
|
fleetVehicles,
|
|
totalFleetVehicles,
|
|
watchFleetVehicles,
|
|
getFleetVehicles,
|
|
getFleetVINs,
|
|
removeFleetVehiclesLocal,
|
|
} = useFleetContext();
|
|
const {
|
|
token: {
|
|
idToken: { jwtToken: token },
|
|
},
|
|
} = useUserContext();
|
|
|
|
const handleSearch = (query) => {
|
|
setSearch(query);
|
|
};
|
|
|
|
useEffect(() => {
|
|
(async () => {
|
|
try {
|
|
if (!name || !token) return;
|
|
await getFleetVehicles(
|
|
name,
|
|
{
|
|
search,
|
|
limit: pageSize,
|
|
offset: pageSize * pageIndex,
|
|
order: `${orderBy} ${order}`,
|
|
},
|
|
token
|
|
);
|
|
await getFleetVINs(
|
|
name,
|
|
token
|
|
).then((vins) => {
|
|
setFleetVINs(vins);
|
|
});
|
|
if (componentMounted.current) {
|
|
watchFleetVehicles.start({ token });
|
|
}
|
|
} catch (e) {
|
|
setMessage(e.message);
|
|
logger.warn(e.stack);
|
|
watchFleetVehicles.end();
|
|
}
|
|
})();
|
|
return () => {
|
|
componentMounted.current = false;
|
|
watchFleetVehicles.end();
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [token, pageIndex, pageSize, orderBy, order, search]);
|
|
|
|
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 handleSelectAll = () => {
|
|
setSelectAll(!selectAll);
|
|
setSelected(!selectAll ? fleetVINs : []);
|
|
};
|
|
|
|
const handleSelect = (vin, setState) => {
|
|
setSelected(selected => setState
|
|
? [...selected, vin]
|
|
: selected.filter(select => select !== vin));
|
|
};
|
|
|
|
const bulkActionCallback = (action, _, context) => {
|
|
if (action === "updateFleetVehicles") {
|
|
const vinsToRemove = context.fromFleet === name ? context.fromVehicles : [];
|
|
removeFleetVehiclesLocal(vinsToRemove);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className={clsx(classes.paper, classes.tableSize)}>
|
|
<Grid container className={classes.root} spacing={2}>
|
|
<Grid item md={1} className={classes.textJustifyAlign}>
|
|
<Link to={`/fleet/${name}/vehicle-add`}>
|
|
<AddCircleIcon fontSize="large" />
|
|
</Link>
|
|
</Grid>
|
|
<Grid item md={4}>
|
|
<BulkActions
|
|
actions={["updateFleetVehicles", "addTags", "sms", "updateConfig", "remoteCommand", "diagnostic", "updateFlashpackNumbers"]}
|
|
ids={selected}
|
|
callback={bulkActionCallback}
|
|
/>
|
|
</Grid>
|
|
<Grid item md={5} align="right" className={classes.textCenterAlign}>
|
|
<SearchField classes={classes} onSearch={handleSearch} />
|
|
</Grid>
|
|
</Grid>
|
|
<Table>
|
|
<TableHeaderSortable
|
|
classes={classes}
|
|
orderBy={orderBy}
|
|
order={order}
|
|
columnData={tableColumns}
|
|
onSortRequest={handleSort}
|
|
multiSelect={true}
|
|
onSelectAll={handleSelectAll}
|
|
selectCount={selected ? selected.length : 0}
|
|
rowCount={fleetVehicles.length}
|
|
selectAllForAllPages={true}
|
|
/>
|
|
<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">
|
|
<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>
|
|
<TableFooter>
|
|
<TableRow>
|
|
<TablePagination
|
|
rowsPerPageOptions={[5, 10, 25, 100]}
|
|
colSpan={8}
|
|
count={totalFleetVehicles}
|
|
rowsPerPage={pageSize}
|
|
page={pageIndex}
|
|
SelectProps={{
|
|
inputProps: { "aria-label": "rows per page" },
|
|
native: true,
|
|
}}
|
|
onPageChange={handleChangePageIndex}
|
|
onRowsPerPageChange={handleChangePageSize}
|
|
/>
|
|
</TableRow>
|
|
</TableFooter>
|
|
</Table>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const FleetVehiclesTable = (props) => (
|
|
<FleetProvider>
|
|
<MainForm {...props} />
|
|
</FleetProvider>
|
|
);
|
|
|
|
export default FleetVehiclesTable;
|