diff --git a/src/constants/session-storage.js b/src/constants/session-storage.js new file mode 100644 index 000000000..589dd6b71 --- /dev/null +++ b/src/constants/session-storage.js @@ -0,0 +1,3 @@ +export const sessionStorageKeyConstants = { + SUBMITTED_STATE: "submittedState", +}; diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js index e7cb38953..1e8945c49 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -109,9 +109,9 @@ const storeActions = { SAVE_IS_RECAL_ACK_OPT_IN: "saveIsRecalAckOptIn", SAVE_IS_MSR_FEE_APPLICABLE: "saveIsMSRFeeApplicable", - CREATE_SUBMITTED_ORDER: "createSubmittedOrder", - RESET_SUBMITTED_ORDER: "resetSubmittedOrder", - ADD_DONATION_TO_SUBMITTED_ORDER: "addDonationToSubmittedOrder", + CREATE_SUBMITTED_STATE: "createSubmittedState", + RESET_SUBMITTED_STATE: "resetSubmittedState", + ADD_DONATION_TO_SUBMITTED_STATE: "addDonationToSubmittedState", RESET_EXTERNAL_PARAMETER_STATE: "resetExternalParameterState", UPDATE_EXTERNAL_PARAMETER_MMS: "updateExternalParameterMMS", diff --git a/src/helpers/experiment-helper.js b/src/helpers/experiment-helper.js new file mode 100644 index 000000000..c0c094529 --- /dev/null +++ b/src/helpers/experiment-helper.js @@ -0,0 +1,20 @@ +export function getExperimentSettingsFromExperimentList(experimentList) { + return ( + experimentList + .filter((x) => !!x.isActive) + .map((x) => x.settings) + .reduce((r, c) => Object.assign(r, c), {}) ?? {} + ); +} + +export function getSettingValue(settingName, experimentSettings) { + return hasSetting(settingName, experimentSettings) ? experimentSettings[settingName] : null; +} + +export function hasSettingEqualTo(settingName, settingValue, experimentSettings) { + return experimentSettings[settingName] == settingValue; +} + +export function hasSetting(settingName, experimentSettings) { + return Object.hasOwn(experimentSettings, settingName); +} diff --git a/src/layouts/confirmation/add-to-calendar/add-to-calendar.vue b/src/layouts/confirmation/add-to-calendar/add-to-calendar.vue index 1fef02247..c986e0366 100644 --- a/src/layouts/confirmation/add-to-calendar/add-to-calendar.vue +++ b/src/layouts/confirmation/add-to-calendar/add-to-calendar.vue @@ -115,7 +115,7 @@ export default { }, ServiceType() { const isRepair = this.getSubmittedOrder()?.damage.isRepair; - const funnelHasRecalibrationPart = store.getters.isRecalibrationOnSubmittedOrder; + const funnelHasRecalibrationPart = store.getters.isRecalibrationOnSubmittedState; if (!isRepair) { if (funnelHasRecalibrationPart) { return serviceType.REPLACEMENT_AND_RECALIBRATION; diff --git a/src/layouts/confirmation/confirmation.vue b/src/layouts/confirmation/confirmation.vue index 8e49f298d..40fa73dbe 100644 --- a/src/layouts/confirmation/confirmation.vue +++ b/src/layouts/confirmation/confirmation.vue @@ -65,7 +65,7 @@ :isNoComp="isNoComp" :isMSRFeeApplicable="isMSRFeeApplicable" /> -
+
({ @@ -147,6 +146,9 @@ beforeEach(() => { jest.restoreAllMocks(); jest.clearAllMocks(); store.getters = { + applicationUser: { + experiments: [], + }, order: { schedule: { date: "2019-01-01", diff --git a/src/layouts/service-location/service-location.spec.js b/src/layouts/service-location/service-location.spec.js index fb34f3921..7dccc1b1e 100644 --- a/src/layouts/service-location/service-location.spec.js +++ b/src/layouts/service-location/service-location.spec.js @@ -174,6 +174,7 @@ const mockMixin = { beforeEach(() => { store.getters = { + applicationUser: { experiments: [] }, lineItems: { supportingItems: [ { diff --git a/src/layouts/service-zip/service-zip.spec.js b/src/layouts/service-zip/service-zip.spec.js index 773157980..3346970cf 100644 --- a/src/layouts/service-zip/service-zip.spec.js +++ b/src/layouts/service-zip/service-zip.spec.js @@ -173,9 +173,14 @@ function resetMockStoreData() { }; } +const applicationUser = { + experiments: [], +}; + function applyMockStoreDataToGetters() { store.getters = { experimentSettings: mockExperimentSettings, + applicationUser: applicationUser, order: mockStoreData, damage: mockStoreData.damage, payment: mockStoreData.payment, diff --git a/src/layouts/vehicle/vehicle.vue b/src/layouts/vehicle/vehicle.vue index 12e7268fa..5f769b9b7 100644 --- a/src/layouts/vehicle/vehicle.vue +++ b/src/layouts/vehicle/vehicle.vue @@ -448,7 +448,7 @@ export default { this.displayNoServiceAlert = false; }, async forwardButtonAction() { - await this.dispatchStoreAction(storeActions.RESET_SUBMITTED_ORDER); + await this.dispatchStoreAction(storeActions.RESET_SUBMITTED_STATE); this.dispatchStoreAction( storeActions.SAVE_VEHICLE, { diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index e59ec5039..d6e608d4b 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -337,7 +337,7 @@ export default { // Recalibration if (hasSubmittedOrder) { - payload.isRecalibrationOnOrder = store.getters.isRecalibrationOnSubmittedOrder; + payload.isRecalibrationOnOrder = store.getters.isRecalibrationOnSubmittedState; } else { payload.isRecalibrationOnOrder = store.getters.isRecalibrationOnOrder; } diff --git a/src/mixins/analytics-mixin.spec.js b/src/mixins/analytics-mixin.spec.js index a4b4672bb..711702c4a 100644 --- a/src/mixins/analytics-mixin.spec.js +++ b/src/mixins/analytics-mixin.spec.js @@ -425,7 +425,7 @@ describe("analyticsMixin.js", () => { workOrderId: "222222222222", }; store.getters.isRecalibrationOnOrder = true; - store.getters.isRecalibrationOnSubmittedOrder = false; + store.getters.isRecalibrationOnSubmittedState = false; }); test("Pushes to data layer if nominal", () => { diff --git a/src/mixins/base-mixin.js b/src/mixins/base-mixin.js index 151d17dd9..b29eae8c1 100644 --- a/src/mixins/base-mixin.js +++ b/src/mixins/base-mixin.js @@ -1,6 +1,7 @@ import store from "@/store"; import { storeActions } from "@/constants/store-actions.js"; import { storeMutations } from "@/constants/store-mutations.js"; +import { sessionStorageKeyConstants } from "@/constants/session-storage.js"; import { navigationScenarios } from "@/router/router-constants/navigation-scenarios"; import { vehicleCategories } from "@/constants/vehicle-categories.js"; import { routerParams } from "@/router/router-constants/router-params"; @@ -198,10 +199,26 @@ export default { } }, getSubmittedOrder() { - return JSON.parse(window.sessionStorage.getItem("submittedOrder")); + return JSON.parse( + window.sessionStorage.getItem(sessionStorageKeyConstants.SUBMITTED_STATE) + )?.order; }, hasSubmittedOrder() { - return window.sessionStorage.getItem("submittedOrder") !== null; + const submittedState = window.sessionStorage.getItem( + sessionStorageKeyConstants.SUBMITTED_STATE + ); + return submittedState !== null && submittedState.order !== null; + }, + getSubmittedApplicationUser() { + return JSON.parse( + window.sessionStorage.getItem(sessionStorageKeyConstants.SUBMITTED_STATE) + )?.applicationUser; + }, + hasSubmittedApplicationUser() { + const submittedState = window.sessionStorage.getItem( + sessionStorageKeyConstants.SUBMITTED_STATE + ); + return submittedState !== null && submittedState.applicationUser !== null; }, }, computed: { diff --git a/src/mixins/experiment-mixin.js b/src/mixins/experiment-mixin.js index 11b573683..474945a54 100644 --- a/src/mixins/experiment-mixin.js +++ b/src/mixins/experiment-mixin.js @@ -1,17 +1,36 @@ import store from "@/store"; +import * as experimentHelper from "../helpers/experiment-helper"; + +/* + This is a wrapper around experiment-helper.js methods that allows an override + to provide a custom list of experiments. At the time of implementation this + was necessary for checking submittedState for experiments +*/ export default { methods: { - hasSettingEqualTo(settingName, settingValue) { - return store.getters.experimentSettings[settingName] == settingValue; + hasSettingEqualTo( + settingName, + settingValue, + experimentList = store.getters.applicationUser.experiments + ) { + const experimentSettings = + experimentHelper.getExperimentSettingsFromExperimentList(experimentList); + return experimentHelper.hasSettingEqualTo( + settingName, + settingValue, + experimentSettings + ); }, - hasSetting(settingName) { - return Object.hasOwn(store.getters.experimentSettings, settingName); + hasSetting(settingName, experimentList = store.getters.applicationUser.experiments) { + const experimentSettings = + experimentHelper.getExperimentSettingsFromExperimentList(experimentList); + return experimentHelper.hasSetting(settingName, experimentSettings); }, - getSettingValue(settingName) { - return this.hasSetting(settingName) - ? store.getters.experimentSettings[settingName] - : null; + getSettingValue(settingName, experimentList = store.getters.applicationUser.experiments) { + const experimentSettings = + experimentHelper.getExperimentSettingsFromExperimentList(experimentList); + return experimentHelper.getSettingValue(settingName, experimentSettings); }, }, }; diff --git a/src/mixins/experiment-mixin.spec.js b/src/mixins/experiment-mixin.spec.js index 1b2bff2f5..8dc3df2b6 100644 --- a/src/mixins/experiment-mixin.spec.js +++ b/src/mixins/experiment-mixin.spec.js @@ -135,6 +135,15 @@ function setupMocks({ experimentSettings }) { store.getters = { experimentSettings: experimentSettings ?? testExperimentSettings, + applicationUser: { + experiments: [ + { + isActive: true, + isExposed: true, + settings: experimentSettings ?? testExperimentSettings, + }, + ], + }, }; const mockComponent = { diff --git a/src/router/index.js b/src/router/index.js index 345790734..5ec909f97 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -2,6 +2,7 @@ import { createWebHistory, createRouter } from "vue-router"; import { storeActions } from "@/constants/store-actions"; import { storeMutations } from "../constants/store-mutations"; +import { sessionStorageKeyConstants } from "@/constants/session-storage.js"; import { lazyLoadComponent } from "@/router/dynamic-routing/component-loader.js"; import { routingTable } from "@/router/router-constants/routing-table.js"; import { globalEvents, globalEventTypes } from "@/constants/events"; @@ -93,10 +94,15 @@ const routes = [ log(" --to.query.fmgPage ", to.query?.fmgPage); // Intercept all navigation if a submitted order exists in storage - if (window.sessionStorage.getItem("submittedOrder") !== null) { + if ( + window.sessionStorage.getItem(sessionStorageKeyConstants.SUBMITTED_STATE) !== + null + ) { if (to.query.fmgPage !== funnelStartPageName) { to.query.fmgPage = fmgPageValues.CONFIRMATION; - log(" --has submittedOrder go to confirmation"); + log( + ` --has ${sessionStorageKeyConstants.SUBMITTED_STATE} go to confirmation` + ); } } // On entering the funnel "fresh", read cookie information, decide what to do next. diff --git a/src/store/index.js b/src/store/index.js index 032660c6f..36a459d80 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,6 +1,7 @@ import { createStore } from "vuex"; import { endpoints } from "@/constants/endpoints.js"; import { storeMutations } from "@/constants/store-mutations"; +import { sessionStorageKeyConstants } from "@/constants/session-storage.js"; import { getDateForSavedSessionTimeout } from "@/helpers/heritage-integration/session-helper"; import createPersistedState from "vuex-persistedstate"; import globalMethods from "@/global-methods"; @@ -20,6 +21,10 @@ import { } from "@/constants/schedule-constants"; import { getQuerystringParameter } from "@/helpers/querystring-helper"; import { deepClone } from "@/helpers/object-helper"; +import { + getExperimentSettingsFromExperimentList, + hasSettingEqualTo, +} from "../helpers/experiment-helper"; import { queryStrings } from "@/constants/query-strings"; import { partTypeStrings } from "@/constants/part-type-strings"; import { @@ -822,8 +827,10 @@ export const getters = { }, coverageIsVerified: (state) => { let order; - if (window.sessionStorage.getItem("submittedOrder") !== null) { - order = JSON.parse(window.sessionStorage.getItem("submittedOrder")); + if (window.sessionStorage.getItem(sessionStorageKeyConstants.SUBMITTED_STATE) !== null) { + order = JSON.parse( + window.sessionStorage.getItem(sessionStorageKeyConstants.SUBMITTED_STATE) + ).order; } else { order = state.order; } @@ -850,31 +857,34 @@ export const getters = { isRecalibrationOnOrder: (state) => { return getHasRecalibrationPart(state); }, - isRecalibrationOnSubmittedOrder: (state) => { - if (window.sessionStorage.getItem("submittedOrder") !== null) { + isRecalibrationOnSubmittedState: (state) => { + if (window.sessionStorage.getItem(sessionStorageKeyConstants.SUBMITTED_STATE) !== null) { return getHasRecalibrationPart({ - order: JSON.parse(window.sessionStorage.getItem("submittedOrder")), + order: JSON.parse( + window.sessionStorage.getItem(sessionStorageKeyConstants.SUBMITTED_STATE) + ).order, }); } - return false; }, shouldHideRecalibration: (state) => { - var order; - if (window.sessionStorage.getItem("submittedOrder") !== null) { - order = JSON.parse(window.sessionStorage.getItem("submittedOrder")); - } else { - order = state.order; + if (window.sessionStorage.getItem(sessionStorageKeyConstants.SUBMITTED_STATE) !== null) { + state = JSON.parse( + window.sessionStorage.getItem(sessionStorageKeyConstants.SUBMITTED_STATE) + ); } - + let order = state.order; + let applicationUser = state.applicationUser; if (order.payment.isInsurance) { return false; } return ( - experimentMixin.methods - .getSettingValue(experimentSettings.RECAL_PRICE_REMOVE) - ?.toLowerCase() === "true" && getters.isRecalibrationOnOrder(state) + hasSettingEqualTo( + experimentSettings.RECAL_PRICE_REMOVE, + "true", + getExperimentSettingsFromExperimentList(applicationUser.experiments) + ) && getters.isRecalibrationOnOrder(state) ); }, areRearWipersOnOrder: (state) => { @@ -974,10 +984,7 @@ export const getters = { }; }, experimentSettings: (state) => - state.applicationUser.experiments - .filter((x) => !!x.isActive) - .map((x) => x.settings) - .reduce((r, c) => Object.assign(r, c), {}) ?? {}, + getExperimentSettingsFromExperimentList(state.applicationUser.experiments), isVerifiedAndDeductibleZeroConfirmed: (state) => { // used in CMS on /payment-method in Header Sub Text, for FunnelSubHeaderWidget on cart pages @@ -3257,19 +3264,25 @@ export const actions = { return false; }, - createSubmittedOrder(context) { - if (window.sessionStorage.getItem("submittedOrder") !== null) { + createSubmittedState(context) { + if (window.sessionStorage.getItem(sessionStorageKeyConstants.SUBMITTED_STATE) !== null) { return; } // create a submitted order object from vuex. - const submittedOrder = context.state.order; + const submittedState = { + order: context.state.order, + applicationUser: context.state.applicationUser, + }; const experiments = context.state.applicationUser.experiments; const affiliateCookies = context.state.applicationUser.affiliateCookies; // set to local storage - window.sessionStorage.setItem("submittedOrder", JSON.stringify(submittedOrder)); + window.sessionStorage.setItem( + sessionStorageKeyConstants.SUBMITTED_STATE, + JSON.stringify(submittedState) + ); window.sessionStorage.setItem("createNewSessionForHeritage", true); // clear vuex @@ -3288,10 +3301,11 @@ export const actions = { context.commit(storeMutations.UPDATE_AFFILIATE_COOKIES, affiliateCookies); }, - addDonationToSubmittedOrder(context, donationAmount) { + addDonationToSubmittedState(context, donationAmount) { console.log("running addDonationToSubmittedOrder()... donationAmount: ", donationAmount); const submittedOrder = deepClone(baseMixin.methods.getSubmittedOrder()); + const submittedApplicationUser = deepClone(baseMixin.methods.getSubmittedApplicationUser()); if (donationAmount > 0) { console.log( @@ -3320,13 +3334,21 @@ export const actions = { }); submittedOrder.lineItems.supportingItems = nonDonationItems; } + + const submittedState = { + order: submittedOrder, + applicationUser: submittedApplicationUser, + }; // set to local storage - window.sessionStorage.setItem("submittedOrder", JSON.stringify(submittedOrder)); + window.sessionStorage.setItem( + sessionStorageKeyConstants.SUBMITTED_STATE, + JSON.stringify(submittedState) + ); }, - resetSubmittedOrder(context) { + resetSubmittedState(context) { // clear from local storage - window.sessionStorage.removeItem("submittedOrder"); + window.sessionStorage.removeItem(sessionStorageKeyConstants.SUBMITTED_STATE); }, resetExternalParameterState(context) { if (context.getters.isExternalParameter) {