Merge pull request #157 from Safelite/feature/CSR-130

Feature/csr 130
This commit is contained in:
Frank Rua 2022-01-20 14:17:08 -05:00 committed by GitHub
commit 882667c8f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 425 additions and 172 deletions

View file

@ -1,33 +1,64 @@
<template> <template>
<div <div class="funnel-header d-flex justify-content-center align-items-center flex-column" v-if="imageSrc">
class="funnel-header d-flex justify-content-center align-items-center"
v-if="imageSrc"
>
<img class="logo-image img-fluid" :src="imageSrc" alt="Safelite logo" /> <img class="logo-image img-fluid" :src="imageSrc" alt="Safelite logo" />
</div> </div>
<alert class="position-absolute rounded-0 w-100 border-0 shadow-sm" v-if="displayGlobalAlert" :alertClass="globalAlertMessage.type" :alertHeadline="globalAlertMessage.messageCopy" :alertCopy="globalAlertMessage.messageHeadline" v-bind:isDismissible="globalAlertMessage.isDismissible" />
</template> </template>
<script> <script>
import alert from "@/ux-components/alert/alert";
import eventBus from "@/helpers/event-bus/event-bus";
import {
globalEvents,
} from "@/constants/events";
export default { export default {
name: "funnel-header", name: "funnel-header",
data() { data() {
return { return {
imageSrc: '', imageSrc: "",
} displayGlobalAlert: false,
}, globalAlertMessage: {
methods: { isDismissible: false,
initializeComponent(cmsContent) { messageCopy: "",
this.imageSrc = cmsContent.LogoImage; messageHeadline: "",
} type: "",
}, },
};
},
methods: {
initializeComponent(cmsContent) {
this.imageSrc = cmsContent.LogoImage;
},
},
components: {
alert,
},
mounted() {
// Check if alert event is on the bus
const alertEvent = eventBus.readAndPopEventFromBus(
globalEvents.Categories.GLOBAL_ALERT,
globalEvents.SubCategories.PAGE_NOT_FOUND
);
// If alert event is on the bus, then display the alert
if (alertEvent !== undefined) {
this.displayGlobalAlert = true;
this.globalAlertMessage = alertEvent;
}
},
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.funnel-header { .funnel-header {
height: 56px; height: 56px;
} }
.logo-image { .logo-image {
max-width: 78px; max-width: 78px;
} }
</style> </style>

View file

@ -3,6 +3,10 @@ const endpoints = {
url: "/content/api/v1/content/RouteInfo", url: "/content/api/v1/content/RouteInfo",
method: "POST", method: "POST",
}, },
GetHomepageInfo: {
url: "/content/api/v1/content/HomepageInfo",
method: "GET",
},
GetVehicleYears: { GetVehicleYears: {
url: "/vehicle/api/v1/vehicle/years", url: "/vehicle/api/v1/vehicle/years",
method: "GET", method: "GET",

17
src/constants/events.js Normal file
View file

@ -0,0 +1,17 @@
const globalEvents = {
Categories: {
GLOBAL_ALERT: "GLOBAL_ALERT",
},
SubCategories: {
PAGE_NOT_FOUND: "PAGE_NOT_FOUND",
}
}
const globalEventTypes = {
Success: "alert-success",
Warning: "alert-warning",
Info: "alert-info",
Danger: "alert-danger",
}
export {globalEvents, globalEventTypes}

View file

@ -1,5 +1,6 @@
const storeActions = { const storeActions = {
GET_ROUTE_INFO_ACTION: "getRouteInfo", GET_ROUTE_INFO_ACTION: "getRouteInfo",
GET_HOMEPAGE_NAME: "getHomepageName",
GET_PAGE_DATA: "getPageData", GET_PAGE_DATA: "getPageData",
GET_VEHICLE_YEARS: "getVehicleYears", GET_VEHICLE_YEARS: "getVehicleYears",
GET_VEHICLE_MAKES: "getVehicleMakes", GET_VEHICLE_MAKES: "getVehicleMakes",
@ -10,6 +11,9 @@ const storeActions = {
GET_EVOX_IMAGE: "getEvoxImage", GET_EVOX_IMAGE: "getEvoxImage",
LOOKUP_VEHICLE_BY_YMMS: "lookupVehicleByYmms", LOOKUP_VEHICLE_BY_YMMS: "lookupVehicleByYmms",
LOOKUP_VEHICLE_BY_VIN: "lookupVehicleByVin", LOOKUP_VEHICLE_BY_VIN: "lookupVehicleByVin",
// EVENT BUS
ADD_EVENT_TO_BUS: "addEventToBus",
REMOVE_EVENT_FROM_BUS: "removeEventFromBus",
}; };
export { storeActions }; export { storeActions };

View file

@ -4,6 +4,7 @@ const storeMutations = {
UPDATE_MODEL: "updateModel", UPDATE_MODEL: "updateModel",
UPDATE_STYLE: "updateStyle", UPDATE_STYLE: "updateStyle",
UPDATE_VEHICLE: "updateVehicle", UPDATE_VEHICLE: "updateVehicle",
UPDATE_LAST_PAGE: "updateLastPage",
}; };
export { storeMutations }; export { storeMutations };

View file

@ -0,0 +1,25 @@
import store from "@/store";
import { storeActions } from "@/constants/store-actions.js";
export default {
// Adds event to the bus given its category, subcategory, and eventValue;
addEventToBus(category, subCategory, eventValue) {
store.commit(storeActions.ADD_EVENT_TO_BUS, { category: category, subCategory: subCategory, eventValue: eventValue });
},
// Finds event on the bus, removes the item, and returns its value to the caller.
readAndPopEventFromBus(category, subCategory) {
const event = store.getters.eventBusItem(category, subCategory);
store.commit(storeActions.REMOVE_EVENT_FROM_BUS, { category: category, subCategory: subCategory });
return event;
},
// Finds the event on the bus and returns its value to the caller, does not remove it.
readEventFromBus(category, subCategory) {
const event = store.getters.eventBusItem(category, subCategory);
return event;
},
}

View file

@ -0,0 +1,50 @@
import { globalEvents, globalEventTypes } from "@/constants/events";
import eventBus from "@/helpers/event-bus/event-bus";
import store from "@/store";
describe("event-bus.js", () => {
let event = {
isDismissible: true,
messageCopy: 'You can get a quote by starting on this page.',
messageHeadline: 'We\'re sorry, something went wrong.',
type: globalEventTypes.Danger
};
it("Puts item on bus and then take it off", () => {
// Arrange / Act
eventBus.addEventToBus(
globalEvents.Categories.GLOBAL_ALERT,
globalEvents.SubCategories.PAGE_NOT_FOUND,
event
);
// Assert
expect(store.getters.eventBusItem(globalEvents.Categories.GLOBAL_ALERT, globalEvents.SubCategories.PAGE_NOT_FOUND)).toEqual(event);
expect(store.state.applicationUser.eventBus.length).toEqual(1);
// Arrange / Act
const eventValue = eventBus.readAndPopEventFromBus(globalEvents.Categories.GLOBAL_ALERT, globalEvents.SubCategories.PAGE_NOT_FOUND);
// Assert
expect(eventValue).toEqual(event);
expect(store.state.applicationUser.eventBus.length).toEqual(0);
});
it("Reads event from bus, should have event value.", () => {
// Arrange / Act
eventBus.addEventToBus(
globalEvents.Categories.GLOBAL_ALERT,
globalEvents.SubCategories.PAGE_NOT_FOUND,
event
);
// Assert
expect(eventBus.readEventFromBus(globalEvents.Categories.GLOBAL_ALERT, globalEvents.SubCategories.PAGE_NOT_FOUND)).toEqual(event);
});
});

View file

@ -1,3 +0,0 @@
<template>
<p>Not Found .... :(</p>
</template>

View file

@ -22,7 +22,7 @@ import { storeActions } from "@/constants/store-actions";
export default ({ export default ({
name: "vehicle-damage", name: "vehicle-damage",
beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
// Call APIs // Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage); const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
const damageOptionsPromise = baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.GET_DAMAGE_OPTIONS, {carId: store.getters.vehicle.carId}); const damageOptionsPromise = baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.GET_DAMAGE_OPTIONS, {carId: store.getters.vehicle.carId});
@ -39,21 +39,27 @@ export default ({
}, },
]; ];
settleAllPromises(promiseResultMap).then((resultMap) => { const resultMap = await settleAllPromises(promiseResultMap);
// Call the "next" function to complete the transition to this page.
next((vm) => { // Call the "next" function to complete the transition to this page.
vm.$refs.funnelHeader.initializeComponent(resultMap.cmsContent.FunnelHeaderWidget); next((vm) => {
vm.$refs.vehicleBanner.initializeComponent(resultMap.cmsContent.VehicleBannerWidget); vm.$refs.funnelHeader.initializeComponent(
vm.$refs.funnelSubHeader.initializeComponent(resultMap.cmsContent.FunnelSubHeaderWidget); resultMap.cmsContent.FunnelHeaderWidget
// use resultMap.damageOptions for damage options );
}); vm.$refs.vehicleBanner.initializeComponent(
}); resultMap.cmsContent.VehicleBannerWidget
);
vm.$refs.funnelSubHeader.initializeComponent(
resultMap.cmsContent.FunnelSubHeaderWidget
);
// use resultMap.damageOptions for damage options
});
},
components: {
funnelHeader,
vehicleBanner,
funnelSubHeader,
}, },
}) components: {
funnelHeader,
vehicleBanner,
funnelSubHeader,
},
});
</script> </script>

View file

@ -34,11 +34,11 @@ export default {
}, },
computed: {}, computed: {},
beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
// Call APIs // Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage); const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
const makeQuestionInitialDataPromise = makeQuestion.methods.loadInitialData(); const makeQuestionInitialDataPromise =
makeQuestion.methods.loadInitialData();
// Settle promises and get results // Settle promises and get results
const promiseResultMap = [ const promiseResultMap = [
@ -51,14 +51,24 @@ export default {
promise: makeQuestionInitialDataPromise, promise: makeQuestionInitialDataPromise,
}, },
]; ];
settleAllPromises(promiseResultMap).then((resultMap) => {
// Call the "next" function to complete the transition to this page. const resultMap = await settleAllPromises(promiseResultMap);
next((vm) => {
vm.$refs.funnelSubHeader.initializeComponent(resultMap.cmsContent.FunnelSubHeaderWidget); // Call the "next" function to complete the transition to this page.
vm.$refs.funnelHeader.initializeComponent(resultMap.cmsContent.FunnelHeaderWidget); next((vm) => {
vm.$refs.vehicleBanner.initializeComponent(resultMap.cmsContent.VehicleBannerWidget); vm.$refs.funnelSubHeader.initializeComponent(
vm.$refs.makeQuestion.initializeComponent(resultMap.cmsContent.VehicleMakeQuestion, resultMap.makeQuestionInitialData); resultMap.cmsContent.FunnelSubHeaderWidget
}); );
vm.$refs.funnelHeader.initializeComponent(
resultMap.cmsContent.FunnelHeaderWidget
);
vm.$refs.vehicleBanner.initializeComponent(
resultMap.cmsContent.VehicleBannerWidget
);
vm.$refs.makeQuestion.initializeComponent(
resultMap.cmsContent.VehicleMakeQuestion,
resultMap.makeQuestionInitialData
);
}); });
}, },
@ -66,14 +76,17 @@ export default {
backButtonAction() { backButtonAction() {
// route to move backwards // route to move backwards
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route); this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
} },
}, },
watch: { watch: {
selectedMake(make) { selectedMake(make) {
this.$store.commit(this.storeMutations.UPDATE_MAKE, make); this.$store.commit(this.storeMutations.UPDATE_MAKE, make);
this.$router.navigate(this.navigationScenarios.SELECTED_MAKE, this.$route); this.$router.navigate(
} this.navigationScenarios.SELECTED_MAKE,
this.$route
);
},
}, },
components: { components: {

View file

@ -34,11 +34,11 @@ export default {
}, },
computed: {}, computed: {},
beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
// Call APIs // Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage); const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
const modelQuestionInitialDataPromise = modelQuestion.methods.loadInitialData(); const modelQuestionInitialDataPromise =
modelQuestion.methods.loadInitialData();
// Settle promises and get results // Settle promises and get results
const promiseResultMap = [ const promiseResultMap = [
@ -51,14 +51,24 @@ export default {
promise: modelQuestionInitialDataPromise, promise: modelQuestionInitialDataPromise,
}, },
]; ];
settleAllPromises(promiseResultMap).then((resultMap) => {
// Call the "next" function to complete the transition to this page. const resultMap = await settleAllPromises(promiseResultMap);
next((vm) => {
vm.$refs.funnelSubHeader.initializeComponent(resultMap.cmsContent.FunnelSubHeaderWidget); // Call the "next" function to complete the transition to this page.
vm.$refs.funnelHeader.initializeComponent(resultMap.cmsContent.FunnelHeaderWidget); next((vm) => {
vm.$refs.vehicleBanner.initializeComponent(resultMap.cmsContent.VehicleBannerWidget); vm.$refs.funnelSubHeader.initializeComponent(
vm.$refs.modelQuestion.initializeComponent(resultMap.cmsContent.VehicleModelQuestion, resultMap.modelQuestionInitialData); resultMap.cmsContent.FunnelSubHeaderWidget
}); );
vm.$refs.funnelHeader.initializeComponent(
resultMap.cmsContent.FunnelHeaderWidget
);
vm.$refs.vehicleBanner.initializeComponent(
resultMap.cmsContent.VehicleBannerWidget
);
vm.$refs.modelQuestion.initializeComponent(
resultMap.cmsContent.VehicleModelQuestion,
resultMap.modelQuestionInitialData
);
}); });
}, },
@ -66,14 +76,17 @@ export default {
backButtonAction() { backButtonAction() {
// route to move backwards // route to move backwards
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route); this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
} },
}, },
watch: { watch: {
selectedModel(model) { selectedModel(model) {
this.$store.commit(this.storeMutations.UPDATE_MODEL, model); this.$store.commit(this.storeMutations.UPDATE_MODEL, model);
this.$router.navigate(this.navigationScenarios.SELECTED_MODEL, this.$route); this.$router.navigate(
} this.navigationScenarios.SELECTED_MODEL,
this.$route
);
},
}, },
components: { components: {

View file

@ -34,11 +34,11 @@ export default {
}, },
computed: {}, computed: {},
beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
// Call APIs // Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage); const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
const styleQuestionInitialDataPromise = styleQuestion.methods.loadInitialData(); const styleQuestionInitialDataPromise =
styleQuestion.methods.loadInitialData();
// Settle promises and get results // Settle promises and get results
const promiseResultMap = [ const promiseResultMap = [
@ -51,14 +51,24 @@ export default {
promise: styleQuestionInitialDataPromise, promise: styleQuestionInitialDataPromise,
}, },
]; ];
settleAllPromises(promiseResultMap).then((resultMap) => {
// Call the "next" function to complete the transition to this page. const resultMap = await settleAllPromises(promiseResultMap);
next((vm) => {
vm.$refs.funnelSubHeader.initializeComponent(resultMap.cmsContent.FunnelSubHeaderWidget); // Call the "next" function to complete the transition to this page.
vm.$refs.funnelHeader.initializeComponent(resultMap.cmsContent.FunnelHeaderWidget); next((vm) => {
vm.$refs.vehicleBanner.initializeComponent(resultMap.cmsContent.VehicleBannerWidget); vm.$refs.funnelSubHeader.initializeComponent(
vm.$refs.styleQuestion.initializeComponent(resultMap.cmsContent.VehicleStyleQuestion, resultMap.styleQuestionInitialData); resultMap.cmsContent.FunnelSubHeaderWidget
}); );
vm.$refs.funnelHeader.initializeComponent(
resultMap.cmsContent.FunnelHeaderWidget
);
vm.$refs.vehicleBanner.initializeComponent(
resultMap.cmsContent.VehicleBannerWidget
);
vm.$refs.styleQuestion.initializeComponent(
resultMap.cmsContent.VehicleStyleQuestion,
resultMap.styleQuestionInitialData
);
}); });
}, },

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="container-fluid shadow rounded-3 p-0"> <div class="container-fluid shadow rounded-3 p-0 position-relative">
<funnelHeader ref="funnelHeader" /> <funnelHeader ref="funnelHeader" />
<div class="select-car"> <div class="select-car">
<div class="select-car-form rounded text-center"> <div class="select-car-form rounded text-center">
@ -22,6 +22,7 @@ import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-he
// Supporting files // Supporting files
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 eventBus from "@/helpers/event-bus/event-bus";
export default { export default {
name: "vehicle-year", name: "vehicle-year",
@ -32,11 +33,11 @@ export default {
}, },
computed: {}, computed: {},
beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
// Call APIs // Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage); const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
const yearQuestionInitialDataPromise = yearQuestion.methods.loadInitialData(); const yearQuestionInitialDataPromise =
yearQuestion.methods.loadInitialData();
// Settle promises and get results // Settle promises and get results
const promiseResultMap = [ const promiseResultMap = [
@ -49,24 +50,36 @@ export default {
promise: yearQuestionInitialDataPromise, promise: yearQuestionInitialDataPromise,
}, },
]; ];
settleAllPromises(promiseResultMap).then((resultMap) => {
// Call the "next" function to complete the transition to this page. let resultMap = await settleAllPromises(promiseResultMap);
next((vm) => {
vm.$refs.funnelSubHeader.initializeComponent(resultMap.cmsContent.FunnelSubHeaderWidget); // Call the "next" function to complete the transition to this page.
vm.$refs.funnelHeader.initializeComponent(resultMap.cmsContent.FunnelHeaderWidget); next((vm) => {
vm.$refs.vehicleBanner.initializeComponent(resultMap.cmsContent.VehicleBannerWidget); vm.$refs.funnelSubHeader.initializeComponent(
vm.$refs.yearQuestion.initializeComponent(resultMap.cmsContent.VehicleYearQuestion, resultMap.yearQuestionInitialData); 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: { watch: {
selectedYear(year) { selectedYear(year) {
this.$store.commit(this.storeMutations.UPDATE_YEAR, 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: { components: {
yearQuestion, yearQuestion,
funnelHeader, funnelHeader,

View file

@ -15,7 +15,6 @@ import buttonQuestion from "@/common-components/button-question/button-question"
// Supporting files // Supporting files
import { storeActions } from "@/constants/store-actions.js"; import { storeActions } from "@/constants/store-actions.js";
import baseMixin from "@/mixins/base-mixin.js"; import baseMixin from "@/mixins/base-mixin.js";
export default { export default {
name: "year-question", name: "year-question",
data() { data() {

View file

@ -14,4 +14,5 @@ vueApp.use(store);
vueApp.use(LoadScript); vueApp.use(LoadScript);
vueApp.mixin(baseMixin); vueApp.mixin(baseMixin);
vueApp.mount("#app"); vueApp.mount("#app");

View file

@ -1,19 +1,18 @@
// Supporting files
import { createWebHistory, createRouter } from "vue-router"; 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 { 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 ComponentTest from "@/layouts/component-test/component-test.vue"; import { globalEvents, globalEventTypes } from "@/constants/events";
import FormTest from "@/layouts/form-test/form-test.vue"; import eventBus from "@/helpers/event-bus/event-bus";
import AddressPOC from "@/layouts/address-poc/address-poc.vue";
import NotFound from "@/layouts/not-found/not-found.vue";
import store from "@/store"; 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 = [ const routes = [
{
path: "/:pathMatch(.*)*",
component: NotFound,
name: "NotFound",
},
{ {
path: "/component-test", // This is a temporary route for testing. path: "/component-test", // This is a temporary route for testing.
name: "ComponentTest", name: "ComponentTest",
@ -29,44 +28,45 @@ const routes = [
name: "AddressPOC", name: "AddressPOC",
component: AddressPOC, component: AddressPOC,
}, },
{
path: "/form-test", // This is a temporary route for testing.
name: "FormTest",
component: FormTest,
},
{ {
path: "/", 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 we have no query string, or we don't have the FmgPage query string.
if (to.query.fmgPage === undefined) { if (to.query.fmgPage === undefined) {
RetainStructureAndGoTo404(to, next); await GoToFunnelStartOn404(next);
} else { } else {
// If we already have our route, go to it.
if (router.hasRoute(to.query.fmgPage)) { try {
return next({ // If we already have our route, go to it.
name: to.query.fmgPage, if (router.hasRoute(to.query.fmgPage)) {
query: to.query, 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) => { router.getNavigationMap = (scenario, currentRoute) => {
const fmgPageValue = currentRoute.query.fmgPage; const fmgPageValue = currentRoute.query.fmgPage;
const matchedQueryValue = routingTable const matchedQueryValue = routingTable
@ -132,38 +132,44 @@ function navigateToUrl(url) {
// Get route information by page name. // 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. // This will reach out to the Cms and there is a 1:1 relationship between page names and route names.
function GetRouteInfoFromPageName(pageName) { async function GetRouteInfoFromPageName(pageName) {
return new Promise((resolve, reject) => { const response = await store.dispatch(storeActions.GET_ROUTE_INFO_ACTION, { pageName: pageName });
store const jsonFromResponse = JSON.parse(response.data.Result);
.dispatch(storeActions.GET_ROUTE_INFO_ACTION, { pageName: pageName }) let routeData = [];
.then((response) => {
// Add our route data and return our array.
let jsonFromResponse = JSON.parse(response.data.Result);
let routeData = [];
Object.keys(jsonFromResponse).forEach((key) => { // Add our route data and return our array.
routeData.push({ Object.keys(jsonFromResponse).forEach((key) => {
path: "/", routeData.push({
name: `${key}`, path: "/",
component: lazyLoadComponent(jsonFromResponse[key].LayoutName), name: `${key}`,
}); component: lazyLoadComponent(jsonFromResponse[key].LayoutName),
}); });
resolve(routeData);
})
.catch((error) => {
reject(error);
});
}); });
return routeData;
} }
// Go to our 404 page but retain our structure when we go there (path, queryString, hash). // Go to our start page on a 404.
function RetainStructureAndGoTo404(to, next) { 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({ next({
name: "NotFound", path: '/',
params: { pathMatch: to.path.split("/").slice(1) }, query: { fmgPage: homepageName },
query: to.query,
hash: to.hash,
}); });
} }

View file

@ -3,13 +3,10 @@ import { endpoints } from "@/constants/endpoints.js";
import { storeMutations } from "@/constants/store-mutations" import { storeMutations } from "@/constants/store-mutations"
import createPersistedState from "vuex-persistedstate"; import createPersistedState from "vuex-persistedstate";
import globalMethods from "@/global-methods"; import globalMethods from "@/global-methods";
import eventBus from "../helpers/event-bus/event-bus";
export default createStore({ export default createStore({
plugins: [ plugins: [createPersistedState()],
createPersistedState({
storage: window.sessionStorage,
}),
],
// IMPORTANT: Be VERY careful when modifying these fields for at least a few reasons: // IMPORTANT: Be VERY careful when modifying these fields for at least a few reasons:
// * The CMS can reference the fields by name // * The CMS can reference the fields by name
@ -58,6 +55,7 @@ export default createStore({
}, },
applicationUser: { applicationUser: {
experiments: null, experiments: null,
eventBus: []
}, },
}, },
// See IMPORTANT note at top of "state" declaration. // See IMPORTANT note at top of "state" declaration.
@ -78,10 +76,28 @@ export default createStore({
updateVehicle(state, data) { updateVehicle(state, data) {
state.order.vehicle.carId = data.carId; state.order.vehicle.carId = data.carId;
state.order.vehicle.category = data.category; 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: { 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: { actions: {
// Vehicle API 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 }) { getPageData(context, { pageName }) {
return globalMethods.callHttpClient({ return globalMethods.callHttpClient({
method: endpoints.GetPageData.method, method: endpoints.GetPageData.method,

View file

@ -253,10 +253,51 @@ describe("Mutations", () => {
expect(store.state.order.vehicle.carId).toBe("123abc"); expect(store.state.order.vehicle.carId).toBe("123abc");
expect(store.state.order.vehicle.category).toBe("car"); 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", () => { 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);
});
}); });