From 4484fdfe4f08b2610da952dda7638ab600118bf8 Mon Sep 17 00:00:00 2001 From: Scott Kiener Date: Thu, 31 Jul 2025 07:38:25 -0400 Subject: [PATCH 01/38] Able to move time-slot-question out of date-picker still lots of work to do comments are in commented out timeslotquestion component in date-picker for help seeing what I did --- .../date-picker/date-picker.vue | 57 ++++--------------- src/layouts/schedule/schedule.vue | 25 ++++++-- .../time-slot-question/time-slot-question.vue | 4 ++ 3 files changed, 35 insertions(+), 51 deletions(-) diff --git a/src/digital-components/date-picker/date-picker.vue b/src/digital-components/date-picker/date-picker.vue index 72bdcb093..5f508f5be 100644 --- a/src/digital-components/date-picker/date-picker.vue +++ b/src/digital-components/date-picker/date-picker.vue @@ -140,9 +140,9 @@ @click="showAnotherMonth"> View more dates - + :selectedDate="selectedDate" // used in date-picker + :appointmentType="appointmentType" // used in date-picker for duration + :premiumAppointmentFee="premiumAppointmentFee" // Removed from date-picker + :displayWaitList="displayWaitList" // Removed from date-picker + :timeSlotsForSelectedDate="timeSlotsForSelectedDate" // Removed from date-picker (but it had a null check for is-same-day) + :estimatedServiceMinutesMinimum="estimatedServiceMinutesMinimum" // used in date-picker for duration + :estimatedServiceMinutesMaximum="estimatedServiceMinutesMaximum" // used in date-picker for duration + @waitListRequested="handleWaitListRequested" // removed, it's a pass-through /> --> @@ -206,7 +205,6 @@ export default { months: null, disableViewMoreDatesButton: false, hideSomeDaysForInitialView: null, - selectedTimeSlotInfo: this.getSelectedTimeSlotInfo(), selectableDatesInshop: [], // NOTE: this and the mobile version below use monthNum (1-based), NOT monthIndex (0-based) selectableDatesMobile: [], durationTextBlockCopyForInshopOrDropoff: null, @@ -243,12 +241,9 @@ export default { pricingByDayBasePrice: Number, pricingByDayUpcharge: Number, isPricingByDayExperiment: Boolean, - timeSlotsForSelectedDate: Object, appointmentType: String, - premiumAppointmentFee: Object, estimatedServiceMinutesMinimum: Number, estimatedServiceMinutesMaximum: Number, - displayWaitList: Boolean, isMobileSelected: Boolean, }, setup(props) { @@ -373,9 +368,6 @@ export default { return null; }, isSameDay() { - if (!this.timeSlotsForSelectedDate) { - return false; - } const todaysDate = new Date().toISOString().split("T")[0]; return this.selectedDate === todaysDate; }, @@ -383,7 +375,6 @@ export default { methods: { async initializeComponent(initialData) { await this.setCalendarData(initialData); - this.$refs.timeSlotModalQuestion.initializeComponent(); }, fireDateSelectedEvent(event, date) { // Ignore if arrow key selected radioButton @@ -950,30 +941,9 @@ export default { }; window.requestAnimationFrame(step); }, - getSelectedTimeSlotInfo() { - const supportingItems = this.getSupportingItems(); - - var isPremiumAppointment = false; - if (supportingItems) { - isPremiumAppointment = - !!supportingItems.filter( - (lineItem) => lineItem.partType === PREMIUM_FEE_PART_TYPE - ).length > 0; - } - - const selectedTimeSlotInfo = { - timeSlot: store.getters.order.schedule, - isPremiumAppointment: isPremiumAppointment, - }; - - return selectedTimeSlotInfo; - }, getSupportingItems() { return store.getters.lineItems.supportingItems; }, - handleWaitListRequested(value) { - this.$emit("waitListRequested", value); - }, getDurationTextBlockCopyForInshopOrDropoff(selectedRouteCode) { if (selectedRouteCode?.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) { return this.overnightDropoffDurationText; @@ -999,17 +969,10 @@ export default { this.scrollToElement("date-of-month-error"); } }, - selectedTimeSlotInfo(newValue) { - const routeCode = newValue?.timeSlot?.routeCode; - this.durationTextBlockCopyForInshopOrDropoff = - this.getDurationTextBlockCopyForInshopOrDropoff(routeCode); - this.$emit("TimeSlotSelected", newValue); // needed to update footer button text on Schedule page and to save to Store correctly - }, }, components: { loader, ErrorMessage, - timeSlotQuestion, textBlock, funnelSubHeader, }, diff --git a/src/layouts/schedule/schedule.vue b/src/layouts/schedule/schedule.vue index 0e68cb458..24635a1f5 100644 --- a/src/layouts/schedule/schedule.vue +++ b/src/layouts/schedule/schedule.vue @@ -133,13 +133,28 @@ :pricingByDayUpcharge="pricingByDayUpcharge" :showPricingByDay="showPricingByDay" :isPricingByDayExperiment="isPricingByDayExperiment" - :timeSlotsForSelectedDate="timeSlotsForSelectedDate" :appointmentType="appointmentType" - :premiumAppointmentFee="mobilePremiumAppointmentFee" :estimatedServiceMinutesMinimum="getServiceMinutesMin" - :estimatedServiceMinutesMaximum="getServiceMinutesMax" + :estimatedServiceMinutesMaximum="getServiceMinutesMax" /> + Date: Thu, 31 Jul 2025 08:09:52 -0400 Subject: [PATCH 02/38] Moved duration into schedule --- .../date-picker/date-picker.vue | 103 ------------------ src/layouts/schedule/schedule.vue | 91 ++++++++++++++++ 2 files changed, 91 insertions(+), 103 deletions(-) diff --git a/src/digital-components/date-picker/date-picker.vue b/src/digital-components/date-picker/date-picker.vue index 5f508f5be..dd3f7862a 100644 --- a/src/digital-components/date-picker/date-picker.vue +++ b/src/digital-components/date-picker/date-picker.vue @@ -2,17 +2,6 @@
-
- - -
-
Select a day and time
diff --git a/src/layouts/schedule/schedule.vue b/src/layouts/schedule/schedule.vue index 24635a1f5..67056c67d 100644 --- a/src/layouts/schedule/schedule.vue +++ b/src/layouts/schedule/schedule.vue @@ -116,6 +116,16 @@
+
+ + +
{ @@ -835,9 +847,88 @@ export default { ); } }, + durationTextBlockCopy() { + console.log("durationTextBlockCopy", this.appointmentType, this.getServiceMinutesMax, this.getServiceMinutesMin); + if (this.appointmentType === AppointmentTypeStrings.MOBILE) { + return this.mobileDurationText; + } else if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) { + return this.inshopDurationText; + } else if ( + this.appointmentType === AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF || + this.appointmentType === AppointmentTypeStrings.DROP_OFF + ) { + return this.getDurationTextBlockCopyForInshopOrDropoff(this.selectedTimeSlotInfo.timeSlot.routeCode); + } + return null; + }, + isSameDay() { + const todaysDate = new Date().toISOString().split("T")[0]; + return this.selectedDate === todaysDate; + }, + dropOffDurationText() { + return this.getCmsContent("DropOffTimeSlotModal", cmsWidgetFieldMappings.DURATION); + }, + sameDayDropoffDurationText() { + return this.getCmsContent( + "SameDayDropOffTimeSlotModal", + cmsWidgetFieldMappings.DURATION + ); + }, + overnightDropoffDurationText() { + return this.getCmsContent( + "OvernightDropOffTimeSlotModal", + cmsWidgetFieldMappings.DURATION + ); + }, + inshopDurationText() { + const inshopDurationTextWithoutTime = this.getCmsContent( + "TimeSlotModalQuestion", + cmsWidgetFieldMappings.DURATION + ); + + const inshopDurationTime = getDisplayTextForDurationLength( + this.getServiceMinutesMin, + this.getServiceMinutesMax + ); + + if (this.getServiceMinutesMin && this.getServiceMinutesMax) { + return `${inshopDurationTextWithoutTime} ${inshopDurationTime}`; + } + + return null; + }, + mobileDurationText() { + const mobileDurationTextWithoutTime = this.getCmsContent( + "TimeSlotModalQuestion", + cmsWidgetFieldMappings.DURATION + ); + + const mobileDurationTime = getDisplayTextForDurationLength( + this.getServiceMinutesMin, + this.getServiceMinutesMax + ); + + if (this.getServiceMinutesMin && this.getServiceMinutesMax) { + return `${mobileDurationTextWithoutTime} ${mobileDurationTime}`; + } + + return null; + }, }, methods: { splitCopyOnCMSPlaceHolder, + getDurationTextBlockCopyForInshopOrDropoff(selectedRouteCode) { + if (selectedRouteCode?.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) { + return this.overnightDropoffDurationText; + } else if (selectedRouteCode?.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) { + if (this.isSameDay) { + return this.sameDayDropoffDurationText; + } else { + return this.dropOffDurationText; + } + } + return this.inshopDurationText; + }, arePagePrerequisitesValid() { const paymentInfo = store.getters.payment.isInsurance !== null; const damageInfo = From 866eaf0e7b1f281a4356100b877de088309be65b Mon Sep 17 00:00:00 2001 From: Scott Kiener Date: Thu, 31 Jul 2025 09:48:43 -0400 Subject: [PATCH 03/38] Move duration outside of date-picker into own component --- .../date-picker/date-picker.vue | 3 - .../duration-text-block.vue | 126 ++++++++++++++++++ src/layouts/schedule/schedule.vue | 106 +++------------ 3 files changed, 143 insertions(+), 92 deletions(-) create mode 100644 src/layouts/schedule/duration-text-block/duration-text-block.vue diff --git a/src/digital-components/date-picker/date-picker.vue b/src/digital-components/date-picker/date-picker.vue index dd3f7862a..1e9d88d48 100644 --- a/src/digital-components/date-picker/date-picker.vue +++ b/src/digital-components/date-picker/date-picker.vue @@ -218,9 +218,6 @@ export default { pricingByDayBasePrice: Number, pricingByDayUpcharge: Number, isPricingByDayExperiment: Boolean, - appointmentType: String, - estimatedServiceMinutesMinimum: Number, - estimatedServiceMinutesMaximum: Number, isMobileSelected: Boolean, }, setup(props) { diff --git a/src/layouts/schedule/duration-text-block/duration-text-block.vue b/src/layouts/schedule/duration-text-block/duration-text-block.vue new file mode 100644 index 000000000..957b0cfb0 --- /dev/null +++ b/src/layouts/schedule/duration-text-block/duration-text-block.vue @@ -0,0 +1,126 @@ + + + + + \ No newline at end of file diff --git a/src/layouts/schedule/schedule.vue b/src/layouts/schedule/schedule.vue index 67056c67d..5128d4906 100644 --- a/src/layouts/schedule/schedule.vue +++ b/src/layouts/schedule/schedule.vue @@ -118,13 +118,14 @@
- + +
+ :isPricingByDayExperiment="isPricingByDayExperiment" /> { @@ -822,12 +819,12 @@ export default { // Splits content when brackets are found in text so that text can be looped through and router-link can be injected when needed return this.splitCopyOnCMSPlaceHolder(this.ChangeShopLinkText); }, - getServiceMinutesMin() { + estimatedServiceMinutesMinimum() { return this.isMobileSelected ? this.selectableDatesMobile.estimatedServiceMinutesMinimum : this.selectableDatesInshop.estimatedServiceMinutesMinimum; }, - getServiceMinutesMax() { + estimatedServiceMinutesMaximum() { return this.isMobileSelected ? this.selectableDatesMobile.estimatedServiceMinutesMaximum : this.selectableDatesInshop.estimatedServiceMinutesMaximum; @@ -847,88 +844,18 @@ export default { ); } }, - durationTextBlockCopy() { - console.log("durationTextBlockCopy", this.appointmentType, this.getServiceMinutesMax, this.getServiceMinutesMin); - if (this.appointmentType === AppointmentTypeStrings.MOBILE) { - return this.mobileDurationText; - } else if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) { - return this.inshopDurationText; - } else if ( - this.appointmentType === AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF || - this.appointmentType === AppointmentTypeStrings.DROP_OFF - ) { - return this.getDurationTextBlockCopyForInshopOrDropoff(this.selectedTimeSlotInfo.timeSlot.routeCode); - } - return null; - }, isSameDay() { const todaysDate = new Date().toISOString().split("T")[0]; return this.selectedDate === todaysDate; }, - dropOffDurationText() { - return this.getCmsContent("DropOffTimeSlotModal", cmsWidgetFieldMappings.DURATION); - }, - sameDayDropoffDurationText() { - return this.getCmsContent( - "SameDayDropOffTimeSlotModal", - cmsWidgetFieldMappings.DURATION + isOvernightDropoff() { + return ( + this.selectedTimeSlotInfo?.timeSlot?.routeCode && this.selectedTimeSlotInfo.timeSlot.routeCode.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF) ); - }, - overnightDropoffDurationText() { - return this.getCmsContent( - "OvernightDropOffTimeSlotModal", - cmsWidgetFieldMappings.DURATION - ); - }, - inshopDurationText() { - const inshopDurationTextWithoutTime = this.getCmsContent( - "TimeSlotModalQuestion", - cmsWidgetFieldMappings.DURATION - ); - - const inshopDurationTime = getDisplayTextForDurationLength( - this.getServiceMinutesMin, - this.getServiceMinutesMax - ); - - if (this.getServiceMinutesMin && this.getServiceMinutesMax) { - return `${inshopDurationTextWithoutTime} ${inshopDurationTime}`; - } - - return null; - }, - mobileDurationText() { - const mobileDurationTextWithoutTime = this.getCmsContent( - "TimeSlotModalQuestion", - cmsWidgetFieldMappings.DURATION - ); - - const mobileDurationTime = getDisplayTextForDurationLength( - this.getServiceMinutesMin, - this.getServiceMinutesMax - ); - - if (this.getServiceMinutesMin && this.getServiceMinutesMax) { - return `${mobileDurationTextWithoutTime} ${mobileDurationTime}`; - } - - return null; - }, + } }, methods: { splitCopyOnCMSPlaceHolder, - getDurationTextBlockCopyForInshopOrDropoff(selectedRouteCode) { - if (selectedRouteCode?.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) { - return this.overnightDropoffDurationText; - } else if (selectedRouteCode?.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) { - if (this.isSameDay) { - return this.sameDayDropoffDurationText; - } else { - return this.dropOffDurationText; - } - } - return this.inshopDurationText; - }, arePagePrerequisitesValid() { const paymentInfo = store.getters.payment.isInsurance !== null; const damageInfo = @@ -1851,6 +1778,7 @@ export default { contentGroupModal, shopQuestionPopup, buttonQuestion, + durationTextBlock }, }; From 6af4bca738fcf74e6acc9c00c7a288b810eb50a4 Mon Sep 17 00:00:00 2001 From: Scott Kiener Date: Thu, 31 Jul 2025 11:15:35 -0400 Subject: [PATCH 04/38] Time-slot refactor - move logic into parent --- .../duration-text-block.vue | 8 +-- src/layouts/schedule/schedule.vue | 38 ++++++++++-- .../time-slot-question/time-slot-question.vue | 61 +++---------------- 3 files changed, 43 insertions(+), 64 deletions(-) diff --git a/src/layouts/schedule/duration-text-block/duration-text-block.vue b/src/layouts/schedule/duration-text-block/duration-text-block.vue index 957b0cfb0..2ac1c3938 100644 --- a/src/layouts/schedule/duration-text-block/duration-text-block.vue +++ b/src/layouts/schedule/duration-text-block/duration-text-block.vue @@ -1,6 +1,5 @@