DigitalConsumer.FixMyGlass/src/layouts/quote/quote.vue
2023-11-24 09:44:26 -05:00

339 lines
14 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">
<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">
<vehicleBanner
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false" />
<funnelSubHeader class="mb-5" cmsWidgetName="FunnelSubHeaderWidget" />
<cashOrInsuranceQuestion
ref="cashOrInsurance"
cmsWidgetName="CashOrInsuranceQuestionWidget"
v-model="isInsuranceSelected"
groupName="CashOrInsuranceQuestion" />
<servicePackageQuestion
ref="servicePackage"
cashCmsWidgetName="CashServicePackageQuestionWidget"
insuranceCmsWidgetName="InsuranceServicePackageQuestionWidget"
groupName="ServicePackageQuestion"
:availableLineItems="availableLineItems"
:isInsuranceSelected="isInsuranceSelected"
@vapsItemsSelected="vapsItemsSelectedAction"
:activePromos="allActivePromos"
v-on="{ 'buttonEvent.openModal': openModalAction }"
validationRules="option-required"
isRequired />
<textBlock
cmsWidgetName="quoteDisclaimer"
justifyText="left"
typeStyle="caption"
class="mt-4" />
<contentGroupModal ref="RainDefenseModal" cmsWidgetName="RainDefenseModal" />
<contentGroupModal ref="FrontWiperModal" cmsWidgetName="FrontWiperModal" />
<contentGroupModal ref="RearWiperModal" cmsWidgetName="RearWiperModal" />
<contentGroupModal ref="RecalModal" cmsWidgetName="RecalModal" />
<navbar
cmsWidgetName="FunnelFooterWidget"
:isForwardActionDisabled="!meta.valid"
:isBackButtonHidden="shouldHideBackButton"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" />
</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 vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner";
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 baseMixin from "@/mixins/base-mixin.js";
import vehicleQuestionsMixin from "../../mixins/vehicle-questions-mixin";
import { settleAllPromises } from "@/helpers/layout-helper";
import { storeActions } from "@/constants/store-actions";
import store from "@/store";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { required } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages";
import { Form, defineRule } from "vee-validate";
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
import { applicationConfig } from "@/constants/application-config";
import { payWithInsuranceStates } from "@/constants/pay-with-insurance-states";
import {
revalidatePromosAndValidateQueryStringPromo,
buildToastMessagesFromRevalidateOrValidatePromoResponse,
} from "@/helpers/promotions-helper";
import { queryStrings } from "@/constants/query-strings";
import { getQuerystringParameter } from "@/helpers/querystring-helper";
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
export default {
name: "quote",
async beforeRouteEnter(to, from, next) {
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
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 promiseResultMap = [
{
resultKey: "cmsContent",
promise: cmsContentPromise,
},
{
resultKey: "wipers",
promise: wipersPromise,
},
{
resultKey: "rainDefense",
promise: rainDefensePromise,
},
{
resultKey: "supportingItems",
promise: supportingItemsPromise,
},
];
const resultMap = await settleAllPromises(promiseResultMap);
const nullSafeGlassParts = store.getters.order.lineItems.glassParts ?? [];
const availableLineItems = [
resultMap.rainDefense,
...resultMap.supportingItems,
...resultMap.wipers,
...nullSafeGlassParts,
];
const pricingResults = await baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA,
{
availableLineItems: availableLineItems,
},
"quote",
false
);
baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_SUPPORTING_ITEMS,
resultMap.supportingItems,
false
);
// 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 = getQuerystringParameter(queryStrings.PROMO);
const { validatePromoResponse, revalidatePromoResponse } =
await revalidatePromosAndValidateQueryStringPromo(
promoCodeFromQueryString,
pricingResults,
"quote"
);
// End of promo logic
// Call the "next" function to complete the transition to this page.
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
vm.pricedGlassParts = nullSafeGlassParts;
vm.supportingItems = resultMap.supportingItems;
vm.availableLineItems = pricingResults;
vm.isInsuranceSelected = vm.getDefaultIsInsuranceSelectedValue(vm.availableLineItems);
vm.newValidatedPromos = validatePromoResponse?.orderPromos;
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
);
}
});
},
data() {
return {
isInsuranceSelected: null,
selectedVaps: null,
availableLineItems: null,
supportingItems: null,
pricedGlassParts: null,
newValidatedPromos: null,
};
},
computed: {
allActivePromos() {
const allActivePromos = [];
if (this.newValidatedPromos) allActivePromos.push(...this.newValidatedPromos);
if (this.$store.getters.order.lineItems.promos)
allActivePromos.push(...this.$store.getters.order.lineItems.promos);
return allActivePromos;
},
},
methods: {
openModalAction(modalName) {
this.$refs[modalName].openModal();
},
arePagePrerequisitesValid() {
return (
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)) &&
store.getters.order.referralNumber?.length !== 6
);
},
getDefaultIsInsuranceSelectedValue(availableLineItems) {
const serviceLocationState = store.getters.order.serviceLocation.state;
// Override if coming back from QuoteDetails. Remove override after Quote release
const isInsuranceOverrideValue = this.$route.query?.isInsurance;
const defaultIsInsuranceSelectedValue = this.$store.getters.order.payment.isInsurance;
//Default to Insurance Tab if user Service zip is from certain States
if (
serviceLocationState != null &&
payWithInsuranceStates.find((item) => item === serviceLocationState)
)
return true;
else if (isInsuranceOverrideValue != null) {
return isInsuranceOverrideValue == "true";
} else if (defaultIsInsuranceSelectedValue != null) {
return defaultIsInsuranceSelectedValue;
} else {
return availableLineItems
? baseMixin.methods.getTierOnePackagePrice(
baseMixin.methods.filterOutFees(availableLineItems)
) > 300
: null;
}
},
vapsItemsSelectedAction(vapsItemsSelected) {
this.selectedVaps = vapsItemsSelected;
},
backButtonAction() {
vehicleQuestionsMixin.methods.navigateBack(this);
},
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
);
}
if (
this.$store.getters.order.payment.parentAccountNumber !=
applicationConfig.CASH_PARENT_ACCOUNT_NUMBER
) {
this.supportingItems = this.filterOutFees(this.supportingItems);
}
if (this.pricedGlassParts.length > 0) {
this.dispatchStoreAction(
this.storeActions.SAVE_GLASS_PARTS_SUPPRESSING_STATE_RESETTING,
this.pricedGlassParts,
false
);
}
this.dispatchStoreAction(this.storeActions.SAVE_VAPS, this.selectedVaps, false);
this.dispatchStoreAction(
this.storeActions.SAVE_ACTIVE_AND_OR_INACTIVE_PROMOS,
{ activePromos: this.allActivePromos },
false
);
// Clear out local promos to avoid duplicates in `allActivePromos` computed
// otherwise package price changes while next page is loading
this.newValidatedPromos = [];
const payment = this.$store.getters.payment;
if (payment.isInsurance) {
navigateToHeritageFunnel({
shouldSaveSession: true,
pageNameToLog: "quote",
loadingModal: this.$refs.loadingModal,
});
} else {
this.$router.navigateWithSaving(
this.navigationScenarios.CLICKED_FORWARD_WITH_CASH,
this.$route
);
}
},
},
components: {
funnelHeader,
navbar,
vehicleBanner,
funnelSubHeader,
Form,
textBlock,
cashOrInsuranceQuestion,
servicePackageQuestion,
contentGroupModal,
loadingModal,
},
};
</script>
<style scoped>
.text-block {
display: block;
}
</style>