Implement group bookings price calculator
This commit is contained in:
+2
@@ -21,6 +21,8 @@ mod {
|
||||
}
|
||||
frontpageEvent < .frontpage
|
||||
frontpageEvent.title = Startseite Events
|
||||
popup < .frontpage
|
||||
popup.title = Popup
|
||||
detail {
|
||||
title = Detailseite
|
||||
config {
|
||||
|
||||
@@ -58,6 +58,8 @@ plugin.tx_eptheme {
|
||||
myEpPageUid =
|
||||
# cat=eptheme/200/310; type=string; label=Booking links PID
|
||||
bookingLinksPid =
|
||||
# cat=eptheme/200/330; type=string; label=Groups price popup page UID
|
||||
groupsPricePopupPageUid =
|
||||
|
||||
# cat=eptheme/400/100; type=string; label=Phone number general
|
||||
phoneNumber = 0221 - 272 276 0
|
||||
|
||||
@@ -66,6 +66,7 @@ plugin.tx_eptheme {
|
||||
eventDisturberButtonLink = {$plugin.tx_eptheme.settings.eventDisturberButtonLink}
|
||||
googleTagManagerId = {$plugin.tx_eptheme.settings.googleTagManagerId}
|
||||
bpnBookingUrlTemplateCode = {$plugin.tx_eptheme.settings.bpnBookingUrlTemplateCode}
|
||||
groupsPricePopupPageUid = {$plugin.tx_eptheme.settings.groupsPricePopupPageUid}
|
||||
googleStaticMapsBaseUrl = {$plugin.tx_eptheme.settings.googleStaticMapsBaseUrl}
|
||||
googleMapsJsApiUrl = {$plugin.tx_eptheme.settings.googleMapsJsApiUrl}
|
||||
googleMapsApiKey = {$plugin.tx_eptheme.settings.googleMapsApiKey}
|
||||
|
||||
@@ -40,6 +40,9 @@ const sliderPrevArrow = '<button class="slider-pagination__arrow slider-paginati
|
||||
const sliderNextArrow = '<button class="slider-pagination__arrow slider-pagination__arrow--next"><i class="fa fa-2x fa-chevron-right"></i></button>';
|
||||
const sliderDot = '<button class="slider-pagination__dot" data-role="none" role="button" tabindex="0" />';
|
||||
|
||||
// Configure fancybox
|
||||
$.fancybox.defaults.iframe.css.width = '800px';
|
||||
|
||||
// Define date filter for vue templates
|
||||
Vue.filter('date', (value) => {
|
||||
return dayjs(value).format('DD.MM.YYYY');
|
||||
@@ -67,6 +70,7 @@ new Vue({
|
||||
HotelList,
|
||||
BackLink,
|
||||
MyEp: () => import('./myep/App.vue'),
|
||||
GroupsPriceCalculator: () => import('./components/GroupsPriceCalculator.vue'),
|
||||
FacebookPixel
|
||||
},
|
||||
data () {
|
||||
|
||||
+277
-18
@@ -1,53 +1,312 @@
|
||||
<template>
|
||||
<div>
|
||||
Foo!
|
||||
<div class="form-wrapper form--border">
|
||||
<div class="form-group" v-show="!submitted">
|
||||
<label>Zeitraum</label>
|
||||
<input type="text" class="form-control datepicker--visible" placeholder="von... bis" ref="picker"/>
|
||||
</div>
|
||||
<div class="form-group" v-show="!submitted">
|
||||
<label>Personenzahl</label>
|
||||
<input type="number" class="form-control" v-model="selectedPax" @change="onPaxUpdated()"/>
|
||||
</div>
|
||||
<div class="form-group" v-show="options.length > 0 && !submitted">
|
||||
<label>Optionale Zusatzleistungen</label>
|
||||
<div class="checkbox" v-for="option of options" :key="option.uid">
|
||||
<label>
|
||||
<input type="checkbox" v-model="selectedOptions" :value="option">
|
||||
<span class="cr"><i class="cr-icon glyphicon glyphicon-ok"></i></span>
|
||||
{{ option.title }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" v-show="boards.length > 0 && !submitted">
|
||||
<label>Optionale Verpflegungsleistungen</label>
|
||||
<select class="form-control" v-model="selectedBoard" v-if="selectedPax >= 30">
|
||||
<option :value="null">Keine Auswahl</option>
|
||||
<option v-for="board of boards" :value="board" :key="board.uid">{{ board.title }}</option>
|
||||
</select>
|
||||
<div v-show="selectedPax < 30">
|
||||
<span class="label label-warning">Erst ab 30 Personen buchbar.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Preise</div>
|
||||
<div class="panel-body">
|
||||
<div v-show="nightsCount > 0">
|
||||
Zeitraum: {{ rangeFormatted }}, {{ nightsCount }} Nächte<br>
|
||||
Basispreis: {{ pricePax.base | money }} €<br>
|
||||
<span v-if="pricePax.additional > 0">Aufpreis Personenzahl: {{ pricePax.additional | money }} €<br></span>
|
||||
<span v-if="priceShortTerm > 0">Aufpreis Kurzzeit: {{ priceShortTerm | money }} €<br></span>
|
||||
<span v-if="selectedOptions.length > 0">Zusatzleistungen: {{ priceOptions | money }} €<br></span>
|
||||
<span v-if="priceBoard > 0">Verpflegung: {{ priceBoard | money }} €<br></span>
|
||||
<span v-if="priceRunningCosts > 0">Strom- und Abfallgebühren: {{ priceRunningCosts | money }} €<br></span>
|
||||
<span><strong>Gesamtpreis: {{ totalPrice | money }} €</strong></span>
|
||||
</div>
|
||||
<div v-show="nightsCount === 0">
|
||||
<span class="label label-warning">Bitte wählen Sie einen Zeitraum</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default" v-show="nightsCount > 0 && !submitted">
|
||||
<div class="panel-heading">Angebot anfordern</div>
|
||||
<div class="panel-body">
|
||||
<div class="form-group" :class="{ 'has-error': formErrors.name }">
|
||||
<label>Name*</label>
|
||||
<input type="text" class="form-control" v-model="name">
|
||||
<div class="help-block"
|
||||
v-show="formErrors.name" v-html="formErrors.name"></div>
|
||||
</div>
|
||||
<div class="form-group" :class="{ 'has-error': formErrors.email }">
|
||||
<label>E-Mail*</label>
|
||||
<input type="text" class="form-control" v-model="email">
|
||||
<div class="help-block"
|
||||
v-show="formErrors.email" v-html="formErrors.email"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="button" type="submit" @click.prevent="submitForm()">Abschicken</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default" v-show="submitted">
|
||||
<div class="panel-body">
|
||||
Vielen Dank für Ihre Anfrage. Wir werden Sie schnellstmöglich bearbeiten.
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-overlay" v-show="processing">
|
||||
<img :src="loaderUri" alt="Loading...">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import $ from 'jquery';
|
||||
import dayjs from 'dayjs'
|
||||
import 'dayjs/locale/de'
|
||||
import 'flatpickr';
|
||||
import { German } from 'flatpickr/dist/l10n/de';
|
||||
import Vue from 'vue';
|
||||
import axios from 'axios';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
},
|
||||
props: {
|
||||
loaderUri: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
endpoint: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
configs: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
boards: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
options: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
processing: false,
|
||||
price: {},
|
||||
configs: [],
|
||||
boards: [],
|
||||
options: []
|
||||
submitted: false,
|
||||
picker: null,
|
||||
formErrors: {},
|
||||
selectedFrom: null,
|
||||
selectedTo: null,
|
||||
selectedRange: [],
|
||||
selectedPax: 30,
|
||||
selectedOptions: [],
|
||||
selectedBoard: null,
|
||||
name: null,
|
||||
email: null
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
money (value) {
|
||||
if (0 === value) {
|
||||
return '-';
|
||||
}
|
||||
return parseFloat(value).toFixed(2).replace(/\./, ',');
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
load () {
|
||||
initSelectableRanges () {
|
||||
let enabledDates = [];
|
||||
for (let config of this.configs) {
|
||||
const dateFrom = dayjs(config.dateFrom);
|
||||
const dateTo = dayjs(config.dateTo);
|
||||
enabledDates.push({
|
||||
from: dateFrom.format('DD.MM.YYYY'),
|
||||
to: dateTo.format('DD.MM.YYYY')
|
||||
});
|
||||
}
|
||||
this.picker.set('enable', enabledDates);
|
||||
if (enabledDates.length > 0) {
|
||||
const firstDate = enabledDates[0].from;
|
||||
this.picker.set('minDate', firstDate);
|
||||
}
|
||||
},
|
||||
updateSelectedRange () {
|
||||
let range = [];
|
||||
let currentDate = this.selectedFrom;
|
||||
while (currentDate < this.selectedTo) {
|
||||
range.push(currentDate);
|
||||
currentDate = currentDate.add(1, 'day');
|
||||
}
|
||||
this.selectedRange = range;
|
||||
},
|
||||
onPaxUpdated () {
|
||||
if (this.selectedPax < 30) {
|
||||
this.selectedBoard = null;
|
||||
}
|
||||
},
|
||||
submitForm () {
|
||||
let options = [];
|
||||
for (let option of this.selectedOptions) {
|
||||
options.push(option.uid);
|
||||
}
|
||||
let formData = {
|
||||
'tx_epproducts_ajax[name]': this.name,
|
||||
'tx_epproducts_ajax[email]': this.email,
|
||||
'tx_epproducts_ajax[dateFrom]': this.selectedFrom.format('DD.MM.YYYY'),
|
||||
'tx_epproducts_ajax[dateTo]': this.selectedTo.format('DD.MM.YYYY'),
|
||||
'tx_epproducts_ajax[pax]': this.selectedPax,
|
||||
'tx_epproducts_ajax[board]': this.selectedBoard ? this.selectedBoard.uid : null,
|
||||
'tx_epproducts_ajax[options]': options
|
||||
};
|
||||
this.processing = true;
|
||||
this.submitted = false;
|
||||
axios({
|
||||
url: this.endpoint,
|
||||
method: 'POST',
|
||||
data: $.param({
|
||||
'tx_epproducts_ajax[pax]': 60,
|
||||
'tx_epproducts_ajax[dateFrom]': '2020-04-28',
|
||||
'tx_epproducts_ajax[dateTo]': '2020-04-31'
|
||||
})
|
||||
data: $.param(formData)
|
||||
}).then(response => {
|
||||
this.configs = response.data.configs;
|
||||
this.boards = response.data.boards;
|
||||
this.options = response.data.options;
|
||||
this.price = response.data.price;
|
||||
if ('validation' === response.data.status) {
|
||||
this.formErrors = response.data.errors;
|
||||
} else {
|
||||
this.submitted = true;
|
||||
}
|
||||
}).catch(error => {
|
||||
}).finally(() => {
|
||||
this.processing = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
nightsCount () {
|
||||
return this.selectedRange.length;
|
||||
},
|
||||
rangeFormatted () {
|
||||
if (null === this.selectedFrom || null === this.selectedTo) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
return this.selectedFrom.format('DD.MM.YYYY') + ' - ' + this.selectedTo.format('DD.MM.YYYY');
|
||||
},
|
||||
pricePax () {
|
||||
let base = 0;
|
||||
let additional = 0;
|
||||
for (let date of this.selectedRange) {
|
||||
for (let config of this.configs) {
|
||||
if (date >= dayjs(config.dateFrom) && date < dayjs(config.dateTo)) {
|
||||
base += config.price;
|
||||
if (this.selectedPax > config.personsIncluded) {
|
||||
additional += (this.selectedPax - config.personsIncluded) * config.priceAdditionalPerson;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return { base, additional };
|
||||
},
|
||||
priceOptions () {
|
||||
let price = 0;
|
||||
for (let option of this.selectedOptions) {
|
||||
if (true === option.ignoreWithBoard && this.selectedBoard) {
|
||||
continue;
|
||||
}
|
||||
if (1 === option.type) {
|
||||
price += option.price;
|
||||
} else if (2 === option.type) {
|
||||
price += option.price * this.selectedPax;
|
||||
} else if (3 === option.type) {
|
||||
price += option.price * this.nightsCount;
|
||||
} else if (4 === option.type) {
|
||||
price += option.price * this.nightsCount * this.selectedPax;
|
||||
}
|
||||
}
|
||||
return price;
|
||||
},
|
||||
priceBoard () {
|
||||
let price = 0;
|
||||
if (this.selectedBoard) {
|
||||
price = this.selectedBoard.price * this.nightsCount * this.selectedPax;
|
||||
}
|
||||
return price;
|
||||
},
|
||||
priceShortTerm () {
|
||||
let price = 0;
|
||||
const totalPricePax = this.pricePax.base + this.pricePax.additional;
|
||||
if (2 === this.nightsCount) {
|
||||
price = totalPricePax * 0.2;
|
||||
}
|
||||
if (3 === this.nightsCount) {
|
||||
price = totalPricePax * 0.1;
|
||||
}
|
||||
return price;
|
||||
},
|
||||
priceRunningCosts () {
|
||||
if (this.selectedBoard) {
|
||||
return 0;
|
||||
}
|
||||
return this.nightsCount * this.selectedPax * 1.7;
|
||||
},
|
||||
totalPrice () {
|
||||
return this.pricePax.base + this.pricePax.additional + this.priceOptions
|
||||
+ this.priceBoard + this.priceShortTerm + this.priceRunningCosts;
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.load();
|
||||
Vue.nextTick(() => {
|
||||
this.picker = $(this.$refs.picker).flatpickr({
|
||||
mode: 'range',
|
||||
dateFormat: 'd.m.Y',
|
||||
locale: German,
|
||||
onChange: selectedDates => {
|
||||
if (2 === selectedDates.length) {
|
||||
this.selectedFrom = dayjs(selectedDates[0]);
|
||||
this.selectedTo = dayjs(selectedDates[1]);
|
||||
this.updateSelectedRange();
|
||||
}
|
||||
}
|
||||
});
|
||||
this.initSelectableRanges();
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.form-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
.form-control[readonly] {
|
||||
background-color: white;
|
||||
}
|
||||
.form-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgba(white, 0.85);
|
||||
}
|
||||
</style>
|
||||
@@ -28,7 +28,6 @@
|
||||
DaterangeSelect,
|
||||
AjaxContent,
|
||||
Calendar,
|
||||
GroupsPriceCalculator: () => import('./GroupsPriceCalculator.vue'),
|
||||
FacebookPixel,
|
||||
WatchlistToggle,
|
||||
OsmMap,
|
||||
|
||||
@@ -42,6 +42,7 @@ $output-bourbon-deprecation-warnings: false;
|
||||
@import "remix/faq";
|
||||
@import "remix/destination-pulldown";
|
||||
@import "remix/watchlist";
|
||||
@import "remix/calculator";
|
||||
|
||||
// core config
|
||||
@import "fonts";
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
.calculator {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.calculator__loading {
|
||||
position: absolute;
|
||||
@include size(100%);
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
background-color: rgba(white, 0.5);
|
||||
|
||||
img {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@
|
||||
}
|
||||
|
||||
.flatpickr-calendar {
|
||||
box-shadow: none;
|
||||
//box-shadow: none;
|
||||
|
||||
&.inline {
|
||||
border: 1px solid $color-grey-light;
|
||||
|
||||
@@ -75,18 +75,27 @@
|
||||
<trans-unit id="tx_eptheme.message.contactForm.name.1221560718">
|
||||
<source>Bitte angeben</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_eptheme.message.name.1221560718">
|
||||
<source>Bitte angeben</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_eptheme.message.contactForm.firstName.1221560718">
|
||||
<source>Bitte angeben</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_eptheme.message.contactForm.email.1221560718">
|
||||
<source>Bitte angeben</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_eptheme.message.email.1221560718">
|
||||
<source>Bitte angeben</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_eptheme.message.contactForm.phone.1221560718">
|
||||
<source>Bitte angeben</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_eptheme.message.contactForm.email.1221559976">
|
||||
<source>Ungültige E-Mail Adresse</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_eptheme.message.email.1221559976">
|
||||
<source>Ungültige E-Mail Adresse</source>
|
||||
</trans-unit>
|
||||
|
||||
<trans-unit id="label.whatsapp_instructions">
|
||||
<source>Nummer einfach als neuen Kontakt abspeichern und los geht es! Wir beraten Dich über WhatsApp!</source>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<html data-namespace-typo3-fluid="true" lang="en"
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
|
||||
|
||||
<div id="app">
|
||||
<f:render section="Content"/>
|
||||
</div>
|
||||
<f:render partial="Config" arguments="{_all}" />
|
||||
</html>
|
||||
@@ -0,0 +1,59 @@
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" lang="en"
|
||||
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
|
||||
|
||||
<f:layout name="Email/Default"/>
|
||||
|
||||
<f:section name="Main">
|
||||
<h1>Anfrage Gruppenhaus vom <f:format.date date="now" format="d.m.y" /></h1>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Haus</th>
|
||||
<td>{hotel.name}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<td>{name}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>E-Mail</th>
|
||||
<td>{email}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Zeitraum</th>
|
||||
<td>{dateFrom} - {dateTo}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Anzahl der Personen</th>
|
||||
<td>{pax}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Verpflegung</th>
|
||||
<td>
|
||||
<f:if condition="{board}">
|
||||
<f:then>
|
||||
{board.title}
|
||||
</f:then>
|
||||
<f:else>
|
||||
-
|
||||
</f:else>
|
||||
</f:if>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Optionale Zusatzleistungen</th>
|
||||
<td>
|
||||
<f:if condition="{options}">
|
||||
<f:then>
|
||||
<ul>
|
||||
<f:for each="{options}" as="option">
|
||||
<li>{option.title}</li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</f:then>
|
||||
</f:if>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</f:section>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,22 @@
|
||||
<html data-namespace-typo3-fluid="true" lang="en"
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers"
|
||||
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<h1>Preiskalkulator {hotel.name}</h1>
|
||||
<groups-price-calculator
|
||||
:configs='{configs -> f:format.raw()}'
|
||||
:boards='{boards -> f:format.raw()}'
|
||||
:options='{options -> f:format.raw()}'
|
||||
endpoint="{ep:uri.ajax(action: 'processForm', controller: 'AjaxGroupsPrice', pageUid: settings.defaultAjaxUid, arguments: '{hotel: hotel}')}"
|
||||
loader-uri="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif')}"
|
||||
>
|
||||
</groups-price-calculator>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,11 @@
|
||||
<html data-namespace-typo3-fluid="true" lang="en"
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
|
||||
|
||||
<f:layout name="Page/Popup"/>
|
||||
|
||||
<f:section name="Content">
|
||||
<f:cObject typoscriptObjectPath="lib.dynamicContent" data="{pageUid: '{data.uid}', colPos: '0'}" />
|
||||
</f:section>
|
||||
|
||||
</html>
|
||||
@@ -87,13 +87,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="hidden-xs">
|
||||
<f:comment>
|
||||
<f:if condition="{product.calendarHotel}">
|
||||
<groups-price-calculator
|
||||
endpoint="{ep:uri.ajax(action: 'index', controller: 'AjaxGroupsPrice', extensionName: 'epproducts', pageUid: settings.defaultAjaxUid, arguments: '{hotel: hotel}')}"
|
||||
></groups-price-calculator>
|
||||
</f:if>
|
||||
</f:comment>
|
||||
<f:render section="Calendar" arguments="{_all}"/>
|
||||
<f:render partial="Facts" arguments="{facts: product.facts}"/>
|
||||
<f:if condition="{product.video}">
|
||||
@@ -255,6 +248,9 @@
|
||||
|
||||
<f:section name="Calendar">
|
||||
<f:if condition="{product.calendarHotel}">
|
||||
<a class="button button--full" data-fancybox data-type="iframe" href="{f:uri.action(action: 'index', controller: 'GroupsPrice', arguments: '{hotel: hotel}', pageUid: settings.groupsPricePopupPageUid)}">
|
||||
Preiskalkulator
|
||||
</a>
|
||||
<div class="ep-sidebar ep-facts">
|
||||
<p><strong>Belegungskalender</strong></p>
|
||||
<calendar
|
||||
|
||||
Reference in New Issue
Block a user