diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index 9adfc0613..efb32ffc6 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -25,7 +25,7 @@ const endpoints = { method: "GET", }, GetDamageOptions: { - url: "/parts/api/v1/parts/damage-options", + url: "/parts/api/v2/parts/damage-options", method: "GET", }, LookupVehicleByYmms: { diff --git a/src/fmg-components/cart/cart.vue b/src/fmg-components/cart/cart.vue index 9a6f55576..faa28376d 100644 --- a/src/fmg-components/cart/cart.vue +++ b/src/fmg-components/cart/cart.vue @@ -962,7 +962,8 @@ export default { return this.supportingItems.find( (lineItem) => lineItem.partType == partTypeStrings.MOBILE_FEE && - lineItem.partNumber == partNumberStrings.MOBILE_STATIC_RECAL_FEE + (lineItem.partNumber == partNumberStrings.MOBILE_STATIC_RECAL_FEE || + lineItem.partNumber == partNumberStrings.MOBILE_DUAL_RECAL_FEE) ); }, msrFeeCartItem() { diff --git a/src/helpers/damage-helper.js b/src/helpers/damage-helper.js index 1763ffbea..5c7a2de7c 100644 --- a/src/helpers/damage-helper.js +++ b/src/helpers/damage-helper.js @@ -103,7 +103,7 @@ export function getSideDoorGlassString(glassLocation, glassName) { export function getDamageInfoWordingText(damageInfo) { const glassTextArray = []; - damageInfo.glassToReplace.forEach((item) => { + damageInfo.glassToReplace?.forEach((item) => { let string = ""; if (item.glassLocation === glassLocations.WINDSHIELD) { string = item.glassLocation; diff --git a/src/helpers/pricing-helper.js b/src/helpers/pricing-helper.js index 9564b5c1f..fff87d82f 100644 --- a/src/helpers/pricing-helper.js +++ b/src/helpers/pricing-helper.js @@ -108,7 +108,9 @@ export async function getPricingByDayPartWithPrice(pageNameToLog) { export function getMSRFeePartPrice(supportingItems, includeTax) { let msrFeePrice = 0; const msrFeeLineItem = supportingItems?.find( - (lineItem) => lineItem.partNumber === partNumberStrings.MOBILE_STATIC_RECAL_FEE + (lineItem) => + lineItem.partNumber === partNumberStrings.MOBILE_STATIC_RECAL_FEE || + lineItem.partNumber === partNumberStrings.MOBILE_DUAL_RECAL_FEE ); if (msrFeeLineItem) { msrFeePrice = baseMixin?.methods?.getTotalPriceOfAllLineItemsAndChildParts( diff --git a/src/layouts/payment-adyen/payment-adyen.vue b/src/layouts/payment-adyen/payment-adyen.vue index 5b3a663ea..fc3ad668d 100644 --- a/src/layouts/payment-adyen/payment-adyen.vue +++ b/src/layouts/payment-adyen/payment-adyen.vue @@ -35,6 +35,7 @@ :insuranceCompanyName="insuranceCompanyName" :showInsuranceCoverageAs="showInsuranceCoverageAs" :isMSRFeeApplicable="isMSRFeeApplicable" + :IsMSRFeeCoveredByInsurance="isMSRFeeCoveredByInsurance" :isItac="isItac" :isNoComp="isNoComp" :isCollapsible="false" /> @@ -77,6 +78,7 @@ import { createAdyenCheckout } from "@/helpers/adyen-helper"; import { Dropin } from "@adyen/adyen-web/auto"; import { applicationConfig } from "@/constants/application-config"; import { paymentMethods } from "@/constants/payment-method-constants"; +import analyticsMixin from "@/mixins/analytics-mixin"; import "@adyen/adyen-web/styles/adyen.css"; import { mapAdyenToFmgPaymentMethod, mapFmgToAdyenPaymentMethod } from "../../helpers/adyen-helper"; @@ -133,6 +135,25 @@ export default { }, methods: { + async initializeAdyenWithErrorHandling() { + try { + await this.initializeAdyen(); + } catch (error) { + console.error("Failed to initialize Adyen payment:", error); + + const errorJson = JSON.stringify(error, Object.getOwnPropertyNames(Object(error))); + analyticsMixin.methods.pushFmgSessionData(errorJson); + await global.$logger.logError(errorJson); + + this.$router.navigateWithoutSaving( + this.navigationScenarios.PIA_ERROR, + this.pageName + ); + + return; + } + }, + async backButtonAction() { // Stub, for navigating back via nav-bar. this.$router.navigateWithoutSaving( @@ -203,6 +224,12 @@ export default { }, onError: (error, component) => { console.log(`Error from Adyen`); + const errorJson = JSON.stringify( + error, + Object.getOwnPropertyNames(Object(error)) + ); + analyticsMixin.methods.pushFmgSessionData(errorJson); + this.handleError(error); }, }, @@ -371,7 +398,12 @@ export default { await global.$logger.logError(stringToLog); - this.hasPaymentFailureError = true; + this.$router.navigateWithoutSaving( + this.navigationScenarios.PIA_ERROR, + this.pageName + ); + + return; } this.dropinComponent?.update(); @@ -560,13 +592,16 @@ export default { isMSRFeeApplicable() { return this.$store.getters.order.isMSRFeeApplicable; }, + isMSRFeeCoveredByInsurance() { + return this.$store.getters.order.isMSRFeeCoveredByInsurance; + }, lineItems() { return deepClone(this.$store.getters.lineItems); }, }, mounted() { - this.initializeAdyen(); + this.initializeAdyenWithErrorHandling(); }, components: { diff --git a/src/layouts/return-user/return-user.vue b/src/layouts/return-user/return-user.vue index ad307080d..6ce0b2528 100644 --- a/src/layouts/return-user/return-user.vue +++ b/src/layouts/return-user/return-user.vue @@ -47,6 +47,7 @@ import { Form } from "vee-validate"; import { getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper"; import { deleteFunnelCookie } from "@/helpers/heritage-integration/cookie-helper"; import { storeActions } from "@/constants/store-actions"; +import baseMixin from "@/mixins/base-mixin"; export default { name: "return-user", @@ -74,6 +75,13 @@ export default { startOverImage: null, }; }, + mounted() { + // The global baseMixin.mounted() skips hiding the loading modal when + // external-parameter state is active, but those parameters have no + // consumer on this interstitial. Clear that state and ensure the loader + // is hidden so the user can never get stuck behind it on this page. + baseMixin.methods.ResetExternalParamsAndHideModal(); + }, methods: { arePagePrerequisitesValid() { return getFunnelCookie() !== null; diff --git a/src/layouts/schedule/schedule.vue b/src/layouts/schedule/schedule.vue index c483c90df..6f2db110b 100644 --- a/src/layouts/schedule/schedule.vue +++ b/src/layouts/schedule/schedule.vue @@ -706,10 +706,16 @@ export default { isMobileStaticRecalibrationApplicable() { return ( this.displayMSR && - this.mobileFeePart?.partNumber == partNumberStrings.MOBILE_STATIC_RECAL_FEE && + this.isMSRFeePartNumber && (this.enableMSRSplitPay || this.isCashItacNoComp || this.mobileFeePart?.isInsurable) ); }, + isMSRFeePartNumber() { + return ( + this.mobileFeePart?.partNumber === partNumberStrings.MOBILE_STATIC_RECAL_FEE || + this.mobileFeePart?.partNumber === partNumberStrings.MOBILE_DUAL_RECAL_FEE + ); + }, displayMSR() { return ( experimentMixin.methods diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index 3ab575f58..1ea812c49 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -160,12 +160,16 @@ export default { // and aws FireHose stream, DigitalConsumer-Session-Firehose, to push data to an // S3 bucket, safelite-dev-digitalconsumer-session-data-us-east-2/1. // This bucket data is then picked up by snowflake for analytics use. - async pushFmgSessionData() { + async pushFmgSessionData(errorJson = null) { var currentPageName = getPageNameFromRouter(true); if (!currentPageName) { return; } + if (errorJson) { + currentPageName += ` ERROR: ${errorJson}`; + } + const hasSubmittedOrder = baseMixin.methods.hasSubmittedOrder(); const submittedOrder = baseMixin.methods.getSubmittedOrder(); const order = hasSubmittedOrder ? submittedOrder : store.getters.order; @@ -209,6 +213,7 @@ export default { var appointment = `${order?.schedule?.date ?? ""} ${order?.schedule?.startTime ?? ""}`; var sessionData = {}; + sessionData.currentPage = currentPageName; sessionData.sid = getSessionIdValue(); sessionData.deviceId = getDeviceIdValue(); @@ -931,6 +936,11 @@ function getPageNameFromRouter(useDefaultUrl = false) { router.currentRoute.value && router.currentRoute.value.name ) { + const query = router.currentRoute.value.query; + if (query && Object.keys(query).length > 0 && useDefaultUrl === true) { + const queryString = new URLSearchParams(query).toString(); + return `${router.currentRoute.value.name}?${queryString}`; + } return router.currentRoute.value.name; }