feat: Implement comprehensive geocoding improvements with loading indicators

- Add multi-strategy geocoding fallback system for better address resolution
- Implement loading spinners and visual feedback for all geocoding operations
- Move geocoding messages to venue section for better visibility
- Add dynamic message template system with proper styling
- Optimize backend to trust frontend coordinates and reduce API calls
- Add rate limiting and proper User-Agent headers for Nominatim compliance
- Improve error handling and user feedback throughout geocoding flow

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
kbe
2025-09-10 21:24:28 +02:00
parent d5c0276fcc
commit 9bebdef5a5
3 changed files with 392 additions and 76 deletions

View File

@@ -23,27 +23,7 @@ class Event < ApplicationRecord
has_many :orders
# === Callbacks ===
before_validation :geocode_address, if: :venue_address_changed?
# === Instance Methods ===
# Check if coordinates were successfully geocoded or are fallback coordinates
def geocoding_successful?
return false if latitude.blank? || longitude.blank?
# Check if coordinates are exactly the fallback coordinates
fallback_lat = 46.603354
fallback_lng = 1.888334
!(latitude == fallback_lat && longitude == fallback_lng)
end
# Get a user-friendly status message about geocoding
def geocoding_status_message
return nil if geocoding_successful?
"Les coordonnées exactes n'ont pas pu être déterminées automatiquement. Une localisation approximative a été utilisée."
end
before_validation :geocode_address, if: :should_geocode_address?
# Validations for Event attributes
# Basic information
@@ -75,23 +55,79 @@ class Event < ApplicationRecord
# Scope for published events ordered by start time
scope :upcoming, -> { published.where("start_time >= ?", Time.current).order(start_time: :asc) }
# === Instance Methods ===
# Check if coordinates were successfully geocoded or are fallback coordinates
def geocoding_successful?
coordinates_look_valid?
end
# Get a user-friendly status message about geocoding
def geocoding_status_message
return nil if geocoding_successful?
"Les coordonnées exactes n'ont pas pu être déterminées automatiquement. Une localisation approximative a été utilisée."
end
private
# Automatically geocode address to get latitude and longitude
def geocode_address
return if venue_address.blank?
# Determine if we should perform server-side geocoding
def should_geocode_address?
# Don't geocode if address is blank
return false if venue_address.blank?
# Don't geocode if we already have valid coordinates (likely from frontend)
return false if coordinates_look_valid?
# Only geocode if address changed and we don't have coordinates
venue_address_changed?
end
# If we already have coordinates and this is an update, try to geocode
# If it fails, keep the existing coordinates
# Check if the current coordinates look like they were set by frontend geocoding
def coordinates_look_valid?
return false if latitude.blank? || longitude.blank?
lat_f = latitude.to_f
lng_f = longitude.to_f
# Basic sanity checks for coordinate ranges
return false if lat_f < -90 || lat_f > 90
return false if lng_f < -180 || lng_f > 180
# Check if coordinates are not the default fallback coordinates
fallback_lat = 46.603354
fallback_lng = 1.888334
# Check if coordinates are not exactly 0,0 (common invalid default)
return false if lat_f == 0.0 && lng_f == 0.0
# Coordinates are valid if they're not exactly the fallback coordinates
!(lat_f == fallback_lat && lng_f == fallback_lng)
end
# Automatically geocode address to get latitude and longitude
# This only runs when no valid coordinates are provided (fallback for non-JS users)
def geocode_address
Rails.logger.info "Running server-side geocoding for '#{venue_address}' (no frontend coordinates provided)"
# Store original coordinates in case we need to fall back
original_lat = latitude
original_lng = longitude
begin
# Use OpenStreetMap Nominatim API for geocoding
encoded_address = URI.encode_www_form_component(venue_address.strip)
uri = URI("https://nominatim.openstreetmap.org/search?q=#{encoded_address}&format=json&limit=1")
uri = URI("https://nominatim.openstreetmap.org/search?q=#{encoded_address}&format=json&limit=1&addressdetails=1")
response = Net::HTTP.get_response(uri)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['User-Agent'] = 'AperoNight Event Platform/1.0 (https://aperonight.com)'
request['Accept'] = 'application/json'
response = http.request(request)
if response.code == "200"
data = JSON.parse(response.body)
@@ -100,7 +136,7 @@ class Event < ApplicationRecord
result = data.first
self.latitude = result["lat"].to_f.round(6)
self.longitude = result["lon"].to_f.round(6)
Rails.logger.info "Geocoded address '#{venue_address}' to coordinates: #{latitude}, #{longitude}"
Rails.logger.info "Server-side geocoded '#{venue_address}' to coordinates: #{latitude}, #{longitude}"
return
end
end
@@ -109,7 +145,7 @@ class Event < ApplicationRecord
handle_geocoding_failure(original_lat, original_lng)
rescue => e
Rails.logger.error "Geocoding failed for address '#{venue_address}': #{e.message}"
Rails.logger.error "Server-side geocoding failed for '#{venue_address}': #{e.message}"
handle_geocoding_failure(original_lat, original_lng)
end
end