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>