import React, { useState, useRef, useEffect } from 'react'; import { Search, MapPin, X } from 'lucide-react'; interface SearchResult { place_id: number; display_name: string; lon: string; lat: string; type: string; } export default function FloatingSearchBar({ setEndLoc, setEndQuery, viewState }: { setEndLoc?: (loc: [number, number] | null) => void; setEndQuery?: (query: string) => void; viewState?: { longitude: number, latitude: number } }) { const [query, setQuery] = useState(''); const [results, setResults] = useState([]); const [isOpen, setIsOpen] = useState(false); const [isLoading, setIsLoading] = useState(false); const inputRef = useRef(null); const containerRef = useRef(null); const searchPlaces = async (searchQuery: string) => { if (!searchQuery || searchQuery.length < 2) { setResults([]); setIsOpen(false); return; } setIsLoading(true); setIsOpen(true); try { let url = `/api/geocode?q=${encodeURIComponent(searchQuery)}`; if (viewState?.latitude && viewState?.longitude) { url += `&lat=${viewState.latitude}&lon=${viewState.longitude}`; } const response = await fetch(url); if (!response.ok) throw new Error('Search failed'); const data = await response.json(); setResults(Array.isArray(data) ? data : []); } catch (error) { console.error('Search error:', error); setResults([]); } finally { setIsLoading(false); } }; useEffect(() => { const timer = setTimeout(() => { if (query.length >= 2) { searchPlaces(query); } else { setResults([]); setIsOpen(false); } }, 300); return () => clearTimeout(timer); }, [query]); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (containerRef.current && !containerRef.current.contains(event.target as Node)) { setIsOpen(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); const handleSelectResult = (result: SearchResult) => { const lat = parseFloat(result.lat); const lon = parseFloat(result.lon); if (setEndLoc) { setEndLoc([lon, lat]); } if (setEndQuery) { setEndQuery(result.display_name); } setQuery(result.display_name); setIsOpen(false); setResults([]); // Save to recent searches try { const recents = JSON.parse(localStorage.getItem('recentSearches') || '[]'); const newRecent = { id: Date.now(), name: result.display_name.split(',')[0], address: result.display_name, loc: [lon, lat] as [number, number], timestamp: Date.now() }; const filtered = recents.filter((r: any) => r.address !== result.display_name); const updated = [newRecent, ...filtered].slice(0, 10); localStorage.setItem('recentSearches', JSON.stringify(updated)); window.dispatchEvent(new Event('recentsUpdated')); } catch (e) { console.error('Failed to save recent:', e); } }; const handleClear = () => { setQuery(''); setResults([]); setIsOpen(false); inputRef.current?.focus(); }; const categories = [ { label: 'ATM', query: 'ATM' }, { label: 'Hospital', query: 'hospital' }, { label: 'Petrol', query: 'petrol pump' }, { label: 'Parking', query: 'parking' }, { label: 'Police', query: 'police station' }, ]; return (
{/* Search Input */}
setQuery(e.target.value)} onFocus={() => results.length > 0 && setIsOpen(true)} placeholder="Search city, address, ATM, hospital..." className="flex-1 bg-transparent outline-none text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400" /> {query && ( )} {isLoading && (
)}
{/* Results Dropdown */} {isOpen && (
{results.length > 0 ? (
{results.map((result, index) => ( ))}
) : query.length >= 2 && !isLoading ? (

No results found for "{query}"

Try different keywords

) : isLoading ? (

Searching...

) : null}
)} {/* Category Buttons */}
{categories.map((cat) => ( ))}
); }