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 / Customize.vue
ameliabooking / v3 / src / views / admin / customize Last commit date
fields 2 years ago navigation 4 years ago pages 1 year ago settings 1 year ago steps 1 year ago Customize.vue 1 year ago
Customize.vue
630 lines
1 <template>
2 <PageWrapper page-id="am-customize">
3 <template #header>
4 <PageHeader></PageHeader>
5 </template>
6
7 <template v-if="pageRenderKey !== 'main' && pageRenderKey !== 'cbf' && pageRenderKey !== 'elf' && pageRenderKey !== 'ecf'" #filter>
8 <!-- Step by step select for page type -->
9 <div
10 class="am-customize__fs-flow"
11 :class="[
12 {'am-customize__capc-panel': pageRenderKey === 'capc' && pagesType === 'panel'},
13 {'am-customize__capc-auth': pageRenderKey === 'capc' && pagesType === 'auth'}
14 ]"
15 style="justify-content: space-between"
16 >
17 <div
18 v-if="!licence.isBasic && !licence.isStarter && !licence.isLite && pageRenderKey !== 'capc' && pageRenderKey !== 'cape'"
19 style="display: flex; align-items: center"
20 >
21 <div class="am-customize__fs-flow__label">
22 {{amLabels.steps}}:
23 </div>
24 <AmSelect
25 v-model="bookableType"
26 @change="handleClick('menu', 0)"
27 >
28 <AmOption value="appointment" :label="amLabels.service_option" />
29 <AmOption value="package" :label="amLabels.package_option" />
30 </AmSelect>
31 </div>
32 <!--/ Step by step select for page type -->
33
34 <!-- Cabinet select for page type -->
35 <div
36 v-if="!licence.isLite && (pageRenderKey === 'capc' || pageRenderKey === 'cape')"
37 style="display: flex; align-items: center"
38 >
39 <div class="am-customize__fs-flow__label">
40 {{amLabels.steps}}:
41 </div>
42 <AmSelect
43 v-model="pagesType"
44 @change="handleClick('menu', 0)"
45 >
46 <AmOption value="panel" label="Panel" />
47 <AmOption value="auth" label="Login" />
48 </AmSelect>
49 </div>
50 <!-- /Cabinet select for page type -->
51
52 <!-- Button that will redirect to Catalog form -->
53 <AmButton
54 v-if="urlParams.get('current') !== 'sbsNew' && pageRenderKey !== 'capc' && pageRenderKey !== 'cape'"
55 :style="{margin: '0 0 0 auto'}"
56 @click="handleClick('menu', 0, urlParams.get('current'))"
57 >
58 Go Back To Catalog Form
59 </AmButton>
60 <!-- /Button that will redirect to Catalog form -->
61 </div>
62 </template>
63
64 <template #default>
65 <component :is="pagesObject[pageRenderKey]"></component>
66 </template>
67
68 <template #sidebar>
69 <SettingsSidebar v-if="pageRenderKey !== 'main'"></SettingsSidebar>
70 </template>
71
72 <template #menu>
73 <SettingsSidebar v-if="pageRenderKey !== 'main'" ref="sidebarRef"></SettingsSidebar>
74 </template>
75 </PageWrapper>
76 </template>
77
78 <script setup>
79 // * Components
80 import AmSelect from '../../_components/select/AmSelect.vue'
81 import AmOption from '../../_components/select/AmOption.vue'
82
83 // * Page Shell
84 import PageWrapper from '../parts/PageWrapper';
85 import PageHeader from '../parts/PageHeader';
86 import SettingsSidebar from "./settings/SettingsSidebar.vue";
87
88 // * Pages
89 import CustomizeMainPage from './navigation/CustomizeMainPage.vue';
90 import CustomizeStepNew from "./pages/CustomizeStepNew.vue";
91 import CustomizeCatalog from "./pages/CustomizeCatalog.vue";
92 import CustomizeEventList from "./pages/CustomizeEventList.vue";
93 import CustomizeEventCalendar from "./pages/CustomizeEventCalendar.vue";
94 import CustomizeCustomerPanel from "./pages/CustomizeCustomerPanel.vue";
95 import CustomizeEmployeePanel from "./pages/CustomizeEmployeePanel.vue";
96
97 // * Import form Vue
98 import {
99 markRaw,
100 inject,
101 provide,
102 ref,
103 reactive,
104 computed,
105 onMounted,
106 watchEffect
107 } from 'vue';
108 import { ElNotification } from 'element-plus'
109 // * Deepmerge
110 import deepMerge from 'deepmerge'
111 // * Import for axios
112 import httpClient from '../../../plugins/axios'
113 // * Import Composables
114 import {
115 usePopulateMultiDimensionalObject,
116 useOverrideValuesOfOneObjectWithAnother
117 } from '../../../assets/js/common/objectAndArrayManipulation'
118 // * Default Customize Settings
119 import { defaultCustomizeSettings, defaultTranslations } from '../../../assets/js/common/defaultCustomize.js'
120 import AmButton from "../../_components/button/AmButton";
121
122 // * Plugin Licence
123 let licence = inject('licence')
124
125 function notify (title, message, type, customClass) {
126 if (typeof customClass === 'undefined') {
127 customClass = ''
128 }
129 ElNotification({
130 title: title,
131 message: message,
132 type: type,
133 offset: 125,
134 position: 'top-left',
135 appendTo: '#am-customize'
136 })
137 }
138
139 let bookableType = ref('appointment')
140 provide('bookableType', bookableType)
141
142 let pagesType = ref('auth')
143 provide('pagesType', pagesType)
144
145 // * Settings data
146 let settings = inject('settings')
147
148 // * Labels
149 let amLabels = inject('labels')
150
151 // * key that depends on selected language in customize header
152 let langKey = ref('default')
153 provide('langKey', langKey)
154
155 // * Object of all languages
156 let amLanguages = inject('languages')
157
158 // * Languages that were selected in general settings
159 let selectedLanguages = ref([])
160 settings.general.usedLanguages.forEach(lang => {
161 selectedLanguages.value.push(amLanguages[lang])
162 })
163
164 provide('languageFunctionality', {
165 langKey,
166 selectedLanguages
167 })
168
169 // * Loading state
170 let loading = ref(false)
171 provide('loading', loading)
172
173 // * Sidebar Reference
174 let sidebarRef = ref(null)
175
176 // * Page Header block
177 let pageName = ref('Customize')
178
179 let stepIndex = ref(0)
180 provide('stepIndex', stepIndex)
181
182 // * Change name of the page in page header
183 function pageNameHandler (name = '') {
184 pageName.value = name ? name : 'Customize'
185 }
186
187 provide('headerFunctionality', {
188 pageNameHandler,
189 pageName
190 })
191
192 // * Step name
193 let stepName = ref('')
194 provide('stepName', stepName)
195 let subStepName = ref('')
196 provide('subStepName', subStepName)
197
198 // * Sidebar functionality
199 let componentKey = ref('menu')
200 let goBackPath = ref('menu')
201 let parentPath = ref('menu')
202
203 function handleClick (data = '', index, pageKey = '') {
204 componentKey.value = data ? data : goBackPath.value
205 goBackPath.value = 'menu'
206
207 if (pageKey) {
208 pageRenderKey.value = pageKey
209 }
210 if (componentKey.value === 'menu') {
211 subStepName.value = ''
212 }
213 if (index !== undefined) {
214 stepIndex.value = index
215 }
216 }
217
218 provide('sidebarFunctionality', {
219 componentKey,
220 parentPath,
221 goBackPath,
222 handleClick
223 })
224
225 // * Pages that represents all form types
226 let pagesObject = {
227 main: markRaw(CustomizeMainPage),
228 sbsNew: markRaw(CustomizeStepNew),
229 cbf: markRaw(CustomizeCatalog),
230 elf: markRaw(CustomizeEventList),
231 ecf: markRaw(CustomizeEventCalendar),
232 capc: markRaw(CustomizeCustomerPanel),
233 cape: markRaw(CustomizeEmployeePanel)
234 }
235
236 let urlParams = new URLSearchParams(window.location.search)
237
238 // * Key that reference to page from type
239 let pageRenderKey = ref(urlParams.get('current') ? urlParams.get('current') : 'sbsNew')
240 provide('pageRenderKey', pageRenderKey)
241
242 // * Function that changes the pages ( value of pageRenderKey )
243 function changePages(pageString) {
244 pageRenderKey.value = pageString
245 }
246 provide('pageFunctions', {
247 changePages
248 })
249
250 watchEffect(() => {
251 if (pageRenderKey.value === 'elf' || pageRenderKey.value === 'ecf') {
252 bookableType.value = 'event'
253 }
254 })
255
256 // * Customize Object settings
257 let amCustomize = ref({})
258 const combineMerge = (target, source, options) => {
259 const destination = target.slice()
260 source.forEach((item, index) => {
261 if (typeof destination[index] === 'undefined') {
262 destination[index] = options.cloneUnlessOtherwiseSpecified(item, options)
263 } else if (options.isMergeableObject(item)) {
264 destination[index] = deepMerge(target[index], item, options)
265 } else if (target.indexOf(item) === -1) {
266 destination.push(item)
267 }
268 })
269 return destination
270 }
271
272 if (settings.customizedData) {
273 amCustomize.value = deepMerge.all([settings.customizedData, JSON.parse(JSON.stringify({...defaultCustomizeSettings})), settings.customizedData], {arrayMerge: combineMerge})
274 } else {
275 amCustomize.value = JSON.parse(JSON.stringify({...defaultCustomizeSettings}))
276 }
277
278 // * Customize data
279 provide('customize', amCustomize)
280
281 // * Labels
282 // * amTranslations object is used in sidebar segments
283 let amTranslations = reactive(JSON.parse(JSON.stringify({...defaultTranslations})))
284
285 // * Set selected languages and data from server
286 function labelsKeysLanguages(labelObj) {
287 Object.keys(labelObj).forEach((item) => {
288 labelObj[item] = {
289 default : ''
290 }
291 settings.general.usedLanguages.forEach(lang => {
292 labelObj[item][lang] = ''
293 })
294 })
295 }
296 usePopulateMultiDimensionalObject('labels', amTranslations, labelsKeysLanguages)
297
298 provide('translations', amTranslations)
299
300 function changeNamesInObject (a, b) {
301 b.name = a.name
302 }
303 onMounted(() => {
304 useOverrideValuesOfOneObjectWithAnother('name', defaultCustomizeSettings, amCustomize.value, changeNamesInObject)
305 })
306
307 let stepKey = computed(() => {
308 return subStepName.value ? subStepName.value : stepName.value
309 })
310
311 let filterByKey = (key) => {
312 return computed(() =>
313 amCustomize.value[key] ? { [key]: amCustomize.value[key], 'fonts': amCustomize.value['fonts'] } : amCustomize.value
314 )
315 }
316
317 function updateCustomizeLabel (labelObj) {
318 let parentObjKey = stepKey.value
319 if (parentPath.value === 'sidebar') parentObjKey = parentPath.value
320
321 if (
322 amCustomize.value[pageRenderKey.value][parentObjKey].translations === null
323 || (Array.isArray(amCustomize.value[pageRenderKey.value][parentObjKey].translations)
324 && amCustomize.value[pageRenderKey.value][parentObjKey].translations.length === 0)
325 ) {
326 amCustomize.value[pageRenderKey.value][parentObjKey].translations = {}
327 }
328
329 let labelLangObj = {}
330 Object.keys(labelObj).forEach((label) => {
331 Object.keys(labelObj[label]).forEach((lang) => {
332 if (labelObj[label][lang]) {
333 labelLangObj[label] = {}
334 labelLangObj[label][lang] = labelObj[label][lang]
335 if (amCustomize.value[pageRenderKey.value][parentObjKey].translations[label]) {
336 Object.assign(amCustomize.value[pageRenderKey.value][parentObjKey].translations[label], labelLangObj[label])
337 } else {
338 amCustomize.value[pageRenderKey.value][parentObjKey].translations[label] = labelLangObj[label]
339 }
340 } else if (
341 !labelObj[label][lang] &&
342 amCustomize.value[pageRenderKey.value][parentObjKey].translations &&
343 Object.keys(amCustomize.value[pageRenderKey.value][parentObjKey].translations).includes(label) &&
344 Object.keys(amCustomize.value[pageRenderKey.value][parentObjKey].translations[label]).includes(lang)
345 ) {
346
347 delete amCustomize.value[pageRenderKey.value][parentObjKey].translations[label][lang]
348
349 if (!Object.keys(amCustomize.value[pageRenderKey.value][parentObjKey].translations[label]).length) {
350 delete amCustomize.value[pageRenderKey.value][parentObjKey].translations[label]
351 }
352 }
353 })
354 })
355
356 if (!Object.keys(amCustomize.value[pageRenderKey.value][parentObjKey].translations).length) {
357 amCustomize.value[pageRenderKey.value][parentObjKey].translations = null
358 }
359 }
360
361 function updateLabelObject () {
362 let parentObjKey = stepKey.value
363 if (parentPath.value === 'sidebar') parentObjKey = parentPath.value
364
365 usePopulateMultiDimensionalObject('labels', amTranslations[pageRenderKey.value][parentObjKey], updateCustomizeLabel)
366 }
367
368 provide('updateLabelObject', updateLabelObject)
369
370 // * Save Changes function
371 function saveChanges () {
372 let currentForm = filterByKey(pageRenderKey.value)
373
374 httpClient.post(
375 '/settings',
376 {customizedData: currentForm.value}
377 )
378 .then(() => {
379 notify(amLabels.success, amLabels.settings_saved, 'success')
380 })
381 .catch(e => {
382 notify(amLabels.error, e.message, 'error')
383 })
384 }
385
386 function resetChanges () {
387 amCustomize.value[pageRenderKey.value] = JSON.parse(JSON.stringify({...defaultCustomizeSettings[pageRenderKey.value]}))
388 amTranslations[pageRenderKey.value] = JSON.parse(JSON.stringify({...defaultTranslations[[pageRenderKey.value]]}))
389 amCustomize.value.fonts = JSON.parse(JSON.stringify({...defaultCustomizeSettings.fonts}))
390 usePopulateMultiDimensionalObject('labels', amTranslations[pageRenderKey.value], labelsKeysLanguages)
391
392 saveChanges()
393 }
394
395 provide('customizeSaveFunctionality', {
396 saveChanges,
397 resetChanges
398 })
399 </script>
400
401 <script>
402 export default {
403 name: 'AmeliaCustomize'
404 }
405 </script>
406
407 <style lang="scss">
408 @import '../../../../src/assets/scss/common/icon-fonts/style';
409
410 :root {
411 // Colors
412 // shortcuts
413 // -c- color
414 // -bgr- background
415 // -prim- primary
416 // -sec- secondary
417 // primitive colors
418 --am-c-primary: #{$blue-1000};
419 --am-c-success: #{$green-1000};
420 --am-c-error: #{$red-900};
421 --am-c-warning: #{$yellow-1000};
422 // main container colors - right part of the form
423 --am-c-main-bgr: #{$am-white};
424 --am-c-main-heading-text: #{$shade-800};
425 --am-c-main-text: #{$shade-900};
426 // sidebar container colors - left part of the form
427 --am-c-sb-bgr: #17295A;
428 --am-c-sb-text: #{$am-white};
429 // input global colors - usage input, textarea, checkbox, radio button, select input, adv select input
430 --am-c-inp-bgr: #{$am-white};
431 --am-c-inp-border: #{$shade-250};
432 --am-c-inp-text: #{$shade-900};
433 --am-c-inp-placeholder: #{$shade-500};
434 --am-c-checkbox-border: #{$shade-300};
435 --am-c-checkbox-border-disabled: #{$blue-600};
436 --am-c-checkbox-border-focused: #{$blue-700};
437 --am-c-checkbox-label-disabled: #{$shade-600};
438 // dropdown global colors - usage select dropdown, adv select dropdown
439 --am-c-drop-bgr: #{$am-white};
440 --am-c-drop-text: #{$shade-1000};
441 // card global colors
442 --am-c-card-bgr: #{$am-white};
443 --am-c-card-border: #{$shade-250};
444 --am-c-card-text: #{$shade-900};
445 // button global colors
446 --am-c-btn-prim: #{$blue-900};
447 --am-c-btn-prim-text: #{$am-white};
448 --am-c-btn-sec: #{$am-white};
449 --am-c-btn-sec-text: #{$shade-900};
450
451 // Properties
452 // shortcuts
453 // -h- height
454 // -fs- font size
455 // -rad- border radius
456 --am-h-input: 40px;
457 --am-fs-input: 15px;
458 --am-rad-input: 6px;
459 --am-fs-label: 15px;
460 --am-fs-btn: 15px;
461
462 // Font
463 --am-font-family: 'Amelia Roboto', sans-serif;
464 }
465
466 .am-customize {
467 &__fs {
468 &-flow {
469 max-width: 760px;
470 width: 100%;
471 margin: 16px auto;
472 display: flex;
473 flex-direction: row;
474 align-items: center;
475
476 &.am-customize__capc-panel {
477 max-width: 1024px;
478 }
479
480 &.am-customize__capc-auth {
481 max-width: 400px;
482 }
483
484 .am-select-wrapper {
485 max-width: 180px;
486 margin-left: 10px;
487 }
488 }
489 }
490 }
491
492 .el-notification {
493 --el-notification-width: 330px;
494 --el-notification-padding: 14px 26px 14px 13px;
495 --el-notification-radius: 8px;
496 --el-notification-shadow: var(--el-box-shadow-light);
497 --el-notification-border-color: var(--el-border-color-lighter);
498 --el-notification-icon-size: 24px;
499 --el-notification-close-font-size: var(--el-message-close-size, 16px);
500 --el-notification-group-margin-left: 13px;
501 --el-notification-group-margin-right: 8px;
502 --el-notification-content-font-size: var(--el-font-size-base);
503 --el-notification-content-color: var(--el-text-color-regular);
504 --el-notification-title-font-size: 16px;
505 --el-notification-title-color: var(--el-text-color-primary);
506 --el-notification-close-color: var(--el-text-color-secondary);
507 --el-notification-close-hover-color: var(--el-text-color-regular)
508 }
509
510 .el-notification {
511 display: flex;
512 width: var(--el-notification-width);
513 padding: var(--el-notification-padding);
514 border-radius: var(--el-notification-radius);
515 box-sizing: border-box;
516 border: 1px solid var(--el-notification-border-color);
517 position: fixed;
518 background-color: #FFFFFF;
519 box-shadow: var(--el-notification-shadow);
520 transition: opacity var(--el-transition-duration),transform var(--el-transition-duration),left var(--el-transition-duration),right var(--el-transition-duration),top .4s,bottom var(--el-transition-duration);
521 overflow-wrap: anywhere;
522 overflow: hidden;
523 z-index: 9999
524 }
525
526 .el-notification.right {
527 right: 16px
528 }
529
530 .el-notification.left {
531 left: 175px;
532 @include media-breakpoint-down($am-small-max) {
533 --el-notification-width: 300;
534 left: 30px
535 }
536 }
537
538 .el-notification__group {
539 margin-left: var(--el-notification-group-margin-left);
540 margin-right: var(--el-notification-group-margin-right)
541 }
542
543 .el-notification__title {
544 font-weight: 700;
545 font-size: var(--el-notification-title-font-size);
546 line-height: var(--el-notification-icon-size);
547 color: var(--el-notification-title-color);
548 margin: 0
549 }
550
551 .el-notification__content {
552 font-size: var(--el-notification-content-font-size);
553 line-height: 24px;
554 margin: 6px 0 0;
555 color: var(--el-notification-content-color);
556 text-align: justify
557 }
558
559 .el-notification__content p {
560 margin: 0
561 }
562
563 .el-notification .el-notification__icon {
564 height: var(--el-notification-icon-size);
565 width: var(--el-notification-icon-size);
566 font-size: var(--el-notification-icon-size)
567 }
568
569 .el-notification .el-notification__closeBtn {
570 position: absolute;
571 top: 18px;
572 right: 15px;
573 cursor: pointer;
574 color: var(--el-notification-close-color);
575 font-size: var(--el-notification-close-font-size)
576 }
577
578 .el-notification .el-notification__closeBtn:hover {
579 color: var(--el-notification-close-hover-color)
580 }
581
582 .el-notification .el-notification--success {
583 --el-notification-icon-color: var(--el-color-success);
584 color: var(--el-notification-icon-color)
585 }
586
587 .el-notification .el-notification--info {
588 --el-notification-icon-color: var(--el-color-info);
589 color: var(--el-notification-icon-color)
590 }
591
592 .el-notification .el-notification--warning {
593 --el-notification-icon-color: var(--el-color-warning);
594 color: var(--el-notification-icon-color)
595 }
596
597 .el-notification .el-notification--error {
598 --el-notification-icon-color: var(--el-color-error);
599 color: var(--el-notification-icon-color)
600 }
601
602 .el-notification-fade-enter-from.right {
603 right: 0;
604 transform: translate(100%)
605 }
606
607 .el-notification-fade-enter-from.left {
608 left: 0;
609 transform: translate(-100%)
610 }
611
612 .el-notification-fade-leave-to {
613 opacity: 0
614 }
615
616 #am-customize{
617 .am-lite-version {
618 display: none !important;
619 }
620
621 .am-basic-version {
622 display: none !important;
623 }
624
625 .am-starter-version {
626 display: none !important;
627 }
628 }
629 </style>
630