WIP Migrate to stimulus.js and tailwind

This commit is contained in:
Björn Fromme
2021-08-17 19:31:42 +02:00
parent 39f1c72972
commit 0d01f4212b
17 changed files with 1023 additions and 617 deletions
@@ -13,7 +13,6 @@ smoothscroll.polyfill()
import Alpine from 'alpinejs'
import parseAppConfig from './components/appconfig'
import appendPaCode from './components/pacode'
import productDetail from './components/product-detail'
import daytripDateSelect from './components/daytrip-date-select'
import searchBar from './components/search-bar'
import calendar from './components/calendar'
@@ -22,7 +21,6 @@ import contactForm from './components/contact-form'
const appConfig = parseAppConfig()
appendPaCode(appConfig)
Alpine.store('appconfig', appConfig)
Alpine.data('productDetail', productDetail)
Alpine.data('daytripDateSelect', daytripDateSelect)
Alpine.data('searchBar', searchBar)
Alpine.data('calendar', calendar)
@@ -1,98 +0,0 @@
import axios from 'axios'
export default props => ({
uris: props.uris,
show: props.hotelOnTop ? 'hotel' : 'dates',
tableMode: 'dates',
datesRows: [],
pricesRows: [],
servicesIncluded: [],
dateRange: '',
available: false,
singleDate: false,
bookable: true,
hasSurcharge: false,
hasDiscount: false,
altLabel: null,
altProductLink: null,
loading: false,
loaded: false,
addBus: false,
discountBus: false,
showAll: false,
init() {
// Load dates instantly when initial view is dates
if ('dates' === this.show) {
this.loadDates()
}
// Scroll to top when switching table mode
this.$watch('tableMode', () => {
this.$el.scrollIntoView({ block: 'start', behavior: 'smooth' })
})
// Load dates (once) when switching to dates view
this.$watch('show', () => {
if ('dates' === this.show && false === this.loaded) {
this.loadDates()
}
})
},
loadDates() {
this.loading = true
axios.get(this.uris.dates)
.then(response => {
const json = response.data
if (json.dates.length === 1) {
this.singleDate = true
const dateUid = json.dates[0].dateUid
this.loadPrices(dateUid)
} else {
this.singleDate = false
this.datesRows = json.dates
this.bookable = json.bookable
this.altLabel = json.altLabel
this.altProductLink = json.altProductLink
this.hasSurcharge = json.hasSurcharge
this.hasDiscount = json.hasDiscount
this.available = json.dates.length > 0
this.tableMode = 'dates'
}
}).catch(() => {
}).finally(() => {
this.loading = false
this.loaded = true
})
},
loadPrices(dateUid) {
this.loading = true
const data = new URLSearchParams({
'tx_epproducts_ajax[date]': dateUid,
'tx_epproducts_ajax[paCode]': this.$store.appconfig.paCode,
})
axios.post(this.uris.pricetable, data)
.then(response => {
const json = response.data
this.pricesRows = json.prices
this.servicesIncluded = json.servicesIncluded
this.dateRange = json.dateRange
this.tableMode = 'prices'
}).catch(() => {
}).finally(() => {
this.loading = false
})
},
calculatePrice(minPrice, busPrice, discount) {
if (this.addBus) {
return minPrice + busPrice
}
if (this.discountBus) {
return minPrice + discount
}
return minPrice
},
showBusIcon(busPrice, busIncluded) {
return (
busPrice && this.addBus ||
busIncluded && !this.discountBus
)
},
})
@@ -0,0 +1,18 @@
import { Controller } from 'stimulus'
export default class extends Controller {
static targets = [ 'content' ]
open(e) {
this.contentTargets.forEach(element => {
let uid = element.dataset.uid
element.classList.toggle('hidden', e.detail !== uid)
})
this.element.classList.remove('hidden')
}
close() {
this.element.classList.add('hidden', true)
}
}
@@ -0,0 +1,95 @@
import { Controller } from 'stimulus'
import { useDispatch } from 'stimulus-use'
export default class extends Controller {
static targets = [
'row',
'price',
'busIcon',
'surchargeToggleLabel',
'discountToggleLabel',
'rowToggleLabel',
'rowToggleIcon',
]
static values = {
mode: String,
showAllRows: Boolean,
}
initialize() {
useDispatch(this)
}
toggleSurcharge() {
this.modeValue = this.modeValue === 'surcharge' ? '' : 'surcharge'
}
toggleDiscount() {
this.modeValue = this.modeValue === 'discount' ? '' : 'discount'
}
toggleRows() {
this.showAllRowsValue = !this.showAllRowsValue
}
openModal(e) {
let eventName = `${e.currentTarget.dataset.type}-modal:open`
this.dispatch(eventName, e.currentTarget.dataset.uid)
}
modeValueChanged() {
this.priceTargets.forEach(price => {
let room = parseInt(price.dataset.room)
let bus = parseInt(price.dataset.bus)
let discount = parseInt(price.dataset.discount)
price.innerText = `${this.calculatePrice(room, bus, discount)}`
})
this.busIconTargets.forEach(icon => {
let busPrice = parseInt(icon.dataset.busPrice)
let busIncluded = !!icon.dataset.busIncluded
icon.classList.toggle('hidden', this.hideBusIcon(busPrice, busIncluded))
})
if (this.hasSurchargeToggleLabelTarget) {
let labelDefault = this.surchargeToggleLabelTarget.dataset.labelDefault
let labelAlt = this.surchargeToggleLabelTarget.dataset.labelAlt
this.surchargeToggleLabelTarget.innerText = this.modeValue === 'surcharge' ? labelDefault : labelAlt
}
if (this.hasDiscountToggleLabelTarget) {
let labelDefault = this.discountToggleLabelTarget.dataset.labelDefault
let labelAlt = this.discountToggleLabelTarget.dataset.labelAlt
this.discountToggleLabelTarget.innerText = this.modeValue === 'discount' ? labelDefault : labelAlt
}
}
showAllRowsValueChanged() {
let additionalRows = this.rowTargets.slice(5)
if (additionalRows.length) {
additionalRows.forEach(row => {
row.classList.toggle('hidden', this.showAllRowsValue === false)
})
}
if (this.hasRowToggleLabelTarget && this.hasRowToggleIconTarget) {
this.rowToggleLabelTarget.innerText = this.showAllRowsValue ? 'weniger Termine anzeigen' : 'alle Termine anzeigen'
this.rowToggleIconTarget.setAttribute('href', this.showAllRowsValue ? '#icon-minus-circle' : '#icon-plus-circle')
}
}
calculatePrice(minPrice, busPrice, discount) {
if ('surcharge' === this.modeValue) {
return minPrice + busPrice
}
if ('discount' === this.modeValue) {
return minPrice + discount
}
return minPrice
}
hideBusIcon(busPrice, busIncluded) {
return (
busPrice && this.modeValue !== 'surcharge' ||
busIncluded && this.modeValue === 'discount'
)
}
}
@@ -0,0 +1,93 @@
import { Controller } from 'stimulus'
import { useFetch } from '../mixins/use_fetch'
import { useConfig } from '../mixins/use_config'
export default class extends Controller {
static targets = [
'loadingIndicator',
'loadingMessage',
'datesTableContainer',
'priceTableContainer',
]
static values = {
datestableUri: String,
pricetableUri: String,
travelAlertUri: String,
loading: Boolean,
loaded: Boolean,
mode: String,
}
initialize() {
useFetch(this)
useConfig(this)
}
connect() {
if ('dates' === this.modeValue) {
this.loadDates()
}
}
showDates() {
this.modeValue = 'dates'
}
showPrices() {
this.modeValue = 'prices'
}
loadDates() {
if (true === this.loadedValue) {
this.modeValue = 'dates'
return
}
this.loadingValue = true
this.datesTableContainerTarget.innerHTML = ''
this.modeValue = 'dates'
fetch(this.datestableUriValue)
.then(this.checkStatus)
.then(this.parseHTML)
.then(html => {
this.datesTableContainerTarget.innerHTML = html
}).catch(() => {
}).finally(() => {
this.loadingValue = false
this.loadedValue = true
})
}
loadPrices(e) {
this.loadingValue = true
this.priceTableContainerTarget.innerHTML = ''
this.modeValue = 'prices'
let paCode = this.getConfig().paCode
let dateUid = e.currentTarget.dataset.dateUid
const data = new URLSearchParams({
'tx_epproducts_ajax[date]': dateUid,
'tx_epproducts_ajax[paCode]': paCode,
})
fetch(this.pricetableUriValue, { method: 'POST', body: data })
.then(this.checkStatus)
.then(this.parseHTML)
.then(html => {
this.priceTableContainerTarget.innerHTML = html
}).catch(() => {
}).finally(() => {
this.loadingValue = false
})
}
loadingValueChanged() {
this.loadingMessageTarget.innerText = this.modeValue === 'prices' ? 'Lade Buchungsptionen...' : 'Lade Termine...'
this.loadingIndicatorTarget.classList.toggle('hidden', this.loadingValue === false)
}
modeValueChanged() {
this.datesTableContainerTarget.classList.toggle('hidden', this.modeValue === 'prices')
this.priceTableContainerTarget.classList.toggle('hidden', this.modeValue === 'dates')
this.element.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
}
@@ -0,0 +1,27 @@
import { Controller } from 'stimulus'
import { useDispatch } from 'stimulus-use'
export default class extends Controller {
static targets = [ 'tab', 'button' ]
initialize() {
useDispatch(this)
}
select(e) {
e.preventDefault()
let selectedTab = e.currentTarget.dataset.tab
this.buttonTargets.forEach(element => {
let currentTab = element.dataset.tab
element.classList.toggle('button--active', selectedTab === currentTab)
})
this.tabTargets.forEach(element => {
let currentTab = element.dataset.tab
element.classList.toggle('hidden', selectedTab !== currentTab)
})
this.dispatch('selected', selectedTab)
this.element.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
}
@@ -8,7 +8,11 @@ export default class extends Controller {
onList: Boolean
}
static targets = [ 'label', 'icon' ]
static targets = [
'label',
'icon',
'ariaLabel',
]
initialize() {
useDispatch(this)
@@ -28,7 +32,10 @@ export default class extends Controller {
}
onListValueChanged() {
this.labelTarget.setAttribute('aria-label', true === this.onListValue ? 'von der Merkliste entfernen' : 'auf die Merkliste setzen')
this.labelTarget.innerText = true === this.onListValue ? 'von der Merkliste entfernen' : 'auf die Merkliste setzen'
this.iconTarget.setAttribute('href', true === this.onListValue ? '#icon-minus-circle' : '#icon-plus-circle')
if (this.hasAriaLabelTarget) {
this.ariaLabelTarget.setAttribute('aria-label', true === this.onListValue ? 'von der Merkliste entfernen' : 'auf die Merkliste setzen')
}
}
}
@@ -1,5 +1,5 @@
.button {
@apply inline-flex items-center justify-center px-4 py-2 md:px-8;
@apply inline-flex items-center justify-center space-x-2 px-4 py-2 md:px-8;
@apply uppercase leading-none;
@apply transition-colors outline-none focus:outline-none;
@apply bg-ep-secondary border-2 border-ep-secondary text-white;
@@ -24,7 +24,7 @@
@apply font-bold;
&:hover,
&.active {
&.button--active {
@apply bg-white border-ep-secondary text-ep-secondary;
}
}
@@ -149,7 +149,7 @@
<span data-microtip-position="top"
role="tooltip"
aria-label="auf die Merkliste setzen"
data-watchlist-toggle-target="label">
data-watchlist-toggle-target="ariaLabel">
<svg class="w-5 h-5 text-ep-secondary">
<use href="#icon-plus-circle" data-watchlist-toggle-target="icon"></use>
</svg>
@@ -0,0 +1,266 @@
<html xmlns="http://www.w3.org/1999/xhtml" lang="en"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers">
<f:layout name="Default"/>
<f:section name="Main">
<div class="headerbar">
Buchungsoptionen {hotel.name}
</div>
<div data-controller="pricetable">
<f:if condition="{hasSurcharge} || {hasDiscount}">
<div class="w-full flex justify-end mb-2">
<f:if condition="{hasSurcharge}">
<button data-action="pricetable#toggleSurcharge"
class="flex items-center space-x-1 focus:outline-none text-ep-primary">
<svg class="w-4 h-4">
<use href="#icon-arrow-circle-right"></use>
</svg>
<span data-pricetable-target="surchargeToggleLabel"
data-label-default="Preise für Eigenanreise anzeigen"
data-label-alt="Preise für Busanreise anzeigen">
Preise für Eigenanreise anzeigen
</span>
</button>
</f:if>
<f:if condition="{hasDiscount}">
<button data-action="pricetable#toggleDiscount"
class="flex items-center space-x-1 focus:outline-none text-ep-primary">
<svg class="w-4 h-4">
<use href="#icon-arrow-circle-right"></use>
</svg>
<span data-pricetable-target="discountToggleLabel"
data-label-default="Preise für Busanreise anzeigen"
data-label-alt="Preise für Eigenanreise anzeigen">
Preise für Busanreise anzeigen
</span>
</button>
</f:if>
</div>
</f:if>
<div class="w-full max-w-full overflow-x-scroll mb-4">
<table class="min-w-full border-collapse">
<thead>
<tr>
<th class="p-1 text-left">
Termin
</th>
<th class="p-1 text-left">
<span data-microtip-position="bottom" aria-label="Nächte" role="tooltip">
<svg class="w-4 h-4">
<use href="#icon-bed"></use>
</svg>
</span>
</th>
<th class="p-1 text-left whitespace-nowrap">
Preis ab
</th>
<th class="p-1 text-left">
inkl.
</th>
<th class="hidden lg:table-cell"></th>
</tr>
</thead>
<tbody>
<f:for each="{dates}" as="row" iteration="iteration">
<tr class="even:bg-gray-200{f:if(condition: '{iteration.cycle} > 5', then: ' hidden')}"
data-pricetable-target="row">
<td class="px-2 py-1">
<f:if condition="{bookable} && {row.available} && {row.showBookingButton}">
<f:then>
<button class="text-ep-primary-light"
data-action="product-details#loadPrices"
data-date-uid="{row.dateUid}">
{row.dateStart} - {row.dateEnd}
</button>
<div class="md:hidden">
<button class="button--small"
data-action="product-details#loadPrices"
data-date-uid="{row.dateUid}">
Details
</button>
<a href="{row.bookingUrl}" target="_blank"
class="button button--small bg-green-500 border-green-500">
Buchen
</a>
</div>
</f:then>
<f:else>
<span>
{row.dateStart} - {row.dateEnd}
</span>
</f:else>
</f:if>
</td>
<td class="px-2 py-1">
{row.nights}
</td>
<td class="px-2 py-1">
<f:if condition="{row.pseudoPrice}">
<span class="text-sm line-through">
{row.pseudoPrice} €
</span>
</f:if>
<span class="font-bold"
data-pricetable-target="price"
data-room="{row.minPrice}"
data-bus="{row.busPrice}"
data-discount="{row.discount}">
{row.minPrice} €
</span>
</td>
<td class="px-2 py-1">
<div class="hidden md:flex items-center space-x-2">
<f:if condition="{row.busIncluded}">
<span data-microtip-position="top"
aria-label="Busfahrt"
role="tooltip"
data-pricetable-target="busIcon"
data-bus-included="{row.busIncluded}"
data-bus-price="{row.busPrice}">
<svg class="w-4 h-4">
<use href="#icon-bus"></use>
</svg>
</span>
</f:if>
<f:if condition="{row.skipassIncluded}">
<span data-microtip-position="top"
aria-label="{f:if(condition: row.summer, then: 'Bergbahnticket inklusive', else: 'Skipass inklusive')}"
role="tooltip">
<svg class="w-4 h-4">
<use href="#icon-tag"></use>
</svg>
</span>
</f:if>
<button class="button button--light button--small hidden md:block"
data-action="pricetable#openModal"
data-type="services"
data-uid="{row.dateUid}"
data-microtip-position="top"
aria-label="Alle Inklusivleistungen anzeigen"
role="tooltip">
Leistungen
</button>
<button class="md:hidden">
<svg class="w-4 h-4">
<use href="#icon-info-circle"></use>
</svg>
</button>
</div>
</td>
<td class="px-2 py-1 hidden sm:table-cell">
<button class="button button--small"
data-action="product-details#loadPrices"
data-date-uid="{row.dateUid}">
Details
</button>
<f:if condition="{row.available} && {row.showBookingButton}">
<a href="{row.bookingUrl}"
target="_blank"
class="button button--small bg-green-500 border-green-500">
Buchen
</a>
</f:if>
<f:if condition="{row.showBookingButton}">
<f:else>
<a href="#"
class="button button--small bg-gray-300">
Reisen-Alert
</a>
</f:else>
</f:if>
<f:if condition="{row.available} && {row.showBookingButton}">
<f:else>
<span class="inline-block px-2 mx-1 text-xs text-gray-500">
ausgebucht
</span>
</f:else>
</f:if>
</td>
</tr>
</f:for>
</tbody>
</table>
</div>
<f:if condition="{dates -> f:count()} > 5 || {altProductLink}">
<div class="w-full flex justify-between pb-4">
<f:if condition="{dates -> f:count()} > 5">
<button class="flex items-center space-x-1 focus:outline-none text-ep-primary"
data-action="pricetable#toggleRows">
<svg class="w-4 h-4">
<use href="#icon-plus-circle"
data-pricetable-target="rowToggleIcon"></use>
</svg>
<span data-pricetable-target="rowToggleLabel">
Alle Termine anzeigen
</span>
</button>
</f:if>
<f:if condition="{altProductLink}">
<a href="{altProductLink}"
title="{altLabel}"
class="flex items-center space-x-1 focus:outline-none text-ep-primary">
<svg class="w-4 h-4">
<use href="#icon-arrow-circle-right"></use>
</svg>
<span>
{altLabel}
</span>
</a>
</f:if>
</div>
</f:if>
</div>
<div class="hidden fixed inset-0 w-full h-full z-50"
data-controller="modal"
data-action="pricetable:services-modal:open@window->modal#open">
<div class="absolute inset-0 w-full h-full bg-overlay-black z-25 flex items-center justify-center"
data-action="click->modal#close"></div>
<div class="absolute top-1/2 left-1/2 transform -translate-xy-1/2 bg-white shadow w-full max-w-xl rounded">
<div class="shadow flex items-center justify-between p-4 bg-ep-primary text-white rounded-t">
<span class="text-xl font-bold">
Inklusivleistungen {hotel.name}
</span>
<button class="inline-block"
data-action="modal#close">
<svg class="w-8 h-8">
<use href="#icon-close"></use>
</svg>
</button>
</div>
<div class="p-4">
<f:for each="{dates}" as="row">
<table class="hidden table table-striped"
data-modal-target="content"
data-uid="{row.dateUid}">
<tbody>
<f:for each="{row.servicesIncluded}" as="service">
<tr>
<td>
<div class="flex items-center space-x-2">
<svg class="w-6 h-6 text-ep-primary">
<use href="#icon-check"></use>
</svg>
<span>
{service}
</span>
</div>
</td>
</tr>
</f:for>
</tbody>
</table>
</f:for>
</div>
</div>
</div>
</f:section>
</html>
@@ -0,0 +1,254 @@
<html xmlns="http://www.w3.org/1999/xhtml" lang="en"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers">
<f:layout name="Default"/>
<f:section name="Main">
<div class="headerbar flex items-center justify-between">
<span>Buchungsoptionen</span>
<span>{hotel.name} {dateRange}</span>
</div>
<div data-controller="pricetable"
data-pricetable-show-all-rows-value="true">
<f:if condition="{hasSurcharge} || {hasDiscount}">
<div class="w-full flex justify-end mb-2">
<f:if condition="{hasSurcharge}">
<button data-action="pricetable#toggleSurcharge"
class="flex items-center space-x-1 focus:outline-none ep:text-ep-primary">
<svg class="w-4 h-4">
<use href="#icon-arrow-circle-right"></use>
</svg>
<span data-pricetable-target="surchargeToggleLabel"
data-label-default="Preise für Eigenanreise anzeigen"
data-label-alt="Preise für Busanreise anzeigen">
Preise für Eigenanreise anzeigen
</span>
</button>
</f:if>
<f:if condition="{hasDiscount}">
<button data-action="pricetable#toggleDiscount"
class="flex items-center space-x-1 focus:outline-none ep:text-ep-primary">
<svg class="w-4 h-4">
<use href="#icon-arrow-circle-right"></use>
</svg>
<span data-pricetable-target="discountToggleLabel"
data-label-default="Preise für Busanreise anzeigen"
data-label-alt="Preise für Eigenanreise anzeigen">
Preise für Busanreise anzeigen
</span>
</button>
</f:if>
</div>
</f:if>
<div class="w-full max-w-full overflow-x-scroll mb-4">
<table class="min-w-full border-collapse">
<thead>
<tr>
<th class="text-left">
Zimmertyp
</th>
<th class="text-left">
Preis
</th>
<th class="text-left">
inkl.
</th>
<th></th>
<th class="hidden lg:table-cell"></th>
</tr>
</thead>
<tbody>
<f:for each="{prices}" as="row">
<tr class="even:bg-gray-200"
data-pricetable-target="row">
<td class="px-2 py-1">
<f:if condition="{row.showBookingButton} && {row.available}">
<f:then>
<a href="{row.bookingUrl}"
target="_blank">
{row.roomName}
</a>
<a class="button button--small button--action lg:hidden"
href="{row.bookingUrl}"
target="_blank">
Jetzt buchen
</a>
</f:then>
<f:else>
<span>
{row.roomName}
</span>
</f:else>
</f:if>
<f:if condition="{row.available}">
<f:else>
<div class="lg:hidden">
<span class="inline-block px-2 mx-1 text-xs text-gray-500">
ausgebucht
</span>
</div>
</f:else>
</f:if>
</td>
<td class="px-2 py-1 whitespace-nowrap"
data-pricetable-target="price"
data-room="{row.minPrice}"
data-bus="{row.busPrice}"
data-discount="{row.discount}">
{row.minPrice} €
</td>
<td class="px-2 py-1">
<div class="flex items-center space-x-1">
<f:if condition="{row.busIncluded}">
<span data-microtip-position="top"
aria-label="Busfahrt"
role="tooltip"
data-pricetable-target="busIcon"
data-bus-included="{row.busIncluded}"
data-bus-price="{row.busPrice}">
<svg class="w-4 h-4">
<use href="#icon-bus"></use>
</svg>
</span>
</f:if>
<f:if condition="{row.skipassIncluded}">
<span data-microtip-position="top"
aria-label="{f:if(condition: row.summer, then: 'Bergbahnticket inklusive', else: 'Skipass inklusive')}"
role="tooltip">
<svg class="w-4 h-4">
<use href="#icon-tag"></use>
</svg>
</span>
</f:if>
</div>
</td>
<td class="px-2 py-1">
<f:if condition="{row.hasOptionalServices} && {row.available}">
<button class="hidden sm:block button button--light button--small"
data-action="pricetable#openModal"
data-type="options"
data-uid="{row.roomUid}"
data-microtip-position="top"
aria-label="Alle Zusatzleistungen anzeigen"
role="tooltip">
Optionen
</button>
<button class="sm:hidden text-ep-primary">
<svg class="w-4 h-4">
<use href="#icon-info-circle"></use>
</svg>
</button>
</f:if>
</td>
<td class="hidden lg:table-cell px-2 py-1">
<f:if condition="{row.isHideBookingButton}">
<f:then>
<a class="button button--small button--mute"
href="#">
Reisen-Alert
</a>
</f:then>
<f:else>
<f:if condition="{row.available}">
<f:then>
<a class="button button--small button--action"
href="{row.bookingUrl}" target="_blank">
Buchen
</a>
</f:then>
<f:else>
<span class="inline-block px-2 mx-1 text-xs text-gray-500">
ausgebucht
</span>
</f:else>
</f:if>
</f:else>
</f:if>
</td>
</tr>
</f:for>
</tbody>
</table>
</div>
<f:if condition="{isSingleDate}">
<f:else>
<button class="button button--small mb-4"
data-action="product-details#showDates">
<svg class="w-4 h-4 text-white">
<use href="#icon-chevron-left"></use>
</svg>
<span>zurück zur Terminübersicht</span>
</button>
</f:else>
</f:if>
<div class="headerbar">
Inklusivleistungen
</div>
<div class="bg-gray-200 p-4 mb-8">
<ul class="list-none m-0 p-0">
<f:for each="{servicesIncluded}" as="service">
<li class="flex items-center space-x-2">
<svg class="w-6 h-6 text-ep-primary">
<use href="#icon-check"></use>
</svg>
<span>
{service}
</span>
</li>
</f:for>
</ul>
</div>
</div>
<div class="hidden fixed inset-0 w-full h-full z-50"
data-controller="modal"
data-action="pricetable:options-modal:open@window->modal#open">
<div class="absolute inset-0 w-full h-full bg-overlay-black z-25 flex items-center justify-center"
data-action="click->modal#close"></div>
<div class="absolute top-1/2 left-1/2 transform -translate-xy-1/2 bg-white shadow w-full max-w-xl rounded">
<div class="absolute top-0 left-0 inset-x-0 shadow flex items-center justify-between p-4 bg-ep-primary text-white rounded-t">
<span class="text-xl font-bold">
Optionale Zusatzleistungen
</span>
<button class="inline-block"
data-action="modal#close">
<svg class="w-8 h-8">
<use href="#icon-close"></use>
</svg>
</button>
</div>
<div class="h-128 p-4 mt-12 overflow-y-scroll">
<f:for each="{prices}" as="row">
<div class="hidden"
data-modal-target="content"
data-uid="{row.roomUid}">
<f:for each="{row.optionalServices}" as="section">
<div class="py-2 -mx-1">
<div class="font-bold text-xl px-1">
{section.label}
</div>
<f:for each="{section.services}" as="service">
<dl class="flex items-start justify-between p-1 hover:bg-gray-200">
<dt class="font-normal">
{service.label}:
</dt>
<dd class="whitespace-nowrap">
{service.price} €
</dd>
</dl>
</f:for>
</div>
</f:for>
</div>
</f:for>
</div>
</div>
</div>
</f:section>
</html>
@@ -9,54 +9,71 @@
<article>
<f:render partial="Product/HeaderDetail" arguments="{_all}"/>
<div class="container"
id="detail"
x-data='productDetail({
uris: {
dates: "<ep:uri.ajax action="dates" controller="AjaxDate" arguments="{product: product, hotel: hotel}" format="json" pageUid="{settings.defaultAjaxUid}"/>",
pricetable: "<ep:uri.ajax action="pricetable" controller="AjaxTable" arguments="{product: product, hotel: hotel}" format="html" pageUid="{settings.defaultAjaxUid}"/>",
travelAlert: "<f:uri.typolink parameter="{settings.travelAlertPageUid}"/>"
},
hotelOnTop: <f:if condition="{settings.hotelOnTop}"><f:then>true</f:then><f:else>false</f:else></f:if>
})'>
data-controller="product-details"
data-action="tabs:selected->product-details#loadDates"
<f:if condition="{settings.hotelOnTop}">
<f:else>
data-product-details-mode-value="dates"
</f:else>
</f:if>
data-product-details-datestable-uri-value="{ep:uri.ajax(action: 'datestable', controller: 'AjaxTable', arguments: '{product: product, hotel: hotel}', format: 'html', pageUid: settings.defaultAjaxUid)}"
data-product-details-pricetable-uri-value="{ep:uri.ajax(action: 'pricetable', controller: 'AjaxTable', arguments: '{product: product, hotel: hotel}', format: 'html', pageUid: settings.defaultAjaxUid)}"
data-product-details-travel-alert-uri-value="{f:uri.typolink(parameter: settings.travelAlertPageUid)}">
<f:render partial="Header/Header" arguments="{layout: 8, subheader: '{product.region.name} / {product.name}', header: product.headline}"/>
<div class="flex flex-col lg:flex-row-reverse lg:items-start">
<div class="flex flex-col lg:flex-row-reverse lg:items-start"
data-controller="tabs">
<div class="w-full lg:w-1/3">
<div class="lg:pl-4">
<div class="grid grid-cols-2 gap-2">
<f:if condition="{settings.hotelOnTop}">
<f:then>
<button class="button button--light button--active col-span-1 md:col-span-2"
data-action="tabs#select"
data-tabs-target="button"
data-tab="hotel">
Unterkunft
</button>
<button class="button button--light col-span-1 md:col-span-2"
x-bind:class="{ 'button--active': show === 'hotel'}"
x-on:click.prevent="show = 'hotel'">Unterkunft</button>
data-action="tabs#select"
data-tabs-target="button"
data-tab="dates">
Termine/Preise
</button>
</f:then>
<f:else>
<button class="button button--light button--active col-span-1 md:col-span-2"
data-action="tabs#select"
data-tabs-target="button"
data-tab="dates">
Termine/Preise
</button>
<button class="button button--light col-span-1 md:col-span-2"
x-bind:class="{ 'button--active': show === 'dates'}"
x-on:click.prevent="show = 'dates'">Termine/Preise</button>
</f:else>
</f:if>
<f:if condition="{settings.hotelOnTop}">
<f:then>
<button class="button button--light col-span-1 md:col-span-2"
x-bind:class="{ 'button--active': show === 'dates'}"
x-on:click.prevent="show = 'dates'">Termine/Preise</button>
</f:then>
<f:else>
<button class="button button--light col-span-1 md:col-span-2"
x-bind:class="{ 'button--active': show === 'hotel'}"
x-on:click.prevent="show = 'hotel'">Unterkunft</button>
data-action="tabs#select"
data-tabs-target="button"
data-tab="hotel">
Unterkunft
</button>
</f:else>
</f:if>
<button class="button button--light col-span-1 md:col-span-2"
x-bind:class="{ 'button--active': show === 'region'}"
x-on:click.prevent="show = 'region'">Gebiet</button>
data-action="tabs#select"
data-tabs-target="button"
data-tab="region">
Gebiet
</button>
<button class="button button--light col-span-1 md:col-span-2"
x-bind:class="{ 'button--active': show === 'city'}"
x-on:click.prevent="show = 'city'">Ort</button>
data-action="tabs#select"
data-tabs-target="button"
data-tab="city">
Ort
</button>
<f:if condition="{product.journey}">
<button class="button button--light col-span-1 md:col-span-2"
x-bind:class="{ 'button--active': show === 'journey'}"
x-on:click.prevent="show = 'journey'">Anreise</button>
data-action="tabs#select"
data-tabs-target="button"
data-tab="journey">
Anreise
</button>
</f:if>
<f:if condition="{product.faqs}">
<button class="button button--light col-span-1 md:col-span-2"
@@ -68,17 +85,17 @@
</f:if>
<div class="col-span-2">
<p class="mb-4">
<button class="flex items-center space-x-2"
<button class="button button--light w-full"
data-controller="watchlist-toggle"
data-watchlist-toggle-key-value="{hotel.uid}:{product.uid}"
data-action="watchlist-toggle#toggle">
<span data-microtip-position="top" aria-label="" role="tooltip" data-watchlist-toggle-target="label">
<svg class="w-5 h-5">
<use href="#icon-plus-circle"
data-watchlist-toggle-target="icon"></use>
</svg>
<svg class="w-5 h-5">
<use href="#icon-plus-circle"
data-watchlist-toggle-target="icon"></use>
</svg>
<span data-watchlist-toggle-target="label">
Auf die Merkliste setzen
</span>
<span></span>
</button>
</p>
<p class="mb-4">
@@ -98,17 +115,18 @@
<div id="main" class="w-full lg:w-2/3">
<div class="lg:pr-4">
<!-- Loader -->
<div x-show="loading"
x-cloak
class="flex items-center space-x-2 mb-4">
<div class="flex items-center space-x-2 mb-4 hidden border border-ep-primary p-2"
data-product-details-target="loadingIndicator">
<f:image class="block w-4 h-4" src="EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif" alt="Loading"/>
<span>Loading...</span>
<span data-product-details-target="loadingMessage">
Loading...
</span>
</div>
<!-- /Loader -->
<!-- Dates -->
<div x-show="show === 'dates' && !loading"
x-transition.opacity
x-cloak>
<div class="{f:if(condition: settings.hotelOnTop, then: 'hidden')}"
data-tabs-target="tab"
data-tab="dates">
<f:if condition="{isProductWithExcludedConcept}">
<f:if condition="{product.region.season} == 'w'">
<div class="alert alert-info">
@@ -125,12 +143,8 @@
<f:render section="DaytripDateSelect" arguments="{_all}"/>
</f:then>
<f:else>
<div x-show="tableMode === 'dates'">
<f:render section="DatesTable" arguments="{_all}"/>
</div>
<div x-show="tableMode === 'prices'">
<f:render section="PriceTable" arguments="{_all}"/>
</div>
<div class="hidden" data-product-details-target="datesTableContainer"></div>
<div class="hidden" data-product-details-target="priceTableContainer"></div>
<f:if condition="{isProductWithNoInfoConcept}">
<f:else>
<template x-if="!available">
@@ -140,28 +154,34 @@
</f:if>
</f:else>
</f:if>
</div>
<!-- /Dates -->
<div x-show="show === 'dates' || show === 'prices'" x-cloak>
<f:render partial="Product/Details" arguments="{_all}"/>
</div>
<!-- /Dates -->
<!-- Hotel -->
<div x-show="show === 'hotel'" x-transition.opacity x-cloak>
<div class="{f:if(condition: settings.hotelOnTop, else: 'hidden')}"
data-tabs-target="tab"
data-tab="hotel">
<f:render partial="Hotel/Details" arguments="{hotel: hotel, bars: 1, showNonBookableHint: 0}"/>
</div>
<!-- /Hotel -->
<!-- City -->
<div x-show="show === 'city'" x-transition.opacity x-cloak>
<div class="hidden"
data-tabs-target="tab"
data-tab="city">
<f:render partial="City/Details" arguments="{city: product.city, bars: 1}"/>
</div>
<!-- /City -->
<!-- Region -->
<div x-show="show === 'region'" x-transition.opacity x-cloak>
<div class="hidden"
data-tabs-target="tab"
data-tab="region">
<f:render partial="Region/Details" arguments="{region: product.region, bars: 1}"/>
</div>
<!-- /Region -->
<!-- Journey -->
<div x-show="show === 'journey'" x-transition.opacity x-cloak>
<div class="hidden"
data-tabs-target="tab"
data-tab="journey">
<f:render partial="Journey/Details" arguments="{journey: product.journey, bars: 1}"/>
</div>
<!-- /Journey -->
@@ -173,8 +193,11 @@
</div>
</div>
</article>
<div x-data x-init='fbq("track", "ViewContent", { "content_ids": [ {product.busProId -> f:format.raw()} ], "content_name": "{product.nameInternal -> f:format.raw()}", "content_type": "product" })'></div>
<f:render partial="Faq" arguments="{faqs: product.faqs, settings: settings}"/>
<div data-controller="fbpixel"
data-fbpixel-props-value='{ "content_ids": [ {product.busProId -> f:format.raw()} ], "content_name": "{product.nameInternal -> f:format.raw()}", "content_type": "product" }'></div>
<div id="faq">
<f:render partial="Faq" arguments="{faqs: product.faqs, settings: settings}"/>
</div>
</f:section>
<f:section name="Sidebar">
@@ -219,360 +242,8 @@
</f:if>
</f:section>
<f:section name="DatesTable">
<div x-show="bookable"
x-cloak>
<div class="headerbar">
Buchungsoptionen {hotel.name}
</div>
<f:render section="PriceToggle"/>
<div class="w-full max-w-full overflow-x-scroll mb-4">
<table class="min-w-full border-collapse">
<thead>
<tr>
<th class="p-1 text-left">
Termin
</th>
<th class="p-1 text-left">
<span data-microtip-position="bottom" aria-label="Nächte" role="tooltip">
<svg class="w-4 h-4">
<use href="#icon-bed"></use>
</svg>
</span>
</th>
<th class="p-1 text-left whitespace-nowrap">
Preis ab
</th>
<th class="p-1 text-left">
inkl.
</th>
<th class="hidden lg:table-cell"></th>
</tr>
</thead>
<tbody>
<template x-for="(row, index) in datesRows" x-bind:key="row.dateUid">
<tr class="even:bg-gray-200" x-bind:class="{ 'hidden': index > 4 && !showAll }">
<td class="px-2 py-1">
<template x-if="bookable && row.available && !row.isHideBookingButton">
<a class="text-ep-primary-light" href="#"
x-on:click.prevent="loadPrices(row.dateUid)"
x-text="row.dateStart + ' - ' + row.dateEnd"></a>
<div class="md:hidden">
<button x-on:click.prevent="loadPrices(row.dateUid)" class="button button--small">
Details
</button>
<a x-bind:href="row.bookingUrl" target="_blank" class="button button--small bg-green-500 border-green-500">
Buchen
</a>
</div>
</template>
<template x-if="!bookable || !row.available || row.isHideBookingButton">
<span x-text="row.dateStart + ' - ' + row.dateEnd"></span>
</template>
</td>
<td class="px-2 py-1"
x-text="row.dateNights"></td>
<td class="px-2 py-1">
<span class="text-sm line-through" x-show="row.datePseudoPrice" x-text="row.datePseudoPrice + ' €'"></span>
<strong x-text="calculatePrice(row.dateMinPrice, row.dateBusPrice, row.dateDiscount) + ' €'"></strong>
</td>
<td class="px-2 py-1">
<div class="hidden md:flex items-center space-x-2">
<div x-show="showBusIcon(row.dateBusPrice, row.dateBusIncluded)" data-microtip-position="top" aria-label="Busfahrt inklusive" role="tooltip">
<svg class="w-4 h-4">
<use href="#icon-bus"></use>
</svg>
</div>
<div x-show="row.dateSkipassIncluded" data-microtip-position="top" x-bind:aria-label="row.season === 's' ? 'Bergbahnticket inklusive' : 'Skipass inklusive'" role="tooltip">
<svg class="w-4 h-4">
<use href="#icon-tag"></use>
</svg>
</div>
<button class="button button--light button--small hidden md:block"
data-microtip-position="top" aria-label="Alle Inklusivleistungen anzeigen" role="tooltip">
Leistungen
</button>
<button class="md:hidden">
<svg class="w-4 h-4">
<use href="#icon-info-circle"></use>
</svg>
</button>
</div>
</td>
<td class="px-2 py-1 hidden sm:table-cell">
<button x-show="row.available || row.isHideBookingButton" class="button button--small"
x-on:click.prevent="loadPrices(row.dateUid)">
Details
</button>
<a x-show="row.available && !row.isHideBookingButton" x-bind:href="row.bookingUrl"
target="_blank"
class="button button--small bg-green-500 border-green-500">
Buchen
</a>
<a x-show="row.isHideBookingButton" x-bind:href="uris.travelAlertUrl"
class="button button--small bg-gray-300">
Reisen-Alert
</a>
<span x-show="!row.available && !row.isHideBookingButton"
class="inline-block px-2 mx-1 text-xs text-gray-500">
ausgebucht
</span>
</td>
</tr>
</template>
</tbody>
</table>
</div>
<template x-if="datesRows.length > 5 || altProductLink">
<div class="grid grid-cols-2 gap-8 pb-4">
<button x-show="datesRows.length > 5"
x-on:click.prevent="showAll = !showAll"
class="flex items-center space-x-1 focus:outline-none ep:text-ep-primary">
<svg class="w-4 h-4">
<use x-bind:href="showAll ? '#icon-minus-circle' : '#icon-plus-circle'"></use>
</svg>
<span x-text="showAll ? 'Weniger Termine anzeigen' : 'Alle Termine anzeigen'"></span>
</button>
<template x-if="altProductLink">
<a x-bind:href="altProductLink"
x-bind:title="altLabel"
class="flex items-center space-x-1 focus:outline-none ep:text-ep-primary">
<svg class="w-4 h-4">
<use href="#icon-arrow-circle-right"></use>
</svg>
<span x-text="altLabel"></span>
</a>
</template>
</div>
</template>
<template x-for="row in datesRows">
<div x-bind:id="'services-' + row.dateUid"
x-transition:enter="transition duration-200 ease-in-out"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition duration-200 ease-in-out"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
x-cloak
class="hidden fixed inset-0 w-full h-full z-50">
<div class="absolute inset-0 w-full h-full bg-overlay-black"></div>
<div class="absolute top-1/2 left-1/2 transform -translate-xy-1/2 bg-white p-8 shadow w-full max-w-3xl rounded">
<div class="flex justify-between border-b border-gray-200 pb-2 mb-4">
<span class="text-lg font-bold">
Inklusivleistungen
</span>
<button class="inline-block">
<svg class="w-8 h-8">
<use href="#icon-close"></use>
</svg>
</button>
</div>
<table class="table table-striped">
<tbody>
<template x-for="service in row.dateServicesIncluded">
<tr>
<td>
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
<span x-text="service"></span>
</td>
</tr>
</template>
</tbody>
</table>
</div>
</div>
</template>
</div>
</f:section>
<f:section name="PriceTable">
<div class="headerbar flex items-center justify-between">
<span>Buchungsoptionen</span>
<span x-text="dateRange"></span>
</div>
<f:render section="PriceToggle"/>
<div class="w-full max-w-full overflow-x-scroll mb-4">
<table class="min-w-full border-collapse">
<thead>
<tr>
<th class="text-left">
Zimmertyp
</th>
<th class="text-left">
Preis
</th>
<th class="text-left">
inkl.
</th>
<th></th>
<th class="hidden lg:table-cell"></th>
</tr>
</thead>
<tbody>
<template x-for="(row, index) in pricesRows" x-bind:key="row.roomBusProId">
<tr class="even:bg-gray-200">
<td class="px-2 py-1">
<span x-show="row.isHideBookingButton || !row.roomAvailable" x-text="row.roomName"></span>
<a x-show="!row.isHideBookingButton && row.roomAvailable"
x-bind:href="row.bookingUrl" target="_blank"
x-text="row.roomName"></a>
<a class="button button--small button--action lg:hidden"
x-show="!row.isHideBookingButton && row.roomAvailable"
x-bind:href="row.bookingUrl" target="_blank">
Jetzt buchen
</a>
<div class="lg:hidden"
x-show="!row.roomAvailable">
<span class="inline-block px-2 mx-1 text-xs text-gray-500">
ausgebucht
</span>
</div>
</td>
<td class="px-2 py-1 whitespace-nowrap" x-bind:class="!row.roomAvailable && !row.isHideBookingButton && 'line-through'"
x-text="calculatePrice(row.roomPrice, row.roomBusPrice, row.roomDiscount) + ' €'"></td>
<td class="px-2 py-1">
<div class="flex items-center space-x-1">
<span x-show="showBusIcon(row.roomBusPrice, row.dateBusIncluded)" data-microtip-position="top" aria-label="Busfahrt inklusive" role="tooltip">
<svg class="w-4 h-4">
<use href="#icon-bus"></use>
</svg>
</span>
<span x-show="row.dateSkipassIncluded" data-microtip-position="top" x-bind:aria-label="row.season === 's' ? 'Bergbahnticket inklusive' : 'Skipass inklusive'" role="tooltip">
<svg class="w-4 h-4">
<use href="#icon-tag"></use>
</svg>
</span>
</div>
</td>
<td class="px-2 py-1">
<button x-show="row.roomHasOptionalServices && row.roomAvailable"
class="hidden sm:block button button--light button--small"
data-microtip-position="top" aria-label="Alle Zusatzleistungen anzeigen" role="tooltip">
Optionen
</button>
<button x-show="row.roomHasOptionalServices && row.roomAvailable"
class="sm:hidden ep:text-ep-primary">
<svg class="w-4 h-4">
<use href="#icon-info-circle"></use>
</svg>
</button>
</td>
<td class="hidden lg:table-cell px-2 py-1">
<a class="button button--small button--action"
x-show="row.roomAvailable && !row.isHideBookingButton"
x-bind:href="row.bookingUrl" target="_blank">
Buchen
</a>
<a class="button button--small button--mute"
x-show="row.isHideBookingButton"
x-bind:href="uris.travelAlertUrl">
Reisen-Alert
</a>
<span x-show="!row.roomAvailable && !row.isHideBookingButton"
class="inline-block px-2 mx-1 text-xs text-gray-500">
ausgebucht
</span>
</td>
</tr>
</template>
</tbody>
</table>
</div>
<template x-for="row in pricesRows">
<div x-bind:id="'optional-services-' + row.roomUid"
x-transition:enter="transition duration-200 ease-in-out"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition duration-200 ease-in-out"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
class="hidden fixed inset-0 w-full h-full z-50">
<div class="absolute inset-0 w-full h-full bg-overlay-black z-25 flex items-center justify-center"></div>
<div class="absolute top-1/2 left-1/2 transform -translate-xy-1/2 bg-white p-8 shadow w-full max-w-3xl h-128 overflow-y-scroll rounded">
<div class="flex justify-between pb-2 mb-4">
<span class="text-2xl font-bold">
Optionale Zusatzleistungen
</span>
<button class="inline-block">
<svg class="w-8 h-8">
<use href="#icon-close"></use>
</svg>
</button>
</div>
<template x-for="section in row.roomOptionalServices">
<div class="py-2 -mx-1">
<div class="font-bold text-xl px-1" x-text="section.label"></div>
<template x-for="optionalService in section.services">
<dl class="flex items-start justify-between p-1 hover:bg-gray-200">
<dt class="font-normal" x-text="optionalService.label + ':'"></dt>
<dd class="whitespace-nowrap" x-text="optionalService.price + ' €'"></dd>
</dl>
</template>
</div>
</template>
</div>
</div>
</template>
<template x-if="!singleDate">
<button class="button button--small mb-4"
x-on:click.prevent="tableMode = 'dates'">
<svg class="w-4 h-4 text-white">
<use href="#icon-chevron-left"></use>
</svg>
<span>zurück zur Terminübersicht</span>
</button>
</template>
<div class="headerbar">
Inklusivleistungen
</div>
<div class="bg-gray-200 p-4 mb-8">
<ul class="list-none m-0 p-0">
<template x-for="service in servicesIncluded">
<li class="flex items-center space-x-2">
<svg class="w-6 h-6 text-ep-primary">
<use href="#icon-check"></use>
</svg>
<span x-text="service"></span>
</li>
</template>
</ul>
</div>
</f:section>
<f:section name="PriceToggle">
<div class="w-full flex justify-end mb-2" x-show="hasSurcharge || hasDiscount">
<template x-if="hasSurcharge">
<button x-on:click.prevent="addBus = !addBus"
class="flex items-center space-x-1 focus:outline-none ep:text-ep-primary">
<svg class="w-4 h-4">
<use href="#icon-arrow-circle-right"></use>
</svg>
<span x-show="addBus">
Preise für Eigenanreise anzeigen
</span>
<span x-show="! addBus">
Preise für Busanreise anzeigen
</span>
</button>
</template>
<template x-if="hasDiscount">
<button x-on:click.prevent="discountBus = !discountBus"
class="flex items-center space-x-1 focus:outline-none ep:text-ep-primary">
<svg class="w-4 h-4">
<use href="#icon-arrow-circle-right"></use>
</svg>
<span x-show="discountBus">
Preise für Busanreise anzeigen
</span>
<span x-show="!discountBus">
Preise für Eigenanreise anzeigen
</span>
</button>
</template>
</div>
</f:section>
<f:section name="DaytripDateSelect">
<f:comment><!--
<div x-data='daytripDateSelect({
<f:format.raw>
hotelName: "{hotel.name}",
@@ -657,6 +328,7 @@
</div>
</div>
</div>
--></f:comment>
</f:section>
<f:section name="Calendar">
@@ -673,23 +345,19 @@
<span class="block font-bold mb-2">
Belegungskalender
</span>
<div x-data='calendar({
<f:format.raw>
url: "{ep:uri.ajax(action: 'range', controller: 'AjaxCalendar', extensionName: 'epproducts', pageUid: settings.defaultAjaxUid, format: 'html')}",
hotel: {product.calendarHotel.uid}
</f:format.raw>
})'>
<div data-controller="calendar"
data-calendar-uri-value="{ep:uri.ajax(action: 'range', controller: 'AjaxCalendar', extensionName: 'epproducts', pageUid: settings.defaultAjaxUid, format: 'html')}"
data-calendar-hotel-uid-value="{product.calendarHotel.uid}">
<select class="w-full border-gray-400 focus:border-gray-400"
x-model="selectedIndex"
x-on:change="load">
data-action="calendar#selectMonth">
<template x-for="(month, index) in selectableMonths">
<option x-bind:value="index" x-text="month.toLocaleDateString('de-DE', {year: 'numeric', month: 'long'})"></option>
<option value="index" data-text="month.toLocaleDateString('de-DE', {year: 'numeric', month: 'long'})"></option>
</template>
</select>
<div class="mt-4"
x-ref="calendarWrapper"
x-cloak></div>
<div class="absolute top-0 left-0 inset-0 w-full h-full bg-overlay-white" x-show="loading">
data-calendar-target="container"></div>
<div class="absolute top-0 left-0 inset-0 w-full h-full bg-overlay-white"
data-calendar-target="loadingIndicator">
<f:image class="absolute top-1/2 left-1/2 -translate-xy-1/2" src="EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif" alt="Loading"/>
</div>
</div>