diff --git a/src/layouts/quote/cash-or-insurance-question/cash-or-insurance-question.vue b/src/layouts/quote/cash-or-insurance-question/cash-or-insurance-question.vue
index 45fef683a..a2afb1a7c 100644
--- a/src/layouts/quote/cash-or-insurance-question/cash-or-insurance-question.vue
+++ b/src/layouts/quote/cash-or-insurance-question/cash-or-insurance-question.vue
@@ -5,7 +5,7 @@
:groupName="groupName"
buttonTypeString="listButtonHorizontal"
v-model="selectedValues"
- :additionalButtonData="{ additionalButtonStyling: 'listButtonHorizontalStrong' }"
+ :additionalButtonData="additionalButtonData"
isRequired />
@@ -27,6 +27,11 @@ export default {
answersFromCms() {
return this.getCmsContent(this.cmsWidgetName, "Answers");
},
+ additionalButtonData() {
+ return {
+ additionalButtonStyling: "listButtonHorizontalStrong",
+ };
+ },
selectedValues: {
get: function () {
// Convert to CMS answer name from bool
diff --git a/src/layouts/schedule/constants/schedule-constants.js b/src/layouts/schedule/constants/schedule-constants.js
new file mode 100644
index 000000000..89b4a4c1a
--- /dev/null
+++ b/src/layouts/schedule/constants/schedule-constants.js
@@ -0,0 +1,11 @@
+const AppointmentTypeStrings = {
+ IN_SHOP: "Inshop",
+ MOBILE: "Mobile",
+ DROP_OFF: "Dropoff",
+};
+
+const PREMIUM_TIME_SLOT_ID_FLAG = "-premium";
+
+const PREMIUM_FEE_PART_TYPE = "EARLY BIRD";
+
+export { AppointmentTypeStrings, PREMIUM_TIME_SLOT_ID_FLAG, PREMIUM_FEE_PART_TYPE };
diff --git a/src/layouts/schedule/schedule.vue b/src/layouts/schedule/schedule.vue
index d5a1283c9..654f35a15 100644
--- a/src/layouts/schedule/schedule.vue
+++ b/src/layouts/schedule/schedule.vue
@@ -28,13 +28,14 @@
@@ -49,15 +49,17 @@ import { defineRule, useField } from "vee-validate";
import { errorMessages } from "@/constants/error-messages";
import { required } from "@/helpers/validation-rules";
+// Constants
+import {
+ AppointmentTypeStrings,
+ PREMIUM_TIME_SLOT_ID_FLAG,
+ PREMIUM_FEE_PART_TYPE,
+} from "../constants/schedule-constants";
+
// Validation for the modal button
defineRule("time-slot-required", required(errorMessages.OPTION_REQUIRED));
// Constants
-const AppointmentTypeStrings = {
- IN_SHOP: "Inshop",
- MOBILE: "Mobile",
- DROP_OFF: "Dropoff",
-};
export default {
name: "timeSlotModalQuestion",
@@ -65,18 +67,20 @@ export default {
modelValue: Object,
cmsWidgetName: String,
mobileCmsWidgetName: String,
- earlyBirdCmsWidgetName: String,
+ mobilePremiumCmsWidgetName: String,
dropoffCmsWidgetName: String,
+ sameDayDropOffCmsWidgetName: String,
appointmentType: String,
dateAndTimeSlotData: Object,
- mobileEarlyBirdFee: Object,
+ premiumAppointmentFee: Object,
estimatedServiceMinutesMinimum: Number,
estimatedServiceMinutesMaximum: Number,
validationRules: String,
},
data() {
return {
- selectedTimeSlot: null,
+ selectedTimeSlotId: null,
+ isSelectedAppointmentPremium: null,
timeslotModalListButton: timeslotModalListButton,
};
},
@@ -89,42 +93,38 @@ export default {
},
watch: {
modelValue() {
- this.selectedTimeSlot = this.modelValue;
// Run component validation that is used at parent level
- this.handleChange(this.modelValue);
+ this.handleChange(this.modelValue.id);
+ },
+ dateAndTimeSlotData(newValue, oldValue) {
+ const numberOfOptions = newValue?.timeSlots.length;
+ if (numberOfOptions === 1) {
+ this.selectedTimeSlotId = newValue.timeSlots[0].id;
+ }
},
- // dateAndTimeSlotData(newValue, oldValue) {
- // const numberOfOptions = newValue?.timeSlots.length;
- // console.log('running');
- // if (numberOfOptions === 1) {
- // this.selectedTimeSlot = newValue.timeSlots[0].id;
- // }
- // }
},
computed: {
supplementalInformationBlock() {
let appointmentTypeCmsWidgetName;
- let cmsFieldName = "BodyText";
if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) {
return null;
} else if (this.appointmentType === AppointmentTypeStrings.MOBILE) {
appointmentTypeCmsWidgetName =
- this.selectedTimeSlot === this.earlyBirdButtonText
- ? this.earlyBirdCmsWidgetName
+ this.selectedTimeSlotId === this.premiumAppointmentButtonText
+ ? this.mobilePremiumCmsWidgetName
: this.mobileCmsWidgetName;
} else {
- appointmentTypeCmsWidgetName = this.dropoffCmsWidgetName;
- if (this.isSameDay) {
- cmsFieldName = "BodyText2";
- }
+ appointmentTypeCmsWidgetName = this.isSameDay
+ ? this.sameDayDropOffCmsWidgetName
+ : this.dropoffCmsWidgetName;
}
- return this.getCmsContent(appointmentTypeCmsWidgetName, cmsFieldName);
+ return this.getCmsContent(appointmentTypeCmsWidgetName, "BodyText");
},
footerCloseButtonText() {
return this.getCmsContent(this.cmsWidgetName, "FooterText");
},
- earlyBirdButtonText() {
- return this.getCmsContent(this.earlyBirdCmsWidgetName, "HeaderText");
+ premiumAppointmentButtonText() {
+ return this.getCmsContent(this.mobilePremiumCmsWidgetName, "HeaderText");
},
dropoffButtonText() {
return this.getCmsContent(this.dropoffCmsWidgetName, "HeaderText");
@@ -203,13 +203,20 @@ export default {
}
if (this.appointmentType === AppointmentTypeStrings.MOBILE) {
- const offerPremium = this.dateAndTimeSlotData.timeSlots[0].offerPremium;
- const hasEarlyBird = this.mobileEarlyBirdFee?.partType === "EARLY BIRD";
- if (offerPremium && hasEarlyBird) {
+ const isPremiumTimeSlot = this.dateAndTimeSlotData.timeSlots[0].offerPremium;
+ const hasPremiumPartAvailable =
+ this.premiumAppointmentFee?.partType === PREMIUM_FEE_PART_TYPE;
+ if (isPremiumTimeSlot && hasPremiumPartAvailable) {
+ const formattedPrice =
+ "+$" + this.getTotalLineItemPrice(this.premiumAppointmentFee).toFixed(2);
availableTimeSlots.unshift({
- value: this.dateAndTimeSlotData.timeSlots[0].id + "-earlybird",
- buttonLabel: this.earlyBirdButtonText,
- buttonLabelSubCopy: this.getTotalLineItemPrice(this.mobileEarlyBirdFee),
+ // Unique value is required for each and the premium appoinment shares a timeslot ID
+ value: this.addPremiumFlagToInput(this.dateAndTimeSlotData.timeSlots[0].id),
+ buttonLabel: this.premiumAppointmentButtonText,
+ buttonLabelSubCopy: formattedPrice,
+ additionalButtonData: {
+ isPremiumAppointment: true,
+ },
});
}
}
@@ -222,12 +229,27 @@ export default {
},
// fires any time the footer button is used, is fired before "onModalClosed"
closeModal() {
- this.$emit("update:modelValue", this.selectedTimeSlot);
+ if (this.selectedTimeSlotId.toString().includes(PREMIUM_TIME_SLOT_ID_FLAG)) {
+ this.selectedTimeSlotId = this.removePremiumFlagFromInput(this.selectedTimeSlotId);
+ this.isSelectedAppointmentPremium = true;
+ } else {
+ this.isSelectedAppointmentPremium = false;
+ }
+ const selectedTimeSlotData = {
+ id: this.selectedTimeSlotId,
+ isPremiumAppointment: this.isSelectedAppointmentPremium,
+ };
+ this.$emit("update:modelValue", selectedTimeSlotData);
this.$refs["timeSlots"].closeModal();
},
// fires any time the modal is closed, AFTER "closeModal" fires if footer button is used
onModalClosed() {
- this.selectedTimeSlot = this.modelValue;
+ this.isSelectedAppointmentPremium = this.modelValue.isPremiumAppointment;
+ if (this.isSelectedAppointmentPremium) {
+ this.selectedTimeSlotId = this.addPremiumFlagToInput(this.modelValue.id);
+ } else {
+ this.selectedTimeSlotId = this.modelValue.id;
+ }
this.$emit("time-slot-modal-closed");
},
// Expected input: "HH:MM:SS"
@@ -252,6 +274,12 @@ export default {
}
return displayTextForDurationLength;
},
+ addPremiumFlagToInput(timeSlotId) {
+ return (timeSlotId += PREMIUM_TIME_SLOT_ID_FLAG);
+ },
+ removePremiumFlagFromInput(timeSlotId) {
+ return parseInt(timeSlotId.trim(PREMIUM_TIME_SLOT_ID_FLAG.length));
+ },
},
components: {
modal,
diff --git a/src/layouts/schedule/time-slot-modal-question/timeslot-modal-list-button/timeslot-modal-list-button.vue b/src/layouts/schedule/time-slot-modal-question/timeslot-modal-list-button/timeslot-modal-list-button.vue
index 60b2f9e81..4f5b17461 100644
--- a/src/layouts/schedule/time-slot-modal-question/timeslot-modal-list-button/timeslot-modal-list-button.vue
+++ b/src/layouts/schedule/time-slot-modal-question/timeslot-modal-list-button/timeslot-modal-list-button.vue
@@ -6,12 +6,16 @@
-
+
{{ buttonLabel }}
+
+ {{ formattedButtonLabelSubCopy }}
+
-
- {{ formattedButtonLabelSubCopy }}
-
+
{{ screenReaderOnlyText }}
@@ -28,7 +32,7 @@ import baseInputButton from "@/digital-components/base-input-button/base-input-b
import inputButtonWrapperMixin from "@/mixins/input-button-wrapper-mixin";
export default {
- name: "timeslotMOdalListButton",
+ name: "timeslotModalListButton",
mixins: [inputButtonWrapperMixin],
props: {
loaderColor: String,
@@ -44,7 +48,7 @@ export default {
},
computed: {
formattedButtonLabelSubCopy() {
- return this.buttonLabelSubCopy?.toFixed(2);
+ return this.buttonLabelSubCopy;
},
},
methods: {
@@ -85,6 +89,9 @@ export default {
font-weight: 500;
background: $blue-100;
box-shadow: 0 0 0 1px $blue;
+ span.premium-appointment-price {
+ background: $green-200;
+ }
}
&:checked:focus + .list-button-content {
box-shadow: 0 0 0 2.5px $blue;
@@ -109,11 +116,20 @@ export default {
width: 100%;
outline: none;
- span {
- &.small {
- font-size: 0.75rem;
- color: $gray-550;
- }
+ span.premium-appointment-price {
+ position: absolute;
+ background: $green-100;
+ border-radius: 4.5rem;
+ line-height: 1.25rem;
+ color: $green-700;
+ font-size: 0.75rem;
+ margin-left: 4px;
+ padding: 2px 8px;
+ font-weight: 500;
}
}
+
+.position-relative {
+ position: relative;
+}