diff --git a/public/typo3conf/ext/ep_products/Classes/Service/DateService.php b/public/typo3conf/ext/ep_products/Classes/Service/DateService.php
index dabe960a..e2dddef3 100644
--- a/public/typo3conf/ext/ep_products/Classes/Service/DateService.php
+++ b/public/typo3conf/ext/ep_products/Classes/Service/DateService.php
@@ -60,6 +60,18 @@ class DateService implements SingletonInterface
*/
protected $uriBuilder;
+ /**
+ * @var string[]
+ */
+ protected $serviceSections = [
+ 'BUS' => 'Anreise',
+ 'VPF' => 'Verpflegung',
+ 'SPA' => 'Skipass',
+ 'KUR' => 'Kurse',
+ 'VER' => 'Verleih',
+ 'SON' => 'Sonstiges',
+ ];
+
/**
* @param ProductRepository $productRepository
* @param DateRepository $dateRepository
@@ -303,6 +315,7 @@ class DateService implements SingletonInterface
'paCode' => $paCode,
'isDayTrip' => $isDayTrip,
]);
+ $optionalServices = $this->preprocessOptionalServices(unserialize($row['roomOptionalServices']));
$priceTable[] = [
'dateStart' => $dateStart->format('d.m.Y'),
'dateEnd' => $dateEnd->format('d.m.Y'),
@@ -316,7 +329,8 @@ class DateService implements SingletonInterface
'roomBusPrice' => $row['busPrice'],
'roomDiscount' => $row['roomDiscount'],
'roomNights' => $row['roomNights'],
- 'roomOptionalServices' => unserialize($row['roomOptionalServices']),
+ 'roomOptionalServices' => $optionalServices,
+ 'roomHasOptionalServices' => count($optionalServices) > 0,
'dateBusProId' => $row['dateBusProId'],
'hotelBusProId' => $row['hotelBusProId'],
'roomBusProId' => $row['roomBusProId'],
@@ -329,6 +343,23 @@ class DateService implements SingletonInterface
return $priceTable;
}
+ public function preprocessOptionalServices(array $servicesFlat): array
+ {
+ $processed = [];
+
+ foreach ($servicesFlat as $key => $services) {
+ if (!array_key_exists($key, $processed)) {
+ $processed[$key] = [
+ 'label' => $this->serviceSections[$key],
+ 'services' => [],
+ ];
+ $processed[$key]['services'] += $services;
+ }
+ }
+
+ return $processed;
+ }
+
/**
* @param array $options
* @return array
diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/_app.js b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/_app.js
index 52a09284..ed3fc484 100644
--- a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/_app.js
+++ b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/_app.js
@@ -24,6 +24,7 @@ import ajaxContent from './components/ajax-content'
import faq from './components/faq'
import osmMap from './components/osm-map'
import productDetail from './components/product-detail'
+import daytripDateSelect from './components/daytrip-date-select'
// Executed on document ready
document.addEventListener('DOMContentLoaded', () => {
@@ -35,6 +36,7 @@ document.addEventListener('DOMContentLoaded', () => {
Alpine.store('show', {
searchBox: false,
mobileNav: false,
+ modal: null,
})
Alpine.store('watchlist', storedWatchlist)
Alpine.data('scrollTop', scrollTop)
@@ -47,6 +49,7 @@ document.addEventListener('DOMContentLoaded', () => {
Alpine.data('faq', faq)
Alpine.data('osmMap', osmMap)
Alpine.data('productDetail', productDetail)
+ Alpine.data('daytripDateSelect', daytripDateSelect)
Alpine.start()
Glightbox({
diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/DateSelect.vue b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/DateSelect.vue
deleted file mode 100644
index efc3dc7d..00000000
--- a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/DateSelect.vue
+++ /dev/null
@@ -1,272 +0,0 @@
-
-
-
{{ headerLabel }}
-
-
-
-
-
-
- | Zimmerart |
- Preis |
- inkl. |
- |
-
-
-
-
- | {{ row.label }} |
- ab {{ calculatePrice(row.code) }} € |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- | {{ service }} |
-
-
-
-
-
-
-
-
- |
-
- Buchen
- |
-
-
-
-
- Loading...
![Loading Loading]()
-
-
{{ message }}
-
-
-
-
-
-
-
-
diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/DatesTable.vue b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/DatesTable.vue
deleted file mode 100644
index 44732f12..00000000
--- a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/DatesTable.vue
+++ /dev/null
@@ -1,225 +0,0 @@
-
-
-
- {{ headerLabel }}
- Termine & Leistungen
-
-
-
-
-
-
- | Termin |
-
-
-
-
- |
- Preis ab |
- inkl. |
- |
-
-
-
-
- |
-
-
- {{ row.dateStart }} - {{ row.dateEnd }}
-
-
-
- {{ row.dateStart }} - {{ row.dateEnd }}
- |
- {{ row.dateNights }} |
-
- {{ row.datePseudoPrice }} €
- {{ calculatePrice(row) }} €
- |
-
-
-
-
-
-
-
-
-
-
-
- |
-
-
- Buchen
- Reisen-Alert
- ausgebucht
- |
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/PriceTable.vue b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/PriceTable.vue
deleted file mode 100644
index 8af16f8b..00000000
--- a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/PriceTable.vue
+++ /dev/null
@@ -1,211 +0,0 @@
-
-
-
-
Buchungsoptionen
-
{{dateLabel}}
-
-
-
-
-
-
- | Zimmertyp |
- Preis |
- inkl. |
- |
- |
-
-
-
-
- |
-
- {{ row.roomName }}
-
-
- {{ row.roomName }}
-
-
-
- ausgebucht
-
- |
- {{ calculatePrice(row) }} € |
-
-
-
-
-
-
-
- |
-
-
-
-
- |
-
- Buchen
- Reisen-Alert
- ausgebucht
- |
-
-
-
-
-
- zurück zur Terminübersicht
-
-
-
-
-
-
diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/ProductDetail.vue b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/ProductDetail.vue
deleted file mode 100644
index 4fb05735..00000000
--- a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/ProductDetail.vue
+++ /dev/null
@@ -1,215 +0,0 @@
-
diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/ServicesListIncluded.vue b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/ServicesListIncluded.vue
deleted file mode 100644
index 55ed51aa..00000000
--- a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/ServicesListIncluded.vue
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
- | {{ service }} |
-
-
-
-
-
-
diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/ServicesListOptional.vue b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/ServicesListOptional.vue
deleted file mode 100644
index b30b0a50..00000000
--- a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/ServicesListOptional.vue
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-
-
-
-
- | {{ section }} |
-
-
- | {{ service.label }} |
- {{ service.price }} € |
-
-
-
-
-
-
-
-
diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/daytrip-date-select.js b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/daytrip-date-select.js
new file mode 100644
index 00000000..08bc0998
--- /dev/null
+++ b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/daytrip-date-select.js
@@ -0,0 +1,153 @@
+import axios from 'axios'
+import flatpickr from 'flatpickr'
+import { German } from 'flatpickr/dist/l10n/de'
+import dayjs from 'dayjs'
+import 'dayjs/locale/de'
+
+export default props => ({
+ hotelName: props.hotelName,
+ hotelUid: props.hotelUid,
+ productUid: props.productUid,
+ contingentEndpointUri: props.contingentEndpointUri,
+ roomsEndpointUri: props.roomsEndpointUri,
+ loading: false,
+ picker: null,
+ displayedFrom: dayjs().startOf('month'),
+ displayedTo: dayjs().endOf('month'),
+ selectedFrom: null,
+ selectedTo: null,
+ selectedNumberOfNights: 0,
+ minNightsForSelectedDate: 0,
+ availableDates: [],
+ availableRooms: [],
+ init() {
+ this.picker = flatpickr(this.$refs.field, {
+ mode: 'range',
+ dateFormat: 'Y-m-d',
+ locale: German,
+ inline: true,
+ onChange: selectedDates => {
+ this.availableRooms = []
+ if (selectedDates.length === 2) {
+ this.selectedFrom = dayjs(selectedDates[0])
+ this.selectedTo = dayjs(selectedDates[1])
+ this.selectedNumberOfNights = this.getSelectedNumberOfNights()
+ this.minNightsForSelectedDate = this.getMinNightsForSelectedDate()
+ if (this.selectedNumberOfNights >= this.minNightsForSelectedDate) {
+ this.fetchAvailableRooms()
+ }
+ }
+ },
+ onDayCreate: (dObj, dStr, fp, dayElem) => {
+ const day = dayElem.dateObj.getFullYear() + '-' + ('0' + (dayElem.dateObj.getMonth()+1)).slice(-2)
+ + '-' + ('0' + dayElem.dateObj.getDate()).slice(-2)
+ if (day in this.availableDates) {
+ dayElem.classList.add('available')
+ }
+ }
+ })
+ this.$nextTick(() => {
+ this.fetchEnabledDates()
+ })
+ },
+ headerLabel () {
+ let label = 'Buchungsoptionen'
+ if (this.hotelName) {
+ label += ' ' + this.hotelName
+ }
+ if (this.selectedFrom && this.selectedTo) {
+ label += ' ' + this.selectedFrom.format('DD.MM.YY') + ' - ' + this.selectedTo.format('DD.MM.YY')
+ label += ' (' + this.selectedNumberOfNights + ' Nächte)'
+ }
+ return label
+ },
+ message() {
+ if (this.loading) {
+ return ''
+ }
+ if (this.selectedFrom === null || this.selectedTo === null) {
+ return 'Bitte einen Zeitraum auswählen.'
+ }
+ if (this.selectedNumberOfNights < this.minNightsForSelectedDate) {
+ return 'Bitte mindestens ' + this.minNightsForSelectedDate + ' Nächte auswählen.'
+ }
+ if (this.availableRooms.length === 0) {
+ return 'Im gewählten Zeitraum sind leider keine Zimmer verfügbar.'
+ }
+ return ''
+ },
+ onDateSelected(date) {
+ this.selectedDate = dayjs(date)
+ },
+ fetchEnabledDates() {
+ this.loading = true
+ const data = new FormData()
+ data.set('tx_epproducts_ajax[hotel]', this.hotelUid)
+ data.set('tx_epproducts_ajax[product]', this.productUid)
+ axios({
+ url: this.contingentEndpointUri,
+ method: 'post',
+ data
+ }).then(response => {
+ const json = response.data
+ if (json) {
+ this.availableDates = json
+ const enabledDates = []
+ for (const date in this.availableDates) {
+ if (this.availableDates.hasOwnProperty(date)) {
+ enabledDates.push(this.availableDates[date].date)
+ }
+ }
+ this.picker.set('enable', enabledDates)
+ this.picker.set('minDate', enabledDates[0])
+ this.picker.set('maxDate', enabledDates[enabledDates.length - 1])
+ this.picker.jumpToDate(enabledDates[0])
+ }
+ }).finally(() => {
+ this.loading = false
+ })
+ },
+ fetchAvailableRooms() {
+ this.loading = true
+ const data = new FormData()
+ data.set('tx_epproducts_ajax[hotel]', this.hotelUid)
+ data.set('tx_epproducts_ajax[product]', this.productUid)
+ data.set('tx_epproducts_ajax[dateFrom]', this.selectedFrom.format('YYYY-MM-DD'))
+ data.set('tx_epproducts_ajax[dateTo]', this.selectedTo.format('YYYY-MM-DD'))
+ axios({
+ url: this.roomsEndpointUri,
+ method: 'post',
+ data
+ }).then(response => {
+ this.availableRooms = response.data
+ }).finally(() => {
+ this.loading = false
+ })
+ },
+ calculatePrice (roomCode) {
+ const room = this.availableRooms.find(obj => {
+ return obj.code === roomCode
+ });
+ if (room) {
+ const nights = this.getSelectedNumberOfNights()
+ return room.price + (nights - room.minNights) * room.additionalNightPrice
+ }
+ return null;
+ },
+ getMinNightsForSelectedDate () {
+ if (this.selectedFrom === null) {
+ return 0
+ }
+ const key = this.selectedFrom.format('YYYY-MM-DD')
+ if (!key in this.availableDates) {
+ return 0
+ }
+ return this.availableDates[key].minNights
+ },
+ getSelectedNumberOfNights () {
+ if (this.selectedFrom === null || this.selectedTo === null) {
+ return 0
+ }
+ return this.selectedTo.diff(this.selectedFrom, 'days');
+ }
+})
diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/product-detail.js b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/product-detail.js
index 836dc7f4..f4f52287 100644
--- a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/product-detail.js
+++ b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/product-detail.js
@@ -21,6 +21,9 @@ export default uris => ({
showAll: false,
init() {
this.loadDates()
+ this.$watch('tableMode', () => {
+ this.$el.scrollIntoView({ block: 'start', behavior: 'smooth' })
+ })
},
loadDates() {
this.loading = true
diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Partials/Modal.html b/public/typo3conf/ext/ep_theme/Resources/Private/Partials/Modal.html
new file mode 100644
index 00000000..20298dc8
--- /dev/null
+++ b/public/typo3conf/ext/ep_theme/Resources/Private/Partials/Modal.html
@@ -0,0 +1,22 @@
+
+
+
+
+
diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Templates/AjaxDate/Dates.html b/public/typo3conf/ext/ep_theme/Resources/Private/Templates/AjaxDate/Dates.html
deleted file mode 100644
index 46973206..00000000
--- a/public/typo3conf/ext/ep_theme/Resources/Private/Templates/AjaxDate/Dates.html
+++ /dev/null
@@ -1,173 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- Buchungsoptionen{f:if(condition: hotelName, then: ' {hotelName}')}
-
-
- Termine & Leistungen
-
-
-
-
-
-
-
-
-
-
- | Termin |
-
-
-
-
- |
- Preis ab |
- inkl. |
- |
-
-
-
-
- 4', then: ' x-bind:class="{ \'hidden\': !showAll }"')}>
- |
-
-
-
-
-
-
- {row.dateStart} - {row.dateEnd}
-
-
- |
-
- {row.dateNights}
- |
-
-
-
- {row.datePseudoPrice} €
-
-
-
- |
-
-
-
-
-
-
-
-
-
-
-
- |
-
-
-
-
-
-
- Buchen
-
-
-
-
- Reisen-Alert
-
-
-
- ausgebucht
-
- |
-
-
-
-
-
-
-
-
-
-
-
- {altLabel}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Templates/AjaxTable/Pricetable.html b/public/typo3conf/ext/ep_theme/Resources/Private/Templates/AjaxTable/Pricetable.html
deleted file mode 100644
index d80927ab..00000000
--- a/public/typo3conf/ext/ep_theme/Resources/Private/Templates/AjaxTable/Pricetable.html
+++ /dev/null
@@ -1,146 +0,0 @@
-
-
-
-
-
-
-
-
Buchungsoptionen
-
{dateStart} - {dateEnd}
-
-
-
-
-
-
-
-
- | Zimmertyp |
- Preis |
- inkl. |
- |
- |
-
-
-
-
-
- |
-
-
- {row.roomName}
-
-
-
- {row.roomName}
-
-
-
-
-
- ausgebucht
-
-
-
- |
- |
-
-
-
-
-
-
-
-
-
- |
-
-
-
-
-
-
-
- |
-
-
- Buchen
-
-
- Reisen-Alert
-
-
-
- ausgebucht
-
-
- |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Product/Detail.html b/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Product/Detail.html
index 117a3f36..7921614a 100644
--- a/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Product/Detail.html
+++ b/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Product/Detail.html
@@ -11,8 +11,6 @@
",
- contingents: "{ep:uri.ajax(action: 'list', controller: 'AjaxContingent', format: 'json', pageUid: settings.defaultAjaxUid)}",
- rooms: "{ep:uri.ajax(action: 'rooms', controller: 'AjaxContingent', 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)}"
})'>
@@ -133,7 +131,7 @@
-
+
Loading...
@@ -153,17 +151,7 @@
-
+
@@ -250,9 +238,8 @@
-
-
Buchungsoptionen {hotel.name}
-
Termine & Leistungen
+
+
Buchungsoptionen {hotel.name}
@@ -304,10 +291,10 @@
-
-
|
@@ -338,6 +325,33 @@
+
+
+
@@ -385,30 +399,12 @@
|
-
-
-
-
-
-
+
|
|
+
+
+
+
+ ",
+ roomsEndpointUri: "
{ep:uri.ajax(action: 'rooms', controller: 'AjaxContingent', format: 'json', pageUid: settings.defaultAjaxUid)}"
+ })'>
+
+
+
+
+
+
+
+ | Zimmerart |
+ Preis |
+ inkl. |
+ |
+
+
+
+
+
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | {{ service }} |
+
+
+
+
+
+
+
+
+ |
+
+ Buchen
+ |
+
+
+
+
+
+ Loading...
+
+
+
+
+
+
+
+
diff --git a/tailwind.config.js b/tailwind.config.js
index 7048135f..b53a4b3d 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -64,6 +64,7 @@ module.exports = {
extend: {
colors: {
'overlay': {
+ 'black': tinycolor('#000000').setAlpha(0.85).toRgbString(),
'white': tinycolor('#ffffff').setAlpha(0.85).toRgbString(),
},
'ep': {
@@ -92,6 +93,9 @@ module.exports = {
'primary-80': tinycolor('#b5e400').setAlpha(0.8).toRgbString(),
},
},
+ height: {
+ '128': '32rem',
+ },
inset: {
'100': '100%',
'1/2': '50%',