diff --git a/src/digital-components/modal/modal.vue b/src/digital-components/modal/modal.vue index 5ca708288..0676cb610 100644 --- a/src/digital-components/modal/modal.vue +++ b/src/digital-components/modal/modal.vue @@ -216,8 +216,12 @@ export default { overflow: auto; flex: none; } + .textbox-question { + padding: .25rem 0; + } } } + &.modal-dialog-centered { align-items: flex-end; min-height: 100%; @@ -269,8 +273,6 @@ export default { flex-direction: column; .textbox-question { - padding: 0; - label { text-align: left; font-weight: 900; @@ -284,6 +286,10 @@ export default { .modal-disclaimer { font-size: 0.75rem; order: 2; + text-align: left; + a { + text-decoration: none; + } } .modal-footer { margin: 0 0 1.5rem 0; diff --git a/src/fmg-components/save-progress-modal-question/save-progress-modal-question.vue b/src/fmg-components/save-progress-modal-question/save-progress-modal-question.vue index c8465fef7..510f7bc3e 100644 --- a/src/fmg-components/save-progress-modal-question/save-progress-modal-question.vue +++ b/src/fmg-components/save-progress-modal-question/save-progress-modal-question.vue @@ -1,7 +1,8 @@ @@ -38,7 +38,6 @@ import { required } from "@/helpers/validation-rules"; import { errorMessages } from "@/constants/error-messages"; import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin"; import baseMixin from "@/mixins/base-mixin.js"; -import { saveQuote } from "@/helpers/heritage-integration/order-helper.js"; // DEFINE VALIDATION RULES defineRule("questions-required", required(errorMessages.OPTION_REQUIRED)); @@ -65,7 +64,7 @@ export default { // Call the "next" function to complete the transition to this page. next(async (vm) => { vm.setCmsContent(resultMap.cmsContent); - vm.isEmailInStoreOnPageLoad = emailFromStore?.length > 0; + vm.showSaveProgressModal = !(emailFromStore?.length > 0); if (store.getters.externalParameterState?.isExternalParameter) { if (store.getters.externalParameterServiceZip.zipCode) { await baseMixin.methods.dispatchStoreAction( @@ -87,7 +86,7 @@ export default { questionsData: null, selectedAnswers: {}, currentGlassIndex: 0, - isEmailInStoreOnPageLoad: null, + showSaveProgressModal: null, }; }, computed: { @@ -157,9 +156,6 @@ export default { this.questionsData ); }, - saveProgress() { - saveQuote({ pageNameToLog: "capability-questions" }); - }, async forwardButtonAction() { const questionAnswersArray = this.questionsData.map((glass) => { // get answerResult2 of returned answer diff --git a/src/layouts/molding-questions/molding-questions.vue b/src/layouts/molding-questions/molding-questions.vue index 2f2cc346c..1bcc3d7a8 100644 --- a/src/layouts/molding-questions/molding-questions.vue +++ b/src/layouts/molding-questions/molding-questions.vue @@ -16,8 +16,8 @@ + pageName="molding-questions" + v-if="showSaveProgressModal" /> @@ -38,7 +38,6 @@ import { required } from "@/helpers/validation-rules"; import { errorMessages } from "@/constants/error-messages"; import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin"; import baseMixin from "@/mixins/base-mixin.js"; -import { saveQuote } from "@/helpers/heritage-integration/order-helper.js"; // DEFINE VALIDATION RULES defineRule("questions-required", required(errorMessages.OPTION_REQUIRED)); @@ -65,7 +64,7 @@ export default { // Call the "next" function to complete the transition to this page. next(async (vm) => { vm.setCmsContent(resultMap.cmsContent); - vm.isEmailInStoreOnPageLoad = emailFromStore?.length > 0; + vm.showSaveProgressModal = !(emailFromStore?.length > 0); if (store.getters.externalParameterState?.isExternalParameter) { if (store.getters.externalParameterServiceZip.zipCode) { await baseMixin.methods.dispatchStoreAction( @@ -88,7 +87,7 @@ export default { questionsData: null, selectedAnswers: {}, currentGlassIndex: 0, - isEmailInStoreOnPageLoad: null, + showSaveProgressModal: null, }; }, computed: { @@ -157,9 +156,6 @@ export default { this.questionsData ); }, - saveProgress() { - saveQuote({ pageNameToLog: "molding-questions" }); - }, async forwardButtonAction() { const questionAnswersArray = this.questionsData.map((glass) => { return { diff --git a/src/layouts/part-questions/part-questions.vue b/src/layouts/part-questions/part-questions.vue index a5441acd6..f905e86c7 100644 --- a/src/layouts/part-questions/part-questions.vue +++ b/src/layouts/part-questions/part-questions.vue @@ -16,8 +16,8 @@ + pageName="part-questions" + v-if="showSaveProgressModal" /> @@ -38,7 +38,6 @@ import { required } from "@/helpers/validation-rules"; import { errorMessages } from "@/constants/error-messages"; import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin"; import baseMixin from "@/mixins/base-mixin.js"; -import { saveQuote } from "@/helpers/heritage-integration/order-helper.js"; // DEFINE VALIDATION RULES defineRule("questions-required", required(errorMessages.OPTION_REQUIRED)); @@ -65,7 +64,7 @@ export default { // Call the "next" function to complete the transition to this page. next(async (vm) => { vm.setCmsContent(resultMap.cmsContent); - vm.isEmailInStoreOnPageLoad = emailFromStore?.length > 0; + vm.showSaveProgressModal = !(emailFromStore?.length > 0); if (store.getters.externalParameterState?.isExternalParameter) { if (store.getters.externalParameterServiceZip.zipCode) { const serviceState = store.getters.order.serviceLocation.state; @@ -89,7 +88,7 @@ export default { questionsData: null, selectedAnswers: {}, currentGlassIndex: 0, - isEmailInStoreOnPageLoad: null, + showSaveProgressModal: null, }; }, computed: { @@ -156,9 +155,6 @@ export default { this.questionsData ); }, - saveProgress() { - saveQuote({ pageNameToLog: "part-questions" }); - }, async forwardButtonAction() { const questionAnswersArray = this.questionsData.map((glass) => { return { diff --git a/src/layouts/quote/quote.vue b/src/layouts/quote/quote.vue index 366a9d2cc..221eae723 100644 --- a/src/layouts/quote/quote.vue +++ b/src/layouts/quote/quote.vue @@ -20,7 +20,7 @@
-
+
+ v-if="showSaveProgressModal" + pageName="quote" + @save-progress-saved="updateSaveProgressAsSaved" />
@@ -209,6 +211,7 @@ export default { const emailFromStore = store.getters.order.customer.emailAddress; const isEmailInStoreOnPageLoad = emailFromStore?.length > 0; const showSaveProgressPopup = isEmailInStoreOnPageLoad ? false : true; + const showSaveProgressModal = isEmailInStoreOnPageLoad ? false : true; const lineItems = deepClone(store.getters.order.lineItems); lineItems.vaps = lineItems.vaps ?? []; @@ -290,8 +293,8 @@ export default { // Call the "next" function to complete the transition to this page. next(async (vm) => { vm.setCmsContent(resultMap.cmsContent); - vm.isEmailInStoreOnPageLoad = isEmailInStoreOnPageLoad; vm.showSaveProgressPopup = showSaveProgressPopup; + vm.showSaveProgressModal = showSaveProgressModal; vm.addableVaps = addableVaps; vm.lineItems = lineItems; vm.availableLineItems = pricingResults; @@ -438,10 +441,14 @@ export default { servicePackage: null, holdParentAccountNumber: null, holdBillToAccountNumber: null, - isEmailInStoreOnPageLoad: null, showSaveProgressPopup: null, + showSaveProgressModal: null, + isSaveProgressComplete: null, }; }, + mounted() { + this.attachCustomEvents(); + }, computed: { lineItemsCloneForWatcher() { return Object.assign({}, this.lineItems); @@ -526,10 +533,8 @@ export default { backButtonAction() { vehicleQuestionsMixin.methods.navigateBack(this); }, - saveProgress() { - this.isEmailInStoreOnPageLoad = true; - this.showSaveProgressPopup = false; - saveQuote({ pageNameToLog: "quote" }); + updateSaveProgressAsSaved() { + this.showSaveProgressModal = false; }, closeSaveProgressPopup() { this.showSaveProgressPopup = false; @@ -677,9 +682,6 @@ export default { }); }, }, - mounted() { - this.attachCustomEvents(); - }, watch: { lineItemsCloneForWatcher: { handler(newValue, oldValue) { diff --git a/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue b/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue index b147447f6..1ad393650 100644 --- a/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue +++ b/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue @@ -141,8 +141,11 @@ export default { .package-main { .package-wrapper { margin: 1rem 0; + &:last-child { + margin: 1rem 0 0 0; + } @include media-breakpoint-up(md) { - margin: 1rem 0.5rem; + margin: 1rem 0.5rem 0 0.5rem; display: flex; flex-direction: column; } @@ -322,6 +325,7 @@ export default { display: flex; justify-content: flex-end; align-items: center; + align-self: flex-start; div.strikethrough-discount-price { text-decoration: line-through; margin-right: 0.5rem; diff --git a/src/layouts/vehicle-parts/vehicle-parts.vue b/src/layouts/vehicle-parts/vehicle-parts.vue index b0a1ffcd9..fd9b90b79 100644 --- a/src/layouts/vehicle-parts/vehicle-parts.vue +++ b/src/layouts/vehicle-parts/vehicle-parts.vue @@ -48,8 +48,8 @@ + pageName="vehicle-parts" + v-if="showSaveProgressModal" />
@@ -75,7 +75,6 @@ import store from "@/store"; import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin"; import baseMixin from "@/mixins/base-mixin.js"; import { partTypeStrings } from "@/constants/part-type-strings"; -import { saveQuote } from "@/helpers/heritage-integration/order-helper.js"; export default { name: "vehicle-parts", @@ -95,7 +94,7 @@ export default { // Call the "next" function to complete the transition to this page. next((vm) => { vm.setCmsContent(resultMap.cmsContent); - vm.isEmailInStoreOnPageLoad = emailFromStore?.length > 0; + vm.showSaveProgressModal = !(emailFromStore?.length > 0); // Glass Part Question dynamic component Object.keys(vm.$refs) .filter((r) => r.includes(vm.RefPrefix) && vm.$refs[r][0] !== undefined) @@ -115,7 +114,7 @@ export default { selectedGlassParts: {}, alertWidgetData: Object, alreadyPopulatedPartsData: [], - isEmailInStoreOnPageLoad: null, + showSaveProgressModal: null, }; }, computed: { @@ -234,9 +233,6 @@ export default { }); }); }, - saveProgress() { - saveQuote({ pageNameToLog: "vehicle-parts" }); - }, }, mounted() { this.LoadInitialPartsData(); diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index e970e94a8..d534a7200 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -650,6 +650,14 @@ export default { }); }, + pushPageErrorToDataLayer(error) { + + pushToDataLayerIfDefined({ + event: "page-error", + error: error, + }); + }, + prependActionToMethod(object, method, actionToPrepend) { const baseMethodName = method.name.startsWith("bound ") ? method.name.substring(6) diff --git a/src/router/index.js b/src/router/index.js index 6b11edff5..f6950b624 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -65,9 +65,17 @@ const routes = [ // If the saved session has timed out, clear the session, execute 404 logic. if (getFunnelCookie() !== null && !isSavedSessionStillActive()) { log(" --save session timeout go to start"); + + const errorPayload = { + cause: "expired session", + currentPage: from?.query?.fmgPage, + nextPage: to?.query?.fmgPage, + }; + await baseMixin.methods.dispatchStoreAction(storeActions.RESET_STATE); deleteFunnelCookie(); - GoToFunnelStartOn404(next); + GoToFunnelStartOn404(next, errorPayload); + return; } // Intercept all navigation if a submitted order exists in storage @@ -206,7 +214,14 @@ const routes = [ return; } - GoToFunnelStartOn404(next); + const errorPayload = { + cause: "invalid page prerequisites for page", + currentPage: from?.query?.fmgPage, + nextPage: to?.query?.fmgPage, + }; + + GoToFunnelStartOn404(next, errorPayload); + return; } log(" --has route:", to.query.fmgPage); @@ -214,9 +229,15 @@ const routes = [ } if (!isExistingFmgPageName(to.query.fmgPage)) { - console.log("Not an existing fmg page name:" + to.query.fmgPage); - GoToFunnelStartOn404(next); + const errorPayload = { + cause: "invalid page name", + currentPage: from?.query?.fmgPage, + nextPage: to?.query?.fmgPage, + }; + + GoToFunnelStartOn404(next, errorPayload); return; + } // Get route info for the given url. Names will have a 1:1 relationship with names in the Cms. @@ -237,9 +258,18 @@ const routes = [ .components.default(); if (!arePagePrerequisitesValid(nextComponent)) { + // prettier-ignore console.log("Page Prereqs not valid for next component: " + nextComponent.default.name); - GoToFunnelStartOn404(next); + + const errorPayload = { + cause: "invalid page prerequisites for page", + currentPage: from?.query?.fmgPage, + nextPage: to?.query?.fmgPage, + }; + + GoToFunnelStartOn404(next, errorPayload); + return; } log("------------- router index.js beforeEnter end -----------------"); @@ -261,7 +291,16 @@ const routes = [ console.log(new Date() + " Exception in beforeEnter:" + JSON.stringify(error)); // If we don't have a route, go to our 404 page. - GoToFunnelStartOn404(next); + const errorPayload = { + cause: "uncaught error in beforeEnter", + currentPage: from?.query?.fmgPage, + nextPage: to?.query?.fmgPage, + fullError: error, + errorStack: error?.stack, + }; + + GoToFunnelStartOn404(next, errorPayload); + return; } }, }, @@ -397,8 +436,8 @@ router.navigateToExternalUrl = (url, optionalQuery = {}) => { navigateToUrl(url, optionalQuery); }; -router.navigateError = () => { - DisplayPageError(); +router.navigateError = (errorPayload = null) => { + DisplayPageError(errorPayload); }; //Use this navigation when you need to call next() explicitly. beforeRouteEnter is a good example. @@ -588,7 +627,19 @@ function GetRouteInfoFromPageName(pageName) { } // Go to our start page on a 404. -function GoToFunnelStartOn404(next) { +function GoToFunnelStartOn404(next, errorPayload = null) { + if (errorPayload !== null) { + errorPayload.type = "GoToFunnelStartOn404"; + analyticsMixin.methods.pushPageErrorToDataLayer(errorPayload); + + console.log( + "%cGoToFunnelStartOn404()... errorPayload", + "color: white; background-color: blue; padding: 5px;", + errorPayload + ); + + } + // Put item on the bus eventBus.addEventToBus( globalEvents.Categories.GLOBAL_ALERT, @@ -607,7 +658,28 @@ function GoToFunnelStartOn404(next) { }); } -async function DisplayPageError() { +async function DisplayPageError(errorPayload = null) { + console.log("%c running DisplayPageError()... ", "font-size: 20px; color: purple;"); + + if(errorPayload !== null) { + errorPayload.type = "DisplayPageError"; + analyticsMixin.methods.pushPageErrorToDataLayer(errorPayload); + } else { + // very cautiously to avoid additional errors: + var currentPage = ""; + try { + currentPage = getQuerystringParameter(queryStrings.FMG_PAGE); + } catch (e) { + // pass + } + errorPayload = { + type: "DisplayPageError", + cause: "Unknown page error", + currentPage: currentPage, + }; + analyticsMixin.methods.pushPageErrorToDataLayer(errorPayload); + } + // Put item on the bus eventBus.addEventToBus( globalEvents.Categories.GLOBAL_ALERT, @@ -620,12 +692,9 @@ async function DisplayPageError() { } ); if ( - store.getters.externalParameterState?.isExternalParameter == - externalParameterStatus.ACTIVE || - store.getters.externalParameterState?.isExternalParameter == - externalParameterStatus.INACTIVE + store.getters.externalParameterState?.isExternalParameter == externalParameterStatus.ACTIVE ) { - store.commit(storeMutations.UPDATE_IS_EXTERNAL_PARAMETER, externalParameterStatus.NOT_SET); + store.commit(storeMutations.UPDATE_IS_EXTERNAL_PARAMETER, externalParameterStatus.INACTIVE); } baseMixin.methods.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE); const pageError = getQuerystringParameter(queryStrings.PAGE_ERROR);