PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.4.8
Booking for Appointments and Events Calendar – Amelia v2.4.8
2.4.9 2.4.8 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 / Cabinet / common / Events / Events.vue
ameliabooking / v3 / src / views / public / Cabinet / common / Events Last commit date
parts 1 week ago Events.vue 1 week ago
Events.vue
1069 lines
1 <template>
2 <div
3 ref="pageContainer"
4 class="am-cap am-cape-main"
5 :class="{
6 'am-capei-main': eventVisibility || eventAttendeeVisibility || attendeeOpenFromAttendees,
7 }"
8 :style="cssVars"
9 >
10 <div class="am-cape-main__inner" :class="responsiveClass">
11 <AmAlert
12 v-if="alertVisibility"
13 ref="alertContainer"
14 :type="alertType"
15 :show-border="true"
16 :close-after="5000"
17 custom-class="am-cap__alert"
18 @close="closeAlert"
19 @trigger-close="closeAlert"
20 >
21 <template #title>
22 <span class="am-icon-checkmark-circle-full"></span> {{ alertMessage }}
23 </template>
24 </AmAlert>
25
26 <CabinetFilters
27 v-if="!eventAttendeeVisibility && !eventVisibility && !eventAttendeesVisibility && ready"
28 :step-key="'events'"
29 :empty="empty"
30 :responsive-class="responsiveClass"
31 @change-filters="getEvents"
32 />
33
34 <div
35 v-if="
36 shortcodeData.cabinetType === 'employee' &&
37 ready &&
38 !eventAttendeeVisibility &&
39 !eventVisibility &&
40 !eventAttendeesVisibility &&
41 (((licence.isPro || licence.isDeveloper) &&
42 amSettings.featuresIntegrations.eTickets.enabled &&
43 !employeePanelReadOnly) ||
44 amSettings.roles.allowWriteEvents)
45 "
46 class="am-cap__actions"
47 :class="responsiveClass"
48 >
49 <AmButton
50 v-if="
51 (licence.isPro || licence.isDeveloper) &&
52 amSettings.featuresIntegrations.eTickets.enabled &&
53 !employeePanelReadOnly &&
54 (amCustomize.events.options?.scanQrCodeBtn?.visibility ?? true)
55 "
56 prefix="scan-qr-code"
57 size="small"
58 category="secondary"
59 type="plain"
60 @click="qrScannerVisibility = true"
61 >
62 {{ amLabels.scan_e_ticket }}
63 </AmButton>
64 <AmButton
65 v-if="amSettings.roles.allowWriteEvents"
66 :icon="plusIcon"
67 prefix="plus"
68 size="small"
69 category="primary"
70 :type="amCustomize.events.options.newEvtBtn.buttonType"
71 @click="addEvent"
72 >
73 <span>{{ amLabels.new_event }}</span>
74 </AmButton>
75 </div>
76
77 <template v-if="!loading && ready">
78 <div
79 v-if="
80 !eventAttendeeVisibility &&
81 !eventVisibility &&
82 !eventAttendeesVisibility &&
83 dateGroupedEvents &&
84 Object.keys(dateGroupedEvents).length > 0
85 "
86 class="am-cape__wrapper"
87 :class="[
88 {
89 'am-no-border': dateGroupedEvents && Object.keys(dateGroupedEvents).length === 1,
90 },
91 responsiveClass,
92 ]"
93 >
94 <div v-for="(item, dateKey) in dateGroupedEvents" :key="dateKey" class="am-cape">
95 <div
96 class="am-cape__date"
97 :class="[
98 {
99 'am-today':
100 getFrontedFormattedDate(dateKey) ===
101 getFrontedFormattedDate(moment().format('YYYY-MM-DD')),
102 },
103 {
104 'am-no-flag': dateGroupedEvents && Object.keys(dateGroupedEvents).length === 1,
105 },
106 responsiveClass,
107 ]"
108 >
109 {{ getFrontedFormattedDate(dateKey) }}
110 </div>
111 <CollapseCard
112 v-for="(event, index) in item.events"
113 :key="index"
114 :start="
115 getFrontedFormattedTime(event.periods[0].periodStart.split(' ')[1].slice(0, 5))
116 "
117 :name="event.name"
118 :employee="eventEmployees(event)"
119 :customers="
120 event.bookings
121 .filter((i) => i.status !== 'rejected' && i.status !== 'canceled')
122 .map((i) => i.customer)
123 "
124 :price="useEventBookingsPrice(event)"
125 :duration="null"
126 :periods="(periods = usePeriodsData(event.periods)) ? periods : []"
127 :extras="[]"
128 :tickets="useTicketsData(event)"
129 :custom-fields="useCustomFieldsData(event.bookings, shortcodeData.cabinetType)"
130 :location="useEventLocation(store, event)"
131 :qr-codes="qrCodeTicketsVisibility ? useEventQrCodes(event) : []"
132 :google-meet-link="
133 event.periods.length === 1 && event.periods[0].googleMeetUrl
134 ? event.periods[0].googleMeetUrl
135 : ''
136 "
137 :microsoft-teams-link="
138 event.periods.length === 1 && event.periods[0].microsoftTeamsUrl
139 ? event.periods[0].microsoftTeamsUrl
140 : ''
141 "
142 :zoom-link="
143 event.periods.length === 1 && event.periods[0].zoomMeeting
144 ? event.periods[0].zoomMeeting.joinUrl
145 : ''
146 "
147 :lesson-space-link="
148 event.periods.length === 1 && event.periods[0].lessonSpace
149 ? event.periods[0].lessonSpace
150 : ''
151 "
152 :bookable="event"
153 :reservation="event"
154 :booking="event.bookings[0]"
155 :responsive-class="responsiveClass"
156 :parent-width="pageWidth"
157 :customized-options="customizedOptions('events')"
158 @edit-event="editEvent"
159 @add-event-attendee="addEventAttendee"
160 @list-event-attendees="listEventAttendees"
161 @cancel-booking="
162 (data) => {
163 targetBooking = data
164 }
165 "
166 ></CollapseCard>
167 </div>
168 </div>
169
170 <EmptyState
171 v-if="
172 !eventAttendeeVisibility &&
173 !eventVisibility &&
174 !eventAttendeesVisibility &&
175 (dateGroupedEvents === null || Object.keys(dateGroupedEvents).length === 0)
176 "
177 :heading="amLabels.no_evt_found"
178 :text="amLabels.have_no_evt"
179 ></EmptyState>
180
181 <AmPagination
182 v-if="
183 !eventAttendeeVisibility &&
184 !eventVisibility &&
185 !eventAttendeesVisibility &&
186 dateGroupedEvents &&
187 Object.keys(dateGroupedEvents).length > 0 &&
188 eventsCount > amSettings.general.itemsPerPage
189 "
190 :page-size="amSettings.general.itemsPerPage"
191 :pager-count="5"
192 layout="prev, pager, next"
193 :total="eventsCount"
194 :current-page="eventsPage"
195 @current-change="eventsPageChange"
196 />
197
198 <Event
199 v-if="eventVisibility"
200 :page-width="pageWidth"
201 :responsive-class="responsiveClass"
202 @close="closeEvent"
203 @save="saveEventCallback"
204 @duplicate="duplicateEvent"
205 ></Event>
206
207 <EventAttendees
208 v-if="!eventAttendeeVisibility && eventAttendeesVisibility"
209 :event="targetEvent"
210 :page-width="pageWidth"
211 :responsive-class="responsiveClass"
212 @close="closeEventAttendees"
213 @open-attendee="openAttendee"
214 ></EventAttendees>
215
216 <Attendee
217 v-if="eventAttendeeVisibility"
218 :visibility="eventAttendeeVisibility"
219 :title="
220 editAttendeeRecognition ? amLabels.event_edit_attendee : amLabels.event_add_attendee
221 "
222 :event="targetEvent"
223 :page-width="pageWidth"
224 :is-new="!editAttendeeRecognition"
225 @save="savedEventAttendee"
226 @close="closedEventAttendee"
227 ></Attendee>
228
229 <CancelPopup
230 v-if="originKey === 'capc'"
231 :visibility="targetBooking !== null"
232 :title="customizedLabels('cancelEvent').cancel_event"
233 :description="customizedLabels('cancelEvent').confirm_cancel_event"
234 :close-btn-text="customizedLabels('cancelEvent').close"
235 :confirm-btn-text="customizedLabels('cancelEvent').confirm"
236 :customized-options="customizedOptions('cancelEvent')"
237 :loading="waitingForCancelation"
238 @close="targetBooking = null"
239 @decline="targetBooking = null"
240 @confirm="cancelBooking"
241 >
242 </CancelPopup>
243
244 <QrCodeScanner
245 v-if="qrScannerVisibility"
246 v-model:visibility="qrScannerVisibility"
247 @update:visibility="(value) => (qrScannerVisibility = value)"
248 />
249 </template>
250 <Skeleton v-else></Skeleton>
251 </div>
252 </div>
253 </template>
254
255 <script setup>
256 // * import from Vue
257 import { ref, reactive, computed, inject, provide, onMounted, watch } from 'vue'
258
259 import { useElementSize } from '@vueuse/core'
260
261 // * Import from Vuex
262 import { useStore } from 'vuex'
263
264 // * Import from Libraries
265 import httpClient from '../../../../../plugins/axios'
266
267 // * Templates
268 import CancelPopup from '../../common/parts/CancelPopup.vue'
269 import CollapseCard from '../parts/CollapseCard/CollapseCard.vue'
270 import CabinetFilters from '../parts/Filters.vue'
271 import Skeleton from '../../common/parts/Skeleton.vue'
272 import QrCodeScanner from '../parts/QrCodeScanner.vue'
273
274 // * Composables
275 import {
276 getDateRange,
277 getFrontedFormattedDate,
278 getFrontedFormattedTime,
279 } from '../../../../../assets/js/common/date'
280 import {
281 useParsedEvents,
282 useEventBookingsPrice,
283 usePeriodsData,
284 useTicketsData,
285 useEventLocation,
286 useEventQrCodes,
287 } from '../../../../../assets/js/admin/event'
288 import { useFrontEvent } from '../../../../../assets/js/common/events'
289 import { useInitAttendee } from '../../../../../assets/js/common/attendees'
290 import { useCustomFieldsData } from '../../../../../assets/js/admin/booking'
291 import { useResponsiveClass } from '../../../../../assets/js/common/responsive'
292 import { useColorTransparency } from '../../../../../assets/js/common/colorManipulation'
293 import { useUrlParams } from '../../../../../assets/js/common/helper'
294 import moment from 'moment'
295 import EmptyState from '../parts/EmptyState.vue'
296 import AmAlert from '../../../../_components/alert/AmAlert.vue'
297 import AmButton from '../../../../_components/button/AmButton.vue'
298 import Event from '../Events/parts/Event.vue'
299 import EventAttendees from '../Events/parts/Attendees.vue'
300 import Attendee from '../Events/parts/Attendee.vue'
301 import { useScrollTo } from '../../../../../assets/js/common/scrollElements'
302 import IconComponent from '../../../../_components/icons/IconComponent.vue'
303 import { usePopulateSettings } from '../../../../../assets/js/common/settings'
304 import AmPagination from '../../../../_components/pagination/AmPagination.vue'
305
306 // * Store
307 let store = useStore()
308
309 // * Page Content width
310 let pageContainer = ref(null)
311
312 let { width: pageWidth } = useElementSize(pageContainer)
313
314 let responsiveClass = computed(() => {
315 return useResponsiveClass(pageWidth.value)
316 })
317
318 // * Plus icon
319 let plusIcon = {
320 components: { IconComponent },
321 template: `<IconComponent icon="plus"/>`,
322 }
323
324 // * Plugin Licence
325 let licence = inject('licence')
326
327 // * Root Settings
328 const amSettings = inject('settings')
329
330 let originKey = inject('originKey')
331
332 // * Customized form data
333 let amCustomize = inject('amCustomize')
334
335 // * Data in shortcode
336 const shortcodeData = inject('shortcodeData')
337
338 // * View-only mode for admin/manager in the employee panel
339 let employeePanelReadOnly = inject('employeePanelReadOnly', ref(false))
340
341 // * Qr Code Tickets visibility
342 let qrCodeTicketsVisibility = computed(() => {
343 return (
344 shortcodeData.value.cabinetType === 'customer' &&
345 (licence.isPro || licence.isDeveloper) &&
346 amSettings.featuresIntegrations.eTickets.enabled
347 )
348 })
349
350 // * labels
351 const labels = inject('labels')
352
353 // * local language short code
354 const localLanguage = inject('localLanguage')
355
356 // * if local lang is in settings lang
357 let langDetection = computed(() => amSettings.general.usedLanguages.includes(localLanguage.value))
358
359 // * Computed labels
360 let amLabels = computed(() => {
361 let computedLabels = reactive({ ...labels })
362
363 let customizedLabels = amCustomize.value.events.translations
364 if (customizedLabels) {
365 Object.keys(customizedLabels).forEach((labelKey) => {
366 if (customizedLabels[labelKey][localLanguage.value] && langDetection.value) {
367 computedLabels[labelKey] = customizedLabels[labelKey][localLanguage.value]
368 } else if (customizedLabels[labelKey].default) {
369 computedLabels[labelKey] = customizedLabels[labelKey].default
370 }
371 })
372 }
373 return computedLabels
374 })
375
376 provide('amLabels', amLabels)
377
378 // * Alert block
379 let alertContainer = ref(null)
380 let alertVisibility = ref(false)
381 let alertType = ref('success')
382
383 let alertMessage = ref('')
384
385 function closeAlert() {
386 alertVisibility.value = false
387 store.commit('cabinet/setPaymentLinkError', { value: false, type: 'event' })
388 }
389
390 function customizedLabels(step) {
391 let computedLabels = reactive({ ...labels })
392
393 let customizedLabels = amCustomize.value[step].translations
394 if (customizedLabels) {
395 Object.keys(customizedLabels).forEach((labelKey) => {
396 if (customizedLabels[labelKey][localLanguage.value] && langDetection.value) {
397 computedLabels[labelKey] = customizedLabels[labelKey][localLanguage.value]
398 } else if (customizedLabels[labelKey].default) {
399 computedLabels[labelKey] = customizedLabels[labelKey].default
400 }
401 })
402 }
403 return computedLabels
404 }
405
406 function customizedOptions(step) {
407 return amCustomize.value[step].options
408 }
409
410 let ready = computed(() => store.getters['entities/getReady'])
411
412 /********
413 * Form *
414 ********/
415 let props = defineProps({
416 loadBookingsCounter: {
417 type: Number,
418 default: 0,
419 },
420 })
421
422 // * Cabinet type
423 let cabinetType = inject('cabinetType')
424 store.commit('cabinetFilters/setDates', getDateRange(cabinetType.value))
425
426 // * Loading
427 let loading = computed(() => store.getters['cabinet/getEventsLoading'])
428
429 let empty = ref(false)
430
431 let targetBooking = ref(null)
432
433 let dateGroupedEvents = ref(null)
434
435 let eventsCount = ref(0)
436
437 let eventsPage = ref(1)
438
439 function eventsPageChange(page) {
440 eventsPage.value = page
441
442 getEvents(page)
443 }
444
445 function getEvents(page = 1) {
446 targetBooking.value = null
447
448 store.commit('cabinet/setEventsLoading', true)
449
450 let timeZone = store.getters['cabinet/getTimeZone']
451 let params = JSON.parse(JSON.stringify(store.getters['cabinetFilters/getEventsFilters']))
452 params.dates = params.dates.map((d) => moment(d).format('YYYY-MM-DD'))
453 params.timeZone = timeZone
454 params.source = 'cabinet-' + cabinetType.value
455 params.id = params.events
456 params.group = true
457 params.page = page
458 params.limit = amSettings.general.itemsPerPage
459
460 if (params.customers) {
461 params.customerId = params.customers
462 }
463
464 store.commit('auth/setLoadingEventsCounter', store.getters['auth/getLoadingEventsCounter'] + 1)
465
466 let loadingCounter = store.getters['auth/getLoadingEventsCounter']
467
468 httpClient
469 .get('/events', { params: useUrlParams(params) })
470 .then((response) => {
471 if (loadingCounter !== store.getters['auth/getLoadingEventsCounter']) {
472 return
473 }
474
475 store.dispatch('cabinetFilters/injectEventsOptions', response.data.data.events)
476
477 store.commit('auth/setPreloadedEvents', response.data.data.events)
478
479 eventsCount.value = response.data.data.count
480
481 store.commit('eventEntities/setEvents', response.data.data.events)
482 dateGroupedEvents.value = useParsedEvents(
483 response.data.data.events,
484 store.getters['cabinet/getTimeZone'],
485 store,
486 )
487 })
488 .catch((error) => {
489 if (
490 error?.response?.data?.data?.reauthorize !== undefined &&
491 error.response.data.data.reauthorize
492 ) {
493 store.dispatch('auth/logout')
494 }
495
496 console.log(error)
497 })
498 .finally(() => {
499 if (loadingCounter !== store.getters['auth/getLoadingEventsCounter']) {
500 return
501 }
502
503 store.commit('cabinet/setEventsLoading', false)
504 })
505 }
506
507 /*********
508 * Event *
509 *********/
510 function editEvent(event) {
511 store.commit('cabinet/setEventsLoading', true)
512
513 httpClient
514 .get('/events/' + event.id, {
515 params: {
516 source: 'cabinet-' + cabinetType.value,
517 timeZone: store.getters['cabinet/getTimeZone'],
518 },
519 })
520 .then((response) => {
521 store.commit('event/setEvent', useFrontEvent(store, response.data.data.event))
522
523 eventVisibility.value = true
524 })
525 .catch((error) => {
526 console.log(error)
527 })
528 .finally(() => {
529 store.commit('cabinet/setEventsLoading', false)
530 })
531 }
532
533 let eventVisibility = ref(false)
534
535 let eventAttendeesVisibility = ref(false)
536
537 let eventAttendeeVisibility = ref(false)
538
539 let editAttendeeRecognition = ref(false)
540
541 let attendeeOpenFromAttendees = ref(false)
542
543 let targetEvent = ref(null)
544
545 function duplicateEvent(event) {
546 store.commit('cabinet/setEventsLoading', true)
547
548 eventVisibility.value = false
549
550 setTimeout(() => {
551 store.commit('event/setEvent', event)
552
553 eventVisibility.value = true
554
555 store.commit('cabinet/setEventsLoading', false)
556 }, 500)
557 }
558
559 function saveEventCallback() {
560 alertVisibility.value = true
561 alertMessage.value = amLabels.value.event_saved
562
563 if (pageContainer.value && alertContainer.value) {
564 setTimeout(function () {
565 useScrollTo(pageContainer.value, alertContainer.value.$el, 0, 300)
566 }, 500)
567 }
568
569 getEvents()
570
571 closeEvent()
572 }
573
574 function addEvent() {
575 resetEvent()
576
577 eventVisibility.value = true
578 }
579
580 function addEventAttendee(event) {
581 targetEvent.value = event
582
583 resetEventAttendee(useInitAttendee(store, store.getters['attendee/getDefaultAttendee'], event))
584
585 eventAttendeeVisibility.value = true
586 }
587
588 function savedEventAttendee() {
589 getEvents(eventsPage.value)
590
591 closedEventAttendee()
592 }
593
594 function closedEventAttendee() {
595 eventAttendeeVisibility.value = false
596 editAttendeeRecognition.value = false
597 }
598
599 function listEventAttendees(event) {
600 targetEvent.value = event
601
602 eventAttendeesVisibility.value = true
603 }
604
605 function closeEventAttendees() {
606 targetEvent.value = null
607
608 getEvents(eventsPage.value)
609
610 resetEventAttendee(useInitAttendee(store, store.getters['attendee/getDefaultAttendee']))
611
612 eventAttendeesVisibility.value = false
613 attendeeOpenFromAttendees.value = false
614 }
615
616 function openAttendee(open) {
617 attendeeOpenFromAttendees.value = open
618 }
619
620 function resetEventAttendee(payload = {}) {
621 store.commit('attendee/setAttendee', payload)
622 }
623
624 function closeEvent() {
625 eventVisibility.value = false
626
627 resetEvent()
628 }
629
630 function resetEvent() {
631 store.commit('event/setEvent', {
632 bookings: [],
633 settings: usePopulateSettings(amSettings, store.getters['event/getSettings'], {}, {}),
634 })
635 }
636
637 let waitingForCancelation = ref(false)
638 function cancelBooking() {
639 waitingForCancelation.value = true
640 httpClient
641 .post(
642 '/bookings/cancel/' + targetBooking.value.id,
643 {
644 type: 'event',
645 source: 'cabinet-' + cabinetType.value,
646 },
647 {
648 params: { source: 'cabinet-' + cabinetType.value },
649 },
650 )
651 .then(() => {
652 targetBooking.value = null
653 getEvents()
654
655 alertVisibility.value = true
656 alertMessage.value = amLabels.value.event_canceled
657
658 if (pageContainer.value && alertContainer.value) {
659 setTimeout(function () {
660 useScrollTo(pageContainer.value, alertContainer.value.$el, 0, 300)
661 }, 500)
662 }
663 })
664 .catch((error) => {
665 if (!('data' in error.response.data) && 'message' in error.response.data) {
666 alertVisibility.value = true
667 alertMessage.value = error.response.data.message
668
669 if (pageContainer.value && alertContainer.value) {
670 setTimeout(function () {
671 useScrollTo(pageContainer.value, alertContainer.value.$el, 0, 300)
672 }, 500)
673 }
674 }
675 })
676 .finally(() => {
677 waitingForCancelation.value = false
678 })
679 }
680
681 watch(
682 () => props.loadBookingsCounter,
683 () => {
684 getEvents()
685 },
686 )
687
688 onMounted(() => {
689 getEvents()
690 })
691
692 // * Event oraganizer
693 function eventEmployees(evt) {
694 let arr = []
695 let employees = store.getters['entities/getEmployees']
696
697 if (evt.organizerId) {
698 const organizerEmployee = employees.find((item) => Number(item.id) === Number(evt.organizerId))
699 const organizerData = organizerEmployee || evt.organizer
700
701 if (organizerData) {
702 let organizer = { ...organizerData, rank: 'organizer' }
703
704 if (!evt.providers.length) return organizer
705
706 arr.push(organizer)
707 }
708 }
709
710 if (evt.providers.length) {
711 evt.providers.forEach((pro) => {
712 if (
713 Number(pro.id) !== Number(evt.organizerId) &&
714 employees.find((item) => Number(item.id) === Number(pro.id))
715 ) {
716 arr.push(employees.find((item) => Number(item.id) === Number(pro.id)))
717 }
718 })
719 }
720
721 return arr
722 }
723
724 // * No Show Function
725 function noShowData(noShowCount) {
726 if (noShowCount) {
727 if (noShowCount === 1) {
728 return {
729 class: 'am-no-show-1',
730 icon: 'no-show-1',
731 }
732 }
733 if (noShowCount === 2) {
734 return {
735 class: 'am-no-show-2',
736 icon: 'no-show-2',
737 }
738 }
739
740 if (noShowCount > 2) {
741 return {
742 class: 'am-no-show-3',
743 icon: 'no-show-3',
744 }
745 }
746 }
747
748 return {
749 class: '',
750 icon: '',
751 }
752 }
753 provide('noShowData', { noShowData })
754
755 // * Qr Scanner
756 let qrScannerVisibility = ref(false)
757
758 // * Colors
759 let amColors = inject('amColors')
760
761 let cssVars = computed(() => {
762 return {
763 '--am-c-cape-bgr': amColors.value.colorMainBgr,
764 '--am-c-cape-text': amColors.value.colorMainText,
765 '--am-c-cape-text-op70': useColorTransparency(amColors.value.colorMainText, 0.7),
766 '--am-c-cape-text-op25': useColorTransparency(amColors.value.colorMainText, 0.25),
767 '--am-c-cape-primary': amColors.value.colorPrimary,
768
769 '--am-c-cust-no1': amColors.value.colorMainText,
770 '--am-c-cust-no1-bgr': useColorTransparency(amColors.value.colorMainText, 0.1),
771 '--am-c-cust-no2': amColors.value.colorWarning,
772 '--am-c-cust-no2-bgr': useColorTransparency(amColors.value.colorWarning, 0.1),
773 '--am-c-cust-no3': amColors.value.colorError,
774 '--am-c-cust-no3-bgr': useColorTransparency(amColors.value.colorError, 0.1),
775 '--am-c-cust-text': amColors.value.colorMainText,
776 '--am-c-cust-link': useColorTransparency(amColors.value.colorMainText, 0.5),
777 }
778 })
779 </script>
780
781 <script>
782 export default {
783 name: 'CabinetEvents',
784 key: 'events',
785 }
786 </script>
787
788 <style lang="scss">
789 @mixin am-cabinet-events {
790 // cape -- cabinet panel events
791 .am-cape {
792 position: relative;
793
794 &__wrapper {
795 position: relative;
796 padding: 0 0 0 32px;
797
798 &:before {
799 content: '';
800 display: block;
801 width: 1px;
802 height: calc(100% - 4px);
803 position: absolute;
804 top: 4px;
805 left: 8px;
806 background-image:
807 linear-gradient(
808 180deg,
809 transparent,
810 transparent 50%,
811 var(--am-c-cape-bgr) 50%,
812 var(--am-c-cape-bgr) 100%
813 ),
814 linear-gradient(
815 180deg,
816 var(--am-c-cape-text-op25),
817 var(--am-c-cape-text-op25),
818 var(--am-c-cape-text-op25),
819 var(--am-c-cape-text-op25)
820 );
821 background-size:
822 3px 12px,
823 100% 20px;
824 }
825
826 &.am-no-border {
827 padding: 0;
828
829 &:before {
830 display: none;
831 }
832 }
833
834 &.am-rw-500 {
835 padding: 0;
836
837 &:before {
838 display: none;
839 }
840 }
841 }
842
843 &__date {
844 font-size: 15px;
845 font-weight: 500;
846 line-height: 1.6;
847 color: var(--am-c-cape-text-op70);
848 margin: 24px 0 8px;
849
850 &:before {
851 content: '';
852 display: block;
853 width: 16px;
854 height: 16px;
855 position: absolute;
856 top: 4px;
857 left: -32px;
858 background-color: var(--am-c-cape-text-op70);
859 border-radius: 50%;
860 z-index: 2;
861 }
862
863 &:after {
864 content: '';
865 display: block;
866 width: 16px;
867 height: 16px;
868 position: absolute;
869 top: 4px;
870 left: -32px;
871 background-color: var(--am-c-cape-bgr);
872 border-radius: 50%;
873 z-index: 1;
874 }
875
876 &.am-today {
877 &:before {
878 background-color: var(--am-c-cape-primary);
879 }
880 }
881
882 &.am-no-flag {
883 &:before,
884 &:after {
885 display: none;
886 }
887 }
888
889 &.am-rw-500 {
890 display: flex;
891 align-items: center;
892 &:before {
893 position: static;
894 margin-right: 8px;
895 }
896
897 &:after {
898 display: none;
899 }
900 }
901 }
902 }
903
904 .am-cap {
905 &__actions {
906 width: 100%;
907 display: flex;
908 justify-content: flex-end;
909 margin: 0 0 16px;
910 gap: 0 10px;
911
912 &.am-rw-420 {
913 justify-content: flex-start;
914 flex-wrap: wrap;
915 gap: 8px;
916
917 .am-button {
918 width: 100%;
919 }
920 }
921
922 .am-button {
923 .am-icon-plus,
924 .am-icon-scan-qr-code {
925 font-size: 24px;
926 }
927 }
928 }
929
930 &__alert {
931 margin: 0 0 16px;
932 .el-alert {
933 padding: 4px 12px 4px 0;
934 box-sizing: border-box;
935
936 &__content {
937 .el-alert__closebtn {
938 top: 50%;
939 transform: translateY(-50%);
940 }
941 }
942
943 &__title {
944 display: flex;
945 align-items: center;
946 font-size: 16px;
947 line-height: 1.5;
948
949 .am-icon-checkmark-circle-full {
950 font-size: 28px;
951 line-height: 1;
952 color: var(--am-c-alerts-bgr);
953 }
954 }
955 }
956 }
957 }
958
959 .am-fs__main-content.am-cap {
960 // am - amelia
961 // capei - cabinet panel event item
962 &.am-capei-main {
963 height: calc(100% - 148px);
964 }
965
966 &.am-cape-main {
967 padding: 0;
968
969 .am-cape-main__inner {
970 display: block;
971 padding: 16px 32px;
972
973 &.am-rw- {
974 &480 {
975 padding: 16px;
976 }
977 }
978 }
979 }
980 }
981
982 // Customer
983 .am-capei-customer {
984 &__name {
985 display: flex;
986 align-items: center;
987 gap: 4px;
988 font-size: 18px;
989 font-weight: 500;
990 line-height: 1.555556;
991 color: var(--am-c-cust-text);
992
993 &[class*='am-no-show-']*='am-no-show-'] {
994 border: 1px solid var(--am-c-no-show);
995 padding: 0 8px 0 4px;
996 border-radius: 6px;
997 background-color: var(--am-c-no-show-bgr);
998 }
999
1000 [class^='am-icon-']^='am-icon-'] {
1001 font-size: 28px;
1002 line-height: 1;
1003 color: var(--am-c-no-show);
1004 }
1005
1006 &.am-no-show-1 {
1007 --am-c-no-show: var(--am-c-cust-no1);
1008 --am-c-no-show-bgr: var(--am-c-cust-no1-bgr);
1009 }
1010
1011 &.am-no-show-2 {
1012 --am-c-no-show: var(--am-c-cust-no2);
1013 --am-c-no-show-bgr: var(--am-c-cust-no2-bgr);
1014 }
1015
1016 &.am-no-show-3 {
1017 --am-c-no-show: var(--am-c-cust-no3);
1018 --am-c-no-show-bgr: var(--am-c-cust-no3-bgr);
1019 }
1020 }
1021
1022 // Customer Email and Phone
1023 &__data {
1024 display: flex;
1025 align-items: center;
1026 gap: 0 4px;
1027
1028 &.am-rw {
1029 &-420 {
1030 width: 100%;
1031 }
1032 }
1033
1034 &-wrapper {
1035 width: 100%;
1036 display: flex;
1037 align-items: center;
1038 gap: 8px;
1039
1040 &.am-rw {
1041 &-420 {
1042 flex-wrap: wrap;
1043 gap: 4px;
1044 }
1045 }
1046 }
1047
1048 a {
1049 font-size: 14px;
1050 font-weight: 400;
1051 line-height: 1;
1052 color: var(--am-c-cust-link);
1053 text-decoration: none;
1054 }
1055
1056 [class^='am-icon-']^='am-icon-'] {
1057 font-size: 28px;
1058 line-height: 1;
1059 color: var(--am-c-cust-text);
1060 }
1061 }
1062 }
1063 }
1064
1065 .amelia-v2-booking #amelia-container {
1066 @include am-cabinet-events;
1067 }
1068 </style>
1069