// Supporting files import { createWebHistory, createRouter } from "vue-router"; import { storeActions } from "@/constants/store-actions"; import baseMixin from "@/mixins/base-mixin"; import { lazyLoadComponent } from "@/router/dynamic-routing/component-loader.js"; import { routingTable } from "@/router/router-constants/routing-table.js"; import { globalEvents, globalEventTypes } from "@/constants/events"; import 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"; import NestedRadio from "@/layouts/nested-radio-poc/nested-radio.vue"; const routes = [ { path: "/component-test", // This is a temporary route for testing. name: "ComponentTest", component: ComponentTest, }, { path: "/form-test", // This is a temporary route for testing. name: "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: "/", name: "root", async beforeEnter(to, from, next) { // If we have no query string, or we don't have the FmgPage query string. if (to.query.fmgPage === undefined) { await GoToFunnelStartOn404(next); } else { try { // If we already have our route, go to it. if (router.hasRoute(to.query.fmgPage)) { // Since our route is already in scope, we can grab the component from it and call the arePagePrerequisitesValid function. const component = router .getRoutes() .filter((x) => x.name === to.query.fmgPage)[0].components; if (!arePagePrerequisitesValid(component)) { await GoToFunnelStartOn404(next); } return next({ name: to.query.fmgPage, query: to.query, params: to.params }); } // Get route info for the given url. Names will have a 1:1 relationship with names in the Cms. const routeData = await GetRouteInfoFromPageName(to.query.fmgPage); // Add our dynamic route. router.addRoute({ path: routeData[0].path, // Always the same path, because we control it with query strings. name: routeData[0].name, component: routeData[0].component, }); // Call the next components arePagePrerequisitesValid method before load. // If it returns false, use the 404 logic. const nextComponent = await router .getRoutes() .filter((x) => x.name === routeData[0].name)[0] .components.default(); if (!arePagePrerequisitesValid(nextComponent)) { await GoToFunnelStartOn404(next); } // Assign current query string parameters, as well as our fmgPage one. next({ name: routeData[0].name, query: Object.assign(to.query, { fmgPage: routeData[0].name }), params: to.params }); } catch (error) { console.log(error); // If we don't have a route, go to our 404 page. await GoToFunnelStartOn404(next); } } }, }, ]; const router = createRouter({ history: createWebHistory("/fmg/"), routes, }); //---------------------------------------------------------- Router Functions ---------------------------------------------------------- router.navigate = (scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) => { navigate(scenario, currentRoute, false, optionalQuery, optionalParams, optionalPageData); } router.navigateAfterSave = (scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) => { navigate(scenario, currentRoute, true, optionalQuery, optionalParams, optionalPageData); } // PRIVATE FUNCTIONS // Navigate to the next route, depending on the scenario. function navigate(scenario, currentRoute, invalidateOnSave, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) { if (!scenario) { console.error("No scenario provided. Please review the routing table."); return; } // Match our maps up and navigate if we have a destination. const matchingScenarioMap = getNavigationMap(scenario, currentRoute); if (matchingScenarioMap.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. // If we need to do invalidation const currentComponent = currentRoute.matched[0].components; if (invalidateOnSave) { resetDependentState(currentComponent); } // 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); router.push({ name: "root", query: Object.assign(optionalQuery, { fmgPage: matchingScenarioMap.destinationFmgPageValue, }), params: optionalParams, }); } else if (matchingScenarioMap.destinationUrl !== undefined) { navigateToUrl(matchingScenarioMap.destinationUrl); } } // Get navigation map depending on the scenario and the current 'page' you're on. function getNavigationMap(scenario, currentRoute) { const fmgPageValue = currentRoute.query.fmgPage; const matchedQueryValue = routingTable .filter( (item) => item.fmgPageValue === fmgPageValue && item.maps.filter((map) => map.scenario === scenario).length > 0 ) .map((m) => m.maps.filter((map) => map.scenario === scenario)); return matchedQueryValue[0][0]; } //---------------------------------------------------------- Private Functions ---------------------------------------------------------- // Navigate to an external url. function navigateToUrl(url) { // possibly show some loading screen in the future here. window.location.assign(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. 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 = []; // 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 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({ path: "/", query: { fmgPage: homepageName }, }); } // Checks arePagePrerequisitesValid on the component passed in. function arePagePrerequisitesValid(component) { return component.default.methods.arePagePrerequisitesValid(); } // Reset dependant state on route change. function resetDependentState(component) { return component.default.methods.resetDependentState(); } export default router;