396 lines
14 KiB
Vue
396 lines
14 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="VinNumber"
|
|
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="ServiceZIP"
|
|
v-model="zip"
|
|
inputId="zip"
|
|
mask="#####"
|
|
isRequired
|
|
disableAutoFill
|
|
validationRules="zip-required|zip-format"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div class="row my-2">
|
|
<div class="col">
|
|
<textboxQuestion
|
|
cmsWidgetName="EmailAddress"
|
|
v-model="email"
|
|
inputId="email"
|
|
isRequired
|
|
disableAutoFill
|
|
validationRules="email-address-required|email-address-format"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<alert
|
|
class="my-4"
|
|
:manualHeadline="PerfectMatchInsuranceVerifiedAlertHeader"
|
|
:manualCopy="PerfectMatchInsuranceVerifiedAlertBody"
|
|
v-model="customAlertData"
|
|
v-if="vinPopulatedOnPageLoad && isInsuranceVerified"
|
|
alertClass="alert-success"
|
|
/>
|
|
<alert
|
|
class="my-4"
|
|
:manualHeadline="MatchedDifferentVehicleAlertHeader"
|
|
:manualCopy="MatchedDifferentVehicleAlertBody"
|
|
v-model="customAlertData"
|
|
v-if="isCarIdDifferent && !vinNotFound && !perfectMatchNewVinAlert"
|
|
alertClass="alert-warning"
|
|
/>
|
|
<alert
|
|
class="my-4"
|
|
:manualHeadline="NoServiceZipHeader"
|
|
:manualCopy="NoServiceZipBody"
|
|
v-model="customAlertData"
|
|
v-if="noServiceZip"
|
|
alertClass="alert-danger"
|
|
/>
|
|
<alert
|
|
class="my-4"
|
|
v-model="customAlertData"
|
|
v-if="vinNotFound"
|
|
alertClass="alert-danger"
|
|
cmsWidgetName="VinNotFound"
|
|
/>
|
|
<alert
|
|
class="my-4"
|
|
:manualHeadline="PerfectMatchInsuranceNotVerifiedAlertHeader"
|
|
:manualCopy="PerfectMatchInsuranceNotVerifiedAlertBody"
|
|
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 {
|
|
isCarIdDifferent: false,
|
|
noServiceZip: false,
|
|
vinNotFound: false,
|
|
vin: this.getVinFromStore(),
|
|
zip: this.getZipFromStore(),
|
|
email: this.getEmailFromStore(),
|
|
customAlertData: {},
|
|
previouslyEnteredCarId: '',
|
|
invalidZip: '',
|
|
vinPopulatedOnPageLoad: this.getVinFromStore()?.length > 0,
|
|
isSelectedGlassAvailableForVehicle: true
|
|
};
|
|
},
|
|
mounted() {
|
|
this.attachCustomEvents();
|
|
},
|
|
watch: {
|
|
vin() {
|
|
this.vinNotFound = false;
|
|
this.$refs.funnelFooter.updateButtonText(this.getCmsContent("FunnelFooterWidget", "ForwardButtonText"));
|
|
},
|
|
zip() {
|
|
this.noServiceZip = false;
|
|
},
|
|
},
|
|
computed: {
|
|
MatchedDifferentVehicleAlertHeader(){
|
|
const text = this.getCmsContent("MatchedDifferentVehicle",
|
|
"HeadlineText").replaceAll("{custom:damage}", getDamageString());
|
|
|
|
return text;
|
|
},
|
|
MatchedDifferentVehicleAlertBody(){
|
|
const text = this.getCmsContent("MatchedDifferentVehicle",
|
|
"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);
|
|
|
|
return text;
|
|
},
|
|
NoServiceZipHeader(){
|
|
const text = this.getCmsContent("NoServiceZipWidget", "HeadlineText").replaceAll("{custom:serviceZip}", this.invalidZip);
|
|
return text;
|
|
},
|
|
NoServiceZipBody(){
|
|
return this.getCmsContent("NoServiceZipWidget", "BodyText");
|
|
},
|
|
PerfectMatchInsuranceNotVerifiedAlertHeader() {
|
|
return this.getCmsContent("PerfectMatchInsuranceNotVerifiedAlert", "HeadlineText");
|
|
},
|
|
PerfectMatchInsuranceNotVerifiedAlertBody() {
|
|
return this.getCmsContent("PerfectMatchInsuranceNotVerifiedAlert", "BodyText").replaceAll("{custom:damage}",
|
|
getDamageString())
|
|
},
|
|
PerfectMatchInsuranceVerifiedAlertHeader () {
|
|
return this.getCmsContent("PerfectMatchInsuranceVerifiedAlert", "HeadlineText");
|
|
},
|
|
PerfectMatchInsuranceVerifiedAlertBody () {
|
|
return this.getCmsContent("PerfectMatchInsuranceVerifiedAlert", "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';
|
|
}
|
|
},
|
|
},
|
|
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
|
|
);
|
|
});
|
|
},
|
|
updateIsCarIdDifferent(isVinPerfectMatch){
|
|
if (isVinPerfectMatch) {
|
|
this.isCarIdDifferent = false;
|
|
}
|
|
},
|
|
backButtonAction() {
|
|
if (this.$store.getters.vehicle.vin) {
|
|
this.$router.navigate(this.navigationScenarios.CLICKED_BACK_WITH_VIN, this.$route);
|
|
}
|
|
else {
|
|
this.$router.navigate(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 validateZipResponse = this.dispatchStoreAction(storeActions.VALIDATE_ZIP, {zip: this.zip});
|
|
const vehicleLookupResponse = this.dispatchStoreAction(storeActions.LOOKUP_VEHICLE_BY_VIN, { vin: this.vin });
|
|
|
|
// Settle promises and get results
|
|
const promiseResultMap = [
|
|
{
|
|
resultKey: "validateZipResponse",
|
|
promise: validateZipResponse,
|
|
},
|
|
{
|
|
resultKey: "vehicleLookupResponse",
|
|
promise: vehicleLookupResponse,
|
|
},
|
|
];
|
|
|
|
const resultMap = await settleAllPromises(promiseResultMap);
|
|
|
|
// If either lookup fails, remove the loader and stop processing the page.
|
|
if (!resultMap.vehicleLookupResponse || !resultMap.validateZipResponse.isServiceable) {
|
|
|
|
// If the vehicle result is undefined, the vin entered was invalid.
|
|
if(!resultMap.vehicleLookupResponse) {
|
|
this.vinNotFound = true;
|
|
}
|
|
|
|
// Check if Service Zip entered is serviceable, if not display an alert
|
|
if (!resultMap.validateZipResponse.isServiceable) {
|
|
this.setupUiForNonServiceableZip(this.zip);
|
|
}
|
|
|
|
// 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.$refs.funnelFooter.updateButtonText(`Continue with ${resultMap.vehicleLookupResponse.year} ${resultMap.vehicleLookupResponse.make} ${resultMap.vehicleLookupResponse.model}`);
|
|
this.isSelectedGlassAvailableForVehicle = await isGlassAvailableForCarId(resultMap.vehicleLookupResponse.carId);
|
|
|
|
this.noServiceZip = false;
|
|
this.isVinValid = true;
|
|
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.email, false);
|
|
await this.dispatchStoreAction(storeActions.SAVE_SERVICE_LOCATION, {
|
|
zipCode: this.zip,
|
|
state: resultMap.validateZipResponse.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.zip});
|
|
|
|
// 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.zip) {
|
|
await this.dispatchStoreAction(storeActions.SAVE_SERVICE_LOCATION, {
|
|
zipCode: this.zip,
|
|
state: zipValidationResponse.data.state
|
|
}, false);
|
|
}
|
|
|
|
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.email, false);
|
|
|
|
return await this.navigateForward();
|
|
}
|
|
|
|
// If the Service Zip is NOT serviceable then show an alert
|
|
this.setupUiForNonServiceableZip(this.zip);
|
|
|
|
return this.$refs.funnelFooter.removeLoader();
|
|
},
|
|
async navigateForward(){
|
|
if (this.isCarIdDifferent && !this.isSelectedGlassAvailableForVehicle) {
|
|
this.$router.navigate(this.navigationScenarios.SELECTED_VIN_HAS_MISMATCHED_GLASS, this.$route, {}, { [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true });
|
|
} else {
|
|
await this.navigateForwardWithSingleCarMatch();
|
|
}
|
|
},
|
|
setupUiForNonServiceableZip(zip) {
|
|
this.customAlertData.zip = zip;
|
|
this.invalidZip = zip;
|
|
this.noServiceZip = true;
|
|
},
|
|
},
|
|
components: {
|
|
Form,
|
|
funnelHeader,
|
|
vehicleBanner,
|
|
funnelSubHeader,
|
|
textboxQuestion,
|
|
alert,
|
|
funnelFooter,
|
|
vinInformation,
|
|
loadingModal,
|
|
},
|
|
};
|
|
</script>
|