DigitalConsumer.FixMyGlass/src/layouts/quote/quote.vue
2024-03-15 11:03:20 +05:30

386 lines
15 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="lineItems.promos"
v-on="{ 'buttonEvent.openModal': openModalAction }"
validationRules="option-required"
isRequired />
<afterpayModalBanner
v-if="!isInsuranceSelected"
cmsWidgetName="AfterpayModalWidget"
:activePromos="lineItems.promos"
:availableLineItems="availableLineItems"
:lineItems="lineItems" />
<promoModalQuestion
class="small mt-4"
v-model="lineItems"
:addableVaps="addableVaps"
pageName="quote"
:taxPromos="false"
modalWidgetName="PromoModalWidget" />
<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 afterpayModalBanner from "@/layouts/quote/afterpay-modal-banner/afterpay-modal-banner";
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 { deepClone } from "@/helpers/object-helper";
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 { applicationConfig } from "@/constants/application-config";
import { payWithInsuranceStates } from "@/constants/pay-with-insurance-states";
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";
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 lineItems = deepClone(store.getters.order.lineItems);
lineItems.vaps = lineItems.vaps ?? [];
const nullSafeGlassParts = 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
);
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 = getQuerystringParameter(queryStrings.PROMO);
const { validatePromoResponse, revalidatePromoResponse } =
await revalidatePromosAndValidateQueryStringPromo(
promoCodeFromQueryString,
pricingResults,
"quote"
);
// 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((vm) => {
vm.setCmsContent(resultMap.cmsContent);
vm.addableVaps = addableVaps;
vm.lineItems = lineItems;
vm.availableLineItems = pricingResults;
vm.isInsuranceSelected = vm.getDefaultIsInsuranceSelectedValue(vm.availableLineItems);
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,
availableLineItems: null,
lineItems: [],
addableVaps: [],
};
},
computed: {
lineItemsCloneForWatcher() {
return Object.assign({}, this.lineItems);
},
},
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.lineItems.vaps = 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
);
this.dispatchStoreAction(
this.storeActions.SAVE_BILL_TO_ACCOUNT_NUMBER,
applicationConfig.CASH_DEFAULT_BILL_TO_ACCOUNT_NUMBER,
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
);
}
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.$route
);
} else {
this.$router.navigateWithSaving(
this.navigationScenarios.CLICKED_FORWARD_WITH_CASH,
this.$route
);
}
},
},
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,
vehicleBanner,
funnelSubHeader,
Form,
textBlock,
cashOrInsuranceQuestion,
servicePackageQuestion,
contentGroupModal,
loadingModal,
afterpayModalBanner,
promoModalQuestion,
},
};
</script>
<style scoped>
.text-block {
display: block;
}
</style>