DigitalConsumer.FixMyGlass/src/layouts/address-lookup/address-lookup.vue
CarlNation eedcf4dfdd CASH-291
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.
2025-02-22 06:09:45 -05:00

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>