import { Controller } from "@hotwired/stimulus" export default class extends Controller { static targets = ["modal", "form", "nameInput", "latitudeInput", "longitudeInput", "noteInput", "nearbyList", "loadingSpinner", "tagCheckboxes", "loadMoreContainer", "loadMoreButton", "modalTitle", "submitButton", "placeIdInput"] static values = { apiKey: String } connect() { this.setupEventListeners() this.currentRadius = 0.5 // Start with 500m (0.5km) this.maxRadius = 1.5 // Max 1500m (1.5km) this.setupTagListeners() this.editingPlaceId = null } setupEventListeners() { document.addEventListener('place:create', (e) => { this.open(e.detail.latitude, e.detail.longitude) }) document.addEventListener('place:edit', (e) => { this.openForEdit(e.detail.place) }) } setupTagListeners() { // Listen for checkbox changes to update badge styling if (this.hasTagCheckboxesTarget) { this.tagCheckboxesTarget.addEventListener('change', (e) => { if (e.target.type === 'checkbox' && e.target.name === 'tag_ids[]') { const badge = e.target.nextElementSibling const color = badge.dataset.color if (e.target.checked) { // Filled style badge.classList.remove('badge-outline') badge.style.backgroundColor = color badge.style.borderColor = color badge.style.color = 'white' } else { // Outline style badge.classList.add('badge-outline') badge.style.backgroundColor = 'transparent' badge.style.borderColor = color badge.style.color = color } } }) } } async open(latitude, longitude) { this.editingPlaceId = null this.latitudeInputTarget.value = latitude this.longitudeInputTarget.value = longitude this.currentRadius = 0.5 // Reset radius when opening modal // Update modal for creation mode if (this.hasModalTitleTarget) { this.modalTitleTarget.textContent = 'Create New Place' } if (this.hasSubmitButtonTarget) { this.submitButtonTarget.textContent = 'Create Place' } this.modalTarget.classList.add('modal-open') this.nameInputTarget.focus() await this.loadNearbyPlaces(latitude, longitude) } async openForEdit(place) { this.editingPlaceId = place.id this.currentRadius = 0.5 // Fill in form with place data this.nameInputTarget.value = place.name this.latitudeInputTarget.value = place.latitude this.longitudeInputTarget.value = place.longitude if (this.hasNoteInputTarget && place.note) { this.noteInputTarget.value = place.note } // Update modal for edit mode if (this.hasModalTitleTarget) { this.modalTitleTarget.textContent = 'Edit Place' } if (this.hasSubmitButtonTarget) { this.submitButtonTarget.textContent = 'Update Place' } // Check the appropriate tag checkboxes const tagCheckboxes = this.formTarget.querySelectorAll('input[name="tag_ids[]"]') tagCheckboxes.forEach(checkbox => { const isSelected = place.tags.some(tag => tag.id === parseInt(checkbox.value)) checkbox.checked = isSelected // Trigger change event to update badge styling const event = new Event('change', { bubbles: true }) checkbox.dispatchEvent(event) }) this.modalTarget.classList.add('modal-open') this.nameInputTarget.focus() // Load nearby places for suggestions await this.loadNearbyPlaces(place.latitude, place.longitude) } close() { this.modalTarget.classList.remove('modal-open') this.formTarget.reset() this.nearbyListTarget.innerHTML = '' this.loadMoreContainerTarget.classList.add('hidden') this.currentRadius = 0.5 this.editingPlaceId = null const event = new CustomEvent('place:create:cancelled') document.dispatchEvent(event) } async loadNearbyPlaces(latitude, longitude, radius = null) { this.loadingSpinnerTarget.classList.remove('hidden') // Use provided radius or current radius const searchRadius = radius || this.currentRadius const isLoadingMore = radius !== null && radius > this.currentRadius - 0.5 // Only clear the list on initial load, not when loading more if (!isLoadingMore) { this.nearbyListTarget.innerHTML = '' } try { const response = await fetch( `/api/v1/places/nearby?latitude=${latitude}&longitude=${longitude}&radius=${searchRadius}&limit=5`, { headers: { 'Authorization': `Bearer ${this.apiKeyValue}` } } ) if (!response.ok) throw new Error('Failed to load nearby places') const data = await response.json() this.renderNearbyPlaces(data.places, isLoadingMore) // Show load more button if we can expand radius further if (searchRadius < this.maxRadius) { this.loadMoreContainerTarget.classList.remove('hidden') this.updateLoadMoreButton(searchRadius) } else { this.loadMoreContainerTarget.classList.add('hidden') } } catch (error) { console.error('Error loading nearby places:', error) this.nearbyListTarget.innerHTML = '
Failed to load suggestions
' } finally { this.loadingSpinnerTarget.classList.add('hidden') } } renderNearbyPlaces(places, append = false) { if (!places || places.length === 0) { if (!append) { this.nearbyListTarget.innerHTML = 'No nearby places found
' } return } // Calculate starting index based on existing items const currentCount = append ? this.nearbyListTarget.querySelectorAll('.card').length : 0 const html = places.map((place, index) => `${this.escapeHtml(place.street)}
` : ''} ${place.city ? `${this.escapeHtml(place.city)}, ${this.escapeHtml(place.country || '')}
` : ''}