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/v-on-event-hyphenation': ['warn', 'never'],
'object-curly-newline': ['error', { consistent: true }],
'function-paren-newline': ['error', 'never'],
'operator-linebreak': ['error', 'before', { overrides: { '=': 'after' }}],
'function-paren-newline': ['error', 'multiline'],
'operator-linebreak': ['error', 'before', { overrides: { '=': 'after' } }],
'implicit-arrow-linebreak': ['off'],
'comma-dangle': ['error', 'never'],
indent: ['error', 4, { SwitchCase: 1 }],
@ -33,6 +33,7 @@ module.exports = {
'jsdoc/check-tag-names': ['error', {
definedTags: ['store', 'endpoint', 'category', 'subcategory', 'remarks']
}],
'jsdoc/require-jsdoc': 0,
'vue/html-self-closing': ['error', {
html: {
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) => {
vm.setCmsContent(resultMap.cmsContent);
vm.$refs.damageLocation.initializeComponent(resultMap.damageOptions);
vm.$refs.sideDoorOptions.initializeComponent(resultMap.damageOptions.driverSideOptions.availableReplacementOptions,
resultMap.damageOptions.passengerSideOptions.availableReplacementOptions);
vm.$refs.sideDoorOptions.initializeComponent(
resultMap.damageOptions.driverSideOptions.availableReplacementOptions,
resultMap.damageOptions.passengerSideOptions.availableReplacementOptions
);
vm.$refs.windshieldOptions.initializeComponent(resultMap.damageOptions.windshieldOptions.availableReplacementOptions);
vm.$refs.backGlassOptions.initializeComponent(resultMap.damageOptions.backGlassOptions.availableReplacementOptions);
});
@ -228,7 +230,7 @@ export default {
},
methods: {
arePagePrerequisitesValid() {
if (this.mainStore.order.vehicle.carId) {
if (useMainStore().order.vehicle.carId) {
return true;
}
return false;
@ -362,23 +364,22 @@ export default {
},
async forwardButtonAction() {
await this.mainStore.saveVehicleDamage(this.isWindshieldRepair,
await this.mainStore.saveVehicleDamage(
this.isWindshieldRepair,
this.selectedGlassToReplace(),
this.selectedWindshieldOptions.selectedWindshieldChipCount);
this.selectedWindshieldOptions.selectedWindshieldChipCount
);
return this.navigateForward();
},
navigateForward() {
if (this.mainStore.damage.isRepair) {
this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_WITH_REPAIR,
this.$route);
this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_WITH_REPAIR, this.$route);
} else if (this.mainStore.order.vehicle.vin) {
// If vin already exists, navigate directly to vin-lookup
this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_WITH_VIN,
this.$route);
this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_WITH_VIN, this.$route);
} else {
this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN,
this.$route);
this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN, this.$route);
}
},

View file

@ -1,3 +1,4 @@
/* eslint-disable no-use-before-define */
import { createWebHistory, createRouter } from 'vue-router';
import lazyLoadComponent from '@/router/dynamic-routing/component-loader';
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 { globalEvents, globalEventTypes } from '@/constants/events';
import baseMixin from '@/mixins/base-mixin';
import {
getDeviceIdValue,
updateOrCreateISSCookie,
updateSessionIdCookie
} from '@/helpers/cookie-helper';
import { isSavedSessionStillActive } from '@/helpers/session-helper';
import { getDeviceIdValue, getISSCookie, updateOrCreateISSCookie, updateSessionIdCookie } from '@/helpers/cookie-helper';
import { experimentTriggers } from '@/constants/experiments';
import applicationConfig from '@/constants/application-config';
@ -23,29 +21,52 @@ const routes = [
name: 'root',
async beforeEnter(to, from, next) {
try {
to.query.issPage = !to.query.issPage
? issPageValues.WELCOME_PAGE
: to.query.issPage;
const issPageToUse = !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.
if (to.query.issPage !== issPageValues.ENTRY_PAGE) {
if (issPageToUse !== issPageValues.ENTRY_PAGE) {
if (analyticsMixin.methods.noSession()) {
await analyticsMixin.methods.initSession();
} else {
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.
updateOrCreateISSCookie();
if (router.hasRoute(to.query.issPage)) {
return next({ name: to.query.issPage, query: to.query, params: to.params });
if (router.hasRoute(issPageToUse)) {
// 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') {
throw new Error('Page not found!');
@ -58,6 +79,19 @@ const routes = [
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.
next({
name: routeData[0].name,
@ -66,7 +100,7 @@ const routes = [
});
} catch (error) {
window.console.warn(error);
GoToStartOn404(next);
await GoToStartOn404(next);
}
return null;
}
@ -76,15 +110,13 @@ const routes = [
const router = createRouter({
history: createWebHistory('/'),
routes,
scrollBehavior(to, from, savedPosition) {
scrollBehavior() {
// always scroll to top
return { top: 0 };
}
});
router.afterEach((to, from) => {
/*eslint-disable-line*/
router.afterEach((to) => {
const store = useMainStore();
// Update lastPageVisited in the store
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.
router.overrideNavigation = (scenario,
router.overrideNavigation = (
scenario,
currentRoute,
next,
isSavingNavigation,
optionalQuery = {},
optionalParams = {},
optionalPageData) => {
navigate(scenario,
currentRoute,
isSavingNavigation,
optionalQuery,
optionalParams,
optionalPageData);
optionalPageData = {}
) => {
navigate(scenario, currentRoute, isSavingNavigation, optionalQuery, optionalParams, optionalPageData);
next();
};
router.navigate = (scenario,
currentRoute,
optionalQuery = {},
optionalParams = {},
optionalPageData = {}) => {
router.navigate = (scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) => {
navigate(scenario, currentRoute, optionalQuery, optionalParams, optionalPageData);
};
// Navigate to the next route, depending on the scenario.
function navigate(scenario,
currentRoute,
optionalQuery = {},
optionalParams = {},
optionalPageData = {}) {
function navigate(scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) {
/*eslint-disable-line*/
if (!scenario) {
window.console.error('No scenario provided. Please review the routing table.');
@ -171,10 +192,10 @@ function navigate(scenario,
} 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
const existingPageDataForPage = useMainStore().pageData(matchingScenarioMap.destinationIssPageValue);
baseMixin.methods.savePageDataToStore(matchingScenarioMap.destinationIssPageValue,
Object.keys(optionalPageData).length > 0
? optionalPageData
: existingPageDataForPage ?? {});
baseMixin.methods.savePageDataToStore(
matchingScenarioMap.destinationIssPageValue,
Object.keys(optionalPageData).length > 0 ? optionalPageData : existingPageDataForPage ?? {}
);
// We're always pushing the same path, just changing query strings.
// Make sure our optional query strings get combined with our issPage one.
@ -194,7 +215,7 @@ function navigate(scenario,
function navigateToUrl(url, optionalQuery = {}) {
// possibly show some loading screen in the future here.
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) {
externalUrl.searchParams.append(queryKey, optionalQuery[queryKey]);
}
@ -207,9 +228,7 @@ function getNavigationMap(scenario, currentRoute) {
const issPageValue = currentRoute.query.issPage;
try {
const matchedQueryValue = routingTable(useMainStore())
.filter((item) =>
item.issPageValue === issPageValue
&& item.maps.filter((map) => map.scenario === scenario).length > 0);
.filter((item) => 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;
@ -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;
router.addRoute({
path: '/',
@ -229,14 +262,12 @@ function GoToStartOn404(next) {
});
// 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
});
eventBus.addEventToBus(globalEvents.Categories.GLOBAL_ALERT, globalEvents.SubCategories.PAGE_NOT_FOUND, {
isDismissible: true,
messageCopy: msgCopy ?? 'You can get a quote by starting on this page.',
messageHeadline: msgHeadline ?? "We're sorry, something went wrong.",
type: globalEventTypes.Danger
});
next({
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
async function runExperiments(nextPage) {
const store = useMainStore();

View file

@ -1,4 +1,5 @@
const issPageValues = Object.freeze({
ACCESS_DENIED: 'access-denied',
ENTRY_PAGE: 'entry-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,
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_CURRENT_ENVIRONMENT = 'Localhost';
process.env.VUE_APP_GOOGLE_PLACES_API_KEY
= 'AIzaSyCuLhQcDdZTTb4JzpUFms1OCch2dk5lHF0';
process.env.VUE_APP_GOOGLE_PLACES_API_KEY = 'AIzaSyCuLhQcDdZTTb4JzpUFms1OCch2dk5lHF0';
// GA & GTM
// 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
= "(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
= 'https://www.googletagmanager.com/ns.html?id=GTM-KKNWZ3&gtm_auth=amlAYNhxUxuskQo7jmjadg&gtm_preview=env-38&gtm_cookies_win=x';
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');";
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';
module.exports = {
publicPath: '/',