Merge branch 'develop' into feature/CASH-153

This commit is contained in:
Adam Caouette 2025-02-13 19:54:12 -05:00
commit d860701f6d
28 changed files with 478 additions and 133 deletions

View file

@ -18,6 +18,8 @@ const errorMessages = {
LAST_NAME_REQUIRED: "Please enter your last name", LAST_NAME_REQUIRED: "Please enter your last name",
EMAIL_ADDRESS_REQUIRED: "Please enter your email address", EMAIL_ADDRESS_REQUIRED: "Please enter your email address",
EMAIL_ADDRESS_FORMAT: "Please enter a valid email address", EMAIL_ADDRESS_FORMAT: "Please enter a valid email address",
EMAIL_SMS_REQUIRED: "Please enter your mobile phone number or email",
EMAIL_SMS_FORMAT: "Please enter a valid mobile phone number or email",
SERVICE_ZIP_REQUIRED: "Please enter your service ZIP", SERVICE_ZIP_REQUIRED: "Please enter your service ZIP",
SERVICE_ZIP_FORMAT: "Please enter a valid service ZIP", SERVICE_ZIP_FORMAT: "Please enter a valid service ZIP",
VIN_REQUIRED: "Please enter your VIN", VIN_REQUIRED: "Please enter your VIN",

View file

@ -1,6 +1,7 @@
const storeActions = { const storeActions = {
// Content Actions // Content Actions
GET_PAGE_DATA: "getPageData", GET_PAGE_DATA: "getPageData",
SAVE_PAGE_DATA: "savePageData",
// Vehicle Actions // Vehicle Actions
GET_VEHICLE_YEARS: "getVehicleYears", GET_VEHICLE_YEARS: "getVehicleYears",
@ -54,7 +55,7 @@ const storeActions = {
GET_RECAL_PARTS_AND_SAVE_TO_LINE_ITEMS: "getRecalPartsAndSaveToLineItems", GET_RECAL_PARTS_AND_SAVE_TO_LINE_ITEMS: "getRecalPartsAndSaveToLineItems",
// Analytics // Analytics
LOG_EXPERIMENT_EXPOSURE: "logExperimentExposure", LOG_EXPERIMENT_EXPOSURE_AND_UPDATE_STORE: "logExperimentExposureAndUpdateStore",
LOG_PAGE_VIEW: "logPageView", LOG_PAGE_VIEW: "logPageView",
LOG_CUSTOM_EVENT: "logCustomEvent", LOG_CUSTOM_EVENT: "logCustomEvent",
INITIALIZE_SESSION: "initializeSession", INITIALIZE_SESSION: "initializeSession",
@ -77,6 +78,7 @@ const storeActions = {
SAVE_SCHEDULE: "saveSchedule", SAVE_SCHEDULE: "saveSchedule",
SAVE_EMAIL: "saveEmail", SAVE_EMAIL: "saveEmail",
SAVE_PHONE_NUMBER: "savePhoneNumber", SAVE_PHONE_NUMBER: "savePhoneNumber",
SAVE_IS_SMS_OPT_IN: "saveIsSmsOptIn",
SAVE_REGISTRATION_LICENSE_PLATE_LOOKUP: "saveRegistrationLicensePlateLookup", SAVE_REGISTRATION_LICENSE_PLATE_LOOKUP: "saveRegistrationLicensePlateLookup",
SAVE_VIN: "saveVin", SAVE_VIN: "saveVin",
SAVE_REGISTRATION_ADDRESS_LOOKUP: "saveRegistrationAddressLookup", SAVE_REGISTRATION_ADDRESS_LOOKUP: "saveRegistrationAddressLookup",

View file

@ -40,6 +40,7 @@ const storeMutations = {
// CUSTOMER MUTATIONS // CUSTOMER MUTATIONS
UPDATE_CUSTOMER_EMAIL_ADDRESS: "updateCustomerEmailAddress", UPDATE_CUSTOMER_EMAIL_ADDRESS: "updateCustomerEmailAddress",
UPDATE_CUSTOMER_PHONE_NUMBER: "updateCustomerPhoneNumber", UPDATE_CUSTOMER_PHONE_NUMBER: "updateCustomerPhoneNumber",
UPDATE_CUSTOMER_IS_SMS_OPT_IN: "updateCustomerIsSmsOptin",
UPDATE_CUSTOMER_DETAILS: "updateCustomerDetails", UPDATE_CUSTOMER_DETAILS: "updateCustomerDetails",
// ORDER MUTATIONS // ORDER MUTATIONS

View file

@ -158,8 +158,8 @@ export default {
box-shadow: none; box-shadow: none;
} }
&:focus-visible span { &:focus-visible span {
outline: 2px solid #005FCC; outline: 2px solid #005fcc;
border-radius: .25rem; border-radius: 0.25rem;
} }
&.delay { &.delay {
// fixes flicker while transitioning between states // fixes flicker while transitioning between states

View file

@ -18,7 +18,7 @@
<customerQuestions <customerQuestions
ref="customerQuestions" ref="customerQuestions"
v-model="customerQuestions" v-model="customerQuestions"
:validationRules="EmailValidationRules" :validationRules="EmailOrSmsValidationRules"
:isEmailOptional="IsEmailOptional" :isEmailOptional="IsEmailOptional"
:isAddressFieldFocused="IsAddressFieldFocused" /> :isAddressFieldFocused="IsAddressFieldFocused" />
<alert <alert
@ -117,6 +117,7 @@ import { getDamageString, isGlassAvailableForCarId } from "@/helpers/damage-help
import baseMixin from "@/mixins/base-mixin.js"; import baseMixin from "@/mixins/base-mixin.js";
import store from "@/store"; import store from "@/store";
import vinPagesMixin from "@/mixins/vin-pages-mixin"; import vinPagesMixin from "@/mixins/vin-pages-mixin";
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
// DEFINE VALIDATION RULES - Note: Additional rules are defined in Customer Questions and Address Questions components // DEFINE VALIDATION RULES - Note: Additional rules are defined in Customer Questions and Address Questions components
defineRule("service-zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED)); defineRule("service-zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
@ -161,7 +162,7 @@ export default {
}, },
firstName: this.getRegistrationFirstNameFromStore(), firstName: this.getRegistrationFirstNameFromStore(),
lastName: this.getRegistrationLastNameFromStore(), lastName: this.getRegistrationLastNameFromStore(),
emailAddress: this.getEmailFromStore(), emailOrSms: this.getEmailOrSmsFromStore(),
}, },
serviceZipCode: this.getServiceZipFromStore(), serviceZipCode: this.getServiceZipFromStore(),
displayNonServiceableZipAlert: false, displayNonServiceableZipAlert: false,
@ -213,8 +214,8 @@ export default {
getRegistrationLastNameFromStore() { getRegistrationLastNameFromStore() {
return this.$store.getters.vehicle.registration.lastName; return this.$store.getters.vehicle.registration.lastName;
}, },
getEmailFromStore() { getEmailOrSmsFromStore() {
return this.$store.getters.order.customer.emailAddress; return this.$store.getters.emailOrSms;
}, },
getServiceZipFromStore() { getServiceZipFromStore() {
return this.$store.getters.order.serviceLocation.zipCode; return this.$store.getters.order.serviceLocation.zipCode;
@ -356,9 +357,23 @@ export default {
false false
); );
if (vinPagesMixin.methods.isPhoneNumber(this.customerQuestions.emailOrSms)) {
const phone = this.customerQuestions.emailOrSms.replace(/[()]/g, "");
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, phone, false);
} else {
await this.dispatchStoreAction(
storeActions.SAVE_EMAIL,
this.customerQuestions.emailOrSms,
false
);
}
await this.dispatchStoreAction( await this.dispatchStoreAction(
storeActions.SAVE_EMAIL, storeActions.SAVE_PAGE_DATA,
this.customerQuestions.emailAddress, {
page: fmgPageValues.EMAIL_SMS_PAGES,
data: { emailOrSmsValue: this.customerQuestions.emailOrSms },
},
false false
); );
await this.dispatchStoreAction( await this.dispatchStoreAction(

View file

@ -30,11 +30,11 @@
<div class="col"> <div class="col">
<textboxQuestion <textboxQuestion
cmsWidgetName="EmailAddressQuestionWidget" cmsWidgetName="EmailAddressQuestionWidget"
v-model="customerModel.emailAddress" v-model="customerModel.emailOrSms"
ref="emailAddress" ref="emailAddress"
customInputId="emailAddress" customInputId="emailOrSms"
:isRequired="!isEmailOptional" :isRequired="!isEmailOptional"
:validationRules="validationRules" :validationRules="EmailOrSmsvalidationRules"
:addOptionalText="isEmailOptional" :addOptionalText="isEmailOptional"
autocomplete="email" /> autocomplete="email" />
</div> </div>
@ -59,12 +59,12 @@ import textBlock from "@/digital-components/text-block/text-block";
// DEFINE VALIDATION RULES // DEFINE VALIDATION RULES
defineRule("first-name-required", required(errorMessages.FIRST_NAME_REQUIRED)); defineRule("first-name-required", required(errorMessages.FIRST_NAME_REQUIRED));
defineRule("last-name-required", required(errorMessages.LAST_NAME_REQUIRED)); defineRule("last-name-required", required(errorMessages.LAST_NAME_REQUIRED));
defineRule("email-address-required", required(errorMessages.EMAIL_ADDRESS_REQUIRED)); defineRule("email-sms-required", required(errorMessages.EMAIL_SMS_REQUIRED));
defineRule( defineRule(
"email-address-format", "email-sms-format",
regex( regex(
/^([a-zA-Z0-9_\-.+]+)@([a-zA-Z0-9-]+)\.([a-zA-Z]{2,})$/, /^([a-zA-Z0-9_\-.+]+)@([a-zA-Z0-9-]+)\.([a-zA-Z]{2,})$|^\(?(\d{3})\)?[-. ]?(\d{3})[-. ]?(\d{4})$/,
errorMessages.EMAIL_ADDRESS_FORMAT errorMessages.EMAIL_SMS_FORMAT
) )
); );
@ -84,7 +84,7 @@ export default {
}, },
firstName: "", firstName: "",
lastName: "", lastName: "",
emailAddress: "", emailOrSms: "",
}, },
}), }),
}, },

View file

@ -121,9 +121,9 @@ export default {
const partsOrQuestions = orderFromStore.damage?.partQuestionAnswers; const partsOrQuestions = orderFromStore.damage?.partQuestionAnswers;
if (partsOrQuestions) { if (partsOrQuestions) {
partsOrQuestions.forEach(pqa => { partsOrQuestions.forEach((pqa) => {
const payloadPartQuestions = []; const payloadPartQuestions = [];
pqa.answeredQuestions.forEach(answer => { pqa.answeredQuestions.forEach((answer) => {
payloadPartQuestions.push({ payloadPartQuestions.push({
questionSeq: answer?.questionNum, questionSeq: answer?.questionNum,
questionText: answer?.questionText, questionText: answer?.questionText,

View file

@ -145,6 +145,20 @@ export default {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
if (store.getters.externalParameterState?.isExternalParameter) { if (store.getters.externalParameterState?.isExternalParameter) {
if (store.getters.externalParameterCustomer?.phoneNumber) {
await baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_PHONE_NUMBER,
store.getters.externalParameterCustomer.phoneNumber,
false
);
await baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_IS_SMS_OPT_IN,
true,
false
);
}
if (store.getters.externalParameterEstimate.vinSelection) { if (store.getters.externalParameterEstimate.vinSelection) {
vm.selectedVinLookupMethod = vinPagesMixin.methods.getVinlookupMethod( vm.selectedVinLookupMethod = vinPagesMixin.methods.getVinlookupMethod(
store.getters.externalParameterEstimate.vinSelection store.getters.externalParameterEstimate.vinSelection

View file

@ -16,6 +16,13 @@
v-model="parentAccountNumber" v-model="parentAccountNumber"
:originalList="originalList" /> :originalList="originalList" />
</div> </div>
<textLink
id="payOnMyOwnBackButton"
useLoadingModal
linkType="text"
:text="payWithCashButtonCopy"
href="javascript:void(0)"
@click-event="backButtonAction(foreceCashSelectionOnQuote = true)" />
<navbar <navbar
cmsWidgetName="FunnelFooterWidget" cmsWidgetName="FunnelFooterWidget"
@ -36,6 +43,7 @@ import navbar from "@/fmg-components/nav-bar/nav-bar";
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header"; import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue"; import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
import insuranceCompanyQuestion from "@/layouts/insurance-company/insurance-company-question"; import insuranceCompanyQuestion from "@/layouts/insurance-company/insurance-company-question";
import textLink from "../../ux-components/text-link/text-link.vue";
// Supporting files // Supporting files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
@ -92,6 +100,9 @@ export default {
isParentAccountNumber() { isParentAccountNumber() {
return !!this.parentAccountNumber; return !!this.parentAccountNumber;
}, },
payWithCashButtonCopy() {
return this.getCmsContent("PayWithCashWidget", "Text");
}
}, },
methods: { methods: {
isCashParentAccountNumber() { isCashParentAccountNumber() {
@ -109,8 +120,13 @@ export default {
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
return store.getters.order.payment.isInsurance === true; return store.getters.order.payment.isInsurance === true;
}, },
backButtonAction() { backButtonAction(forceCashSelectionOnQuote) {
// Go back to Quote page // Go back to Quote page
if (forceCashSelectionOnQuote) {
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route, {"isCashSelected": true});
return;
}
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route); this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
}, },
async forwardButtonAction() { async forwardButtonAction() {
@ -157,6 +173,7 @@ export default {
Form, Form,
loadingModal, loadingModal,
insuranceCompanyQuestion, insuranceCompanyQuestion,
textLink,
}, },
}; };
</script> </script>

View file

@ -68,7 +68,7 @@ describe("license-plate-lookup.vue", () => {
const { wrapper } = setupMocks({ emailAddress: mockEmail }); const { wrapper } = setupMocks({ emailAddress: mockEmail });
// ACT // ACT
const customerEmail = wrapper.vm.getEmailFromStore(); const customerEmail = wrapper.vm.getEmailOrSmsFromStore();
// Assert // Assert
expect(customerEmail).toEqual(mockEmail); expect(customerEmail).toEqual(mockEmail);
@ -665,6 +665,7 @@ function setupMocks({
}, },
store: { store: {
getters: { getters: {
emailOrSms: emailAddress,
vehicle: { vehicle: {
registration: { registration: {
licensePlate: licensePlate, licensePlate: licensePlate,

View file

@ -28,10 +28,10 @@
<textboxQuestion <textboxQuestion
cmsWidgetName="EmailAddressQuestionWidget" cmsWidgetName="EmailAddressQuestionWidget"
v-model="email" v-model="emailOrSms"
customInputId="email" customInputId="emailOrSms"
:isRequired="!IsEmailOptional" :isRequired="!IsEmailOptional"
:validationRules="EmailValidationRules" :validationRules="EmailOrSmsValidationRules"
:addOptionalText="IsEmailOptional" /> :addOptionalText="IsEmailOptional" />
<textBlock cmsWidgetName="QuoteEmailTextBlockWidget" typeStyle="caption" /> <textBlock cmsWidgetName="QuoteEmailTextBlockWidget" typeStyle="caption" />
@ -110,17 +110,18 @@ import { Form, defineRule } from "vee-validate";
import baseMixin from "@/mixins/base-mixin.js"; import baseMixin from "@/mixins/base-mixin.js";
import store from "@/store"; import store from "@/store";
import vinPagesMixin from "@/mixins/vin-pages-mixin"; import vinPagesMixin from "@/mixins/vin-pages-mixin";
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
// DEFINE VALIDATION RULES // DEFINE VALIDATION RULES
defineRule("license-plate-required", required(errorMessages.LICENSE_PLATE_REQUIRED)); defineRule("license-plate-required", required(errorMessages.LICENSE_PLATE_REQUIRED));
defineRule("registration-zip-required", required(errorMessages.REGISTRATION_ZIP_REQUIRED)); defineRule("registration-zip-required", required(errorMessages.REGISTRATION_ZIP_REQUIRED));
defineRule("zip-format", regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.SERVICE_ZIP_FORMAT)); defineRule("zip-format", regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.SERVICE_ZIP_FORMAT));
defineRule("email-address-required", required(errorMessages.EMAIL_ADDRESS_REQUIRED)); defineRule("email-sms-required", required(errorMessages.EMAIL_SMS_REQUIRED));
defineRule( defineRule(
"email-address-format", "email-sms-format",
regex( regex(
/^([a-zA-Z0-9_\-.+]+)@([a-zA-Z0-9-]+)\.([a-zA-Z]{2,})$/, /^([a-zA-Z0-9_\-.+]+)@([a-zA-Z0-9-]+)\.([a-zA-Z]{2,})$|^\(?(\d{3})\)?[-. ]?(\d{3})[-. ]?(\d{4})$/,
errorMessages.EMAIL_ADDRESS_FORMAT errorMessages.EMAIL_SMS_FORMAT
) )
); );
@ -155,7 +156,7 @@ export default {
return { return {
licensePlate: this.getLicensePlateFromStore(), licensePlate: this.getLicensePlateFromStore(),
registrationZipCode: this.getRegistrationZipFromStore() ?? this.$route.query.zipcode, registrationZipCode: this.getRegistrationZipFromStore() ?? this.$route.query.zipcode,
email: this.getEmailFromStore(), emailOrSms: this.getEmailOrSmsFromStore(),
serviceZipCode: this.getServiceZipFromStore(), serviceZipCode: this.getServiceZipFromStore(),
displayNonServiceableZipAlert: false, displayNonServiceableZipAlert: false,
displayVinNotFoundAlert: false, displayVinNotFoundAlert: false,
@ -193,8 +194,8 @@ export default {
getRegistrationZipFromStore() { getRegistrationZipFromStore() {
return this.$store.getters.vehicle.registration.zipCode; return this.$store.getters.vehicle.registration.zipCode;
}, },
getEmailFromStore() { getEmailOrSmsFromStore() {
return this.$store.getters.order.customer.emailAddress; return this.$store.getters.emailOrSms;
}, },
getServiceZipFromStore() { getServiceZipFromStore() {
return this.$store.getters.order.serviceLocation.zipCode; return this.$store.getters.order.serviceLocation.zipCode;
@ -310,7 +311,21 @@ export default {
false false
); );
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.email, false); if (vinPagesMixin.methods.isPhoneNumber(this.emailOrSms)) {
const phone = this.emailOrSms.replace(/[()]/g, "");
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, phone, false);
} else {
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.emailOrSms, false);
}
await this.dispatchStoreAction(
storeActions.SAVE_PAGE_DATA,
{
page: fmgPageValues.EMAIL_SMS_PAGES,
data: { emailOrSmsValue: this.emailOrSms },
},
false
);
await this.dispatchStoreAction( await this.dispatchStoreAction(
storeActions.SAVE_SERVICE_ZIP_CODE_INFO, storeActions.SAVE_SERVICE_ZIP_CODE_INFO,

View file

@ -78,6 +78,20 @@ export default {
); );
} }
if (store.getters.externalParameterCustomer?.phoneNumber) {
await baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_PHONE_NUMBER,
store.getters.externalParameterCustomer.phoneNumber,
false
);
await baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_IS_SMS_OPT_IN,
true,
false
);
}
baseMixin.methods.ResetExternalParamsAndHideModal(); baseMixin.methods.ResetExternalParamsAndHideModal();
} }
}); });

View file

@ -79,6 +79,21 @@ export default {
false false
); );
} }
if (store.getters.externalParameterCustomer?.phoneNumber) {
await baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_PHONE_NUMBER,
store.getters.externalParameterCustomer.phoneNumber,
false
);
await baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_IS_SMS_OPT_IN,
true,
false
);
}
baseMixin.methods.ResetExternalParamsAndHideModal(); baseMixin.methods.ResetExternalParamsAndHideModal();
} }
}); });

View file

@ -577,6 +577,56 @@ describe("quote.vue", () => {
//Assert //Assert
expect(wrapper.vm.isInsuranceSelected).toBe(false); expect(wrapper.vm.isInsuranceSelected).toBe(false);
}); });
test("should default to cash if isCashSelected is true, despite all other checks pointing to insurance selection", async () => {
// Also needs no query parameter or previous selection in store to be present
//Arrange
store.getters = {
lineItems: {
glassParts: ["item", "item2"],
},
applicationUser: {
experiments: [],
},
order: {
lineItems: {
glassParts: ["item", "item2"],
},
payment: {
isInsurance: true,
},
customer: {
emailAddress: "test@test.com",
},
serviceLocation: {
state: null,
},
},
vehicle: {
cardId: "123",
},
experimentSettings: {
settingName: "SERVICE_PACKAGE_DISCOUNT",
},
};
mockTierOnePrice = 505;
const { wrapper } = setupMocks({});
//Act
await quote.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "quote",
isInsuranceSelected: true,
isCashSelected: true,
} },
undefined,
(c) => c(wrapper.vm)
);
//Assert
expect(wrapper.vm.isInsuranceSelected).toBe(false);
});
test("should default to insurance if total economy package price is over $500", async () => { test("should default to insurance if total economy package price is over $500", async () => {
// Also needs no query parameter or previous selection in store to be present // Also needs no query parameter or previous selection in store to be present
//Arrange //Arrange

View file

@ -328,6 +328,10 @@ export default {
} }
const getIsInsuranceSelectedValue = (availableLineItems, insuranceThreshold) => { const getIsInsuranceSelectedValue = (availableLineItems, insuranceThreshold) => {
// query param checked here is set on insurance-selection "Pay on my own" link click
if (to.query?.isCashSelected || to.query?.iscashselected) {
return false;
}
const serviceLocationState = store.getters.order.serviceLocation.state; const serviceLocationState = store.getters.order.serviceLocation.state;
// usually true when returning from heritage but can be false when returning from heritage on a save quote // usually true when returning from heritage but can be false when returning from heritage on a save quote

View file

@ -203,7 +203,7 @@ describe("mobile-location-modal-questions.vue", () => {
expect(wrapper.vm.$refs.MobileLocationModalWidget.openModal).toHaveBeenCalled(); expect(wrapper.vm.$refs.MobileLocationModalWidget.openModal).toHaveBeenCalled();
}); });
it("Should emit update:modelValue on setMobileLocation for a valid address and vehicle protection answer", async () => { it("Should emit updated-mobile-location-questions on setMobileLocation for a valid address and vehicle protection answer", async () => {
// Arrange // Arrange
const mobileLocationQuestions = { const mobileLocationQuestions = {
addressQuestions: { addressQuestions: {
@ -240,17 +240,26 @@ describe("mobile-location-modal-questions.vue", () => {
}); });
wrapper.vm.$refs.MobileLocationModalWidget.closeModal = jest.fn(); wrapper.vm.$refs.MobileLocationModalWidget.closeModal = jest.fn();
// Mock the emitEvent function
wrapper.vm.emitEvent = jest.fn(async (eventName, payload) => {
return new Promise((resolve) => {
wrapper.vm.$emit(eventName, newMobileLocationQuestions, resolve);
resolve(newMobileLocationQuestions);
});
});
// Act // Act
wrapper.vm.internalModel = newMobileLocationQuestions; wrapper.vm.internalModel = newMobileLocationQuestions;
await wrapper.vm.setMobileLocation(); await wrapper.vm.setMobileLocation();
let expectedEmit = [[newMobileLocationQuestions]];
// Assert // Assert
expect(wrapper.emitted("update:modelValue")).toEqual(expectedEmit); expect(wrapper.emitted()["updated-mobile-location-questions"]).toBeTruthy();
expect(wrapper.emitted()["updated-mobile-location-questions"][0][0]).toBe(
newMobileLocationQuestions
);
}); });
it("Should not emit update:modelValue on setMobileLocation for an invalid address zip code", async () => { it("Should not emit updated-mobile-location-questions on setMobileLocation for an invalid address zip code", async () => {
// Arrange // Arrange
const mobileLocationQuestions = { const mobileLocationQuestions = {
addressQuestions: { addressQuestions: {
@ -293,7 +302,7 @@ describe("mobile-location-modal-questions.vue", () => {
await wrapper.vm.setMobileLocation(); await wrapper.vm.setMobileLocation();
// Assert // Assert
expect(wrapper.emitted("update:modelValue")).not.toBeTruthy(); expect(wrapper.emitted("updated-mobile-location-questions")).not.toBeTruthy();
}); });
it("Should display invalid zip alert for invalid address zip inputs", async () => { it("Should display invalid zip alert for invalid address zip inputs", async () => {

View file

@ -98,6 +98,7 @@ export default {
"updated-contains-military-base", "updated-contains-military-base",
"updated-bill-to-account-number", "updated-bill-to-account-number",
"mobileLocationSelected", "mobileLocationSelected",
"updated-mobile-location-questions",
], ],
data() { data() {
return { return {
@ -251,10 +252,15 @@ export default {
if (!zipCodeData.isValid) { if (!zipCodeData.isValid) {
this.displayInvalidZipAlert = true; this.displayInvalidZipAlert = true;
this.resetModalButtonStyle(); this.resetModalButtonStyle();
return;
} else if (zipCodeData.state != this.internalModel.addressQuestions.state) { } else if (zipCodeData.state != this.internalModel.addressQuestions.state) {
this.displayMismatchStateAndZipAlert = true; this.displayMismatchStateAndZipAlert = true;
this.resetModalButtonStyle(); this.resetModalButtonStyle();
} else { return;
} else if (
this.internalModel.addressQuestions.zipCode !==
this.modelValue.addressQuestions.zipCode
) {
// retrieve mobile fee part // retrieve mobile fee part
const serviceZipCode = this.internalModel.addressQuestions.zipCode; const serviceZipCode = this.internalModel.addressQuestions.zipCode;
const mobileFeePart = await getPricedMobileFeePart( const mobileFeePart = await getPricedMobileFeePart(
@ -279,13 +285,17 @@ export default {
this.$emit("updated-contains-military-base", zipCodeData.containsMilitaryBase); this.$emit("updated-contains-military-base", zipCodeData.containsMilitaryBase);
this.$emit("updated-mobile-ctu", zipCodeData.zipCodeCtu); this.$emit("updated-mobile-ctu", zipCodeData.zipCodeCtu);
this.$emit("updated-bill-to-account-number", billToAccountNumber); this.$emit("updated-bill-to-account-number", billToAccountNumber);
// update the page level model
this.$emit("update:modelValue", this.internalModel);
//Page advance to Schedule page
this.$emit("mobileLocationSelected");
} }
// update the page level model
await this.emitEvent("updated-mobile-location-questions", this.internalModel);
this.$emit("mobileLocationSelected");
},
//creating emitEvent function to raise events with resolve callback to handle events sequencing
async emitEvent(eventName, payload) {
return new Promise((resolve) => {
this.$emit(eventName, payload, resolve);
});
}, },
}, },
watch: { watch: {

View file

@ -460,11 +460,23 @@ describe("service-location.vue", () => {
}; };
// Act // Act
mobileLocationQuestionsComponent.vm.$emit(
"update:modelValue", // Mock the emitEvent function
mobileLocationQuestionsComponent.vm.emitEvent = jest.fn(async (eventName, payload) => {
return new Promise((resolve) => {
mobileLocationQuestionsComponent.vm.$emit(eventName, payload, resolve);
});
});
// Trigger the event
await mobileLocationQuestionsComponent.vm.emitEvent(
"updated-mobile-location-questions",
newMobileLocationQuestions newMobileLocationQuestions
); );
await wrapper.vm.$nextTick(); // Wait for DOM updates
await new Promise((resolve) => setTimeout(resolve, 0)); // Wait for promise to resolve
// Assert // Assert
expect(wrapper.vm.mobileLocationQuestions).toStrictEqual(newMobileLocationQuestions); expect(wrapper.vm.mobileLocationQuestions).toStrictEqual(newMobileLocationQuestions);
}); });
@ -529,11 +541,23 @@ describe("service-location.vue", () => {
}; };
// Act // Act
mobileLocationQuestionsComponent.vm.$emit(
"update:modelValue", // Mock the emitEvent function
mobileLocationQuestionsComponent.vm.emitEvent = jest.fn(async (eventName, payload) => {
return new Promise((resolve) => {
mobileLocationQuestionsComponent.vm.$emit(eventName, payload, resolve);
});
});
// Trigger the event
await mobileLocationQuestionsComponent.vm.emitEvent(
"updated-mobile-location-questions",
newMobileLocationQuestions newMobileLocationQuestions
); );
await wrapper.vm.$nextTick(); // Wait for DOM updates
await new Promise((resolve) => setTimeout(resolve, 0)); // Wait for promise to resolve
// Assert // Assert
expect(wrapper.vm.serviceZipCodeQuestion).toStrictEqual(newServiceZipCodeInfo); expect(wrapper.vm.serviceZipCodeQuestion).toStrictEqual(newServiceZipCodeInfo);
}); });

View file

@ -93,6 +93,7 @@
@updated-serviceability="setServiceabilityDetails" @updated-serviceability="setServiceabilityDetails"
@updated-contains-military-base="setContainsMilitaryBase" @updated-contains-military-base="setContainsMilitaryBase"
@updated-mobile-ctu="setCtuForMobile" @updated-mobile-ctu="setCtuForMobile"
@updated-mobile-location-questions="setMobileLocationQuestions"
validationRules="mobile-location-required" validationRules="mobile-location-required"
ref="mobileLocationQuestions" ref="mobileLocationQuestions"
linkWidgetName="MobileLocationLinkWidget" linkWidgetName="MobileLocationLinkWidget"
@ -294,17 +295,18 @@ export default {
isVehicleProtected: this.isVehicleProtected, isVehicleProtected: this.isVehicleProtected,
}; };
}, },
set: function (newValue) { async set(newValue) {
if (newValue.addressQuestions.zipCode !== this.zipCode) { if (newValue.addressQuestions.zipCode !== this.zipCode) {
getShopProviderData(newValue.addressQuestions.zipCode).then((result) => { await getShopProviderData(newValue.addressQuestions.zipCode).then((result) => {
this.shopProviderData = result.data; this.shopProviderData = result.data;
this.selectedProvider = new Provider( this.selectedProvider = new Provider(
this.shopProviderData.mobileProviderNumber this.shopProviderData.mobileProviderNumber
); );
}); });
} }
this.setMobileLocation(newValue); this.setMobileLocation(newValue);
//handle promise
newValue.resolve?.();
}, },
}, },
isServiceableMobile() { isServiceableMobile() {
@ -485,6 +487,10 @@ export default {
setMobileFeePart(mobileFeePart) { setMobileFeePart(mobileFeePart) {
this.mobileFeePart = mobileFeePart; this.mobileFeePart = mobileFeePart;
}, },
//creating async function as the computed property can not directly handle asynchronous operations or promises.
async setMobileLocationQuestions(newValue, resolve) {
this.mobileLocationQuestions = { ...newValue, resolve };
},
getServiceAddressFromStore() { getServiceAddressFromStore() {
return store.getters.order.serviceLocation.address; return store.getters.order.serviceLocation.address;
}, },

View file

@ -181,6 +181,7 @@ function applyMockStoreDataToGetters() {
payment: mockStoreData.payment, payment: mockStoreData.payment,
policy: mockStoreData.policy, policy: mockStoreData.policy,
externalParameterServiceZip: mockStoreData.externalParameterServiceZip, externalParameterServiceZip: mockStoreData.externalParameterServiceZip,
emailOrSms: mockStoreData.customer.emailAddress,
}; };
store.state.order = mockStoreData; store.state.order = mockStoreData;
store.state.applicationUser.experiments = mockExperimentSettings; store.state.applicationUser.experiments = mockExperimentSettings;
@ -241,7 +242,7 @@ describe("service-zip.vue", () => {
// Assert // Assert
expect(wrapper.vm.serviceZipCode).toEqual("11111"); expect(wrapper.vm.serviceZipCode).toEqual("11111");
expect(wrapper.vm.emailAddress).toEqual("builddigitaltest@safelite.com"); expect(wrapper.vm.emailOrSms).toEqual("builddigitaltest@safelite.com");
}); });
test("Zip in querystring -> pushed to data", () => { test("Zip in querystring -> pushed to data", () => {

View file

@ -23,11 +23,11 @@
<textboxQuestion <textboxQuestion
class="mb-0" class="mb-0"
cmsWidgetName="EmailAddressQuestionWidget" cmsWidgetName="EmailAddressQuestionWidget"
v-model="emailAddress" v-model="emailOrSms"
inputId="emailAddress" inputId="emailOrSms"
:isRequired="!IsEmailOptional" :isRequired="!IsEmailOptional"
disableAutoFill disableAutoFill
:validationRules="EmailValidationRules" :validationRules="EmailOrSmsValidationRules"
:addOptionalText="IsEmailOptional" /> :addOptionalText="IsEmailOptional" />
<textBlock cmsWidgetName="QuoteEmailTextBlockWidget" typeStyle="caption" /> <textBlock cmsWidgetName="QuoteEmailTextBlockWidget" typeStyle="caption" />
@ -88,16 +88,17 @@ import vinPagesMixin from "@/mixins/vin-pages-mixin";
import { saveSession } from "@/helpers/heritage-integration/order-helper.js"; import { saveSession } from "@/helpers/heritage-integration/order-helper.js";
import baseMixin from "@/mixins/base-mixin.js"; import baseMixin from "@/mixins/base-mixin.js";
import { queryStrings } from "@/constants/query-strings"; import { queryStrings } from "@/constants/query-strings";
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
// Define Validation Rules // Define Validation Rules
defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED)); defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
defineRule("zip-format", regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.SERVICE_ZIP_FORMAT)); defineRule("zip-format", regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.SERVICE_ZIP_FORMAT));
defineRule("email-address-required", required(errorMessages.EMAIL_ADDRESS_REQUIRED)); defineRule("email-sms-required", required(errorMessages.EMAIL_SMS_REQUIRED));
defineRule( defineRule(
"email-address-format", "email-sms-format",
regex( regex(
/^([a-zA-Z0-9_\-.+]+)@([a-zA-Z0-9-]+)\.([a-zA-Z]{2,})$/, /^([a-zA-Z0-9_\-.+]+)@([a-zA-Z0-9-]+)\.([a-zA-Z]{2,})$|^\(?(\d{3})\)?[-. ]?(\d{3})[-. ]?(\d{4})$/,
errorMessages.EMAIL_ADDRESS_FORMAT errorMessages.EMAIL_SMS_FORMAT
) )
); );
defineRule("option-required", required(errorMessages.OPTION_REQUIRED)); defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
@ -108,7 +109,7 @@ export default {
data() { data() {
return { return {
serviceZipCode: this.getZipFromStore() ?? this.$route.query.zipcode, serviceZipCode: this.getZipFromStore() ?? this.$route.query.zipcode,
emailAddress: this.getEmailFromStore(), emailOrSms: this.getEmailOrSmsFromStore(),
displayInvalidZipAlert: false, displayInvalidZipAlert: false,
displayNonServiceableZipAlert: false, displayNonServiceableZipAlert: false,
}; };
@ -132,22 +133,36 @@ export default {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
if (store.getters.externalParameterState?.isExternalParameter) { if (store.getters.externalParameterState?.isExternalParameter) {
if (store.getters.externalParameterServiceZip.zipCode) {
await baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_SERVICE_ZIP_CODE_INFO,
{
zipCode: store.getters.externalParameterServiceZip.zipCode,
state: null,
zipCodeCtu: null,
},
false
);
}
if (store.getters.externalParameterCustomer?.phoneNumber) {
await baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_PHONE_NUMBER,
store.getters.externalParameterCustomer.phoneNumber,
false
);
await baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_IS_SMS_OPT_IN,
true,
false
);
}
const isValid = await baseMixin.methods.isFormValid(vm.$refs.theForm); const isValid = await baseMixin.methods.isFormValid(vm.$refs.theForm);
if (isValid) { if (isValid) {
vm.forwardButtonAction(); vm.forwardButtonAction();
} else { } else {
if (store.getters.externalParameterServiceZip.zipCode) {
await baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_SERVICE_ZIP_CODE_INFO,
{
zipCode: store.getters.externalParameterServiceZip.zipCode,
state: null,
zipCodeCtu: null,
},
false
);
}
baseMixin.methods.ResetExternalParamsAndHideModal(); baseMixin.methods.ResetExternalParamsAndHideModal();
} }
} }
@ -160,11 +175,11 @@ export default {
store.getters.order.serviceLocation.zipCode store.getters.order.serviceLocation.zipCode
); );
}, },
getEmailFromStore() { getEmailOrSmsFromStore() {
return ( return store.getters.emailOrSms;
store.getters.externalParameterServiceZip.emailAddress ?? },
store.getters.order.customer.emailAddress getPhoneFromStore() {
); return store.getters.order.customer.phoneNumber;
}, },
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
return store.getters.damage.isRepair || store.getters.damage.glassToReplace?.length > 0; return store.getters.damage.isRepair || store.getters.damage.glassToReplace?.length > 0;
@ -186,7 +201,30 @@ export default {
}, },
async forwardButtonAction() { async forwardButtonAction() {
const zipCodeData = await this.getZipCodeData(this.serviceZipCode); const zipCodeData = await this.getZipCodeData(this.serviceZipCode);
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.emailAddress, false);
// if no value due to field being optional, blank both phone and email address
if (!this.emailOrSms) {
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, "", false);
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, "", false);
}
else {
if (vinPagesMixin.methods.isPhoneNumber(this.emailOrSms)) {
const phone = this.emailOrSms.replace(/[()]/g, "");
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, phone, false);
} else {
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.emailOrSms, false);
}
}
await this.dispatchStoreAction(
storeActions.SAVE_PAGE_DATA,
{
page: fmgPageValues.EMAIL_SMS_PAGES,
data: { emailOrSmsValue: this.emailOrSms },
},
false
);
await this.dispatchStoreAction( await this.dispatchStoreAction(
storeActions.SAVE_SERVICE_ZIP_CODE_INFO, storeActions.SAVE_SERVICE_ZIP_CODE_INFO,
{ {

View file

@ -152,6 +152,20 @@ export default {
if (isValid) { if (isValid) {
vm.forwardButtonAction(); vm.forwardButtonAction();
} else { } else {
if (store.getters.externalParameterCustomer?.phoneNumber) {
await baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_PHONE_NUMBER,
store.getters.externalParameterCustomer.phoneNumber,
false
);
await baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_IS_SMS_OPT_IN,
true,
false
);
}
if (store.getters.externalParameterServiceZip.zipCode) { if (store.getters.externalParameterServiceZip.zipCode) {
await baseMixin.methods.dispatchStoreAction( await baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_SERVICE_ZIP_CODE_INFO, storeActions.SAVE_SERVICE_ZIP_CODE_INFO,

View file

@ -186,7 +186,7 @@ export default {
if (experimentForLogging !== undefined) { if (experimentForLogging !== undefined) {
// Log experiment exposure // Log experiment exposure
baseMixin.methods.dispatchStoreActionWithLogging( baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.LOG_EXPERIMENT_EXPOSURE, storeActions.LOG_EXPERIMENT_EXPOSURE_AND_UPDATE_STORE,
{ {
userId: getUserIdValue(), userId: getUserIdValue(),
deviceId: getDeviceIdValue(), deviceId: getDeviceIdValue(),

View file

@ -46,10 +46,10 @@
<textboxQuestion <textboxQuestion
cmsWidgetName="EmailAddressQuestionWidget" cmsWidgetName="EmailAddressQuestionWidget"
v-model="emailAddress" v-model="emailOrSms"
customInputId="emailAddress" customInputId="emailOrSms"
:isRequired="!IsEmailOptional" :isRequired="!IsEmailOptional"
:validationRules="EmailValidationRules" :validationRules="EmailOrSmsValidationRules"
:addOptionalText="IsEmailOptional" /> :addOptionalText="IsEmailOptional" />
<textBlock cmsWidgetName="QuoteEmailTextBlockWidget" typeStyle="caption" /> <textBlock cmsWidgetName="QuoteEmailTextBlockWidget" typeStyle="caption" />
@ -151,16 +151,17 @@ import { routerParams } from "@/router/router-constants/router-params";
import baseMixin from "@/mixins/base-mixin.js"; import baseMixin from "@/mixins/base-mixin.js";
import store from "@/store"; import store from "@/store";
import vinPagesMixin from "@/mixins/vin-pages-mixin"; import vinPagesMixin from "@/mixins/vin-pages-mixin";
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
// DEFINE VALIDATION RULES // DEFINE VALIDATION RULES
defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED)); defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
defineRule("zip-format", regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.SERVICE_ZIP_FORMAT)); defineRule("zip-format", regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.SERVICE_ZIP_FORMAT));
defineRule("email-address-required", required(errorMessages.EMAIL_ADDRESS_REQUIRED)); defineRule("email-sms-required", required(errorMessages.EMAIL_SMS_REQUIRED));
defineRule( defineRule(
"email-address-format", "email-sms-format",
regex( regex(
/^([a-zA-Z0-9_\-.+]+)@([a-zA-Z0-9-]+)\.([a-zA-Z]{2,})$/, /^([a-zA-Z0-9_\-.+]+)@([a-zA-Z0-9-]+)\.([a-zA-Z]{2,})$|^\(?(\d{3})\)?[-. ]?(\d{3})[-. ]?(\d{4})$/,
errorMessages.EMAIL_ADDRESS_FORMAT errorMessages.EMAIL_SMS_FORMAT
) )
); );
defineRule("vin-required", required(errorMessages.VIN_REQUIRED)); defineRule("vin-required", required(errorMessages.VIN_REQUIRED));
@ -195,7 +196,7 @@ export default {
return { return {
vin: this.getVinFromStore(), vin: this.getVinFromStore(),
serviceZipCode: this.getZipFromStore() ?? this.$route.query.zipcode, serviceZipCode: this.getZipFromStore() ?? this.$route.query.zipcode,
emailAddress: this.getEmailFromStore(), emailOrSms: this.getEmailOrSmsFromStore(),
isCarIdDifferent: false, isCarIdDifferent: false,
customAlertData: {}, customAlertData: {},
previouslyEnteredCarId: "", previouslyEnteredCarId: "",
@ -212,8 +213,8 @@ export default {
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
return store.getters.vehicle.carId !== null; return store.getters.vehicle.carId !== null;
}, },
getEmailFromStore() { getEmailOrSmsFromStore() {
return this.$store.getters.order.customer.emailAddress; return this.$store.getters.emailOrSms;
}, },
getVinFromStore() { getVinFromStore() {
return this.$store.getters.vehicle.vin; return this.$store.getters.vehicle.vin;
@ -329,7 +330,21 @@ export default {
false false
); );
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.emailAddress, false); if (vinPagesMixin.methods.isPhoneNumber(this.emailOrSms)) {
const phone = this.emailOrSms.replace(/[()]/g, "");
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, phone, false);
} else {
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.emailOrSms, false);
}
await this.dispatchStoreAction(
storeActions.SAVE_PAGE_DATA,
{
page: fmgPageValues.EMAIL_SMS_PAGES,
data: { emailOrSmsValue: this.emailOrSms },
},
false
);
await this.dispatchStoreAction( await this.dispatchStoreAction(
storeActions.SAVE_SERVICE_ZIP_CODE_INFO, storeActions.SAVE_SERVICE_ZIP_CODE_INFO,
{ {
@ -365,7 +380,21 @@ export default {
); );
} }
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.emailAddress, false); if (vinPagesMixin.methods.isPhoneNumber(this.emailOrSms)) {
const phone = this.emailOrSms.replace(/[()]/g, "");
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, phone, false);
} else {
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.emailOrSms, false);
}
await this.dispatchStoreAction(
storeActions.SAVE_PAGE_DATA,
{
page: fmgPageValues.EMAIL_SMS_PAGES,
data: { emailOrSmsValue: this.emailOrSms },
},
false
);
return await this.navigateForward(); return await this.navigateForward();
} }

View file

@ -16,6 +16,11 @@ export default {
? "email-address-format" ? "email-address-format"
: "email-address-required|email-address-format"; : "email-address-required|email-address-format";
}, },
EmailOrSmsValidationRules() {
return this.IsEmailOptional
? "email-sms-format"
: "email-sms-required|email-sms-format";
},
}, },
methods: { methods: {
async navigateForwardWithSingleCarMatch() { async navigateForwardWithSingleCarMatch() {
@ -56,5 +61,9 @@ export default {
false false
); );
}, },
isPhoneNumber(input) {
const phoneRegex = /^\(?(\d{3})\)?[-. ]?(\d{3})[-. ]?(\d{4})$/;
return phoneRegex.test(input);
},
}, },
}; };

View file

@ -898,7 +898,6 @@ function updateExternalParameterState() {
storeMutations.UPDATE_EXTERNAL_PARAMETER_PHONE_NUMBER, storeMutations.UPDATE_EXTERNAL_PARAMETER_PHONE_NUMBER,
externalParameterPhoneNumber externalParameterPhoneNumber
); );
store.dispatch(storeActions.SAVE_PHONE_NUMBER, externalParameterPhoneNumber);
} }
} }

View file

@ -23,6 +23,7 @@ const fmgPageValues = {
PAYMENT_PIA_RETURN: "payment-pia-return", PAYMENT_PIA_RETURN: "payment-pia-return",
CONFIRMATION: "confirmation", CONFIRMATION: "confirmation",
RETURN_USER: "return-user", RETURN_USER: "return-user",
EMAIL_SMS_PAGES: "email-or-sms-related-pages",
}; };
const funnelStartPageName = fmgPageValues.VEHICLE; const funnelStartPageName = fmgPageValues.VEHICLE;

View file

@ -341,6 +341,9 @@ export const mutations = {
updateCustomerPhoneNumber(state, phoneNumber) { updateCustomerPhoneNumber(state, phoneNumber) {
state.order.customer.phoneNumber = phoneNumber; state.order.customer.phoneNumber = phoneNumber;
}, },
updateCustomerIsSmsOptin(state, isSmsOptIn) {
state.order.customer.isSmsOptIn = isSmsOptIn;
},
updateVehicle(state, vehicleInfo) { updateVehicle(state, vehicleInfo) {
state.order.vehicle.year = vehicleInfo.year; state.order.vehicle.year = vehicleInfo.year;
state.order.vehicle.make = vehicleInfo.make; state.order.vehicle.make = vehicleInfo.make;
@ -476,7 +479,7 @@ export const mutations = {
saveExternalParameterState(externalParameterState); saveExternalParameterState(externalParameterState);
}, },
updateExternalParameterEmailAddress(state, emailAddress) { updateExternalParameterEmailAddress(state, emailAddress) {
externalParameterState.serviceZip.emailAddress = emailAddress; externalParameterState.customer.emailAddress = emailAddress;
saveExternalParameterState(externalParameterState); saveExternalParameterState(externalParameterState);
}, },
updateExternalParameterIsInsurance(state, isInsurance) { updateExternalParameterIsInsurance(state, isInsurance) {
@ -517,7 +520,6 @@ export const mutations = {
}, },
resetExternalParameterServiceZipState(state) { resetExternalParameterServiceZipState(state) {
externalParameterState.serviceZip.zipCode = null; externalParameterState.serviceZip.zipCode = null;
externalParameterState.serviceZip.emailAddress = null;
saveExternalParameterState(externalParameterState); saveExternalParameterState(externalParameterState);
}, },
resetExternalParameterQuoteState(state) { resetExternalParameterQuoteState(state) {
@ -529,6 +531,7 @@ export const mutations = {
saveExternalParameterState(externalParameterState); saveExternalParameterState(externalParameterState);
}, },
resetExternalParameterCustomerState(state) { resetExternalParameterCustomerState(state) {
externalParameterState.customer.emailAddress = null;
externalParameterState.customer.phoneNumber = null; externalParameterState.customer.phoneNumber = null;
saveExternalParameterState(externalParameterState); saveExternalParameterState(externalParameterState);
}, },
@ -670,34 +673,38 @@ export const mutations = {
sessionInformation.order.payment.billToAccountNumber; sessionInformation.order.payment.billToAccountNumber;
state.order.payment.inactivePromos = sessionInformation.order.payment.inactivePromos; state.order.payment.inactivePromos = sessionInformation.order.payment.inactivePromos;
state.order.serviceLocation.address = //Do not assign default service type once user make service type selection
sessionInformation.order.serviceLocation.streetAddress; if (state.order.serviceLocation.appointmentType == null) {
state.order.serviceLocation.address2 = state.order.serviceLocation.address =
sessionInformation.order.serviceLocation.streetAddress2; sessionInformation.order.serviceLocation.streetAddress;
state.order.serviceLocation.city = sessionInformation.order.serviceLocation.city; state.order.serviceLocation.address2 =
state.order.serviceLocation.state = sessionInformation.order.serviceLocation.state; sessionInformation.order.serviceLocation.streetAddress2;
state.order.serviceLocation.zipCode = sessionInformation.order.serviceLocation.zipCode; state.order.serviceLocation.city = sessionInformation.order.serviceLocation.city;
state.order.serviceLocation.zipCodeCtu = state.order.serviceLocation.state = sessionInformation.order.serviceLocation.state;
sessionInformation.order.serviceLocation.zipCodeCtu; state.order.serviceLocation.zipCode = sessionInformation.order.serviceLocation.zipCode;
state.order.serviceLocation.zipCodeCtu =
sessionInformation.order.serviceLocation.zipCodeCtu;
state.order.serviceLocation.appointmentType = state.order.serviceLocation.appointmentType =
sessionInformation.order.serviceLocation.appointmentType; sessionInformation.order.serviceLocation.appointmentType;
state.order.serviceLocation.isVehicleProtected = state.order.serviceLocation.isVehicleProtected =
sessionInformation.order.serviceLocation.isVehicleProtected; sessionInformation.order.serviceLocation.isVehicleProtected;
state.order.serviceLocation.provider.providerNumber = state.order.serviceLocation.provider.providerNumber =
sessionInformation.order.serviceLocation.provider?.providerNumber; sessionInformation.order.serviceLocation.provider?.providerNumber;
state.order.serviceLocation.provider.address.streetAddress = state.order.serviceLocation.provider.address.streetAddress =
sessionInformation.order.serviceLocation.provider?.address?.streetAddress; sessionInformation.order.serviceLocation.provider?.address?.streetAddress;
state.order.serviceLocation.provider.address.city = state.order.serviceLocation.provider.address.city =
sessionInformation.order.serviceLocation.provider?.address?.city; sessionInformation.order.serviceLocation.provider?.address?.city;
state.order.serviceLocation.provider.address.state = state.order.serviceLocation.provider.address.state =
sessionInformation.order.serviceLocation.provider?.address?.state; sessionInformation.order.serviceLocation.provider?.address?.state;
state.order.serviceLocation.provider.address.zipCode = state.order.serviceLocation.provider.address.zipCode =
sessionInformation.order.serviceLocation.provider?.address?.zipCode; sessionInformation.order.serviceLocation.provider?.address?.zipCode;
state.order.serviceLocation.provider.address.zipCodeCtu = state.order.serviceLocation.provider.address.zipCodeCtu =
sessionInformation.order.serviceLocation.provider?.address?.zipCodeCtu; sessionInformation.order.serviceLocation.provider?.address?.zipCodeCtu;
state.order.serviceLocation.techNotes = sessionInformation.order.serviceLocation.techNotes; state.order.serviceLocation.techNotes =
sessionInformation.order.serviceLocation.techNotes;
}
// if we're loading a session and we do not have a provider number yet, then set isInsurance to null so that // if we're loading a session and we do not have a provider number yet, then set isInsurance to null so that
// a service package is not selected by default on the quote page. // a service package is not selected by default on the quote page.
@ -741,14 +748,15 @@ export const mutations = {
state.applicationUser.savedSessionId = state.applicationUser.savedSessionId =
sessionInformation.applicationUser.savedSessionId; sessionInformation.applicationUser.savedSessionId;
} }
//Do not assign default schedule details once user make schedule selection
state.order.schedule.date = sessionInformation.order.schedule?.date; if (state.order.schedule.date == null) {
state.order.schedule.startTime = sessionInformation.order.schedule?.startTime; state.order.schedule.date = sessionInformation.order.schedule?.date;
state.order.schedule.endTime = sessionInformation.order.schedule?.endTime; state.order.schedule.startTime = sessionInformation.order.schedule?.startTime;
state.order.schedule.routeCode = sessionInformation.order.schedule?.routeCode; state.order.schedule.endTime = sessionInformation.order.schedule?.endTime;
state.order.schedule.jobMaxMinutes = sessionInformation.order.schedule?.jobMaxMinutes; state.order.schedule.routeCode = sessionInformation.order.schedule?.routeCode;
state.order.schedule.jobMinMinutes = sessionInformation.order.schedule?.jobMinMinutes; state.order.schedule.jobMaxMinutes = sessionInformation.order.schedule?.jobMaxMinutes;
state.order.schedule.jobMinMinutes = sessionInformation.order.schedule?.jobMinMinutes;
}
state.order.policy.currentDeductible = sessionInformation.order.policy?.currentDeductible; state.order.policy.currentDeductible = sessionInformation.order.policy?.currentDeductible;
state.order.policy.originalDeductible = sessionInformation.order.policy?.originalDeductible; state.order.policy.originalDeductible = sessionInformation.order.policy?.originalDeductible;
state.order.policy.additionalAuthFlag = sessionInformation.order.policy?.additionalAuthFlag; state.order.policy.additionalAuthFlag = sessionInformation.order.policy?.additionalAuthFlag;
@ -989,6 +997,28 @@ export const getters = {
externalParameterEstimate: (state) => externalParameterState?.estimate, externalParameterEstimate: (state) => externalParameterState?.estimate,
externalParameterCustomer: (state) => externalParameterState?.customer, externalParameterCustomer: (state) => externalParameterState?.customer,
isExternalParameter: (state) => externalParameterState?.isExternalParameter, isExternalParameter: (state) => externalParameterState?.isExternalParameter,
emailOrSms: (state) => {
const email =
externalParameterState.customer.emailAddress ?? state.order.customer.emailAddress;
const phone =
externalParameterState.customer.phoneNumber ?? state.order.customer.phoneNumber;
if (!phone && !email) {
return null;
}
if (phone && !email) {
return phone;
}
if (email && !phone) {
return email;
}
// if we're here, then we have both email and phone so get the value from pageData to reflect what they entered on the page
return state.applicationUser.pageData[fmgPageValues.EMAIL_SMS_PAGES]?.emailOrSmsValue;
},
}; };
function getNonFalseValuesOfPropertyInArrayOfObjects(array, propertyName) { function getNonFalseValuesOfPropertyInArrayOfObjects(array, propertyName) {
@ -1254,10 +1284,25 @@ export const actions = {
}, },
// Analytics Actions // Analytics Actions
logExperimentExposure( logExperimentExposureAndUpdateStore(
context, context,
{ payload: { userId, deviceId, sessionKey, pageName, experiment }, pageNameToLog } { payload: { userId, deviceId, sessionKey, pageName, experiment }, pageNameToLog }
) { ) {
const experiments = deepClone(context.getters.applicationUser.experiments);
const experimentToMarkAsExposed = experiments.find((ex) => {
return (
ex.universeId == experiment.universeId &&
ex.testId == experiment.testId &&
ex.variationId == experiment.variationId &&
ex.assignmentId == experiment.assignmentId
);
});
if (experimentToMarkAsExposed) {
experimentToMarkAsExposed.isExposed = true;
}
context.commit(storeMutations.UPDATE_EXPERIMENTS, experiments);
return globalMethods.callHttpClient({ return globalMethods.callHttpClient({
method: endpoints.LogExperimentExposureIfAssigned.method, method: endpoints.LogExperimentExposureIfAssigned.method,
endpoint: endpoints.LogExperimentExposureIfAssigned.url, endpoint: endpoints.LogExperimentExposureIfAssigned.url,
@ -2242,7 +2287,7 @@ export const actions = {
response.data response.data
); );
await resetScheduleIfUnavailable(context, response.data.order, pageNameToLog); await resetScheduleIfUnavailable(context, context.state.order, pageNameToLog);
return response; return response;
}, },
(error) => { (error) => {
@ -3015,7 +3060,17 @@ export const actions = {
}, },
savePhoneNumber(context, phoneNumber) { savePhoneNumber(context, phoneNumber) {
context.commit(storeMutations.UPDATE_CUSTOMER_PHONE_NUMBER, phoneNumber === "" ? null : phoneNumber); context.commit(
storeMutations.UPDATE_CUSTOMER_PHONE_NUMBER,
phoneNumber === "" ? null : phoneNumber
);
},
saveIsSmsOptIn(context, isSmsOptIn) {
context.commit(storeMutations.UPDATE_CUSTOMER_IS_SMS_OPT_IN, isSmsOptIn);
},
savePageData(context, emailOrSms) {
context.commit(storeMutations.UPDATE_PAGE_DATA, emailOrSms);
}, },
saveVin(context, { isSelectedGlassAvailableForVehicle, vehicleInfo }) { saveVin(context, { isSelectedGlassAvailableForVehicle, vehicleInfo }) {
@ -3677,7 +3732,6 @@ function createExternalParameterDefaultState() {
vinSelection: null, vinSelection: null,
}, },
serviceZip: { serviceZip: {
emailAddress: null,
zipCode: null, zipCode: null,
}, },
quote: { quote: {
@ -3685,6 +3739,7 @@ function createExternalParameterDefaultState() {
servicePackage: null, servicePackage: null,
}, },
customer: { customer: {
emailAddress: null,
phoneNumber: null, phoneNumber: null,
}, },
}; };