CASH-185
CASH-185 added waitlist feature to schedule page
This commit is contained in:
parent
eafb4385dd
commit
4b5ee4ebc3
7 changed files with 132 additions and 3 deletions
|
|
@ -18,6 +18,8 @@ const experimentSettings = {
|
|||
INSURANCE_TAB_TO_DISPLAY_THRESHOLD_INTERNAL: "NextGen_InternalInsuranceTabDisplayThreshold",
|
||||
INSURANCE_TAB_TO_DISPLAY_THRESHOLD_EXTERNAL: "NextGen_ExternalInsuranceTabDisplayThreshold",
|
||||
DISPLAY_MSR: "DisplayMSR",
|
||||
DISPLAY_WAITLIST: "DisplayWaitlist2.0",
|
||||
WAITLIST_THRESHOLD_DAYS: "Waitlist_Threshold_Days",
|
||||
};
|
||||
|
||||
const experimentTriggers = {
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ const storeActions = {
|
|||
SAVE_EMAIL: "saveEmail",
|
||||
SAVE_PHONE_NUMBER: "savePhoneNumber",
|
||||
SAVE_IS_SMS_OPT_IN: "saveIsSmsOptIn",
|
||||
SAVE_WAITLIST_REQUESTED: "saveWaitListRequested",
|
||||
SAVE_REGISTRATION_LICENSE_PLATE_LOOKUP: "saveRegistrationLicensePlateLookup",
|
||||
SAVE_VIN: "saveVin",
|
||||
SAVE_REGISTRATION_ADDRESS_LOOKUP: "saveRegistrationAddressLookup",
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ const storeMutations = {
|
|||
UPDATE_CUSTOMER_PHONE_NUMBER: "updateCustomerPhoneNumber",
|
||||
UPDATE_CUSTOMER_IS_SMS_OPT_IN: "updateCustomerIsSmsOptin",
|
||||
UPDATE_CUSTOMER_DETAILS: "updateCustomerDetails",
|
||||
UPDATE_CUSTOMER_WAITLIST_REQUESTED: "updateCustomerWaitListRequested",
|
||||
|
||||
// ORDER MUTATIONS
|
||||
UPDATE_REFERRAL_NUMBER: "updateReferralNumber",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
<template>
|
||||
<div class="text-block w-100" :class="[justifyText, typeStyle, fontWeight, marginTopClass]">
|
||||
<div
|
||||
class="text-block w-100"
|
||||
:class="[justifyText, typeStyle, fontWeight, marginTopClass]"
|
||||
:style="{ 'font-weight': fontWeight }">
|
||||
<span v-for="copy in splitCopyOnCMSPlaceHolder(this.textBlockCopy)" :key="copy">
|
||||
<span v-if="doesCopyContainRouterLink(copy)">
|
||||
<textLink
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@
|
|||
customComponentId="timeSlotModalQuestion"
|
||||
v-model="selectedTimeSlotInfo"
|
||||
cmsWidgetName="TimeSlotModalQuestion"
|
||||
waitListLabelWidget="WaitListLabelWidget"
|
||||
waitListQuestionWidget="WaitListQuestionWidget"
|
||||
mobilePremiumCmsWidgetName="MobilePremiumTimeSlotModal"
|
||||
mobileCmsWidgetName="MobileTimeSlotModal"
|
||||
dropoffCmsWidgetName="DropOffTimeSlotModal"
|
||||
|
|
@ -40,6 +42,7 @@
|
|||
:selectedDate="selectedDate"
|
||||
:appointmentType="appointmentType"
|
||||
:premiumAppointmentFee="mobilePremiumAppointmentFee"
|
||||
:displayWaitList="displayWaitList"
|
||||
:timeSlotsForSelectedDate="timeSlotsForSelectedDate"
|
||||
:estimatedServiceMinutesMinimum="
|
||||
selectableDatesData.estimatedServiceMinutesMinimum
|
||||
|
|
@ -47,6 +50,7 @@
|
|||
:estimatedServiceMinutesMaximum="
|
||||
selectableDatesData.estimatedServiceMinutesMaximum
|
||||
"
|
||||
@waitListRequested-emitted="handleWaitListRequested"
|
||||
@time-slot-modal-closed="timeSlotModalClosed"
|
||||
validationRules="time-slot-selection-required"
|
||||
@TimeSlotSelected="forwardButtonAction" />
|
||||
|
|
@ -79,6 +83,8 @@ import { queryStrings } from "@/constants/query-strings";
|
|||
import { getQuerystringParameter } from "@/helpers/querystring-helper";
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
import experimentMixin from "@/mixins/experiment-mixin";
|
||||
import { experimentSettings } from "@/constants/experiments";
|
||||
import { storeActions } from "@/constants/store-actions";
|
||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
import { splitCopyOnCMSPlaceHolder } from "@/helpers/cms-content-helper";
|
||||
|
|
@ -203,6 +209,8 @@ export default {
|
|||
selectedTimeSlotInfo: this.getSelectedTimeSlotInfo(),
|
||||
selectableDatesData: [],
|
||||
mobilePremiumAppointmentFee: null,
|
||||
waitListRequested: null,
|
||||
displayWaitList: null,
|
||||
};
|
||||
},
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
|
|
@ -279,6 +287,7 @@ export default {
|
|||
? resultMap.premiumFeeWithPrice[0]
|
||||
: null;
|
||||
vm.updateFooterButtonText(vm.selectedTimeSlotInfo);
|
||||
vm.setDisplayWaitList();
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -446,6 +455,29 @@ export default {
|
|||
forwardButtonAction() {
|
||||
this.updateSupportingItems();
|
||||
|
||||
if (this.waitListRequested) {
|
||||
var gaLabel = store.getters.isMobileAppointment
|
||||
? "checked_mobile"
|
||||
: "checked_inshop";
|
||||
|
||||
const currentDate = new Date();
|
||||
const dateString = this.selectableDatesData.days[0].date;
|
||||
const [year, month, day] = dateString.split("-").map(Number);
|
||||
const appointmentDate = new Date(year, month - 1, day);
|
||||
const timeDifference = appointmentDate - currentDate;
|
||||
|
||||
// Convert the time difference from milliseconds to days
|
||||
const daysUntilAppointment = Math.ceil(timeDifference / (1000 * 60 * 60 * 24));
|
||||
|
||||
gaLabel +=
|
||||
"_" +
|
||||
daysUntilAppointment.toString() +
|
||||
"_" +
|
||||
store.getters.order.serviceLocation.zipCode +
|
||||
"_" +
|
||||
store.getters.order.serviceLocation.zipCodeCtu;
|
||||
this.pushEventToGA("waitlist", "add_to_waitlist_check_box_status", gaLabel, true);
|
||||
}
|
||||
if (this.selectedTimeSlotInfo.timeSlot.jobMinMinutes == null) {
|
||||
this.selectedTimeSlotInfo.timeSlot.jobMinMinutes =
|
||||
this.selectableDatesData?.estimatedServiceMinutesMinimum?.toString();
|
||||
|
|
@ -459,8 +491,37 @@ export default {
|
|||
false
|
||||
);
|
||||
|
||||
this.dispatchStoreAction(
|
||||
this.storeActions.SAVE_WAITLIST_REQUESTED,
|
||||
this.waitListRequested,
|
||||
false
|
||||
);
|
||||
|
||||
this.$router.navigateWithSaving(this.navigationScenarios.CLICKED_FORWARD, this.$route);
|
||||
},
|
||||
setDisplayWaitList() {
|
||||
const dateString = this.selectableDatesData.days[0].date;
|
||||
const [year, month, day] = dateString.split("-").map(Number);
|
||||
const targetDate = new Date(year, month - 1, day);
|
||||
const currentDate = new Date();
|
||||
const futureDate = new Date(currentDate);
|
||||
const experimentThresholdDays = experimentMixin.methods.hasSetting(
|
||||
experimentSettings.WAITLIST_THRESHOLD_DAYS
|
||||
)
|
||||
? parseInt(
|
||||
experimentMixin.methods.getSettingValue(
|
||||
experimentSettings.WAITLIST_THRESHOLD_DAYS
|
||||
)
|
||||
)
|
||||
: 0;
|
||||
futureDate.setDate(currentDate.getDate() + experimentThresholdDays);
|
||||
|
||||
if (targetDate >= futureDate) {
|
||||
this.displayWaitList = true;
|
||||
} else {
|
||||
this.displayWaitList = false;
|
||||
}
|
||||
},
|
||||
updateSupportingItems() {
|
||||
const supportingItems = this.getSupportingItems();
|
||||
|
||||
|
|
@ -509,6 +570,9 @@ export default {
|
|||
}
|
||||
}
|
||||
},
|
||||
handleWaitListRequested(value) {
|
||||
this.waitListRequested = value;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectedDate(newValue, oldValue) {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,21 @@
|
|||
isRequired
|
||||
validationRules="time-slot-required" />
|
||||
<div
|
||||
class="mt-1 mb-2 supplemental-information"
|
||||
v-if="hasWaitListExperiment && displayWaitListFeature"
|
||||
class="mt-5 bg-light rounded waitlist">
|
||||
<textBlock
|
||||
cmsWidgetName="WaitListLabelWidget"
|
||||
typeStyle="medium"
|
||||
fontWeight="600"
|
||||
marginTopSizeOverride="0" />
|
||||
<checkboxQuestion
|
||||
class="mt-3"
|
||||
cmsWidgetName="WaitListQuestionWidget"
|
||||
v-model="waitListRequested"
|
||||
@click="waitListChecked" />
|
||||
</div>
|
||||
<div
|
||||
class="mt-5 mb-2 supplemental-information"
|
||||
v-if="supplementalInformationBlock"
|
||||
v-html="supplementalInformationBlock"></div>
|
||||
<textBlock
|
||||
|
|
@ -44,7 +58,11 @@
|
|||
import modal from "@/digital-components/modal/modal";
|
||||
import textBlock from "@/digital-components/text-block/text-block";
|
||||
import buttonQuestion from "@/digital-components/button-question/button-question";
|
||||
import checkboxQuestion from "@/digital-components/checkbox-question/checkbox-question";
|
||||
import timeSlotModalListButton from "./time-slot-modal-list-button/time-slot-modal-list-button";
|
||||
import experimentMixin from "@/mixins/experiment-mixin";
|
||||
import { experimentSettings } from "@/constants/experiments";
|
||||
import store from "@/store";
|
||||
|
||||
// Helpers
|
||||
import { deepClone } from "@/helpers/object-helper";
|
||||
|
|
@ -82,7 +100,7 @@ defineRule("time-slot-required", required(errorMessages.OPTION_REQUIRED));
|
|||
|
||||
export default {
|
||||
name: "time-slot-modal-question",
|
||||
emits: ["update:modelValue", "TimeSlotSelected"],
|
||||
emits: ["update:modelValue", "TimeSlotSelected", "click-event"],
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Object,
|
||||
|
|
@ -112,12 +130,14 @@ export default {
|
|||
validationRules: String,
|
||||
customComponentId: String,
|
||||
selectedDate: String,
|
||||
displayWaitList: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isModalOpened: false,
|
||||
selectedRouteCode: this.getSelectedRouteCode(),
|
||||
timeSlotModalListButton: timeSlotModalListButton,
|
||||
waitListRequested: this.getWaitListRequestedFromStore(),
|
||||
};
|
||||
},
|
||||
setup(props) {
|
||||
|
|
@ -330,6 +350,15 @@ export default {
|
|||
return this.getAvailableTimeSlotsForInshop(this.timeSlotsForSelectedDate.timeSlots);
|
||||
}
|
||||
},
|
||||
hasWaitListExperiment() {
|
||||
return experimentMixin.methods.hasSettingEqualTo(
|
||||
experimentSettings.DISPLAY_WAITLIST,
|
||||
"true"
|
||||
);
|
||||
},
|
||||
displayWaitListFeature() {
|
||||
return this.displayWaitList;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
openModal() {
|
||||
|
|
@ -445,6 +474,9 @@ export default {
|
|||
|
||||
return selectedRouteCode;
|
||||
},
|
||||
getWaitListRequestedFromStore() {
|
||||
return store.getters.order.customer?.waitListRequested;
|
||||
},
|
||||
autoSelectTimeSlotIfOnlyOneIsAvailable() {
|
||||
const numberOfOptions = this.availableTimeSlots?.length;
|
||||
if (numberOfOptions === 1) {
|
||||
|
|
@ -493,6 +525,9 @@ export default {
|
|||
isPremiumAppointment: null,
|
||||
};
|
||||
},
|
||||
waitListChecked(event) {
|
||||
this.$emit("waitListRequested-emitted", event.target.checked);
|
||||
},
|
||||
},
|
||||
unmounted() {
|
||||
if (this.isModalOpened) this.closeModal();
|
||||
|
|
@ -515,6 +550,7 @@ export default {
|
|||
modal,
|
||||
textBlock,
|
||||
buttonQuestion,
|
||||
checkboxQuestion,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -538,5 +574,19 @@ export default {
|
|||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
.waitlist {
|
||||
box-shadow: 0px 1px 4px 0px #00000033;
|
||||
border-radius: 8px;
|
||||
font-size: $h6-font-size;
|
||||
padding: 16px;
|
||||
margin-top: 16px;
|
||||
margin-bottom: -8px;
|
||||
.ui-checkbox {
|
||||
padding-left: 0px;
|
||||
}
|
||||
.form-check-input {
|
||||
margin-left: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ const getDefaultState = () => {
|
|||
emailAddress: null,
|
||||
phoneNumber: null,
|
||||
isSmsOptIn: null,
|
||||
waitListRequested: null,
|
||||
},
|
||||
damage: {
|
||||
isRepair: null,
|
||||
|
|
@ -349,6 +350,9 @@ export const mutations = {
|
|||
updateCustomerIsSmsOptin(state, isSmsOptIn) {
|
||||
state.order.customer.isSmsOptIn = isSmsOptIn;
|
||||
},
|
||||
updateCustomerWaitListRequested(state, waitListRequested) {
|
||||
state.order.customer.waitListRequested = waitListRequested;
|
||||
},
|
||||
updateVehicle(state, vehicleInfo) {
|
||||
state.order.vehicle.year = vehicleInfo.year;
|
||||
state.order.vehicle.make = vehicleInfo.make;
|
||||
|
|
@ -3096,6 +3100,10 @@ export const actions = {
|
|||
context.commit(storeMutations.UPDATE_CUSTOMER_IS_SMS_OPT_IN, isSmsOptIn);
|
||||
},
|
||||
|
||||
saveWaitListRequested(context, waitListRequested) {
|
||||
context.commit(storeMutations.UPDATE_CUSTOMER_WAITLIST_REQUESTED, waitListRequested);
|
||||
},
|
||||
|
||||
savePageData(context, emailOrSms) {
|
||||
context.commit(storeMutations.UPDATE_PAGE_DATA, emailOrSms);
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue