Merge branch 'develop' into feature/CSR-92
This commit is contained in:
commit
8930cc8681
53 changed files with 1628 additions and 755 deletions
|
|
@ -78,4 +78,5 @@ stages:
|
||||||
appDeployVariables:
|
appDeployVariables:
|
||||||
__VUE_APP_CONSUMER_API_GATEWAY__: $(__VUE_APP_CONSUMER_API_GATEWAY__)
|
__VUE_APP_CONSUMER_API_GATEWAY__: $(__VUE_APP_CONSUMER_API_GATEWAY__)
|
||||||
__VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__)
|
__VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__)
|
||||||
|
__VUE_APP_HERITAGE_FUNNEL__: $(__VUE_APP_HERITAGE_FUNNEL__)
|
||||||
cfDistributionId: $(cfDistributionId)
|
cfDistributionId: $(cfDistributionId)
|
||||||
6
src/.eslintrc.js
Normal file
6
src/.eslintrc.js
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
jest: true
|
||||||
|
},
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
|
@ -68,4 +68,7 @@ export default {
|
||||||
.logo-image {
|
.logo-image {
|
||||||
max-width: 78px;
|
max-width: 78px;
|
||||||
}
|
}
|
||||||
|
.alert {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import buttonBack from "@/common-components/button-back/button-back";
|
import buttonBack from "@/common-components/funnel-sub-header/button-back/button-back";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "FunnelSubHeader",
|
name: "FunnelSubHeader",
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ function setupMocks({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Mock store
|
//Mock store
|
||||||
store.dispatch = jest.fn(() => dataFromStoreApi);
|
store.dispatch = jest.fn(() => {});
|
||||||
store.getters = { vehicle: { category: categoryValue, imageUrl: imageUrlValue } };
|
store.getters = { vehicle: { category: categoryValue, imageUrl: imageUrlValue } };
|
||||||
const mountOptions = getMountOptions({
|
const mountOptions = getMountOptions({
|
||||||
store: {
|
store: {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
|
|
||||||
const applicationConfig = {
|
const applicationConfig = {
|
||||||
CONSUMER_APIGATEWAY_URL: process.env.VUE_APP_CONSUMER_API_GATEWAY,
|
CONSUMER_APIGATEWAY_URL: process.env.VUE_APP_CONSUMER_API_GATEWAY,
|
||||||
GOOGLE_PLACES_API_KEY: process.env.VUE_APP_GOOGLE_PLACES_API_KEY,
|
GOOGLE_PLACES_API_KEY: process.env.VUE_APP_GOOGLE_PLACES_API_KEY,
|
||||||
|
ANALYTICS_SESSION_TIMEOUT_MINUTES: 30,
|
||||||
|
SAVED_SESSION_TIMEOUT_DAYS: 45,
|
||||||
|
COOKIE_PATH: "/"
|
||||||
};
|
};
|
||||||
|
|
||||||
export { applicationConfig };
|
export { applicationConfig };
|
||||||
6
src/constants/cookie-names.js
Normal file
6
src/constants/cookie-names.js
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
const cookieNames = {
|
||||||
|
FUNNEL_SESSION_INFO: "FunnelSessionInfo",
|
||||||
|
};
|
||||||
|
|
||||||
|
export { cookieNames };
|
||||||
|
|
||||||
|
|
@ -51,6 +51,14 @@ const endpoints = {
|
||||||
url: "/parts/api/v1/parts/parts-or-questions",
|
url: "/parts/api/v1/parts/parts-or-questions",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
},
|
},
|
||||||
|
SaveOrder: {
|
||||||
|
url: "/order/api/v1/order/save",
|
||||||
|
method: "POST",
|
||||||
|
},
|
||||||
|
LoadOrder: {
|
||||||
|
url: "/order/api/v1/order/load",
|
||||||
|
method: "POST",
|
||||||
|
}
|
||||||
ValidateZip: {
|
ValidateZip: {
|
||||||
url: "/location/api/v1/location/zip",
|
url: "/location/api/v1/location/zip",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
|
|
||||||
7
src/constants/query-strings.js
Normal file
7
src/constants/query-strings.js
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
const queryStrings = {
|
||||||
|
FMG_PAGE: 'fmgPage',
|
||||||
|
START_TYPE: 'start_type'
|
||||||
|
};
|
||||||
|
|
||||||
|
export { queryStrings };
|
||||||
|
|
||||||
|
|
@ -13,6 +13,9 @@ const storeActions = {
|
||||||
LOOKUP_VEHICLE_BY_VIN: "lookupVehicleByVin",
|
LOOKUP_VEHICLE_BY_VIN: "lookupVehicleByVin",
|
||||||
LOOKUP_VIN_BY_PLATE: "lookupVinByPlate",
|
LOOKUP_VIN_BY_PLATE: "lookupVinByPlate",
|
||||||
GET_PARTS_OR_QUESTIONS: "getPartsOrQuestions",
|
GET_PARTS_OR_QUESTIONS: "getPartsOrQuestions",
|
||||||
|
SAVE_ORDER: "saveOrder",
|
||||||
|
LOAD_ORDER: "loadOrder",
|
||||||
|
SET_REFERRAL_INFORMATION: "setReferralInformation",
|
||||||
VALIDATE_ZIP: "validateZip",
|
VALIDATE_ZIP: "validateZip",
|
||||||
|
|
||||||
// DEPENDENCY MUTATIONS
|
// DEPENDENCY MUTATIONS
|
||||||
|
|
@ -20,6 +23,7 @@ const storeActions = {
|
||||||
RESET_DAMAGE_STATE_AND_DEPENDENCIES: "resetDamageAndDependencies",
|
RESET_DAMAGE_STATE_AND_DEPENDENCIES: "resetDamageAndDependencies",
|
||||||
RESET_REGISTRATION_STATE_AND_DEPENDENCIES: "resetRegistrationAndDependencies",
|
RESET_REGISTRATION_STATE_AND_DEPENDENCIES: "resetRegistrationAndDependencies",
|
||||||
RESET_PARTS_STATE_AND_DEPENDENCIES: "resetPartsAndDependencies",
|
RESET_PARTS_STATE_AND_DEPENDENCIES: "resetPartsAndDependencies",
|
||||||
|
RESET_STATE: "resetState",
|
||||||
};
|
};
|
||||||
|
|
||||||
export { storeActions };
|
export { storeActions };
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,12 @@ const storeMutations = {
|
||||||
UPDATE_SERVICE_LOCATION_ZIP: "updateServiceLocationZip",
|
UPDATE_SERVICE_LOCATION_ZIP: "updateServiceLocationZip",
|
||||||
UPDATE_CUSTOMER_EMAIL_ADDRESS: "updateCustomerEmailAddress",
|
UPDATE_CUSTOMER_EMAIL_ADDRESS: "updateCustomerEmailAddress",
|
||||||
|
|
||||||
|
// ORDER MUTATIONS
|
||||||
|
UPDATE_REFERRAL_NUMBER: "updateReferralNumber",
|
||||||
|
UPDATE_REFERRAL_DATE: "updateReferralDate",
|
||||||
|
UPDATE_REFERRAL_CORRELATION_ID: "updateReferralCorrelationId",
|
||||||
|
UPDATE_PARENT_ACCT_NUMBER: "updateParentAcctNumber",
|
||||||
|
|
||||||
// EVENT BUS MUTATIONS
|
// EVENT BUS MUTATIONS
|
||||||
ADD_EVENT_TO_BUS: "addEventToBus",
|
ADD_EVENT_TO_BUS: "addEventToBus",
|
||||||
REMOVE_EVENT_FROM_BUS: "removeEventFromBus",
|
REMOVE_EVENT_FROM_BUS: "removeEventFromBus",
|
||||||
|
|
@ -34,9 +40,11 @@ const storeMutations = {
|
||||||
RESET_DAMAGE_STATE: "resetDamageState",
|
RESET_DAMAGE_STATE: "resetDamageState",
|
||||||
RESET_REGISTRATION_STATE: "resetRegistrationState",
|
RESET_REGISTRATION_STATE: "resetRegistrationState",
|
||||||
RESET_PARTS_STATE: "resetPartsState",
|
RESET_PARTS_STATE: "resetPartsState",
|
||||||
|
RESET_STATE: "resetState",
|
||||||
|
|
||||||
// OTHER MUTATIONS
|
// OTHER MUTATIONS
|
||||||
UPDATE_PAGE_DATA: "updatePageData",
|
UPDATE_PAGE_DATA: "updatePageData",
|
||||||
|
SET_LOAD_FUNNEL_SESSION_INFO: "updateStateWithOrderInformation"
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
const widgetNames = {
|
|
||||||
FUNNEL_SUB_HEADER_WIDGET: "FunnelSubHeaderWidget",
|
|
||||||
RADIO_QUESTION_WIDGET: "RadioQuestionWidget",
|
|
||||||
VEHICLE_BANNER_WIDGET: "VehicleBannerWidget",
|
|
||||||
FUNNEL_HEADER_WIDGET: "FunnelHeaderWidget",
|
|
||||||
};
|
|
||||||
|
|
||||||
export { widgetNames };
|
|
||||||
|
|
@ -6,6 +6,7 @@ export default {
|
||||||
callHttpClient({ method, endpoint, payload }) {
|
callHttpClient({ method, endpoint, payload }) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const apiGatewayUrl = applicationConfig.CONSUMER_APIGATEWAY_URL;
|
const apiGatewayUrl = applicationConfig.CONSUMER_APIGATEWAY_URL;
|
||||||
|
|
||||||
const payloadAndAnalyticsData = Object.assign({}, payload, {
|
const payloadAndAnalyticsData = Object.assign({}, payload, {
|
||||||
AppName: "FixMyGlass",
|
AppName: "FixMyGlass",
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { storeActions } from "@/constants/store-actions.js";
|
import { storeActions } from "@/constants/store-actions.js";
|
||||||
import { widgetNames } from "@/constants/widget-names.js";
|
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
|
||||||
export function fetchCmsContentForPage(fmgPage) {
|
export function fetchCmsContentForPage(fmgPage) {
|
||||||
|
|
|
||||||
84
src/helpers/heritage-integration/cookie-helper.js
Normal file
84
src/helpers/heritage-integration/cookie-helper.js
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
import { cookieNames } from "@/constants/cookie-names";
|
||||||
|
import store from "@/store";
|
||||||
|
import { applicationConfig } from "@/constants/application-config";
|
||||||
|
|
||||||
|
/*
|
||||||
|
Will update the cookie if present, or create a new one if not.
|
||||||
|
*/
|
||||||
|
export function updateOrCreateFunnelCookie() {
|
||||||
|
// Create the cookie
|
||||||
|
document.cookie = `${cookieNames.FUNNEL_SESSION_INFO}={}; path=${applicationConfig.COOKIE_PATH}; ${getCookieDomainValue()};`;
|
||||||
|
|
||||||
|
// Set up cookie with all the props.
|
||||||
|
setFunnelCookieProperties({
|
||||||
|
LastTouched: new Date().toUTCString(),
|
||||||
|
SavedQuoteTimeoutDate: store.getters.applicationUser.savedSessionTimeout,
|
||||||
|
DidHeritageFunnelUpdateLast: false,
|
||||||
|
ShouldResetState: false,
|
||||||
|
ReferralNumber: store.getters.order.referralNumber,
|
||||||
|
ReferralDate: store.getters.order.referralDate,
|
||||||
|
ReferralCorrelationId: store.getters.order.referralCorrelationId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Removes cookie from browser.
|
||||||
|
*/
|
||||||
|
export function deleteFunnelCookie() {
|
||||||
|
document.cookie = `${cookieNames.FUNNEL_SESSION_INFO}=; Max-Age=0; path=${applicationConfig.COOKIE_PATH}; ${getCookieDomainValue()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Used to set properties on the funnel cookie.
|
||||||
|
Takes an object with properties to set. Will overwrite existing properties.
|
||||||
|
*/
|
||||||
|
function setFunnelCookieProperties(properties) {
|
||||||
|
if (typeof properties == "object") {
|
||||||
|
let cookie = getFunnelCookie();
|
||||||
|
|
||||||
|
if (cookie !== null) {
|
||||||
|
Object.keys(properties).forEach(key => {
|
||||||
|
cookie[key] = properties[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
const cookieValueJson = JSON.stringify(cookie);
|
||||||
|
|
||||||
|
document.cookie = `${cookieNames.FUNNEL_SESSION_INFO}=${cookieValueJson}; path=${applicationConfig.COOKIE_PATH}; ${getCookieDomainValue()}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCookieDomainValue() {
|
||||||
|
return location.hostname.includes("localhost") ? "" : `domain=${getDomainWithoutSubdomain()};`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDomainWithoutSubdomain() {
|
||||||
|
let url = location.hostname;
|
||||||
|
if (url.includes("localhost")) {
|
||||||
|
return "localhost";
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlParts = url.split('.');
|
||||||
|
|
||||||
|
return `.${urlParts
|
||||||
|
.slice(0)
|
||||||
|
.slice(-(urlParts.length === 4 ? 3 : 2))
|
||||||
|
.join('.')}`;
|
||||||
|
}
|
||||||
104
src/helpers/heritage-integration/cookie-helper.spec.js
Normal file
104
src/helpers/heritage-integration/cookie-helper.spec.js
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
import {getFunnelCookie} from "@/helpers/heritage-integration/cookie-helper.js";
|
||||||
|
import { removeAllTestCookies, setupCookies } from "@/helpers/unit-test-helper";
|
||||||
|
|
||||||
|
describe("cookies", () => {
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
removeAllTestCookies();
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("getFunnelCookie method", () => {
|
||||||
|
test("gets correct value when cookie is present", () => {
|
||||||
|
// Arrange
|
||||||
|
const testReferralNumber = 1566818;
|
||||||
|
const testReferralDate = "2022-03-15T10:56:24.597";
|
||||||
|
const testReferralCorrelationId = "404d2b04-f86e-45c3-b373-127b6217b060";
|
||||||
|
const testShouldResetState = false;
|
||||||
|
const testDidHeritageFunnelUpdateLast = true;
|
||||||
|
|
||||||
|
const testCookieValue = {
|
||||||
|
ReferralNumber: testReferralNumber,
|
||||||
|
ReferralDate: testReferralDate,
|
||||||
|
ReferralCorrelationId: testReferralCorrelationId,
|
||||||
|
ShouldResetState: testShouldResetState,
|
||||||
|
DidHeritageFunnelUpdateLast: testDidHeritageFunnelUpdateLast
|
||||||
|
}
|
||||||
|
|
||||||
|
setupCookies({ funnelCookieValue: JSON.stringify(testCookieValue) });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = getFunnelCookie();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result).toEqual(testCookieValue);
|
||||||
|
expect(typeof result).toEqual("object");
|
||||||
|
expect(result.ReferralNumber).toEqual(testReferralNumber);
|
||||||
|
expect(result.ReferralDate).toEqual(testReferralDate);
|
||||||
|
expect(result.ReferralCorrelationId).toEqual(testReferralCorrelationId);
|
||||||
|
expect(result.ShouldResetState).toEqual(testShouldResetState);
|
||||||
|
expect(result.DidHeritageFunnelUpdateLast).toEqual(testDidHeritageFunnelUpdateLast);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns empty object when value is empty object", () => {
|
||||||
|
// Arrange
|
||||||
|
const testCookieValue = {};
|
||||||
|
setupCookies({ funnelCookieValue: JSON.stringify(testCookieValue) });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = getFunnelCookie();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result).toEqual(testCookieValue);
|
||||||
|
expect(typeof result).toEqual("object");
|
||||||
|
expect(Object.keys(result)).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns null when value is empty string", () => {
|
||||||
|
// Arrange
|
||||||
|
const testCookieValue = "";
|
||||||
|
setupCookies({ funnelCookieValue: testCookieValue });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = getFunnelCookie();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result).toEqual(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns null when funnel cookie doesn't exist", () => {
|
||||||
|
// Arrange
|
||||||
|
setupCookies({ includeHeritageCookie: false });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = getFunnelCookie();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result).toEqual(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("gets correct cookie value", () => {
|
||||||
|
// Arrange
|
||||||
|
const testCookieValue = { test: "testValue" };
|
||||||
|
|
||||||
|
setupCookies({ funnelCookieValue: JSON.stringify(testCookieValue) });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const actualCookieValue = getFunnelCookie();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(actualCookieValue).toEqual(testCookieValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getCookieValue: Gets null cookie value", () => {
|
||||||
|
// Arrange
|
||||||
|
setupCookies({ includeHeritageCookie: false });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const actualCookieValue = getFunnelCookie();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(actualCookieValue).toBeNull();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
74
src/helpers/heritage-integration/navigation-helper.js
Normal file
74
src/helpers/heritage-integration/navigation-helper.js
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
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 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.
|
||||||
|
*/
|
||||||
|
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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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 'heritage';
|
||||||
|
//return "vin-lookup"; (uncomment)
|
||||||
|
} else {
|
||||||
|
return 'heritage';
|
||||||
|
//return "estimate" (uncomment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Used to navigate to the heritage funnel with the correct query string and url.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export async function navigateToHeritageFunnel() {
|
||||||
|
// Create the order (or save existing order) when navigating to Heritage Funnel.
|
||||||
|
await saveOrder();
|
||||||
|
|
||||||
|
router.navigateToExternalUrl(
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
372
src/helpers/heritage-integration/navigation-helper.spec.js
Normal file
372
src/helpers/heritage-integration/navigation-helper.spec.js
Normal file
|
|
@ -0,0 +1,372 @@
|
||||||
|
import { getPageToRouteExistingOrderTo, navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||||
|
import * as orderHelper from "@/helpers/heritage-integration/order-helper";
|
||||||
|
import { lazyLoadComponent } from "@/router/dynamic-routing/component-loader.js";
|
||||||
|
import { storeActions } from "@/constants/store-actions";
|
||||||
|
import { setupMocksForJsFiles, getMockOrderInfo } from "@/helpers/unit-test-helper.js";
|
||||||
|
import { externalUrls } from "@/router/router-constants/externalUrl-values";
|
||||||
|
import { queryStrings } from "@/constants/query-strings";
|
||||||
|
|
||||||
|
import store from "@/store";
|
||||||
|
import router from "@/router";
|
||||||
|
|
||||||
|
// Mock Lazy Load
|
||||||
|
jest.mock("@/router/dynamic-routing/component-loader.js", () => ({
|
||||||
|
lazyLoadComponent: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("getPageToRouteExistingOrderTo", () => {
|
||||||
|
|
||||||
|
test("getPageToRouteExistingOrderTo, should return vehicle-year", async () => {
|
||||||
|
// Arrange
|
||||||
|
const toRoute = {
|
||||||
|
query: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mock out the lazy load calls for all components.
|
||||||
|
lazyLoadComponent
|
||||||
|
.mockReturnValueOnce(() => {
|
||||||
|
return {
|
||||||
|
default: {
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.mockReturnValueOnce(() => {
|
||||||
|
return {
|
||||||
|
default: {
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.mockReturnValueOnce(() => {
|
||||||
|
return {
|
||||||
|
default: {
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.mockReturnValueOnce(() => {
|
||||||
|
return {
|
||||||
|
default: {
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(result).toBe('vehicle-year');
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getPageToRouteExistingOrderTo, should return vehicle-model", async () => {
|
||||||
|
// Arrange
|
||||||
|
const toRoute = {
|
||||||
|
query: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mock out the lazy load calls for all components.
|
||||||
|
lazyLoadComponent
|
||||||
|
.mockReturnValueOnce(() => {
|
||||||
|
return {
|
||||||
|
default: {
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.mockReturnValueOnce(() => {
|
||||||
|
return {
|
||||||
|
default: {
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.mockReturnValueOnce(() => {
|
||||||
|
return {
|
||||||
|
default: {
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.mockReturnValueOnce(() => {
|
||||||
|
return {
|
||||||
|
default: {
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(result).toBe('vehicle-model');
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getPageToRouteExistingOrderTo, should return vehicle-damage", async () => {
|
||||||
|
// Arrange
|
||||||
|
const toRoute = {
|
||||||
|
query: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mock out the lazy load calls for all components.
|
||||||
|
lazyLoadComponent
|
||||||
|
.mockReturnValueOnce(() => {
|
||||||
|
return {
|
||||||
|
default: {
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.mockReturnValueOnce(() => {
|
||||||
|
return {
|
||||||
|
default: {
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.mockReturnValueOnce(() => {
|
||||||
|
return {
|
||||||
|
default: {
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.mockReturnValueOnce(() => {
|
||||||
|
return {
|
||||||
|
default: {
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
store.getters.damage.isRepair = undefined;
|
||||||
|
store.getters.vehicle.carId = 'C00000';
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(result).toBe('vehicle-damage');
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getPageToRouteExistingOrderTo, should return vin-lookup", async () => {
|
||||||
|
// Arrange
|
||||||
|
const toRoute = {
|
||||||
|
query: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mock out the lazy load calls for all components.
|
||||||
|
lazyLoadComponent
|
||||||
|
.mockReturnValueOnce(() => {
|
||||||
|
return {
|
||||||
|
default: {
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.mockReturnValueOnce(() => {
|
||||||
|
return {
|
||||||
|
default: {
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.mockReturnValueOnce(() => {
|
||||||
|
return {
|
||||||
|
default: {
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.mockReturnValueOnce(() => {
|
||||||
|
return {
|
||||||
|
default: {
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
store.getters.damage.isRepair = true;
|
||||||
|
store.getters.vehicle.carId = 'C00000';
|
||||||
|
store.getters.vehicle.vin = "1FADP3F26DL212886"
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
//expect(result).toBe('vin-lookup');
|
||||||
|
expect(result).toBe('heritage');
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getPageToRouteExistingOrderTo, should return estimate", async () => {
|
||||||
|
// Arrange
|
||||||
|
const toRoute = {
|
||||||
|
query: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mock out the lazy load calls for all components.
|
||||||
|
lazyLoadComponent
|
||||||
|
.mockReturnValueOnce(() => {
|
||||||
|
return {
|
||||||
|
default: {
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.mockReturnValueOnce(() => {
|
||||||
|
return {
|
||||||
|
default: {
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.mockReturnValueOnce(() => {
|
||||||
|
return {
|
||||||
|
default: {
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.mockReturnValueOnce(() => {
|
||||||
|
return {
|
||||||
|
default: {
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
store.getters.damage.isRepair = true;
|
||||||
|
store.getters.vehicle.carId = 'C00000';
|
||||||
|
store.getters.vehicle.vin = null;
|
||||||
|
// Act
|
||||||
|
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
//expect(result).toBe('estimate');
|
||||||
|
expect(result).toBe('heritage');
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getPageToRouteExistingOrderTo, existing order, should return heritage", async () => {
|
||||||
|
// Arrange
|
||||||
|
const toRoute = {
|
||||||
|
query: {
|
||||||
|
[queryStrings.START_TYPE]: 'fmg'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = await getPageToRouteExistingOrderTo(toRoute, true);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result).toBe("heritage");
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("navigateToHeritageFunnel", () => {
|
||||||
|
test("should save order", async () => {
|
||||||
|
// Arrange
|
||||||
|
const mockReferralNumber = 2;
|
||||||
|
const mockCorrelationId = "55";
|
||||||
|
const mockReferralDate = "2022";
|
||||||
|
|
||||||
|
const mockOrderInfo = getMockOrderInfo(mockReferralNumber, mockCorrelationId, mockReferralDate);
|
||||||
|
|
||||||
|
const mockData = {
|
||||||
|
actionList: [{
|
||||||
|
actionName: storeActions.SAVE_ORDER,
|
||||||
|
data: mockOrderInfo,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
setupMocksForJsFiles(mockData);
|
||||||
|
const saveOrderFunction = jest.spyOn(orderHelper, "saveOrder");
|
||||||
|
router.navigateToExternalUrl = jest.fn();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await navigateToHeritageFunnel();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(saveOrderFunction).toHaveBeenCalled();
|
||||||
|
|
||||||
|
// Should alway save before we navigate to heritage
|
||||||
|
const saveOrderFunctionCallOrder = saveOrderFunction.mock.invocationCallOrder[0];
|
||||||
|
const routerNavigateFunctionCallOrder = router.navigateToExternalUrl.mock.invocationCallOrder[0];
|
||||||
|
expect(saveOrderFunctionCallOrder).toBeLessThan(routerNavigateFunctionCallOrder);
|
||||||
|
saveOrderFunction.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should go to heritage funnel", async () => {
|
||||||
|
// Arrange
|
||||||
|
const mockReferralNumber = 2;
|
||||||
|
const mockCorrelationId = "55";
|
||||||
|
const mockReferralDate = "2022";
|
||||||
|
|
||||||
|
const mockOrderInfo = getMockOrderInfo(mockReferralNumber, mockCorrelationId, mockReferralDate);
|
||||||
|
|
||||||
|
const mockData = {
|
||||||
|
actionList: [{
|
||||||
|
actionName: storeActions.SAVE_ORDER,
|
||||||
|
data: mockOrderInfo,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
setupMocksForJsFiles(mockData);
|
||||||
|
|
||||||
|
store.getters.order.referralCorrelationId = mockCorrelationId
|
||||||
|
|
||||||
|
router.navigateToExternalUrl = jest.fn();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await navigateToHeritageFunnel();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(router.navigateToExternalUrl).toHaveBeenCalled();
|
||||||
|
expect(router.navigateToExternalUrl).toHaveBeenCalledWith(externalUrls.HERITAGE_FUNNEL,
|
||||||
|
expect.objectContaining({
|
||||||
|
corid: mockCorrelationId
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
66
src/helpers/heritage-integration/order-helper.js
Normal file
66
src/helpers/heritage-integration/order-helper.js
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { storeActions } from "@/constants/store-actions.js";
|
||||||
|
import { getFunnelCookie, updateOrCreateFunnelCookie, deleteFunnelCookie } from "@/helpers/heritage-integration/cookie-helper.js";
|
||||||
|
import baseMixin from "@/mixins/base-mixin";
|
||||||
|
|
||||||
|
/*
|
||||||
|
Will call API and hydrate state with data from API if present. If there is no order present
|
||||||
|
method will return null. If the cookie dictates the state should be reset
|
||||||
|
it will reset the state and go back to the start of the funnel.
|
||||||
|
|
||||||
|
*/
|
||||||
|
export async function loadOrderIfPresent() {
|
||||||
|
const funnelCookie = getFunnelCookie();
|
||||||
|
|
||||||
|
// Do nothing if there is no cookie or no correlation id.
|
||||||
|
if (funnelCookie == null || funnelCookie.ReferralCorrelationId == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset state if cookie says to.
|
||||||
|
if (funnelCookie.ShouldResetState) {
|
||||||
|
baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.RESET_STATE);
|
||||||
|
deleteFunnelCookie();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Will call API to save existing order, or create new one depending where it's called from.
|
||||||
|
This will also set Referral information in the store after saving, and then
|
||||||
|
update the cookie.
|
||||||
|
*/
|
||||||
|
export async function saveOrder() {
|
||||||
|
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,
|
||||||
|
referralCorrelationId: savedOrderInfo.data.referralCorrelationId,
|
||||||
|
referralDate: savedOrderInfo.data.referralDate,
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
// Update the cookie with the referral information when saved.
|
||||||
|
updateOrCreateFunnelCookie();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// PRIVATE FUNCTIONS //
|
||||||
|
|
||||||
|
/*
|
||||||
|
Calls API to load order given the referral number, referralDate, and referralCorrelationId
|
||||||
|
and returns the response.
|
||||||
|
*/
|
||||||
|
async function loadOrder(referralNumber, referralDate, referralCorrelationId) {
|
||||||
|
const response = await baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.LOAD_ORDER,
|
||||||
|
{
|
||||||
|
referralNumber: referralNumber.toString(),
|
||||||
|
referralDate: referralDate,
|
||||||
|
referralCorrelationId: referralCorrelationId
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
172
src/helpers/heritage-integration/order-helper.spec.js
Normal file
172
src/helpers/heritage-integration/order-helper.spec.js
Normal file
|
|
@ -0,0 +1,172 @@
|
||||||
|
import * as cookieHelper from "@/helpers/heritage-integration/cookie-helper";
|
||||||
|
import { loadOrderIfPresent, saveOrder } from "@/helpers/heritage-integration/order-helper";
|
||||||
|
import { cookieNames } from "@/constants/cookie-names";
|
||||||
|
import { setupMocksForJsFiles, removeAllTestCookies, getMockOrderInfo, setupCookies } from "@/helpers/unit-test-helper.js";
|
||||||
|
import { storeActions } from "@/constants/store-actions";
|
||||||
|
import router from "@/router";
|
||||||
|
|
||||||
|
describe("loadOrderIfPresent", () => {
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
removeAllTestCookies();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("ShouldResetState == true => funnel cookie is deleted", () => {
|
||||||
|
// Arrange
|
||||||
|
cookieHelper.deleteFunnelCookie = jest.spyOn(cookieHelper, "deleteFunnelCookie");
|
||||||
|
const testShouldResetState = true;
|
||||||
|
|
||||||
|
const testCookieValue = {
|
||||||
|
ShouldResetState: testShouldResetState,
|
||||||
|
ReferralCorrelationId: "xxx"
|
||||||
|
}
|
||||||
|
|
||||||
|
document.cookie = `${cookieNames.FUNNEL_SESSION_INFO}=${JSON.stringify(testCookieValue)}; path=/; ${cookieHelper.getCookieDomainValue()}`;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
loadOrderIfPresent();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(cookieHelper.deleteFunnelCookie).toHaveBeenCalled();
|
||||||
|
expect(document.cookie).toBe("");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("ShouldResetState == true => reset store", () => {
|
||||||
|
// Arrange
|
||||||
|
cookieHelper.getFunnelCookie = jest.spyOn(cookieHelper, "getFunnelCookie").mockReturnValueOnce({ ShouldResetState: true, ReferralCorrelationId: "xxx-xxx-xxx" });
|
||||||
|
|
||||||
|
const mockData = {
|
||||||
|
actionList: [{
|
||||||
|
actionName: storeActions.RESET_STATE
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
|
||||||
|
var mocks = setupMocksForJsFiles(mockData);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
loadOrderIfPresent();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(cookieHelper.getFunnelCookie).toHaveBeenCalled();
|
||||||
|
expect(mocks.baseMixin.methods.dispatchNonBlockingStoreAction).toHaveBeenCalledWith(storeActions.RESET_STATE);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Funnel cookie is null => store is unchanged", () => {
|
||||||
|
// Arrange
|
||||||
|
cookieHelper.getFunnelCookie = jest.spyOn(cookieHelper, "getFunnelCookie").mockReturnValueOnce(null);
|
||||||
|
|
||||||
|
const mockData = {
|
||||||
|
actionList: [{
|
||||||
|
actionName: storeActions.RESET_STATE
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
|
||||||
|
var mocks = setupMocksForJsFiles(mockData);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
loadOrderIfPresent();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(cookieHelper.getFunnelCookie).toHaveBeenCalled();
|
||||||
|
expect(mocks.baseMixin.methods.dispatchNonBlockingStoreAction).not.toHaveBeenCalledWith(storeActions.RESET_STATE);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Funnel cookie valid, should call loadOrder", async () => {
|
||||||
|
// Arrange
|
||||||
|
cookieHelper.getFunnelCookie = jest.spyOn(cookieHelper, "getFunnelCookie")
|
||||||
|
.mockReturnValueOnce({ ShouldResetState: false, ReferralNumber: 123456, ReferralCorrelationId: "yyy-yyy-yyyy", ReferralDate: new Date()});
|
||||||
|
|
||||||
|
const mockData = {
|
||||||
|
actionList: [{
|
||||||
|
actionName: storeActions.LOAD_ORDER,
|
||||||
|
data: { ReferralNumber: 123456, vehicle: { year: 2010 } }
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
|
||||||
|
var mocks = setupMocksForJsFiles(mockData);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = await loadOrderIfPresent();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(cookieHelper.getFunnelCookie).toHaveBeenCalled();
|
||||||
|
expect(mocks.baseMixin.methods.dispatchNonBlockingStoreAction).not.toHaveBeenCalledWith(storeActions.LOAD_ORDER);
|
||||||
|
expect(result.ReferralNumber).toBe(123456);
|
||||||
|
expect(result.vehicle.year).toBe(2010);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("saveOrder", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
removeAllTestCookies();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("saveOrder => should set state order values", async () => {
|
||||||
|
// Arrange
|
||||||
|
const mockReferralNumber = 2;
|
||||||
|
const mockCorrelationId = "55";
|
||||||
|
const mockReferralDate = "2022";
|
||||||
|
|
||||||
|
const mockOrderInfo = getMockOrderInfo(mockReferralNumber, mockCorrelationId, mockReferralDate);
|
||||||
|
|
||||||
|
const mockData = {
|
||||||
|
actionList: [
|
||||||
|
{
|
||||||
|
actionName: storeActions.SAVE_ORDER,
|
||||||
|
data: mockOrderInfo,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
actionName: storeActions.SET_REFERRAL_INFORMATION
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const mocks = setupMocksForJsFiles(mockData);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await saveOrder();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(mocks.baseMixin.methods.dispatchNonBlockingStoreAction).toHaveBeenCalledWith(storeActions.SAVE_ORDER);
|
||||||
|
expect(mocks.baseMixin.methods.dispatchNonBlockingStoreAction).toHaveBeenCalledWith(storeActions.SET_REFERRAL_INFORMATION, {
|
||||||
|
referralNumber: mockReferralNumber,
|
||||||
|
referralDate: mockReferralDate,
|
||||||
|
referralCorrelationId: mockCorrelationId
|
||||||
|
}, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("saveOrder => should update DidHeritageFunnelUpdateLast cookie value to false", async () => {
|
||||||
|
// Arrange
|
||||||
|
const testReferralNumber = 1566818;
|
||||||
|
const testReferralDate = "2022-03-15T10:56:24.597";
|
||||||
|
const testReferralCorrelationId = "404d2b04-f86e-45c3-b373-127b6217b060";
|
||||||
|
|
||||||
|
const mockOrderInfo = getMockOrderInfo(testReferralNumber, testReferralCorrelationId, testReferralDate);
|
||||||
|
|
||||||
|
const mockData = {
|
||||||
|
actionList: [{
|
||||||
|
actionName: storeActions.SAVE_ORDER,
|
||||||
|
data: mockOrderInfo,
|
||||||
|
}],
|
||||||
|
router: router
|
||||||
|
};
|
||||||
|
|
||||||
|
setupMocksForJsFiles(mockData);
|
||||||
|
|
||||||
|
const testCookieValue = {
|
||||||
|
ReferralNumber: testReferralNumber,
|
||||||
|
ReferralDate: testReferralDate,
|
||||||
|
ReferralCorrelationId: testReferralCorrelationId,
|
||||||
|
ShouldResetState: false,
|
||||||
|
DidHeritageFunnelUpdateLast: true
|
||||||
|
}
|
||||||
|
|
||||||
|
setupCookies({ funnelCookieValue: JSON.stringify(testCookieValue) });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await saveOrder();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(cookieHelper.getFunnelCookie().DidHeritageFunnelUpdateLast).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
50
src/helpers/heritage-integration/session-helper.js
Normal file
50
src/helpers/heritage-integration/session-helper.js
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { applicationConfig } from "@/constants/application-config";
|
||||||
|
import { getFunnelCookie} from "@/helpers/heritage-integration/cookie-helper.js";
|
||||||
|
|
||||||
|
/*
|
||||||
|
Method to determine if our analytics session has timed out or not.
|
||||||
|
Amount used for timeout is configurable in application-config.js
|
||||||
|
*/
|
||||||
|
export function isAnalyticsSessionStillActive() {
|
||||||
|
if (getFunnelCookie() !== null) {
|
||||||
|
const lastTouchedValue = getFunnelCookie().LastTouched;
|
||||||
|
const timeoutAmount = applicationConfig.ANALYTICS_SESSION_TIMEOUT_MINUTES;
|
||||||
|
const isMoreThanHalfHourAgo = ((new Date() - new Date(lastTouchedValue)) / 60000) > timeoutAmount;
|
||||||
|
|
||||||
|
if (isMoreThanHalfHourAgo) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Method to determine if the users 'saved' session is still active.
|
||||||
|
When user state is created, there is a date that is saved into state
|
||||||
|
this method checks against that date.
|
||||||
|
|
||||||
|
Note: That time for saved session timeout is configurable in application-config.js
|
||||||
|
*/
|
||||||
|
export function isSavedSessionStillActive() {
|
||||||
|
if (getFunnelCookie() !== null) {
|
||||||
|
const savedSessionTimeStamp = new Date(getFunnelCookie().SavedQuoteTimeoutDate);
|
||||||
|
const isSavedSessionTimedOut = (new Date(new Date().toUTCString()) > savedSessionTimeStamp);
|
||||||
|
|
||||||
|
if (isSavedSessionTimedOut) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Function to get the date for the saved session timeout.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function getDateForSavedSessionTimeout() {
|
||||||
|
const currentDate = new Date(new Date().toUTCString())
|
||||||
|
currentDate.setDate(currentDate.getDate() + applicationConfig.SAVED_SESSION_TIMEOUT_DAYS)
|
||||||
|
return currentDate.toUTCString();
|
||||||
|
}
|
||||||
82
src/helpers/heritage-integration/session-helper.spec.js
Normal file
82
src/helpers/heritage-integration/session-helper.spec.js
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
import * as cookieHelper from "@/helpers/heritage-integration/cookie-helper";
|
||||||
|
import { isAnalyticsSessionStillActive, isSavedSessionStillActive, getDateForSavedSessionTimeout} from "@/helpers/heritage-integration/session-helper";
|
||||||
|
import { applicationConfig } from "@/constants/application-config";
|
||||||
|
|
||||||
|
describe("isAnalyticsSessionStillActive", () => {
|
||||||
|
test("isAnalyticsSessionStillActive, should return true", () => {
|
||||||
|
// Arrange
|
||||||
|
const mockDate = new Date(new Date().toUTCString())
|
||||||
|
mockDate.setDate(mockDate.getDate() + 1)
|
||||||
|
|
||||||
|
cookieHelper.getFunnelCookie = jest.spyOn(cookieHelper, "getFunnelCookie")
|
||||||
|
.mockReturnValue({ LastTouched: mockDate });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = isAnalyticsSessionStillActive();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("isAnalyticsSessionStillActive, should return false", () => {
|
||||||
|
// Arrange
|
||||||
|
const mockDate = new Date(new Date().toUTCString())
|
||||||
|
mockDate.setDate(mockDate.getDate() - 1)
|
||||||
|
|
||||||
|
cookieHelper.getFunnelCookie = jest.spyOn(cookieHelper, "getFunnelCookie")
|
||||||
|
.mockReturnValue({ LastTouched: mockDate });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = isAnalyticsSessionStillActive();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("isSavedSessionStillActive", () => {
|
||||||
|
test("isSavedSessionStillActive, should return true", () => {
|
||||||
|
// Arrange
|
||||||
|
const mockDate = new Date(new Date().toUTCString())
|
||||||
|
mockDate.setDate(mockDate.getDate() + 1);
|
||||||
|
|
||||||
|
cookieHelper.getFunnelCookie = jest.spyOn(cookieHelper, "getFunnelCookie")
|
||||||
|
.mockReturnValue({ SavedQuoteTimeoutDate: mockDate });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = isSavedSessionStillActive();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("isSavedSessionStillActive, should return false", () => {
|
||||||
|
// Arrange
|
||||||
|
const mockDate = new Date(new Date().toUTCString())
|
||||||
|
mockDate.setDate(mockDate.getDate() - 1)
|
||||||
|
|
||||||
|
cookieHelper.getFunnelCookie = jest.spyOn(cookieHelper, "getFunnelCookie")
|
||||||
|
.mockReturnValue({ SavedQuoteTimeoutDate: mockDate });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = isSavedSessionStillActive();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result).toBe(false);
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getDateForSavedSessionTimeout", () => {
|
||||||
|
test("getDateForSavedSessionTimeout, should equal application config setting", () =>{
|
||||||
|
// Arrange
|
||||||
|
const currentDate = new Date(new Date().toUTCString())
|
||||||
|
currentDate.setDate(currentDate.getDate() + applicationConfig.SAVED_SESSION_TIMEOUT_DAYS)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = getDateForSavedSessionTimeout();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result).toEqual(currentDate.toUTCString());
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
@ -3,28 +3,19 @@ import { storeMutations } from "@/constants/store-mutations.js";
|
||||||
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios.js";
|
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios.js";
|
||||||
import { vehicleCategories } from "@/constants/vehicle-categories.js";
|
import { vehicleCategories } from "@/constants/vehicle-categories.js";
|
||||||
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
|
import { cookieNames } from "@/constants/cookie-names";
|
||||||
import { Form } from "vee-validate";
|
import { Form } from "vee-validate";
|
||||||
import baseMixin from "@/mixins/base-mixin";
|
import baseMixin from "@/mixins/base-mixin";
|
||||||
|
import { getCookieDomainValue } from "@/helpers/heritage-integration/cookie-helper";
|
||||||
|
|
||||||
|
// Common methods
|
||||||
export function getMountOptions(mockData) {
|
export function getMountOptions(mockData) {
|
||||||
// Define our mocks to attached to the 'global' object for Vue/Jest.
|
// Define our mocks to attached to the 'global' object for Vue/Jest.
|
||||||
const mocks = {};
|
const mocks = {};
|
||||||
|
|
||||||
//this is mocking if you use the mixin directly(baseMixin.methods.dispatchNonBlockingStoreAction) vs this.dispatchNonBlockingStoreAction
|
//this is mocking if you use the mixin directly(baseMixin.methods.dispatchNonBlockingStoreAction) vs this.dispatchNonBlockingStoreAction
|
||||||
if (mockData.actionList !== undefined) {
|
setupBaseMixinDispatchNonBlockingStoreAction(mockData);
|
||||||
baseMixin.methods.dispatchNonBlockingStoreAction = jest.fn();
|
|
||||||
baseMixin.methods.dispatchNonBlockingStoreAction.mockImplementation((actionName) => {
|
|
||||||
let actionFilterResult = mockData.actionList.filter(
|
|
||||||
(x) => x.actionName == actionName
|
|
||||||
);
|
|
||||||
|
|
||||||
if (actionFilterResult.length > 0 && actionFilterResult.length === 1) {
|
|
||||||
return Promise.resolve({
|
|
||||||
data: actionFilterResult[0].data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
mocks.dispatchNonBlockingStoreAction = jest.fn();
|
mocks.dispatchNonBlockingStoreAction = jest.fn();
|
||||||
mocks.dispatchNonBlockingStoreAction.mockImplementation((actionName) => {
|
mocks.dispatchNonBlockingStoreAction.mockImplementation((actionName) => {
|
||||||
let actionFilterResult = mockData.actionList.filter(
|
let actionFilterResult = mockData.actionList.filter(
|
||||||
|
|
@ -50,7 +41,6 @@ export function getMountOptions(mockData) {
|
||||||
mocks.$router = mockData.router;
|
mocks.$router = mockData.router;
|
||||||
mocks.$route = mockData.route;
|
mocks.$route = mockData.route;
|
||||||
|
|
||||||
|
|
||||||
const global = {
|
const global = {
|
||||||
mocks: mocks,
|
mocks: mocks,
|
||||||
stubs: { Form }
|
stubs: { Form }
|
||||||
|
|
@ -58,3 +48,58 @@ export function getMountOptions(mockData) {
|
||||||
|
|
||||||
return { global };
|
return { global };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setupMocksForJsFiles(mockData = {}) {
|
||||||
|
setupBaseMixinDispatchNonBlockingStoreAction(mockData);
|
||||||
|
|
||||||
|
return { baseMixin };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Heritage integration common methods
|
||||||
|
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": "{}"
|
||||||
|
};
|
||||||
|
|
||||||
|
export function removeAllTestCookies() {
|
||||||
|
Object.keys(cookies).forEach(key => {
|
||||||
|
document.cookie = `${key}=;Max-Age=0;`;
|
||||||
|
document.cookie = `${key}=;Max-Age=0;path=/;${getCookieDomainValue()}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMockOrderInfo(mockReferralNumber, mockCorrelationId, mockReferralDate) {
|
||||||
|
return {
|
||||||
|
referralNumber: mockReferralNumber,
|
||||||
|
referralCorrelationId: mockCorrelationId,
|
||||||
|
referralDate: mockReferralDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setupCookies({ funnelCookieValue = "", includeHeritageCookie = true }) {
|
||||||
|
Object.keys(cookies).forEach(key => {
|
||||||
|
const cookieValue = key == cookieNames.FUNNEL_SESSION_INFO ? funnelCookieValue : cookies[key];
|
||||||
|
if (includeHeritageCookie || key != cookieNames.FUNNEL_SESSION_INFO)
|
||||||
|
document.cookie = `${key}=${cookieValue}; path=/; ${getCookieDomainValue()}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private methods
|
||||||
|
function setupBaseMixinDispatchNonBlockingStoreAction(mockData) {
|
||||||
|
if (mockData.actionList !== undefined) {
|
||||||
|
baseMixin.methods.dispatchNonBlockingStoreAction = jest.fn();
|
||||||
|
baseMixin.methods.dispatchNonBlockingStoreAction.mockImplementation((actionName) => {
|
||||||
|
let actionFilterResult = mockData.actionList.filter(
|
||||||
|
(x) => x.actionName == actionName
|
||||||
|
);
|
||||||
|
|
||||||
|
if (actionFilterResult.length > 0 && actionFilterResult.length === 1) {
|
||||||
|
return Promise.resolve({
|
||||||
|
data: actionFilterResult[0].data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,233 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="container-fluid container-shadow p-2 rounded-3">
|
|
||||||
<div class="row my-3">
|
|
||||||
<div class="col">
|
|
||||||
<vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage=true />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<form>
|
|
||||||
<div class="row my-4">
|
|
||||||
<div class="col">
|
|
||||||
<label for="autocomplete">Street address</label>
|
|
||||||
<input ref="autocomplete"
|
|
||||||
id="autocomplete"
|
|
||||||
placeholder="Search"
|
|
||||||
class="form-control search-location"
|
|
||||||
type="text"
|
|
||||||
autocomplete="off" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<form>
|
|
||||||
<div v-show="showAddressFields" class="row my-4">
|
|
||||||
<div class="col">
|
|
||||||
<label for="city">City</label>
|
|
||||||
<input ref="city"
|
|
||||||
id="city"
|
|
||||||
class="form-control"
|
|
||||||
type="text"
|
|
||||||
autocomplete="off" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<form>
|
|
||||||
<div v-show="showAddressFields" class="row my-4">
|
|
||||||
<div class="col-6">
|
|
||||||
<label for="state">State</label>
|
|
||||||
<input ref="state"
|
|
||||||
id="state"
|
|
||||||
class="form-control"
|
|
||||||
type="text"
|
|
||||||
autocomplete="off" />
|
|
||||||
</div>
|
|
||||||
<div class="col-6">
|
|
||||||
<label for="zip">Zip</label>
|
|
||||||
<input ref="zip"
|
|
||||||
id="zip"
|
|
||||||
class="form-control"
|
|
||||||
type="text"
|
|
||||||
autocomplete="off" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<div v-show="displayVerificationWarning" class="row my-4">
|
|
||||||
<div class="col">
|
|
||||||
<span style="color: red;">An accurate match may not have have been located. Please verify your address before continuing</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-show="displayNoMatchWarning" class="row my-4">
|
|
||||||
<div class="col">
|
|
||||||
<span style="color: red;">An accurate match could not be located. Please re-enter your street address before continuing</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row my-4">
|
|
||||||
<div class="col">
|
|
||||||
<label for="firstName">First name</label>
|
|
||||||
<input ref="firstName"
|
|
||||||
id="firstName"
|
|
||||||
class="form-control"
|
|
||||||
type="text"
|
|
||||||
autocomplete="off" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row my-4">
|
|
||||||
<div class="col">
|
|
||||||
<label for="lastName">Last name</label>
|
|
||||||
<input ref="lastName"
|
|
||||||
id="lastName"
|
|
||||||
class="form-control"
|
|
||||||
type="text"
|
|
||||||
autocomplete="off" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row my-4">
|
|
||||||
<div class="col">
|
|
||||||
<label for="email">Email address</label>
|
|
||||||
<input ref="email"
|
|
||||||
id="email"
|
|
||||||
class="form-control"
|
|
||||||
type="text"
|
|
||||||
autocomplete="off" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
|
||||||
export default {
|
|
||||||
name: "App",
|
|
||||||
components: {
|
|
||||||
vehicleBanner,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showAddressFields: false,
|
|
||||||
displayVerificationWarning: false,
|
|
||||||
displayNoMatchWarning: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.$refs.vehicleBanner.initializeComponent({
|
|
||||||
GenericVehicleImage:
|
|
||||||
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3",
|
|
||||||
});
|
|
||||||
|
|
||||||
let addressField1 = document.getElementById("autocomplete");
|
|
||||||
let self = this;
|
|
||||||
|
|
||||||
//let apiKey = "AIzaSyDptGCkOPgN2uWJOy4ou4M33phRD4MAoJo"; // localhost:8080
|
|
||||||
let apiKey = "AIzaSyBji_2ApAj2vE_XNGyV-LaUEKrmuElpJ1w"; // Dev and above
|
|
||||||
|
|
||||||
this.$loadScript(`https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places`)
|
|
||||||
.then(() => {
|
|
||||||
|
|
||||||
// Script is loaded, initialize the autocomplete textbox
|
|
||||||
self.autocomplete = new window.google.maps.places.Autocomplete(
|
|
||||||
self.$refs.autocomplete,
|
|
||||||
{
|
|
||||||
componentRestrictions: { country: ["us"] },
|
|
||||||
fields: ["address_components"],
|
|
||||||
types: ["address"],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
self.autocomplete.addListener('place_changed', fillInAddress);
|
|
||||||
|
|
||||||
addressField1.onblur = function() {
|
|
||||||
var hover = document.querySelector(".pac-container .pac-item:hover");
|
|
||||||
|
|
||||||
// if an item has been clicked, do nothing, otherwise get first solution and use Geocoder to get the place
|
|
||||||
if (hover === null) {
|
|
||||||
var item = document.querySelector(".pac-container .pac-item");
|
|
||||||
if (item != null) {
|
|
||||||
var firstResult = item.textContent;
|
|
||||||
var geocoder = new window.google.maps.Geocoder();
|
|
||||||
geocoder.geocode({
|
|
||||||
address: firstResult
|
|
||||||
}, function (results, status) {
|
|
||||||
if (status === window.google.maps.GeocoderStatus.OK) {
|
|
||||||
fillInAddress(results[0]);
|
|
||||||
self.displayVerificationWarning = true;
|
|
||||||
self.displayNoMatchWarning = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
let city = document.querySelector("#city");
|
|
||||||
let state = document.querySelector("#state");
|
|
||||||
let zip = document.querySelector("#zip");
|
|
||||||
|
|
||||||
city.value = "";
|
|
||||||
state.value = "";
|
|
||||||
zip.value = "";
|
|
||||||
|
|
||||||
self.displayVerificationWarning = false;
|
|
||||||
self.displayNoMatchWarning = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function fillInAddress(place) {
|
|
||||||
if (!place) {
|
|
||||||
place = self.autocomplete.getPlace();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (place && place.address_components) {
|
|
||||||
let address1 = "";
|
|
||||||
let city = document.querySelector("#city");
|
|
||||||
let state = document.querySelector("#state");
|
|
||||||
let zip = document.querySelector("#zip");
|
|
||||||
self.showAddressFields = true;
|
|
||||||
|
|
||||||
for (const component of place.address_components) {
|
|
||||||
const componentType = component.types[0];
|
|
||||||
|
|
||||||
switch (componentType) {
|
|
||||||
case "street_number": {
|
|
||||||
address1 = `${component.long_name} ${address1}`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case "route": {
|
|
||||||
address1 += component.short_name;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "locality": {
|
|
||||||
city.value = component.long_name;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "administrative_area_level_1": {
|
|
||||||
state.value = component.short_name;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "postal_code": {
|
|
||||||
zip.value = component.long_name;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
addressField1.value = address1;
|
|
||||||
self.displayVerificationWarning = false;
|
|
||||||
self.displayNoMatchWarning = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
self.displayVerificationWarning = true;
|
|
||||||
self.displayNoMatchWarning = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
// Failed to fetch script
|
|
||||||
console.log("Unable to load Google Places API script");
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
@ -1,138 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="container-fluid">
|
|
||||||
<h4 class="m-0 p-2 bg-light rounded">List Buttons</h4>
|
|
||||||
|
|
||||||
<buttonQuestion :answers="years" text-position="text-start" questionText="List Button as Radio" groupName="years" />
|
|
||||||
|
|
||||||
<buttonQuestion :answers="checkboxAnswers" isMultiSelect text-position="text-start" questionText="List Button as checkbox" groupName="check-box-answers" />
|
|
||||||
|
|
||||||
<buttonQuestion :answers="checkboxAnswers" isMultiSelect text-position="text-end" questionText="List Button as checkbox text end" groupName="check-box-answers-text-right" />
|
|
||||||
|
|
||||||
<buttonQuestion :answers="years" questionText="List Button as Radio text center (default)" groupName="years-text-center" />
|
|
||||||
|
|
||||||
<buttonQuestion :answers="years" selectingInitiatesLoad questionText="List Button as Radio with spinner" groupName="years-text-spinner" />
|
|
||||||
<span>(Selecting button with spinner will cause page overlay, page refresh needed to select buttons from other groups)</span>
|
|
||||||
|
|
||||||
<h4 class="m-0 p-2 bg-light rounded mt-5">List ButtonHorizontals</h4>
|
|
||||||
|
|
||||||
<buttonQuestion buttonType="listButtonHorizontal" :answers="radioAnswers" questionText="List Button Horizontal as radio" groupName="lbh-radio" />
|
|
||||||
|
|
||||||
<buttonQuestion buttonType="listButtonHorizontal" isMultiSelect :answers="horizontalCheckboxAnswers" questionText="List Button Horizontal as checkbox" groupName="lbh-checkbox" />
|
|
||||||
|
|
||||||
<h4 class="m-0 p-2 bg-light rounded mt-5">List Cards</h4>
|
|
||||||
|
|
||||||
<buttonQuestion buttonType="listCard" :answers="listCardRadio" questionText="List Card as radio" groupName="lc-radio" />
|
|
||||||
|
|
||||||
<buttonQuestion isMultiSelect buttonType="listCard" :answers="listCardCheckBox" questionText="List Card as checkbox" groupName="lc-checkbox" />
|
|
||||||
|
|
||||||
<buttonQuestion buttonType="listCard" :answers="listCardGroup" questionText="List Cards as radios" groupName="lc-mult" />
|
|
||||||
|
|
||||||
<buttonQuestion buttonType="listCard" isMultiSelect :answers="listCardGroup" questionText="List Cards as checkboxes" groupName="lcc-mult" />
|
|
||||||
|
|
||||||
<buttonQuestion isWide buttonType="listCard" :answers="listCardGroup" questionText="List Cards as radios full width (text left image right)" groupName="lc-multWide" />
|
|
||||||
|
|
||||||
<buttonQuestion isWide isMultiSelect buttonType="listCard" :answers="listCardGroup" questionText="List Cards as checkboxes full width (text left image right)" groupName="lcc-multWide" />
|
|
||||||
|
|
||||||
<h4 class="m-0 p-2 bg-light rounded mt-5">Radio</h4>
|
|
||||||
|
|
||||||
<buttonQuestion buttonType="radio" :answers="radioAnswers" questionText="Used with radio" groupName="radio" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import buttonQuestion from "@/common-components/button-question/button-question";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "App",
|
|
||||||
components: {
|
|
||||||
buttonQuestion
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
years: [2023, 2022, 2021, 2020],
|
|
||||||
checkboxAnswers: [
|
|
||||||
"Checkbox Answer 1",
|
|
||||||
"Checkbox Answer 2",
|
|
||||||
"Checkbox Answer 3",
|
|
||||||
],
|
|
||||||
radioAnswers: ["Radio Answer 1", "Radio Answer 2", "Radio Answer 3"],
|
|
||||||
horizontalCheckboxAnswers: ["HCB Answer 1","HCB Answer 2","HCB Answer 3"],
|
|
||||||
horizontalRadioAnswers: ["HR Answer 1", "HR Answer 2", "HR Answer 3"],
|
|
||||||
listCardCheckBox: [
|
|
||||||
{
|
|
||||||
Name: "List-Card-CB",
|
|
||||||
Text: "List Card CB",
|
|
||||||
SubText: "checkbox",
|
|
||||||
ImageId: "53343ce4-5b6a-46aa-a80a-f948c1723955",
|
|
||||||
AnswerImageUrl: "https://digitalconsumercms-dev.safelite.com/images/default-source/damage-options/car.svg?sfvrsn=1468d7f1_3",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
listCardRadio: [
|
|
||||||
{
|
|
||||||
Name: "List-Card-R",
|
|
||||||
Text: "List Card R",
|
|
||||||
SubText: "Radio",
|
|
||||||
ImageId: "53343ce4-5b6a-46aa-a80a-f948c1723955",
|
|
||||||
AnswerImageUrl: "https://digitalconsumercms-dev.safelite.com/images/default-source/damage-options/car.svg?sfvrsn=1468d7f1_3",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
listCardCheckBoxHorizontalSubText: [
|
|
||||||
{
|
|
||||||
Name: "List-Card-CBHst",
|
|
||||||
Text: "List Card CBHst",
|
|
||||||
SubText: "With Subtext",
|
|
||||||
ImageId: "53343ce4-5b6a-46aa-a80a-f948c1723955",
|
|
||||||
AnswerImageUrl: "https://digitalconsumercms-dev.safelite.com/images/default-source/damage-options/car.svg?sfvrsn=1468d7f1_3",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
listCardCheckBoxHorizontal: [
|
|
||||||
{
|
|
||||||
Name: "List-Card-CBH",
|
|
||||||
Text: "List Card CBH",
|
|
||||||
ImageId: "53343ce4-5b6a-46aa-a80a-f948c1723955",
|
|
||||||
AnswerImageUrl: "https://digitalconsumercms-dev.safelite.com/images/default-source/damage-options/car.svg?sfvrsn=1468d7f1_3",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
listRadioHorizontalSubText: [
|
|
||||||
{
|
|
||||||
Name: "List-Card-RHst",
|
|
||||||
Text: "List Card RHst",
|
|
||||||
SubText: "With Subtext",
|
|
||||||
ImageId: "53343ce4-5b6a-46aa-a80a-f948c1723955",
|
|
||||||
AnswerImageUrl: "https://digitalconsumercms-dev.safelite.com/images/default-source/damage-options/car.svg?sfvrsn=1468d7f1_3",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
listRadioHorizontal: [
|
|
||||||
{
|
|
||||||
Name: "List-Card-RH",
|
|
||||||
Text: "List Card RH",
|
|
||||||
ImageId: "53343ce4-5b6a-46aa-a80a-f948c1723955",
|
|
||||||
AnswerImageUrl: "https://digitalconsumercms-dev.safelite.com/images/default-source/damage-options/car.svg?sfvrsn=1468d7f1_3",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
listCardGroup: [
|
|
||||||
{
|
|
||||||
Name: "Side-Window-1",
|
|
||||||
Text: "Side Window",
|
|
||||||
SubText: "With Subtext",
|
|
||||||
ImageId: "53343ce4-5b6a-46aa-a80a-f948c1723955",
|
|
||||||
AnswerImageUrl: "https://digitalconsumercms-dev.safelite.com/images/default-source/damage-options/car.svg?sfvrsn=1468d7f1_3",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "Side-Window-2",
|
|
||||||
Text: "Side Window",
|
|
||||||
SubText: "With Subtext that is more than one line",
|
|
||||||
ImageId: "53343ce4-5b6a-46aa-a80a-f948c1723955",
|
|
||||||
AnswerImageUrl: "https://digitalconsumercms-dev.safelite.com/images/default-source/damage-options/car.svg?sfvrsn=1468d7f1_3",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "Side-Window-3",
|
|
||||||
Text: "Side Window",
|
|
||||||
ImageId: "53343ce4-5b6a-46aa-a80a-f948c1723955",
|
|
||||||
AnswerImageUrl: "https://digitalconsumercms-dev.safelite.com/images/default-source/damage-options/car.svg?sfvrsn=1468d7f1_3",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
@ -1144,7 +1144,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import buttonMain from "@/ux-components/button-main/button-main";
|
import buttonMain from "@/ux-components/button-main/button-main";
|
||||||
import buttonBack from "@/common-components/button-back/button-back";
|
import buttonBack from "@/common-components/funnel-sub-header/button-back/button-back";
|
||||||
import listCard from "@/ux-components/list-card/list-card";
|
import listCard from "@/ux-components/list-card/list-card";
|
||||||
import listButton from "@/ux-components/list-button/list-button";
|
import listButton from "@/ux-components/list-button/list-button";
|
||||||
import alert from "@/ux-components/alert/alert";
|
import alert from "@/ux-components/alert/alert";
|
||||||
|
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="container-fluid nested-radio">
|
|
||||||
<div class="row mb-1">
|
|
||||||
<div class="col">
|
|
||||||
<listCard
|
|
||||||
groupName="listcard1"
|
|
||||||
isWide="true"
|
|
||||||
buttonImage="https://digitalconsumercms-dev.safelite.com/images/default-source/damage-options/car.svg?sfvrsn=1468d7f1_3"
|
|
||||||
buttonLabel="Green Tint, Blue Shade"
|
|
||||||
buttonID="radio1"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1">
|
|
||||||
<div class="col">
|
|
||||||
<buttonQuestion
|
|
||||||
buttonType="radio"
|
|
||||||
groupName="Demo Group"
|
|
||||||
buttonID="button1"
|
|
||||||
isRequired
|
|
||||||
value="test button"
|
|
||||||
screenReaderOnlyText="rain sensor, solar, 3rd visor band"
|
|
||||||
:answers="demoArray"
|
|
||||||
questionText="ok now select your features"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-2">
|
|
||||||
<div class="col">
|
|
||||||
<listCard
|
|
||||||
groupName="listcard1"
|
|
||||||
isWide="true"
|
|
||||||
buttonImage="https://digitalconsumercms-dev.safelite.com/images/default-source/damage-options/car.svg?sfvrsn=1468d7f1_3"
|
|
||||||
buttonLabel="Green Tint"
|
|
||||||
buttonID="radio2"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import buttonQuestion from "@/common-components/button-question/button-question";
|
|
||||||
import listCard from "@/ux-components/list-card/list-card";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "nestedRadio",
|
|
||||||
components: {
|
|
||||||
buttonQuestion,
|
|
||||||
listCard,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
demoArray: [
|
|
||||||
"rain sensor, solar, 3rd visor band",
|
|
||||||
"rain sensor, heated glass, solar, 3rd visor band",
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.nested-radio {
|
|
||||||
.ui-radio {
|
|
||||||
flex-direction: column;
|
|
||||||
margin: 0.25rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: 0.875rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -3,7 +3,7 @@ import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-que
|
||||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
import { nextTick } from "vue";
|
import { nextTick } from "vue";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
jest.mock("@/store", () => { return {}; }, {virtual: true});
|
jest.mock("@/store",()=>{return{};},{virtual:true});
|
||||||
|
|
||||||
describe("replace-options-question.vue", () => {
|
describe("replace-options-question.vue", () => {
|
||||||
test("Selected damage option is emitted upon selection.", async () => {
|
test("Selected damage option is emitted upon selection.", async () => {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import sideDoorOptions from "@/layouts/vehicle-damage/side-door-options/side-doo
|
||||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-question/replace-options-question";
|
import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-question/replace-options-question";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
jest.mock("@/store", () => { return {}; }, {virtual: true});
|
jest.mock("@/store",()=>{return{};},{virtual:true});
|
||||||
|
|
||||||
describe("replace-options-question.vue", () => {
|
describe("replace-options-question.vue", () => {
|
||||||
test("Selected side door option is emitted upon selection.", async () => {
|
test("Selected side door option is emitted upon selection.", async () => {
|
||||||
|
|
@ -95,7 +95,7 @@ describe("replace-options-question.vue", () => {
|
||||||
}) {
|
}) {
|
||||||
|
|
||||||
//Mock store
|
//Mock store
|
||||||
store.dispatch = jest.fn(() => dataFromStoreApi);
|
store.dispatch = jest.fn(() => {});
|
||||||
store.getters = { vehicle: {year: 2019, make: 'honda', model: 'civc', style: '2 Door', category: 'CAR'} };
|
store.getters = { vehicle: {year: 2019, make: 'honda', model: 'civc', style: '2 Door', category: 'CAR'} };
|
||||||
const mountOptions = getMountOptions({
|
const mountOptions = getMountOptions({
|
||||||
store: {
|
store: {
|
||||||
|
|
|
||||||
|
|
@ -72,13 +72,15 @@ import alert from "@/ux-components/alert/alert";
|
||||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
import store from "@/store";
|
|
||||||
import baseMixin from "@/mixins/base-mixin";
|
|
||||||
import { Form, defineRule } from "vee-validate";
|
import { Form, defineRule } from "vee-validate";
|
||||||
import { required } from "@/helpers/validation-rules";
|
import { required } from "@/helpers/validation-rules";
|
||||||
import { errorMessages } from "@/constants/error-messages";
|
import { errorMessages } from "@/constants/error-messages";
|
||||||
import { damageLocationsCms } from "@/constants/damage-locations-cms.js";
|
import { damageLocationsCms } from "@/constants/damage-locations-cms.js";
|
||||||
import { damageLocationsSelected } from "@/constants/damage-locations-selected.js";
|
import { damageLocationsSelected } from "@/constants/damage-locations-selected.js";
|
||||||
|
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||||
|
|
||||||
|
import store from "@/store";
|
||||||
|
import baseMixin from "@/mixins/base-mixin";
|
||||||
|
|
||||||
// DEFINE VALIDATION RULES
|
// DEFINE VALIDATION RULES
|
||||||
defineRule("replace-options-required", required(errorMessages.REPLACE_OPTIONS_REQUIRED));
|
defineRule("replace-options-required", required(errorMessages.REPLACE_OPTIONS_REQUIRED));
|
||||||
|
|
@ -139,7 +141,10 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
return store.getters.vehicle.carId !== null;
|
if(store.getters.vehicle.carId){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
resetDependentState() {
|
resetDependentState() {
|
||||||
store.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
store.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||||
|
|
@ -271,6 +276,12 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
navigateForward(partsData){
|
navigateForward(partsData){
|
||||||
|
// CSR-98 TEMP
|
||||||
|
if (store.getters.vehicle.year == 2010) {
|
||||||
|
navigateToHeritageFunnel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//found problem questions
|
//found problem questions
|
||||||
if (partsData.data.partsOrQuestions.some(pq => pq.partQuestions != null && pq.partQuestions.length > 0)){
|
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);
|
this.$router.navigateAfterSave(this.navigationScenarios.SELECTED_DAMAGE_WITH_PART_QUESTIONS, this.$route, {}, {}, partsData.data);
|
||||||
|
|
@ -373,15 +384,15 @@ export default {
|
||||||
hasSplitSingleConflict() {
|
hasSplitSingleConflict() {
|
||||||
if (!this.selectedWindshieldOptions.selectedWindshieldReplaceOptions) return false;
|
if (!this.selectedWindshieldOptions.selectedWindshieldReplaceOptions) return false;
|
||||||
|
|
||||||
return this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.some(selectedSingleWindshield =>
|
return this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.some(selectedSingleWindshield =>
|
||||||
{
|
{
|
||||||
return selectedSingleWindshield.toUpperCase() === damageLocationsSelected.SINGLE.toUpperCase();
|
return selectedSingleWindshield.toUpperCase() === damageLocationsSelected.SINGLE.toUpperCase();
|
||||||
}) &&
|
}) &&
|
||||||
(this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.some(selectedDriverWindshield =>
|
(this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.some(selectedDriverWindshield =>
|
||||||
{
|
{
|
||||||
return selectedDriverWindshield.toUpperCase() === damageLocationsSelected.DRIVER.toUpperCase();
|
return selectedDriverWindshield.toUpperCase() === damageLocationsSelected.DRIVER.toUpperCase();
|
||||||
}) ||
|
}) ||
|
||||||
this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.some(selectedPassengerWindshield =>
|
this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.some(selectedPassengerWindshield =>
|
||||||
{
|
{
|
||||||
return selectedPassengerWindshield.toUpperCase() === damageLocationsSelected.PASSENGER.toUpperCase();
|
return selectedPassengerWindshield.toUpperCase() === damageLocationsSelected.PASSENGER.toUpperCase();
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,10 @@ export default {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
return store.getters.vehicle.year !== null;
|
if (store.getters.vehicle.year){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
resetDependentState() {
|
resetDependentState() {
|
||||||
// Set
|
// Set
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,10 @@ export default {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
return store.getters.vehicle.make !== null;
|
if(store.getters.vehicle.make){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
resetDependentState() {
|
resetDependentState() {
|
||||||
// Set
|
// Set
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ describe("vehicle-parts.vue", () => {
|
||||||
test("PageData / isRepair populated in Vuex. arePagePrerequisitesValid should be true ", async () => {
|
test("PageData / isRepair populated in Vuex. arePagePrerequisitesValid should be true ", async () => {
|
||||||
|
|
||||||
//Arrange
|
//Arrange
|
||||||
store.getters.pageData.mockReturnValueOnce(basePartResponse);
|
store.getters.pageData.mockReturnValue(basePartResponse);
|
||||||
store.getters.lineItems = { glassParts: {} }
|
store.getters.lineItems = { glassParts: {} }
|
||||||
|
|
||||||
const { wrapper } = setupMocks({
|
const { wrapper } = setupMocks({
|
||||||
|
|
|
||||||
|
|
@ -4,20 +4,32 @@
|
||||||
<vehicleBanner ref="vehicleBanner" cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage="false" />
|
<vehicleBanner ref="vehicleBanner" cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage="false" />
|
||||||
<funnelSubHeader ref="funnelSubHeader" cmsWidgetName="FunnelSubHeaderWidget" />
|
<funnelSubHeader ref="funnelSubHeader" cmsWidgetName="FunnelSubHeaderWidget" />
|
||||||
<div class="container-fluid prevent-squish my-5">
|
<div class="container-fluid prevent-squish my-5">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<alert class="rounded border-0 shadow-sm" alertClass="alert-warning" :alertHeadline="alertWidgetData.headline" :alertCopy="alertWidgetData.copy" :isDismissible="false" />
|
<alert
|
||||||
</div>
|
class="rounded border-0 shadow-sm"
|
||||||
|
alertClass="alert-warning"
|
||||||
|
:alertHeadline="alertWidgetData.headline"
|
||||||
|
:alertCopy="alertWidgetData.copy"
|
||||||
|
:isDismissible="false"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-for="(item, i) in PartsForQuestions" :key="i">
|
<div v-for="(item, i) in PartsForQuestions" :key="i">
|
||||||
<!-- Render horizontal lines if there is multi-glass (aka if i > 0) -->
|
<!-- Render horizontal lines if there is multi-glass (aka if i > 0) -->
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<hr v-if="i > 0" />
|
<hr v-if="i > 0" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<glassPartQuestion :ref="`${RefPrefix}-${item.glassLocation}-${item.glassName}`" v-model="glassParts[item.glassLocation + '-' + item.glassName]" :glassLocation="item.glassLocation" :glassName="item.glassName" :colorAnswers="item.colorAnswers" />
|
<glassPartQuestion
|
||||||
|
:ref="`${RefPrefix}-${item.glassLocation}-${item.glassName}`"
|
||||||
|
v-model="glassParts[item.glassLocation + '-' + item.glassName]"
|
||||||
|
:glassLocation="item.glassLocation"
|
||||||
|
:glassName="item.glassName"
|
||||||
|
:colorAnswers="item.colorAnswers"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<funnelFooter cmsWidgetName="FunnelFooterWidget" ref="funnelFooter" @back-clicked="backButtonAction" @ForwardClicked="forwardButtonAction" />
|
<funnelFooter cmsWidgetName="FunnelFooterWidget" ref="funnelFooter" @back-clicked="backButtonAction" @ForwardClicked="forwardButtonAction" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -32,18 +44,10 @@ import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-he
|
||||||
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
|
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
|
||||||
import alert from "@/ux-components/alert/alert";
|
import alert from "@/ux-components/alert/alert";
|
||||||
// Supporting Files
|
// Supporting Files
|
||||||
import {
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
fetchCmsContentForPage
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
} from "@/helpers/cms-content-helper";
|
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
import {
|
import { storeMutations } from "@/constants/store-mutations";
|
||||||
settleAllPromises
|
|
||||||
} from "@/helpers/layout-helper";
|
|
||||||
import {
|
|
||||||
fmgPageValues
|
|
||||||
} from "@/router/router-constants/fmgPage-values";
|
|
||||||
import {
|
|
||||||
storeMutations
|
|
||||||
} from "@/constants/store-mutations";
|
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -71,148 +75,151 @@ export default {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Set alertData for the page alert. These use props so we don't call
|
// Set alertData for the page alert. These use props so we don't call
|
||||||
// initializeComponent here.
|
// initializeComponent here.
|
||||||
vm.alertWidgetData = {
|
vm.alertWidgetData = {
|
||||||
copy: resultMap.cmsContent.AlertWidget.BodyText,
|
copy: resultMap.cmsContent.AlertWidget.BodyText,
|
||||||
headline: resultMap.cmsContent.AlertWidget.HeadlineText,
|
headline: resultMap.cmsContent.AlertWidget.HeadlineText,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
return {
|
||||||
|
glassParts: {},
|
||||||
|
alertWidgetData: Object,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
glassPartQuestion,
|
||||||
|
funnelHeader,
|
||||||
|
vehicleBanner,
|
||||||
|
funnelSubHeader,
|
||||||
|
funnelFooter,
|
||||||
|
alert,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
PartsForQuestions() {
|
||||||
|
const partsData = this.PartsFromApi;
|
||||||
|
|
||||||
|
// Map API result data, to vehicle-parts data structure
|
||||||
|
const mappedData = partsData.partsOrQuestions.map((g) => {
|
||||||
return {
|
return {
|
||||||
glassParts: {},
|
glassName: g.glassName,
|
||||||
alertWidgetData: Object,
|
glassLocation: g.glassLocation,
|
||||||
|
colorAnswers: g.parts.reduce((arr, p) => {
|
||||||
|
arr.push({
|
||||||
|
ColorAnswerText: p.color,
|
||||||
|
FeatureAnswers: [
|
||||||
|
{
|
||||||
|
FeatureAnswerText:
|
||||||
|
p.description === "" ? p.color : p.description,
|
||||||
|
PartNumber: p.partNumber,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
return arr;
|
||||||
|
}, []),
|
||||||
};
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return mappedData;
|
||||||
},
|
},
|
||||||
components: {
|
|
||||||
glassPartQuestion,
|
PartsFromApi() {
|
||||||
funnelHeader,
|
return store.getters.pageData(fmgPageValues.VEHICLE_PARTS);
|
||||||
vehicleBanner,
|
|
||||||
funnelSubHeader,
|
|
||||||
funnelFooter,
|
|
||||||
alert,
|
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
PartsForQuestions() {
|
|
||||||
const partsData = this.PartsFromApi;
|
|
||||||
|
|
||||||
// Map API result data, to vehicle-parts data structure
|
RefPrefix() {
|
||||||
const mappedData = partsData.partsOrQuestions.map((g) => {
|
return "partQuestion";
|
||||||
|
|
||||||
return {
|
|
||||||
glassName: g.glassName,
|
|
||||||
glassLocation: g.glassLocation,
|
|
||||||
colorAnswers: g.parts.reduce((arr, p) => {
|
|
||||||
arr.push({
|
|
||||||
ColorAnswerText: p.color,
|
|
||||||
FeatureAnswers: [{
|
|
||||||
FeatureAnswerText: p.description === "" ? p.color : p.description,
|
|
||||||
PartNumber: p.partNumber,
|
|
||||||
}, ],
|
|
||||||
});
|
|
||||||
return arr;
|
|
||||||
}, []),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return mappedData;
|
|
||||||
},
|
|
||||||
|
|
||||||
PartsFromApi() {
|
|
||||||
return store.getters.pageData(fmgPageValues.VEHICLE_PARTS);
|
|
||||||
},
|
|
||||||
|
|
||||||
RefPrefix() {
|
|
||||||
return "partQuestion";
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
methods: {
|
},
|
||||||
arePagePrerequisitesValid() {
|
methods: {
|
||||||
// Check if isRepair is populated and if the pageData we need is here (Parts data)
|
arePagePrerequisitesValid() {
|
||||||
if (
|
// Check if isRepair is populated and if the pageData we need is here (Parts data)
|
||||||
store.getters.damage.isRepair !== null &&
|
if (
|
||||||
Object.keys(store.getters.pageData(fmgPageValues.VEHICLE_PARTS))
|
(store.getters.damage.isRepair != null) &&
|
||||||
.length !== 0
|
Object.keys(store.getters.pageData(fmgPageValues.VEHICLE_PARTS))
|
||||||
) {
|
.length !== 0
|
||||||
return true;
|
) {
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
backButtonAction() {
|
backButtonAction() {
|
||||||
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
|
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||||
},
|
},
|
||||||
forwardButtonAction() {
|
forwardButtonAction() {
|
||||||
const selectedGlassPartNumbers = [];
|
const selectedGlassPartNumbers = [];
|
||||||
const matchedParts = [];
|
const matchedParts = [];
|
||||||
|
|
||||||
// Compile all selected parts from the page.
|
// Compile all selected parts from the page.
|
||||||
for (let [key, value] of Object.entries(this.glassParts)) {
|
for (let [key, value] of Object.entries(this.glassParts)) {
|
||||||
for (let [glassKey, glassValue] of Object.entries(value)) {
|
for (let [glassKey, glassValue] of Object.entries(value)) {
|
||||||
selectedGlassPartNumbers.push(glassValue[0]);
|
selectedGlassPartNumbers.push(glassValue[0]);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match them to the parts from the API.
|
|
||||||
for (let [key, value] of Object.entries(
|
|
||||||
this.PartsFromApi.partsOrQuestions
|
|
||||||
)) {
|
|
||||||
for (let [partKey, partValue] of Object.entries(value.parts)) {
|
|
||||||
const currentPart =
|
|
||||||
this.PartsFromApi.partsOrQuestions[key].parts[partKey];
|
|
||||||
const isMatched = selectedGlassPartNumbers.some(
|
|
||||||
(p) => p === currentPart.partNumber
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isMatched) {
|
|
||||||
matchedParts.push(currentPart);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no parts could be matched, throw an error.
|
|
||||||
if (matchedParts.length === 0) {
|
|
||||||
throw new Error("Could not match any parts to the selected parts");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save parts to the store.
|
|
||||||
store.commit(storeMutations.UPDATE_PARTS, matchedParts);
|
|
||||||
|
|
||||||
// Navigate to the next page.
|
|
||||||
this.$router.navigateAfterSave(
|
|
||||||
this.navigationScenarios.SELECTED_PARTS,
|
|
||||||
this.$route
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
resetDependentState() {
|
|
||||||
// Nothing additional to reset here: The page save is already fully resetting all the line-items on the order
|
|
||||||
},
|
|
||||||
|
|
||||||
LoadInitialPartsData() {
|
|
||||||
const partsData = this.PartsFromApi;
|
|
||||||
const alreadyPopulatedPartsData =
|
|
||||||
this.$store.getters.lineItems.glassParts === null ? {} :
|
|
||||||
this.$store.getters.lineItems.glassParts;
|
|
||||||
|
|
||||||
partsData.partsOrQuestions.map((g) => {
|
|
||||||
// If the part is already populated, use the value from the store and populate the v-model.
|
|
||||||
Object.keys(alreadyPopulatedPartsData).forEach((key) => {
|
|
||||||
const partNumber = alreadyPopulatedPartsData[key].partNumber;
|
|
||||||
g.parts.forEach((p) => {
|
|
||||||
if (p.partNumber === partNumber) {
|
|
||||||
this.glassParts[g.glassLocation + "-" + g.glassName] = {
|
|
||||||
[g.glassLocation]: [partNumber],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match them to the parts from the API.
|
||||||
|
for (let [key, value] of Object.entries(
|
||||||
|
this.PartsFromApi.partsOrQuestions
|
||||||
|
)) {
|
||||||
|
for (let [partKey, partValue] of Object.entries(value.parts)) {
|
||||||
|
const currentPart =
|
||||||
|
this.PartsFromApi.partsOrQuestions[key].parts[partKey];
|
||||||
|
const isMatched = selectedGlassPartNumbers.some(
|
||||||
|
(p) => p === currentPart.partNumber
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isMatched) {
|
||||||
|
matchedParts.push(currentPart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no parts could be matched, throw an error.
|
||||||
|
if (matchedParts.length === 0) {
|
||||||
|
throw new Error("Could not match any parts to the selected parts");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save parts to the store.
|
||||||
|
store.commit(storeMutations.UPDATE_PARTS, matchedParts);
|
||||||
|
|
||||||
|
// Navigate to the next page.
|
||||||
|
this.$router.navigateAfterSave(
|
||||||
|
this.navigationScenarios.SELECTED_PARTS,
|
||||||
|
this.$route
|
||||||
|
);
|
||||||
},
|
},
|
||||||
mounted(){
|
|
||||||
this.LoadInitialPartsData();
|
resetDependentState() {
|
||||||
}
|
// Nothing additional to reset here: The page save is already fully resetting all the line-items on the order
|
||||||
|
},
|
||||||
|
|
||||||
|
LoadInitialPartsData() {
|
||||||
|
const partsData = this.PartsFromApi;
|
||||||
|
const alreadyPopulatedPartsData =
|
||||||
|
this.$store.getters.lineItems.glassParts === null
|
||||||
|
? {}
|
||||||
|
: this.$store.getters.lineItems.glassParts;
|
||||||
|
|
||||||
|
partsData.partsOrQuestions.map((g) => {
|
||||||
|
// If the part is already populated, use the value from the store and populate the v-model.
|
||||||
|
Object.keys(alreadyPopulatedPartsData).forEach((key) => {
|
||||||
|
const partNumber = alreadyPopulatedPartsData[key].partNumber;
|
||||||
|
g.parts.forEach((p) => {
|
||||||
|
if (p.partNumber === partNumber) {
|
||||||
|
this.glassParts[g.glassLocation + "-" + g.glassName] = {
|
||||||
|
[g.glassLocation]: [partNumber],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.LoadInitialPartsData();
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,10 @@ export default {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
return store.getters.vehicle.model !== null;
|
if(store.getters.vehicle.model){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
resetDependentState() {
|
resetDependentState() {
|
||||||
// Invokes
|
// Invokes
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import store from "@/store";
|
||||||
import { storeActions } from "@/constants/store-actions.js";
|
import { storeActions } from "@/constants/store-actions.js";
|
||||||
import { storeMutations } from "@/constants/store-mutations.js";
|
import { storeMutations } from "@/constants/store-mutations.js";
|
||||||
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
|
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
|
||||||
import { widgetNames } from "@/constants/widget-names.js";
|
|
||||||
import { vehicleCategories } from "@/constants/vehicle-categories.js";
|
import { vehicleCategories } from "@/constants/vehicle-categories.js";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -52,9 +51,6 @@ export default {
|
||||||
navigationScenarios() {
|
navigationScenarios() {
|
||||||
return navigationScenarios;
|
return navigationScenarios;
|
||||||
},
|
},
|
||||||
widgetNames() {
|
|
||||||
return widgetNames;
|
|
||||||
},
|
|
||||||
vehicleCategories() {
|
vehicleCategories() {
|
||||||
return vehicleCategories;
|
return vehicleCategories;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import baseMixin from "@/mixins/base-mixin";
|
import baseMixin from "@/mixins/base-mixin";
|
||||||
import { storeActions } from "@/constants/store-actions.js";
|
import { storeActions } from "@/constants/store-actions.js";
|
||||||
import { widgetNames } from "@/constants/widget-names.js";
|
|
||||||
import { storeMutations } from "@/constants/store-mutations.js";
|
import { storeMutations } from "@/constants/store-mutations.js";
|
||||||
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
|
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
|
||||||
import { vehicleCategories } from "@/constants/vehicle-categories.js";
|
import { vehicleCategories } from "@/constants/vehicle-categories.js";
|
||||||
|
|
@ -67,18 +66,6 @@ describe("baseMixin.js", () => {
|
||||||
// Assert
|
// Assert
|
||||||
expect(navigationScenariosForTest).toEqual(navigationScenarios);
|
expect(navigationScenariosForTest).toEqual(navigationScenarios);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("computed: widgetNames should be equal to import object", () => {
|
|
||||||
// Arrange
|
|
||||||
const mixIn = getMixInInstance({});
|
|
||||||
|
|
||||||
// Act
|
|
||||||
let widgetNamesForTest = mixIn.computed.widgetNames();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(widgetNamesForTest).toEqual(widgetNames);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("computed: vehicleCategories should be equal to import object", () => {
|
test("computed: vehicleCategories should be equal to import object", () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const mixIn = getMixInInstance({});
|
const mixIn = getMixInInstance({});
|
||||||
|
|
@ -131,7 +118,6 @@ function getMixInInstance({ isDispatchSuccess = true }) {
|
||||||
const baseMixIn = baseMixin;
|
const baseMixIn = baseMixin;
|
||||||
baseMixIn.methods.$route = route;
|
baseMixIn.methods.$route = route;
|
||||||
baseMixIn.methods.storeActions = storeActions;
|
baseMixIn.methods.storeActions = storeActions;
|
||||||
baseMixIn.methods.widgetNames = widgetNames;
|
|
||||||
baseMixIn.methods.vehicleCategories = vehicleCategories;
|
baseMixIn.methods.vehicleCategories = vehicleCategories;
|
||||||
|
|
||||||
store.dispatch = storeDispatch;
|
store.dispatch = storeDispatch;
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,23 @@
|
||||||
// Supporting files
|
// Supporting files
|
||||||
import { createWebHistory, createRouter } from "vue-router";
|
import { createWebHistory, createRouter } from "vue-router";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
import baseMixin from "@/mixins/base-mixin";
|
|
||||||
import { lazyLoadComponent } from "@/router/dynamic-routing/component-loader.js";
|
import { lazyLoadComponent } from "@/router/dynamic-routing/component-loader.js";
|
||||||
import { routingTable } from "@/router/router-constants/routing-table.js";
|
import { routingTable } from "@/router/router-constants/routing-table.js";
|
||||||
import { globalEvents, globalEventTypes } from "@/constants/events";
|
import { globalEvents, globalEventTypes } from "@/constants/events";
|
||||||
|
|
||||||
|
// Heritage integration
|
||||||
|
import { isSavedSessionStillActive } from "@/helpers/heritage-integration/session-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 baseMixin from "@/mixins/base-mixin";
|
||||||
import eventBus from "@/helpers/event-bus/event-bus";
|
import eventBus from "@/helpers/event-bus/event-bus";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import ComponentTest from "@/layouts/component-test/component-test.vue";
|
import ComponentTest from "@/layouts/component-test/component-test.vue";
|
||||||
import AddressPOC from "@/layouts/address-poc/address-poc.vue";
|
|
||||||
import FormTest from "@/layouts/form-test/form-test.vue";
|
import FormTest from "@/layouts/form-test/form-test.vue";
|
||||||
import NestedRadio from "@/layouts/nested-radio-poc/nested-radio.vue";
|
|
||||||
import buttonQuestionExamples from "@/layouts/button-question-examples/button-question-examples.vue"
|
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
|
|
@ -26,26 +30,6 @@ const routes = [
|
||||||
name: "FormTest",
|
name: "FormTest",
|
||||||
component: FormTest,
|
component: FormTest,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "/address-poc", // This is a temporary route for testing.
|
|
||||||
name: "AddressPOC",
|
|
||||||
component: AddressPOC,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/form-test", // This is a temporary route for testing.
|
|
||||||
name: "FormTest",
|
|
||||||
component: FormTest,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/nested-radio", // This is a temporary route for testing.
|
|
||||||
name: "NestedRadio",
|
|
||||||
component: NestedRadio,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/button-question-examples", // This is a temporary route for testing.
|
|
||||||
name: "buttonQuestionExamples",
|
|
||||||
component: buttonQuestionExamples,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
name: "root",
|
name: "root",
|
||||||
|
|
@ -55,9 +39,35 @@ const routes = [
|
||||||
await GoToFunnelStartOn404(next);
|
await GoToFunnelStartOn404(next);
|
||||||
} else {
|
} 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 we already have our route, go to it.
|
||||||
if (router.hasRoute(to.query.fmgPage)) {
|
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.
|
// 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;
|
let component = router.getRoutes().filter((x) => x.name === to.query.fmgPage)[0].components;
|
||||||
|
|
||||||
|
|
@ -123,10 +133,14 @@ router.navigateAfterSave = (scenario, currentRoute, optionalQuery = {}, optional
|
||||||
navigate(scenario, currentRoute, true, optionalQuery, optionalParams, optionalPageData);
|
navigate(scenario, currentRoute, true, optionalQuery, optionalParams, optionalPageData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
router.navigateToExternalUrl = (url, optionalQuery = {}) => {
|
||||||
|
navigateToUrl(url, optionalQuery);
|
||||||
|
}
|
||||||
|
|
||||||
// PRIVATE FUNCTIONS
|
// PRIVATE FUNCTIONS
|
||||||
|
|
||||||
// Navigate to the next route, depending on the scenario.
|
// Navigate to the next route, depending on the scenario.
|
||||||
function navigate(scenario, currentRoute, invalidateOnSave, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) {
|
async function navigate(scenario, currentRoute, invalidateOnSave, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) {
|
||||||
if (!scenario) {
|
if (!scenario) {
|
||||||
console.error("No scenario provided. Please review the routing table.");
|
console.error("No scenario provided. Please review the routing table.");
|
||||||
return;
|
return;
|
||||||
|
|
@ -134,8 +148,9 @@ function navigate(scenario, currentRoute, invalidateOnSave, optionalQuery = {},
|
||||||
|
|
||||||
// Match our maps up and navigate if we have a destination.
|
// Match our maps up and navigate if we have a destination.
|
||||||
const matchingScenarioMap = getNavigationMap(scenario, currentRoute);
|
const matchingScenarioMap = getNavigationMap(scenario, currentRoute);
|
||||||
|
const destinationFmgPageValue = matchingScenarioMap.destinationFmgPageValue;
|
||||||
|
|
||||||
if (matchingScenarioMap.destinationFmgPageValue !== undefined) {
|
if (destinationFmgPageValue !== undefined) {
|
||||||
// We're always pushing the same path, just changing query strings. Make sure our optional query strings get combined with our fmgPage one.
|
// We're always pushing the same path, just changing query strings. Make sure our optional query strings get combined with our fmgPage one.
|
||||||
|
|
||||||
// If we need to do invalidation
|
// If we need to do invalidation
|
||||||
|
|
@ -146,17 +161,22 @@ 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.
|
// 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(matchingScenarioMap.destinationFmgPageValue, optionalPageData);
|
baseMixin.methods.savePageDataToStore(destinationFmgPageValue, optionalPageData);
|
||||||
|
|
||||||
|
// if cookie and referralNumber/Date exists
|
||||||
|
if (getFunnelCookie()?.ReferralNumber && getFunnelCookie()?.ReferralDate) {
|
||||||
|
await saveOrder();
|
||||||
|
}
|
||||||
|
|
||||||
router.push({
|
router.push({
|
||||||
name: "root",
|
name: "root",
|
||||||
query: Object.assign(optionalQuery, {
|
query: Object.assign(optionalQuery, {
|
||||||
fmgPage: matchingScenarioMap.destinationFmgPageValue,
|
fmgPage: destinationFmgPageValue,
|
||||||
}),
|
}),
|
||||||
params: optionalParams,
|
params: optionalParams
|
||||||
});
|
});
|
||||||
} else if (matchingScenarioMap.destinationUrl !== undefined) {
|
} else if (matchingScenarioMap.destinationUrl !== undefined) {
|
||||||
navigateToUrl(matchingScenarioMap.destinationUrl);
|
navigateToUrl(matchingScenarioMap.destinationUrl, optionalQuery);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,9 +197,15 @@ function getNavigationMap(scenario, currentRoute) {
|
||||||
//---------------------------------------------------------- Private Functions ----------------------------------------------------------
|
//---------------------------------------------------------- Private Functions ----------------------------------------------------------
|
||||||
|
|
||||||
// Navigate to an external url.
|
// Navigate to an external url.
|
||||||
function navigateToUrl(url) {
|
function navigateToUrl(url, optionalQuery = {}) {
|
||||||
// possibly show some loading screen in the future here.
|
// possibly show some loading screen in the future here.
|
||||||
window.location.assign(url);
|
var externalUrl = new URL(url);
|
||||||
|
|
||||||
|
for (const queryKey in optionalQuery) {
|
||||||
|
externalUrl.searchParams.append(queryKey, optionalQuery[queryKey]);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.location.assign(externalUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get route information by page name.
|
// Get route information by page name.
|
||||||
|
|
|
||||||
5
src/router/router-constants/externalUrl-values.js
Normal file
5
src/router/router-constants/externalUrl-values.js
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
const externalUrls = {
|
||||||
|
HERITAGE_FUNNEL: process.env.VUE_APP_HERITAGE_FUNNEL,
|
||||||
|
};
|
||||||
|
|
||||||
|
export { externalUrls };
|
||||||
|
|
@ -4,6 +4,7 @@ const navigationScenarios = {
|
||||||
SELECTED_MAKE: "SELECTED_MAKE",
|
SELECTED_MAKE: "SELECTED_MAKE",
|
||||||
SELECTED_STYLE: "SELECTED_STYLE",
|
SELECTED_STYLE: "SELECTED_STYLE",
|
||||||
CLICKED_BACK: "CLICKED_BACK",
|
CLICKED_BACK: "CLICKED_BACK",
|
||||||
|
CLICKED_FORWARD: "CLICKED_FORWARD",
|
||||||
SELECTED_PARTS: "SELECTED_PARTS",
|
SELECTED_PARTS: "SELECTED_PARTS",
|
||||||
SELECTED_DAMAGE_WITH_SINGLE_PART: "SELECTED_DAMAGE_WITH_SINGLE_PART",
|
SELECTED_DAMAGE_WITH_SINGLE_PART: "SELECTED_DAMAGE_WITH_SINGLE_PART",
|
||||||
SELECTED_DAMAGE_WITH_MULTIPLE_PARTS: "SELECTED_DAMAGE_WITH_MULTIPLE_PARTS",
|
SELECTED_DAMAGE_WITH_MULTIPLE_PARTS: "SELECTED_DAMAGE_WITH_MULTIPLE_PARTS",
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
const routerParameterKeys = { };
|
|
||||||
|
|
||||||
export { routerParameterKeys };
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
|
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
|
||||||
|
import { applicationConfig } from "@/constants/application-config";
|
||||||
|
|
||||||
const routingTable = [
|
const routingTable = [
|
||||||
{
|
{
|
||||||
|
|
@ -59,7 +60,7 @@ const routingTable = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
scenario: navigationScenarios.SELECTED_DAMAGE_WITH_SINGLE_PART,
|
scenario: navigationScenarios.SELECTED_DAMAGE_WITH_SINGLE_PART,
|
||||||
destinationFmgPageValue: fmgPageValues.REVEAL,
|
destinationFmgPageValue: fmgPageValues.REVEAL
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
scenario: navigationScenarios.SELECTED_DAMAGE_WITH_MULTIPLE_PARTS,
|
scenario: navigationScenarios.SELECTED_DAMAGE_WITH_MULTIPLE_PARTS,
|
||||||
|
|
@ -80,6 +81,10 @@ const routingTable = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
scenario: navigationScenarios.SELECTED_PARTS,
|
scenario: navigationScenarios.SELECTED_PARTS,
|
||||||
|
destinationFmgPageValue: fmgPageValues.QUOTE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.CLICKED_FORWARD,
|
||||||
destinationFmgPageValue: fmgPageValues.REVEAL,
|
destinationFmgPageValue: fmgPageValues.REVEAL,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
import { createStore } from "vuex";
|
import { createStore } from "vuex";
|
||||||
import { endpoints } from "@/constants/endpoints.js";
|
import { endpoints } from "@/constants/endpoints.js";
|
||||||
import { storeMutations } from "@/constants/store-mutations";
|
import { storeMutations } from "@/constants/store-mutations";
|
||||||
|
import { getDateForSavedSessionTimeout } from "@/helpers/heritage-integration/session-helper";
|
||||||
import createPersistedState from "vuex-persistedstate";
|
import createPersistedState from "vuex-persistedstate";
|
||||||
import globalMethods from "@/global-methods";
|
import globalMethods from "@/global-methods";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Export State
|
// Export State
|
||||||
export const state = {
|
export const state = {
|
||||||
order: {
|
order: {
|
||||||
|
|
@ -29,28 +28,35 @@ export const state = {
|
||||||
firstName: null,
|
firstName: null,
|
||||||
lastName: null,
|
lastName: null,
|
||||||
},
|
},
|
||||||
|
serviceLocation: {
|
||||||
|
zip: null,
|
||||||
|
},
|
||||||
|
customer: {
|
||||||
|
emailAddress: null,
|
||||||
|
},
|
||||||
|
damage: {
|
||||||
|
isRepair: null,
|
||||||
|
numberOfChips: null,
|
||||||
|
glassToReplace: null,
|
||||||
|
},
|
||||||
|
lineItems: {
|
||||||
|
glassParts: null,
|
||||||
|
otherParts: null
|
||||||
|
},
|
||||||
|
referralNumber: null,
|
||||||
|
referralDate: null,
|
||||||
|
referralCorrelationId: null,
|
||||||
|
parentAccountNumber: null,
|
||||||
},
|
},
|
||||||
serviceLocation: {
|
applicationUser: {
|
||||||
zip: null,
|
eventBus: [],
|
||||||
|
pageData: {},
|
||||||
|
savedSessionTimeout: getDateForSavedSessionTimeout()
|
||||||
},
|
},
|
||||||
customer: {
|
}
|
||||||
emailAddress: null,
|
};
|
||||||
},
|
|
||||||
damage: {
|
export const state = getDefaultState();
|
||||||
isRepair: null,
|
|
||||||
numberOfChips: null,
|
|
||||||
glassToReplace: null,
|
|
||||||
},
|
|
||||||
lineItems:{
|
|
||||||
glassParts: {},
|
|
||||||
otherParts: {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
applicationUser: {
|
|
||||||
eventBus: [],
|
|
||||||
pageData: {}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export Mutations
|
// Export Mutations
|
||||||
export const mutations = {
|
export const mutations = {
|
||||||
|
|
@ -88,46 +94,32 @@ export const mutations = {
|
||||||
updateIsRepair(state, isRepair){
|
updateIsRepair(state, isRepair){
|
||||||
state.order.damage.isRepair = isRepair;
|
state.order.damage.isRepair = isRepair;
|
||||||
},
|
},
|
||||||
updateNumberOfChips(state, numberOfChips){
|
updateNumberOfChips(state, numberOfChips) {
|
||||||
state.order.damage.numberOfChips = numberOfChips;
|
state.order.damage.numberOfChips = numberOfChips;
|
||||||
},
|
},
|
||||||
updateGlassToReplace(state, glassToReplace){
|
updateGlassToReplace(state, glassToReplace) {
|
||||||
state.order.damage.glassToReplace = glassToReplace;
|
state.order.damage.glassToReplace = glassToReplace;
|
||||||
},
|
},
|
||||||
updateParts(state, partsData){
|
updateParts(state, partsData) {
|
||||||
state.order.lineItems.glassParts = partsData;
|
state.order.lineItems.glassParts = partsData;
|
||||||
},
|
},
|
||||||
updatePageData(state, pageData){
|
updatePageData(state, pageData) {
|
||||||
state.applicationUser.pageData[pageData.page] = pageData.data;
|
state.applicationUser.pageData[pageData.page] = pageData.data;
|
||||||
},
|
},
|
||||||
updateRegistrationAddress(state, registrationAddress){
|
updateReferralCorrelationId(state, referralCorrelationId) {
|
||||||
state.order.vehicle.registration.address = registrationAddress;
|
state.order.referralCorrelationId = referralCorrelationId;
|
||||||
},
|
},
|
||||||
updateRegistrationCity(state, registrationCity){
|
updateReferralNumber(state, referralNumber) {
|
||||||
state.order.vehicle.registration.city = registrationCity;
|
state.order.referralNumber = referralNumber;
|
||||||
},
|
},
|
||||||
updateRegistrationState(state, registrationState){
|
updateReferralDate(state, referralDate) {
|
||||||
state.order.vehicle.registration.state = registrationState;
|
state.order.referralDate = referralDate;
|
||||||
},
|
},
|
||||||
updateRegistrationZipCode(state, registrationZipCode){
|
updateParentAcctNumber(state, parentAcctNumber) {
|
||||||
state.order.vehicle.registration.zipCode = registrationZipCode;
|
state.order.parentAccountNumber = parentAcctNumber;
|
||||||
},
|
|
||||||
updateRegistrationFirstName(state, registrationFirstName){
|
|
||||||
state.order.vehicle.registration.firstName = registrationFirstName;
|
|
||||||
},
|
|
||||||
updateRegistrationLastName(state, registrationLastName){
|
|
||||||
state.order.vehicle.registration.lastName = registrationLastName;
|
|
||||||
},
|
|
||||||
updateRegistrationLicensePlate(state, registrationLicensePlate){
|
|
||||||
state.order.vehicle.registration.licensePlate = registrationLicensePlate;
|
|
||||||
},
|
|
||||||
updateServiceLocationZip(state, serviceLocationZip){
|
|
||||||
state.order.vehicle.serviceLocation.zip = serviceLocationZip;
|
|
||||||
},
|
|
||||||
updateCustomerEmailAddress(state, customerEmailAddress){
|
|
||||||
state.order.vehicle.customer.emailAddress = customerEmailAddress;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
// EVENT BUS MUTATIONS
|
// EVENT BUS MUTATIONS
|
||||||
addEventToBus(state, event) {
|
addEventToBus(state, event) {
|
||||||
state.applicationUser.eventBus.push(event);
|
state.applicationUser.eventBus.push(event);
|
||||||
|
|
@ -172,6 +164,33 @@ export const mutations = {
|
||||||
resetPartsState(state) {
|
resetPartsState(state) {
|
||||||
state.order.lineItems.glassParts = null;
|
state.order.lineItems.glassParts = null;
|
||||||
state.order.lineItems.otherParts = null;
|
state.order.lineItems.otherParts = null;
|
||||||
|
},
|
||||||
|
resetState(state) {
|
||||||
|
Object.assign(state, getDefaultState());
|
||||||
|
},
|
||||||
|
|
||||||
|
// Misc Mutations
|
||||||
|
updateStateWithOrderInformation(state, orderInformation) {
|
||||||
|
state.order.referralNumber = orderInformation.referralNumber;
|
||||||
|
state.order.referralDate = orderInformation.referralDate;
|
||||||
|
state.order.referralCorrelationId = orderInformation.referralCorrelationId;
|
||||||
|
state.order.vehicle = {
|
||||||
|
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.serviceLocation.zipCode = orderInformation.zipCode; // TODO CSR-416 Make sure this is correct
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -191,7 +210,9 @@ export const getters = {
|
||||||
lineItems: (state) => state.order.lineItems,
|
lineItems: (state) => state.order.lineItems,
|
||||||
pageData: (state) => (page) => {
|
pageData: (state) => (page) => {
|
||||||
return state.applicationUser.pageData[page];
|
return state.applicationUser.pageData[page];
|
||||||
}
|
},
|
||||||
|
applicationUser: (state) => state.applicationUser,
|
||||||
|
order: (state) => state.order,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export Actions
|
// Export Actions
|
||||||
|
|
@ -298,6 +319,9 @@ export const actions = {
|
||||||
resetPartsAndDependencies(context) {
|
resetPartsAndDependencies(context) {
|
||||||
context.commit(storeMutations.RESET_PARTS_STATE);
|
context.commit(storeMutations.RESET_PARTS_STATE);
|
||||||
},
|
},
|
||||||
|
resetState(context) {
|
||||||
|
context.commit(storeMutations.RESET_STATE);
|
||||||
|
},
|
||||||
|
|
||||||
// Content API Actions
|
// Content API Actions
|
||||||
getRouteInfo(context, { pageName }) {
|
getRouteInfo(context, { pageName }) {
|
||||||
|
|
@ -330,6 +354,13 @@ export const actions = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Misc Actions
|
||||||
|
setReferralInformation(context, { referralNumber, referralDate, referralCorrelationId }) {
|
||||||
|
context.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber);
|
||||||
|
context.commit(storeMutations.UPDATE_REFERRAL_DATE, referralDate);
|
||||||
|
context.commit(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, referralCorrelationId);
|
||||||
|
},
|
||||||
|
|
||||||
// Parts API Actions
|
// Parts API Actions
|
||||||
getPartsOrQuestions(context, { carId, glassArray, zipCode, vin = '' }) {
|
getPartsOrQuestions(context, { carId, glassArray, zipCode, vin = '' }) {
|
||||||
return globalMethods.callHttpClient({
|
return globalMethods.callHttpClient({
|
||||||
|
|
@ -342,6 +373,46 @@ export const actions = {
|
||||||
vin: vin
|
vin: vin
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Order API Actions
|
||||||
|
saveOrder(context) {
|
||||||
|
const vehicle = context.getters.vehicle;
|
||||||
|
const damage = context.getters.damage;
|
||||||
|
|
||||||
|
return globalMethods.callHttpClient({
|
||||||
|
method: endpoints.SaveOrder.method,
|
||||||
|
endpoint: endpoints.SaveOrder.url,
|
||||||
|
payload: {
|
||||||
|
vehicle: {
|
||||||
|
carId: vehicle.carId,
|
||||||
|
year: vehicle.year,
|
||||||
|
make: vehicle.make,
|
||||||
|
model: vehicle.model,
|
||||||
|
style: vehicle.style,
|
||||||
|
},
|
||||||
|
numberOfChips: damage.numberOfChips,
|
||||||
|
zipCode: 43215, // TODO CSR-416, should not be hardcoded (state.order.serviceLocation.zipCode)
|
||||||
|
glassToReplace: damage.glassToReplace,
|
||||||
|
referralNumber: context.state.order.referralNumber,
|
||||||
|
referralDate: context.state.order.referralDate
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
loadOrder(context, { referralNumber, referralDate, referralCorrelationId }) {
|
||||||
|
return globalMethods.callHttpClient({
|
||||||
|
method: endpoints.LoadOrder.method,
|
||||||
|
endpoint: endpoints.LoadOrder.url,
|
||||||
|
payload: {
|
||||||
|
referralNumber: referralNumber,
|
||||||
|
referralDate: referralDate,
|
||||||
|
referralCorrelationId: referralCorrelationId
|
||||||
|
},
|
||||||
|
}).then((response) => {
|
||||||
|
context.commit(storeMutations.SET_LOAD_FUNNEL_SESSION_INFO, response.data);
|
||||||
|
return response;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -357,3 +428,5 @@ export default createStore({
|
||||||
getters,
|
getters,
|
||||||
actions,
|
actions,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Private Functions
|
||||||
|
|
|
||||||
|
|
@ -200,6 +200,38 @@ describe("Mutations", () => {
|
||||||
expect(storeState.applicationUser.pageData['vehicle-year']).toEqual({});
|
expect(storeState.applicationUser.pageData['vehicle-year']).toEqual({});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("updateStateWithOrderInformation, should set order information in state", () => {
|
||||||
|
// Arrange
|
||||||
|
const storeState = state;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
mutations.updateStateWithOrderInformation(storeState, {
|
||||||
|
referralNumber: 123,
|
||||||
|
referralDate: new Date().toUTCString(),
|
||||||
|
referralCorrelationId: "xxx-xxx-xxx",
|
||||||
|
vehicle: {
|
||||||
|
year: "2019",
|
||||||
|
make: "Acura",
|
||||||
|
model: "ILX",
|
||||||
|
style: "4 DOOR SEDAN",
|
||||||
|
carId: "C0000001",
|
||||||
|
category: "CAR"
|
||||||
|
},
|
||||||
|
glassToReplace: ["Windshield"],
|
||||||
|
isRepair: false,
|
||||||
|
numberOfChips: 0,
|
||||||
|
parts: [],
|
||||||
|
parentAccountNumber: "123456789",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(storeState.order.referralNumber).toEqual(123);
|
||||||
|
expect(storeState.order.referralCorrelationId).toEqual("xxx-xxx-xxx");
|
||||||
|
expect(storeState.order.vehicle.year).toEqual("2019");
|
||||||
|
expect(storeState.order.vehicle.make).toEqual("Acura");
|
||||||
|
expect(storeState.order.vehicle.model).toEqual("ILX");
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Actions", () => {
|
describe("Actions", () => {
|
||||||
|
|
@ -466,6 +498,66 @@ describe("Actions", () => {
|
||||||
expect(response.data).toEqual({ imageUrl: "https://test.com" });
|
expect(response.data).toEqual({ imageUrl: "https://test.com" });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("saveOrder action, returns order information", async () => {
|
||||||
|
// Arrange
|
||||||
|
const context = state;
|
||||||
|
|
||||||
|
context.getters = {
|
||||||
|
vehicle: {},
|
||||||
|
damage: {},
|
||||||
|
};
|
||||||
|
context.state = {
|
||||||
|
order: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
globalMethods.callHttpClient.mockImplementation(() => {
|
||||||
|
return Promise.resolve({ data: { referralNumber: 123 } });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const response = await actions.saveOrder(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(response.data).toEqual({ referralNumber: 123 });
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it("loadOrder action, returns order information, calls mutation", async () => {
|
||||||
|
// Arrange
|
||||||
|
const context = state;
|
||||||
|
|
||||||
|
globalMethods.callHttpClient.mockImplementation(() => {
|
||||||
|
return Promise.resolve({ data: { referralNumber: 123 } });
|
||||||
|
});
|
||||||
|
|
||||||
|
const commit = jest.fn();
|
||||||
|
|
||||||
|
context.commit = commit;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const response = await actions.loadOrder(context, {referralNumber: "123", referralDate: new Date().toUTCString(), referralCorrelationId: "xxx-xxx-xxx"});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(response.data).toEqual({ referralNumber: 123 });
|
||||||
|
expect(commit).toBeCalledWith(storeMutations.SET_LOAD_FUNNEL_SESSION_INFO, {"referralNumber": 123});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("setReferralInformation, should call commit three times", () => {
|
||||||
|
// Arrange
|
||||||
|
const context = state;
|
||||||
|
const commit = jest.fn();
|
||||||
|
|
||||||
|
context.commit = commit;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
actions.setReferralInformation(context, {referralNumber: "123", referralDate: new Date().toUTCString(), referralCorrelationId: "xxx-xxx-xxx"});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(commit).toBeCalledWith(storeMutations.UPDATE_REFERRAL_NUMBER, "123");
|
||||||
|
expect(commit).toBeCalledWith(storeMutations.UPDATE_REFERRAL_DATE, new Date().toUTCString());
|
||||||
|
expect(commit).toBeCalledWith(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, "xxx-xxx-xxx");
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Getters", () => {
|
describe("Getters", () => {
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,19 @@
|
||||||
&.list-card {
|
&.list-card {
|
||||||
border: 1px solid $red;
|
border: 1px solid $red;
|
||||||
color: $red;
|
color: $red;
|
||||||
label {
|
|
||||||
box-shadow: 0 0 1px $red !important;
|
|
||||||
border-radius: .5rem;
|
|
||||||
}
|
|
||||||
input[type=checkbox]:focus + label,
|
input[type=checkbox]:focus + label,
|
||||||
input[type=radio]:focus + label {
|
input[type=radio]:focus + label {
|
||||||
box-shadow: 0 0 0 2.5px $red;
|
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=checkbox]:checked + label,
|
||||||
|
|
@ -21,6 +26,9 @@
|
||||||
color: $red;
|
color: $red;
|
||||||
label {
|
label {
|
||||||
border: 1px solid $red;
|
border: 1px solid $red;
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0px 0px 0px 4px $red-200;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
input[type=checkbox]:focus + label,
|
input[type=checkbox]:focus + label,
|
||||||
input[type=radio]:focus + label {
|
input[type=radio]:focus + label {
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,9 @@ export default {
|
||||||
@media (hover: hover) {
|
@media (hover: hover) {
|
||||||
background: linear-gradient(270deg, $blue-500 0%, $blue-700 100%);
|
background: linear-gradient(270deg, $blue-500 0%, $blue-700 100%);
|
||||||
}
|
}
|
||||||
|
&:focus {
|
||||||
|
box-shadow: 0 0 0 3px, 0 0 0 5.5px $blue-700;
|
||||||
|
}
|
||||||
&:focus, // Mouse, touch, stylus focus
|
&:focus, // Mouse, touch, stylus focus
|
||||||
&:focus-visible {
|
&:focus-visible {
|
||||||
// Keyboard focus for accessibility
|
// Keyboard focus for accessibility
|
||||||
|
|
@ -91,6 +94,7 @@ export default {
|
||||||
&.has-loader {
|
&.has-loader {
|
||||||
color: $white;
|
color: $white;
|
||||||
background: $blue-700;
|
background: $blue-700;
|
||||||
|
box-shadow: 0 0 0 3px, 0 0 0 5.5px $blue-700;
|
||||||
}
|
}
|
||||||
&.delay {// fixes flicker while transitioning between states
|
&.delay {// fixes flicker while transitioning between states
|
||||||
transition: background 0s 0s ease-in-out;
|
transition: background 0s 0s ease-in-out;
|
||||||
|
|
|
||||||
|
|
@ -161,6 +161,7 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
label {
|
label {
|
||||||
|
outline: none;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: $white;
|
background: $white;
|
||||||
transition: all 150ms linear;
|
transition: all 150ms linear;
|
||||||
|
|
|
||||||
|
|
@ -128,9 +128,9 @@ export default {
|
||||||
fieldOptions['initialValue'] = fieldOptions.potentialInitialValue;
|
fieldOptions['initialValue'] = fieldOptions.potentialInitialValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
checked,
|
checked,
|
||||||
handleChange,
|
handleChange,
|
||||||
errors,
|
errors,
|
||||||
} = useField(props.groupName, props.validationRules, fieldOptions);
|
} = useField(props.groupName, props.validationRules, fieldOptions);
|
||||||
|
|
||||||
|
|
@ -147,6 +147,7 @@ export default {
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.list-group {
|
.list-group {
|
||||||
&.list-button {
|
&.list-button {
|
||||||
|
outline: none;
|
||||||
input[type="radio"],
|
input[type="radio"],
|
||||||
input[type="checkbox"] {
|
input[type="checkbox"] {
|
||||||
position: static; //override bootstrap
|
position: static; //override bootstrap
|
||||||
|
|
@ -183,6 +184,7 @@ export default {
|
||||||
border-radius: $border-radius-lg;
|
border-radius: $border-radius-lg;
|
||||||
border: 1px solid $gray-500;
|
border: 1px solid $gray-500;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
span {
|
span {
|
||||||
&.small {
|
&.small {
|
||||||
|
|
|
||||||
|
|
@ -192,6 +192,7 @@ export default {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
||||||
+ label {
|
+ label {
|
||||||
|
outline: none;
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
|
@ -219,13 +220,13 @@ export default {
|
||||||
box-shadow: 0 0 0 2.5px $blue;
|
box-shadow: 0 0 0 2.5px $blue;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:checked {
|
&:checked {
|
||||||
&:focus + label {
|
&:focus + label {
|
||||||
box-shadow: 0 0 0 2.5px $blue;
|
box-shadow: 0 0 0 2.5px $blue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:checked + label {
|
&:checked + label {
|
||||||
p {
|
p {
|
||||||
color: $black;
|
color: $black;
|
||||||
|
|
@ -331,6 +332,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
+ label {
|
+ label {
|
||||||
|
outline: none;
|
||||||
min-height: 48px;
|
min-height: 48px;
|
||||||
color: $gray-600;
|
color: $gray-600;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
process.env.VUE_APP_CONSUMER_API_GATEWAY =
|
process.env.VUE_APP_CONSUMER_API_GATEWAY =
|
||||||
"https://consumerapidev.safelite.com";
|
"https://consumerapidev.safelite.com";
|
||||||
|
process.env.VUE_APP_HERITAGE_FUNNEL =
|
||||||
|
"http://localhost:38000/default.aspx";
|
||||||
process.env.VUE_APP_GOOGLE_PLACES_API_KEY =
|
process.env.VUE_APP_GOOGLE_PLACES_API_KEY =
|
||||||
"AIzaSyDptGCkOPgN2uWJOy4ou4M33phRD4MAoJo"
|
"AIzaSyDptGCkOPgN2uWJOy4ou4M33phRD4MAoJo"
|
||||||
|
|
||||||
|
|
@ -22,4 +23,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
configureWebpack: {
|
||||||
|
devtool: 'source-map'
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
process.env.VUE_APP_CONSUMER_API_GATEWAY = "__VUE_APP_CONSUMER_API_GATEWAY__";
|
process.env.VUE_APP_CONSUMER_API_GATEWAY = "__VUE_APP_CONSUMER_API_GATEWAY__";
|
||||||
process.env.VUE_APP_GOOGLE_PLACES_API_KEY = "__VUE_APP_GOOGLE_PLACES_API_KEY__";
|
process.env.VUE_APP_GOOGLE_PLACES_API_KEY = "__VUE_APP_GOOGLE_PLACES_API_KEY__";
|
||||||
|
process.env.VUE_APP_HERITAGE_FUNNEL = "__VUE_APP_HERITAGE_FUNNEL__";
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
outputDir: "dist/fmg",
|
outputDir: "dist/fmg",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue