CASH-2378: implement new multi location modal
This commit is contained in:
parent
e5aa47673b
commit
364b5d29bf
1 changed files with 193 additions and 5 deletions
|
|
@ -6,6 +6,11 @@
|
|||
modalWidgetName="MobileFirstModalWidget"
|
||||
ref="mobileFirstModal"
|
||||
@confirm-appointment="updateMobileFirstTimeSlotandNavigateForward" />
|
||||
<multiLocationModal
|
||||
modalWidgetName="MultiLocationModalWidget"
|
||||
buttonWidgetName="MultiLocationRadioButtonWidget"
|
||||
ref="multiLocationModal"
|
||||
@confirm-appointment="updateMultiLocationTimeSlotandNavigateForward" />
|
||||
<recalAckModal
|
||||
modalWidgetName="RecalAckModalWidget"
|
||||
agreementWidgetName="RecalAgreementQuestionWidget"
|
||||
|
|
@ -14,7 +19,9 @@
|
|||
ref="recalAckModal" />
|
||||
<div
|
||||
class="container page-container-grouped-styles"
|
||||
:class="[isMobileFirstModalOpen ? 'hidden-background' : '']">
|
||||
:class="[
|
||||
isMobileFirstModalOpen || isMultiLocationModalOpen ? 'hidden-background' : '',
|
||||
]">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-10 col-lg-8 col-xl-7">
|
||||
<mobileFeeWaiverAlert
|
||||
|
|
@ -201,6 +208,7 @@ import shopLocation from "@/layouts/schedule/shop-location/shop-location";
|
|||
import timeSlotQuestion from "@/layouts/schedule/time-slot-question/time-slot-question.vue";
|
||||
import durationTextBlock from "@/layouts/schedule/duration-text-block/duration-text-block.vue";
|
||||
import mobileFirstModal from "@/layouts/schedule/mobile-first-modal/mobile-first-modal.vue";
|
||||
import multiLocationModal from "@/layouts/schedule/multi-location-modal/multi-location-modal.vue";
|
||||
import recalAckModal from "@/layouts/schedule/recal-ack-modal/recal-ack-modal.vue";
|
||||
|
||||
import shopQuestion from "@/layouts/service-location/shop-question/shop-question";
|
||||
|
|
@ -488,6 +496,7 @@ export default {
|
|||
shopListButton: shopListButton,
|
||||
lastSelectedInshopOrDropoffProvider: null,
|
||||
selectedMobileFirstAppointment: false,
|
||||
selectedMultiLocationAppointment: false,
|
||||
calendarLoadingStatus: "none",
|
||||
};
|
||||
},
|
||||
|
|
@ -620,7 +629,8 @@ export default {
|
|||
shopProviderData.data
|
||||
);
|
||||
vm.initializeDatePicker().then(() => {
|
||||
vm.showMobileFirstModal();
|
||||
// vm.showMobileFirstModal(); // KEEP IN CASE WE WANT TO REINSTATE MOBILE FIRST
|
||||
vm.showMultiLocationModal();
|
||||
vm.hideLoadingModal();
|
||||
vm.calendarLoadingStatus = "none";
|
||||
if (!vm.preSelectedDate) vm.setSelectedDateToFirstAvailable();
|
||||
|
|
@ -930,6 +940,11 @@ export default {
|
|||
? this.$refs.mobileFirstModal?.getIsModalOpen()
|
||||
: false;
|
||||
},
|
||||
isMultiLocationModalOpen() {
|
||||
return this.selectableDatesMobile.days && this.selectableDatesInshop.days
|
||||
? this.$refs.multiLocationModal?.getIsModalOpen()
|
||||
: false;
|
||||
},
|
||||
displayWaitList() {
|
||||
if (!this.appointmentType) return false;
|
||||
const firstMobileDateString =
|
||||
|
|
@ -990,6 +1005,12 @@ export default {
|
|||
"true"
|
||||
);
|
||||
},
|
||||
isShowMultiLocationPopup() {
|
||||
return experimentMixin.methods.hasSettingEqualTo(
|
||||
experimentSettings.SHOW_MULTI_LOCATION_APPT,
|
||||
"true"
|
||||
);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
splitCopyOnCMSPlaceHolder,
|
||||
|
|
@ -1275,7 +1296,8 @@ export default {
|
|||
}
|
||||
}
|
||||
this.initializeDatePicker().then(() => {
|
||||
this.showMobileFirstModal();
|
||||
// this.showMobileFirstModal(); // KEEP IN CASE WE WANT TO REINSTATE MOBILE FIRST
|
||||
this.showMultiLocationModal();
|
||||
this.hideLoadingModal();
|
||||
this.calendarLoadingStatus = "none";
|
||||
this.setSelectedDateToFirstAvailable();
|
||||
|
|
@ -1309,7 +1331,7 @@ export default {
|
|||
},
|
||||
|
||||
async initializeDatePicker() {
|
||||
this.isShowMobileFirstAppt && this.showLoadingModal();
|
||||
this.isShowMultiLocationPopup && this.showLoadingModal();
|
||||
|
||||
const includeMobileTimeSlots = this.isServiceableMobile;
|
||||
const includeInshopTimeSlots = this.isServiceableInshop || this.isServiceableDropoff;
|
||||
|
|
@ -1821,7 +1843,8 @@ export default {
|
|||
this.zipCodeCtu = newZipCode.zipCodeCtu;
|
||||
this.selectedDate = null;
|
||||
this.initializeDatePicker().then(() => {
|
||||
this.showMobileFirstModal();
|
||||
// this.showMobileFirstModal(); // KEEP IN CASE WE WANT TO REINSTATE MOBILE FIRST
|
||||
this.showMultiLocationModal();
|
||||
this.hideLoadingModal();
|
||||
this.calendarLoadingStatus = "none";
|
||||
this.setSelectedDateToFirstAvailable();
|
||||
|
|
@ -2064,6 +2087,157 @@ export default {
|
|||
}
|
||||
}
|
||||
},
|
||||
async showMultiLocationModal() {
|
||||
if (!this.selectedRouteCodeData?.routeCode) {
|
||||
let maxDayRangeToShowPmTimeslot = experimentMixin.methods.getSettingValue(
|
||||
experimentSettings.SHOW_PM_DAYS_MULTI_LOCATION
|
||||
);
|
||||
let maxDayRangeToShowNoPmTimeslot = experimentMixin.methods.getSettingValue(
|
||||
experimentSettings.SHOW_NO_PM_DAYS_MULTI_LOCATION
|
||||
);
|
||||
let maxDayRangeToShowAnyTimeslot = experimentMixin.methods.getSettingValue(
|
||||
experimentSettings.SHOW_NO_AVAILABILE_DAYS_MULTI_LOCATION
|
||||
);
|
||||
let showMultiLocationAppointment = experimentMixin.methods.getSettingValue(
|
||||
experimentSettings.SHOW_MULTI_LOCATION_APPT
|
||||
);
|
||||
|
||||
// temp vars to use for testing; TO BE REMOVED
|
||||
maxDayRangeToShowPmTimeslot = 2;
|
||||
maxDayRangeToShowNoPmTimeslot = 2;
|
||||
maxDayRangeToShowAnyTimeslot = 5;
|
||||
|
||||
let promotedMobileAppointment = null;
|
||||
let promotedInshopAppointment = null;
|
||||
const todaysDate = getTodayDate();
|
||||
|
||||
// Helper function to calculate days until appointment (handles null/undefined)
|
||||
const calculateDaysUntilDate = (appointmentObj) => {
|
||||
const dateString = appointmentObj?.date;
|
||||
return dateString
|
||||
? (new Date(dateString) - todaysDate) / (1000 * 60 * 60 * 24)
|
||||
: null;
|
||||
};
|
||||
|
||||
// Fetch all appointments
|
||||
const [firstMobileAMAppt, firstMobilePMAppt, firstInshopAMAppt, firstInshopPMAppt] =
|
||||
await Promise.all([
|
||||
this.getFirstAvailableApptByTOD("AM", "mobile"),
|
||||
this.getFirstAvailableApptByTOD("PM", "mobile"),
|
||||
this.getFirstAvailableApptByTOD("AM", "inshop"),
|
||||
this.getFirstAvailableApptByTOD("PM", "inshop"),
|
||||
]);
|
||||
|
||||
// Extract dates
|
||||
const firstMobileAMDate = firstMobileAMAppt?.date;
|
||||
const firstMobilePMDate = firstMobilePMAppt?.date;
|
||||
const firstInshopAMDate = firstInshopAMAppt?.date;
|
||||
const firstInshopPMDate = firstInshopPMAppt?.date;
|
||||
|
||||
// Calculate days until appointments
|
||||
const numberOfDaysToFirstMobileAMDate = calculateDaysUntilDate(firstMobileAMAppt);
|
||||
const numberOfDaysToFirstMobilePMDate = calculateDaysUntilDate(firstMobilePMAppt);
|
||||
const numberOfDaysToFirstInshopAMDate = calculateDaysUntilDate(firstInshopAMAppt);
|
||||
const numberOfDaysToFirstInshopPMDate = calculateDaysUntilDate(firstInshopPMAppt);
|
||||
|
||||
const shouldExposeMultiLocationModal = () => {
|
||||
const isMobileAppointmentInDateRange =
|
||||
(firstMobileAMDate &&
|
||||
numberOfDaysToFirstMobileAMDate <= maxDayRangeToShowAnyTimeslot) ||
|
||||
(firstMobilePMDate &&
|
||||
numberOfDaysToFirstMobilePMDate <= maxDayRangeToShowAnyTimeslot);
|
||||
const isInshopAppointmentInDateRange =
|
||||
(firstInshopAMDate &&
|
||||
numberOfDaysToFirstInshopAMDate <= maxDayRangeToShowAnyTimeslot) ||
|
||||
(firstInshopPMDate &&
|
||||
numberOfDaysToFirstInshopPMDate <= maxDayRangeToShowAnyTimeslot);
|
||||
|
||||
if (showMultiLocationAppointment) {
|
||||
return isMobileAppointmentInDateRange && isInshopAppointmentInDateRange;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (shouldExposeMultiLocationModal()) {
|
||||
const multiLocationPopupExperiment =
|
||||
store.getters.applicationUser.experiments.find(
|
||||
(e) => e.universeName === experimentUniverses.MULTI_LOCATION_POPUP
|
||||
);
|
||||
const hasExposedMultiLocationPopup = multiLocationPopupExperiment?.isExposed;
|
||||
|
||||
if (!hasExposedMultiLocationPopup && multiLocationPopupExperiment) {
|
||||
baseMixin.methods.dispatchStoreActionWithLogging(
|
||||
storeActions.LOG_EXPERIMENT_EXPOSURE_AND_UPDATE_STORE,
|
||||
{
|
||||
userId: getUserIdValue(),
|
||||
deviceId: getDeviceIdValue(),
|
||||
sessionKey: getSessionKeyValue(),
|
||||
pageName: "schedule",
|
||||
experiment: multiLocationPopupExperiment,
|
||||
},
|
||||
"schedule",
|
||||
false
|
||||
);
|
||||
}
|
||||
if (this.isShowMultiLocationPopup) {
|
||||
if (maxDayRangeToShowPmTimeslot == 0) {
|
||||
// Selects the first available mobile time slot
|
||||
promotedMobileAppointment =
|
||||
numberOfDaysToFirstMobileAMDate <= numberOfDaysToFirstMobilePMDate
|
||||
? firstMobileAMAppt
|
||||
: firstMobilePMAppt;
|
||||
// Selects the first available inshop time slot
|
||||
promotedInshopAppointment =
|
||||
numberOfDaysToFirstInshopAMDate <= numberOfDaysToFirstInshopPMDate
|
||||
? firstInshopAMAppt
|
||||
: firstInshopPMAppt;
|
||||
} else {
|
||||
if (
|
||||
firstMobilePMDate &&
|
||||
numberOfDaysToFirstMobilePMDate <= maxDayRangeToShowPmTimeslot
|
||||
) {
|
||||
// Selects the first available PM time slot
|
||||
promotedMobileAppointment = firstMobilePMAppt;
|
||||
} else if (
|
||||
firstMobilePMDate &&
|
||||
numberOfDaysToFirstMobilePMDate >= maxDayRangeToShowNoPmTimeslot
|
||||
) {
|
||||
// Selects the first available AM time slot. If none, selects the first available PM time slot
|
||||
promotedMobileAppointment = firstMobileAMAppt
|
||||
? firstMobileAMAppt
|
||||
: firstMobilePMAppt;
|
||||
}
|
||||
|
||||
if (
|
||||
firstInshopPMDate &&
|
||||
numberOfDaysToFirstInshopPMDate <= maxDayRangeToShowPmTimeslot
|
||||
) {
|
||||
// Selects the first available PM time slot
|
||||
promotedInshopAppointment = firstInshopPMAppt;
|
||||
} else if (
|
||||
firstInshopPMDate &&
|
||||
numberOfDaysToFirstInshopPMDate >= maxDayRangeToShowNoPmTimeslot
|
||||
) {
|
||||
// Selects the first available AM time slot. If none, selects the first available PM time slot
|
||||
promotedInshopAppointment = firstInshopAMAppt
|
||||
? firstInshopAMAppt
|
||||
: firstInshopPMAppt;
|
||||
}
|
||||
}
|
||||
|
||||
if (promotedMobileAppointment && promotedInshopAppointment) {
|
||||
this.$refs.multiLocationModal.setPromotedMobileAppointment(
|
||||
promotedMobileAppointment
|
||||
);
|
||||
this.$refs.multiLocationModal.setPromotedInshopAppointment(
|
||||
promotedInshopAppointment
|
||||
);
|
||||
this.$refs.multiLocationModal.openModal();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
async getFirstAvailableApptByTOD(timeOfDay, appointmentType) {
|
||||
let selectableDays;
|
||||
if (appointmentType?.toLowerCase() == "mobile") {
|
||||
|
|
@ -2084,6 +2258,7 @@ export default {
|
|||
},
|
||||
timeSlot: null,
|
||||
date: null,
|
||||
addressCopy: null,
|
||||
};
|
||||
const isMatchingApptDay = dateObj.timeSlots.some(
|
||||
(slot) =>
|
||||
|
|
@ -2116,6 +2291,18 @@ export default {
|
|||
this.updateTimeSlot(timeSlotObj);
|
||||
this.forwardButtonAction();
|
||||
},
|
||||
updateMultiLocationTimeSlotandNavigateForward({ selectedTimeSlot, appointmentType }) {
|
||||
if (appointmentType?.toLowerCase() == "mobile") {
|
||||
this.appointmentType = AppointmentTypeStrings.MOBILE;
|
||||
} else if (appointmentType?.toLowerCase() == "inshop") {
|
||||
this.appointmentType = AppointmentTypeStrings.IN_SHOP;
|
||||
}
|
||||
this.selectedMultiLocationAppointment = true;
|
||||
this.selectedDate = selectedTimeSlot.date;
|
||||
this.updateSelectedProvider();
|
||||
this.updateTimeSlot(selectedTimeSlot);
|
||||
this.forwardButtonAction();
|
||||
},
|
||||
updateRecalAcknowledgedAndNavigateForward(isAcknowledged) {
|
||||
this.isRecalAcknowledgedForScheduling = isAcknowledged;
|
||||
this.forwardButtonAction();
|
||||
|
|
@ -2139,6 +2326,7 @@ export default {
|
|||
textBlock,
|
||||
timeSlotQuestion,
|
||||
mobileFirstModal,
|
||||
multiLocationModal,
|
||||
recalAckModal,
|
||||
alert,
|
||||
serviceZipModalQuestion,
|
||||
|
|
|
|||
Loading…
Reference in a new issue