diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index 16e0803e6..c972d8118 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -124,6 +124,10 @@ const endpoints = { url: "/order/api/v1/order/load-session", method: "POST", }, + SaveQuote: { + url: "/order/api/v1/order/initiate-saved-progress-email", + method: "POST", + }, GetSignature: { url: "/order/api/v1/order/sign", method: "POST", diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js index 41e5896bd..45a18bace 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -37,6 +37,7 @@ const storeActions = { GET_MOBILE_PREMIUM_FEE: "getMobilePremiumFee", SAVE_SESSION: "saveSession", LOAD_SESSION: "loadSession", + SAVE_QUOTE: "saveQuote", UPDATE_STORE_WITH_SAVE_SESSION_RESPONSE: "updateStoreWithSaveSessionResponse", VALIDATE_ZIP: "validateZip", PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA: "priceOrderItemsAndSaveServerData", diff --git a/src/digital-components/modal/modal.vue b/src/digital-components/modal/modal.vue index 37ebee18e..36765bacd 100644 --- a/src/digital-components/modal/modal.vue +++ b/src/digital-components/modal/modal.vue @@ -2,7 +2,9 @@ @@ -53,6 +55,8 @@ export default { props: { headerText: String, footerButtonText: String, + suppressPageScroll: Boolean, + staticBackdrop: Boolean, onModalOpenedCallback: { type: Function, }, @@ -91,7 +95,7 @@ export default { e.preventDefault(); if (this.isEnterClicked) return; this.isEnterClicked = true; - document.getElementById("modalbtn")?.focus(); + document.getElementById(this.modalId + "-modalbtn")?.focus(); this.$refs.modalButtonMain.clicked(); document.getElementById(this.modalId)?.focus(); setTimeout(() => { @@ -107,8 +111,12 @@ export default { onModalClosed() { this.resetButtonStyle(); this.onModalClosedCallback?.(); + if (this.suppressPageScroll) + document.querySelector("body").classList.remove("prevent-modal-scroll"); }, openModal() { + if (this.suppressPageScroll) + document.querySelector("body").classList.add("prevent-modal-scroll"); const modal = Modal.getOrCreateInstance(document.getElementById(this.modalId)); modal.show(); this.$emit("isModalOpened", true); @@ -126,6 +134,9 @@ export default { } return !this.meta.dirty || !this.meta.valid; }, + backdropSetting() { + return this.staticBackdrop ? "static" : "true"; + }, }, components: { modalButtonMain, @@ -162,6 +173,7 @@ export default { } } .modal-footer { + padding: 1rem 0; position: sticky; width: 100%; bottom: 0; @@ -231,6 +243,57 @@ export default { } } } +.save-progress-modal-question { + p.modal-body { + padding: 0; + } + .modal-body { + display: flex; + flex-direction: column; + + .textbox-question { + padding: 0; + + margin: 0 0 1.5rem 0; + } + } +} + +.save-progress-modal-question, +.save-progress-popup-question { + p.modal-body { + padding: 0; + } + .modal-body { + display: flex; + flex-direction: column; + + .textbox-question { + padding: 0; + + label { + text-align: left; + font-weight: 900; + } + } + } + .modal-body-inner { + text-align: center; + margin-bottom: 1.5rem; + } + .modal-disclaimer { + font-size: 0.75rem; + order: 2; + a { + text-decoration: none; + } + } + .modal-footer { + margin: 0 0 1.5rem 0; + padding-top: 0; + padding-bottom: 0; + } +} body { .modal-backdrop { height: 100%; diff --git a/src/fmg-components/layouts/questions-page-layout/questions-page-layout.vue b/src/fmg-components/layouts/questions-page-layout/questions-page-layout.vue index 982738394..2c4d71803 100644 --- a/src/fmg-components/layouts/questions-page-layout/questions-page-layout.vue +++ b/src/fmg-components/layouts/questions-page-layout/questions-page-layout.vue @@ -25,7 +25,7 @@
-
+
+ + diff --git a/src/fmg-components/save-progress-modal-question/save-progress-modal-question.spec.js b/src/fmg-components/save-progress-modal-question/save-progress-modal-question.spec.js new file mode 100644 index 000000000..15fb1481d --- /dev/null +++ b/src/fmg-components/save-progress-modal-question/save-progress-modal-question.spec.js @@ -0,0 +1,51 @@ +import { mount, shallowMount } from "@vue/test-utils"; +import saveProgressModalQuestion from "./save-progress-modal-question"; +import { getMountOptions } from "@/helpers/unit-test-helper.js"; + +jest.mock("@/digital-components/modal/modal", () => ({ + methods: { + openModal: jest.fn(), + resetButtonStyle: jest.fn(), + }, +})); + +describe("save-progress-modal-question ", () => { + describe("when openModal is run ", () => { + test("the modal should open ", () => { + // Arrange + const { wrapper } = setupMocks({ + props: { + modelValue: "", + modalWidgetName: "testModal", + }, + }); + + // Act + wrapper.vm.openModal(); + + // Assert + expect(wrapper.vm.modal).not.toBeNull(); + }); + }); +}); + +function setupMocks({ options, props }) { + const mountOptions = getMountOptions({ + ...options, + }); + + const mockBaseMixin = { + methods: { + getCmsContent: jest.fn(), + dispatchStoreAction: jest.fn(), + }, + }; + + if (props) mountOptions.propsData = props; + + mountOptions.global.mixins = [mockBaseMixin]; + + const wrapper = mount(saveProgressModalQuestion, mountOptions); + + return { wrapper }; +} 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 new file mode 100644 index 000000000..d0ac790f3 --- /dev/null +++ b/src/fmg-components/save-progress-modal-question/save-progress-modal-question.vue @@ -0,0 +1,197 @@ + + + + + diff --git a/src/fmg-components/save-progress-modal-question/save-progress-question/save-progress-question.spec.js b/src/fmg-components/save-progress-modal-question/save-progress-question/save-progress-question.spec.js new file mode 100644 index 000000000..7cc3d100c --- /dev/null +++ b/src/fmg-components/save-progress-modal-question/save-progress-question/save-progress-question.spec.js @@ -0,0 +1,40 @@ +import { shallowMount } from "@vue/test-utils"; +import saveProgressQuestion from "./save-progress-question"; + +describe("save-progress-question.vue", () => { + it("Should get the modelValue", async () => { + // Arrange + const text = "test"; + const wrapper = shallowMount(saveProgressQuestion, { + props: { + modelValue: text, + }, + attachTo: document.body, + }); + + // Act + const modelValueText = wrapper.vm.value; + wrapper.vm.value = "test also"; + + // Assert + expect(modelValueText).toEqual("test"); + }); + + it("Should emit to set value", async () => { + // Arrange + const text = "test"; + const wrapper = shallowMount(saveProgressQuestion, { + props: { + modelValue: text, + }, + attachTo: document.body, + }); + + // Act + const modelValueText = wrapper.vm.value; + wrapper.vm.value = "test also"; + + // Assert + expect(wrapper.emitted("update:modelValue")).toEqual([["test also"]]); + }); +}); diff --git a/src/fmg-components/save-progress-modal-question/save-progress-question/save-progress-question.vue b/src/fmg-components/save-progress-modal-question/save-progress-question/save-progress-question.vue new file mode 100644 index 000000000..221aa3415 --- /dev/null +++ b/src/fmg-components/save-progress-modal-question/save-progress-question/save-progress-question.vue @@ -0,0 +1,62 @@ + + + diff --git a/src/fmg-components/save-progress-popup-question/save-progress-popup-question.spec.js b/src/fmg-components/save-progress-popup-question/save-progress-popup-question.spec.js new file mode 100644 index 000000000..e2ca84f1d --- /dev/null +++ b/src/fmg-components/save-progress-popup-question/save-progress-popup-question.spec.js @@ -0,0 +1,51 @@ +import { mount, shallowMount } from "@vue/test-utils"; +import saveProgressPopupQuestion from "./save-progress-popup-question"; +import { getMountOptions } from "@/helpers/unit-test-helper.js"; + +jest.mock("@/digital-components/modal/modal", () => ({ + methods: { + closeModal: jest.fn(), + resetButtonStyle: jest.fn(), + }, +})); + +describe("save-progress-popup-question ", () => { + describe("when closeModal is run ", () => { + test("the modal should close ", () => { + // Arrange + const { wrapper } = setupMocks({ + props: { + modelValue: "", + modalWidgetName: "testModal", + }, + }); + + // Act + wrapper.vm.closeModal(); + + // Assert + expect(wrapper.vm.modal).not.toBeNull(); + }); + }); +}); + +function setupMocks({ options, props }) { + const mountOptions = getMountOptions({ + ...options, + }); + + const mockBaseMixin = { + methods: { + getCmsContent: jest.fn(), + dispatchStoreAction: jest.fn(), + }, + }; + + if (props) mountOptions.propsData = props; + + mountOptions.global.mixins = [mockBaseMixin]; + + const wrapper = mount(saveProgressPopupQuestion, mountOptions); + + return { wrapper }; +} diff --git a/src/fmg-components/save-progress-popup-question/save-progress-popup-question.vue b/src/fmg-components/save-progress-popup-question/save-progress-popup-question.vue new file mode 100644 index 000000000..5fbc95827 --- /dev/null +++ b/src/fmg-components/save-progress-popup-question/save-progress-popup-question.vue @@ -0,0 +1,228 @@ + + + + + diff --git a/src/helpers/heritage-integration/order-helper.js b/src/helpers/heritage-integration/order-helper.js index 555c41325..76729e49f 100644 --- a/src/helpers/heritage-integration/order-helper.js +++ b/src/helpers/heritage-integration/order-helper.js @@ -98,6 +98,25 @@ export async function submitWorkOrder({ }); } +export async function saveQuote({ pageNameToLog }) { + await baseMixin.methods.dispatchStoreActionWithLogging( + storeActions.SAVE_QUOTE, + {}, + pageNameToLog, + false + ); + + // save session back to sv2 so the email gets saved just in case they refresh or step away and come back later + await saveSession({ + pageNameToLog: pageNameToLog, + shouldAwaitSaveSessionQueue: true, + submitAfterSave: false, + createUnscheduledStatusWorkOrderForPIA: false, + }); + + return; +} + // PRIVATE FUNCTIONS // /* diff --git a/src/layouts/capability-questions/capability-questions.vue b/src/layouts/capability-questions/capability-questions.vue index 4d637c978..9d51ec2f7 100644 --- a/src/layouts/capability-questions/capability-questions.vue +++ b/src/layouts/capability-questions/capability-questions.vue @@ -12,13 +12,20 @@ @forwardButtonAction="forwardButtonAction" @back-click="navigateBack" :key="currentGlassIndex" - :index="currentGlassIndex" /> + :index="currentGlassIndex"> + + + diff --git a/src/layouts/molding-questions/molding-questions.vue b/src/layouts/molding-questions/molding-questions.vue index 00df8c04b..2f2cc346c 100644 --- a/src/layouts/molding-questions/molding-questions.vue +++ b/src/layouts/molding-questions/molding-questions.vue @@ -12,13 +12,20 @@ @forwardButtonAction="forwardButtonAction" @back-click="navigateBack" :key="currentGlassIndex" - :index="currentGlassIndex" /> + :index="currentGlassIndex"> + + + diff --git a/src/layouts/part-questions/part-questions.vue b/src/layouts/part-questions/part-questions.vue index bc1ec308e..a5441acd6 100644 --- a/src/layouts/part-questions/part-questions.vue +++ b/src/layouts/part-questions/part-questions.vue @@ -12,13 +12,20 @@ @forwardButtonAction="forwardButtonAction" @back-click="navigateBack" :key="currentGlassIndex" - :index="currentGlassIndex" /> + :index="currentGlassIndex"> + + + diff --git a/src/layouts/quote/quote.spec.js b/src/layouts/quote/quote.spec.js index 778ce0ce8..deca0d6d1 100644 --- a/src/layouts/quote/quote.spec.js +++ b/src/layouts/quote/quote.spec.js @@ -121,6 +121,9 @@ store.getters = { isInsurance: false, parentAccountNumber: applicationConfig.CASH_PARENT_ACCOUNT_NUMBER, }, + customer: { + emailAddress: "test@test.com", + }, }, }; @@ -136,6 +139,9 @@ afterEach(() => { insuranceCoverage: {}, isInsurance: false, }, + customer: { + emailAddress: "test@test.com", + }, }; }); @@ -263,6 +269,9 @@ describe("quote.vue", () => { glassParts: ["item", "item2"], }, payment: {}, + customer: { + emailAddress: "test@test.com", + }, serviceLocation: { zipCode: "12345", zipCodeCtu: "value", @@ -309,6 +318,9 @@ describe("quote.vue", () => { glassParts: ["item", "item2"], }, payment: {}, + customer: { + emailAddress: "test@test.com", + }, serviceLocation: { zipCode: "12345", zipCodeCtu: "value", @@ -355,6 +367,9 @@ describe("quote.vue", () => { payment: { isInsurance: null, }, + customer: { + emailAddress: "test@test.com", + }, serviceLocation: { state: null, }, @@ -396,6 +411,9 @@ describe("quote.vue", () => { payment: { isInsurance: null, }, + customer: { + emailAddress: "test@test.com", + }, serviceLocation: { state: null, }, @@ -438,6 +456,9 @@ describe("quote.vue", () => { payment: { isInsurance: true, }, + customer: { + emailAddress: "test@test.com", + }, serviceLocation: { state: null, }, @@ -480,6 +501,9 @@ describe("quote.vue", () => { payment: { isInsurance: false, }, + customer: { + emailAddress: "test@test.com", + }, serviceLocation: { state: null, }, @@ -523,6 +547,9 @@ describe("quote.vue", () => { payment: { isInsurance: null, // Ensure that previous selection isn't overriding selection }, + customer: { + emailAddress: "test@test.com", + }, serviceLocation: { state: null, }, @@ -568,6 +595,9 @@ describe("quote.vue", () => { payment: { isInsurance: null, // Ensure that previous selection isn't overriding selection }, + customer: { + emailAddress: "test@test.com", + }, serviceLocation: { state: null, }, @@ -612,6 +642,9 @@ describe("quote.vue", () => { payment: { isInsurance: null, }, + customer: { + emailAddress: "test@test.com", + }, serviceLocation: { state: "AZ", }, @@ -717,6 +750,9 @@ describe("quote.vue", () => { isInsurance: true, inactivePromos: [], }, + customer: { + emailAddress: "test@test.com", + }, }, externalParameterState: { isExternalParameter: 1 }, externalParameterQuote: { diff --git a/src/layouts/quote/quote.vue b/src/layouts/quote/quote.vue index 6f3809304..366a9d2cc 100644 --- a/src/layouts/quote/quote.vue +++ b/src/layouts/quote/quote.vue @@ -1,8 +1,10 @@ @@ -100,6 +120,8 @@ import contentGroupModal from "@/fmg-components/content-group-modal/content-grou import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue"; import afterpayModalBanner from "@/layouts/quote/afterpay-modal-banner/afterpay-modal-banner"; import recalDisclaimer from "@/layouts/quote/recal-disclaimer/recal-disclaimer.vue"; +import saveProgressModalQuestion from "@/fmg-components/save-progress-modal-question/save-progress-modal-question"; +import saveProgressPopupQuestion from "@/fmg-components/save-progress-popup-question/save-progress-popup-question"; // Supporting files import baseMixin from "@/mixins/base-mixin.js"; @@ -134,6 +156,7 @@ import { packageNames } from "@/constants/package-names"; import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; import { containsRecalParts } from "@/helpers/recal-helper"; import { externalParameterStatus } from "@/constants/external-parameters"; +import { saveQuote } from "@/helpers/heritage-integration/order-helper.js"; defineRule("option-required", required(errorMessages.OPTION_REQUIRED)); @@ -183,6 +206,10 @@ export default { ]; const resultMap = await settleAllPromises(promiseResultMap); + const emailFromStore = store.getters.order.customer.emailAddress; + const isEmailInStoreOnPageLoad = emailFromStore?.length > 0; + const showSaveProgressPopup = isEmailInStoreOnPageLoad ? false : true; + const lineItems = deepClone(store.getters.order.lineItems); lineItems.vaps = lineItems.vaps ?? []; const nullSafeGlassParts = lineItems.glassParts ?? []; @@ -263,6 +290,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.addableVaps = addableVaps; vm.lineItems = lineItems; vm.availableLineItems = pricingResults; @@ -409,6 +438,8 @@ export default { servicePackage: null, holdParentAccountNumber: null, holdBillToAccountNumber: null, + isEmailInStoreOnPageLoad: null, + showSaveProgressPopup: null, }; }, computed: { @@ -495,6 +526,14 @@ export default { backButtonAction() { vehicleQuestionsMixin.methods.navigateBack(this); }, + saveProgress() { + this.isEmailInStoreOnPageLoad = true; + this.showSaveProgressPopup = false; + saveQuote({ pageNameToLog: "quote" }); + }, + closeSaveProgressPopup() { + this.showSaveProgressPopup = false; + }, forwardButtonAction() { this.dispatchStoreAction( this.storeActions.SAVE_PAYMENT_TYPE, @@ -680,10 +719,22 @@ export default { afterpayModalBanner, promoModalQuestion, recalDisclaimer, + saveProgressModalQuestion, + saveProgressPopupQuestion, }, }; +