DigitalConsumer.FixMyGlass/src/layouts/vin-lookup/vin-lookup.vue

406 lines
16 KiB
Vue

<template>
<Form
@submit="onSubmit"
@invalid-submit="onInvalidSubmit"
ref="theForm"
v-slot="{ meta }"
>
<div class="page-container-grouped-styles">
<loadingModal ref="loadingModal" />
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
<vehicleBanner cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage="false" />
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
<div class="fade-on-route-transition sub-container make-tall">
<div class="row mt-2">
<div class="col">
<textboxQuestion
cmsWidgetName="VinNumberQuestionWidget"
v-model="vin"
inputId="vin"
isRequired
disableAutoFill
validationRules="vin-required|vin-format"
:isDisabled="vinPopulatedOnPageLoad"
maxLength="17"
:mask="vinMask"
/>
</div>
</div>
<div class="row mb-2">
<div class="col">
<vinInformation />
</div>
</div>
<div class="row my-2">
<div class="col">
<textboxQuestion
cmsWidgetName="ServiceZipQuestionWidget"
v-model="serviceZipCode"
inputId="serviceZipCode"
mask="#####"
isRequired
disableAutoFill
validationRules="zip-required|zip-format"
/>
</div>
</div>
<div class="row my-2">
<div class="col">
<textboxQuestion
cmsWidgetName="EmailAddressQuestionWidget"
v-model="emailAddress"
inputId="emailAddress"
isRequired
disableAutoFill
validationRules="email-address-required|email-address-format"
/>
</div>
</div>
<alert ref="alertInvalidZip"
v-if="displayInvalidZipAlert"
class="my-4"
cmsWidgetName="AlertInvalidZipWidget"
alertClass="alert-danger"
v-bind:isDismissible="false"
/>
<alert
class="my-4"
:manualHeadline="AlertPerfectMatchInsuranceVerifiedHeader"
:manualCopy="AlertPerfectMatchInsuranceVerifiedBody"
v-model="customAlertData"
v-if="vinPopulatedOnPageLoad && isInsuranceVerified"
alertClass="alert-success"
/>
<alert
class="my-4"
:manualHeadline="AlertMatchedDifferentVehicleHeader"
:manualCopy="AlertMatchedDifferentVehicleBody"
v-model="customAlertData"
v-if="displayMatchedDifferentVehicleAlert"
alertClass="alert-warning"
/>
<alert
class="my-4"
:manualHeadline="AlertNonServiceableZipHeader"
:manualCopy="AlertNonServiceableZipBody"
v-model="customAlertData"
v-if="displayNonServiceableZipAlert"
alertClass="alert-danger"
/>
<alert
class="my-4"
v-model="customAlertData"
v-if="displayVinNotFoundAlert"
alertClass="alert-danger"
cmsWidgetName="AlertVinNotFoundWidget"
/>
<alert
class="my-4"
:manualHeadline="AlertPerfectMatchInsuranceNotVerifiedHeader"
:manualCopy="AlertPerfectMatchInsuranceNotVerifiedBody"
v-model="customAlertData"
v-if="vinPopulatedOnPageLoad && !isInsuranceVerified"
alertClass="alert-success"
/>
<funnelFooter
cmsWidgetName="FunnelFooterWidget"
ref="funnelFooter"
:isForwardActionDisabled="!meta.valid"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction"
/>
</div>
</div>
</Form>
</template>
<script>
// Components
import funnelHeader from "@/common-components/funnel-header/funnel-header";
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
import alert from "@/ux-components/alert/alert";
import textboxQuestion from "@/common-components/textbox-question/textbox-question";
import vinInformation from "@/layouts/vin-lookup/vin-information/vin-information";
import loadingModal from '@/common-components/loading-modal/loading-modal.vue';
// Supporting files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { settleAllPromises } from "@/helpers/layout-helper";
import { storeActions } from "@/constants/store-actions";
import { errorMessages } from "@/constants/error-messages";
import { getDamageString, getIsWindshieldOnly, isGlassAvailableForCarId } from "@/helpers/damage-helper";
import { required, regex } from "@/helpers/validation-rules";
import { Form, defineRule } from "vee-validate";
import { getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
import { routerParams } from "@/router/router-constants/router-params";
import store from "@/store";
import vinPagesMixin from "@/mixins/vin-pages-mixin";
// DEFINE VALIDATION RULES
defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
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-address-format", regex(/^([a-zA-Z0-9_\-.+]+)@([a-zA-Z0-9_\-.]+)\.([a-zA-Z]{2,})$/, errorMessages.EMAIL_ADDRESS_FORMAT));
defineRule("vin-required", required(errorMessages.VIN_REQUIRED));
defineRule("vin-format", regex(/^[a-hA-Hj-nJ-NpPr-zR-Z0-9]{17}$/, errorMessages.VIN_FORMAT));
export default {
name: "vin-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);
});
},
data() {
return {
vin: this.getVinFromStore(),
serviceZipCode: this.getZipFromStore(),
emailAddress: this.getEmailFromStore(),
isCarIdDifferent: false,
customAlertData: {},
previouslyEnteredCarId: '',
vinPopulatedOnPageLoad: this.getVinFromStore()?.length > 0,
isSelectedGlassAvailableForVehicle: true,
displayInvalidZipAlert: false,
displayNonServiceableZipAlert: false,
displayVinNotFoundAlert: false,
displayMatchedDifferentVehicleAlert: false,
};
},
methods: {
arePagePrerequisitesValid() {
return store.getters.vehicle.carId !== null;
},
getEmailFromStore(){
return this.$store.getters.order.customer.emailAddress;
},
getVinFromStore(){
return this.$store.getters.vehicle.vin;
},
getZipFromStore(){
return this.$store.getters.order.serviceLocation.zipCode;
},
attachCustomEvents() {
this.prependActionToMethod(this, this.forwardButtonAction, () => {
this.pushEventToGA(
this.$route.query[this.queryStrings.FMG_PAGE],
this.GaActions.SUBMITTED,
this.GaLabels.VIN_LOOKUP,
true
);
});
},
backButtonAction() {
if (this.$store.getters.vehicle.vin) {
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK_WITH_VIN, this.$route);
}
else {
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
}
},
async forwardButtonAction() {
// If this is a new VIN Lookup, do both a Vehicle Lookup and a Zip Validation
if (!this.vinPopulatedOnPageLoad) {
const serviceZipValidationResponse = this.dispatchStoreAction(storeActions.VALIDATE_ZIP, { zip: this.serviceZipCode });
const vehicleLookupResponse = this.dispatchStoreAction(storeActions.LOOKUP_VEHICLE_BY_VIN, { vin: this.vin });
// Settle promises and get results
const promiseResultMap = [
{
resultKey: "serviceZipValidationResponse",
promise: serviceZipValidationResponse,
},
{
resultKey: "vehicleLookupResponse",
promise: vehicleLookupResponse,
},
];
const resultMap = await settleAllPromises(promiseResultMap);
// 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.funnelFooter.removeLoader();
}
this.displayInvalidZipAlert = false;
// If either lookup fails, remove the loader and stop processing the page.
if (!resultMap.vehicleLookupResponse || !resultMap.serviceZipValidationResponse.isServiceable) {
// If the vehicle result is undefined, the vin entered was invalid.
if(!resultMap.vehicleLookupResponse) {
this.displayVinNotFoundAlert = true;
}
// Check if Service Zip entered is serviceable, if not display an alert
if (!resultMap.serviceZipValidationResponse.isServiceable) {
this.displayNonServiceableZipAlert = true;
}
// Remove loader and stop processing the page.
return this.$refs.funnelFooter.removeLoader();
}
// Check if the CarId is different from the lookup vs what is in state currently.
this.isCarIdDifferent = resultMap.vehicleLookupResponse.carId !== this.$store.getters.vehicle.carId;
if (this.isCarIdDifferent && (resultMap.vehicleLookupResponse.carId !== this.previouslyEnteredCarId)) {
this.previouslyEnteredCarId = resultMap.vehicleLookupResponse.carId;
this.customAlertData.vehicleInfo = resultMap.vehicleLookupResponse;
this.displayMatchedDifferentVehicleAlert = true;
this.isSelectedGlassAvailableForVehicle = await isGlassAvailableForCarId(resultMap.vehicleLookupResponse.carId);
// Update button "Continue with..."
this.$refs.funnelFooter.updateButtonText(`Continue with ${resultMap.vehicleLookupResponse.year} ${resultMap.vehicleLookupResponse.make} ${resultMap.vehicleLookupResponse.model}`);
return this.$refs.funnelFooter.removeLoader();
}
// Save vin, vehicle, customer and service information
await this.dispatchStoreAction(storeActions.SAVE_VIN_LOOKUP, {
isSelectedGlassAvailableForVehicle: this.isSelectedGlassAvailableForVehicle,
vehicleInfo: Object.assign(resultMap.vehicleLookupResponse, { vin: this.vin })
}, false);
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.emailAddress, false);
await this.dispatchStoreAction(storeActions.SAVE_SERVICE_LOCATION, {
zipCode: this.serviceZipCode,
state: resultMap.serviceZipValidationResponse.state,
}, false);
return await this.navigateForward();
}
// If a VIN has already been found. Validate the Service Zip (in case of changes)
const zipValidationResponse = await this.dispatchStoreAction(storeActions.VALIDATE_ZIP, {zip: this.serviceZipCode});
// Check if Service Zip entered is serviceable
if (zipValidationResponse.data.isServiceable) {
// If the Service Zip entered is serviceable then save the Zip Info and Email Address and navigate forward
if (this.$store.getters.order.serviceLocation.zipCode != this.serviceZipCode) {
const vehicleRegistrationInfo = this.$store.getters.vehicle.registration;
if (vehicleRegistrationInfo.zipCode == this.serviceZipCode) {
await this.dispatchStoreAction(storeActions.SAVE_SERVICE_LOCATION, {
address: vehicleRegistrationInfo.address,
city: vehicleRegistrationInfo.city,
zipCode: vehicleRegistrationInfo.zipCode,
state: vehicleRegistrationInfo.state
}, false);
}
else {
await this.dispatchStoreAction(storeActions.SAVE_SERVICE_LOCATION, {
zipCode: this.serviceZipCode,
state: zipValidationResponse.data.state
}, false);
}
}
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.emailAddress, false);
return await this.navigateForward();
}
// If the Service Zip is NOT serviceable then show an alert
this.displayNonServiceableZipAlert = true;
return this.$refs.funnelFooter.removeLoader();
},
async navigateForward(){
if (this.isCarIdDifferent && !this.isSelectedGlassAvailableForVehicle) {
this.$router.navigateWithSaving(this.navigationScenarios.SELECTED_VIN_HAS_MISMATCHED_GLASS, this.$route, {}, { [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true });
} else {
await this.navigateForwardWithSingleCarMatch();
}
},
},
mounted() {
this.attachCustomEvents();
},
computed: {
AlertMatchedDifferentVehicleHeader(){
return this.getCmsContent("AlertMatchedDifferentVehicleWidget", "HeadlineText").replaceAll("{custom:damage}", getDamageString());
},
AlertMatchedDifferentVehicleBody(){
return this.getCmsContent("AlertMatchedDifferentVehicleWidget",
"BodyText").replaceAll("{custom:damage}", getDamageString()).replaceAll("{custom:vinlookupYear}", this.customAlertData?.vehicleInfo?.year).replaceAll("{custom:vinlookupMake}", this.customAlertData?.vehicleInfo?.make).replaceAll("{custom:vinlookupModel}",
this.customAlertData?.vehicleInfo?.model);
},
AlertNonServiceableZipHeader(){
return this.getCmsContent("AlertNonServiceableZipWidget", "HeadlineText").replaceAll("{custom:serviceZip}", this.serviceZipCode);
},
AlertNonServiceableZipBody(){
return this.getCmsContent("AlertNonServiceableZipWidget", "BodyText");
},
AlertPerfectMatchInsuranceNotVerifiedHeader() {
return this.getCmsContent("AlertPerfectMatchInsuranceNotVerified", "HeadlineText");
},
AlertPerfectMatchInsuranceNotVerifiedBody() {
return this.getCmsContent("AlertPerfectMatchInsuranceNotVerified", "BodyText").replaceAll("{custom:damage}", getDamageString())
},
AlertPerfectMatchInsuranceVerifiedHeader () {
return this.getCmsContent("AlertPerfectMatchInsuranceVerified", "HeadlineText");
},
AlertPerfectMatchInsuranceVerifiedBody () {
return this.getCmsContent("AlertPerfectMatchInsuranceVerified", "BodyText").replaceAll("{custom:damage}",
getIsWindshieldOnly())
},
isInsuranceVerified() {
return store.getters.payment.insuranceCoverage.isVerified || getFunnelCookie().HasDelayedClaimRegistration;
},
vinMask() {
if (this.vinPopulatedOnPageLoad) {
const lastSixChars = this.vin.substring(11, this.vin.length);
return `!X!X!X!X!X!X!X!X!X!X!X${lastSixChars}`;
}
else {
return 'XXXXXXXXXXXXXXXXX';
}
},
},
watch: {
vin() {
this.displayVinNotFoundAlert = false;
this.$refs.funnelFooter.updateButtonText(this.getCmsContent("FunnelFooterWidget", "ForwardButtonText"));
},
serviceZipCode() {
this.displayNonServiceableZipAlert = false;
},
},
components: {
funnelHeader,
funnelFooter,
vehicleBanner,
funnelSubHeader,
textboxQuestion,
alert,
vinInformation,
loadingModal,
Form,
},
};
</script>