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 / admin / customize / steps / parts / Calendar.vue
ameliabooking / v3 / src / views / admin / customize / steps / parts Last commit date
AddToCalendar.vue 1 month ago AppointmentInfo.vue 2 days ago AppointmentInfoService.vue 1 month ago Calendar.vue 1 month ago CartItemBody.vue 1 month ago CartItemHead.vue 1 month ago CustomerCongratsInfo.vue 1 month ago PackageInfo.vue 1 month ago PackageInfoService.vue 1 month ago PaymentCongratsInfo.vue 1 month ago PaymentPackageInfo.vue 1 month ago
Calendar.vue
404 lines
1 <template>
2 <div ref="dateTimeRef" class="am-fs-dt__calendar">
3 <AmAdvancedSlotCalendar
4 v-if="showCalendar"
5 :id="0"
6 :slots="calendarEvents"
7 :calendar-minimum-date="moment().format('YYYY-MM-DD hh:mm')"
8 :calendar-maximum-date="moment().add(1, 'year').format('YYYY-MM-DD hh:mm')"
9 :time-zone="
10 features.timeZones &&
11 amCustomize[pageRenderKey][stepRecognition].options.timeZoneVisibility.visibility
12 "
13 :end-time="amCustomize[pageRenderKey][stepRecognition].options.endTimeVisibility.visibility"
14 :show-calendar-date-busyness="showCalendarDateBusyness"
15 :busyness="previewCalendarBusyness"
16 :show-estimated-pricing="
17 stepRecognition === 'rescheduleAppointment'
18 ? amCustomize[pageRenderKey][stepRecognition].options.estimatedPricingVisibility
19 .visibility
20 : false
21 "
22 :show-indicator-pricing="
23 stepRecognition === 'rescheduleAppointment'
24 ? amCustomize[pageRenderKey][stepRecognition].options.indicatorPricingVisibility
25 .visibility
26 : false
27 "
28 :show-slot-pricing="
29 stepRecognition === 'rescheduleAppointment'
30 ? amCustomize[pageRenderKey][stepRecognition].options.slotPricingVisibility.visibility
31 : false
32 "
33 :label-slots-selected="labelsDisplay('date_time_slots_selected')"
34 :period-pricing="stepRecognition === 'rescheduleAppointment' ? periodPricing : {}"
35 @selected-date="setSelectedDate"
36 @selected-time="setSelectedTime"
37 @unselect-date="unselectDate"
38 ></AmAdvancedSlotCalendar>
39 </div>
40 </template>
41
42 <script setup>
43 // * Import from libraries
44 import moment from 'moment'
45
46 // * Import from Vue
47 import { ref, provide, inject, computed, watchEffect, onMounted } from 'vue'
48
49 // * Import composables
50 import { useReactiveCustomize } from '../../../../../assets/js/admin/useReactiveCustomize.js'
51
52 // * Dedicated component
53 import AmAdvancedSlotCalendar from '../../../../_components/advanced-slot-calendar/AmAdvancedSlotCalendar'
54
55 // * Customize
56 const { amCustomize } = useReactiveCustomize()
57
58 // * Features
59 let features = inject('features')
60
61 // * Languages
62 let langKey = inject('langKey')
63
64 // * Global Labels
65 let amLabels = inject('labels')
66
67 // * Plugin Licence
68 let licence = inject('licence')
69
70 // * Step key name
71 let stepName = inject('stepName')
72
73 // * Sub step key name
74 let subStepName = inject('subStepName')
75
76 let stepRecognition = computed(() => {
77 if (subStepName.value) return subStepName.value
78 return stepName.value
79 })
80
81 let customOptions = computed(() => {
82 return amCustomize.value[pageRenderKey.value][stepRecognition.value].options
83 })
84
85 let showCalendarDateBusyness = computed(() => {
86 if (
87 stepRecognition.value === 'rescheduleAppointment' ||
88 stepRecognition.value === 'bookAppointment'
89 ) {
90 return false
91 }
92 if ('calendarDateBusynessVisibility' in customOptions.value) {
93 return customOptions.value.calendarDateBusynessVisibility.visibility
94 }
95 return true
96 })
97
98 let previewCalendarBusyness = computed(() => {
99 if (
100 stepRecognition.value === 'rescheduleAppointment' ||
101 stepRecognition.value === 'bookAppointment'
102 ) {
103 return {}
104 }
105 const opts = customOptions.value
106 if (!(opts.calendarDateBusynessVisibility?.visibility ?? true)) {
107 return {}
108 }
109 const base = moment().startOf('day')
110 return {
111 [base.clone().add(6, 'days').format('YYYY-MM-DD')]: 40,
112 [base.clone().add(20, 'days').format('YYYY-MM-DD')]: 82,
113 }
114 })
115
116 // * Form key
117 let pageRenderKey = inject('pageRenderKey')
118
119 /*****************
120 * Calendar Data *
121 ****************/
122 let showCalendar = ref(false)
123
124 let periodPricing = ref({})
125
126 function getCustomPeriodsPricing() {
127 let result = {}
128 let today = moment()
129
130 let current = today.clone()
131
132 for (let i = 0; i <= 31; i++) {
133 let type =
134 current.day() === 6 || current.day() === 0
135 ? 'high'
136 : current.day() === 1 || current.day() === 2
137 ? 'low'
138 : 'mid'
139
140 result[current.format('YYYY-MM-DD')] = {
141 type: type,
142 slots: {
143 '09:00': {
144 type: type === 'mid' ? 'low' : type,
145 price: type === 'mid' ? 5 : type === 'low' ? 5 : 15,
146 },
147 '09:30': {
148 type: type === 'mid' ? 'low' : type,
149 price: type === 'mid' ? 5 : type === 'low' ? 5 : 15,
150 },
151 '10:00': {
152 type: type,
153 price: type === 'mid' ? 10 : type === 'low' ? 5 : 15,
154 },
155 '10:30': {
156 type: type,
157 price: type === 'mid' ? 10 : type === 'low' ? 5 : 15,
158 },
159 '11:00': {
160 type: type,
161 price: type === 'mid' ? 10 : type === 'low' ? 5 : 15,
162 },
163 '11:30': {
164 type: type,
165 price: type === 'mid' ? 10 : type === 'low' ? 5 : 15,
166 },
167 '12:00': {
168 type: type === 'mid' ? 'high' : type,
169 price: type === 'mid' ? 15 : type === 'high' ? 15 : 5,
170 },
171 '12:30': {
172 type: type === 'mid' ? 'high' : type,
173 price: type === 'mid' ? 15 : type === 'high' ? 15 : 5,
174 },
175 },
176 }
177
178 current.add(1, 'day')
179 }
180
181 return {
182 price: {
183 low: 5,
184 mid: 10,
185 high: 15,
186 uniqueMin: true,
187 uniqueMid: true,
188 uniqueMax: true,
189 },
190 dates: result,
191 }
192 }
193
194 let dateTimeRef = ref(null)
195 provide('formWrapper', dateTimeRef)
196
197 let calendarEvents = ref([])
198
199 let calendarEventDate = ref('')
200
201 let calendarServiceDuration = ref('0')
202
203 let calendarEventSlots = ref([])
204
205 let calendarEventBusySlots = ref()
206
207 watchEffect(() => {
208 calendarEventBusySlots.value =
209 'busyTimeSlotsVisibility' in customOptions.value &&
210 customOptions.value.busyTimeSlotsVisibility.visibility
211 ? ['08:00', '08:30', '09:30', '12:30', '14:00']
212 : []
213 })
214
215 let today = moment().format('YYYY-MM-DD')
216
217 for (let i = 0; i <= 31; i++) {
218 let block = {
219 display: 'background',
220 extendedProps: {
221 slots: { '09:00': [7, 3] },
222 slotsAvailable: 1,
223 slotsTotal: 100,
224 },
225 start: moment(today).add(i, 'd').format('YYYY-MM-DD'),
226 title: 'e',
227 }
228
229 calendarEvents.value.push(block)
230 }
231
232 let calendarEventSlot = ref('')
233 let calendarStartDate = ref(moment().format('YYYY-MM-DD'))
234 let calendarChangeSideBar = ref(true)
235
236 provide('calendarEvents', calendarEvents)
237 provide('calendarEventDate', calendarEventDate)
238 provide('calendarEventSlots', calendarEventSlots)
239 provide('calendarEventBusySlots', calendarEventBusySlots)
240 provide('calendarEventSlot', calendarEventSlot)
241 provide('calendarStartDate', calendarStartDate)
242 provide('calendarChangeSideBar', calendarChangeSideBar)
243 provide('calendarServiceDuration', calendarServiceDuration)
244
245 let calendarSlotDuration = 1800
246 provide('calendarSlotDuration', calendarSlotDuration)
247
248 // * Label computed function
249 function labelsDisplay(label) {
250 let computedLabel = computed(() => {
251 return amCustomize.value[pageRenderKey.value][stepRecognition.value].translations &&
252 amCustomize.value[pageRenderKey.value][stepRecognition.value].translations[label] &&
253 amCustomize.value[pageRenderKey.value][stepRecognition.value].translations[label][
254 langKey.value
255 ]
256 ? amCustomize.value[pageRenderKey.value][stepRecognition.value].translations[label][
257 langKey.value
258 ]
259 : amLabels[label]
260 })
261
262 return computedLabel.value
263 }
264
265 onMounted(() => {
266 periodPricing.value =
267 !licence.isBasic && !licence.isStarter && !licence.isLite ? getCustomPeriodsPricing() : {}
268
269 showCalendar.value = true
270 })
271
272 function setSelectedDate(value) {
273 unselectDate()
274
275 for (let i = 0; i < 18; i++) {
276 if (i > 8 && i < 13) {
277 calendarEventSlots.value.push(`${i < 10 ? '0' + i : i}:00`)
278 calendarEventSlots.value.push(`${i < 10 ? '0' + i : i}:30`)
279 }
280 }
281
282 if (calendarEventSlots.value.length) {
283 setSelectedTime(calendarEventSlots.value[0])
284 }
285
286 calendarEventDate.value = value
287 }
288
289 function unselectDate() {
290 calendarEventBusySlots.value =
291 'busyTimeSlotsVisibility' in customOptions.value &&
292 customOptions.value.busyTimeSlotsVisibility.visibility
293 ? ['08:00', '08:30', '09:30', '12:30', '14:00']
294 : []
295
296 calendarEventSlots.value = []
297
298 calendarEventSlot.value = ''
299
300 calendarEventDate.value = ''
301 }
302
303 function setSelectedTime(value) {
304 calendarEventSlot.value = value
305 }
306 </script>
307
308 <script>
309 export default {
310 name: 'CalendarBlock',
311 }
312 </script>
313
314 <style lang="scss">
315 // am -- amelia
316 // fs -- form steps
317
318 .amelia-v2-booking #amelia-container {
319 // Amelia Form Steps
320 .am-fs {
321 // Container Wrapper
322 &__main {
323 &-heading {
324 &-inner {
325 display: flex;
326 align-items: center;
327
328 .am-heading-prev {
329 margin-right: 12px;
330 }
331 }
332 }
333 &-inner {
334 &#{&}-dt {
335 padding: 0 20px;
336 }
337 }
338 }
339 }
340
341 // Skeleton
342 .am-skeleton-slots {
343 &-mobile {
344 padding: 0;
345
346 .am-skeleton-slots-days {
347 gap: 6px;
348
349 .el-skeleton__item {
350 height: 28px;
351 max-width: 56px;
352 }
353 }
354 }
355
356 &-filters {
357 display: flex;
358 flex-direction: row;
359 justify-content: space-between;
360 padding: 0 0 24px;
361
362 .el-skeleton__item {
363 height: 36px;
364 width: 20%;
365 }
366
367 :first-child {
368 width: 26%;
369 margin-right: 16px;
370 }
371
372 :last-child {
373 width: 16%;
374 margin-left: 16px;
375 }
376 }
377
378 &-weekdays {
379 padding-bottom: 12px;
380 display: flex;
381 flex-direction: row;
382 justify-content: space-around;
383
384 .el-skeleton__item {
385 max-width: 30px;
386 height: 24px;
387 }
388 }
389
390 &-days {
391 display: grid;
392 grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
393 gap: 8px;
394
395 .el-skeleton__item {
396 margin: 0 1.5px;
397 height: 40px;
398 max-width: 56px;
399 }
400 }
401 }
402 }
403 </style>
404