diff --git a/.eslintrc.js b/.eslintrc.js
index 96d45e2..7a7b1c0 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -4,7 +4,7 @@ module.exports = {
plugins: ['react', 'jsx-a11y', 'import', 'flowtype'],
rules: {
'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx'] }],
- 'no-underscore-dangle': ["error", { "allow": ["_ne", "_sw"] }],
+ 'no-underscore-dangle': ["error", { "allow": ["_ne", "_sw", "_id"] }],
"react/jsx-props-no-spreading": [1, {
"exceptions": ["ReactMapGL"]
}]
diff --git a/package.json b/package.json
index 6b98f6b..91d43e5 100644
--- a/package.json
+++ b/package.json
@@ -1,15 +1,15 @@
{
- "name": "e85map",
+ "name": "prix-carburants",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
+ "axios": "^0.19.2",
"bootstrap": "^4.4.1",
"eslint-config-prettier": "^6.10.0",
"eslint-plugin-prettier": "^3.1.2",
- "iconv-lite": "^0.5.1",
"mapbox-gl": "^1.8.1",
"moment": "^2.24.0",
"prop-types": "^15.7.2",
@@ -19,8 +19,7 @@
"react-map-gl": "^5.2.3",
"react-moment": "^0.9.7",
"react-scripts": "3.4.0",
- "react-toast-notifications": "^2.4.0",
- "xml-reader": "^2.4.3"
+ "react-toast-notifications": "^2.4.0"
},
"scripts": {
"start": "react-scripts start",
diff --git a/public/index.html b/public/index.html
index c53c344..9858a7c 100644
--- a/public/index.html
+++ b/public/index.html
@@ -10,7 +10,7 @@
-
Carte des stations e85 - un service proposé par DarKou.fr
+ Carte des stations - un service proposé par DarKou.fr
diff --git a/src/App.css b/src/App.css
index a39e679..c4bed73 100644
--- a/src/App.css
+++ b/src/App.css
@@ -1,24 +1,12 @@
-.mapContainer {
- position: absolute;
- top: 0;
- right: 0;
- left: 0;
- bottom: 0;
+.locationIcon {
+ width: 32px;
}
-.sidebarStyle {
- display: inline-block;
- position: absolute;
- top: 0;
- left: 0;
- margin: 12px;
- background-color: #404040;
- color: #ffffff;
- z-index: 1 !important;
- padding: 6px;
- font-weight: bold;
- }
+div.react-toast-notifications__container {
+ z-index: 1031 ;
+}
- .locationIcon {
- width: 32px;
- }
\ No newline at end of file
+.selected-gasType.list-group-item {
+ font-style: italic;
+ font-weight: bold;
+}
\ No newline at end of file
diff --git a/src/App.js b/src/App.js
index 1ce61c1..728316d 100644
--- a/src/App.js
+++ b/src/App.js
@@ -43,7 +43,11 @@ class Application extends React.Component {
selectedGasStation,
} = this.state;
if (goToWaze) {
- window.open(`https://www.waze.com/livemap/directions?navigate=yes&latlng=${selectedGasStation.latitude}%2C${selectedGasStation.longitude}&zoom=17`);
+ const {
+ coordinates,
+ } = selectedGasStation.location;
+
+ window.open(`https://www.waze.com/livemap/directions?navigate=yes&latlng=${coordinates[1]}%2C${coordinates[0]}&zoom=17`);
}
this.setState(prevState => ({
...prevState,
@@ -81,6 +85,7 @@ class Application extends React.Component {
showModal={showModal}
hideModal={this.hideModal}
selectedGasStation={selectedGasStation}
+ selectedGasType={selectedGasType}
/>
diff --git a/src/Components/GasStation.js b/src/Components/GasStation.js
index 3745893..ee3d270 100644
--- a/src/Components/GasStation.js
+++ b/src/Components/GasStation.js
@@ -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 {
{selectedGasStation.prices ? selectedGasStation.prices.map(price => {
return (
-
- {`${price.type} : ${price.price} € `}
-
+
+ {`${this.renderGasType(price.gasType)} : ${price.price} € `}
+
);
}) : (null)}
@@ -69,6 +81,7 @@ GasStation.defaultProps = {
};
GasStation.propTypes = {
+ selectedGasType: PropTypes.string.isRequired,
showModal: PropTypes.bool.isRequired,
hideModal: PropTypes.func.isRequired,
selectedGasStation: PropTypes.shape({
diff --git a/src/Components/GasTypes.js b/src/Components/GasTypes.js
index 8915506..eadccf1 100644
--- a/src/Components/GasTypes.js
+++ b/src/Components/GasTypes.js
@@ -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 (
-
-
-
- {gasTypes.map(gasType => ())}
-
-
-
- );
- }
+ return (
+
+
+
+ {gasTypes.map(gasType => ())}
+
+
+
+ );
}
GasTypes.propTypes = {
diff --git a/src/Components/Map.js b/src/Components/Map.js
index d920763..a6d7d17 100644
--- a/src/Components/Map.js
+++ b/src/Components/Map.js
@@ -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 {
) : (null)
}
- {gasStations.filter(this.displayThisGasStation).filter((station) => haveSelectedGas(station, selectedGasType)).map((gasStation) => (
+ {gasStations.filter((station) => haveSelectedGas(station, selectedGasType)).map((gasStation) => (
@@ -187,6 +219,7 @@ Map.propTypes = {
showGasStation: PropTypes.func.isRequired,
toastManager: PropTypes.shape({
add: PropTypes.func,
+ removeAll: PropTypes.func,
}).isRequired,
};
diff --git a/src/config.js b/src/config.js
new file mode 100644
index 0000000..b27a553
--- /dev/null
+++ b/src/config.js
@@ -0,0 +1,29 @@
+export const gasTypes = [
+ {
+ name: "Superéthanol 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"
+ }
+];
+export const mapboxToken = 'pk.eyJ1IjoiZGFya291IiwiYSI6ImNrNzkwdmlsdTBtMmwzZnM0ZmI4Z3h4czIifQ.GU2CdcMiKiApHNhI0ylGtQ';
+export const baseApiUrl = 'https://api.carburants.darkou.fr/v1/';
+export const radius = 20000;
\ No newline at end of file
diff --git a/src/helpers.js b/src/helpers.js
index aff2b6f..21d4ee2 100644
--- a/src/helpers.js
+++ b/src/helpers.js
@@ -1,66 +1,16 @@
-export const getFuelPrices = (pdv) => {
- const prices = []
- if (!pdv.children || pdv.children.length === 0) {
- return false;
- }
- for (let i = 0; i < pdv.children.length; i += 1) {
- const currentChildren = pdv.children[i];
- if (currentChildren.type === 'element' && currentChildren.name === 'prix') {
- prices.push({
- type: currentChildren.attributes.nom,
- price: parseInt(currentChildren.attributes.valeur, 10) / 1000,
- updatedAt: currentChildren.attributes.maj,
- });
- }
- }
- return prices;
-};
-
-export const getPlvInformation = (pdv, name) => {
- if (!pdv.children || pdv.children.length === 0) {
- return false;
- }
- for (let i = 0; i < pdv.children.length; i += 1) {
- const currentChildren = pdv.children[i];
- if (currentChildren.type === 'element' && currentChildren.name === name) {
-
- if ( currentChildren.children && currentChildren.children.length > 0 ) {
- return currentChildren.children[0].value.toLowerCase();
- }
-
- return null;
- }
- }
- return false;
-};
-
-export const formatPosition = (value) => value / 100000;
-
export const haveSelectedGas = (station, gas) => {
if (!station.prices || station.prices.length === 0 ) {
return false;
}
for (let i = 0 ; i < station.prices.length ; i +=1 ){
- if (station.prices[i].type === gas ) {
+ if (station.prices[i].gasType === gas ) {
return true;
}
}
return false;
}
-export const extractGasStationFromXml = (currentPdv ) => {
- return {
- id: currentPdv.attributes.id,
- latitude: formatPosition(currentPdv.attributes.latitude),
- longitude: formatPosition(currentPdv.attributes.longitude),
- prices: getFuelPrices(currentPdv),
- postCode: currentPdv.attributes.cp,
- address: getPlvInformation(currentPdv, 'adresse'),
- city: getPlvInformation(currentPdv, 'ville')
- }
-}
-
export const capitalizeFirstLetter = (string) => {
if ( !string){
return '';
diff --git a/yarn.lock b/yarn.lock
index c580bed..f902208 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2224,6 +2224,13 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
+axios@^0.19.2:
+ version "0.19.2"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
+ integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==
+ dependencies:
+ follow-redirects "1.5.10"
+
axobject-query@^2.0.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.1.2.tgz#2bdffc0371e643e5f03ba99065d5179b9ca79799"
@@ -3607,6 +3614,13 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9:
dependencies:
ms "2.0.0"
+debug@=3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
+ integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
+ dependencies:
+ ms "2.0.0"
+
debug@^3.0.0, debug@^3.1.1, debug@^3.2.5:
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
@@ -4801,6 +4815,13 @@ flush-write-stream@^1.0.0:
inherits "^2.0.3"
readable-stream "^2.3.6"
+follow-redirects@1.5.10:
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
+ integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==
+ dependencies:
+ debug "=3.1.0"
+
follow-redirects@^1.0.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.10.0.tgz#01f5263aee921c6a54fb91667f08f4155ce169eb"