CASH-2378: new component for multi location modal
This commit is contained in:
parent
0c32eecdcc
commit
b3fb548813
1 changed files with 317 additions and 0 deletions
|
|
@ -0,0 +1,317 @@
|
||||||
|
<template>
|
||||||
|
<div id="multi-location-modal-container">
|
||||||
|
<modal
|
||||||
|
:ref="modalName"
|
||||||
|
:headerText="modalHeaderText"
|
||||||
|
suppressPageScroll
|
||||||
|
:onModalClosedCallback="onModalClosed"
|
||||||
|
:footerButtonText="modalFooterText"
|
||||||
|
:isFooterButtonPrimary="true"
|
||||||
|
@footer-button-event="confirmAppointment"
|
||||||
|
@isModalOpened="setIsModalOpen">
|
||||||
|
<buttonQuestion
|
||||||
|
ref="buttonQuestion"
|
||||||
|
buttonTypeString="multiLocationRadioButton"
|
||||||
|
:buttonTypeObject="multiLocationRadioButton"
|
||||||
|
class="mt-5"
|
||||||
|
:answers="availableAppointments"
|
||||||
|
groupName="multiLocationQuestion"
|
||||||
|
textPosition="text-center"
|
||||||
|
v-model="selectedValue"
|
||||||
|
isRequired
|
||||||
|
validationRules="time-slot-required" />
|
||||||
|
|
||||||
|
<template v-slot:modal-header-slot>
|
||||||
|
<span>{{ modalSubHeaderText }}</span>
|
||||||
|
</template>
|
||||||
|
<template v-slot:modal-footer-slot>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-link"
|
||||||
|
id="see-more-options"
|
||||||
|
:disabled="isLoading"
|
||||||
|
@click="closeModal">
|
||||||
|
{{ buttonText }}
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Supporting files
|
||||||
|
import store from "@/store";
|
||||||
|
import modal from "@/digital-components/modal/modal";
|
||||||
|
import { splitCopyOnCMSPlaceHolder } from "@/helpers/cms-content-helper";
|
||||||
|
import { get12HourTimeFormat } from "@/helpers/date-helper";
|
||||||
|
import { getDisplayTextForDurationLength } from "@/helpers/duration-length-helper";
|
||||||
|
import buttonQuestion from "@/digital-components/button-question/button-question";
|
||||||
|
import multiLocationRadioButton from "./multi-location-radio/multi-location-radio";
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
import { defineRule, useField } from "vee-validate";
|
||||||
|
import { errorMessages } from "@/constants/error-messages";
|
||||||
|
import { required } from "@/helpers/validation-rules";
|
||||||
|
|
||||||
|
const NULL_APPOINTMENT = {
|
||||||
|
estimatedServiceMinutes: {
|
||||||
|
minimum: null,
|
||||||
|
maximum: null,
|
||||||
|
},
|
||||||
|
timeSlot: null,
|
||||||
|
date: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Validation for the modal button
|
||||||
|
defineRule("time-slot-required", required(errorMessages.OPTION_REQUIRED));
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "multi-location-modal",
|
||||||
|
props: {
|
||||||
|
cmsWidgetName: String,
|
||||||
|
modalWidgetName: String,
|
||||||
|
buttonWidgetName: String,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isModalOpen: false,
|
||||||
|
mobileAppointmentOption: NULL_APPOINTMENT,
|
||||||
|
inshopAppointmentOption: NULL_APPOINTMENT,
|
||||||
|
confirmedAppointment: false,
|
||||||
|
multiLocationRadioButton: multiLocationRadioButton,
|
||||||
|
selectedValue: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
splitCopyOnCMSPlaceHolder,
|
||||||
|
get12HourTimeFormat,
|
||||||
|
openModal() {
|
||||||
|
this.modal.openModal();
|
||||||
|
},
|
||||||
|
onModalClosed() {
|
||||||
|
this.pushEvent();
|
||||||
|
this.$emit("close-mobile-first-modal");
|
||||||
|
this.confirmedAppointment = false;
|
||||||
|
},
|
||||||
|
closeModal() {
|
||||||
|
this.modal.closeModal();
|
||||||
|
},
|
||||||
|
confirmAppointment() {
|
||||||
|
if (!this.selectedAppointment) return;
|
||||||
|
|
||||||
|
const selectedTimeSlot = {
|
||||||
|
timeSlot: this.selectedAppointment.timeSlot,
|
||||||
|
routeCode: this.selectedAppointment.timeSlot.id,
|
||||||
|
date: this.selectedAppointment.date,
|
||||||
|
};
|
||||||
|
this.confirmedAppointment = true;
|
||||||
|
this.$emit("confirm-appointment", {
|
||||||
|
selectedTimeSlot: selectedTimeSlot,
|
||||||
|
appointmentType: this.selectedValue,
|
||||||
|
});
|
||||||
|
this.modal.closeModal();
|
||||||
|
},
|
||||||
|
setPromotedMobileAppointment(appointment) {
|
||||||
|
this.mobileAppointmentOption = appointment;
|
||||||
|
},
|
||||||
|
setPromotedInshopAppointment(appointment) {
|
||||||
|
this.inshopAppointmentOption = appointment;
|
||||||
|
},
|
||||||
|
getIsModalOpen() {
|
||||||
|
return this.isModalOpen;
|
||||||
|
},
|
||||||
|
setIsModalOpen(isOpen) {
|
||||||
|
this.isModalOpen = isOpen;
|
||||||
|
},
|
||||||
|
pushEvent() {
|
||||||
|
let gaAction = this.confirmedAppointment ? this.GaActions.YES : this.GaActions.NO;
|
||||||
|
if (this.mobileAppointmentOption && this.mobileAppointmentOption.timeSlot) {
|
||||||
|
this.pushEventToGA(
|
||||||
|
this.GaCategories.CONFIRMATION_CLICKED,
|
||||||
|
gaAction,
|
||||||
|
this.mobileAppointmentOption.timeSlot.startTime +
|
||||||
|
"-" +
|
||||||
|
this.mobileAppointmentOption.timeSlot.endTime +
|
||||||
|
" " +
|
||||||
|
this.mobileAppointmentOption.date,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
selectedAppointment() {
|
||||||
|
if (this.selectedValue?.toLowerCase() === "mobile") {
|
||||||
|
return this.mobileAppointmentOption;
|
||||||
|
} else if (this.selectedValue?.toLowerCase() === "inshop") {
|
||||||
|
return this.inshopAppointmentOption;
|
||||||
|
} else {
|
||||||
|
return NULL_APPOINTMENT;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modalName() {
|
||||||
|
return this.modalWidgetName;
|
||||||
|
},
|
||||||
|
modal() {
|
||||||
|
return this.$refs[this.modalName];
|
||||||
|
},
|
||||||
|
modalSubHeaderText() {
|
||||||
|
return this.getCmsContent(this.modalWidgetName, "SubheaderText");
|
||||||
|
},
|
||||||
|
modalHeaderText() {
|
||||||
|
return this.getCmsContent(this.modalWidgetName, "HeaderText");
|
||||||
|
},
|
||||||
|
modalFooterText() {
|
||||||
|
return this.getCmsContent(this.modalWidgetName, "FooterText");
|
||||||
|
},
|
||||||
|
buttonText() {
|
||||||
|
return this.getCmsContent(this.modalWidgetName, "FooterText2");
|
||||||
|
},
|
||||||
|
mobileButtonLabel() {
|
||||||
|
return this.getCmsContent(this.buttonWidgetName, "HeaderText");
|
||||||
|
},
|
||||||
|
inshopButtonLabel() {
|
||||||
|
return this.getCmsContent(this.buttonWidgetName, "SubheaderText");
|
||||||
|
},
|
||||||
|
mobileButtonDate() {
|
||||||
|
if (!this.mobileAppointmentOption.date) return "";
|
||||||
|
return new Date(this.mobileAppointmentOption.date).toLocaleDateString("en-US", {
|
||||||
|
timeZone: "UTC",
|
||||||
|
weekday: "short",
|
||||||
|
month: "long",
|
||||||
|
day: "numeric",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
inshopButtonDate() {
|
||||||
|
if (!this.inshopAppointmentOption.date) return "";
|
||||||
|
return new Date(this.inshopAppointmentOption.date).toLocaleDateString("en-US", {
|
||||||
|
timeZone: "UTC",
|
||||||
|
weekday: "short",
|
||||||
|
month: "long",
|
||||||
|
day: "numeric",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
mobileButtonTime() {
|
||||||
|
const timeslot =
|
||||||
|
this.get12HourTimeFormat(this.mobileAppointmentOption.timeSlot?.startTime) +
|
||||||
|
" - " +
|
||||||
|
this.get12HourTimeFormat(this.mobileAppointmentOption.timeSlot?.endTime);
|
||||||
|
return timeslot;
|
||||||
|
},
|
||||||
|
inshopButtonTime() {
|
||||||
|
const timeslot = this.get12HourTimeFormat(
|
||||||
|
this.inshopAppointmentOption.timeSlot?.startTime
|
||||||
|
);
|
||||||
|
return timeslot;
|
||||||
|
},
|
||||||
|
mobileLocationLabel() {
|
||||||
|
return this.getCmsContent(this.buttonWidgetName, "BodyText");
|
||||||
|
},
|
||||||
|
inshopLocationLabel() {
|
||||||
|
return this.inshopAppointmentOption?.addressCopy;
|
||||||
|
},
|
||||||
|
mobileServiceDuration() {
|
||||||
|
const durationTime = getDisplayTextForDurationLength(
|
||||||
|
this.mobileAppointmentOption.estimatedServiceMinutes.minimum,
|
||||||
|
this.mobileAppointmentOption.estimatedServiceMinutes.maximum
|
||||||
|
);
|
||||||
|
const footerText = this.getCmsContent(this.buttonWidgetName, "FooterText").replaceAll(
|
||||||
|
"{custom:serviceLength}",
|
||||||
|
durationTime
|
||||||
|
);
|
||||||
|
return footerText;
|
||||||
|
},
|
||||||
|
inshopServiceDuration() {
|
||||||
|
const durationTime = getDisplayTextForDurationLength(
|
||||||
|
this.inshopAppointmentOption.estimatedServiceMinutes.minimum,
|
||||||
|
this.inshopAppointmentOption.estimatedServiceMinutes.maximum
|
||||||
|
);
|
||||||
|
const footerText = this.getCmsContent(this.buttonWidgetName, "FooterText").replaceAll(
|
||||||
|
"{custom:serviceLength}",
|
||||||
|
durationTime
|
||||||
|
);
|
||||||
|
return footerText;
|
||||||
|
},
|
||||||
|
availableAppointments() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
value: "mobile",
|
||||||
|
buttonLabel: this.mobileButtonDate,
|
||||||
|
buttonLabelSubCopy: this.mobileButtonTime,
|
||||||
|
buttonBodyCopy: this.mobileLocationLabel,
|
||||||
|
buttonAuxillaryCopy: this.mobileButtonLabel,
|
||||||
|
buttonFooterCopy: this.mobileServiceDuration,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "inshop",
|
||||||
|
buttonLabel: this.inshopButtonDate,
|
||||||
|
buttonLabelSubCopy: this.inshopButtonTime,
|
||||||
|
buttonBodyCopy: this.inshopLocationLabel,
|
||||||
|
buttonAuxillaryCopy: this.inshopButtonLabel,
|
||||||
|
buttonFooterCopy: this.inshopServiceDuration,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: { modal, buttonQuestion },
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="scss">
|
||||||
|
#multi-location-modal-container {
|
||||||
|
.modal-component {
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
.modal-dialog {
|
||||||
|
left: 0;
|
||||||
|
align-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
width: 22.063rem;
|
||||||
|
transform: translate(0, 0);
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
border-radius: $border-radius-lg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.modal-dialog {
|
||||||
|
.modal-content {
|
||||||
|
.modal-header {
|
||||||
|
flex-direction: column;
|
||||||
|
& > span {
|
||||||
|
color: $red;
|
||||||
|
font-size: $font-size-14;
|
||||||
|
font-weight: $font-weight-600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
font-size: $font-size-20;
|
||||||
|
font-weight: $font-weight-normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.modal-body {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
|
||||||
|
.modal-body-inner {
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
font-size: $font-size-14;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.modal-footer {
|
||||||
|
flex-flow: wrap-reverse;
|
||||||
|
|
||||||
|
#see-more-options {
|
||||||
|
width: 100%;
|
||||||
|
text-decoration: none;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
font-weight: $font-weight-600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in a new issue