PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 1.2.20
Booking for Appointments and Events Calendar – Amelia v1.2.20
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 / admin / customize / pages / CustomizeEmployeePanel.vue
ameliabooking / v3 / src / views / admin / customize / pages Last commit date
CustomizeCatalog.vue 3 years ago CustomizeCustomerPanel.vue 2 years ago CustomizeEmployeePanel.vue 1 year ago CustomizeEventCalendar.vue 1 year ago CustomizeEventList.vue 1 year ago CustomizeSearch.vue 4 years ago CustomizeStepNew.vue 2 years ago CustomizeStepOld.vue 4 years ago
CustomizeEmployeePanel.vue
619 lines
1 <template>
2 <template v-if="!amCustomize.fonts.customFontSelected">
3 <link rel="preconnect" href="https://fonts.googleapis.com">
4 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
5 <link rel="stylesheet" :href="`${baseUrls.wpAmeliaPluginURL}v3/src/assets/scss/common/fonts/font.css`" type="text/css" media="all">
6 </template>
7 <div
8 id="amelia-container"
9 ref="ameliaContainer"
10 class="am-cap__wrapper"
11 :class="[{'am-collapsed': sidebarCollapsed}, {'am-auth': pagesType === 'auth'}]"
12 :style="cssVars"
13 >
14 <template v-if="pagesType === 'auth'">
15 <component :is="stepsArray[stepIndex]"></component>
16 </template>
17 <template v-if="pagesType === 'panel'">
18 <SideBar
19 v-if="sidebarVisibility"
20 class="am-fs-sb"
21 :class="{'am-collapsed': sidebarCollapsed}"
22 :style="{width: !sidebarCollapsed ? '240px' : '72px', paddingBottom: `${sidebarFooterHeight + 16}px` }"
23 >
24 <template #step-list>
25 <div class="am-fs-sb__page-wrapper">
26 <template
27 v-for="(step, index) in sidebarSteps"
28 :key="step.key"
29 >
30 <div
31 class="am-fs-sb__page"
32 :class="[{'selected': stepName === step.key}, {'selected': (stepName === 'packagesList' || stepName === 'packageAppointmentsList') && step.key === 'packages'}]"
33 >
34 <div
35 class="am-fs-sb__page-inner"
36 :class="{'am-collapsed': sidebarCollapsed}"
37 >
38 <div class="am-fs-sb__page-icon">
39 <span :class="`am-icon-${step.icon}`"></span>
40 </div>
41 <transition name="fade">
42 <p
43 v-if="!sidebarCollapsed"
44 class="am-fs-sb__page-heading"
45 :class="sidebarCollapseItemsClass"
46 >
47 {{ step.label }}
48 </p>
49 </transition>
50 <transition name="fade">
51 <div
52 v-if="!sidebarCollapsed"
53 class="am-fs-sb__page-indicator"
54 :class="sidebarCollapseItemsClass"
55 >
56 <span class="am-icon-arrow-big-right"></span>
57 </div>
58 </transition>
59 </div>
60 </div>
61 <div
62 v-if="index === 0"
63 class="am-fs-sb__page-divider"
64 ></div>
65 </template>
66 </div>
67 </template>
68 <template #support-info>
69 <div
70 ref="sidebarFooterRef"
71 class="am-fs-sb__footer"
72 >
73 <div
74 class="am-fs-sb__page"
75 @click="sidebarCollapsed = !sidebarCollapsed"
76 >
77 <div class="am-fs-sb__page-inner">
78 <div class="am-fs-sb__page-icon">
79 <span class="am-icon-dashboard"></span>
80 </div>
81 <transition name="fade">
82 <p
83 v-if="!sidebarCollapsed"
84 class="am-fs-sb__page-heading"
85 :class="sidebarCollapseItemsClass"
86 >
87 {{ labelsDisplay('toggle_sidebar', 'sidebar') }}
88 </p>
89 </transition>
90 </div>
91 </div>
92 <div class="am-fs-sb__page-divider"></div>
93 <div class="am-fs-sb__page">
94 <div class="am-fs-sb__page-inner">
95 <div class="am-fs-sb__page-icon">
96 <span class="am-icon-logout"></span>
97 </div>
98 <transition name="fade">
99 <p
100 v-if="!sidebarCollapsed"
101 class="am-fs-sb__page-heading"
102 :class="sidebarCollapseItemsClass"
103 >
104 {{ labelsDisplay('log_out', 'sidebar') }}
105 </p>
106 </transition>
107 </div>
108 </div>
109 </div>
110 </template>
111 </SideBar>
112 <MainContent
113 :max-width="786"
114 :old-responsive="false"
115 >
116 <template #header>
117 <MainPanelHeader>
118 <template #default>
119 <div class="am-caph">
120 <div class="am-caph__text">
121 {{ sidebarSteps[stepIndex+1].label }}
122 </div>
123 <div
124 v-if="!sidebarVisibility"
125 class="am-cap__menu"
126 @click="() => menuVisibility = !menuVisibility"
127 >
128 <span class="am-icon-menu"></span>
129 </div>
130 <TimeZoneSelect
131 v-if="sidebarVisibility && stepName !== 'profile' && customizedOptions.timeZone.visibility"
132 />
133 <MenuSlideDialog
134 :menu-items="sidebarSteps"
135 :monitor="stepName"
136 :visibility="menuVisibility"
137 :custom-css="cssSideMenu"
138 :width="240"
139 :customized-labels="globalStepLabels('sidebar')"
140 position="right"
141 @update:visibility="(e) => menuVisibility = e"
142 @logout="menuLogout"
143 />
144 </div>
145 </template>
146 </MainPanelHeader>
147 </template>
148 <template #step>
149 <component
150 :is="stepsArray[stepIndex]"
151 class="am-fs__main-content"
152 ></component>
153 </template>
154 </MainContent>
155 </template>
156 </div>
157 </template>
158
159 <script setup>
160 // * Form construction
161 import MainContent from "../../../common/SbsFormConstruction/MainContent/MainContent.vue";
162 import SideBar from "../../../common/SbsFormConstruction/SideBar/SideBar.vue";
163 import MainPanelHeader from "../../../common/SbsFormConstruction/MainContent/parts/MainPanelHeader.vue";
164
165 // * Form Component Collection
166 // authentication
167 import SignIn from '../steps/Cabinet/common/Authentication/SignIn.vue'
168 import SendAccessLink from '../steps/Cabinet/common/Authentication/SendAccessLink.vue'
169 import SendAccessLinkSuccess from '../steps/Cabinet/common/Authentication/SendAccessLinkSuccess.vue'
170 import SetPass from '../steps/Cabinet/common/Authentication/SetPass.vue'
171 // panel
172 import Appointments from "../steps/Cabinet/common/Appointments/Appointments.vue";
173 import Events from "../steps/Cabinet/common/Events/Events.vue";
174
175 // * Dedicated Components
176 import TimeZoneSelect from "../steps/Cabinet/common/parts/TimeZoneSelect.vue";
177 import MenuSlideDialog from "../../../common/SbsFormConstruction/MenuSlideDialog/MenuSlideDialog.vue";
178
179 // * import from Vue
180 import {
181 ref,
182 provide,
183 inject,
184 markRaw,
185 computed,
186 onBeforeMount,
187 onMounted,
188 readonly,
189 watchEffect
190 } from 'vue'
191
192 // * + this has to be tested for responsive usage
193 // import { useElementSize } from "@vueuse/core";
194
195 // * import composable
196 import {
197 defaultCustomizeSettings
198 } from '../../../../assets/js/common/defaultCustomize.js'
199 import {
200 useColorTransparency
201 } from '../../../../assets/js/common/colorManipulation.js'
202 import {usePopulateMultiDimensionalObject} from "../../../../assets/js/common/objectAndArrayManipulation";
203
204 let { pageNameHandler } = inject('headerFunctionality', {
205 pageNameHandler: () => 'Step-by-Step Booking Form'
206 })
207
208 pageNameHandler('Employee Panel')
209
210 // * Plugin Licence
211 // let licence = inject('licence')
212
213 // * Step index
214 let stepIndex = inject('stepIndex')
215
216 let pagesType = inject('pagesType')
217
218 const amLabels = inject('labels')
219 let amTranslations = inject('translations')
220 let langKey = inject('langKey')
221 let stepName = inject('stepName')
222 let pageRenderKey = inject('pageRenderKey')
223 let amCustomize = inject('customize')
224
225 let amFonts = computed(() => {
226 return amCustomize.value.fonts
227 })
228 provide('amFonts', amFonts)
229
230 // * Origin key
231 let originKey = ref('cape')
232 provide('originKey', originKey)
233
234 // * Cabinet Type
235 let cabinetType = ref('customer')
236 provide('cabinetType', cabinetType)
237
238 // * Autehtication steps
239 let signIn = markRaw(SignIn)
240 let sendAccessLink = markRaw(SendAccessLink)
241 let sendAccessLinkSuccess = markRaw(SendAccessLinkSuccess)
242 let setPass = markRaw(SetPass)
243
244 let authFlow = ref([
245 signIn,
246 sendAccessLink,
247 sendAccessLinkSuccess,
248 setPass
249 ])
250
251 // * Panel steps
252 let appointments = markRaw(Appointments)
253 let events = markRaw(Events)
254
255 let panelFlow = ref([
256 appointments,
257 events,
258 ])
259
260 let stepsArray = computed(() => {
261 if (pagesType.value === 'auth') return authFlow.value
262
263 return panelFlow.value
264 })
265
266 watchEffect(() => {
267 stepName.value = stepsArray.value[stepIndex.value].key
268 })
269
270 // * Component reference
271 let ameliaContainer = ref(null)
272
273 // * Plugin wrapper width
274 let containerWidth = ref()
275 provide('containerWidth', containerWidth)
276
277 // * Empty state
278 // let empty = ref(false)
279
280 // * Mobile menu
281 let menuVisibility = ref(false)
282
283 function menuLogout () {
284 menuVisibility.value = false
285 }
286
287 // * Form Sidebar Collapse
288 let sidebarCollapsed = ref(false)
289 provide('sidebarCollapsed', readonly(sidebarCollapsed))
290
291 let sidebarCollapseItemsClass = ref('')
292
293 watchEffect(() => {
294 if (sidebarCollapsed.value) {
295 setTimeout(() => {
296 sidebarCollapseItemsClass.value = 'am-collapsed'
297 }, 1000)
298 } else {
299 sidebarCollapseItemsClass.value = ''
300 }
301 })
302
303 let toggleSidebar = computed(() => {
304 return amCustomize.value.cape.sidebar.options.toggle.visibility
305 })
306
307 watchEffect( () => {
308 sidebarCollapsed.value = toggleSidebar.value
309 })
310
311 let sidebarFooterRef = ref(null)
312 let sidebarFooterHeight = ref(0)
313
314 // * Form Sidebar Visibility
315 let sidebarVisibility = ref(true)
316
317 onMounted(() => {
318 if(sidebarFooterRef.value) {
319 setTimeout(() => {
320 sidebarFooterHeight.value = sidebarFooterRef.value.offsetHeight
321 }, 200)
322 }
323
324 if (ameliaContainer.value) {
325 containerWidth.value = ameliaContainer.value.offsetWidth
326 sidebarCollapsed.value = !toggleSidebar.value ? ameliaContainer.value.offsetWidth <= 600 : toggleSidebar.value
327 sidebarVisibility.value = ameliaContainer.value.offsetWidth > 480
328 }
329 })
330
331 // * window resize listener
332 window.addEventListener('resize', resize);
333 // * resize function
334 function resize() {
335 if (ameliaContainer.value) {
336 containerWidth.value = ameliaContainer.value.offsetWidth
337 sidebarCollapsed.value = !toggleSidebar.value ? ameliaContainer.value.offsetWidth <= 600 : toggleSidebar.value
338 sidebarVisibility.value = ameliaContainer.value.offsetWidth > 480
339 menuVisibility.value = ameliaContainer.value.offsetWidth > 481 ? false : menuVisibility.value
340 }
341 }
342
343 // * Root Urls
344 const baseUrls = inject('baseUrls')
345
346 // * Array of Sidebar steps
347 const sidebarSteps = computed(() =>
348 [{
349 key: 'profile',
350 icon: 'user',
351 pageLabel: amLabels.my_profile,
352 label: 'John Doe',
353 },
354 {
355 key: 'appointments',
356 icon: 'service',
357 label: labelsDisplay('appointments', 'appointments'),
358 },
359 {
360 key: 'events',
361 icon: 'star-outline',
362 label: labelsDisplay('events', 'events'),
363 }]
364 )
365 provide('sidebarSteps', sidebarSteps)
366
367 let sidebarIndex = ref(0)
368
369 onMounted(() => {
370 sidebarIndex.value = sidebarSteps.value.findIndex(a => a.key === stepName.value)
371
372 if (amCustomize.value.fonts.customFontSelected) {
373 activateCustomFontStyles()
374 }
375 })
376
377 // * implementation of saved labels into amTranslation object
378 let stepKey = ref('')
379
380 function savedLabelsImplementation (labelObj) {
381 Object.keys(labelObj).forEach((labelKey) => {
382 if (labelKey in amCustomize.value[pageRenderKey.value][stepKey.value].translations) {
383 labelObj[labelKey] = {...labelObj[labelKey], ...amCustomize.value[pageRenderKey.value][stepKey.value].translations[labelKey]}
384 }
385 })
386 }
387
388 function activateCustomFontStyles () {
389 let head = document.head || document.getElementsByTagName('head')[0]
390 if (head.querySelector('#amCustomFont')) {
391 head.querySelector('#amCustomFont').remove()
392 }
393
394 let css = `@font-face {font-family: '${amCustomize.value.fonts.fontFamily}'; src: url(${amCustomize.value.fonts.fontUrl});}`
395 let style = document.createElement('style')
396 head.appendChild(style)
397 style.setAttribute('type', 'text/css')
398 style.setAttribute('id', 'amCustomFont')
399 style.appendChild(document.createTextNode(css))
400 }
401
402 /**
403 * Lifecycle Hooks
404 */
405 onBeforeMount(() => {
406 window.scrollTo({
407 top: 0,
408 left: 0,
409 behavior: 'smooth'
410 })
411 Object.keys(amCustomize.value[pageRenderKey.value]).forEach(step => {
412 if (step !== 'colors' && amCustomize.value[pageRenderKey.value][step].translations) {
413 stepKey.value = step
414 usePopulateMultiDimensionalObject('labels', amTranslations[pageRenderKey.value][step], savedLabelsImplementation)
415 }
416 })
417 })
418
419 // * Label computed function
420 function labelsDisplay (label, stepKey) {
421 let computedLabel = computed(() => {
422 return amCustomize.value[pageRenderKey.value][stepKey].translations
423 && amCustomize.value[pageRenderKey.value][stepKey].translations[label]
424 && amCustomize.value[pageRenderKey.value][stepKey].translations[label][langKey.value]
425 ? amCustomize.value[pageRenderKey.value][stepKey].translations[label][langKey.value]
426 : amLabels[label]
427 })
428
429 return computedLabel.value
430 }
431
432 // * Step labels passed to inner components
433 function globalStepLabels (step) {
434 let stepLabels = {}
435 let customizedLabels = step ? amCustomize.value[pageRenderKey.value][step].translations : null
436 if (customizedLabels && Object.keys(customizedLabels)) {
437 Object.keys(amCustomize.value[pageRenderKey.value][step].translations).forEach(label => {
438 stepLabels[label] = amCustomize.value[pageRenderKey.value][step].translations[label][langKey.value]
439 })
440 } else {
441 stepLabels = {}
442 }
443
444 return stepLabels
445 }
446
447 // * Custmozes Options
448 let customizedOptions = computed(() => {
449 if (stepName.value === 'packageAppointmentsList') return amCustomize.value[pageRenderKey.value].packagesList.options
450 return amCustomize.value[pageRenderKey.value][stepName.value].options
451 })
452
453 onBeforeMount(() => {})
454
455 // * Colors block
456 let amColors = computed(() => {
457 return amCustomize.value[pageRenderKey.value] ? amCustomize.value[pageRenderKey.value].colors : defaultCustomizeSettings[pageRenderKey.value].colors
458 })
459 provide('amColors', amColors);
460
461 let cssVars = computed(() => {
462 return {
463 '--am-c-primary': amColors.value.colorPrimary,
464 '--am-c-success': amColors.value.colorSuccess,
465 '--am-c-error': amColors.value.colorError,
466 '--am-c-warning': amColors.value.colorWarning,
467 '--am-c-main-bgr': amColors.value.colorMainBgr,
468 '--am-c-main-heading-text': amColors.value.colorMainHeadingText,
469 '--am-c-main-text': amColors.value.colorMainText,
470 '--am-c-sb-bgr': amColors.value.colorSbBgr,
471 '--am-c-sb-text': amColors.value.colorSbText,
472 '--am-c-inp-bgr': amColors.value.colorInpBgr,
473 '--am-c-inp-border': amColors.value.colorInpBorder,
474 '--am-c-inp-text': amColors.value.colorInpText,
475 '--am-c-inp-placeholder': amColors.value.colorInpPlaceHolder,
476 '--am-c-drop-bgr': amColors.value.colorDropBgr,
477 '--am-c-drop-text': amColors.value.colorDropText,
478 '--am-c-btn-prim': amColors.value.colorBtnPrim,
479 '--am-c-btn-prim-text': amColors.value.colorBtnPrimText,
480 '--am-c-btn-sec': amColors.value.colorBtnSec,
481 '--am-c-btn-sec-text': amColors.value.colorBtnSecText,
482 '--am-c-skeleton-op20': useColorTransparency(amColors.value.colorMainText, 0.2),
483 '--am-c-skeleton-op60': useColorTransparency(amColors.value.colorMainText, 0.6),
484 '--am-c-skeleton-sb-op20': useColorTransparency(amColors.value.colorSbText, 0.2),
485 '--am-c-skeleton-sb-op60': useColorTransparency(amColors.value.colorSbText, 0.6),
486 '--am-font-family': amFonts.value.fontFamily,
487
488 // css properties
489 '--am-rad-input': '6px',
490 '--am-fs-input': '15px',
491 // -mw- max width
492 // -brad- border-radius
493 '--am-mw-main': sidebarVisibility.value ? sidebarCollapsed.value ? '858px' : '1024px' : '520px',
494 '--am-brad-main': sidebarVisibility.value ? '0 0.5rem 0.5rem 0' : '0.5rem'
495 }
496 })
497
498 let cssSideMenu = computed(() => {
499 return {
500 '--am-c-msd-bgr': amColors.value.colorSbBgr,
501 '--am-c-msd-text': amColors.value.colorSbText,
502 '--am-c-msd-text-op05': useColorTransparency(amColors.value.colorSbText, 0.05),
503 '--am-c-msd-text-op10': useColorTransparency(amColors.value.colorSbText, 0.1),
504 '--am-c-msd-text-op60': useColorTransparency(amColors.value.colorSbText, 0.6),
505 }
506 })
507 </script>
508
509 <script>
510
511 export default {
512 name: "CustomerPanel",
513 }
514 </script>
515
516 <style lang="scss">
517 @import '../../../../assets/scss/public/overides/overides';
518 @import '../../../../assets/scss/common/icon-fonts/style';
519 @import '../../../../assets/scss/common/empty-state/_empty-state-mixin.scss';
520 @import '../../../../assets/scss/common/transitions/_transitions-mixin.scss';
521
522 #amelia-app-backend-new {
523 background-color: transparent;
524
525 #amelia-container {
526 background-color: transparent;
527
528 * {
529 font-family: var(--am-font-family), sans-serif;
530 font-style: initial;
531 box-sizing: border-box;
532 }
533
534 // cap - cabinet panel
535 &.am-cap {
536 // Container Wrapper
537 &__wrapper {
538 display: flex;
539 justify-content: center;
540 max-width: var(--am-mw-main);
541 width: 100%;
542 height: 700px;
543 margin: 16px auto 100px;
544 border-radius: 8px;
545 box-shadow: 0 30px 40px rgba(0, 0, 0, 0.12);
546 transition: max-width 0.3s ease-in-out;
547
548 &.am-auth {
549 box-shadow: unset;
550 height: auto;
551 }
552
553 &.am-collapsed {
554 transition-delay: 1s;
555 }
556
557 * {
558 font-family: var(--am-font-family), sans-serif;
559 box-sizing: border-box;
560 }
561 }
562 }
563
564 .am-cap {
565 &__header {
566 display: flex;
567 align-items: center;
568 justify-content: space-between;
569
570 .am-select-wrapper {
571 max-width: 250px;
572 }
573 }
574 }
575
576 .am-asi {
577 .el-form {
578 &-item {
579 display: block;
580 font-family: var(--am-font-family), sans-serif;
581 font-size: var(--am-fs-label);
582 margin-bottom: 24px;
583
584 &__label {
585 flex: 0 0 auto;
586 text-align: left;
587 font-size: var(--am-fs-label);
588 line-height: 1.3;
589 color: var(--am-c-main-text);
590 box-sizing: border-box;
591 margin: 0;
592
593 &:before {
594 color: var(--am-c-error);
595 }
596 }
597
598 &__content {
599 display: flex;
600 flex-wrap: wrap;
601 align-items: center;
602 flex: 1;
603 position: relative;
604 font-size: var(--am-fs-input);
605 min-width: 0;
606 }
607
608 &__error {
609 font-size: 12px;
610 color: var(--am-c-error);
611 padding-top: 4px;
612 }
613 }
614 }
615 }
616 }
617 }
618 </style>
619