commit
882667c8f7
19 changed files with 425 additions and 172 deletions
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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
17
src/constants/events.js
Normal 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}
|
||||||
|
|
@ -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 };
|
||||||
|
|
|
||||||
|
|
@ -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 };
|
||||||
|
|
|
||||||
25
src/helpers/event-bus/event-bus.js
Normal file
25
src/helpers/event-bus/event-bus.js
Normal 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;
|
||||||
|
},
|
||||||
|
}
|
||||||
50
src/helpers/event-bus/event-bus.spec.js
Normal file
50
src/helpers/event-bus/event-bus.spec.js
Normal 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);
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
<template>
|
|
||||||
<p>Not Found .... :(</p>
|
|
||||||
</template>
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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: {
|
||||||
|
|
|
||||||
|
|
@ -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: {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
|
|
||||||
|
|
@ -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,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue