import React, { useState, useEffect, useMemo, useRef } from "react";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import Cookies from "js-cookie";
import Navbar from "./components/Navbar";
import Statistics from "./components/Statistics.jsx";
import Sidebar from "./components/Sidebar";
import MapComponent from "./components/MapComponent";
import LoadingOverlay from "./components/LoadingOverlay";
import LuasPopup from "./components/LuasPopup";
import TrainStationPopup from "./components/TrainStationPopup";
import IrishRailTrainPopup from "./components/IrishRailTrainPopup";
import BusPopup from "./components/BusPopup.jsx";
import BusStopPopup from "./components/BusStopPopup.jsx";
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: "irish-rail-trains", name: "Irish Rail Trains", api: "transient", objectType: "IrishRailTrain" },
{ id: "irish-rail-stations", name: "Irish Rail Stations", api: "permanent", objectType: "IrishRailStation" },
{ id: "luas-stops", name: "Luas Stops", api: "permanent", objectType: "LuasStop" },
{ id: "bus-stops", name: "Bus Stops", api: "permanent", objectType: "BusStop" },
{ id: "buses", name: "Buses", api: "transient", objectType: "Bus" },
];
const defaultFavourites = {
IrishRailTrain: [],
Bus: [],
LuasStop: [],
BusStop: [],
IrishRailStation: []
};
function App() {
const [favourites, setFavourites] = useState(defaultFavourites);
const [showFaovouritesOnly, setShowFavouritesOnly] = useState(false);
useEffect(() => {
try {
const savedFavourites = Cookies.get("favourites");
if (savedFavourites) {
const parsedFavourites = JSON.parse(savedFavourites);
setFavourites({ ...defaultFavourites, ...parsedFavourites });
}
} catch (error) {
console.error("Error loading favourites from cookies:", error);
setFavourites(defaultFavourites);
}
}, []);
const toggleFavourite = (type, id) => {
setFavourites((prev) => {
const updatedFavourites = {
...defaultFavourites,
...prev,
[type]: prev[type]?.includes(id)
? prev[type].filter((fav) => fav !== id)
: [...(prev[type] || []), id]
};
Cookies.set("favourites", JSON.stringify(updatedFavourites), { expires: 365 });
return updatedFavourites;
});
};
const [selectedSources, setSelectedSources] = useState([]);
const [markers, setMarkers] = useState([]);
const [loading, setLoading] = useState(false);
const [clusteringEnabled, setClusteringEnabled] = useState(true);
const [searchTerm, setSearchTerm] = useState("");
const debounceTimeout = useRef(null);
const [filteredMarkers, setFilteredMarkers] = useState([]);
const [numMarkers, setNumMarkers] = useState(0);
const [userLocation, setUserLocation] = useState(null);
const [userLocationAvailable, setUserLocationAvailable] = useState(false);
useEffect(() => {
if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition(
(position) => {
setUserLocation([position.coords.latitude, position.coords.longitude]);
setUserLocationAvailable(true);
},
(error) => {
console.error("Error getting location:", error);
setUserLocation([53.4494762, -7.5029786]);
setUserLocationAvailable(false);
},
{
enableHighAccuracy: true,
timeout: 2000,
maximumAge: 0
}
);
} else {
setUserLocation([53.4494762, -7.5029786]);
setUserLocationAvailable(false);
}
}, []);
const handleSearchChange = (e) => {
const value = e.target.value;
if (debounceTimeout.current) {
clearTimeout(debounceTimeout.current);
}
debounceTimeout.current = setTimeout(() => {
setSearchTerm(value);
}, 300);
};
// calculate distance between 2 points
function haversineDistance(coord1, coord2) {
const R = 6371; // Radius of the Earth in km
const toRad = (angle) => angle * (Math.PI / 180);
const [lat1, lon1] = coord1;
const [lat2, lon2] = coord2;
const dLat = toRad(lat2 - lat1);
const dLon = toRad(lon2 - lon1);
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c; // Distance in km
}
const fetchData = async (enabledSources, numberInputValue) => {
setLoading(true);
try {
const transientTypes = dataSources.filter(({ id, api }) => enabledSources.includes(id) && api === "transient").map(({ objectType }) => objectType);
const permanentTypes = dataSources.filter(({ id, api }) => enabledSources.includes(id) && api === "permanent").map(({ objectType }) => objectType);
const requests = [];
if (transientTypes.length) requests.push(fetch(`${TRANSIENT_DATA_API}?objectType=${transientTypes.join(",")}`).then(res => res.json()));
if (permanentTypes.length) requests.push(fetch(`${PERMANENT_DATA_API}?objectType=${permanentTypes.join(",")}`).then(res => res.json()));
const responses = await Promise.all(requests);
const newMarkers = responses.flat()
.map((item) => {
const showMainline = enabledSources.includes("mainline");
const showSuburban = enabledSources.includes("suburban");
const showDart = enabledSources.includes("dart");
const showRunning = enabledSources.includes("running");
const showNotYetRunning = enabledSources.includes("not-yet-running");
const showTerminated = enabledSources.includes("terminated");
const showEarly = enabledSources.includes("early");
const showOnTime = enabledSources.includes("on-time");
const showLate = enabledSources.includes("late");
const showRedLine = enabledSources.includes("red-line");
const showGreenLine = enabledSources.includes("green-line");
const showParkAndRide = enabledSources.includes("park-and-ride");
const showCycleAndRide = enabledSources.includes("cycle-and-ride");
const showEnabled = enabledSources.includes("enabled");
const showDisabled = enabledSources.includes("disabled");
let icon = item.objectType;
let popupContent;
let objectTitle;
let display = false;
let markerText = "";
switch (item.objectType) {
case "IrishRailTrain":
objectTitle = "Irish Rail Train: " + item.trainCode;
icon = item.trainTypeFull + item.trainPunctualityStatus;
if (item.trainStatusFull == "Terminated" || item.trainStatusFull == "Not yet running") {
icon = item.trainTypeFull + "NotRunning";
}
popupContent = (