Merge pull request #2387 from Safelite/feature/CASH-423

AB Test: Skip Quote for Recal Vehicles
This commit is contained in:
chloeherdsafelite 2025-04-01 09:34:21 -04:00 committed by GitHub
commit 879f08f13d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 95 additions and 20 deletions

View file

@ -2,6 +2,7 @@ const experimentUniverses = {
CONCEPT_FUNNEL: "ConceptFunnel", CONCEPT_FUNNEL: "ConceptFunnel",
RECAL_PRICE_REMOVAL: "NextGen_RecalPriceRemoval", RECAL_PRICE_REMOVAL: "NextGen_RecalPriceRemoval",
MSR: "MSR", MSR: "MSR",
IGQ_SkipQuote: "NextGen_IGQSkipToInsurance",
}; };
const experimentSettings = { const experimentSettings = {
@ -22,6 +23,7 @@ const experimentSettings = {
WAITLIST_THRESHOLD_DAYS: "Waitlist_Threshold_Days", WAITLIST_THRESHOLD_DAYS: "Waitlist_Threshold_Days",
PRICING_BY_DAY: "DisplayPricingByDay", PRICING_BY_DAY: "DisplayPricingByDay",
FOSTER_LOVE: "DisplayFosterLove", FOSTER_LOVE: "DisplayFosterLove",
SKIP_TO_INSURANCE: "SkipToInsurance",
}; };
const experimentTriggers = { const experimentTriggers = {

View file

@ -3,3 +3,9 @@ export const packageNames = {
TIER_TWO: "TierTwo", TIER_TWO: "TierTwo",
TIER_THREE: "TierThree", TIER_THREE: "TierThree",
}; };
export const externalParamPackageLabels = {
GLASS_ONLY: "glassonly",
STANDARD: "standard",
PREMIUM: "premium",
};

View file

@ -1,6 +1,6 @@
import { partTypeStrings } from "@/constants/part-type-strings"; import { partTypeStrings } from "@/constants/part-type-strings";
import { damageLocationsSelected as glassLocations } from "@/constants/damage-locations-selected"; 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 baseMixin from "@/mixins/base-mixin.js";
import { getItemsWithoutRecalParts } from "@/helpers/recal-helper"; import { getItemsWithoutRecalParts } from "@/helpers/recal-helper";
import { import {
@ -238,9 +238,9 @@ export function getHighestFullySatisfiedTier(glassToReplace, availableLineItems,
export function getPackageNameByType(packageType) { export function getPackageNameByType(packageType) {
const package_names = { const package_names = {
glassonly: packageNames.TIER_ONE, [externalParamPackageLabels.GLASS_ONLY]: packageNames.TIER_ONE,
standard: packageNames.TIER_TWO, [externalParamPackageLabels.STANDARD]: packageNames.TIER_TWO,
premium: packageNames.TIER_THREE, [externalParamPackageLabels.PREMIUM]: packageNames.TIER_THREE,
}; };
return package_names[packageType] || null; return package_names[packageType] || null;
} }

View file

@ -836,6 +836,7 @@ describe("quote.vue", () => {
nextFunction nextFunction
); );
await nextTick(); await nextTick();
await nextTick();
expect(nextFunction).toHaveBeenCalled(); expect(nextFunction).toHaveBeenCalled();
expect(baseMixin.methods.isFormValid).toHaveBeenCalled(); expect(baseMixin.methods.isFormValid).toHaveBeenCalled();
expect(baseMixin.methods.isFormValid()).toBe(true); expect(baseMixin.methods.isFormValid()).toBe(true);

View file

@ -134,7 +134,7 @@ import { storeActions } from "@/constants/store-actions";
import { deepClone } from "@/helpers/object-helper"; import { deepClone } from "@/helpers/object-helper";
import store from "@/store"; import store from "@/store";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; 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 { required } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages"; import { errorMessages } from "@/constants/error-messages";
import { Form, defineRule } from "vee-validate"; import { Form, defineRule } from "vee-validate";
@ -154,12 +154,18 @@ import {
findLineItemsWithPartType, findLineItemsWithPartType,
} from "@/helpers/service-package-helper"; } from "@/helpers/service-package-helper";
import { nextTick } from "vue"; 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 { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
import { containsRecalParts } from "@/helpers/recal-helper"; import { containsRecalParts } from "@/helpers/recal-helper";
import { externalParameterStatus } from "@/constants/external-parameters"; import { externalParameterStatus } from "@/constants/external-parameters";
import { saveQuote } from "@/helpers/heritage-integration/order-helper.js"; import { saveQuote } from "@/helpers/heritage-integration/order-helper.js";
import { getBoolFromString } from "@/helpers/boolean-helper"; 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)); defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
@ -189,6 +195,31 @@ export default {
"quote" "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 = [ const promiseResultMap = [
{ {
resultKey: "cmsContent", resultKey: "cmsContent",
@ -213,6 +244,12 @@ export default {
const isEmailInStoreOnPageLoad = emailFromStore?.length > 0; const isEmailInStoreOnPageLoad = emailFromStore?.length > 0;
const showSaveProgressPopup = isEmailInStoreOnPageLoad ? false : true; const showSaveProgressPopup = isEmailInStoreOnPageLoad ? false : true;
const showSaveProgressModal = 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); const lineItems = deepClone(store.getters.order.lineItems);
lineItems.vaps = lineItems.vaps ?? []; lineItems.vaps = lineItems.vaps ?? [];
@ -302,6 +339,7 @@ export default {
vm.availableLineItems = pricingResults; vm.availableLineItems = pricingResults;
vm.holdParentAccountNumber = holdParentAccountNumber; vm.holdParentAccountNumber = holdParentAccountNumber;
vm.holdBillToAccountNumber = holdBillToAccountNumber; vm.holdBillToAccountNumber = holdBillToAccountNumber;
vm.skipToInsurance = shouldSkipToInsurance;
if (revalidatePromoResponse) { if (revalidatePromoResponse) {
const revalidateAlerts = buildToastMessagesFromRevalidateOrValidatePromoResponse( const revalidateAlerts = buildToastMessagesFromRevalidateOrValidatePromoResponse(
@ -434,21 +472,16 @@ export default {
} }
} }
// did user intentionally select insurance? const insuranceSelection = true;
vm.isInsuranceSelected = true; const servicePackage = store.getters.externalParameterQuote.servicePackage;
vm.servicePackage = store.getters.externalParameterQuote.servicePackage;
await nextTick();
if ( if (
vm.servicePackage != null && servicePackage != null &&
isExternalParameter === externalParameterStatus.ACTIVE isExternalParameter === externalParameterStatus.ACTIVE
) { ) {
const isValid = await baseMixin.methods.isFormValid(vm.$refs.theForm); await vm.skip(insuranceSelection, servicePackage);
if (isValid) {
baseMixin.methods.ResetExternalParamsAndHideModal();
vm.forwardButtonAction();
}
} }
baseMixin.methods.ResetExternalParamsAndHideModal(); baseMixin.methods.ResetExternalParamsAndHideModal();
} else { } else {
if (externalThreshold) thresholdToUse = externalThreshold; if (externalThreshold) thresholdToUse = externalThreshold;
@ -467,6 +500,11 @@ export default {
baseMixin.methods.ResetExternalParamsAndHideModal(); baseMixin.methods.ResetExternalParamsAndHideModal();
} }
} }
if (vm.skipToInsurance && !vm.showSaveProgressPopup) {
// skip ahead only if no email popup.
await vm.skip(true, externalParamPackageLabels.GLASS_ONLY);
}
}); });
}, },
data() { data() {
@ -482,6 +520,7 @@ export default {
showSaveProgressModal: null, showSaveProgressModal: null,
isSaveProgressComplete: null, isSaveProgressComplete: null,
packageNumber: null, packageNumber: null,
skipToInsurance: null,
}; };
}, },
mounted() { mounted() {
@ -584,8 +623,13 @@ export default {
updateSaveProgressAsSaved() { updateSaveProgressAsSaved() {
this.showSaveProgressModal = false; this.showSaveProgressModal = false;
}, },
closeSaveProgressPopup() { async closeSaveProgressPopup() {
this.showSaveProgressPopup = false; this.showSaveProgressPopup = false;
if (this.skipToInsurance) {
// skip ahead now if in IGQ skip experiment
await this.skip(true, externalParamPackageLabels.GLASS_ONLY);
}
}, },
forwardButtonAction() { forwardButtonAction() {
this.dispatchStoreAction( this.dispatchStoreAction(
@ -729,6 +773,20 @@ export default {
this.pushEventToGA("quote", this.GaActions.SERVICE_PACKAGE_PRICE, eventLabel, true); 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: { watch: {
lineItemsCloneForWatcher: { lineItemsCloneForWatcher: {

View file

@ -102,6 +102,11 @@ export default {
this.$emit("servicePackageDiscountSelected", supportingItems); this.$emit("servicePackageDiscountSelected", supportingItems);
}, },
servicePackage(newValue) {
if (newValue !== null) {
this.selectDefaultPackage();
}
},
}, },
computed: { computed: {
nullSafeAvailableLineItems() { nullSafeAvailableLineItems() {

View file

@ -230,6 +230,12 @@ export default {
return "widget-name-" + this.cmsWidgetName; return "widget-name-" + this.cmsWidgetName;
}, },
}, },
mounted() {
if (!store.getters.externalParameterState?.isExternalParameter) {
showFmgLoadingModal(false);
}
},
}; };
function encodeUriData(payload) { function encodeUriData(payload) {

View file

@ -433,9 +433,6 @@ router.afterEach(async (to, from) => {
await saveSession({ pageNameToLog: to.query.fmgPage }); await saveSession({ pageNameToLog: to.query.fmgPage });
} }
} }
if (!store.getters.externalParameterState?.isExternalParameter) {
showFmgLoadingModal(false);
}
// Push page view to GA // Push page view to GA
analyticsMixin.methods.pushPageViewToGA(); analyticsMixin.methods.pushPageViewToGA();