diff --git a/src/constants/schedule-constants.js b/src/constants/schedule-constants.js index 8ac97b5de..2b626df18 100644 --- a/src/constants/schedule-constants.js +++ b/src/constants/schedule-constants.js @@ -16,10 +16,19 @@ const RouteCodeFlags = { OVERNIGHT_DROP_OFF: "OVERNIGHT DROP OFF", }; +const cmsWidgetFieldMappings = { + MODAL_CLOSE_BUTTON: "FooterText", + SUPPLEMENTAL_INFORMATION: "BodyText", + TIME_SLOT_BUTTON: "HeaderText", + DISCLAIMER: "FooterText", + DURATION: "SubheaderText", +}; + export { AppointmentTypeStrings, PREMIUM_TIME_SLOT_ID_FLAG, PREMIUM_FEE_PART_TYPE, PRICING_BY_DAY_PART_TYPE, RouteCodeFlags, + cmsWidgetFieldMappings, }; diff --git a/src/digital-components/date-picker/date-picker.vue b/src/digital-components/date-picker/date-picker.vue index bc3682cf0..164010f5d 100644 --- a/src/digital-components/date-picker/date-picker.vue +++ b/src/digital-components/date-picker/date-picker.vue @@ -2,6 +2,17 @@
+
+ + +
+
Select a day and time
import timeSlotQuestion from "@/layouts/schedule/time-slot-question/time-slot-question.vue"; +import textBlock from "@/digital-components/text-block/text-block"; +import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header"; // Supporting files import loader from "@/ux-components/loader/loader"; @@ -166,7 +179,12 @@ import { MONTHS_OF_YEAR, DAYS_OF_WEEK, } from "@/digital-components/date-picker/mixins/constants"; -import { AppointmentTypeStrings, PREMIUM_FEE_PART_TYPE } from "@/constants/schedule-constants"; +import { + AppointmentTypeStrings, + RouteCodeFlags, + PREMIUM_FEE_PART_TYPE, + cmsWidgetFieldMappings, +} from "@/constants/schedule-constants"; import { selectableDaysOptions, requiredParameter, @@ -178,6 +196,7 @@ import { import { useField, ErrorMessage } from "vee-validate"; import { deepClone } from "@/helpers/object-helper"; import { v4 as uuidv4 } from "uuid"; +import { getDisplayTextForDurationLength } from "@/helpers/duration-length-helper"; export default { name: "datePicker", @@ -190,6 +209,7 @@ export default { selectedTimeSlotInfo: this.getSelectedTimeSlotInfo(), selectableDatesInshop: [], // NOTE: this and the mobile version below use monthNum (1-based), NOT monthIndex (0-based) selectableDatesMobile: [], + durationTextBlockCopyForInshopOrDropoff: null, }; }, props: { @@ -224,7 +244,7 @@ export default { pricingByDayUpcharge: Number, isPricingByDayExperiment: Boolean, timeSlotsForSelectedDate: Object, - appointmentTypeForTimeSlotQuestion: String, + appointmentType: String, premiumAppointmentFee: Object, estimatedServiceMinutesMinimum: Number, estimatedServiceMinutesMaximum: Number, @@ -289,6 +309,54 @@ export default { this.$emit("update:modelValue", newSelectedDate); }, }, + 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.estimatedServiceMinutesMinimum, + this.estimatedServiceMinutesMaximum + ); + + return `${inshopDurationTextWithoutTime} ${inshopDurationTime}`; + }, + durationTextBlockCopy() { + if (this.appointmentType === AppointmentTypeStrings.MOBILE) { + return null; + } 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.durationTextBlockCopyForInshopOrDropoff ?? this.inshopDurationText; + } + return null; + }, + isSameDay() { + if (!this.timeSlotsForSelectedDate) { + return false; + } + const todaysDate = new Date().toISOString().split("T")[0]; + return this.selectedDate === todaysDate; + }, }, methods: { initializeComponent(initialData) { @@ -877,6 +945,18 @@ export default { handleWaitListRequested(value) { this.$emit("waitListRequested", value); }, + 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; + }, }, watch: { modelValue(newValue) { @@ -891,13 +971,18 @@ export default { } }, selectedTimeSlotInfo(newValue) { - this.$emit("TimeSlotSelected", newValue); // NEEDED ON SCHEDULE - to update footer button text and to save to Store correctly + 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, }, }; @@ -913,6 +998,10 @@ export default { display: flex; flex-direction: column; + .date-picker-header { + margin-bottom: 1.5rem; + } + fieldset { flex-grow: 1; position: relative; diff --git a/src/layouts/schedule/schedule.vue b/src/layouts/schedule/schedule.vue index 675b54e2e..c861035f2 100644 --- a/src/layouts/schedule/schedule.vue +++ b/src/layouts/schedule/schedule.vue @@ -130,7 +130,6 @@
-
-
.time-slots-question { - border-top: 1px solid $gray-500; margin-top: 1.5rem; .time-slot-header {