shell bypass 403
<template>
<div id="amelia-booking-wrap" class="am-wrap">
<!-- Spinner -->
<div class="am-spinner am-section" v-show="!fetched">
<img class="svg-booking am-spin" :src="$root.getUrl + 'public/img/oval-spinner.svg'">
<img class="svg-booking am-hourglass" :src="$root.getUrl + 'public/img/hourglass.svg'">
</div>
<div id="am-events-booking" v-show="fetched">
<!-- Event Filter -->
<div class="am-events-filter">
<el-row :gutter="24">
<!-- Event Filter Tags -->
<el-col
v-show="showTags()"
:sm="getColumnLength()[0]"
:class="$root.settings.customization.forms ? `el-form-item am-select-${$options.name}`: ''"
>
<el-select
v-model="params.tag"
clearable
:placeholder="eventFilterLabels.event_type.value || $root.labels.event_type"
:popper-class="$root.settings.customization.forms ? `am-dropdown-${$options.name}` : ''"
@change="getEvents(false)"
>
<template slot="prefix">
<svg width="20" height="18" viewBox="0 0 20 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.5 17.4999H2.5C1.84661 17.508 1.21666 17.2568 0.748098 16.8013C0.279533 16.3459 0.0105328 15.7233 0 15.0699V2.92994C0.0105328 2.27659 0.279533 1.65403 0.748098 1.19858C1.21666 0.743137 1.84661 0.491921 2.5 0.499942H7.1C7.24771 0.500919 7.39336 0.534605 7.5265 0.59858C7.65964 0.662555 7.77695 0.755229 7.87 0.869942L10.47 4.04994H17.47C17.796 4.04198 18.1204 4.09866 18.4244 4.21672C18.7285 4.33478 19.0061 4.51188 19.2413 4.7378C19.4765 4.96372 19.6647 5.23398 19.7949 5.53299C19.9251 5.83201 19.9948 6.15385 20 6.47994V15.0699C19.9895 15.7233 19.7205 16.3459 19.2519 16.8013C18.7833 17.2568 18.1534 17.508 17.5 17.4999Z"/>
</svg>
</template>
<el-option
v-for="(tag, index) in options.entities.tags.map(eventTag => eventTag.name)"
:key="index"
:label="tag"
:value="tag"
>
</el-option>
</el-select>
</el-col>
<!-- /Event Filter Tags -->
<!-- Event Filter Date -->
<el-col
v-show="showDatePicker()"
:sm="getColumnLength()[1]"
:class="$root.settings.customization.forms ? `am-calendar-${$options.name}`: ''"
class="v-calendar-column"
>
<v-date-picker
v-model="params.date"
id="am-calendar-picker"
class="am-calendar-picker"
tint-color='#1A84EE'
mode="single"
popover-visibility="focus"
popover-direction="bottom"
popover-align="center"
:input-props="{class: 'el-input__inner', placeholder: this.$root.labels.event_pick_min_date, readonly: true}"
:show-day-popover=false
:is-expanded=false
:is-inline=false
:is-required=true
:formats="vCalendarFormats"
@input="getEvents(false)"
>
<el-input
v-model="selectedDateInput"
readonly
>
<template slot="prefix">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="22" viewBox="0 0 22 24">
<path fill-rule="evenodd" d="M15.714 2.667H6.286V0H2.75v2.667H.78A.783.783 0 0 0 0 3.45v19.764A.78.78 0 0 0 .783 24h20.434a.785.785 0 0 0 .783-.785V3.451a.782.782 0 0 0-.78-.784h-1.97V0h-3.536v2.667zM2.75 21.429V8h16.5v13.429H2.75zM12 14v6h6v-6h-6z"/>
</svg>
</template>
</el-input>
</v-date-picker>
</el-col>
<!-- /Event Filter Date -->
<!-- Event Filter Location -->
<el-col
v-show="showLocations()"
:sm="getColumnLength()[2]"
:class="$root.settings.customization.forms ? `el-form-item am-select-${$options.name}`: ''"
>
<el-select
v-model="params.locationId"
clearable
:placeholder="(eventFilterLabels.hasOwnProperty('event_location') ? eventFilterLabels.event_location.value : '') || $root.labels.event_location"
:popper-class="$root.settings.customization.forms ? `am-dropdown-${$options.name}` : ''"
@change="getEvents(false)"
>
<template slot="prefix">
<svg width="20" height="24" viewBox="0 0 20 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 0.16674C7.54075 0.166617 5.18082 1.13713 3.43314 2.86733C1.68546 4.59753 0.69128 6.9476 0.666687 9.40674C0.666687 15.8001 8.89169 22.9167 9.24169 23.2201C9.45301 23.4008 9.72194 23.5001 10 23.5001C10.2781 23.5001 10.547 23.4008 10.7584 23.2201C11.1667 22.9167 19.3334 15.8001 19.3334 9.40674C19.3088 6.9476 18.3146 4.59753 16.5669 2.86733C14.8192 1.13713 12.4593 0.166617 10 0.16674ZM10 20.7584C8.05169 18.9034 3.00002 13.7584 3.00002 9.40674C3.00002 7.55022 3.73752 5.76975 5.05027 4.45699C6.36303 3.14424 8.1435 2.40674 10 2.40674C11.8565 2.40674 13.637 3.14424 14.9498 4.45699C16.2625 5.76975 17 7.55022 17 9.40674C17 13.7234 11.9484 18.9034 10 20.7584Z"/>
<path d="M10 4.83337C9.19241 4.83337 8.40294 5.07286 7.73144 5.52154C7.05994 5.97022 6.53656 6.60795 6.22751 7.35408C5.91845 8.10022 5.83759 8.92124 5.99514 9.71333C6.1527 10.5054 6.5416 11.233 7.11266 11.8041C7.68373 12.3751 8.41131 12.764 9.2034 12.9216C9.99548 13.0791 10.8165 12.9983 11.5626 12.6892C12.3088 12.3802 12.9465 11.8568 13.3952 11.1853C13.8439 10.5138 14.0833 9.72431 14.0833 8.91671C14.0833 7.83374 13.6531 6.79513 12.8874 6.02935C12.1216 5.26358 11.083 4.83337 10 4.83337ZM10 10.6667C9.6539 10.6667 9.31555 10.5641 9.02777 10.3718C8.73998 10.1795 8.51568 9.90617 8.38323 9.5864C8.25077 9.26663 8.21612 8.91477 8.28364 8.5753C8.35116 8.23583 8.51784 7.92401 8.76258 7.67927C9.00732 7.43453 9.31914 7.26786 9.65861 7.20033C9.99807 7.13281 10.3499 7.16746 10.6697 7.29992C10.9895 7.43237 11.2628 7.65667 11.4551 7.94446C11.6474 8.23225 11.75 8.57059 11.75 8.91671C11.75 9.38084 11.5656 9.82596 11.2375 10.1541C10.9093 10.4823 10.4641 10.6667 10 10.6667Z"/>
</svg>
</template>
<el-option
v-for="(location, index) in options.entities.locations"
:key="index"
:label="location.name"
:value="location.id"
>
</el-option>
</el-select>
</el-col>
<!-- /Event Filter Location -->
</el-row>
</div>
<!-- /Event Filter -->
<!-- Event List -->
<div class="am-event-list" :style="{'opacity': !fetchedFiltered ? '0.3' : 1, 'pointer-events': fetchedFiltered ? 'all' : 'none'}">
<!-- Event -->
<div
v-for="(evt, index) in events"
v-if="evt.show"
:id="'am-event-' + evt.id"
:key="index"
class="am-event"
:class="[{
'am-active': evt.showEventDetails,
'inactive': events.filter(event => (event.showEventDetails || event.showEventBooking || event.showAddToCalendar) && event.id !== evt.id).length > 0,
'canceled': getEventAvailability(evt).class === 'canceled',
'closed': getEventAvailability(evt).class === 'closed' && !evt.showAddToCalendar,
'full': getEventAvailability(evt).class === 'full',
'upcoming': getEventAvailability(evt).class === 'upcoming'
}, $root.settings.customization.forms ? `am-form-${$options.name}` : '']"
:style="{'pointer-events': 'all'}"
>
<!-- Event Data -->
<div class="am-event-data" @click="toggleEventDetails(evt)">
<!-- Event Dates -->
<div v-if="isEventInSameDay(evt)" class="am-event-date">
<div class="am-event-date-month" :style="getBookableColor(evt, false)">
{{ getEventFrontedFormattedDate(evt.periods[0].periodStart).split(' ')[0] }}
</div>
<div class="am-event-date-day">
{{ getEventFrontedFormattedDate(evt.periods[0].periodStart).split(' ')[1] }}
</div>
</div>
<div v-else class="am-event-dates">
<div>
<div class="am-event-date-month" :style="getBookableColor(evt, false)">
{{ getEventFrontedFormattedDate(evt.periods[0].periodStart).split(' ')[0] }}
</div>
<div class="am-event-date-day">
{{ getEventFrontedFormattedDate(evt.periods[0].periodStart).split(' ')[1] }}
</div>
</div>
<div>
<div class="am-event-date-month" :style="getBookableColor(evt, false)">
{{ getEventFrontedFormattedDate(evt.periods[evt.periods.length - 1].periodEnd).split(' ')[0] }}
</div>
<div class="am-event-date-day">
{{ getEventFrontedFormattedDate(evt.periods[evt.periods.length - 1].periodEnd).split(' ')[1] }}
</div>
</div>
</div>
<!-- Event Info -->
<div class="am-event-info">
<div class="am-event-title">
{{ evt.name }}
<span class="am-event-booking-status" :class="getEventAvailability(evt).class" >
{{ getEventAvailability(evt).label }}
</span>
</div>
<div class="am-event-sub-info">
<div v-if="eventInfoLabels.event_capacity.visibility" class="am-event-sub-info-capacity">
<img :src="$root.getUrl + 'public/img/capacity.svg'">
{{ eventInfoLabels.event_capacity.value || $root.labels.event_capacity}}
{{ evt.maxCapacity - evt.places }} / {{ evt.maxCapacity }}
</div>
<div v-if="getLocation(evt) && eventInfoLabels.location.visibility">
<img :src="$root.getUrl + 'public/img/pin.svg'"> {{ getLocation(evt) }}
</div>
<div v-if="eventInfoLabels.event_date.visibility">
<img :src="$root.getUrl + 'public/img/clock.svg'"> {{ getEventDatesAndTimes(evt.periods) }}
</div>
<div v-if="eventInfoLabels.time_zone.visibility">
{{ timeZoneString }}
</div>
</div>
</div>
<!-- Event Price -->
<div v-if="(evt.price !== 0 && eventInfoLabels.event_price.visibility) || (evt.customTickets.length && evt.customPricing && eventInfoLabels.event_price.visibility)"
class="am-event-price" :style="getBookableColor(evt, true)">
{{
(evt.customTickets.length && evt.customPricing) ?
($root.labels.from + ' ' + getFormattedPrice(getMinTicketPrice(evt), !$root.settings.payments.hideCurrencySymbolFrontend)):
getFormattedPrice(evt.price, !$root.settings.payments.hideCurrencySymbolFrontend)
}}
</div>
<div v-else-if="eventInfoLabels.event_price.visibility" class="am-event-price am-event-free" :style="getBookableColor(evt, false)">
{{eventInfoLabels.event_free.value || $root.labels.event_free}}
</div>
<!-- /Event Price -->
</div>
<!-- /Event Data -->
<!-- Event Details -->
<transition name="fade">
<div v-show="evt.showEventDetails">
<!-- Event Details -->
<div
v-if="(evt.gallery && evt.gallery.length) || (evt.description && evt.description.length)"
class="am-event-details"
>
<div
v-if="evt.gallery && evt.gallery.length"
class="am-event-photos"
>
<div
v-for="(photo, index) in evt.gallery"
:key="photo.id"
>
<lightbox
:thumbnail="photo.pictureThumbPath"
:images="getImages(evt.gallery.map(image => image.pictureFullPath), index)"
>
<lightbox-default-loader slot="loader"></lightbox-default-loader>
<!-- If you want to use built-in loader -->
<!-- <div slot="loader"></div> --> <!-- If you want to use your own loader -->
</lightbox>
</div>
<!-- <img v-for="photo in evt.photos" :src="photo.url">-->
</div>
<div v-if="evt.description && evt.description.length" class="am-event-about">
<div>{{ eventInfoLabels.event_about.value || $root.labels.event_about }}</div>
<div v-html="evt.description" class="ql-description"></div>
</div>
</div>
<!-- Event Book -->
<div
v-if="getEventAvailability(evt).class === 'open'"
class="am-event-book-cta"
:class="getEventAvailability(evt).class"
>
<div v-if="eventInfoLabels.event_book.visibility" class="am-event-book-cta__inner">
<span>
{{eventInfoLabels.event_book.value || $root.labels.event_book}}
</span>
</div>
<div :style="{flexWrap: 'wrap', width: '100%'}">
<el-form>
<el-form-item
v-if="evt.bringingAnyone && !evt.customPricing"
:class="$root.settings.customization.forms ? `am-input-number-${$options.name}`: ''"
>
<template slot="label">
<span
:style="{
fontWeight: 700,
color: $root.settings.customization.useGlobalColors.eventListForm ? $root.settings.customization.globalColors.formTextColor : forms.eventListForm.globalSettings.formTextColor
}"
>
{{ eventInfoLabels.event_book_persons.value || $root.labels.event_book_persons }}
</span>
</template>
<el-input-number
:value="appointment.bookings[0].persons"
:min="1"
:max="(evt.maxExtraPeople !== null && evt.places > evt.maxExtraPeople ? (evt.maxExtraPeople + 1) : evt.places)"
type="number"
@change="setPlaces"
>
</el-input-number>
</el-form-item>
<!-- Custom Tickets -->
<div
:style="{flexWrap: 'wrap'}"
v-else-if="evt.customTickets.length && evt.customPricing"
class="am-ticket-box"
>
<div
class="am-ticket"
v-for="(ticket, index) in evt.customTickets"
:key="ticket.id"
v-if="ticket.enabled"
>
<el-row class="justify-content-center">
<el-col :sm="12" :lg="12" class="am-ticket-info">
<span class="am-ticket-name" :style="{fontWeight: 500, color: forms.eventListForm.globalSettings.formTextColor}">
{{ ticket.name }}
</span>
<div class="am-event-price" :style="{fontWeight: 500}">
{{ getFormattedPrice(ticket.dateRangePrice ? ticket.dateRangePrice : ticket.price, !$root.settings.payments.hideCurrencySymbolFrontend) }}
</div>
<span v-if="!evt.maxCustomCapacity && eventSpotsVisibility">
{{ ticketSpotsNumber(ticketSpots(evt, index, ticket)) }}
</span>
</el-col>
<el-col :sm="12" :lg="12" class="am-ticket-number-of-people">
<p>
{{ eventInfoLabels.event_book_persons.value || $root.labels.event_book_persons }}
</p>
<el-input-number
v-model="evt.bookingToEventTickets[index].persons"
size="mini"
type="number"
:style="{maxHeight: '32px', lineHeight: '32px', overflow: 'hidden'}"
:max="getMaxTicketSelected(evt, ticket, evt.bookingToEventTickets)"
:min="0"
:disabled="disableTicketSelection(evt, ticket, evt.bookingToEventTickets, evt.bookingToEventTickets[index].persons)"
@blur="() => {
if (!evt.bookingToEventTickets[index].persons) {
evt.bookingToEventTickets[index].persons = 0
}
}"
>
</el-input-number>
</el-col>
</el-row>
</div>
</div>
<!-- /Custom Tickets -->
</el-form>
</div>
</div>
<div class="am-event-book-continue" v-if="getEventAvailability(evt).class === 'open'">
<el-button
type="primary"
:style="getBookableColor(evt, true)"
:disabled="(!evt.customPricing && evt.places <= 0) || (evt.customPricing && evt.customTickets.length > 0 && selectedTicketsCount(evt.bookingToEventTickets) === 0)"
@click="toggleEventBooking(evt)"
>
{{$root.labels.continue}}
</el-button>
</div>
</div>
</transition>
<!-- /Event Details -->
<!-- Confirm Booking -->
<transition name="fade">
<div
v-show="evt.showEventBooking"
class="am-event-booking"
>
<confirm-booking
v-if="evt.showEventBooking"
:visible.sync="evt.showEventBooking"
dialogClass="am-confirm-booking-events-list"
v-bind="cacheData && (cacheData.status === 'canceled' || cacheData.status === 'failed') ? getCacheDataRequestProps() : {
bookableType: 'event',
containerId: getContainerId(),
taxes: options.entities.taxes,
bookable: getBookableData(evt),
marketing: marketing,
appointment: appointment,
event: evt,
customFields: options.entities.customFields,
phonePopulated: phonePopulated,
useGlobalCustomization: useGlobalCustomization(),
queryParams: getSearchParams()
}"
:form-type= "'eventListForm'"
:forms-data="forms['eventListForm']"
@confirmedBooking="confirmedBooking"
@cancelBooking="evt.showEventBooking = false"
>
</confirm-booking>
</div>
</transition>
<!-- /Confirm Booking -->
<!-- Add To Calendar -->
<transition name="fade">
<div
v-show="evt.showAddToCalendar"
class="am-event-booking"
>
<add-to-calendar
v-if="evt.showAddToCalendar"
:form-type="'eventListForm'"
:forms-data="forms['eventListForm']"
:addToCalendarData="evt.addToCalendarData"
@closeDialogAddToCalendar="evt.showAddToCalendar = false"
>
</add-to-calendar>
</div>
</transition>
<!-- /Add To Calendar -->
</div>
</div>
<!-- /Event List -->
<div class="am-event-pagination">
<el-pagination
v-show="pagination.count > pagination.show && events.length > 0"
:page-size="pagination.show"
:total="pagination.count"
layout="prev, pager, next"
:current-page.sync=pagination.page
>
</el-pagination>
</div>
<div class="am-empty-state am-section" v-show="events.length === 0">
<img :src="$root.getUrl + 'public/img/emptystate.svg'">
<p>{{ $root.labels.no_results }}</p>
</div>
</div>
<div v-if="$root.licence.isLite && $root.settings.general.backLink.enabled" class="am-lite-footer">
<a
rel="nofollow"
class="am-lite-footer-link"
:href="$root.settings.general.backLink.url"
target="_blank"
>
{{ $root.settings.general.backLink.label }}
</a>
</div>
</div>
</template>
<script>
import moment from 'moment'
import formsCustomizationMixin from '../../../js/common/mixins/formsCustomizationMixin'
import imageMixin from '../../../js/common/mixins/imageMixin'
import settingsMixin from '../../../js/common/mixins/settingsMixin'
import dateMixin from '../../../js/common/mixins/dateMixin'
import priceMixin from '../../../js/common/mixins/priceMixin'
import ConfirmBooking from '../parts/ConfirmBooking.vue'
import bookingMixin from '../../../js/frontend/mixins/bookingMixin'
import cacheMixin from '../../../js/frontend/mixins/cacheMixin'
import commonEventMixin from '../../../js/common/mixins/eventMixin'
import helperMixin from '../../../js/backend/mixins/helperMixin'
import AddToCalendar from '../parts/AddToCalendar.vue'
import customFieldMixin from '../../../js/common/mixins/customFieldMixin'
import translationMixin from '../../../js/common/mixins/translationMixin'
import marketingMixin from '../../../js/frontend/mixins/marketingMixin'
export default {
name: 'eventListForm',
mixins: [formsCustomizationMixin, cacheMixin, translationMixin, imageMixin, dateMixin, priceMixin, bookingMixin, commonEventMixin, helperMixin, customFieldMixin, settingsMixin],
data () {
return {
marketing: {
event: null,
payment: null
},
tags: [],
pagination: {
show: this.$root.settings.general.itemsPerPage,
page: 1,
count: 0
},
params: {
tag: null,
locationId: null,
date: 'ameliaBooking' in window && 'pastEventsDays' in window['ameliaBooking'] ? moment().subtract(window['ameliaBooking']['pastEventsDays'], 'days').toDate() : new Date(),
page: 1
},
options: {
entities: {
taxes: [],
tags: [],
locations: []
}
},
fetched: false,
fetchedFiltered: false,
events: [],
event: null,
payment: null,
appointment: {
bookings: [{
customer: {
email: '',
externalId: null,
firstName: '',
id: null,
lastName: '',
phone: ''
},
customFields: {},
customerId: 0,
extras: [],
persons: 1
}],
payment: {
amount: 0,
gateway: '',
data: {}
}
},
forms: {},
eventInfoLabels: {},
eventFilterLabels: {},
eventSpotLabel: '',
eventSpotsLabel: '',
eventNoSpotsLabel: '',
eventSpotsVisibility: true,
showSingleEventDetails: true,
timeZoneString: this.$root.settings.general.showClientTimeZone ? Intl.DateTimeFormat().resolvedOptions().timeZone : this.$root.settings.wordpress.timezone
}
},
created () {
this.forms = this.getTranslatedForms('eventListForm')
this.eventInfoLabels = this.$root.settings.customization.forms ? this.forms.eventListForm.eventDetailsForm.itemsStatic.eventDetailsFormField.labels : this.defaultFormsData.eventListForm.eventDetailsForm.itemsStatic.eventDetailsFormField.labels
this.showSingleEventDetails = (this.$root.settings.customization.forms && this.forms.eventListForm.eventDetailsForm.itemsStatic.eventDetailsFormField.hasOwnProperty('showSingleEvent')) ? this.forms.eventListForm.eventDetailsForm.itemsStatic.eventDetailsFormField.showSingleEvent : this.defaultFormsData.eventListForm.eventDetailsForm.itemsStatic.eventDetailsFormField.showSingleEvent
this.eventFilterLabels = this.$root.settings.customization.forms ? this.forms.eventListForm.eventFilterForm.itemsStatic.eventFilterFormField.labels : this.defaultFormsData.eventListForm.eventFilterForm.itemsStatic.eventFilterFormField.labels
this.eventSpotLabel = 'ev_spot' in this.eventInfoLabels ? this.eventInfoLabels.ev_spot.value || this.$root.labels.ev_spot : this.defaultFormsData.eventListForm.eventDetailsForm.itemsStatic.eventDetailsFormField.labels.ev_spot.value || this.$root.labels.ev_spot
this.eventSpotsLabel = 'ev_spots' in this.eventInfoLabels ? this.eventInfoLabels.ev_spots.value || this.$root.labels.ev_spots : this.defaultFormsData.eventListForm.eventDetailsForm.itemsStatic.eventDetailsFormField.labels.ev_spots.value || this.$root.labels.ev_spots
this.eventNoSpotsLabel = 'ev_no_spots' in this.eventInfoLabels ? this.eventInfoLabels.ev_no_spots.value || this.$root.labels.ev_no_spots : this.defaultFormsData.eventListForm.eventDetailsForm.itemsStatic.eventDetailsFormField.labels.ev_no_spots.value || this.$root.labels.ev_no_spots
this.eventSpotsVisibility =
this.$root.settings.customization.forms &&
'parts' in this.forms.eventListForm.eventDetailsForm.itemsStatic.eventDetailsFormField
? this.forms.eventListForm.eventDetailsForm.itemsStatic.eventDetailsFormField.parts.ev_spots_number.visibility
: this.defaultFormsData.eventListForm.eventFilterForm.itemsStatic.eventFilterFormField.parts.ev_spots_number.visibility
this.setCacheData(this.getContainerId(), true)
if (this.cacheData && this.cacheData.request.queryParams) {
this.pagination.page = this.cacheData.request.queryParams.page
this.params.page = this.cacheData.request.queryParams.page
this.params.date = new Date(this.cacheData.request.queryParams.dates[0])
if (this.cacheData.request.queryParams.tag) {
this.params.tag = this.cacheData.request.queryParams.tag
}
if (this.cacheData.request.queryParams.locationId) {
this.params.locationId = this.cacheData.request.queryParams.locationId
}
}
},
mounted () {
this.getEntities(this.processListEntities)
if (!this.$root.shortcodeData.hasEventShortcode) {
this.inlineBookingSVG()
}
// expand event if its selected on shortcode and recurring is set to off
let expandEvent = this.$root.shortcodeData.booking.eventId && this.$root.shortcodeData.booking.eventRecurring === 0
this.getEvents(expandEvent)
},
methods: {
runCacheAction () {
if (this.loadingCacheBookingData) {
if (this.cacheData.status === 'canceled' || this.cacheData.status === 'failed') {
let event = this.events.find(event => event.id === this.cacheData.request.bookable.id)
event.showEventBooking = true
setTimeout(() => {
this.cacheData = null
}, 500)
} else if (this.cacheData.status === 'paid' || this.cacheData.status === null) {
this.confirmedBooking(this.cacheData.response, true, this.cacheData.request)
}
this.loadingCacheBookingData = false
}
},
getContainerId () {
return 'amelia-app-booking' + this.$root.shortcodeData.counter
},
useGlobalCustomization () {
return 'ameliaBooking' in window && 'useGlobalCustomization' in window.ameliaBooking && window.ameliaBooking.useGlobalCustomization === true
},
getImages (gallery, index) {
for (let i = 0; i < index; i++) {
gallery.push(gallery.shift())
}
return gallery
},
setPlaces (value) {
this.appointment.bookings[0].persons = value
},
getBookableColor (bookable, colorBackground) {
return colorBackground ? {
'color': '#ffffff',
'background-color': bookable.color,
'border-color': '#ffffff'
} : {
'color': bookable.color,
'background-color': '',
'border-color': ''
}
},
getEventDatesAndTimes (periods) {
let $this = this
let resultPeriods = []
this.getImplodedPeriods(periods).forEach(function (period) {
let periodStart = period.periodStart.split(' ')
let periodEnd = period.periodEnd.split(' ')
if (period.isConnected) {
resultPeriods.push($this.getFrontedFormattedDateTime(periodStart) + ' - ' + $this.getFrontedFormattedDateTime(periodEnd))
} else {
if (periodStart[0] === periodEnd[0]) {
resultPeriods.push($this.getFrontedFormattedDate(periodStart[0]) + ' ' + $this.getFrontedFormattedTime(periodStart[1]) + ' - ' + $this.getFrontedFormattedTime(periodEnd[1]))
} else {
resultPeriods.push($this.getFrontedFormattedDate(periodStart[0]) + ' - ' + $this.getFrontedFormattedDate(periodEnd[0]) + ' ' + $this.getFrontedFormattedTime(periodStart[1]) + ' - ' + $this.getFrontedFormattedTime(periodEnd[1]))
}
}
})
return resultPeriods.join(', ')
},
showTags () {
return this.options.entities.tags.length > 1 && this.showDatePicker()
},
showDatePicker () {
return this.getPreselectedEventId() === null || (this.getPreselectedEventId() !== null && this.getPreselectedEventRecurring())
},
showLocations () {
return this.options.entities.locations.length > 1 && this.showDatePicker()
},
getColumnLength () {
if (this.showTags() && this.showLocations()) {
return [12, 12, 24]
}
if (!this.showTags() && this.showLocations()) {
return [0, 12, 12]
}
if (this.showTags() && !this.showLocations()) {
return [12, 12, 0]
}
if (!this.showTags() && !this.showLocations()) {
return [0, 24, 0]
}
return [12, 12, 24]
},
getEventAvailability (evt) {
if (evt.status === 'approved' || evt.status === 'pending') {
if (evt.full) {
return {
label: this.eventInfoLabels.full.value || this.$root.labels.full,
class: 'full'
}
}
if (evt.upcoming) {
return {
label: this.eventInfoLabels.upcoming.value || this.$root.labels.upcoming,
class: 'upcoming'
}
}
return !evt.bookable ? {
label: this.eventInfoLabels.closed.value || this.$root.labels.closed,
class: 'closed'
} : {
label: this.eventInfoLabels.open.value || this.$root.labels.open,
class: 'open'
}
} else {
return {
label: this.eventInfoLabels.canceled.value || this.$root.labels.canceled,
class: 'canceled'
}
}
},
isEventInSameDay (evt) {
let result = true
if (evt.periods.length === 1) {
result = evt.periods[0].periodStart.split(' ')[0] === evt.periods[0].periodEnd.split(' ')[0]
} else {
let periodStart = evt.periods[0].periodStart.split(' ')[0]
let periodEnd = evt.periods[0].periodEnd.split(' ')[0]
evt.periods.forEach(function (period) {
if (period.periodStart.split(' ')[0] !== periodStart || period.periodEnd.split(' ')[0] !== periodEnd) {
result = false
}
})
}
return result
},
confirmedBooking (responseData, skipNotify, requestData) {
let event = this.events.find(event => event.id === responseData.event.id)
if (event.customTickets.length && event.customPricing) {
event.bookingToEventTickets.forEach(ticket => {
event.customTickets.filter(tick => tick.id === ticket.eventTicketId)[0].sold += ticket.persons
ticket.persons = 0
})
event.places = (typeof requestData === 'undefined') ? event.maxCapacity - this.getAllSoldTickets(event) : event.places
} else {
event.places = (typeof requestData === 'undefined') ? event.places - responseData.booking.persons : event.places
}
if (event.places <= 0) event.full = true
event.addToCalendarData = this.getEventAddToCalendarData(
responseData,
skipNotify
)
if ((typeof requestData !== 'undefined')) {
event.showEventDetails = false
event.addToCalendarData.forceScroll = true
}
if (marketingMixin.hasAmeliaTracking(this)) {
this.marketing.event = responseData.event
if (typeof requestData !== 'undefined' && requestData !== null) {
this.marketing.payment = requestData.appointment.payment
} else {
this.marketing.payment = this.appointment.payment
}
if (this.appointment.payment.gateway === 'onSite') {
marketingMixin.trackAmeliaData(this, this.$root.marketing, 'event', 'Schedule')
} else {
marketingMixin.trackAmeliaData(this, this.$root.marketing, 'event', 'Purchase')
}
}
// Customization hook
if ('beforeConfirmedBooking' in window) {
window.beforeConfirmedBooking(event.addToCalendarData)
} else {
event.showEventBooking = false
event.showAddToCalendar = true
event.bookingCompleted = true
}
},
getSearchParams () {
let params = JSON.parse(JSON.stringify(this.params))
let eventId = this.getPreselectedEventId()
let recurring = this.getPreselectedEventRecurring()
let tagName = this.getPreselectedTag() ? this.getPreselectedTag() : params.tag
let employee = this.getPreselectedEmployee()
if (!tagName) {
tagName = null
}
return {
dates: params.date ? [
this.getDateString(params.date)
] : [
this.getDateString(this.getNowDate())
],
tag: tagName,
locationId: params.locationId,
page: this.pagination.page,
id: eventId,
recurring: recurring,
providers: employee ? [employee] : null
}
},
getBookableData (evt) {
let ticketsData = null
if (evt.customTickets.length && evt.customPricing) {
let totalPrice = 0
let totalTickets = 0
evt.customTickets.forEach(ticket => {
let ticketPrice = evt.bookingToEventTickets.filter(tick => tick.eventTicketId === ticket.id)[0].price
let ticketQuant = evt.bookingToEventTickets.filter(tick => tick.eventTicketId === ticket.id)[0].persons
evt.bookingToEventTickets.filter(tick => tick.eventTicketId === ticket.id)[0].name = ticket.name
totalTickets += ticketQuant
totalPrice += (evt.aggregatedPrice || ticketQuant === 0 ? ticketQuant : 1) * ticketPrice
})
evt.price = totalPrice
ticketsData = {
totalTickets,
totalPrice,
bookingToEventTickets: evt.bookingToEventTickets
}
}
return {
id: evt.id,
name: evt.name,
price: evt.price,
depositData: evt.depositPayment !== 'disabled' ? {
deposit: evt.deposit,
depositPayment: evt.depositPayment,
depositPerPerson: evt.depositPerPerson,
depositFullPayment: evt.fullPayment
} : null,
maxCapacity: evt.maxCapacity,
color: evt.color,
aggregatedPrice: evt.aggregatedPrice,
bookingStart: evt.periods[0].periodStart,
bookingStartTime: evt.periods[0].periodStart.split(' ')[1],
ticketsData
}
},
getEvents (expandEvent) {
this.$http.get(`${this.$root.getAjaxUrl}/events`, {
params: this.getAppropriateUrlParams(this.getSearchParams())
})
.then(response => {
let $this = this
this.events = []
this.pagination.count = response.data.data.count
response.data.data.events.forEach(function (event) {
event.gallery = event.gallery.sort((a, b) => (a.position > b.position) ? 1 : -1)
event.showEventDetails = (expandEvent || response.data.data.events.length === 1) && $this.showSingleEventDetails
event.showEventBooking = false
event.showAddToCalendar = false
event.bookingCompleted = false
if ($this.$root.useTranslations) {
event.name = $this.getNameTranslated(event)
event.description = $this.getDescriptionTranslated(event)
event.customTickets.forEach((ticket) => {
ticket.name = $this.getTicketTranslated(ticket)
})
}
if (event.customTickets.length && event.customPricing) {
event.bookingToEventTickets = $this.setTicketsData(event)
}
$this.events.push(event)
if ($this.$root.settings.general.showClientTimeZone) {
event.periods.forEach(function (period) {
let utcOffsetStart = moment(period.periodStart, 'YYYY-MM-DD HH:mm:ss').toDate().getTimezoneOffset()
let utcOffsetEnd = moment(period.periodEnd, 'YYYY-MM-DD HH:mm:ss').toDate().getTimezoneOffset()
if (utcOffsetStart > 0) {
period.periodStart = moment.utc(period.periodStart, 'YYYY-MM-DD HH:mm:ss').subtract(utcOffsetStart, 'minutes').format('YYYY-MM-DD HH:mm:ss')
} else {
period.periodStart = moment.utc(period.periodStart, 'YYYY-MM-DD HH:mm:ss').add(-1 * utcOffsetStart, 'minutes').format('YYYY-MM-DD HH:mm:ss')
}
if (utcOffsetEnd > 0) {
period.periodEnd = moment.utc(period.periodEnd, 'YYYY-MM-DD HH:mm:ss').subtract(utcOffsetEnd, 'minutes').format('YYYY-MM-DD HH:mm:ss')
} else {
period.periodEnd = moment.utc(period.periodEnd, 'YYYY-MM-DD HH:mm:ss').add(-1 * utcOffsetEnd, 'minutes').format('YYYY-MM-DD HH:mm:ss')
}
})
}
})
this.runCacheAction()
if ('ameliaEventsLoaded' in window) {
window.ameliaEventsLoaded($this.events)
}
this.fetched = true
this.fetchedFiltered = true
})
.catch(e => {
console.log(e.message)
})
},
toggleEventDetails (evt) {
evt.showEventDetails = !evt.showEventDetails
this.events.forEach(function (event) {
if (event.id !== evt.id) {
event.showEventDetails = false
event.showEventBooking = false
event.showAddToCalendar = false
event.showConfirmBooking = false
}
})
evt.showEventBooking = false
this.appointment.bookings[0].persons = 1
this.updateSettings(evt.settings)
setTimeout(() => {
this.scrollView('am-event-' + evt.id, 'start')
}, 300)
if ('ameliaEventSelected' in window) {
window.ameliaEventSelected(evt)
}
if (marketingMixin.hasAmeliaTracking(this.$root.marketing, evt)) {
this.marketing.event = evt
marketingMixin.trackAmeliaData(this, this.$root.marketing, 'event', 'SelectEvent')
}
},
toggleEventBooking (evt) {
evt.showEventDetails = !evt.showEventDetails
evt.showEventBooking = !evt.showEventBooking
this.appointment.payment = {
amount: 0,
gateway: '',
data: {}
}
setTimeout(() => {
let element = document.getElementById(`am-event-${evt.id}`)
if (typeof element !== 'undefined' && element !== null) {
element.scrollIntoView({behavior: 'smooth', block: 'start', inline: 'start'})
}
}, 300)
this.updateSettings(evt.settings)
},
getLocation (evt) {
if (evt.locationId && this.options.entities.locations.length) {
let location = this.options.entities.locations.find(location => location.id === evt.locationId)
return typeof location !== 'undefined' ? location.name : ''
} else if (evt.customLocation) {
return evt.customLocation
}
},
inlineBookingSVG () {
let inlineSVG = require('inline-svg')
inlineSVG.init({
svgSelector: 'img.svg-booking',
initClass: 'js-inlinesvg'
})
},
ticketSpots (evt, index, ticket) {
return ticket.spots - ticket.sold - evt.bookingToEventTickets[index].persons
},
ticketSpotsNumber (spots) {
if (spots <= 0) {
spots = ''
}
return `${spots} ${this.spotsLabels(spots)}`
},
spotsLabels (number) {
if (number > 1) {
return this.eventSpotsLabel
}
if (number === 1) {
return this.eventSpotLabel
}
return this.eventNoSpotsLabel
}
},
computed: {
selectedDateInput () {
return this.$moment(this.params.date).format(this.momentDateFormat)
}
},
watch: {
'pagination.page' () {
if (this.cacheData && this.cacheData.request.queryParams) {
this.cacheData.request.queryParams = null
} else {
this.getEvents(false)
}
}
},
components: {
ConfirmBooking,
AddToCalendar
}
}
</script>