diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index e58f51862..2760d6a1a 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -79,4 +79,44 @@ stages:
__VUE_APP_CONSUMER_API_GATEWAY__: $(__VUE_APP_CONSUMER_API_GATEWAY__)
__VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__)
__VUE_APP_HERITAGE_FUNNEL__: $(__VUE_APP_HERITAGE_FUNNEL__)
+ indexDeployVariables:
+ __VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__)
+ __VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__)
+ cfDistributionId: $(cfDistributionId)
+
+
+ # QA Build/Deploy
+ - stage: Qa
+ variables:
+ - group: FixMyGlassQa
+ jobs:
+ - deployment: qaBuildDeployment
+ displayName: Build and Deploy FMG - QA
+ environment: digitalCloud-qa
+ container: node
+ workspace:
+ clean: all
+ strategy:
+ runOnce:
+ deploy:
+ steps:
+ - checkout: self
+ clean: true
+ - template: templates/digital/step-build-vue.yml@AzureDevOps
+ parameters:
+ buildOutputDir: dist
+ - template: templates/digital/step-deploy-vue.yml@AzureDevOps
+ parameters:
+ artifactName: vueDist
+ awsProfile: $(qaDeploymentProfile)
+ outputPath: /fmg/
+ deployBuckets:
+ safelite-qa-fmg-us-east-1:
+ clearFolder: true
+ deployFolder: ''
+ region: us-east-1
+ appDeployVariables:
+ __VUE_APP_CONSUMER_API_GATEWAY__: $(__VUE_APP_CONSUMER_API_GATEWAY__)
+ __VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__)
+ __VUE_APP_HERITAGE_FUNNEL__: $(__VUE_APP_HERITAGE_FUNNEL__)
cfDistributionId: $(cfDistributionId)
\ No newline at end of file
diff --git a/jest.config.js b/jest.config.js
index b387a0e5d..20e2d863e 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -17,11 +17,9 @@ module.exports = {
"!src/layouts/vin-lookup/vin-lookup.vue",
"!src/layouts/vehicle-damage/windshield-damage-type-question/windshield-damage-type-question.vue",
"!src/layouts/vehicle-damage/windshield-options/windshield-options.vue",
- "!src/layouts/address-poc/address-poc.vue",
- "!src/layouts/nested-radio-poc/nested-radio.vue",
- "!src/layouts/button-question-examples/**/*.vue",
"!src/layouts/part-questions/**/*.vue",
"!src/layouts/reveal/**/*.vue",
+ "!src/layouts/estimate/**/*.vue",
// REMOVE THESE AFTER WRITING UNIT TESTS
"!src/layouts/address-lookup/customer-questions/customer-questions.vue",
@@ -31,7 +29,7 @@ module.exports = {
testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"],
coverageThreshold: {
global: {
- statements: 87,
+ statements: 86,
// Got the go ahead from Mark to temporarily lower this. Taking out initialize component made the year,make,model and style coverage drop a bit. Once unit tests for license plate lookup, vin lookup and address lookup are in the coverage should go back up to 90
},
},
diff --git a/public/index.html b/public/index.html
index b926ca739..b8a80e384 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1,6 +1,10 @@
+
+
@@ -11,9 +15,17 @@
<%= htmlWebpackPlugin.options.title %>
+
+
+
+
+
diff --git a/src/common-components/button-question/button-question.vue b/src/common-components/button-question/button-question.vue
index 79937c780..e29f1fd93 100644
--- a/src/common-components/button-question/button-question.vue
+++ b/src/common-components/button-question/button-question.vue
@@ -6,7 +6,9 @@
diff --git a/src/common-components/funnel-footer/funnel-footer.vue b/src/common-components/funnel-footer/funnel-footer.vue
index cea6b403e..abac8a91f 100644
--- a/src/common-components/funnel-footer/funnel-footer.vue
+++ b/src/common-components/funnel-footer/funnel-footer.vue
@@ -22,13 +22,13 @@
isPrimary
:buttonText="buttonText"
loaderColor="white"
- :class="isDisabled && 'form-test-invalid'"
- :aria-disabled="isDisabled"
- :isDisabled="isDisabled"
+ :class="isForwardActionDisabled && 'form-test-invalid'"
+ :aria-disabled="isForwardActionDisabled"
+ :isDisabled="isForwardActionDisabled"
@click-event="buttonClick"
/>
-
+
-
@@ -56,7 +56,7 @@ export default {
setup(props) {
const fieldOptions = {
type: "text",
- value: props.modelValue,
+ value: props.modelValue,
};
const {
@@ -64,15 +64,17 @@ export default {
handleBlur,
handleChange,
meta,
- validate
+ validate,
+ errors,
} = useField(props.inputId, props.validationRules, fieldOptions);
- return {
+ return {
errorMessage,
handleBlur,
handleChange,
validate,
- meta,
+ meta,
+ errors,
};
},
computed: {
@@ -86,7 +88,7 @@ export default {
set: function(newValue) {
this.$emit("update:modelValue", newValue);
}
- },
+ },
labelText: {
get: function () {
var thisQuestionText = "";
@@ -102,7 +104,7 @@ export default {
return thisQuestionText;
}
- }
+ }
},
watch: {
value(newValue) {
diff --git a/src/constants/cookie-names.js b/src/constants/cookie-names.js
index 2756a8eb7..c52debc2a 100644
--- a/src/constants/cookie-names.js
+++ b/src/constants/cookie-names.js
@@ -1,5 +1,10 @@
const cookieNames = {
FUNNEL_SESSION_INFO: "FunnelSessionInfo",
+
+ // Existing Safelite.com cookies
+ DXDEV: "dxdev",
+ SESSION_ID: "sid",
+ SESSION_KEY: "skey"
};
export { cookieNames };
diff --git a/src/constants/dynamic-strings.js b/src/constants/dynamic-strings.js
new file mode 100644
index 000000000..1c3b8ade7
--- /dev/null
+++ b/src/constants/dynamic-strings.js
@@ -0,0 +1,7 @@
+const dynamicStrings = {
+ GLOBAL_STATE: "globalState",
+ CUSTOM: "custom",
+ ROUTER_LINK: "routerLink"
+ };
+
+ export { dynamicStrings };
\ No newline at end of file
diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js
index 0e5b0f78a..2ea951e80 100644
--- a/src/constants/endpoints.js
+++ b/src/constants/endpoints.js
@@ -67,6 +67,10 @@ const endpoints = {
url: "/location/api/v1/location/zip",
method: "GET",
},
+ LogExperimentExposureIfAssigned:{
+ url: "/analytics/api/v1/analytics/log-experiment-exposure",
+ method: "POST",
+ }
};
export { endpoints };
diff --git a/src/constants/error-messages.js b/src/constants/error-messages.js
index f2a74a723..17012ab68 100644
--- a/src/constants/error-messages.js
+++ b/src/constants/error-messages.js
@@ -19,6 +19,8 @@ const errorMessages = {
EMAIL_ADDRESS_FORMAT: "Please enter a valid email address",
SERVICE_ZIP_REQUIRED: "Please enter your Service ZIP",
SERVICE_ZIP_FORMAT: "Please enter a valid Service ZIP",
+ VIN_REQUIRED: "Please enter your VIN",
+ VIN_FORMAT: "Please enter a valid VIN",
};
-
-export { errorMessages };
\ No newline at end of file
+
+export { errorMessages };
diff --git a/src/constants/experiments.js b/src/constants/experiments.js
new file mode 100644
index 000000000..13a51aa7f
--- /dev/null
+++ b/src/constants/experiments.js
@@ -0,0 +1,6 @@
+const experimentUniverses = {
+ CONCEPT_FUNNEL: 'ConceptFunnel'
+};
+
+export { experimentUniverses };
+
\ No newline at end of file
diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js
index 3ce2bd6da..75e780e0f 100644
--- a/src/constants/store-actions.js
+++ b/src/constants/store-actions.js
@@ -18,6 +18,7 @@ const storeActions = {
LOAD_ORDER: "loadOrder",
SET_REFERRAL_INFORMATION: "setReferralInformation",
VALIDATE_ZIP: "validateZip",
+ LOG_EXPERIMENT_EXPOSURE: "logExperimentExposure",
// DEPENDENCY MUTATIONS
RESET_VEHICLE_STATE_AND_DEPENDENCIES: "resetVehicleAndDependencies",
diff --git a/src/global-methods.js b/src/global-methods.js
index f0675c02d..c0823f29a 100644
--- a/src/global-methods.js
+++ b/src/global-methods.js
@@ -30,6 +30,7 @@ export default {
}
},
(error) => {
+ console.error(error);
return reject(error.response);
}
);
diff --git a/src/helpers/cms-content-helper.js b/src/helpers/cms-content-helper.js
index 3bd5319b5..642f0d014 100644
--- a/src/helpers/cms-content-helper.js
+++ b/src/helpers/cms-content-helper.js
@@ -1,5 +1,6 @@
import { storeActions } from "@/constants/store-actions.js";
import store from "@/store";
+import { dynamicStrings } from "../constants/dynamic-strings";
export function fetchCmsContentForPage(fmgPage) {
return store
@@ -40,12 +41,15 @@ export function fetchCmsContentForPage(fmgPage) {
function mapStringToState(str) {
// Pull all matches out of the string.
const regexExp = new RegExp("{(.*?):(.*?)}", "g");
- const matches = [...str.matchAll(regexExp)];
+ const regexMatches = [...str.matchAll(regexExp)];
+ const globalStateMatches = regexMatches.filter(match => {
+ return match[1] === dynamicStrings.GLOBAL_STATE;
+ })
// Our final string value that will be built from the matches.
let stringBuilder = "";
- for (const match of matches) {
+ for (const match of globalStateMatches) {
// Reset store state for each match.
let storeState = store.state;
@@ -60,7 +64,7 @@ function mapStringToState(str) {
const stringWithReplacement = str.replace(match[0], storeState);
// If we still have values we need to substitute, call this function again.
- if (stringWithReplacement.includes("{globalState:")) {
+ if (stringWithReplacement.includes(dynamicStrings.GLOBAL_STATE)) {
return mapStringToState(stringWithReplacement);
}
@@ -96,7 +100,7 @@ function findAndReplaceGlobalStateValues(widgetModel, widgetName) {
function processWidgetItemForReplacement(widgetModel, key) {
// If we have a string, and it needs to be replaced.
if (typeof widgetModel[key] === "string") {
- if (widgetModel[key].includes("{globalState:")) {
+ if (widgetModel[key].includes(dynamicStrings.GLOBAL_STATE)) {
widgetModel[key] = mapStringToState(widgetModel[key]);
}
return widgetModel[key];
@@ -116,4 +120,4 @@ function processWidgetItemForReplacement(widgetModel, key) {
// If we have something else like a number, boolean, etc. just return it
return widgetModel[key];
-}
+}
\ No newline at end of file
diff --git a/src/helpers/damage-helper.js b/src/helpers/damage-helper.js
new file mode 100644
index 000000000..c39283d72
--- /dev/null
+++ b/src/helpers/damage-helper.js
@@ -0,0 +1,22 @@
+import store from "@/store";
+
+export function getDamageString() {
+ return store.getters.damage.glassToReplace.length > 1 ? "match" : store.getters.damage.glassToReplace[0].location;
+}
+
+export function compareGlassOptions(newOptions, currentOptions){
+ const optionsMap = {
+ Windshield: "windshieldOptions",
+ Driver: "driverSideOptions",
+ Passenger: "passengerSideOptions",
+ Rear: "backGlassOptions"
+ }
+
+ for(const option of currentOptions){
+ if(!newOptions[optionsMap[option.location]].availableReplacementOptions.includes(option.name)){
+ return true;
+ }
+ }
+
+ return false;
+ }
\ No newline at end of file
diff --git a/src/helpers/damage-helper.spec.js b/src/helpers/damage-helper.spec.js
new file mode 100644
index 000000000..ceb08f876
--- /dev/null
+++ b/src/helpers/damage-helper.spec.js
@@ -0,0 +1,37 @@
+import {getDamageString, compareGlassOptions} from "./damage-helper";
+
+jest.mock("@/store", () => ({
+ getters: {damage: {
+ glassToReplace: [{location: "TEST"}]
+ }
+ }
+ }));
+
+ describe("damage-helper.js", () => {
+ it("Should return damage getter info", () => {
+ const damage = getDamageString();
+ expect(damage).toEqual("TEST")
+ });
+ });
+
+ describe("damage-helper.js", () => {
+ it("Should return false if no mismatches between each array", () => {
+ const newOptions = {
+ windshieldOptions: {availableReplacementOptions: ["windshield"]}
+ }
+ const currentOptions = [{location: "Windshield", name: "windshield"}];
+ const misMatch = compareGlassOptions(newOptions, currentOptions);
+ expect(misMatch).toEqual(false);
+ });
+ });
+
+ describe("damage-helper.js", () => {
+ it("Should return true if there are any mismatches between arrays", () => {
+ const newOptions = {
+ windshieldOptions: {availableReplacementOptions: ["window"]}
+ }
+ const currentOptions = [{location: "Windshield", name: "windshield"}];
+ const misMatch = compareGlassOptions(newOptions, currentOptions);
+ expect(misMatch).toEqual(true);
+ });
+ });
\ No newline at end of file
diff --git a/src/helpers/heritage-integration/cookie-helper.js b/src/helpers/heritage-integration/cookie-helper.js
index 36516a570..bf1c56b72 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=[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}");
+ if(cookieValueMatch){
+ return cookieValueMatch[0].split('=')[1];
+ }
+
+ 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/cookie-helper.spec.js b/src/helpers/heritage-integration/cookie-helper.spec.js
index 9d14389e2..b4498f61b 100644
--- a/src/helpers/heritage-integration/cookie-helper.spec.js
+++ b/src/helpers/heritage-integration/cookie-helper.spec.js
@@ -1,4 +1,4 @@
-import {getFunnelCookie} from "@/helpers/heritage-integration/cookie-helper.js";
+import {getFunnelCookie, getDeviceIdValue, getSessionKeyValue} from "@/helpers/heritage-integration/cookie-helper.js";
import { removeAllTestCookies, setupCookies } from "@/helpers/unit-test-helper";
describe("cookies", () => {
@@ -100,5 +100,31 @@ describe("cookies", () => {
expect(actualCookieValue).toBeNull();
});
})
+
+ describe("getDeviceIdValue", () => {
+ test("getDeviceIdValue, should return GUID", () => {
+ // Arrange
+ setupCookies({});
+
+ // Act
+ const result = getDeviceIdValue();
+
+ //Assert
+ expect(result).toBe('21b9b94a-ec23-42c1-aaac-e2ae4e4dbffe');
+
+ });
+
+ test("getSessionKeyValue, should return session key int", () => {
+ // Arrange
+ setupCookies({});
+
+ // Act
+ const result = getSessionKeyValue();
+
+ //Assert
+ expect(result).toBe('12345');
+
+ });
+ });
})
\ 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..581d81bb6 100644
--- a/src/helpers/heritage-integration/navigation-helper.js
+++ b/src/helpers/heritage-integration/navigation-helper.js
@@ -2,55 +2,38 @@ import { queryStrings } from "@/constants/query-strings";
import { externalUrls } from "@/router/router-constants/externalUrl-values";
import { lazyLoadComponent } from "@/router/dynamic-routing/component-loader.js";
import { saveOrder } from "@/helpers/heritage-integration/order-helper.js";
+import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
import store from "@/store";
import router from "@/router";
/*
If the user has visited the funnel before this method will determine the bets place to
drop them so they don't start at the beginning again. This method will return 'heritage' if
- the user has an existing order and they come back in from the Safelite.com CTA.
+ the user has an existing order and they come back in from the Safelite.com CTA.
*/
export async function getPageToRouteExistingOrderTo(toRoute = {}, existingHeritageOrder = false) {
// If the user is coming in via the Safelite.Com CTA
if (toRoute.query[queryStrings.START_TYPE] === 'fmg') {
-
// If they have an existing order, return 'heritage' for the page name.
if (existingHeritageOrder) {
return 'heritage';
}
+
+ return await getLatestPageForRedirection();
}
-
- // If this is a non-CTA navigation, determine where to send the user based on page prerequisites.
- // This also works if a user has a 'fmg' start_type query string but no current order.
- // That shouldn't happen, but it's possible.
- const vehicleMakeComponent = (await lazyLoadComponent('vehicle-make')()).default;
- const vehicleModelComponent = (await lazyLoadComponent('vehicle-model')()).default;
- const vehicleStyleComponent = (await lazyLoadComponent('vehicle-style')()).default;
- const vehicleDamageComponent = (await lazyLoadComponent('vehicle-damage')()).default;
-
-
- if (!vehicleMakeComponent.methods.arePagePrerequisitesValid()) {
- return "vehicle-year";
- } else if (!vehicleModelComponent.methods.arePagePrerequisitesValid()) {
- return "vehicle-make";
- } else if (!vehicleStyleComponent.methods.arePagePrerequisitesValid()) {
- return "vehicle-model";
- } else if (!vehicleDamageComponent.methods.arePagePrerequisitesValid()) {
- return "vehicle-style";
- } else if (store.getters.damage.isRepair == null || !store.getters.vehicle.carId) {
- return 'vehicle-damage'
- } else {
- if (store.getters.vehicle.vin) {
- return 'vehicle-damage';
- //return "vin-lookup"; (uncomment)
- } else {
- return 'vehicle-damage';
- //return "estimate" (uncomment)
- }
+ // If navigating to a specific page, and that page is not part of the vin pages.
+ // Return that page, so that it can navigate like normal.
+ if (toRoute.query[queryStrings.FMG_PAGE] !== undefined && !isVinRelatedPage(toRoute)) {
+ return overrideYmmsDirectionIfNeeded(toRoute);
}
+ // If this is not a direct link to a page using fmgPage, not from Safelite.com CTA or this is a vin related page.
+ // Get the latest page for redirection.
+ const latestPageRoute = await getLatestPageForRedirection();
+
+ return latestPageRoute;
}
/*
@@ -65,10 +48,81 @@ export async function navigateToHeritageFunnel() {
externalUrls.HERITAGE_FUNNEL,
{
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"
+ src: "concept-funnel"
}
);
-}
\ No newline at end of file
+}
+
+/*
+ Logic for getting the last "valid" page a user visited.
+*/
+async function getLatestPageForRedirection() {
+ // If this is a non-CTA navigation, determine where to send the user based on page prerequisites.
+ // This also works if a user has a 'fmg' start_type query string but no current order.
+ // That shouldn't happen, but it's possible.
+ const vehicleMakeComponent = (await lazyLoadComponent('vehicle-make')()).default;
+ const vehicleModelComponent = (await lazyLoadComponent('vehicle-model')()).default;
+ const vehicleStyleComponent = (await lazyLoadComponent('vehicle-style')()).default;
+ const vehicleDamageComponent = (await lazyLoadComponent('vehicle-damage')()).default;
+
+
+ if (!vehicleMakeComponent.methods.arePagePrerequisitesValid()) {
+ return fmgPageValues.VEHICLE_YEAR;
+ } else if (!vehicleModelComponent.methods.arePagePrerequisitesValid()) {
+ return fmgPageValues.VEHICLE_MAKE;
+ } else if (!vehicleStyleComponent.methods.arePagePrerequisitesValid()) {
+ return fmgPageValues.VEHICLE_MODEL;
+ } else if (!vehicleDamageComponent.methods.arePagePrerequisitesValid()) {
+ return fmgPageValues.VEHICLE_STYLE;
+ } else if (store.getters.damage.isRepair == null || !store.getters.vehicle.carId) {
+ return fmgPageValues.VEHICLE_DAMAGE;
+ } else {
+ if (store.getters.vehicle.vin) {
+ return fmgPageValues.VIN_LOOKUP;
+ } else {
+ return fmgPageValues.ESTIMATE;
+ }
+ }
+}
+
+/*
+ Overrides functionality to go to the YMMS pages in certain cases.
+ If this is not one of the cases, it returns the 'to' fmgPage value.
+*/
+
+/* istanbul ignore next */
+function overrideYmmsDirectionIfNeeded(toRoute) {
+ const fmgPageValue = toRoute.query[queryStrings.FMG_PAGE];
+
+ if (store.getters.payment.insuranceCoverage.isVerified) {
+ switch (fmgPageValue) {
+ case fmgPageValues.VEHICLE_YEAR:
+ case fmgPageValues.VEHICLE_MAKE:
+ case fmgPageValues.VEHICLE_MODEL:
+ case fmgPageValues.VEHICLE_STYLE:
+ {
+ return fmgPageValues.VEHICLE_DAMAGE;
+ }
+ default: {
+ return fmgPageValue;
+ }
+ }
+ } else {
+ return fmgPageValue;
+ }
+}
+
+/*
+ Determine if the page is a vin related page.
+*/
+
+/* istanbul ignore next */
+function isVinRelatedPage(toRoute) {
+ const fmgPageValue = toRoute.query[queryStrings.FMG_PAGE];
+
+ return fmgPageValue === fmgPageValues.VIN_LOOKUP ||
+ fmgPageValue === fmgPageValues.LICENSE_PLATE_LOOKUP ||
+ fmgPageValue === fmgPageValues.ADDRESS_LOOKUP ||
+ fmgPageValue === fmgPageValues.ADDRESS_VEHICLES ||
+ fmgPageValue === fmgPageValues.ESTIMATE;
+}
diff --git a/src/helpers/heritage-integration/navigation-helper.spec.js b/src/helpers/heritage-integration/navigation-helper.spec.js
index be1136e63..fd06920c2 100644
--- a/src/helpers/heritage-integration/navigation-helper.spec.js
+++ b/src/helpers/heritage-integration/navigation-helper.spec.js
@@ -228,8 +228,7 @@ describe("getPageToRouteExistingOrderTo", () => {
const result = await getPageToRouteExistingOrderTo(toRoute, false);
//Assert
- //expect(result).toBe('vin-lookup');
- expect(result).toBe('vehicle-damage');
+ expect(result).toBe('vin-lookup');
});
test("getPageToRouteExistingOrderTo, should return estimate", async () => {
@@ -284,8 +283,7 @@ describe("getPageToRouteExistingOrderTo", () => {
const result = await getPageToRouteExistingOrderTo(toRoute, false);
//Assert
- //expect(result).toBe('estimate');
- expect(result).toBe('vehicle-damage');
+ expect(result).toBe('estimate');
});
test("getPageToRouteExistingOrderTo, existing order, should return heritage", async () => {
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/helpers/unit-test-helper.js b/src/helpers/unit-test-helper.js
index 4b9aeaccd..20fad7ba2 100644
--- a/src/helpers/unit-test-helper.js
+++ b/src/helpers/unit-test-helper.js
@@ -60,7 +60,9 @@ export const cookies = {
[cookieNames.FUNNEL_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",
"anotherCookie": "{}",
- "someOtherCookie": "{}"
+ "someOtherCookie": "{}",
+ "dxdev": "did=21b9b94a-ec23-42c1-aaac-e2ae4e4dbffe",
+ "skey": "12345"
};
export function removeAllTestCookies() {
diff --git a/src/layouts/address-lookup/address-lookup.vue b/src/layouts/address-lookup/address-lookup.vue
index 4ef9bbd6d..4b17c77a6 100644
--- a/src/layouts/address-lookup/address-lookup.vue
+++ b/src/layouts/address-lookup/address-lookup.vue
@@ -44,6 +44,7 @@
ref="funnelFooter"
:isDisabled="!meta.valid"
@ForwardClicked="forwardButtonAction"
+ :isForwardActionDisabled="!meta.valid"
/>
diff --git a/src/layouts/estimate/estimate.vue b/src/layouts/estimate/estimate.vue
new file mode 100644
index 000000000..0e41b24ed
--- /dev/null
+++ b/src/layouts/estimate/estimate.vue
@@ -0,0 +1,13 @@
+
+ Estimate
+
+
diff --git a/src/layouts/form-test/form-test.vue b/src/layouts/form-test/form-test.vue
index 8e9aefe22..046b0c234 100644
--- a/src/layouts/form-test/form-test.vue
+++ b/src/layouts/form-test/form-test.vue
@@ -44,7 +44,7 @@
/>
diff --git a/src/layouts/license-plate-lookup/license-plate-lookup.vue b/src/layouts/license-plate-lookup/license-plate-lookup.vue
index a6d181ddd..f76a98c69 100644
--- a/src/layouts/license-plate-lookup/license-plate-lookup.vue
+++ b/src/layouts/license-plate-lookup/license-plate-lookup.vue
@@ -26,7 +26,8 @@
@@ -38,7 +39,8 @@
/>
@@ -50,7 +52,7 @@
@@ -75,8 +77,10 @@ import baseMixin from "@/mixins/base-mixin.js";
import { storeActions } from "@/constants/store-actions";
import { storeMutations } from "@/constants/store-mutations";
import { errorMessages } from "@/constants/error-messages";
+import { getDamageString, compareGlassOptions } from "@/helpers/damage-helper";
import { required, regex } from "@/helpers/validation-rules";
import { Form, defineRule } from "vee-validate";
+import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
// DEFINE VALIDATION RULES
defineRule("license-plate-required", required(errorMessages.LICENSE_PLATE_REQUIRED));
@@ -118,8 +122,32 @@ export default {
zip: '',
email: '',
serviceZip: '',
+ carIdEntered: '',
+ customAlertData: {},
+ newCarId: false,
+ glassOptionsMismatch: false,
};
},
+ computed: {
+ MatchedDifferentVehicleAlertHeader(){
+ let text = this.getCmsContent("MatchedDifferentVehicleAlertWidget", "HeadlineText").replaceAll("{custom:damage}", getDamageString());
+
+ return text;
+ },
+ MatchedDifferentVehicleAlertBody(){
+ let text = this.getCmsContent("MatchedDifferentVehicleAlertWidget", "BodyText").replaceAll("{custom:damage}", getDamageString()).replaceAll("{custom:plateLookupYear}", this.customAlertData?.vehicleInfo?.year).replaceAll("{custom:plateLookupMake}", this.customAlertData?.vehicleInfo?.make).replaceAll("{custom:plateLookupModel}", this.customAlertData?.vehicleInfo?.model);
+
+ return text;
+ },
+ NoServiceZipHeader(){
+ let text = this.getCmsContent("NoServiceZipWidget", "HeadlineText").replaceAll("{custom:zip}", this.zip);
+
+ return text;
+ },
+ NoServiceZipBody(){
+ return this.getCmsContent("NoServiceZipWidget", "BodyText");
+ }
+ },
methods: {
arePagePrerequisitesValid() {
return store.getters.vehicle.carId !== null;
@@ -137,43 +165,58 @@ export default {
async forwardButtonAction() {
const zipValidation = this.serviceZip ? await this.validateZip(this.serviceZip) : await this.validateZip(this.zip);
if (!zipValidation.data.isServiceable) {
+ this.customAlertData.zip = this.zip;
this.$refs.funnelFooter.removeLoader();
+ this.vinDoesNotMatchCarId = false;
+ this.vinNotValid = false;
this.newServiceZipRequired = true;
return;
- }
+ }
+
const vinLookup = await this.lookupVin(this.licensePlate, zipValidation.data.state).catch(() => {
this.$refs.funnelFooter.removeLoader();
+ this.vinDoesNotMatchCarId = false;
this.vinNotValid = true;
return;
});
- if (vinLookup.data.vehicle.carId !== store.getters.vehicle.carId) {
+
+ if ((vinLookup.data.vehicle.carId !== store.getters.vehicle.carId) && (vinLookup.data.vehicle.carId !== this.carIdEntered)) {
+ this.carIdEntered = vinLookup.data.vehicle.carId;
+ this.customAlertData.vehicleInfo = vinLookup.data.vehicle;
+ this.newCarId = true;
+ const glassOptions = await baseMixin.methods.dispatchNonBlockingStoreAction(
+ storeActions.GET_DAMAGE_OPTIONS,
+ { carId: vinLookup.data.vehicle.carId }
+ );
+ this.glassOptionsMismatch = compareGlassOptions(glassOptions.data, store.getters.damage.glassToReplace);
this.$refs.funnelFooter.updateButtonText(`Continue with ${vinLookup.data.vin} ${vinLookup.data.vehicle.year} ${vinLookup.data.vehicle.make} ${vinLookup.data.vehicle.model}`);
this.$refs.funnelFooter.removeLoader();
+ this.vinNotValid = false;
this.vinDoesNotMatchCarId = true;
return;
}
+ this.updateCustomerInfo(vinLookup.data.vin, vinLookup.data.vehicle, zipValidation.data.state);
+
const partsData = await baseMixin.methods.dispatchNonBlockingStoreAction(
this.storeActions.GET_PARTS_OR_QUESTIONS,
{
- carId: store.getters.vehicle.carId,
- glassArray: store.getters.damage.glassToReplace,
- zipCode: this.zip,
- vin: vinLookup.vin
+ carId: vinLookup.data.vehicle.carId,
+ glassArray: store.getters.damage.glassToReplace ? store.getters.damage.glassToReplace : [],
+ zipCode: this.serviceZip ? this.serviceZip : this.zip,
+ vin: vinLookup.data.vin
},
false
);
this.navigateForward(partsData);
},
navigateForward(partsData){
- if(partsData.data.partsOrQuestions[0].partQuestions && partsData.data.partsOrQuestions[0].partQuestions.length > 0){
- this.$router.navigateAfterSave(this.navigationScenarios.CONTINUING_WITH_PARTS_QUESTION, this.$route, {}, {}, partsData.data);
+ if(this.newCarId && this.glassOptionsMismatch){
+ this.$router.navigateAfterSave(this.navigationScenarios.CLICKED_FORWARD, this.$route, {}, { displayVehicleChangeAlert: true }, partsData.data);
return;
- } else if((!partsData.data.partsOrQuestions[0].partQuestions || partsData.data.partsOrQuestions[0].partQuestions.length < 1) && partsData.data.partsOrQuestions[0].parts.length > 1) {
- this.$router.navigateAfterSave(this.navigationScenarios.CONTINUING_WITH_MULTIPLE_PARTS, this.$route, {}, {}, partsData.data);
- return;
} else {
- this.$router.navigate(this.navigationScenarios.CONTINUING_WITH_SINGLE_PART, this.$route);
+ navigateToHeritageFunnel();
+ return;
}
},
validateZip(zip) {
@@ -188,27 +231,32 @@ export default {
{ licensePlate: plate, licenseState: state }
);
},
- updateStore() {
- // if(vehicleDamage){
- // store.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
- // }
- store.commit(storeMutations.UPDATE_VEHICLE_VIN, null);
- store.commit(storeMutations.UPDATE_YEAR, null);
- store.commit(storeMutations.UPDATE_MAKE, null);
- store.commit(storeMutations.UPDATE_MODEL, null);
- store.commit(storeMutations.UPDATE_STYLE, null);
- store.commit(storeMutations.UPDATE_CAR_ID, null);
- store.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, null);
- store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, null);
- store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, null);
- store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, null);
- store.commit(storeMutations.UPDATE_REGISTRATION_LICENSE_PLATE, null);
- store.commit(storeMutations.UPDATE_REGISTRATION_STATE, null);
- store.commit(storeMutations.UPDATE_REGISTRATION_ZIP_CODE, null);
- store.commit(storeMutations.UPDATE_SERVICE_LOCATION_ZIP, null);
- store.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, null);
+ updateCustomerInfo(vin, vehicleInfo, registrationState) {
+ if(this.newCarId && this.glassOptionsMismatch){
+ store.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
+ }
+ store.commit(storeMutations.UPDATE_VEHICLE_VIN, vin);
+ store.commit(storeMutations.UPDATE_YEAR, vehicleInfo.year);
+ store.commit(storeMutations.UPDATE_MAKE, vehicleInfo.make);
+ store.commit(storeMutations.UPDATE_MODEL, vehicleInfo.model);
+ store.commit(storeMutations.UPDATE_STYLE, vehicleInfo.style);
+ store.commit(storeMutations.UPDATE_CAR_ID, vehicleInfo.carId);
+ store.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, vehicleInfo.category);
+ store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, vehicleInfo.imageUrl);
+ store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, vehicleInfo.imageVifNumber);
+ store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, vehicleInfo.imageColor);
+ store.commit(storeMutations.UPDATE_REGISTRATION_LICENSE_PLATE, this.licensePlate);
+ store.commit(storeMutations.UPDATE_REGISTRATION_STATE, registrationState);
+ store.commit(storeMutations.UPDATE_REGISTRATION_ZIP_CODE, this.zip);
+ store.commit(storeMutations.UPDATE_SERVICE_LOCATION_ZIP, this.serviceZip);
+ store.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, this.email);
},
},
+ watch: {
+ licensePlate() {
+ this.$refs.funnelFooter.updateButtonText(this.getCmsContent("FunnelFooterWidget", "ForwardButtonText"));
+ }
+ },
components: {
Form,
funnelHeader,
diff --git a/src/layouts/part-questions/part-questions.vue b/src/layouts/part-questions/part-questions.vue
index c1b9142bd..0818bb46f 100644
--- a/src/layouts/part-questions/part-questions.vue
+++ b/src/layouts/part-questions/part-questions.vue
@@ -1,10 +1,12 @@
-
-
-
+
+
+
Part Questions Page Placeholder
-
+
@@ -49,18 +51,7 @@ export default {
// Call the "next" function to complete the transition to this page.
next((vm) => {
- vm.$refs.funnelHeader.initializeComponent(
- resultMap.cmsContent.FunnelHeaderWidget
- );
- vm.$refs.vehicleBanner.initializeComponent(
- resultMap.cmsContent.VehicleBannerWidget
- );
- vm.$refs.funnelSubHeader.initializeComponent(
- resultMap.cmsContent.FunnelSubHeaderWidget
- );
- vm.$refs.funnelFooter.initializeComponent(
- resultMap.cmsContent.FunnelFooterWidget
- );
+ vm.setCmsContent(resultMap.cmsContent);
});
},
methods: {
diff --git a/src/layouts/vehicle-damage/vehicle-damage.spec.js b/src/layouts/vehicle-damage/vehicle-damage.spec.js
index fe3a4aa51..7b28e4bb5 100644
--- a/src/layouts/vehicle-damage/vehicle-damage.spec.js
+++ b/src/layouts/vehicle-damage/vehicle-damage.spec.js
@@ -36,6 +36,11 @@ jest.mock("@/store", () => ({
vehicle: {
carId: "C00000000",
image: "test.jpg",
+ payment: {
+ insuranceCoverage: {
+ isVerified: false
+ }
+ }
},
eventBusItem: jest.fn(),
damage: {
@@ -134,6 +139,7 @@ describe("vehicle-damage.vue", () => {
store: {
getters: {
vehicle: {},
+ payment: { insuranceCoverage: { isVerified: false } },
},
},
},
@@ -201,6 +207,7 @@ describe("vehicle-damage.vue", () => {
store: {
getters: {
vehicle: {},
+ payment: { insuranceCoverage: { isVerified: false } },
},
},
},
@@ -291,6 +298,7 @@ describe("vehicle-damage.vue", () => {
store: {
getters: {
vehicle: {},
+ payment: { insuranceCoverage: { isVerified: false } },
},
},
},
@@ -336,6 +344,7 @@ describe("vehicle-damage.vue", () => {
store: {
getters: {
vehicle: {},
+ payment: { insuranceCoverage: { isVerified: false } },
},
},
},
@@ -734,6 +743,7 @@ function setupMocks({
store: {
getters: {
vehicle: {},
+ payment: { insuranceCoverage: { isVerified: false } },
},
},
},
diff --git a/src/layouts/vehicle-damage/vehicle-damage.vue b/src/layouts/vehicle-damage/vehicle-damage.vue
index b84e2fbfa..543a02bef 100644
--- a/src/layouts/vehicle-damage/vehicle-damage.vue
+++ b/src/layouts/vehicle-damage/vehicle-damage.vue
@@ -47,7 +47,8 @@
/>
@@ -145,9 +146,11 @@ export default {
}
return false;
},
+
resetDependentState() {
store.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
},
+
backButtonAction() {
// route to move backwards
this.$router.navigate(
@@ -155,19 +158,20 @@ export default {
this.$route
);
},
+
getDamageLocationsFromStore() {
var glassSelections = [];
- if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD }) ||
+ if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD }) ||
store.getters.damage.isRepair) {
glassSelections.push(damageLocationsSelected.WINDSHIELD);
}
- if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.DRIVER ||
+ if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.DRIVER ||
glass.location === damageLocationsSelected.PASSENGER })) {
glassSelections.push(damageLocationsSelected.SIDEDOOR);
}
- if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.REAR })) {
+ if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.REAR })) {
glassSelections.push(damageLocationsSelected.REARWINDOW);
}
@@ -180,19 +184,19 @@ export default {
if (store.getters.damage.isRepair === undefined) return windshieldOptions;
if (!store.getters.damage.isRepair) {
- if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD &&
+ if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD &&
glass.name === damageLocationsSelected.SINGLE })) {
windShieldOptions.selectedWindshieldDamageType.push(damageLocationsSelected.REPLACE);
windShieldOptions.selectedWindshieldReplaceOptions.push(damageLocationsSelected.SINGLE);
}
- if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD &&
+ if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD &&
glass.name === damageLocationsSelected.DRIVER })) {
windShieldOptions.selectedWindshieldDamageType.push(damageLocationsSelected.REPLACE);
windShieldOptions.selectedWindshieldReplaceOptions.push(damageLocationsSelected.DRIVER);
}
- if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD &&
+ if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD &&
glass.name === damageLocationsSelected.PASSENGER })) {
windShieldOptions.selectedWindshieldDamageType.push(damageLocationsSelected.REPLACE);
windShieldOptions.selectedWindshieldReplaceOptions.push(damageLocationsSelected.PASSENGER);
@@ -210,11 +214,11 @@ export default {
getDoorSidesFromStore() {
var doorSides = [];
- if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.DRIVER })){
+ if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.DRIVER })){
doorSides.push(damageLocationsSelected.DRIVERSIDE);
}
- if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.PASSENGER })){
+ if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.PASSENGER })){
doorSides.push(damageLocationsSelected.PASSENGERSIDE);
}
@@ -224,7 +228,7 @@ export default {
getDriverSideReplaceOptionsFromStore() {
var driverSideReplaceOptions = [];
- store.getters.damage.glassToReplace.forEach(glass => {
+ store.getters.damage.glassToReplace?.forEach(glass => {
if (glass.location === damageLocationsSelected.DRIVER){
driverSideReplaceOptions.push(glass.name);
}
@@ -236,7 +240,7 @@ export default {
getPassengerSideReplaceOptionsFromStore() {
var passengerSideReplaceOptions = [];
- store.getters.damage.glassToReplace.forEach(glass => {
+ store.getters.damage.glassToReplace?.forEach(glass => {
if (glass.location === damageLocationsSelected.PASSENGER){
passengerSideReplaceOptions.push(glass.name);
}
@@ -248,7 +252,7 @@ export default {
getRearReplaceOptionsFromStore(){
var rearReplaceOptions = [];
- store.getters.damage.glassToReplace.forEach(glass => {
+ store.getters.damage.glassToReplace?.forEach(glass => {
if (glass.location === damageLocationsSelected.REAR){
rearReplaceOptions.push(glass.name);
}
@@ -275,13 +279,20 @@ export default {
},
navigateForward(partsData){
- // CSR-98 TEMP
- const vehicleYearsToShowHeritageFunnel = [ "2001", "2002", "2010" ];
+
+ // Temporary easter egg to navigate to heritage funnel.
+ const vehicleYearsToShowHeritageFunnel = [ 2001, 2002, 2010, 2016 ];
if (vehicleYearsToShowHeritageFunnel.includes(store.getters.vehicle.year)) {
navigateToHeritageFunnel();
return;
}
+ // If vin already exists, navigate directly to vin-lookup
+ if(this.$store.getters.vehicle.vin){
+ this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_WITH_VIN, this.$route);
+ return;
+ }
+
//found problem questions
if (partsData.data.partsOrQuestions.some(pq => pq.partQuestions != null && pq.partQuestions.length > 0)){
this.$router.navigateAfterSave(this.navigationScenarios.SELECTED_DAMAGE_WITH_PART_QUESTIONS, this.$route, {}, {}, partsData.data);
@@ -382,7 +393,7 @@ export default {
return this.isWindshieldDamageLocation && this.selectedDamageLocations.length > 1 && this.isWindshieldRepair;
},
hasSplitSingleConflict() {
- if (!this.selectedWindshieldOptions.selectedWindshieldReplaceOptions) return false;
+ if (!this.selectedDamageLocations || !this.selectedDamageLocations.includes("Windshield") || !this.selectedWindshieldOptions.selectedWindshieldDamageType || !this.selectedWindshieldOptions.selectedWindshieldDamageType.includes("Replace") || !this.selectedWindshieldOptions.selectedWindshieldReplaceOptions) return false;
return this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.some(selectedSingleWindshield =>
{
@@ -398,6 +409,9 @@ export default {
})
);
},
+ shouldHideBackButton(){
+ return this.$store.getters.payment.insuranceCoverage.isVerified;
+ }
},
components: {
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/layouts/vin-lookup/vin-lookup.vue b/src/layouts/vin-lookup/vin-lookup.vue
index fc9058abf..c3a1bbc33 100644
--- a/src/layouts/vin-lookup/vin-lookup.vue
+++ b/src/layouts/vin-lookup/vin-lookup.vue
@@ -1,41 +1,272 @@
-
-
-
-
-
-
-
+
-
-
diff --git a/src/mixins/base-mixin.js b/src/mixins/base-mixin.js
index b5a954c43..11947f26a 100644
--- a/src/mixins/base-mixin.js
+++ b/src/mixins/base-mixin.js
@@ -3,6 +3,7 @@ import { storeActions } from "@/constants/store-actions.js";
import { storeMutations } from "@/constants/store-mutations.js";
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
import { vehicleCategories } from "@/constants/vehicle-categories.js";
+import { queryStrings } from "@/constants/query-strings";
export default {
data() {
@@ -11,10 +12,10 @@ export default {
};
},
methods: {
- setCmsContent(cmsContent){
+ setCmsContent(cmsContent) {
this.$root.cmsContentByWidget = cmsContent;
},
- getCmsContent(widgetName, fieldName){
+ getCmsContent(widgetName, fieldName) {
return this.$root.cmsContentByWidget?.[widgetName]?.[fieldName] ? this.$root.cmsContentByWidget[widgetName][fieldName] : '';
},
dispatchNonBlockingStoreAction(type, payload, encodePayload = true) {
@@ -25,10 +26,10 @@ export default {
return store.dispatch(type, payload);
},
- savePageDataToStore(page, data){
+ savePageDataToStore(page, data) {
store.commit(storeMutations.UPDATE_PAGE_DATA, { page: page, data: data });
},
- onSubmit() {}, // DO NOT REMOVE; needed to prevent default form submit behavior
+ onSubmit() { }, // DO NOT REMOVE; needed to prevent default form submit behavior
onInvalidSubmit({ values, errors, results }) {
// identify the first error field and put focus on it
// get error names array
@@ -40,6 +41,28 @@ export default {
el && el.focus();
}
},
+
+ pushEventToGA(category, action, label, value, pageName) {
+ const eventToBePushed = {
+ 'event': 'ga_event',
+ 'category': category,
+ 'action': action,
+ 'label': label,
+ 'value': value,
+ 'path': `/fmg/?${queryStrings.FMG_PAGE}=${pageName}`
+ }
+ pushToDataLayerIfDefined(eventToBePushed);
+ },
+
+ pushPageViewToGA(pageName) {
+ const pageViewEvent = {
+ 'event': 'logPageview',
+ 'pagePath': `/fmg/?${queryStrings.FMG_PAGE}=${pageName}`,
+ 'pageTitle': pageName
+ };
+ pushToDataLayerIfDefined(pageViewEvent);
+ }
+
},
computed: {
storeActions() {
@@ -65,3 +88,9 @@ function encodeUriData(payload) {
});
}
}
+
+function pushToDataLayerIfDefined(data) {
+ if (window.dataLayer !== undefined) {
+ window.dataLayer.push(data);
+ }
+}
diff --git a/src/router/index.js b/src/router/index.js
index 778c55cfc..54c0bfac2 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -4,12 +4,13 @@ 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 { queryStrings } from "@/constants/query-strings";
// Heritage integration
import { isSavedSessionStillActive } from "@/helpers/heritage-integration/session-helper";
-import { updateOrCreateFunnelCookie,getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
+import { updateOrCreateFunnelCookie, getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
import { loadOrderIfPresent, saveOrder } from "@/helpers/heritage-integration/order-helper";
-import { getPageToRouteExistingOrderTo, navigateToHeritageFunnel} from "@/helpers/heritage-integration/navigation-helper";
+import { getPageToRouteExistingOrderTo, navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
import baseMixin from "@/mixins/base-mixin";
import eventBus from "@/helpers/event-bus/event-bus";
@@ -35,84 +36,79 @@ const routes = [
name: "root",
async beforeEnter(to, from, next) {
// 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 {
+ try {
- // If the saved session has timed out, clear the session, execute 404 logic.
- if (getFunnelCookie() !== null && !isSavedSessionStillActive()) {
- await GoToFunnelStartOn404(next);
- }
-
-
-
- // On entering the funnel "fresh", read cookie information, decide what to do next.
- if (from.redirectedFrom === undefined) {
- const loadOrderResponse = await loadOrderIfPresent();
- const pageToRedirectTo = await getPageToRouteExistingOrderTo(to, loadOrderResponse);
-
- // If getPageToRouteExistingOrderTo determines that the return user needs to
- // go back to heritage funnel, send them there and stop our current navigation.
- if (pageToRedirectTo === 'heritage') {
- await navigateToHeritageFunnel();
- return next(false);
- }
-
- // Assign our fmgPage so it will load normally like the other pages.
- to.query.fmgPage = pageToRedirectTo;
- }
-
- // Process funnel cookie.
- updateOrCreateFunnelCookie();
-
- // 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.
- let component = router.getRoutes().filter((x) => x.name === to.query.fmgPage)[0].components;
-
- // If the component hasn't been loaded fully, load it before we check prerequisites.
- if (component.default.methods === undefined) {
- component = await component.default();
- }
-
- if (!arePagePrerequisitesValid(component)) {
- await GoToFunnelStartOn404(next);
- }
-
- return next({ name: to.query.fmgPage, query: to.query, params: to.params });
- }
-
- // Get route info for the given url. Names will have a 1:1 relationship with names in the Cms.
- const routeData = await GetRouteInfoFromPageName(to.query.fmgPage);
-
- // Add our dynamic route.
- router.addRoute({
- path: routeData[0].path, // Always the same path, because we control it with query strings.
- name: routeData[0].name,
- component: routeData[0].component,
- });
-
- // Call the next components arePagePrerequisitesValid method before load.
- // If it returns false, use the 404 logic.
- const nextComponent = await router.getRoutes().filter((x) => x.name === routeData[0].name)[0].components.default();
-
- if (!arePagePrerequisitesValid(nextComponent)) {
- await GoToFunnelStartOn404(next);
- }
-
- // Assign current query string parameters, as well as our fmgPage one.
- next({
- name: routeData[0].name,
- query: Object.assign(to.query, { fmgPage: routeData[0].name }),
- params: to.params
- });
- } catch (error) {
- console.log(error);
-
- // If we don't have a route, go to our 404 page.
+ // If the saved session has timed out, clear the session, execute 404 logic.
+ if (getFunnelCookie() !== null && !isSavedSessionStillActive()) {
await GoToFunnelStartOn404(next);
}
+
+ // On entering the funnel "fresh", read cookie information, decide what to do next.
+ if (from.redirectedFrom === undefined) {
+ const loadOrderResponse = await loadOrderIfPresent();
+ const pageToRedirectTo = await getPageToRouteExistingOrderTo(to, loadOrderResponse);
+
+ // If getPageToRouteExistingOrderTo determines that the return user needs to
+ // go back to heritage funnel, send them there and stop our current navigation.
+ if (pageToRedirectTo === 'heritage') {
+ await navigateToHeritageFunnel();
+ return next(false);
+ }
+
+
+ // Assign our fmgPage so it will load normally like the other pages.
+ to.query.fmgPage = pageToRedirectTo;
+ }
+
+ // Process funnel cookie.
+ updateOrCreateFunnelCookie();
+
+ // 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.
+ let component = router.getRoutes().filter((x) => x.name === to.query.fmgPage)[0].components;
+
+ // If the component hasn't been loaded fully, load it before we check prerequisites.
+ if (component.default.methods === undefined) {
+ component = await component.default();
+ }
+
+ if (!arePagePrerequisitesValid(component)) {
+ await GoToFunnelStartOn404(next);
+ }
+
+ return next({ name: to.query.fmgPage, query: to.query, params: to.params });
+ }
+
+ // Get route info for the given url. Names will have a 1:1 relationship with names in the Cms.
+ const routeData = await GetRouteInfoFromPageName(to.query.fmgPage);
+
+ // Add our dynamic route.
+ router.addRoute({
+ path: routeData[0].path, // Always the same path, because we control it with query strings.
+ name: routeData[0].name,
+ component: routeData[0].component,
+ });
+
+ // Call the next components arePagePrerequisitesValid method before load.
+ // If it returns false, use the 404 logic.
+ const nextComponent = await router.getRoutes().filter((x) => x.name === routeData[0].name)[0].components.default();
+
+ if (!arePagePrerequisitesValid(nextComponent)) {
+ await GoToFunnelStartOn404(next);
+ }
+
+ // Assign current query string parameters, as well as our fmgPage one.
+ next({
+ name: routeData[0].name,
+ query: Object.assign(to.query, { fmgPage: routeData[0].name }),
+ params: to.params
+ });
+ } catch (error) {
+ console.log(error);
+
+ // If we don't have a route, go to our 404 page.
+ await GoToFunnelStartOn404(next);
}
},
},
@@ -125,6 +121,10 @@ const router = createRouter({
//---------------------------------------------------------- Router Functions ----------------------------------------------------------
+router.afterEach((to, from) => {
+ baseMixin.methods.pushPageViewToGA(to.query[queryStrings.FMG_PAGE]);
+});
+
router.navigate = (scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) => {
navigate(scenario, currentRoute, false, optionalQuery, optionalParams, optionalPageData);
}
@@ -205,6 +205,13 @@ function navigateToUrl(url, optionalQuery = {}) {
externalUrl.searchParams.append(queryKey, optionalQuery[queryKey]);
}
+ /////////////////////////////////////////////////////
+ // TEMP CODE FOR TESTING WITH SPECIFIC EXPERIMENTS //
+ /////////////////////////////////////////////////////
+ if (externalUrl.search.indexOf("corid=") != -1)
+ externalUrl.search = externalUrl.search + '&experiments=CollectEmailOnQuote=CollectEmailOnQuote_V1=YesCollectEmail_TEST1=true,RemoveServiceAreaPage=ServAreaRemoval_V7=ServAreaNoRemove_V7_TEST=true,VINeducationV2=VINeducation_V2=NoShowVINmodalV2_CONTROL=true,ServicePackages=ServicePackages_V1=NoShowPackages_CONTROL=true,PhotoUploadRedesign=PhotoUploadRedesign_V1=CurrentPhotoUpload_CONTROL=true,ScheduleDetailsServiceType=ScheduleBeforeServiceType_V1=ServTypeThenSched_CONTROL=true';
+ ///////////// END TEMP CODE /////////////////////////
+
window.location.assign(externalUrl);
}
diff --git a/src/router/router-constants/fmgPage-values.js b/src/router/router-constants/fmgPage-values.js
index f3c811de3..9abb45cd2 100644
--- a/src/router/router-constants/fmgPage-values.js
+++ b/src/router/router-constants/fmgPage-values.js
@@ -11,6 +11,7 @@ const fmgPageValues = {
LICENSE_PLATE_LOOKUP: "license-plate-lookup",
REVEAL: "reveal",
ESTIMATE: "estimate",
+ ADDRESS_VEHICLES: "address-vehicles",
};
export { fmgPageValues };
diff --git a/src/router/router-constants/navigation-scenarios.js b/src/router/router-constants/navigation-scenarios.js
index da170da53..57f07a041 100644
--- a/src/router/router-constants/navigation-scenarios.js
+++ b/src/router/router-constants/navigation-scenarios.js
@@ -4,7 +4,9 @@ const navigationScenarios = {
SELECTED_MAKE: "SELECTED_MAKE",
SELECTED_STYLE: "SELECTED_STYLE",
CLICKED_BACK: "CLICKED_BACK",
+ CLICKED_BACK_WITH_VIN: "CLICKED_BACK_WITH_VIN",
CLICKED_FORWARD: "CLICKED_FORWARD",
+ CLICKED_FORWARD_WITH_VIN: "CLICKED_FORWARD_WITH_VIN",
SELECTED_PARTS: "SELECTED_PARTS",
SELECTED_DAMAGE_WITH_SINGLE_PART: "SELECTED_DAMAGE_WITH_SINGLE_PART",
SELECTED_DAMAGE_WITH_MULTIPLE_PARTS: "SELECTED_DAMAGE_WITH_MULTIPLE_PARTS",
diff --git a/src/router/router-constants/routing-table.js b/src/router/router-constants/routing-table.js
index fd750e1a8..5b991b63e 100644
--- a/src/router/router-constants/routing-table.js
+++ b/src/router/router-constants/routing-table.js
@@ -1,6 +1,5 @@
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
-import { applicationConfig } from "@/constants/application-config";
const routingTable = [
{
@@ -59,6 +58,10 @@ const routingTable = [
destinationFmgPageValue: fmgPageValues.VEHICLE_STYLE,
},
// UNDO THESE CHANGES BEFORE MERGING TO develop
+ {
+ scenario: navigationScenarios.CLICKED_FORWARD_WITH_VIN,
+ destinationFmgPageValue: fmgPageValues.VIN_LOOKUP,
+ },
{
scenario: navigationScenarios.SELECTED_DAMAGE_WITH_SINGLE_PART,
destinationFmgPageValue: fmgPageValues.ADDRESS_LOOKUP
@@ -109,6 +112,10 @@ const routingTable = [
maps: [
{
scenario: navigationScenarios.CLICKED_BACK,
+ destinationFmgPageValue: fmgPageValues.REVEAL,
+ },
+ {
+ scenario: navigationScenarios.CLICKED_BACK_WITH_VIN,
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
},
{
@@ -136,6 +143,10 @@ const routingTable = [
scenario: navigationScenarios.CONTINUING_WITH_SINGLE_PART,
destinationFmgPageValue: fmgPageValues.REVEAL,
},
+ {
+ scenario: navigationScenarios.CLICKED_FORWARD,
+ destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
+ },
],
},
{
diff --git a/src/store/index.js b/src/store/index.js
index 780b50c89..8b630b57e 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -45,10 +45,16 @@ const getDefaultState = () => {
glassParts: null,
otherParts: null
},
+ payment:{
+ isInsurance: null,
+ insuranceCoverage: {
+ isVerified: null
+ }
+ },
referralNumber: null,
referralDate: null,
referralCorrelationId: null,
- parentAccountNumber: null,
+ parentAccountNumber: 0,
},
applicationUser: {
eventBus: [],
@@ -93,7 +99,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) {
@@ -120,6 +126,27 @@ export const mutations = {
updateParentAcctNumber(state, parentAcctNumber) {
state.order.parentAccountNumber = parentAcctNumber;
},
+ updateIsInsurance(state, isInsurance) {
+ state.order.payment.isInsurance = isInsurance;
+ },
+ updateInsuranceVerifiedStatus(state, isVerified) {
+ state.order.payment.insuranceCoverage.isVerified = isVerified;
+ },
+ updateRegistrationLicensePlate(state, licensePlate){
+ state.order.vehicle.registration.licensePlate = licensePlate;
+ },
+ updateRegistrationState(state, registrationState){
+ state.order.vehicle.registration.state = registrationState;
+ },
+ updateRegistrationZipCode(state, reistrationZipCode){
+ state.order.vehicle.registration.zipCode = reistrationZipCode;
+ },
+ updateServiceLocationZip(state, serviceLocationZip){
+ state.order.serviceLocation.zip = serviceLocationZip;
+ },
+ updateCustomerEmailAddress(state, customerEmailAddress){
+ state.order.customer.emailAddress = customerEmailAddress;
+ },
// EVENT BUS MUTATIONS
@@ -176,23 +203,30 @@ 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,
style: orderInformation.vehicle?.style,
+ vin: orderInformation.vehicle?.vin,
carId: orderInformation.vehicle?.carId,
category: orderInformation.vehicle?.category,
imageUrl: orderInformation.vehicle?.imageUrl,
imageVifNumber: orderInformation.vehicle?.imageVifNumber,
- imageColor: orderInformation.vehicle?.imageVifColor
- };
+ 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;
+
+ state.order.payment.isInsurance = orderInformation.IsInsuranceOrder;
+ state.order.payment.insuranceCoverage.isVerified = orderInformation?.insuranceInfo.coverageVerified;
}
}
@@ -210,11 +244,10 @@ export const getters = {
eventBus: (state) => state.applicationUser.eventBus,
damage: (state) => state.order.damage,
lineItems: (state) => state.order.lineItems,
- pageData: (state) => (page) => {
- return state.applicationUser.pageData[page];
- },
+ pageData: (state) => (page) => { return state.applicationUser.pageData[page]; },
applicationUser: (state) => state.applicationUser,
order: (state) => state.order,
+ payment: (state) => state.order.payment,
}
// Export Actions
@@ -248,7 +281,7 @@ export const actions = {
method: endpoints.LookupVinByPlate.method,
endpoint: endpoints.LookupVinByPlate.url,
payload: {
- licensePlate: licensePlate,
+ licensePlate: licensePlate,
licenseState: licenseState
},
});
@@ -304,12 +337,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}`
@@ -375,6 +408,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({
@@ -414,16 +460,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/store/store.spec.js b/src/store/store.spec.js
index 08db50bd0..7618cc25a 100644
--- a/src/store/store.spec.js
+++ b/src/store/store.spec.js
@@ -222,6 +222,7 @@ describe("Mutations", () => {
numberOfChips: 0,
parts: [],
parentAccountNumber: "123456789",
+ insuranceInfo: {}
});
// Assert
@@ -232,6 +233,17 @@ describe("Mutations", () => {
expect(storeState.order.vehicle.model).toEqual("ILX");
});
+ it("updateInsuranceVerifiedStatus, should set isVerified flag", () => {
+ // Arrange
+ const storeState = state;
+
+ // Act
+ mutations.updateInsuranceVerifiedStatus(storeState, true);
+
+ // Assert
+ expect(storeState.order.payment.insuranceCoverage.isVerified).toEqual(true);
+ });
+
});
describe("Actions", () => {
@@ -690,4 +702,15 @@ describe("Getters", () => {
});
+ it("Payment getter, should return payment data", () => {
+ // Arrange
+ const storeState = state;
+
+ //Act
+ mutations.updateInsuranceVerifiedStatus(storeState, true );
+
+ //Assert
+ expect(getters.payment(storeState).insuranceCoverage.isVerified).toEqual(true);
+ });
+
});
\ No newline at end of file
diff --git a/src/styles/common-error-styles.scss b/src/styles/common-error-styles.scss
index fe1637e43..165101177 100644
--- a/src/styles/common-error-styles.scss
+++ b/src/styles/common-error-styles.scss
@@ -1,92 +1,126 @@
-.has-error {
- &.list-button,
- &.list-card {
- border: 1px solid $red;
- color: $red;
- input[type=checkbox]:focus + label,
- 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 {
+html {
+ .has-error {
+ &.list-button,
+ &.list-card {
border: 1px solid $red;
+ color: $red;
+ input[type=checkbox]:focus + label,
+ input[type=radio]:focus + label {
+ box-shadow: 0 0 0 2.5px $red;
+ }
+ input[type=checkbox]:checked + label {
+ box-shadow: 0 0 0 1px $red;
+ }
&:hover {
box-shadow: 0px 0px 0px 4px $red-200;
+ border-radius: 10px;
}
}
- input[type=checkbox]:focus + label,
- input[type=radio]:focus + label {
- box-shadow: 0 0 1px $red !important;
- }
- }
- &.ui-radio,
- &.ui-checkbox {
- input[type=checkbox],
- input[type=radio],
- input[type=radio]+label:before,
- input[type=checkbox]+label:before {
- border: 1px solid $red;
- }
- input[type=checkbox]:checked + label:before {
- border: 1px solid $blue;
- }
- }
- &.textbox-question,
- &.dropdown-question {
- p {
+ &.list-button-horizontal {
color: $red;
- }
- input,
- select {
- border: 1px solid $red;
- &:focus {
- border: 1px solid transparent;
+ label {
+ border: 1px solid $red;
+ &:hover {
+ box-shadow: 0px 0px 0px 4px $red-200;
+ }
+ }
+ input[type=checkbox]:focus + label,
+ input[type=radio]:focus + label {
+ box-shadow: 0 0 1px $red;
}
}
- select {
- background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 8.89' xml:space='preserve'%3e%3cpath d='M8 8.89c-.24 0-.46-.09-.63-.26L.26 1.53a.901.901 0 0 1 0-1.27C.43.1.66 0 .9 0s.47.1.64.26L8 6.74 14.47.27c.17-.17.4-.27.64-.27s.47.1.63.27c.17.17.26.4.26.64s-.1.47-.27.63l-7.1 7.09a.86.86 0 0 1-.63.26z' fill='%23d4281c'/%3e%3c/svg%3e");
- background-repeat: no-repeat;
- background-position: right 0.75rem center;
- background-size: 16px 12px;
+ &.ui-radio,
+ &.ui-checkbox {
+ input[type=checkbox],
+ input[type=radio],
+ input[type=radio]+label:before,
+ input[type=checkbox]+label:before {
+ border: 1px solid $red;
+ }
+ input[type=checkbox]:checked + label:before {
+ border: 1px solid $blue;
+ }
+ }
+ &.textbox-question,
+ &.dropdown-question {
+ p {
+ color: $red;
+ }
+ input,
+ select {
+ border: 1px solid $red;
+ &:focus {
+ border: 1px solid transparent;
+ }
+ }
+ select {
+ background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 8.89' xml:space='preserve'%3e%3cpath d='M8 8.89c-.24 0-.46-.09-.63-.26L.26 1.53a.901.901 0 0 1 0-1.27C.43.1.66 0 .9 0s.47.1.64.26L8 6.74 14.47.27c.17-.17.4-.27.64-.27s.47.1.63.27c.17.17.26.4.26.64s-.1.47-.27.63l-7.1 7.09a.86.86 0 0 1-.63.26z' fill='%23d4281c'/%3e%3c/svg%3e");
+ background-repeat: no-repeat;
+ background-position: right 0.75rem center;
+ background-size: 16px 12px;
+ }
+ }
+ }
+ //Restore to default style if alert box is present
+ .alertError {
+ .has-error {
+ &.list-button,
+ &.list-card {
+ border: 1px solid $gray-500;
+ input:not(:focus) {
+ + label {
+ box-shadow: 0 0 0 1px $gray-500;
+ border-radius: .5rem;
+ }
+ }
+ input:checked:focus {
+ + label {
+ box-shadow: 0 0 0 1px $blue;
+ border-radius: .5rem;
+ }
+ }
+ input:checked:not(:focus) {
+ + label {
+ box-shadow: 0 0 0 1px $blue;
+ border-radius: .5rem;
+ }
+ }
+ input:focus {
+ border: 1px solid $gray-500;
+ + label {
+ box-shadow: 0 0 0 2.5px transparent;
+ }
+ }
+ &:hover {
+ box-shadow: 0 0 0 4px $blue-300;
+ + label {
+ box-shadow: 0 0 0 2.5px transparent;
+ border: 1px solid $blue;
+ }
+ }
+ }
+ }
+ }
+
+ .form-test-error {
+ color: $red;
+ font-size: .875rem;
+ font-weight: 500;
+ }
+
+ .form-test-invalid {
+ &.btn.btn-primary {
+ color: $gray;
+ background: $gray-200;
+ cursor: pointer;
+ pointer-events: all;
+ }
+ &.btn.btn-primary:hover,
+ &.btn.btn-primary:focus,
+ &.btn.btn-primary:focus-visible {
+ color: $gray !important;
+ background: $gray-200;
+ box-shadow: none;
}
}
}
-
-.form-test-error {
- color: $red;
- font-size: .875rem;
- font-weight: 500;
-}
-
-.form-test-invalid {
- &.btn.btn-primary {
- color: $gray;
- background: $gray-200;
- cursor: pointer;
- pointer-events: all;
- }
- &.btn.btn-primary:hover,
- &.btn.btn-primary:focus,
- &.btn.btn-primary:focus-visible {
- color: $gray !important;
- background: $gray-200 !important;
- box-shadow: none !important;
- }
-
-}
diff --git a/src/ux-components/alert/alert.spec.js b/src/ux-components/alert/alert.spec.js
index 8072f8d73..17ed6988a 100644
--- a/src/ux-components/alert/alert.spec.js
+++ b/src/ux-components/alert/alert.spec.js
@@ -1,4 +1,4 @@
-import { shallowMount } from "@vue/test-utils";
+import { shallowMount } from "@vue/test-utils";
import alert from "./alert";
describe("alert.vue", () => {
@@ -9,6 +9,13 @@ describe("alert.vue", () => {
propsData: {
isDismissible: true
},
+ computed: {
+ splitAlertCopyForLink: {
+ get() {
+ return "TEST";
+ },
+ }
+ },
mixins: [mockMixin]
});
@@ -24,6 +31,13 @@ describe("alert.vue", () => {
propsData: {
alertClass: 'warning'
},
+ computed: {
+ splitAlertCopyForLink: {
+ get() {
+ return "TEST";
+ },
+ }
+ },
mixins: [mockMixin]
});
diff --git a/src/ux-components/alert/alert.vue b/src/ux-components/alert/alert.vue
index 515658e16..7cd719ba7 100644
--- a/src/ux-components/alert/alert.vue
+++ b/src/ux-components/alert/alert.vue
@@ -5,7 +5,15 @@
:class="[isDismissible ? 'alert-dismissible' : '', this.alertClass]"
>
{{ alertHeadline }}
- {{ alertCopy }}
+
+
+
+ {{ copy.split(':')[1].split(',')[1] }}
+
+
+
+
+