@@ -22,6 +22,7 @@ import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-he
// Supporting files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { settleAllPromises } from "@/helpers/layout-helper";
+import eventBus from "@/helpers/event-bus/event-bus";
export default {
name: "vehicle-year",
@@ -32,11 +33,11 @@ export default {
},
computed: {},
- beforeRouteEnter(to, from, next) {
-
+ async beforeRouteEnter(to, from, next) {
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
- const yearQuestionInitialDataPromise = yearQuestion.methods.loadInitialData();
+ const yearQuestionInitialDataPromise =
+ yearQuestion.methods.loadInitialData();
// Settle promises and get results
const promiseResultMap = [
@@ -49,24 +50,36 @@ export default {
promise: yearQuestionInitialDataPromise,
},
];
- settleAllPromises(promiseResultMap).then((resultMap) => {
- // Call the "next" function to complete the transition to this page.
- next((vm) => {
- vm.$refs.funnelSubHeader.initializeComponent(resultMap.cmsContent.FunnelSubHeaderWidget);
- vm.$refs.funnelHeader.initializeComponent(resultMap.cmsContent.FunnelHeaderWidget);
- vm.$refs.vehicleBanner.initializeComponent(resultMap.cmsContent.VehicleBannerWidget);
- vm.$refs.yearQuestion.initializeComponent(resultMap.cmsContent.VehicleYearQuestion, resultMap.yearQuestionInitialData);
- });
+
+ let resultMap = await settleAllPromises(promiseResultMap);
+
+ // Call the "next" function to complete the transition to this page.
+ next((vm) => {
+ vm.$refs.funnelSubHeader.initializeComponent(
+ resultMap.cmsContent.FunnelSubHeaderWidget
+ );
+ vm.$refs.funnelHeader.initializeComponent(
+ resultMap.cmsContent.FunnelHeaderWidget
+ );
+ vm.$refs.vehicleBanner.initializeComponent(
+ resultMap.cmsContent.VehicleBannerWidget
+ );
+ vm.$refs.yearQuestion.initializeComponent(
+ resultMap.cmsContent.VehicleYearQuestion,
+ resultMap.yearQuestionInitialData
+ );
});
},
watch: {
selectedYear(year) {
this.$store.commit(this.storeMutations.UPDATE_YEAR, year);
- this.$router.navigate(this.navigationScenarios.SELECTED_YEAR, this.$route);
- }
+ this.$router.navigate(
+ this.navigationScenarios.SELECTED_YEAR,
+ this.$route
+ );
+ },
},
-
components: {
yearQuestion,
funnelHeader,
diff --git a/src/layouts/vehicle-year/year-question/year-question.vue b/src/layouts/vehicle-year/year-question/year-question.vue
index 98538a361..523a25bb3 100644
--- a/src/layouts/vehicle-year/year-question/year-question.vue
+++ b/src/layouts/vehicle-year/year-question/year-question.vue
@@ -15,7 +15,6 @@ import buttonQuestion from "@/common-components/button-question/button-question"
// Supporting files
import { storeActions } from "@/constants/store-actions.js";
import baseMixin from "@/mixins/base-mixin.js";
-
export default {
name: "year-question",
data() {
@@ -45,4 +44,4 @@ export default {
}
},
};
-
+
\ No newline at end of file
diff --git a/src/main.js b/src/main.js
index c78696333..322374cfd 100644
--- a/src/main.js
+++ b/src/main.js
@@ -14,4 +14,5 @@ vueApp.use(store);
vueApp.use(LoadScript);
vueApp.mixin(baseMixin);
+
vueApp.mount("#app");
diff --git a/src/mixins/base-mixin.js b/src/mixins/base-mixin.js
index a31a5368f..bfa9a5d7e 100644
--- a/src/mixins/base-mixin.js
+++ b/src/mixins/base-mixin.js
@@ -1,7 +1,7 @@
import store from "@/store";
import { storeActions } from "@/constants/store-actions.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";
export default {
diff --git a/src/router/index.js b/src/router/index.js
index 7633f9d1a..2a80032f6 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -1,19 +1,18 @@
+// Supporting files
import { createWebHistory, createRouter } from "vue-router";
-import { storeActions } from "@/constants/store-actions.js";
+import { storeActions } from "@/constants/store-actions";
import { lazyLoadComponent } from "@/router/dynamic-routing/component-loader.js";
import { routingTable } from "@/router/router-constants/routing-table.js";
-import ComponentTest from "@/layouts/component-test/component-test.vue";
-import FormTest from "@/layouts/form-test/form-test.vue";
-import AddressPOC from "@/layouts/address-poc/address-poc.vue";
-import NotFound from "@/layouts/not-found/not-found.vue";
+import { globalEvents, globalEventTypes } from "@/constants/events";
+import eventBus from "@/helpers/event-bus/event-bus";
import store from "@/store";
+// Components
+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";
+
const routes = [
- {
- path: "/:pathMatch(.*)*",
- component: NotFound,
- name: "NotFound",
- },
{
path: "/component-test", // This is a temporary route for testing.
name: "ComponentTest",
@@ -29,44 +28,45 @@ const routes = [
name: "AddressPOC",
component: AddressPOC,
},
+ {
+ path: "/form-test", // This is a temporary route for testing.
+ name: "FormTest",
+ component: FormTest,
+ },
{
path: "/",
- beforeEnter(to, from, next) {
+ async beforeEnter(to, from, next) {
// If we have no query string, or we don't have the FmgPage query string.
if (to.query.fmgPage === undefined) {
- RetainStructureAndGoTo404(to, next);
+ await GoToFunnelStartOn404(next);
} else {
- // If we already have our route, go to it.
- if (router.hasRoute(to.query.fmgPage)) {
- return next({
- name: to.query.fmgPage,
- query: to.query,
+
+ try {
+ // If we already have our route, go to it.
+ if (router.hasRoute(to.query.fmgPage)) {
+ return next({ name: to.query.fmgPage, query: to.query });
+ }
+
+ // Get route info for the given url. Names will have a 1:1 relationship with names in the Cms.
+ const routeData = await GetRouteInfoFromPageName(to.query.fmgPage);
+
+ // Add our dynamic route.
+ router.addRoute({
+ path: routeData[0].path, // Always the same path, because we control it with query strings.
+ name: routeData[0].name,
+ component: routeData[0].component,
});
+
+ // Assign current query string parameters, as well as our fmgPage one.
+ next({ name: routeData[0].name, query: Object.assign(to.query, { fmgPage: routeData[0].name }) });
+
+ } catch (error) {
+ console.log(error);
+
+ // If we don't have a route, go to our 404 page.
+ await GoToFunnelStartOn404(next);
}
- // Get route info for the given url. Names will have a 1:1 relationship with names in the Cms.
- GetRouteInfoFromPageName(to.query.fmgPage)
- .then((routeData) => {
- // Add our dynamic route.
- router.addRoute({
- path: routeData[0].path, // Always the same path, because we control it with query strings.
- name: routeData[0].name,
- component: routeData[0].component,
- });
-
- // Assign current query string parameters, as well as our fmgPage one.
- next({
- name: routeData[0].name,
- query: Object.assign(to.query, { fmgPage: routeData[0].name }),
- });
- })
- .catch((error) => {
- // If we can't find the route, go to the 404 page.
- RetainStructureAndGoTo404(to, next);
-
- console.log("error:");
- console.log(error);
- });
}
},
},
@@ -108,7 +108,7 @@ router.navigate = (
}
};
-// Get navigation map depeding on the scenario and the current 'page' you're on.
+// Get navigation map depending on the scenario and the current 'page' you're on.
router.getNavigationMap = (scenario, currentRoute) => {
const fmgPageValue = currentRoute.query.fmgPage;
const matchedQueryValue = routingTable
@@ -132,38 +132,44 @@ function navigateToUrl(url) {
// Get route information by page name.
// This will reach out to the Cms and there is a 1:1 relationship between page names and route names.
-function GetRouteInfoFromPageName(pageName) {
- return new Promise((resolve, reject) => {
- store
- .dispatch(storeActions.GET_ROUTE_INFO_ACTION, { pageName: pageName })
- .then((response) => {
- // Add our route data and return our array.
- let jsonFromResponse = JSON.parse(response.data.Result);
- let routeData = [];
+async function GetRouteInfoFromPageName(pageName) {
+ const response = await store.dispatch(storeActions.GET_ROUTE_INFO_ACTION, { pageName: pageName });
+ const jsonFromResponse = JSON.parse(response.data.Result);
+ let routeData = [];
- Object.keys(jsonFromResponse).forEach((key) => {
- routeData.push({
- path: "/",
- name: `${key}`,
- component: lazyLoadComponent(jsonFromResponse[key].LayoutName),
- });
- });
-
- resolve(routeData);
- })
- .catch((error) => {
- reject(error);
- });
+ // Add our route data and return our array.
+ Object.keys(jsonFromResponse).forEach((key) => {
+ routeData.push({
+ path: "/",
+ name: `${key}`,
+ component: lazyLoadComponent(jsonFromResponse[key].LayoutName),
+ });
});
+
+ return routeData;
}
-// Go to our 404 page but retain our structure when we go there (path, queryString, hash).
-function RetainStructureAndGoTo404(to, next) {
+// Go to our start page on a 404.
+async function GoToFunnelStartOn404(next) {
+
+ const apiResponse = await store.dispatch(storeActions.GET_HOMEPAGE_NAME);
+ const homepageName = apiResponse.data.Result;
+
+ // Put item on the bus
+ eventBus.addEventToBus(
+ globalEvents.Categories.GLOBAL_ALERT,
+ globalEvents.SubCategories.PAGE_NOT_FOUND,
+ {
+ isDismissible: true,
+ messageCopy: 'You can get a quote by starting on this page.',
+ messageHeadline: 'We\'re sorry, something went wrong.',
+ type: globalEventTypes.Danger
+ }
+ );
+
next({
- name: "NotFound",
- params: { pathMatch: to.path.split("/").slice(1) },
- query: to.query,
- hash: to.hash,
+ path: '/',
+ query: { fmgPage: homepageName },
});
}
diff --git a/src/store/index.js b/src/store/index.js
index f27a362b5..f3937726a 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -3,13 +3,10 @@ import { endpoints } from "@/constants/endpoints.js";
import { storeMutations } from "@/constants/store-mutations"
import createPersistedState from "vuex-persistedstate";
import globalMethods from "@/global-methods";
+import eventBus from "../helpers/event-bus/event-bus";
export default createStore({
- plugins: [
- createPersistedState({
- storage: window.sessionStorage,
- }),
- ],
+ plugins: [createPersistedState()],
// IMPORTANT: Be VERY careful when modifying these fields for at least a few reasons:
// * The CMS can reference the fields by name
@@ -58,6 +55,7 @@ export default createStore({
},
applicationUser: {
experiments: null,
+ eventBus: []
},
},
// See IMPORTANT note at top of "state" declaration.
@@ -78,10 +76,28 @@ export default createStore({
updateVehicle(state, data) {
state.order.vehicle.carId = data.carId;
state.order.vehicle.category = data.category;
+ },
+ addEventToBus(state, event) {
+ state.applicationUser.eventBus.push(event);
+ },
+ removeEventFromBus(state, eventData) {
+ const matchedEvent = state.applicationUser.eventBus.find(({ category, subCategory }) => category === eventData.category && subCategory === eventData.subCategory);
+ const itemIndex = state.applicationUser.eventBus.indexOf(matchedEvent);
+
+ // If the item exists, remove it.
+ if (itemIndex > -1) {
+ state.applicationUser.eventBus.splice(itemIndex, 1);
+ }
}
},
getters: {
- vehicle: state => state.order.vehicle
+ vehicle: state => state.order.vehicle,
+ eventBusItem: (state) => (eventCategory, eventSubCategory) => {
+ const matchedEvent = state.applicationUser.eventBus.find(({ category, subCategory }) => category === eventCategory && subCategory === eventSubCategory);
+
+ return matchedEvent !== undefined ? matchedEvent.eventValue : undefined;
+ },
+ eventBus: state => state.applicationUser.eventBus
},
actions: {
// Vehicle API Actions
@@ -157,6 +173,12 @@ export default createStore({
},
});
},
+ getHomepageName(context) {
+ return globalMethods.callHttpClient({
+ method: endpoints.GetHomepageInfo.method,
+ endpoint: endpoints.GetHomepageInfo.url,
+ });
+ },
getPageData(context, { pageName }) {
return globalMethods.callHttpClient({
method: endpoints.GetPageData.method,
diff --git a/src/store/store.spec.js b/src/store/store.spec.js
index 68de66b5e..0702f625d 100644
--- a/src/store/store.spec.js
+++ b/src/store/store.spec.js
@@ -253,10 +253,51 @@ describe("Mutations", () => {
expect(store.state.order.vehicle.carId).toBe("123abc");
expect(store.state.order.vehicle.category).toBe("car");
});
+
+ it("Should add event onto bus and update state", () => {
+ // Arrange
+ const event = {category: 'TestCategoryOne', subCategory: 'TestSubCategoryOne', eventValue: 'TestEventValueOne'};
+
+ // Act
+ store.commit("addEventToBus", event);
+
+ //Assert
+ expect(store.state.applicationUser.eventBus[0].category).toBe('TestCategoryOne');
+ expect(store.state.applicationUser.eventBus[0].subCategory).toBe('TestSubCategoryOne');
+ expect(store.state.applicationUser.eventBus[0].eventValue).toBe('TestEventValueOne');
+ });
});
describe("Getters", () => {
- const vehicle = store.getters.vehicle;
- expect(typeof vehicle).toBe('object');
+ it("Should validate vehicle getter", () => {
+ // Arrange
+ const vehicle = store.getters.vehicle;
+
+ // Assert
+ expect(typeof vehicle).toBe('object');
+ });
+
+ it("Should get item from bus via getter", () => {
+ // Arrange
+ const event = {category: 'TestCategoryOne', subCategory: 'TestSubCategoryOne', eventValue: 'TestEventValueOne'};
+
+ // Act
+ store.commit("addEventToBus", event)
+
+ // Assert
+ const returnedEventValue = store.getters.eventBusItem(event.category, event.subCategory);
+ expect(returnedEventValue).toBe('TestEventValueOne');
+ });
+
+ it("Should get eventbus from getter, should have length > 0", () => {
+ // Arrange
+ const event = {category: 'TestCategoryOne', subCategory: 'TestSubCategoryOne', eventValue: 'TestEventValueOne'};
+
+ // Act
+ store.commit("addEventToBus", event);
+
+ //Assert
+ expect(store.getters.eventBus.length).toBeGreaterThan(0);
+ });
});