import { Controller } from '@hotwired/stimulus' export default class extends Controller { static targets = ['mount', 'error', 'message', 'resetButton', 'day', 'dateFrom', 'dateTo', 'submit'] static values = { hotelCode: String, selectedFrom: String, selectedTo: String, calendarRefreshUrl: String, } connect() { this.startDate = null this.endDate = null this.sessionRestored = false // Re-apply highlights after every HTMX month swap (navigation or initial load). this.mountTarget.addEventListener('htmx:afterSwap', () => requestAnimationFrame(() => this.initAfterLoad())) this.mountTarget.addEventListener('htmx:responseError', () => this.showError()) } initAfterLoad() { // Restore from session values exactly once on the first swap. if (!this.sessionRestored) { this.sessionRestored = true const from = this.selectedFromValue const to = this.selectedToValue if (from && to) { this.startDate = from this.endDate = to if (this.hasResetButtonTarget) this.resetButtonTarget.classList.remove('hidden') } } this.updateDisplay() } selectDate(event) { const btn = event.currentTarget btn.blur() const date = btn.dataset.date const status = btn.dataset.status if (!status || status === 'blocked' || status === 'past') return if (this.startDate && this.endDate) { this.resetSelection() } if (!this.startDate) { if (status === 'checkout-only') { this.showMessage('Bitte wähle einen Anreisetag.') return } this.startDate = date this.endDate = null this.updateDisplay() this.showMessage('Bitte wähle das Abreisedatum.') return } if (date === this.startDate) { this.resetSelection() return } const [from, to] = date < this.startDate ? [date, this.startDate] : [this.startDate, date] if (this.hasBlockedInRange(from, to)) { this.resetSelection() this.showMessage('Dieser Zeitraum enthält nicht verfügbare Daten.') return } const startBtn = this.dayTargets.find(b => b.dataset.date === from) const minNights = parseInt(startBtn?.dataset.minNights) || 0 const nights = (new Date(to) - new Date(from)) / 86400000 if (minNights > 0 && nights < minNights) { this.showMessage(`Weniger als ${minNights} Nächte nur auf Anfrage.`) } else { this.hideMessage() } this.startDate = from this.endDate = to this.updateDisplay() this.activateSubmit(from, to) this.saveAndRefreshSummary(from, to) this.dispatch('range-select', { detail: { dateFrom: from, dateTo: to } }) } hasBlockedInRange(from, to) { for (const btn of this.dayTargets) { const d = btn.dataset.date const s = btn.dataset.status if (d >= from && d < to && (s === 'blocked' || s === 'past' || s === 'checkout-only')) return true } return false } updateDisplay() { for (const btn of this.dayTargets) { const d = btn.dataset.date btn.classList.remove('pc-day--pending-start', 'pc-day--selected-start', 'pc-day--selected-end', 'pc-day--in-range') if (!this.startDate) continue if (d === this.startDate) { btn.classList.add(this.endDate ? 'pc-day--selected-start' : 'pc-day--pending-start') } else if (this.endDate) { if (d === this.endDate) btn.classList.add('pc-day--selected-end') else if (d > this.startDate && d < this.endDate) btn.classList.add('pc-day--in-range') } } } activateSubmit(dateFrom, dateTo) { if (this.hasDateFromTarget) this.dateFromTarget.value = dateFrom if (this.hasDateToTarget) this.dateToTarget.value = dateTo if (this.hasSubmitTarget) { this.submitTarget.disabled = false this.submitTarget.classList.remove('opacity-50', 'cursor-not-allowed', 'pointer-events-none') } if (this.hasResetButtonTarget) this.resetButtonTarget.classList.remove('hidden') } resetSelection() { this.startDate = null this.endDate = null this.updateDisplay() this.hideMessage() if (this.hasDateFromTarget) this.dateFromTarget.value = '' if (this.hasDateToTarget) this.dateToTarget.value = '' if (this.hasSubmitTarget) { this.submitTarget.disabled = true this.submitTarget.classList.add('opacity-50', 'cursor-not-allowed', 'pointer-events-none') } if (this.hasResetButtonTarget) this.resetButtonTarget.classList.add('hidden') this.saveAndRefreshSummary() } reset() { this.resetSelection() } saveAndRefreshSummary(dateFrom = '', dateTo = '') { if (!this.hasCalendarRefreshUrlValue) return htmx.ajax('POST', this.calendarRefreshUrlValue, { values: { date_from: dateFrom, date_to: dateTo }, swap: 'none', target: this.element, }) } showMessage(text) { if (this.hasMessageTarget) this.messageTarget.textContent = text } hideMessage() { if (this.hasMessageTarget) this.messageTarget.textContent = '' } showError() { if (this.hasErrorTarget) this.errorTarget.classList.remove('hidden') } }