PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.4.6
Booking for Appointments and Events Calendar – Amelia v2.4.6
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 month ago Events.vue 1 month ago
Events.vue
1064 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 if (employees.find((item) => item.id === evt.organizerId)) {
699 let organizer = {
700 ...employees.find((item) => item.id === evt.organizerId),
701 ...{ rank: 'organizer' },
702 }
703 if (!evt.providers.length) return organizer
704 arr.push(organizer)
705 }
706 }
707
708 if (evt.providers.length) {
709 evt.providers.forEach((pro) => {
710 if (pro.id !== evt.organizerId && employees.find((item) => item.id === pro.id)) {
711 arr.push(employees.find((item) => item.id === pro.id))
712 }
713 })
714 }
715
716 return arr
717 }
718
719 // * No Show Function
720 function noShowData(noShowCount) {
721 if (noShowCount) {
722 if (noShowCount === 1) {
723 return {
724 class: 'am-no-show-1',
725 icon: 'no-show-1',
726 }
727 }
728 if (noShowCount === 2) {
729 return {
730 class: 'am-no-show-2',
731 icon: 'no-show-2',
732 }
733 }
734
735 if (noShowCount > 2) {
736 return {
737 class: 'am-no-show-3',
738 icon: 'no-show-3',
739 }
740 }
741 }
742
743 return {
744 class: '',
745 icon: '',
746 }
747 }
748 provide('noShowData', { noShowData })
749
750 // * Qr Scanner
751 let qrScannerVisibility = ref(false)
752
753 // * Colors
754 let amColors = inject('amColors')
755
756 let cssVars = computed(() => {
757 return {
758 '--am-c-cape-bgr': amColors.value.colorMainBgr,
759 '--am-c-cape-text': amColors.value.colorMainText,
760 '--am-c-cape-text-op70': useColorTransparency(amColors.value.colorMainText, 0.7),
761 '--am-c-cape-text-op25': useColorTransparency(amColors.value.colorMainText, 0.25),
762 '--am-c-cape-primary': amColors.value.colorPrimary,
763
764 '--am-c-cust-no1': amColors.value.colorMainText,
765 '--am-c-cust-no1-bgr': useColorTransparency(amColors.value.colorMainText, 0.1),
766 '--am-c-cust-no2': amColors.value.colorWarning,
767 '--am-c-cust-no2-bgr': useColorTransparency(amColors.value.colorWarning, 0.1),
768 '--am-c-cust-no3': amColors.value.colorError,
769 '--am-c-cust-no3-bgr': useColorTransparency(amColors.value.colorError, 0.1),
770 '--am-c-cust-text': amColors.value.colorMainText,
771 '--am-c-cust-link': useColorTransparency(amColors.value.colorMainText, 0.5),
772 }
773 })
774 </script>
775
776 <script>
777 export default {
778 name: 'CabinetEvents',
779 key: 'events',
780 }
781 </script>
782
783 <style lang="scss">
784 @mixin am-cabinet-events {
785 // cape -- cabinet panel events
786 .am-cape {
787 position: relative;
788
789 &__wrapper {
790 position: relative;
791 padding: 0 0 0 32px;
792
793 &:before {
794 content: '';
795 display: block;
796 width: 1px;
797 height: calc(100% - 4px);
798 position: absolute;
799 top: 4px;
800 left: 8px;
801 background-image:
802 linear-gradient(
803 180deg,
804 transparent,
805 transparent 50%,
806 var(--am-c-cape-bgr) 50%,
807 var(--am-c-cape-bgr) 100%
808 ),
809 linear-gradient(
810 180deg,
811 var(--am-c-cape-text-op25),
812 var(--am-c-cape-text-op25),
813 var(--am-c-cape-text-op25),
814 var(--am-c-cape-text-op25)
815 );
816 background-size:
817 3px 12px,
818 100% 20px;
819 }
820
821 &.am-no-border {
822 padding: 0;
823
824 &:before {
825 display: none;
826 }
827 }
828
829 &.am-rw-500 {
830 padding: 0;
831
832 &:before {
833 display: none;
834 }
835 }
836 }
837
838 &__date {
839 font-size: 15px;
840 font-weight: 500;
841 line-height: 1.6;
842 color: var(--am-c-cape-text-op70);
843 margin: 24px 0 8px;
844
845 &:before {
846 content: '';
847 display: block;
848 width: 16px;
849 height: 16px;
850 position: absolute;
851 top: 4px;
852 left: -32px;
853 background-color: var(--am-c-cape-text-op70);
854 border-radius: 50%;
855 z-index: 2;
856 }
857
858 &:after {
859 content: '';
860 display: block;
861 width: 16px;
862 height: 16px;
863 position: absolute;
864 top: 4px;
865 left: -32px;
866 background-color: var(--am-c-cape-bgr);
867 border-radius: 50%;
868 z-index: 1;
869 }
870
871 &.am-today {
872 &:before {
873 background-color: var(--am-c-cape-primary);
874 }
875 }
876
877 &.am-no-flag {
878 &:before,
879 &:after {
880 display: none;
881 }
882 }
883
884 &.am-rw-500 {
885 display: flex;
886 align-items: center;
887 &:before {
888 position: static;
889 margin-right: 8px;
890 }
891
892 &:after {
893 display: none;
894 }
895 }
896 }
897 }
898
899 .am-cap {
900 &__actions {
901 width: 100%;
902 display: flex;
903 justify-content: flex-end;
904 margin: 0 0 16px;
905 gap: 0 10px;
906
907 &.am-rw-420 {
908 justify-content: flex-start;
909 flex-wrap: wrap;
910 gap: 8px;
911
912 .am-button {
913 width: 100%;
914 }
915 }
916
917 .am-button {
918 .am-icon-plus,
919 .am-icon-scan-qr-code {
920 font-size: 24px;
921 }
922 }
923 }
924
925 &__alert {
926 margin: 0 0 16px;
927 .el-alert {
928 padding: 4px 12px 4px 0;
929 box-sizing: border-box;
930
931 &__content {
932 .el-alert__closebtn {
933 top: 50%;
934 transform: translateY(-50%);
935 }
936 }
937
938 &__title {
939 display: flex;
940 align-items: center;
941 font-size: 16px;
942 line-height: 1.5;
943
944 .am-icon-checkmark-circle-full {
945 font-size: 28px;
946 line-height: 1;
947 color: var(--am-c-alerts-bgr);
948 }
949 }
950 }
951 }
952 }
953
954 .am-fs__main-content.am-cap {
955 // am - amelia
956 // capei - cabinet panel event item
957 &.am-capei-main {
958 height: calc(100% - 148px);
959 }
960
961 &.am-cape-main {
962 padding: 0;
963
964 .am-cape-main__inner {
965 display: block;
966 padding: 16px 32px;
967
968 &.am-rw- {
969 &480 {
970 padding: 16px;
971 }
972 }
973 }
974 }
975 }
976
977 // Customer
978 .am-capei-customer {
979 &__name {
980 display: flex;
981 align-items: center;
982 gap: 4px;
983 font-size: 18px;
984 font-weight: 500;
985 line-height: 1.555556;
986 color: var(--am-c-cust-text);
987
988 &[class*='am-no-show-']*='am-no-show-'] {
989 border: 1px solid var(--am-c-no-show);
990 padding: 0 8px 0 4px;
991 border-radius: 6px;
992 background-color: var(--am-c-no-show-bgr);
993 }
994
995 [class^='am-icon-']^='am-icon-'] {
996 font-size: 28px;
997 line-height: 1;
998 color: var(--am-c-no-show);
999 }
1000
1001 &.am-no-show-1 {
1002 --am-c-no-show: var(--am-c-cust-no1);
1003 --am-c-no-show-bgr: var(--am-c-cust-no1-bgr);
1004 }
1005
1006 &.am-no-show-2 {
1007 --am-c-no-show: var(--am-c-cust-no2);
1008 --am-c-no-show-bgr: var(--am-c-cust-no2-bgr);
1009 }
1010
1011 &.am-no-show-3 {
1012 --am-c-no-show: var(--am-c-cust-no3);
1013 --am-c-no-show-bgr: var(--am-c-cust-no3-bgr);
1014 }
1015 }
1016
1017 // Customer Email and Phone
1018 &__data {
1019 display: flex;
1020 align-items: center;
1021 gap: 0 4px;
1022
1023 &.am-rw {
1024 &-420 {
1025 width: 100%;
1026 }
1027 }
1028
1029 &-wrapper {
1030 width: 100%;
1031 display: flex;
1032 align-items: center;
1033 gap: 8px;
1034
1035 &.am-rw {
1036 &-420 {
1037 flex-wrap: wrap;
1038 gap: 4px;
1039 }
1040 }
1041 }
1042
1043 a {
1044 font-size: 14px;
1045 font-weight: 400;
1046 line-height: 1;
1047 color: var(--am-c-cust-link);
1048 text-decoration: none;
1049 }
1050
1051 [class^='am-icon-']^='am-icon-'] {
1052 font-size: 28px;
1053 line-height: 1;
1054 color: var(--am-c-cust-text);
1055 }
1056 }
1057 }
1058 }
1059
1060 .amelia-v2-booking #amelia-container {
1061 @include am-cabinet-events;
1062 }
1063 </style>
1064