%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /home/forge/takeaseat.eco-n-tech.co.uk/pages/booking/
Upload File :
Create Path :
Current File : //home/forge/takeaseat.eco-n-tech.co.uk/pages/booking/_slug.vue

<template>
  <div class="page-wrapper">
    <div class="py-10 sm:py-20 bg-white">
      <div class="relative z-20 text-center">
        <figure class="relative inline-block w-32 mb-4 sm:mb-8">
          <div :class="'tas-border-'+therapist.colour"
            class="absolute top-0 left-0 w-full h-full border-15"></div>
          <img class="block w-full"
            :src="therapist.user.photo_url"
            :alt="therapist.user.first_name" />
        </figure>
        <h1>
          <span class="block w-full text-5xl md:text-7xl leading-none ">{{ therapist.user.first_name }}</span>
          <span class="block w-full uppercase text-2xl">{{ therapist.user.last_name }}</span>
        </h1>
      </div>
    </div>
    <div class="bg-gray px-4 py-6 sm:py-10 sm:px-8 md:py-20">
      <div class="max-w-lg w-full mx-auto">
        <div class="py-8 px-6 sm:py-12 sm:px-16 rounded-xl bg-white">
          <template v-if="step == 1">
            <ValidationObserver ref="bookingStepOne">
              <form @submit.prevent="summaryStep" role="form" method="POST">
                <span class="block text-center text-3xl font-bold mb-6">Your Session</span>
                <t-calendar 
                  @user-date-changed="changeDates"
                  v-model="selected_dates"
                  multiple
                  :minDate="minDate"
                  :maxDate="maxDate"
                  :yearsPerView="1"
                  :disabledDates="getDisabledDates"
                  inline></t-calendar>
                <div class="mb-6">
                  <span class="block font-bold mb-3">Select time</span>
                  <div class="flex items-center justify-center"
                    v-if="loading == true">
                    <svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-current" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
                      <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
                      <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
                    </svg>
                    Loading Times...
                  </div>
                  <div v-else-if="availability.length !== 0 || availability.length == undefined">
                    <div class="mb-3"
                      v-for="(date, key) in availability"
                      v-bind:key="key">
                      <template v-if="date.length > 0">
                        <span class="block mb-2 font-bold">{{ $moment(key).format('DD/MM/Y') }}</span>
                        <div class="grid grid-cols-2 sm:grid-cols-3 gap-3">
                          <label 
                            v-for="(slot, index) in date"
                            v-bind:key="index"
                            :class="selected_slots[key].slot == slot.start ? 'bg-yellow' : ''"
                            class="border border-black px-3 py-2 rounded-md cursor-pointer text-center text-sm">
                            <input class="hidden"
                              @change="changeTimeSlot(key, slot)"
                              v-model="selected_slots[key].slot"
                              type="radio"
                              :id="'times-'+index"
                              :name="'times-'+index"
                              :value="slot.start" />
                            {{ $moment.utc(slot.start).format('H:mm') }}
                          </label>
                        </div>
                        <div class="mt-4 mb-6">
                          <span class="block text-sm font-bold mb-2">Session type</span>
                          <div class="grid grid-cols-2 sm:grid-cols-3 gap-3 mb-3"
                            v-if="selected_slots[key].session_types">
                            <label 
                              v-for="(item, session_key) in selected_slots[key].session_types"
                              v-bind:key="session_key"
                              :class="item.id == selected_slots[key].session_id ? 'bg-yellow' : ''"
                              class="border border-black px-3 py-2 rounded-md cursor-pointer text-center text-sm">
                              <input class="hidden"
                                v-model="selected_slots[key].session_id"
                                type="radio"
                                :name="'session-'+key"
                                :value="item.id" />
                              {{ item.name }}
                            </label>
                          </div>
                          <div v-else>
                            <p>No session types available.</p>
                          </div>
                          <div class="relative pl-10 mb-3 px-3 py-2 text-sm leading-5 rounded bg-blue text-white"
                            v-if="selected_slots[key].session_id == 3 && selected_slots[key].address && selected_slots[key].address.address_type !== 'Personal'">
                            <svg class="absolute top-2 left-3 w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
                            </svg>
                            <span>This session will be at: {{ selected_slots[key].address.formatted }}</span>
                          </div>
                          <t-radio-group 
                            :classes="{
                              groupWrapper: 'grid grid-cols-2 sm:grid-cols-3 gap-3',
                              label: 'block pl-2.5',
                              input: 'rounded-md w-5 h-5 text-black transition duration-100 ease-in-out border-gray-300 shadow-sm focus:border-black focus:ring-2 focus:ring-black focus:outline-none focus:ring-opacity-50 focus:ring-offset-0  disabled:opacity-50 disabled:cursor-not-allowed',
                              inputWrapper: 'inline-flex',
                              wrapper: 'flex items-center my-1',
                            }"
                            v-model="selected_slots[key].product_id"
                            textAttribute="type"
                            valueAttribute="id"
                            :name="'product-'+key"
                            :options="therapist.products"></t-radio-group>
                        </div>
                      </template>
                      <template v-else>
                        <span class="block mb-2 text-sm font-bold">{{ $moment(key).format('DD/MM/Y') }}</span>
                        <p>No availability for this day</p>
                      </template>
                    </div>
                  </div>
                  <div v-else>
                    <p>No availability found</p>
                  </div>
                </div>
                <div class="mt-6 flex items-center justify-center">
                  <button 
                    :disabled="isSlotSelected == false"
                    @click="getSummary"
                    class="btn btn-primary btn-small rounded-full w-full sm:w-auto"
                    type="button">Next</button>
                </div>
              </form>
            </ValidationObserver>
          </template>
          <SummaryStep 
            v-if="step == 2"
            v-model="step"
            :selected_slots="selectedSlots"
            :working="working"
            :nextStep="getPaymentStep"
            button_text="Proceed to Payment" />
          <RegisterStep
            v-if="step == 3"
            v-model="step" />
          <PaymentStep 
            v-if="step == 4"
            v-model="step"
            :selected_slots="selectedSlots" />
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  head () {
    return {
      titleTemplate: 'Book '+this.full_name+' | %s'
    }
  },

  data() {
    return {
      step: 1,
      isLoaded: true,
      loading: false,
      working: false,
      timer: null,
      minDate: null,
      maxDate: null,
      availability: {},
      available_dates: [],
      selected_dates: [],
      selected_slots: {}
    }
  },

  computed: {
    is_logged_in() {
      return this.$auth.loggedIn;
    },
    therapist() {
      return this.$store.state.booking.therapist
    },
    full_name() {
      return this.therapist.user.full_name;
    },
    isSlotSelected() {
      const object = this.selected_slots;
      
      for (const [key, value] of Object.entries(object)) {
        if(value.slot !== null && value.session_id !== null && value.product_id !== null) {
          return true;
        }
      }

      return false;
    },
    selectedSlots() {
      const slots = this.selected_slots;

      return Object.keys(slots)
      .filter(key => slots[key].slot !== null && slots[key].session_id !== null && slots[key].product_id !== null)
      .reduce((obj, key) => {
        return {
          ...obj,
          [key]: slots[key]
        };
      }, {});
    }
  },

  async mounted() {
    let query = this.$route.query;
    let today = new Date();
    
    // Set some defaults
    this.minDate = this.$moment(today).add(1, 'days').format('Y-MM-DD');

    await this.$axios.post('/availability/'+this.therapist.id, {
      dates: this.selected_dates
    }).then((response) => {
      this.loading = false;
      this.availability = response.data.availability;
      this.available_dates = response.data.available_dates;
      
      // Add the dates to the calendar
      this.minDate = this.available_dates[0];
      this.maxDate = this.available_dates[this.available_dates.length - 1];
      this.selected_dates = [this.available_dates[0]];
    }).catch((response) => {
      this.loading = false;
      this.$toast.error(response).goAway(3000)
    })
  },

  async asyncData({ store, params }) {
    await store.dispatch('booking/get', params.slug)
  },

  methods: {
    changeDates() {
      if (this.timer) {
        clearTimeout(this.timer);
        this.timer = null;
      }

      this.timer = setTimeout(() => {
        this.getTimes()
      }, 250);
    },

    changeTimeSlot(key, slot) {
      this.selected_slots[key].session_types = slot.session_types

      // Set address if it exists
      if(slot.address !== null) {
        this.selected_slots[key].address = slot.address
        this.selected_slots[key].address_id = slot.address.id;
      }
    },

    async getTimes() {
      let query = this.$route.query;

      this.loading = true;
      await this.$axios.post('/availability/'+this.therapist.id, {
        dates: this.selected_dates
      }).then((response) => {
        this.loading = false;
        this.availability = response.data.availability;
        this.available_dates = response.data.available_dates;
        this.selected_slots = response.data.selected_slots;
        
        // Add the dates to the calendar
        this.minDate = this.available_dates[0];
        this.maxDate = this.available_dates[this.available_dates.length - 1];

        if(this.isLoaded == true && query.time !== undefined && query.date !== undefined) {
          this.selected_slots[query.date].slot = query.time;
          this.selected_slots[query.date].product_id = this.therapist.products[0].id;
          
          if(this.availability[query.date][0].address) {
            this.selected_slots[query.date].address_id = this.availability[query.date][0].address.id;
            this.selected_slots[query.date].address = this.availability[query.date][0].address;
          }

          if(this.availability[query.date][0].session_types) {
            this.selected_slots[query.date].session_types = this.availability[query.date][0].session_types;
            this.selected_slots[query.date].session_id = this.availability[query.date][0].session_types[0].id;
          }

          this.isLoaded = false;
        }
      }).catch((response) => {
        this.loading = false;
        this.$toast.error(response).goAway(3000)
      })
    },
    
    async getSummary() {
      this.$refs.bookingStepOne.validate().then(success => {
        if (!success) {
          return;
        }

        this.step = 2;
      });
    },

    async getPaymentStep() {
      if(this.is_logged_in == false) {
        this.step = 3;
      } else {
        this.step = 4;
      }
    },

    getDisabledDates(rawDate) {
      var date = this.$moment(rawDate).format('Y-MM-DD')
      
      return !this.available_dates.includes(date);
    }
  }
}
</script>

Zerion Mini Shell 1.0