Finalize migration of product detail page
This commit is contained in:
committed by
Björn Fromme
parent
5a63f88153
commit
06c67b4fad
@@ -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
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -1,272 +0,0 @@
|
||||
<template>
|
||||
<div class="date-select-wrapper">
|
||||
<div class="ep-headbar">{{ headerLabel }}</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<div class="date-select">
|
||||
<div class="date-select__picker">
|
||||
<div class="daterange__panel">
|
||||
<input ref="field" type="hidden">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<table class="table table-striped pricetable" v-show="availableRooms.length">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Zimmerart</th>
|
||||
<th>Preis</th>
|
||||
<th>inkl.</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(row, index) of availableRooms">
|
||||
<td>{{ row.label }}</td>
|
||||
<td style="white-space: nowrap">ab {{ calculatePrice(row.code) }} €</td>
|
||||
<td>
|
||||
<i v-if="row.skipassIncluded" aria-label="Skipass inklusive" role="tooltip" data-microtip-position="top" class="fa fa-tag"></i>
|
||||
<a href="#" data-toggle="modal"
|
||||
:data-target="'#services' + index"
|
||||
data-microtip-position="top" aria-label="Inklusivleistungen zeigen" role="tooltip">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
</a>
|
||||
<div class="modal fade" :id="'services' + index" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
<h5 class="modal-title">Inklusivleistungen</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<tbody>
|
||||
<tr v-for="service of row.servicesIncluded">
|
||||
<td><span class="glyphicon glyphicon-ok" aria-hidden="true"></span> {{ service }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a :href="row.bookingUrl" target="_blank"
|
||||
class="button button--small button--action">Buchen</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div v-show="loading" class="loader">
|
||||
Loading... <img :src="loaderUri" alt="Loading" title="Loading">
|
||||
</div>
|
||||
<div v-show="message">{{ message }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import $ from 'jquery'
|
||||
import axios from 'axios';
|
||||
import Vue from 'vue'
|
||||
import EventBus from '../_bus'
|
||||
import flatpickr from 'flatpickr'
|
||||
import { German } from 'flatpickr/dist/l10n/de'
|
||||
import dayjs from 'dayjs'
|
||||
import 'dayjs/locale/de'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
picker: null,
|
||||
displayedFrom: dayjs().startOf('month'),
|
||||
displayedTo: dayjs().endOf('month'),
|
||||
selectedFrom: null,
|
||||
selectedTo: null,
|
||||
selectedNumberOfNights: 0,
|
||||
minNightsForSelectedDate: 0,
|
||||
availableDates: [],
|
||||
availableRooms: []
|
||||
}
|
||||
},
|
||||
props: {
|
||||
hotelName: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
hotelUid: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
productUid: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
contingentEndpointUri: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
roomsEndpointUri: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
argumentPrefix: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
loaderUri: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
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 '';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onDateSelected (date) {
|
||||
this.selectedDate = dayjs(date);
|
||||
},
|
||||
fetchEnabledDates () {
|
||||
let query = {};
|
||||
query[this.argumentPrefix + '[hotel]'] = this.hotelUid;
|
||||
query[this.argumentPrefix + '[product]'] = this.productUid;
|
||||
this.loading = true;
|
||||
axios({
|
||||
url: this.contingentEndpointUri,
|
||||
method: 'post',
|
||||
data: $.param(query)
|
||||
}).then(response => {
|
||||
const json = response.data;
|
||||
this.loading = false;
|
||||
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]);
|
||||
}
|
||||
});
|
||||
},
|
||||
fetchAvailableRooms () {
|
||||
let query = {};
|
||||
query[this.argumentPrefix + '[hotel]'] = this.hotelUid;
|
||||
query[this.argumentPrefix + '[product]'] = this.productUid;
|
||||
query[this.argumentPrefix + '[dateFrom]'] = this.selectedFrom.format('YYYY-MM-DD');
|
||||
query[this.argumentPrefix + '[dateTo]'] = this.selectedTo.format('YYYY-MM-DD');
|
||||
this.loading = true;
|
||||
axios({
|
||||
url: this.roomsEndpointUri,
|
||||
method: 'post',
|
||||
data: $.param(query)
|
||||
}).then(response => {
|
||||
const json = response.data;
|
||||
this.loading = false;
|
||||
this.availableRooms = json;
|
||||
EventBus.$emit('tableLoaded');
|
||||
});
|
||||
},
|
||||
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');
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.picker = $(this.$refs.field).flatpickr({
|
||||
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');
|
||||
}
|
||||
}
|
||||
});
|
||||
Vue.nextTick(() => {
|
||||
this.fetchEnabledDates();
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.date-select-wrapper {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,225 +0,0 @@
|
||||
<template>
|
||||
<div class="dates-table-wrapper">
|
||||
<template v-if="showHeader">
|
||||
<div v-if="bookable" class="ep-headbar">{{ headerLabel }}</div>
|
||||
<div v-if="!bookable" class="ep-headbar">Termine & Leistungen</div>
|
||||
</template>
|
||||
<div class="dates-table__pricetoggle" v-if="hasSurcharge || hasDiscount">
|
||||
<a href="#" v-if="hasSurcharge" @click.prevent="toggleBus">
|
||||
<template v-if="addBus">
|
||||
<i class="fa fa-arrow-circle-right"></i> Preise für Eigenanreise anzeigen
|
||||
</template>
|
||||
<template v-else>
|
||||
<i class="fa fa-arrow-circle-right"></i> Preise für Busanreise anzeigen
|
||||
</template>
|
||||
</a>
|
||||
<a href="#" v-if="hasDiscount" @click.prevent="toggleDiscount">
|
||||
<template v-if="discountBus">
|
||||
<i class="fa fa-arrow-circle-right"></i> Preise für Busanreise anzeigen
|
||||
</template>
|
||||
<template v-else>
|
||||
<i class="fa fa-arrow-circle-right"></i> Preise für Eigenanreise anzeigen
|
||||
</template>
|
||||
</a>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped dates-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Termin</th>
|
||||
<th>
|
||||
<span data-microtip-position="bottom" aria-label="Nächte" role="tooltip">
|
||||
<i class="fa fa-bed"></i>
|
||||
</span>
|
||||
</th>
|
||||
<th>Preis ab</th>
|
||||
<th>inkl.</th>
|
||||
<th class="hidden-xs"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(row, index) of datesRows" :class="{ 'hidden': index > 4 && !showAll }">
|
||||
<td class="dates-table__cell dates-table__cell--date">
|
||||
<template v-if="bookable && row.available && !row.isHideBookingButton">
|
||||
<a class="dates-table__date" href="#"
|
||||
@click.prevent="loadPriceTable(row.dateUid)">
|
||||
{{ row.dateStart }} - {{ row.dateEnd }}
|
||||
</a>
|
||||
<div class="hidden-md hidden-lg dates-table__links">
|
||||
<a href="#" @click.prevent="loadPriceTable(row.dateUid)" class="button button--small">
|
||||
Details
|
||||
</a>
|
||||
<a :href="row.bookingUrl" target="_blank" class="button button--small button--action">
|
||||
Buchen
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>{{ row.dateStart }} - {{ row.dateEnd }}</template>
|
||||
</td>
|
||||
<td class="dates-table__cell dates-table__cell--nights">{{ row.dateNights }}</td>
|
||||
<td class="dates-table__cell dates-table__cell--price">
|
||||
<span class="dates-table__pseudo-price" v-if="row.datePseudoPrice">{{ row.datePseudoPrice }} €</span>
|
||||
<strong>{{ calculatePrice(row) }} €</strong>
|
||||
</td>
|
||||
<td class="dates-table__cell dates-table__cell--icons">
|
||||
<span class="icon-wrapper hidden-xs hidden-sm">
|
||||
<span v-if="showBusIcon(row)" data-microtip-position="top" aria-label="Busfahrt inklusive" role="tooltip">
|
||||
<i class="fa fa-bus"></i>
|
||||
</span>
|
||||
<span v-if="row.dateSkipassIncluded" data-microtip-position="top" :aria-label="row.season === 's' ? 'Bergbahnticket inklusive' : 'Skipass inklusive'" role="tooltip">
|
||||
<i class="fa fa-tag"></i>
|
||||
</span>
|
||||
</span>
|
||||
<button type="button" class="button button--light button--small hidden-xs"
|
||||
data-toggle="modal" :data-target="'#dateservices' + index"
|
||||
data-microtip-position="top" aria-label="Alle Inklusivleistungen anzeigen" role="tooltip">Leistungen</button>
|
||||
<a href="#" data-toggle="modal" :data-target="'#dateservices' + index"
|
||||
class="hidden-sm hidden-md hidden-lg"><i class="fa fa-info-circle"></i></a>
|
||||
</td>
|
||||
<td class="dates-table__cell dates-table__cell--buttons hidden-xs">
|
||||
<button v-if="row.available || row.isHideBookingButton" class="button button--small" @click="loadPriceTable(row.dateUid)">Details</button>
|
||||
<a :href="row.bookingUrl" target="_blank" v-if="row.available && !row.isHideBookingButton" class="button button--small button--action">Buchen</a>
|
||||
<a :href="travelAlertUrl" v-if="row.isHideBookingButton" class="button button--small button--mute">Reisen-Alert</a>
|
||||
<span v-if="!row.available && !row.isHideBookingButton" class="label label-default">ausgebucht</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="row dates-table__links" v-if="datesRows.length > 5 || altProductLink">
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<a href="#" v-if="datesRows.length > 5" @click.prevent="toggleAllDates()">
|
||||
<i class="fa" :class="[{ 'fa-minus-circle': showAll }, { 'fa-plus-circle': !showAll }]"></i>
|
||||
<template v-if="showAll">Weniger Termine anzeigen</template><template v-else>Alle Termine anzeigen</template>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<a :href="altProductLink" v-if="altProductLink" :title="altLabel" class="dates-table__altlink">
|
||||
<i class="fa fa-arrow-circle-right"></i> {{ altLabel }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<template v-for="(row, index) of datesRows">
|
||||
<div class="modal fade" :id="'dateservices' + index" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"><span>×</span></button>
|
||||
<h5 class="modal-title">Inklusivleistungen</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="table-responsive">
|
||||
<services-included :services="row.dateServicesIncluded" class="services"></services-included>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import $ from 'jquery'
|
||||
import EventBus from '../_bus'
|
||||
import ServicesIncluded from './ServicesListIncluded.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ServicesIncluded
|
||||
},
|
||||
props: {
|
||||
datesRows: {
|
||||
required: true
|
||||
},
|
||||
hotelName: {
|
||||
default: null
|
||||
},
|
||||
bookable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
altProductLink: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
altLabel: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
showHeader: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
travelAlertUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
showAll: false,
|
||||
addBus: false,
|
||||
discountBus: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
headerLabel () {
|
||||
let label = 'Buchungsoptionen';
|
||||
if (this.hotelName) {
|
||||
label += ' ' + this.hotelName;
|
||||
}
|
||||
return label;
|
||||
},
|
||||
hasSurcharge () {
|
||||
let result = false;
|
||||
$.each(this.datesRows, (index, row) => {
|
||||
if (row.dateBusPrice > 0) {
|
||||
result = true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
hasDiscount () {
|
||||
let result = false;
|
||||
$.each(this.datesRows, (index, row) => {
|
||||
if (row.dateDiscount < 0) {
|
||||
result = true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadPriceTable (dateUid) {
|
||||
this.showAll = false;
|
||||
EventBus.$emit('loadPriceTable', dateUid);
|
||||
},
|
||||
toggleBus () {
|
||||
this.addBus = !this.addBus;
|
||||
},
|
||||
toggleDiscount () {
|
||||
this.discountBus = !this.discountBus;
|
||||
},
|
||||
toggleAllDates () {
|
||||
this.showAll = !this.showAll;
|
||||
},
|
||||
calculatePrice (row) {
|
||||
if (this.addBus) {
|
||||
return row.dateMinPrice + row.dateBusPrice;
|
||||
} else if (this.discountBus) {
|
||||
return row.dateMinPrice + row.dateDiscount;
|
||||
}
|
||||
return row.dateMinPrice;
|
||||
},
|
||||
showBusIcon (row) {
|
||||
return (
|
||||
row.dateBusPrice && this.addBus ||
|
||||
row.dateBusIncluded && !this.discountBus
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,211 +0,0 @@
|
||||
<template>
|
||||
<div class="price-table-wrapper">
|
||||
<div class="row ep-headbar" v-if="showHeader">
|
||||
<div class="col-xs-6">Buchungsoptionen</div>
|
||||
<div class="col-xs-6 text-right">{{dateLabel}}</div>
|
||||
</div>
|
||||
<div class="dates-table__pricetoggle" v-if="hasSurcharge || hasDiscount">
|
||||
<a href="#" v-if="hasSurcharge" @click.prevent="toggleBus">
|
||||
<template v-if="addBus">
|
||||
<i class="fa fa-car"></i> Preise für Eigenanreise anzeigen
|
||||
</template>
|
||||
<template v-else>
|
||||
<i class="fa fa-bus"></i> Preise für Busanreise anzeigen
|
||||
</template>
|
||||
</a>
|
||||
<a href="#" v-if="hasDiscount" @click.prevent="toggleDiscount">
|
||||
<template v-if="discountBus">
|
||||
<i class="fa fa-bus"></i> Preise für Busanreise anzeigen
|
||||
</template>
|
||||
<template v-else>
|
||||
<i class="fa fa-car"></i> Preise für Eigenanreise anzeigen
|
||||
</template>
|
||||
</a>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped pricetable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Zimmertyp</th>
|
||||
<th>Preis</th>
|
||||
<th>inkl.</th>
|
||||
<th></th>
|
||||
<th class="hidden-xs hidden-md"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(row, index) of priceTableRows">
|
||||
<td style="white-space: normal">
|
||||
<template v-if="row.isHideBookingButton || !row.roomAvailable">
|
||||
{{ row.roomName }}
|
||||
</template>
|
||||
<template v-else>
|
||||
<a class="pricetable__room-label"
|
||||
:href="row.bookingUrl" target="_blank">{{ row.roomName }}</a>
|
||||
<div class="hidden-lg">
|
||||
<a class="button button--small button--action"
|
||||
:href="row.bookingUrl" target="_blank">Jetzt buchen</a>
|
||||
</div>
|
||||
</template>
|
||||
<div class="hidden-lg" v-if="!row.roomAvailable">
|
||||
<span class="label label-default">ausgebucht</span>
|
||||
</div>
|
||||
</td>
|
||||
<td style="white-space: nowrap" :style="{'text-decoration': !row.roomAvailable && !row.isHideBookingButton ? 'line-through' : ''}">{{ calculatePrice(row) }} €</td>
|
||||
<td>
|
||||
<span v-if="showBusIcon(row)" data-microtip-position="top" aria-label="Busfahrt inklusive" role="tooltip">
|
||||
<i class="fa fa-bus"></i>
|
||||
</span>
|
||||
<span v-if="row.dateSkipassIncluded" data-microtip-position="top" :aria-label="row.season === 's' ? 'Bergbahnticket inklusive' : 'Skipass inklusive'" role="tooltip">
|
||||
<i class="fa fa-tag"></i>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<button v-if="hasOptionalServices(row) && row.roomAvailable" type="button"
|
||||
class="button button--light button--small hidden-xs"
|
||||
data-toggle="modal" :data-target="'#optservices' + index"
|
||||
data-microtip-position="top" aria-label="Alle Zusatzleistungen anzeigen" role="tooltip">Optionen</button>
|
||||
<a href="#" v-if="hasOptionalServices(row) && row.roomAvailable" class="hidden-sm hidden-md hidden-lg"
|
||||
data-toggle="modal" :data-target="'#optservices' + index"><i class="fa fa-info-circle"></i></a>
|
||||
<div class="modal fade" :id="'optservices' + index" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"><span>×</span></button>
|
||||
<h5 class="modal-title">Optionale Zusatzleistungen</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="table-responsive">
|
||||
<services-optional :services="row.roomOptionalServices" class="services"></services-optional>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="hidden-xs hidden-md">
|
||||
<a class="button button--small button--action" v-if="row.roomAvailable && !row.isHideBookingButton"
|
||||
:href="row.bookingUrl" target="_blank">Buchen</a>
|
||||
<a class="button button--small button--mute" v-if="row.isHideBookingButton"
|
||||
:href="travelAlertUrl">Reisen-Alert</a>
|
||||
<span v-if="!row.roomAvailable && !row.isHideBookingButton" class="label label-default">ausgebucht</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p v-if="!singleDate">
|
||||
<a class="button button--small"
|
||||
href="#" @click.prevent="loadDatesTable()"><i class="fa fa-undo"></i> zurück zur Terminübersicht</a>
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="ep-headbar">Inklusivleistungen</div>
|
||||
<div class="ep-box">
|
||||
<ul class="list-unstyled">
|
||||
<li v-for="service of servicesIncluded"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span> {{service}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ServicesIncluded from './ServicesListIncluded.vue'
|
||||
import ServicesOptional from './ServicesListOptional.vue'
|
||||
import $ from 'jquery'
|
||||
import EventBus from '../_bus'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ServicesIncluded,
|
||||
ServicesOptional
|
||||
},
|
||||
props: {
|
||||
priceTableRows: {
|
||||
required: true
|
||||
},
|
||||
templateSuffix: {
|
||||
type: String,
|
||||
default: '_ep'
|
||||
},
|
||||
dateLabel: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
servicesIncluded: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
singleDate: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showHeader: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
travelAlertUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
addBus: false,
|
||||
discountBus: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadDatesTable () {
|
||||
EventBus.$emit('loadDatesTable');
|
||||
},
|
||||
hasOptionalServices (row) {
|
||||
return !$.isEmptyObject(row.roomOptionalServices);
|
||||
},
|
||||
toggleBus () {
|
||||
this.addBus = !this.addBus;
|
||||
},
|
||||
toggleDiscount () {
|
||||
this.discountBus = !this.discountBus;
|
||||
},
|
||||
calculatePrice (row) {
|
||||
if (this.addBus) {
|
||||
return row.roomPrice + row.roomBusPrice;
|
||||
} else if (this.discountBus) {
|
||||
return row.roomPrice + row.roomDiscount;
|
||||
}
|
||||
return row.roomPrice;
|
||||
},
|
||||
showBusIcon (row) {
|
||||
return (
|
||||
row.roomBusPrice && this.addBus ||
|
||||
row.dateBusIncluded && !this.discountBus
|
||||
);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasSurcharge () {
|
||||
let result = false;
|
||||
$.each(this.priceTableRows, (index, row) => {
|
||||
if (row.roomBusPrice > 0) {
|
||||
result = true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
hasDiscount () {
|
||||
let result = false;
|
||||
$.each(this.priceTableRows, (index, row) => {
|
||||
if (row.roomDiscount < 0) {
|
||||
result = true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
-215
@@ -1,215 +0,0 @@
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import $ from 'jquery'
|
||||
import axios from 'axios';
|
||||
import EventBus from '../_bus'
|
||||
import DatesTable from './DatesTable.vue'
|
||||
import PriceTable from './PriceTable.vue'
|
||||
import ServicesIncluded from './ServicesListIncluded.vue'
|
||||
import ServicesOptional from './ServicesListOptional.vue'
|
||||
import DateSelect from './DateSelect.vue'
|
||||
import DaterangeSelect from './DaterangeSelect.vue'
|
||||
import Calendar from './Calendar.vue'
|
||||
import OsmMap from './OsmMap.vue'
|
||||
import dayjs from 'dayjs'
|
||||
import 'dayjs/locale/de'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DatesTable,
|
||||
PriceTable,
|
||||
ServicesIncluded,
|
||||
ServicesOptional,
|
||||
DateSelect,
|
||||
DaterangeSelect,
|
||||
Calendar,
|
||||
OsmMap,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
datesRows: {},
|
||||
available: true,
|
||||
altLabel: null,
|
||||
altProductLink: null,
|
||||
priceTableRows: {},
|
||||
dateStart: '',
|
||||
dateEnd: '',
|
||||
servicesIncluded: [],
|
||||
detailHtml: '',
|
||||
bookable: true,
|
||||
datesShow: false,
|
||||
priceTableShow: false,
|
||||
detailShow: false,
|
||||
loading: false,
|
||||
activeSection: null,
|
||||
singleDate: false,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
argumentPrefix: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
uris: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
hotelFirst: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
busProId: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
nameInternal: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
daytrip: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
dateFrom: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
dateTo: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
dateLabel () {
|
||||
return this.dateStart + ' - ' + this.dateEnd;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadDates () {
|
||||
this.loading = true;
|
||||
this.datesShow = true;
|
||||
this.priceTableShow = false;
|
||||
this.detailShow = false;
|
||||
const data = $.param({
|
||||
'tx_epproducts_ajax[paCode]': this.$store.state.config.paCode,
|
||||
'tx_epproducts_ajax[dateFrom]': this.dateFrom,
|
||||
'tx_epproducts_ajax[dateTo]': this.dateTo
|
||||
});
|
||||
axios({
|
||||
url: this.uris['dates'],
|
||||
method: 'post',
|
||||
data: data
|
||||
}).then(response => {
|
||||
const json = response.data;
|
||||
if (json.dates.length === 1) {
|
||||
this.singleDate = true;
|
||||
let dateRow = json.dates[0];
|
||||
this.loadPriceTableView(dateRow.dateUid);
|
||||
} else {
|
||||
this.singleDate = false;
|
||||
this.datesRows = json.dates;
|
||||
this.bookable = json.bookable;
|
||||
this.altLabel = json.altLabel;
|
||||
this.altProductLink = json.altProductLink;
|
||||
}
|
||||
this.available = json.dates.length > 0;
|
||||
this.loading = false;
|
||||
EventBus.$emit('tableLoaded');
|
||||
});
|
||||
},
|
||||
loadDatesView (scroll = true) {
|
||||
if (this.daytrip) {
|
||||
this.loading = false;
|
||||
this.datesShow = true;
|
||||
} else {
|
||||
this.loadDates();
|
||||
}
|
||||
this.activeSection = 'dates';
|
||||
if (scroll) {
|
||||
this.scrollToMain();
|
||||
}
|
||||
},
|
||||
loadPriceTable (dateUid) {
|
||||
this.loading = true;
|
||||
this.datesShow = false;
|
||||
this.priceTableShow = true;
|
||||
this.detailShow = false;
|
||||
let query = {};
|
||||
query[this.argumentPrefix + '[date]'] = dateUid;
|
||||
query[this.argumentPrefix + '[paCode]'] = this.$store.state.config.paCode;
|
||||
axios({
|
||||
url: this.uris['pricetable'],
|
||||
method: 'post',
|
||||
data: $.param(query)
|
||||
}).then(response => {
|
||||
const json = response.data;
|
||||
this.priceTableRows = json.priceTable;
|
||||
this.dateStart = dayjs(json.dateStart).format('DD.MM.YYYY');
|
||||
this.dateEnd = dayjs(json.dateEnd).format('DD.MM.YYYY');
|
||||
this.servicesIncluded = json.servicesIncluded;
|
||||
this.loading = false;
|
||||
EventBus.$emit('tableLoaded');
|
||||
});
|
||||
},
|
||||
loadPriceTableView (dateuid) {
|
||||
this.loadPriceTable(dateuid);
|
||||
this.scrollToMain();
|
||||
},
|
||||
showHtml (section, scroll = true) {
|
||||
this.datesShow = false;
|
||||
this.priceTableShow = false;
|
||||
this.detailShow = true;
|
||||
if (scroll) {
|
||||
this.scrollToMain();
|
||||
}
|
||||
this.activeSection = section;
|
||||
},
|
||||
scrollToMain () {
|
||||
EventBus.$emit('scrollTo', '#main');
|
||||
},
|
||||
trackConversion () {
|
||||
const dataLayer = window.dataLayer || [];
|
||||
dataLayer.push({
|
||||
'google_tag_params': {
|
||||
'travel_destid': this.busProId,
|
||||
'travel_originid': this.busProId,
|
||||
'travel_pagetype': 'offerdetails',
|
||||
'travel_startdate': this.dateStart,
|
||||
'travel_enddate': this.dateEnd,
|
||||
'travel_totalvalue': this.calculateMinPriceFromPriceTable() + '.00 EUR'
|
||||
}
|
||||
});
|
||||
},
|
||||
calculateMinPriceFromPriceTable () {
|
||||
let minPrice = 0;
|
||||
for (let index in this.priceTableRows) {
|
||||
if (!this.priceTableRows.hasOwnProperty(index)) {
|
||||
continue;
|
||||
}
|
||||
if (minPrice === 0 || this.priceTableRows[index].roomPrice < minPrice) {
|
||||
minPrice = this.priceTableRows[index].roomPrice;
|
||||
}
|
||||
}
|
||||
return minPrice;
|
||||
}
|
||||
},
|
||||
created () {
|
||||
EventBus.$on('loadDatesTable', () => {
|
||||
this.loadDatesView();
|
||||
});
|
||||
EventBus.$on('loadPriceTable', (dateUid) => {
|
||||
this.loadPriceTableView(dateUid);
|
||||
});
|
||||
},
|
||||
mounted () {
|
||||
Vue.nextTick(() => {
|
||||
this.trackConversion();
|
||||
if (this.hotelFirst) {
|
||||
this.showHtml('hotel', false);
|
||||
} else {
|
||||
this.loadDatesView(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
<template>
|
||||
<table class="table table-striped">
|
||||
<tbody>
|
||||
<tr v-for="service of services">
|
||||
<td><span class="glyphicon glyphicon-ok" aria-hidden="true"></span> {{ service }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
services: {
|
||||
type: Array,
|
||||
default () {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
<template>
|
||||
<table class="table table-striped">
|
||||
<tbody>
|
||||
<template v-for="(section, key) of sections">
|
||||
<template v-if="(key in services)">
|
||||
<tr>
|
||||
<th colspan="2"><strong>{{ section }}</strong></th>
|
||||
</tr>
|
||||
<tr v-for="service of services[key]">
|
||||
<td>{{ service.label }}</td>
|
||||
<td style="text-align: right; white-space: nowrap">{{ service.price }} €</td>
|
||||
</tr>
|
||||
</template>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
services: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {
|
||||
BUS: [],
|
||||
VPF: [],
|
||||
SPA: [],
|
||||
KUR: [],
|
||||
VER: [],
|
||||
SON: [],
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
sections: {
|
||||
BUS: 'Anreise',
|
||||
VPF: 'Verpflegung',
|
||||
SPA: 'Skipass',
|
||||
KUR: 'Kurse',
|
||||
VER: 'Verleih',
|
||||
SON: 'Sonstiges',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
+153
@@ -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');
|
||||
}
|
||||
})
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<html data-namespace-typo3-fluid="true" lang="en"
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
|
||||
|
||||
<div x-data x-show="$store.show.modal === '{id}'" x-transition x-cloak class="fixed inset-0 w-full h-full z-50">
|
||||
<div class="absolute inset-0 w-full h-full bg-overlay-black" x-on:click="$store.show.modal = null"></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">
|
||||
<template x-if="$store.modal.title">
|
||||
<span class="text-lg font-bold">
|
||||
{title -> f:format.raw()}
|
||||
</span>
|
||||
</template>
|
||||
<button x-on:click.prevent="$store.show.modal = null" class="inline-block">
|
||||
<svg class="w-8 h-8"><use href="#icon-close"></use></svg>
|
||||
</button>
|
||||
</div>
|
||||
{content -> f:format.raw()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</html>
|
||||
@@ -1,173 +0,0 @@
|
||||
<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">
|
||||
<f:variable name="rowCount" value="{rows -> f:count()}"/>
|
||||
<f:if condition="{rowCount} > 0">
|
||||
<div class="dates-table-wrapper" x-data="datesTable">
|
||||
<f:if condition="{showHeader}">
|
||||
<f:if condition="{bookable}">
|
||||
<f:then>
|
||||
<div class="ep-headbar">Buchungsoptionen{f:if(condition: hotelName, then: ' {hotelName}')}</div>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<div class="ep-headbar">Termine & Leistungen</div>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</f:if>
|
||||
<f:if condition="{hasSourcharge} || {hasDiscount}">
|
||||
<div class="dates-table__pricetoggle">
|
||||
<f:if condition="{hasSurcharge}">
|
||||
<a href="#" x-on:click.prevent="addBus = !addBus" x-show="addBus">
|
||||
<i class="fa fa-arrow-circle-right"></i> Preise für Eigenanreise anzeigen
|
||||
</a>
|
||||
<a href="#" x-on:click.prevent="addBus = !addBus" x-show="! addBus">
|
||||
<i class="fa fa-arrow-circle-right"></i> Preise für Busanreise anzeigen
|
||||
</a>
|
||||
</f:if>
|
||||
<f:if condition="{hasDiscount}">
|
||||
<a href="#" x-on:click.prevent="discountBus = !discountBus" x-show="discountBus">
|
||||
<i class="fa fa-arrow-circle-right"></i> Preise für Busanreise anzeigen
|
||||
</a>
|
||||
<a href="#" x-on:click.prevent="discountBus = !discountBus" x-show="! discountBus">
|
||||
<i class="fa fa-arrow-circle-right"></i> Preise für Eigenanreise anzeigen
|
||||
</a>
|
||||
</f:if>
|
||||
</div>
|
||||
</f:if>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped dates-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Termin</th>
|
||||
<th>
|
||||
<span data-microtip-position="bottom" aria-label="Nächte" role="tooltip">
|
||||
<i class="fa fa-bed"></i>
|
||||
</span>
|
||||
</th>
|
||||
<th>Preis ab</th>
|
||||
<th>inkl.</th>
|
||||
<th class="hidden-xs"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<f:for each="{rows}" as="row" iteration="iteration">
|
||||
<tr{f:if(condition: '{iteration.index} > 4', then: ' x-bind:class="{ \'hidden\': !showAll }"')}>
|
||||
<td class="dates-table__cell dates-table__cell--date">
|
||||
<f:if condition="{bookable} && {row.available} && {row.showBookingButton}">
|
||||
<f:then>
|
||||
<button class="dates-table__date"
|
||||
x-on:click.prevent="loadPrices({row.dateUid})">
|
||||
{row.dateStart} - {row.dateEnd}
|
||||
</button>
|
||||
<div class="hidden-md hidden-lg dates-table__links">
|
||||
<button x-on:click.prevent="loadPrices({row.dateUid})" class="button button--small">
|
||||
Details
|
||||
</button>
|
||||
<a href="{row.bookingUrl}" target="_blank" class="button button--small button--action">
|
||||
Buchen
|
||||
</a>
|
||||
</div>
|
||||
</f:then>
|
||||
<f:else>
|
||||
{row.dateStart} - {row.dateEnd}
|
||||
</f:else>
|
||||
</f:if>
|
||||
</td>
|
||||
<td class="dates-table__cell dates-table__cell--nights">
|
||||
{row.dateNights}
|
||||
</td>
|
||||
<td class="dates-table__cell dates-table__cell--price">
|
||||
<f:if condition="{row.datePseudoPrice}">
|
||||
<span class="dates-table__pseudo-price">
|
||||
{row.datePseudoPrice} €
|
||||
</span>
|
||||
</f:if>
|
||||
<strong x-text="calculatePrice({row.dateMinPrice}, {row.dateBusPrice}, {row.dateDiscount}) + '€'"></strong>
|
||||
</td>
|
||||
<td class="dates-table__cell dates-table__cell--icons">
|
||||
<div class="icon-wrapper hidden-xs hidden-sm">
|
||||
<span x-show="showBusIcon({row.dateBusPrice}, {row.dateBusIncluded})" data-microtip-position="top" aria-label="Busfahrt inklusive" role="tooltip">
|
||||
<i class="fa fa-bus"></i>
|
||||
</span>
|
||||
<span x-show="{row.dateSkipassIncluded}" data-microtip-position="top" aria-label="{f:if(condition: '{row.season} =="s"', then: 'Bergbahnticket inklusive', else: 'Skipass inklusive')}" role="tooltip">
|
||||
<i class="fa fa-tag"></i>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="button button--light button--small hidden-xs"
|
||||
data-toggle="modal"
|
||||
data-microtip-position="top" aria-label="Alle Inklusivleistungen anzeigen" role="tooltip">Leistungen</button>
|
||||
<a href="#" data-toggle="modal"
|
||||
class="hidden-sm hidden-md hidden-lg"><i class="fa fa-info-circle"></i></a>
|
||||
</td>
|
||||
<td class="dates-table__cell dates-table__cell--buttons hidden-xs">
|
||||
<f:if condition="{row.available} || {row.isHideBookingButton}">
|
||||
<button class="button button--small" x-on:click="loadPrices({row.dateUid})">
|
||||
Details
|
||||
</button>
|
||||
</f:if>
|
||||
<f:if condition="{row.available} && {row.showBookingButton}">
|
||||
<f:link.typolink parameter="{row.bookingUrl}" target="_blank" class="button button--small button--action">
|
||||
Buchen
|
||||
</f:link.typolink>
|
||||
</f:if>
|
||||
<f:if condition="{row.isHideBookingButton}">
|
||||
<f:link.typolink parameter="{travelAlertUrl}" class="button button--small button--mute">
|
||||
Reisen-Alert
|
||||
</f:link.typolink>
|
||||
</f:if>
|
||||
<f:if condition="{row.unavailable} && {row.showBookingButton}">
|
||||
<span class="label label-default">ausgebucht</span>
|
||||
</f:if>
|
||||
</td>
|
||||
</tr>
|
||||
</f:for>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<f:if condition="{rowCount} > 5 || {altProductLink}">
|
||||
<div class="row dates-table__links">
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<f:if condition="{rows -> f:count()} > 5">
|
||||
<a href="#" x-on:click.prevent="showAll = !showAll">
|
||||
<i class="fa" x-bind:class="[{ 'fa-minus-circle': showAll }, { 'fa-plus-circle': !showAll }]"></i>
|
||||
<template v-show="showAll">Weniger Termine anzeigen</template>
|
||||
<template v-show="!showAll">Alle Termine anzeigen</template>
|
||||
</a>
|
||||
</f:if>
|
||||
</div>
|
||||
<f:if condition="{altProductLink}">
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<f:link.typolink parameter="{altProductLink}" class="dates-table__altlink">
|
||||
<i class="fa fa-arrow-circle-right"></i> {altLabel}
|
||||
</f:link.typolink>
|
||||
</div>
|
||||
</f:if>
|
||||
</div>
|
||||
</f:if>
|
||||
<!-- ToDo: Implement modals -->
|
||||
<template v-for="(row, index) of datesRows">
|
||||
<div class="modal fade" :id="'dateservices' + index" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"><span>×</span></button>
|
||||
<h5 class="modal-title">Inklusivleistungen</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="table-responsive">
|
||||
<services-included :services="row.dateServicesIncluded" class="services"></services-included>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</f:if>
|
||||
</f:section>
|
||||
|
||||
</html>
|
||||
@@ -1,146 +0,0 @@
|
||||
<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="price-table-wrapper" x-data="datesTable">
|
||||
<div class="row ep-headbar">
|
||||
<div class="col-xs-6">Buchungsoptionen</div>
|
||||
<div class="col-xs-6 text-right">{dateStart} - {dateEnd}</div>
|
||||
</div>
|
||||
<f:if condition="{hasSurcharge} || {hasDiscount}">
|
||||
<div class="dates-table__pricetoggle">
|
||||
<f:if condition="{hasSurcharge}">
|
||||
<a href="#" x-on:click.prevent="addBus = !addBus" x-show="addBus">
|
||||
<i class="fa fa-car"></i> Preise für Eigenanreise anzeigen
|
||||
</a>
|
||||
<a href="#" x-on:click.prevent="addBus = !addBus" x-show="! addBus">
|
||||
<i class="fa fa-bus"></i> Preise für Busanreise anzeigen
|
||||
</a>
|
||||
</f:if>
|
||||
<f:if condition="{hasDiscount}">
|
||||
<a href="#" @click.prevent="discountBus = !discountBus" x-show="discountBus">
|
||||
<i class="fa fa-bus"></i> Preise für Busanreise anzeigen
|
||||
</a>
|
||||
<a href="#" @click.prevent="discountBus = !discountBus" x-show="! discountBus">
|
||||
<i class="fa fa-car"></i> Preise für Eigenanreise anzeigen
|
||||
</a>
|
||||
</f:if>
|
||||
</div>
|
||||
</f:if>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped pricetable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Zimmertyp</th>
|
||||
<th>Preis</th>
|
||||
<th>inkl.</th>
|
||||
<th></th>
|
||||
<th class="hidden-xs hidden-md"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<f:for each="{rows}" as="row" iteration="iteration">
|
||||
<tr>
|
||||
<td style="white-space: normal">
|
||||
<f:if condition="{row.showBookingButton} && {row.roomAvailable}">
|
||||
<f:then>
|
||||
<a class="pricetable__room-label" href="{row.bookingUrl}" target="_blank">{row.roomName}</a>
|
||||
<div class="hidden-lg">
|
||||
<a class="button button--small button--action" href="{row.bookingUrl}" target="_blank">Jetzt buchen</a>
|
||||
</div>
|
||||
</f:then>
|
||||
<f:else>
|
||||
{row.roomName}
|
||||
</f:else>
|
||||
</f:if>
|
||||
<f:if condition="{row.roomAvailable}">
|
||||
<f:else>
|
||||
<div class="hidden-lg">
|
||||
<span class="label label-default">ausgebucht</span>
|
||||
</div>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</td>
|
||||
<td style="white-space: nowrap{f:if(condition: '{row.roomAvailable} && {row.showBookingButton}', else: ' line-through')}"
|
||||
x-text="calculatePrice({row.roomPrice}, {row.roomBusPrice}, {row.roomDiscount}) + '€'"></td>
|
||||
<td>
|
||||
<span x-show="showBusIcon({row.roomBusPrice}, {row.roomBusIncluded})" data-microtip-position="top" aria-label="Busfahrt inklusive" role="tooltip">
|
||||
<i class="fa fa-bus"></i>
|
||||
</span>
|
||||
<f:if condition="{row.dateSkipassIncluded}">
|
||||
<span data-microtip-position="top" aria-label="{f:if(condition: '{row.season} == \'s\'', then: 'Bergbahnticket inklusive', else: 'Skipass inklusive')}" role="tooltip">
|
||||
<i class="fa fa-tag"></i>
|
||||
</span>
|
||||
</f:if>
|
||||
</td>
|
||||
<td>
|
||||
<f:if condition="{row.roomAvailable} && {row.roomOptionalServices -> f:count()} > 0">
|
||||
<button class="button button--light button--small hidden-xs"
|
||||
data-toggle="modal" data-target="'#optservices' + index"
|
||||
data-microtip-position="top" aria-label="Alle Zusatzleistungen anzeigen" role="tooltip">Optionen</button>
|
||||
<a href="#" class="hidden-sm hidden-md hidden-lg"
|
||||
data-toggle="modal" data-target="'#optservices' + index"><i class="fa fa-info-circle"></i></a>
|
||||
</f:if>
|
||||
<!-- ToDo: Implement modals -->
|
||||
<div class="modal fade" id="'optservices' + index" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"><span>×</span></button>
|
||||
<h5 class="modal-title">Optionale Zusatzleistungen</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="table-responsive">
|
||||
<services-optional services="row.roomOptionalServices" class="services"></services-optional>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="hidden-xs hidden-md">
|
||||
<f:if condition="{row.roomAvailable} && {row.showBookingButton}">
|
||||
<a class="button button--small button--action"
|
||||
href="{row.bookingUrl}" target="_blank">Buchen</a>
|
||||
</f:if>
|
||||
<f:if condition="{row.isHideBookingButton}">
|
||||
<a class="button button--small button--mute" v-if="row.isHideBookingButton"
|
||||
href="{travelAlertUrl}">Reisen-Alert</a>
|
||||
</f:if>
|
||||
<f:if condition="{row.roomAvailable} || {row.isHideBookingButton}">
|
||||
<f:else>
|
||||
<span class="label label-default">ausgebucht</span>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</td>
|
||||
</tr>
|
||||
</f:for>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<f:if condition="{rows -> f:count()} > 1">
|
||||
<p>
|
||||
<button class="button button--small" x-on:click.prevent="tableMode = 'dates'">
|
||||
<i class="fa fa-undo"></i> zurück zur Terminübersicht
|
||||
</button>
|
||||
</p>
|
||||
</f:if>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="ep-headbar">Inklusivleistungen</div>
|
||||
<div class="ep-box">
|
||||
<ul class="list-unstyled">
|
||||
<f:for each="{servicesIncluded}" as="service">
|
||||
<li><span class="glyphicon glyphicon-ok" aria-hidden="true"></span> {service}</li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</f:section>
|
||||
|
||||
</html>
|
||||
@@ -11,8 +11,6 @@
|
||||
<f:render partial="Product/HeaderDetail" arguments="{_all}"/>
|
||||
<div class="container" id="detail" x-data='productDetail({
|
||||
dates: "<f:format.raw>{ep:uri.ajax(action: 'dates', controller: 'AjaxDate', arguments: '{product: product, hotel: hotel}', format: 'json', pageUid: settings.defaultAjaxUid)}</f:format.raw>",
|
||||
contingents: "<f:format.raw>{ep:uri.ajax(action: 'list', controller: 'AjaxContingent', format: 'json', pageUid: settings.defaultAjaxUid)}</f:format.raw>",
|
||||
rooms: "<f:format.raw>{ep:uri.ajax(action: 'rooms', controller: 'AjaxContingent', format: 'json', pageUid: settings.defaultAjaxUid)}</f:format.raw>",
|
||||
pricetable: "<f:format.raw>{ep:uri.ajax(action: 'pricetable', controller: 'AjaxTable', arguments: '{product: product, hotel: hotel}', format: 'html', pageUid: settings.defaultAjaxUid)}</f:format.raw>",
|
||||
travelAlert: "<f:format.raw>{f:uri.typolink(parameter: settings.travelAlertPageUid)}</f:format.raw>"
|
||||
})'>
|
||||
@@ -133,7 +131,7 @@
|
||||
</div>
|
||||
<div id="main" class="col-md-8 col-md-pull-4">
|
||||
<!-- Loader -->
|
||||
<div x-show="loading" class="flex space-x-2 mb-8">
|
||||
<div x-show="loading" class="flex items-center space-x-2 mb-8">
|
||||
<f:image class="block w-4 h-4" src="EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif" alt="Loading"/>
|
||||
<span>Loading...</span>
|
||||
</div>
|
||||
@@ -153,17 +151,7 @@
|
||||
</f:if>
|
||||
<f:if condition="{product.daytrip}">
|
||||
<f:then>
|
||||
<f:comment><!--
|
||||
<date-select
|
||||
hotel-name="{hotel.name}"
|
||||
:hotel-uid="{hotel.uid}"
|
||||
:product-uid="{product.uid}"
|
||||
:contingent-endpoint-uri="uris.contingents"
|
||||
:rooms-endpoint-uri="uris.rooms"
|
||||
:argument-prefix="argumentPrefix"
|
||||
loader-uri="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif')}"
|
||||
></date-select>
|
||||
--></f:comment>
|
||||
<f:render section="DaytripDateSelect" arguments="{_all}"/>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<div x-show="tableMode === 'dates'">
|
||||
@@ -250,9 +238,8 @@
|
||||
</f:section>
|
||||
|
||||
<f:section name="DatesTable">
|
||||
<div class="dates-table-wrapper">
|
||||
<div x-show="bookable" class="ep-headbar">Buchungsoptionen {hotel.name}</div>
|
||||
<div x-show="!bookable" class="ep-headbar">Termine & Leistungen</div>
|
||||
<div class="dates-table-wrapper" x-show="bookable">
|
||||
<div class="ep-headbar">Buchungsoptionen {hotel.name}</div>
|
||||
<f:render section="PriceToggle"/>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped dates-table">
|
||||
@@ -304,10 +291,10 @@
|
||||
<i class="fa fa-tag"></i>
|
||||
</span>
|
||||
</span>
|
||||
<button type="button" class="button button--light button--small hidden-xs"
|
||||
data-toggle="modal" data-target="'#dateservices' + index"
|
||||
<button class="button button--light button--small hidden-xs"
|
||||
x-on:click.prevent="$store.show.modal = 'services-' + row.dateUid"
|
||||
data-microtip-position="top" aria-label="Alle Inklusivleistungen anzeigen" role="tooltip">Leistungen</button>
|
||||
<a href="#" data-toggle="modal" data-target="'#dateservices' + index"
|
||||
<a href="#" x-on:click.prevent="$store.show.modal = 'services-' + row.dateUid"
|
||||
class="hidden-sm hidden-md hidden-lg"><i class="fa fa-info-circle"></i></a>
|
||||
</td>
|
||||
<td class="dates-table__cell dates-table__cell--buttons hidden-xs">
|
||||
@@ -338,6 +325,33 @@
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template x-for="row in datesRows">
|
||||
<div x-bind:id="'services-' + row.dateUid" x-show="$store.show.modal === $el.id" x-transition x-cloak class="fixed inset-0 w-full h-full z-50">
|
||||
<div class="absolute inset-0 w-full h-full bg-overlay-black" x-on:click="$store.show.modal = null"></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 x-on:click.prevent="$store.show.modal = null" 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>
|
||||
|
||||
@@ -385,30 +399,12 @@
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
<button x-show="hasOptionalServices(row) && row.roomAvailable"
|
||||
<button x-show="row.roomHasOptionalServices && row.roomAvailable"
|
||||
class="button button--light button--small hidden-xs"
|
||||
data-toggle="modal" data-target="'#optservices' + index"
|
||||
x-on:click.prevent="$store.show.modal = 'optional-services-' + row.roomUid"
|
||||
data-microtip-position="top" aria-label="Alle Zusatzleistungen anzeigen" role="tooltip">Optionen</button>
|
||||
<a href="#" x-show="hasOptionalServices(row) && row.roomAvailable" class="hidden-sm hidden-md hidden-lg"
|
||||
data-toggle="modal" data-target="'#optservices' + index"><i class="fa fa-info-circle"></i></a>
|
||||
|
||||
<div class="modal fade" :id="'optservices' + index" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"><span>×</span></button>
|
||||
<h5 class="modal-title">Optionale Zusatzleistungen</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="table-responsive">
|
||||
<services-optional :services="row.roomOptionalServices" class="services"></services-optional>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="#" x-show="row.roomHasOptionalServices && row.roomAvailable" class="hidden-sm hidden-md hidden-lg"
|
||||
x-on:click.prevent="$store.show.modal = 'optional-services-' + row.roomUid"><i class="fa fa-info-circle"></i></a>
|
||||
</td>
|
||||
<td class="hidden-xs hidden-md">
|
||||
<a class="button button--small button--action" x-show="row.roomAvailable && !row.isHideBookingButton"
|
||||
@@ -422,6 +418,32 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<template x-for="row in pricesRows">
|
||||
<div x-bind:id="'optional-services-' + row.roomUid" x-show="$store.show.modal === $el.id" x-transition x-cloak class="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" x-on:click="$store.show.modal = null"></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 border-b border-gray-200 pb-2 mb-4">
|
||||
<span class="text-2xl font-bold">
|
||||
Optionale Zusatzleistungen
|
||||
</span>
|
||||
<button x-on:click.prevent="$store.show.modal = null" 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="pb-2 border-t border-gray-200 first:border-0">
|
||||
<div class="font-bold text-xl" x-text="section.label"></div>
|
||||
<template x-for="optionalService in section.services">
|
||||
<dl class="flex items-start justify-between mb-0">
|
||||
<dt class="font-normal" x-text="optionalService.label + ':'"></dt>
|
||||
<dd class="whitespace-no-wrap" x-text="optionalService.price + ' €'"></dd>
|
||||
</dl>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="!singleDate">
|
||||
<p>
|
||||
<button class="button button--small" x-on:click.prevent="tableMode = 'dates'">
|
||||
@@ -471,6 +493,89 @@
|
||||
</div>
|
||||
</f:section>
|
||||
|
||||
<f:section name="DaytripDateSelect">
|
||||
<div class="date-select-wrapper" x-data='daytripDateSelect({
|
||||
hotelName: "{hotel.name}",
|
||||
hotelUid: {hotel.uid},
|
||||
productUid: {product.uid},
|
||||
contingentEndpointUri: "<f:format.raw>{ep:uri.ajax(action: 'list', controller: 'AjaxContingent', format: 'json', pageUid: settings.defaultAjaxUid)}</f:format.raw>",
|
||||
roomsEndpointUri: "<f:format.raw>{ep:uri.ajax(action: 'rooms', controller: 'AjaxContingent', format: 'json', pageUid: settings.defaultAjaxUid)}</f:format.raw>"
|
||||
})'>
|
||||
<div class="ep-headbar" x-text="headerLabel"></div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<div class="date-select">
|
||||
<div class="date-select__picker">
|
||||
<div class="daterange__panel">
|
||||
<input x-ref="field" type="hidden">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<table class="table table-striped pricetable" x-show="availableRooms.length">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Zimmerart</th>
|
||||
<th>Preis</th>
|
||||
<th>inkl.</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template x-for="(row, index) in availableRooms">
|
||||
<tr>
|
||||
<td x-text="row.label"></td>
|
||||
<td style="white-space: nowrap" x-text="'ab ' + calculatePrice(row.code) + ' €'"></td>
|
||||
<td>
|
||||
<i x-show="row.skipassIncluded" aria-label="Skipass inklusive" role="tooltip" data-microtip-position="top" class="fa fa-tag"></i>
|
||||
<a href="#" data-toggle="modal"
|
||||
data-target="'#services' + index"
|
||||
data-microtip-position="top" aria-label="Inklusivleistungen zeigen" role="tooltip">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
</a>
|
||||
<div class="modal fade" id="'services' + index" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
<h5 class="modal-title">Inklusivleistungen</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<tbody>
|
||||
<tr v-for="service of row.servicesIncluded">
|
||||
<td><span class="glyphicon glyphicon-ok" aria-hidden="true"></span> {{ service }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a x-bind:href="row.bookingUrl" target="_blank"
|
||||
class="button button--small button--action">Buchen</a>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
<div x-show="loading" class="loader flex items-center space-x-2">
|
||||
<span>Loading...</span>
|
||||
<f:image class="block w-4 h-4" src="EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif" alt="Loading"/>
|
||||
</div>
|
||||
<div x-show="message" x-text="message"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</f:section>
|
||||
|
||||
<f:section name="Calendar">
|
||||
<f:if condition="{product.calendarHotel}">
|
||||
<f:if condition="{hotel.groupsPriceConfigs}">
|
||||
|
||||
@@ -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%',
|
||||
|
||||
Reference in New Issue
Block a user