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 / public / StepForm / Congratulations / AddToCalendar.vue
ameliabooking / v3 / src / views / public / StepForm / Congratulations Last commit date
parts 2 years ago AddToCalendar.vue 2 years ago Congratulations.vue 1 year ago
AddToCalendar.vue
273 lines
1 <template>
2 <div v-if="settings.general.addToCalendar && booked && booked.data.length" class="am-fs-sb-cs" :style="cssVars">
3 <p>{{amLabels.add_to_calendar}}</p>
4 <div class="am-fs-sb-cs-cals">
5 <div v-for="(cal, index) in calendars" :key="index" class="am-fs-sb-cs-cals-cards">
6 <a
7 v-for="calPair in cal"
8 :key="calPair.type"
9 :href="calPair.links[0]"
10 :target="(calPair.type === 'apple' || calPair.type === 'outlook') ? '_self' : '_blank'"
11 :style="{borderColor : 'var(--am-c-atc-sb-text-op10)'}"
12 class="am-fs-sb-cs-cals-card"
13 @click="executeIfMultipleLinks(calPair)"
14 >
15 <div>
16 <span :class="`am-icon-${calPair.type}`"></span>
17 </div>
18 <p :style="{color : 'var(--am-c-atc-sb-text)'}">{{ calPair.label }}</p>
19 </a>
20 </div>
21
22 </div>
23 </div>
24 </template>
25
26 <script setup>
27 import { computed, inject } from 'vue'
28 import { useStore } from 'vuex'
29 import { useColorTransparency } from '../../../../assets/js/common/colorManipulation'
30
31 import {
32 settings,
33 ajaxUrl
34 } from '../../../../plugins/settings.js'
35
36 const store = useStore()
37
38 // * Labels
39 const amLabels = inject('amLabels')
40
41 let ready = computed(() => store.getters['entities/getReady'])
42
43 let booked = computed(() => store.getters['booking/getBooked'])
44
45 let calendars = computed(() => {
46 return ready.value && booked.value ?
47 [
48 [
49 getCalendarLinkData(booked.value.data, 'google'),
50 {
51 type: 'outlook',
52 label: 'Outlook',
53 links: [
54 ajaxUrl + '/bookings/ics/' + (booked.value.data.length ? booked.value.data[0].bookingId : 0) +
55 '&type=' + (booked.value.type === 'package' ? 'appointment' : booked.value.type) + getRecurringIdsParams(booked.value) +
56 '&token=' + booked.value.token
57 ]
58 }
59 ],
60 [
61 getCalendarLinkData(booked.value.data, 'yahoo'),
62 {
63 type: 'apple',
64 label: 'Apple',
65 links: [
66 ajaxUrl + '/bookings/ics/' + (booked.value.data.length ? booked.value.data[0].bookingId : 0) +
67 '&type=' + (booked.value.type === 'package' ? 'appointment' : booked.value.type) + getRecurringIdsParams(booked.value) +
68 '&token=' + booked.value.token
69 ]
70 }
71 ]
72 ] : []
73 })
74
75 function executeIfMultipleLinks (calendar) {
76 if (calendar.links.length > 1) {
77 let popupBlocked = false
78 setTimeout(function () {
79 calendar.links.forEach(function (link, index) {
80 if (index !== 0) {
81 if (!popupBlocked) {
82 let newWin = window.open(link, '_blank')
83 try {
84 newWin.addEventListener('load', function () {})
85 }
86 catch (e) {
87 popupBlocked = true
88 alert(amLabels.value.disable_popup_blocker)
89 }
90 } else {
91 window.open(link, '_blank')
92 }
93 }
94 })
95 }, 1000)
96 }
97
98 return true
99 }
100
101 function getRecurringIdsParams (booked) {
102 let recurringIdsParams = ''
103
104 booked.data.forEach((item, index) => {
105 if (index > 0) {
106 recurringIdsParams += '&recurring[]=' + item.bookingId
107 }
108 })
109
110 return recurringIdsParams
111 }
112
113 function getCalendarLinkData(eventData, type) {
114 let links = []
115
116 switch (type) {
117 case ('yahoo'):
118 eventData.forEach(function (data) {
119 let location = data.locationId?
120 store.getters['entities/getLocation'](
121 data.locationId
122 ) : ''
123
124 let address = data.cfAddress ? data.cfAddress : (location ? (location.address ? location.address : location.name) : '')
125
126 let duration = (data.utcEnd.getTime() - data.utcStart.getTime()) / (60 * 1000)
127
128 duration = (duration < 600 ? '0' + Math.floor((duration / 60)) : Math.floor((duration / 60)) + '') +
129 (duration % 60 < 10 ? '0' + duration % 60 : duration % 60 + '')
130
131 let st = formatTime(new Date(data.utcStart - (data.utcStart.getTimezoneOffset() * (60 * 1000))))
132
133 links.push(
134 encodeURI([
135 'http://calendar.yahoo.com/?v=60&view=d&type=20',
136 '&title=' + (data.title || ''),
137 '&st=' + st,
138 '&dur=' + (duration || ''),
139 '&desc=' + (data.description || ''),
140 '&in_loc=' + address
141 ].join(''))
142 )
143 })
144
145 return {
146 type: 'yahoo',
147 label: 'Yahoo',
148 links: links
149 }
150
151 case ('google'):
152 eventData.forEach(function (data) {
153 let location = data.locationId?
154 store.getters['entities/getLocation'](
155 data.locationId
156 ) : ''
157
158 let address = data.cfAddress ? data.cfAddress : (location ? (location.address ? location.address : location.name) : '')
159
160 let startTime = formatTime(data.utcStart)
161 let endTime = formatTime(data.utcEnd)
162
163 links.push(
164 encodeURI([
165 'https://www.google.com/calendar/render',
166 '?action=TEMPLATE',
167 '&text=' + (data.title || ''),
168 '&dates=' + (startTime || ''),
169 '/' + (endTime || ''),
170 '&details=' + (data.description || ''),
171 '&location=' + address,
172 '&sprop=&sprop=name:'
173 ].join(''))
174 )
175 })
176
177 return {
178 type: 'google',
179 label: 'Google',
180 links: links
181 }
182 }
183 }
184
185 function formatTime (date) {
186 return date.toISOString().replace(/-|:|\.\d+/g, '')
187 }
188
189 let amColors = inject('amColors')
190
191 const cssVars = computed(() => {
192 return {
193 '--am-c-atc-sb-text-op10': useColorTransparency(amColors.value.colorSbText, 0.1),
194 '--am-c-atc-sb-text-op5': useColorTransparency(amColors.value.colorSbText, 0.05)
195 }
196 })
197
198 </script>
199
200 <script>
201 export default {
202 name: 'AddToCalendar'
203 }
204 </script>
205
206 <style lang="scss">
207 .amelia-v2-booking #amelia-container {
208 .am-fs-sb-cs {
209 --am-c-atc-sb-text: var(--am-c-sb-text);
210 & > p {
211 text-align: center;
212 font-size: 14px;
213 line-height: 20px;
214 color: var(--am-c-atc-sb-text);
215 font-weight: 400;
216 margin-bottom: 16px;
217 }
218 &-cals{
219 display: flex;
220 flex-wrap: wrap;
221 justify-content: center;
222 // gap: 8px;
223 &-cards {
224 display: flex;
225 flex-direction: row;
226 // gap: 8px;
227 align-items: center;
228 justify-content: center;
229 margin-bottom: 8px;
230 }
231 &-card {
232 display: flex;
233 flex-direction: column;
234 justify-content: center;
235 align-items: center;
236 width: 86px;
237 text-decoration: none;
238 color: var(--am-c-atc-sb-text);
239 border: 1px solid;
240 border-radius: 4px;
241 background-color: var(--am-c-atc-sb-text-op5);
242 padding: 16px 0 8px;
243 margin-right: 8px;
244 box-sizing: border-box;
245 box-shadow: 0 1px 1px rgba(115, 134, 169, 0.06);
246
247 p {
248 font-style: normal;
249 font-weight: 500;
250 font-size: 15px;
251 line-height: 24px;
252 }
253 div {
254 display: flex;
255 height: 24px;
256 align-items: center;
257 span {
258 font-size: 24px;
259 color: var(--am-c-atc-sb-text);
260 }
261 .am-icon-yahoo {
262 font-size: 17px
263 }
264 }
265 &:hover {
266 background-color: var(--am-c-atc-sb-text-op10);
267 }
268 }
269 }
270 }
271 }
272 </style>
273