From 39d1f9f26a26d414dc28edbc191c9adeeb08dd34 Mon Sep 17 00:00:00 2001 From: hiteshkumar87 Date: Tue, 6 May 2025 14:32:11 +0530 Subject: [PATCH 01/12] CASH-597 Heavy truck service location inshop pagination --- src/layouts/service-location/service-location.vue | 8 +++++++- .../service-location/shop-question/shop-question.vue | 10 +++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/layouts/service-location/service-location.vue b/src/layouts/service-location/service-location.vue index 7b3163c87..2dfba347c 100644 --- a/src/layouts/service-location/service-location.vue +++ b/src/layouts/service-location/service-location.vue @@ -111,7 +111,8 @@ :selectedAppointmentType="selectedAppointmentType" :shopProviderData="shopProviderData" :isDisplayed="isShopQuestionDisplayed" - cmsWidgetName="ShopQuestionWidget" /> + cmsWidgetName="ShopQuestionWidget" + :isInshopOnly="isInshopOnly" /> @@ -413,6 +414,11 @@ export default { isNoComp() { return store.getters.order.policy.isNoComp; }, + isInshopOnly() { + return ( + this.isServiceableInshop && !this.isServiceableMobile && !this.isServiceableDropoff + ); + }, }, methods: { arePagePrerequisitesValid() { diff --git a/src/layouts/service-location/shop-question/shop-question.vue b/src/layouts/service-location/shop-question/shop-question.vue index 417a3b4c4..76d59be7c 100644 --- a/src/layouts/service-location/shop-question/shop-question.vue +++ b/src/layouts/service-location/shop-question/shop-question.vue @@ -77,6 +77,7 @@ export default { cmsWidgetName: String, validationRules: String, isDisplayed: Boolean, + isInshopOnly: Boolean, }, computed: { questionText() { @@ -233,10 +234,13 @@ export default { // nothing to do with this component. We could mitigate this by showing / hiding this component with v-if but that messes // up the component initialization on page load. async handler() { - this.resetAnswers(); + //We do not need to run this handler in case of Inshop only else it will ended running twice function getNextShopsFromList and will cause pagination + if (!this.isInshopOnly) { + this.resetAnswers(); - await nextTick(); - this.getNextShopsFromList(); + await nextTick(); + this.getNextShopsFromList(); + } }, }, shopProviders: { From 168a948d04d8d3331286b0636e93fb8d70c12b73 Mon Sep 17 00:00:00 2001 From: Minojhini Valaiyapathi Date: Tue, 6 May 2025 13:04:42 -0400 Subject: [PATCH 02/12] CASH-535 - New field needed for datalayer (CashSubtotal) --- src/constants/analytics.js | 15 +++++++++++++-- src/constants/store-mutations.js | 1 + src/layouts/quote/quote.vue | 17 ++++++++--------- .../service-package-question.vue | 9 +++++++++ src/mixins/analytics-mixin.js | 8 ++++++++ src/store/index.js | 4 ++++ 6 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/constants/analytics.js b/src/constants/analytics.js index f6534cda4..3461d82d1 100644 --- a/src/constants/analytics.js +++ b/src/constants/analytics.js @@ -23,7 +23,6 @@ const GaActions = { SUBMITTED: "Submitted", DISPLAYED: "Displayed", CASH_QUOTE_DISPLAYED: "Cash quote displayed", - SERVICE_PACKAGE_PRICE: "Service package price", MOBILE_AVAILABLE: "mobile_available", SHOPS_FIRST_DISPLAYED: "shops_first_displayed", MORE_LOCATIONS_CLICKED: "more_locations_clicked", @@ -43,4 +42,16 @@ const ValueToLogTypes = { LAST_5: "last_5", }; -export { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents, ValueToLogTypes }; +const Variables = { + CASH_SUBTOTAL: "cashSubTotal", +}; + +export { + analyticsPageEvents, + GaCategories, + GaActions, + GaLabels, + GaEvents, + ValueToLogTypes, + Variables, +}; diff --git a/src/constants/store-mutations.js b/src/constants/store-mutations.js index 2f53d8133..89c82e3c9 100644 --- a/src/constants/store-mutations.js +++ b/src/constants/store-mutations.js @@ -69,6 +69,7 @@ const storeMutations = { UPDATE_SETTLED_TENDER_AMOUNT: "updateSettledTenderAmount", UPDATE_IS_RECAL_ACK_OPT_IN: "updateIsRecalAckOptIn", UPDATE_IS_MSR_FEE_APPLICABLE: "updateIsMSRFeeApplicable", + UPDATE_CASH_SUBTOTAL: "updateCashSubTotal", // EVENT BUS MUTATIONS ADD_EVENT_TO_BUS: "addEventToBus", diff --git a/src/layouts/quote/quote.vue b/src/layouts/quote/quote.vue index 4a20b4dbc..c4ae1ff4c 100644 --- a/src/layouts/quote/quote.vue +++ b/src/layouts/quote/quote.vue @@ -789,8 +789,10 @@ export default { }); this.$nextTick(() => { const availablePackageNames = this.$refs.servicePackage?.servicePackageAnswers; - var eventLabel = ""; - availablePackageNames?.forEach((tier) => { + var priceLabel = ""; + + if (availablePackageNames.length > 1 || availablePackageNames.length == 1) { + var tier = availablePackageNames[0]; if (tier) { let lineItemsToPrice = this.availableLineItems; if (this.isRecalibrationOnOrder && this.shouldHideRecalibration) { @@ -815,14 +817,11 @@ export default { baseMixin.methods.filterOutFees(lineItemsToPrice) ); price += this.$refs.servicePackage.getVapsPrice(tier.value); - const formattedPrice = parseFloat(price).toFixed(2); - eventLabel += tier.value + "_" + formattedPrice; + priceLabel = price.toFixed(2); } - eventLabel += ","; - }); - eventLabel = eventLabel.slice(0, -1); - - this.pushEventToGA("quote", this.GaActions.SERVICE_PACKAGE_PRICE, eventLabel, true); + } + const subTotalLabel = this.Variables.CASH_SUBTOTAL; + this.pushVariableToDataLayer({ [subTotalLabel]: priceLabel }); }); }, async skip(insuranceSelection, packageSelection) { diff --git a/src/layouts/quote/service-package-question/service-package-question.vue b/src/layouts/quote/service-package-question/service-package-question.vue index 64c075408..10dfb2724 100644 --- a/src/layouts/quote/service-package-question/service-package-question.vue +++ b/src/layouts/quote/service-package-question/service-package-question.vue @@ -21,6 +21,8 @@ import servicePackageRadioExperiment from "@/experiment-components/service-packa import servicePackageRadioAfterpayExperiment from "@/experiment-components/service-package-radio-for-afterpay"; import { partTypeStrings } from "@/constants/part-type-strings"; import { packageNames } from "@/constants/package-names"; +import store from "@/store"; +import { storeMutations } from "@/constants/store-mutations"; import { shouldFrontWipersBeAvailable, shouldRearWipersBeAvailable, @@ -161,6 +163,13 @@ export default { isInsuranceSelected: this.isInsuranceSelected, }, })); + if (availablePackages.length > 1 || availablePackages.length == 1) { + let price = this.getPackagePrice(availablePackages[0].packageName, { + discountedPrice: false, + servicePackageDiscount: false, + }); + store.commit(storeMutations.UPDATE_CASH_SUBTOTAL, price?.toFixed(2)); + } this.$emit("currentNumberOfPackages", modifiedAnswers.length); return modifiedAnswers; }, diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index d6e608d4b..05d6f2632 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -30,6 +30,7 @@ import { getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper"; import { fmgPageValues } from "@/router/router-constants/fmgPage-values"; import router from "@/router/index.js"; import { getQuerystringParameter } from "@/helpers/querystring-helper"; +import { Variables } from "../constants/analytics"; export default { methods: { @@ -120,6 +121,10 @@ export default { } }, + async pushVariableToDataLayer(data) { + pushToDataLayerIfDefined(data); + }, + async pushPageViewToGA() { const currentPageName = getPageNameByQueryString(); const pageViewEvent = { @@ -770,6 +775,9 @@ export default { ValueToLogTypes() { return ValueToLogTypes; }, + Variables() { + return Variables; + }, }, }; diff --git a/src/store/index.js b/src/store/index.js index c36af8bb0..973108d3f 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -340,6 +340,9 @@ export const mutations = { updateIsMSRFeeApplicable(state, isMSRFeeApplicable) { state.order.isMSRFeeApplicable = isMSRFeeApplicable; }, + updateCashSubTotal(state, cashSubTotal) { + state.order.cashSubTotal = cashSubTotal; + }, updateCCToken(state, ccToken) { state.order.payment.ccToken.subscriptionId = ccToken.subscriptionId; state.order.payment.ccToken.expMonth = ccToken.expMonth; @@ -2339,6 +2342,7 @@ export const actions = { lockToken: order.lockToken, isRecalAckOptIn: order.isRecalAckOptIn, isMSRFeeApplicable: order.isMSRFeeApplicable, + cashSubTotal: order.cashSubTotal, }, }, logApiCall: true, From fc459f89cf929659fb03c938737a712cca8a7978 Mon Sep 17 00:00:00 2001 From: Minojhini Valaiyapathi Date: Tue, 6 May 2025 14:27:06 -0400 Subject: [PATCH 03/12] CASH-535 - Added null check --- src/layouts/quote/quote.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/layouts/quote/quote.vue b/src/layouts/quote/quote.vue index c4ae1ff4c..57c0178e3 100644 --- a/src/layouts/quote/quote.vue +++ b/src/layouts/quote/quote.vue @@ -791,7 +791,7 @@ export default { const availablePackageNames = this.$refs.servicePackage?.servicePackageAnswers; var priceLabel = ""; - if (availablePackageNames.length > 1 || availablePackageNames.length == 1) { + if (availablePackageNames?.length > 1 || availablePackageNames?.length == 1) { var tier = availablePackageNames[0]; if (tier) { let lineItemsToPrice = this.availableLineItems; @@ -820,8 +820,8 @@ export default { priceLabel = price.toFixed(2); } } - const subTotalLabel = this.Variables.CASH_SUBTOTAL; - this.pushVariableToDataLayer({ [subTotalLabel]: priceLabel }); + const subTotalLabel = this.Variables?.CASH_SUBTOTAL; + this.pushVariableToDataLayer?.({ [subTotalLabel]: priceLabel }); }); }, async skip(insuranceSelection, packageSelection) { From 63c9f8e184c8c235856979d910436adeacfe0dbe Mon Sep 17 00:00:00 2001 From: Bryan Mauger Date: Tue, 6 May 2025 15:42:14 -0400 Subject: [PATCH 04/12] CASH-313 Chat style updates. --- .../salesforce-webchat/salesforce-webchat.vue | 164 +++++++++++------- 1 file changed, 97 insertions(+), 67 deletions(-) diff --git a/src/digital-components/salesforce-webchat/salesforce-webchat.vue b/src/digital-components/salesforce-webchat/salesforce-webchat.vue index 975c639a8..84a06d241 100644 --- a/src/digital-components/salesforce-webchat/salesforce-webchat.vue +++ b/src/digital-components/salesforce-webchat/salesforce-webchat.vue @@ -65,77 +65,107 @@ export default { } } -.dockableContainer.showDockableContainer .sidebarBody { - button { - background: #0070d1; - color: #fff; - &:hover { - background: #06577c !important; +.dockableContainer { + &.showDockableContainer { + font-family: AvertaRegular; + color: #4d5151; + .sidebarBody { + button { + background: #0070d1; + color: #fff; + &:hover { + background: #06577c !important; + } + } + .form-element__help { + color: #db0020; + } + .embeddedServiceSidebarFormField { + .uiInput { + .uiLabel-left { + color: #4d5151; + } + } + } + .embeddedServiceSidebarButton { + &.uiButton--inverse { + .label { + color: #fff; + text-decoration: underline; + } + &:not(:disabled):focus { + background-color: #0070d1; + } + } + } + .embeddedServiceLiveAgentStateChatAvatar { + &.isLightningOutContext { + .agentIconColor0 { + background-color: #0070d1; + } + } + } + .embeddedServiceLiveAgentStateChatInputFooter { + .footerMenuWrapper { + .footer-menu-items { + .slds-dropdown__item { + > a { + padding-left: 0; + max-width: 100%; + display: flex; + justify-content: center; + } + } + } + } + } + .embeddedServiceLiveAgentStateChatInputFooter { + .footerMenuWrapper { + .footer-menu-items { + a { + margin: 0 auto; + } + } + } + } + } + .chatHeaderBranding { + background-color: #0070d1; + } + span { + &.required { + color: #db0020; + } } } - .embeddedServiceSidebarFormField .uiInput input { - border: 1px solid #8e9292; +} +.embeddedServiceHelpButton { + .helpButton { + .uiButton { + background-color: #0070d1 !important; + } } - .embeddedServiceSidebarButton.uiButton--inverse .label { - color: #fff; - text-decoration: underline; - } - .embeddedServiceLiveAgentStateChatAvatar.isLightningOutContext .agentIconColor0 { - background-color: #0070d1; - } - .embeddedServiceSidebarFormField .uiInput .uiLabel-left { - font-family: AvertaRegular; - color: #727676; - } - .form-element__help { - color: #db0020; - } - .embeddedServiceSidebarFormField .uiInput input { - border: 1px solid #06577c; - } - .embeddedServiceLiveAgentStateChatInputFooter - .footerMenuWrapper - .footer-menu-items - .slds-dropdown__item - > a { - padding-left: 0; - max-width: 100%; - display: flex; - justify-content: center; - } - .embeddedServiceLiveAgentStateChatInputFooter .footerMenuWrapper .footer-menu-items a { - margin: 0 auto; - } - .embeddedServiceSidebarButton.uiButton--inverse:not(:disabled):focus { - background-color: #0070d1; + .embeddedServiceIcon { + display: inline-block!important; } } - -.embeddedServiceHelpButton .helpButton .uiButton { - background-color: #0070d1 !important; -} - -.modalContainer.sidebarMinimized.layout-docked.embeddedServiceSidebar - .embeddedServiceSidebarMinimizedDefaultUI.helpButton, -.modalContainer.sidebarMinimized.layout-docked.embeddedServiceSidebar - .sidebarHeader.minimizedContainer.embeddedServiceSidebarMinimizedDefaultUI { - background-color: #0070d1; - border: 1px solid #0070d1; -} - -.dockableContainer.showDockableContainer .chatHeaderBranding { - background-color: #0070d1; -} - -.dockableContainer.showDockableContainer { - font-family: AvertaRegular; - color: #727676; -} - -.dockableContainer.showDockableContainer span.required { - color: #db0020; -} - -.dockableContainer.showDockableContainer .sidebarBody button:hover { +.modalContainer { + &.sidebarMinimized { + &.layout-docted { + &.embeddedServiceSidebar { + .embeddedServiceSidebarMinimizedDefaultUI { + .helpButton, + .sidebarHeader { + &.minimizedContainer { + .embeddedServiceSidebarMinimizedDefaultUI { + background-color: #0070d1; + border: 1px solid #0070d1; + } + } + } + } + } + } + } } From 30b290b4e92303ebe0b4a3ca32942c4ee52d5b8a Mon Sep 17 00:00:00 2001 From: Bryan Mauger Date: Tue, 6 May 2025 15:49:45 -0400 Subject: [PATCH 05/12] Format code. --- .../salesforce-webchat/salesforce-webchat.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/digital-components/salesforce-webchat/salesforce-webchat.vue b/src/digital-components/salesforce-webchat/salesforce-webchat.vue index 84a06d241..9022553a7 100644 --- a/src/digital-components/salesforce-webchat/salesforce-webchat.vue +++ b/src/digital-components/salesforce-webchat/salesforce-webchat.vue @@ -146,7 +146,7 @@ export default { } } .embeddedServiceIcon { - display: inline-block!important; + display: inline-block !important; } } .modalContainer { From 54cdb6f2cf6e4848864e79d9329b8e4d86a4e300 Mon Sep 17 00:00:00 2001 From: Adam Caouette Date: Wed, 7 May 2025 08:54:12 -0400 Subject: [PATCH 06/12] CASH-552: update selected date after second api date load --- src/layouts/schedule/schedule.vue | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/layouts/schedule/schedule.vue b/src/layouts/schedule/schedule.vue index b1e04a734..ae05b9358 100644 --- a/src/layouts/schedule/schedule.vue +++ b/src/layouts/schedule/schedule.vue @@ -375,6 +375,10 @@ export default { setTimeout(() => { this.$refs.datePicker.showAnotherMonth().then((moreSelectableDatesData) => { this.selectableDatesData = moreSelectableDatesData; + if (selectableDatesData?.days?.length > 0) { + this.selectedDate = selectableDatesData.days[0].date; + } + this.setDisplayWaitList(); }); }, 50); From e6910c574956e0828fee3a74304ded9fe886ae0f Mon Sep 17 00:00:00 2001 From: Bryan Mauger Date: Wed, 7 May 2025 09:53:52 -0400 Subject: [PATCH 07/12] CASH-313 style updates. --- .../salesforce-webchat/salesforce-webchat.vue | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/digital-components/salesforce-webchat/salesforce-webchat.vue b/src/digital-components/salesforce-webchat/salesforce-webchat.vue index 9022553a7..8cffc143f 100644 --- a/src/digital-components/salesforce-webchat/salesforce-webchat.vue +++ b/src/digital-components/salesforce-webchat/salesforce-webchat.vue @@ -68,40 +68,40 @@ export default { .dockableContainer { &.showDockableContainer { font-family: AvertaRegular; - color: #4d5151; + color: $gray-600; .sidebarBody { button { - background: #0070d1; - color: #fff; + background: $blue; + color: $white; &:hover { - background: #06577c !important; + background: $blue-700 !important; } } .form-element__help { - color: #db0020; + color: $red; } .embeddedServiceSidebarFormField { .uiInput { .uiLabel-left { - color: #4d5151; + color: $gray-600; } } } .embeddedServiceSidebarButton { &.uiButton--inverse { .label { - color: #fff; + color: $white; text-decoration: underline; } &:not(:disabled):focus { - background-color: #0070d1; + background-color: $blue; } } } .embeddedServiceLiveAgentStateChatAvatar { &.isLightningOutContext { .agentIconColor0 { - background-color: #0070d1; + background-color: $blue; } } } @@ -130,11 +130,11 @@ export default { } } .chatHeaderBranding { - background-color: #0070d1; + background-color: $blue; } span { &.required { - color: #db0020; + color: $red; } } } @@ -142,7 +142,7 @@ export default { .embeddedServiceHelpButton { .helpButton { .uiButton { - background-color: #0070d1 !important; + background-color: $blue !important; } } .embeddedServiceIcon { @@ -158,8 +158,8 @@ export default { .sidebarHeader { &.minimizedContainer { .embeddedServiceSidebarMinimizedDefaultUI { - background-color: #0070d1; - border: 1px solid #0070d1; + background-color: $blue; + border: 1px solid $blue; } } } From dd1852d4c3c50306655027564f41bcdff1e8562b Mon Sep 17 00:00:00 2001 From: Matt Caimi Date: Wed, 7 May 2025 10:15:45 -0400 Subject: [PATCH 08/12] CASH-376 fix for error message appearing when address modal is open --- src/digital-components/modal/modal.vue | 2 ++ .../mobile-location-modal-questions.vue | 2 ++ .../service-location/service-location.spec.js | 14 +++++++------- src/layouts/service-location/service-location.vue | 11 +++++++++-- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/digital-components/modal/modal.vue b/src/digital-components/modal/modal.vue index 74cfc0964..7b89b7e57 100644 --- a/src/digital-components/modal/modal.vue +++ b/src/digital-components/modal/modal.vue @@ -119,12 +119,14 @@ export default { }, onModalOpened() { this.onModalOpenedCallback?.(); + this.$emit("isModalOpened", true); }, onModalClosed() { this.resetButtonStyle(); this.onModalClosedCallback?.(); if (this.suppressPageScroll) document.querySelector("body").classList.remove("prevent-modal-scroll"); + this.$emit("isModalOpened", false); }, openModal() { if (this.suppressPageScroll) diff --git a/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue b/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue index ac597c72b..c72f7d4dd 100644 --- a/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue +++ b/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue @@ -97,6 +97,7 @@ export default { "updated-contains-military-base", "updated-bill-to-account-number", "setMobileLocationAndProceed", + "updated-modal-opened", ], data() { return { @@ -228,6 +229,7 @@ export default { }, setModalStatus(isOpened) { this.isModalOpened = isOpened; + this.$emit("updated-modal-opened", isOpened); }, closeModal() { this.modal.closeModal(); diff --git a/src/layouts/service-location/service-location.spec.js b/src/layouts/service-location/service-location.spec.js index 3b20c69f0..3a499b2ca 100644 --- a/src/layouts/service-location/service-location.spec.js +++ b/src/layouts/service-location/service-location.spec.js @@ -343,7 +343,7 @@ describe("service-location.vue", () => { zipCode: "43054", }, isVehicleProtected: true, - isMobileSelected: true, + isMobileSelectedAndModalClosed: true, }; wrapper.vm.mobileLocationQuestions = mobileLocationQuestions; @@ -356,7 +356,7 @@ describe("service-location.vue", () => { zipCode: "61606", }, isVehicleProtected: null, - isMobileSelected: false, + isMobileSelectedAndModalClosed: false, }; // Act @@ -459,7 +459,7 @@ describe("service-location.vue", () => { zipCode: "", }, isVehicleProtected: null, - isMobileSelected: false, + isMobileSelectedAndModalClosed: false, }; wrapper.vm.mobileLocationQuestions = mobileLocationQuestions; const newMobileLocationQuestions = { @@ -471,7 +471,7 @@ describe("service-location.vue", () => { zipCode: "43054", }, isVehicleProtected: true, - isMobileSelected: true, + isMobileSelectedAndModalClosed: true, }; wrapper.vm.closeModalAction = jest.fn(); @@ -521,7 +521,7 @@ describe("service-location.vue", () => { zipCode: "", }, isVehicleProtected: null, - isMobileSelected: false, + isMobileSelectedAndModalClosed: false, mobileFeePart: null, }; wrapper.vm.mobileLocationQuestions = mobileLocationQuestions; @@ -535,7 +535,7 @@ describe("service-location.vue", () => { zipCode: "43081", }, isVehicleProtected: true, - isMobileSelected: true, + isMobileSelectedAndModalClosed: true, mobileFeePart: null, }; @@ -599,7 +599,7 @@ describe("service-location.vue", () => { zipCode: "43081", }, isVehicleProtected: "YesAnswer", - isMobileSelected: true, + isMobileSelectedAndModalClosed: true, }; wrapper.vm.closeModalAction = jest.fn(); diff --git a/src/layouts/service-location/service-location.vue b/src/layouts/service-location/service-location.vue index 88cedcee2..ef27cbbb8 100644 --- a/src/layouts/service-location/service-location.vue +++ b/src/layouts/service-location/service-location.vue @@ -98,6 +98,7 @@ @updated-serviceability="setServiceabilityDetails" @updated-contains-military-base="setContainsMilitaryBase" @updated-mobile-ctu="setCtuForMobile" + @updated-modal-opened="setMobileLocationModalOpened" validationRules="mobile-location-required" ref="mobileLocationQuestions" linkWidgetName="MobileLocationLinkWidget" @@ -172,7 +173,7 @@ const MOBILE_FEE_PART_TYPE = "MOBILE FEE"; // DEFINE VALIDATION RULES defineRule("mobile-location-required", (value) => { if ( - value.isMobileSelected && + value.isMobileSelectedAndModalClosed && (!value.addressQuestions.streetAddress || !value.addressQuestions.city || !value.addressQuestions.state || @@ -211,6 +212,7 @@ export default { billToAccountNumber: null, shopProviderData: null, navigatingForward: false, + mobileLocationModalOpened: false, }; }, async beforeRouteEnter(to, from, next) { @@ -301,7 +303,9 @@ export default { zipCode: this.zipCode, }, isVehicleProtected: this.isVehicleProtected, - isMobileSelected: this.selectedAppointmentType === "Mobile", + isMobileSelectedAndModalClosed: + this.selectedAppointmentType === "Mobile" && + !this.mobileLocationModalOpened, }; }, }, @@ -502,6 +506,9 @@ export default { setRecycleFeePart(recycleFeePart) { this.recycleFeePart = recycleFeePart; }, + setMobileLocationModalOpened(isModalOpened) { + this.mobileLocationModalOpened = isModalOpened; + }, //creating async function as the computed property can not directly handle asynchronous operations or promises. async setMobileLocationQuestions(newValue) { var newZipCode = newValue.addressQuestions.zipCode; From 5aacb6ec2ac9efa0bc5ae70c0fe1a1543818bf07 Mon Sep 17 00:00:00 2001 From: Minojhini Valaiyapathi Date: Wed, 7 May 2025 12:57:30 -0400 Subject: [PATCH 09/12] CASH-535 - updated 'cashSubTotal' to be 'cashPriceSubTotal' and to match the logic of 'priceSubTotal' --- src/constants/analytics.js | 14 +------------- src/constants/store-mutations.js | 2 +- src/helpers/heritage-integration/order-helper.js | 16 ++++++++++++++++ .../service-package-question.vue | 8 +------- src/mixins/analytics-mixin.js | 12 ++++++++++++ src/store/index.js | 7 ++++--- 6 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/constants/analytics.js b/src/constants/analytics.js index 3461d82d1..fe43be06d 100644 --- a/src/constants/analytics.js +++ b/src/constants/analytics.js @@ -42,16 +42,4 @@ const ValueToLogTypes = { LAST_5: "last_5", }; -const Variables = { - CASH_SUBTOTAL: "cashSubTotal", -}; - -export { - analyticsPageEvents, - GaCategories, - GaActions, - GaLabels, - GaEvents, - ValueToLogTypes, - Variables, -}; +export { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents, ValueToLogTypes }; diff --git a/src/constants/store-mutations.js b/src/constants/store-mutations.js index 89c82e3c9..5064864d0 100644 --- a/src/constants/store-mutations.js +++ b/src/constants/store-mutations.js @@ -69,7 +69,7 @@ const storeMutations = { UPDATE_SETTLED_TENDER_AMOUNT: "updateSettledTenderAmount", UPDATE_IS_RECAL_ACK_OPT_IN: "updateIsRecalAckOptIn", UPDATE_IS_MSR_FEE_APPLICABLE: "updateIsMSRFeeApplicable", - UPDATE_CASH_SUBTOTAL: "updateCashSubTotal", + UPDATE_CASH_PRICE_SUBTOTAL: "updateCashPriceSubTotal", // EVENT BUS MUTATIONS ADD_EVENT_TO_BUS: "addEventToBus", diff --git a/src/helpers/heritage-integration/order-helper.js b/src/helpers/heritage-integration/order-helper.js index 76729e49f..11750108c 100644 --- a/src/helpers/heritage-integration/order-helper.js +++ b/src/helpers/heritage-integration/order-helper.js @@ -83,6 +83,22 @@ export async function saveSession({ if (!store.getters.applicationUser.savedSessionId || shouldAwaitSaveSessionQueue) { await saveSessionPromise; } + + const lineItems = store.getters.order?.lineItems ?? {}; + const combinedLineItems = [ + ...(lineItems.glassParts || []), + ...(lineItems.supportingItems || []), + ...(lineItems.vaps || []), + ...(lineItems.promos || []), + ]; + + const cashPriceSubtotal = combinedLineItems.length + ? baseMixin.methods + .getTotalPriceOfAllLineItemsAndChildParts(combinedLineItems, false) + .toFixed(2) + : ""; + + store.commit(storeMutations.UPDATE_CASH_PRICE_SUBTOTAL, cashPriceSubtotal); } export async function submitWorkOrder({ diff --git a/src/layouts/quote/service-package-question/service-package-question.vue b/src/layouts/quote/service-package-question/service-package-question.vue index 10dfb2724..546bb573a 100644 --- a/src/layouts/quote/service-package-question/service-package-question.vue +++ b/src/layouts/quote/service-package-question/service-package-question.vue @@ -163,13 +163,7 @@ export default { isInsuranceSelected: this.isInsuranceSelected, }, })); - if (availablePackages.length > 1 || availablePackages.length == 1) { - let price = this.getPackagePrice(availablePackages[0].packageName, { - discountedPrice: false, - servicePackageDiscount: false, - }); - store.commit(storeMutations.UPDATE_CASH_SUBTOTAL, price?.toFixed(2)); - } + this.$emit("currentNumberOfPackages", modifiedAnswers.length); return modifiedAnswers; }, diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index 05d6f2632..b1df7156c 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -311,6 +311,18 @@ export default { } else { payload.priceSubTotal = ""; } + + // Cash Quote or Cash Price Sub Total + if (isPricingAvailable) { + const subtotal = baseMixin.methods + .getTotalPriceOfAllLineItemsAndChildParts(combinedLineItems, false) + .toFixed(2); + + payload.cashPriceSubTotal = parseFloat(subtotal); + } else { + payload.cashPriceSubTotal = ""; + } + //unverified (in scenarios we don’t display the price) if ( order.payment.isInsurance && diff --git a/src/store/index.js b/src/store/index.js index 973108d3f..53ec6864e 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -340,8 +340,9 @@ export const mutations = { updateIsMSRFeeApplicable(state, isMSRFeeApplicable) { state.order.isMSRFeeApplicable = isMSRFeeApplicable; }, - updateCashSubTotal(state, cashSubTotal) { - state.order.cashSubTotal = cashSubTotal; + updateCashPriceSubTotal(state, cashPriceSubTotal) { + state.order.cashPriceSubTotal = + cashPriceSubTotal === "" ? null : cashPriceSubTotal.toString(); }, updateCCToken(state, ccToken) { state.order.payment.ccToken.subscriptionId = ccToken.subscriptionId; @@ -2342,7 +2343,7 @@ export const actions = { lockToken: order.lockToken, isRecalAckOptIn: order.isRecalAckOptIn, isMSRFeeApplicable: order.isMSRFeeApplicable, - cashSubTotal: order.cashSubTotal, + cashPriceSubTotal: order.cashPriceSubTotal, }, }, logApiCall: true, From b564f257860799c98afba2950e870e408b8fe348 Mon Sep 17 00:00:00 2001 From: Matt Caimi Date: Thu, 8 May 2025 09:24:08 -0400 Subject: [PATCH 10/12] CASH-593 pass mobile provider number to MSR fees check --- .../service-location-helper/service-location-helper.js | 2 ++ .../service-location-helper.spec.js | 10 ++++++++++ src/store/index.js | 6 +++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/layouts/service-location/helpers/service-location-helper/service-location-helper.js b/src/layouts/service-location/helpers/service-location-helper/service-location-helper.js index 106ed892c..a85b180f9 100644 --- a/src/layouts/service-location/helpers/service-location-helper/service-location-helper.js +++ b/src/layouts/service-location/helpers/service-location-helper/service-location-helper.js @@ -8,6 +8,7 @@ export async function getPricedMobileFeePart(serviceZipCode, pageNameToLog) { } const zipCodeData = await getZipCodeData(serviceZipCode); + const providerData = await getShopProviderData(serviceZipCode); // Get the Mobile Fee Part const mobileFeePart = await baseMixin.methods.dispatchStoreActionWithLogging( @@ -15,6 +16,7 @@ export async function getPricedMobileFeePart(serviceZipCode, pageNameToLog) { { serviceZipCode: serviceZipCode, serviceZipCodeCtu: zipCodeData.zipCodeCtu, + mobileProviderNumber: providerData.data.mobileProviderNumber, }, pageNameToLog, false diff --git a/src/layouts/service-location/helpers/service-location-helper/service-location-helper.spec.js b/src/layouts/service-location/helpers/service-location-helper/service-location-helper.spec.js index 5bb41c657..06f34f125 100644 --- a/src/layouts/service-location/helpers/service-location-helper/service-location-helper.spec.js +++ b/src/layouts/service-location/helpers/service-location-helper/service-location-helper.spec.js @@ -91,6 +91,7 @@ const mockStoreActionPriceOrderItemsAndSaveServerData = storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA; const mockStoreActionGetServiceabilityDetails = storeActions.GET_SERVICEABILITY_DETAILS; const mockStoreActionGetShopTimeSlots = storeActions.GET_SHOP_TIME_SLOTS; +const mockStoreActionGetProviders = storeActions.GET_PROVIDERS; const mockGetShopTimeSlotsGoodAvailability = { data: { @@ -236,6 +237,15 @@ jest.mock("@/mixins/base-mixin.js", () => ({ return Promise.resolve(mockGetShopTimeSlotsLowAvailability); } + + if (actionName === mockStoreActionGetProviders) { + return Promise.resolve({ + data: { + mobileProviderNumber: "001820", + shopProviders: [{}], + }, + }); + } }), }, })); diff --git a/src/store/index.js b/src/store/index.js index 53ec6864e..753d8509b 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1736,7 +1736,10 @@ export const actions = { return response; }, - getMobileFeePart(context, { payload: { serviceZipCode, serviceZipCodeCtu }, pageNameToLog }) { + getMobileFeePart( + context, + { payload: { serviceZipCode, serviceZipCodeCtu, mobileProviderNumber }, pageNameToLog } + ) { const serviceType = context.getters.damage.isRepair ? "Repair" : "Install"; const facilityType = "Mobile"; const parentAccountNumber = context.getters.payment.parentAccountNumber; @@ -1754,6 +1757,7 @@ export const actions = { order.serviceLocation?.zipCode; var providerNumber = + mobileProviderNumber ?? serviceZipCodeCtu ?? order.serviceLocation?.provider?.providerNumber ?? order.serviceLocation?.zipCodeCtu; From 69543c0cd3d14b2aa3b4c1c90df33b28ae7278ec Mon Sep 17 00:00:00 2001 From: Matt Caimi Date: Thu, 8 May 2025 11:24:24 -0400 Subject: [PATCH 11/12] CASH-376 disable forward nav button when modal is open --- src/layouts/service-location/service-location.vue | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/layouts/service-location/service-location.vue b/src/layouts/service-location/service-location.vue index ef27cbbb8..e10b5033f 100644 --- a/src/layouts/service-location/service-location.vue +++ b/src/layouts/service-location/service-location.vue @@ -119,7 +119,9 @@ From 8ddc94a88581ab0a21bd81a08daaba56b7ebe74f Mon Sep 17 00:00:00 2001 From: hiteshkumar87 Date: Thu, 8 May 2025 21:41:35 +0530 Subject: [PATCH 12/12] CASH-602 This is the edge case when we have only inshop service and change the zip for another inshop only services. appointment-type-question is not triggering because no changes were made to the serviceability-details that is causing Inshop service type to get auto select. --- .../appointment-type-question.vue | 21 ++++++++++++------- .../service-location/service-location.vue | 1 + .../shop-question/shop-question.vue | 10 +++------ 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/layouts/service-location/appointment-type-question/appointment-type-question.vue b/src/layouts/service-location/appointment-type-question/appointment-type-question.vue index 4c1c0d031..cc702529a 100644 --- a/src/layouts/service-location/appointment-type-question/appointment-type-question.vue +++ b/src/layouts/service-location/appointment-type-question/appointment-type-question.vue @@ -34,6 +34,7 @@ export default { isServiceableInshop: Boolean, isServiceableDropoff: Boolean, mobileFeeApplies: Boolean, + zipCode: String, }, computed: { questionText() { @@ -47,6 +48,7 @@ export default { const shouldShowInshop = this.isServiceableInshop; const shouldShowDropoff = this.isServiceableDropoff && !this.$store.getters.damage.isRepair; + const zipCode = this.zipCode; var answers = this.answersFromCms ? this.answersFromCms.filter((answer) => { @@ -87,12 +89,15 @@ export default { watch: { answersToDisplay: { handler(newValue) { - // If there is only one option to display and that option is 'Mobile' then select it - if ( - newValue.length == 1 && - newValue.findIndex((answer) => answer.Name == "Mobile") != -1 - ) { - this.selectedValue = "Mobile"; + // If there is only one option to display and that option is 'Mobile' or 'Inshop' then select it + if (newValue.length == 1) { + const name = newValue[0].Name; + if ( + name === AppointmentTypeStrings.MOBILE || + name === AppointmentTypeStrings.IN_SHOP + ) { + this.selectedValue = name; + } } }, immediate: true, @@ -100,14 +105,14 @@ export default { isMobileOnly: { handler(newValue) { if (newValue) { - this.selectedValue = "Mobile"; + this.selectedValue = AppointmentTypeStrings.MOBILE; } }, }, isInshopOnly: { handler(newValue) { if (newValue) { - this.selectedValue = "Inshop"; + this.selectedValue = AppointmentTypeStrings.IN_SHOP; } }, }, diff --git a/src/layouts/service-location/service-location.vue b/src/layouts/service-location/service-location.vue index 367211264..b161a6141 100644 --- a/src/layouts/service-location/service-location.vue +++ b/src/layouts/service-location/service-location.vue @@ -79,6 +79,7 @@ :isServiceableDropoff="isServiceableDropoff" :isDisplayed="isAppointmentTypeDisplayed" :mobileFeeApplies="mobileFeeApplies" + :zipCode="zipCode" ref="appointmentTypeQuestion" groupName="appointmentTypeQuestion" cmsWidgetName="AppointmentTypeQuestionWidget" diff --git a/src/layouts/service-location/shop-question/shop-question.vue b/src/layouts/service-location/shop-question/shop-question.vue index 76d59be7c..5e85b752b 100644 --- a/src/layouts/service-location/shop-question/shop-question.vue +++ b/src/layouts/service-location/shop-question/shop-question.vue @@ -234,13 +234,9 @@ export default { // nothing to do with this component. We could mitigate this by showing / hiding this component with v-if but that messes // up the component initialization on page load. async handler() { - //We do not need to run this handler in case of Inshop only else it will ended running twice function getNextShopsFromList and will cause pagination - if (!this.isInshopOnly) { - this.resetAnswers(); - - await nextTick(); - this.getNextShopsFromList(); - } + await nextTick(); + this.resetAnswers(); + this.getNextShopsFromList(); }, }, shopProviders: {