PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.4.8
Booking for Appointments and Events Calendar – Amelia v2.4.8
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 / CustomerPanel / CustomerPanel.vue
ameliabooking / v3 / src / views / public / Cabinet / CustomerPanel Last commit date
CustomerPanel.vue 1 month ago
CustomerPanel.vue
1181 lines
1 <template>
2 <template v-if="!amFonts.customFontSelected">
3 <link rel="preconnect" href="https://fonts.googleapis.com" />
4 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
5 <link
6 rel="stylesheet"
7 type="text/css"
8 :href="`${baseUrls.wpAmeliaPluginURL}v3/src/assets/scss/common/fonts/font.css`"
9 media="all"
10 />
11 </template>
12 <div
13 id="amelia-container"
14 ref="ameliaContainer"
15 class="am-cap__wrapper"
16 :class="[{ 'am-collapsed': sidebarCollapsed }, { 'am-auth': !authenticated }]"
17 :style="cssVars"
18 @keydown.esc="menuVisibility = false"
19 >
20 <Auth v-if="!authenticated"></Auth>
21 <template v-if="authenticated">
22 <SideBar
23 v-if="sidebarVisibility"
24 class="am-fs-sb"
25 :class="[{ 'am-collapsed': sidebarCollapsed }, { 'am-rtl': isRtl }]"
26 :style="{
27 width: !sidebarCollapsed ? '240px' : '72px',
28 paddingBottom: `${sidebarFooterHeight + 16}px`,
29 }"
30 >
31 <template #step-list>
32 <nav
33 class="am-fs-sb__page-wrapper am-fs-sb__page-wrapper__cabinet"
34 :aria-label="amLabels.cabinet_navigation || 'Cabinet navigation'"
35 >
36 <template v-for="(step, index) in sidebarSteps" :key="step.key">
37 <div
38 v-if="step.key !== 'packages' || store.getters['entities/getPackages'].length"
39 class="am-fs-sb__page"
40 :class="{
41 selected: pageKey === step.key,
42 'is-disabled': isStepDisabled(step),
43 }"
44 role="button"
45 :tabindex="isStepDisabled(step) ? -1 : 0"
46 :aria-disabled="isStepDisabled(step)"
47 :aria-current="pageKey === step.key ? 'page' : undefined"
48 :aria-label="step.label"
49 @click="onSidebarStepInteraction(step, index)"
50 @keydown.enter.prevent="onSidebarStepInteraction(step, index)"
51 @keydown.space.prevent="onSidebarStepInteraction(step, index)"
52 >
53 <div class="am-fs-sb__page-inner" :class="{ 'am-collapsed': sidebarCollapsed }">
54 <div class="am-fs-sb__page-icon" aria-hidden="true">
55 <span :class="`am-icon-${step.icon}`"></span>
56 </div>
57 <transition name="fade">
58 <p
59 v-if="!sidebarCollapsed"
60 class="am-fs-sb__page-heading"
61 :class="[sidebarCollapseItemsClass, { 'am-rtl': isRtl }]"
62 >
63 {{ step.label }}
64 </p>
65 </transition>
66 <transition name="fade">
67 <div
68 v-if="!sidebarCollapsed"
69 class="am-fs-sb__page-indicator"
70 :class="[sidebarCollapseItemsClass, { 'am-rtl': isRtl }]"
71 aria-hidden="true"
72 >
73 <span
74 :class="isRtl ? 'am-icon-arrow-big-left' : 'am-icon-arrow-big-right'"
75 ></span>
76 </div>
77 </transition>
78 </div>
79 </div>
80 <div
81 v-if="index === 0 && sidebarSteps[0].key === 'profile'"
82 class="am-fs-sb__page-divider"
83 ></div>
84 </template>
85 </nav>
86 </template>
87 <template #support-info>
88 <div ref="sidebarFooterRef" class="am-fs-sb__footer">
89 <div
90 class="am-fs-sb__page"
91 role="button"
92 tabindex="0"
93 :aria-expanded="!sidebarCollapsed"
94 :aria-label="amLabels.toggle_sidebar"
95 @click="toggleSidebar"
96 @keydown.enter.prevent="toggleSidebar"
97 @keydown.space.prevent="toggleSidebar"
98 >
99 <div class="am-fs-sb__page-inner">
100 <div class="am-fs-sb__page-icon" aria-hidden="true">
101 <span class="am-icon-dashboard"></span>
102 </div>
103 <transition name="fade">
104 <p
105 v-if="!sidebarCollapsed"
106 class="am-fs-sb__page-heading"
107 :class="sidebarCollapseItemsClass"
108 >
109 {{ amLabels.toggle_sidebar }}
110 </p>
111 </transition>
112 </div>
113 </div>
114 <div class="am-fs-sb__page-divider"></div>
115 <div
116 class="am-fs-sb__page"
117 role="button"
118 tabindex="0"
119 :aria-label="amLabels.log_out"
120 @click="logout"
121 @keydown.enter.prevent="logout"
122 @keydown.space.prevent="logout"
123 >
124 <div class="am-fs-sb__page-inner">
125 <div class="am-fs-sb__page-icon" aria-hidden="true">
126 <span class="am-icon-logout"></span>
127 </div>
128 <transition name="fade">
129 <p
130 v-if="!sidebarCollapsed"
131 class="am-fs-sb__page-heading"
132 :class="sidebarCollapseItemsClass"
133 >
134 {{ amLabels.log_out }}
135 </p>
136 </transition>
137 </div>
138 </div>
139 </div>
140 </template>
141 </SideBar>
142 <MainContent :max-width="786" :old-responsive="false">
143 <template #header>
144 <MainPanelHeader :ready="ready">
145 <template #default>
146 <div class="am-caph">
147 <div class="am-caph__text">
148 {{ header }}
149 </div>
150 <div
151 v-if="!sidebarVisibility"
152 class="am-caph__menu"
153 role="button"
154 tabindex="0"
155 :aria-expanded="menuVisibility"
156 :aria-controls="menuDialogId"
157 :aria-label="amLabels.open_menu || 'Open menu'"
158 @click="toggleMenu"
159 @keydown.enter.prevent="toggleMenu"
160 @keydown.space.prevent="toggleMenu"
161 >
162 <span class="am-icon-menu" aria-hidden="true"></span>
163 </div>
164 <TimeZoneSelect
165 v-if="entitiesReady && sidebarVisibility && timeZoneSelectVisibility"
166 />
167 <MenuSlideDialog
168 :id="menuDialogId"
169 :menu-items="sidebarSteps"
170 :monitor="pageKey"
171 :visibility="menuVisibility"
172 :custom-css="cssSideMenu"
173 :customized-labels="amLabels"
174 position="right"
175 :width="240"
176 @update:visibility="(e) => (menuVisibility = e)"
177 @click="menuSelection"
178 @logout="menuLogout"
179 />
180 </div>
181 </template>
182 </MainPanelHeader>
183 </template>
184 <template #step>
185 <component
186 :is="pagesObj[pageKey]"
187 class="am-fs__main-content"
188 :load-bookings-counter="loadBookingsCounter"
189 ></component>
190 </template>
191 </MainContent>
192 </template>
193 </div>
194 </template>
195
196 <script setup>
197 // * Form construction
198 import MainContent from '../../../common/SbsFormConstruction/MainContent/MainContent.vue'
199 import SideBar from '../../../common/SbsFormConstruction/SideBar/SideBar.vue'
200 import MainPanelHeader from '../../../common/SbsFormConstruction/MainContent/parts/MainPanelHeader.vue'
201
202 // * Dedicated Components
203 import TimeZoneSelect from '../common/parts/TimeZoneSelect.vue'
204 import MenuSlideDialog from '../../../common/SbsFormConstruction/MenuSlideDialog/MenuSlideDialog.vue'
205
206 // * import from Vue
207 import {
208 ref,
209 reactive,
210 watch,
211 provide,
212 inject,
213 markRaw,
214 computed,
215 onMounted,
216 readonly,
217 onBeforeMount,
218 onBeforeUnmount,
219 } from 'vue'
220
221 // * import from Vuex
222 import { useStore } from 'vuex'
223
224 // * import composable
225 import { defaultCustomizeSettings } from '../../../../assets/js/common/defaultCustomize.js'
226 import { useColorTransparency } from '../../../../assets/js/common/colorManipulation.js'
227 import { useCurrentTimeZone } from '../../../../assets/js/common/helper'
228 import { useElementSize } from '@vueuse/core'
229 import { useFrontendEmployeeServiceList } from '../../../../assets/js/common/employee'
230
231 // * Form Component Collection
232 import Auth from '../common/Authentication/Auth.vue'
233 import Profile from '../common/Profile/Profile.vue'
234 import EmployeeProfile from '../common/Profile/EmployeeProfile.vue'
235 import Appointments from '../common/Appointments/Appointments.vue'
236 import Events from '../common/Events/Events.vue'
237 import Packages from '../common/Packages/Packages.vue'
238
239 // * Import from Libraries
240 import { useCookies } from 'vue3-cookies'
241 import { usePopulateSettings } from '../../../../assets/js/common/settings'
242 import { useInitAttendee } from '../../../../assets/js/common/attendees'
243
244 // * Vars
245 const vueCookies = useCookies()['cookies']
246
247 const store = useStore()
248
249 // * RTL - right to left
250 let isRtl = computed(() => store.getters['getIsRtl'])
251
252 // * Data in shortcode
253 const shortcodeData = inject('shortcodeData')
254
255 // * Authenticated
256 let authenticated = computed(() => {
257 return store.getters['auth/getAuthenticated']
258 })
259
260 watch(authenticated, (current) => {
261 if (current) {
262 store.commit('auth/setNewPassword', '')
263 store.commit('auth/setConfirmPassword', '')
264 }
265 })
266
267 // * Cabinet Type
268 let cabinetType = ref(shortcodeData.value.cabinetType === 'employee' ? 'provider' : 'customer')
269 provide('cabinetType', cabinetType)
270
271 // * Plugin Licence
272 let licence = inject('licence')
273
274 // * Origin key
275 let originKey = computed(() => {
276 if (cabinetType.value === 'provider') return 'cape'
277 return 'capc'
278 })
279 provide('originKey', originKey)
280
281 // * Component reference
282 let ameliaContainer = ref(null)
283
284 // * Plugin wrapper width
285 let { width: containerWidth } = useElementSize(ameliaContainer)
286 provide('containerWidth', containerWidth)
287
288 // * Root Settings
289 const amSettings = inject('settings')
290
291 let pageKey = ref(
292 shortcodeData.value.appointments || !shortcodeData.value.events ? 'appointments' : 'events',
293 )
294
295 // * Customize
296 const amCustomize = computed(() => {
297 return amSettings.customizedData && originKey.value in amSettings.customizedData
298 ? amSettings.customizedData[originKey.value]
299 : defaultCustomizeSettings[originKey.value]
300 })
301 provide('amCustomize', amCustomize)
302
303 // * Fonts
304 const amFonts = ref(
305 amSettings.customizedData ? amSettings.customizedData.fonts : defaultCustomizeSettings.fonts,
306 )
307 provide('amFonts', amFonts)
308
309 // * labels
310 const labels = inject('labels')
311
312 provide('amLabels', labels)
313
314 // * local language short code
315 const localLanguage = inject('localLanguage')
316
317 // * if local lang is in settings lang
318 let langDetection = computed(() => amSettings.general.usedLanguages.includes(localLanguage.value))
319
320 // * Computed labels
321 let amLabels = computed(() => {
322 let computedLabels = reactive({ ...labels })
323
324 if (amSettings.customizedData) {
325 Object.keys(amCustomize.value).forEach((stepKey) => {
326 if (stepKey !== 'colors' && amCustomize.value[stepKey].translations) {
327 let customizedLabels = amCustomize.value[stepKey].translations
328 Object.keys(customizedLabels).forEach((labelKey) => {
329 if (customizedLabels[labelKey][localLanguage.value] && langDetection.value) {
330 computedLabels[labelKey] = customizedLabels[labelKey][localLanguage.value]
331 } else if (customizedLabels[labelKey].default) {
332 computedLabels[labelKey] = customizedLabels[labelKey].default
333 }
334 })
335 }
336 })
337 }
338 return computedLabels
339 })
340
341 // * Activate custom font styles
342 function activateCustomFontStyles() {
343 let head = document.head || document.getElementsByTagName('head')[0]
344 if (head.querySelector('#amCustomFont')) {
345 head.querySelector('#amCustomFont').remove()
346 }
347
348 let css = `@font-face {font-family: 'comic'; src: url(http://localhost/amelia-test/wp-content/uploads/amelia/fonts/comic.woff);}`
349 let style = document.createElement('style')
350 head.appendChild(style)
351 style.setAttribute('type', 'text/css')
352 style.setAttribute('id', 'amCustomFont')
353 style.appendChild(document.createTextNode(css))
354 }
355
356 if (amFonts.value.customFontSelected) activateCustomFontStyles()
357
358 // * Customized Options
359 let customizedOptions = computed(() => {
360 if (pageKey.value === 'packages') return amCustomize.value.packagesList.options
361 return amCustomize.value[pageKey.value].options
362 })
363
364 let timeZoneSelectVisibility = computed(() => {
365 let key = pageKey.value
366
367 if (cabinetType.value === 'customer') {
368 return key !== 'profile' ? customizedOptions.value.timeZone.visibility : true
369 }
370
371 return key === 'appointments' || key === 'events'
372 ? customizedOptions.value.timeZone.visibility
373 : true
374 })
375
376 // * Mobile menu
377 let menuVisibility = ref(false)
378 const menuDialogId = 'am-customer-panel-mobile-menu'
379
380 function toggleMenu() {
381 menuVisibility.value = !menuVisibility.value
382 }
383
384 // * Form Sidebar Collapse
385 let sidebarCollapsed = ref(false)
386 provide('sidebarCollapsed', readonly(sidebarCollapsed))
387
388 function toggleSidebar() {
389 sidebarCollapsed.value = !sidebarCollapsed.value
390 }
391
392 let sidebarCollapseItemsClass = ref('')
393
394 watch(sidebarCollapsed, (current) => {
395 if (current) {
396 setTimeout(() => {
397 sidebarCollapseItemsClass.value = 'am-collapsed'
398 }, 1000)
399 } else {
400 sidebarCollapseItemsClass.value = ''
401 }
402 })
403
404 let sidebarFooterRef = ref(null)
405 let sidebarFooterHeight = ref(0)
406
407 // * Form Sidebar Visibility
408 let sidebarVisibility = computed(() => containerWidth.value > 480)
409
410 onMounted(() => {
411 if (sidebarFooterRef.value) {
412 setTimeout(() => {
413 sidebarFooterHeight.value = sidebarFooterRef.value.offsetHeight
414 }, 200)
415 }
416
417 setTimeout(() => {
418 sidebarCollapsed.value =
419 containerWidth.value <= 600 ? true : amCustomize.value.sidebar.options.toggle.visibility
420 }, 1300)
421 })
422
423 /*************
424 * Watchers *
425 *************/
426 const handleResize = () => {
427 if (containerWidth.value <= 600) {
428 sidebarCollapsed.value = true
429 } else {
430 sidebarCollapsed.value = amCustomize.value.sidebar.options.toggle.visibility
431 }
432 }
433
434 window.addEventListener('resize', handleResize)
435
436 onBeforeUnmount(() => {
437 window.removeEventListener('resize', handleResize)
438 })
439
440 watch(containerWidth, (current) => {
441 menuVisibility.value = current > 481 ? false : menuVisibility.value
442 })
443
444 function populateFilterOptions() {
445 store.dispatch(
446 'cabinetFilters/injectServiceOptions',
447 store.getters['entities/getServices'].filter((s) => s.show).map((i) => i.id),
448 )
449
450 store.dispatch(
451 'cabinetFilters/injectProviderOptions',
452 cabinetType.value === 'customer'
453 ? store.getters['entities/getEmployees'].filter((e) => e.show).map((i) => i.id)
454 : [],
455 )
456
457 store.dispatch(
458 'cabinetFilters/injectLocationOptions',
459 store.getters['entities/getLocations'].map((i) => i.id),
460 )
461 }
462
463 const entitiesReady = computed(() => store.getters['entities/getReady'])
464 watch(entitiesReady, (ready) => {
465 if (ready) {
466 if (shortcodeData.value.cabinetType === 'employee' && store.getters['employee/getId']) {
467 let employee = store.getters['employee/getEmployee']
468
469 store.commit(
470 'employee/setServiceList',
471 useFrontendEmployeeServiceList(store, employee.serviceList),
472 )
473
474 store.commit('entities/setEmployees', [
475 store.getters['entities/getEmployee'](store.getters['employee/getId']),
476 ])
477 }
478
479 populateFilterOptions()
480 }
481 })
482
483 // * Root Urls
484 const baseUrls = inject('baseUrls')
485
486 let entities = [
487 'employees',
488 'categories',
489 'locations',
490 'packages',
491 'entitiesRelations',
492 'customFields',
493 ]
494
495 if (shortcodeData.value.cabinetType === 'employee') {
496 entities = entities.concat(['taxes', 'spaces', 'tags', 'settings'])
497 }
498
499 store.dispatch('entities/getEntities', {
500 types: entities,
501 licence: 'basic',
502 loadEntities: true,
503 showHidden: true,
504 isPanel: true,
505 })
506
507 // * Data loaded
508 let ready = computed(() => store.getters['getReady'])
509
510 // * Panel Fluid Pages
511 let pagesObj = ref({
512 profile: markRaw(shortcodeData.value.cabinetType === 'employee' ? EmployeeProfile : Profile),
513 })
514
515 if (shortcodeData.value.appointments || !shortcodeData.value.events) {
516 pagesObj.value.appointments = markRaw(Appointments)
517 }
518
519 if (shortcodeData.value.events || !shortcodeData.value.appointments) {
520 pagesObj.value.events = markRaw(Events)
521 }
522
523 if (
524 amSettings.featuresIntegrations.packages.enabled &&
525 (shortcodeData.value.appointments || !shortcodeData.value.events) &&
526 (licence.isPro || licence.isDeveloper)
527 ) {
528 pagesObj.value.packages = markRaw(Packages)
529 }
530
531 // * Logged in user Amelia role (provider, admin or manager)
532 let loggedInUserRole = computed(() => store.getters['auth/getProfile']?.type)
533
534 // * Profile page (and all its tabs) is only available for providers in the employee panel
535 let profilePageVisibility = computed(
536 () => shortcodeData.value.cabinetType !== 'employee' || loggedInUserRole.value === 'provider',
537 )
538
539 // * Admin/manager logged into the employee panel may only view appointments and events,
540 // * they must not be able to add, edit, reschedule or otherwise manipulate them.
541 let employeePanelReadOnly = computed(
542 () =>
543 shortcodeData.value.cabinetType === 'employee' &&
544 !!loggedInUserRole.value &&
545 loggedInUserRole.value !== 'provider',
546 )
547 provide('employeePanelReadOnly', employeePanelReadOnly)
548
549 // * Roles exposed to the panel - all write capabilities are disabled for read-only users
550 let panelRoles = computed(() => {
551 if (!employeePanelReadOnly.value) {
552 return amSettings.roles
553 }
554
555 return {
556 ...amSettings.roles,
557 allowWriteAppointments: false,
558 allowWriteEvents: false,
559 allowWriteCustomers: false,
560 }
561 })
562
563 // * Re-provide settings to the panel subtree so the read-only role overrides take effect
564 // * everywhere the cabinet reads `settings.roles.*`, without touching the global settings.
565 const panelSettings = new Proxy(amSettings, {
566 get(target, prop) {
567 if (prop === 'roles') {
568 return panelRoles.value
569 }
570
571 return target[prop]
572 },
573 })
574 provide('settings', panelSettings)
575
576 // * Array of Sidebar steps
577 const sidebarSteps = computed(() => {
578 const steps = []
579
580 if (profilePageVisibility.value) {
581 const profile = store.getters['auth/getProfile']
582
583 let profileLabel = ''
584 if (profile) {
585 if (originKey.value === 'cape' || amCustomize.value.profile.options.lastName.visibility) {
586 profileLabel = `${profile.firstName} ${profile.lastName}`
587 } else {
588 profileLabel = `${profile.firstName}`
589 }
590 }
591
592 steps.push({
593 key: 'profile',
594 icon: 'user',
595 pageLabel: amLabels.value.my_profile,
596 label: profileLabel,
597 })
598 }
599
600 if (shortcodeData.value.appointments || !shortcodeData.value.events) {
601 steps.push({
602 key: 'appointments',
603 icon: 'service',
604 label: amLabels.value.appointments,
605 })
606 }
607
608 if (shortcodeData.value.events || !shortcodeData.value.appointments) {
609 steps.push({
610 key: 'events',
611 icon: 'star-outline',
612 label: amLabels.value.events,
613 })
614 }
615
616 if (
617 amSettings.featuresIntegrations.packages.enabled &&
618 shortcodeData.value.cabinetType === 'customer' &&
619 (shortcodeData.value.appointments || !shortcodeData.value.events) &&
620 (licence.isPro || licence.isDeveloper)
621 ) {
622 steps.push({
623 key: 'packages',
624 icon: 'shipment',
625 label: amLabels.value.packages,
626 })
627 }
628
629 return steps
630 })
631
632 provide('sidebarSteps', sidebarSteps)
633
634 let sidebarIndex = computed(() => {
635 const index = sidebarSteps.value.findIndex((a) => a.key === pageKey.value)
636 return index === -1 ? 0 : index
637 })
638
639 function sidebarSelection(step, index) {
640 if (shortcodeData.value.cabinetType === 'employee') {
641 if (step.key !== 'appointments') {
642 store.commit('appointment/resetAppointment', {
643 providerId: store.getters['auth/getProfile'].id,
644 categoryId: null,
645 serviceId: null,
646 locationId: null,
647 })
648 }
649
650 if (step.key !== 'events') {
651 store.commit('event/setEvent', {
652 settings: usePopulateSettings(amSettings, store.getters['event/getSettings'], {}, {}),
653 })
654
655 store.commit(
656 'attendee/setAttendee',
657 useInitAttendee(store, store.getters['attendee/getDefaultAttendee']),
658 )
659 }
660 }
661
662 if (step.key === 'appointments' || step.key === 'events') {
663 populateFilterOptions()
664 }
665
666 pageKey.value = step.key
667 store.commit('cabinetFilters/setResetFilters')
668 }
669
670 function isStepDisabled(step) {
671 return (
672 shortcodeData.value.cabinetType === 'employee' &&
673 shortcodeData.value.profile &&
674 step.key === 'profile'
675 )
676 }
677
678 function onSidebarStepInteraction(step, index) {
679 if (isStepDisabled(step)) return
680 sidebarSelection(step, index)
681 }
682
683 function menuSelection(obj) {
684 if (
685 shortcodeData.value.cabinetType === 'employee' &&
686 shortcodeData.value.profile &&
687 obj.step.key === 'profile'
688 )
689 return
690 sidebarSelection(obj.step, obj.index)
691 store.commit('cabinetFilters/setResetFilters')
692 }
693
694 function logout() {
695 store.dispatch('auth/logout')
696 }
697
698 function menuLogout() {
699 menuVisibility.value = false
700 logout()
701 }
702
703 let loadBookingsCounter = ref(0)
704
705 function bookingsCounterChanger() {
706 loadBookingsCounter.value++
707 }
708
709 let header = computed(() => {
710 switch (sidebarSteps.value[sidebarIndex.value].key) {
711 case 'profile':
712 return sidebarSteps.value[sidebarIndex.value].pageLabel
713
714 case 'appointments':
715 if (store.getters['appointment/getActive'] && !store.getters['appointment/getId']) {
716 return amLabels.value.new_appointment
717 } else if (store.getters['appointment/getActive'] && store.getters['appointment/getId']) {
718 return amLabels.value.edit_appointment
719 } else {
720 return sidebarSteps.value[sidebarIndex.value].label
721 }
722
723 case 'events':
724 if (store.getters['attendee/getActive'] && !store.getters['attendee/getId']) {
725 return amLabels.value.event_add_attendee
726 } else if (store.getters['attendee/getActive'] && store.getters['attendee/getId']) {
727 return amLabels.value.event_edit_attendee
728 } else if (store.getters['event/getActive'] && !store.getters['event/getId']) {
729 return amLabels.value.new_event
730 } else if (store.getters['event/getActive'] && store.getters['event/getId']) {
731 return amLabels.value.edit_event
732 } else {
733 return sidebarSteps.value[sidebarIndex.value].label
734 }
735
736 default:
737 return sidebarSteps.value[sidebarIndex.value].label
738 }
739 })
740
741 provide('bookingsCounterChanger', {
742 bookingsCounterChanger,
743 })
744
745 const adminTimeZone = inject('timeZone')
746
747 onBeforeMount(() => {
748 if (!amSettings.general.showClientTimeZone) {
749 let initialTimeZone = adminTimeZone.value
750
751 if (vueCookies.get('ameliaUserTimeZone')) {
752 initialTimeZone = vueCookies.get('ameliaUserTimeZone')
753 }
754
755 store.commit('cabinet/setTimeZone', initialTimeZone)
756 }
757
758 if (!store.getters['cabinet/getTimeZone'])
759 store.commit('cabinet/setTimeZone', useCurrentTimeZone())
760 })
761
762 // * Colors block
763 let amColors = computed(() => {
764 return amCustomize.value.colors
765 })
766 provide('amColors', amColors)
767
768 let cssVars = computed(() => {
769 return {
770 '--am-c-primary': amColors.value.colorPrimary,
771 '--am-c-success': amColors.value.colorSuccess,
772 '--am-c-error': amColors.value.colorError,
773 '--am-c-warning': amColors.value.colorWarning,
774 '--am-c-main-bgr': amColors.value.colorMainBgr,
775 '--am-c-main-heading-text': amColors.value.colorMainHeadingText,
776 '--am-c-main-text': amColors.value.colorMainText,
777 '--am-c-main-text-op10': useColorTransparency(amColors.value.colorMainText, 0.1),
778 '--am-c-sb-bgr': amColors.value.colorSbBgr,
779 '--am-c-sb-text': amColors.value.colorSbText,
780 '--am-c-inp-bgr': amColors.value.colorInpBgr,
781 '--am-c-inp-border': amColors.value.colorInpBorder,
782 '--am-c-inp-text': amColors.value.colorInpText,
783 '--am-c-inp-placeholder': amColors.value.colorInpPlaceHolder,
784 '--am-c-drop-bgr': amColors.value.colorDropBgr,
785 '--am-c-drop-text': amColors.value.colorDropText,
786 '--am-c-btn-prim': amColors.value.colorBtnPrim,
787 '--am-c-btn-prim-text': amColors.value.colorBtnPrimText,
788 '--am-c-btn-sec': amColors.value.colorBtnSec,
789 '--am-c-btn-sec-text': amColors.value.colorBtnSecText,
790 '--am-c-btn-danger': amColors.value.colorBtnDanger,
791 '--am-c-btn-danger-text': amColors.value.colorBtnDangerText,
792 '--am-c-skeleton-op20': useColorTransparency(amColors.value.colorMainText, 0.2),
793 '--am-c-skeleton-op60': useColorTransparency(amColors.value.colorMainText, 0.6),
794 '--am-c-skeleton-sb-op20': useColorTransparency(amColors.value.colorSbText, 0.2),
795 '--am-c-skeleton-sb-op60': useColorTransparency(amColors.value.colorSbText, 0.6),
796 '--am-c-scroll-op30': useColorTransparency(amColors.value.colorPrimary, 0.3),
797 '--am-c-scroll-op10': useColorTransparency(amColors.value.colorPrimary, 0.1),
798 '--am-font-family': amFonts.value.fontFamily,
799
800 // css properties
801 '--am-rad-inp': '6px',
802 '--am-fs-inp': '15px',
803 // -mw- max width
804 // -brad- border-radius
805 '--am-mw-main': sidebarVisibility.value
806 ? sidebarCollapsed.value
807 ? '858px'
808 : '1024px'
809 : '520px',
810 '--am-brad-main': sidebarVisibility.value ? '0 0.5rem 0.5rem 0' : '0.5rem',
811 }
812 })
813
814 let cssSideMenu = computed(() => {
815 return {
816 '--am-c-msd-bgr': amColors.value.colorSbBgr,
817 '--am-c-msd-text': amColors.value.colorSbText,
818 '--am-c-msd-text-op05': useColorTransparency(amColors.value.colorSbText, 0.05),
819 '--am-c-msd-text-op10': useColorTransparency(amColors.value.colorSbText, 0.1),
820 '--am-c-msd-text-op60': useColorTransparency(amColors.value.colorSbText, 0.6),
821 }
822 })
823 </script>
824
825 <script>
826 export default {
827 name: 'CustomerPanel',
828 }
829 </script>
830
831 <style lang="scss">
832 @import '../../../../assets/scss/public/overides/overides';
833 @import '../../../../assets/scss/common/reset/reset';
834 @import '../../../../assets/scss/common/icon-fonts/style';
835 @import '../../../../assets/scss/common/skeleton/skeleton.scss';
836 @import '../../../../assets/scss/common/empty-state/_empty-state-mixin.scss';
837 @import '../../../../assets/scss/common/transitions/_transitions-mixin.scss';
838
839 :root {
840 // Colors
841 // shortcuts
842 // -c- color
843 // -bgr- background
844 // -prim- primary
845 // -sec- secondary
846 // primitive colors
847 --am-c-primary: #{$blue-1000};
848 --am-c-success: #{$green-1000};
849 --am-c-error: #{$red-900};
850 --am-c-warning: #{$yellow-1000};
851 // main container colors - right part of the form
852 --am-c-main-bgr: #{$am-white};
853 --am-c-main-heading-text: #{$shade-800};
854 --am-c-main-text: #{$shade-900};
855 // sidebar container colors - left part of the form
856 --am-c-sb-bgr: #17295a;
857 --am-c-sb-text: #{$am-white};
858 // input global colors - usage input, textarea, checkbox, radio button, select input, adv select input
859 --am-c-inp-bgr: #{$am-white};
860 --am-c-inp-border: #{$shade-250};
861 --am-c-inp-text: #{$shade-900};
862 --am-c-inp-placeholder: #{$shade-500};
863 // dropdown global colors - usage select dropdown, adv select dropdown
864 --am-c-drop-bgr: #{$am-white};
865 --am-c-drop-text: #{$shade-1000};
866 // button global colors
867 --am-c-btn-prim: #{$blue-900};
868 --am-c-btn-prim-text: #{$am-white};
869 --am-c-btn-sec: #{$am-white};
870 --am-c-btn-sec-text: #{$shade-900};
871
872 // Properties
873 // shortcuts
874 // -h- height
875 // -fs- font size
876 // -rad- border radius
877 --am-h-inp: 40px;
878 --am-fs-inp: 15px;
879 --am-rad-inp: 6px;
880 --am-fs-label: 15px;
881 --am-fs-btn: 15px;
882
883 // Font
884 --am-font-family: 'Amelia Roboto', sans-serif;
885 }
886
887 .amelia-v2-booking {
888 background-color: transparent;
889
890 #amelia-container {
891 background-color: transparent;
892
893 * {
894 font-family: var(--am-font-family), sans-serif;
895 font-style: initial;
896 box-sizing: border-box;
897 word-break: break-word;
898 }
899
900 // cap - cabinet panel
901 &.am-cap {
902 // Container Wrapper
903 &__wrapper {
904 display: flex;
905 justify-content: center;
906 max-width: var(--am-mw-main);
907 width: 100%;
908 height: 700px;
909 margin: 100px auto;
910 border-radius: 8px;
911 box-shadow: 0 30px 40px rgba(0, 0, 0, 0.12);
912 transition: max-width 0.3s ease-in-out;
913
914 &.am-auth {
915 box-shadow: unset;
916 height: auto;
917 }
918
919 &.am-collapsed {
920 transition-delay: 1s;
921 }
922
923 // overrides for element plus components
924 // tabs
925 .el-tabs {
926 display: flex;
927 flex-direction: column;
928 gap: 16px 0;
929
930 &__nav {
931 gap: 0 40px;
932
933 &-wrap {
934 &:after {
935 background-color: var(--am-c-main-text-op10);
936 }
937
938 &.is-scrollable {
939 padding: 0 24px;
940 }
941 }
942
943 &-scroll {
944 overflow: visible;
945 }
946
947 &-next,
948 &-prev {
949 color: var(--am-c-main-text);
950 top: 11px;
951 background-color: var(--am-c-main-bgr);
952 z-index: 10;
953 }
954 }
955
956 &__active-bar {
957 background-color: var(--am-c-primary);
958 }
959
960 &__item {
961 padding: 0;
962 line-height: 40px;
963 color: var(--am-c-main-text);
964 transition: color 0.2s ease-in-out;
965 padding: 0 4px;
966 border-radius: 4px;
967 font-size: var(--am-fs-label);
968
969 &.is-active {
970 color: var(--am-c-primary);
971 }
972
973 &:focus-visible {
974 box-shadow: 0 0 0 2px var(--am-c-primary) inset;
975 }
976
977 &:hover {
978 color: var(--am-c-primary);
979 }
980
981 &.is-focus {
982 color: var(--am-c-main-text);
983
984 &.is-active {
985 color: var(--am-c-primary);
986 }
987 }
988 }
989
990 &__content {
991 overflow: unset;
992 position: static;
993 }
994 }
995
996 // collapse
997 .el-collapse-item {
998 &__wrap {
999 overflow: visible;
1000 }
1001
1002 &__header {
1003 border: none;
1004 font-size: var(--am-fs-label);
1005 line-height: 1.6;
1006 height: unset;
1007 }
1008 }
1009
1010 * {
1011 font-family: var(--am-font-family), sans-serif;
1012 box-sizing: border-box;
1013 }
1014
1015 .am-fs-sb__page {
1016 max-height: 40px;
1017
1018 &:focus:not(:focus-visible) {
1019 outline: none;
1020 }
1021
1022 &:focus-visible {
1023 outline: 2px solid var(--am-c-primary);
1024 outline-offset: -2px;
1025 }
1026
1027 &.is-disabled {
1028 opacity: 0.6;
1029 cursor: not-allowed;
1030 }
1031 }
1032
1033 .am-caph__menu {
1034 &:focus-visible {
1035 outline: 2px solid var(--am-c-primary);
1036 outline-offset: 2px;
1037 border-radius: 4px;
1038 }
1039 }
1040 }
1041 }
1042
1043 // cap - cabinet panel for inner elements
1044 .am-cap {
1045 &__header {
1046 display: flex;
1047 align-items: center;
1048 justify-content: space-between;
1049
1050 .am-select-wrapper {
1051 max-width: 250px;
1052 }
1053 }
1054
1055 .el-collapse-item {
1056 &__wrap {
1057 background-color: transparent;
1058 }
1059
1060 &__content {
1061 background-color: transparent;
1062 }
1063 }
1064
1065 .el-form-item {
1066 &__label {
1067 display: inline-flex;
1068 align-items: center;
1069 justify-content: flex-start;
1070 gap: 4px;
1071 }
1072 }
1073
1074 .am-tooltip {
1075 &__trigger {
1076 color: var(--am-c-main-text);
1077 cursor: help;
1078 }
1079 }
1080 }
1081
1082 .am-asi {
1083 .el-form {
1084 &-item {
1085 display: block;
1086 font-family: var(--am-font-family), sans-serif;
1087 font-size: var(--am-fs-label);
1088 margin-bottom: 30px;
1089
1090 &__label {
1091 flex: 0 0 auto;
1092 text-align: left;
1093 font-size: var(--am-fs-label);
1094 line-height: 1.3;
1095 color: var(--am-c-main-text);
1096 box-sizing: border-box;
1097 margin: 0;
1098
1099 &:before {
1100 color: var(--am-c-error);
1101 }
1102 }
1103
1104 &__content {
1105 display: flex;
1106 flex-wrap: wrap;
1107 align-items: center;
1108 flex: 1;
1109 position: relative;
1110 font-size: var(--am-fs-inp);
1111 min-width: 0;
1112 }
1113
1114 &__error {
1115 font-size: 12px;
1116 color: var(--am-c-error);
1117 padding-top: 4px;
1118 }
1119 }
1120 }
1121 }
1122 }
1123 }
1124
1125 // * Date picker override
1126 .am-cap__datepicker-popper {
1127 @media only screen and (max-width: 660px) {
1128 left: 0 !important;
1129 width: 100%;
1130
1131 .el-picker-panel.el-date-range-picker {
1132 width: 100%;
1133
1134 .el-picker-panel__body {
1135 min-width: unset;
1136
1137 @media only screen and (max-width: 570px) {
1138 display: flex;
1139 flex-direction: column;
1140
1141 .el-date-range-picker__content {
1142 width: 100%;
1143
1144 &.is-left {
1145 border: none;
1146 }
1147 }
1148 }
1149 }
1150 }
1151 }
1152 }
1153
1154 // * Customer select options styles
1155 .am-cap-cust__popper {
1156 .el-select-dropdown__item {
1157 --am-pad-select-option: 8px 8px 0;
1158 }
1159
1160 .am-cap__cust-option {
1161 display: flex;
1162 flex-direction: column;
1163 align-items: flex-start;
1164 padding-bottom: 8px;
1165 border-bottom: 1px solid var(--am-c-option-text-op15);
1166
1167 &__heading {
1168 font-size: 15px;
1169 font-weight: 500;
1170 color: var(--am-c-option-text);
1171 }
1172
1173 &__inner {
1174 font-size: 12px;
1175 font-weight: 400;
1176 color: var(--am-c-option-text-op65);
1177 }
1178 }
1179 }
1180 </style>
1181