common
5 days ago
BringingAnyone.vue
5 days ago
CartStep.vue
5 days ago
Congratulations.vue
5 days ago
DateTimeStep.vue
5 days ago
EmployeeStep.vue
5 days ago
Extras.vue
5 days ago
InfoStep.vue
5 days ago
InitStep.vue
5 days ago
LocationStep.vue
5 days ago
PackageAppointmentsListStep.vue
5 days ago
PackageAppointmentsStep.vue
5 days ago
PackageInfoStep.vue
5 days ago
PackageStep.vue
5 days ago
PaymentStep.vue
5 days ago
RecurringStep.vue
5 days ago
RecurringSummary.vue
5 days ago
ServiceStep.vue
5 days ago
RecurringSummary.vue
420 lines
| 1 | <template> |
| 2 | <div class="am-fs__ro" :class="props.globalClass" :style="cssVars"> |
| 3 | <div class="am-fs__ro-warning"> |
| 4 | <AmAlert |
| 5 | type="warning" |
| 6 | :title="labelsDisplay('recurring_unavailable_slots')" |
| 7 | :description="`2 ${labelsDisplay('recurring_alert_content')}`" |
| 8 | :show-icon="true" |
| 9 | :closable="false" |
| 10 | ></AmAlert> |
| 11 | </div> |
| 12 | <AmCollapse> |
| 13 | <AmCollapseItem |
| 14 | v-for="(appointment, index) in serviceSelection.list" |
| 15 | :ref="(el) => (recurringList[index] = el)" |
| 16 | :key="index" |
| 17 | :name="index.toString()" |
| 18 | class="am-fs__ro-app-items" |
| 19 | :class="{ 'am-fs__ro-app-unavailable': appointment.isSubstitute }" |
| 20 | :side="true" |
| 21 | :delay="500" |
| 22 | :collapsable="false" |
| 23 | > |
| 24 | <template #heading> |
| 25 | <div class="am-fs__ro-app-heading"> |
| 26 | <span> |
| 27 | {{ |
| 28 | index + |
| 29 | 1 + |
| 30 | '. ' + |
| 31 | (appointment.date && appointment.time |
| 32 | ? appointment.date + ' ' + appointment.time |
| 33 | : labelsDisplay('recurring_chose_date')) |
| 34 | }} |
| 35 | </span> |
| 36 | </div> |
| 37 | </template> |
| 38 | |
| 39 | <template #icon-end> |
| 40 | <SideMenu :cancel-label="labelsDisplay('recurring_delete')"></SideMenu> |
| 41 | </template> |
| 42 | |
| 43 | <template #default> |
| 44 | <div class="am-fs__ro-app-content"> |
| 45 | <AmAdvancedSlotCalendar |
| 46 | v-if="showCalendar" |
| 47 | :id="0" |
| 48 | :slots="calendarEvents" |
| 49 | :calendar-minimum-date="moment().format('YYYY-MM-DD hh:mm')" |
| 50 | :calendar-maximum-date="moment().add(1, 'year').format('YYYY-MM-DD hh:mm')" |
| 51 | :time-zone=" |
| 52 | features.timeZones && |
| 53 | amCustomize[pageRenderKey].recurringSummary.options.timeZoneVisibility.visibility |
| 54 | " |
| 55 | :show-estimated-pricing=" |
| 56 | amCustomize[pageRenderKey].recurringSummary.options.estimatedPricingVisibility |
| 57 | .visibility |
| 58 | " |
| 59 | :show-indicator-pricing=" |
| 60 | amCustomize[pageRenderKey].recurringSummary.options.indicatorPricingVisibility |
| 61 | .visibility |
| 62 | " |
| 63 | :show-slot-pricing=" |
| 64 | amCustomize[pageRenderKey].recurringSummary.options.slotPricingVisibility.visibility |
| 65 | " |
| 66 | :show-people-waiting="false" |
| 67 | :show-calendar-date-busyness=" |
| 68 | amCustomize[pageRenderKey].recurringSummary.options.calendarDateBusynessVisibility |
| 69 | ?.visibility ?? true |
| 70 | " |
| 71 | :label-slots-selected="labelsDisplay('recurring_slots_selected')" |
| 72 | :period-pricing="periodPricing" |
| 73 | :busyness="previewCalendarBusyness" |
| 74 | ></AmAdvancedSlotCalendar> |
| 75 | </div> |
| 76 | </template> |
| 77 | </AmCollapseItem> |
| 78 | </AmCollapse> |
| 79 | </div> |
| 80 | </template> |
| 81 | |
| 82 | <script setup> |
| 83 | import { computed, inject, provide, ref, onMounted } from 'vue' |
| 84 | import moment from 'moment' |
| 85 | import AmAlert from '../../../../_components/alert/AmAlert' |
| 86 | import AmCollapse from '../../../../_components/collapse/AmCollapse' |
| 87 | import AmCollapseItem from '../../../../_components/collapse/AmCollapseItem' |
| 88 | import SideMenu from '../../../../public/StepForm/RecurringStep/parts/SideMenu.vue' |
| 89 | import AmAdvancedSlotCalendar from '../../../../_components/advanced-slot-calendar/AmAdvancedSlotCalendar.vue' |
| 90 | import { useColorTransparency } from '../../../../../assets/js/common/colorManipulation' |
| 91 | import { useReactiveCustomize } from '../../../../../assets/js/admin/useReactiveCustomize.js' |
| 92 | |
| 93 | let props = defineProps({ |
| 94 | globalClass: { |
| 95 | type: String, |
| 96 | default: '', |
| 97 | }, |
| 98 | }) |
| 99 | |
| 100 | // Container Width |
| 101 | let cWidth = inject('containerWidth', 0) |
| 102 | |
| 103 | // * Plugin Licence |
| 104 | let licence = inject('licence') |
| 105 | |
| 106 | // * Features |
| 107 | let features = inject('features') |
| 108 | |
| 109 | // * Languages |
| 110 | let langKey = inject('langKey') |
| 111 | |
| 112 | // * Global Labels |
| 113 | let amLabels = inject('labels') |
| 114 | |
| 115 | let pageRenderKey = inject('pageRenderKey') |
| 116 | const { amCustomize } = useReactiveCustomize() |
| 117 | |
| 118 | let previewCalendarBusyness = computed(() => { |
| 119 | const opts = amCustomize.value[pageRenderKey.value].recurringSummary.options |
| 120 | if (!(opts.calendarDateBusynessVisibility?.visibility ?? true)) { |
| 121 | return {} |
| 122 | } |
| 123 | const base = moment().startOf('day') |
| 124 | return { |
| 125 | [base.clone().add(6, 'days').format('YYYY-MM-DD')]: 40, |
| 126 | [base.clone().add(20, 'days').format('YYYY-MM-DD')]: 82, |
| 127 | } |
| 128 | }) |
| 129 | |
| 130 | // * Label computed function |
| 131 | function labelsDisplay(label) { |
| 132 | let computedLabel = computed(() => { |
| 133 | return amCustomize.value[pageRenderKey.value].recurringSummary.translations && |
| 134 | amCustomize.value[pageRenderKey.value].recurringSummary.translations[label] && |
| 135 | amCustomize.value[pageRenderKey.value].recurringSummary.translations[label][langKey.value] |
| 136 | ? amCustomize.value[pageRenderKey.value].recurringSummary.translations[label][langKey.value] |
| 137 | : amLabels[label] |
| 138 | }) |
| 139 | |
| 140 | return computedLabel.value |
| 141 | } |
| 142 | |
| 143 | let serviceSelection = ref({ |
| 144 | list: [ |
| 145 | { date: moment().format('YYYY-MM-DD'), time: '13:05' }, |
| 146 | { |
| 147 | date: moment().add(3, 'days').format('YYYY-MM-DD'), |
| 148 | time: '13:05', |
| 149 | isSubstitute: false, |
| 150 | }, |
| 151 | { date: '', time: '', isSubstitute: true }, |
| 152 | { |
| 153 | date: moment().add(9, 'days').format('YYYY-MM-DD'), |
| 154 | time: '13:05', |
| 155 | isSubstitute: true, |
| 156 | }, |
| 157 | ], |
| 158 | }) |
| 159 | |
| 160 | /***************** |
| 161 | * Calendar Data * |
| 162 | ****************/ |
| 163 | let showCalendar = ref(false) |
| 164 | |
| 165 | let periodPricing = ref({}) |
| 166 | |
| 167 | function getCustomPeriodsPricing() { |
| 168 | let result = {} |
| 169 | let today = moment() |
| 170 | |
| 171 | let current = today.clone() |
| 172 | |
| 173 | for (let i = 0; i <= 31; i++) { |
| 174 | let type = |
| 175 | current.day() === 6 || current.day() === 0 |
| 176 | ? 'high' |
| 177 | : current.day() === 1 || current.day() === 2 |
| 178 | ? 'low' |
| 179 | : 'mid' |
| 180 | |
| 181 | result[current.format('YYYY-MM-DD')] = { |
| 182 | type: type, |
| 183 | slots: { |
| 184 | '09:00': { |
| 185 | type: type === 'mid' ? 'low' : type, |
| 186 | price: type === 'mid' ? 5 : type === 'low' ? 5 : 15, |
| 187 | }, |
| 188 | '09:30': { |
| 189 | type: type === 'mid' ? 'low' : type, |
| 190 | price: type === 'mid' ? 5 : type === 'low' ? 5 : 15, |
| 191 | }, |
| 192 | '10:00': { |
| 193 | type: type, |
| 194 | price: type === 'mid' ? 10 : type === 'low' ? 5 : 15, |
| 195 | }, |
| 196 | '10:30': { |
| 197 | type: type, |
| 198 | price: type === 'mid' ? 10 : type === 'low' ? 5 : 15, |
| 199 | }, |
| 200 | '11:00': { |
| 201 | type: type, |
| 202 | price: type === 'mid' ? 10 : type === 'low' ? 5 : 15, |
| 203 | }, |
| 204 | '11:30': { |
| 205 | type: type, |
| 206 | price: type === 'mid' ? 10 : type === 'low' ? 5 : 15, |
| 207 | }, |
| 208 | '12:00': { |
| 209 | type: type === 'mid' ? 'high' : type, |
| 210 | price: type === 'mid' ? 15 : type === 'high' ? 15 : 5, |
| 211 | }, |
| 212 | '12:30': { |
| 213 | type: type === 'mid' ? 'high' : type, |
| 214 | price: type === 'mid' ? 15 : type === 'high' ? 15 : 5, |
| 215 | }, |
| 216 | }, |
| 217 | } |
| 218 | |
| 219 | current.add(1, 'day') |
| 220 | } |
| 221 | |
| 222 | return { |
| 223 | price: { |
| 224 | low: 5, |
| 225 | mid: 10, |
| 226 | high: 15, |
| 227 | uniqueMin: true, |
| 228 | uniqueMid: true, |
| 229 | uniqueMax: true, |
| 230 | }, |
| 231 | dates: result, |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | let calendarEvents = ref([]) |
| 236 | |
| 237 | let calendarEventDate = ref('') |
| 238 | |
| 239 | let calendarServiceDuration = ref('0') |
| 240 | |
| 241 | let today = moment().format('YYYY-MM-DD') |
| 242 | |
| 243 | for (let i = 0; i <= 31; i++) { |
| 244 | let block = { |
| 245 | display: 'background', |
| 246 | extendedProps: { |
| 247 | slots: { '09:00': [7, 3] }, |
| 248 | slotsAvailable: 1, |
| 249 | slotsTotal: 100, |
| 250 | }, |
| 251 | start: moment(today).add(i, 'd').format('YYYY-MM-DD'), |
| 252 | title: 'e', |
| 253 | } |
| 254 | calendarEvents.value.push(block) |
| 255 | } |
| 256 | |
| 257 | let calendarEventSlots = ref([]) |
| 258 | let calendarEventSlot = ref('') |
| 259 | let calendarStartDate = ref(moment().format('YYYY-MM-DD')) |
| 260 | let calendarChangeSideBar = ref(true) |
| 261 | let calendarWaitingListSlots = ref([]) |
| 262 | provide('calendarEvents', calendarEvents) |
| 263 | provide('calendarEventDate', calendarEventDate) |
| 264 | provide('calendarEventSlots', calendarEventSlots) |
| 265 | provide('calendarEventSlot', calendarEventSlot) |
| 266 | provide('calendarStartDate', calendarStartDate) |
| 267 | provide('calendarChangeSideBar', calendarChangeSideBar) |
| 268 | provide('calendarServiceDuration', calendarServiceDuration) |
| 269 | provide('calendarWaitingListSlots', calendarWaitingListSlots) |
| 270 | |
| 271 | /********* |
| 272 | * Other * |
| 273 | ********/ |
| 274 | let calendarSlotDuration = 3600 |
| 275 | |
| 276 | provide('calendarSlotDuration', calendarSlotDuration) |
| 277 | |
| 278 | let recurringList = ref([]) |
| 279 | |
| 280 | onMounted(() => { |
| 281 | periodPricing.value = |
| 282 | !licence.isBasic && !licence.isStarter && !licence.isLite ? getCustomPeriodsPricing() : {} |
| 283 | |
| 284 | showCalendar.value = true |
| 285 | |
| 286 | recurringList.value[0].contentVisibility = true |
| 287 | }) |
| 288 | |
| 289 | let amColors = inject('amColors') |
| 290 | |
| 291 | let cssVars = computed(() => { |
| 292 | return { |
| 293 | '--am-c-ro-warning-op10': useColorTransparency(amColors.value.colorWarning, 0.1), |
| 294 | '--am-c-ro-warning-op60': useColorTransparency(amColors.value.colorWarning, 0.6), |
| 295 | } |
| 296 | }) |
| 297 | </script> |
| 298 | |
| 299 | <script> |
| 300 | export default { |
| 301 | name: 'RecurringSummary', |
| 302 | key: 'recurringSummary', |
| 303 | sidebarData: { |
| 304 | label: 'recurring_summary', |
| 305 | icon: 'calendar-pencil', |
| 306 | stepSelectedData: [], |
| 307 | finished: false, |
| 308 | selected: false, |
| 309 | }, |
| 310 | } |
| 311 | </script> |
| 312 | |
| 313 | <style lang="scss"> |
| 314 | #amelia-app-backend-new { |
| 315 | #amelia-container { |
| 316 | // ro - recurring overview |
| 317 | .am-fs__ro { |
| 318 | --am-c-ro-text: var(--am-c-main-text); |
| 319 | --am-c-ro-success: var(--am-c-success); |
| 320 | |
| 321 | &-warning { |
| 322 | .am-alert { |
| 323 | i { |
| 324 | font-size: 20px; |
| 325 | } |
| 326 | margin-bottom: 16px; |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | .am-collapse-item { |
| 331 | &__heading { |
| 332 | transition-delay: 0.5s; |
| 333 | |
| 334 | &-side { |
| 335 | transition-delay: 0s; |
| 336 | } |
| 337 | } |
| 338 | } |
| 339 | |
| 340 | &-app { |
| 341 | &-unavailable { |
| 342 | .am-collapse-item__heading { |
| 343 | /* $yellow-300 */ |
| 344 | background-color: var(--am-c-ro-warning-op10); |
| 345 | /* $yellow-600 */ |
| 346 | border: 1px solid var(--am-c-ro-warning-op60); |
| 347 | &-active { |
| 348 | border-bottom: 0; |
| 349 | } |
| 350 | } |
| 351 | .am-collapse-item__content { |
| 352 | /* $yellow-300 */ |
| 353 | background-color: var(--am-c-ro-warning-op10); |
| 354 | /* $yellow-600 */ |
| 355 | border: 1px solid var(--am-c-ro-warning-op60); |
| 356 | } |
| 357 | } |
| 358 | |
| 359 | &-items { |
| 360 | .am-collapse-item__content { |
| 361 | display: block; |
| 362 | } |
| 363 | .am-collapse-item__trigger { |
| 364 | padding: 0; |
| 365 | } |
| 366 | } |
| 367 | |
| 368 | &-heading { |
| 369 | display: flex; |
| 370 | justify-content: space-between; |
| 371 | width: 100%; |
| 372 | font-size: 14px; |
| 373 | font-weight: 400; |
| 374 | line-height: 1.42857; |
| 375 | /* $shade-900 */ |
| 376 | color: var(--am-c-ro-text); |
| 377 | margin: 0 20px 0 0; |
| 378 | } |
| 379 | |
| 380 | &-text { |
| 381 | font-weight: 500; |
| 382 | font-size: 14px; |
| 383 | line-height: 20px; |
| 384 | /* $shade-900 */ |
| 385 | color: var(--am-c-ro-text); |
| 386 | &-selected { |
| 387 | /* $green-900 */ |
| 388 | color: var(--am-c-ro-success); |
| 389 | } |
| 390 | margin-right: 10px; |
| 391 | white-space: nowrap; |
| 392 | } |
| 393 | } |
| 394 | } |
| 395 | |
| 396 | // Amelia Form Steps |
| 397 | .am-fs { |
| 398 | // Container Wrapper |
| 399 | &__main { |
| 400 | &-heading { |
| 401 | &-inner { |
| 402 | display: flex; |
| 403 | align-items: center; |
| 404 | |
| 405 | .am-heading-prev { |
| 406 | margin-right: 12px; |
| 407 | } |
| 408 | } |
| 409 | } |
| 410 | &-inner { |
| 411 | &#{&}-dt { |
| 412 | padding: 0 20px; |
| 413 | } |
| 414 | } |
| 415 | } |
| 416 | } |
| 417 | } |
| 418 | } |
| 419 | </style> |
| 420 |