import { Controller } from "@hotwired/stimulus" export default class extends Controller { static targets = ["name", "slug", "latitude", "longitude", "address", "mapLinksContainer"] static values = { geocodeDelay: { type: Number, default: 1500 } // Delay before auto-geocoding } connect() { this.geocodeTimeout = null // Initialize map links if we have an address and coordinates already exist if (this.hasAddressTarget && this.addressTarget.value.trim() && this.hasLatitudeTarget && this.hasLongitudeTarget && this.latitudeTarget.value && this.longitudeTarget.value) { this.updateMapLinks() } } disconnect() { if (this.geocodeTimeout) { clearTimeout(this.geocodeTimeout) } } // Generate slug from name generateSlug() { const name = this.nameTarget.value const slug = name .toLowerCase() .replace(/[^a-z0-9\s-]/g, '') // Remove special characters .replace(/\s+/g, '-') // Replace spaces with hyphens .replace(/-+/g, '-') // Replace multiple hyphens with single .replace(/^-|-$/g, '') // Remove leading/trailing hyphens this.slugTarget.value = slug } // Handle address changes with debounced geocoding addressChanged() { // Clear any existing timeout if (this.geocodeTimeout) { clearTimeout(this.geocodeTimeout) } const address = this.addressTarget.value.trim() if (!address) { this.clearCoordinates() this.clearMapLinks() return } // Debounce geocoding to avoid too many API calls this.geocodeTimeout = setTimeout(() => { this.geocodeAddressQuiet(address) }, this.geocodeDelayValue) } // Get user's current location and reverse geocode to address async getCurrentLocation() { if (!navigator.geolocation) { this.showLocationError("La géolocalisation n'est pas supportée par ce navigateur.") return } this.showLocationLoading() const options = { enableHighAccuracy: true, timeout: 10000, maximumAge: 60000 } try { const position = await this.getCurrentPositionPromise(options) const lat = position.coords.latitude const lng = position.coords.longitude // Set coordinates first this.latitudeTarget.value = lat.toFixed(6) this.longitudeTarget.value = lng.toFixed(6) // Then reverse geocode to get address const address = await this.reverseGeocode(lat, lng) if (address) { this.addressTarget.value = address this.showLocationSuccess("Position actuelle détectée et adresse mise à jour!") } else { this.showLocationSuccess("Position actuelle détectée!") } this.updateMapLinks() } catch (error) { this.hideLocationLoading() let message = "Erreur lors de la récupération de la localisation." switch(error.code) { case error.PERMISSION_DENIED: message = "L'accès à la localisation a été refusé." break case error.POSITION_UNAVAILABLE: message = "Les informations de localisation ne sont pas disponibles." break case error.TIMEOUT: message = "La demande de localisation a expiré." break } this.showLocationError(message) } } // Promise wrapper for geolocation getCurrentPositionPromise(options) { return new Promise((resolve, reject) => { navigator.geolocation.getCurrentPosition(resolve, reject, options) }) } // Reverse geocode coordinates to get address async reverseGeocode(lat, lng) { try { const response = await fetch(`https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lng}&format=json`) const data = await response.json() if (data && data.display_name) { return data.display_name } return null } catch (error) { console.log("Reverse geocoding failed:", error) return null } } // Preview location - same as updating map links but with user feedback previewLocation() { if (!this.hasAddressTarget || !this.addressTarget.value.trim()) { this.showLocationError("Veuillez saisir une adresse pour la prévisualiser.") return } // If we already have coordinates, just update map links if (this.hasLatitudeTarget && this.hasLongitudeTarget && this.latitudeTarget.value && this.longitudeTarget.value) { this.updateMapLinks() this.showLocationSuccess("Liens de carte mis à jour!") } else { // Otherwise geocode the address first this.geocodeAddress() } } // Geocode address manually (with user feedback) async geocodeAddress() { if (!this.hasAddressTarget || !this.addressTarget.value.trim()) { this.showLocationError("Veuillez saisir une adresse.") return } const address = this.addressTarget.value.trim() try { this.showLocationLoading() const result = await this.performGeocode(address) if (result) { this.latitudeTarget.value = result.lat this.longitudeTarget.value = result.lng this.updateMapLinks() this.showLocationSuccess("Coordonnées trouvées pour cette adresse!") } else { this.showLocationError("Impossible de trouver les coordonnées pour cette adresse.") } } catch (error) { this.showLocationError("Erreur lors de la recherche de l'adresse.") } finally { this.hideLocationLoading() } } // Geocode address quietly (no user feedback, for auto-geocoding) async geocodeAddressQuiet(address) { try { const result = await this.performGeocode(address) if (result) { this.latitudeTarget.value = result.lat this.longitudeTarget.value = result.lng this.updateMapLinks() } else { // If auto-geocoding fails, show a subtle warning this.showGeocodingWarning(address) } } catch (error) { console.log("Auto-geocoding failed:", error) this.showGeocodingWarning(address) } } // Perform the actual geocoding request async performGeocode(address) { const encodedAddress = encodeURIComponent(address) const response = await fetch(`https://nominatim.openstreetmap.org/search?q=${encodedAddress}&format=json&limit=1`) const data = await response.json() if (data && data.length > 0) { const result = data[0] return { lat: parseFloat(result.lat).toFixed(6), lng: parseFloat(result.lon).toFixed(6) } } return null } // Update map links based on current coordinates updateMapLinks() { if (!this.hasMapLinksContainerTarget) return const lat = parseFloat(this.latitudeTarget.value) const lng = parseFloat(this.longitudeTarget.value) const address = this.hasAddressTarget ? this.addressTarget.value.trim() : "" if (isNaN(lat) || isNaN(lng) || !address) { this.clearMapLinks() return } const links = this.generateMapLinks(lat, lng, address) this.mapLinksContainerTarget.innerHTML = links } // Generate map links HTML generateMapLinks(lat, lng, address) { const encodedAddress = encodeURIComponent(address) const providers = { openstreetmap: { name: "OpenStreetMap", url: `https://www.openstreetmap.org/?mlat=${lat}&mlon=${lng}#map=16/${lat}/${lng}`, icon: "🗺️" }, google: { name: "Google Maps", url: `https://www.google.com/maps/search/${encodedAddress}/@${lat},${lng},16z`, icon: "🔍" }, apple: { name: "Apple Plans", url: `https://maps.apple.com/?address=${encodedAddress}&ll=${lat},${lng}`, icon: "🍎" } } return `