Added external API for getting gas stations
This commit is contained in:
parent
d9d02ad0c7
commit
658f9aebb7
11 changed files with 179 additions and 177 deletions
|
@ -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({
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue