Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 39 additions & 15 deletions roam-next-app/src/app/components/Map/MapComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,57 @@ import 'leaflet/dist/leaflet.css';
import L from 'leaflet';
import { useEffect, useState } from 'react';

// Fix for missing default icon
const iconRetinaUrl = 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon-2x.png';
const iconUrl = 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png';
const shadowUrl = 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png';

// Component to update the map's center
const ChangeMapCenter = ({ center }: { center: [number, number] }) => {
const map = useMap();
useEffect(() => {
map.setView(center); // Dynamically change map view when the center changes
map.setView(center);
}, [center, map]);

return null;
};

const MapComponent = () => {
interface MapComponentProps {
restaurants: { id: string; name: string; coordinates: { latitude: number; longitude: number; }; }[];
}

const MapComponent: React.FC<MapComponentProps> = ({ restaurants }) => {
const [currentLocation, setCurrentLocation] = useState<[number, number] | null>(null);

useEffect(() => {
// Override default icon settings
L.Icon.Default.mergeOptions({
iconRetinaUrl,
iconUrl,
shadowUrl,
});

// Get the user's current location
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((position) => {
const { latitude, longitude } = position.coords;
setCurrentLocation([latitude, longitude]); // Set the user's current location
},
(error) => {
setCurrentLocation([latitude, longitude]);
}, (error) => {
console.error("Geolocation error:", error);
});
} else {
console.error("Geolocation is not supported by this browser.");
}
}, []);

// Custom blue dot using Tailwind CSS
const blueDotIcon = L.divIcon({
className: 'relative flex items-center justify-center',
html: `
<div class="relative flex items-center justify-center">
<div class="w-3 h-3 bg-blue-500 rounded-full border-2 border-white animate-bounce shadow-lg"></div>
</div>
`,
iconSize: [12, 12], // Size of the blue dot
iconAnchor: [6, 6], // Center the dot
});

if (!currentLocation) return <div>Loading location...</div>;

return (
Expand All @@ -54,13 +65,26 @@ const MapComponent = () => {
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
/>
{/* Update the map center when the location changes */}
<ChangeMapCenter center={currentLocation} />
<Marker position={currentLocation}>
<Popup>
You are here!
</Popup>
</Marker>

{/* Current Location Blue Dot Marker */}
{currentLocation && (
<Marker position={currentLocation} icon={blueDotIcon}>
<Popup>You are here!</Popup>
</Marker>
)}

{/* Add markers for each restaurant */}
{restaurants.map((restaurant) => (
<Marker
key={restaurant.id}
position={[restaurant.coordinates.latitude, restaurant.coordinates.longitude]}
>
<Popup>
<strong>{restaurant.name}</strong>
</Popup>
</Marker>
))}
</MapContainer>
</div>
);
Expand Down
61 changes: 38 additions & 23 deletions roam-next-app/src/app/components/Results/Results.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,32 @@ type Restaurant = {
id: string;
name: string;
location: {
address1: string; // street address
city: string; // city name
address1: string;
city: string;
};
coordinates: {
latitude: number;
longitude: number;
};
rating: number;
distance: number; // Add distance to the Restaurant type
review_count: number; // Add review_count to the Restaurant type
distance: number;
review_count: number;
};

interface ResultsProps {
searchTerm: string; // Define the type for searchTerm
searchTerm: string;
setRestaurants: React.Dispatch<React.SetStateAction<Restaurant[]>>;
}

const Results: React.FC<ResultsProps> = ({ searchTerm }) => {
const [restaurants, setRestaurants] = useState<Restaurant[]>([]);
const Results: React.FC<ResultsProps> = ({ searchTerm, setRestaurants }) => {
const [restaurants, setLocalRestaurants] = useState<Restaurant[]>([]);
const [currentLocation, setCurrentLocation] = useState<[number, number] | null>(null);

useEffect(() => {
// Get the user's current location
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((position) => {
const { latitude, longitude } = position.coords;
setCurrentLocation([latitude, longitude]);
console.log("Current Location:", latitude, longitude);
}, (error) => {
console.error("Geolocation error:", error);
});
Expand All @@ -46,8 +45,6 @@ const Results: React.FC<ResultsProps> = ({ searchTerm }) => {
const fetchRestaurants = async () => {
if (!currentLocation) return;

console.log("Fetching restaurants for location:", currentLocation);

const apiKey = process.env.NEXT_PUBLIC_YELP_API_KEY;
const config = {
headers: {
Expand All @@ -61,19 +58,40 @@ const Results: React.FC<ResultsProps> = ({ searchTerm }) => {
};

try {
const response = await axios.get(
'https://api.yelp.com/v3/businesses/search',
config
);
const response = await axios.get('https://api.yelp.com/v3/businesses/search', config);

// Calculate a relevance score based on how closely the restaurant name matches the search term
const calculateRelevance = (restaurantName: string, searchTerm: string): number => {
const lowerCaseName = restaurantName.toLowerCase();
const lowerCaseSearch = searchTerm.toLowerCase();

if (lowerCaseName.includes(lowerCaseSearch)) {
// Higher relevance score if the name contains the search term
return 2;
} else if (lowerCaseName.split(' ').some(word => word.startsWith(lowerCaseSearch))) {
// Medium relevance score if some word starts with the search term
return 1;
} else {
return 0;
}
};

const sortedRestaurants = response.data.businesses.sort((a: Restaurant, b: Restaurant) => {
// Sort by rating first, then by number of reviews
const relevanceA = calculateRelevance(a.name, searchTerm);
const relevanceB = calculateRelevance(b.name, searchTerm);

// Primary sort by relevance, then rating, then review count
if (relevanceB !== relevanceA) {
return relevanceB - relevanceA;
}
if (b.rating !== a.rating) {
return b.rating - a.rating; // Higher rating first
return b.rating - a.rating;
}
return b.review_count - a.review_count; // Higher review count first
return b.review_count - a.review_count;
});

setLocalRestaurants(sortedRestaurants);
setRestaurants(sortedRestaurants);
console.log("Restaurants fetched and sorted:", sortedRestaurants);
} catch (error) {
console.error('Error fetching restaurants:', error);
}
Expand Down Expand Up @@ -111,10 +129,7 @@ const Results: React.FC<ResultsProps> = ({ searchTerm }) => {
{restaurants.length ? (
<ul className="space-y-4">
{restaurants.map((restaurant) => {
// Convert distance from meters to miles
const distanceInMiles = restaurant.distance * 0.000621371;

console.log(`Distance to ${restaurant.name}:`, distanceInMiles.toFixed(2));
const distanceInMiles = restaurant.distance * 0.000621371;

return (
<li
Expand Down
25 changes: 21 additions & 4 deletions roam-next-app/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,37 @@ import Results from "./components/Results/Results";
import { useState } from "react";
import Navbar from "./components/navbar/Navbar";

type Restaurant = {
id: string;
name: string;
location: {
address1: string;
city: string;
};
coordinates: {
latitude: number;
longitude: number;
};
rating: number;
distance: number;
review_count: number;
};

export default function Home() {
const [searchTerm, setSearchTerm] = useState("");
const [restaurants, setRestaurants] = useState<Restaurant[]>([]); // Use the Restaurant type

return (
<>
<Navbar setSearchTerm={setSearchTerm} />
<main className="flex flex-row w-screen h-[calc(100vh-56px)]"> {/* Adjust height to account for navbar */}
<main className="flex flex-row w-screen h-[calc(100vh-56px)]">
<div className="flex flex-col w-1/3 h-full">
<div className="overflow-y-auto h-full"> {/* Allow results to scroll vertically */}
<Results searchTerm={searchTerm} />
<div className="overflow-y-auto h-full">
<Results searchTerm={searchTerm} setRestaurants={setRestaurants} /> {/* Pass setRestaurants */}
</div>
</div>
<div className="flex w-2/3 h-full">
<MapComponent />
<MapComponent restaurants={restaurants} /> {/* Pass restaurants to MapComponent */}
</div>
</main>
</>
Expand Down