PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.4.6
Booking for Appointments and Events Calendar – Amelia v2.4.6
2.4.7 2.4.6 2.4.5 2.4.4 2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.32 1.2.33 1.2.34 1.2.35 1.2.36 1.2.37 1.2.38 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.3
ameliabooking / v3 / src / views / public / StepForm / Congratulations / Congratulations.vue
ameliabooking / v3 / src / views / public / StepForm / Congratulations Last commit date
parts 1 month ago AddToCalendar.vue 1 month ago Congratulations.vue 1 month ago
Congratulations.vue
449 lines
1 <template>
2 <div
3 v-if="ready && booked && !loading"
4 :style="cssVars"
5 class="am-fs__main-content am-fs__congrats"
6 :class="[{ 'am-fs-sb-atc': checkScreen }, props.globalClass]"
7 >
8 <div class="am-fs__congrats-main">
9 <img
10 :src="baseUrls.wpAmeliaPluginURL + '/v3/src/assets/img/congratulations/congratulations.svg'"
11 :alt="amLabels.congratulations"
12 />
13 <div
14 v-if="waitingListOptions.enabled && waitingListOptions.isWaitingListSlot"
15 class="am-fs__congrats-main-waiting-list-block"
16 >
17 <p class="am-fs__congrats-main-heading">
18 {{ amLabels.your_position_on_waiting_list }}
19 {{ '#' + (waitingListOptions.peopleWaiting + 1) }}
20 </p>
21 <span>{{ amLabels.appointment_waiting_list_notify_message }}</span>
22 </div>
23 <p v-else class="am-fs__congrats-main-heading">
24 {{ amLabels.congratulations }}
25 </p>
26 <span v-if="booked && booked.data.length && bookedType === 'appointment'"
27 >{{ amLabels.appointment_id }} #{{ booked.data[0].appointmentId }}</span
28 >
29
30 <AddToCalendar v-if="checkScreen" class="am-fs__congrats-main-atc"></AddToCalendar>
31 </div>
32
33 <div
34 class="am-fs__congrats-info"
35 :class="[
36 { 'am-fs__congrats-info-mobile': checkScreen },
37 { 'am-fs__congrats-info-mobile-s': mobileS },
38 ]"
39 >
40 <div class="am-fs__congrats-info-customer">
41 <component :is="componentTypes[bookedType]"></component>
42 <div
43 v-if="booked.price > 0 || (booked.price <= 0 && coupon.code)"
44 class="am-fs__congrats-info-payment"
45 >
46 <span
47 v-if="
48 booked && booked.paymentAmount && getPayment(booked.payments).gateway === 'onSite'
49 "
50 >
51 {{ amLabels.congrats_total_amount }}:
52 </span>
53 <span v-else>{{ amLabels.congrats_payment }}:</span>
54 <span v-if="booked && booked.paymentAmount && getPayment(booked.payments).gateway">
55 {{
56 `${useFormattedPrice(booked.paymentAmount)} - ${getPayment(booked.payments).gatewayTitle ? getPayment(booked.payments).gatewayTitle : getPaymentGatewayNiceName(getPayment(booked.payments))}`
57 }}
58 </span>
59 <span v-else-if="booked">
60 {{
61 (getPayment(booked.payments).gateway !== 'onSite'
62 ? useFormattedPrice(booked.paymentAmount)
63 : useFormattedPrice(booked.price < 0 ? 0 : booked.price)) +
64 (getPayment(booked.payments).status !== 'paid' ? ' - ' + amLabels.on_site : '')
65 }}
66 </span>
67 <span v-else></span>
68 </div>
69 <div class="am-fs__congrats-info-customer-border am-fs__congrats-info-customer-name">
70 <span>{{ amLabels.your_name_colon }}:</span>
71 <span>{{ customer.firstName + ' ' + customer.lastName }}</span>
72 </div>
73 <div v-if="customer.email" class="am-fs__congrats-info-customer-email">
74 <span>{{ amLabels.email_address_colon }}:</span>
75 <span>{{ customer.email }}</span>
76 </div>
77 <div v-if="customer.phone" class="am-fs__congrats-info-customer-phone">
78 <span>{{ amLabels.phone_number_colon }}:</span>
79 <span>{{ customer.phone }}</span>
80 </div>
81 </div>
82 </div>
83 </div>
84
85 <!-- Skeleton -->
86 <el-skeleton v-else animated class="am-skeleton-congratz">
87 <template #template>
88 <div class="am-skeleton-congratz-heading">
89 <el-skeleton-item variant="circle" />
90 <el-skeleton-item variant="h3" />
91 <el-skeleton-item variant="text" />
92 </div>
93 <div class="am-skeleton-congratz-booking-info">
94 <div
95 v-for="(item, i) in new Array(10)"
96 :key="item"
97 :class="{ 'am-customer-info': i === 6 }"
98 >
99 <el-skeleton-item
100 variant="text"
101 :style="{ width: `${useRandomIntFromInterval(14, 36)}%` }"
102 />
103 <el-skeleton-item
104 variant="text"
105 :style="{ width: `${useRandomIntFromInterval(14, 36)}%` }"
106 />
107 </div>
108 </div>
109 </template>
110 </el-skeleton>
111 <!-- /Skeleton -->
112 </template>
113
114 <script setup>
115 import AddToCalendar from './AddToCalendar'
116 import CartInfoService from './parts/CartInfoService'
117 import AppointmentInfoService from './parts/AppointmentInfoService'
118 import PackageInfoService from './parts/PackageInfoService'
119 import { computed, inject, provide, markRaw, watchEffect, onMounted } from 'vue'
120 import { useColorTransparency } from '../../../../assets/js/common/colorManipulation'
121 import {
122 useRandomIntFromInterval,
123 useFormattedPrice,
124 } from '../../../../assets/js/common/formatting'
125 import { useStore } from 'vuex'
126 import { useCart } from '../../../../assets/js/public/cart'
127 import { useRenderAction } from '../../../../assets/js/public/renderActions'
128
129 let props = defineProps({
130 globalClass: {
131 type: String,
132 default: '',
133 },
134 })
135
136 // * Root Settings
137 const amSettings = inject('settings')
138
139 const store = useStore()
140
141 // * Labels
142 const amLabels = inject('amLabels')
143
144 /**************
145 * Computed *
146 *************/
147
148 let ready = computed(() => store.getters['entities/getReady'])
149
150 let booked = computed(() => store.getters['booking/getBooked'])
151
152 provide('booked', booked)
153
154 let bookedType = computed(() => {
155 return useCart(store).filter((i) => i.serviceId && i.serviceId in i.services).length > 1
156 ? 'cart'
157 : booked.value.type
158 })
159
160 // * Coupon
161 let coupon = computed(() => store.getters['booking/getCoupon'])
162
163 let loading = computed(() => store.getters['booking/getLoading'])
164
165 let customer = computed(() => {
166 return {
167 firstName: store.getters['booking/getCustomerFirstName'],
168 lastName: store.getters['booking/getCustomerLastName'],
169 email: store.getters['booking/getCustomerEmail'],
170 phone: store.getters['booking/getCustomerPhone'],
171 }
172 })
173
174 let waitingListOptions = computed(() => store.getters['appointmentWaitingListOptions/getOptions'])
175
176 /**************
177 * Navigation *
178 *************/
179
180 const { footerButtonClicked } = inject('changingStepsFunctions', {
181 footerButtonClicked: {
182 value: false,
183 },
184 })
185
186 function getPayment(payments) {
187 let onlinePayments = payments.filter((i) => i.gateway !== 'onSite')
188
189 return onlinePayments.length ? onlinePayments[0] : payments[0]
190 }
191
192 function getPaymentGatewayNiceName(payment) {
193 if (payment.gateway === 'onSite') {
194 return amLabels.value['on_site']
195 }
196
197 if (payment.gateway === 'wc') {
198 return payment.gatewayTitle
199 }
200
201 if (payment.gateway) {
202 return payment.gateway.charAt(0).toUpperCase() + payment.gateway.slice(1)
203 }
204 }
205
206 // * Watching when footer button was clicked
207 watchEffect(() => {
208 if (footerButtonClicked.value) {
209 finish()
210 }
211 })
212
213 const componentTypes = {
214 cart: markRaw(CartInfoService),
215 appointment: markRaw(AppointmentInfoService),
216 package: markRaw(PackageInfoService),
217 }
218
219 const globalSettings = inject('settings')
220 const baseUrls = inject('baseUrls')
221
222 let cWidth = inject('containerWidth', 0)
223 let checkScreen = computed(() => cWidth.value < 540)
224 let mobileS = computed(() => cWidth.value < 340)
225
226 function finish() {
227 let entity = store.getters['entities/getBookableFromBookableEntities'](
228 store.getters['booking/getSelection'],
229 )
230
231 let entitySettings = entity.settings ? JSON.parse(entity.settings) : globalSettings
232
233 if (
234 'general' in entitySettings &&
235 'redirectUrlAfterAppointment' in entitySettings.general &&
236 entitySettings.general.redirectUrlAfterAppointment
237 ) {
238 window.location.href = entitySettings.general.redirectUrlAfterAppointment
239 } else if (globalSettings.general.redirectUrlAfterAppointment) {
240 window.location.href = globalSettings.general.redirectUrlAfterAppointment
241 } else {
242 window.location.reload()
243 }
244 }
245
246 let amColors = inject('amColors')
247
248 const cssVars = computed(() => {
249 if (checkScreen.value) {
250 return {
251 '--am-c-atc-text-op40': useColorTransparency(amColors.value.colorMainText, 0.4),
252 '--am-c-atc-heading-text-op40': useColorTransparency(amColors.value.colorSbText, 0.4),
253 '--am-c-atc-text-op30': useColorTransparency(amColors.value.colorMainText, 0.3),
254 '--am-c-atc-text': amColors.value.colorMainText,
255 '--am-c-atc-heading-text': amColors.value.colorSbText,
256 '--am-c-atc-bgr-coverage':
257 booked.value.type === 'package' &&
258 !(amSettings.general.addToCalendar && booked.value && booked.value.data.length)
259 ? '50%'
260 : '80%',
261 }
262 } else {
263 return {
264 '--am-c-atc-text-op40': useColorTransparency(amColors.value.colorMainText, 0.4),
265 '--am-c-atc-heading-text-op40': useColorTransparency(
266 amColors.value.colorMainHeadingText,
267 0.4,
268 ),
269 '--am-c-atc-text-op30': useColorTransparency(amColors.value.colorMainText, 0.3),
270 '--am-c-atc-text': amColors.value.colorMainText,
271 '--am-c-atc-heading-text': amColors.value.colorMainHeadingText,
272 }
273 }
274 })
275
276 onMounted(() => {
277 useRenderAction('congratulationsLoaded')
278 })
279 </script>
280
281 <script>
282 export default {
283 name: 'CongratulationsStep',
284 key: 'congratulations',
285 sidebarData: {
286 label: 'congratulations',
287 icon: 'pennant',
288 selected: true,
289 finished: true,
290 },
291 }
292 </script>
293
294 <style lang="scss">
295 .amelia-v2-booking #amelia-container {
296 .am-fs__main-content.am-fs__congrats {
297 padding: 16px 16px 16px;
298 height: calc(100% - 56px);
299 margin-top: 0;
300 }
301
302 .am-fs-sb-atc {
303 --am-c-sb-bgr-atc: var(--am-c-sb-bgr);
304 background-image: linear-gradient(
305 var(--am-c-sb-bgr-atc) var(--am-c-atc-bgr-coverage),
306 transparent 20%
307 );
308 }
309
310 .am-fs__congrats {
311 &-main {
312 margin-top: 22px;
313 display: flex;
314 flex-direction: column;
315 align-items: center;
316 font-family: var(--am-font-family);
317 margin-bottom: 16px;
318
319 & > * {
320 $count: 5;
321 @for $i from 0 through $count {
322 &:nth-child(#{$i + 1}) {
323 animation: 600ms cubic-bezier(0.45, 1, 0.4, 1.2) #{$i * 100}ms am-animation-slide-up;
324 animation-fill-mode: both;
325 }
326 }
327 }
328
329 & img {
330 width: 54px;
331 margin-bottom: 8px;
332 }
333 &-heading {
334 margin-bottom: 2px;
335 font-weight: 500;
336 font-size: 18px;
337 line-height: 28px;
338 color: var(--am-c-atc-heading-text);
339 }
340 &-atc {
341 margin-top: 16px;
342 }
343 & span {
344 color: var(--am-c-atc-heading-text-op40);
345 font-weight: 400;
346 font-size: 13px;
347 line-height: 18px;
348 }
349
350 &-waiting-list-block {
351 display: flex;
352 flex-direction: column;
353 align-items: center;
354 }
355 }
356 &-info-mobile {
357 padding: 16px;
358 margin-top: 24px;
359 border-radius: 6px;
360 box-shadow: 0 2px 3px 2px rgba(26, 44, 55, 0.1);
361 }
362 &-info {
363 --am-c-atc-main-bgr: var(--am-c-main-bgr);
364 background-color: var(--am-c-atc-main-bgr);
365 &-customer {
366 &-border {
367 border-top: 1px solid var(--am-c-atc-text-op30);
368 padding-top: 16px;
369 }
370
371 & > div {
372 $count: 20;
373 @for $i from 0 through $count {
374 &:nth-child(#{$i + 1}) {
375 animation: 600ms cubic-bezier(0.45, 1, 0.4, 1.2) #{$i * 100}ms am-animation-slide-up;
376 animation-fill-mode: both;
377 }
378 }
379 }
380
381 & div {
382 display: flex;
383 justify-content: space-between;
384 font-weight: 400;
385 font-size: 14px;
386 margin-bottom: 12px;
387 line-height: 20px;
388 & span {
389 color: var(--am-c-atc-text-op40);
390 }
391 & span:nth-child(2) {
392 color: var(--am-c-atc-text);
393 }
394 }
395 }
396 }
397
398 &-info-mobile-s {
399 .am-fs__congrats-info-customer > div {
400 flex-direction: column;
401 }
402 }
403 }
404
405 .am-skeleton-congratz {
406 &-heading {
407 display: flex;
408 flex-direction: column;
409 align-items: center;
410 margin-bottom: 13px;
411
412 :first-child {
413 width: 52px;
414 height: 52px;
415 margin-bottom: 9px;
416 }
417
418 :nth-child(2) {
419 height: 28px;
420 max-width: 132px;
421 margin-bottom: 4px;
422 }
423
424 :last-child {
425 max-width: 148px;
426 height: 18px;
427 }
428 }
429
430 &-booking-info {
431 & > div {
432 display: flex;
433 justify-content: space-between;
434 margin-bottom: 12px;
435
436 .el-skeleton__item {
437 height: 20px;
438 }
439 }
440
441 .am-customer-info {
442 border-bottom: 1px solid var(--am-c-skeleton-op20);
443 padding-bottom: 12px;
444 }
445 }
446 }
447 }
448 </style>
449