From f7bb4f3736103c1c0048124e640eb7573a8176b7 Mon Sep 17 00:00:00 2001 From: Johnny shultz Date: Mon, 23 Jun 2025 17:31:53 -0400 Subject: [PATCH 01/61] CASH-816 CASH-816 progress so far --- .../service-location/service-location.vue | 28 +- .../service-zip-modal-question.vue | 2 + .../service-zip-question.vue | 11 +- .../shop-question/shop-question-popup.vue | 615 ++++++++++++++++++ .../shop-question/shop-question.vue | 24 +- 5 files changed, 670 insertions(+), 10 deletions(-) create mode 100644 src/layouts/service-location/shop-question/shop-question-popup.vue diff --git a/src/layouts/service-location/service-location.vue b/src/layouts/service-location/service-location.vue index 6893ad9b4..f9787a0e0 100644 --- a/src/layouts/service-location/service-location.vue +++ b/src/layouts/service-location/service-location.vue @@ -107,14 +107,27 @@ @setMobileLocation="setMobileLocationQuestions" @set-mobile-location-invalid="setMobileLocationInValid" /> - + cmsWidgetName="YourSafeliteShopWidget" /> --> + + + + @@ -137,6 +150,7 @@ import serviceZipModalQuestion from "@/layouts/service-location/service-zip-moda import mobileLocationModalQuestions from "@/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions"; import appointmentTypeQuestion from "@/layouts/service-location/appointment-type-question/appointment-type-question"; import shopQuestion from "@/layouts/service-location/shop-question/shop-question"; +import shopQuestionPopup from "@/layouts/service-location/shop-question/shop-question-popup"; import funnelHeader from "@/fmg-components/funnel-header/funnel-header"; import navbar from "@/fmg-components/nav-bar/nav-bar"; @@ -763,6 +777,13 @@ export default { setMobileLocationInValid(isMobileLocationInValid) { this.isMobileAddressValid = !isMobileLocationInValid; }, + onShopSelected(providerNumber) { + // Find the provider object from your shopProviderData + const selectedProvider = this.shopProviderData.shopProviders.find( + (p) => p.providerNumber === providerNumber + ); + this.selectedProvider = selectedProvider; + }, }, watch: { zipCode: { @@ -814,7 +835,8 @@ export default { Form, loadingModal, contentGroupModal, - shopQuestion, + //shopQuestion, + shopQuestionPopup, }, }; diff --git a/src/layouts/service-location/service-zip-modal-question/service-zip-modal-question.vue b/src/layouts/service-location/service-zip-modal-question/service-zip-modal-question.vue index 5e7aa73e4..0bbfbadf4 100644 --- a/src/layouts/service-location/service-zip-modal-question/service-zip-modal-question.vue +++ b/src/layouts/service-location/service-zip-modal-question/service-zip-modal-question.vue @@ -49,6 +49,7 @@ import textLink from "@/ux-components/text-link/text-link"; import serviceZipQuestion from "@/layouts/service-location/service-zip-modal-question/service-zip-question/service-zip-question"; import modal from "@/digital-components/modal/modal"; import alert from "@/ux-components/alert/alert"; +import shopQuestion from "@/layouts/service-location/shop-question/shop-question"; import { getPricedMobileFeePart, @@ -245,6 +246,7 @@ export default { serviceZipQuestion, modal, alert, + // shopQuestion }, }; diff --git a/src/layouts/service-location/service-zip-modal-question/service-zip-question/service-zip-question.vue b/src/layouts/service-location/service-zip-modal-question/service-zip-question/service-zip-question.vue index 9841c5b16..a0a43dc14 100644 --- a/src/layouts/service-location/service-zip-modal-question/service-zip-question/service-zip-question.vue +++ b/src/layouts/service-location/service-zip-modal-question/service-zip-question/service-zip-question.vue @@ -8,8 +8,8 @@ cornerStyle="rounded" mask="#####" :displayQuestionText="false" - isRequired - validationRules="zip-required|zip-format" /> + :isRequired="isRequired" + :validationRules="computedValidationRules" /> + + diff --git a/src/layouts/service-location/shop-question/shop-question.vue b/src/layouts/service-location/shop-question/shop-question.vue index 5a9278d78..d1dc3a24c 100644 --- a/src/layouts/service-location/shop-question/shop-question.vue +++ b/src/layouts/service-location/shop-question/shop-question.vue @@ -20,7 +20,7 @@ v-model="selectedProviderNumber" isRequired validationRules="option-required" /> - + @click-event="getNextShopsFromList(1)" + :aria-label="showMoreShopsLinkText" /> --> @@ -41,6 +41,8 @@ import alert from "@/ux-components/alert/alert"; import buttonQuestion from "@/digital-components/button-question/button-question"; import shopListButton from "./shop-list-button/shop-list-button"; import textLink from "@/ux-components/text-link/text-link"; +//import shopQuestionPopup from "@/layouts/service-location/shop-question/shop-question-popup"; +//import serviceZipModalQuestion from "@/layouts/service-location/service-zip-modal-question/service-zip-modal-question"; // Supporting files import { defineRule } from "vee-validate"; @@ -125,7 +127,7 @@ export default { }, }, methods: { - async getNextShopsFromList(numberToGet = 3) { + async getNextShopsFromList(numberToGet = 1) { const logFirstShopsDisplayed = this.shopIndex == 0; const shopIterator = (array, n) => { @@ -234,6 +236,13 @@ export default { await nextTick(); } }, + onShopSelectedFromPopup(providerNumber) { + // Option 1: Update local state and emit up + this.$emit("shop-selected", providerNumber); + + // Option 2: If you want to update local state only, do: + // this.pendingSelectedProvider = providerNumber; + }, }, watch: { selectedAppointmentType: { @@ -258,8 +267,13 @@ export default { components: { alert, buttonQuestion, - textLink, + //textLink, + //shopQuestionPopup, + //serviceZipModalQuestion, }, + // mounted(){ + // console.log("shopProviderData" + this.shopProviderData); + // } }; From 14e1be18d8c6cb78ae09869ac2e3d7c79731b60d Mon Sep 17 00:00:00 2001 From: Matt Caimi Date: Thu, 26 Jun 2025 14:53:00 -0400 Subject: [PATCH 02/61] CASH-926 amount due correction --- src/helpers/pricing-helper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers/pricing-helper.js b/src/helpers/pricing-helper.js index 33311fd21..9aa317353 100644 --- a/src/helpers/pricing-helper.js +++ b/src/helpers/pricing-helper.js @@ -67,7 +67,7 @@ export function getAmountDueWithDonation(lineItemsObject, donationLineItem) { // this is amount due with Donation added const lineItemsCloneWithDonation = deepClone(lineItemsObject); lineItemsCloneWithDonation.supportingItems.push(donationLineItem); - return getAmountDue(lineItemsCloneWithDonation, false); + return getAmountDue(lineItemsCloneWithDonation); } export async function getPricingByDayPartWithPrice(pageNameToLog) { From 86fdfd81f870f2cf738ea0316f4d1ad926112591 Mon Sep 17 00:00:00 2001 From: AdamCaouetteSafelite Date: Fri, 27 Jun 2025 09:52:36 -0400 Subject: [PATCH 03/61] CASH-1036: remove test styling --- .../date-picker/date-picker.vue | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/digital-components/date-picker/date-picker.vue b/src/digital-components/date-picker/date-picker.vue index 3d517fd85..5b35792dd 100644 --- a/src/digital-components/date-picker/date-picker.vue +++ b/src/digital-components/date-picker/date-picker.vue @@ -1354,23 +1354,6 @@ export default { } } } - - .inshop-version { - color: limegreen !important; // TODO - THIS IS TEMPORARY CODE; TO BE REMOVED AS PART OF CASH-845 - } - .mobile-version { - color: red !important; // TODO - THIS IS TEMPORARY CODE; TO BE REMOVED AS PART OF CASH-845 - } - .radio-wrapper.inshop-version { - &.selectable-day label span { - color: limegreen !important; // TODO - THIS IS TEMPORARY CODE; TO BE REMOVED AS PART OF CASH-845 - } - } - .radio-wrapper.mobile-version { - &.selectable-day label span { - color: red !important; // TODO - THIS IS TEMPORARY CODE; TO BE REMOVED AS PART OF CASH-845 - } - } } } :deep(.button-inner-wrapper) { From 8056eea2e2714e962f98227e0c345ce50f3c6f50 Mon Sep 17 00:00:00 2001 From: Scott Kiener Date: Fri, 27 Jun 2025 10:10:53 -0400 Subject: [PATCH 04/61] Revert "Merge pull request #2605 from Safelite/feature/Digital/CASH-814" This reverts commit 1e4ee3d544ab28df08521f1bdbb37403ec61303e, reversing changes made to 7cb65a585660919d4244a3439a00638ab7f71c36. --- .../time-slot-question/time-slot-question.vue | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/src/layouts/schedule/time-slot-question/time-slot-question.vue b/src/layouts/schedule/time-slot-question/time-slot-question.vue index f0499e11a..58648746e 100644 --- a/src/layouts/schedule/time-slot-question/time-slot-question.vue +++ b/src/layouts/schedule/time-slot-question/time-slot-question.vue @@ -12,7 +12,10 @@ buttonTypeString="timeSlotModalListButton" :buttonTypeObject="timeSlotModalListButton" class="mt-4" - :class="[isMobileAppointment ? 'is-mobile-appointment' : '']" + :class="[ + isMobileAppointment ? 'is-mobile-appointment' : '', + isDropOffAppointment ? 'is-drop-off-appointment' : '', + ]" :answers="availableTimeSlots" groupName="chooseTimeSlot" textPosition="text-center" @@ -94,7 +97,7 @@ const cmsWidgetFieldMappings = { defineRule("time-slot-required", required(errorMessages.OPTION_REQUIRED)); export default { - name: "time-slot-question", + name: "time-slot-no-modal-question", emits: ["update:modelValue", "TimeSlotSelected", "click-event"], props: { modelValue: { @@ -237,9 +240,6 @@ export default { ); }, disclaimerTextBlockCopy() { - /* AppointmentTypeStrings.DROP_OFF should never be used per CASH-803 epic - but I'm leaving this in assuming that a future card in this epic - will handle disclaimers in a different way */ if (this.appointmentType === AppointmentTypeStrings.DROP_OFF) { if (this.selectedRouteCode?.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) { if (this.isSameDay) { @@ -329,7 +329,11 @@ export default { return null; } - if (this.appointmentType === AppointmentTypeStrings.MOBILE) { + if (this.appointmentType === AppointmentTypeStrings.DROP_OFF) { + return this.getAvailableTimeSlotsForDropOff( + this.timeSlotsForSelectedDate.timeSlots + ); + } else if (this.appointmentType === AppointmentTypeStrings.MOBILE) { return this.getAvailableTimeSlotsForMobile(this.timeSlotsForSelectedDate.timeSlots); } else { return this.getAvailableTimeSlotsForInshop(this.timeSlotsForSelectedDate.timeSlots); @@ -353,6 +357,9 @@ export default { isMobileAppointment() { return this.appointmentType === AppointmentTypeStrings.MOBILE; }, + isDropOffAppointment() { + return this.appointmentType === AppointmentTypeStrings.DROP_OFF; + }, }, methods: { async setSelectedTimeSlot() { @@ -375,20 +382,23 @@ export default { } }, getAvailableTimeSlotsForInshop(timeSlotsForSelectedDate) { - let dropOffIndex = -1; - const availableTimeSlots = timeSlotsForSelectedDate.map((timeSlot, index) => { + return timeSlotsForSelectedDate.map((timeSlot) => { const readableTime = militaryToTwelveHourTime(timeSlot.startTime); - let buttonLabelValue = readableTime; - if (timeSlot.id.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) { - dropOffIndex = index; - if (this.isSameDay) { - buttonLabelValue = this.sameDayDropoffButtonText; - } else { - buttonLabelValue = this.dropoffButtonText; - } - } else if (timeSlot.id.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) { - dropOffIndex = index; + return { + value: timeSlot.id, + buttonLabel: readableTime, + }; + }); + }, + getAvailableTimeSlotsForDropOff(timeSlotsForSelectedDate) { + const availableTimeSlots = timeSlotsForSelectedDate.map((timeSlot) => { + let buttonLabelValue; + if (timeSlot.id.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) { buttonLabelValue = this.overnightDropoffButtonText; + } else if (this.isSameDay) { + buttonLabelValue = this.sameDayDropoffButtonText; + } else { + buttonLabelValue = this.dropoffButtonText; } return { value: timeSlot.id, @@ -396,10 +406,6 @@ export default { }; }); - if (dropOffIndex) { - const dropOffTimeSlot = availableTimeSlots.splice(dropOffIndex, 1)[0]; - availableTimeSlots.unshift(dropOffTimeSlot); - } return availableTimeSlots; }, getAvailableTimeSlotsForMobile(timeSlotsForSelectedDate) { From ebec894e335fcc53646f36efd16200de9020ce28 Mon Sep 17 00:00:00 2001 From: AdamCaouetteSafelite Date: Fri, 27 Jun 2025 10:48:59 -0400 Subject: [PATCH 05/61] CASH-1036: fix dropoff issue for temporary state --- src/layouts/schedule/schedule.vue | 15 +++++--- .../time-slot-question/time-slot-question.vue | 36 ++++++++++++++----- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/layouts/schedule/schedule.vue b/src/layouts/schedule/schedule.vue index 98f5d60ea..ee2096c36 100644 --- a/src/layouts/schedule/schedule.vue +++ b/src/layouts/schedule/schedule.vue @@ -76,6 +76,7 @@ import { } from "@/layouts/schedule/helpers/schedule-helper"; import { AppointmentTypeStrings, + RouteCodeFlags, PREMIUM_FEE_PART_TYPE, PRICING_BY_DAY_PART_TYPE, } from "@/constants/schedule-constants"; @@ -447,7 +448,7 @@ export default { if (this.isMobileSelected) { return "Mobile"; } else { - return "Inshop"; + return "InshopOrDropoff"; } }, getServiceMinutesMin() { @@ -569,10 +570,14 @@ export default { timeSlotInfo.timeSlot.date )}`; - if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) { - navbarButtonText += ` at ${this.getDisplayTextForMilitaryTime( - timeSlotInfo.timeSlot.startTime - )}`; + if (this.appointmentType === AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF) { + if ( + !timeSlotInfo.timeSlot.routeCode.includes(RouteCodeFlags.ALL_DAY_DROP_OFF) + ) { + navbarButtonText += ` at ${this.getDisplayTextForMilitaryTime( + timeSlotInfo.timeSlot.startTime + )}`; + } } else if ( this.appointmentType === AppointmentTypeStrings.MOBILE && !timeSlotInfo.isPremiumAppointment diff --git a/src/layouts/schedule/time-slot-question/time-slot-question.vue b/src/layouts/schedule/time-slot-question/time-slot-question.vue index 58648746e..a245e1d17 100644 --- a/src/layouts/schedule/time-slot-question/time-slot-question.vue +++ b/src/layouts/schedule/time-slot-question/time-slot-question.vue @@ -14,7 +14,7 @@ class="mt-4" :class="[ isMobileAppointment ? 'is-mobile-appointment' : '', - isDropOffAppointment ? 'is-drop-off-appointment' : '', + isInshopOrDropOffAppointment ? 'is-inshop-or-drop-off-appointment' : '', ]" :answers="availableTimeSlots" groupName="chooseTimeSlot" @@ -329,14 +329,12 @@ export default { return null; } - if (this.appointmentType === AppointmentTypeStrings.DROP_OFF) { - return this.getAvailableTimeSlotsForDropOff( - this.timeSlotsForSelectedDate.timeSlots - ); - } else if (this.appointmentType === AppointmentTypeStrings.MOBILE) { + if (this.appointmentType === AppointmentTypeStrings.MOBILE) { return this.getAvailableTimeSlotsForMobile(this.timeSlotsForSelectedDate.timeSlots); } else { - return this.getAvailableTimeSlotsForInshop(this.timeSlotsForSelectedDate.timeSlots); + return this.getAvailableTimeSlotsForInshopOrDropOff( + this.timeSlotsForSelectedDate.timeSlots + ); } }, hasWaitListExperiment() { @@ -357,8 +355,8 @@ export default { isMobileAppointment() { return this.appointmentType === AppointmentTypeStrings.MOBILE; }, - isDropOffAppointment() { - return this.appointmentType === AppointmentTypeStrings.DROP_OFF; + isInshopOrDropOffAppointment() { + return this.appointmentType === AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF; }, }, methods: { @@ -408,6 +406,26 @@ export default { return availableTimeSlots; }, + getAvailableTimeSlotsForInshopOrDropOff(timeSlotsForSelectedDate) { + const availableTimeSlots = timeSlotsForSelectedDate.map((timeSlot) => { + let buttonLabelValue; + if (timeSlot.id.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) { + buttonLabelValue = this.dropoffButtonText; + } else if (timeSlot.id.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) { + buttonLabelValue = this.overnightDropoffButtonText; + } else if (this.isSameDay) { + buttonLabelValue = this.sameDayDropoffButtonText; + } else { + buttonLabelValue = militaryToTwelveHourTime(timeSlot.startTime); + } + return { + value: timeSlot.id, + buttonLabel: buttonLabelValue, + }; + }); + + return availableTimeSlots; + }, getAvailableTimeSlotsForMobile(timeSlotsForSelectedDate) { const availableTimeSlots = timeSlotsForSelectedDate.map((timeSlot) => { const readableTime = `${militaryToTwelveHourTime(timeSlot.startTime)} - ${militaryToTwelveHourTime( From fa401647928990a5dd52a2d614da7aa0d7728c18 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Fri, 27 Jun 2025 12:44:13 -0400 Subject: [PATCH 06/61] Check against constant --- src/global-methods.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/global-methods.js b/src/global-methods.js index 75fabb356..5528e42c6 100644 --- a/src/global-methods.js +++ b/src/global-methods.js @@ -9,6 +9,7 @@ import { GaCategories, GaActions, GaLabels } from "@/constants/analytics"; import { getSessionKeyValue } from "@/helpers/heritage-integration/cookie-helper"; import { headerKeys } from "@/constants/header-keys"; import axiosResponseInterceptorMessages from "@/constants/axios-response-interceptor-messages.js"; +import { endpoints } from "@/constants/endpoints"; // Add a response interceptor for global axios error handing. axios.interceptors.response.use( @@ -100,7 +101,7 @@ export default { return resolve(response); }, (error) => { - if (endpoint.toLowerCase().includes("/digitalconsumer-log")) { + if (endpoint.toLowerCase().includes(endpoints.LogDigitalConsumer.url)) { return resolve({ data: null, error: "Ignore errors when logging" }); } else { if (logApiCall) { From 7f15c135d305711a3abcbf5e22f0ff947bf143ac Mon Sep 17 00:00:00 2001 From: Chris Redelinghuys Date: Mon, 30 Jun 2025 08:50:10 -0400 Subject: [PATCH 07/61] CASH-952: Remove VAPS from price and unused method. --- .../afterpay-modal-banner/afterpay-modal-banner.vue | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/layouts/quote/afterpay-modal-banner/afterpay-modal-banner.vue b/src/layouts/quote/afterpay-modal-banner/afterpay-modal-banner.vue index 32960509c..218ba03ba 100644 --- a/src/layouts/quote/afterpay-modal-banner/afterpay-modal-banner.vue +++ b/src/layouts/quote/afterpay-modal-banner/afterpay-modal-banner.vue @@ -101,11 +101,6 @@ export default { baseMixin.methods.filterOutFees(allLineItems) ); - let vapsLineItemsForSelectedPackage = this.lineItems.vaps; - vapsLineItemsForSelectedPackage.forEach((lineItem) => { - price += baseMixin.methods.getTotalLineItemPrice(lineItem); - }); - if (this.lineItems.promos) { let allLineItems = getArrayOfAllLineItemsAndChildParts(this.lineItems); const promos = getPromosThatMatchLineItemsOnOrder( @@ -119,12 +114,6 @@ export default { return price; }, - afterpayPrice() { - let price = this.getTierOnePackagePrice; - const adjusted = (price / 4).toFixed(2); - - return `$${adjusted}`; - }, showAfterpayExtendedPayOption() { return ( this.afterpayExtendedPayOptionThreshold && From 55b9a86fe465adfed4b304d9f3c5a3f68be627d8 Mon Sep 17 00:00:00 2001 From: Johnny shultz Date: Mon, 30 Jun 2025 15:01:19 -0400 Subject: [PATCH 08/61] CASH-816 CASH-816 finished moving shop functionality into drawer --- .../textbox-question/textbox-question.vue | 20 +- .../service-location/service-location.vue | 159 ++++- .../service-zip-question.vue | 6 + .../shop-question/shop-question-popup.vue | 642 ++++++++++-------- .../shop-question/shop-question.vue | 2 +- 5 files changed, 530 insertions(+), 299 deletions(-) diff --git a/src/digital-components/textbox-question/textbox-question.vue b/src/digital-components/textbox-question/textbox-question.vue index 9602daadb..927af3ba7 100644 --- a/src/digital-components/textbox-question/textbox-question.vue +++ b/src/digital-components/textbox-question/textbox-question.vue @@ -50,6 +50,7 @@ :autocomplete="autocomplete" />