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