Merge pull request #3125 from Safelite/rlsmerge/2026.04.09-to-develop

Rlsmerge/2026.04.09 to develop
This commit is contained in:
Chris 2026-03-31 13:50:27 -04:00 committed by GitHub
commit 9c2ad1786d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 378 additions and 129 deletions

View file

@ -180,10 +180,6 @@ const endpoints = {
url: "/analytics/api/v1/analytics/log-part-questions",
method: "POST",
},
LogDigitalConsumer: {
url: "/analytics/api/v1/analytics/digitalconsumer-log",
method: "POST",
},
LogFmgSessionData: {
url: "/analytics/api/v1/analytics/digitalconsumer-session-logging",
method: "POST",

View file

@ -23,6 +23,7 @@ const experimentSettings = {
INSURANCE_TAB_TO_DISPLAY_THRESHOLD_INTERNAL: "NextGen_InternalInsuranceTabDisplayThreshold",
INSURANCE_TAB_TO_DISPLAY_THRESHOLD_EXTERNAL: "NextGen_ExternalInsuranceTabDisplayThreshold",
DISPLAY_MSR: "DisplayMSR",
ENABLE_MSR_SPLIT_PAY: "EnableMSRSplitPay",
DISPLAY_WAITLIST: "DisplayWaitlist2.0",
WAITLIST_THRESHOLD_DAYS: "Waitlist_Threshold_Days",
PRICING_BY_DAY: "DisplayPricingByDay",

View file

@ -63,7 +63,6 @@ const storeActions = {
LOG_CUSTOM_EVENT: "logCustomEvent",
INITIALIZE_SESSION: "initializeSession",
LOG_PART_QUESTIONS: "logPartQuestions",
LOG_DIGITALCONSUMER: "logDigitalConsumer",
LOG_FMG_SESSION_DATA: "logFmgSessionData",
// DEPENDENCY MUTATIONS
@ -114,6 +113,7 @@ const storeActions = {
SAVE_IS_RECAL_ACKNOWLEDGED_FOR_SCHEDULING: "saveIsRecalAcknowledgedForScheduling",
SAVE_IS_OEM_GLASS_SELECTED: "saveIsOemGlassSelected",
SAVE_IS_MSR_FEE_APPLICABLE: "saveIsMSRFeeApplicable",
SAVE_IS_MSR_FEE_COVERED_BY_INSURANCE: "saveIsMSRFeeCoveredByInsurance",
CREATE_SUBMITTED_STATE: "createSubmittedState",
RESET_SUBMITTED_STATE: "resetSubmittedState",

View file

@ -72,6 +72,7 @@ const storeMutations = {
UPDATE_IS_RECAL_ACKNOWLEDGED_FOR_SCHEDULING: "updateIsRecalAcknowledgedForScheduling",
UPDATE_IS_OEM_GLASS_SELECTED: "updateIsOemGlassSelected",
UPDATE_IS_MSR_FEE_APPLICABLE: "updateIsMSRFeeApplicable",
UPDATE_IS_MSR_FEE_COVERED_BY_INSURANCE: "updateIsMSRFeeCoveredByInsurance",
UPDATE_CASH_PRICE_SUBTOTAL: "updateCashPriceSubTotal",
// EVENT BUS MUTATIONS

View file

@ -77,7 +77,7 @@
i % 2 == 0 ? 'even' : 'odd',
cartItem.isRemovable ? 'removable-cart-item' : '',
]">
<span v-if="cartItem != recycleFeeCartItem">{{ cartItem.name }}</span>
<span v-if="!this.shouldShowInfoIcon(cartItem)">{{ cartItem.name }}</span>
<textLink
v-if="allowItemRemoval && cartItem.isRemovable"
ref="removeLink"
@ -93,6 +93,15 @@
<img :src="infoIcon" alt="Info Icon" class="info-icon" />
</a>
</span>
<span v-if="cartItem == msrFeeCartItem">
{{ msrFeeCartItem.name }}
<a
href="javascript:void(0)"
@click="openModal(msrModalCmsWidgetName)"
id="msr-fee-cartItem">
<img :src="infoIcon" alt="Info Icon" class="info-icon" />
</a>
</span>
<span
v-if="
!showCoverageAsVerified ||
@ -138,6 +147,10 @@
:pageName="pageName"
modalWidgetName="PromoModalWidget"
@promoAdded="saveVaps" />
<removeMsrFeeModal
modalWidgetName="RemoveMsrFeeModalWidget"
ref="removeMsrFeeModal"
@switchToInshop="switchToInshop" />
</div>
</div>
<div
@ -153,6 +166,10 @@
<contentGroupModal ref="RecycleModal" cmsWidgetName="RecycleModal">
<textBlock cmsWidgetName="RecycleTextBlock" />
</contentGroupModal>
<contentGroupModal
ref="MSRModal"
cmsWidgetName="MSRModal"
:customBodyText="msrModalTextBlock"></contentGroupModal>
</div>
</template>
@ -162,6 +179,7 @@ import textLink from "@/ux-components/text-link/text-link";
import textBlock from "@/digital-components/text-block/text-block";
import contentGroupModal from "@/fmg-components/content-group-modal/content-group-modal";
import promoModalQuestion from "@/fmg-components/promo-modal-question/promo-modal-question";
import removeMsrFeeModal from "./remove-msr-fee-modal/remove-msr-fee-modal.vue";
// Mixins
import baseMixin from "@/mixins/base-mixin.js";
@ -183,6 +201,7 @@ import {
getSalesTax,
getAmountDueWithDonation,
} from "@/helpers/pricing-helper.js";
import { formatToUSDollar } from "@/helpers/cms-content-helper";
// Constants
import { partTypeStrings } from "@/constants/part-type-strings";
@ -191,6 +210,7 @@ import { cartItemTypes } from "@/constants/cart-item-types";
import { coverageStatus, cartItemTypesCoveredByInsurance } from "@/constants/insurance";
import { experimentSettings } from "@/constants/experiments";
import { quotePageDiscountTable } from "@/constants/quote-page-discounts";
import { partNumberStrings } from "@/constants/part-number-strings";
export default {
name: "cart",
@ -203,6 +223,7 @@ export default {
pageName: String,
allowItemRemoval: Boolean,
recyclingModalCmsWidgetName: String,
msrModalCmsWidgetName: String,
showAsPaid: Boolean,
isInsurance: Boolean,
insuranceDeductible: Number,
@ -214,6 +235,7 @@ export default {
isExpandedOnLoad: Boolean,
isCollapsible: { type: Boolean, default: true },
isMSRFeeApplicable: Boolean,
IsMSRFeeCoveredByInsurance: Boolean,
donationCartItem: Object,
},
data() {
@ -311,6 +333,10 @@ export default {
},
async removeItem(cartItemType, category) {
if (cartItemType == cartItemTypes.MOBILE_FEE && this.isMSRFeeApplicable) {
this.$refs.removeMsrFeeModal.openModal();
return false;
}
this.lineItems[category] = this.lineItems[category].filter(
(lineItemsToKeep) => lineItemsToKeep.cartItemType != cartItemType
);
@ -375,6 +401,12 @@ export default {
}
return [];
},
shouldShowInfoIcon(cartItem) {
return cartItem == this.recycleFeeCartItem || cartItem == this.msrFeeCartItem;
},
switchToInshop() {
this.$emit("switchToInshop");
},
},
computed: {
isCartReadyToLoad() {
@ -490,6 +522,10 @@ export default {
cartItems.push(this.mobileFeeCartItem);
}
if (this.msrFeeCartItem) {
cartItems.push(this.msrFeeCartItem);
}
if (this.quotePageDiscountCartItem) {
cartItems.push(this.quotePageDiscountCartItem);
}
@ -892,7 +928,9 @@ export default {
let cartItem = null;
let showMobileFeeCartItem = false;
const mobileFeeLineItem = this.supportingItems.find(
(lineItem) => lineItem.partType == partTypeStrings.MOBILE_FEE
(lineItem) =>
lineItem.partType == partTypeStrings.MOBILE_FEE &&
lineItem.partNumber == partTypeStrings.MOBILE_FEE
);
var mobileTotal =
@ -929,6 +967,68 @@ export default {
}
return cartItem;
},
msrFeeCartItemName() {
return this.getCmsContent("MSRFeeTextWidget", "Text");
},
getMsrFeeLineItem() {
return this.supportingItems.find(
(lineItem) =>
lineItem.partType == partTypeStrings.MOBILE_FEE &&
lineItem.partNumber == partNumberStrings.MOBILE_STATIC_RECAL_FEE
);
},
msrFeeCartItem() {
let cartItem = null;
const msrFeeLineItem = this.getMsrFeeLineItem;
const shouldCreateMsrFeeCartItem =
this.isMSRFeeApplicable &&
this.isInsurance &&
!this.IsMSRFeeCoveredByInsurance &&
msrFeeLineItem &&
this.msrFeeAmount > 0;
if (shouldCreateMsrFeeCartItem) {
cartItem = {
name: this.msrFeeCartItemName,
category: cartItemCategories.SUPPORTING_ITEMS,
cartItemType: cartItemTypes.MOBILE_FEE,
isDisplayed: this.isInsurance,
isRemovable: true,
subTotal: 0,
salesTax: 0,
lineItems: [],
isCoveredByInsurance: this.IsMSRFeeCoveredByInsurance,
};
msrFeeLineItem.cartItemType = cartItem.cartItemType;
cartItem.lineItems.push(msrFeeLineItem);
cartItem.subTotal += this.msrFeeAmount;
cartItem.salesTax += msrFeeLineItem.salesTax ?? 0;
}
return cartItem;
},
msrFeeAmount() {
const msrFeeLineItem = this.getMsrFeeLineItem;
if (!msrFeeLineItem) {
return 0;
}
return (
msrFeeLineItem.laborAmount + msrFeeLineItem.sellingPrice + msrFeeLineItem.kitPrice
);
},
msrModalTextBlock() {
let cmsContentText = this.getCmsContent("MSRModal", "BodyText");
if (cmsContentText) {
cmsContentText = cmsContentText.replaceAll(
"{custom:mobileFee}",
formatToUSDollar(this.msrFeeAmount)
);
cmsContentText = cmsContentText.replaceAll(
"{custom:submittedOrder.policy.insuranceCompanyName}",
this.insuranceCompanyName
);
}
return cmsContentText;
},
quotePageDiscountCartItemName() {
return this.getCmsContent("ServicePackageDiscountTextWidget", "Text");
},
@ -1189,6 +1289,7 @@ export default {
textLink,
contentGroupModal,
promoModalQuestion,
removeMsrFeeModal,
},
};
</script>
@ -1289,6 +1390,10 @@ export default {
order: 3;
margin-right: 100%;
text-decoration: none;
&#msr-fee-cartItem {
margin: 0 0 0 0.2rem;
}
}
}
.package-type,

View file

@ -0,0 +1,153 @@
<template>
<div id="msr-fee-modal-container">
<modal
:ref="modalName"
:headerText="modalHeaderText"
suppressPageScroll
:onModalClosedCallback="onModalClosed"
:footerButtonText="modalFooterText"
:isFooterButtonPrimary="true"
@footer-button-event="switchToInshop"
@isModalOpened="setIsModalOpen">
<p class="modal-body-inner" v-html="modalBodyText"></p>
<template v-slot:modal-header-slot>
<span>{{ modalSubHeaderText }}</span>
</template>
<template v-slot:modal-footer-slot>
<button
type="button"
class="btn btn-link"
id="cancel-button"
:disabled="isLoading"
@click="closeModal">
{{ buttonText }}
</button>
</template>
</modal>
</div>
</template>
<script>
// Supporting files
import modal from "@/digital-components/modal/modal";
export default {
name: "remove-msr-fee-modal",
props: {
cmsWidgetName: String,
modalWidgetName: String,
},
data() {
return {
isModalOpen: false,
switchedToInshop: false,
};
},
methods: {
openModal() {
this.modal.openModal();
},
onModalClosed() {
this.$emit("close-remove-msr-fee-modal");
this.switchedToInshop = false;
},
closeModal() {
this.modal.closeModal();
},
getIsModalOpen() {
return this.isModalOpen;
},
setIsModalOpen(isOpen) {
this.isModalOpen = isOpen;
},
switchToInshop() {
this.switchedToInshop = true;
this.$emit("switchToInshop");
this.modal.closeModal();
},
},
computed: {
modalName() {
return this.modalWidgetName;
},
modal() {
return this.$refs[this.modalName];
},
modalSubHeaderText() {
return this.getCmsContent(this.modalWidgetName, "SubheaderText");
},
modalHeaderText() {
return this.getCmsContent(this.modalWidgetName, "HeaderText");
},
modalBodyText() {
return this.getCmsContent(this.modalWidgetName, "BodyText");
},
modalFooterText() {
return this.getCmsContent(this.modalWidgetName, "FooterText");
},
buttonText() {
return this.getCmsContent(this.modalWidgetName, "FooterText2");
},
},
components: { modal },
};
</script>
<style lang="scss">
#msr-fee-modal-container {
.modal-component {
@include media-breakpoint-up(md) {
.modal-dialog {
left: 0;
align-content: center;
flex-wrap: wrap;
width: 22.063rem;
transform: translate(0, 0);
.modal-content {
border-radius: $border-radius-lg;
}
}
}
.modal-dialog {
.modal-content {
.modal-header {
flex-direction: column;
& > span {
color: $red;
font-size: $font-size-14;
font-weight: $font-weight-600;
text-transform: uppercase;
}
.modal-title {
font-size: $font-size-20;
font-weight: $font-weight-normal;
}
}
.modal-body {
padding: 0.5rem 1rem;
.modal-body-inner {
ul {
list-style: none;
padding: 1rem;
background-color: #f4f4f4;
font-size: $font-size-14;
}
}
}
.modal-footer {
flex-flow: wrap-reverse;
#cancel-button {
width: 100%;
text-decoration: none;
margin-top: 1.5rem;
font-weight: $font-weight-600;
}
}
}
}
}
}
</style>

View file

@ -26,6 +26,7 @@ export default {
name: "content-group-modal",
props: {
cmsWidgetName: String,
customBodyText: String,
footerButtonActionName: {
type: String,
default: null,
@ -43,6 +44,9 @@ export default {
return this.getCmsContent(this.cmsWidgetName, "SubheaderText");
},
ModalBodyText() {
if (this.customBodyText) {
return this.customBodyText;
}
return this.getCmsContent(this.cmsWidgetName, "BodyText");
},
ModalSubBodyText() {

View file

@ -102,7 +102,6 @@ export default {
},
(error) => {
if (
endpoint.toLowerCase().includes(endpoints.LogDigitalConsumer.url) ||
endpoint.toLowerCase().includes(endpoints.LogFmgSessionData.url) ||
endpoint.toLowerCase().includes(endpoints.LogPageView.url) ||
endpoint.toLowerCase().includes(endpoints.LogCustomEvent.url) ||

View file

@ -2,6 +2,7 @@ import store from "@/store";
import { storeActions } from "@/constants/store-actions";
import baseMixin from "@/mixins/base-mixin.js";
import { deepClone } from "@/helpers/object-helper";
import { partNumberStrings } from "@/constants/part-number-strings";
export function getDisplayAmountDue(lineItemsObject, includeTax = true) {
return getAmountDue(lineItemsObject, includeTax).toLocaleString("en-US", {
@ -32,6 +33,10 @@ export function getAmountDue(lineItemsObject, includeTax = true) {
if (store.getters.coverageIsVerified && !order.policy.isNoComp && !order.policy.isItac) {
amountDue = order.policy.currentDeductible;
if (order.isMSRFeeApplicable && !order.isMSRFeeCoveredByInsurance) {
amountDue += getMSRFeePartPrice(lineItemsObject?.supportingItems, includeTax);
}
}
if (lineItemsObject?.vaps) {
@ -100,6 +105,20 @@ export async function getPricingByDayPartWithPrice(pageNameToLog) {
return pricingResults[0];
}
export function getMSRFeePartPrice(supportingItems, includeTax) {
let msrFeePrice = 0;
const msrFeeLineItem = supportingItems?.find(
(lineItem) => lineItem.partNumber === partNumberStrings.MOBILE_STATIC_RECAL_FEE
);
if (msrFeeLineItem) {
msrFeePrice = baseMixin?.methods?.getTotalPriceOfAllLineItemsAndChildParts(
[msrFeeLineItem],
includeTax
);
}
return msrFeePrice;
}
export function addPricesToLineItems(lineItems, pricingLineItems) {
lineItems.forEach((lineItem) => {
const lineItemIndex = pricingLineItems.findIndex(

View file

@ -62,6 +62,7 @@
:showAsPaid="isPia"
servicePackageOptionsCmsName="ServicePackageTitle"
recyclingModalCmsWidgetName="RecycleModal"
msrModalCmsWidgetName="MSRModal"
:isInsurance="isInsurance"
:insuranceDeductible="currentDeductible"
:insuranceCompanyName="insuranceCompanyName"
@ -70,7 +71,8 @@
:isItac="isItac"
:isNoComp="isNoComp"
:isExpandedOnLoad="false"
:isMSRFeeApplicable="isMSRFeeApplicable" />
:isMSRFeeApplicable="isMSRFeeApplicable"
:IsMSRFeeCoveredByInsurance="isMSRFeeCoveredByInsurance" />
<hr class="mb-5" />
</div>
@ -419,6 +421,9 @@ export default {
isMSRFeeApplicable() {
return this.submittedOrder?.isMSRFeeApplicable;
},
isMSRFeeCoveredByInsurance() {
return this.submittedOrder?.isMSRFeeCoveredByInsurance;
},
isInsurance() {
return this.submittedOrder?.payment?.isInsurance;
},

View file

@ -29,10 +29,12 @@
v-model="lineItems"
servicePackageOptionsCmsName="ServicePackageTitle"
recyclingModalCmsWidgetName="RecycleModal"
msrModalCmsWidgetName="MSRModal"
:isInsurance="isInsurance"
:insuranceDeductible="currentDeductible"
:insuranceCompanyName="insuranceCompanyName"
:showInsuranceCoverageAs="showInsuranceCoverageAs"
:isMSRFeeApplicable="isMSRFeeApplicable"
:isItac="isItac"
:isNoComp="isNoComp"
:isCollapsible="false" />
@ -568,6 +570,9 @@ export default {
insuranceCompanyName() {
return this.$store.getters.policy.insuranceCompanyName;
},
isMSRFeeApplicable() {
return this.$store.getters.order.isMSRFeeApplicable;
},
lineItems() {
return deepClone(this.$store.getters.lineItems);
},

View file

@ -26,6 +26,7 @@
pageName="payment-method"
servicePackageOptionsCmsName="ServicePackageTitle"
recyclingModalCmsWidgetName="RecycleModal"
msrModalCmsWidgetName="MSRModal"
:isInsurance="isInsurance"
:insuranceDeductible="currentDeductible"
:insuranceCompanyName="insuranceCompanyName"
@ -35,7 +36,9 @@
:isNoComp="isNoComp"
:isExpandedOnLoad="false"
:isMSRFeeApplicable="isMSRFeeApplicable"
@itemRemoved="evaluatePromosAndTaxItemsOnOrder" />
:IsMSRFeeCoveredByInsurance="isMSRFeeCoveredByInsurance"
@itemRemoved="evaluatePromosAndTaxItemsOnOrder"
@switchToInshop="navigateToSchedulePage" />
<afterpayBreakout
v-if="isAfterpayBreakoutDisplay"
@ -214,6 +217,7 @@ export default {
name: "paymentMethod",
props: {
recyclingModalCmsWidgetName: String,
msrModalCmsWidgetName: String,
},
async beforeRouteEnter(to, from, next) {
// Call APIs
@ -607,6 +611,13 @@ export default {
openModal(modalName) {
this.$refs[modalName].openModal();
},
async navigateToSchedulePage() {
this.$refs.loadingModal.showModal();
this.$router.navigateWithSaving(
this.navigationScenarios.CLICKED_SWITCH_TO_INSHOP,
this.pageName
);
},
},
computed: {
AppointmentType() {
@ -682,6 +693,9 @@ export default {
isMSRFeeApplicable() {
return this.$store.getters.order.isMSRFeeApplicable;
},
isMSRFeeCoveredByInsurance() {
return this.$store.getters.order.isMSRFeeCoveredByInsurance;
},
isInsurance() {
return this.hasSubmittedOrder()
? this.getSubmittedOrder()?.payment.isInsurance

View file

@ -47,10 +47,12 @@
v-model="lineItems"
servicePackageOptionsCmsName="ServicePackageTitle"
recyclingModalCmsWidgetName="RecycleModal"
msrModalCmsWidgetName="MSRModal"
:isInsurance="isInsurance"
:insuranceDeductible="currentDeductible"
:insuranceCompanyName="insuranceCompanyName"
:showInsuranceCoverageAs="showInsuranceCoverageAs"
:isMSRFeeApplicable="isMSRFeeApplicable"
:isItac="isItac"
:isNoComp="isNoComp"
:isCollapsible="false" />
@ -410,6 +412,9 @@ export default {
insuranceCompanyName() {
return this.$store.getters.policy.insuranceCompanyName;
},
isMSRFeeApplicable() {
return this.$store.getters.order.isMSRFeeApplicable;
},
isPaypal() {
if (this.paymentType == "pp") {
return true;

View file

@ -590,12 +590,6 @@ export default {
}
// 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)) {
@ -613,8 +607,6 @@ export default {
) {
await vm.skip(insuranceSelection, servicePackage);
}
baseMixin.methods.ResetExternalParamsAndHideModal();
} else {
if (externalThreshold) thresholdToUse = externalThreshold;
@ -622,15 +614,17 @@ export default {
{
debugLog("quote.vue tab select NOT EXTERNAL");
}
vm.isInsuranceSelected = getIsInsuranceSelectedValue(
vm.availableLineItems,
thresholdToUse
);
baseMixin.methods.ResetExternalParamsAndHideModal();
}
}
if (vm.isInsuranceSelected == null) {
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);

View file

@ -704,8 +704,11 @@ export default {
},
isMobileStaticRecalibrationApplicable() {
return (
this.displayMSR &&
this.mobileFeePart?.partNumber == partNumberStrings.MOBILE_STATIC_RECAL_FEE
(this.displayMSR &&
this.mobileFeePart?.partNumber == partNumberStrings.MOBILE_STATIC_RECAL_FEE &&
this.enableMSRSplitPay) ||
this.isCashItacNoComp ||
this.mobileFeePart?.isInsurable
);
},
displayMSR() {
@ -715,6 +718,13 @@ export default {
?.toLowerCase() === "true"
);
},
enableMSRSplitPay() {
return (
experimentMixin.methods
.getSettingValue(experimentSettings.ENABLE_MSR_SPLIT_PAY)
?.toLowerCase() === "true"
);
},
isMSRFeeNotCoveredByInsurance() {
return (
this.isMobileSelected &&
@ -1173,6 +1183,13 @@ export default {
isMSRFeeApplicable
);
},
updateAndSaveIsMSRFeeCoveredByInsurance() {
const isMSRFeeCoveredByInsurance = this.mobileFeePart?.isInsurable;
this.dispatchStoreAction(
this.storeActions.SAVE_IS_MSR_FEE_COVERED_BY_INSURANCE,
isMSRFeeCoveredByInsurance
);
},
updateAndSaveSupportingItems() {
let supportingItems = store.getters.lineItems.supportingItems;
let shouldSaveSupportingItems = false;
@ -1528,6 +1545,7 @@ export default {
this.updateAndSaveSupportingItems();
this.updateAndSaveIsMSRFeeApplicable();
this.updateAndSaveIsMSRFeeCoveredByInsurance();
this.updateSupportingItems();

View file

@ -253,17 +253,17 @@ export default {
const matchingMake = resultMap.makeQuestionInitialData?.filter(
(item) =>
item.toLowerCase() ===
store.getters.externalParameterVehicle.make.toLowerCase()
store.getters.externalParameterVehicle.make?.toLowerCase()
);
const matchingModel = resultMap.modelQuestionInitialData?.filter(
(item) =>
item.toLowerCase() ===
store.getters.externalParameterVehicle.model.toLowerCase()
store.getters.externalParameterVehicle.model?.toLowerCase()
);
const matchingStyle = resultMap.styleQuestionInitialData?.filter(
(item) =>
item.toLowerCase() ===
store.getters.externalParameterVehicle.style.toLowerCase()
store.getters.externalParameterVehicle.style?.toLowerCase()
);
baseMixin.methods.dispatchStoreAction(
storeActions.UPDATE_EXTERNAL_PARAMETER_MMS,

View file

@ -132,51 +132,6 @@ export default {
}
},
async logDigitalConsumer() {
const currentPageName = getPageNameFromRouter();
const universes = store.getters.applicationUser.experiments;
const variationNames = universes
.filter((item) => item.universeName === experimentUniverses.CONCEPT_FUNNEL)
.map((item) => item.variationName)
.filter(Boolean); // removes undefined/null
const conceptVariation = variationNames.length > 0 ? variationNames[0] : "";
const isConceptExposed = universes.find(
(item) => item.universeName === experimentUniverses.CONCEPT_FUNNEL
)?.isExposed;
const submittedOrder = baseMixin.methods.getSubmittedOrder();
const hasSubmittedOrder = baseMixin.methods.hasSubmittedOrder();
const hasSubmittedOrderAtConfirmationPage =
hasSubmittedOrder && currentPageName?.toLowerCase() == routeData.CONFIRMATION.name;
var payload = {
actionName: `Browser page:${currentPageName}`,
referralSequenceNumber: hasSubmittedOrderAtConfirmationPage
? submittedOrder.referralSequenceNumber
: store.getters.order.referralSequenceNumber,
referralNumber: hasSubmittedOrderAtConfirmationPage
? submittedOrder.referralNumber
: store.getters.order.referralNumber,
workOrderId: hasSubmittedOrderAtConfirmationPage
? submittedOrder.workOrderId
: store.getters.order.workOrderId,
workOrderNumber: hasSubmittedOrderAtConfirmationPage
? submittedOrder.workOrderNumber
: store.getters.order.workOrderNumber,
conceptVariation: conceptVariation,
isConceptExposed: isConceptExposed,
};
await baseMixin.methods.dispatchStoreAction(
storeActions.LOG_DIGITALCONSUMER,
payload,
false
);
},
async pushEventForChatsToGA(category, action, label, pushToLogApp = false) {
const currentPageName = getPageNameFromRouter();
const value = `2.0_${currentPageName}`;
@ -206,6 +161,11 @@ export default {
// S3 bucket, safelite-dev-digitalconsumer-session-data-us-east-2/1.
// This bucket data is then picked up by snowflake for analytics use.
async pushFmgSessionData() {
var currentPageName = getPageNameFromRouter(true);
if (!currentPageName) {
return;
}
const hasSubmittedOrder = baseMixin.methods.hasSubmittedOrder();
const submittedOrder = baseMixin.methods.getSubmittedOrder();
const order = hasSubmittedOrder ? submittedOrder : store.getters.order;
@ -247,22 +207,6 @@ export default {
}
var appointment = `${order?.schedule?.date ?? ""} ${order?.schedule?.startTime ?? ""}`;
var currentPageName = getPageNameFromRouter();
// add query strings to the page name for debugging. on the vehicle page, if from an external link, pull it from the stash
if (currentPageName === "vehicle") {
if (!window.location.search) {
if (store.getters.externalParameterState?.qsStash) {
currentPageName += `${store.getters.externalParameterState.qsStash}`;
}
} else {
currentPageName += `${window.location.search}`;
}
} else {
if (window.location.search) {
currentPageName += `${window.location.search}`;
}
}
var sessionData = {};
sessionData.currentPage = currentPageName;
@ -911,6 +855,9 @@ export default {
setSessionIdIfUnset(response.data.sessionId);
}
}
// logging on session init so we capture complete query string data
await this.pushFmgSessionData();
},
noSession() {
@ -977,7 +924,7 @@ function pushToDataLayerIfDefined(data) {
}
}
function getPageNameFromRouter() {
function getPageNameFromRouter(useDefaultUrl = false) {
if (
router &&
router.currentRoute &&
@ -987,7 +934,9 @@ function getPageNameFromRouter() {
return router.currentRoute.value.name;
}
return window.location.href.replace(/\/$/, "").split("/").pop();
return useDefaultUrl
? window.location.search
: window.location.href.replace(/\/$/, "").split("/").pop();
}
function getValueToLog(value, valueToLogType) {

View file

@ -64,6 +64,7 @@ const navigationScenarios = {
CLICKED_PAY_NOW: "CLICKED_PAY_NOW",
CLICKED_PAY_NOW_ADYEN: "CLICKED_PAY_NOW_ADYEN",
CLICKED_PAY_LATER: "CLICKED_PAY_LATER",
CLICKED_SWITCH_TO_INSHOP: "CLICKED_SWITCH_TO_INSHOP",
PIA_ERROR: "PIA_ERROR",
PIA_CC_ERROR: "PIA_CC_ERROR",
PIA_APPLE_ERROR: "PIA_APPLE_ERROR",

View file

@ -553,6 +553,10 @@ const routingTable = function () {
scenario: navigationScenarios.CLICKED_INSURANCE,
destinationPageData: routeData.INSURANCE_COMPANY,
},
{
scenario: navigationScenarios.CLICKED_SWITCH_TO_INSHOP,
destinationPageData: routeData.SCHEDULE,
},
],
},
{

View file

@ -5,9 +5,6 @@ import { storeMutations } from "@/constants/store-mutations";
export async function afterEach(to, from) {
store.commit(storeMutations.UPDATE_LAST_PAGE_VISITED, to.name);
// digital consumer logging
analyticsMixin.methods.logDigitalConsumer();
// digital consumer fmg session logging to snowflake
analyticsMixin.methods.pushFmgSessionData();

View file

@ -19,7 +19,6 @@ export async function errorBeforeEnter(to, from) {
nextPage: to?.name,
};
analyticsMixin.methods.logDigitalConsumer();
await handleHardError(errorPayload);
return;
}
@ -33,7 +32,6 @@ export async function errorBeforeEnter(to, from) {
nextPage: to?.name,
};
analyticsMixin.methods.logDigitalConsumer();
await handleHardError(errorPayload);
return;
} else {

View file

@ -192,6 +192,7 @@ const getDefaultState = () => {
isRecalAckOptIn: false,
isRecalAcknowledgedForScheduling: "",
isMSRFeeApplicable: false,
isMSRFeeCoveredByInsurance: false,
},
applicationUser: {
eventBus: [],
@ -359,6 +360,9 @@ export const mutations = {
updateIsMSRFeeApplicable(state, isMSRFeeApplicable) {
state.order.isMSRFeeApplicable = isMSRFeeApplicable;
},
updateIsMSRFeeCoveredByInsurance(state, isMSRFeeCoveredByInsurance) {
state.order.isMSRFeeCoveredByInsurance = isMSRFeeCoveredByInsurance;
},
updateCashPriceSubTotal(state, cashPriceSubTotal) {
state.order.cashPriceSubTotal =
cashPriceSubTotal === "" ? null : cashPriceSubTotal.toString();
@ -1630,38 +1634,6 @@ export const actions = {
});
},
logDigitalConsumer(
context,
{
actionName,
referralSequenceNumber,
referralNumber,
workOrderId,
workOrderNumber,
conceptVariation,
isConceptExposed,
}
) {
var payload = {
sessionId: getSessionIdValue(),
deviceId: getDeviceIdValue(),
actionName: actionName ?? "",
referralSequenceNumber: referralSequenceNumber ?? "",
referralNumber: referralNumber ?? "",
applicationName: baseMixin.methods.isMobileDevice() ? "2.0 Mobile" : "2.0",
workOrderId: workOrderId ?? "",
workOrderNumber: workOrderNumber ?? "",
conceptVariation: conceptVariation,
isConceptExposed: isConceptExposed,
};
return globalMethods.callHttpClient({
method: endpoints.LogDigitalConsumer.method,
endpoint: endpoints.LogDigitalConsumer.url,
payload: payload,
});
},
logFmgSessionData(
context,
{
@ -1770,6 +1742,7 @@ export const actions = {
method: endpoints.LogFmgSessionData.method,
endpoint: endpoints.LogFmgSessionData.url,
payload: payload,
logApiCall: false,
});
},
@ -2611,6 +2584,7 @@ export const actions = {
lockToken: order.lockToken,
isRecalAckOptIn: order.isRecalAckOptIn,
isMSRFeeApplicable: order.isMSRFeeApplicable,
isMSRFeeCoveredByInsurance: order.isMSRFeeCoveredByInsurance,
cashPriceSubTotal: order.cashPriceSubTotal,
},
},
@ -3043,6 +3017,13 @@ export const actions = {
context.commit(storeMutations.UPDATE_IS_MSR_FEE_APPLICABLE, isMSRFeeApplicable);
},
saveIsMSRFeeCoveredByInsurance(context, isMSRFeeCoveredByInsurance) {
context.commit(
storeMutations.UPDATE_IS_MSR_FEE_COVERED_BY_INSURANCE,
isMSRFeeCoveredByInsurance
);
},
saveParentAccountNumber(context, parentAccountNumber) {
context.commit(storeMutations.UPDATE_PARENT_ACCT_NUMBER, parentAccountNumber);
},