From 2e3af146e6506bad8fd55327eb079ec89d69eb60 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Mon, 31 Mar 2025 15:32:02 -0400 Subject: [PATCH 1/3] Add page-skipping logic to quote --- src/constants/experiments.js | 2 + src/constants/package-names.js | 6 ++ src/helpers/service-package-helper.js | 8 +- src/layouts/quote/quote.vue | 84 ++++++++++++++++--- .../service-package-question.vue | 5 ++ 5 files changed, 88 insertions(+), 17 deletions(-) diff --git a/src/constants/experiments.js b/src/constants/experiments.js index 09dfc4140..d64849466 100644 --- a/src/constants/experiments.js +++ b/src/constants/experiments.js @@ -2,6 +2,7 @@ const experimentUniverses = { CONCEPT_FUNNEL: "ConceptFunnel", RECAL_PRICE_REMOVAL: "NextGen_RecalPriceRemoval", MSR: "MSR", + IGQ_SkipQuote: "NextGen_IGQSkipToInsurance", }; const experimentSettings = { @@ -22,6 +23,7 @@ const experimentSettings = { WAITLIST_THRESHOLD_DAYS: "Waitlist_Threshold_Days", PRICING_BY_DAY: "DisplayPricingByDay", FOSTER_LOVE: "DisplayFosterLove", + SKIP_TO_INSURANCE: "SkipToInsurance", }; const experimentTriggers = { diff --git a/src/constants/package-names.js b/src/constants/package-names.js index 221a75a49..0115daa61 100644 --- a/src/constants/package-names.js +++ b/src/constants/package-names.js @@ -3,3 +3,9 @@ export const packageNames = { TIER_TWO: "TierTwo", TIER_THREE: "TierThree", }; + +export const externalParamPackageLabels = { + GLASS_ONLY: "glassonly", + STANDARD: "standard", + PREMIUM: "premium", +}; diff --git a/src/helpers/service-package-helper.js b/src/helpers/service-package-helper.js index fcbe4aee3..6e0a6743a 100644 --- a/src/helpers/service-package-helper.js +++ b/src/helpers/service-package-helper.js @@ -1,6 +1,6 @@ import { partTypeStrings } from "@/constants/part-type-strings"; import { damageLocationsSelected as glassLocations } from "@/constants/damage-locations-selected"; -import { packageNames } from "@/constants/package-names"; +import { packageNames, externalParamPackageLabels } from "@/constants/package-names"; import baseMixin from "@/mixins/base-mixin.js"; import { getItemsWithoutRecalParts } from "@/helpers/recal-helper"; import { @@ -238,9 +238,9 @@ export function getHighestFullySatisfiedTier(glassToReplace, availableLineItems, export function getPackageNameByType(packageType) { const package_names = { - glassonly: packageNames.TIER_ONE, - standard: packageNames.TIER_TWO, - premium: packageNames.TIER_THREE, + [externalParamPackageLabels.GLASS_ONLY]: packageNames.TIER_ONE, + [externalParamPackageLabels.STANDARD]: packageNames.TIER_TWO, + [externalParamPackageLabels.PREMIUM]: packageNames.TIER_THREE, }; return package_names[packageType] || null; } diff --git a/src/layouts/quote/quote.vue b/src/layouts/quote/quote.vue index 615662013..0ff86494d 100644 --- a/src/layouts/quote/quote.vue +++ b/src/layouts/quote/quote.vue @@ -134,7 +134,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 { experimentSettings } from "@/constants/experiments"; +import { experimentSettings, experimentUniverses } from "@/constants/experiments"; import { required } from "@/helpers/validation-rules"; import { errorMessages } from "@/constants/error-messages"; import { Form, defineRule } from "vee-validate"; @@ -154,12 +154,18 @@ import { findLineItemsWithPartType, } from "@/helpers/service-package-helper"; import { nextTick } from "vue"; -import { packageNames } from "@/constants/package-names"; +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"; defineRule("option-required", required(errorMessages.OPTION_REQUIRED)); @@ -189,6 +195,31 @@ export default { "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.query.fmgPage, + experiment: skipQuoteExperiment, + }, + "quote", + false + ); + } + const promiseResultMap = [ { resultKey: "cmsContent", @@ -213,6 +244,12 @@ export default { const isEmailInStoreOnPageLoad = emailFromStore?.length > 0; const showSaveProgressPopup = isEmailInStoreOnPageLoad ? false : true; 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 ?? []; @@ -302,6 +339,7 @@ export default { vm.availableLineItems = pricingResults; vm.holdParentAccountNumber = holdParentAccountNumber; vm.holdBillToAccountNumber = holdBillToAccountNumber; + vm.skipToInsurance = shouldSkipToInsurance; if (revalidatePromoResponse) { const revalidateAlerts = buildToastMessagesFromRevalidateOrValidatePromoResponse( @@ -434,21 +472,16 @@ export default { } } - // did user intentionally select insurance? - vm.isInsuranceSelected = true; - vm.servicePackage = store.getters.externalParameterQuote.servicePackage; - await nextTick(); + const insuranceSelection = true; + const servicePackage = store.getters.externalParameterQuote.servicePackage; if ( - vm.servicePackage != null && + servicePackage != null && isExternalParameter === externalParameterStatus.ACTIVE ) { - const isValid = await baseMixin.methods.isFormValid(vm.$refs.theForm); - if (isValid) { - baseMixin.methods.ResetExternalParamsAndHideModal(); - vm.forwardButtonAction(); - } + await vm.skip(insuranceSelection, servicePackage); } + baseMixin.methods.ResetExternalParamsAndHideModal(); } else { if (externalThreshold) thresholdToUse = externalThreshold; @@ -467,6 +500,11 @@ export default { baseMixin.methods.ResetExternalParamsAndHideModal(); } } + + if (vm.skipToInsurance && !vm.showSaveProgressPopup) { + // skip ahead only if no email popup. + await vm.skip(true, externalParamPackageLabels.GLASS_ONLY); + } }); }, data() { @@ -482,6 +520,7 @@ export default { showSaveProgressModal: null, isSaveProgressComplete: null, packageNumber: null, + skipToInsurance: null, }; }, mounted() { @@ -584,8 +623,13 @@ export default { updateSaveProgressAsSaved() { this.showSaveProgressModal = false; }, - closeSaveProgressPopup() { + async closeSaveProgressPopup() { this.showSaveProgressPopup = false; + + if (this.skipToInsurance) { + // skip ahead now if in IGQ skip experiment + await this.skip(true, externalParamPackageLabels.GLASS_ONLY); + } }, forwardButtonAction() { this.dispatchStoreAction( @@ -729,6 +773,20 @@ export default { this.pushEventToGA("quote", this.GaActions.SERVICE_PACKAGE_PRICE, eventLabel, true); }); }, + 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: { 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 ab4772107..68f031768 100644 --- a/src/layouts/quote/service-package-question/service-package-question.vue +++ b/src/layouts/quote/service-package-question/service-package-question.vue @@ -102,6 +102,11 @@ export default { this.$emit("servicePackageDiscountSelected", supportingItems); }, + servicePackage(newValue) { + if (newValue !== null) { + this.selectDefaultPackage(); + } + }, }, computed: { nullSafeAvailableLineItems() { From 8f0c5ada0433551924006466ab407ffa1f49aeab Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Mon, 31 Mar 2025 15:32:24 -0400 Subject: [PATCH 2/3] Loading modal refactor --- src/mixins/base-mixin.js | 6 ++++++ src/router/index.js | 3 --- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/mixins/base-mixin.js b/src/mixins/base-mixin.js index 6fd6075ab..151d17dd9 100644 --- a/src/mixins/base-mixin.js +++ b/src/mixins/base-mixin.js @@ -230,6 +230,12 @@ export default { return "widget-name-" + this.cmsWidgetName; }, }, + + mounted() { + if (!store.getters.externalParameterState?.isExternalParameter) { + showFmgLoadingModal(false); + } + }, }; function encodeUriData(payload) { diff --git a/src/router/index.js b/src/router/index.js index 2230a652d..345790734 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -433,9 +433,6 @@ router.afterEach(async (to, from) => { await saveSession({ pageNameToLog: to.query.fmgPage }); } } - if (!store.getters.externalParameterState?.isExternalParameter) { - showFmgLoadingModal(false); - } // Push page view to GA analyticsMixin.methods.pushPageViewToGA(); From 6e0f9d8a20b986a94f4d84b9799dc53ea83b0615 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Mon, 31 Mar 2025 15:38:03 -0400 Subject: [PATCH 3/3] Unit test quirk --- src/layouts/quote/quote.spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/layouts/quote/quote.spec.js b/src/layouts/quote/quote.spec.js index a87d6b0a6..49b90cfb3 100644 --- a/src/layouts/quote/quote.spec.js +++ b/src/layouts/quote/quote.spec.js @@ -836,6 +836,7 @@ describe("quote.vue", () => { nextFunction ); await nextTick(); + await nextTick(); expect(nextFunction).toHaveBeenCalled(); expect(baseMixin.methods.isFormValid).toHaveBeenCalled(); expect(baseMixin.methods.isFormValid()).toBe(true);