import { useState, useCallback, useEffect } from 'react'; import { useDebounce } from '@/hooks/useDebounce'; import { supabase } from '@/integrations/supabase/client'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { Card } from '@/components/ui/card'; import { MapPin, Loader2, X } from 'lucide-react'; import { ParkLocationMap } from '@/components/maps/ParkLocationMap'; import { logger } from '@/lib/logger'; interface LocationResult { place_id: number; display_name: string; lat: string; lon: string; address: { city?: string; town?: string; village?: string; state?: string; country?: string; postcode?: string; }; } interface SelectedLocation { name: string; city?: string; state_province?: string; country: string; postal_code?: string; latitude: number; longitude: number; timezone?: string; display_name: string; // Full OSM display name for reference } interface LocationSearchProps { onLocationSelect: (location: SelectedLocation) => void; initialLocationId?: string; className?: string; } export function LocationSearch({ onLocationSelect, initialLocationId, className }: LocationSearchProps): React.JSX.Element { const [searchQuery, setSearchQuery] = useState(''); const [results, setResults] = useState([]); const [isSearching, setIsSearching] = useState(false); const [searchError, setSearchError] = useState(null); const [selectedLocation, setSelectedLocation] = useState(null); const [showResults, setShowResults] = useState(false); const debouncedSearch = useDebounce(searchQuery, 500); // Load initial location if editing useEffect(() => { if (initialLocationId) { void loadInitialLocation(initialLocationId); } }, [initialLocationId]); const loadInitialLocation = async (locationId: string): Promise => { const { data, error } = await supabase .from('locations') .select('id, name, city, state_province, country, postal_code, latitude, longitude, timezone') .eq('id', locationId) .maybeSingle(); if (data && !error) { setSelectedLocation({ name: data.name, city: data.city || undefined, state_province: data.state_province || undefined, country: data.country, postal_code: data.postal_code || undefined, latitude: parseFloat(data.latitude?.toString() || '0'), longitude: parseFloat(data.longitude?.toString() || '0'), timezone: data.timezone || undefined, display_name: data.name, // Use name as display for existing locations }); } }; const searchLocations = useCallback(async (query: string) => { if (!query || query.length < 3) { setResults([]); setSearchError(null); return; } setIsSearching(true); setSearchError(null); try { const response = await fetch( `https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(query)}&addressdetails=1&limit=5`, { headers: { 'User-Agent': 'ThemeParkDatabase/1.0', }, } ); // Check if response is OK and content-type is JSON if (!response.ok) { const errorMsg = `Location search failed (${response.status}). Please try again.`; console.error('OpenStreetMap API error:', response.status); setSearchError(errorMsg); setResults([]); setShowResults(false); return; } const contentType = response.headers.get('content-type'); if (!contentType || !contentType.includes('application/json')) { const errorMsg = 'Invalid response from location service. Please try again.'; console.error('Invalid response format from OpenStreetMap'); setSearchError(errorMsg); setResults([]); setShowResults(false); return; } const data = await response.json() as LocationResult[]; setResults(data); setShowResults(true); setSearchError(null); } catch { logger.error('Location search failed', { query: searchQuery }); setSearchError('Failed to search locations. Please check your connection.'); setResults([]); setShowResults(false); } finally { setIsSearching(false); } }, []); useEffect(() => { if (debouncedSearch) { void searchLocations(debouncedSearch); } else { setResults([]); setShowResults(false); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [debouncedSearch]); const handleSelectResult = (result: LocationResult): void => { const latitude = parseFloat(result.lat); const longitude = parseFloat(result.lon); // Safely access address properties with fallback const address = result.address || {}; const city = address.city || address.town || address.village; const state = address.state || ''; const country = address.country || 'Unknown'; const locationName = city ? `${city}, ${state} ${country}`.trim() : result.display_name; // Build location data object (no database operations) const locationData: SelectedLocation = { name: locationName, city: city || undefined, state_province: state || undefined, country: country, postal_code: address.postcode || undefined, latitude, longitude, timezone: undefined, // Will be set by server during approval if needed display_name: result.display_name, }; setSelectedLocation(locationData); setSearchQuery(''); setResults([]); setShowResults(false); onLocationSelect(locationData); }; const handleClear = (): void => { setSelectedLocation(null); setSearchQuery(''); setResults([]); setShowResults(false); }; return (
{!selectedLocation ? (
setSearchQuery(e.target.value)} className="pl-10" /> {isSearching && ( )}
{searchError && (
{searchError}
)} {showResults && results.length > 0 && (
{results.map((result) => ( ))}
)} {showResults && results.length === 0 && !isSearching && !searchError && (
No locations found. Try a different search term.
)}
) : (

{selectedLocation.name}

{selectedLocation.city &&

City: {selectedLocation.city}

} {selectedLocation.state_province &&

State/Province: {selectedLocation.state_province}

}

Country: {selectedLocation.country}

{selectedLocation.postal_code &&

Postal Code: {selectedLocation.postal_code}

}

Coordinates: {selectedLocation.latitude.toFixed(6)}, {selectedLocation.longitude.toFixed(6)}

)}
); }