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 / public / EventForm / Common / EventTickets / EventTickets.vue
ameliabooking / v3 / src / views / public / EventForm / Common / EventTickets Last commit date
EventTickets.vue 1 month ago
EventTickets.vue
264 lines
1 <template>
2 <div
3 class="am-elt"
4 :class="props.globalClass"
5 :style="cssVars"
6 role="region"
7 :aria-labelledby="ticketsHeadingId"
8 >
9 <div class="am-elt__header">
10 <div class="am-elt__header-left">
11 <p :id="ticketsHeadingId" class="am-elt__heading">
12 {{ amLabels.event_tickets }}
13 </p>
14 <p>
15 {{ amLabels.event_tickets_context }}
16 </p>
17 </div>
18 <div v-if="capacity" class="am-elt__header-right">
19 <p class="am-elt__heading">
20 {{ amLabels.event_ticket_types }}
21 </p>
22 <p>
23 <span>
24 {{ capacity - totalSold - ticketGlobalSpots }}
25 </span>
26 {{
27 `${capacity - totalSold - ticketGlobalSpots === 1 ? amLabels.event_ticket_left : amLabels.event_tickets_left}`
28 }}
29 </p>
30 </div>
31 </div>
32 <template v-for="ticket in tickets" :key="ticket.id">
33 <EventTicket
34 :ticket="ticket"
35 :capacity="capacity ? capacity - totalSold : null"
36 :extra-people="extraPeople"
37 :customized-labels="amLabels"
38 :in-dialog="props.inDialog"
39 />
40 </template>
41 </div>
42 </template>
43
44 <script setup>
45 // * Parts
46 import EventTicket from '../../Common/Parts/EventTicket.vue'
47
48 // * Import from Vue
49 import { ref, reactive, computed, inject, watchEffect, onMounted } from 'vue'
50
51 // * Import from Vuex
52 import { useStore } from 'vuex'
53
54 // * Composables
55 import { useColorTransparency } from '../../../../../assets/js/common/colorManipulation.js'
56 import useAction from '../../../../../assets/js/public/actions'
57
58 let props = defineProps({
59 globalClass: {
60 type: String,
61 default: '',
62 },
63 inDialog: {
64 type: Boolean,
65 default: false,
66 },
67 })
68
69 // TODO: We need to use import { useId } from 'vue' when we upgrade to Vue 3.5
70 const uid = Math.random().toString(36).slice(2, 8)
71 const ticketsHeadingId = `am-elt-heading-${uid}`
72
73 // * Step functionality
74 let { nextStep, footerButtonClicked, footerBtnDisabled, footerButtonReset } = inject(
75 'changingStepsFunctions',
76 {
77 nextStep: () => {},
78 footerButtonReset: () => {},
79 footerButtonClicked: ref(false),
80 footerBtnDisabled: ref(false),
81 },
82 )
83
84 // * Define store
85 const store = useStore()
86
87 // * Root Settings
88 const amSettings = inject('settings')
89
90 // * Event form customization
91 let customizedDataForm = inject('customizedDataForm')
92
93 // * Labels
94 let labels = inject('labels')
95
96 // * local language short code
97 const localLanguage = inject('localLanguage')
98
99 // * if local lang is in settings lang
100 let langDetection = computed(() => amSettings.general.usedLanguages.includes(localLanguage.value))
101
102 // * Computed labels
103 let amLabels = computed(() => {
104 let computedLabels = reactive({ ...labels })
105
106 if (customizedDataForm.value.tickets.translations) {
107 let customizedLabels = customizedDataForm.value.tickets.translations
108 Object.keys(customizedLabels).forEach((labelKey) => {
109 if (customizedLabels[labelKey][localLanguage.value] && langDetection.value) {
110 computedLabels[labelKey] = customizedLabels[labelKey][localLanguage.value]
111 } else if (customizedLabels[labelKey].default) {
112 computedLabels[labelKey] = customizedLabels[labelKey].default
113 }
114 })
115 }
116 return computedLabels
117 })
118
119 // * isWaitingList available
120 let isWaitingList = computed(() => store.getters['eventWaitingListOptions/getAvailability'])
121
122 // * Event Tickets
123 let tickets = computed(() => {
124 const sorted = [...(store.getters['tickets/getTicketsData'] || [])]
125 if (window?.ameliaActions?.EventTickets) {
126 let override
127 useAction(
128 store,
129 {
130 tickets: sorted,
131 setTickets: (arr) => {
132 if (Array.isArray(arr)) override = arr
133 },
134 },
135 'EventTickets',
136 'event',
137 null,
138 null,
139 )
140 return Array.isArray(override) ? override : sorted
141 }
142 return sorted
143 })
144
145 // * Event Max Capacity
146 let capacity = computed(() => store.getters['tickets/getMaxCustomCapacity'])
147
148 // * Extra People
149 let extraPeople = computed(() => store.getters['tickets/getMaxExtraPeople'])
150
151 let ticketGlobalSpots = computed(() => store.getters['tickets/getEventGlobalSpots'])
152 let totalSold = computed(() => {
153 let spots = 0
154 tickets.value.forEach((t) => {
155 spots += isWaitingList.value ? t.waiting : t.sold
156 })
157
158 return spots
159 })
160
161 let sum = computed(() => store.getters['tickets/getTicketsSum'])
162
163 // * Watching when footer button was clicked
164 watchEffect(() => {
165 if (footerButtonClicked.value) {
166 footerButtonReset()
167 nextStep()
168 }
169
170 footerBtnDisabled.value = sum.value < 1
171 })
172
173 onMounted(() => {
174 store.commit('setLoading', false)
175 })
176
177 // * Fonts
178 let amFonts = inject('amFonts')
179
180 // * Colors
181 let amColors = inject('amColors')
182
183 // * Css Vars
184 let cssVars = computed(() => {
185 return {
186 '--am-c-font-family': amFonts.value.fontFamily,
187 '--am-c-elt-text': amColors.value.colorMainText,
188 '--am-c-elt-text-op90': useColorTransparency(amColors.value.colorMainText, 0.9),
189 '--am-c-elt-text-op80': useColorTransparency(amColors.value.colorMainText, 0.8),
190 }
191 })
192 </script>
193
194 <script>
195 export default {
196 name: 'EventTickets',
197 key: 'tickets',
198 label: 'event_select_tickets',
199 }
200 </script>
201
202 <style lang="scss">
203 .amelia-v2-booking #amelia-container {
204 .am-elt {
205 & > * {
206 font-family: var(--am-font-family), sans-serif;
207 box-sizing: border-box;
208 word-break: break-word;
209
210 $count: 100;
211 @for $i from 0 through $count {
212 &:nth-child(#{$i + 1}) {
213 animation: 600ms cubic-bezier(0.45, 1, 0.4, 1.2) #{$i * 100}ms am-animation-slide-up;
214 animation-fill-mode: both;
215 }
216 }
217 }
218
219 &__header {
220 display: flex;
221 flex-direction: column;
222 margin: 0 0 16px;
223
224 &-left {
225 p {
226 font-size: 13px;
227 font-weight: 400;
228 line-height: 18px;
229 color: var(--am-c-elt-text-op80); /* $shade-700 */
230 margin: 0;
231 }
232
233 p.am-elt__heading {
234 font-size: 15px;
235 font-weight: 500;
236 line-height: 24px;
237 //text-transform: uppercase;
238 color: var(--am-c-elt-text); /* $shade-900 */
239 margin: 0;
240 }
241 }
242
243 &-right {
244 margin-top: 16px;
245 display: flex;
246 justify-content: space-between;
247
248 p {
249 font-weight: 400;
250 font-size: 15px;
251 line-height: 1.6;
252 color: var(--am-c-elt-text-op90); /* $shade-800 */
253
254 span {
255 font-weight: 700;
256 margin-right: 4px;
257 }
258 }
259 }
260 }
261 }
262 }
263 </style>
264