DigitalConsumer.FixMyGlass/src/layouts/quote/quote.vue

918 lines
39 KiB
Vue

<template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
<loadingModal ref="loadingModal" />
<div
class="container-fluid page-container-grouped-styles quote-page"
:class="showSaveProgressPopup ? 'show-save-progress-popup' : ''">
<div class="row justify-content-center funnel-header-wrapper">
<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 class="mb-5" cmsWidgetName="FunnelSubHeaderWidget" />
<cashOrInsuranceQuestion
ref="cashOrInsurance"
cmsWidgetName="CashOrInsuranceQuestionWidget"
v-model="isInsuranceSelected"
groupName="CashOrInsuranceQuestion" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-12 mb-5" :class="getColCount()">
<servicePackageQuestion
ref="servicePackage"
cashCmsWidgetName="CashServicePackageQuestionWidget"
insuranceCmsWidgetName="InsuranceServicePackageQuestionWidget"
groupName="ServicePackageQuestion"
:availableLineItems="availableLineItems"
:isInsuranceSelected="isInsuranceSelected"
:isRecalibrationOnOrder="isRecalibrationOnOrder"
:shouldHideRecalibration="shouldHideRecalibration"
@vapsItemsSelected="vapsItemsSelectedAction"
@servicePackageDiscountSelected="servicePackageDiscountSelectedAction"
@currentNumberOfPackages="currentNumberOfPackagesAction"
:servicePackage="servicePackage"
:activePromos="lineItems.promos"
v-on="{ 'buttonEvent.openModal': openModalAction }"
validationRules="option-required"
isRequired />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-12" :class="getColCount()">
<!-- If Cash AND vehicle does not require recal OR if Cash AND State requires showing recal, then show Afterpay banner -->
<afterpayModalBanner
v-if="showAfterpayBanner"
cmsWidgetName="AfterpayModalWidget"
:afterpayExtendedPayOptionThreshold="afterpayExtendedPayOptionThreshold"
:isInsuranceSelected="isInsuranceSelected"
:lineItems="lineItems" />
<!-- If vehicle requires recal AND we are hiding recal pricing info, then show recal disclaimer banner. -->
<recalDisclaimer
class="mb-0"
cmsWidgetName="RecalDisclaimerWidget"
v-if="showRecalDisclaimer"
v-on="{ textLinkClicked: openModalAction }"
alertClass="alert-info" />
<promoModalQuestion
class="mt-6 d-flex justify-content-center"
v-model="lineItems"
:addableVaps="addableVaps"
pageName="quote"
:taxPromos="false"
:useDefaultCashParentAccount="true"
modalWidgetName="PromoModalWidget" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<contentGroupModal ref="RainDefenseModal" cmsWidgetName="RainDefenseModal" />
<contentGroupModal ref="FrontWiperModal" cmsWidgetName="FrontWiperModal" />
<contentGroupModal ref="RearWiperModal" cmsWidgetName="RearWiperModal" />
<contentGroupModal ref="RecalModal" cmsWidgetName="RecalModal" isRecal />
<navbar
cmsWidgetName="FunnelFooterWidget"
:isForwardActionDisabled="!meta.valid"
:isBackButtonHidden="shouldHideBackButton"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" />
<saveProgressModalQuestion
modalWidgetName="SaveProgressModalWidget"
modalName="SaveProgressModal"
v-if="showSaveProgressModal"
pageName="quote" />
<textBlock
cmsWidgetName="quoteDisclaimer"
justifyText="center"
typeStyle="caption"
class="mb-5 quote-disclaimer" />
</div>
</div>
<div class="row justify-content-center save-progress-popup">
<div class="col-md-6 col-xl-4">
<saveProgressPopupQuestion
modalWidgetName="SaveProgressPopupWidget"
modalName="SaveProgressPopup"
pageName="quote"
v-if="showSaveProgressPopup"
:showSaveProgressPopup="showSaveProgressPopup"
@save-progress-saved="updateSaveProgressAsSaved"
@close-save-progress-popup="closeSaveProgressPopup" />
</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 cashOrInsuranceQuestion from "./cash-or-insurance-question/cash-or-insurance-question";
import servicePackageQuestion from "./service-package-question/service-package-question";
import textBlock from "@/digital-components/text-block/text-block";
import contentGroupModal from "@/fmg-components/content-group-modal/content-group-modal";
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
import afterpayModalBanner from "@/layouts/quote/afterpay-modal-banner/afterpay-modal-banner";
import recalDisclaimer from "@/layouts/quote/recal-disclaimer/recal-disclaimer.vue";
import saveProgressModalQuestion from "@/fmg-components/save-progress-modal-question/save-progress-modal-question";
import saveProgressPopupQuestion from "@/fmg-components/save-progress-popup-question/save-progress-popup-question";
// Supporting files
import baseMixin from "@/mixins/base-mixin.js";
import experimentMixin from "@/mixins/experiment-mixin.js";
import vehicleQuestionsMixin from "../../mixins/vehicle-questions-mixin";
import { settleAllPromises } from "@/helpers/layout-helper";
import { storeActions } from "@/constants/store-actions";
import { deepClone } from "@/helpers/object-helper";
import store from "@/store";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { experimentSettings, experimentUniverses } from "@/constants/experiments";
import { required } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages";
import { Form, defineRule } from "vee-validate";
import { applicationConfig } from "@/constants/application-config";
import { payWithInsuranceStates } from "@/constants/insurance";
import {
revalidatePromosAndValidateQueryStringPromo,
buildToastMessagesFromRevalidateOrValidatePromoResponse,
createPromoSuccessAlert,
} from "@/helpers/promotions-helper";
import { queryStrings } from "@/constants/query-strings";
import { getQuerystringParameter } from "@/helpers/querystring-helper";
import promoModalQuestion from "@/fmg-components/promo-modal-question/promo-modal-question";
import { partTypeStrings } from "@/constants/part-type-strings";
import {
containsLineItemWithPartType,
findLineItemsWithPartType,
} from "@/helpers/service-package-helper";
import { nextTick } from "vue";
import { packageNames, externalParamPackageLabels } from "@/constants/package-names";
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
import { containsRecalParts } from "@/helpers/recal-helper";
import { externalParameterStatus } from "@/constants/external-parameters";
import { saveQuote } from "@/helpers/heritage-integration/order-helper.js";
import { getBoolFromString } from "@/helpers/boolean-helper";
import {
getSessionKeyValue,
getUserIdValue,
getDeviceIdValue,
} from "@/helpers/heritage-integration/cookie-helper";
import { showFmgLoadingModal } from "@/helpers/loading-modal-helper";
import { routeData } from "@/router/constants/routes";
import { consumeQueryFromStash } from "@/router/methods/helpers/querystring-stash";
import { storeMutations } from "@/constants/store-mutations";
import { debugLog } from "@/helpers/debug-log-helper";
import { savePageData } from "@/router/methods/helpers/save-page-data";
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
const INSURANCE_TAB_TO_DISPLAY_THRESHOLD_DEFAULT = 300;
const ACCEPTED_QUOTE_TYPES = {
CASH: "cash",
INSURANCE: "insurance",
};
export default {
name: "quote",
async beforeRouteEnter(to, from, next) {
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.name);
const wipersPromise = baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.GET_WIPERS,
{
serviceZipCode: store.getters.order.serviceLocation.zipCode,
carId: store.getters.vehicle.carId,
},
"quote"
);
const rainDefensePromise = baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.GET_RAIN_DEFENSE,
null,
"quote"
);
const supportingItemsPromise = baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.GET_SUPPORTING_ITEMS,
null,
"quote"
);
const shouldExposeSkipQuote = store.getters.isRecalibrationOnOrder;
const skipQuoteExperiment = store.getters.applicationUser.experiments.find(
(e) => e.universeName === experimentUniverses.IGQ_SkipQuote
);
const hasExposedSkipQuote = skipQuoteExperiment?.isExposed;
const shouldSendExposeRequest =
shouldExposeSkipQuote && !hasExposedSkipQuote && skipQuoteExperiment;
if (shouldSendExposeRequest) {
// Don't await -- response is not needed so can fire fully asynchronously.
baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.LOG_EXPERIMENT_EXPOSURE_AND_UPDATE_STORE,
{
userId: getUserIdValue(),
deviceId: getDeviceIdValue(),
sessionKey: getSessionKeyValue(),
pageName: to.name,
experiment: skipQuoteExperiment,
},
"quote",
false
);
}
// AfterpayBreakoutDisplay Custom Exposure
const IGQExperiment = store.getters.applicationUser.experiments.find(
(e) => e.universeName === experimentUniverses.RECAL_PRICE_REMOVAL
);
const hasExposedIGQExperiment = IGQExperiment?.isExposed;
const afterpayBreakoutDisplayExperiment = store.getters.applicationUser.experiments.find(
(e) => e.universeName === experimentUniverses.AFTERPAY_BREAKOUT_DISPLAY
);
const hasExposedAfterpayBreakoutDisplay = afterpayBreakoutDisplayExperiment?.isExposed;
const shouldSendAfterpayBreakoutDisplayExposeRequest =
afterpayBreakoutDisplayExperiment &&
!hasExposedAfterpayBreakoutDisplay &&
!hasExposedIGQExperiment;
if (shouldSendAfterpayBreakoutDisplayExposeRequest) {
baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.LOG_EXPERIMENT_EXPOSURE_AND_UPDATE_STORE,
{
userId: getUserIdValue(),
deviceId: getDeviceIdValue(),
sessionKey: getSessionKeyValue(),
pageName: to.name,
experiment: afterpayBreakoutDisplayExperiment,
},
"quote",
false
);
}
const promiseResultMap = [
{
resultKey: "cmsContent",
promise: cmsContentPromise,
},
{
resultKey: "wipers",
promise: wipersPromise,
},
{
resultKey: "rainDefense",
promise: rainDefensePromise,
},
{
resultKey: "supportingItems",
promise: supportingItemsPromise,
},
];
const resultMap = await settleAllPromises(promiseResultMap);
const emailFromStore = store.getters.order.customer.emailAddress;
const isEmailInStoreOnPageLoad = emailFromStore?.length > 0;
const isPopupSkipped =
store.getters.pageData(routeData.QUOTE.name)?.saveProgressPopupSkipped === true;
const showSaveProgressPopup = !(isEmailInStoreOnPageLoad || isPopupSkipped);
const showSaveProgressModal = isEmailInStoreOnPageLoad ? false : true;
const shouldSkipToInsurance =
getBoolFromString(
experimentMixin.methods.getSettingValue(experimentSettings.SKIP_TO_INSURANCE)
) &&
shouldExposeSkipQuote &&
!hasExposedSkipQuote;
const lineItems = deepClone(store.getters.order.lineItems);
lineItems.vaps = lineItems.vaps ?? [];
const nullSafeGlassParts = lineItems.glassParts ?? [];
const servicePackageDiscountSettingValue = experimentMixin.methods.getSettingValue(
experimentSettings.SERVICE_PACKAGE_DISCOUNT
);
const isServicePackageDiscount = servicePackageDiscountSettingValue === "True";
//Service package cash discount api call when experiment is active
var servicePackageDiscountPart = [];
if (isServicePackageDiscount) {
const servicePackageDiscountPartResponse =
await baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.GET_SERVICE_PACKAGE_DISCOUNT_PART,
null,
"quote"
);
if (servicePackageDiscountPartResponse.data) {
servicePackageDiscountPart.push(servicePackageDiscountPartResponse.data);
}
}
const availableLineItems = [
resultMap.rainDefense,
...resultMap.supportingItems,
...resultMap.wipers,
...nullSafeGlassParts,
...servicePackageDiscountPart,
];
// When calling pricing from the quote page, always use the cash parent account but save the existing one in case it's unverified insurance navigating backwards
var holdParentAccountNumber = store.getters.order.payment?.parentAccountNumber;
var holdBillToAccountNumber = store.getters.order.payment?.billToAccountNumber;
baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_PARENT_ACCOUNT_NUMBER,
applicationConfig.CASH_PARENT_ACCOUNT_NUMBER,
false
);
baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_BILL_TO_ACCOUNT_NUMBER,
applicationConfig.CASH_DEFAULT_BILL_TO_ACCOUNT_NUMBER,
false
);
const pricingResults = await baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA,
{
availableLineItems: availableLineItems,
},
"quote",
false
);
// This will remove any servicePackageDiscount item from lineItems.supportingItems
baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_SUPPORTING_ITEMS,
resultMap.supportingItems,
false
);
lineItems.supportingItems = resultMap.supportingItems;
const addableVaps = [...resultMap.wipers, resultMap.rainDefense];
// Promo logic
// Populate the previous state of promos for toast message usage in "next()"
const oldActivePromos = store.getters.lineItems.promos?.slice(0);
const oldInactivePromos = store.getters.order.payment.inactivePromos?.slice(0);
const promoCodeFromQueryString = consumeQueryFromStash(queryStrings.PROMO);
const { validatePromoResponse, revalidatePromoResponse } =
await revalidatePromosAndValidateQueryStringPromo(
promoCodeFromQueryString,
pricingResults,
"quote",
true
);
// Sync local promos with any new promos added by validate/revalidate
lineItems.promos = store.getters.lineItems.promos ?? [];
// End of promo logic
// Call the "next" function to complete the transition to this page.
next(async (vm) => {
vm.setCmsContent(resultMap.cmsContent);
vm.showSaveProgressPopup = showSaveProgressPopup;
vm.showSaveProgressModal = showSaveProgressModal;
vm.addableVaps = addableVaps;
vm.lineItems = lineItems;
vm.availableLineItems = pricingResults;
vm.holdParentAccountNumber = holdParentAccountNumber;
vm.holdBillToAccountNumber = holdBillToAccountNumber;
vm.skipToInsurance = shouldSkipToInsurance;
if (revalidatePromoResponse) {
const revalidateAlerts = buildToastMessagesFromRevalidateOrValidatePromoResponse(
revalidatePromoResponse,
oldActivePromos,
oldInactivePromos
);
revalidateAlerts.forEach((alert) => {
vm.$refs.funnelHeader.pushGlobalAlert(alert, alert.shouldAutoFade);
});
}
if (validatePromoResponse) {
const validateAlerts =
buildToastMessagesFromRevalidateOrValidatePromoResponse(validatePromoResponse);
vm.$refs.funnelHeader.pushGlobalAlert(
validateAlerts[0],
validateAlerts[0].shouldAutoFade
);
}
// This is a safeguard to prevent verified insurance from seeing quote page again.
const payment = store.getters.payment;
if (payment?.isInsurance && payment?.insuranceCoverage?.isVerified) {
navigateToHeritageFunnel({ shouldSaveSession: true, pageNameToLog: "quote" });
}
const getIsInsuranceSelectedValue = (availableLineItems, insuranceThreshold) => {
// Override auto-select if specified
const quoteTypeParam = to.query?.offer?.toLowerCase();
if (quoteTypeParam === ACCEPTED_QUOTE_TYPES.CASH) {
return false;
} else if (quoteTypeParam === ACCEPTED_QUOTE_TYPES.INSURANCE) {
return true;
}
const serviceLocationState = store.getters.order.serviceLocation.state;
// usually true when returning from heritage but can be false when returning from heritage on a save quote
const isInsuranceFromQueryString = to.query?.isInsurance || to.query?.isinsurance;
const defaultIsInsuranceSelectedValue = store.getters.order.payment.isInsurance;
const hideRecalCost =
experimentMixin.methods
.getSettingValue(experimentSettings.RECAL_PRICE_REMOVE)
?.toLowerCase() === "true";
//Default to Insurance Tab if user Service zip is from certain States
if (
serviceLocationState != null &&
payWithInsuranceStates.find((item) => item === serviceLocationState)
) {
return true;
}
if (defaultIsInsuranceSelectedValue != null) {
return defaultIsInsuranceSelectedValue;
}
if (availableLineItems) {
const lineItemsForCalculatingPrice = hideRecalCost
? baseMixin.methods.filterOutFees(
baseMixin.methods.filterOutRecalibration(availableLineItems) // Strip out recal before filtering out fees
)
: baseMixin.methods.filterOutFees(availableLineItems);
const lineItemsNoDiscount =
baseMixin.methods.filterOutServicePackageDiscountPart(
lineItemsForCalculatingPrice
);
if (isInsuranceFromQueryString != null) {
if (isInsuranceFromQueryString.toLowerCase() === "true") {
return true;
}
}
const tierOnePrice =
baseMixin.methods.getTierOnePackagePrice(lineItemsNoDiscount);
// prettier-ignore
{
debugLog("quote tierOnePrice comparison: " + tierOnePrice + " > " + insuranceThreshold + "=" + (tierOnePrice > insuranceThreshold));
}
// compare base price vs arbitrary threshold (representing insurance price)
return tierOnePrice > insuranceThreshold;
}
return null;
};
let thresholdToUse = INSURANCE_TAB_TO_DISPLAY_THRESHOLD_DEFAULT;
let internalThreshold = experimentMixin.methods.getSettingValue(
experimentSettings.INSURANCE_TAB_TO_DISPLAY_THRESHOLD_INTERNAL
);
let externalThreshold = experimentMixin.methods.getSettingValue(
experimentSettings.INSURANCE_TAB_TO_DISPLAY_THRESHOLD_EXTERNAL
);
const isExternalParameter = store.getters.externalParameterState?.isExternalParameter;
// prettier-ignore
{
debugLog("--- quote.vue thresholds start ---");
debugLog(" isExternalParameter: ", isExternalParameter);
debugLog(" internalThreshold: ", internalThreshold);
debugLog(" externalThreshold: ", externalThreshold);
debugLog("--- quote.vue thresholds end ---");
}
if (isExternalParameter === externalParameterStatus.NOT_SET) {
// prettier-ignore
{
debugLog("quote.vue tab select EXTERNAL not set");
}
// there are no active or inactive external parameters; use internal threshold
if (internalThreshold) thresholdToUse = internalThreshold;
vm.isInsuranceSelected = getIsInsuranceSelectedValue(
vm.availableLineItems,
thresholdToUse
);
baseMixin.methods.ResetExternalParamsAndHideModal();
} else {
// user came from an external source, however, the externalParms may have been reset on service-zip, property-questions, etc...
if (getBoolFromString(store.getters.externalParameterQuote?.isInsurance)) {
// prettier-ignore
{
debugLog("quote.vue tab select EXTERNAL ISINSURANCE");
}
const insuranceSelection = true;
const servicePackage = store.getters.externalParameterQuote.servicePackage;
if (
servicePackage != null &&
isExternalParameter === externalParameterStatus.ACTIVE
) {
await vm.skip(insuranceSelection, servicePackage);
}
baseMixin.methods.ResetExternalParamsAndHideModal();
} else {
if (externalThreshold) thresholdToUse = externalThreshold;
// prettier-ignore
{
debugLog("quote.vue tab select NOT EXTERNAL");
}
vm.isInsuranceSelected = getIsInsuranceSelectedValue(
vm.availableLineItems,
thresholdToUse
);
baseMixin.methods.ResetExternalParamsAndHideModal();
}
}
if (vm.skipToInsurance && !vm.showSaveProgressPopup) {
// skip ahead only if no email popup.
await vm.skip(true, externalParamPackageLabels.GLASS_ONLY);
}
});
},
data() {
return {
isInsuranceSelected: null,
availableLineItems: null,
lineItems: [],
addableVaps: [],
servicePackage: null,
holdParentAccountNumber: null,
holdBillToAccountNumber: null,
showSaveProgressPopup: null,
showSaveProgressModal: null,
isSaveProgressComplete: null,
packageNumber: null,
skipToInsurance: null,
};
},
async mounted() {
this.attachCustomEventsForAnalytics();
await this.$nextTick();
this.$refs?.servicePackage?.savePackageInfoToPageData?.();
},
computed: {
lineItemsCloneForWatcher() {
return Object.assign({}, this.lineItems);
},
isRecalibrationOnOrder() {
return store.getters.isRecalibrationOnOrder;
},
isServicePackageDiscountOnOrder() {
return containsLineItemWithPartType(
partTypeStrings.SERVICE_PACKAGE_DISCOUNT,
this.availableLineItems
);
},
servicePackageDiscountParts() {
return findLineItemsWithPartType(
partTypeStrings.SERVICE_PACKAGE_DISCOUNT,
this.availableLineItems
);
},
shouldHideRecalibration() {
return (
experimentMixin.methods
.getSettingValue(experimentSettings.RECAL_PRICE_REMOVE)
?.toLowerCase() === "true" && this.isRecalibrationOnOrder
);
},
showAfterpayBanner() {
return !this.isRecalibrationOnOrder || !this.shouldHideRecalibration;
},
isRecalPriceRemove() {
return (
experimentMixin.methods
.getSettingValue(experimentSettings.RECAL_PRICE_REMOVE)
?.toLowerCase() === "true"
);
},
showRecalDisclaimer() {
return this.isRecalibrationOnOrder && this.isRecalPriceRemove;
},
afterpayExtendedPayOptionThreshold() {
let thresholdAmount = experimentMixin.methods.getSettingValue(
experimentSettings.AFTERPAY_EXTENDED_PAY_OPTION_THRESHOLD
);
thresholdAmount && (thresholdAmount = parseInt(thresholdAmount));
return thresholdAmount;
},
},
methods: {
getColCount() {
if (this.packageNumber > 2) {
return "col-xl-10";
} else {
return "col-xl-6";
}
},
currentNumberOfPackagesAction(packageNumber) {
this.packageNumber = packageNumber;
},
openModalAction(modalName) {
this.$refs[modalName].openModal();
},
arePagePrerequisitesValid() {
const payment = store.getters.order.payment;
const preReqResult =
store.getters.order.serviceLocation.zipCode &&
store.getters.order.serviceLocation.zipCodeCtu &&
(store.getters.order.damage.isRepair ||
(store.getters.order.lineItems?.glassParts != null &&
store.getters.order.lineItems.glassParts.length > 0)) &&
(payment.insuranceCoverage?.isVerified == null ||
payment.insuranceCoverage?.isVerified === false);
// prettier-ignore
{
debugLog("--- quote.vue pagePrereqs start ---", null, !preReqResult);
debugLog("store.getters.order.serviceLocation.zipCode:", store.getters.order.serviceLocation?.zipCode, !preReqResult);
debugLog("store.getters.order.serviceLocation.zipCodeCtu:", store.getters.order.serviceLocation?.zipCodeCtu, !preReqResult);
debugLog("store.getters.order.damage.isRepair:", store.getters.order.damage?.isRepair, !preReqResult);
debugLog("store.getters.order.lineItems?.glassParts:", store.getters.order.lineItems?.glassParts, !preReqResult);
debugLog("store.getters.order.payemt.insuranceCoverage?.isVerified:", store.getters.order.payment?.insuranceCoverage?.isVerified, !preReqResult);
debugLog("--- quote.vue pagePrereqs end ---", null, !preReqResult);
}
return preReqResult;
},
vapsItemsSelectedAction(vapsItemsSelected) {
this.lineItems.vaps = vapsItemsSelected;
},
servicePackageDiscountSelectedAction(supportingItemsSelected) {
this.lineItems.supportingItems = supportingItemsSelected;
},
backButtonAction() {
vehicleQuestionsMixin.methods.navigateBack(this);
},
updateSaveProgressAsSaved() {
this.showSaveProgressModal = false;
},
async closeSaveProgressPopup() {
this.showSaveProgressPopup = false;
savePageData(this.pageName, { saveProgressPopupSkipped: true }, true);
if (this.skipToInsurance) {
// skip ahead now if in IGQ skip experiment
await this.skip(true, externalParamPackageLabels.GLASS_ONLY);
}
},
forwardButtonAction() {
this.dispatchStoreAction(
this.storeActions.SAVE_PAYMENT_TYPE,
this.isInsuranceSelected,
false
);
if (!this.isInsuranceSelected) {
this.dispatchStoreAction(
this.storeActions.SAVE_PARENT_ACCOUNT_NUMBER,
applicationConfig.CASH_PARENT_ACCOUNT_NUMBER,
false
);
this.dispatchStoreAction(
this.storeActions.SAVE_BILL_TO_ACCOUNT_NUMBER,
applicationConfig.CASH_DEFAULT_BILL_TO_ACCOUNT_NUMBER,
false
);
} else {
baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_PARENT_ACCOUNT_NUMBER,
this.holdParentAccountNumber,
false
);
baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_BILL_TO_ACCOUNT_NUMBER,
this.holdBillToAccountNumber,
false
);
}
if (
this.$store.getters.order.payment.parentAccountNumber !=
applicationConfig.CASH_PARENT_ACCOUNT_NUMBER
) {
this.lineItems.supportingItems = this.filterOutFees(this.lineItems.supportingItems);
}
if (this.lineItems.glassParts?.length > 0) {
this.dispatchStoreAction(
this.storeActions.SAVE_GLASS_PARTS_SUPPRESSING_STATE_RESETTING,
this.lineItems.glassParts,
false
);
}
if (this.isInsuranceSelected && this.isServicePackageDiscountOnOrder) {
let supportingItems = this.lineItems.supportingItems;
supportingItems = supportingItems.filter((item) => {
return item.partType != this.servicePackageDiscountParts[0].partType;
});
this.dispatchStoreAction(
this.storeActions.SAVE_SUPPORTING_ITEMS,
supportingItems,
false
);
} else {
this.dispatchStoreAction(
this.storeActions.SAVE_SUPPORTING_ITEMS,
this.lineItems.supportingItems,
false
);
}
this.dispatchStoreAction(this.storeActions.SAVE_VAPS, this.lineItems.vaps, false);
this.dispatchStoreAction(
storeActions.SAVE_ACTIVE_AND_OR_INACTIVE_PROMOS,
{
activePromos: this.lineItems.promos,
},
false
);
const payment = this.$store.getters.payment;
if (payment.isInsurance) {
this.$router.navigateWithSaving(
this.navigationScenarios.CLICKED_FORWARD_WITH_INSURANCE,
this.pageName
);
} else {
this.$router.navigateWithSaving(
this.navigationScenarios.CLICKED_FORWARD_WITH_CASH,
this.pageName
);
}
const lineItems = store.getters.order?.lineItems ?? {};
const combinedLineItems = [
...(lineItems.glassParts || []),
...(lineItems.supportingItems || []),
...(lineItems.vaps || []),
...(lineItems.promos || []),
];
const cashPriceSubtotal = combinedLineItems.length
? baseMixin.methods
.getTotalPriceOfAllLineItemsAndChildParts(combinedLineItems, false)
.toFixed(2)
: "";
store.commit(storeMutations.UPDATE_CASH_PRICE_SUBTOTAL, cashPriceSubtotal);
},
attachCustomEventsForAnalytics() {
this.prependActionToMethod(this, this.forwardButtonAction, () => {
if (!this.isInsuranceSelected && this.lineItems.vaps?.length == 0) {
const tierOnePrice =
this.lineItems.promos?.length > 0
? this.$refs.servicePackage.getDiscountedPackagePriceString(
packageNames.TIER_ONE,
false
)
: this.$refs.servicePackage.getPackagePriceString(
packageNames.TIER_ONE
);
this.pushEventToGA(
this.$route.query[this.queryStrings.FMG_PAGE],
this.GaActions.CASH_QUOTE_DISPLAYED,
tierOnePrice,
true
);
}
});
this.$nextTick(() => {
const availablePackageNames = this.$refs.servicePackage?.servicePackageAnswers;
var priceLabel = "";
if (availablePackageNames?.length > 1 || availablePackageNames?.length == 1) {
var tier = availablePackageNames[0];
if (tier) {
let lineItemsToPrice = this.availableLineItems;
if (this.isRecalibrationOnOrder && this.shouldHideRecalibration) {
lineItemsToPrice =
baseMixin.methods.filterOutRecalibration(lineItemsToPrice);
}
if (this.isServicePackageDiscountOnOrder) {
lineItemsToPrice = lineItemsToPrice?.filter((item) => {
return (
item.partType != this.servicePackageDiscountParts[0].partType
);
});
}
let price = 0;
if (
tier.additionalButtonData?.servicePackageDiscount &&
this.isServicePackageDiscountOnOrder
) {
lineItemsToPrice.push(...this.servicePackageDiscountParts);
}
price += baseMixin.methods.getTierOnePackagePrice(
baseMixin.methods.filterOutFees(lineItemsToPrice)
);
price += this.$refs.servicePackage.getVapsPrice(tier.value);
priceLabel = price.toFixed(2);
}
}
const subTotalLabel = this.Variables?.CASH_SUBTOTAL;
this.pushVariableToDataLayer?.({ [subTotalLabel]: priceLabel });
});
},
async skip(insuranceSelection, packageSelection) {
this.isInsuranceSelected = insuranceSelection;
this.servicePackage = packageSelection;
// Allow selections to filter through sub-logic
await nextTick();
await nextTick();
const isValid = await baseMixin.methods.isFormValid(this.$refs.theForm);
if (isValid) {
showFmgLoadingModal(true);
await this.forwardButtonAction();
}
},
},
watch: {
lineItemsCloneForWatcher: {
handler(newValue, oldValue) {
if (
!oldValue ||
oldValue.length == 0 ||
!oldValue.vaps ||
!newValue ||
newValue.length == 0
) {
return;
}
if (oldValue.promos.length < newValue.promos.length) {
const oldPromoCodes = oldValue.promos.map(
(promoObject) => promoObject.promoCode
);
const newlyActivatedPromoCodes = newValue.promos.filter(
(newPromo) => !oldPromoCodes.includes(newPromo.promoCode)
);
const alert = createPromoSuccessAlert(newlyActivatedPromoCodes[0].promoCode);
this.$refs.funnelHeader.pushGlobalAlert(alert, alert.shouldAutoFade);
}
},
deep: true,
},
},
components: {
funnelHeader,
navbar,
funnelSubHeader,
Form,
textBlock,
cashOrInsuranceQuestion,
servicePackageQuestion,
contentGroupModal,
loadingModal,
afterpayModalBanner,
promoModalQuestion,
recalDisclaimer,
saveProgressModalQuestion,
saveProgressPopupQuestion,
},
};
</script>
<style lang="scss" scoped>
.quote-page.show-save-progress-popup .row.justify-content-center {
display: none;
&.funnel-header-wrapper,
&.save-progress-popup {
display: flex;
}
}
.funnel-sub-header {
p {
font-size: 1rem;
}
}
.text-block {
display: block;
}
:deep(.promo-modal-question a) {
@include responsive-font-size-md(0.875rem, 1rem);
}
</style>