Initial commit
This commit is contained in:
commit
80c1729b2f
26 changed files with 311019 additions and 0 deletions
25
src/Components/Footer.js
Normal file
25
src/Components/Footer.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
import React from 'react';
|
||||
import { Navbar } from "react-bootstrap";
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import GasTypes from "./GasTypes";
|
||||
|
||||
const Footer = (props) => {
|
||||
const {
|
||||
selectGasType,
|
||||
selectedGasType,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Navbar bg="light" variant="light" fixed="bottom">
|
||||
<GasTypes selectedGasType={selectedGasType} selectGasType={selectGasType} />
|
||||
</Navbar>
|
||||
);
|
||||
};
|
||||
|
||||
Footer.propTypes = {
|
||||
selectedGasType: PropTypes.string.isRequired,
|
||||
selectGasType: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default Footer;
|
79
src/Components/GasStation.js
Normal file
79
src/Components/GasStation.js
Normal file
|
@ -0,0 +1,79 @@
|
|||
import React from 'react';
|
||||
import { Modal, Button, ListGroup } from "react-bootstrap";
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
class GasStation extends React.Component {
|
||||
renderPrices = () => {
|
||||
const {
|
||||
selectedGasStation,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<ListGroup variant="flush">
|
||||
{selectedGasStation.prices ? selectedGasStation.prices.map(price => {
|
||||
return (
|
||||
<ListGroup.Item key={price.type}>
|
||||
{`${price.type} : ${price.price} € `}
|
||||
</ListGroup.Item>
|
||||
);
|
||||
}) : (null)}
|
||||
</ListGroup>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
showModal,
|
||||
hideModal,
|
||||
selectedGasStation,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
size="xl"
|
||||
aria-labelledby="contained-modal-title-vcenter"
|
||||
centered
|
||||
show={showModal}
|
||||
onHide={hideModal}
|
||||
>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>
|
||||
{`${selectedGasStation.address} - ${selectedGasStation.city}`}
|
||||
</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
{this.renderPrices()}
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant="primary" onClick={() => hideModal(true)}>
|
||||
<img
|
||||
className="locationIcon"
|
||||
src="/waze.png"
|
||||
alt="S'y rendre"
|
||||
/>
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
GasStation.defaultProps = {
|
||||
selectedGasStation: {
|
||||
address: null,
|
||||
city: null,
|
||||
prices: []
|
||||
}
|
||||
};
|
||||
|
||||
GasStation.propTypes = {
|
||||
showModal: PropTypes.bool.isRequired,
|
||||
hideModal: PropTypes.func.isRequired,
|
||||
selectedGasStation: PropTypes.shape({
|
||||
address: PropTypes.string,
|
||||
city: PropTypes.string,
|
||||
prices: PropTypes.array
|
||||
}),
|
||||
};
|
||||
|
||||
export default GasStation;
|
64
src/Components/GasTypes.js
Normal file
64
src/Components/GasTypes.js
Normal file
|
@ -0,0 +1,64 @@
|
|||
import React from 'react';
|
||||
import { Form, Row, Col } from "react-bootstrap";
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
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"
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
GasTypes.propTypes = {
|
||||
selectedGasType: PropTypes.string.isRequired,
|
||||
selectGasType: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default GasTypes;
|
187
src/Components/Map.js
Normal file
187
src/Components/Map.js
Normal file
|
@ -0,0 +1,187 @@
|
|||
import React from 'react';
|
||||
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 { haveSelectedGas, extractGasStationFromXml } from '../helpers';
|
||||
|
||||
import 'mapbox-gl/dist/mapbox-gl.css';
|
||||
|
||||
const mapboxToken = 'pk.eyJ1IjoiZGFya291IiwiYSI6ImNrNzkwdmlsdTBtMmwzZnM0ZmI4Z3h4czIifQ.GU2CdcMiKiApHNhI0ylGtQ';
|
||||
|
||||
class Map extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
viewport: {
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
latitude: 44.837789,
|
||||
longitude: -0.57918,
|
||||
zoom: 11,
|
||||
},
|
||||
userLocation: {
|
||||
// latitude: 44.837789,
|
||||
// longitude: -0.57918,
|
||||
},
|
||||
gasStations: [],
|
||||
};
|
||||
|
||||
this.mapRef = React.createRef();
|
||||
|
||||
this.loadGasStations();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setUserLocation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Méthode permettant de mettre à jour la position de la map
|
||||
* @param {Object} viewport
|
||||
*/
|
||||
onViewportChange = (viewport) => {
|
||||
this.setState({ viewport });
|
||||
}
|
||||
|
||||
/**
|
||||
* Méthode permettant de sauvegarder la position de l'utilisateur
|
||||
*/
|
||||
setUserLocation() {
|
||||
navigator.geolocation.getCurrentPosition((position) => {
|
||||
const setUserLocation = {
|
||||
latitude: position.coords.latitude,
|
||||
longitude: position.coords.longitude,
|
||||
};
|
||||
const newViewport = {
|
||||
height: '100vh',
|
||||
width: '100vw',
|
||||
latitude: position.coords.latitude,
|
||||
longitude: position.coords.longitude,
|
||||
zoom: 10,
|
||||
};
|
||||
this.setState({
|
||||
viewport: newViewport,
|
||||
userLocation: setUserLocation,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
return (bounds._ne.lat > gasStation.latitude && bounds._sw.lat < gasStation.latitude)
|
||||
&& (bounds._ne.lng > gasStation.longitude && bounds._sw.lng < gasStation.longitude);
|
||||
}
|
||||
|
||||
/**
|
||||
* Méthode permettant de charger le fichier xml contenant la liste des stations
|
||||
*/
|
||||
loadGasStations() {
|
||||
const {
|
||||
toastManager,
|
||||
} = this.props;
|
||||
|
||||
fetch('/gasStations.xml')
|
||||
.then((response) => response.text())
|
||||
.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.setState((prevState) => ({
|
||||
...prevState,
|
||||
gasStations,
|
||||
}));
|
||||
});
|
||||
reader.parse(response);
|
||||
}).catch(() => {
|
||||
toastManager.add('Erreur lors du chargement de la liste des stations', { appearance: 'error', autoDismiss: true });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Méthode gérant le rendu de la vue
|
||||
*/
|
||||
render() {
|
||||
const {
|
||||
viewport,
|
||||
userLocation,
|
||||
gasStations,
|
||||
} = this.state;
|
||||
|
||||
const {
|
||||
showGasStation,
|
||||
selectedGasType,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<ReactMapGL
|
||||
{...viewport}
|
||||
mapStyle="mapbox://styles/mapbox/outdoors-v11"
|
||||
onViewportChange={this.onViewportChange}
|
||||
mapboxApiAccessToken={mapboxToken}
|
||||
ref={(map) => { if (map) this.mapRef = map; }}
|
||||
>
|
||||
{
|
||||
Object.keys(userLocation).length !== 0 ? (
|
||||
<Marker
|
||||
latitude={userLocation.latitude}
|
||||
longitude={userLocation.longitude}
|
||||
>
|
||||
<img className="locationIcon" src="/car.png" alt="My position" />
|
||||
</Marker>
|
||||
) : (null)
|
||||
}
|
||||
{gasStations.filter(this.displayThisGasStation).filter((station) => haveSelectedGas(station, selectedGasType)).map((gasStation) => (
|
||||
<Marker
|
||||
key={gasStation.id}
|
||||
latitude={gasStation.latitude}
|
||||
longitude={gasStation.longitude}
|
||||
>
|
||||
<Button
|
||||
variant="link"
|
||||
onClick={() => showGasStation(gasStation)}
|
||||
onFocus={() => { }}
|
||||
onBlur={() => { }}
|
||||
>
|
||||
<img
|
||||
className="locationIcon"
|
||||
src="/gas-station.png"
|
||||
alt={`${gasStation.id}`}
|
||||
/>
|
||||
</Button>
|
||||
|
||||
</Marker>
|
||||
))}
|
||||
</ReactMapGL>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Map.propTypes = {
|
||||
selectedGasType: PropTypes.string.isRequired,
|
||||
showGasStation: PropTypes.func.isRequired,
|
||||
toastManager: PropTypes.shape({
|
||||
add: PropTypes.func,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
export default withToastManager(Map);
|
Loading…
Add table
Add a link
Reference in a new issue