diff --git a/src/constants/application-config.js b/src/constants/application-config.js index 37555119a..d4a3be8d2 100644 --- a/src/constants/application-config.js +++ b/src/constants/application-config.js @@ -1,6 +1,7 @@ const applicationConfig = { CONSUMER_APIGATEWAY_URL: process.env.VUE_APP_CONSUMER_API_GATEWAY, HERITAGE_FUNNEL: process.env.VUE_APP_HERITAGE_FUNNEL, + SESSION_TIMEOUT_CONFIG: 30 }; diff --git a/src/constants/cookie-names.js b/src/constants/cookie-names.js index 33a76ae43..6b1e3c909 100644 --- a/src/constants/cookie-names.js +++ b/src/constants/cookie-names.js @@ -1,5 +1,5 @@ const cookieNames = { - ORDER_INFO: "OrderInfo" + CONCEPT_SESSION_INFO: "ConceptSessionInfo", }; export { cookieNames }; diff --git a/src/constants/store-mutations.js b/src/constants/store-mutations.js index 2636464bd..00ac66d58 100644 --- a/src/constants/store-mutations.js +++ b/src/constants/store-mutations.js @@ -34,7 +34,7 @@ const storeMutations = { // OTHER MUTATIONS UPDATE_PAGE_DATA: "updatePageData", - SET_LOAD_ORDER_INFO: "setLoadOrderInformation" + SET_LOAD_CONCEPT_SESSION_INFO: "setLoadOrderInformation" }; diff --git a/src/helpers/heritage-integration-helper.js b/src/helpers/heritage-integration-helper.js index 9ac3469e4..53ff90aab 100644 --- a/src/helpers/heritage-integration-helper.js +++ b/src/helpers/heritage-integration-helper.js @@ -1,111 +1,157 @@ import { storeActions } from "@/constants/store-actions.js"; import { cookieNames } from "@/constants/cookie-names"; import { navigationScenarios } from "@/router/router-constants/navigation-scenarios"; +import { applicationConfig } from "@/constants/application-config"; + import store from "@/store"; import router from "@/router"; import baseMixin from "../mixins/base-mixin"; -// Read info from heritage funnel and reset state or load referral? -export async function loadReferralFromHeritageFunnelIfPresent() { - const orderInfo = this.getHeritageCookieValue(); - // Do nothing if there is no cookie. - if(orderInfo === null){ +// Saves Referral if one is available and commits referral details to state. +export async function saveOrder() { + console.log("saving order..."); + const savedOrderInfo = await baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.SAVE_ORDER); + + // Save the referral information back from the store. + await baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.SET_REFERRAL_INFORMATION, { + referralNumber: savedOrderInfo.data.referralNumber, + correlationId: savedOrderInfo.data.referralCorrelationId, + referralDate: savedOrderInfo.data.referralDate, + }, false); + + // Update the cookie with the referral information when saved. + updateOrCreateConceptCookie(); +} + +// Loads order based on referral data in cookie, also loads the referral information into state. +export async function loadOrder(referralNumber, referralDate, correlationId) { + console.log("loading order...", referralNumber, referralDate, correlationId); + await baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.LOAD_ORDER, + { referralNumber: referralNumber.toString(), referralDate: referralDate, correlationId: correlationId } + , false); +} + +// Read info from heritage funnel and reset state or load referral +export async function loadReferralIfPresent() { + console.log("attempting to load referral...."); + const conceptCookie = getConceptCookie(); + + // Do nothing if there is no cookie or no correlation id. + if (conceptCookie === null || conceptCookie.ReferralCorrelationId === null) { + console.log("No referral found"); return; } // Reset state if cookie says to. - if (orderInfo?.ShouldResetState) { + if (conceptCookie.ShouldResetState) { + console.log("Resetting state..."); baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.RESET_STATE); - deleteHeritageCookie(); + deleteConceptCookie(); return; } - + + console.log("calling load order from loadReferralIfPresent()..."); // Load referral if there is a cookie, and it doesn't indicate it needs a state reset. - await loadOrder(); + await loadOrder(conceptCookie.ReferralNumber, conceptCookie.ReferralDate, conceptCookie.ReferralCorrelationId); } +export function updateOrCreateConceptCookie() { + console.log("Updating cookie...", { + LastTouched: new Date().toUTCString(), + DidHeritageFunnelUpdateLast: false, + ShouldResetState: false, + ReferralNumber: store.state.order.referralNumber, + ReferralDate: store.state.order.referralDate, + ReferralCorrelationId: store.state.order.referralCorrelationId, + }); + + // Create the cookie + document.cookie = `${cookieNames.CONCEPT_SESSION_INFO}={}; path=/`; + + // Set up cookie with all the props. + setConceptCookieProperties({ + LastTouched: new Date().toUTCString(), + DidHeritageFunnelUpdateLast: false, + ShouldResetState: false, + ReferralNumber: store.state.order.referralNumber, + ReferralDate: store.state.order.referralDate, + ReferralCorrelationId: store.state.order.referralCorrelationId, + }); + +} + +export function isConceptSessionStillActive() { + if (getConceptCookie() !== null) { + const lastTouchedValue = getConceptCookie().LastTouched; + const timeoutAmount = applicationConfig.SESSION_TIMEOUT_CONFIG; + const isMoreThanHalfHourAgo = ((new Date() - new Date(lastTouchedValue)) / 60000) > timeoutAmount; + console.log("has session expired -->", isMoreThanHalfHourAgo); + + if (isMoreThanHalfHourAgo) { + return false; + } + + return true; + } +} + +// Navigate to Heritage Funnel with the proper URL format. +// Will Save the referral if there is one in state, or create a new one if one is not in state. export async function navigateToHeritageFunnel() { - await this.saveOrder(); - const referralCorrelationId = store.getters.referralCorrelationId; + + // Create the order (or save existing order) when navigating to Heritage Funnel. + await saveOrder(); router.navigate( navigationScenarios.MOVE_TO_HERITAGE_FUNNEL, router.currentRoute.value, { - corid: referralCorrelationId, + corid: store.state.order.referralCorrelationId, src: "concept-funnel", - // TODO CSR-98 REMOVE THIS cns: "all", experiments: "RemoveServiceAreaPage=ServAreaRemoval_V7=ServAreaNoRemove_V7_TEST=true,ConceptFunnel=ConceptFunnel_V1=ConceptFunnel_TEST=true" } ); } -export async function saveOrder() { - const savedOrderInfo = (await baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.SAVE_ORDER)).data; - - // Save the referral information back from the store. - await baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.SET_REFERRAL_INFORMATION, { - referralNumber: savedOrderInfo.referralNumber, - correlationId: savedOrderInfo.referralCorrelationId, - referralDate: savedOrderInfo.referralDate, - }) - - // Set cookie properties. - setHeritageCookieProperties({ - DidHeritageFunnelUpdateLast: false - }) -} - -export async function loadOrder() { - const loadOrderInfo = (await baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.LOAD_ORDER)).data; - - // Save the data from the loaded order to state. - await baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.SET_LOAD_ORDER_DATA, { loadOrderInfo }); -} - -// --------- PRIVATE FUNCTIONS --------- - /* Start cookie related functions */ -export function getHeritageCookieValue() { +export function getConceptCookie() { const cookieJson = document.cookie ?.split("; ") - ?.find(row => row.startsWith(`${cookieNames.ORDER_INFO}=`)) + ?.find(row => row.startsWith(`${cookieNames.CONCEPT_SESSION_INFO}=`)) ?.split("=")[1]; - + try { return JSON.parse(cookieJson); - } catch(error) { + } catch (error) { return null; } } -function deleteHeritageCookie() { - // If this cookie is ever created from the concept funnel, will need to add another - // line with the path=/fmg/ - document.cookie = `${cookieNames.ORDER_INFO}=; Max-Age=0; path=/; domain=${location.hostname}`; -} +// --------- PRIVATE FUNCTIONS --------- -function setHeritageCookieValue(cookieValue) { - let cookieValueJson = cookieValue; - if (typeof cookieValue == "object") - cookieValueJson = JSON.stringify(cookieValue); - - document.cookie = `${cookieNames.ORDER_INFO}=${cookieValueJson}; path=/`; -} - -function setHeritageCookieProperties(properties) { +function setConceptCookieProperties(properties) { if (typeof properties == "object") { - let cookie = getHeritageCookieValue(); + let cookie = getConceptCookie(); if (cookie !== null) { Object.keys(properties).forEach(key => { cookie[key] = properties[key]; }); - setHeritageCookieValue(cookie); + const cookieValueJson = JSON.stringify(cookie); + + document.cookie = `${cookieNames.CONCEPT_SESSION_INFO}=${cookieValueJson}; path=/`; } } } + +function deleteConceptCookie() { + // If this cookie is ever created from the concept funnel, will need to add another + // line with the path=/fmg/ + document.cookie = `${cookieNames.CONCEPT_SESSION_INFO}=; Max-Age=0; path=/; domain=${location.hostname}`; +} + + /* End cookie related functions */ diff --git a/src/helpers/heritage-integration-helper.spec.js b/src/helpers/heritage-integration-helper.spec.js index 320cb1ae0..de656f853 100644 --- a/src/helpers/heritage-integration-helper.spec.js +++ b/src/helpers/heritage-integration-helper.spec.js @@ -323,7 +323,7 @@ describe("cookies", () => { const cookies = { "orderconfirmation": 1565980, "optimizelyEndUserId": "oeu1610051865958r0.07937134272718804", - [cookieNames.ORDER_INFO]: `{"ReferralNumber":"1566818","ReferralDate":"2022-03-15T10:56:24.597","ReferralCorrelationId":"404d2b04-f86e-45c3-b373-127b6217b060","ShouldResetState":false,"DidHeritageFunnelUpdateLast":true}`, + [cookieNames.CONCEPT_SESSION_INFO]: `{"ReferralNumber":"1566818","ReferralDate":"2022-03-15T10:56:24.597","ReferralCorrelationId":"404d2b04-f86e-45c3-b373-127b6217b060","ShouldResetState":false,"DidHeritageFunnelUpdateLast":true}`, "UNIQUE_SESSION_ID": "33756020-b58e-4ec7-b8b8-3f1576719c40", "_ga": "GA1.1.1631274579.1622658999", "da_lid": "B10847599A73EA95861EBB99009A283E27|0|0|0 clientTag=null", @@ -332,9 +332,9 @@ const cookies = { function setupCookies({ heritageCookieValue = "", includeHeritageCookie = true }) { Object.keys(cookies).forEach(key => { - const cookieValue = key == cookieNames.ORDER_INFO ? heritageCookieValue : cookies[key]; - - if (includeHeritageCookie || key != cookieNames.ORDER_INFO) + const cookieValue = key == cookieNames.CONCEPT_SESSION_INFO ? heritageCookieValue : cookies[key]; + + if (includeHeritageCookie || key != cookieNames.CONCEPT_SESSION_INFO) document.cookie = `${key}=${cookieValue}; path=/;`; }); } diff --git a/src/router/index.js b/src/router/index.js index 2bd9ea0e2..54a64c5d2 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -4,8 +4,7 @@ import { storeActions } from "@/constants/store-actions"; import { lazyLoadComponent } from "@/router/dynamic-routing/component-loader.js"; import { routingTable } from "@/router/router-constants/routing-table.js"; import { globalEvents, globalEventTypes } from "@/constants/events"; -import { routerParameterKeys } from '@/router/router-constants/router-parameter-keys' -import { loadReferralFromHeritageFunnelIfPresent, saveOrder, getHeritageCookieValue } from "@/helpers/heritage-integration-helper"; +import * as integrationHelper from "@/helpers/heritage-integration-helper"; import baseMixin from "@/mixins/base-mixin"; import eventBus from "@/helpers/event-bus/event-bus"; @@ -30,17 +29,27 @@ const routes = [ path: "/", name: "root", async beforeEnter(to, from, next) { - // On entering the concept funnel - if (from.redirectedFrom === undefined) { - // Read cookie information, decide what to do next - loadReferralFromHeritageFunnelIfPresent(); - } - // If we have no query string, or we don't have the FmgPage query string. if (to.query.fmgPage === undefined) { await GoToFunnelStartOn404(next); } else { try { + + // Check our 'Session' is still good. + // If not, reset state and go back to the start. + if (!integrationHelper.isConceptSessionStillActive()) { + baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.RESET_STATE); + await GoToFunnelStartOn404(next); + } + + // Create funnel cookie, or update it if it already exists. + integrationHelper.updateOrCreateConceptCookie(); + + // On entering the concept funnel "fresh", read cookie information, decide what to do next. + if (from.redirectedFrom === undefined) { + await integrationHelper.loadReferralIfPresent(); + } + // If we already have our route, go to it. if (router.hasRoute(to.query.fmgPage)) { // Since our route is already in scope, we can grab the component from it and call the arePagePrerequisitesValid function. @@ -134,16 +143,17 @@ async function navigate(scenario, currentRoute, invalidateOnSave, optionalQuery // Append page data to the store for the NEXT page, if any. It will be an empty object if none is provided. baseMixin.methods.savePageDataToStore(destinationFmgPageValue, optionalPageData); - // if cookie and referral exists - if (getHeritageCookieValue()?.ReferralNumber) - saveOrder(); + // if cookie and referralNumber/Date exists + if (integrationHelper.getConceptCookie()?.ReferralNumber && integrationHelper.getConceptCookie()?.ReferralDate) { + await integrationHelper.saveOrder(); + } router.push({ name: "root", query: Object.assign(optionalQuery, { fmgPage: destinationFmgPageValue, }), - params: Object.assign(optionalParams, {[routerParameterKeys.FROM_ROUTER_NAVIGATE]: true}) + params: optionalParams }); } else if (matchingScenarioMap.destinationUrl !== undefined) { navigateToUrl(matchingScenarioMap.destinationUrl, optionalQuery); @@ -152,7 +162,6 @@ async function navigate(scenario, currentRoute, invalidateOnSave, optionalQuery // Get navigation map depending on the scenario and the current 'page' you're on. function getNavigationMap(scenario, currentRoute) { - console.log("A") const fmgPageValue = currentRoute.query.fmgPage; const matchedQueryValue = routingTable .filter( diff --git a/src/router/router-constants/router-parameter-keys.js b/src/router/router-constants/router-parameter-keys.js index 7ace86f37..2407d74c4 100644 --- a/src/router/router-constants/router-parameter-keys.js +++ b/src/router/router-constants/router-parameter-keys.js @@ -1,5 +1,3 @@ -const routerParameterKeys = { - FROM_ROUTER_NAVIGATE: 'fromRouterNavigate', - }; +const routerParameterKeys = { }; export { routerParameterKeys }; \ No newline at end of file diff --git a/src/store/index.js b/src/store/index.js index 888ae41f4..343fb5a53 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -148,25 +148,25 @@ export const mutations = { // Misc Mutations setLoadOrderInformation(state, orderInformation) { - state.order.referralNumber = orderInformation.ReferralNumber; - state.order.referralDate = orderInformation.ReferralDate; - state.order.referralCorrelationId = orderInformation.CorrelationId; + state.order.referralNumber = orderInformation.referralNumber; + state.order.referralDate = orderInformation.referralDate; + state.order.referralCorrelationId = orderInformation.correlationId; state.order.vehicle = { - year: orderInformation.Year, - make: orderInformation.Make, - model: orderInformation.Model, - style: orderInformation.Style, - carId: orderInformation.CarId, - category: orderInformation.Category, - imageUrl: orderInformation.ImageUrl, - imageVifNumber: orderInformation.ImageVifNumber, - imageColor: orderInformation.ImageColor + year: orderInformation.vehicle.year, + make: orderInformation.vehicle.make, + model: orderInformation.vehicle.model, + style: orderInformation.vehicle.style, + carId: orderInformation.vehicle.carId, + category: orderInformation.vehicle.category, + imageUrl: orderInformation.vehicle.imageUrl, + imageVifNumber: orderInformation.vehicle.imageVifNumber, + imageColor: orderInformation.vehicle.imageVifColor }; - state.order.damage.glassToReplace = orderInformation.GlassToReplace; - state.order.damage.isRepair = orderInformation.IsRepair; - state.order.damage.numberOfChips = orderInformation.NumberOfChips; - state.order.lineItems.glassParts = orderInformation.Parts; - state.order.parentAccountNumber = orderInformation.ParentAccountNumber; + state.order.damage.glassToReplace = orderInformation.glassToReplace; + state.order.damage.isRepair = orderInformation.isRepair; + state.order.damage.numberOfChips = orderInformation.numberOfChips; + state.order.lineItems.glassParts = orderInformation.parts; + state.order.parentAccountNumber = orderInformation.parentAccountNumber; } } @@ -184,9 +184,6 @@ export const getters = { eventBus: (state) => state.applicationUser.eventBus, damage: (state) => state.order.damage, lineItems: (state) => state.order.lineItems, - referralNumber: (state) => state.order.referralNumber, - referralDate: (state) => state.order.referralDate, - referralCorrelationId: (state) => state.order.referralCorrelationId, pageData: (state) => (page) => { return state.applicationUser.pageData[page]; } @@ -321,9 +318,6 @@ export const actions = { context.commit(storeMutations.UPDATE_REFERRAL_DATE, referralDate); context.commit(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, referralCorrelationId); }, - setLoadOrderData(context, { loadOrderData }) { - - }, // Parts API Actions getPartsOrQuestions(context, { carId, glassArray, zipCode, vin = '' }) { @@ -357,8 +351,8 @@ export const actions = { }, numberOfChips: damage.numberOfChips, glassToReplace: damage.glassToReplace, - referralNumber: context.getters.referralNumber, - referralDate: context.getters.referralDate + referralNumber: context.state.order.referralNumber, + referralDate: context.state.order.referralDate }, }); }, @@ -372,6 +366,9 @@ export const actions = { referralDate: referralDate, correlationId: correlationId }, + }).then( (response) => { + context.commit(storeMutations.SET_LOAD_CONCEPT_SESSION_INFO, response.data); + return response; }); } }