167 lines
5.6 KiB
JavaScript
167 lines
5.6 KiB
JavaScript
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')
|
|
}
|
|
}
|