284 lines
8.3 KiB
JavaScript
284 lines
8.3 KiB
JavaScript
import {
|
|
Checkbox,
|
|
Grid,
|
|
LinearProgress,
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableFooter,
|
|
TablePagination,
|
|
TableRow,
|
|
Tooltip,
|
|
IconButton,
|
|
} from "@material-ui/core";
|
|
import AddCircleIcon from "@material-ui/icons/AddCircle";
|
|
import DeleteIcon from "@material-ui/icons/Delete";
|
|
import clsx from "clsx";
|
|
import React, { useEffect, useState } 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 ConnectedIcon from "../../../../Controls/ConnectedIcon";
|
|
import BulkActions from "../../../../BulkActions";
|
|
import Battery from "../../../../Battery";
|
|
import useStyles from "../../../../useStyles";
|
|
|
|
const tableColumns = [
|
|
{
|
|
id: "vin",
|
|
label: "VIN",
|
|
},
|
|
{
|
|
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",
|
|
},
|
|
{
|
|
id: "",
|
|
label: "Actions",
|
|
},
|
|
];
|
|
|
|
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 classes = useStyles();
|
|
const { setMessage } = useStatusContext();
|
|
const {
|
|
fleetVehicles,
|
|
totalFleetVehicles,
|
|
watchFleetVehicles,
|
|
getFleetVehicles,
|
|
deleteFleetVehicle,
|
|
} = 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
|
|
);
|
|
watchFleetVehicles.start({ token });
|
|
} catch (e) {
|
|
setMessage(e.message);
|
|
logger.warn(e.stack);
|
|
}
|
|
})();
|
|
return () => {
|
|
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 handleSelect = (vin, setState) => {
|
|
setSelected(selected => setState
|
|
? [...selected, vin]
|
|
: selected.filter(select => select !== vin));
|
|
};
|
|
|
|
const handleSelectAll = (event) => {
|
|
const allSelected = !event.target.checked;
|
|
setSelected(() => allSelected
|
|
? []
|
|
: fleetVehicles.map((vehicle) => vehicle.vin));
|
|
}
|
|
|
|
const onDelete = async (vin) => {
|
|
try {
|
|
await deleteFleetVehicle(name, { vin: vin }, token);
|
|
setMessage(`Deleted ${vin}`);
|
|
} catch (e) {
|
|
setMessage(e.message);
|
|
logger.warn(e.stack);
|
|
}
|
|
};
|
|
|
|
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 ids={selected} actions={["addTags", "sms", "updateConfig", "remoteCommand", "diagnostic"]} />
|
|
</Grid>
|
|
<Grid item md={7} 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.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 }}
|
|
/>
|
|
}
|
|
<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}>
|
|
<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>
|
|
<TableCell key={"cell8" + car.vin} align="center">
|
|
<Tooltip key={`delete-${car.vin}`} title={`Delete "${car.vin}"`}>
|
|
<IconButton onClick={() => onDelete(car.vin)}>
|
|
<DeleteIcon aria-label={`Delete ${car.vin}`} />
|
|
</IconButton>
|
|
</Tooltip>
|
|
</TableCell>
|
|
</TableRow>
|
|
)
|
|
})}
|
|
</TableBody>
|
|
<TableFooter>
|
|
<TableRow>
|
|
<TablePagination
|
|
rowsPerPageOptions={[5, 10, 25, 100]}
|
|
colSpan={9}
|
|
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;
|