Files
ota-admin-portal/src/components/VehicleMap/index.jsx
John Wu 86eeaab869 Development (#94)
* CEC-371 Car ECU display (#79)

* Merge Development (#53)

* Use responsive iframe control for charts (#49)

* Use responsive iframe control to charts

* Move external Grafana link to Dashboard page

* Remove unused embedded style class

* Add button label

* added delete button to deploy packages

* Fix unit test warning
Remove unused route from test

* Fix styling of button

* minor fixes per pr review

Co-authored-by: jcw-fisker <jwatson@fiskerinc.com>
Co-authored-by: John Cotten Watson <83605808+jcw-fisker@users.noreply.github.com>

* Development Merge (#57)

* CEC-287 Car connection status (#59) (#60)

* Car connection status

* Formatting

* Merge Development (#64)

* Add connection status to vehicles page

* ConnectedIcon control

* Handle Style

* Development (#67)

* preliminary map for vehicles

* weird zoom bug

* passing react tests

* fixing warnings and updating snapshots

* update node environment to 14

* addressing comments by changing variable types and adding styles to home page title

* adding CODEOWNERS file

* fixing token error

* 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

Co-authored-by: jcw-fisker <jwatson@fiskerinc.com>
Co-authored-by: John Cotten Watson <83605808+jcw-fisker@users.noreply.github.com>
Co-authored-by: Drew Taylor <69828061+drew-fisker@users.noreply.github.com>

* CEC-394 Car update log (#81)

* CEC-394 Car update status control

* Remove Datadog RUM
Remove package update components
Move control components into Controls folder
Add Car update status page

* Display update status log
Clean up unused update package code

* Remove console.logs

* no vars

* adding timestamp to vehicle popup

* modifying vehicle data query

* removing extraneous code

* removing console log

* Clean up SonarCloud warnings (#83)

* Clean up SonarCloud warnings

* Bogus security warning

* Fix another warning

* Fix unauthorized locations request

* Fix update progress control

* CEC-563 New manifest format (#88)

* Add ManifestCreateContext
Update create manifest page

* Finish UI changes and API integration

* Fixes

* Fix test

* Remove manifest ECU file version and type

* Fixes

* Add manifest ecu file type control

* Fix Sonar warnings

* Fix test

* Update codeowners

* Formatting

* CEC-553 Change file type to string (#90)

* CEC-553 File type uses string enum

* Fix test timeout

* Fix

* Merge development

* Increase timeout

* Clean up (#95)

* Clean up
Mock missing methods

* Smell

Co-authored-by: jcw-fisker <jwatson@fiskerinc.com>
Co-authored-by: John Cotten Watson <83605808+jcw-fisker@users.noreply.github.com>
Co-authored-by: Drew Taylor <69828061+drew-fisker@users.noreply.github.com>
Co-authored-by: Drew Taylor <dtaylor@fiskerinc.com>
2021-10-14 12:23:16 -07:00

212 lines
5.3 KiB
JavaScript

import React, { useEffect, useState } from "react";
import useStyles from "../useStyles";
import L from "leaflet";
import { MapContainer, TileLayer, Marker, Popup, useMap } from "react-leaflet";
import { Button } from "@material-ui/core";
import { useUserContext } from "../Contexts/UserContext";
import { useVehicleContext, VehicleProvider } from "../Contexts/VehicleContext";
import { VehiclePopUp } from "./popup";
import GreenMarkerIcon from "../../assets/green-marker.png";
import GrayMarkerIcon from "../../assets/gray-marker.png";
import { logger } from "../../services/monitoring";
const Component = () => {
const classes = useStyles();
const {
token: {
idToken: { jwtToken: token },
},
} = useUserContext();
const { getConnections, getLocations, getState } = useVehicleContext();
const REQUEST_INTERVAL = 10000;
const [center, setCenter] = useState([0, 0]);
const [zoom, setZoom] = useState(2);
const [markers, setMarkers] = useState([]);
useEffect(() => {
if (!token) return;
retrieveAndStoreLocations(token).then((points) => {
centerAroundMarkers(points);
});
const id = setInterval(function () {
retrieveAndStoreLocations(token);
}, REQUEST_INTERVAL);
return () => {
clearInterval(id);
};
// eslint-disable-next-line
}, [token]);
const retrieveAndStoreLocations = (accessToken) => {
return getLocations(accessToken)
.then((result) => {
if (result.data != null) {
const points = result.data.map((point) => [
point.latitude,
point.longitude,
point.vin,
]);
setMarkers(points);
return points;
}
return [];
})
.catch((error) => logger.warn(error.stack));
};
const centerAroundMarkers = (points) => {
// if (markers == null) {
// markers = []
// }
// const coord = markers.reduce((coord, marker) => {
// coord[0] += marker[0] / markers.length;
// coord[1] += marker[1] / markers.length;
// return coord;
// }, [0, 0])
setCenter([37.0902, -95.7129]);
setZoom(4.5);
};
const [connections, setConnections] = useState({});
useEffect(() => {
if (!token) return;
if (markers.length > 0) {
const vins = markers.map((marker) => marker[2]);
getConnections(vins, token).then((conns) => {
setConnections(conns);
});
}
// eslint-disable-next-line
}, [markers, token]);
const [selectedVIN, setSelectedVIN] = useState(null);
const [carState, setCarState] = useState(null);
useEffect(() => {
if (selectedVIN != null) {
retrieveAndStoreCarState(selectedVIN);
const id = setInterval(function () {
retrieveAndStoreCarState(selectedVIN);
}, REQUEST_INTERVAL);
return () => {
clearInterval(id);
};
}
// eslint-disable-next-line
}, [selectedVIN]);
const selectCar = (e, vin) => {
e.preventDefault();
setSelectedVIN(vin);
};
const retrieveAndStoreCarState = (vin) => {
getState(token, vin).then((results) => {
setCarState({ ...results.data, vin: vin });
});
};
const handleClose = () => {
setSelectedVIN(null);
setCarState(null);
};
function getCarIcon(vin) {
let icon = GrayMarkerIcon;
if (connections[vin]) {
icon = GreenMarkerIcon;
}
return new L.Icon({
iconUrl: icon,
iconAnchor: [24, 42],
});
}
return (
<MapContainer
center={center}
zoom={zoom}
style={{
width: "100%",
height: "900px",
}}
>
<TileLayer
attribution='&copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<CenterFocus center={center} zoom={zoom} />
{markers.map((marker) => (
<Marker
icon={getCarIcon(marker[2])}
key={marker[2]}
position={[marker[0], marker[1]]}
title={marker[2]}
opacity={0.9}
>
<Popup>
<div align="center">
<p className={classes.markerTitle}>
<b>{marker[2]}</b>
</p>
<Button
type="submit"
variant="contained"
color="primary"
onClick={(e) => selectCar(e, marker[2])}
>
View Stats
</Button>
</div>
</Popup>
</Marker>
))}
{carState ? (
<VehiclePopUp
key={carState.vin}
vin={carState.vin}
online={carState.online}
battery={carState.battery}
doors={carState.doors}
location={carState.location}
windows={carState.windows}
updatedAt={carState.updated}
className={classes.popup}
onClose={handleClose}
/>
) : null}
</MapContainer>
);
};
const CenterFocus = ({ center, zoom }) => {
const map = useMap();
useEffect(() => {
if (center[0] === 0 && center[1] === 0) {
map.flyTo([0, 0], 2, { duration: 1.5 });
} else {
map.flyTo(center, zoom, { duration: 1.5 });
}
}, [center, zoom, map]);
return null;
};
const VehicleMap = () => (
<VehicleProvider>
<Component />
</VehicleProvider>
);
export default VehicleMap;