diff --git a/src/digital-components/date-picker/date-picker.vue b/src/digital-components/date-picker/date-picker.vue index 09b6ab44..70519fe4 100644 --- a/src/digital-components/date-picker/date-picker.vue +++ b/src/digital-components/date-picker/date-picker.vue @@ -99,9 +99,9 @@
@@ -256,6 +261,10 @@ export default { appointmentEstimate() { return getDisplayTextForDurationLength(this.appointmentDurationMinutesMinimum, this.appointmentDurationMinutesMaximum); }, + calendarViewDirection() { + if (this.selectableDatesSetting === 'custom') return 'future'; + return 'past'; + }, daysToAdd() { return this.isMobileView ? this.daysInViewMobile : this.daysInViewStandard; }, @@ -284,10 +293,6 @@ export default { this.todayOverrideDateString || convertDateToDateString(new Date()) ); - }, - calendarViewDirection() { - if (this.selectableDatesSetting === 'custom') return 'future'; - return 'past'; } }, watch: { @@ -301,16 +306,16 @@ export default { this.findFirstAvailableDateInView(); } }, + selectedDate(newValue, oldValue) { + if (newValue !== oldValue) { + this.$emit('dateSelected', newValue); + } + }, selectedTimeSlot(newValue, oldValue) { if (newValue !== oldValue) { const testObj = this.getSelectedTimeSlotInfoObject(newValue); this.$emit('timeSlotSelected', testObj); } - }, - selectedDate(newValue, oldValue) { - if (newValue !== oldValue) { - this.$emit('dateSelected', newValue); - } } }, methods: { @@ -340,50 +345,44 @@ export default { addPremiumFlagToInput(routeCode) { return (`${routeCode}${PREMIUM_TIME_SLOT_ID_FLAG}`); }, - removePremiumFlagFromInput(routeCode) { - return routeCode.replace(PREMIUM_TIME_SLOT_ID_FLAG, ''); + checkIsSelectedDay(index, timeOfDayGrouping) { + const dateToShowString = this.getDateToShowString(index); + return this.selectedDate === dateToShowString && this.selectedTimeOfDayGrouping === timeOfDayGrouping; }, - getSelectedTimeSlotInfoObject(timeSlot) { - const routeCode = timeSlot?.id; - let routeCodeToUse = routeCode; - const routeCodeIncludesPremium = routeCode?.includes(PREMIUM_TIME_SLOT_ID_FLAG); - if (routeCodeIncludesPremium) { - routeCodeToUse = this.removePremiumFlagFromInput(routeCode); + checkIsSelectedTimeSlot(timeSlot) { + if (!this.selectedTimeSlot) { + return false; } - - const fullTimeSlot = this.selectableTimeSlotsData?.find((ts) => ts.id === routeCodeToUse); - - if (fullTimeSlot) { - return { - timeSlot: { - date: this.selectedDate, - routeCode: fullTimeSlot.id, - startTime: fullTimeSlot.startTime, - endTime: fullTimeSlot.endTime, - jobMaxMinutes: this.appointmentDurationMinutesMaximum.toString(), - jobMinMinutes: this.appointmentDurationMinutesMinimum.toString() - }, - isPremiumAppointment: !!routeCodeIncludesPremium - }; + return (this.selectedTimeSlot.isDropoff === timeSlot.isDropoff + && this.selectedTimeSlot.startTime === timeSlot.startTime + && this.selectedTimeSlot.timeOfDay === timeSlot.timeOfDay); + }, + displayDropOffInformation(timeSlot) { + if (timeSlot.isDropoff) { + if (timeSlot.timeOfDay === 'morning') { + return this.getCmsContent( + 'DropOffTimeSlotModal', + 'BodyText' + ); + } + return this.getCmsContent( + 'OvernightDropOffTimeSlotModal', + 'BodyText' + ); } - - return { - timeSlot: { - date: null, - startTime: null, - endTime: null, - routeCode: null, - jobMaxMinutes: null, - jobMinMinutes: null - }, - isPremiumAppointment: null - }; + return ''; }, displayTimeSlotTime(timeSlot) { const appointmentType = this.activeAppointmentType || AppointmentTypeStrings.IN_SHOP; if (appointmentType === AppointmentTypeStrings.MOBILE || appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP) { return `${militaryToTwelveHourTime(timeSlot.startTime)} - ${militaryToTwelveHourTime(timeSlot.endTime)}`; } + if (timeSlot.isDropoff) { + if (timeSlot.timeOfDay === 'morning') { + return 'Drop & Go'; + } + return 'Overnight drop off'; + } return militaryToTwelveHourTime(timeSlot.startTime); }, findFirstAvailableDateInView() { @@ -436,8 +435,7 @@ export default { if (!initialTimeSlot || !initialTimeSlot.startTime) { return; } - const initialStartTime = initialTimeSlot.startTime; - const foundTimeSlot = timeSlots.find((ts) => ts.startTime === initialStartTime); + const foundTimeSlot = timeSlots.find((ts) => ts.startTime === initialTimeSlot.startTime && ts.endTime === initialTimeSlot.endTime); if (foundTimeSlot) { this.selectTimeSlotForDay(foundTimeSlot); } @@ -452,6 +450,44 @@ export default { const dateToShow = this.getDateToShow(index); return convertDateToDateString(dateToShow); }, + getSelectedTimeSlotInfoObject(timeSlot) { + const routeCode = timeSlot?.id; + let routeCodeToUse = routeCode; + const routeCodeIncludesPremium = routeCode?.includes(PREMIUM_TIME_SLOT_ID_FLAG); + if (routeCodeIncludesPremium) { + routeCodeToUse = this.removePremiumFlagFromInput(routeCode); + } + + const fullTimeSlot = this.selectableTimeSlotsData?.find((ts) => ts.id === routeCodeToUse); + + if (fullTimeSlot) { + return { + timeSlot: { + date: this.selectedDate, + routeCode: fullTimeSlot.id, + startTime: fullTimeSlot.startTime, + endTime: fullTimeSlot.endTime, + isDropoff: fullTimeSlot.isDropoff, + jobMaxMinutes: this.appointmentDurationMinutesMaximum.toString(), + jobMinMinutes: this.appointmentDurationMinutesMinimum.toString() + }, + isPremiumAppointment: !!routeCodeIncludesPremium + }; + } + + return { + timeSlot: { + date: null, + routeCode: null, + startTime: null, + endTime: null, + isDropoff: null, + jobMaxMinutes: null, + jobMinMinutes: null + }, + isPremiumAppointment: null + }; + }, async gotoNextPage() { const maxPageIndex = Math.floor((this.daysLoaded - 1) / this.daysToAdd); if (this.activePageIndex >= maxPageIndex) { @@ -479,15 +515,15 @@ export default { this.selectedTimeSlot = null; this.findFirstAvailableDateInView(); }, - checkIsSelectedDay(index, timeOfDayGrouping) { - const dateToShowString = this.getDateToShowString(index); - return this.selectedDate === dateToShowString && this.selectedTimeOfDayGrouping === timeOfDayGrouping; + mapTimeSlot(timeSlot, timeOfDay) { + return { + ...timeSlot, + isDropoff: timeSlot.id.endsWith('DROP OFF'), + timeOfDay + }; }, - checkIsSelectedTimeSlot(timeSlot) { - if (!this.selectedTimeSlot) { - return false; - } - return this.selectedTimeSlot.startTime === timeSlot.startTime; + removePremiumFlagFromInput(routeCode) { + return routeCode.replace(PREMIUM_TIME_SLOT_ID_FLAG, ''); }, selectTimeSlotForDay(timeSlot) { if (timeSlot) { @@ -567,13 +603,28 @@ export default { morningTimeSlots: selectableDate.timeSlots.filter((timeSlot) => { const timeHour = parseInt(timeSlot.startTime.split(':')[0], 10); return timeHour < 12; - }), + }).map((timeSlot) => this.mapTimeSlot(timeSlot, 'morning')), afternoonTimeSlots: selectableDate.timeSlots.filter((timeSlot) => { const timeHour = parseInt(timeSlot.startTime.split(':')[0], 10); return timeHour >= 12; - }), + }).map((timeSlot) => this.mapTimeSlot(timeSlot, 'afternoon')), isSelected: false }; + + if (dateObjectToPush.morningTimeSlots.length > 0) { + const findIndex = dateObjectToPush.morningTimeSlots.findIndex((timeSlot) => timeSlot.id.endsWith('DROP OFF')); + if (findIndex !== -1) { + const dropOffSlot = dateObjectToPush.morningTimeSlots.splice(findIndex, 1)[0]; + dateObjectToPush.morningTimeSlots.unshift(dropOffSlot); + } + } + if (dateObjectToPush.afternoonTimeSlots.length > 0) { + const findIndex = dateObjectToPush.afternoonTimeSlots.findIndex((timeSlot) => timeSlot.id.endsWith('DROP OFF')); + if (findIndex !== -1) { + const dropOffSlot = dateObjectToPush.afternoonTimeSlots.splice(findIndex, 1)[0]; + dateObjectToPush.afternoonTimeSlots.push(dropOffSlot); + } + } this.selectableDatesData.push(dateObjectToPush); }); @@ -635,14 +686,28 @@ export default { morningTimeSlots: selectableDate.timeSlots.filter((timeSlot) => { const timeHour = parseInt(timeSlot.startTime.split(':')[0], 10); return timeHour < 12; - }), + }).map((timeSlot) => this.mapTimeSlot(timeSlot, 'morning')), afternoonTimeSlots: selectableDate.timeSlots.filter((timeSlot) => { const timeHour = parseInt(timeSlot.startTime.split(':')[0], 10); return timeHour >= 12; - }), + }).map((timeSlot) => this.mapTimeSlot(timeSlot, 'afternoon')), isSelected: false }; + if (dateObjectToPush.morningTimeSlots.length > 0) { + const findIndex = dateObjectToPush.morningTimeSlots.findIndex((timeSlot) => timeSlot.id.endsWith('DROP OFF')); + if (findIndex !== -1) { + const dropOffSlot = dateObjectToPush.morningTimeSlots.splice(findIndex, 1)[0]; + dateObjectToPush.morningTimeSlots.unshift(dropOffSlot); + } + } + if (dateObjectToPush.afternoonTimeSlots.length > 0) { + const findIndex = dateObjectToPush.afternoonTimeSlots.findIndex((timeSlot) => timeSlot.id.endsWith('DROP OFF')); + if (findIndex !== -1) { + const dropOffSlot = dateObjectToPush.afternoonTimeSlots.splice(findIndex, 1)[0]; + dateObjectToPush.afternoonTimeSlots.push(dropOffSlot); + } + } this.selectableDatesData.push(dateObjectToPush); } }); @@ -832,16 +897,55 @@ export default { text-align: center; cursor: pointer; + :deep(.dropoff-label) { + sup { + top: -0.375rem; + font-size: 0.75rem; + } + } + + & + .time-slot-drop-off-information { + max-height: 0; + overflow: hidden; + transition: max-height 0.75s ease-in-out; + font-weight: 400; + text-align: left; + color: $darker-gray; + + :deep(ul) { + margin: 0; + margin-bottom: 0.375rem; + + > li { + padding-left: 0.625rem; + margin-top: 0.625rem; + } + } + } + &.selected-time-slot { background-color: $background-color-selected; border: solid 1px #0070d1; color:#000; font-weight: 500; + + :deep(.dropoff-label) { + sup { + top: .25rem; + font-size: 1.625rem; + } + } + + & + .time-slot-drop-off-information { + max-height: 12rem; + transition: max-height 0.75s ease-in-out; + } } &.has-date-error { border: 1px solid #d93025; } + } } diff --git a/src/layouts/schedule-page/schedule-page.vue b/src/layouts/schedule-page/schedule-page.vue index 8ee70191..da3ef43a 100644 --- a/src/layouts/schedule-page/schedule-page.vue +++ b/src/layouts/schedule-page/schedule-page.vue @@ -6,7 +6,6 @@
@@ -176,9 +175,7 @@ const getAvailableDates = async ( storeAction.payload.endDate, storeAction.payload.shopAppointmentType, storeAction.payload.providerNumber - ).catch(() => { - console.warn('Error fetching shop time slots...'); - }); + ); } else if (storeAction.payload?.zipCodeOverride) { timeSlotsResponse = await useMainStore().getMobileTimeSlots( storeAction.payload.startDate, @@ -343,7 +340,7 @@ export default { && (((this.selectedAppointmentType === AppointmentTypeStrings.MOBILE || this.selectedAppointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP) && serviceLocationToUse.mobileProviderNumber) - || (this.selectedAppointmentType === AppointmentTypeStrings.IN_SHOP + || ((this.selectedAppointmentType === AppointmentTypeStrings.IN_SHOP || this.selectedAppointmentType === AppointmentTypeStrings.DROP_OFF) && (this.selectedProvider?.providerNumber || serviceLocationToUse.provider?.providerNumber))); const damageInfo = useMainStore().order.damage.isRepair @@ -652,8 +649,13 @@ export default { return; } - // Save service location then schedule and navigate forward - await this.$refs.serviceLocation.forwardButtonAction(); + let appointmentTypeToUse = this.selectedAppointmentType; + if (appointmentTypeToUse === AppointmentTypeStrings.IN_SHOP && this.selectedTimeSlotInfo.timeSlot?.isDropoff === true) { + appointmentTypeToUse = AppointmentTypeStrings.DROP_OFF; + } + + // Save service location then save schedule then navigate forward + await this.$refs.serviceLocation.forwardButtonAction(appointmentTypeToUse); this.mainStore.saveSchedule(this.selectedTimeSlotInfo.timeSlot); this.$router.navigate( this.navigationScenarios.CLICKED_FORWARD, diff --git a/src/layouts/schedule-page/service-location/service-location.vue b/src/layouts/schedule-page/service-location/service-location.vue index d664a8ba..cc96f7c2 100644 --- a/src/layouts/schedule-page/service-location/service-location.vue +++ b/src/layouts/schedule-page/service-location/service-location.vue @@ -308,7 +308,7 @@ export default { } }, selectedAppointmentType(newValue, oldValue) { - if (this.selectedAppointmentType === AppointmentTypeStrings.IN_SHOP && this.selectedProvider && this.availabilityRating === null) { + if (this.isInshop && this.selectedProvider && this.availabilityRating === null) { this.refreshAvailabilityRating(); const appointmenTypeServiceLocationObj = { @@ -336,7 +336,7 @@ export default { }, selectedProvider(newProvider, oldProvider) { if (newProvider?.providerNumber !== oldProvider?.providerNumber || this.availabilityRating === null) { - const appointmentIsInshop = this.selectedAppointmentType === AppointmentTypeStrings.IN_SHOP; + const appointmentIsInshop = this.isInshop; const returnedProvider = { provider: newProvider, refreshDatePicker: appointmentIsInshop && oldProvider?.providerNumber !== null @@ -358,7 +358,7 @@ export default { await this.setData(initialData); this.initializingData = false; }, - async forwardButtonAction() { + async forwardButtonAction(appointmentType) { let provider = this.selectedProvider; this.mainStore.updateMobileFee(null); if (this.isMobile) { @@ -382,7 +382,7 @@ export default { state: this.state, zipCode: this.zipCode, zipCodeCtu: this.zipCodeCtu, - appointmentType: this.selectedAppointmentType, + appointmentType: appointmentType || this.selectedAppointmentType, provider }); }, diff --git a/src/store/index.js b/src/store/index.js index e9b39fd0..74c3deb0 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -965,7 +965,8 @@ export const useMainStore = defineStore({ providerNumber, startDate, endDate, - shopAppointmentType, + shopAppointmentType: (shopAppointmentType === AppointmentTypeStrings.IN_SHOP + || shopAppointmentType === AppointmentTypeStrings.DROP_OFF) ? 'InShopOrDropoff' : shopAppointmentType, applicationName: applicationConfig.APPLICATION_NAME, billToAccountNumber: this.billToAccountNumber, parentAccountNumber: this.order.parentAccountNumber, // this.payment.parentAccountNumber, @@ -1605,7 +1606,7 @@ export const useMainStore = defineStore({ order.customer.firstName = data?.customer?.firstName; order.customer.lastName = data?.customer?.lastName; order.customer.emailAddress = data?.customer?.emailAddress; - + order.contactInfo.extension = data?.customer?.homePhone?.slice(10); order.contactInfo.homePhone = data?.customer?.homePhone?.slice(0, 10); order.contactInfo.servicePhone = data?.customer?.servicePhone;