[frontend]: Add 'Favourite' functionality

This commit is contained in:
2025-03-14 21:11:15 +00:00
parent 2703ff7dc8
commit 4bec4459a9
3 changed files with 62 additions and 51 deletions

View File

@ -3,6 +3,7 @@
- Add 'favourite' button to train station pop-up. - Add 'favourite' button to train station pop-up.
- Create `IrishRailTrainPopup` with favourite button. - Create `IrishRailTrainPopup` with favourite button.
- Create `BusPopup` with favourite button. - Create `BusPopup` with favourite button.
- Create `BusStopPopup` with favourite button.
## Wed 12 Mar 2025 ## Wed 12 Mar 2025
- Fix search input freezing when there's a large number of data. - Fix search input freezing when there's a large number of data.

View File

@ -13,6 +13,7 @@ import LuasPopup from "./components/LuasPopup";
import TrainStationPopup from "./components/TrainStationPopup"; import TrainStationPopup from "./components/TrainStationPopup";
import IrishRailTrainPopup from "./components/IrishRailTrainPopup"; import IrishRailTrainPopup from "./components/IrishRailTrainPopup";
import BusPopup from "./components/BusPopup.jsx"; 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 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 PERMANENT_DATA_API = "https://a6y312dpuj.execute-api.us-east-1.amazonaws.com/permanent_data";
@ -35,6 +36,7 @@ const defaultFavourites = {
function App() { function App() {
const [favourites, setFavourites] = useState(defaultFavourites); const [favourites, setFavourites] = useState(defaultFavourites);
const [showFaovouritesOnly, setShowFavouritesOnly] = useState(false);
useEffect(() => { useEffect(() => {
try { try {
@ -270,7 +272,8 @@ function App() {
((showRunning && trainStatus == "Running") || (showNotYetRunning && trainStatus == "Not yet running") || (showTerminated && trainStatus == "Terminated")) && ((showRunning && trainStatus == "Running") || (showNotYetRunning && trainStatus == "Not yet running") || (showTerminated && trainStatus == "Terminated")) &&
((trainStatus == "Running" && showEarly && punctualityStr == "early") || (trainStatus == "Running" && showOnTime && punctualityStr == "On time") || (trainStatus == "Running" && showLate && punctualityStr == "late") ((trainStatus == "Running" && showEarly && punctualityStr == "early") || (trainStatus == "Running" && showOnTime && punctualityStr == "On time") || (trainStatus == "Running" && showLate && punctualityStr == "late")
|| (trainStatus == "Not yet running" && showNotYetRunning) || (trainStatus == "Terminated" && showTerminated))) && || (trainStatus == "Not yet running" && showNotYetRunning) || (trainStatus == "Terminated" && showTerminated))) &&
(numberInputValue && userLocationAvailable ? haversineDistance(userLocation, [item.latitude, item.longitude]) < numberInputValue : true); (numberInputValue && userLocationAvailable ? haversineDistance(userLocation, [item.latitude, item.longitude]) < numberInputValue : true) &&
(showFaovouritesOnly ? favourites.IrishRailTrain.includes(item.trainCode) : true);
break; break;
@ -287,7 +290,8 @@ function App() {
markerText = item.trainStationCode + " " + item.trainStationDesc; markerText = item.trainStationCode + " " + item.trainStationDesc;
display = (item.latitude !== "0" && item.longitude !== "0") && display = (item.latitude !== "0" && item.longitude !== "0") &&
(numberInputValue && userLocationAvailable ? haversineDistance(userLocation, [item.latitude, item.longitude]) < numberInputValue : true); (numberInputValue && userLocationAvailable ? haversineDistance(userLocation, [item.latitude, item.longitude]) < numberInputValue : true) &&
(showFaovouritesOnly ? favourites.IrishRailStation.includes(item.trainStationCode) : true);
break; break;
@ -300,40 +304,30 @@ function App() {
toggleFavourite={toggleFavourite} toggleFavourite={toggleFavourite}
favourites={favourites} favourites={favourites}
/> />
// <div>
// <h3>{objectTitle}</h3>
// <ul>
// <li><b>Bus ID:</b> {item.busID}</li>
// <li><b>Bus Route ID:</b> {item.busRoute}</li>
// <li><b>Bus Route Short Name:</b> {item.busRouteShortName}</li>
// <li><b>Bus Route Long Name:</b> {item.busRouteLongName}</li>
// <li><b>Agency: </b> {item.busRouteAgencyName}</li>
// </ul>
// </div>
); );
markerText = item.busRouteAgencyName + " " + item.busRouteShortName + " " + item.busRouteLongName; markerText = item.busRouteAgencyName + " " + item.busRouteShortName + " " + item.busRouteLongName;
display = (item.latitude !== "0" && item.longitude !== "0") && display = (item.latitude !== "0" && item.longitude !== "0") &&
(numberInputValue && userLocationAvailable ? haversineDistance(userLocation, [item.latitude, item.longitude]) < numberInputValue : true); (numberInputValue && userLocationAvailable ? haversineDistance(userLocation, [item.latitude, item.longitude]) < numberInputValue : true) &&
(showFaovouritesOnly ? favourites.Bus.includes(item.busRoute) : true);
break; break;
case "BusStop": case "BusStop":
objectTitle = item.busStopName + " Bus Stop"; objectTitle = item.busStopName + " Bus Stop";
popupContent = ( popupContent = (
<div> <BusStopPopup
<h3>{objectTitle}</h3> item={item}
<ul> objectTitle={objectTitle}
<li><b>Bus Stop ID:</b> {item.busStopID}</li> toggleFavourite={toggleFavourite}
<li><b>Bus Stop Name:</b> {item.busStopName}</li> favourites={favourites}
<li><b>Bus Stop Code:</b> {item.busStopCode || "N/A"}</li> />
</ul>
</div>
); );
markerText = item.busStopName; markerText = item.busStopName;
display = (item.latitude !== "0" && item.longitude !== "0") && display = (item.latitude !== "0" && item.longitude !== "0") &&
(numberInputValue && userLocationAvailable ? haversineDistance(userLocation, [item.latitude, item.longitude]) < numberInputValue : true); (numberInputValue && userLocationAvailable ? haversineDistance(userLocation, [item.latitude, item.longitude]) < numberInputValue : true) &&
(showFaovouritesOnly ? favourites.BusStop.includes(item.busStopID) : true);
break; break;
@ -370,7 +364,8 @@ function App() {
(showEnabled && item.luasStopIsEnabled === "1" || showDisabled && item.luasStopIsEnabled === "0") && (showEnabled && item.luasStopIsEnabled === "1" || showDisabled && item.luasStopIsEnabled === "0") &&
(!showCycleAndRide || (showCycleAndRide && item.luasStopIsCycleAndRide === "1")) && (!showCycleAndRide || (showCycleAndRide && item.luasStopIsCycleAndRide === "1")) &&
(!showParkAndRide || (showParkAndRide && item.luasStopIsParkAndRide === "1")) && (!showParkAndRide || (showParkAndRide && item.luasStopIsParkAndRide === "1")) &&
(numberInputValue && userLocationAvailable ? haversineDistance(userLocation, [item.latitude, item.longitude]) < numberInputValue : true) (numberInputValue && userLocationAvailable ? haversineDistance(userLocation, [item.latitude, item.longitude]) < numberInputValue : true) &&
(showFaovouritesOnly ? favourites.LuasStop.includes(item.luasStopID) : true)
); );
break; break;
@ -469,6 +464,8 @@ function App() {
setClusteringEnabled={setClusteringEnabled} setClusteringEnabled={setClusteringEnabled}
fetchData={fetchData} fetchData={fetchData}
userLocationAvailable={userLocationAvailable} userLocationAvailable={userLocationAvailable}
showFavouritesOnly={showFaovouritesOnly}
setShowFavouritesOnly={setShowFavouritesOnly}
/> />
<div style={{ flex: 1 }}> <div style={{ flex: 1 }}>
<MapComponent <MapComponent

View File

@ -131,7 +131,7 @@ const CheckboxItem = ({ item, selectedSources, setSelectedSources, enabledSource
); );
}; };
const Sidebar = ({ selectedSources, setSelectedSources, clusteringEnabled, setClusteringEnabled, fetchData, userLocationAvailable }) => { const Sidebar = ({ selectedSources, setSelectedSources, clusteringEnabled, setClusteringEnabled, fetchData, userLocationAvailable, showFavouritesOnly, setShowFavouritesOnly }) => {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [enabledSources, setEnabledSources] = useState([]); // New state to track enabled sources const [enabledSources, setEnabledSources] = useState([]); // New state to track enabled sources
const [numberInputValue, setNumberInputValue] = useState(""); // State to manage number input value const [numberInputValue, setNumberInputValue] = useState(""); // State to manage number input value
@ -183,41 +183,54 @@ const Sidebar = ({ selectedSources, setSelectedSources, clusteringEnabled, setCl
setEnabledSources={setEnabledSources} setEnabledSources={setEnabledSources}
/> />
))} ))}
<div style={{marginTop: "10px", display: "flex", alignItems: "center", gap: "8px"}}>
<div style={{marginTop: "10px", display: "flex", alignItems: "center", gap: "4px"}}>
<input <input
type="checkbox" type="checkbox"
id="toggleClustering" id="showFavouritesOnly"
checked={clusteringEnabled} checked={showFavouritesOnly}
onChange={() => setClusteringEnabled(!clusteringEnabled)} onChange={() => setShowFavouritesOnly(!showFavouritesOnly)}
/> />
<label htmlFor="toggleClustering">Cluster overlapping icons</label> <label htmlFor={"showFavouritesOnly"}>Show Favourites Only</label>
</div> </div>
{userLocationAvailable && (
<div style={{marginTop: "10px", display: "flex", alignItems: "center", gap: "8px"}}> <div style={{marginTop: "10px", display: "flex", alignItems: "center", gap: "4px"}}>
<label htmlFor="numberInput" style={{maxWidth: "40%"}}>Within KM:</label>
<input <input
type="number" type="checkbox"
id="numberInput" id="toggleClustering"
value={numberInputValue} checked={clusteringEnabled}
onChange={(e) => setNumberInputValue(e.target.value)} onChange={() => setClusteringEnabled(!clusteringEnabled)}
style={{maxWidth: "40%"}}
/> />
<label htmlFor="toggleClustering">Cluster overlapping icons</label>
</div> </div>
{userLocationAvailable && (
<div style={{marginTop: "10px", display: "flex", alignItems: "center", gap: "8px"}}>
<label htmlFor="numberInput" style={{maxWidth: "40%"}}>Within KM:</label>
<input
type="number"
id="numberInput"
value={numberInputValue}
onChange={(e) => setNumberInputValue(e.target.value)}
style={{maxWidth: "40%"}}
/>
</div>
)}
<button onClick={handleSubmit} style={{marginTop: "10px", color: "white"}}>Submit</button>
</div>
)} )}
<button onClick={handleSubmit} style={{marginTop: "10px", color: "white"}}>Submit</button>
</div> </div>
)} );
</div> };
);
};
Sidebar.propTypes = { Sidebar.propTypes = {
selectedSources: PropTypes.array.isRequired, selectedSources: PropTypes.array.isRequired,
setSelectedSources: PropTypes.func.isRequired, setSelectedSources: PropTypes.func.isRequired,
clusteringEnabled: PropTypes.bool.isRequired, clusteringEnabled: PropTypes.bool.isRequired,
setClusteringEnabled: PropTypes.func.isRequired, setClusteringEnabled: PropTypes.func.isRequired,
fetchData: PropTypes.func.isRequired, fetchData: PropTypes.func.isRequired,
userLocationAvailable: PropTypes.bool.isRequired, userLocationAvailable: PropTypes.bool.isRequired,
}; showFavouritesOnly: PropTypes.bool.isRequired,
setShowFavouritesOnly: PropTypes.func.isRequired,
};
export default Sidebar export default Sidebar