import { useState, useCallback, useEffect } from 'react'; import { useDebounce } from '@/hooks/useDebounce'; import { supabase } from '@/lib/supabaseClient'; 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 { handleNonCriticalError } from '@/lib/errorHandler'; interface LocationResult { place_id: number; display_name: string; lat: string; lon: string; address: { house_number?: string; road?: string; city?: string; town?: string; village?: string; municipality?: string; state?: string; province?: string; state_district?: string; county?: string; region?: string; territory?: string; country?: string; country_code?: string; postcode?: string; }; } interface SelectedLocation { name: string; street_address?: 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, street_address, city, state_province, country, postal_code, latitude, longitude, timezone') .eq('id', locationId) .maybeSingle(); if (data && !error) { setSelectedLocation({ name: data.name, street_address: data.street_address || undefined, 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.`; 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.'; setSearchError(errorMsg); setResults([]); setShowResults(false); return; } const data = await response.json() as LocationResult[]; setResults(data); setShowResults(true); setSearchError(null); } catch (error: unknown) { handleNonCriticalError(error, { action: 'Search locations', metadata: { 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 || {}; // Extract street address components const houseNumber = address.house_number || ''; const road = address.road || ''; const streetAddress = [houseNumber, road].filter(Boolean).join(' ').trim() || undefined; // Extract city const city = address.city || address.town || address.village || address.municipality; // Extract state/province (try multiple fields for international support) const state = address.state || address.province || address.state_district || address.county || address.region || address.territory; const country = address.country || 'Unknown'; const postalCode = address.postcode; // Build location name const locationParts = [streetAddress, city, state, country].filter(Boolean); const locationName = locationParts.join(', '); // Build location data object (no database operations) const locationData: SelectedLocation = { name: locationName, street_address: streetAddress, city: city || undefined, state_province: state || undefined, country: country, postal_code: postalCode || 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.street_address &&

Street: {selectedLocation.street_address}

} {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)}

)}
); }