Merge pull request #427 from Safelite/feature/richardson/SSR-425.1

Access Denied implementation
This commit is contained in:
brich1212safe 2023-08-28 15:38:59 -04:00 committed by GitHub
commit bd9f9b3ccf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 149 additions and 72 deletions

View file

@ -18,8 +18,8 @@ module.exports = {
'vue/attribute-hyphenation': ['warn', 'never'], 'vue/attribute-hyphenation': ['warn', 'never'],
'vue/v-on-event-hyphenation': ['warn', 'never'], 'vue/v-on-event-hyphenation': ['warn', 'never'],
'object-curly-newline': ['error', { consistent: true }], 'object-curly-newline': ['error', { consistent: true }],
'function-paren-newline': ['error', 'never'], 'function-paren-newline': ['error', 'multiline'],
'operator-linebreak': ['error', 'before', { overrides: { '=': 'after' }}], 'operator-linebreak': ['error', 'before', { overrides: { '=': 'after' } }],
'implicit-arrow-linebreak': ['off'], 'implicit-arrow-linebreak': ['off'],
'comma-dangle': ['error', 'never'], 'comma-dangle': ['error', 'never'],
indent: ['error', 4, { SwitchCase: 1 }], indent: ['error', 4, { SwitchCase: 1 }],
@ -33,6 +33,7 @@ module.exports = {
'jsdoc/check-tag-names': ['error', { 'jsdoc/check-tag-names': ['error', {
definedTags: ['store', 'endpoint', 'category', 'subcategory', 'remarks'] definedTags: ['store', 'endpoint', 'category', 'subcategory', 'remarks']
}], }],
'jsdoc/require-jsdoc': 0,
'vue/html-self-closing': ['error', { 'vue/html-self-closing': ['error', {
html: { html: {
void: 'any', void: 'any',

View file

@ -0,0 +1,29 @@
<template>
<div class="page-container-grouped-styles access-denied">
<div class="fade-on-route-transition position-relative">
<div class="container-fluid px-5">
<div class="row mt-5">
<div class="col d-flex justify-content-center">
<span>Unauthorized Access</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
// Supporting files
import BaseFormMixin from '@/mixins/base-form-mixin.js';
export default {
name: 'access-denied',
mixins: [BaseFormMixin],
data() {
return {};
},
methods: {}
};
</script>
<style lang="scss"></style>

View file

@ -138,8 +138,10 @@ export default {
next((vm) => { next((vm) => {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
vm.$refs.damageLocation.initializeComponent(resultMap.damageOptions); vm.$refs.damageLocation.initializeComponent(resultMap.damageOptions);
vm.$refs.sideDoorOptions.initializeComponent(resultMap.damageOptions.driverSideOptions.availableReplacementOptions, vm.$refs.sideDoorOptions.initializeComponent(
resultMap.damageOptions.passengerSideOptions.availableReplacementOptions); resultMap.damageOptions.driverSideOptions.availableReplacementOptions,
resultMap.damageOptions.passengerSideOptions.availableReplacementOptions
);
vm.$refs.windshieldOptions.initializeComponent(resultMap.damageOptions.windshieldOptions.availableReplacementOptions); vm.$refs.windshieldOptions.initializeComponent(resultMap.damageOptions.windshieldOptions.availableReplacementOptions);
vm.$refs.backGlassOptions.initializeComponent(resultMap.damageOptions.backGlassOptions.availableReplacementOptions); vm.$refs.backGlassOptions.initializeComponent(resultMap.damageOptions.backGlassOptions.availableReplacementOptions);
}); });
@ -228,7 +230,7 @@ export default {
}, },
methods: { methods: {
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
if (this.mainStore.order.vehicle.carId) { if (useMainStore().order.vehicle.carId) {
return true; return true;
} }
return false; return false;
@ -362,23 +364,22 @@ export default {
}, },
async forwardButtonAction() { async forwardButtonAction() {
await this.mainStore.saveVehicleDamage(this.isWindshieldRepair, await this.mainStore.saveVehicleDamage(
this.isWindshieldRepair,
this.selectedGlassToReplace(), this.selectedGlassToReplace(),
this.selectedWindshieldOptions.selectedWindshieldChipCount); this.selectedWindshieldOptions.selectedWindshieldChipCount
);
return this.navigateForward(); return this.navigateForward();
}, },
navigateForward() { navigateForward() {
if (this.mainStore.damage.isRepair) { if (this.mainStore.damage.isRepair) {
this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_WITH_REPAIR, this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_WITH_REPAIR, this.$route);
this.$route);
} else if (this.mainStore.order.vehicle.vin) { } else if (this.mainStore.order.vehicle.vin) {
// If vin already exists, navigate directly to vin-lookup // If vin already exists, navigate directly to vin-lookup
this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_WITH_VIN, this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_WITH_VIN, this.$route);
this.$route);
} else { } else {
this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN, this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN, this.$route);
this.$route);
} }
}, },

View file

@ -1,3 +1,4 @@
/* eslint-disable no-use-before-define */
import { createWebHistory, createRouter } from 'vue-router'; import { createWebHistory, createRouter } from 'vue-router';
import lazyLoadComponent from '@/router/dynamic-routing/component-loader'; import lazyLoadComponent from '@/router/dynamic-routing/component-loader';
import issPageValues from '@/router/router-constants/issPage-values'; import issPageValues from '@/router/router-constants/issPage-values';
@ -6,11 +7,8 @@ import { useMainStore } from '@/store';
import eventBus from '@/helpers/event-bus/event-bus'; import eventBus from '@/helpers/event-bus/event-bus';
import { globalEvents, globalEventTypes } from '@/constants/events'; import { globalEvents, globalEventTypes } from '@/constants/events';
import baseMixin from '@/mixins/base-mixin'; import baseMixin from '@/mixins/base-mixin';
import { import { isSavedSessionStillActive } from '@/helpers/session-helper';
getDeviceIdValue, import { getDeviceIdValue, getISSCookie, updateOrCreateISSCookie, updateSessionIdCookie } from '@/helpers/cookie-helper';
updateOrCreateISSCookie,
updateSessionIdCookie
} from '@/helpers/cookie-helper';
import { experimentTriggers } from '@/constants/experiments'; import { experimentTriggers } from '@/constants/experiments';
import applicationConfig from '@/constants/application-config'; import applicationConfig from '@/constants/application-config';
@ -23,29 +21,52 @@ const routes = [
name: 'root', name: 'root',
async beforeEnter(to, from, next) { async beforeEnter(to, from, next) {
try { try {
to.query.issPage = !to.query.issPage const issPageToUse = !to.query.issPage ? issPageValues.WELCOME_PAGE : to.query.issPage;
? issPageValues.WELCOME_PAGE
: to.query.issPage; if ((issPageToUse === issPageValues.ACCESS_DENIED
|| (issPageToUse !== issPageValues.ENTRY_PAGE && !useMainStore().issConfig.accountNumber))
&& process.env.VUE_APP_CURRENT_ENVIRONMENT !== 'Localhost'
) {
return await GoToAccessIsDenied(next);
}
// Do not run these for the main entry page - as it is not part of the user flow. // Do not run these for the main entry page - as it is not part of the user flow.
if (to.query.issPage !== issPageValues.ENTRY_PAGE) { if (issPageToUse !== issPageValues.ENTRY_PAGE) {
if (analyticsMixin.methods.noSession()) { if (analyticsMixin.methods.noSession()) {
await analyticsMixin.methods.initSession(); await analyticsMixin.methods.initSession();
} else { } else {
updateSessionIdCookie(); updateSessionIdCookie();
} }
await runExperiments(to.query.issPage); await runExperiments(issPageToUse); // fmg has this further down
}
// If the saved session has timed out, clear the session, execute 404 logic.
if (getISSCookie() !== null && !isSavedSessionStillActive()) {
// await baseMixin.methods.dispatchStoreAction(storeActions.RESET_STATE);
await GoToStartOn404(next);
} }
// Process ISS cookie. // Process ISS cookie.
updateOrCreateISSCookie(); updateOrCreateISSCookie();
if (router.hasRoute(to.query.issPage)) { if (router.hasRoute(issPageToUse)) {
return next({ name: to.query.issPage, query: to.query, params: to.params }); // Since our route is already in scope, we can grab the component and call the arePagePrerequisitesValid function.
let component = router.getRoutes().filter((x) => x.name === issPageToUse)[0].components;
// If the component hasn't been loaded fully, load it before we check prerequisites.
if (component.default.methods === undefined) {
component = await component.default();
}
if (!arePagePrerequisitesValid(component)) {
await GoToStartOn404(next);
}
return next({ name: issPageToUse, query: to.query, params: to.params });
} }
const routeData = await GetRouteInfoFromPageName(to.query.issPage); const routeData = await GetRouteInfoFromPageName(issPageToUse);
if (routeData[0].name.toLowerCase() === 'error') { if (routeData[0].name.toLowerCase() === 'error') {
throw new Error('Page not found!'); throw new Error('Page not found!');
@ -58,6 +79,19 @@ const routes = [
component: routeData[0].component 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)) {
const tempMsgCopy = 'Pre Requisites failed, need to handle. Default is Welcome Page.';
const tempMsgHeadline = `${issPageToUse}: pre-req failed...`;
await GoToStartOn404(next, tempMsgCopy, tempMsgHeadline);
}
// Assign current query string parameters, as well as our issPage one. // Assign current query string parameters, as well as our issPage one.
next({ next({
name: routeData[0].name, name: routeData[0].name,
@ -66,7 +100,7 @@ const routes = [
}); });
} catch (error) { } catch (error) {
window.console.warn(error); window.console.warn(error);
GoToStartOn404(next); await GoToStartOn404(next);
} }
return null; return null;
} }
@ -76,15 +110,13 @@ const routes = [
const router = createRouter({ const router = createRouter({
history: createWebHistory('/'), history: createWebHistory('/'),
routes, routes,
scrollBehavior(to, from, savedPosition) { scrollBehavior() {
// always scroll to top // always scroll to top
return { top: 0 }; return { top: 0 };
} }
}); });
router.afterEach((to, from) => { router.afterEach((to) => {
/*eslint-disable-line*/
const store = useMainStore(); const store = useMainStore();
// Update lastPageVisited in the store // Update lastPageVisited in the store
store.updateLastPageVisited(to.name); store.updateLastPageVisited(to.name);
@ -119,36 +151,25 @@ async function GetRouteInfoFromPageName(pageName) {
} }
// Use this navigation when you need to call next() explicitly. beforeRouteEnter is a good example. // Use this navigation when you need to call next() explicitly. beforeRouteEnter is a good example.
router.overrideNavigation = (scenario, router.overrideNavigation = (
scenario,
currentRoute, currentRoute,
next, next,
isSavingNavigation, isSavingNavigation,
optionalQuery = {}, optionalQuery = {},
optionalParams = {}, optionalParams = {},
optionalPageData) => { optionalPageData = {}
navigate(scenario, ) => {
currentRoute, navigate(scenario, currentRoute, isSavingNavigation, optionalQuery, optionalParams, optionalPageData);
isSavingNavigation,
optionalQuery,
optionalParams,
optionalPageData);
next(); next();
}; };
router.navigate = (scenario, router.navigate = (scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) => {
currentRoute,
optionalQuery = {},
optionalParams = {},
optionalPageData = {}) => {
navigate(scenario, currentRoute, optionalQuery, optionalParams, optionalPageData); navigate(scenario, currentRoute, optionalQuery, optionalParams, optionalPageData);
}; };
// Navigate to the next route, depending on the scenario. // Navigate to the next route, depending on the scenario.
function navigate(scenario, function navigate(scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) {
currentRoute,
optionalQuery = {},
optionalParams = {},
optionalPageData = {}) {
/*eslint-disable-line*/ /*eslint-disable-line*/
if (!scenario) { if (!scenario) {
window.console.error('No scenario provided. Please review the routing table.'); window.console.error('No scenario provided. Please review the routing table.');
@ -171,10 +192,10 @@ function navigate(scenario,
} else if (matchingScenarioMap.destinationIssPageValue) { } else if (matchingScenarioMap.destinationIssPageValue) {
// Update page data to the store for next page if provided. Otherwise, keep existing page data or set to empty object // Update page data to the store for next page if provided. Otherwise, keep existing page data or set to empty object
const existingPageDataForPage = useMainStore().pageData(matchingScenarioMap.destinationIssPageValue); const existingPageDataForPage = useMainStore().pageData(matchingScenarioMap.destinationIssPageValue);
baseMixin.methods.savePageDataToStore(matchingScenarioMap.destinationIssPageValue, baseMixin.methods.savePageDataToStore(
Object.keys(optionalPageData).length > 0 matchingScenarioMap.destinationIssPageValue,
? optionalPageData Object.keys(optionalPageData).length > 0 ? optionalPageData : existingPageDataForPage ?? {}
: existingPageDataForPage ?? {}); );
// We're always pushing the same path, just changing query strings. // We're always pushing the same path, just changing query strings.
// Make sure our optional query strings get combined with our issPage one. // Make sure our optional query strings get combined with our issPage one.
@ -194,7 +215,7 @@ function navigate(scenario,
function navigateToUrl(url, optionalQuery = {}) { function navigateToUrl(url, optionalQuery = {}) {
// possibly show some loading screen in the future here. // possibly show some loading screen in the future here.
const externalUrl = new URL(url); const externalUrl = new URL(url);
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax, guard-for-in
for (const queryKey in optionalQuery) { for (const queryKey in optionalQuery) {
externalUrl.searchParams.append(queryKey, optionalQuery[queryKey]); externalUrl.searchParams.append(queryKey, optionalQuery[queryKey]);
} }
@ -207,9 +228,7 @@ function getNavigationMap(scenario, currentRoute) {
const issPageValue = currentRoute.query.issPage; const issPageValue = currentRoute.query.issPage;
try { try {
const matchedQueryValue = routingTable(useMainStore()) const matchedQueryValue = routingTable(useMainStore())
.filter((item) => .filter((item) => item.issPageValue === issPageValue && item.maps.filter((map) => map.scenario === scenario).length > 0);
item.issPageValue === issPageValue
&& item.maps.filter((map) => map.scenario === scenario).length > 0);
const maps = matchedQueryValue ? matchedQueryValue.map((m) => m.maps.filter((map) => map.scenario === scenario))[0] : undefined; const maps = matchedQueryValue ? matchedQueryValue.map((m) => m.maps.filter((map) => map.scenario === scenario))[0] : undefined;
@ -220,7 +239,21 @@ function getNavigationMap(scenario, currentRoute) {
} }
} }
function GoToStartOn404(next) { async function GoToAccessIsDenied(next) {
const errorPageName = issPageValues.ACCESS_DENIED;
router.addRoute({
path: '/',
name: errorPageName,
component: lazyLoadComponent(errorPageName)
});
next({
name: errorPageName,
query: { issPage: errorPageName }
});
}
async function GoToStartOn404(next, msgCopy = null, msgHeadline = null) {
const errorPageName = issPageValues.WELCOME_PAGE; const errorPageName = issPageValues.WELCOME_PAGE;
router.addRoute({ router.addRoute({
path: '/', path: '/',
@ -229,14 +262,12 @@ function GoToStartOn404(next) {
}); });
// Put item on the bus // Put item on the bus
eventBus.addEventToBus(globalEvents.Categories.GLOBAL_ALERT, eventBus.addEventToBus(globalEvents.Categories.GLOBAL_ALERT, globalEvents.SubCategories.PAGE_NOT_FOUND, {
globalEvents.SubCategories.PAGE_NOT_FOUND, isDismissible: true,
{ messageCopy: msgCopy ?? 'You can get a quote by starting on this page.',
isDismissible: true, messageHeadline: msgHeadline ?? "We're sorry, something went wrong.",
messageCopy: 'You can get a quote by starting on this page.', type: globalEventTypes.Danger
messageHeadline: "We're sorry, something went wrong.", });
type: globalEventTypes.Danger
});
next({ next({
name: errorPageName, name: errorPageName,
@ -244,6 +275,11 @@ function GoToStartOn404(next) {
}); });
} }
// Checks arePagePrerequisitesValid on the component passed in.
function arePagePrerequisitesValid(component) {
return component.default.methods.arePagePrerequisitesValid === undefined || component.default.methods.arePagePrerequisitesValid();
}
// Run SiteEntry and PageEntry triggers for experiments // Run SiteEntry and PageEntry triggers for experiments
async function runExperiments(nextPage) { async function runExperiments(nextPage) {
const store = useMainStore(); const store = useMainStore();

View file

@ -1,4 +1,5 @@
const issPageValues = Object.freeze({ const issPageValues = Object.freeze({
ACCESS_DENIED: 'access-denied',
ENTRY_PAGE: 'entry-page', ENTRY_PAGE: 'entry-page',
WELCOME_PAGE: 'welcome-page', WELCOME_PAGE: 'welcome-page',

View file

@ -319,6 +319,15 @@ const routingTable = () => [
} }
] ]
}, },
{
issPageValue: issPageValues.ACCESS_DENIED,
maps: [
{
scenario: navigationScenarios.CLICKED_BACK,
destinationIssPageValue: issPageValues.ACCESS_DENIED
}
]
},
{ {
issPageValue: issPageValues.ENTRY_PAGE, issPageValue: issPageValues.ENTRY_PAGE,
maps: [ maps: [

View file

@ -1,14 +1,14 @@
/* eslint-disable max-len */
process.env.VUE_APP_CONSUMER_CF_DISTRO = 'https://digitalapi.dev.safelite.io'; process.env.VUE_APP_CONSUMER_CF_DISTRO = 'https://digitalapi.dev.safelite.io';
process.env.VUE_APP_CURRENT_ENVIRONMENT = 'Localhost'; process.env.VUE_APP_CURRENT_ENVIRONMENT = 'Localhost';
process.env.VUE_APP_GOOGLE_PLACES_API_KEY process.env.VUE_APP_GOOGLE_PLACES_API_KEY = 'AIzaSyCuLhQcDdZTTb4JzpUFms1OCch2dk5lHF0';
= 'AIzaSyCuLhQcDdZTTb4JzpUFms1OCch2dk5lHF0';
// GA & GTM // GA & GTM
// NOTE: Using the old ISS site GTM Cotnainer ID for now, will create a new one soon. // NOTE: Using the old ISS site GTM Cotnainer ID for now, will create a new one soon.
process.env.VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY process.env.VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY =
= "(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','GTM-KKNWZ3');"; "(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','GTM-KKNWZ3');";
process.env.VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC process.env.VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC =
= 'https://www.googletagmanager.com/ns.html?id=GTM-KKNWZ3&gtm_auth=amlAYNhxUxuskQo7jmjadg&gtm_preview=env-38&gtm_cookies_win=x'; 'https://www.googletagmanager.com/ns.html?id=GTM-KKNWZ3&gtm_auth=amlAYNhxUxuskQo7jmjadg&gtm_preview=env-38&gtm_cookies_win=x';
module.exports = { module.exports = {
publicPath: '/', publicPath: '/',