diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 9b68301..66fa369 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,47 +1,14 @@ import React, { useState } from "react"; -import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet"; -import "leaflet/dist/leaflet.css"; -import MarkerClusterGroup from "react-leaflet-markercluster"; -import L, { Icon } from "leaflet"; - -import "leaflet.markercluster/dist/MarkerCluster.css"; -import "leaflet.markercluster/dist/MarkerCluster.Default.css"; - -// Icons -import trainStationIconURL from "../src/assets/icons/train-station.png"; -import trainIconURL from "../src/assets/icons/train.png"; - -// Fix marker icon issue with Leaflet -delete L.Icon.Default.prototype._getIconUrl; -L.Icon.Default.mergeOptions({ - iconUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png", - iconRetinaUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon-2x.png", - shadowUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png", -}); - -const icons = new Map(); -icons.set( - "IrishRailStation", - new Icon({ - iconUrl: trainStationIconURL, - iconSize: [24, 24], - }) -); - -icons.set( - "IrishRailTrain", - new Icon({ - iconUrl: trainIconURL, - iconSize: [24, 24], - }) -); +import Sidebar from "./components/Sidebar"; +import MapComponent from "./components/MapComponent"; +import LoadingOverlay from "./components/LoadingOverlay"; const TRANSIENT_DATA_API = "https://281bc6mcm5.execute-api.us-east-1.amazonaws.com/transient_data"; const PERMANENT_DATA_API = "https://a6y312dpuj.execute-api.us-east-1.amazonaws.com/permanent_data"; const dataSources = [ - { id: "IrishRailTrains", name: "Irish Rail Trains", url: TRANSIENT_DATA_API + "?objectType=IrishRailTrain" }, - { id: "IrishRailStations", name: "Irish Rail Stations", url: PERMANENT_DATA_API + "?objectType=IrishRailStation" }, + { id: "IrishRailTrains", name: "Irish Rail Trains", url: `${TRANSIENT_DATA_API}?objectType=IrishRailTrain` }, + { id: "IrishRailStations", name: "Irish Rail Stations", url: `${PERMANENT_DATA_API}?objectType=IrishRailStation` }, ]; function App() { @@ -50,95 +17,37 @@ function App() { const [loading, setLoading] = useState(false); const [clusteringEnabled, setClusteringEnabled] = useState(true); - const handleCheckboxChange = (id) => { - setSelectedSources((prev) => - prev.includes(id) ? prev.filter((source) => source !== id) : [...prev, id] - ); - }; - const fetchData = async () => { setLoading(true); - const newMarkers = []; - for (const source of dataSources) { - if (selectedSources.includes(source.id)) { - try { - const response = await fetch(source.url); - const data = await response.json(); - data.forEach((item) => { - newMarkers.push({ - coords: [item.latitude, item.longitude], - popup: item.objectType, - icon: item.objectType, - }); - }); - } catch (error) { - console.error(`Error fetching data from ${source.name}:`, error); - } - } + try { + const newMarkers = (await Promise.all( + dataSources + .filter(({ id }) => selectedSources.includes(id)) + .map(({ url }) => fetch(url).then((res) => res.json())) + )).flat().map(({ latitude, longitude, objectType }) => ({ + coords: [latitude, longitude], + popup: objectType, + icon: objectType, + })); + setMarkers(newMarkers); + } catch (error) { + console.error("Error fetching data:", error); } - setMarkers(newMarkers); setLoading(false); }; return (
- {loading && ( -
- Loading data... -
- )} -
-

Select Data Sources

- {dataSources.map((source) => ( -
- handleCheckboxChange(source.id)} - /> - -
- ))} -
- setClusteringEnabled(!clusteringEnabled)} - /> - -
- -
+ {loading && } +
- - - {clusteringEnabled ? ( - - {markers.map((marker, index) => ( - - {marker.popup} - - ))} - - ) : ( - markers.map((marker, index) => ( - - {marker.popup} - - )) - )} - +
); diff --git a/frontend/src/components/LoadingOverlay.jsx b/frontend/src/components/LoadingOverlay.jsx new file mode 100644 index 0000000..76ac3d4 --- /dev/null +++ b/frontend/src/components/LoadingOverlay.jsx @@ -0,0 +1,15 @@ +import React from "react"; + +const LoadingOverlay = () => ( +
+ Loading data... +
+); + +export default LoadingOverlay; diff --git a/frontend/src/components/MapComponent.jsx b/frontend/src/components/MapComponent.jsx new file mode 100644 index 0000000..f3b21b4 --- /dev/null +++ b/frontend/src/components/MapComponent.jsx @@ -0,0 +1,51 @@ +import React from "react"; +import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet"; +import MarkerClusterGroup from "react-leaflet-markercluster"; +import L, { Icon } from "leaflet"; +import "leaflet/dist/leaflet.css"; +import "leaflet.markercluster/dist/MarkerCluster.css"; +import "leaflet.markercluster/dist/MarkerCluster.Default.css"; + +import trainStationIconURL from "../assets/icons/train-station.png"; +import trainIconURL from "../assets/icons/train.png"; + +// Fix Leaflet marker icon issue +delete L.Icon.Default.prototype._getIconUrl; +L.Icon.Default.mergeOptions({ + iconUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png", + iconRetinaUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon-2x.png", + shadowUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png", +}); + +const icons = new Map([ + ["IrishRailStation", new Icon({ iconUrl: trainStationIconURL, iconSize: [24, 24] })], + ["IrishRailTrain", new Icon({ iconUrl: trainIconURL, iconSize: [24, 24] })], +]); + +const MapComponent = ({ markers, clusteringEnabled }) => { + return ( + + + {clusteringEnabled ? ( + + {markers.map(({ coords, popup, icon }, index) => ( + + {popup} + + ))} + + ) : ( + markers.map(({ coords, popup, icon }, index) => ( + + {popup} + + )) + )} + + ); +}; + +export default MapComponent; diff --git a/frontend/src/components/Sidebar.jsx b/frontend/src/components/Sidebar.jsx new file mode 100644 index 0000000..0db7211 --- /dev/null +++ b/frontend/src/components/Sidebar.jsx @@ -0,0 +1,59 @@ +import React, { useState } from "react"; + +const Sidebar = ({ selectedSources, setSelectedSources, clusteringEnabled, setClusteringEnabled, fetchData }) => { + const [isOpen, setIsOpen] = useState(true); + const dataSources = [ + { id: "IrishRailTrains", name: "Irish Rail Trains" }, + { id: "IrishRailStations", name: "Irish Rail Stations" }, + ]; + + return ( +
+ + {isOpen && ( +
+

Select Data Sources

+ {dataSources.map(({ id, name }) => ( +
+ + setSelectedSources((prev) => prev.includes(id) ? prev.filter((s) => s !== id) : [...prev, id]) + } + /> + +
+ ))} +
+ setClusteringEnabled(!clusteringEnabled)} + /> + +
+ +
+ )} +
+ ); +}; + +export default Sidebar; \ No newline at end of file