Merge branch 'release/2026.02.12' into feature/CASH-1790-refactor-fixes
This commit is contained in:
commit
85ab8462f9
12 changed files with 201 additions and 125 deletions
|
|
@ -10,7 +10,6 @@ module.exports = {
|
||||||
},
|
},
|
||||||
transformIgnorePatterns: ["'/node_modules/(?!vee-validate)"],
|
transformIgnorePatterns: ["'/node_modules/(?!vee-validate)"],
|
||||||
moduleFileExtensions: ["js", "vue"],
|
moduleFileExtensions: ["js", "vue"],
|
||||||
modulePathIgnorePatterns: ["vin-lookup"],
|
|
||||||
collectCoverageFrom: [
|
collectCoverageFrom: [
|
||||||
"src/**/*.{js,vue}",
|
"src/**/*.{js,vue}",
|
||||||
"!src/main.js",
|
"!src/main.js",
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,9 @@ const experimentSettings = {
|
||||||
SHOW_PM_MOBILE_DAYS: "Show_PMmobileDays",
|
SHOW_PM_MOBILE_DAYS: "Show_PMmobileDays",
|
||||||
SHOW_NO_PM_MOBILE_DAYS: "Show_NoPMmobileDays",
|
SHOW_NO_PM_MOBILE_DAYS: "Show_NoPMmobileDays",
|
||||||
SHOW_NO_MOBILE_AVAILABLE_DAYS: "Show_NoMobileAvailableDays",
|
SHOW_NO_MOBILE_AVAILABLE_DAYS: "Show_NoMobileAvailableDays",
|
||||||
|
SHOW_MOBILE_FIRST_POPUP_REPAIR: "Show_Mobile_First_Popup_Repair",
|
||||||
|
SHOW_MOBILE_FIRST_POPUP_REPLACE_WITHOUT_MSR: "Show_Mobile_First_Popup_Replace_Without_MSR",
|
||||||
|
SHOW_MOBILE_FIRST_POPUP_REPLACE_WITH_MSR: "Show_Mobile_First_Popup_Replace_With_MSR",
|
||||||
OFFER_OEM: "OfferOem",
|
OFFER_OEM: "OfferOem",
|
||||||
USE_ADYEN_PAYMENT: "UseAdyenPayment",
|
USE_ADYEN_PAYMENT: "UseAdyenPayment",
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -289,7 +289,7 @@ export default {
|
||||||
if (event.screenX === 0 && event.screenY === 0) {
|
if (event.screenX === 0 && event.screenY === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.$emit("date-clicked", date);
|
this.$emit("date-selected", date);
|
||||||
},
|
},
|
||||||
getWeekStartDate(dateString) {
|
getWeekStartDate(dateString) {
|
||||||
const date = convertDateStringToDate(dateString);
|
const date = convertDateStringToDate(dateString);
|
||||||
|
|
@ -332,9 +332,9 @@ export default {
|
||||||
let preSelectedDateMonthEnd = this.getMonthEnd(
|
let preSelectedDateMonthEnd = this.getMonthEnd(
|
||||||
preSelectedDate.replace("-mobile", "")
|
preSelectedDate.replace("-mobile", "")
|
||||||
);
|
);
|
||||||
let weekIncludesPreSelectedMonthEnd = false;
|
let monthEndsByThisWeek = false;
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while (!weekIncludesPreSelectedMonthEnd) {
|
while (!monthEndsByThisWeek && i < 52) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
weekStartDate = this.getNextWeekSunday(weekEndDate);
|
weekStartDate = this.getNextWeekSunday(weekEndDate);
|
||||||
weekEndDate = this.getWeekEndDate(weekStartDate);
|
weekEndDate = this.getWeekEndDate(weekStartDate);
|
||||||
|
|
@ -346,7 +346,12 @@ export default {
|
||||||
preSelectedDateMonthEnd === weekEndDate
|
preSelectedDateMonthEnd === weekEndDate
|
||||||
) {
|
) {
|
||||||
weekEndDate = preSelectedDateMonthEnd;
|
weekEndDate = preSelectedDateMonthEnd;
|
||||||
weekIncludesPreSelectedMonthEnd = true;
|
monthEndsByThisWeek = true;
|
||||||
|
} else if (preSelectedDateMonthEnd < weekEndDate) {
|
||||||
|
// Additional case: if month ends before this week (but not necessarily during it),
|
||||||
|
// still cut the loop here, but don't truncate the week.
|
||||||
|
|
||||||
|
monthEndsByThisWeek = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
weeks.push({
|
weeks.push({
|
||||||
|
|
|
||||||
|
|
@ -138,10 +138,7 @@ export default {
|
||||||
}
|
}
|
||||||
// Pass data to Salesforce chat if available
|
// Pass data to Salesforce chat if available
|
||||||
const transfer = event.detail;
|
const transfer = event.detail;
|
||||||
if (
|
if (window.embeddedservice_bootstrap && window.embeddedservice_bootstrap.prechatAPI) {
|
||||||
window.embeddedservice_bootstrap &&
|
|
||||||
typeof window.embeddedservice_bootstrap.bootstrapEmbeddedService === "function"
|
|
||||||
) {
|
|
||||||
if (
|
if (
|
||||||
transfer &&
|
transfer &&
|
||||||
transfer.data &&
|
transfer.data &&
|
||||||
|
|
|
||||||
|
|
@ -246,7 +246,7 @@ export default {
|
||||||
console.log(`Price = ${this.amountDue}`);
|
console.log(`Price = ${this.amountDue}`);
|
||||||
console.log(`Adyen Price = ${this.adyenPriceTotal}`);
|
console.log(`Adyen Price = ${this.adyenPriceTotal}`);
|
||||||
|
|
||||||
const requestBody = this.adyenInitRequestInfo;
|
const requestBody = this.getAdyenInitRequestInfo();
|
||||||
|
|
||||||
console.log(`Calling with:`);
|
console.log(`Calling with:`);
|
||||||
console.log(requestBody);
|
console.log(requestBody);
|
||||||
|
|
@ -294,6 +294,15 @@ export default {
|
||||||
|
|
||||||
console.log(checkout);
|
console.log(checkout);
|
||||||
|
|
||||||
|
const expiryTime = new Date(checkout.options.expiresAt);
|
||||||
|
const expiryInterval = expiryTime.getTime() - new Date().getTime();
|
||||||
|
|
||||||
|
const handleTimeout = () => {
|
||||||
|
this.resetAdyenDropin();
|
||||||
|
};
|
||||||
|
|
||||||
|
const timeout = setTimeout(handleTimeout, expiryInterval);
|
||||||
|
|
||||||
const configuration = {
|
const configuration = {
|
||||||
paymentMethodsConfiguration: {
|
paymentMethodsConfiguration: {
|
||||||
ideal: {
|
ideal: {
|
||||||
|
|
@ -496,7 +505,7 @@ export default {
|
||||||
await baseMixin.methods.dispatchStoreAction(storeActions.CREATE_SUBMITTED_STATE);
|
await baseMixin.methods.dispatchStoreAction(storeActions.CREATE_SUBMITTED_STATE);
|
||||||
|
|
||||||
this.$router.navigateWithoutSaving(
|
this.$router.navigateWithoutSaving(
|
||||||
this.navigationScenarios.CLICKED_FORWARD,
|
this.navigationScenarios.CLICKED_PAY_LATER,
|
||||||
this.pageName
|
this.pageName
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -505,14 +514,26 @@ export default {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
async resetAdyenDropin() {
|
||||||
piaType() {
|
this?.dropinComponent?.unmount();
|
||||||
return this.$store.getters.order.payment.piaType;
|
await this.initializeAdyen();
|
||||||
},
|
},
|
||||||
|
|
||||||
adyenInitRequestInfo() {
|
// Now a Method so it is always freshly called and not cached.
|
||||||
|
getIdempotencyKey() {
|
||||||
|
const currentDateTime = new Date();
|
||||||
|
const currentHour = currentDateTime.getUTCHours();
|
||||||
|
const currentDate = currentDateTime.getUTCDate();
|
||||||
|
const id = this?.$store?.getters?.order?.referralCorrelationId;
|
||||||
|
const system = this.sourceSystem;
|
||||||
|
const total = this.adyenPriceTotal;
|
||||||
|
|
||||||
|
return `${id}-${system}-${currentDate}-${currentHour}-${total}`;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Now a Method so it is always freshly called and not cached.
|
||||||
|
getAdyenInitRequestInfo() {
|
||||||
return {
|
return {
|
||||||
sourceSystem: this.sourceSystem,
|
sourceSystem: this.sourceSystem,
|
||||||
referralSequenceNumber: this.$store.getters.order.referralSequenceNumber,
|
referralSequenceNumber: this.$store.getters.order.referralSequenceNumber,
|
||||||
|
|
@ -529,9 +550,15 @@ export default {
|
||||||
IP: "127.0.0.1", // TODO
|
IP: "127.0.0.1", // TODO
|
||||||
firstName: this.$store.getters.order.customer.firstName,
|
firstName: this.$store.getters.order.customer.firstName,
|
||||||
lastName: this.$store.getters.order.customer.lastName,
|
lastName: this.$store.getters.order.customer.lastName,
|
||||||
idempotencyKey: this.idempotencyKey,
|
idempotencyKey: this.getIdempotencyKey(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
piaType() {
|
||||||
|
return this.$store.getters.order.payment.piaType;
|
||||||
|
},
|
||||||
|
|
||||||
workOrderNumberLastSixDigits() {
|
workOrderNumberLastSixDigits() {
|
||||||
const workOrderNumber = this.$store.getters.order.workOrderNumber ?? "";
|
const workOrderNumber = this.$store.getters.order.workOrderNumber ?? "";
|
||||||
|
|
@ -588,10 +615,6 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
idempotencyKey() {
|
|
||||||
return `${this.$store.getters.order.referralCorrelationId}-${this.sourceSystem}`;
|
|
||||||
},
|
|
||||||
|
|
||||||
sourceSystem() {
|
sourceSystem() {
|
||||||
return "FMG-2.0";
|
return "FMG-2.0";
|
||||||
},
|
},
|
||||||
|
|
@ -601,7 +624,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
adyenPriceTotal() {
|
adyenPriceTotal() {
|
||||||
return this.amountDue * 100;
|
return Math.round(this.amountDue * 100);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Cart info
|
// Cart info
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
cmsWidgetName="ScheduleYourServiceWidget"
|
cmsWidgetName="ScheduleYourServiceWidget"
|
||||||
id="schedule-your-service" />
|
id="schedule-your-service" />
|
||||||
<appointmentTypeQuestion
|
<appointmentTypeQuestion
|
||||||
|
v-if="hasSelectableDatesLoaded"
|
||||||
v-model="appointmentTypeFromAppointmentTypeQuestion"
|
v-model="appointmentTypeFromAppointmentTypeQuestion"
|
||||||
v-show="isAppointmentTypeDisplayed"
|
v-show="isAppointmentTypeDisplayed"
|
||||||
:isServiceableMobile="isServiceableMobile"
|
:isServiceableMobile="isServiceableMobile"
|
||||||
|
|
@ -97,15 +98,11 @@
|
||||||
linkWidgetName="ServiceZipLinkWidget"
|
linkWidgetName="ServiceZipLinkWidget"
|
||||||
modalWidgetName="ServiceZipModalWidget" />
|
modalWidgetName="ServiceZipModalWidget" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="row your-shop-location" v-if="showInshopComponent">
|
||||||
class="row your-shop-location"
|
|
||||||
v-if="appointmentType && appointmentType !== appointmentTypeStrings.MOBILE">
|
|
||||||
<div class="col-12 col-md-10 col-lg-8 col-xl-7">
|
<div class="col-12 col-md-10 col-lg-8 col-xl-7">
|
||||||
<div>
|
<div>
|
||||||
<shopLocation
|
<shopLocation
|
||||||
v-if="
|
v-if="showInshopComponent"
|
||||||
appointmentType && appointmentType !== appointmentTypeStrings.MOBILE
|
|
||||||
"
|
|
||||||
:selectedShop="selectedShopAnswer"
|
:selectedShop="selectedShopAnswer"
|
||||||
:selectedProviderNumberFromParent="selectedProvider?.providerNumber"
|
:selectedProviderNumberFromParent="selectedProvider?.providerNumber"
|
||||||
:shopProviderDataFromParent="shopProviderData"
|
:shopProviderDataFromParent="shopProviderData"
|
||||||
|
|
@ -139,8 +136,6 @@
|
||||||
:isOvernightDropoff="isOvernightDropoff" />
|
:isOvernightDropoff="isOvernightDropoff" />
|
||||||
</div>
|
</div>
|
||||||
<datePicker
|
<datePicker
|
||||||
:currentZip="zipCode"
|
|
||||||
:currentProviderNumber="selectedProvider?.providerNumber"
|
|
||||||
v-show="appointmentType"
|
v-show="appointmentType"
|
||||||
customComponentId="dateQuestion"
|
customComponentId="dateQuestion"
|
||||||
selectableDatesSetting="custom"
|
selectableDatesSetting="custom"
|
||||||
|
|
@ -150,7 +145,7 @@
|
||||||
class="text-link-small"
|
class="text-link-small"
|
||||||
:getMoreDatesCallback="getMoreScheduleData"
|
:getMoreDatesCallback="getMoreScheduleData"
|
||||||
validationRules="date-required"
|
validationRules="date-required"
|
||||||
@date-clicked="handleDateClicked"
|
@date-selected="handleDateSelected"
|
||||||
:pricingByDayBasePrice="pricingByDayBasePrice"
|
:pricingByDayBasePrice="pricingByDayBasePrice"
|
||||||
:pricingByDayUpcharge="pricingByDayUpcharge"
|
:pricingByDayUpcharge="pricingByDayUpcharge"
|
||||||
:showPricingByDay="showPricingByDay"
|
:showPricingByDay="showPricingByDay"
|
||||||
|
|
@ -638,6 +633,21 @@ export default {
|
||||||
this.handleZipCodeChange(newValue);
|
this.handleZipCodeChange(newValue);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
showInshopComponent() {
|
||||||
|
// If we get no inshop or mobile data, then show the shoplocation component so the user is not
|
||||||
|
// looking at a half blank screen with no way to try another zip/location.
|
||||||
|
if (
|
||||||
|
(this.inshopTimeSlotsData === undefined &&
|
||||||
|
this.mobileTimeSlotsData === undefined) ||
|
||||||
|
(this.inshopTimeSlotsData.days.length === 0 &&
|
||||||
|
this.mobileTimeSlotsData.days.length === 0) ||
|
||||||
|
(this.appointmentType && this.appointmentType !== AppointmentTypeStrings.MOBILE)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
isMobileSelected() {
|
isMobileSelected() {
|
||||||
return (
|
return (
|
||||||
this.appointmentType == AppointmentTypeStrings.MOBILE ||
|
this.appointmentType == AppointmentTypeStrings.MOBILE ||
|
||||||
|
|
@ -660,12 +670,12 @@ export default {
|
||||||
showTimeSlotQuestion() {
|
showTimeSlotQuestion() {
|
||||||
if (
|
if (
|
||||||
this.selectedDate &&
|
this.selectedDate &&
|
||||||
this.selectedDate.includes("mobile") &&
|
this.appointmentTypeFromAppointmentTypeQuestion &&
|
||||||
!this.isMobileSelected
|
this.timeSlotsForSelectedDate
|
||||||
) {
|
) {
|
||||||
this.setSelectedDateToFirstAvailable();
|
return true;
|
||||||
}
|
}
|
||||||
return this.selectedDate && this.appointmentTypeFromAppointmentTypeQuestion;
|
return false;
|
||||||
},
|
},
|
||||||
isMobileStaticRecalibrationApplicable() {
|
isMobileStaticRecalibrationApplicable() {
|
||||||
return (
|
return (
|
||||||
|
|
@ -940,6 +950,13 @@ export default {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
hasSelectableDatesLoaded() {
|
||||||
|
if (this.isMobileSelected) {
|
||||||
|
return this.selectableDatesMobile?.days?.length > 0;
|
||||||
|
} else {
|
||||||
|
return this.selectableDatesInshop?.days?.length > 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
splitCopyOnCMSPlaceHolder,
|
splitCopyOnCMSPlaceHolder,
|
||||||
|
|
@ -1261,8 +1278,6 @@ export default {
|
||||||
async initializeDatePicker() {
|
async initializeDatePicker() {
|
||||||
this.isShowMobileFirstAppt && this.showLoadingModal();
|
this.isShowMobileFirstAppt && this.showLoadingModal();
|
||||||
|
|
||||||
this.selectedDate = null;
|
|
||||||
|
|
||||||
const includeMobileTimeSlots = this.isServiceableMobile;
|
const includeMobileTimeSlots = this.isServiceableMobile;
|
||||||
const includeInshopTimeSlots = this.isServiceableInshop || this.isServiceableDropoff;
|
const includeInshopTimeSlots = this.isServiceableInshop || this.isServiceableDropoff;
|
||||||
const datePickerInitialData = await this.$refs.datePicker.loadInitialData({
|
const datePickerInitialData = await this.$refs.datePicker.loadInitialData({
|
||||||
|
|
@ -1637,12 +1652,12 @@ export default {
|
||||||
handleWaitListRequested(value) {
|
handleWaitListRequested(value) {
|
||||||
this.waitListRequested = value;
|
this.waitListRequested = value;
|
||||||
},
|
},
|
||||||
handleDateClicked(date) {
|
handleDateSelected(date) {
|
||||||
// do something to mark this as upcharge day or not...
|
const previousDate = this.selectedDate;
|
||||||
if (date.isPricingByDayUpchargeDay) {
|
// check if date actually changed
|
||||||
this.includePricingByDayUpcharge = true;
|
if (previousDate !== date.dateString) {
|
||||||
} else {
|
this.selectedTimeSlotInfo = this.getEmptyTimeSlot();
|
||||||
this.includePricingByDayUpcharge = false;
|
this.updateFooterButtonText(this.selectedTimeSlotInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: REMOVE AS PART OF CASH-1634
|
// TODO: REMOVE AS PART OF CASH-1634
|
||||||
|
|
@ -1669,17 +1684,7 @@ export default {
|
||||||
updateTimeSlot(timeSlotObj) {
|
updateTimeSlot(timeSlotObj) {
|
||||||
if (!timeSlotObj?.routeCode) {
|
if (!timeSlotObj?.routeCode) {
|
||||||
this.appointmentType = AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF;
|
this.appointmentType = AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF;
|
||||||
this.selectedTimeSlotInfo = {
|
this.selectedTimeSlotInfo = this.getEmptyTimeSlot();
|
||||||
timeSlot: {
|
|
||||||
date: null,
|
|
||||||
routeCode: null,
|
|
||||||
startTime: null,
|
|
||||||
endTime: null,
|
|
||||||
jobMaxMinutes: null,
|
|
||||||
jobMinMinutes: null,
|
|
||||||
},
|
|
||||||
isPremiumAppointment: null,
|
|
||||||
};
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const timeSlot = this.timeSlotsForSelectedDate?.timeSlots?.find(
|
const timeSlot = this.timeSlotsForSelectedDate?.timeSlots?.find(
|
||||||
|
|
@ -1801,6 +1806,9 @@ export default {
|
||||||
},
|
},
|
||||||
handleAppointmentTypeChange(newAppointmentType) {
|
handleAppointmentTypeChange(newAppointmentType) {
|
||||||
this.updateFooterButtonText();
|
this.updateFooterButtonText();
|
||||||
|
// if time appointment type changes, clear any selected time slot
|
||||||
|
this.selectedTimeSlotInfo = this.getEmptyTimeSlot();
|
||||||
|
|
||||||
if (newAppointmentType === AppointmentTypeStrings.MOBILE) {
|
if (newAppointmentType === AppointmentTypeStrings.MOBILE) {
|
||||||
// Remember last shop selected if previous selection was inshop/dropoff
|
// Remember last shop selected if previous selection was inshop/dropoff
|
||||||
if (
|
if (
|
||||||
|
|
@ -1809,17 +1817,6 @@ export default {
|
||||||
AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF) &&
|
AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF) &&
|
||||||
this.selectedProvider
|
this.selectedProvider
|
||||||
) {
|
) {
|
||||||
this.selectedTimeSlotInfo = {
|
|
||||||
timeSlot: {
|
|
||||||
date: null,
|
|
||||||
routeCode: null,
|
|
||||||
startTime: null,
|
|
||||||
endTime: null,
|
|
||||||
jobMaxMinutes: null,
|
|
||||||
jobMinMinutes: null,
|
|
||||||
},
|
|
||||||
isPremiumAppointment: null,
|
|
||||||
};
|
|
||||||
this.lastSelectedInshopOrDropoffProvider = this.selectedProvider;
|
this.lastSelectedInshopOrDropoffProvider = this.selectedProvider;
|
||||||
}
|
}
|
||||||
this.appointmentType = AppointmentTypeStrings.MOBILE;
|
this.appointmentType = AppointmentTypeStrings.MOBILE;
|
||||||
|
|
@ -1846,7 +1843,8 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
this.appointmentType = null;
|
this.appointmentType = null;
|
||||||
}
|
}
|
||||||
this.setSelectedDateToFirstAvailable();
|
this.setSelectedDateToFirstAvailable(); // v-if on appointmentTypeQuestion ensures dates have been loaded by now
|
||||||
|
this.setDisplayWaitList(); // v-if on appointmentTypeQuestion ensures dates have been loaded by now
|
||||||
},
|
},
|
||||||
setSelectedDateToFirstAvailable() {
|
setSelectedDateToFirstAvailable() {
|
||||||
this.selectedDate = this.getFirstAvailableDate();
|
this.selectedDate = this.getFirstAvailableDate();
|
||||||
|
|
@ -1888,6 +1886,20 @@ export default {
|
||||||
const noMobileAvailableDays = experimentMixin.methods.getSettingValue(
|
const noMobileAvailableDays = experimentMixin.methods.getSettingValue(
|
||||||
experimentSettings.SHOW_NO_MOBILE_AVAILABLE_DAYS
|
experimentSettings.SHOW_NO_MOBILE_AVAILABLE_DAYS
|
||||||
);
|
);
|
||||||
|
const mobileFirstPopupRepairSetting = experimentMixin.methods.getSettingValue(
|
||||||
|
experimentSettings.SHOW_MOBILE_FIRST_POPUP_REPAIR
|
||||||
|
);
|
||||||
|
const mobileFirstPopupReplaceWithoutMsrSetting =
|
||||||
|
experimentMixin.methods.getSettingValue(
|
||||||
|
experimentSettings.SHOW_MOBILE_FIRST_POPUP_REPLACE_WITHOUT_MSR
|
||||||
|
);
|
||||||
|
const mobileFirstPopupReplaceWithMsrSetting =
|
||||||
|
experimentMixin.methods.getSettingValue(
|
||||||
|
experimentSettings.SHOW_MOBILE_FIRST_POPUP_REPLACE_WITH_MSR
|
||||||
|
);
|
||||||
|
|
||||||
|
const order = this.$store.getters.order;
|
||||||
|
const isRepair = order.damage.isRepair;
|
||||||
|
|
||||||
let preSelectedMobileAppointment = null;
|
let preSelectedMobileAppointment = null;
|
||||||
const todaysDate = getTodayDate();
|
const todaysDate = getTodayDate();
|
||||||
|
|
@ -1904,12 +1916,63 @@ export default {
|
||||||
(new Date(firstMobilePMDate) - todaysDate) / (1000 * 60 * 60 * 24);
|
(new Date(firstMobilePMDate) - todaysDate) / (1000 * 60 * 60 * 24);
|
||||||
|
|
||||||
const shouldExposeMobileFirstAppointment = () => {
|
const shouldExposeMobileFirstAppointment = () => {
|
||||||
return (
|
const mobileFirstDaysCheck =
|
||||||
(firstMobileAMDate &&
|
(firstMobileAMDate &&
|
||||||
numberOfDaysToFirstMobileAMDate <= noMobileAvailableDays) ||
|
numberOfDaysToFirstMobileAMDate <= noMobileAvailableDays) ||
|
||||||
(firstMobilePMDate &&
|
(firstMobilePMDate &&
|
||||||
numberOfDaysToFirstMobilePMDate <= noMobileAvailableDays)
|
numberOfDaysToFirstMobilePMDate <= noMobileAvailableDays);
|
||||||
);
|
debugLog("::: Mobile First Days Check:", mobileFirstDaysCheck);
|
||||||
|
|
||||||
|
if (
|
||||||
|
mobileFirstPopupRepairSetting === "false" &&
|
||||||
|
mobileFirstPopupReplaceWithoutMsrSetting === "false" &&
|
||||||
|
mobileFirstPopupReplaceWithMsrSetting === "false"
|
||||||
|
) {
|
||||||
|
debugLog(
|
||||||
|
"::: Mobile First Damage Popup settings are all false, using default check."
|
||||||
|
);
|
||||||
|
return mobileFirstDaysCheck;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRepair) {
|
||||||
|
if (mobileFirstPopupRepairSetting === "true") {
|
||||||
|
debugLog(
|
||||||
|
"::: Mobile First Popup Repair setting is true, showing mobile first appointment for repair."
|
||||||
|
);
|
||||||
|
return mobileFirstDaysCheck;
|
||||||
|
} else {
|
||||||
|
debugLog(
|
||||||
|
"::: Mobile First Popup Repair setting is false, not showing mobile first appointment for repair."
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isMobileStaticRecalibrationApplicable) {
|
||||||
|
if (mobileFirstPopupReplaceWithMsrSetting === "true") {
|
||||||
|
debugLog(
|
||||||
|
"::: Mobile First Popup Replace with MSR setting is true, showing mobile first appointment for replace with MSR."
|
||||||
|
);
|
||||||
|
return mobileFirstDaysCheck;
|
||||||
|
} else {
|
||||||
|
debugLog(
|
||||||
|
"::: Mobile First Popup Replace with MSR setting is false, not showing mobile first appointment for replace with MSR."
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mobileFirstPopupReplaceWithoutMsrSetting === "true") {
|
||||||
|
debugLog(
|
||||||
|
"::: Mobile First Popup Replace without MSR setting is true, showing mobile first appointment for replace without MSR."
|
||||||
|
);
|
||||||
|
return mobileFirstDaysCheck;
|
||||||
|
} else {
|
||||||
|
debugLog(
|
||||||
|
"::: Mobile First Popup Replace without MSR setting is false, not showing mobile first appointment for replace without MSR."
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (shouldExposeMobileFirstAppointment()) {
|
if (shouldExposeMobileFirstAppointment()) {
|
||||||
|
|
@ -2008,35 +2071,9 @@ export default {
|
||||||
watch: {
|
watch: {
|
||||||
appointmentTypeFromAppointmentTypeQuestion: {
|
appointmentTypeFromAppointmentTypeQuestion: {
|
||||||
handler(newValue, oldValue) {
|
handler(newValue, oldValue) {
|
||||||
this.handleAppointmentTypeChange(newValue);
|
this.handleAppointmentTypeChange(newValue); // v-if on appointmentTypeQuestion ensures dates have been loaded by now
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
selectedDate(newValue, oldValue) {
|
|
||||||
// Clear time slot selection if date selected changes
|
|
||||||
const selectedDate = this.getSelectedDateFromStore();
|
|
||||||
if (oldValue && this.appointmentType === AppointmentTypeStrings.MOBILE) {
|
|
||||||
oldValue = `${oldValue}-mobile`;
|
|
||||||
}
|
|
||||||
const hasValueChanged = newValue !== oldValue;
|
|
||||||
const isDateDifferent = (newValue || oldValue) !== selectedDate;
|
|
||||||
|
|
||||||
if (hasValueChanged && isDateDifferent && !this.selectedMobileFirstAppointment) {
|
|
||||||
this.selectedTimeSlotInfo = {
|
|
||||||
timeSlot: {
|
|
||||||
date: null,
|
|
||||||
routeCode: null,
|
|
||||||
startTime: null,
|
|
||||||
endTime: null,
|
|
||||||
jobMaxMinutes: null,
|
|
||||||
jobMinMinutes: null,
|
|
||||||
},
|
|
||||||
isPremiumAppointment: null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
selectedTimeSlotInfo(newValue) {
|
|
||||||
this.updateFooterButtonText(newValue);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
funnelHeader,
|
funnelHeader,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="time-slots-question">
|
<div class="time-slots-question">
|
||||||
<buttonQuestion
|
<buttonQuestion
|
||||||
|
ref="chooseDropOffOrInshop"
|
||||||
v-if="isDropOffAppointmentAvailable"
|
v-if="isDropOffAppointmentAvailable"
|
||||||
v-model="selectedAnswerForDropOffOrInshop"
|
v-model="selectedAnswerForDropOffOrInshop"
|
||||||
@update:modelValue="dropOffSelectionChanged"
|
@update:modelValue="dropOffSelectionChanged"
|
||||||
|
|
@ -23,8 +24,9 @@
|
||||||
</div>
|
</div>
|
||||||
</buttonQuestion>
|
</buttonQuestion>
|
||||||
<buttonQuestion
|
<buttonQuestion
|
||||||
ref="buttonQuestion"
|
ref="chooseTimeSlot"
|
||||||
v-if="shouldDisplayTimeSlotQuestion"
|
v-if="displayTimeSlotQuestion"
|
||||||
|
v-model="selectedAnswerForTimeSlots"
|
||||||
@update:modelValue="timeSlotSelectionChanged"
|
@update:modelValue="timeSlotSelectionChanged"
|
||||||
buttonTypeString="timeSlotModalListButton"
|
buttonTypeString="timeSlotModalListButton"
|
||||||
:buttonTypeObject="timeSlotModalListButton"
|
:buttonTypeObject="timeSlotModalListButton"
|
||||||
|
|
@ -40,7 +42,6 @@
|
||||||
}"
|
}"
|
||||||
groupName="chooseTimeSlot"
|
groupName="chooseTimeSlot"
|
||||||
textPosition="text-center"
|
textPosition="text-center"
|
||||||
v-model="selectedAnswerForTimeSlots"
|
|
||||||
questionText="Available times:"
|
questionText="Available times:"
|
||||||
isRequired
|
isRequired
|
||||||
validationRules="time-slot-required" />
|
validationRules="time-slot-required" />
|
||||||
|
|
@ -393,7 +394,7 @@ export default {
|
||||||
this.answersForDropOffQuestion[0].value !== PICK_A_TIME_BUTTON_VALUE
|
this.answersForDropOffQuestion[0].value !== PICK_A_TIME_BUTTON_VALUE
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
shouldDisplayTimeSlotQuestion() {
|
displayTimeSlotQuestion() {
|
||||||
return (
|
return (
|
||||||
this.selectedAnswerForDropOffOrInshop == PICK_A_TIME_BUTTON_VALUE ||
|
this.selectedAnswerForDropOffOrInshop == PICK_A_TIME_BUTTON_VALUE ||
|
||||||
!this.isDropOffAppointmentAvailable
|
!this.isDropOffAppointmentAvailable
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ export default {
|
||||||
},
|
},
|
||||||
set: function (newValue) {
|
set: function (newValue) {
|
||||||
this.$emit("update:modelValue", newValue);
|
this.$emit("update:modelValue", newValue);
|
||||||
|
this.$emit("handle-appointment-type-change", newValue);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
isMobileOnly() {
|
isMobileOnly() {
|
||||||
|
|
|
||||||
|
|
@ -95,9 +95,8 @@ describe("vin-lookup.vue", () => {
|
||||||
it("Should call navigateForward() if the store carId does not match the vin response carId but does match previously enterted carId and forward button is clicked", async () => {
|
it("Should call navigateForward() if the store carId does not match the vin response carId but does match previously enterted carId and forward button is clicked", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
mockOutPromises({ carId: "C11111" });
|
mockOutPromises({ carId: "C11111" }); //this line in above tests affects this test, so adding to here too till we figure out how to isolate the calls
|
||||||
|
|
||||||
wrapper.vm.lookupVehicle = jest.fn().mockImplementation(() => vinPromise);
|
|
||||||
wrapper.vm.navigateForward = jest.fn();
|
wrapper.vm.navigateForward = jest.fn();
|
||||||
wrapper.vm.previouslyEnteredCarId = "C11111";
|
wrapper.vm.previouslyEnteredCarId = "C11111";
|
||||||
|
|
||||||
|
|
@ -111,6 +110,7 @@ describe("vin-lookup.vue", () => {
|
||||||
it("Should not call navigateForward() if zip service returns a non-serviceable flag", async () => {
|
it("Should not call navigateForward() if zip service returns a non-serviceable flag", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
mockOutPromises({ carId: "C11111" }); //this line in above tests affects this test, so adding to here too till we figure out how to isolate the calls
|
||||||
const zipValidationApiResponse = {
|
const zipValidationApiResponse = {
|
||||||
data: {
|
data: {
|
||||||
isServiceable: false,
|
isServiceable: false,
|
||||||
|
|
@ -135,6 +135,8 @@ describe("vin-lookup.vue", () => {
|
||||||
it("Should not call navigateForward() when forward button is clicked but lookupVehicle errors out.", async () => {
|
it("Should not call navigateForward() when forward button is clicked but lookupVehicle errors out.", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
mockOutPromises({ carId: "C11111" }); //this line in above tests affects this test, so adding to here too till we figure out how to isolate the calls
|
||||||
|
|
||||||
wrapper.vm.vinTouched = true;
|
wrapper.vm.vinTouched = true;
|
||||||
wrapper.vm.vin = "foo";
|
wrapper.vm.vin = "foo";
|
||||||
wrapper.vm.initialVin = "!foo";
|
wrapper.vm.initialVin = "!foo";
|
||||||
|
|
@ -164,6 +166,7 @@ describe("vin-lookup.vue", () => {
|
||||||
isCarIdDifferent: true,
|
isCarIdDifferent: true,
|
||||||
isSelectedGlassAvailableForVehicle: false,
|
isSelectedGlassAvailableForVehicle: false,
|
||||||
});
|
});
|
||||||
|
wrapper.vm.pageName = "vin-lookup";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await wrapper.vm.navigateForward();
|
await wrapper.vm.navigateForward();
|
||||||
|
|
@ -172,9 +175,7 @@ describe("vin-lookup.vue", () => {
|
||||||
expect(wrapper.vm.$router.navigateWithSaving).toBeCalledTimes(1);
|
expect(wrapper.vm.$router.navigateWithSaving).toBeCalledTimes(1);
|
||||||
expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith(
|
expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith(
|
||||||
navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
|
navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
|
||||||
wrapper.vm.$route,
|
"vin-lookup"
|
||||||
expect.anything(),
|
|
||||||
expect.anything()
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -251,12 +252,14 @@ describe("vin-lookup.vue", () => {
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const vinLookup = wrapper.findComponent('[data-test="vin-lookup-component"]');
|
const vinLookup = wrapper.findComponent({ ref: "vinLookupQuestion" });
|
||||||
vinLookup.trigger("imageLookupError");
|
vinLookup.trigger("image-lookup-error");
|
||||||
await wrapper.vm.$nextTick();
|
await wrapper.vm.$nextTick();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.findAllComponents({ name: "alert" }).length).toBe(1);
|
expect(wrapper.vm.displayVinScanFailedAlert).toBe(true);
|
||||||
|
//TODO - Fix original check for alert box but should show if displayVinScanFailedAlert is true which I'm checking
|
||||||
|
//expect(wrapper.findAllComponents({ cmsWidgetName: "AlertVinScanFailed" }).length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should hide the AlertNoService when displayNoServiceAlert is false", async () => {
|
test("should hide the AlertNoService when displayNoServiceAlert is false", async () => {
|
||||||
|
|
@ -277,6 +280,7 @@ describe("vin-lookup.vue", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
describe("getVinFromImage", () => {
|
describe("getVinFromImage", () => {
|
||||||
test("GetVinFromImage resolves with first valid VIN when any vins are returned.", async () => {
|
test("GetVinFromImage resolves with first valid VIN when any vins are returned.", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
@ -289,7 +293,7 @@ describe("vin-lookup.vue", () => {
|
||||||
|
|
||||||
const storeMixin = {
|
const storeMixin = {
|
||||||
methods: {
|
methods: {
|
||||||
dispatchStoreAction: lookup,
|
dispatchStoreActionWithLogging: lookup,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -354,6 +358,7 @@ describe("vin-lookup.vue", () => {
|
||||||
await expect(promise).rejects.toEqual("An error occurred during the lookup.");
|
await expect(promise).rejects.toEqual("An error occurred during the lookup.");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
});
|
});
|
||||||
|
|
||||||
function setupMocks({ customMountOptions }) {
|
function setupMocks({ customMountOptions }) {
|
||||||
|
|
|
||||||
|
|
@ -393,21 +393,18 @@ export default {
|
||||||
|
|
||||||
// Check if Service Zip entered is serviceable then save the ZIP info
|
// Check if Service Zip entered is serviceable then save the ZIP info
|
||||||
if (zipCodeData.isServiceable) {
|
if (zipCodeData.isServiceable) {
|
||||||
//Only save the zipCode, state, and zipCodeCtu if the zip changed or we lack zipCodeCtu
|
//Always save the service zip info even if it was not changed;
|
||||||
if (
|
// if it didn't change it doesn't alter other values and this makes it more consistent
|
||||||
this.$store.getters.order.serviceLocation.zipCode != this.serviceZipCode ||
|
await this.dispatchStoreAction(
|
||||||
!this.$store.getters.order.serviceLocation.zipCodeCtu
|
storeActions.SAVE_SERVICE_ZIP_CODE_INFO,
|
||||||
) {
|
{
|
||||||
await this.dispatchStoreAction(
|
state: zipCodeData.state,
|
||||||
storeActions.SAVE_SERVICE_ZIP_CODE_INFO,
|
zipCode: this.serviceZipCode,
|
||||||
{
|
zipCodeCtu: zipCodeData.zipCodeCtu,
|
||||||
state: zipCodeData.state,
|
},
|
||||||
zipCode: this.serviceZipCode,
|
false
|
||||||
zipCodeCtu: zipCodeData.zipCodeCtu,
|
);
|
||||||
},
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// if no value due to field being optional, blank both phone and email address
|
// if no value due to field being optional, blank both phone and email address
|
||||||
if (!this.emailOrSms) {
|
if (!this.emailOrSms) {
|
||||||
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, "", false);
|
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, "", false);
|
||||||
|
|
|
||||||
|
|
@ -569,6 +569,10 @@ const routingTable = function () {
|
||||||
piaError: "true",
|
piaError: "true",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.CLICKED_PAY_LATER,
|
||||||
|
destinationPageData: routeData.CONFIRMATION,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -589,6 +593,10 @@ const routingTable = function () {
|
||||||
piaError: "true",
|
piaError: "true",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.CLICKED_PAY_LATER,
|
||||||
|
destinationPageData: routeData.CONFIRMATION,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ export async function vehicleBeforeEnter(to, from) {
|
||||||
if (zipData.isValid) {
|
if (zipData.isValid) {
|
||||||
store.dispatch(storeActions.SAVE_SERVICE_ZIP_CODE_INFO, {
|
store.dispatch(storeActions.SAVE_SERVICE_ZIP_CODE_INFO, {
|
||||||
zipCode: newZipFromQuerystring,
|
zipCode: newZipFromQuerystring,
|
||||||
state: zipData.state.state,
|
state: zipData.state,
|
||||||
zipCodeCtu: zipData.zipCodeCtu,
|
zipCodeCtu: zipData.zipCodeCtu,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue