import React from 'react'
import "leaflet/dist/leaflet.css"
import { MapContainer, TileLayer, Marker, Popup, useMapEvents } from "react-leaflet"
import Navbar from "./Navbar"
import L from "leaflet";
import pin from "../images/pin.png";
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
const Home = () => {
const [pins, setPins] = useState([]);
const [username, setUsername] = useState('');
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [pinMode, setPinMode] = useState(false);
const [showPinForm, setShowPinForm] = useState(false);
const [currentPinLocation, setCurrentPinLocation] = useState(null);
const [pinDetails, setPinDetails] = useState({
pin_name: '',
description: '',
category: '',
public: false,
x: 0,
y: 0
});
// Fetch username and login status from localStorage
useEffect(() => {
const storedUsername = localStorage.getItem('username');
const storedLoginStatus = localStorage.getItem('login');
if (storedUsername && storedLoginStatus === 'true') {
setUsername(storedUsername);
setIsLoggedIn(true);
fetchPins(storedUsername);
} else {
fetchPins(null);
}
}, []);
// Fetch pins from server
const fetchPins = (username) => {
fetch(`https://jp-backend-kc80.onrender.com/api/pins`)
.then(response => response.json())
.then(data => {
// Filter pins: show public ones OR private ones belonging to the logged-in user
const filteredPins = data.filter(pin =>
pin.public || (username && pin.username === username)
);
const formattedPins = filteredPins.map(pin => ({
...pin,
lat: pin.y,
lng: pin.x
}));
setPins(formattedPins);
})
.catch(error => console.error("Error fetching pins:", error));
};
// Handle form input changes
const handleInputChange = (e) => {
const { name, value, type, checked } = e.target;
setPinDetails(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value
}));
};
// Function to handle pin placement
const MapClickHandler = () => {
useMapEvents({
click: (e) => {
const { lat, lng } = e.latlng;
setCurrentPinLocation({ lat, lng });
setPinDetails(prev => ({
...prev,
x: lng,
y: lat
}));
setShowPinForm(true);
},
});
return null;
};
// Function to save pin with details
const savePinWithDetails = () => {
if (!currentPinLocation || !username) return;
const newPin = {
...pinDetails,
username: username,
x: currentPinLocation.lng,
y: currentPinLocation.lat,
lat: currentPinLocation.lat,
lng: currentPinLocation.lng
};
// Send to backend using same URL format as GET
fetch(`https://jp-backend-kc80.onrender.com/api/pins?username=${username}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
pin_name: pinDetails.pin_name,
username: username,
x: currentPinLocation.lng,
y: currentPinLocation.lat,
description: pinDetails.description,
category: pinDetails.category,
public: pinDetails.public
}),
})
.then((response) => response.json())
.then((data) => {
console.log("Pin saved:", data);
// Update local state only after successful save
setPins((prevPins) => [...prevPins, newPin]);
setShowPinForm(false);
setPinDetails({
pin_name: '',
description: '',
category: '',
public: false,
x: 0,
y: 0
});
// Refresh pins from server
fetchPins(username);
})
.catch((error) => {
console.error("Error saving pin:", error);
alert("Failed to save pin. Please try again.");
});
};
// Custom pin icon
const pinIcon = new L.Icon({
iconUrl: pin,
iconSize: [24, 40],
iconAnchor: [15, 40],
popupAnchor: [0, -40],
});
return (
<div className="home-container">
<link rel="stylesheet" href="../App.css"></link>
<Navbar />
<div className="container-fluid p-4">
<div className="row">
<div className="col">
<MapContainer center={[43.7, -79.42]} zoom={10} id='map' className="border rounded shadow-sm">
<TileLayer
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{pinMode && <MapClickHandler />}
{pins.map((pin, index) => (
<Marker key={index} position={[pin.lat, pin.lng]} icon={pinIcon}>
<Popup>
<strong>{pin.pin_name || "Unnamed Pin"}</strong>
<br />
{pin.description || "No description available"}
<br />
<em>By: {pin.username}</em>
<br />
<small>Category: {pin.category || "Unknown"}</small>
{pin.public ? (
<div><small>Status: Public</small></div>
) : (
<div><small>Status: Private (only visible to you)</small></div>
)}
</Popup>
</Marker>
))}
</MapContainer>
</div>
<div className="col text-center">
{/* Dynamic Heading */}
<h1 className="display-1 mb-4">
{username ? `Welcome back, ${username}!` : "Welcome to JourneyPoint!"}
</h1>
<div className="row mt-5">
{/* First Section */}
<h2 className='display-5'>
{username ? "Ready for your next trip?" : "Explore New Destinations"}
</h2>
{username ? "" : "Discover new places by exploring pins from travelers around the world. Get inspiration for your next adventure by seeing where others have been and reading their experiences. Whether you're planning a trip or just dreaming of one, JourneyPoint helps you find your next destination!"}
{/* Second Section */}
<div className="row mt-5">
<h2 className='display-5'>
{username ? "" : "Share Your Experiences"}
</h2>
{username ? "" : "Share your travel stories and experiences with the JourneyPoint community. Pin your favorite locations, add descriptions, and connect with fellow travelers who have visited the same spots. Engage in real-time conversations and inspire others with your adventures!"}
</div>
<div className="row mt-5">
<h2 className='display-5'>
{username ? "" : "Completely Free"}
</h2>
{username ? "" : ""}
</div>
</div>
</div>
</div>
</div>
{/* Floating pin button */}
<button
className={`pin-button ${pinMode ? 'active' : ''}`}
onClick={() => setPinMode(!pinMode)}
disabled={!isLoggedIn}
title={!isLoggedIn ? "Please log in to add pins" : ""}
>
<img src={pin} alt="Pin" width="24" height="24" />
</button>
{/* Pin Details Form Modal */}
{showPinForm && (
<div className="pin-form-modal">
<div className="pin-form-content">
<h3>Add Pin Details</h3>
<div className="form-group">
<label>Pin Name:</label>
<input
type="text"
name="pin_name"
value={pinDetails.pin_name}
onChange={handleInputChange}
required
/>
</div>
<div className="form-group">
<label>Description:</label>
<textarea
name="description"
value={pinDetails.description}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label>Category:</label>
<select
name="category"
value={pinDetails.category}
onChange={handleInputChange}
required
>
<option value="">Select a category</option>
<option value="Landmark">Landmark</option>
<option value="Restaurant">Restaurant</option>
<option value="Nature">Nature</option>
<option value="Event">Event</option>
<option value="Other">Other</option>
</select>
</div>
<div className="form-group checkbox">
<label>
<input
type="checkbox"
name="public"
checked={pinDetails.public}
onChange={handleInputChange}
/>
Public Pin
</label>
</div>
<div className="form-group">
<label>Coordinates:</label>
<p>X (Longitude): {pinDetails.x.toFixed(5)}</p>
<p>Y (Latitude): {pinDetails.y.toFixed(5)}</p>
</div>
<div className="form-actions">
<button onClick={() => setShowPinForm(false)}>Cancel</button>
<button onClick={savePinWithDetails}>Save Pin</button>
</div>
</div>
</div>
)}
{/* Styles */}
<style>
{`
#map {
position: relative;
border: 5px solid #000000 !important;
border-radius: 20px;
box-shadow: 0 0 15px rgba(0, 0, 0, 0.1);
overflow: hidden; /* Ensures rounded corners work properly */
}
.pin-button {
position: absolute;
bottom: 50px;
left: 45px;
background-color: ${pinMode ? '#154475' : '#007bff'};
color: white;
border: none;
border-radius: 50%;
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
transition: background-color 0.3s;
}
.pin-button:hover {
background-color: ${pinMode ? '#0d2a47' : '#0056b3'};
}
.pin-button:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
.pin-button img {
width: 20px;
height: 35px;
}
.pin-form-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0,0,0,0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 2000;
}
.pin-form-content {
background-color: white;
padding: 20px;
border-radius: 8px;
width: 400px;
max-width: 90%;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.form-group input,
.form-group textarea,
.form-group select {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.form-group textarea {
height: 80px;
}
.form-group.checkbox {
display: flex;
align-items: center;
}
.form-group.checkbox input {
width: auto;
margin-right: 10px;
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 20px;
}
.form-actions button {
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.form-actions button:last-child {
background-color: #007bff;
color: white;
}
.form-actions button:last-child:hover {
background-color: #0056b3;
}
`}
</style>
</div>
);
}
export default Home;