Added external API for getting gas stations

This commit is contained in:
Damien Broqua 2020-03-02 22:08:06 +01:00
parent d9d02ad0c7
commit 658f9aebb7
11 changed files with 179 additions and 177 deletions

View file

@ -1,12 +1,24 @@
import React from 'react';
import { Modal, Button, ListGroup } from "react-bootstrap";
import PropTypes from 'prop-types';
import { gasTypes } from "../config";
import { capitalizeFirstLetter} from "../helpers";
class GasStation extends React.Component {
renderGasType = (gasType) => {
for( let i = 0 ; i < gasTypes.length ; i +=1 ) {
if ( gasTypes[i].type === gasType) {
return gasTypes[i].name;
}
}
return (null);
}
renderPrices = () => {
const {
selectedGasType,
selectedGasStation,
} = this.props;
@ -14,9 +26,9 @@ class GasStation extends React.Component {
<ListGroup variant="flush">
{selectedGasStation.prices ? selectedGasStation.prices.map(price => {
return (
<ListGroup.Item key={price.type}>
{`${price.type} : ${price.price}`}
</ListGroup.Item>
<ListGroup.Item key={price._id} className={selectedGasType === price.gasType ? "selected-gasType" : ""}>
{`${this.renderGasType(price.gasType)} : ${price.price}`}
</ListGroup.Item>
);
}) : (null)}
</ListGroup>
@ -69,6 +81,7 @@ GasStation.defaultProps = {
};
GasStation.propTypes = {
selectedGasType: PropTypes.string.isRequired,
showModal: PropTypes.bool.isRequired,
hideModal: PropTypes.func.isRequired,
selectedGasStation: PropTypes.shape({

View file

@ -1,59 +1,23 @@
import React from 'react';
import { Form, Row, Col } from "react-bootstrap";
import PropTypes from 'prop-types';
import {gasTypes} from "../config";
class GasTypes extends React.Component {
constructor(props) {
super(props);
this.state = {
gasTypes: [
{
name: "Ethanol e85",
type: 'E85',
},
{
name: "Sans plomb 95 E10",
type: "E10"
},
{
name: "Sans plomb 95",
type: "SP95"
},
{
name: "Sans plomb 98",
type: "SP98"
},
{
name: "Gazole",
type: "Gazole"
},
{
name: "GPL",
type: "GPLc"
}
]
};
}
const GasTypes = (props) => {
const {
selectGasType,
selectedGasType,
} = props;
render() {
const {
gasTypes,
} = this.state;
const {
selectGasType,
selectedGasType,
} = this.props;
return (
<Row style={{ width: "100%" }}>
<Col>
<Form.Control as="select" value={selectedGasType} onChange={selectGasType}>
{gasTypes.map(gasType => (<option key={gasType.type} value={gasType.type}>{gasType.name}</option>))}
</Form.Control>
</Col>
</Row>
);
}
return (
<Row style={{ width: "100%" }}>
<Col>
<Form.Control as="select" value={selectedGasType} onChange={selectGasType}>
{gasTypes.map(gasType => (<option key={gasType.type} value={gasType.type}>{gasType.name}</option>))}
</Form.Control>
</Col>
</Row>
);
}
GasTypes.propTypes = {

View file

@ -1,16 +1,14 @@
import React from 'react';
import Axios from "axios";
import ReactMapGL, { Marker } from 'react-map-gl';
import { Button } from "react-bootstrap";
import XmlReader from 'xml-reader';
import { withToastManager } from 'react-toast-notifications';
import PropTypes from 'prop-types';
import iconv from "iconv-lite";
import { haveSelectedGas, extractGasStationFromXml } from '../helpers';
import { haveSelectedGas } from '../helpers';
import { mapboxToken, radius, baseApiUrl } from "../config";
import 'mapbox-gl/dist/mapbox-gl.css';
const mapboxToken = 'pk.eyJ1IjoiZGFya291IiwiYSI6ImNrNzkwdmlsdTBtMmwzZnM0ZmI4Z3h4czIifQ.GU2CdcMiKiApHNhI0ylGtQ';
class Map extends React.Component {
constructor(props) {
super(props);
@ -29,6 +27,9 @@ class Map extends React.Component {
gasStations: [],
};
this.currentLoadId = null;
this.isFirstLoading = true;
this.mapRef = React.createRef();
this.loadGasStations();
@ -43,7 +44,9 @@ class Map extends React.Component {
* @param {Object} viewport
*/
onViewportChange = (viewport) => {
this.setState({ viewport });
this.setState({ viewport }, () => {
this.loadGasStations();
});
}
/**
@ -69,56 +72,85 @@ class Map extends React.Component {
});
}
/**
* Méthode permettant de filter sur la liste des stations affichables sur la carte
* @param {Object} gasStation
* @return {Boolean}
*/
displayThisGasStation = (gasStation) => {
const mapGL = this.mapRef.getMap();
const bounds = mapGL.getBounds();
loadAllGasStations(start, newLoadId, callback) {
const limit = 20;
return (bounds._ne.lat > gasStation.latitude && bounds._sw.lat < gasStation.latitude)
&& (bounds._ne.lng > gasStation.longitude && bounds._sw.lng < gasStation.longitude);
const {
viewport,
} = this.state;
const {
latitude,
longitude
} = viewport;
if ( newLoadId !== this.currentLoadId ) {
callback(new Error('Request canceled'))
} else {
Axios.get(`${baseApiUrl}stations?loadId=${newLoadId}&radius=${radius}&lat=${latitude}&lon=${longitude}&start=${start}&limit=${limit}`)
.then( (response) => {
if ( response.status === 200 ){
const newStations = response.data.items;
let stations = newStations;
const nextStart = start + limit;
if ( nextStart > response.data.total ) {
callback( null, stations )
} else {
this.loadAllGasStations(nextStart, newLoadId, (err, otherStations) => {
if ( err ) {
callback(err);
} else {
stations = stations.concat(otherStations)
callback(null, stations);
}
})
}
} else {
callback(null, []);
}
})
.catch( (err) => {
callback(err);
})
}
}
/**
* Méthode permettant de charger le fichier xml contenant la liste des stations
*/
loadGasStations() {
const newLoadId = new Date().getTime();
const {
toastManager,
} = this.props;
toastManager.add('Chargement de la liste des stations...', { appearance: 'info', autoDismiss: true });
if ( !this.currentLoadId ){
if ( this.isFirstLoading ) {
toastManager.add('Chargement des données...', { appearance: 'info', autoDismiss: true });
} else {
toastManager.add('Actualisation des données...', { appearance: 'info', autoDismiss: true });
}
}
fetch('/gasStations.xml')
.then(res => res.arrayBuffer())
.then(arrayBuffer => iconv.decode(Buffer.from(arrayBuffer), 'iso-8859-1').toString())
.then((response) => {
const reader = XmlReader.create();
reader.on('done', (data) => {
const pdv = data.children;
const gasStations = [];
for (let i = 0; i < pdv.length; i += 1) {
const currentPdv = pdv[i];
gasStations.push(extractGasStationFromXml(currentPdv));
}
this.currentLoadId = newLoadId;
this.loadAllGasStations(0, newLoadId, (err,gasStations) => {
this.isLoading = false;
if ( this.currentLoadId === newLoadId ) {
toastManager.removeAll();
if ( err ) {
toastManager.add('Erreur lors du chargement des données', { appearance: 'error', autoDismiss: true });
} else {
toastManager.add('Chargement des données terminé', { appearance: 'success', autoDismiss: true });
this.currentLoadId = null;
this.isFirstLoading = false;
this.setState((prevState) => ({
...prevState,
gasStations,
}));
});
reader.parse(response);
toastManager.add('Liste des stations correctement chargée', { appearance: 'success', autoDismiss: true });
}).catch(() => {
toastManager.add('Erreur lors du chargement de la liste des stations', { appearance: 'error', autoDismiss: true });
});
}
}
})
}
/**
@ -155,11 +187,11 @@ class Map extends React.Component {
</Marker>
) : (null)
}
{gasStations.filter(this.displayThisGasStation).filter((station) => haveSelectedGas(station, selectedGasType)).map((gasStation) => (
{gasStations.filter((station) => haveSelectedGas(station, selectedGasType)).map((gasStation) => (
<Marker
key={gasStation.id}
latitude={gasStation.latitude}
longitude={gasStation.longitude}
key={gasStation.stationId}
latitude={gasStation.location.coordinates[1]}
longitude={gasStation.location.coordinates[0]}
>
<Button
variant="link"
@ -170,7 +202,7 @@ class Map extends React.Component {
<img
className="locationIcon"
src="/gas-station.png"
alt={`${gasStation.id}`}
alt={`${gasStation.stationId}`}
/>
</Button>
@ -187,6 +219,7 @@ Map.propTypes = {
showGasStation: PropTypes.func.isRequired,
toastManager: PropTypes.shape({
add: PropTypes.func,
removeAll: PropTypes.func,
}).isRequired,
};