CASH-291 The issue here is that when computing the tier one price for determining which tab to select. we are including the service package discount of $50 bringing the tier one price to $287(337-50), which is less than $300 and causing the cash tab to be selected. This is not the tier one price we show but only used in the computation for determining which tab to default.
509 lines
22 KiB
Vue
509 lines
22 KiB
Vue
<template>
|
|
<Form
|
|
@submit="onSubmit"
|
|
@invalid-submit="onInvalidSubmit"
|
|
ref="theForm"
|
|
v-slot="{ meta }"
|
|
autocomplete="off">
|
|
<div class="container-fluid page-container-grouped-styles" id="address-lookup">
|
|
<div class="row justify-content-center">
|
|
<div class="col-md-6">
|
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
|
|
</div>
|
|
</div>
|
|
<div class="row justify-content-center">
|
|
<div class="col-md-6 col-xl-4 mt-4">
|
|
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" ref="funnelSubHeader" />
|
|
|
|
<customerQuestions
|
|
ref="customerQuestions"
|
|
v-model="customerQuestions"
|
|
:validationRules="EmailOrSmsValidationRules"
|
|
:isEmailOptional="IsEmailOptional"
|
|
:isAddressFieldFocused="IsAddressFieldFocused" />
|
|
<alert
|
|
ref="alertVinNotFound"
|
|
v-if="displayVinNotFoundAlert"
|
|
class="mb-4"
|
|
cmsWidgetName="AlertVinNotFoundWidget"
|
|
alertClass="alert-danger"
|
|
v-bind:isDismissible="false" />
|
|
|
|
<alert
|
|
ref="alertMatchedDifferentVehicle"
|
|
v-if="displayMatchedDifferentVehicleAlert"
|
|
class="mb-4"
|
|
:manualHeadline="AlertMatchedDifferentVehicleHeader"
|
|
:manualCopy="AlertMatchedDifferentVehicleBody"
|
|
alertClass="alert-warning"
|
|
v-bind:isDismissible="false" />
|
|
|
|
<alert
|
|
ref="alertInvalidZip"
|
|
v-if="displayInvalidZipAlert"
|
|
class="mb-4"
|
|
cmsWidgetName="AlertInvalidZipWidget"
|
|
alertClass="alert-danger"
|
|
v-bind:isDismissible="false" />
|
|
|
|
<alert
|
|
ref="alertNonServiceableZip"
|
|
v-if="displayNonServiceableZipAlert"
|
|
class="mb-4"
|
|
alertClass="alert-danger"
|
|
:manualHeadline="AlertNonServiceableZipHeader"
|
|
:manualCopy="AlertNonServiceableZipBody"
|
|
v-bind:isDismissible="false" />
|
|
|
|
<alert
|
|
ref="alertVinLookupsByHomeAddressNotAllowed"
|
|
v-if="displayVinLookupByHomeAddressNotAllowedAlert"
|
|
class="mb-4"
|
|
cmsWidgetName="AlertVinLookupsByHomeAddressNotAllowedWidget"
|
|
alertClass="alert-danger"
|
|
v-bind:isDismissible="false" />
|
|
|
|
<transition name="fade" mode="out-in">
|
|
<div
|
|
class="service-zip-field"
|
|
v-if="showServiceZipField"
|
|
aria-live="polite">
|
|
<div class="row mt-4 mb-1">
|
|
<div class="col">
|
|
<textboxQuestion
|
|
cmsWidgetName="ServiceZipQuestionWidget"
|
|
v-model="serviceZipCode"
|
|
ref="serviceZip"
|
|
customInputId="serviceZip"
|
|
aria-haspopup=""
|
|
mask="#####"
|
|
validationRules="service-zip-required|service-zip-format" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</transition>
|
|
<navbar
|
|
cmsWidgetName="FunnelFooterWidget"
|
|
ref="navbar"
|
|
:isDisabled="!meta.valid"
|
|
@ForwardClicked="forwardButtonAction"
|
|
@back-clicked="backButtonAction"
|
|
:isForwardActionDisabled="!meta.valid" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Form>
|
|
</template>
|
|
|
|
<script>
|
|
// Components
|
|
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
|
|
import navbar from "@/fmg-components/nav-bar/nav-bar";
|
|
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
|
|
import customerQuestions from "@/layouts/address-lookup/customer-questions/customer-questions";
|
|
import alert from "@/ux-components/alert/alert";
|
|
import textboxQuestion from "@/digital-components/textbox-question/textbox-question";
|
|
|
|
import { Form, defineRule } from "vee-validate";
|
|
import { required, regex } from "@/helpers/validation-rules";
|
|
import { errorMessages } from "@/constants/error-messages";
|
|
|
|
// Supporting files
|
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
|
import { storeActions } from "@/constants/store-actions";
|
|
import { routerParams } from "@/router/router-constants/router-params";
|
|
import { getDamageString, isGlassAvailableForCarId } from "@/helpers/damage-helper";
|
|
import baseMixin from "@/mixins/base-mixin.js";
|
|
import store from "@/store";
|
|
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
|
|
defineRule("service-zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
|
|
defineRule(
|
|
"service-zip-format",
|
|
regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.SERVICE_ZIP_FORMAT)
|
|
);
|
|
|
|
export default {
|
|
name: "address-lookup",
|
|
mixins: [vinPagesMixin],
|
|
async beforeRouteEnter(to, from, next) {
|
|
// Call APIs
|
|
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
|
|
|
// Settle promises and get results
|
|
const promiseResultMap = [
|
|
{
|
|
resultKey: "cmsContent",
|
|
promise: cmsContentPromise,
|
|
},
|
|
];
|
|
|
|
const resultMap = await settleAllPromises(promiseResultMap);
|
|
|
|
// Call the "next" function to complete the transition to this page.
|
|
next((vm) => {
|
|
vm.setCmsContent(resultMap.cmsContent);
|
|
if (store.getters.externalParameterState?.isExternalParameter) {
|
|
baseMixin.methods.ResetExternalParamsAndHideModal();
|
|
}
|
|
});
|
|
},
|
|
data() {
|
|
return {
|
|
customerQuestions: {
|
|
addressQuestions: {
|
|
streetAddress: this.getRegistrationAddressFromStore(),
|
|
city: this.getRegistrationCityFromStore(),
|
|
state: this.getRegistrationStateFromStore(),
|
|
zipCode: this.getRegistrationZipFromStore(),
|
|
},
|
|
firstName: this.getRegistrationFirstNameFromStore(),
|
|
lastName: this.getRegistrationLastNameFromStore(),
|
|
emailOrSms: this.getEmailOrSmsFromStore(),
|
|
},
|
|
serviceZipCode: this.getServiceZipFromStore(),
|
|
displayNonServiceableZipAlert: false,
|
|
displayVinNotFoundAlert: false,
|
|
displayMatchedDifferentVehicleAlert: false,
|
|
displayVinLookupByHomeAddressNotAllowedAlert: false,
|
|
previouslyEnteredCarId: "",
|
|
isCarIdDifferent: false,
|
|
isSelectedGlassAvailableForVehicle: true,
|
|
customAlertData: {},
|
|
displayInvalidZipAlert: false,
|
|
showServiceZipField: this.getServiceZipFromStore(),
|
|
isZipServiceable: false,
|
|
};
|
|
},
|
|
methods: {
|
|
arePagePrerequisitesValid() {
|
|
return store.getters.vehicle.carId !== null;
|
|
},
|
|
backButtonAction() {
|
|
// route to move backwards
|
|
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
|
|
},
|
|
attachCustomEvents() {
|
|
this.prependActionToMethod(this, this.forwardButtonAction, () => {
|
|
this.pushEventToGA(
|
|
this.$route.query[this.queryStrings.FMG_PAGE],
|
|
this.GaActions.SUBMITTED,
|
|
this.GaLabels.ADDRESS_LOOKUP,
|
|
true
|
|
);
|
|
});
|
|
},
|
|
getRegistrationAddressFromStore() {
|
|
return this.$store.getters.vehicle.registration.address;
|
|
},
|
|
getRegistrationCityFromStore() {
|
|
return this.$store.getters.vehicle.registration.city;
|
|
},
|
|
getRegistrationStateFromStore() {
|
|
return this.$store.getters.vehicle.registration.state;
|
|
},
|
|
getRegistrationZipFromStore() {
|
|
return this.$store.getters.vehicle.registration.zipCode;
|
|
},
|
|
getRegistrationFirstNameFromStore() {
|
|
return this.$store.getters.vehicle.registration.firstName;
|
|
},
|
|
getRegistrationLastNameFromStore() {
|
|
return this.$store.getters.vehicle.registration.lastName;
|
|
},
|
|
getEmailOrSmsFromStore() {
|
|
return this.$store.getters.emailOrSms;
|
|
},
|
|
getServiceZipFromStore() {
|
|
return this.$store.getters.order.serviceLocation.zipCode;
|
|
},
|
|
async forwardButtonAction() {
|
|
this.resetWarningsAndErrors();
|
|
|
|
// Vehicle info change in the flow, use a variable to keep track and commit to state at the end.
|
|
let vehicleInfoToCommit = {};
|
|
|
|
const vinLookupResponse = this.dispatchStoreActionWithLogging(
|
|
storeActions.LOOKUP_VIN_BY_ADDRESS,
|
|
{
|
|
licenseLastName: this.customerQuestions.lastName,
|
|
licenseStreetAddress: this.customerQuestions.addressQuestions.streetAddress,
|
|
licenseZip: this.customerQuestions.addressQuestions.zipCode,
|
|
licenseState: this.customerQuestions.addressQuestions.state,
|
|
},
|
|
"address-lookup",
|
|
false
|
|
);
|
|
|
|
// Settle promises and get results
|
|
const promiseResultMap = [
|
|
{
|
|
resultKey: "vinLookupResponse",
|
|
promise: vinLookupResponse,
|
|
},
|
|
{
|
|
resultKey: "serviceZipValidationResponse",
|
|
promise: this.serviceZipCode
|
|
? this.dispatchStoreActionWithLogging(
|
|
storeActions.VALIDATE_ZIP,
|
|
{
|
|
zip: this.serviceZipCode,
|
|
},
|
|
"address-lookup"
|
|
)
|
|
: this.dispatchStoreActionWithLogging(
|
|
storeActions.VALIDATE_ZIP,
|
|
{
|
|
zip: this.customerQuestions.addressQuestions.zipCode,
|
|
},
|
|
"address-lookup"
|
|
),
|
|
},
|
|
];
|
|
|
|
const resultMap = await settleAllPromises(promiseResultMap);
|
|
// If VIN Lookup by address is forbidden by State Restrictions then show an alert
|
|
if (!resultMap.vinLookupResponse.isStatePermissible) {
|
|
// State Restrictions forbid lookup by address
|
|
this.displayVinLookupByHomeAddressNotAllowedAlert = true;
|
|
return this.$refs.navbar.removeLoader();
|
|
}
|
|
|
|
// If a Service Zip is entered and it is an invalid zip code (ex. 11111) then show an alert
|
|
const isZipValid = resultMap.serviceZipValidationResponse.isValid;
|
|
if (this.serviceZipCode && !isZipValid) {
|
|
this.displayInvalidZipAlert = true;
|
|
return this.$refs.navbar.removeLoader();
|
|
}
|
|
this.displayInvalidZipAlert = false;
|
|
|
|
const carsFound = resultMap.vinLookupResponse.vinVehicles;
|
|
|
|
// Handle cases for different amounts of VINS found for the address.
|
|
if (carsFound.length == 1) {
|
|
// Single VIN found
|
|
const carFound = carsFound[0].vehicle;
|
|
|
|
this.isCarIdDifferent = carFound.carId !== this.$store.getters.vehicle.carId;
|
|
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,
|
|
"address-lookup"
|
|
);
|
|
|
|
// Update button "Continue with..."
|
|
this.$refs.navbar.updateButtonText(
|
|
`Continue with ${carFound.year} ${carFound.make} ${carFound.model}`
|
|
);
|
|
return this.$refs.navbar.removeLoader();
|
|
}
|
|
|
|
// update data if the zip or service zip is serviceable
|
|
vehicleInfoToCommit = Object.assign(carFound, { vin: carsFound[0].vin });
|
|
} else if (carsFound.length > 1) {
|
|
// If multiple cars were found and one and only one of them matches the carId entered, save the vehicle info
|
|
// so we can go to the Heritage Funnel directly
|
|
const matchingCars = carsFound.filter(
|
|
(vin) => vin.vehicle.carId === this.$store.getters.vehicle.carId
|
|
);
|
|
|
|
if (matchingCars.length === 1) {
|
|
vehicleInfoToCommit = Object.assign(matchingCars[0].vehicle, {
|
|
vin: matchingCars[0].vin,
|
|
});
|
|
}
|
|
} else {
|
|
// No VINS found.
|
|
this.displayVinNotFoundAlert = true;
|
|
return this.$refs.navbar.removeLoader();
|
|
}
|
|
|
|
// If the either the registration zip code or service zip code are not serviceable
|
|
this.isZipServiceable = resultMap.serviceZipValidationResponse.isServiceable;
|
|
if (!this.isZipServiceable) {
|
|
this.displayNonServiceableZipAlert = true;
|
|
this.showServiceZipField = true;
|
|
return this.$refs.navbar.removeLoader();
|
|
}
|
|
|
|
// If the registration zip code is serviceable and nothing was entered for the service zip code
|
|
// then set the service zip code to the registration zip code
|
|
if (!this.serviceZipCode) {
|
|
this.serviceZipCode = this.customerQuestions.addressQuestions.zipCode;
|
|
}
|
|
|
|
// Save vehicle, customer, service and registration information
|
|
await this.dispatchStoreAction(
|
|
storeActions.SAVE_REGISTRATION_ADDRESS_LOOKUP,
|
|
{
|
|
isSelectedGlassAvailableForVehicle: this.isSelectedGlassAvailableForVehicle,
|
|
vehicleInfo:
|
|
Object.keys(vehicleInfoToCommit).length === 0
|
|
? this.$store.getters.vehicle
|
|
: vehicleInfoToCommit,
|
|
customerInfo: {
|
|
firstName: this.customerQuestions.firstName,
|
|
lastName: this.customerQuestions.lastName,
|
|
},
|
|
},
|
|
false
|
|
);
|
|
|
|
// if no value due to field being optional, blank both phone and email address
|
|
if (!this.customerQuestions.emailOrSms) {
|
|
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, "", false);
|
|
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, "", false);
|
|
} else {
|
|
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(
|
|
storeActions.SAVE_PAGE_DATA,
|
|
{
|
|
page: fmgPageValues.EMAIL_SMS_PAGES,
|
|
data: { emailOrSmsValue: this.customerQuestions.emailOrSms },
|
|
},
|
|
false
|
|
);
|
|
await this.dispatchStoreAction(
|
|
storeActions.SAVE_SERVICE_ZIP_CODE_INFO,
|
|
{
|
|
state: resultMap.serviceZipValidationResponse.state,
|
|
zipCode: this.serviceZipCode,
|
|
zipCodeCtu: resultMap.serviceZipValidationResponse.zipCodeCtu,
|
|
},
|
|
false
|
|
);
|
|
await this.getAndSaveBillToAccountNumber();
|
|
|
|
return await this.navigateForward(carsFound);
|
|
},
|
|
async navigateForward(carsFound) {
|
|
// Match vehicles found to vehicles in state.
|
|
const matchingCars = carsFound.filter(
|
|
(car) => car.vehicle.carId === this.$store.getters.vehicle.carId
|
|
);
|
|
|
|
// If a different vehicle is found than the one entered and the selected glass is not available for that vehicle then navigate back to "vehicle-damage"
|
|
// display vehicle changed alert on that page.
|
|
if (
|
|
this.isCarIdDifferent &&
|
|
!this.isSelectedGlassAvailableForVehicle &&
|
|
matchingCars.length === 1
|
|
) {
|
|
this.$router.navigateWithSaving(
|
|
this.navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
|
|
this.$route,
|
|
{},
|
|
{ [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true }
|
|
);
|
|
} else if (matchingCars.length === 1) {
|
|
await this.navigateForwardWithSingleCarMatch();
|
|
} else {
|
|
this.$router.navigateWithSaving(
|
|
this.navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_VEHICLES,
|
|
this.$route,
|
|
{},
|
|
{},
|
|
carsFound
|
|
);
|
|
}
|
|
},
|
|
resetWarningsAndErrors() {
|
|
this.displayVinNotFoundAlert = false;
|
|
this.displayNonServiceableZipAlert = false;
|
|
this.displayMatchedDifferentVehicleAlert = false;
|
|
this.displayVinLookupByHomeAddressNotAllowedAlert = false;
|
|
},
|
|
},
|
|
mounted() {
|
|
this.attachCustomEvents();
|
|
},
|
|
computed: {
|
|
AlertNonServiceableZipHeader() {
|
|
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() {
|
|
return this.getCmsContent(
|
|
"AlertMatchedDifferentVehicleWidget",
|
|
"HeadlineText"
|
|
).replaceAll("{custom:glassText}", getDamageString());
|
|
},
|
|
AlertMatchedDifferentVehicleBody() {
|
|
const vinYmmFound = `${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model}`;
|
|
const vinYmmExpected = `${this.$store.getters.vehicle.year} ${this.$store.getters.vehicle.make} ${this.$store.getters.vehicle.model}`;
|
|
|
|
return this.getCmsContent("AlertMatchedDifferentVehicleWidget", "BodyText")
|
|
.replaceAll("{custom:glassText}", getDamageString())
|
|
.replaceAll("{custom:vinYmmFound}", vinYmmFound)
|
|
.replaceAll("{custom:vinYmmExpected}", vinYmmExpected);
|
|
},
|
|
IsAddressFieldFocused() {
|
|
return false;
|
|
},
|
|
},
|
|
watch: {
|
|
customerQuestions: {
|
|
handler(newValue) {
|
|
// 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.navbar.updateButtonText(
|
|
this.getCmsContent("FunnelFooterWidget", "ForwardButtonText")
|
|
);
|
|
this.showServiceZipField = false;
|
|
this.resetWarningsAndErrors();
|
|
},
|
|
deep: true,
|
|
},
|
|
serviceZipCode: {
|
|
handler(newValue) {
|
|
// 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,
|
|
navbar,
|
|
funnelSubHeader,
|
|
customerQuestions,
|
|
textboxQuestion,
|
|
alert,
|
|
Form,
|
|
},
|
|
};
|
|
</script>
|