HEX
Server: Microsoft-IIS/8.5
System: Windows NT YDAWBH120 6.3 build 9600 (Windows Server 2012 R2 Standard Edition) AMD64
User: tentjecom_web (0)
PHP: 7.4.14
Disabled: NONE
Upload Files
File: D:/HostingSpaces/SBogers10/farmfun.komma.pro/resources/js/site/components/PlanModal.vue
<template>
  <div class="o-modal" v-if="productId !== null">
    <div class="o-modal__shader" @click="close"></div>

    <section class="o-modal__main" :class="{ 'is-modal-visible' : !hasAnimationClasses}">
      <div class="o-modal__content" v-if="product">

        <div class="o-modal__header">
          <button type="button" class="o-modal__close" @click="close"></button>
          <div class="o-modal__title">
            {{ product.name }}
          </div>
        </div>

        <div class="o-model__selected" v-if="hasSession">

          <template v-if="selectedLocation">
            <div class="c-form-element__label">Gekozen vestiging</div>
            <div class="o-modal__current">
              {{ selectedLocation.name }}<br/>
              {{ date }}
            </div>
          </template>
          <template v-else class="c-form-element__label">
            Niet mogelijk op gekozen vestiging.<br/>
            Wijzig vestiging in de winkelwagen.
          </template>

          <a class="o-modal__edit" :href="cartRoute">
            <svg width="12" height="12">
              <use href="/img/icon-edit.svg#edit"></use>
            </svg>
          </a>

        </div>

        <div class="o-modal__details">

          <label class="c-form-element__label" v-if="!hasSession">{{ trans('date') }}</label>
          <div class="c-input-wrapper" :class="{ 'is-locked': hasSession }">
            <img class="c-input__icon" src="/img/calendar.svg" />
            <input class="c-search__input  c-input"
                   ref="datepicker"
                   name="date"
                   :placeholder="trans('choose_a_date')"
                   type="date"
                   v-model="date"
            />

          </div>

          <label class="c-form-element__label  u-spacing-mt2" v-if="!hasSession">{{ trans('location') }}</label>
          <div class="c-input-wrapper" :class="{ 'is-disabled': availableLocations.length === 0, 'is-locked' : hasSession }">
            <img class="c-input__icon" src="/img/location.svg" />
            <select class="c-select" v-model="location">
              <option value="" disabled="disabled">{{ trans('select_location') }}</option>

              <template v-for="selectLocation in availableLocations">
                <option :value="selectLocation.id" :disabled="!selectLocation.available">
                  {{ selectLocation.name }}
                  <template v-if="selectLocation.reason !== ''">( {{ trans('reason.' + selectLocation.reason)}} )</template>
                </option>
              </template>
            </select>
          </div>

          <template v-if="product && product.has_timeslot_selection">
            <label class="c-form-element__label  u-spacing-mt2">{{ trans('time') }}</label>
            <div class="c-input-wrapper" :class="{'is-disabled' : !selectedLocation || !selectedLocation.available}">
              <img class="c-input__icon" width="23" height="23" src="/img/icon-clock.svg" />
              <select class="c-select" v-model="timeSlot">
                <option value="" disabled="disabled">{{ trans('select_time') }}</option>

                <template v-if="selectedLocation">
                  <template v-for="timeSlots in selectedLocation.timeSlots">
                    <option :value="timeSlots.slot" :disabled="!timeSlots.available">
                      {{ timeSlots.label }}
                      <template v-if="!timeSlots.available">( {{ trans('reason.full')}} )</template>
                    </option>
                  </template>
                </template>
              </select>
            </div>
          </template>

          <label class="c-form-element__label  u-spacing-mt2">{{ trans('amount_of_persons') }}</label>
          <div class="c-input-wrapper" :class="{'is-disabled' : !canSelectPersons}">
            <img class="c-input__icon" src="/img/people.svg" />
            <input class="c-input"
                   type="number"
                   name="amount_of_persons"
                   pattern="[0-9]+"
                   min="0"
                   :max="product.maximum_amount_of_persons === 0 ? null : product.maximum_amount_of_persons"
                   v-model="amountOfPersons"
                   />
            <div class="c-form-element__label" style="font-size: 12px;">Het aantal personen kan gewijzigd worden tot 72 uur voor de activiteit</div>
          </div>

        </div>

        <div class="o-modal__confirm" v-if="product.required_age !== 0">
          <label class="c-checkbox" :class="{ 'is-disabled' : amountOfPersons === null || !selectedLocation.available }">
            <input class="c-checkbox__input" type="checkbox" name="modal-confirm-age" v-model="confirmed"/>
            <span class="c-checkbox__text" v-html="trans('confirm_age_message', {age: product.required_age})"></span>
          </label>
        </div>

        <div v-if="messages.length > 0" class="u-spacing-mt2  u-spacing-mlr2">
          <template v-for="message in messages">
            <flash-message :lines="message.lines" :without-icon="message.withoutIcon" :message-type="message.type"></flash-message>
          </template>
        </div>

        <div class="o-modal__submit" :class="{ 'is-disabled' : !canSubmit }">
          <button class="c-button  c-button--wide" type="button" @click="submitForm">
            <span class="c-button__text">{{  trans('plan_day') }}</span>
          </button>
        </div>

        <div class="o-modal__cancel">
          <button type="button" class="c-text-button  c-text-button--wide  c-text-button--font-size-s" @click="close">
            <span class="c-text-button__text">{{ trans('cancel') }}</span>
          </button>
        </div>

      </div>
    </section>
  </div>

</template>

<script>
import {computed, nextTick, ref, toRefs, watch} from "vue";
import useTranslations from "../services/useTranslations";
import FlashMessage from "./FlashMessage";
import FlashMessageInstance, {FlashMessageType} from "../services/flashMessage";
import flatpickr from "flatpickr";
import useCart from "../services/useCart";

export default {
  name: "PlanModal",
  components: {FlashMessage},
  emits: ['close', 'success-close'],
  props: {
    cartRoute: {
      type: String,
      required: true
    },
    productId: {
      required: true
    },
    offerLink: {
      type: String,
      required: true
    },
    sessionDate: {
      type: String,
    },
    sessionLocation: {
      type: String,
    },
    onSuccessClose: {
      type: Boolean
    }
  },

  setup(props,{emit}) {

    const {trans} = useTranslations()
    const {productId} = toRefs(props);
    const {maxAmount} = useCart()

    const messages = ref([])

    const product = ref(null)
    const availableLocations = ref([])

    const hasSession = ref(false)
    const location = ref('')
    const date = ref(null)
    const amountOfPersons = ref(null)
    const confirmed = ref(false)
    const timeSlot = ref('')

    if(props.sessionDate) {
      date.value = props.sessionDate
      hasSession.value = true
    }
    if(props.sessionLocation) {
      location.value = parseInt(props.sessionLocation)
      hasSession.value = true
    }

    const hasAnimationClasses = ref(true)

    const datepicker = ref(null)

    const canSubmit = computed(() => {

      if(!product.value) return false

      if(date.value === null ) return false
      if(location.value === '') return false
      if(!isset(selectedLocation.value) || !selectedLocation.value.available) return false
      if(product.value.has_timeslot_selection && timeSlot.value === '') return false
      if(amountOfPersons.value === null ) return false

      if(product.value.required_age !== 0) {
        if(!confirmed.value) return false
      }

      return true
    })

    const selectedLocation = computed(() => {
      if(location.value === '') return null
      return availableLocations.value.find((l) => l.id == location.value)
    })

    const canSelectPersons = computed(() => {

      if(!selectedLocation.value || !product.value) return false
      if(!selectedLocation.value.available) return false

      if(!product.value.has_timeslot_selection) return true
      return timeSlot.value !== ''

    })

    const loadProduct = () => {

      if(props.sessionLocation && props.sessionLocation != location.value) location.value = parseInt(props.sessionLocation)

      messages.value = []

      window.axios.get(`/api/availability/${ productId.value }/info`)
          .then(async (response) => {

            product.value = response.data.data

            await nextTick()
            bootModal()

          })
          .catch((error) => { defaultErrorResponse(error) })
    }

    const bootModal = () => {

      const settings = { minDate: new Date().fp_incr(10) }
      if(date) settings.defaultDate = date

      flatpickr(datepicker.value, settings);

      setTimeout(() => {
        hasAnimationClasses.value = false
      }, 100)
    }

    const checkAvailability = () => {

      messages.value = []

      window.axios.get(`/api/availability/${ productId.value }/check`,{
        params: {
          date: date.value
        }
      })
          .then(async (response) => {
            switch (response.status) {

              case 200:
                availableLocations.value = response.data.data
                console.log(response.data.data)
                break


              case 204:
                const noResponseMessage = (new FlashMessageInstance(
                    [trans('product_no_locations')],
                )).setType(FlashMessageType.warning)

                messages.value.push(noResponseMessage)
                availableLocations.value = []
                break

              default:
                availableLocations.value = []
                messages.value.push((new FlashMessageInstance(
                    [trans('unknown_response')],
                )).setType(FlashMessageType.warning))
            }
          })
          .catch((error) => { defaultErrorResponse(error) })
    }

    const defaultErrorResponse = (error) => {
      console.error(error)

      const errorFlash = (new FlashMessageInstance(
          [trans('oops_something_went_wrong')],
          'Error'
      )).setType(FlashMessageType.error)

      messages.value.push(errorFlash)
    }

    const submitForm = () => {

      messages.value = []

      if(amountOfPersons.value === '' || Number.isNaN(amountOfPersons.value)){
        messages.value.push((new FlashMessageInstance(
            [trans('no_valid_number')],
        )).setType(FlashMessageType.warning))
        return
      }

      let persons = parseInt(amountOfPersons.value)

      if(persons < product.value.minimum_amount_of_persons) {
        messages.value.push((new FlashMessageInstance(
            [trans('lower_then_minimum',{amount: product.value.minimum_amount_of_persons})],
        )).setType(FlashMessageType.warning))
        return
      }

      if(product.value.maximum_amount_of_persons !== 0 && persons > product.value.maximum_amount_of_persons) {
        messages.value.push((new FlashMessageInstance(
            [trans('higher_then_prod_maximum',{amount: product.value.maximum_amount_of_persons})],
        )).setType(FlashMessageType.warning))
        return
      }

      if(persons > maxAmount) {
        messages.value.push((new FlashMessageInstance(
            [trans('higher_then_maximum',{link: props.offerLink})],
        )).setType(FlashMessageType.warning))
        return
      }

      const formData = new FormData()
      formData.append('product_id', productId.value)
      formData.append('location_id', location.value)
      formData.append('date', date.value)
      formData.append('timeSlot', timeSlot.value)
      formData.append('amount_of_persons', amountOfPersons.value)

      window.axios.post(`/api/cart/fill-cart`,formData).then(async (response) => {

        switch (response.status) {

          case 206:

            if(response.data.messages) {
              messages.value.push((new FlashMessageInstance(
                  response.data.messages,
              )).setType(FlashMessageType.warning))
            }
            else if(response.data.message) {
              messages.value.push((new FlashMessageInstance(
                  [response.data.message],
              )).setType(FlashMessageType.warning))
            }
            else throw new Error('provide message or messages as key')
            break

          case 204:
            if(props.onSuccessClose) close(null, true)
            else window.location.href = props.cartRoute
            break

          default:
            messages.value.push((new FlashMessageInstance(
                [trans('unknown_response')],
            )).setType(FlashMessageType.warning))
        }
      })
      .catch((error) => { defaultErrorResponse(error) })
    }

    const close = (event = null, fromSuccess = false) => {

      messages.value = []

      amountOfPersons.value = null
      timeSlot.value = ''

      if(fromSuccess) emit('success-close')
      else emit('close')
    }

    watch(productId, async (productIdValue) => {

      if(!productIdValue) {
        product.value = null
        return
      }

      await loadProduct()
      if(hasSession.value && date.value) checkAvailability()
    })

    watch(date, (dateValue) => {

      if(!dateValue) {
        availableLocations.value = []
        location.value = ''
        return
      }

      if(date.value) checkAvailability()
    })

    return {
      hasAnimationClasses,
      product,
      trans,
      messages,

      date,
      location,
      amountOfPersons,
      maxAmount,
      confirmed,
      canSubmit,

      submitForm,
      close,

      datepicker,

      availableLocations,
      selectedLocation,
      timeSlot,

      hasSession,
      canSelectPersons,
    }

  }
}
</script>