diff --git a/src/constants/analytics.js b/src/constants/analytics.js index c3416edea..f6534cda4 100644 --- a/src/constants/analytics.js +++ b/src/constants/analytics.js @@ -12,6 +12,8 @@ const GaEvents = { const GaCategories = { API_RESPONSE: "Api_Response", EVOX: "Evox", + APPOINTMENT: "Appointment", + SERVICE_LOCATION: "service-location", }; const GaActions = { @@ -21,6 +23,10 @@ const GaActions = { SUBMITTED: "Submitted", DISPLAYED: "Displayed", CASH_QUOTE_DISPLAYED: "Cash quote displayed", + SERVICE_PACKAGE_PRICE: "Service package price", + MOBILE_AVAILABLE: "mobile_available", + SHOPS_FIRST_DISPLAYED: "shops_first_displayed", + MORE_LOCATIONS_CLICKED: "more_locations_clicked", }; const GaLabels = { @@ -29,6 +35,8 @@ const GaLabels = { LICENSE_PLATE_LOOKUP: "License_Plate_Look_Up", VIN_LOOKUP: "Vin_Look_Up", ADDRESS_LOOKUP: "Address_Look_up", + YES: "yes", + NO: "no", }; const ValueToLogTypes = { diff --git a/src/constants/header-keys.js b/src/constants/header-keys.js index 7caaef65c..2f7598244 100644 --- a/src/constants/header-keys.js +++ b/src/constants/header-keys.js @@ -4,5 +4,5 @@ export const headerKeys = { REFERRAL_SEQUENCE_NUMBER: "X-Referral-Sequence-Number", SESSION_SEQUENCE_NUMBER: "X-Session-Sequence-Number", TRANSACTION_ID: "X-Transaction-Id", - EON: "X-EON", + EON: "X-Enterprise-Order-Number", }; diff --git a/src/layouts/confirmation/confirmation.vue b/src/layouts/confirmation/confirmation.vue index acb97cba2..72ee038db 100644 --- a/src/layouts/confirmation/confirmation.vue +++ b/src/layouts/confirmation/confirmation.vue @@ -93,7 +93,6 @@ import textBlock from "@/digital-components/text-block/text-block"; //Supporting files import baseMixin from "@/mixins/base-mixin.js"; -import analyticsMixin from "@/mixins/analytics-mixin"; import store from "@/store"; import { storeActions } from "@/constants/store-actions"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; @@ -107,7 +106,8 @@ import { get12HourTimeFormat, get12HourTimeMobileFormat } from "@/helpers/date-h import { coverageStatus } from "@/constants/insurance"; import { getDisplayTextForDurationLength } from "@/helpers/duration-length-helper"; import { experimentSettings } from "@/constants/experiments"; -import experimentMixin from "@/mixins/experiment-mixin.js"; +import { partTypeStrings } from "@/constants/part-type-strings"; +import { containsLineItemWithPartType } from "@/helpers/service-package-helper"; export default { name: "confirmation", @@ -169,8 +169,17 @@ export default { }; }, computed: { + isRecalibrationOnOrder() { + return containsLineItemWithPartType( + partTypeStrings.RECALIBRATION, + this?.lineItems?.supportingItems + ); + }, shouldHideRecalibration() { - return this.getSettingValue(experimentSettings.RECAL_PRICE_REMOVE); + return ( + this.getSettingValue(experimentSettings.RECAL_PRICE_REMOVE)?.toLowerCase() === + "true" && this.isRecalibrationOnOrder + ); }, ShowCart() { if (this.isPia && this.submittedOrder?.settledTenderAmount == 0) { diff --git a/src/layouts/payment-method/payment-method.vue b/src/layouts/payment-method/payment-method.vue index 9b7222bfc..3c8d23502 100644 --- a/src/layouts/payment-method/payment-method.vue +++ b/src/layouts/payment-method/payment-method.vue @@ -593,8 +593,8 @@ export default { }, shouldHideRecalibration() { return ( - this.getSettingValue(experimentSettings.RECAL_PRICE_REMOVE).toLowerCase() === "true" && - this.isRecalibrationOnOrder + this.getSettingValue(experimentSettings.RECAL_PRICE_REMOVE)?.toLowerCase() === + "true" && this.isRecalibrationOnOrder ); }, showApplePay() { diff --git a/src/layouts/quote/quote.spec.js b/src/layouts/quote/quote.spec.js index ef5c945a3..d9c1ffabf 100644 --- a/src/layouts/quote/quote.spec.js +++ b/src/layouts/quote/quote.spec.js @@ -7,6 +7,7 @@ import store from "@/store"; import * as navigateToHeritage from "@/helpers/heritage-integration/navigation-helper"; import { nextTick } from "vue"; import { experimentSettings } from "@/constants/experiments"; +import experimentMixin from "@/mixins/experiment-mixin.js"; import baseMixin from "@/mixins/base-mixin.js"; import { containsLineItemWithPartType } from "../../helpers/service-package-helper"; @@ -746,6 +747,14 @@ function setupMocks({ customMountOptions }) { ...customMountOptions, }); + mountOptions.mixins = [ + { + methods: { + getSettingValue: jest.fn(), + }, + }, + ]; + baseMixin.methods.ResetExternalParamsAndHideModal = jest.fn(); baseMixin.methods.isFormValid = jest.fn().mockReturnValue(true); mountOptions.global.mocks["$store"] = store; diff --git a/src/layouts/quote/quote.vue b/src/layouts/quote/quote.vue index a5222b49b..8aedcc754 100644 --- a/src/layouts/quote/quote.vue +++ b/src/layouts/quote/quote.vue @@ -113,8 +113,7 @@ import { storeActions } from "@/constants/store-actions"; import { deepClone } from "@/helpers/object-helper"; import store from "@/store"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; -import { experimentUniverses, experimentSettings } from "@/constants/experiments"; -import { getSessionKeyValue, getUserIdValue } from "@/helpers/heritage-integration/cookie-helper"; +import { experimentSettings } from "@/constants/experiments"; import { required } from "@/helpers/validation-rules"; import { errorMessages } from "@/constants/error-messages"; import { Form, defineRule } from "vee-validate"; @@ -129,7 +128,10 @@ 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 } from "@/helpers/service-package-helper"; +import { + containsLineItemWithPartType, + findLineItemsWithPartType, +} from "@/helpers/service-package-helper"; import { nextTick } from "vue"; import { packageNames } from "@/constants/package-names"; import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; @@ -296,8 +298,12 @@ export default { const serviceLocationState = store.getters.order.serviceLocation.state; // Override if coming back from QuoteDetails. Remove override after Quote release const isInsuranceOverrideValue = 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 && @@ -309,15 +315,17 @@ export default { } else if (defaultIsInsuranceSelectedValue != null) { return defaultIsInsuranceSelectedValue; } else { - // TEMPORARY CODE WHILE DEVELOPING if (availableLineItems) { - let tierOnePackagePriceTemp = baseMixin.methods.getTierOnePackagePrice( - baseMixin.methods.filterOutFees(availableLineItems) - ); - let whatToReturn = baseMixin.methods.getTierOnePackagePrice( - baseMixin.methods.filterOutFees(availableLineItems) - ) > insuranceThreshold; - return whatToReturn; // TODO AJC: TIGHTEN THIS BACK UP AFTER TESTING + const lineItemsForCalculatingPrice = hideRecalCost + ? baseMixin.methods.filterOutFees( + baseMixin.methods.filterOutRecalibration(availableLineItems) // Strip out recal before filtering out fees + ) + : baseMixin.methods.filterOutFees(availableLineItems); + + return ( + baseMixin.methods.getTierOnePackagePrice(lineItemsForCalculatingPrice) > + insuranceThreshold + ); // compare base price vs arbitrary threshold (representing insurance price) } else { return null; } @@ -328,15 +336,20 @@ export default { experimentSettings.INSURANCE_TAB_TO_DISPLAY_THRESHOLD_INTERNAL ); - if (!store.getters.externalParameterState?.isExternalParameter) { // there are no external parameters + if (!store.getters.externalParameterState?.isExternalParameter) { + // there are no external parameters if (internalThreshold) thresholdToUse = internalThreshold; - vm.isInsuranceSelected = getIsInsuranceSelectedValue(vm.availableLineItems, thresholdToUse); + vm.isInsuranceSelected = getIsInsuranceSelectedValue( + vm.availableLineItems, + thresholdToUse + ); baseMixin.methods.ResetExternalParamsAndHideModal(); + } else { + // there ARE external parameters - } else { // there ARE external parameters - - if (store.getters.externalParameterQuote.isInsurance == true) { // did user intentionally select insurance? + if (store.getters.externalParameterQuote.isInsurance == true) { + // did user intentionally select insurance? vm.isInsuranceSelected = true; vm.servicePackage = store.getters.externalParameterQuote.servicePackage; await nextTick(); @@ -348,7 +361,9 @@ export default { } } else { // did user come from external source (LeadGen)? - let externalSource = store.getters.externalParameterSource ? store.getters.externalParameterSource : null; + let externalSource = store.getters.externalParameterSource + ? store.getters.externalParameterSource + : null; // Business logic to determine what "external" source is if (externalSource?.includes("LeadGen")) { @@ -359,10 +374,12 @@ export default { } else { if (internalThreshold) thresholdToUse = internalThreshold; } - vm.isInsuranceSelected = getIsInsuranceSelectedValue(vm.availableLineItems, thresholdToUse); + vm.isInsuranceSelected = getIsInsuranceSelectedValue( + vm.availableLineItems, + thresholdToUse + ); baseMixin.methods.ResetExternalParamsAndHideModal(); } - } }); }, @@ -387,11 +404,23 @@ export default { this?.availableLineItems ); }, - shouldHideRecalibration() { - const recalSettingValue = experimentMixin.methods.getSettingValue( - experimentSettings.RECAL_PRICE_REMOVE + isServicePackageDiscountOnOrder() { + return containsLineItemWithPartType( + partTypeStrings.SERVICE_PACKAGE_DISCOUNT, + this.availableLineItems + ); + }, + servicePackageDiscountParts() { + return findLineItemsWithPartType( + partTypeStrings.SERVICE_PACKAGE_DISCOUNT, + this.availableLineItems + ); + }, + shouldHideRecalibration() { + return ( + this.getSettingValue(experimentSettings.RECAL_PRICE_REMOVE)?.toLowerCase() === + "true" && this.isRecalibrationOnOrder ); - return recalSettingValue; }, showAfterpayBanner() { return ( @@ -549,6 +578,46 @@ export default { ); } }); + this.$nextTick(() => { + const availablePackageNames = this.$refs.servicePackage.servicePackageAnswers; + var eventLabel = ""; + availablePackageNames?.forEach((tier) => { + if (tier) { + let lineItemsToPrice = this.availableLineItems; + if (this.isRecalibrationOnOrder && this.shouldHideRecalibration) { + lineItemsToPrice = lineItemsToPrice?.filter((item) => { + return item.partType != partTypeStrings.RECALIBRATION; + }); + } + if (this.isServicePackageDiscountOnOrder) { + lineItemsToPrice = lineItemsToPrice?.filter((item) => { + return ( + item.partType != this.servicePackageDiscountParts[0].partType + ); + }); + } + let price = 0; + if (tier.value == "TierTwo" && this.isServicePackageDiscountOnOrder) { + lineItemsToPrice.push(...this.servicePackageDiscountParts); + } + price += baseMixin.methods.getTierOnePackagePrice( + baseMixin.methods.filterOutFees(lineItemsToPrice) + ); + price += this.$refs.servicePackage.getVapsPrice(tier.value); + const formattedPrice = parseFloat(price).toFixed(2); + eventLabel += tier.value + "_" + formattedPrice; + } + eventLabel += ","; + }); + eventLabel = eventLabel.slice(0, -1); + + this.pushEventToGA( + "quote", + this.GaActions.SERVICE_PACKAGE_PRICE, + eventLabel, + true + ); + }); }, }, mounted() { diff --git a/src/layouts/quote/service-package-question/service-package-question.vue b/src/layouts/quote/service-package-question/service-package-question.vue index 4ef027ad5..1a9ed4ed8 100644 --- a/src/layouts/quote/service-package-question/service-package-question.vue +++ b/src/layouts/quote/service-package-question/service-package-question.vue @@ -15,7 +15,6 @@ import buttonQuestion from "@/digital-components/button-question/button-question"; import baseMixin from "@/mixins/base-mixin.js"; import { processIfStatements } from "@/helpers/cms-content-helper"; -import { damageLocationsSelected as glassLocations } from "@/constants/damage-locations-selected"; import servicePackageRadio from "./service-package-radio/service-package-radio"; import { partTypeStrings } from "@/constants/part-type-strings"; import { packageNames } from "@/constants/package-names"; @@ -237,7 +236,7 @@ export default { return (this.isInsuranceSelected ? "As little as $" : "$") + formattedPriceFloat; }, getPackagePrice(packageName, { discountedPrice = false, servicePackageDiscount = false }) { - var lineItemsToPrice = [...this.nullSafeAvailableLineItems]; + let lineItemsToPrice = [...this.nullSafeAvailableLineItems]; if (this.isRecalibrationOnOrder && this.shouldHideRecalibration) { lineItemsToPrice = lineItemsToPrice.filter((item) => { diff --git a/src/layouts/service-location/service-location.vue b/src/layouts/service-location/service-location.vue index 022fa1183..1922d098a 100644 --- a/src/layouts/service-location/service-location.vue +++ b/src/layouts/service-location/service-location.vue @@ -433,6 +433,18 @@ export default { if (shopProviderData) { this.shopProviderData = shopProviderData; } + + var gaLabel = this.GaLabels.NO; + if (this.isServiceableMobile) { + gaLabel = this.GaLabels.YES; + } + + this.pushEventToGA( + this.GaCategories.APPOINTMENT, + this.GaActions.MOBILE_AVAILABLE, + gaLabel, + true + ); }, setContainsMilitaryBase(val) { if (this.zipContainsMilitaryBase !== val) { @@ -607,7 +619,7 @@ export default { }, false ); - console.log(this.billToAccountNumber); + if (this.billToAccountNumber) { this.dispatchStoreAction( this.storeActions.SAVE_BILL_TO_ACCOUNT_NUMBER, diff --git a/src/layouts/service-location/shop-question/shop-question.vue b/src/layouts/service-location/shop-question/shop-question.vue index 1b4d7dea8..d77a7eb04 100644 --- a/src/layouts/service-location/shop-question/shop-question.vue +++ b/src/layouts/service-location/shop-question/shop-question.vue @@ -52,6 +52,7 @@ import { nextTick } from "vue"; import { getAvailabilityRating } from "@/layouts/service-location/helpers/service-location-helper/service-location-helper"; import { Provider } from "@/layouts/service-location/classes/provider"; +import { AppointmentTypeStrings } from "@/constants/schedule-constants"; defineRule("option-required", required(errorMessages.OPTION_REQUIRED)); @@ -125,6 +126,8 @@ export default { }, methods: { async getNextShopsFromList(numberToGet = 3) { + const logFirstShopsDisplayed = this.shopIndex == 0; + const shopIterator = (array, n) => { const l = array.length; return () => { @@ -160,6 +163,27 @@ export default { }; }); + var gaAction = this.GaActions.MORE_LOCATIONS_CLICKED; + if (logFirstShopsDisplayed) { + gaAction = this.GaActions.SHOPS_FIRST_DISPLAYED; + } + if (this.selectedAppointmentType === AppointmentTypeStrings.IN_SHOP) { + var shops = mappedData.map((shop) => { + if (shop.value.length > 5 && shop.value.startsWith("00")) { + return shop.value.substring(1); + } else { + return shop.value; + } + }); + + this.pushEventToGA( + this.GaCategories.SERVICE_LOCATION, + gaAction, + shops.join("/"), + true + ); + } + if (this.answers.length === 0) { this.answers = mappedData; } else { diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index e0517080e..b66ad59dc 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -211,6 +211,13 @@ export default { payload.glassToReplace = glassString; } + //EON + if (order.eon) { + payload.eon = order.eon; + } else { + payload.eon = ""; + } + // Work Order Id if (order.workOrderId) { const parsedId = parseInt(order.workOrderId); diff --git a/src/store/index.js b/src/store/index.js index c0cb2e0e7..89d0db2a3 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1537,12 +1537,9 @@ export const actions = { ? context.getters.payment.billToAccountNumber : applicationConfig.CASH_DEFAULT_BILL_TO_ACCOUNT_NUMBER; - const covStatus = context.getters.payment?.insuranceCoverage?.coverageStatus; - const covSubStatus = context.getters.payment?.insuranceCoverage?.coverageSubStatus; - return globalMethods.callHttpClient({ method: endpoints.GetMobileFeePart.method, - endpoint: `${endpoints.GetMobileFeePart.url}/${damageType}/${parentAccountNumber}/${billToAccountNumber}/${covStatus}/${covSubStatus}`, + endpoint: `${endpoints.GetMobileFeePart.url}/${damageType}/${parentAccountNumber}/${billToAccountNumber}`, logApiCall: true, pageNameToLog: pageNameToLog, });