PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.4.6
Booking for Appointments and Events Calendar – Amelia v2.4.6
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 / Parts / RecurringSetup.vue
ameliabooking / v3 / src / views / public / Parts Last commit date
Congratulations 1 month ago Payment 3 weeks ago Skeletons 1 month ago BackLink.vue 1 month ago BookingSkeleton.vue 1 month ago Calendar.vue 1 month ago DotsPopup.vue 3 weeks ago RecurringSetup.vue 3 weeks ago
RecurringSetup.vue
535 lines
1 <template>
2 <div
3 class="am-fs__rs"
4 :class="[{ 'am-fs__rs-mobile': checkScreen }, props.globalClass]"
5 :style="cssVars"
6 >
7 <template
8 v-if="
9 amCustomize &&
10 amCustomize.recurringStep &&
11 amCustomize.recurringStep.options.heading.visibility
12 "
13 >
14 <p>{{ amLabels.recurrence }}</p>
15 <span>{{ amLabels.recurrence_choosing_time }}</span>
16 </template>
17 <p>{{ amLabels.repeat_every }}:</p>
18 <div class="am-fs__rs-every">
19 <AmInputNumber v-model="repeatInterval" :min="1" :max="100" />
20 <AmSelect v-model="repeatType" :disabled="props.service.recurringCycle !== 'all'">
21 <AmOption
22 v-for="(type, i) in repeatTypes"
23 :key="i"
24 :label="type.label"
25 :value="type.value"
26 />
27 </AmSelect>
28 </div>
29 <div v-if="repeatType === 'weekly'">
30 <p>{{ `${amLabels.recurrence_repeat_on}:` }}</p>
31 <div class="am-fs__rs-on-week">
32 <div
33 v-for="(day, i) in weekDays"
34 :key="i"
35 :class="{
36 'am-fs__rs-on-week-selected': weekDays.find((i) => i.value === day.value).selected,
37 }"
38 @click="selectWeekDay(day.value)"
39 >
40 {{ day.label }}
41 </div>
42 </div>
43 </div>
44 <div v-if="repeatType === 'monthly'">
45 <p>{{ `${amLabels.recurrence_repeat_on}:` }}</p>
46 <div class="am-fs__rs-on-month">
47 <AmSelect v-model="repeatMonthly">
48 <AmOption
49 v-for="(type, i) in monthlyTypes"
50 :key="i"
51 :label="type.label + (i !== 0 ? ' ' + selectedDayInMonth : '')"
52 :value="type.value"
53 />
54 </AmSelect>
55 </div>
56 </div>
57 <p>{{ `${amLabels.recurrence_ends}:` }}</p>
58 <span>{{ amLabels.recurrence_choose_ends }}</span>
59 <div class="am-fs__rs-ends">
60 <div class="am-fs__rs-ends-choose">
61 <AmRadioGroup v-model="occurrenceType">
62 <AmRadio value="On">{{ amLabels.recurrence_on }}</AmRadio>
63 <AmRadio value="After">{{ amLabels.recurrence_after }}</AmRadio>
64 </AmRadioGroup>
65 </div>
66 <div class="am-fs__rs-ends-options">
67 <AmDatePickerFull
68 :calendar-minimum-date="props.startDate"
69 :disabled="occurrenceType !== 'On'"
70 :input-placeholder="amLabels.recurrence_select_date"
71 :calendar-maximum-date="props.maxDate"
72 @selected-date="selectedOccurrenceDate"
73 />
74 <div class="am-fs__rs-ends-after">
75 <AmInputNumber
76 v-model="occurrenceCount"
77 :disabled="occurrenceType !== 'After'"
78 :min="1"
79 :max="100"
80 />
81 <span>{{ ` ${amLabels.occurrences}` }}</span>
82 </div>
83 </div>
84 </div>
85 <div class="am-fs__rs-summary">
86 <p>{{ `${amLabels.appointment_repeats}:` }}</p>
87 <p>
88 {{
89 `${amLabels.recurrence_every} ${repeatInterval > 1 ? repeatInterval : ''} ${repeatTypes.find((item) => item.value === repeatType) ? repeatTypes.find((item) => item.value === repeatType).label : ''}`
90 }}
91 <span v-if="repeatType === 'weekly'">
92 {{
93 `${amLabels.repeats_on} ${weekDays
94 .filter((i) => i.selected)
95 .map((w) => w.labelFull)
96 .join(', ')}`
97 }}
98 </span>
99 <span v-if="repeatType === 'monthly'">
100 {{ ` ${amLabels.repeats_on} ${monthlyTypes[repeatMonthly].label}` }}
101 <span v-if="repeatMonthly !== 0"> {{ ` ${selectedDayInMonth}` }} </span>,
102 </span>
103 <span>
104 {{
105 ` ${amLabels.repeats_from} ${getFrontedFormattedDate(props.startDate)} ${amLabels.repeats_at} ${getFrontedFormattedTime(props.startTime)}`
106 }}
107 </span>
108 </p>
109 <p>
110 <span v-if="occurrenceType === 'After'">
111 {{ amLabels.number_of_recurrences }} {{ ` ${occurrenceCount}` }}
112 </span>
113 <span v-else-if="occurrenceDate">
114 {{ amLabels.ends_on }}
115 {{ ` ${getFrontedFormattedDate(useStringFromDate(occurrenceDate))}` }}
116 </span>
117 </p>
118 </div>
119 </div>
120 </template>
121
122 <script setup>
123 import { useStore } from 'vuex'
124 import {
125 getFrontedFormattedTime,
126 getFrontedFormattedDate,
127 useStringFromDate,
128 weekDaysLocale,
129 weekDaysShortLocale,
130 } from '../../../assets/js/common/date'
131 import AmInputNumber from '../../_components/input-number/AmInputNumber'
132 import AmSelect from '../../_components/select/AmSelect'
133 import AmOption from '../../_components/select/AmOption'
134 import AmRadioGroup from '../../_components/radio/AmRadioGroup'
135 import AmRadio from '../../_components/radio/AmRadio'
136 import AmDatePickerFull from '../../_components/date-picker-full/AmDatePickerFull'
137 import moment from 'moment'
138 import { shortLocale } from '../../../plugins/settings.js'
139
140 import { computed, onMounted, ref, inject } from 'vue'
141 import { useColorTransparency } from '../../../assets/js/common/colorManipulation'
142
143 let props = defineProps({
144 service: {
145 type: Object,
146 default: () => {},
147 },
148 startDate: {
149 type: String,
150 default: '',
151 },
152 startTime: {
153 type: String,
154 default: '',
155 },
156 maxDate: {
157 type: String,
158 default: '',
159 },
160 globalClass: {
161 type: String,
162 default: '',
163 },
164 })
165
166 let amLabels = inject('amLabels')
167
168 let amCustomize = inject('amCustomize')
169
170 // * Store
171 let store = useStore()
172
173 // Container Width
174 let cWidth = inject('containerWidth', 0)
175 let checkScreen = computed(() => cWidth.value < 560 || cWidth.value - 240 < 520)
176
177 /************
178 * Computed *
179 ***********/
180 let repeatType = computed({
181 get: () => store.getters['recurring/getRepeatType'],
182 set: (val) => {
183 store.commit('recurring/setRepeatType', val)
184 },
185 })
186
187 let repeatInterval = computed({
188 get: () => store.getters['recurring/getRepeatInterval'],
189 set: (val) => {
190 store.commit('recurring/setRepeatInterval', val)
191 },
192 })
193
194 let occurrenceType = computed({
195 get: () => store.getters['recurring/getOccurrenceType'],
196 set: (val) => {
197 store.commit('recurring/setOccurrenceType', val)
198 },
199 })
200
201 let occurrenceDate = computed({
202 get: () =>
203 store.getters['recurring/getOccurrenceDate']
204 ? store.getters['recurring/getOccurrenceDate']
205 : new Date(props.startDate),
206 set: (val) => {
207 store.commit('recurring/setOccurrenceDate', val)
208 },
209 })
210
211 let occurrenceCount = computed({
212 get: () => store.getters['recurring/getOccurrenceCount'],
213 set: (val) => {
214 store.commit('recurring/setOccurrenceCount', val)
215 },
216 })
217
218 let repeatMonthly = computed({
219 get: () => store.getters['recurring/getMonthly'],
220 set: (val) => {
221 store.commit('recurring/setMonthly', val)
222 },
223 })
224
225 const repeatTypes = ref([
226 {
227 label: amLabels.value.recurrence_day,
228 labelPlural: amLabels.value.recurrence_days,
229 value: 'daily',
230 },
231 {
232 label: amLabels.value.recurrence_week,
233 labelPlural: amLabels.value.recurrence_weeks,
234 value: 'weekly',
235 },
236 {
237 label: amLabels.value.recurrence_month,
238 labelPlural: amLabels.value.recurrence_months,
239 value: 'monthly',
240 },
241 ])
242
243 const monthlyTypes = ref([
244 {
245 label: amLabels.value.recurrence_specific_date,
246 value: 0,
247 },
248 {
249 label: amLabels.value.recurrence_first,
250 value: 1,
251 },
252 {
253 label: amLabels.value.recurrence_second,
254 value: 2,
255 },
256 {
257 label: amLabels.value.recurrence_third,
258 value: 3,
259 },
260 {
261 label: amLabels.value.recurrence_fourth,
262 value: 4,
263 },
264 {
265 label: amLabels.value.recurrence_last,
266 value: 5,
267 },
268 ])
269
270 let currentDay = ref(moment(props.startDate).format('dddd').toLowerCase())
271
272 let weekDays = ref([
273 { label: 'Mon', labelFull: 'Monday', value: 'monday', selected: false },
274 { label: 'Tue', labelFull: 'Tuesday', value: 'tuesday', selected: false },
275 { label: 'Wed', labelFull: 'Wednesday', value: 'wednesday', selected: false },
276 { label: 'Thu', labelFull: 'Thursday', value: 'thursday', selected: false },
277 { label: 'Fri', labelFull: 'Friday', value: 'friday', selected: false },
278 { label: 'Sat', labelFull: 'Saturday', value: 'saturday', selected: false },
279 { label: 'Sun', labelFull: 'Sunday', value: 'sunday', selected: false },
280 ])
281
282 let selectedDayInMonth = computed(() => {
283 return weekDays.value.find(
284 (i) => i.value === moment(props.startDate, 'YYYY-MM-DD').format('dddd').toLowerCase(),
285 ).labelFull
286 })
287
288 function selectedOccurrenceDate(dateString) {
289 occurrenceDate.value = moment(dateString, 'YYYY-MM-DD').toDate()
290 }
291
292 function selectWeekDay(value, preselected = false) {
293 let weekDay = weekDays.value.find((i) => i.value === value)
294
295 weekDay.selected = preselected ? true : !weekDay.selected
296
297 store.commit('recurring/setDays', { value: value, selected: weekDay.selected })
298 }
299
300 onMounted(() => {
301 if (shortLocale) {
302 weekDays.value = weekDays.value.map((day, i) => {
303 return {
304 ...day,
305 label: weekDaysShortLocale[i],
306 labelFull: weekDaysLocale[i],
307 }
308 })
309 }
310
311 let selectedDays = store.getters['recurring/getDays'].filter((day) => day.selected)
312
313 if (selectedDays.length === 0) {
314 selectWeekDay(
315 weekDays.value.find((day) => currentDay.value && day.value === currentDay.value).value,
316 true,
317 )
318 } else {
319 selectedDays.forEach((val) => {
320 selectWeekDay(val.value, true)
321 })
322 }
323
324 if (props.service.recurringCycle !== 'all') {
325 store.commit('recurring/setRepeatType', props.service.recurringCycle)
326 }
327 })
328
329 // * Colors
330 let amColors = inject('amColors')
331 let cssVars = computed(() => {
332 return {
333 '--am-c-rs-text': amColors.value.colorMainText,
334 '--am-c-rs-text-op60': useColorTransparency(amColors.value.colorMainText, 0.6),
335 '--am-c-rs-bgr': amColors.value.colorMainBgr,
336 '--am-c-rs-primary': amColors.value.colorPrimary,
337 '--am-c-rs-inp-border': amColors.value.colorInpBorder,
338 }
339 })
340 </script>
341
342 <script>
343 export default {
344 name: 'RecurringSetup',
345 }
346 </script>
347
348 <style lang="scss">
349 .amelia-v2-booking {
350 #amelia-container {
351 // rs - Recurring Step
352 .am-fs__rs {
353 & > * {
354 $count: 10;
355 @for $i from 0 through $count {
356 &:nth-child(#{$i + 1}) {
357 animation: 600ms cubic-bezier(0.45, 1, 0.4, 1.2) #{$i * 100}ms am-animation-slide-up;
358 animation-fill-mode: both;
359 }
360 }
361 }
362
363 p {
364 margin-top: 16px;
365 font-weight: 500;
366 font-size: 15px;
367 line-height: 24px;
368 /* $shade-900 */
369 color: var(--am-c-rs-text);
370 margin-bottom: 4px;
371 }
372
373 span {
374 font-weight: 400;
375 font-size: 14px;
376 line-height: 20px;
377 /* $shade-500 */
378 color: var(--am-c-rs-text-op60);
379 }
380
381 &-every {
382 display: flex;
383 .am-select {
384 width: 103px;
385 }
386 & > div {
387 &:first-child {
388 margin-right: 8px;
389 }
390 }
391 }
392
393 &-on-week {
394 display: flex;
395 width: 100%;
396 justify-content: space-between;
397 /* $shade-900 */
398 color: var(--am-c-rs-text);
399 & > div {
400 font-weight: 400;
401 font-size: 15px;
402 line-height: 24px;
403 text-align: center;
404 margin: 0;
405 cursor: pointer;
406 /* $shade-250 */
407 border: 1px solid var(--am-c-rs-inp-border);
408 box-shadow: 0 1px 1px rgba(115, 134, 169, 0.06);
409 border-radius: 4px;
410 width: 52px;
411 padding: 8px 0;
412 align-content: center;
413
414 &.am-fs__rs-on-week-selected {
415 /* $blue-900 */
416 background: var(--am-c-rs-primary);
417 /* $blue-1000 */
418 border: 1px solid var(--am-c-rs-primary);
419 /* $white */
420 color: var(--am-c-rs-bgr);
421 }
422 }
423 }
424 &-ends {
425 display: flex;
426 margin: 16px 0;
427 max-width: 100%;
428
429 &-choose {
430 .am-radio-group {
431 display: flex;
432
433 & > div:first-child {
434 margin-bottom: 16px;
435 }
436 }
437 .am-radio {
438 margin-top: 5px;
439 & > span:nth-child(2) {
440 font-size: 15px;
441 font-weight: 400;
442 line-height: 1.6;
443 word-break: keep-all;
444 /* $shade-900 */
445 color: var(--am-c-rs-text);
446 }
447 }
448 }
449
450 &-options {
451 display: flex;
452 flex-direction: column;
453 .am-input-wrapper {
454 max-width: 174px;
455 width: 100%;
456 }
457
458 & > div:first-child {
459 margin-bottom: 16px;
460 }
461 }
462
463 &-after {
464 .am-input {
465 width: 40px;
466 }
467 span {
468 font-weight: 400;
469 font-size: 15px;
470 line-height: 40px;
471 text-align: center;
472 /* $shade-900 */
473 color: var(--am-c-rs-text);
474 @media only screen and (max-width: 375px) {
475 display: flex;
476 }
477 }
478 }
479 }
480 &-summary {
481 p,
482 span {
483 font-weight: 400;
484 font-size: 14px;
485 line-height: 20px;
486 /* $shade-600 */
487 color: var(--am-c-rs-text-op60);
488 margin: 0;
489 }
490 }
491
492 &-mobile {
493 .el-radio {
494 &__label {
495 white-space: nowrap;
496 }
497 }
498
499 .am-fs__rs-every {
500 & > div {
501 max-width: 162px;
502 width: 100%;
503 }
504 & .am-select {
505 width: 100%;
506 }
507 }
508
509 .am-fs__rs-ends {
510 &-after {
511 .am-input-wrapper {
512 max-width: 40px;
513
514 .el-input__inner {
515 text-align: center;
516 padding: 6px;
517 }
518 }
519 .el-input-number {
520 max-width: 60%;
521 }
522 }
523 }
524 }
525
526 &-on-month {
527 .am-select-wrapper {
528 max-width: 180px;
529 }
530 }
531 }
532 }
533 }
534 </style>
535