DigitalConsumer.FixMyGlass/src/layouts/vehicle/vehicle.vue

656 lines
25 KiB
Vue

<template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
<div class="container vehicle">
<div class="row">
<div class="col-12 col-md-10 col-lg-8 col-xl-7">
<funnelSubHeader
cmsWidgetName="FunnelSubHeaderWidget"
class="siteSubHeader"
:alignLeft="true" />
<vehicleQuestion
ref="vehicleYearQuestion"
v-model="selectedYear"
:isDisabled="isYearDisabled"
:options="yearOptions"
cmsWidgetName="VehicleYearQuestion"
validationRules="year-required"
placeHolderText="Select year"
customDropdownId="yearQuestionField" />
<vehicleQuestion
ref="vehicleMakeQuestion"
v-model="selectedMake"
:isDisabled="isMakeDisabled"
:options="makeOptions"
cmsWidgetName="VehicleMakeQuestion"
validationRules="make-required"
placeHolderText="Select make"
customDropdownId="makeQuestionField" />
<vehicleQuestion
ref="vehicleModelQuestion"
v-model="selectedModel"
cmsWidgetName="VehicleModelQuestion"
:isDisabled="isModelDisabled"
:options="modelOptions"
validationRules="model-required"
placeHolderText="Select model"
customDropdownId="modelQuestionField" />
<vehicleQuestion
ref="vehicleStyleQuestion"
v-model="selectedStyle"
cmsWidgetName="VehicleStyleQuestion"
:isDisabled="isStyleDisabled"
:options="styleOptions"
validationRules="style-required"
placeHolderText="Select style"
customDropdownId="styleQuestionField" />
<div v-if="isBigTruckAndCanService">
<div class="separator-line"></div>
<textboxQuestion
class="mb-4"
cmsWidgetName="ServiceZipQuestionWidget"
v-model="serviceZipCode"
inputId="serviceZipCode"
mask="#####"
isRequired
validationRules="zip-required|zip-format" />
</div>
<alert
ref="AlertNoService"
v-if="displayNoServiceAlert"
class="mt-4 alert"
cmsWidgetName="AlertNoServiceWidget"
alertClass="alert-danger"
:isDismissible="false" />
<navbar
cmsWidgetName="FunnelFooterWidget"
@ForwardClicked="forwardButtonAction"
:isForwardActionDisabled="
!meta.valid || !allDataRetrieved || displayNoServiceAlert
"
ref="navbar" />
</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 alert from "@/ux-components/alert/alert";
import vehicleQuestion from "@/layouts/vehicle/vehicle-question/vehicle-question";
import textboxQuestion from "@/digital-components/textbox-question/textbox-question";
// Supporting files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { experimentUniverses } from "@/constants/experiments";
import {
getSessionKeyValue,
getUserIdValue,
getDeviceIdValue,
} from "@/helpers/heritage-integration/cookie-helper";
import { settleAllPromises } from "@/helpers/layout-helper";
import { Form, defineRule } from "vee-validate";
import { required, regex } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages";
import store from "@/store";
import { storeActions } from "@/constants/store-actions.js";
import baseMixin from "@/mixins/base-mixin.js";
import { getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
import { applicationConfig } from "../../constants/application-config";
import { queryStrings } from "@/constants/query-strings";
import { getQuerystringParameter } from "@/helpers/querystring-helper";
import { showFmgLoadingModal } from "@/helpers/loading-modal-helper";
import { debugLog } from "@/helpers/debug-log-helper";
//define validation rules
defineRule("year-required", required(errorMessages.YEAR_REQUIRED));
defineRule("make-required", required(errorMessages.MAKE_REQUIRED));
defineRule("model-required", required(errorMessages.MODEL_REQUIRED));
defineRule("style-required", required(errorMessages.STYLE_REQUIRED));
defineRule("zip-required", required(errorMessages.ZIP_REQUIRED));
defineRule("zip-format", regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.ZIP_FORMAT));
export default {
name: "vehicle",
data() {
return {
selectedYear: this.selectedYearfromStore(),
selectedMake: this.selectedMakefromStore(),
selectedModel: this.selectedModelfromStore(),
selectedStyle: this.selectedStylefromStore(),
carId: this.getCarIdfromStore(),
category: this.getCategoryfromStore(),
isBigTruck: this.getIsBigTruckfromStore(),
imageUrl: this.getImagefromStore(),
imageVifNumber: this.getImageVifNumberfromStore(),
imageVifColor: this.getImageVifColorfromStore(),
serviceZipCode: this.getZipFromStore(),
yearOptions: [],
makeOptions: [],
modelOptions: [],
styleOptions: [],
displayNoServiceAlert: false,
canSafeliteService: this.canSafeliteServiceFromStore(),
};
},
async beforeRouteEnter(to, from, next) {
debugLog("------------- vehicle.vue applicationConfig start -------------");
debugLog("", applicationConfig);
debugLog("------------- vehicle.vue applicationConfig end -------------");
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.name);
const experimentForLogging = store.getters.applicationUser.experiments.find(
(e) => e.universeName === experimentUniverses.CONCEPT_FUNNEL
);
const yearQuestionInitialDataPromise = baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.GET_VEHICLE_YEARS,
{},
"vehicle"
);
var makeQuestionInitialDataPromise = null;
var modelQuestionInitialDataPromise = null;
var styleQuestionInitialDataPromise = null;
const year =
store.getters.externalParameterVehicle.year || store.getters.order.vehicle.year;
const make =
store.getters.externalParameterVehicle.make || store.getters.order.vehicle.make;
const model =
store.getters.externalParameterVehicle.model || store.getters.order.vehicle.model;
const style =
store.getters.externalParameterVehicle.style || store.getters.order.vehicle.style;
if (make && model && style) {
makeQuestionInitialDataPromise = baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.GET_VEHICLE_MAKES,
{
year: year,
},
"vehicle"
);
modelQuestionInitialDataPromise = baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.GET_VEHICLE_MODELS,
{
year: year,
make: make,
},
"vehicle"
);
styleQuestionInitialDataPromise = baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.GET_VEHICLE_STYLES,
{
year: year,
make: make,
model: model,
},
"vehicle"
);
}
// If the concept funnel experiment is found, as it should be when coming from safelite.com, then log the experiment exposure.
if (experimentForLogging !== undefined) {
// Log experiment exposure
baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.LOG_EXPERIMENT_EXPOSURE_AND_UPDATE_STORE,
{
userId: getUserIdValue(),
deviceId: getDeviceIdValue(),
sessionKey: getSessionKeyValue(),
pageName: to.name,
experiment: experimentForLogging,
},
"vehicle",
false
);
}
// Settle promises and get results
const promiseResultMap = [
{
resultKey: "cmsContent",
promise: cmsContentPromise,
},
{
resultKey: "yearQuestionInitialData",
promise: yearQuestionInitialDataPromise,
},
{
resultKey: "makeQuestionInitialData",
promise: makeQuestionInitialDataPromise,
},
{
resultKey: "modelQuestionInitialData",
promise: modelQuestionInitialDataPromise,
},
{
resultKey: "styleQuestionInitialData",
promise: styleQuestionInitialDataPromise,
},
];
let resultMap = await settleAllPromises(promiseResultMap);
//ExternalParameter widget have different case style value so need to parse with NextGen
if (store.getters.externalParameterState?.isExternalParameter) {
if (
makeQuestionInitialDataPromise &&
modelQuestionInitialDataPromise &&
styleQuestionInitialDataPromise
) {
const matchingMake = resultMap.makeQuestionInitialData?.filter(
(item) =>
item.toLowerCase() ===
store.getters.externalParameterVehicle.make.toLowerCase()
);
const matchingModel = resultMap.modelQuestionInitialData?.filter(
(item) =>
item.toLowerCase() ===
store.getters.externalParameterVehicle.model.toLowerCase()
);
const matchingStyle = resultMap.styleQuestionInitialData?.filter(
(item) =>
item.toLowerCase() ===
store.getters.externalParameterVehicle.style.toLowerCase()
);
baseMixin.methods.dispatchStoreAction(
storeActions.UPDATE_EXTERNAL_PARAMETER_MMS,
{
externalParameterMake: matchingMake[0],
externalParameterModel: matchingModel[0],
externalParameterStyle: matchingStyle[0],
},
false
);
}
}
// Call the "next" function to complete the transition to this page.
next(async (vm) => {
vm.setCmsContent(resultMap.cmsContent);
vm.initializeYearComponent(resultMap.yearQuestionInitialData);
if (
makeQuestionInitialDataPromise &&
modelQuestionInitialDataPromise &&
styleQuestionInitialDataPromise
)
vm.initializeMMSComponent(
resultMap.makeQuestionInitialData,
resultMap.modelQuestionInitialData,
resultMap.styleQuestionInitialData
);
if (
store.getters.externalParameterState?.isExternalParameter &&
store.getters.externalParameterVehicle.year &&
store.getters.externalParameterVehicle.make &&
store.getters.externalParameterVehicle.model
) {
await vm.getVehicleDetails();
if (!vm.displayNoServiceAlert) {
const isValid = await baseMixin.methods.isFormValid(vm.$refs.theForm);
if (isValid) {
vm.forwardButtonAction();
} else {
baseMixin.methods.ResetExternalParamsAndHideModal();
}
} else {
baseMixin.methods.ResetExternalParamsAndHideModal();
}
} else {
showFmgLoadingModal(false);
}
});
},
watch: {
serviceZipCode(newValue, oldValue) {
this.resetAlert();
},
async selectedYear(year) {
this.resetAlert();
this.makeOptions = [];
this.selectedMake = null;
this.selectedModel = null;
this.selectedStyle = null;
this.isBigTruck = false;
this.canSafeliteService = false;
this.carId = null;
if (year) {
const result = await this.getMakeOptions(year);
if (this.selectedYear == year) {
this.makeOptions = result?.data;
if (this.selectedYearfromStore() !== year) {
this.selectedMake = null;
this.selectedModel = null;
this.selectedStyle = null;
this.carId = null;
}
}
}
},
async selectedMake(make) {
this.resetAlert();
this.modelOptions = [];
this.selectedModel = null;
this.selectedStyle = null;
this.isBigTruck = null;
this.carId = null;
if (make) {
const result = await this.getModelOptions(this.selectedYear, make);
if (this.selectedMake == make) {
this.modelOptions = result?.data;
if (this.modelOptions.length === 1) this.selectedModel = this.modelOptions[0];
else {
this.selectedModel = null;
this.selectedStyle = null;
this.carId = null;
}
}
}
},
async selectedModel(model) {
this.resetAlert();
this.styleOptions = [];
this.selectedStyle = null;
this.isBigTruck = false;
this.carId = null;
if (model) {
const result = await this.getStyleOptions(
this.selectedYear,
this.selectedMake,
model
);
if (this.selectedModel == model) {
this.styleOptions = result?.data;
if (this.styleOptions.length === 1) {
const sameStyle = this.selectedStyle === this.styleOptions[0];
this.selectedStyle = this.styleOptions[0];
if (sameStyle) {
this.getVehicleDetails();
}
} else {
this.selectedStyle = null;
this.carId = null;
}
}
}
},
async selectedStyle(style) {
this.resetAlert();
if (style) {
this.getVehicleDetails();
} else {
this.imageUrl = null;
this.carId = null;
}
},
},
computed: {
isYearDisabled() {
return this.isDisabled(this.yearOptions);
},
isMakeDisabled() {
return this.isDisabled(this.makeOptions);
},
isModelDisabled() {
return this.isDisabled(this.modelOptions);
},
isStyleDisabled() {
return this.isDisabled(this.styleOptions);
},
unmatchedVehicleIcon() {
if (this.imageUrl == null && this.carId !== null) return true;
else return false;
},
displayGeneric() {
if (!this.selectedStyle) return true;
else if (this.carId == null) return true;
else if (this.imageUrl == null && this.carId !== null) return false;
else return true;
},
allDataRetrieved() {
return (
!!this.selectedYear &&
!!this.selectedMake &&
!!this.selectedModel &&
!!this.selectedStyle &&
!!this.carId
);
},
isBigTruckAndCanService() {
return this.isBigTruck && this.canSafeliteService;
},
},
methods: {
getZipFromStore() {
return (
store.getters?.externalParameterServiceZip?.zipCode ??
store.getters.order?.serviceLocation?.zipCode
);
},
isDisabled(options) {
if (
!options.length ||
store.getters.payment?.insuranceCoverage?.isVerified ||
(store.getters.order.payment?.isInsurance &&
getFunnelCookie()?.HasDelayedClaimRegistration)
) {
return true;
} else {
return false;
}
},
getVehicle(year, make, model, style) {
return this.dispatchStoreActionWithLogging(
this.storeActions.GET_VEHICLE,
{
year: year,
make: make,
model: model,
style: style,
},
"vehicle"
);
},
arePagePrerequisitesValid() {
return true;
},
async getVehicleDetails() {
this.carId = null;
const result = await this.getVehicle(
this.selectedYear,
this.selectedMake,
this.selectedModel,
this.selectedStyle
);
this.carId = result?.data.carId;
this.category = result?.data.category;
this.imageUrl = result?.data.imageUrl;
this.imageVifNumber = result?.data.imageVifNumber;
this.imageVifColor = result?.data.imageVifColor;
this.displayNoServiceAlert = !result?.data.canSafeliteService;
this.canSafeliteService = result?.data.canSafeliteService;
this.vehicleSubType = result?.data.vehicleSubType;
this.vehicleSpecialClass = result?.data.vehicleSpecialClass;
this.isBigTruck = result?.data.isBigTruck;
},
resetAlert() {
this.displayNoServiceAlert = false;
},
async forwardButtonAction() {
await this.dispatchStoreAction(storeActions.RESET_SUBMITTED_STATE);
let zipCodeData;
if (this.isBigTruck) {
zipCodeData = await this.getZipCodeData(this.serviceZipCode);
// check the location endpoint to verify this zip can service a heavy truck
var closestShops = await this.dispatchStoreActionWithLogging(
storeActions.GET_CLOSEST_APPLICABLE_SHOPS,
{ zip: this.serviceZipCode, carId: this.carId },
"vehicle"
);
if (
!closestShops?.data?.inShopProviders ||
closestShops.data.inShopProviders.length === 0
) {
this.displayNoServiceAlert = true;
if (store.getters.externalParameterState?.isExternalParameter) {
return baseMixin.methods.ResetExternalParamsAndHideModal();
}
return this.$refs.navbar.removeLoader();
}
}
if (zipCodeData) {
await this.dispatchStoreAction(
storeActions.SAVE_SERVICE_ZIP_CODE_INFO,
{
zipCode: this.serviceZipCode,
state: zipCodeData.state,
zipCodeCtu: zipCodeData.zipCodeCtu,
},
false
);
}
this.dispatchStoreAction(
storeActions.SAVE_VEHICLE,
{
year: this.selectedYear,
make: this.selectedMake,
model: this.selectedModel,
style: this.selectedStyle,
vehicleSubType: this.vehicleSubType,
vehicleSpecialClass: this.vehicleSpecialClass,
isBigTruck: this.isBigTruck,
carId: this.carId,
category: this.category,
canSafeliteService: this.canSafeliteService,
imageUrl: this.imageUrl,
imageVifNumber: this.imageVifNumber,
imageVifColor: this.imageVifColor,
},
false
);
this.$router.navigateWithSaving(
this.navigationScenarios.CLICKED_FORWARD,
this.pageName
);
},
initializeYearComponent(initialData) {
this.yearOptions = initialData;
},
initializeMMSComponent(makeOptions, modelOptions, styleOptions) {
this.makeOptions = makeOptions;
this.modelOptions = modelOptions;
this.styleOptions = styleOptions;
},
selectedYearfromStore() {
return (
store.getters.externalParameterVehicle.year ??
store.getters.vehicle.year?.toString()
);
},
selectedMakefromStore() {
return store.getters.externalParameterVehicle.make ?? store.getters.vehicle.make;
},
selectedModelfromStore() {
return store.getters.externalParameterVehicle.model ?? store.getters.vehicle.model;
},
selectedStylefromStore() {
return store.getters.externalParameterVehicle.style ?? store.getters.vehicle.style;
},
canSafeliteServiceFromStore() {
return store.getters.vehicle.canSafeliteService ?? false;
},
async getMakeOptions(year) {
return await baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.GET_VEHICLE_MAKES,
{
year: year,
},
"vehicle"
);
},
async getModelOptions(year, make) {
return await baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.GET_VEHICLE_MODELS,
{
year: year,
make: make,
},
"vehicle"
);
},
async getStyleOptions(year, make, model) {
return await baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.GET_VEHICLE_STYLES,
{
year: year,
make: make,
model: model,
},
"vehicle"
);
},
getImagefromStore() {
return store.getters.vehicle.imageUrl;
},
getCarIdfromStore() {
return store.getters.vehicle.carId;
},
getCategoryfromStore() {
return store.getters.vehicle.category;
},
getImageVifNumberfromStore() {
return store.getters.vehicle.imageVifNumber;
},
getImageVifColorfromStore() {
return store.getters.vehicle.imageVifColor;
},
getIsBigTruckfromStore() {
return store.getters.vehicle.isBigTruck;
},
},
components: {
funnelHeader,
navbar,
funnelSubHeader,
Form,
alert,
vehicleQuestion,
textboxQuestion,
},
};
</script>
<style lang="scss">
.vehicle-footer {
margin-bottom: 0;
}
.siteSubHeader {
margin: 0;
}
.separator-line {
grid-area: 2/1/2/8;
border-top: 1px solid $gray-500;
margin: 0.5rem 0;
}
</style>