Uname: Linux webm012.cluster130.gra.hosting.ovh.net 5.15.167-ovh-vps-grsec-zfs-classid #1 SMP Tue Sep 17 08:14:20 UTC 2024 x86_64
Software: Apache
PHP version: 8.0.30 [ PHP INFO ] PHP os: Linux
Server Ip: 145.239.37.162
Your Ip: 216.73.216.190
User: dreampi (1009562) | Group: users (100)
Safe Mode: OFF
Disable Function:
_dyuweyrj4,_dyuweyrj4r,dl

name : Booking.vue
<template>
  <div id="amelia-booking-wrap" class="am-wrap">
    <!-- Spinner -->
    <div class="am-spinner am-section" v-show="!empty && (!fetched || loadingCacheBookingData)">
      <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 v-if="empty" class="am-no-services">
      <img :src="$root.getUrl+'public/img/am-empty-booking.svg'" style="margin-top: 10px;">
      <h1>{{$root.labels.oops}}</h1>
      <h3 v-if="$root.shortcodeData.booking.show !== 'packages'">{{$root.labels.no_services_employees}}</h3>
      <p v-if="$root.shortcodeData.booking.show !== 'packages'">{{$root.labels.add_services_employees}}</p>
      <p v-if="$root.shortcodeData.booking.show === 'packages'">{{$root.labels.no_package_services}}</p>
      <a href="https://wpamelia.com/services-and-categories/" rel="nofollow">
        {{$root.labels.add_services_url}}
      </a>
      <span v-if="$root.shortcodeData.booking.show !== 'packages'" style="font-size:14px">{{$root.labels.and}}</span>
      <a v-if="$root.shortcodeData.booking.show !== 'packages'" href="https://wpamelia.com/employees/" rel="nofollow">
        {{$root.labels.add_employees_url}}
      </a>
    </div>

    <div :id="id" class="am-step-booking-catalog" :class="$root.settings.customization.forms ? `am-form-${formType}` : ''" v-show="!empty && fetched">
      <!-- Select Service Form -->
      <select-service-form
        v-if="!bookingCompleted && !activePackage && !passedPackage && !showPackagesOnly && !packageCalendarActive"
        :show-services="showServices"
        :show-locations="showLocations"
        :show-employees="showEmployees"
        :passed-service="passedService"
        :appointment="appointment"
        :group="group"
        :selected-extras="selectedExtras"
        :options="options"
        :fetched="fetched"
        :extras-error="extrasError"
        :form-type="formType"
        :forms-data="forms[formType]"
        :loading-time-slots="loadingTimeSlots"
        @change="changeFormItem"
        @getSlots="getTimeSlots"
        @enableGrouping="enableGrouping"
      ></select-service-form>
      <!-- /Select Service Form -->

      <!-- Recurring Setup Form -->
      <recurring-setup
        v-if="activeRecurringSetup && !bookingCompleted"
        :containerId="id"
        :initialRecurringData="initialRecurringData"
        :recurringData="recurringData"
        :disabledWeekdays="disabledWeekdays"
        :availableDates="availableDates"
        :calendarTimeSlots="calendarTimeSlots"
        :occupiedTimeSlots="occupiedTimeSlots"
        :service="getServiceById(appointment.serviceId)"
        :isFrontend="true"
        :form-type="formType"
        :forms-data="forms[formType]"
        @cancelRecurringSetup="cancelRecurringSetup"
        @confirmRecurringSetup="confirmRecurringSetup"
      >
      </recurring-setup>
      <!-- /Recurring Setup Form -->

      <!-- Recurring Dates Form -->
      <recurring-dates
        v-if="activeRecurringDates && !bookingCompleted"
        dialogClass="am-recurring-dates"
        :recurringData="recurringData"
        :availableDates="availableDates"
        :calendarTimeSlots="calendarTimeSlots"
        :isFrontend="true"
        :service="getServiceById(appointment.serviceId)"
        :selectedExtras="selectedExtras"
        :form-type="formType"
        :forms-data="forms[formType]"
        @confirmRecurringDates="confirmRecurringDates"
        @cancelRecurringDates="cancelRecurringDates"
      >
      </recurring-dates>
      <!-- /Recurring Dates Form -->

      <!-- Calendar Date Time Form -->
      <calendar-date-time-form
        v-if="!bookingCompleted && !activeRecurringSetup && !activePackage && !passedPackage && !showPackagesOnly && !packageCalendarActive"
        :id="id"
        :selected-date="selectedDate"
        :available-dates="availableDates"
        :disabled-weekdays="disabledWeekdays"
        :disabled-attribute="disabledAttribute"
        :calendar-id="calendarId"
        :show-times="showTimes"
        :appointment="appointment"
        :available-time-slots="availableTimeSlots"
        :is-recurring-available="isRecurringAvailable"
        :active-recurring="activeRecurring"
        :show-calendar-back-button="showCalendarBackButton"
        :show-calendar-continue-button="showCalendarContinueButton"
        :loading="loading"
        :loading-time-slots="loadingTimeSlots"
        :durations="getDurations()"
        :form-type="formType"
        :forms-data="forms[formType]"
        @selectDuration="getTimeSlots"
        @selectDate="selectDate"
        @setTimeSlots="setTimeSlots"
        @selectTime="selectTime"
        @changeMonth="changeMonth"
        @togglePicker="togglePicker"
        @showNextScreen="showNextScreen"
        @changeRecurring="changeActiveRecurring"
      ></calendar-date-time-form>
      <!-- /Calendar Date Time Form -->

      <!-- Select Package Form -->
      <select-package-form
        v-if="!bookingCompleted && !activePackage && !passedPackage && !packageListShown && !activeConfirm && showPackagesOnly"
        :selected-package-id="selectedPackageId"
        :fetched="fetched"
        :options="options"
        :form-type="formType"
        :forms-data="forms[formType]"
        @continuePackage="continuePackage"
      ></select-package-form>
      <!-- /Select Package Form -->

      <!-- Package Info Form -->
      <package-info
        v-if="!bookingCompleted && activePackage && !passedPackage"
        class="am-package-selected"
        :id="id"
        :fetched-slots="fetchedSlots"
        :selected-package="selectedPackage"
        :package-slots-fetched="packageSlotsFetched"
        :has-back="packagesHaveBack"
        :has-header="true"
        :form-type="formType"
        :forms-data="forms[formType]"
        @showPackageCalendar="packageServiceSelected++"
        @closePackage="closePackage"
        @packageSlotsSelectedCallback="packageSlotsSelectedCallback"
      >
      </package-info>
      <!-- /Package Info Form -->

      <!-- Package Setup Form -->
      <package-setup
        v-if="!bookingCompleted && (selectedPackage ? selectedPackage.hasSlots : true) && packageCalendarActive"
        class="am-select-date am-select-service-date-transition am-package-setup"
        :id="id"
        :options="options"
        :selectedPackage="selectedPackage"
        :packageServiceSelected="packageServiceSelected"
        :packageCreated="packageCreated"
        :isPassedPackage="!!passedPackage"
        :disableFetchSlots="disableFetchPackageSlots"
        :form-type="formType"
        :forms-data="forms[formType]"
        @packageSlotsFetchedCallback="packageSlotsFetchedCallback"
        @packageSlotsSelectedCallback="packageSlotsSelectedCallback"
        @packageListShow="packageListShow"
      >
      </package-setup>
      <!-- /Package Setup Form -->

      <!-- Package List Form -->
      <package-list
        v-if="!bookingCompleted && packageListShown"
        :container-id="'amelia-app-booking' + $root.shortcodeData.counter"
        :bookable="getBookablePackageData()"
        :has-header="!passedPackage"
        :quantity="selectedPackage.sharedCapacity ? selectedPackage.quantity : 0"
        :form-type="formType"
        :forms-data="forms[formType]"
        @cancelBooking="cancelPackageList"
        @packageSlotsSelectedCallback="packageSlotsSelectedCallback"
      >
      </package-list>
      <!-- /Package List Form -->

      <!-- Confirm Booking -->
      <confirm-booking
        v-if="activeConfirm && !bookingCompleted"
        dialogClass="am-confirm-booking am-scroll"
        v-bind="cacheData && (cacheData.status === 'canceled' || cacheData.status === 'failed') ? getCacheDataRequestProps() : {
          bookableType: selectedPackage === null ? 'appointment' : 'package',
          containerId: getContainerId(),
          taxes: options.entities.taxes,
          bookable: getBookableData(),
          appointment: appointment,
          marketing: marketing,
          provider: selectedProvider,
          location: selectedLocation,
          service: selectedService,
          customFields: options.entities.customFields,
          recurringData: getRecurringAppointmentsData(),
          packageData: selectedPackage === null ? null : getPackageAppointmentsData(),
          recurringString: recurringData.recurringString + ' ' + recurringData.untilString,
          hasCancel: !passedPackage || passedPackage.hasSlots,
          hasHeader: !passedPackage,
          phonePopulated: phonePopulated,
          passedCategoryId: passedCategory ? passedCategory.id : null,
          queryParams: []
        }"
        :form-type="formType"
        :forms-data="forms[formType]"
        @confirmedBooking="confirmedBooking"
        @cancelBooking="cancelBooking"
      >
      </confirm-booking>
      <!-- /Confirm Booking -->

      <!-- Add To Calendar -->
      <transition name="fade">
        <add-to-calendar
          v-if="showAddToCalendar"
          ref="congratulations"
          :addToCalendarData="addToCalendarData"
          :form-type="formType"
          :forms-data="forms[formType]"
          @closeDialogAddToCalendar="closeDialogAddToCalendar"
        ></add-to-calendar>
      </transition>
      <!-- /Add To Calendar -->

    </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 marketingMixin from '../../../js/frontend/mixins/marketingMixin'
  import cacheMixin from '../../../js/frontend/mixins/cacheMixin'
  import formsCustomizationMixin from '../../../js/common/mixins/formsCustomizationMixin'
  import imageMixin from '../../../js/common/mixins/imageMixin'
  import dateMixin from '../../../js/common/mixins/dateMixin'
  import priceMixin from '../../../js/common/mixins/priceMixin'
  import servicePriceMixin from '../../../js/common/mixins/servicePriceMixin'
  import helperMixin from '../../../js/backend/mixins/helperMixin'
  import settingsMixin from '../../../js/common/mixins/settingsMixin'
  import entitiesMixin from '../../../js/common/mixins/entitiesMixin'
  import recurringMixin from '../../../js/common/mixins/recurringMixin'
  import packageMixin from '../../../js/frontend/mixins/packageMixin'
  import durationMixin from '../../../js/common/mixins/durationMixin'
  import customFieldMixin from '../../../js/common/mixins/customFieldMixin'
  import bookingMixin from '../../../js/frontend/mixins/bookingMixin'
  import slotsMixin from '../../../js/common/mixins/slotsMixin'
  import selectServiceForm from './components/formSteps/SelectServiceForm'
  import calendarDateTimeForm from './components/formSteps/CalendarDateTimeForm'
  import recurringSetup from '../../parts/RecurringSetup.vue'
  import recurringDates from '../../parts/RecurringDates.vue'
  import selectPackageForm from './components/formSteps/SelectPackageForm'
  import packageInfo from '../package/Package.vue'
  import packageSetup from '../package/PackageSetup.vue'
  import packageList from '../package/PackageList.vue'
  import confirmBooking from '../parts/ConfirmBooking.vue'
  import addToCalendar from '../parts/AddToCalendar.vue'

  export default {

    mixins: [
      slotsMixin,
      cacheMixin,
      formsCustomizationMixin,
      packageMixin,
      recurringMixin,
      imageMixin,
      dateMixin,
      entitiesMixin,
      bookingMixin,
      helperMixin,
      durationMixin,
      priceMixin,
      servicePriceMixin,
      customFieldMixin,
      settingsMixin
    ],

    props: {
      id: {
        default: 'am-step-booking'
      },
      showService: {
        type: Boolean,
        default: true
      },
      addToCalendarProperty: {
        type: Object,
        default: () => {
          return {
            visible: true
          }
        }
      },
      passedService: {
        default: () => {},
        type: Object
      },
      passedPackage: {
        default: () => {},
        type: Object
      },
      passedCategory: {
        default: () => {},
        type: Object
      },
      passedEntities: {
        default: () => {},
        type: Object
      },
      passedEntitiesRelations: {
        default: () => {},
        type: Object
      },
      formType: {
        type: String,
        default: 'stepByStepForm'
      }
    },

    data () {
      return {
        empty: false,
        packagesHaveBack: true,
        showPackagesOnly: false,
        selectedWeekIndex: 0,
        selectedProvider: null,
        selectedLocation: null,
        selectedService: null,
        loading: false,
        isRecurringAvailable: false,
        initialRecurringData: null,
        recurringData: {
          dates: [],
          startAppointment: null,
          startDate: null,
          startTime: null,
          pageRecurringDates: [],
          pagination: {
            show: this.$root.settings.general.itemsPerPage,
            page: 1,
            count: 0
          },
          recurringString: '',
          untilString: '',
          datesCallback: null,
          setupCallback: null
        },
        activeRecurring: false,
        activeRecurringSetup: false,
        activeRecurringDates: false,
        selectedMonth: moment().format('YYYY-MM'),
        isServiceChanged: true,
        calendarId: '',
        activeConfirm: false,
        bookingCompleted: false,
        activePicker: false,
        packageCalendarActive: false,
        addToCalendarData: null,
        appointment: {
          bookingStart: '',
          bookingStartTime: '',
          bookings: [{
            customer: {
              email: '',
              externalId: null,
              firstName: '',
              id: null,
              lastName: '',
              phone: ''
            },
            customFields: {},
            customerId: 0,
            extras: [],
            persons: 1,
            haveMandatoryExtras: false,
            minSelectedExtras: 0
          }],
          duration: 0,
          group: false,
          notifyParticipants: this.$root.settings.notifications.notifyCustomers,
          payment: {
            amount: 0,
            gateway: '',
            data: {}
          },
          categoryId: null,
          providerId: 0,
          serviceId: null,
          locationId: null
        },
        availableDates: [],
        availableTimeSlots: [],
        calendar: '',
        calendarTimeSlots: {},
        occupiedTimeSlots: {},
        bookedTimeSlots: {},
        calendarVisible: false,
        customer: {
          name: '',
          email: '',
          phone: '',
          paymentMethod: ''
        },
        customerRules: {
          name: [
            {required: true, message: 'Please input name', trigger: 'submit'},
            {min: 3, max: 50, message: 'Length should be 3 to 50', trigger: 'submit'}],
          email: [
            {required: true, message: 'Please input name', trigger: 'submit'},
            {min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'submit'}],
          phone: '',
          paymentMethod: ''
        },
        disabledAttribute: {
          contentStyle: {
            color: '#ccc',
            opacity: 0.4,
            textDecoration: 'line-through'
          }
        },
        disabledWeekdays: null,
        fetched: false,
        fetchedSlots: false,
        group: {
          allowed: false,
          enabled: false,
          count: 1,
          options: []
        },
        loadingTimeSlots: false,
        slotsIndexStarted: 0,
        responseEntities: {
          taxes: [],
          categories: [],
          employees: [],
          locations: [],
          services: []
        },
        options: {
          availableEntitiesIds: {
            categories: [],
            employees: [],
            locations: [],
            services: []
          },
          entitiesRelations: {},
          entities: {
            taxes: [],
            packages: [],
            services: [],
            employees: [],
            locations: [],
            customFields: []
          }
        },
        marketing: {
          service: null,
          employee: null,
          location: null,
          category: null,
          package: null,
          payment: null
        },
        selectedExtras: [],
        previouslySelectedExtras: [],
        selectedDate: null,
        showAddToCalendar: false,
        showExtras: false,
        showFilters: false,
        showTimes: false,
        showServices: false,
        showEmployees: false,
        showLocations: false,
        extrasMandatory: false,
        showCalendarBackButton: false,
        showCalendarContinueButton: false,
        times: '',
        extrasError: [],
        forms: {}
      }
    },

    created () {
      this.forms = this.getTranslatedForms('stepByStepForm')

      this.calendarId = 'am-appointment-times' + this.$root.shortcodeData.counter
      window.addEventListener('resize', this.handleResize)
    },

    mounted () {
      if (this.addToCalendarProperty.visible) {
        this.setCacheData(this.getContainerId(), true)
      } else {
        this.showAddToCalendar = false
      }

      if (!this.$root.shortcodeData.hasBookingShortcode || !this.$root.shortcodeData.hasCategoryShortcode) {
        this.inlineBookingSVG()
      }

      if (this.passedService) {
        this.checkMandatoryExtras(this.passedService)
      }

      // Customization hook
      if ('beforeBookingLoaded' in window) {
        window.beforeBookingLoaded(this)
      }

      if (this.passedEntities && !this.passedPackage) {
        this.options.isFrontEnd = true
        this.options.entitiesRelations = Object.assign({}, this.passedEntitiesRelations)

        let shortCodeEntitiesIds = this.getShortCodeEntityIds()

        this.filterEntities(this.passedEntities, {
          categoryId: this.passedService.categoryId,
          serviceId: this.passedService.id,
          employeeId: shortCodeEntitiesIds.employeeId,
          locationId: shortCodeEntitiesIds.locationId
        })

        this.fetchedEntities()

        this.times = document.getElementById(this.calendarId)
      } else if (!this.passedPackage) {
        let shortCodeEntitiesIds = this.getShortCodeEntityIds()

        if (shortCodeEntitiesIds.show === 'packages') {
          this.showPackagesOnly = true
          this.packageCalendarActive = true
        } else if (shortCodeEntitiesIds.show === 'services') {
          this.showPackagesOnly = false
        }

        let $this = this

        this.fetchEntities(function (success) {
          if (success) {
            $this.fetchedEntities()
          }
        }, {
          types: ['locations', 'employees', 'categories', 'custom_fields', 'packages', 'taxes'],
          isFrontEnd: true,
          isPanel: false
        })

        this.times = document.getElementById(this.calendarId)
      } else if (this.passedPackage) {
        this.options.isFrontEnd = true
        this.options.entitiesRelations = Object.assign({}, this.passedEntitiesRelations)
        this.options.entities = this.passedEntities
        this.setBookingCustomFields()
        this.selectPackage(this.passedPackage)

        if (!this.passedPackage.hasSlots || this.loadingCacheBookingData) {
          this.fetched = true
          this.packageSlotsSelectedCallback()

          this.runCacheAction()
        }
        if (this.options.entities.services.length === 0 || this.options.entities.employees.length === 0) this.empty = true
      }
    },

    updated () {
      this.handleResize()
    },

    methods: {
      changeFormItem (fieldObj) {
        if (fieldObj.identifier === 'changeService') {
          this.changeService()

          if (marketingMixin.hasAmeliaTracking(this.$root.marketing, this.appointment.serviceId)) {
            this.marketing.service = this.getServiceById(this.appointment.serviceId)

            this.marketing.category = this.marketing.service
              ? this.getCategoryById(this.marketing.service.categoryId) : null

            marketingMixin.trackAmeliaData(this, this.$root.marketing, 'appointment', 'SelectService')
          }
        }

        if (fieldObj.identifier === 'selectPackage') {
          this.selectPackage(fieldObj.value)

          if (marketingMixin.hasAmeliaTracking(this.$root.marketing, fieldObj.value.id)) {
            this.marketing.package = this.getPackageById(fieldObj.value.id)

            marketingMixin.trackAmeliaData(this, this.$root.marketing, 'package', 'SelectPackage')
          }
        }

        if (fieldObj.identifier === 'changeLocation') {
          this.changeLocation(fieldObj.required)

          if (marketingMixin.hasAmeliaTracking(this.$root.marketing, this.appointment.locationId)) {
            this.marketing.location = this.getLocationById(this.appointment.locationId)

            marketingMixin.trackAmeliaData(this, this.$root.marketing, 'appointment', 'SelectLocation')
          }
        }

        if (fieldObj.identifier === 'changeEmployee') {
          this.changeEmployee(fieldObj.required)

          if (marketingMixin.hasAmeliaTracking(this.$root.marketing, this.appointment.providerId)) {
            this.marketing.employee = this.getProviderById(this.appointment.providerId)

            marketingMixin.trackAmeliaData(this, this.$root.marketing, 'appointment', 'SelectEmployee')
          }
        }

        if (fieldObj.identifier === 'enableGrouping') {
          this.enableGrouping()
        }

        if (fieldObj.identifier === 'changeNumberOfPersons') {
          this.changeNumberOfPersons()
        }

        if (fieldObj.identifier === 'addExtra') {
          this.addExtra()
        }

        if (fieldObj.identifier === 'changeSelectedExtra') {
          this.changeSelectedExtra(fieldObj.value, fieldObj.key)
        }

        if (fieldObj.identifier === 'deleteExtra') {
          this.deleteExtra(fieldObj.value)
        }
      },

      changeActiveRecurring (recurring) {
        this.activeRecurring = recurring
      },

      runCacheAction () {
        if (this.loadingCacheBookingData) {
          if (this.cacheData.status === 'canceled' || this.cacheData.status === 'failed') {
            this.showConfirmBooking()
          } 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
      },

      showConfirmBooking () {
        this.activeConfirm = true

        this.loading = true

        let amContainer = document.getElementById(this.id)

        setTimeout(() => {
          this.loading = false

          this.loadingCacheBookingData = false

          amContainer.classList.toggle('am-active-picker', false)
          amContainer.classList.toggle('am-active-confirm', this.activeConfirm)
        }, 500)
      },

      checkMandatoryExtras (service) {
        this.appointment.bookings[0].haveMandatoryExtras = service.mandatoryExtra
        this.appointment.bookings[0].minSelectedExtras = service.minSelectedExtras
        for (let i = 0; i < service.minSelectedExtras; i++) {
          if (service.mandatoryExtra) {
            this.extrasMandatory = true
            this.addExtra()
          }
        }
      },

      getBookableData () {
        if (this.selectedPackage === null) {
          this.selectedService = this.getProviderById(this.selectedProvider.id).serviceList.find(service => service.id === this.appointment.serviceId)

          if (marketingMixin.hasAmeliaTracking(this)) {
            this.marketing.service = this.selectedService
            this.marketing.employee = this.selectedProvider
            this.marketing.location = this.selectedLocation
            this.marketing.category = this.getCategoryById(this.selectedService.categoryId)
          }

          return {
            id: this.selectedService.id,
            name: this.selectedService.name,
            price: this.getBookingServicePrice(this.selectedService, this.appointment.serviceDuration, this.appointment.bookings[0].persons),
            depositData: this.selectedService.depositPayment !== 'disabled' ? {
              deposit: this.selectedService.deposit,
              depositPayment: this.selectedService.depositPayment,
              depositPerPerson: this.selectedService.depositPerPerson,
              depositFullPayment: this.selectedService.fullPayment
            } : null,
            maxCapacity: this.selectedService.maxCapacity,
            pictureThumbPath: this.selectedService.pictureThumbPath,
            aggregatedPrice: this.selectedService.aggregatedPrice,
            bookingStart: this.appointment.bookingStart
          }
        } else {
          return {
            id: this.selectedPackage.id,
            name: this.selectedPackage.name,
            price: this.getPackagePrice(this.selectedPackage),
            depositData: this.selectedPackage.depositPayment !== 'disabled' ? {
              deposit: this.selectedPackage.deposit,
              depositPayment: this.selectedPackage.depositPayment,
              depositPerPerson: this.selectedPackage.depositPerPerson,
              depositFullPayment: this.selectedPackage.fullPayment
            } : null,
            calculatedPrice: this.selectedPackage.calculatedPrice,
            discount: this.selectedPackage.discount,
            maxCapacity: 1,
            pictureThumbPath: this.selectedPackage.pictureThumbPath,
            aggregatedPrice: this.selectedPackage.aggregatedPrice
          }
        }
      },

      changeMonth (page) {
        this.selectedMonth = page ? moment().year(page.year).month(page.month - 1).date(1).format('YYYY-MM') : null
      },

      showCalendarOnly (initCall) {
        let providerService = null

        if (this.appointment.serviceId && this.appointment.providerId) {
          providerService = this.getProviderService(this.appointment.providerId, this.appointment.serviceId)
        }

        let service = null

        if (this.appointment.serviceId) {
          service = this.getServiceById(this.appointment.serviceId)
        }

        let serviceProviderVisibility = true
        let servicePackageVisibility = true
        let serviceLocationVisibility = true
        let addExtraVisibility = true

        for (const key in this.forms[this.formType].selectServiceForm.itemsDraggable) {
          if (this.forms[this.formType].selectServiceForm.itemsDraggable[key].hasOwnProperty('visibility') && !this.forms[this.formType].selectServiceForm.itemsDraggable[key].visibility) {
            if (key === 'employeeFormField') {
              serviceProviderVisibility = false
            }

            if (key === 'servicePackageFormField') {
              servicePackageVisibility = false
            }

            if (key === 'locationFormField') {
              serviceLocationVisibility = false
            }

            if (key === 'addExtraFormField') {
              addExtraVisibility = false
            }
          }
        }

        let anyEmployeeHasBringingAnyone = false

        if (providerService === null) {
          this.employeesFiltered.forEach((employeeItem) => {
            employeeItem.serviceList.forEach((serviceItem) => {
              if (service && serviceItem.id === service.id && serviceItem.maxCapacity > 1 && serviceItem.bringingAnyone) {
                anyEmployeeHasBringingAnyone = true
              }
            })
          })
        }

        return initCall &&
            !this.showServices &&
            (!serviceProviderVisibility || !this.showEmployees) &&
            (!serviceLocationVisibility || !this.showLocations) &&
            (service && service.extras.length ? !this.extrasMandatory : true) &&
            (service && service.extras.length ? !addExtraVisibility : true) &&
            (!serviceProviderVisibility || this.employeesFiltered.length === 1) &&
            (
              (providerService !== null && (providerService.maxCapacity === 1 || providerService.bringingAnyone === false)) ||
              (providerService === null && !anyEmployeeHasBringingAnyone)
            ) &&
            (!servicePackageVisibility || (typeof this.passedService === 'undefined' ? this.options.entities.packages.length === 0 : true))
      },

      changeService () {
        if ('ameliaBooking' in window && 'changedEntity' in window.ameliaBooking) {
          window.ameliaBooking.changedEntity('service', this.appointment)
        }

        if (this.appointment.serviceId) {
          let selectedService = this.getServiceById(this.appointment.serviceId)

          this.appointment.serviceDuration = selectedService.duration

          if (selectedService.mandatoryExtra) {
            this.closePicker()
          }

          this.updateSettings(selectedService.settings)
          this.isServiceChanged = true

          // this.clearValidation()
          this.extrasError = []

          this.appointment.bookings[0].extras = []
          this.selectedExtras = []

          this.checkMandatoryExtras(selectedService)

          this.handleCapacity(true, false, selectedService.maxExtraPeople)

          this.toggleRecurringActive()

          if (this.calendarVisible) {
            this.getTimeSlots()
          }
        } else {
          this.cancelRecurringSetup()

          setTimeout(() => {
            this.selectedDate = null
            this.closePicker()
            this.resetAppointment()
            this.unSelectTime()
            this.activeRecurringSetup = false
            this.showTimes = false
          }, 200)
        }
      },

      changeEmployee (required = false) {
        let selectedService = null
        if (this.appointment.serviceId) {
          selectedService = this.getServiceById(this.appointment.serviceId)
        }
        this.handleCapacity(true, false, selectedService ? selectedService.maxExtraPeople : null)

        if (this.calendarVisible && this.appointment.providerId) {
          this.getTimeSlots()
        }

        if (required && !this.appointment.providerId) {
          this.cancelRecurringSetup()

          setTimeout(() => {
            this.selectedDate = null
            this.closePicker()
            this.resetAppointment()
            this.unSelectTime()
            this.activeRecurringSetup = false
            this.showTimes = false
          }, 200)
        }
      },

      changeLocation (required = false) {
        if (this.calendarVisible && this.appointment.locationId) {
          this.getTimeSlots()
        }

        if (!this.appointment.locationId) {
          this.appointment.locationId = null
        }

        if (required && !this.appointment.locationId) {
          this.cancelRecurringSetup()

          setTimeout(() => {
            this.selectedDate = null
            this.closePicker()
            this.resetAppointment()
            this.unSelectTime()
            this.activeRecurringSetup = false
            this.showTimes = false
          }, 200)
        }
      },

      enableGrouping () {
        let selectedService = null
        if (this.appointment.serviceId) {
          selectedService = this.getServiceById(this.appointment.serviceId)
        }
        this.handleCapacity(true, true, selectedService ? selectedService.maxExtraPeople : null)

        this.group.enabled === true ? this.appointment.bookings[0].persons += 1 : this.appointment.bookings[0].persons = 1

        if (this.calendarVisible) {
          this.getTimeSlots()
        }
      },

      changeNumberOfPersons () {
        if (this.calendarVisible) { this.getTimeSlots() }
      },

      getSelectedExtraMaxQuantity (selectedExtra) {
        let extra = this.getServiceById(this.appointment.serviceId).extras.find(extra => extra.id === selectedExtra.id)

        return typeof extra !== 'undefined' ? extra.maxQuantity : ''
      },

      getAvailableExtras (selectedExtra) {
        let selectedExtras = []
        let availableExtras = []

        this.selectedExtras.forEach(function (extra) {
          if (extra.id) {
            selectedExtras.push(extra.id)
          }
        })

        this.getServiceById(this.appointment.serviceId).extras.forEach(function (extra) {
          if (selectedExtras.indexOf(extra.id) === -1 || selectedExtra.id === extra.id) {
            availableExtras.push(extra)
          }
        })

        return availableExtras
      },

      addExtra () {
        this.selectedExtras.push({
          id: null,
          extraId: null,
          quantity: '',
          duration: 0,
          name: ''
        })

        this.previouslySelectedExtras.push({
          id: null,
          extraId: null,
          quantity: '',
          duration: 0,
          name: ''
        })
      },

      deleteExtra (key) {
        if (this.calendarVisible && !!this.selectedExtras[key].duration) {
          this.selectedExtras.splice(key, 1)
          this.previouslySelectedExtras.splice(key, 1)
          this.getTimeSlots()
        } else {
          this.selectedExtras.splice(key, 1)
          this.previouslySelectedExtras.splice(key, 1)
        }
      },

      changeSelectedExtra (selectedExtra, key) {
        this.extrasError = []
        if (this.previouslySelectedExtras && (key in this.previouslySelectedExtras) && this.previouslySelectedExtras[key].extraId === null) {
          this.previouslySelectedExtras[key].extraId = selectedExtra.extraId
          this.previouslySelectedExtras[key].duration = selectedExtra.duration
        } else {
          this.previouslySelectedExtras[key] = {}
        }

        selectedExtra.quantity = (selectedExtra.quantity === '') ? 1 : selectedExtra.quantity

        let extra = this.getServiceById(this.appointment.serviceId).extras.find(extra => extra.id === selectedExtra.id)

        selectedExtra.extraId = extra.id
        selectedExtra.duration = extra.duration
        selectedExtra.name = extra.name
        selectedExtra.price = extra.price
        selectedExtra.selected = true
        selectedExtra.aggregatedPrice = extra.aggregatedPrice

        if (selectedExtra.quantity > extra.maxQuantity) {
          selectedExtra.quantity = extra.maxQuantity
        }

        if (this.calendarVisible && (!!extra.duration || (this.previouslySelectedExtras[key].duration !== null && selectedExtra.duration === null))) {
          this.getTimeSlots()
        }

        this.previouslySelectedExtras[key].extraId = selectedExtra.extraId
        this.previouslySelectedExtras[key].duration = selectedExtra.duration
      },

      fetchedEntities () {
        if (this.$root.shortcodeData.booking.service && this.$root.shortcodeData.booking.service.length) {
          this.checkMandatoryExtras(this.options.entities.services[0])
        }

        if (this.options.entities.services.length === 0 ||
            this.options.entities.employees.length === 0 ||
            (this.$root.shortcodeData.booking.show === 'packages' && this.options.entities.packages.length === 0)
        ) {
          this.empty = true
        }

        this.setBookingCustomFields()

        if (this.employeesFiltered.length === 1) {
          this.appointment.providerId = this.employeesFiltered[0].id

          this.$root.shortcodeData.booking.employee = this.appointment.providerId
        } else if (this.employeesFiltered.length > 1) {
          this.showEmployees = true
        } else {
          return
        }

        if (this.locationsFiltered.length === 1) {
          this.appointment.locationId = this.locationsFiltered[0].id

          this.$root.shortcodeData.booking.location = this.appointment.locationId
        } else if (this.locationsFiltered.length > 1) {
          this.showLocations = true
        }

        if (this.servicesFiltered.length === 1) {
          this.appointment.serviceId = this.servicesFiltered[0].id

          this.$root.shortcodeData.booking.service = this.appointment.serviceId
        } else if (this.servicesFiltered.length > 1) {
          this.showServices = true
        } else {
          return
        }

        this.toggleRecurringActive()

        let service = null
        if (this.appointment.serviceId) {
          service = this.getServiceById(this.appointment.serviceId)

          this.appointment.serviceDuration = service.duration

          this.updateSettings(service.settings)
        }
        this.handleCapacity(true, false, service ? service.maxExtraPeople : null)

        if (('showAmeliaCalendarOnLoad' in window && window.showAmeliaCalendarOnLoad && this.appointment.serviceId) ||
          (this.showCalendarOnly(true) && this.$root.shortcodeData.booking.show !== 'packages')
        ) {
          document.getElementById(this.id + '-calendar').classList.remove('am-select-service-date-transition')
          this.getTimeSlots()
        } else {
          let shortCodeEntitiesIds = this.getShortCodeEntityIds()

          if (shortCodeEntitiesIds.show === 'packages') {
            this.options.entities.packages.sort((a, b) => a.position - b.position)
            if (this.options.entities.packages.length === 1) {
              this.selectedPackageId = this.options.entities.packages[0].id

              this.packagesHaveBack = false

              this.selectPackage(this.getPackageById(this.selectedPackageId))
            }
          }

          this.fetched = true

          this.runCacheAction()
        }
      },

      getPossibleProvidersIfNoneSelected () {
        let providers = []

        // If Employee is not selected, select ones that can provide the service
        if (!this.appointment.providerId) {
          // If grouping is enabled check employees capacity for selected service
          if (this.group.enabled) {
            this.employeesFiltered.forEach((employee) => {
              if (typeof (employee.serviceList.find(service => service.id === this.appointment.serviceId && service.maxCapacity >= this.appointment.bookings[0].persons)) !== 'undefined') {
                providers.push(employee)
              }
            })
          } else {
            this.employeesFiltered.forEach((employee) => {
              if (typeof (employee.serviceList.find(service => service.id === this.appointment.serviceId)) !== 'undefined') {
                providers.push(employee)
              }
            })
          }
        }

        return providers
      },

      getTimeSlots (formValidation = true) {
        this.getCurrentUser()

        let service = this.appointment.serviceId ? this.getServiceById(this.appointment.serviceId) : null

        this.selectedExtras.forEach((extra, index) => {
          if (service && service.mandatoryExtra && !extra.id && this.appointment.bookings[0].minSelectedExtras > index) {
            this.extrasError.push(index)
          }
        })

        if (this.extrasError.length) {
          return false
        }

        if (this.appointment.serviceId && formValidation) {
          this.loadingTimeSlots = true
          let extras = []

          this.selectedExtras.forEach(function (extra) {
            if (extra.id) {
              extras.push({
                id: extra.id,
                quantity: extra.quantity
              })
            }
          })

          // Customization hook
          if ('afterBookingSelectService' in window) {
            window.afterBookingSelectService(this.appointment, this.getServiceById(this.appointment.serviceId), this.getProviderById(this.appointment.providerId), this.getLocationById(this.appointment.locationId))
          }

          this.slotsIndexStarted++

          let currentIndex = this.slotsIndexStarted

          this.$http.get(`${this.$root.getAjaxUrl}/slots`, {
            params: this.getAppropriateUrlParams({
              locationId: this.appointment.locationId,
              serviceId: this.appointment.serviceId,
              serviceDuration: this.appointment.serviceDuration,
              providerIds: this.appointment.providerId ? [this.appointment.providerId] : this.getPossibleProvidersIfNoneSelected().map(i => i.id),
              extras: JSON.stringify(extras),
              group: 1,
              page: 'booking',
              persons: this.appointment.bookings[0].persons
            })
          }).then(response => {
            if (currentIndex < this.slotsIndexStarted) {
              this.fetchedSlots = true
              this.fetched = true
              this.loadingTimeSlots = false
              return
            }

            let dateSlots = this.$root.settings.general.showClientTimeZone ? this.getConvertedTimeSlots(response.data.data.slots) : response.data.data.slots

            let occupiedSlots = this.$root.settings.general.showClientTimeZone ? this.getConvertedTimeSlots(response.data.data.occupied) : response.data.data.occupied

            if (!this.calendarVisible) {
              this.activePicker = !this.activePicker
              document.getElementById(this.id).classList.toggle('am-active-picker', this.activePicker)
            }

            let availableDates = []

            let minDate = null

            this.useSortedDateStrings(Object.keys(dateSlots)).forEach(function (dateString) {
              if (minDate === null) {
                minDate = dateString
              }

              availableDates.push(moment(dateString).toDate())
            })

            this.showFirstEventMonth(minDate)

            this.calendarTimeSlots = dateSlots
            this.occupiedTimeSlots = occupiedSlots
            this.disabledWeekdays = {weekdays: []}
            this.disabledWeekdays = (availableDates.length === 0) ? {weekdays: [1, 2, 3, 4, 5, 6, 7]} : null
            this.availableDates = availableDates
            this.calendarVisible = true

            if (this.availableDates.length) {
              this.setTimeSlots()
            }

            if (!this.availableDates.length || !this.isSelectedDateAvailable()) {
              this.showTimes = false
              let amContainer = document.getElementById(this.id)
              amContainer.classList.remove('am-show-times')
            }

            let dateIsNotAvailable = !this.availableDates.length || !this.isSelectedDateAvailable()
            let timeIsNotAvailable = (this.appointment.bookingStartTime && this.availableTimeSlots.indexOf(this.appointment.bookingStartTime) === -1)

            if (dateIsNotAvailable || timeIsNotAvailable) {
              if (dateIsNotAvailable) {
                this.selectedDate = null
              }

              this.unSelectTime()
            }

            if (this.activeRecurringSetup) {
              let amContainer = document.getElementById(this.id)

              amContainer.classList.add('am-show-calendar')

              this.activeRecurringSetup = false

              if (!dateIsNotAvailable) {
                this.showTimeSlots()
              } else {
                this.times = document.getElementById(this.calendarId)
              }
            }

            this.fetchedSlots = true
            this.fetched = true
            this.loadingTimeSlots = false

            this.runCacheAction()
          }).catch(e => {
            console.log(e.message)
            this.fetchedSlots = true
            this.fetched = true
          })
        }
      },

      showFirstEventMonth (minDate) {
        if (this.isServiceChanged && (
          (this.selectedDate === null && moment(this.selectedMonth).format('YYYY-MM') !== moment(minDate, 'YYYY-MM-DD').format('YYYY-MM')) ||
          (this.selectedDate !== null && moment(this.selectedDate).format('YYYY-MM') !== moment(minDate, 'YYYY-MM-DD').format('YYYY-MM'))
        )) {
          this.selectedDate = moment(minDate).toDate()

          let $this = this

          setTimeout(function () {
            $this.selectedDate = null
          }, 100)
        }

        this.isServiceChanged = false
      },

      addSlotsElementToWeekRow (weekRow) {
        if (!this.times) {
          this.times = document.getElementById(this.calendarId)
        }
        weekRow.parentNode.insertBefore(this.times, weekRow.nextSibling)

        this.isRecurringAvailable = this.getServiceById(this.appointment.serviceId).recurringCycle !== 'disabled'

        setTimeout(() => {
          if (this.availableTimeSlots.length && this.selectedDate) {
            if (moment(this.selectedDate).format('YYYY-MM-DD') === moment(this.availableDates[this.availableDates.length - 1]).format('YYYY-MM-DD')) {
              this.activeRecurring = false
              this.isRecurringAvailable = false
            }
            this.showTimes = true
            let amContainer = document.getElementById(this.id)
            amContainer.classList.add('am-show-times')
            this.appointment.bookingStartTime = this.availableTimeSlots[0]
            this.selectedWeekIndex = [...weekRow.parentNode.children].indexOf(weekRow)

            this.selectTime()
          }
        }, 200)
      },

      selectDate (dayInfo, calendarDate) {
        this.selectedDate = calendarDate
        this.unSelectTime()
        let isDisabled = false

        dayInfo.attributes.forEach(function (attrItem) {
          if (attrItem.hasOwnProperty('key') && attrItem['key'] === 'disabled') {
            isDisabled = true
          }
        })

        if (isDisabled) {
          return
        }

        let amContainer = document.getElementById(this.id)
        amContainer.classList.remove('am-show-times')

        this.showTimes = false

        if (dayInfo.inMonth) {
          let weekRowInMonth = dayInfo.event.target.parentNode.parentNode.parentNode.parentNode.parentNode

          if (weekRowInMonth.classList.contains('c-day')) {
            weekRowInMonth = dayInfo.event.target.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode
          }

          if (!weekRowInMonth.classList.contains('c-week')) {
            weekRowInMonth = dayInfo.event.target.parentNode.parentNode.parentNode.parentNode
          }

          if (!weekRowInMonth.classList.contains('c-week')) {
            weekRowInMonth = dayInfo.event.target.parentNode.parentNode.parentNode
          }

          this.addSlotsElementToWeekRow(weekRowInMonth)
        } else {
          setTimeout(() => {
            let weekRowNotInMonth = document.getElementById(this.id + '-calendar')
              .getElementsByClassName('c-weeks-rows')[0].children[dayInfo.weekdayOrdinal - 1]
            this.addSlotsElementToWeekRow(weekRowNotInMonth)
          }, 1000)
        }
      },

      isSelectedDateAvailable () {
        let momentDate = moment(this.selectedDate)
        return this.calendarTimeSlots.hasOwnProperty(momentDate.format('YYYY-MM-DD'))
      },

      setTimeSlots (calendarDate) {
        if (typeof calendarDate !== 'undefined') {
          this.selectedDate = calendarDate
        }
        let momentDate = moment(this.selectedDate)
        let dateString = momentDate.format('YYYY-MM-DD')

        if (this.isSelectedDateAvailable()) {
          this.availableTimeSlots = Object.keys(this.calendarTimeSlots[dateString])
          this.appointment.duration = this.getAppointmentDuration(
            this.appointment.serviceDuration ? parseInt(this.appointment.serviceDuration) : this.getServiceById(this.appointment.serviceId).duration,
            this.selectedExtras
          )
        }
      },

      togglePicker () {
        this.calendarVisible = false
        this.activePicker = !this.activePicker
        let amContainer = document.getElementById(this.id)
        amContainer.classList.toggle('am-active-picker', this.activePicker)
      },

      closePicker () {
        this.calendarVisible = false
        this.activePicker = false
        let amContainer = document.getElementById(this.id)
        amContainer.classList.remove('am-active-picker')
        this.isRecurringAvailable = false
        this.activeRecurring = false
      },

      selectTime () {
        this.appointment.bookingStart = moment(this.selectedDate).format('YYYY-MM-DD') + ' ' + this.appointment.bookingStartTime
        this.showCalendarContinueButton = true

        if ('afterBookingTimeSelected' in window) {
          window.afterBookingTimeSelected(this.appointment)
        }

        if ('ameliaBooking' in window && 'disableScrollView' in window.ameliaBooking && window.ameliaBooking.disableScrollView === true) {
          return
        }

        this.scrollView('am-button-wrapper-' + this.$root.shortcodeData.counter, 'end')
      },

      unSelectTime () {
        this.appointment.bookingStartTime = null
        this.showCalendarContinueButton = false
      },

      refreshCalendar () {
        let calendarTimeSlots = []
        let availableDates = []

        for (let dateKey in this.calendarTimeSlots) {
          for (let timeKey in this.calendarTimeSlots[dateKey]) {
            for (let slotInfoKey in this.calendarTimeSlots[dateKey][timeKey]) {
              if (this.appointment.providerId && this.calendarTimeSlots[dateKey][timeKey][slotInfoKey][0] === this.appointment.providerId) {
                if (!(dateKey in calendarTimeSlots)) {
                  availableDates.push(moment(dateKey).toDate())
                  calendarTimeSlots[dateKey] = {}
                }

                if (!(timeKey in calendarTimeSlots[dateKey])) {
                  calendarTimeSlots[dateKey][timeKey] = []
                }

                calendarTimeSlots[dateKey][timeKey].push(this.calendarTimeSlots[dateKey][timeKey][slotInfoKey])
              }
            }
          }
        }

        this.calendarTimeSlots = calendarTimeSlots
        this.disabledWeekdays = {weekdays: []}
        this.disabledWeekdays = (availableDates.length === 0) ? {weekdays: [1, 2, 3, 4, 5, 6, 7]} : null
        this.availableDates = availableDates
        this.availableTimeSlots = Object.keys(calendarTimeSlots[moment(this.selectedDate).format('YYYY-MM-DD')])
      },

      showTimeSlots () {
        setTimeout(() => {
          let weeksWrapper = document.getElementsByClassName('c-weeks-rows-wrapper')[0]
          let weekRow = weeksWrapper.firstChild.children.item(this.selectedWeekIndex)

          this.times = document.getElementById(this.calendarId)
          weekRow.parentNode.insertBefore(this.times, weekRow.nextSibling)

          this.showTimes = true
        }, 200)
      },

      cancelRecurringSetup () {
        this.activeRecurringSetup = false

        let amContainer = document.getElementById(this.id)

        amContainer.classList.remove('am-recurring-active')

        this.showTimeSlots()
      },

      confirmRecurringSetup () {
        let amContainer = document.getElementById(this.id)

        this.activeRecurringDates = true
        this.activeRecurringSetup = true

        amContainer.classList.toggle('am-active-picker', false)
        amContainer.classList.toggle('am-active-recurring-dates', true)
      },

      cancelRecurringDates () {
        this.activeRecurringDates = false
        this.activeRecurringSetup = true

        let amContainer = document.getElementById(this.id)

        amContainer.classList.toggle('am-active-picker', true)
        amContainer.classList.toggle('am-active-confirm', this.activeRecurringDates)

        amContainer.classList.toggle('am-active-recurring-dates', false)

        if (this.showCalendarOnly(true)) {
          amContainer.classList.add('am-mobile-collapsed')
          amContainer.classList.remove('am-desktop')
        }
      },

      confirmRecurringDates () {
        let selectedAppointment = this.recurringData.startAppointment

        this.selectedProvider = this.getProviderById(selectedAppointment.providerId)
        this.selectedLocation = this.getLocationById(selectedAppointment.locationId)

        this.appointment.bookings[0].extras = this.selectedExtras

        // Customization hook
        if ('afterBookingSelectDateAndTime' in window) {
          window.afterBookingSelectDateAndTime(Object.assign(this.appointment, {
            providerId: selectedAppointment.providerId,
            locationId: selectedAppointment.locationId
          }), this.getServiceById(this.appointment.serviceId), this.selectedProvider, this.selectedLocation)
        }

        this.activeConfirm = true
        this.activeRecurringDates = false
        this.activeRecurringSetup = false
        let amContainer = document.getElementById(this.id)

        amContainer.classList.remove('am-active-recurring-dates')

        amContainer.classList.toggle('am-active-confirm', this.activeConfirm)
      },

      showNextScreen () {
        this.recurringData.dates = []

        if (this.activeRecurring && this.isRecurringAvailable && !this.activeRecurringSetup) {
          this.loading = true
          this.recurringData.startDate = this.appointment.bookingStart
          this.recurringData.startTime = this.appointment.bookingStart.split(' ')[1]

          let service = this.getServiceById(this.appointment.serviceId)

          this.initialRecurringData = this.getDefaultRecurringSettings(
            this.appointment.bookingStart,
            service.recurringCycle,
            this.calendarTimeSlots
          )

          setTimeout(() => {
            let amContainer = document.getElementById(this.id)

            amContainer.classList.remove('am-show-calendar')

            this.loading = false
            this.showTimes = false
            this.activeRecurringSetup = true
          }, 500)

          return
        }

        let dateString = moment(this.selectedDate).format('YYYY-MM-DD')

        if (!this.appointment.providerId) {
          let employeesIds = this.calendarTimeSlots[dateString][this.appointment.bookingStartTime].map(
            i => i[0]
          ).filter(
            (v, i, a) => a.indexOf(v) === i
          )

          this.appointment.providerId = employeesIds[Math.floor(Math.random() * (employeesIds.length) + 1) - 1]
        }

        if (!this.appointment.locationId) {
          let locationsIds = this.calendarTimeSlots[dateString][this.appointment.bookingStartTime].filter(
            i => i[0] === this.appointment.providerId
          ).map(i => i[1])

          this.appointment.locationId = locationsIds.length ? this.getPreferredEntityId(
            this.calendarTimeSlots[dateString],
            dateString in this.occupiedTimeSlots ? this.occupiedTimeSlots[dateString] : {},
            this.appointment.bookingStartTime,
            this.appointment.providerId,
            locationsIds,
            1
          ) : null
        }

        this.selectedProvider = this.getProviderById(this.appointment.providerId)
        this.selectedLocation = this.getLocationById(this.appointment.locationId)

        this.refreshCalendar()

        this.appointment.bookings[0].extras = this.selectedExtras

        // Customization hook
        if ('afterBookingSelectDateAndTime' in window) {
          window.afterBookingSelectDateAndTime(this.appointment, this.getServiceById(this.appointment.serviceId), this.getProviderById(this.appointment.providerId), this.getLocationById(this.appointment.locationId))
        }

        this.showConfirmBooking()
      },

      cancelPackageList () {
        let amContainer = document.getElementById(this.id)

        this.packageListShown = false
        this.activePackage = true

        amContainer.classList.toggle('am-active-picker', true)
        amContainer.classList.toggle('am-active-package-list', false)
      },

      cancelBooking () {
        if (this.cacheData) {
          this.unsetCacheData()

          let amContainer = document.getElementById(this.id)

          this.activeConfirm = false

          amContainer.classList.toggle('am-active-confirm', false)

          return
        }

        if (this.activeRecurring) {
          let amContainer = document.getElementById(this.id)

          this.activeRecurringSetup = true
          this.activeRecurringDates = true
          this.activeConfirm = false

          amContainer.classList.toggle('am-active-confirm', false)
          amContainer.classList.add('am-active-recurring-dates')

          return
        }

        if (this.selectedPackage) {
          let amContainer = document.getElementById(this.id)

          this.activeConfirm = false
          this.activePackage = true

          if (this.selectedPackage.hasSlots) {
            this.packageListShown = true
            this.disableFetchPackageSlots = true
            amContainer.classList.add('am-active-package-list')
          }

          amContainer.classList.toggle('am-active-confirm', false)

          return
        }

        this.activeConfirm = false
        let amContainer = document.getElementById(this.id)
        amContainer.classList.toggle('am-active-confirm', this.activeConfirm)

        if (this.appointment.serviceId && this.appointment.providerId) {
          amContainer.classList.toggle('am-active-picker', true)
        }

        if (this.showCalendarOnly(true)) {
          amContainer.classList.add('am-mobile-collapsed')
          amContainer.classList.remove('am-desktop')
        }
      },

      inlineBookingSVG () {
        let inlineSVG = require('inline-svg')
        inlineSVG.init({
          svgSelector: 'img.svg-booking',
          initClass: 'js-inlinesvg'
        })
      },

      handleResize () {
        let amContainer = document.getElementById(this.id)

        if (this.showCalendarOnly(true) || this.passedPackage) {
          if (amContainer) {
            amContainer.classList.add('am-mobile-collapsed')
            amContainer.classList.remove('am-desktop')
          }

          this.showCalendarBackButton = this.options.entities.packages.length > 0

          return
        }

        if (amContainer) {
          let amContainerWidth = amContainer.offsetWidth

          if (this.showCalendarOnly(false) || this.passedPackage) {
            amContainer.classList.add('am-mobile-collapsed')
            amContainer.classList.remove('am-desktop')
            this.showCalendarBackButton = this.options.entities.packages.length > 0
          } else {
            if (amContainerWidth < 670) {
              amContainer.classList.add('am-mobile-collapsed')
              amContainer.classList.remove('am-desktop')
              this.showCalendarBackButton = true
            } else {
              amContainer.classList.add('am-desktop')
              amContainer.classList.remove('am-mobile-collapsed')
              this.showCalendarBackButton = this.options.entities.packages.length > 0
            }
          }
        }
      },

      confirmedBooking (responseData, skipNotify, requestData) {
        this.activeConfirm = false
        this.bookingCompleted = true

        this.addToCalendarData = this.getAppointmentAddToCalendarData(
          responseData,
          skipNotify
        )

        if (marketingMixin.hasAmeliaTracking(this)) {
          if (responseData.packageId) {
            this.marketing.package = responseData.packageId ? this.getPackageById(responseData.packageId) : null
          } else {
            this.marketing.service = responseData.appointment.serviceId ? this.getServiceById(responseData.appointment.serviceId) : null
            this.marketing.employee = responseData.appointment.providerId ? this.getProviderById(responseData.appointment.providerId) : null
            this.marketing.location = responseData.appointment.locationId ? this.getLocationById(responseData.appointment.locationId) : null
            this.marketing.category = responseData.appointment.serviceId ? this.getCategoryById(this.marketing.service.categoryId) : null
          }

          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, responseData.type, 'Schedule')
          } else {
            marketingMixin.trackAmeliaData(this, this.$root.marketing, responseData.type, 'Purchase')
          }
        }

        // Customization hook
        if ('beforeConfirmedBooking' in window) {
          window.beforeConfirmedBooking(this.addToCalendarData)
        }

        this.showAddToCalendar = true

        if (this.showAddToCalendar) {
          setTimeout(() => {
            if (!!Object.keys(this.$refs).length && this.$refs.congratulations) {
              this.$refs.congratulations.$el.scrollIntoView({ behavior: 'smooth', block: 'start', inline: 'nearest' })
            }
          }, 500)
        }
      },

      clearValidation () {
        if (typeof this.$refs.booking !== 'undefined') {
          this.$refs.booking.clearValidate()
        }
      },

      closeDialogAddToCalendar () {
        this.showAddToCalendar = false
        document.getElementsByClassName('amelia-app-booking')[0].style.display = 'none'
        window.location.reload()
      },

      resetAppointment () {
        if (this.$root.shortcodeData.booking.employee) {
          this.appointment.providerId = this.$root.shortcodeData.booking.employee
        }

        if (this.$root.shortcodeData.booking.location) {
          this.appointment.locationId = this.$root.shortcodeData.booking.location
        }

        if (this.$root.shortcodeData.booking.service) {
          this.appointment.serviceId = this.$root.shortcodeData.booking.service
        }

        if (this.$root.shortcodeData.booking.category) {
          this.appointment.categoryId = this.$root.shortcodeData.booking.category
        }

        this.appointment = {
          bookingStart: '',
          bookingStartTime: '',
          bookings: [{
            customer: {
              email: '',
              externalId: null,
              firstName: '',
              id: null,
              lastName: '',
              phone: ''
            },
            customFields: {},
            customerId: 0,
            extras: [],
            persons: 1
          }],
          duration: 0,
          serviceDuration: 0,
          group: false,
          notifyParticipants: this.$root.settings.notifications.notifyCustomers,
          payment: {
            amount: 0,
            gateway: '',
            currency: '',
            data: {}
          },
          categoryId: null,
          providerId: this.forms[this.formType].selectServiceForm.itemsDraggable.employeeFormField.anyEmployeeVisible ? 0 : null,
          serviceId: this.setServiceIdOnResetAppointment(),
          locationId: null
        }

        this.setBookingCustomFields()
      },

      setServiceIdOnResetAppointment () {
        if (this.$root.shortcodeData.booking.service) {
          return this.$root.shortcodeData.booking.service
        }

        if (this.passedService && this.passedService.id) {
          return this.passedService.id
        }

        return null
      },

      continuePackage (selectedId) {
        if (!selectedId) {
          return false
        }

        this.selectedPackageId = selectedId
        this.selectPackage(this.getPackageById(selectedId))
      }
    },

    components: {
      moment,
      selectServiceForm,
      calendarDateTimeForm,
      recurringSetup,
      recurringDates,
      selectPackageForm,
      packageInfo,
      packageSetup,
      packageList,
      confirmBooking,
      addToCalendar
    }
  }
</script>
© 2026 GrazzMean-Shell