class ZipCodeCityUpdater { constructor(zipCodeInputSelector, citySelectSelector, apiUrl) { this.zipCodeInput = document.querySelector(zipCodeInputSelector); this.citySelect = document.querySelector(citySelectSelector); // Changed to citySelect this.apiUrl = apiUrl; this.timer = null; this.debounceTime = 500; // ms // Store initial value from the select element (if any was pre-selected by Twig) this.initialCityValue = this.citySelect ? this.citySelect.value : ''; if (this.zipCodeInput && this.citySelect) { this.zipCodeInput.addEventListener('input', this.debounce(this.handleZipCodeChange.bind(this))); // Trigger on page load if a zip code is already present if (this.zipCodeInput.value) { this.handleZipCodeChange(); } } } debounce(func) { return function(...args) { const context = this; clearTimeout(context.timer); context.timer = setTimeout(() => func.apply(context, args), context.debounceTime); }; } async handleZipCodeChange() { const zipCode = this.zipCodeInput.value.trim(); // Save current city selection if any, before clearing const currentlySelectedCity = this.citySelect.value; // Clear existing options, but keep the default placeholder if any this.citySelect.innerHTML = ''; this.citySelect.value = ''; this.citySelect.disabled = true; // Disable until cities are loaded or cleared if (zipCode.length !== 5 || !/^\d+$/.test(zipCode)) { // If zip code is invalid or empty, disable and reset select this.citySelect.disabled = false; // Re-enable for placeholder return; } try { const response = await fetch(this.apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ zipCode: zipCode }), }); if (!response.ok) { console.error(`HTTP error! status: ${response.status}`); this.citySelect.disabled = false; // Re-enable for placeholder return; } const data = await response.json(); if (data.cities && data.cities.length > 0) { data.cities.forEach(city => { const option = document.createElement('option'); option.value = city; option.textContent = city; this.citySelect.appendChild(option); }); // Attempt to re-select the city that was previously selected, or the initial one if (data.cities.includes(currentlySelectedCity)) { this.citySelect.value = currentlySelectedCity; } else if (data.cities.includes(this.initialCityValue)) { this.citySelect.value = this.initialCityValue; } else if (data.cities.length === 1) { // Automatically select if only one city is returned and no previous match this.citySelect.value = data.cities[0]; } this.citySelect.disabled = false; // Enable once options are loaded } else { // If no cities found, ensure select is enabled but shows only placeholder this.citySelect.disabled = false; } } catch (error) { console.error('Error fetching city:', error); this.citySelect.disabled = false; // Re-enable on error } } } // Global initialization - run when the DOM is fully loaded or Turbo loads a new page document.addEventListener('turbo:load', () => { // Note: Use CSS selectors for querySelector new ZipCodeCityUpdater('input[name="billingZipCode"]', '#billingTownSelect', '/cities/lookup'); new ZipCodeCityUpdater('input[name="zipCodeEvent"]', '#townEventSelect', '/cities/lookup'); });