diff --git a/public/css/README.md b/public/css/README.md index f08e6c6d7..74cd82e39 100644 --- a/public/css/README.md +++ b/public/css/README.md @@ -3,11 +3,11 @@ This public/css folder uses a standalone .scss > .css setup. ## Usage -From a Terminal window, run the Sass Watch command on this folder only: +From a Terminal window, run the Sass Watch command from the public folder only: ```bash -sass --no-source-map --watch public/css/hop-styling.scss:public/css/hop-styling.css +sass --no-source-map --watch scss:css ``` -This will output a CSS file that can be consumed by the Payment page. +This will compile and output .css files (into the public/css folder) that can be consumed by the Payment page. -# Do not use the .css file on its own. You must use the .scss file and have Sass compile it to the .css file. +# Do not use the .css files on their own. You must use the .scss files and have Sass compile them to .css files. diff --git a/public/css/hop-styling.css b/public/css/hop-styling.css index ded3578c8..1f0b0ddc2 100644 --- a/public/css/hop-styling.css +++ b/public/css/hop-styling.css @@ -15,6 +15,7 @@ body .validation-info { } body .italicSmall { font-style: normal; + font-size: 14px; } body span { line-height: 24px; @@ -27,7 +28,7 @@ body .form-group { display: flex; flex-direction: column; } -body h2 { +body .headerlinecontainer .headerline1 { font-size: 20px; line-height: 32px; font-weight: 400; @@ -76,12 +77,12 @@ body .creditCardSpecific div { body .creditCardSpecific div .headerlinecontainer { padding: 0; } -body .creditCardSpecific div .headerlinecontainer .headerline5v2 { - display: none; -} body .creditCardSpecific div label { font-weight: 500; } +body .creditCardSpecific div:first-child { + display: none; +} body .creditCardSpecific #infoRow2 { margin-bottom: 0; } @@ -91,7 +92,7 @@ body #cardExpirationContainer { justify-content: space-between; padding: 0; } -@media (min-width: 368px) { +@media (min-width: 400px) { body #cardExpirationContainer { flex-direction: row; } @@ -116,7 +117,6 @@ body .optional-label:after { } body .buttonContainer { height: 48px; - margin-top: 36px; } body .buttonContainer button { position: relative; diff --git a/public/css/site-2020Styling.css b/public/css/site-2020Styling.css new file mode 100644 index 000000000..4aacb297d --- /dev/null +++ b/public/css/site-2020Styling.css @@ -0,0 +1,90 @@ +/* IMPORTANT: READ README.md in this folder! Do not edit .css file directly! */ +/* IMPORTANT: READ README.md in this folder! Do not edit .css file directly! */ +/* IMPORTANT: READ README.md in this folder! Do not edit .css file directly! */ +/* IMPORTANT: READ README.md in this folder! Do not edit .css file directly! */ +/* Style the modal when PayPal button is selected */ +#pageLoadingModal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.35); + display: flex; +} +#pageLoadingModal .modal-content { + margin: auto auto; +} +#pageLoadingModal p { + margin: 0; + text-transform: uppercase; + font-size: 14px; +} +#pageLoadingModal .modal { + display: none; + overflow: hidden; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + -webkit-overflow-scrolling: touch; + outline: 0; +} +#pageLoadingModal .modal-content { + position: relative; + background-color: #fff; + border-radius: 8px; + background-clip: padding-box; + outline: 0; + width: 168px; + height: 168px; + display: flex; + justify-content: center; + align-items: center; +} +#pageLoadingModal .modal-content:before, #pageLoadingModal .modal-content:after { + content: ""; + border-radius: 50%; + position: absolute; + inset: 0; + box-shadow: 0 0 3px 2px rgba(0, 0, 0, 0.25) inset; + top: 24px; + bottom: 24px; + right: 24px; + left: 24px; +} +#pageLoadingModal .modal-content:after { + box-shadow: 0 3px 0 #da291c inset; + animation: rotate 1s linear infinite; +} +#pageLoadingModal .modal-backdrop { + position: absolute; + top: 0; + right: 0; + left: 0; + background-color: #000000; +} +#pageLoadingModal .modal-backdrop.fade { + opacity: 0; + filter: alpha(opacity=0); +} +#pageLoadingModal.hide { + display: none; +} + +@keyframes rotate { + 0% { + transform: rotate(0); + } + 100% { + transform: rotate(360deg); + } +} +.creditCardSpecific { + display: none; +} diff --git a/public/css/hop-styling.scss b/public/scss/hop-styling.scss similarity index 95% rename from public/css/hop-styling.scss rename to public/scss/hop-styling.scss index 81b798b78..0e646a591 100644 --- a/public/css/hop-styling.scss +++ b/public/scss/hop-styling.scss @@ -16,6 +16,7 @@ body { .italicSmall { font-style: normal; + font-size: 14px; } span { @@ -32,11 +33,13 @@ body { flex-direction: column; } - h2 { - font-size: 20px; - line-height: 32px; - font-weight: 400; - margin: 24px 0; + .headerlinecontainer { + .headerline1 { + font-size: 20px; + line-height: 32px; + font-weight: 400; + margin: 24px 0; + } } input, @@ -86,13 +89,13 @@ body { padding: 8px 0; .headerlinecontainer { padding: 0; - .headerline5v2 { - display: none; - } } label { font-weight: 500; } + &:first-child { + display: none; + } } #infoRow2 { margin-bottom: 0; @@ -104,7 +107,7 @@ body { flex-direction: column; justify-content: space-between; padding: 0; - @media (min-width: 368px) { + @media (min-width: 400px) { flex-direction: row; .card_expirationYear, .card_expirationMonth { @@ -132,7 +135,6 @@ body { .buttonContainer { height: 48px; - margin-top: 36px; button { position: relative; background: linear-gradient(270deg, #1574a1 0%, #003d58 100%); diff --git a/public/scss/site-2020Styling.scss b/public/scss/site-2020Styling.scss new file mode 100644 index 000000000..4630bfdfc --- /dev/null +++ b/public/scss/site-2020Styling.scss @@ -0,0 +1,95 @@ +/* IMPORTANT: READ README.md in this folder! Do not edit .css file directly! */ +/* IMPORTANT: READ README.md in this folder! Do not edit .css file directly! */ +/* IMPORTANT: READ README.md in this folder! Do not edit .css file directly! */ +/* IMPORTANT: READ README.md in this folder! Do not edit .css file directly! */ + +/* Style the modal when PayPal button is selected */ +#pageLoadingModal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + width: 100%; + height: 100%; + background-color: rgba(0,0,0,.35); + display: flex; + + .modal-content { + margin: auto auto; + } + + p { + margin: 0; + text-transform: uppercase; + font-size: 14px; + } + + .modal { + display: none; + overflow: hidden; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + -webkit-overflow-scrolling: touch; + outline: 0; + } + + .modal-content { + position: relative; + background-color: #fff; + border-radius: 8px; + background-clip: padding-box; + outline: 0; + width: 168px; + height: 168px; + display: flex; + justify-content: center; + align-items: center; + &:before, + &:after { + content: ''; + border-radius: 50%; + position: absolute; + inset: 0; + box-shadow: 0 0 3px 2px rgba(0, 0, 0, 0.25) inset; + top: 24px; + bottom: 24px; + right: 24px; + left: 24px; + } + &:after { + box-shadow: 0 3px 0 #da291c inset; + animation: rotate 1s linear infinite; + } + } + + .modal-backdrop { + position: absolute; + top: 0; + right: 0; + left: 0; + background-color: #000000; + &.fade { + opacity: 0; + filter: alpha(opacity=0); + } + } + + &.hide { + display: none; + } +} + +@keyframes rotate { + 0% { transform: rotate(0)} + 100% { transform: rotate(360deg)} +} + +.creditCardSpecific { + display: none; +} diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js index 64e6df3aa..bb2ef16a0 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -89,9 +89,6 @@ const storeActions = { SAVE_PROMOS: "savePromos", SAVE_CUSTOMER_DETAILS: "saveCustomerDetails", SAVE_SERVICE_LOCATION_TECH_NOTES: "saveServiceLocationTechNotes", - SAVE_WORK_ORDER_FLAG: "saveWorkOrderFlag", - SAVE_PIA_ERROR_CODE: "savePiaErrorCode", - SAVE_PIA_WORK_ORDER: "savePiaWorkOrder", SAVE_CCTOKEN: "saveCCToken", SAVE_PAYPAL_TOKEN: "savePaypalToken", }; diff --git a/src/constants/store-mutations.js b/src/constants/store-mutations.js index 9e2e30a14..b7ac47d38 100644 --- a/src/constants/store-mutations.js +++ b/src/constants/store-mutations.js @@ -1,8 +1,5 @@ const storeMutations = { // PAYMENT MUTATIONS - UPDATE_WORK_ORDER_FLAG: "updateWorkOrderFlag", - UPDATE_PIA_ERROR_CODE: "updatePiaErrorCode", - UPDATE_PIA_WORK_ORDER: "updatePiaWorkOrder", UPDATE_CCTOKEN: "updateCCToken", UPDATE_PAYPAL_TOKEN: "updatePaypalToken", diff --git a/src/helpers/heritage-integration/order-helper.js b/src/helpers/heritage-integration/order-helper.js index 91f4768f3..01a5a780f 100644 --- a/src/helpers/heritage-integration/order-helper.js +++ b/src/helpers/heritage-integration/order-helper.js @@ -53,17 +53,30 @@ export async function loadSessionIfPresent(isConceptInsurance, pageNameToLog) { This will also set Referral information in the store after saving, and then update the cookie. To force synchronous behavior pass in 'true' for shouldAwaitSaveSessionQueue */ -export async function saveSession({ pageNameToLog, shouldAwaitSaveSessionQueue = false }) { +export async function saveSession({ + pageNameToLog, + shouldAwaitSaveSessionQueue = false, + submitAfterSave = false, + createDeleteStatusWorkOrderForPia = false, +}) { var saveSessionPromise; if (store.getters.applicationUser.saveSessionPromise) { // queue newest request after current saveSessionPromise resolves saveSessionPromise = store.getters.applicationUser.saveSessionPromise.then(() => { // get a new saveSessionPromise - return saveSessionHelper(pageNameToLog); + return saveSessionHelper( + pageNameToLog, + submitAfterSave, + createDeleteStatusWorkOrderForPia + ); }); } else { // create an initial saveSessionPromise - saveSessionPromise = saveSessionHelper(pageNameToLog); + saveSessionPromise = saveSessionHelper( + pageNameToLog, + submitAfterSave, + createDeleteStatusWorkOrderForPia + ); } store.commit(storeMutations.UPDATE_SAVE_SESSION_PROMISE, saveSessionPromise); // await here to allow for a caller to await and make the function synchronous @@ -72,10 +85,16 @@ export async function saveSession({ pageNameToLog, shouldAwaitSaveSessionQueue = } } -export async function submitWorkOrder({ pageNameToLog }) { +export async function submitWorkOrder({ + pageNameToLog, + submitAfterSave = false, + createDeleteStatusWorkOrderForPia = false, +}) { await saveSession({ pageNameToLog: pageNameToLog, shouldAwaitSaveSessionQueue: true, + submitAfterSave: submitAfterSave, + createDeleteStatusWorkOrderForPia: createDeleteStatusWorkOrderForPia, }); } @@ -117,10 +136,17 @@ async function loadSession( /* Encapsulates asynchronous Save Session logic inside a promise to allow for Save Session queuing */ -async function saveSessionHelper(pageNameToLog) { +async function saveSessionHelper( + pageNameToLog, + submitAfterSave = false, + createDeleteStatusWorkOrderForPia = false +) { const savedSessionInfo = await baseMixin.methods.dispatchStoreActionWithLogging( storeActions.SAVE_SESSION, - null, + { + submitAfterSave: submitAfterSave, + createDeleteStatusWorkOrderForPia: createDeleteStatusWorkOrderForPia, + }, pageNameToLog ); // Update the store with information received from the saveSession response diff --git a/src/helpers/heritage-integration/order-helper.spec.js b/src/helpers/heritage-integration/order-helper.spec.js index 322d9237d..daf9af6bb 100644 --- a/src/helpers/heritage-integration/order-helper.spec.js +++ b/src/helpers/heritage-integration/order-helper.spec.js @@ -179,7 +179,7 @@ describe("saveSession", () => { // Assert expect(mocks.baseMixin.methods.dispatchStoreActionWithLogging).toHaveBeenCalledWith( storeActions.SAVE_SESSION, - null, + { createDeleteStatusWorkOrderForPia: false, submitAfterSave: false }, "test" ); expect(mocks.baseMixin.methods.dispatchStoreAction).toHaveBeenCalledWith( @@ -282,12 +282,12 @@ describe("submitWorkOrder", () => { const mocks = setupMocksForJsFiles(mockData); // Act - await submitWorkOrder({ pageNameToLog: "test" }); + await submitWorkOrder({ pageNameToLog: "test", submitAfterSave: true }); // Assert expect(mocks.baseMixin.methods.dispatchStoreActionWithLogging).toHaveBeenCalledWith( storeActions.SAVE_SESSION, - null, + { createDeleteStatusWorkOrderForPia: false, submitAfterSave: true }, "test" ); expect(mocks.baseMixin.methods.dispatchStoreAction).toHaveBeenCalledWith( @@ -345,7 +345,7 @@ describe("submitWorkOrder", () => { setupCookies({ funnelCookieValue: JSON.stringify(testCookieValue) }); // Act - await submitWorkOrder({ pageNameToLog: "test" }); + await submitWorkOrder({ pageNameToLog: "test", submitAfterSave: true }); // Assert expect(cookieHelper.getFunnelCookie().DidHeritageFunnelUpdateLast).toEqual(false); diff --git a/src/layouts/confirmation/confirmation.vue b/src/layouts/confirmation/confirmation.vue index 5adcc5613..b958d5b9f 100644 --- a/src/layouts/confirmation/confirmation.vue +++ b/src/layouts/confirmation/confirmation.vue @@ -244,7 +244,7 @@ export default { diff --git a/src/layouts/payment-method/payment-method.spec.js b/src/layouts/payment-method/payment-method.spec.js index 803f10829..8cba373de 100644 --- a/src/layouts/payment-method/payment-method.spec.js +++ b/src/layouts/payment-method/payment-method.spec.js @@ -50,12 +50,13 @@ function setupMocks() { lineItems: [], order: { payment: { - piaErrorCode: "", + isPia: false, }, }, }; const mountOptions = getMountOptions({ + route: { query: {}, params: {} }, router: { navigate: jest.fn(), navigateWithSaving: jest.fn(), diff --git a/src/layouts/payment-method/payment-method.vue b/src/layouts/payment-method/payment-method.vue index 65e9c9f32..58dfdaead 100644 --- a/src/layouts/payment-method/payment-method.vue +++ b/src/layouts/payment-method/payment-method.vue @@ -35,7 +35,7 @@ @@ -262,7 +262,6 @@ export default { } }, backButtonAction() { - this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false); this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route); }, async forwardButtonAction() { @@ -276,42 +275,38 @@ export default { await this.dispatchStoreAction(storeActions.SAVE_PROMOS, this.lineItems.promos, false); - switch (this.paymentMethod) { - case paymentMethods.CREDIT_CARD: - this.setupPia(this.paymentMethod); - break; - case paymentMethods.PAYPAL: - this.setupPia(this.paymentMethod); - break; - case paymentMethods.AFTERPAY: - this.setupPia(this.paymentMethod); - break; - default: - this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false); - this.dispatchStoreAction(this.storeActions.SAVE_WORK_ORDER_FLAG, true, false); - await submitWorkOrder({ pageNameToLog: "payment-method" }); - this.$router.navigateWithoutSaving( - this.navigationScenarios.CLICKED_FORWARD, - this.$route - ); + if (this.paymentMethod == paymentMethods.LATER) { + // this creates the final work order + await submitWorkOrder({ pageNameToLog: "payment-method", submitAfterSave: true }); + this.$router.navigateWithoutSaving( + this.navigationScenarios.CLICKED_FORWARD, + this.$route + ); + } else { + this.setupPia(); } }, - async setupPia(payNowType) { + async setupPia() { this.$refs.loadingModal.showModal(); - // since we're leaving the site for pia, clear any save session promises that we will not be able to resolve when we return - await this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE); - - // make sure pia error codes are reset - this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false); - - // if we don't have a work order in delete substatus, set the flag and submit + // if we don't have a work order in delete substatus, set the flag and submit. + // we need a work order number for pia so we can pass it to safeliteHop. if (!store.getters.order.workOrderNumber) { - this.dispatchStoreAction(storeActions.SAVE_PIA_WORK_ORDER, true); - await submitWorkOrder({ pageNameToLog: "payment-method" }); - this.dispatchStoreAction(storeActions.SAVE_PIA_WORK_ORDER, false); + try { + await submitWorkOrder({ + pageNameToLog: "payment-method", + submitAfterSave: false, + createDeleteStatusWorkOrderForPia: true, + }); + } catch (error) { + console.log("error: response from pia submit work order:" + error.message); + this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE); + this.$route.params[this.routerParams.DISPLAY_PIA_ALERT] = true; + return; + } } + this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE); this.$router.navigateWithoutSaving( this.navigationScenarios.CLICKED_PAY_NOW, this.$route @@ -340,11 +335,8 @@ export default { isPiaDisabled() { const piaExperience = this.getSettingValue(experimentSettings.PIA_EXPERIENCE) === "PIA Optional"; - const enablePIA = - this.getSettingValue(experimentSettings.SUBMIT_ORDER_ENABLE_PIA) === "true"; - return false; - //return !(piaExperience && enablePIA); + return !piaExperience; }, paymentMethod() { if (this.isPiaDisabled) { @@ -353,8 +345,8 @@ export default { return this.paymentMethodInternalModel; }, - displayPiaAlert() { - return store.getters.order.payment.piaErrorCode ? true : false; + shouldDisplayPiaAlert() { + return this.$route.params[this.routerParams.DISPLAY_PIA_ALERT]; }, }, watch: { diff --git a/src/layouts/payment-pia-return/payment-pia-return.vue b/src/layouts/payment-pia-return/payment-pia-return.vue index aa9340995..54e49c620 100644 --- a/src/layouts/payment-pia-return/payment-pia-return.vue +++ b/src/layouts/payment-pia-return/payment-pia-return.vue @@ -14,6 +14,7 @@ import baseMixin from "@/mixins/base-mixin.js"; import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue"; import { Form } from "vee-validate"; import { paymentMethods } from "@/constants/payment-method-constants"; +import { routerParams } from "@/router/router-constants/router-params"; export default { name: "payment-pia-return", @@ -23,12 +24,12 @@ export default { if (piaError) { console.log("Error during payment: " + piaError); - baseMixin.methods.dispatchStoreAction( - storeActions.SAVE_PIA_ERROR_CODE, - piaError, - false + this.$router.navigateWithoutSaving( + this.navigationScenarios.PIA_ERROR, + this.$route, + {}, + { [routerParams.DISPLAY_PIA_ALERT]: true } ); - this.$router.navigateWithoutSaving(this.navigationScenarios.PIA_ERROR, this.$route); } else { switch (store.getters.order.payment.piaType) { case paymentMethods.CREDIT_CARD: @@ -41,14 +42,11 @@ export default { default: var msg = "Unknown Pia type: " + store.getters.order.payment.piaType; console.log(msg); - baseMixin.methods.dispatchStoreAction( - storeActions.SAVE_PIA_ERROR_CODE, - msg, - false - ); this.$router.navigateWithoutSaving( this.navigationScenarios.PIA_ERROR, - this.$route + this.$route, + {}, + { [routerParams.DISPLAY_PIA_ALERT]: true } ); } } @@ -60,16 +58,12 @@ export default { async processPaypalResponse() { const token = getQuerystringParameter(queryStrings.TOKEN); const payerId = getQuerystringParameter(queryStrings.PAYERID); - baseMixin.methods.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false); - baseMixin.methods.dispatchStoreAction(storeActions.SAVE_WORK_ORDER_FLAG, true, false); baseMixin.methods.dispatchStoreAction(storeActions.SAVE_PAYPAL_TOKEN, token, false); await this.saveAndSubmitWorkOrder(); }, async processCreditCardResponse() { const subscriptionID = getQuerystringParameter(queryStrings.SUBSCRIPTIONID); - baseMixin.methods.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false); - baseMixin.methods.dispatchStoreAction(storeActions.SAVE_WORK_ORDER_FLAG, true, false); const referralSeqNum = getQuerystringParameter(queryStrings.REFERRAL_SEQ_NUM); if (referralSeqNum != store.getters.order.referralSequenceNumber) { @@ -79,12 +73,12 @@ export default { " " + store.getters.order.referralSequenceNumber ); - baseMixin.methods.dispatchStoreAction( - storeActions.SAVE_PIA_ERROR_CODE, - "unknown error", - false + this.$router.navigateWithoutSaving( + this.navigationScenarios.PIA_ERROR, + this.$route, + {}, + { [routerParams.DISPLAY_PIA_ALERT]: true } ); - this.$router.navigateWithoutSaving(this.navigationScenarios.PIA_ERROR, this.$route); } else { const expMonth = getQuerystringParameter(queryStrings.CARD_EXPIRATION_MONTH); const expYear = getQuerystringParameter(queryStrings.CARD_EXPIRATION_YEAR); @@ -119,7 +113,23 @@ export default { async saveAndSubmitWorkOrder() { // Final work order submit after returning from PIA. await this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE); - await submitWorkOrder({ pageNameToLog: "payment-pia-return" }); + try { + await submitWorkOrder({ + pageNameToLog: "payment-pia-return", + submitAfterSave: true, + }); + } catch (error) { + console.log("error: response from submit work order:" + error.message); + this.$router.navigateWithoutSaving( + this.navigationScenarios.PIA_ERROR, + this.$route, + {}, + { [routerParams.DISPLAY_PIA_ALERT]: true } + ); + this.$refs.loadingModal.isModalVisible = false; + return; + } + this.$refs.loadingModal.isModalVisible = false; this.$router.navigateWithoutSaving(this.navigationScenarios.PIA_SUCCESS, this.$route); }, diff --git a/src/layouts/payment/payment.vue b/src/layouts/payment/payment.vue index 2d7b1248e..437fbe743 100644 --- a/src/layouts/payment/payment.vue +++ b/src/layouts/payment/payment.vue @@ -20,21 +20,52 @@ scrolling="no"> -
- +
{{ switchPaymentText }}
+
+
+
or
+
+ +
+
or
+
+ +
+
+ @@ -167,6 +199,7 @@ import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { settleAllPromises } from "@/helpers/layout-helper"; import { applicationConfig } from "@/constants/application-config"; import { paymentMethods } from "@/constants/payment-method-constants"; +import { AppointmentTypeStrings } from "@/constants/schedule-constants"; import baseMixin from "@/mixins/base-mixin.js"; // Validation @@ -234,6 +267,10 @@ export default { }); }, computed: { + dynamicCSSUrl() { + const { protocol, hostname, port } = window.location; + return `${protocol}//${hostname}:${port}/fmg/css/site-2020Styling.css`; + }, dynamicHopCSSUrl() { const { protocol, hostname, port } = window.location; return `${protocol}//${hostname}:${port}/fmg/css/hop-styling.css`; @@ -250,11 +287,71 @@ export default { } return ""; }, + switchPaymentText() { + return this.getCmsContent("PaymentMethodWidget", "QuestionText"); + }, }, methods: { arePagePrerequisitesValid() { - // TODO - return true; + // Line Items + const packageReqs = !!store.getters.order.lineItems.supportingItems; + + // Service Location + const serviceLocation = store.getters.order.serviceLocation; + const mobileReqs = !!( + serviceLocation.address && + serviceLocation.city && + serviceLocation.state && + serviceLocation.zipCode + ); + + const providerLocation = serviceLocation.provider.address; + const dropOffInshopReqs = !!( + providerLocation.streetAddress && + providerLocation.city && + providerLocation.state && + providerLocation.zipCode + ); + + const isMobile = serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE; + + const serviceLocationReqs = + (isMobile && mobileReqs) || (!isMobile && dropOffInshopReqs); + + // Insurance + const isInsuranceSet = store.getters.order.payment.isInsurance !== null; + + // Schedule + const schedule = store.getters.order.schedule; + const scheduleReqs = !!( + schedule.date && + schedule.startTime && + (!isMobile || schedule.endTime) && + schedule.jobMaxMinutes && + schedule.jobMinMinutes + ); + + // Customer + const customer = store.getters.order.customer; + const customerReqs = !!( + customer.firstName && + customer.lastName && + customer.phoneNumber && + customer.emailAddress + ); + + const paymentMethodReqs = + store.getters.order.payment.isPia !== null && + (store.getters.order.payment.isPia || !!store.getters.order.payment.piaType); + + return ( + packageReqs && + serviceLocationReqs && + isInsuranceSet && + scheduleReqs && + customerReqs && + paymentMethodReqs + ); }, getWOrkOrderNumber() { if (store.getters.order.workOrderNumber) { @@ -262,8 +359,6 @@ export default { return items[1]; } - // no work order, set pia error and return - this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, "NO WORK ORDER", false); this.backButtonAction(); }, getInvoiceNumber() { @@ -271,8 +366,6 @@ export default { return store.getters.order.workOrderNumber.replace("-", ""); } - // no work order, set pia error and return - this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, "NO WORK ORDER", false); this.backButtonAction(); }, getAddress1() { @@ -351,6 +444,28 @@ export default { window.location = applicationConfig.PIA_CANCEL_URL; //this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route); }, + switchPIAMethod(payMethod) { + this.paymentType = payMethod; + this.submitHopForm(); + }, + submitHopForm() { + this.$nextTick(() => { + this.$refs.hopForm.submit(); + + const iframe = this.$refs.paymentFrame; + if (iframe) { + iframe.onload = () => { + if (iframe.contentWindow) { + if (this.paymentType == "cc") { + iframe.contentWindow.postMessage("CreditCardChosen", "*"); + } else if (this.paymentType == "ap") { + iframe.contentWindow.postMessage("afterpay", "*"); + } + } + }; + } + }); + }, async fetchSignatureInfo(signatureInfo) { if (this.isPaypal()) { this.$refs.loadingModal.showModal(); @@ -360,20 +475,7 @@ export default { this.authSignature = signatureInfo.signature; this.authSignatureStart = signatureInfo.startDate; - this.$nextTick(() => { - this.$refs.myForm.submit(); - - if (this.paymentType == "ap") { - const iframe = this.$refs.paymentFrame; - if (iframe) { - iframe.onload = () => { - if (iframe.contentWindow) { - iframe.contentWindow.postMessage("afterpay", "*"); - } - }; - } - } - }); + this.submitHopForm(); }, }, components: { @@ -387,8 +489,11 @@ export default { diff --git a/src/router/router-constants/router-params.js b/src/router/router-constants/router-params.js index 3bebdc33e..ae7b88283 100644 --- a/src/router/router-constants/router-params.js +++ b/src/router/router-constants/router-params.js @@ -1,5 +1,6 @@ const routerParams = { DISPLAY_VEHICLE_CHANGE_ALERT: "displayVehicleChangeAlert", + DISPLAY_PIA_ALERT: "displayPiaAlert", }; export { routerParams }; diff --git a/src/store/index.js b/src/store/index.js index 2cddcc849..46c36c263 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -28,7 +28,6 @@ import { getDateDifferenceInDays } from "@/helpers/date-helper"; // Export State const getDefaultState = () => { return { - submitAfterSave: false, order: { vehicle: { year: null, @@ -97,7 +96,6 @@ const getDefaultState = () => { parentAccountNumber: 0, isPia: null, piaType: null, - piaErrorCode: null, inactivePromos: null, paypalToken: null, ccToken: { @@ -128,7 +126,6 @@ const getDefaultState = () => { referralCorrelationId: null, eon: null, workOrderNumber: null, - createDeleteStatusWorkOrderForPia: false, }, applicationUser: { eventBus: [], @@ -148,10 +145,6 @@ export const state = getDefaultState(); // Export Mutations export const mutations = { - updateWorkOrderFlag(state, submitAfterSave) { - state.submitAfterSave = submitAfterSave; - }, - // VEHICLE MUTATIONS updateYear(state, year) { state.order.vehicle.year = year; @@ -249,12 +242,6 @@ export const mutations = { updatePiaType(state, piaType) { state.order.payment.piaType = piaType; }, - updatePiaErrorCode(state, piaErrorCode) { - state.order.payment.piaErrorCode = piaErrorCode; - }, - updatePiaWorkOrder(state, piaWorkOrder) { - state.order.createDeleteStatusWorkOrderForPia = piaWorkOrder; - }, updateWorkOrderNumber(state, workOrderNumber) { state.order.workOrderNumber = workOrderNumber; }, @@ -570,7 +557,6 @@ export const mutations = { // Export Getters export const getters = { - submitAfterSave: (state) => state.submitAfterSave, vehicle: (state) => state.order.vehicle, eventBusItem: (state) => (eventCategory, eventSubCategory) => { const matchedEvent = state.applicationUser.eventBus.find( @@ -697,6 +683,7 @@ export const getters = { }, experimentSettings: (state) => state.applicationUser.experiments + .filter((x) => !!x.isActive) .map((x) => x.settings) .reduce((r, c) => Object.assign(r, c), {}) ?? {}, }; @@ -1581,13 +1568,17 @@ export const actions = { }, // Session API Actions - saveSession(context, { pageNameToLog }) { + saveSession(context, { pageNameToLog, payload }) { const vehicle = context.getters.vehicle; const damage = context.getters.damage; const order = context.state.order; const applicationUser = context.getters.applicationUser; const lineItems = context.state.order.lineItems; + const submitAfterSave = payload?.submitAfterSave === "true"; + const createDeleteStatusWorkOrderForPia = + payload?.createDeleteStatusWorkOrderForPia === "true"; + // create a new array to avoid mutating state const newGlassToReplace = convertGlassPieceNamingForApi(damage.glassToReplace); @@ -1595,7 +1586,7 @@ export const actions = { method: endpoints.SaveSession.method, endpoint: endpoints.SaveSession.url, payload: { - submitAfterSave: context.state.submitAfterSave, + submitAfterSave: submitAfterSave, userAgent: navigator.userAgent, applicationUser: { crmCustomerId: applicationUser.crmCustomerId, @@ -1706,7 +1697,7 @@ export const actions = { referralNumber: order.referralNumber?.toString(), referralSequenceNumber: order.referralSequenceNumber, eon: order.eon, - createDeleteStatusWorkOrderForPia: order.createDeleteStatusWorkOrderForPia, + createDeleteStatusWorkOrderForPia: createDeleteStatusWorkOrderForPia, }, }, logApiCall: true, @@ -1777,15 +1768,6 @@ export const actions = { ); }, - saveWorkOrderFlag(context, submitAfterSave) { - context.commit(storeMutations.UPDATE_WORK_ORDER_FLAG, submitAfterSave); - }, - savePiaErrorCode(context, piaErrorCode) { - context.commit(storeMutations.UPDATE_PIA_ERROR_CODE, piaErrorCode); - }, - savePiaWorkOrder(context, piaWorkOrder) { - context.commit(storeMutations.UPDATE_PIA_WORK_ORDER, piaWorkOrder); - }, saveCCToken(context, ccToken) { context.commit(storeMutations.UPDATE_CCTOKEN, ccToken); }, diff --git a/src/store/store.spec.js b/src/store/store.spec.js index b0cd5154a..e7c004090 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -2791,17 +2791,6 @@ describe("Actions", () => { }); describe("Getters", () => { - it("submitAfterSave, should return true", () => { - // Arrange - const storeState = state; - - // Act - mutations.updateWorkOrderFlag(storeState, true); - - // Assert - expect(getters.submitAfterSave(storeState)).toEqual(true); - }); - it("Vehicle getter, should return vehicle data", () => { // Arrange const storeState = state;