PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / trunk
Booking for Appointments and Events Calendar – Amelia vtrunk
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 / _fields / ServiceFormField.vue
ameliabooking / v3 / src / views / public / StepForm / _fields Last commit date
EmployeeFormField.vue 1 day ago LocationFormField.vue 1 day ago ServiceFormField.vue 1 day ago
ServiceFormField.vue
290 lines
1 <template>
2 <el-form-item
3 v-if="
4 store.state.entities.services.length > 1 &&
5 store.state.entities.preselected.service.length !== 1
6 "
7 class="am-fs__init-form__item"
8 :prop="store.state.entities.categories.length > 1 ? 'service' : 'onlyService'"
9 :class="'am-service-select'"
10 label-position="top"
11 >
12 <template #label>
13 <span class="am-fs__init-form__label"> {{ amLabels.service_colon }}: </span>
14 </template>
15 <AmAdvancedSelect
16 v-if="store.state.entities.categories.length > 1"
17 v-model="initFormData.service"
18 :filterable="props.filterable"
19 :options="categoryOptions"
20 :props-data="advSelectProps"
21 :placeholder="amLabels.select_service"
22 :category-name="amLabels.dropdown_category_heading"
23 :sub-category-name="amLabels.dropdown_items_heading"
24 :empty-state-string="amLabels.dropdown_empty"
25 :tax-options="taxServicesOptions"
26 :tax-label="`+${amLabels.total_tax_colon}`"
27 :tax-label-incl="amLabels.incl_tax"
28 :tax-visible="props.taxVisible"
29 :aria-label="amLabels.select_service"
30 @change="changeService"
31 ></AmAdvancedSelect>
32 <AmSelect
33 v-else
34 v-model="initFormData.onlyService"
35 clearable
36 :filterable="props.filterable"
37 :popper-class="'am-service-dropdown'"
38 :placeholder="amLabels.select_service"
39 :aria-label="amLabels.select_service"
40 :no-match-text="amLabels.dropdown_empty"
41 :no-data-text="amLabels.dropdown_empty"
42 :filter-method="filterService"
43 @change="changeOnlyService"
44 >
45 <AmOption
46 v-for="service in filteredServices"
47 :key="service.id"
48 :value="service.id"
49 :label="service.name"
50 >
51 <div class="am-select-service">
52 <span class="am-select-service-name">
53 {{ service.name }}
54 </span>
55 <span v-if="service.price > 0" class="am-select-service-price">
56 {{ useFormattedPrice(service.price) }}
57 <span v-if="taxVisibility(service.id)" class="am-select-service-tax">
58 <template v-if="taxServicesOptions.find((a) => a.id === service.id).excluded">
59 {{ `+${amLabels.total_tax_colon}` }}
60 </template>
61 <template v-else>
62 {{ `${amLabels.incl_tax}` }}
63 </template>
64 </span>
65 </span>
66 </div>
67 </AmOption>
68 </AmSelect>
69 </el-form-item>
70 </template>
71
72 <script setup>
73 import { useStore } from 'vuex'
74 import { computed, inject, reactive, ref } from 'vue'
75 import AmSelect from '../../../_components/select/AmSelect'
76 import AmOption from '../../../_components/select/AmOption'
77 import AmAdvancedSelect from '../../../_components/advanced-select/AmAdvancedSelect'
78 import useAction from '../../../../assets/js/public/actions'
79 import { useFormattedPrice } from '../../../../assets/js/common/formatting'
80 import { useEntityTax } from '../../../../assets/js/common/pricing'
81
82 let props = defineProps({
83 filterable: {
84 type: Boolean,
85 default: true,
86 },
87 taxVisible: {
88 type: Boolean,
89 default: true,
90 },
91 })
92
93 // * Amelia Settings
94 const amSettings = inject('settings')
95
96 // * Store
97 let store = useStore()
98
99 // * Labels
100 const amLabels = inject('amLabels')
101
102 // * Sidebar steps
103 let { sidebarDataCollector } = inject('sidebarStepsFunctions', {
104 sidebarDataCollector: () => {},
105 })
106
107 const { changeInitStepDataService } = inject('initDataChanges', {
108 changeInitStepDataService: () => {},
109 })
110
111 const { footerBtnDisabledUpdater } = inject('changingStepsFunctions', {
112 footerBtnDisabledUpdater: () => {},
113 })
114
115 // Container Width
116 let cWidth = inject('containerWidth', 0)
117 let checkScreen = computed(() => cWidth.value < 560 || cWidth.value - 240 < 520)
118
119 /**
120 * * Category - Service Block
121 */
122
123 let initFormData = inject('initFormData')
124
125 // * Service select props
126 let advSelectProps = reactive({
127 expandTrigger: checkScreen.value ? 'click' : 'hover',
128 multiple: false,
129 checkStrictly: false,
130 emitPath: true,
131 lazy: false,
132 value: 'id',
133 label: 'name',
134 children: 'serviceList',
135 disabled: 'disabled',
136 leaf: 'leaf',
137 })
138
139 // * Set Category Options from entities
140 let categoryOptions = computed(() =>
141 store.getters['entities/filteredCategories'](
142 Object.assign(store.getters['booking/getSelection'], { serviceId: null, categoryId: null }),
143 ),
144 )
145
146 // * Set Service Options from entities
147 let serviceOptions = computed(() =>
148 store.getters['entities/filteredServices'](
149 Object.assign(store.getters['booking/getSelection'], { serviceId: null, categoryId: null }),
150 ),
151 )
152 let taxServicesOptions = computed(() => {
153 let arr = []
154
155 if (amSettings.payments.taxes.enabled) {
156 serviceOptions.value.forEach((service) => {
157 let tax = useEntityTax(store, service.id, 'service')
158 if (tax) {
159 let obj = {
160 id: service.id,
161 excluded: amSettings.payments.taxes.excluded,
162 }
163 arr.push(obj)
164 }
165 })
166 }
167
168 return arr
169 })
170
171 // * Filter service
172 let queryLower = ref('')
173
174 function filterService(query) {
175 queryLower.value = query.toLowerCase()
176 }
177
178 let filteredServices = computed(() => {
179 if (queryLower.value) {
180 return serviceOptions.value.filter((item) => {
181 return item.name.toLowerCase().includes(queryLower.value)
182 })
183 }
184
185 return serviceOptions.value
186 })
187
188 /**
189 * Change Service function
190 * @param val
191 */
192 let changeService = (val) => {
193 let serviceData = {
194 reference: 'service',
195 // position will depends on fields order
196 position: 0,
197 value: '',
198 }
199
200 if (Array.isArray(val)) {
201 serviceData.value = categoryOptions.value
202 .find((item) => item.id === val[0])
203 .serviceList.find((item) => item.id === val[1]).name
204 }
205
206 if (!store.getters['booking/getPackageId']) {
207 changeInitStepDataService()
208 }
209
210 useAction(store, {}, 'SelectService', 'appointment', null, null)
211
212 sidebarDataCollector(serviceData)
213
214 footerBtnDisabledUpdater(false)
215 }
216
217 function changeOnlyService(val) {
218 let serviceData = {
219 reference: 'service',
220 // position will depends on fields order
221 position: 0,
222 value: '',
223 }
224
225 if (val) {
226 let service = serviceOptions.value.find((item) => item.id === val)
227 serviceData.value = service ? service.name : ''
228 }
229
230 changeInitStepDataService()
231
232 useAction(store, {}, 'SelectService', 'appointment', null, null)
233
234 sidebarDataCollector(serviceData)
235 }
236
237 function taxVisibility(id) {
238 return props.taxVisible && !!taxServicesOptions.value.filter((a) => a.id === id).length
239 }
240 </script>
241
242 <script>
243 export default {
244 name: 'ServiceFormField',
245 }
246 </script>
247
248 <style lang="scss">
249 .am-service-dropdown {
250 .am-select-service {
251 --am-c-ss-item-name: var(--am-c-option-text);
252 --am-c-ss-item-price: var(--am-c-option-selected);
253
254 width: 100%;
255 display: flex;
256 align-items: center;
257 justify-content: space-between;
258
259 &-name {
260 display: block;
261 font-size: 14px;
262 font-weight: 400;
263 line-height: 1.43;
264 color: var(--am-c-ss-item-name);
265 white-space: nowrap;
266 overflow: hidden;
267 text-overflow: ellipsis;
268 }
269
270 &-price {
271 font-size: 14px;
272 font-weight: 400;
273 line-height: 1.714;
274 color: var(--am-c-ss-item-price);
275 }
276
277 &-tax {
278 display: inline-flex;
279 font-size: 14px;
280 font-weight: 400;
281 line-height: 1;
282 color: var(--am-c-ss-item-price);
283 background-color: var(--am-c-option-selected-op10);
284 border-radius: 10px;
285 padding: 3px 8px;
286 }
287 }
288 }
289 </style>
290