chore: remove obsolete vue component
This commit is contained in:
-791
@@ -1,791 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="relative">
|
|
||||||
<div class="mb-8"
|
|
||||||
v-show="!submitted">
|
|
||||||
<label class="mb-2 font-bold">
|
|
||||||
Zeitraum
|
|
||||||
</label>
|
|
||||||
<input type="text"
|
|
||||||
class="form-field"
|
|
||||||
placeholder="von... bis" ref="picker"/>
|
|
||||||
<p class="text-sm mt-2" v-if="mainSeasonFrom && mainSeasonTo">
|
|
||||||
in der Hauptsaison {{ mainSeasonFrom }}-{{ mainSeasonTo }} mind. {{ minBookingDays }} Tage
|
|
||||||
</p>
|
|
||||||
<p class="text-sm italic mt-2"
|
|
||||||
v-show="nightsCount === 1">
|
|
||||||
Einzelne Nächte sind nur auf Anfrage buchbar
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="mb-8 grid sm:grid-cols-2 gap-8"
|
|
||||||
v-show="!submitted">
|
|
||||||
<div>
|
|
||||||
<label class="mb-2 font-bold">
|
|
||||||
Personenzahl
|
|
||||||
</label>
|
|
||||||
<input type="number"
|
|
||||||
class="form-field"
|
|
||||||
min="30"
|
|
||||||
v-model.number="selectedPax"
|
|
||||||
@change="onPaxUpdated()"
|
|
||||||
v-show="nightsCount > 0"/>
|
|
||||||
<p class="text-sm italic"
|
|
||||||
v-show="nightsCount === 0">
|
|
||||||
Bitte zuerst einen Zeitraum wählen
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label class="mb-2 font-bold">
|
|
||||||
davon Kinder 0-3 Jahre
|
|
||||||
</label>
|
|
||||||
<input type="number"
|
|
||||||
class="form-field"
|
|
||||||
min="0"
|
|
||||||
v-model.number="childrenCount"
|
|
||||||
@change="onPaxUpdated()"
|
|
||||||
v-show="nightsCount > 0"/>
|
|
||||||
<p class="text-sm italic"
|
|
||||||
v-show="nightsCount === 0">
|
|
||||||
Bitte zuerst einen Zeitraum wählen
|
|
||||||
</p>
|
|
||||||
<p class="text-sm italic"
|
|
||||||
v-show="nightsCount > 0">
|
|
||||||
Kinder werden nur bei Strom- und Abfallgebühren berücksichtigt.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label class="mb-2 font-bold">
|
|
||||||
davon Kinder 4-5 Jahre*
|
|
||||||
</label>
|
|
||||||
<input type="number"
|
|
||||||
class="form-field"
|
|
||||||
min="0"
|
|
||||||
v-model.number="minorsCount"
|
|
||||||
@change="onPaxUpdated()"
|
|
||||||
v-show="nightsCount > 0"/>
|
|
||||||
<p class="text-sm italic"
|
|
||||||
v-show="nightsCount === 0">
|
|
||||||
Bitte zuerst einen Zeitraum wählen
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div v-if="adolescentsAge > 0">
|
|
||||||
<label class="mb-2 font-bold">
|
|
||||||
davon Kinder 6-{{ adolescentsAge }} Jahre*
|
|
||||||
</label>
|
|
||||||
<input type="number"
|
|
||||||
class="form-field"
|
|
||||||
min="0"
|
|
||||||
v-model.number="adolescentsCount"
|
|
||||||
@change="onPaxUpdated()"
|
|
||||||
v-show="nightsCount > 0"/>
|
|
||||||
<p class="text-sm italic"
|
|
||||||
v-show="nightsCount === 0">
|
|
||||||
Bitte zuerst einen Zeitraum wählen
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="sm:col-span-2 text-sm">
|
|
||||||
*Die Kosten für Essen und Kurtaxe werden wir entsprechend der Altersstruktur der Gäste in der Rechnung
|
|
||||||
anpassen.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="mb-8"
|
|
||||||
v-show="options.length > 0 && !submitted">
|
|
||||||
<div class="mb-2 font-bold">
|
|
||||||
Optionale Zusatzleistungen
|
|
||||||
</div>
|
|
||||||
<ul class="mb-0 p-0 space-y-2">
|
|
||||||
<li v-for="option of options" :key="option.uid">
|
|
||||||
<label class="flex items-start">
|
|
||||||
<input type="checkbox"
|
|
||||||
class="w-5 h-5 border-ep-primary-light ring-0 bg-transparent focus:ring-0 focus:border-none text-ep-primary"
|
|
||||||
v-model="selectedOptions"
|
|
||||||
:value="option">
|
|
||||||
<span class="pl-2 leading-none">{{ option.title }}{{ formatOptionPriceType(option) }}</span>
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="mb-8"
|
|
||||||
v-show="boards.length > 0 && !submitted">
|
|
||||||
<label class="mb-2 font-bold">
|
|
||||||
Verpflegungsleistungen
|
|
||||||
</label>
|
|
||||||
<select class="form-field"
|
|
||||||
v-model="selectedBoard"
|
|
||||||
v-if="selectedPax >= 30">
|
|
||||||
<option :value="null" disabled>bitte auswählen</option>
|
|
||||||
<option v-for="board of boards"
|
|
||||||
:value="board"
|
|
||||||
:key="board.uid">
|
|
||||||
{{ board.title }} ({{ formatCurrency(board.price) }} pro Person und Nacht)
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
<div class="text-sm text-red-600 mt-1"
|
|
||||||
v-show="formErrors.board"
|
|
||||||
v-html="formErrors.board"></div>
|
|
||||||
<div v-show="selectedPax < 30">
|
|
||||||
<span class="text-red-600">Erst ab 30 Personen buchbar.</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="mb-8">
|
|
||||||
<div class="pb-2" v-if="submitted">
|
|
||||||
<p class="text-lg font-bold pb-2">
|
|
||||||
Vielen Dank für Deine {{ mode === 'booking' ? 'Buchung' : 'Anfrage' }}.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Für folgendes habt ihr euch entschieden:
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="p-2 uppercase bg-ep-primary-dark text-white w-full">
|
|
||||||
Preise
|
|
||||||
</div>
|
|
||||||
<div class="bg-zinc-100 p-2 mb-2">
|
|
||||||
<table class="w-full" v-show="nightsCount > 0">
|
|
||||||
<tbody>
|
|
||||||
<tr class="odd:bg-zinc-100">
|
|
||||||
<th class="text-left py-2">Zeitraum</th>
|
|
||||||
<td>{{ rangeFormatted }}, {{ nightsCount }} Nächte</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="odd:bg-zinc-100">
|
|
||||||
<th class="text-left py-2">Personenzahl</th>
|
|
||||||
<td>
|
|
||||||
{{ selectedPax }}<span v-if="childrenCount > 0">, davon Kinder {{ childrenCount }}</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="odd:bg-zinc-100">
|
|
||||||
<th class="text-left py-2">Basispreis</th>
|
|
||||||
<td>{{ formatCurrency(pricePax.base) }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="odd:bg-zinc-100"
|
|
||||||
v-if="pricePax.additional.EUR > 0 || pricePax.additional.CHF > 0">
|
|
||||||
<th class="text-left py-2">Aufpreis Personenzahl</th>
|
|
||||||
<td>{{ formatCurrency(pricePax.additional) }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="odd:bg-zinc-100"
|
|
||||||
v-if="priceShortTerm.EUR > 0 || priceShortTerm.CHF > 0">
|
|
||||||
<th class="text-left py-2">Aufpreis Kurzzeit</th>
|
|
||||||
<td>{{ formatCurrency(priceShortTerm) }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="odd:bg-zinc-100"
|
|
||||||
v-for="selectedOption in selectedOptions">
|
|
||||||
<th class="text-left py-2">{{ selectedOption.title }}</th>
|
|
||||||
<td>{{ formatCurrency(calculateOptionPrice(selectedOption)) }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="odd:bg-zinc-100"
|
|
||||||
v-if="priceBoard.EUR > 0 || priceBoard.CHF > 0">
|
|
||||||
<th class="text-left py-2">{{ selectedBoard.title }}</th>
|
|
||||||
<td>{{ formatCurrency(priceBoard) }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="odd:bg-zinc-100"
|
|
||||||
v-if="false === isSelfCatering && undersubscriptionLimit > 0">
|
|
||||||
<th class="text-left py-2">Verpflegungs-Aufschlag für Gruppen unter {{ undersubscriptionLimit }} Personen</th>
|
|
||||||
<td>{{ formatCurrency(pricePax.undersubscription) }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="odd:bg-zinc-100"
|
|
||||||
v-if="priceRunningCosts.EUR > 0 || priceRunningCosts.CHF > 0">
|
|
||||||
<th class="text-left py-2">Strom- und Abfallgebühren</th>
|
|
||||||
<td>{{ formatCurrency(priceRunningCosts) }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="odd:bg-zinc-100">
|
|
||||||
<th class="text-left py-2">Gesamtpreis</th>
|
|
||||||
<td>
|
|
||||||
<strong>{{ formatCurrency(priceTotal) }}</strong>
|
|
||||||
<br>
|
|
||||||
<small>{{ taxLabel }}</small>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<p class="text-sm italic"
|
|
||||||
v-show="nightsCount === 0">
|
|
||||||
Bitte zuerst einen Zeitraum wählen
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div v-if="mode === 'booking' && submitted">
|
|
||||||
Bitte beachtet, dass die Buchung bei uns eingegangen, aber <strong>noch nicht bestätigt</strong> ist.
|
|
||||||
Wir behalten uns vor, die Buchungen auf Verfügbarkeit und Länge des gewünschten Aufenthalts zu prüfen.
|
|
||||||
Erst wenn wir euch die Buchung per Mail bestätigt haben, wird sie bindend.
|
|
||||||
</div>
|
|
||||||
<div v-if="mode === 'inquiry' && submitted">
|
|
||||||
Die Anfrage ist bei uns eingegangen und wird schnellstmöglich bearbeitet. Wir melden uns telefonisch
|
|
||||||
oder per Mail bei euch.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="mb-8"
|
|
||||||
v-show="nightsCount > 0 && !submitted">
|
|
||||||
<div class="p-2 uppercase bg-ep-primary-dark text-white w-full">
|
|
||||||
Jetzt <span v-if="!forceInquiry">buchen</span><span v-if="forceInquiry">anfragen</span>
|
|
||||||
</div>
|
|
||||||
<div class="bg-zinc-100 p-2">
|
|
||||||
<div class="grid md:grid-cols-2 gap-4 mb-4">
|
|
||||||
<div :class="{ 'has-error': formErrors.name }">
|
|
||||||
<label class="font-bold mb-2">
|
|
||||||
Name*
|
|
||||||
</label>
|
|
||||||
<input type="text"
|
|
||||||
class="form-field"
|
|
||||||
v-model="name">
|
|
||||||
<div class="text-sm text-red-600 mt-1"
|
|
||||||
v-show="formErrors.name"
|
|
||||||
v-html="formErrors.name"></div>
|
|
||||||
</div>
|
|
||||||
<div :class="{ 'has-error': formErrors.street }">
|
|
||||||
<label class="font-bold mb-2">
|
|
||||||
Strasse, Nr.*
|
|
||||||
</label>
|
|
||||||
<input type="text"
|
|
||||||
class="form-field"
|
|
||||||
v-model="street">
|
|
||||||
<div class="text-sm text-red-600 mt-1"
|
|
||||||
v-show="formErrors.street"
|
|
||||||
v-html="formErrors.street"></div>
|
|
||||||
</div>
|
|
||||||
<div :class="{ 'has-error': formErrors.postcode }">
|
|
||||||
<label class="font-bold mb-2">
|
|
||||||
Postleitzahl*
|
|
||||||
</label>
|
|
||||||
<input type="text"
|
|
||||||
class="form-field"
|
|
||||||
v-model="postcode">
|
|
||||||
<div class="text-sm text-red-600 mt-1"
|
|
||||||
v-show="formErrors.postcode"
|
|
||||||
v-html="formErrors.postcode"></div>
|
|
||||||
</div>
|
|
||||||
<div :class="{ 'has-error': formErrors.city }">
|
|
||||||
<label class="font-bold mb-2">
|
|
||||||
Ort*
|
|
||||||
</label>
|
|
||||||
<input type="text"
|
|
||||||
class="form-field"
|
|
||||||
v-model="city">
|
|
||||||
<div class="text-sm text-red-600 mt-1"
|
|
||||||
v-show="formErrors.city"
|
|
||||||
v-html="formErrors.city"></div>
|
|
||||||
</div>
|
|
||||||
<div :class="{ 'has-error': formErrors.group }">
|
|
||||||
<label class="font-bold mb-2">
|
|
||||||
Name der Gruppe*
|
|
||||||
</label>
|
|
||||||
<input type="text"
|
|
||||||
class="form-field"
|
|
||||||
v-model="group">
|
|
||||||
<div class="text-sm text-red-600 mt-1"
|
|
||||||
v-show="formErrors.group"
|
|
||||||
v-html="formErrors.group"></div>
|
|
||||||
</div>
|
|
||||||
<div :class="{ 'has-error': formErrors.email }">
|
|
||||||
<label class="font-bold mb-2">
|
|
||||||
E-Mail*
|
|
||||||
</label>
|
|
||||||
<input type="text"
|
|
||||||
class="form-field"
|
|
||||||
v-model="email">
|
|
||||||
<div class="text-sm text-red-600 mt-1"
|
|
||||||
v-show="formErrors.email"
|
|
||||||
v-html="formErrors.email"></div>
|
|
||||||
</div>
|
|
||||||
<div :class="{ 'has-error': formErrors.phone }">
|
|
||||||
<label class="font-bold mb-2">
|
|
||||||
Telefon*
|
|
||||||
</label>
|
|
||||||
<input type="text"
|
|
||||||
class="form-field"
|
|
||||||
v-model="phone">
|
|
||||||
<div class="text-sm text-red-600 mt-1"
|
|
||||||
v-show="formErrors.phone"
|
|
||||||
v-html="formErrors.phone"></div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label class="font-bold mb-2">
|
|
||||||
Bemerkungen/Wünsche
|
|
||||||
</label>
|
|
||||||
<textarea class="form-field"
|
|
||||||
cols="30"
|
|
||||||
rows="3"
|
|
||||||
v-model="remarks"/>
|
|
||||||
</div>
|
|
||||||
<div v-if="mode === 'booking'" :class="{ 'has-error': formErrors.confirmation }">
|
|
||||||
<label for="confirmation" class="flex items-start">
|
|
||||||
<input type="checkbox" v-model="confirmation" name="confirmation" id="confirmation">
|
|
||||||
<span class="block ml-2">
|
|
||||||
Durch Anklicken des Buttons 'Buchung abschicken' bestätige ich, dass ich die
|
|
||||||
<a :href="termsUrls[countryCode]"
|
|
||||||
class="text-ep-primary" target="_blank">
|
|
||||||
Allgemeinen Geschäftsbedingungen (AGB)
|
|
||||||
</a> gelesen habe und damit einverstanden bin, dass eine
|
|
||||||
kostenpflichtige Buchung zustande kommt.
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
<div class="text-sm text-red-600 mt-1"
|
|
||||||
v-show="formErrors.confirmation"
|
|
||||||
v-html="formErrors.confirmation"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="py-4">
|
|
||||||
Ihr erhaltet von uns eine Bestätigung. Wir behalten uns vor die Buchung zu prüfen. Erst nach
|
|
||||||
Rückbestätigung von uns gilt die Buchung auch von unserer Seite als bindend.
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center space-x-2">
|
|
||||||
<button class="button bg-button"
|
|
||||||
type="submit"
|
|
||||||
v-if="!forceInquiry"
|
|
||||||
@click.prevent="submitForm('booking')">
|
|
||||||
Buchung abschicken
|
|
||||||
</button>
|
|
||||||
<button class="button bg-button"
|
|
||||||
:class="{ 'button--small bg-ep-secondary': !forceInquiry }"
|
|
||||||
type="submit"
|
|
||||||
@click.prevent="submitForm('inquiry')">
|
|
||||||
als Anfrage senden
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<img :src="logoAt" alt="" v-if="countryCode === 'AT'" class="block w-full h-auto max-w-24">
|
|
||||||
<img :src="logoCh" alt="" v-if="countryCode === 'CH'" class="block w-full h-auto max-w-24">
|
|
||||||
</div>
|
|
||||||
<div class="fixed top-0 left-0 inset-0 bg-white/85 flex items-center justify-center" v-show="processing">
|
|
||||||
<img :src="loaderUri" alt="Loading...">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import dayjs from 'dayjs'
|
|
||||||
import 'dayjs/locale/de'
|
|
||||||
import 'flatpickr';
|
|
||||||
import {German} from 'flatpickr/dist/l10n/de';
|
|
||||||
import Vue from 'vue';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'GroupsPriceCalculator',
|
|
||||||
props: {
|
|
||||||
loaderUri: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
endpoint: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
logoAt: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
logoCh: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
configsJson: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
boardsJson: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
optionsJson: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
runningCostsFactorEur: {
|
|
||||||
type: Number,
|
|
||||||
default: 2.5,
|
|
||||||
},
|
|
||||||
runningCostsFactorChf: {
|
|
||||||
type: Number,
|
|
||||||
default: 2.9,
|
|
||||||
},
|
|
||||||
undersubscription30Eur: {
|
|
||||||
type: Number,
|
|
||||||
default: 5.0,
|
|
||||||
},
|
|
||||||
undersubscription30Chf: {
|
|
||||||
type: Number,
|
|
||||||
default: 5.0,
|
|
||||||
},
|
|
||||||
undersubscription40Eur: {
|
|
||||||
type: Number,
|
|
||||||
default: 2.5,
|
|
||||||
},
|
|
||||||
undersubscription40Chf: {
|
|
||||||
type: Number,
|
|
||||||
default: 2.5,
|
|
||||||
},
|
|
||||||
countryCode: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
shortTermFactors: {
|
|
||||||
type: Array,
|
|
||||||
default() {
|
|
||||||
return [
|
|
||||||
{ nights: 1, factor: 0.3 },
|
|
||||||
{ nights: 2, factor: 0.2 },
|
|
||||||
{ nights: 3, factor: 0.1 },
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mainSeasonFrom: {
|
|
||||||
type: String,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
mainSeasonTo: {
|
|
||||||
type: String,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
minBookingDays: {
|
|
||||||
type: Number,
|
|
||||||
default: 5,
|
|
||||||
},
|
|
||||||
taxLabel: {
|
|
||||||
type: String,
|
|
||||||
default: 'zzgl. vorab zu entrichtender Ortstaxe',
|
|
||||||
},
|
|
||||||
adolescentsAge: {
|
|
||||||
type: Number,
|
|
||||||
default: 0,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
configs: JSON.parse(this.configsJson),
|
|
||||||
boards: JSON.parse(this.boardsJson),
|
|
||||||
options: JSON.parse(this.optionsJson),
|
|
||||||
processing: false,
|
|
||||||
submitted: false,
|
|
||||||
picker: null,
|
|
||||||
formErrors: {},
|
|
||||||
selectedFrom: null,
|
|
||||||
selectedTo: null,
|
|
||||||
selectedRange: [],
|
|
||||||
selectedPax: 30,
|
|
||||||
childrenCount: 0,
|
|
||||||
minorsCount: 0,
|
|
||||||
adolescentsCount: 0,
|
|
||||||
selectedOptions: [],
|
|
||||||
selectedBoard: null,
|
|
||||||
name: '',
|
|
||||||
email: '',
|
|
||||||
phone: '',
|
|
||||||
remarks: '',
|
|
||||||
group: '',
|
|
||||||
street: '',
|
|
||||||
postcode: '',
|
|
||||||
city: '',
|
|
||||||
mode: 'booking',
|
|
||||||
confirmation: false,
|
|
||||||
termsUrls: {
|
|
||||||
AT: 'https://www.ep-reisen.de/fileadmin/user_upload/allgemein/AGB_Gruppen/AGB_Gruppen_CLLT_Touristik_GmbH.pdf',
|
|
||||||
CH: 'https://www.ep-reisen.de/fileadmin/user_upload/allgemein/AGB_Gruppen/AVB_Gruppen_AlpineVacation_GmbH.pdf',
|
|
||||||
IT: 'https://www.ep-reisen.de/fileadmin/user_upload/allgemein/AGB_Gruppen/AGB_Gruppen_E_P_Reisen.pdf',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
formatCurrency({ EUR, CHF }) {
|
|
||||||
let price
|
|
||||||
|
|
||||||
if ('CH' === this.countryCode) {
|
|
||||||
let formatter = new Intl.NumberFormat('de-DE', {
|
|
||||||
style: 'currency',
|
|
||||||
currency: 'CHF',
|
|
||||||
})
|
|
||||||
price = formatter.format(CHF)
|
|
||||||
} else {
|
|
||||||
let formatter = new Intl.NumberFormat('de-DE', {
|
|
||||||
style: 'currency',
|
|
||||||
currency: 'EUR',
|
|
||||||
})
|
|
||||||
price = formatter.format(EUR)
|
|
||||||
}
|
|
||||||
|
|
||||||
return price
|
|
||||||
},
|
|
||||||
calculateOptionPrice(option) {
|
|
||||||
let price = {
|
|
||||||
EUR: 0,
|
|
||||||
CHF: 0,
|
|
||||||
};
|
|
||||||
let optionPriceEUR = option.price.EUR
|
|
||||||
let optionPriceCHF = option.price.CHF
|
|
||||||
if (1 === option.type) {
|
|
||||||
price.EUR = optionPriceEUR;
|
|
||||||
price.CHF = optionPriceCHF;
|
|
||||||
} else if (2 === option.type) {
|
|
||||||
price.EUR = optionPriceEUR * this.paxCount;
|
|
||||||
price.CHF = optionPriceCHF * this.paxCount;
|
|
||||||
} else if (3 === option.type) {
|
|
||||||
price.EUR = optionPriceEUR * this.nightsCount;
|
|
||||||
price.CHF = optionPriceCHF * this.nightsCount;
|
|
||||||
} else if (4 === option.type) {
|
|
||||||
price.EUR = optionPriceEUR * this.nightsCount * this.paxCount;
|
|
||||||
price.CHF = optionPriceCHF * this.nightsCount * this.paxCount;
|
|
||||||
}
|
|
||||||
return price;
|
|
||||||
},
|
|
||||||
formatOptionPriceType(option) {
|
|
||||||
if (2 === option.type) {
|
|
||||||
return ` (${this.formatCurrency(option.price)} pro Person)`
|
|
||||||
}
|
|
||||||
if (3 === option.type) {
|
|
||||||
return ` (${this.formatCurrency(option.price)} pro Nacht)`
|
|
||||||
}
|
|
||||||
if (4 === option.type) {
|
|
||||||
return ` (${this.formatCurrency(option.price)} pro Person und Nacht)`
|
|
||||||
}
|
|
||||||
|
|
||||||
return ` (${this.formatCurrency(option.price)})`
|
|
||||||
},
|
|
||||||
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(mode) {
|
|
||||||
this.mode = mode
|
|
||||||
let confirmation = 'inquiry' === mode ? true : this.confirmation
|
|
||||||
let formData = new URLSearchParams({
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][mode]': mode,
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][confirmation]': confirmation ? '1' : '0',
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][name]': this.name ? this.name : '',
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][group]': this.group ? this.group : '',
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][street]': this.street ? this.street : '',
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][postcode]': this.postcode ? this.postcode : '',
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][city]': this.city ? this.city : '',
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][email]': this.email? this.email : '',
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][phone]': this.phone ? this.phone : '',
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][remarks]': this.remarks,
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][dateFrom]': this.selectedFrom.format('DD.MM.YYYY'),
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][dateTo]': this.selectedTo.format('DD.MM.YYYY'),
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][nights]': this.nightsCount,
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][pax]': this.selectedPax,
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][children]': this.childrenCount,
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][minors]': this.minorsCount,
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][adolescents]': this.adolescentsCount,
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][summary][paxBase]': this.formatCurrency(this.pricePax.base),
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][summary][paxAdditional]': this.formatCurrency(this.pricePax.additional),
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][summary][undersubscription]': this.formatCurrency(this.pricePax.undersubscription),
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][summary][shortTerm]': this.formatCurrency(this.priceShortTerm),
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][summary][options]': this.formatCurrency(this.priceOptions),
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][summary][board]': this.formatCurrency(this.priceBoard),
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][summary][runningCosts]': this.formatCurrency(this.priceRunningCosts),
|
|
||||||
'tx_epproducts_ajax[groupsPriceInquiry][summary][total]': this.formatCurrency(this.priceTotal),
|
|
||||||
})
|
|
||||||
if (this.selectedBoard) {
|
|
||||||
formData.append('tx_epproducts_ajax[groupsPriceInquiry][board]', this.selectedBoard.uid)
|
|
||||||
}
|
|
||||||
this.selectedOptions.map(option => (
|
|
||||||
formData.append('tx_epproducts_ajax[groupsPriceInquiry][options][]', option.uid)
|
|
||||||
))
|
|
||||||
this.processing = true;
|
|
||||||
this.submitted = false;
|
|
||||||
fetch(this.endpoint, { method: 'POST', body: formData })
|
|
||||||
.then(response => response.json())
|
|
||||||
.then((json) => {
|
|
||||||
if ('validation' === json.status) {
|
|
||||||
this.formErrors = json.errors
|
|
||||||
} else {
|
|
||||||
this.submitted = true;
|
|
||||||
window.dataLayer = window.dataLayer || [];
|
|
||||||
dataLayer.push({
|
|
||||||
'event': 'Preisberechnung'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.processing = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
isSelfCatering() {
|
|
||||||
let selectedBoard = this.selectedBoard
|
|
||||||
if (null === selectedBoard) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if ('CH' === this.countryCode) {
|
|
||||||
return selectedBoard.price.CHF === 0
|
|
||||||
} else {
|
|
||||||
return selectedBoard.price.EUR === 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
undersubscriptionLimit() {
|
|
||||||
if (this.paxCount < 40) {
|
|
||||||
return 40
|
|
||||||
} else if (this.paxCount < 50) {
|
|
||||||
return 50
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
|
||||||
},
|
|
||||||
paxCount() {
|
|
||||||
// subtract number of children from pax for price calculation, but minimum 30 pax
|
|
||||||
return Math.max(this.selectedPax - this.childrenCount, 30)
|
|
||||||
},
|
|
||||||
nightsCount() {
|
|
||||||
return this.selectedRange.length;
|
|
||||||
},
|
|
||||||
forceInquiry() {
|
|
||||||
return 1 === 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 = {
|
|
||||||
EUR: 0,
|
|
||||||
CHF: 0,
|
|
||||||
}
|
|
||||||
let additional = {
|
|
||||||
EUR: 0,
|
|
||||||
CHF: 0,
|
|
||||||
}
|
|
||||||
let undersubscription = {
|
|
||||||
EUR: 0,
|
|
||||||
CHF: 0,
|
|
||||||
}
|
|
||||||
for (let date of this.selectedRange) {
|
|
||||||
for (let config of this.configs) {
|
|
||||||
if (date >= dayjs(config.dateFrom) && date < dayjs(config.dateTo)) {
|
|
||||||
base.EUR += config.price.EUR
|
|
||||||
base.CHF += config.price.CHF
|
|
||||||
let included = config.personsIncluded
|
|
||||||
if (this.paxCount > included) {
|
|
||||||
let additionalPax = this.paxCount - included
|
|
||||||
additional.EUR += additionalPax * config.priceAdditionalPerson.EUR
|
|
||||||
additional.CHF += additionalPax * config.priceAdditionalPerson.CHF
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (false === this.isSelfCatering) {
|
|
||||||
if (this.paxCount < 40) {
|
|
||||||
undersubscription.EUR = this.paxCount * this.undersubscription30Eur * this.selectedRange.length
|
|
||||||
undersubscription.CHF = this.paxCount * this.undersubscription30Chf * this.selectedRange.length
|
|
||||||
} else if (this.paxCount < 50) {
|
|
||||||
undersubscription.EUR = this.paxCount * this.undersubscription40Eur * this.selectedRange.length
|
|
||||||
undersubscription.CHF = this.paxCount * this.undersubscription40Chf * this.selectedRange.length
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {base, additional, undersubscription}
|
|
||||||
},
|
|
||||||
priceOptions() {
|
|
||||||
let price = {
|
|
||||||
EUR: 0,
|
|
||||||
CHF: 0,
|
|
||||||
};
|
|
||||||
for (let option of this.selectedOptions) {
|
|
||||||
let optionPrice = this.calculateOptionPrice(option)
|
|
||||||
price.EUR += optionPrice.EUR;
|
|
||||||
price.CHF += optionPrice.CHF;
|
|
||||||
}
|
|
||||||
return price;
|
|
||||||
},
|
|
||||||
priceBoard() {
|
|
||||||
let price = {
|
|
||||||
EUR: 0,
|
|
||||||
CHF: 0,
|
|
||||||
}
|
|
||||||
if (this.selectedBoard) {
|
|
||||||
price.EUR = this.selectedBoard.price.EUR * this.nightsCount * this.paxCount;
|
|
||||||
price.CHF = this.selectedBoard.price.CHF * this.nightsCount * this.paxCount;
|
|
||||||
}
|
|
||||||
return price
|
|
||||||
},
|
|
||||||
priceShortTerm() {
|
|
||||||
let price = {
|
|
||||||
EUR: 0,
|
|
||||||
CHF: 0,
|
|
||||||
}
|
|
||||||
const totalPricePax = this.pricePax.base.EUR + this.pricePax.additional.EUR;
|
|
||||||
const totalPricePaxCHF = this.pricePax.base.CHF + this.pricePax.additional.CHF;
|
|
||||||
const shorTermFactor = this.shortTermFactors.find(element => element.nights === this.nightsCount);
|
|
||||||
if (undefined !== shorTermFactor) {
|
|
||||||
price.EUR = totalPricePax * shorTermFactor.factor;
|
|
||||||
price.CHF = totalPricePaxCHF * shorTermFactor.factor;
|
|
||||||
}
|
|
||||||
return price
|
|
||||||
},
|
|
||||||
priceRunningCosts() {
|
|
||||||
return {
|
|
||||||
EUR: this.nightsCount * this.selectedPax * this.runningCostsFactorEur,
|
|
||||||
CHF: this.nightsCount * this.selectedPax * this.runningCostsFactorChf,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
priceTotal() {
|
|
||||||
let totalEUR = this.pricePax.base.EUR
|
|
||||||
+ this.pricePax.additional.EUR
|
|
||||||
+ this.pricePax.undersubscription.EUR
|
|
||||||
+ this.priceOptions.EUR
|
|
||||||
+ this.priceBoard.EUR
|
|
||||||
+ this.priceShortTerm.EUR
|
|
||||||
+ this.priceRunningCosts.EUR
|
|
||||||
|
|
||||||
let totalCHF = 0
|
|
||||||
|
|
||||||
// calculate CHF price when pax price in CHF is available since CHF prices may not be provided via CMS yet
|
|
||||||
if (0 < this.pricePax.base.CHF) {
|
|
||||||
totalCHF = this.pricePax.base.CHF
|
|
||||||
+ this.pricePax.additional.CHF
|
|
||||||
+ this.pricePax.undersubscription.CHF
|
|
||||||
+ this.priceOptions.CHF
|
|
||||||
+ this.priceBoard.CHF
|
|
||||||
+ this.priceShortTerm.CHF
|
|
||||||
+ this.priceRunningCosts.CHF
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return {
|
|
||||||
EUR: totalEUR,
|
|
||||||
CHF: totalCHF,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
Vue.nextTick(() => {
|
|
||||||
this.picker = flatpickr(this.$refs.picker, {
|
|
||||||
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>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
// Initialize vue app
|
|
||||||
import Vue from 'vue'
|
|
||||||
import vueCustomElement from 'vue-custom-element'
|
|
||||||
import 'document-register-element/build/document-register-element'
|
|
||||||
import GroupsPriceCalculator from './components/GroupsPriceCalculator'
|
|
||||||
|
|
||||||
Vue.use(vueCustomElement)
|
|
||||||
Vue.config.productionTip = false
|
|
||||||
|
|
||||||
Vue.customElement('groups-price-calculator', GroupsPriceCalculator)
|
|
||||||
@@ -9,7 +9,6 @@ Encore
|
|||||||
.setOutputPath('./public/typo3conf/ext/ep_theme/Resources/Public/')
|
.setOutputPath('./public/typo3conf/ext/ep_theme/Resources/Public/')
|
||||||
.setPublicPath('/typo3conf/ext/ep_theme/Resources/Public/')
|
.setPublicPath('/typo3conf/ext/ep_theme/Resources/Public/')
|
||||||
.addEntry('main', './public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/main.js')
|
.addEntry('main', './public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/main.js')
|
||||||
.addEntry('groups-calculator', './public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/groups-calculator.js')
|
|
||||||
.addStyleEntry('rte', './public/typo3conf/ext/ep_theme/Resources/Private/Assets/scss/rte.scss')
|
.addStyleEntry('rte', './public/typo3conf/ext/ep_theme/Resources/Private/Assets/scss/rte.scss')
|
||||||
.splitEntryChunks()
|
.splitEntryChunks()
|
||||||
.enableSingleRuntimeChunk()
|
.enableSingleRuntimeChunk()
|
||||||
|
|||||||
Reference in New Issue
Block a user