FileMaster
Search
Toggle Dark Mode
Home
/
.
/
wp-content
/
plugins
/
ameliabooking
/
assets
/
views
/
backend
/
events
Edit File: DialogAttendees.vue
<template> <div class="am-dialog-attendees-inner"> <!-- Dialog Loader --> <div class="am-dialog-loader" v-show="dialogLoading"> <div class="am-dialog-loader-content"> <img :src="$root.getUrl + 'public/img/spinner.svg'" class=""/> <p>{{ $root.labels.loader_message }}</p> </div> </div> <!-- Dialog Content --> <div class="am-dialog-scrollable" v-if="bookings && !dialogLoading"> <!-- Dialog Header --> <div v-if="showHeader" class="am-dialog-header" style="border-bottom: none;"> <el-row> <el-col :span="18"> <h2>{{ $root.labels.event_attendees }}</h2> </el-col> <el-col :span="6" class="align-right"> <el-button @click="closeDialog" class="am-dialog-close" size="small" icon="el-icon-close"> </el-button> </el-col> </el-row> </div> <el-button v-if="writeEvents" @click="addAttendee" size="large" type="primary" class="am-dialog-create" style="width: 100%" > <i class="el-icon-plus"></i> <span class="button-text">{{ $root.labels.event_add_attendee }}</span> </el-button> <!-- Search --> <div class="am-search"> <el-row :gutter="10"> <el-col :lg="showExport ? 20 : 24"> <el-input v-model="search" class="" :placeholder="$root.labels.event_attendees_search" @input="searchAttendees()" > </el-input> </el-col> <el-col v-if="showExport" :lg="4"> <!-- Export --> <el-tooltip placement="top"> <div slot="content" v-html="$root.labels.export_tooltip_attendees"></div> <el-button class="button-export am-button-icon" @click="openExportAttendeesDialog" > <img class="svg-amelia" :alt="$root.labels.export" :src="$root.getUrl+'public/img/export.svg'"/> </el-button> </el-tooltip> </el-col> </el-row> </div> <!-- Attendees --> <el-tabs v-model="activeTab"> <el-tab-pane :label="$root.labels.event_attendees" name="Approved"> <div class="am-attendees"> <el-collapse> <el-collapse-item v-for="(booking, index) in bookings" v-show="booking.show && booking.status !== 'waiting'" :key="index" :name="booking.id" class="am-attendee"> <template slot="title"> <div class="am-attendee-data" style="width: 100%"> <el-row :gutter="10"> <el-col v-if="$root.settings.capabilities.canDelete === true" :sm="2"> <span class="am-attendee-checkbox" @click.stop> <el-checkbox v-if="$root.settings.capabilities.canDelete === true" v-model="booking.checked"> </el-checkbox> </span> </el-col> <el-col :sm="$root.settings.capabilities.canDelete === true ? 17 : 19"> <div class="am-attendee-name"> <h3 :class="showExport ? (getNoShowClass((booking.customerId !== 0 ? booking.customerId : booking.customer.id), customersNoShowCount, null, booking.customer.status)): ''" > {{ ((user = getCustomer(booking)) !== null ? user.firstName + ' ' + user.lastName : '') + (booking.token ? ' (' + booking.token.substring(0, 5) + ')' : '') }} <span v-if="booking.persons > 1" class="am-attendees-plus">+{{ booking.persons - 1 }}</span> </h3> <a class="am-attendee-email" :href="`mailto:${booking.customer.email}`">{{ booking.customer.email }}</a> <a class="am-attendee-phone" :href="`tel:${((user = getCustomer(booking)) !== null ? user.phone : '')}`">{{ ((user = getCustomer(booking)) !== null ? user.phone : '') }}</a> <span class="am-attendees-plus" :style="{ marginLeft: 0}" v-if="customTickets.length" v-for="ticket in booking.ticketsData" > {{ getTicketsSoldString(ticket) }} </span> </div> </el-col> <el-col :sm="5"> <div class="am-appointment-status small"> <span :class="'am-appointment-status-symbol am-appointment-status-symbol-'+getBookingStatus(booking)"></span> <el-select :value="booking.status" :popper-append-to-body="popperAppendToBody" :disabled="!writeEvents" @change="updateBookingStatus(booking, $event)" > <el-option v-for="item in statuses" :key="item.value" :value="item.value" class="am-appointment-dialog-status-option" > <span :class="'am-appointment-status-symbol am-appointment-status-symbol-'+(item.value === 'rejected' ? 'canceled' : item.value)"> </span> </el-option> </el-select> </div> </el-col> </el-row> </div> </template> <div class="am-attendee-collapse"> <el-row :gutter="10" v-if="booking.payments.length" class="am-attendee-collapse-payments"> <el-col :span="6"> <span>{{ $root.labels.payment }}</span> </el-col> <el-col :span="18"> <p v-for="payment in booking.payments"> <span style="display: flex; align-items: center"> <img class="svg-amelia" :style="{width: getPaymentIconWidth(payment.gateway)}" :src="$root.getUrl + 'public/img/payments/' + payment.gateway + '.svg'"/> {{ getPaymentGatewayNiceName(payment.gateway) }} <span class="am-semi-strong am-payment-status" style="padding-left: 15px"> <span :class="'am-payment-status-symbol am-payment-status-symbol-' + payment.status"></span> {{ getPaymentStatusNiceName(payment.status) }} </span> </span> </p> </el-col> </el-row> <el-row :gutter="10" v-if="booking.payments.filter(p => p.wcOrderId).length > 0" class="am-attendee-collapse-payments"> <el-col :span="6"> <span>{{ $root.labels.wc_order }}:</span> </el-col> <el-col :span="18"> <p v-for="payment in booking.payments" :key="payment.id"> <a :href="payment.wcOrderUrl" target="_blank"> #{{ payment.wcOrderId }} </a> </p> </el-col> </el-row> <el-row :gutter="10"> <el-col :span="12"> <div class=""> <el-button v-if="writeEvents" :loading="booking.removing" @click="removeAttendee(index)"> {{ $root.labels.event_attendee_remove }} </el-button> </div> </el-col> <el-col :span="12"> <div class=""> <el-button v-if="writeEvents" @click="editAttendee(index)"> {{ $root.labels.event_edit_attendee }} </el-button> </div> </el-col> </el-row> </div> </el-collapse-item> </el-collapse> </div> <div v-if="bookings.filter(b => b.status !== 'waiting').length === 0" class="am-empty-state am-section"> <img :src="$root.getUrl + 'public/img/emptystate.svg'"> <h2>{{ $root.labels.no_attendees_yet }}</h2> </div> <div v-show="!hasResult && bookings.filter(b => b.status !== 'waiting').length > 0" class="am-empty-state am-section"> <img :src="$root.getUrl + 'public/img/emptystate.svg'"> <h2>{{ $root.labels.no_results }}</h2> </div> </el-tab-pane> <el-tab-pane :label="$root.labels.waiting_list" name="WaitingList" v-if="event.settings.waitingList.enabled && $root.settings.appointments.waitingListEvents.enabled" > <div class="am-attendees"> <el-collapse> <el-collapse-item v-for="(booking, index) in bookings" v-show="booking.show && booking.status === 'waiting'" :key="index" :name="booking.id" class="am-attendee" > <template slot="title"> <div class="am-attendee-data" style="width: 100%"> <el-row :gutter="10"> <el-col v-if="$root.settings.capabilities.canDelete === true" :sm="2"> <span class="am-attendee-checkbox" @click.stop> <el-checkbox v-if="$root.settings.capabilities.canDelete === true" v-model="booking.checked"> </el-checkbox> </span> </el-col> <el-col :sm="$root.settings.capabilities.canDelete === true ? 17 : 19"> <div class="am-attendee-name"> <h3> {{ ((user = getCustomer(booking)) !== null ? user.firstName + ' ' + user.lastName : '') + (booking.token ? ' (' + booking.token.substring(0, 5) + ')' : '') }} <span v-if="booking.persons > 1" class="am-attendees-plus">+{{ booking.persons - 1 }}</span> </h3> <a class="am-attendee-email" :href="`mailto:${booking.customer.email}`">{{ booking.customer.email }}</a> <a class="am-attendee-phone" :href="`tel:${((user = getCustomer(booking)) !== null ? user.phone : '')}`">{{ ((user = getCustomer(booking)) !== null ? user.phone : '') }}</a> <span class="am-attendees-plus" :style="{ marginLeft: 0}" v-if="customTickets.length" v-for="ticket in booking.ticketsData" > {{ getTicketsSoldString(ticket) }} </span> </div> </el-col> <el-col :sm="5"> <div class="am-appointment-status small"> <span :class="'am-appointment-status-symbol am-appointment-status-symbol-' + getBookingStatus(booking)"></span> <el-select :value="booking.status" :popper-append-to-body="popperAppendToBody" :disabled="!writeEvents" @change="updateBookingStatus(booking, $event)" > <el-option v-for="item in statuses" v-show="item.value !== 'no-show'" :key="item.value" :value="item.value" class="am-appointment-dialog-status-option" > <span :class="'am-appointment-status-symbol am-appointment-status-symbol-'+(item.value === 'rejected' ? 'canceled' : item.value)"> </span> </el-option> </el-select> </div> </el-col> </el-row> </div> </template> <div class="am-attendee-collapse"> <el-row :gutter="10" v-if="booking.payments.length" class="am-attendee-collapse-payments"> <el-col :span="6"> <span>{{ $root.labels.payment }}</span> </el-col> <el-col :span="18"> <p v-for="payment in booking.payments"> <img class="svg-amelia" :style="{width: getPaymentIconWidth(payment.gateway)}" :src="$root.getUrl + 'public/img/payments/' + payment.gateway + '.svg'"/> {{ getPaymentGatewayNiceName(payment.gateway) }} </p> </el-col> </el-row> <el-row :gutter="10" v-if="booking.payments.filter(p => p.wcOrderId).length > 0" class="am-attendee-collapse-payments"> <el-col :span="6"> <span>{{ $root.labels.wc_order }}:</span> </el-col> <el-col :span="18"> <p v-for="payment in booking.payments" :key="payment.id"> <a :href="payment.wcOrderUrl" target="_blank"> #{{ payment.wcOrderId }} </a> </p> </el-col> </el-row> <el-row :gutter="10"> <el-col :span="12"> <div class=""> <el-button v-if="writeEvents" :loading="booking.removing" @click="removeAttendee(index)"> {{ $root.labels.event_attendee_remove }} </el-button> </div> </el-col> <el-col :span="12"> <div class=""> <el-button v-if="writeEvents" @click="editAttendee(index)"> {{ $root.labels.event_edit_attendee }} </el-button> </div> </el-col> </el-row> </div> </el-collapse-item> </el-collapse> </div> <div v-if="bookings.filter(b => b.status === 'waiting').length === 0" class="am-empty-state am-section"> <img :src="$root.getUrl + 'public/img/emptystate.svg'"> <h2>{{ $root.labels.waiting_list_empty }}</h2> </div> <div v-show="!hasResult && bookings.filter(b => b.status === 'waiting').length > 0" class="am-empty-state am-section"> <img :src="$root.getUrl + 'public/img/emptystate.svg'"> <h2>{{ $root.labels.no_results }}</h2> </div> </el-tab-pane> </el-tabs> </div> <!-- Dialog Actions --> <transition name="slide-vertical"> <div v-show="!dialogLoading && bookings.length > 0 && bookings.filter(booking => booking.checked).length > 0"> <div class="am-dialog-footer"> <div class="am-dialog-footer-actions"> <el-row> <el-col :sm="12" class="align-left"> <el-button class="am-button-icon" @click="showDeleteConfirmation = !showDeleteConfirmation"> <img class="svg-amelia" :alt="$root.labels.delete" :src="$root.getUrl+'public/img/delete.svg'"/> </el-button> </el-col> </el-row> </div> </div> </div> </transition> <!-- Dialog Delete Confirmation --> <transition name="slide-vertical"> <div class="am-dialog-confirmation" v-show="!dialogLoading && showDeleteConfirmation"> <h3>{{ bookings.filter(booking => booking.checked).length > 1 ? $root.labels.confirm_delete_attendees : $root.labels.confirm_delete_attendee }}</h3> <div class="align-left"> <el-button size="small" @click="showDeleteConfirmation = !showDeleteConfirmation"> {{ $root.labels.cancel }} </el-button> <el-button v-if="writeEvents" size="small" @click="removeAttendees" type="primary"> {{ $root.labels.delete }} </el-button> </div> </div> </transition> </div> </template> <script> import dateMixin from '../../../js/common/mixins/dateMixin' import deleteMixin from '../../../js/backend/mixins/deleteMixin' import entitiesMixin from '../../../js/common/mixins/entitiesMixin' import imageMixin from '../../../js/common/mixins/imageMixin' import notifyMixin from '../../../js/backend/mixins/notifyMixin' import paymentMixin from '../../../js/backend/mixins/paymentMixin' import customerMixin from '../../../js/backend/mixins/customerMixin' export default { mixins: [ imageMixin, dateMixin, entitiesMixin, paymentMixin, notifyMixin, deleteMixin, customerMixin ], props: { event: null, eventStatus: null, customTickets: null, options: null, eventBookings: null, aggregatedPrice: true, bookingCreatedCount: 0, newBooking: null, showHeader: { required: false, default: true, type: Boolean }, showExport: { required: false, default: true, type: Boolean }, writeEvents: { type: Boolean, default: true, required: false }, popperAppendToBody: { type: Boolean, default: true, required: false }, customersNoShowCount: { type: Array, default: () => [], required: false } }, data () { return { dialogExport: false, bookings: [], name: 'events/bookings', successMessage: { single: this.$root.labels.event_attendee_deleted, multiple: this.$root.labels.event_attendees_deleted }, errorMessage: { single: this.$root.labels.event_attendee_not_deleted, multiple: this.$root.labels.event_attendees_not_deleted }, search: '', hasResult: true, dialogLoading: true, showDeleteConfirmation: false, statuses: [ { value: 'approved', label: this.$root.labels.approved }, { value: 'rejected', label: this.$root.labels.rejected }, { value: 'no-show', label: this.$root.labels['no-show'] } ], activeTab: 'Approved' } }, methods: { getBookingStatus (booking) { if (this.eventStatus === 'rejected' || this.eventStatus === 'canceled') { return 'canceled' } return (booking.status === 'rejected' ? 'canceled' : booking.status) }, getInitAttendeeObject () { return { id: 0, customer: null, status: 'approved', persons: 1, added: false, info: null, aggregatedPrice: this.aggregatedPrice, customFields: {} } }, addAttendee () { this.$emit('showDialogAttendee', this.getInitAttendeeObject()) }, hasCapacity (bookingToTransfer) { let takenSpots = 0 if (this.event.customPricing && !this.event.maxCustomCapacity) { let canBeTransferred = true let freeTickets = {} this.event.customTickets.forEach(ticket => { freeTickets[ticket.id] = ticket.spots }) this.event.bookings.forEach(booking => { if (booking.status === 'approved') { booking.ticketsData.forEach(item => { freeTickets[item.eventTicketId] -= item.persons }) } }) bookingToTransfer.ticketsData.forEach(bookedTicket => { if (freeTickets[bookedTicket.eventTicketId] < bookedTicket.persons) canBeTransferred = false }) return canBeTransferred } else if (this.event.maxCustomCapacity) { this.event.bookings.forEach(booking => { if (booking.status === 'approved') { booking.ticketsData.forEach(item => { takenSpots += item.persons }) } }) return this.event.maxCustomCapacity > takenSpots } else { this.event.bookings.forEach(b => { if (b.status === 'approved') { takenSpots += b.persons } }) return this.event.maxCapacity > takenSpots } }, updateBookingStatus (booking, newStatus) { let exceededCapacityWarning = false let createPaymentLinks = false if (booking.status === 'waiting' && newStatus === 'approved') { exceededCapacityWarning = !this.hasCapacity(booking) createPaymentLinks = true } this.$http.post(`${this.$root.getAjaxUrl}/events/bookings/` + booking.id, { status: newStatus, bookings: [{status: newStatus}], createPaymentLinks }).then(() => { if (exceededCapacityWarning) { this.notify( this.$root.labels.warning, this.$root.labels.waiting_list_capacity_warning, 'warning' ) } this.notify( this.$root.labels.success, this.$root.labels.event_status_changed + (this.$root.labels[newStatus]).toLowerCase(), 'success' ) let oldStatus = booking.status booking.status = newStatus if (newStatus === 'no-show' || oldStatus === 'no-show') { let customersIds = this.options.entities.customers.map(c => c.id) if (customersIds.includes(booking.customer.id)) { let customerIndex = customersIds.indexOf(booking.customer.id) let customerNoShowCount = this.options.entities.customers[customerIndex].noShowCount this.options.entities.customers[customerIndex].noShowCount = customerNoShowCount + (newStatus === 'no-show' ? 1 : -1) } } this.$emit('updateAttendeesCallback') }).catch(e => { this.notify(this.$root.labels.error, e.message, 'error') }) }, getCustomer (booking) { return booking.info ? JSON.parse(booking.info) : booking.customer }, getTicketsSoldString (bookedTicket) { let ticketName = this.customTickets.filter(ticket => ticket.id === bookedTicket.eventTicketId)[0].name return bookedTicket.persons + ' x ' + ticketName }, instantiateDialog () { if (this.eventBookings) { this.bookings = this.eventBookings this.bookings.tickets = [] this.dialogLoading = false } if (this.$root.settings.appointments.waitingListEvents.enabled && this.event.settings.waitingList.enabled) { this.statuses.push({ value: 'waiting', label: this.$root.labels.waiting_list }) } }, closeDialog () { this.$emit('closeDialog') }, removeAttendee (index) { let $this = this let deletedSuccessIds = [] this.bookings[index].removing = true this.deleteEntities( [this.bookings[index].id], function () { setTimeout(function () { for (let i = $this.bookings.length - 1; i >= 0; i--) { if (deletedSuccessIds.indexOf($this.bookings[i].id) !== -1) { $this.bookings.splice(i, 1) } } $this.$emit('updateAttendeesCallback') if ($this.bookings.length === 0) { $this.$emit('closeDialog') } }, 500) }, function (id) { deletedSuccessIds.push(id) }, function (id) { } ) }, editAttendee (index) { this.$emit('showDialogAttendee', this.bookings[index]) }, removeAttendees () { let $this = this let deletedSuccessIds = [] $this.dialogLoading = true $this.showDeleteConfirmation = false this.deleteEntities( $this.bookings.filter(booking => booking.checked).map(booking => booking.id), function () { setTimeout(function () { for (let i = $this.bookings.length - 1; i >= 0; i--) { if (deletedSuccessIds.indexOf($this.bookings[i].id) !== -1) { $this.bookings.splice(i, 1) } } $this.dialogLoading = false $this.$emit('updateAttendeesCallback') if ($this.bookings.length === 0) { $this.$emit('closeDialog') } }, 500) }, function (id) { deletedSuccessIds.push(id) }, function (id) { } ) }, searchAttendees () { let $this = this this.bookings.forEach(function (booking) { booking.show = (booking.customer.firstName.toLowerCase().startsWith($this.search.toLowerCase()) || booking.customer.lastName.toLowerCase().startsWith($this.search.toLowerCase()) || (booking.customer.firstName + ' ' + booking.customer.lastName).toLowerCase().startsWith($this.search.toLowerCase()) || (booking.customer.lastName + ' ' + booking.customer.firstName).toLowerCase().startsWith($this.search.toLowerCase()) || (booking.customer.email !== null && booking.customer.email.toLowerCase().startsWith($this.search.toLowerCase())) || (booking.customer.phone !== null && booking.customer.phone.toLowerCase().startsWith($this.search.toLowerCase())) || (booking.token !== null ? booking.token.toLowerCase().substring(0, 5).startsWith($this.search.toLowerCase()) : false) || (booking.customer.firstName.split(' ').map(part => part.toLowerCase().startsWith($this.search.toLowerCase())).includes(true)) || (booking.customer.lastName.split(' ').map(part => part.toLowerCase().startsWith($this.search.toLowerCase())).includes(true)) ) }) this.hasResult = this.bookings.filter(booking => booking.show === true).length > 0 if (this.hasResult) { this.activeTab = this.bookings.filter(booking => booking.show === true)[0].status === 'waiting' ? 'WaitingList' : 'Approved' } }, openExportAttendeesDialog () { this.$emit('openExportAttendeesDialog') } }, mounted () { this.instantiateDialog() }, watch: { 'bookingCreatedCount' () { this.bookings = this.eventBookings this.bookings.sort(function (a, b) { return (a.customer.firstName + ' ' + a.customer.lastName).localeCompare((b.customer.firstName + ' ' + b.customer.lastName)) }) this.hasResult = true this.search = '' } } } </script>
Save
Back