@@ -51,12 +51,26 @@ export default {
default: '',
},
validationRules: String,
- cmsWidgetName: String,
+ cmsWidgetName: String
},
setup(props) {
+ const propsClone = Object.assign({}, props);
+ const modelValue = propsClone.modelValue;
+ let initialValue;
+
+ switch (typeof modelValue) {
+ case "number":
+ initialValue = modelValue;
+ break;
+ default:
+ initialValue = (modelValue && modelValue.length > 0) ? modelValue : "";
+ break;
+ }
+
const fieldOptions = {
type: "text",
- value: props.modelValue,
+ value: modelValue,
+ initialValue: initialValue
};
const {
@@ -98,10 +112,10 @@ export default {
words.forEach(function (word) {
const position = 1;
word = [word.toString().slice(0, position), noBreakChar, word.toString().slice(position)].join('');
- questionText += `${word} `;
- });
-
- questionText = questionText.trimEnd();
+ questionText += `${word} `;
+ });
+
+ questionText = questionText.trimEnd();
} else {
questionText = this.questionText.toString();
}
@@ -110,6 +124,11 @@ export default {
}
}
},
+ watch: {
+ value(newValue) {
+ this.handleChange(newValue);
+ }
+ }
};
@@ -117,6 +136,7 @@ export default {
.textbox-question {
label {
color: $black;
+ font-weight: 500;
}
input {
&.has-icon {
@@ -161,4 +181,4 @@ export default {
display: none;
}
}
-
+
\ No newline at end of file
diff --git a/src/constants/error-messages.js b/src/constants/error-messages.js
index 963ba0b5d..a9f09326b 100644
--- a/src/constants/error-messages.js
+++ b/src/constants/error-messages.js
@@ -13,6 +13,7 @@ const errorMessages = {
ZIP_REQUIRED: "Please enter your ZIP",
ZIP_FORMAT: "Please enter a valid ZIP",
LICENSE_PLATE_REQUIRED: "Please enter your license plate number",
+ REGISTRATION_ZIP_REQUIRED: "Please enter your registration ZIP code",
FIRST_NAME_REQUIRED: "Please enter your first name",
LAST_NAME_REQUIRED: "Please enter your last name",
EMAIL_ADDRESS_REQUIRED: "Please enter your email address",
@@ -20,7 +21,7 @@ const errorMessages = {
SERVICE_ZIP_REQUIRED: "Please enter your Service ZIP",
SERVICE_ZIP_FORMAT: "Please enter a valid Service ZIP",
VIN_REQUIRED: "Please enter your VIN",
- VIN_FORMAT: "Please enter a valid VIN",
+ VIN_FORMAT: "Invalid VIN. Please make sure that you entered the correct 17-digit, alpha-numeric number. VINs do not contain the letters I, O, or Q",
OPTION_REQUIRED: "Please select an option",
};
diff --git a/src/helpers/damage-helper.js b/src/helpers/damage-helper.js
index e56761dc5..f47499bed 100644
--- a/src/helpers/damage-helper.js
+++ b/src/helpers/damage-helper.js
@@ -3,9 +3,32 @@ import baseMixin from "@/mixins/base-mixin.js";
import { storeActions } from "@/constants/store-actions";
export function getDamageString() {
- return store.getters.damage.glassToReplace.length > 1 ? "match" : store.getters.damage.glassToReplace[0].location;
+ const damageLocations = store.getters.damage.glassToReplace;
+ let returnString;
+ if(damageLocations.length > 1){
+ returnString = "match"
+ } else {
+ switch(damageLocations[0]?.location) {
+ case "Windshield":
+ returnString = "windshield"
+ break;
+ case "Driver":
+ case "Passenger":
+ returnString = "side window"
+ break;
+ case "Rear":
+ returnString = "rear window"
+ }
+ }
+ return returnString;
}
+ export function getIsWindshieldOnly () {
+ const damageLocations = store.getters.damage.glassToReplace;
+ const returnString = damageLocations.length === 1 && damageLocations[0]?.location === "Windshield" ? "windshield" : "glass";
+ return returnString;
+ }
+
export async function isGlassAvailableForCarId(carId){
const newGlassOptions = await baseMixin.methods.dispatchStoreAction(
storeActions.GET_DAMAGE_OPTIONS,
diff --git a/src/helpers/damage-helper.spec.js b/src/helpers/damage-helper.spec.js
index ad5f8f878..2a46978dc 100644
--- a/src/helpers/damage-helper.spec.js
+++ b/src/helpers/damage-helper.spec.js
@@ -1,16 +1,91 @@
import {getDamageString, isGlassAvailableForCarId} from "./damage-helper";
-//import baseMixin from "@/mixins/base-mixin.js";
+import store from "@/store";
-jest.mock("@/store", () => ({
- getters: {damage: {
- glassToReplace: [{location: "Windshield", name: "windshield"}]
- }
- }
- }));
+// Mock basemixin.
+jest.mock("@/mixins/base-mixin.js", () => ({
+ methods: {
+ dispatchStoreAction: jest.fn().mockImplementation(() => { return {
+ data: {
+ windshieldOptions: {availableReplacementOptions: ["windshield"]}
+ }
+ } }),
+ },
+}));
describe("damage-helper.js", () => {
- it("Should return damage getter info", () => {
+ it("Should return match when multiple selected damage options are in the store", () => {
+
+ // Arrange / Act
+ store.getters.damage.glassToReplace = [{location: "Windshield", name: "windshield"}, {location: "Passenger", name: "sideWindow"}];
+
const damage = getDamageString();
- expect(damage).toEqual("Windshield")
+
+ // Assert
+ expect(damage).toEqual("match");
});
- });
\ No newline at end of file
+ });
+
+ describe("damage-helper.js", () => {
+ it("Should return windshield when Windshield is the only selected damage option in the store", () => {
+
+ // Arrange / Act
+ store.getters.damage.glassToReplace = [{location: "Windshield", name: "windshield"}];
+
+ const damage = getDamageString();
+
+ // Assert
+ expect(damage).toEqual("windshield");
+ });
+ });
+
+ describe("damage-helper.js", () => {
+ it("Should return side window when Driver or Passenger is the only selected damage option in the store", () => {
+
+ // Arrange / Act
+ store.getters.damage.glassToReplace = [{location: "Passenger", name: "sideWindow"}];
+
+ const damage = getDamageString();
+
+ // Assert
+ expect(damage).toEqual("side window");
+ });
+ });
+
+ describe("damage-helper.js", () => {
+ it("Should return rear window when Rear is the only selected damage option in the store", () => {
+
+ // Arrange / Act
+ store.getters.damage.glassToReplace = [{location: "Rear", name: "rear"}];
+
+ const damage = getDamageString();
+
+ // Assert
+ expect(damage).toEqual("rear window");
+ });
+ });
+
+ describe("damage-helper.js", () => {
+ it("Should return true if no mismatches between each array exist", async () => {
+ // Arrange
+ store.getters.damage.glassToReplace = [{location: "Windshield", name: "windshield"}];
+
+ // Act
+ const isGlassAvailable = await isGlassAvailableForCarId();
+
+ // Assert
+ expect(isGlassAvailable).toEqual(true);
+ });
+ });
+
+ describe("damage-helper.js", () => {
+ it("Should return false if any mismatches between each array exist", async () => {
+ // Arrange
+ store.getters.damage.glassToReplace = [{location: "Windshield", name: "sideWindow"}];
+
+ const isGlassAvailable = await isGlassAvailableForCarId();
+
+ // Assert
+ expect(isGlassAvailable).toEqual(false);
+ });
+ });
+
diff --git a/src/helpers/heritage-integration/navigation-helper.js b/src/helpers/heritage-integration/navigation-helper.js
index 61c07203d..c7cf34940 100644
--- a/src/helpers/heritage-integration/navigation-helper.js
+++ b/src/helpers/heritage-integration/navigation-helper.js
@@ -140,4 +140,4 @@ function isVinRelatedPage(toRoute) {
fmgPageValue === fmgPageValues.ADDRESS_LOOKUP ||
fmgPageValue === fmgPageValues.ADDRESS_VEHICLES ||
fmgPageValue === fmgPageValues.ESTIMATE;
-}
+}
\ No newline at end of file
diff --git a/src/helpers/heritage-integration/navigation-helper.spec.js b/src/helpers/heritage-integration/navigation-helper.spec.js
index fd06920c2..01bf5dcdc 100644
--- a/src/helpers/heritage-integration/navigation-helper.spec.js
+++ b/src/helpers/heritage-integration/navigation-helper.spec.js
@@ -367,4 +367,4 @@ describe("navigateToHeritageFunnel", () => {
})
);
});
-});
+});
\ No newline at end of file
diff --git a/src/layouts/address-lookup/address-lookup.vue b/src/layouts/address-lookup/address-lookup.vue
index 032b42268..b6140be2f 100644
--- a/src/layouts/address-lookup/address-lookup.vue
+++ b/src/layouts/address-lookup/address-lookup.vue
@@ -5,54 +5,56 @@
ref="theForm"
v-slot="{ meta }"
autocomplete="off" >
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
@@ -67,6 +69,7 @@ import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-he
import customerQuestions from "@/layouts/address-lookup/customer-questions/customer-questions";
import alert from "@/ux-components/alert/alert";
import textboxQuestion from "@/common-components/textbox-question/textbox-question";
+import loadingModal from '@/common-components/loading-modal/loading-modal.vue';
import { Form } from "vee-validate";
import { defineRule } from "vee-validate";
@@ -115,19 +118,22 @@ export default {
streetAddress: this.getRegistrationAddressFromStore(),
city: this.getRegistrationCityFromStore(),
state: this.getRegistrationStateFromStore(),
- zip: this.getRegistrationZipFromStore(),
+ zipCode: this.getRegistrationZipFromStore(),
},
firstName: this.getRegistrationFirstNameFromStore(),
lastName: this.getRegistrationLastNameFromStore(),
emailAddress: this.getEmailFromStore(),
},
- serviceZip: this.getServiceZipFromStore(),
+ serviceZipCode: this.getServiceZipFromStore(),
displayNonServiceableZipAlert: false,
displayVinNotFoundAlert: false,
displayMatchedDifferentVehicleAlert: false,
displayVinLookupByHomeAddressNotAllowedAlert: false,
- previousCarIdFound: "",
- customAlertData: {},
+ previouslyEnteredCarId: "",
+ isSelectedGlassAvailableForVehicle: false,
+ customAlertData: {},
+ showServiceZipField: this.getServiceZipFromStore(),
+ isZipServicable: false,
}
},
methods: {
@@ -173,65 +179,83 @@ export default {
this.resetWarningsAndErrors();
// Lookup VIN(s) with the provided address
- const vinLookup = await this.lookupVin(
+ const vinLookup = this.lookupVin(
this.customerQuestions.lastName,
this.customerQuestions.addressQuestions.streetAddress,
- this.customerQuestions.addressQuestions.zip,
+ this.customerQuestions.addressQuestions.zipCode,
this.customerQuestions.addressQuestions.state
);
- if (!vinLookup.data.isStatePermissible) {
+ // Verify if the service zip code or registration zip code provided is serviceable
+ const zipValidation = this.serviceZipCode ? this.validateZip(this.serviceZipCode) : this.validateZip(this.customerQuestions.addressQuestions.zipCode);
+
+ const vinLookupResponse = await vinLookup;
+ const zipValidationResponse = await zipValidation;
+
+ if (!vinLookupResponse.data.isStatePermissible) {
// State Restrictions forbid lookup by address
this.displayVinLookupByHomeAddressNotAllowedAlert = true;
this.$refs.funnelFooter.removeLoader();
return;
}
- // Validate if the original or service zip provided is serviceable
- const zipValidation = this.serviceZip ? await this.validateZip(this.serviceZip) : await this.validateZip(this.customerQuestions.addressQuestions.zip);
- if (!zipValidation.data.isServiceable) {
+ // if the neither the registration zip code or service zip code are not serviceable
+ this.isZipServicable = zipValidationResponse.data.isServiceable;
+ if (!this.isZipServicable) {
this.displayNonServiceableZipAlert = true;
this.showServiceZipField = true;
- this.$refs.funnelFooter.removeLoader();
+ this.$refs.funnelFooter.removeLoader();
+ } else if (!this.serviceZipCode) {
+ // if the registration zip code is servicable and nothing was entered for the service zip code
+ // then set the service zip code to the registration zip code
+ this.serviceZipCode = this.customerQuestions.addressQuestions.zipCode;
}
const carEntered = store.getters.vehicle;
- const carsFound = vinLookup.data.vinVehicles;
+ const carsFound = vinLookupResponse.data.vinVehicles;
+
if (carsFound.length == 0) {
// No VINs found
this.displayVinNotFoundAlert = true;
this.$refs.funnelFooter.removeLoader();
return;
} else if (carsFound.length == 1) {
- var carFound = carsFound[0].vehicle;
+ const carFound = carsFound[0].vehicle;
+ this.isCarIdDifferent = carFound.carId !== carEntered.carId;
- if (carEntered.carId == carFound.carId || carFound.carId == this.previousCarIdFound) {
- // update data
- this.updateVehicleInfo(carFound.vin, carFound);
- this.updateCustomerInfo();
-
- // navigate forward
- this.navigateForward(carEntered, carsFound);
- } else {
+ if (this.isCarIdDifferent && carFound.carId !== this.previouslyEnteredCarId) {
// Display Alert
+ this.previouslyEnteredCarId = carFound.carId;
this.customAlertData.vehicleInfo = carFound;
this.displayMatchedDifferentVehicleAlert = true;
+ this.isSelectedGlassAvailableForVehicle = await isGlassAvailableForCarId(carFound.carId);
+
// Update button "Continue with..."
this.$refs.funnelFooter.updateButtonText(`Continue with ${carFound.year} ${carFound.make} ${carFound.model}`);
this.$refs.funnelFooter.removeLoader();
- }
+
+ return;
+ }
+
+ if (!this.isZipServicable) {
+ return;
+ }
- this.previousCarIdFound = carFound.carId;
-
- } else if (vinLookup.data.vinVehicles.length > 1) {
+ // update data if the zip or service zip is servicable
+ this.updateVehicleInfo(carsFound[0].vin, carFound);
this.updateCustomerInfo();
+
+ } else if (carsFound.length > 1) {
+ if (!this.isZipServicable) {
+ return;
+ }
- // navigate forward
- this.navigateForward(carEntered, carsFound);
+ // update data if the zip or service zip is servicable
+ this.updateCustomerInfo();
}
-
+ this.navigateForward(carEntered, carsFound);
},
resetWarningsAndErrors() {
this.displayVinNotFoundAlert = false;
@@ -239,30 +263,35 @@ export default {
this.displayMatchedDifferentVehicleAlert = false;
this.displayVinLookupByHomeAddressNotAllowedAlert = false;
},
- async navigateForward(carEntered, carsFound) {
+ navigateForward(carEntered, carsFound) {
if (carsFound.length == 1) {
- // get the damage options for the car that was found
- const carFound = carsFound[0].vehicle;
- const glassOptions = await baseMixin.methods.dispatchStoreAction(
- storeActions.GET_DAMAGE_OPTIONS,
- { carId: carFound.carId }
- );
-
- // if the car entered is the same as the car found OR the glass options for the found car match the users damage selections
- if (carEntered.carId == carFound.carId || isGlassAvailableForCarId(carFound.carId)) {
- navigateAfterSaveToHeritageFunnel(this.$route);
+ // if a different vehicle is found than the one entered and the selected glass
+ // is not available for that vehicle
+ if (this.isCarIdDifferent && !this.isSelectedGlassAvailableForVehicle) {
+ this.$router.navigateAfterSave(
+ // then navigate back to "vehicle-damage", and display vehicle changed alert
+ // on that page
+ this.navigationScenarios.CONTINUING_WITH_DIFFERENT_GLASS,
+ this.$route, {}, {
+ displayVehicleChangeAlert: true
+ }, {}
+ );
} else {
- // if not then navigate to the "vehicle-damage" page
- this.$router.navigateAfterSave(this.navigationScenarios.CLICKED_FORWARD, this.$route, {}, { displayVehicleChangeAlert: true }, {});
- }
+ // otherwise
+ this.$refs.loadingModal.showModal();
+ navigateAfterSaveToHeritageFunnel(this.$route);
+ }
} else if (carsFound.length > 1) {
// if multiple cars were found
if (carsFound.find(car => car.carId === carEntered.carId)) {
// and one of them matches the car id entered
+ this.$refs.loadingModal.showModal();
navigateAfterSaveToHeritageFunnel(this.$route);
} else {
- // and there is no match, navigate to "address-vehicle" page
- this.$router.navigateAfterSave(this.navigationScenarios.CONTINUING_WITH_MULTIPLE_VEHICLES, this.$route, {}, {}, carsFound);
+ // and there is no match, navigate to "address-vehicles" page
+ this.$router.navigateAfterSave(this.navigationScenarios.CONTINUING_WITH_MULTIPLE_VEHICLES,
+ this.$route, {}, {},
+ carsFound);
}
}
@@ -302,29 +331,29 @@ export default {
store.commit(storeMutations.UPDATE_REGISTRATION_ZIP_CODE, this.customerQuestions.addressQuestions.zipCode);
store.commit(storeMutations.UPDATE_REGISTRATION_FIRST_NAME, this.customerQuestions.firstName);
store.commit(storeMutations.UPDATE_REGISTRATION_LAST_NAME, this.customerQuestions.lastName);
- store.commit(storeMutations.UPDATE_SERVICE_LOCATION_ZIP_CODE, this.serviceZip);
- store.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, this.customerQuestions.email);
+ store.commit(storeMutations.UPDATE_SERVICE_LOCATION_ZIP_CODE, this.serviceZipCode);
+ store.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, this.customerQuestions.emailAddress);
},
},
computed: {
AlertNonServiceableZipHeader(){
- let zip = this.serviceZip ? this.serviceZip : this.customerQuestions.addressQuestions.zip;
- let text = this.getCmsContent("AlertNonServiceableZipWidget", "HeadlineText").replaceAll("{custom:serviceZip}", zip);
+ const zipCode = this.serviceZipCode ? this.serviceZipCode : this.customerQuestions.addressQuestions.zipCode;
+ const text = this.getCmsContent("AlertNonServiceableZipWidget", "HeadlineText").replaceAll("{custom:serviceZip}", zipCode);
return text;
},
AlertNonServiceableZipBody(){
return this.getCmsContent("AlertNonServiceableZipWidget", "BodyText");
},
AlertMatchedDifferentVehicleHeader(){
- let text = this.getCmsContent("AlertMatchedDifferentVehicleWidget", "HeadlineText").replaceAll("{custom:glassText}", getDamageString());
+ const text = this.getCmsContent("AlertMatchedDifferentVehicleWidget", "HeadlineText").replaceAll("{custom:glassText}", getDamageString());
return text;
},
AlertMatchedDifferentVehicleBody(){
let content = this.getCmsContent("AlertMatchedDifferentVehicleWidget", "BodyText");
content = content.replaceAll("{custom:glassText}", getDamageString());
- let vinYmmFound = `${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model}`;
- let vinYmmExpected = `${store.getters.vehicle.year} ${store.getters.vehicle.make} ${store.getters.vehicle.model}`;
+ const vinYmmFound = `${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model}`;
+ const vinYmmExpected = `${store.getters.vehicle.year} ${store.getters.vehicle.make} ${store.getters.vehicle.model}`;
content = content.replaceAll("{custom:vinYmmFound}", vinYmmFound);
content = content.replaceAll("{custom:vinYmmExpected}", vinYmmExpected);
@@ -335,20 +364,27 @@ export default {
watch: {
customerQuestions: {
handler(newValue) {
- // if they modify one of the lookup fields (address, city, state, zip, or lastName), then modify the button text back to “Get my personalized quote”
+ // if they modify one of the lookup fields (address, city, state, zipCode, or lastName), then modify the button text back to “Get my personalized quote”
this.$refs.funnelFooter.updateButtonText(this.getCmsContent("FunnelFooterWidget", "ForwardButtonText"));
this.showServiceZipField = false;
this.resetWarningsAndErrors();
},
deep: true
},
- serviceZip: {
+ serviceZipCode: {
handler(newValue) {
- // if they modify the service zip, then hide the error message”
+ // if they modify the service zip code, then hide the error message”
this.displayNonServiceableZipAlert = false;
},
+ },
+ showServiceZipField: {
+ handler(newValue) {
+ // if the Service Zip Code field is ever hidden, clear out it's value
+ if (!newValue) {
+ this.serviceZipCode = null;
+ }
+ },
}
-
},
components: {
funnelHeader,
@@ -358,6 +394,7 @@ export default {
customerQuestions,
textboxQuestion,
alert,
+ loadingModal,
Form
},
};
diff --git a/src/layouts/address-lookup/customer-questions/address-questions/address-questions.vue b/src/layouts/address-lookup/customer-questions/address-questions/address-questions.vue
index 4a772e468..6192561e6 100644
--- a/src/layouts/address-lookup/customer-questions/address-questions/address-questions.vue
+++ b/src/layouts/address-lookup/customer-questions/address-questions/address-questions.vue
@@ -17,7 +17,7 @@