diff --git a/src/common-components/button-question/button-question.vue b/src/common-components/button-question/button-question.vue index 79937c780..91c83fc2a 100644 --- a/src/common-components/button-question/button-question.vue +++ b/src/common-components/button-question/button-question.vue @@ -6,7 +6,9 @@
- {{groupName}} + + {{(isMultiSelect && answers && answers.length > 1) ? 'Select one or more options below.' : 'Select an option below.' }} +
-
+
{ + console.error(error); return reject(error.response); } ); diff --git a/src/helpers/heritage-integration/cookie-helper.js b/src/helpers/heritage-integration/cookie-helper.js index 36516a570..8f0a99709 100644 --- a/src/helpers/heritage-integration/cookie-helper.js +++ b/src/helpers/heritage-integration/cookie-helper.js @@ -18,19 +18,20 @@ export function updateOrCreateFunnelCookie() { ReferralNumber: store.getters.order.referralNumber, ReferralDate: store.getters.order.referralDate, ReferralCorrelationId: store.getters.order.referralCorrelationId, + ReferralParentAccountNumber: store.getters.order.parentAccountNumber, }); } /* Gets the current instance of the funnel cookie. Returns null if cookie isn't valid JSON. -*/ +*/ export function getFunnelCookie() { const cookieJson = document.cookie ?.split("; ") ?.find(row => row.startsWith(`${cookieNames.FUNNEL_SESSION_INFO}=`)) ?.split("=")[1]; - + try { return JSON.parse(cookieJson); } catch (error) { @@ -45,6 +46,55 @@ export function deleteFunnelCookie() { document.cookie = `${cookieNames.FUNNEL_SESSION_INFO}=; Max-Age=0; path=${applicationConfig.COOKIE_PATH}; ${getCookieDomainValue()}`; } +/* + Gets cookie domain value. Localhost will be empty "". +*/ +export function getCookieDomainValue() { + return location.hostname.includes("localhost") ? "" : `domain=${getDomainWithoutSubdomain()};`; +} + +/* + Gets value of dxdev cookie, and then extracts "did" value from it. + Returns empty string if cookie not found or "did" string not present. +*/ +export function getDeviceIdValue(){ + // Sometimes these cookie contains more than the device ID. + const cookieValue = getCookieValueByName(cookieNames.DXDEV); + const cookieValuesSplit = cookieValue.split('='); + + // If this is the only value, just use that. + if(cookieValuesSplit.length === 2 && cookieValuesSplit[0] === 'did'){ + return cookieValuesSplit[1]; + } + + const cookieValueMatch = cookieValue.match("(?<=did=).*?(?=&)"); + if(cookieValueMatch){ + return cookieValueMatch[0]; + } + + return ''; +} + +/* + Gets value of skey cookie, returns 0 if not found. +*/ +export function getSessionKeyValue(){ + const cookieValue = getCookieValueByName(cookieNames.SESSION_KEY); + + if(cookieValue){ + return cookieValue; + } + + return 0; +} + +/* +=========================== += PRIVATE FUNCTIONS = +=========================== +*/ + + /* Used to set properties on the funnel cookie. Takes an object with properties to set. Will overwrite existing properties. @@ -65,10 +115,9 @@ function setFunnelCookieProperties(properties) { } } -export function getCookieDomainValue() { - return location.hostname.includes("localhost") ? "" : `domain=${getDomainWithoutSubdomain()};`; -} - +/* + Gets current domain without the subdomain for cookie. +*/ function getDomainWithoutSubdomain() { let url = location.hostname; if (url.includes("localhost")) { @@ -82,3 +131,16 @@ function getDomainWithoutSubdomain() { .slice(-(urlParts.length === 4 ? 3 : 2)) .join('.')}`; } + +/* + Gets cookie value by name, returns empty string if not found. +*/ +function getCookieValueByName(name) { + const value = "; " + document.cookie; + const parts = value.split("; " + name + "="); + + if (parts.length === 2) { + return parts.pop().split(";").shift(); + } + return ""; +} \ No newline at end of file diff --git a/src/helpers/heritage-integration/navigation-helper.js b/src/helpers/heritage-integration/navigation-helper.js index 77d6abe92..06eff0042 100644 --- a/src/helpers/heritage-integration/navigation-helper.js +++ b/src/helpers/heritage-integration/navigation-helper.js @@ -66,9 +66,6 @@ export async function navigateToHeritageFunnel() { { corid: store.getters.order.referralCorrelationId, src: "concept-funnel", - // TODO CSR-28, remove this - cns: "all", - experiments: "RemoveServiceAreaPage=ServAreaRemoval_V7=NoShowPackages_CONTROL=true,ConceptFunnel=ConceptFunnel_V1=ConceptFunnel_TEST=true" } ); } \ No newline at end of file diff --git a/src/helpers/heritage-integration/order-helper.js b/src/helpers/heritage-integration/order-helper.js index 1ce2ac2cc..73ffa0b7c 100644 --- a/src/helpers/heritage-integration/order-helper.js +++ b/src/helpers/heritage-integration/order-helper.js @@ -25,7 +25,7 @@ export async function loadOrderIfPresent() { // Load referral if there is a cookie, and it doesn't indicate it needs a state reset. - return (await loadOrder(funnelCookie.ReferralNumber, funnelCookie.ReferralDate, funnelCookie.ReferralCorrelationId)).data; + return (await loadOrder(funnelCookie.ReferralNumber, funnelCookie.ReferralDate, funnelCookie.ReferralCorrelationId, funnelCookie.ReferralParentAccountNumber)).data; } /* @@ -54,12 +54,13 @@ export async function saveOrder() { Calls API to load order given the referral number, referralDate, and referralCorrelationId and returns the response. */ -async function loadOrder(referralNumber, referralDate, referralCorrelationId) { +async function loadOrder(referralNumber, referralDate, referralCorrelationId, accountNumber) { const response = await baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.LOAD_ORDER, { referralNumber: referralNumber.toString(), referralDate: referralDate, - referralCorrelationId: referralCorrelationId + referralCorrelationId: referralCorrelationId, + accountNumber: accountNumber?.toString() }, false); return response; diff --git a/src/helpers/layout-helper.js b/src/helpers/layout-helper.js index 10f331faf..0a8177e25 100644 --- a/src/helpers/layout-helper.js +++ b/src/helpers/layout-helper.js @@ -15,10 +15,10 @@ export function settleAllPromises(promiseResultMap) { // when returned, so other promises do. Map the results to the object // so that the object is the return data. - if (results[i].value.data === undefined) { - resultMap[promiseName] = results[i].value; + if (results[i]?.value?.data === undefined) { + resultMap[promiseName] = results[i]?.value; } else { - resultMap[promiseName] = results[i].value.data; + resultMap[promiseName] = results[i]?.value?.data; } } diff --git a/src/layouts/vehicle-damage/vehicle-damage.vue b/src/layouts/vehicle-damage/vehicle-damage.vue index b84e2fbfa..be071a76a 100644 --- a/src/layouts/vehicle-damage/vehicle-damage.vue +++ b/src/layouts/vehicle-damage/vehicle-damage.vue @@ -276,7 +276,7 @@ export default { navigateForward(partsData){ // CSR-98 TEMP - const vehicleYearsToShowHeritageFunnel = [ "2001", "2002", "2010" ]; + const vehicleYearsToShowHeritageFunnel = [ 2001, 2002, 2010 ]; if (vehicleYearsToShowHeritageFunnel.includes(store.getters.vehicle.year)) { navigateToHeritageFunnel(); return; diff --git a/src/layouts/vehicle-year/vehicle-year.vue b/src/layouts/vehicle-year/vehicle-year.vue index 9e3dd7e2f..96f62409c 100644 --- a/src/layouts/vehicle-year/vehicle-year.vue +++ b/src/layouts/vehicle-year/vehicle-year.vue @@ -17,11 +17,16 @@ import yearQuestion from "@/layouts/vehicle-year/year-question/year-question"; import funnelHeader from "@/common-components/funnel-header/funnel-header"; import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner"; import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header"; + // Supporting files import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { settleAllPromises } from "@/helpers/layout-helper"; import { storeMutations } from "@/constants/store-mutations"; import { storeActions } from "@/constants/store-actions"; +import { experimentUniverses } from "@/constants/experiments"; +import { getDeviceIdValue, getSessionIdValue, getSessionKeyValue } from "@/helpers/heritage-integration/cookie-helper"; + +import baseMixin from "@/mixins/base-mixin"; import store from "@/store"; export default { @@ -36,8 +41,16 @@ export default { async beforeRouteEnter(to, from, next) { // Call APIs const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage); - const yearQuestionInitialDataPromise = - yearQuestion.methods.loadInitialData(); + const yearQuestionInitialDataPromise = yearQuestion.methods.loadInitialData(); + + // Log experiment exposure + const logExperimentExposurePromise = baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.LOG_EXPERIMENT_EXPOSURE, + { + userId: getDeviceIdValue(), + sessionKey: getSessionKeyValue(), + pageName: to.query.fmgPage, + universeName: experimentUniverses.CONCEPT_FUNNEL + }, false); // Settle promises and get results const promiseResultMap = [ @@ -49,6 +62,10 @@ export default { resultKey: "yearQuestionInitialData", promise: yearQuestionInitialDataPromise, }, + { + resultKey: "logExperimentExposure", + promise: logExperimentExposurePromise, + }, ]; let resultMap = await settleAllPromises(promiseResultMap); @@ -64,8 +81,8 @@ export default { watch: { selectedYear(year) { - - this.$store.commit(this.storeMutations.UPDATE_YEAR, year); + const parsedYear = parseInt(year); + this.$store.commit(this.storeMutations.UPDATE_YEAR, parsedYear); this.$router.navigateAfterSave( this.navigationScenarios.SELECTED_YEAR, this.$route diff --git a/src/store/index.js b/src/store/index.js index 65eff4230..b101d0524 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -48,7 +48,7 @@ const getDefaultState = () => { referralNumber: null, referralDate: null, referralCorrelationId: null, - parentAccountNumber: null, + parentAccountNumber: 0, }, applicationUser: { eventBus: [], @@ -93,7 +93,7 @@ export const mutations = { updateVehicleVin(state, vin) { state.order.vehicle.vin = vin; }, - updateIsRepair(state, isRepair){ + updateIsRepair(state, isRepair) { state.order.damage.isRepair = isRepair; }, updateNumberOfChips(state, numberOfChips) { @@ -176,7 +176,7 @@ export const mutations = { state.order.referralNumber = orderInformation.referralNumber; state.order.referralDate = orderInformation.referralDate; state.order.referralCorrelationId = orderInformation.referralCorrelationId; - state.order.vehicle = { + state.order.vehicle = Object.assign(state.order.vehicle, { year: orderInformation.vehicle?.year, make: orderInformation.vehicle?.make, model: orderInformation.vehicle?.model, @@ -186,13 +186,13 @@ export const mutations = { 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.serviceLocation.zipCode = orderInformation.zipCode; // TODO CSR-416 Make sure this is correct + state.order.serviceLocation.zipCode = orderInformation.zipCode; } } @@ -248,7 +248,7 @@ export const actions = { method: endpoints.LookupVinByPlate.method, endpoint: endpoints.LookupVinByPlate.url, payload: { - licensePlate: licensePlate, + licensePlate: licensePlate, licenseState: licenseState }, }); @@ -292,12 +292,12 @@ export const actions = { }, getDamageOptions(context, { carId }) { return globalMethods.callHttpClient({ - methods: endpoints.GetDamageOptions.method, + methods: endpoints.GetDamageOptions.method, endpoint: `${endpoints.GetDamageOptions.url}/${carId}`, payload: {}, }); }, - validateZip(context, {zip}) { + validateZip(context, { zip }) { return globalMethods.callHttpClient({ methods: endpoints.ValidateZip.method, endpoint: `${endpoints.ValidateZip.url}/${zip}` @@ -363,6 +363,19 @@ export const actions = { context.commit(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, referralCorrelationId); }, + logExperimentExposure(context, { userId, sessionKey, pageName, universeName }) { + return globalMethods.callHttpClient({ + method: endpoints.LogExperimentExposureIfAssigned.method, + endpoint: endpoints.LogExperimentExposureIfAssigned.url, + payload: { + userId: userId, + sessionKey: sessionKey, + pageName: pageName, + universeName: universeName + } + }); + }, + // Parts API Actions getPartsOrQuestions(context, { carId, glassArray, zipCode, vin = '' }) { return globalMethods.callHttpClient({ @@ -402,16 +415,18 @@ export const actions = { }); }, - loadOrder(context, { referralNumber, referralDate, referralCorrelationId }) { + loadOrder(context, { referralNumber, referralDate, referralCorrelationId, accountNumber}) { return globalMethods.callHttpClient({ method: endpoints.LoadOrder.method, endpoint: endpoints.LoadOrder.url, payload: { referralNumber: referralNumber, referralDate: referralDate, - referralCorrelationId: referralCorrelationId + referralCorrelationId: referralCorrelationId, + accountNumber: accountNumber }, }).then((response) => { + context.commit(storeMutations.RESET_STATE); context.commit(storeMutations.UPDATE_STATE_WITH_ORDER_INFORMATION, response.data); return response; }); diff --git a/src/styles/common-error-styles.scss b/src/styles/common-error-styles.scss index fe1637e43..530f9c392 100644 --- a/src/styles/common-error-styles.scss +++ b/src/styles/common-error-styles.scss @@ -7,21 +7,11 @@ input[type=radio]:focus + label { box-shadow: 0 0 0 2.5px $red; } - input[type=checkbox]:focus + label:hover, - input[type=radio]:focus + label { - box-shadow: 0 0 0 1px $red; - } &:hover { box-shadow: 0px 0px 0px 4px $red-200; border-radius: 10px !important; - border: 1px solid $red; } } - - input[type=checkbox]:checked + label, - input[type=radio]:checked + label { - box-shadow: 0 0 0 2.5px transparent !important; - } &.list-button-horizontal { color: $red; label { diff --git a/src/ux-components/list-button-horizontal/list-button-horizontal.vue b/src/ux-components/list-button-horizontal/list-button-horizontal.vue index cef3d765c..bd1977ba3 100644 --- a/src/ux-components/list-button-horizontal/list-button-horizontal.vue +++ b/src/ux-components/list-button-horizontal/list-button-horizontal.vue @@ -1,7 +1,7 @@