diff --git a/src/digital-components/date-picker/date-picker.vue b/src/digital-components/date-picker/date-picker.vue index 59297ae05..ae6ca6229 100644 --- a/src/digital-components/date-picker/date-picker.vue +++ b/src/digital-components/date-picker/date-picker.vue @@ -153,6 +153,8 @@ import { convertDateToDateString, convertDateStringToDate, getTodayDateString, + getInitialViewWeeks, + getMonthEnd, } from "@/layouts/schedule/helpers/schedule-helper"; import { useField, ErrorMessage } from "vee-validate"; import { deepClone } from "@/helpers/object-helper"; @@ -291,140 +293,6 @@ export default { } this.$emit("date-selected", date); }, - getWeekStartDate(dateString) { - const date = convertDateStringToDate(dateString); - const dayOfWeek = date.getDay(); - // Subtract the day of the week from date to get the date of Sunday - const sunday = new Date(date); - sunday.setDate(sunday.getDate() - dayOfWeek); - return convertDateToDateString(sunday); - }, - getWeekEndDate(dateString) { - const date = convertDateStringToDate(dateString); - const dayOfWeek = date.getDay(); - const daysUntilSaturday = 6 - dayOfWeek; // Calculate the number of days until Saturday - // Clone the given date and add the remaining days until Saturday - const saturday = new Date(date); - saturday.setDate(date.getDate() + daysUntilSaturday); - return convertDateToDateString(saturday); - }, - getNextWeekSunday(dateString) { - const date = convertDateStringToDate(dateString); - const dayOfWeek = date.getDay(); - const daysUntilNextSunday = 7 - dayOfWeek; // Calculate the number of days until the next Sunday - // Clone the given date and add the remaining days until Sunday - const nextSunday = new Date(date); - nextSunday.setDate(date.getDate() + daysUntilNextSunday); - return convertDateToDateString(nextSunday); - }, - getMonthEnd(dateStr) { - // convert string to date, do date calc, then return a string back - const date = new Date(dateStr.split("-")[0], parseInt(dateStr.split("-")[1]), 0); - return convertDateToDateString(date); - }, - getInitialViewWeeks(todayString, initialViewRowsToShow, preSelectedDate) { - // TODO: this only is for future direction; need to create logic for past direction - const weeks = []; - let weekStartDate = this.getWeekStartDate(todayString); - let weekEndDate = this.getWeekEndDate(todayString); - - if (preSelectedDate) { - let preSelectedDateMonthEnd = this.getMonthEnd( - preSelectedDate.replace("-mobile", "") - ); - let monthEndsByThisWeek = false; - let i = 0; - while (!monthEndsByThisWeek && i < 52) { - if (i > 0) { - weekStartDate = this.getNextWeekSunday(weekEndDate); - weekEndDate = this.getWeekEndDate(weekStartDate); - - if ( - (preSelectedDateMonthEnd > weekStartDate && - preSelectedDateMonthEnd < weekEndDate) || - preSelectedDateMonthEnd === weekStartDate || - preSelectedDateMonthEnd === weekEndDate - ) { - weekEndDate = preSelectedDateMonthEnd; - monthEndsByThisWeek = true; - } else if (preSelectedDateMonthEnd < weekEndDate) { - // Additional case: if month ends before this week (but not necessarily during it), - // still cut the loop here, but don't truncate the week. - - monthEndsByThisWeek = true; - } - } - weeks.push({ - weekNum: i + 1, - weekStartDate: weekStartDate, - weekEndDate: weekEndDate, - }); - i++; - } - } else { - for (let i = 0; i < initialViewRowsToShow; i++) { - if (i > 0) { - weekStartDate = this.getNextWeekSunday(weekEndDate); - weekEndDate = this.getWeekEndDate(weekStartDate); - } - weeks.push({ - weekNum: i + 1, - weekStartDate: weekStartDate, - weekEndDate: weekEndDate, - }); - } - // If any of these weeks is split between two months, then make them 2 separate "weeks" - // (a week split between two months is considered 2 weeks per business requirements) - const hasSplitWeek = (week) => { - return week.weekStartDate.split("-")[1] !== week.weekEndDate.split("-")[1] - ? true - : false; - }; - const splitWeekIndex = weeks.findIndex(hasSplitWeek); - - if (splitWeekIndex > -1) { - const week1 = []; - const week2 = []; - let switchToWeek2 = false; - - for (let j = 0; j < 7; j++) { - const newDate = convertDateStringToDate( - weeks[splitWeekIndex].weekStartDate - ); - newDate.setDate(newDate.getDate() + j); - if (newDate.getDate() === 1) switchToWeek2 = true; - if (switchToWeek2) { - week2.push(convertDateToDateString(newDate)); - } else { - week1.push(convertDateToDateString(newDate)); - } - } - - const week1EndDate = week1[week1.length - 1]; - const week2StartDate = week2[0]; - - if (week1EndDate < todayString) { - // replace week 1 with week 2 - weeks[splitWeekIndex].weekStartDate = week2StartDate; - } else { - const newWeek = { - weekNum: weeks[splitWeekIndex].weekNum, - weekStartDate: week2StartDate, - weekEndDate: weeks[splitWeekIndex].weekEndDate, - }; - weeks[splitWeekIndex].weekEndDate = week1EndDate; - weeks.splice(splitWeekIndex + 1, 0, newWeek); - weeks.pop(); - weeks.forEach((item, index) => { - if (index > splitWeekIndex) { - item.weekNum = item.weekNum + 1; - } - }); - } - } - } - return weeks; - }, async loadInitialData(config) { /* ** NOTE: this _could_ be called by a parent before fully loaded, so FYI component data or computeds might not be available @@ -444,10 +312,10 @@ export default { if (config.selectableDatesSetting === "past") calendarViewDirection = "past"; if (config.selectableDatesSetting === "custom") calendarViewDirection = "future"; - const currentMonthEnd = this.getMonthEnd(todayDateString); + const currentMonthEnd = getMonthEnd(todayDateString); // TODO - set up currentMonthStart if direction is PAST: // let currentMonthStart = new Date(todayYearNum, todayMonthIndex - 1, 1); - const initialViewWeeks = this.getInitialViewWeeks( + const initialViewWeeks = getInitialViewWeeks( todayDateString, config.initialViewRowsToShow, config.preSelectedDate diff --git a/src/layouts/schedule/helpers/schedule-helper.js b/src/layouts/schedule/helpers/schedule-helper.js index 4952ce2a9..799d4b92f 100644 --- a/src/layouts/schedule/helpers/schedule-helper.js +++ b/src/layouts/schedule/helpers/schedule-helper.js @@ -79,3 +79,137 @@ export function getTodayDate() { export function getTodayDateString() { return convertDateToDateString(getTodayDate()); } + +export function getMonthEnd(dateStr) { + // convert string to date, do date calc, then return a string back + const date = new Date(dateStr.split("-")[0], parseInt(dateStr.split("-")[1]), 0); + return convertDateToDateString(date); +} + +export function getWeekStartDate(dateString) { + const date = convertDateStringToDate(dateString); + const dayOfWeek = date.getDay(); + // Subtract the day of the week from date to get the date of Sunday + const sunday = new Date(date); + sunday.setDate(sunday.getDate() - dayOfWeek); + return convertDateToDateString(sunday); +} + +export function getWeekEndDate(dateString) { + const date = convertDateStringToDate(dateString); + const dayOfWeek = date.getDay(); + const daysUntilSaturday = 6 - dayOfWeek; // Calculate the number of days until Saturday + // Clone the given date and add the remaining days until Saturday + const saturday = new Date(date); + saturday.setDate(date.getDate() + daysUntilSaturday); + return convertDateToDateString(saturday); +} + +export function getNextWeekSunday(dateString) { + const date = convertDateStringToDate(dateString); + const dayOfWeek = date.getDay(); + const daysUntilNextSunday = 7 - dayOfWeek; // Calculate the number of days until the next Sunday + // Clone the given date and add the remaining days until Sunday + const nextSunday = new Date(date); + nextSunday.setDate(date.getDate() + daysUntilNextSunday); + return convertDateToDateString(nextSunday); +} + +export function getInitialViewWeeks(todayString, initialViewRowsToShow, preSelectedDate) { + const weeks = []; + let weekStartDate = getWeekStartDate(todayString); + let weekEndDate = getWeekEndDate(todayString); + + if (preSelectedDate) { + let preSelectedDateMonthEnd = getMonthEnd(preSelectedDate.replace("-mobile", "")); + let monthEndsByThisWeek = false; + let i = 0; + while (!monthEndsByThisWeek && i < 52) { + if (i > 0) { + weekStartDate = getNextWeekSunday(weekEndDate); + weekEndDate = getWeekEndDate(weekStartDate); + + if ( + (preSelectedDateMonthEnd > weekStartDate && + preSelectedDateMonthEnd < weekEndDate) || + preSelectedDateMonthEnd === weekStartDate || + preSelectedDateMonthEnd === weekEndDate + ) { + weekEndDate = preSelectedDateMonthEnd; + monthEndsByThisWeek = true; + } else if (preSelectedDateMonthEnd < weekEndDate) { + // Additional case: if month ends before this week (but not necessarily during it), + // still cut the loop here, but don't truncate the week. + + monthEndsByThisWeek = true; + } + } + weeks.push({ + weekNum: i + 1, + weekStartDate: weekStartDate, + weekEndDate: weekEndDate, + }); + i++; + } + } else { + for (let i = 0; i < initialViewRowsToShow; i++) { + if (i > 0) { + weekStartDate = getNextWeekSunday(weekEndDate); + weekEndDate = getWeekEndDate(weekStartDate); + } + weeks.push({ + weekNum: i + 1, + weekStartDate: weekStartDate, + weekEndDate: weekEndDate, + }); + } + // If any of these weeks is split between two months, then make them 2 separate "weeks" + // (a week split between two months is considered 2 weeks per business requirements) + const hasSplitWeek = (week) => { + return week.weekStartDate.split("-")[1] !== week.weekEndDate.split("-")[1] + ? true + : false; + }; + const splitWeekIndex = weeks.findIndex(hasSplitWeek); + + if (splitWeekIndex > -1) { + const week1 = []; + const week2 = []; + let switchToWeek2 = false; + + for (let j = 0; j < 7; j++) { + const newDate = convertDateStringToDate(weeks[splitWeekIndex].weekStartDate); + newDate.setDate(newDate.getDate() + j); + if (newDate.getDate() === 1) switchToWeek2 = true; + if (switchToWeek2) { + week2.push(convertDateToDateString(newDate)); + } else { + week1.push(convertDateToDateString(newDate)); + } + } + + const week1EndDate = week1[week1.length - 1]; + const week2StartDate = week2[0]; + + if (week1EndDate < todayString) { + // replace week 1 with week 2 + weeks[splitWeekIndex].weekStartDate = week2StartDate; + } else { + const newWeek = { + weekNum: weeks[splitWeekIndex].weekNum, + weekStartDate: week2StartDate, + weekEndDate: weeks[splitWeekIndex].weekEndDate, + }; + weeks[splitWeekIndex].weekEndDate = week1EndDate; + weeks.splice(splitWeekIndex + 1, 0, newWeek); + weeks.pop(); + weeks.forEach((item, index) => { + if (index > splitWeekIndex) { + item.weekNum = item.weekNum + 1; + } + }); + } + } + } + return weeks; +} diff --git a/src/layouts/schedule/multi-location-modal/multi-location-modal.vue b/src/layouts/schedule/multi-location-modal/multi-location-modal.vue index effa3c91b..68f8d4f7c 100644 --- a/src/layouts/schedule/multi-location-modal/multi-location-modal.vue +++ b/src/layouts/schedule/multi-location-modal/multi-location-modal.vue @@ -103,6 +103,7 @@ export default { timeSlot: this.selectedAppointment.timeSlot, routeCode: this.selectedAppointment.timeSlot.id, date: this.selectedAppointment.date, + provider: this.selectedAppointment.provider, }; this.confirmedAppointment = true; this.$emit("confirm-appointment", { diff --git a/src/layouts/schedule/schedule.vue b/src/layouts/schedule/schedule.vue index 89ed21a08..77f79f197 100644 --- a/src/layouts/schedule/schedule.vue +++ b/src/layouts/schedule/schedule.vue @@ -252,6 +252,8 @@ import { sumDateString, isDropOffRouteCode, getTodayDate, + getTodayDateString, + getInitialViewWeeks, } from "@/layouts/schedule/helpers/schedule-helper"; import { containsRecalParts, anyPartWithRequiresRecalFlag } from "@/helpers/recal-helper"; @@ -809,13 +811,6 @@ export default { }; }, selectedShopAnswer() { - const toTitleCase = (str) => { - if (!str) return ""; - return str.replace(/\w\S*/g, function (txt) { - return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); - }); - }; - if ( this.selectedProvider && this.selectedProvider.address && @@ -824,18 +819,11 @@ export default { const provider = this.shopProviderData?.shopProviders?.find( (p) => p.providerNumber === this.selectedProvider?.providerNumber ); - const streetAddress = toTitleCase(this.selectedProvider.address.streetAddress); - const city = toTitleCase(this.selectedProvider.address.city); - const state = this.selectedProvider.address.state; - const zipCode = this.selectedProvider.address.zipCode; - const distanceInMiles = provider ? Math.round(provider.distanceInMiles * 2) / 2 : 0; + const distanceInMiles = provider?.distanceInMiles + ? Math.round(provider.distanceInMiles * 2) / 2 + : 0; - return { - city: `${city}`, - distance: `${distanceInMiles} mi`, - address1: `${streetAddress}`, - address2: `${city}, ${state} ${zipCode}`, - }; + return this.getFormattedShopAddress(provider?.address, distanceInMiles); } return []; }, @@ -977,6 +965,17 @@ export default { "true" ); }, + initialViewStartDate() { + return getTodayDateString(); + }, + initialViewEndDate() { + const initialViewWeeks = getInitialViewWeeks( + this.initialViewStartDate, + NUMBER_OF_CALENDAR_ROWS_TO_SHOW_FOR_INITIAL_VIEW, + this.preSelectedDate + ); + return initialViewWeeks[initialViewWeeks.length - 1].weekEndDate; + }, }, methods: { splitCopyOnCMSPlaceHolder, @@ -1927,8 +1926,8 @@ export default { let promotedMobileAppointment = null; const todaysDate = getTodayDate(); - let firstMobileAMAppt = await this.getFirstAvailableApptByTOD("AM", "mobile"); - let firstMobilePMAppt = await this.getFirstAvailableApptByTOD("PM", "mobile"); + let firstMobileAMAppt = await this.getFirstAvailableMobileApptByTOD("AM"); + let firstMobilePMAppt = await this.getFirstAvailableMobileApptByTOD("PM"); if (!firstMobileAMAppt && !firstMobilePMAppt) { return; } @@ -2054,7 +2053,14 @@ export default { } }, async showMultiLocationModal() { - if (!this.selectedRouteCodeData?.routeCode) { + const showMultiLocationAppointment = experimentMixin.methods.getSettingValue( + experimentSettings.SHOW_MULTI_LOCATION_APPT + ); + + if ( + !this.selectedRouteCodeData?.routeCode && + showMultiLocationAppointment?.toLowerCase() === "true" + ) { let maxDayRangeToShowPmTimeslot = experimentMixin.methods.getSettingValue( experimentSettings.SHOW_PM_DAYS_MULTI_LOCATION ); @@ -2064,9 +2070,6 @@ export default { let maxDayRangeToShowAnyTimeslot = experimentMixin.methods.getSettingValue( experimentSettings.SHOW_NO_AVAILABILE_DAYS_MULTI_LOCATION ); - let showMultiLocationAppointment = experimentMixin.methods.getSettingValue( - experimentSettings.SHOW_MULTI_LOCATION_APPT - ); let experimentSettingsString = "ShowPm:" + maxDayRangeToShowPmTimeslot + @@ -2089,25 +2092,23 @@ export default { }; // Fetch all appointments - const [firstMobileAMAppt, firstMobilePMAppt, firstInshopAMAppt, firstInshopPMAppt] = - await Promise.all([ - this.getFirstAvailableApptByTOD("AM", "mobile"), - this.getFirstAvailableApptByTOD("PM", "mobile"), - this.getFirstAvailableApptByTOD("AM", "inshop"), - this.getFirstAvailableApptByTOD("PM", "inshop"), - ]); + const [firstMobileAMAppt, firstMobilePMAppt, firstInshopAppts] = await Promise.all([ + this.getFirstAvailableMobileApptByTOD("AM"), + this.getFirstAvailableMobileApptByTOD("PM"), + this.getFirstAvailableInshopApptsByTOD(["AM", "PM"]), + ]); // Extract dates const firstMobileAMDate = firstMobileAMAppt?.date; const firstMobilePMDate = firstMobilePMAppt?.date; - const firstInshopAMDate = firstInshopAMAppt?.date; - const firstInshopPMDate = firstInshopPMAppt?.date; + const firstInshopAMDate = firstInshopAppts[0]?.date; + const firstInshopPMDate = firstInshopAppts[1]?.date; // Calculate days until appointments const numberOfDaysToFirstMobileAMDate = calculateDaysUntilDate(firstMobileAMAppt); const numberOfDaysToFirstMobilePMDate = calculateDaysUntilDate(firstMobilePMAppt); - const numberOfDaysToFirstInshopAMDate = calculateDaysUntilDate(firstInshopAMAppt); - const numberOfDaysToFirstInshopPMDate = calculateDaysUntilDate(firstInshopPMAppt); + const numberOfDaysToFirstInshopAMDate = calculateDaysUntilDate(firstInshopAppts[0]); + const numberOfDaysToFirstInshopPMDate = calculateDaysUntilDate(firstInshopAppts[1]); const shouldExposeMultiLocationModal = () => { const isMobileAppointmentInDateRange = @@ -2121,10 +2122,7 @@ export default { (firstInshopPMDate && numberOfDaysToFirstInshopPMDate <= maxDayRangeToShowAnyTimeslot); - if (showMultiLocationAppointment?.toLowerCase() === "true") { - return isMobileAppointmentInDateRange && isInshopAppointmentInDateRange; - } - return false; + return isMobileAppointmentInDateRange && isInshopAppointmentInDateRange; }; if (shouldExposeMultiLocationModal()) { @@ -2158,8 +2156,8 @@ export default { // Selects the first available inshop time slot promotedInshopAppointment = numberOfDaysToFirstInshopAMDate <= numberOfDaysToFirstInshopPMDate - ? firstInshopAMAppt - : firstInshopPMAppt; + ? firstInshopAppts[0] + : firstInshopAppts[1]; } else { if ( firstMobilePMDate && @@ -2182,15 +2180,15 @@ export default { numberOfDaysToFirstInshopPMDate <= maxDayRangeToShowPmTimeslot ) { // Selects the first available PM time slot - promotedInshopAppointment = firstInshopPMAppt; + promotedInshopAppointment = firstInshopAppts[1]; } else if ( firstInshopPMDate && numberOfDaysToFirstInshopPMDate >= maxDayRangeToShowNoPmTimeslot ) { // Selects the first available AM time slot. If none, selects the first available PM time slot - promotedInshopAppointment = firstInshopAMAppt - ? firstInshopAMAppt - : firstInshopPMAppt; + promotedInshopAppointment = firstInshopAppts[0] + ? firstInshopAppts[0] + : firstInshopAppts[1]; } } @@ -2210,15 +2208,9 @@ export default { } } }, - async getFirstAvailableApptByTOD(timeOfDay, appointmentType) { - let selectableDays; - if (appointmentType?.toLowerCase() == "mobile") { - selectableDays = this.selectableDatesMobile?.days; - } else if (appointmentType?.toLowerCase() == "inshop") { - selectableDays = this.selectableDatesInshop?.days; - } else { - return; - } + + async getFirstAvailableMobileApptByTOD(timeOfDay) { + const selectableDays = this.selectableDatesMobile?.days; if (!selectableDays?.length) { return null; } else { @@ -2230,7 +2222,6 @@ export default { }, timeSlot: null, date: null, - addressCopy: null, }; const isMatchingApptDay = dateObj.timeSlots.some( (slot) => @@ -2238,16 +2229,6 @@ export default { (matchingTimeSlot.timeSlot = slot) && (matchingTimeSlot.date = dateObj.date) ); - if ( - appointmentType?.toLowerCase() == "inshop" && - this.selectedShopAnswer?.address1 && - this.selectedShopAnswer?.address2 - ) { - matchingTimeSlot.addressCopy = - this.selectedShopAnswer?.address1 + - ", " + - this.selectedShopAnswer?.address2; - } if (isMatchingApptDay && matchingTimeSlot) { return matchingTimeSlot; } @@ -2255,6 +2236,136 @@ export default { return null; } }, + + findMatchingInshopAppt(selectableDays, timeOfDay, provider) { + if (!selectableDays?.length) return null; + for (const dateObj of selectableDays) { + const distanceInMiles = provider?.distanceInMiles + ? Math.round(provider.distanceInMiles * 2) / 2 + : 0; + const providerAddress = this.getFormattedShopAddress( + provider?.address, + distanceInMiles + ); + let matchingTimeSlot = { + estimatedServiceMinutes: { + minimum: this.estimatedServiceMinutesMinimum, + maximum: this.estimatedServiceMinutesMaximum, + }, + timeSlot: null, + date: null, + addressCopy: providerAddress.address1 + ", " + providerAddress.address2, + provider: provider, + }; + const isMatchingApptDay = dateObj.timeSlots.some( + (slot) => + slot.id.includes(timeOfDay) && + (matchingTimeSlot.timeSlot = slot) && + (matchingTimeSlot.date = dateObj.date) + ); + if (isMatchingApptDay && matchingTimeSlot) { + return matchingTimeSlot; + } + } + return null; + }, + + async getFirstAvailableInshopApptsByTOD(timeOfDayArray) { + let matchingApptNearestAM1 = this.findMatchingInshopAppt( + this.selectableDatesInshop?.days, + timeOfDayArray[0], + this.selectedProvider + ); + let matchingApptNearestPM1 = this.findMatchingInshopAppt( + this.selectableDatesInshop?.days, + timeOfDayArray[1], + this.selectedProvider + ); + let matchingApptNearestAM2; + let matchingApptNearestPM2; + let matchingApptNearestAM3; + let matchingApptNearestPM3; + + const providerNearest2 = this.shopProviderData.shopProviders[1]; + const providerNearest3 = this.shopProviderData.shopProviders[2]; + + if (providerNearest2?.distanceInMiles && providerNearest2.distanceInMiles < 25) { + this.calendarLoadingStatus = "more"; + const selectableDaysFromProviderNearest2 = await getScheduleApiResponse({ + startDateString: this.initialViewStartDate, + endDateString: this.initialViewEndDate, + inshopProviderNumber: providerNearest2.providerNumber, + zipCode: this.zipCode, + includeMobileTimeSlots: false, + includeInshopTimeSlots: true, + }); + matchingApptNearestAM2 = this.findMatchingInshopAppt( + selectableDaysFromProviderNearest2?.inshopTimeSlotsData?.days, + "AM", + providerNearest2 + ); + matchingApptNearestPM2 = this.findMatchingInshopAppt( + selectableDaysFromProviderNearest2?.inshopTimeSlotsData?.days, + "PM", + providerNearest2 + ); + } + if (providerNearest3?.distanceInMiles && providerNearest3?.distanceInMiles < 25) { + this.calendarLoadingStatus = "more"; + const selectableDaysFromProviderNearest3 = await getScheduleApiResponse({ + startDateString: this.initialViewStartDate, + endDateString: this.initialViewEndDate, + inshopProviderNumber: providerNearest3.providerNumber, + zipCode: this.zipCode, + includeMobileTimeSlots: false, + includeInshopTimeSlots: true, + }); + matchingApptNearestAM3 = this.findMatchingInshopAppt( + selectableDaysFromProviderNearest3?.inshopTimeSlotsData?.days, + "AM", + providerNearest3 + ); + matchingApptNearestPM3 = this.findMatchingInshopAppt( + selectableDaysFromProviderNearest3?.inshopTimeSlotsData?.days, + "PM", + providerNearest3 + ); + } + this.calendarLoadingStatus = "none"; + + // WHICH OF THE 3 INSHOP APPTS IS THE EARLIEST? + const compareAppointments = (appt1, appt2) => { + // Handle null/undefined appointments + if (!appt1?.date) return 1; // appt1 is later (or invalid) + if (!appt2?.date) return -1; // appt2 is later (or invalid) + + // Compare dates first (YYYY-MM-DD format allows string comparison) + if (appt1.date < appt2.date) return -1; // appt1 is earlier + if (appt1.date > appt2.date) return 1; // appt2 is earlier + + // Dates are equal, compare times (HH:MM format allows string comparison) + const time1 = appt1.timeSlot?.startTime || "23:59"; + const time2 = appt2.timeSlot?.startTime || "23:59"; + + if (time1 < time2) return -1; // appt1 is earlier + if (time1 > time2) return 1; // appt2 is earlier + + return 0; // Completely equal + }; + // Find earliest AM appointment + const preferredApptAM = + [matchingApptNearestAM1, matchingApptNearestAM2, matchingApptNearestAM3] + .filter((appt) => appt?.date) // Remove null/undefined + .sort(compareAppointments)[0] || null; + + // Find earliest PM appointment + const preferredApptPM = + [matchingApptNearestPM1, matchingApptNearestPM2, matchingApptNearestPM3] + .filter((appt) => appt?.date) + .sort(compareAppointments)[0] || null; + + return [preferredApptAM, preferredApptPM]; + }, updateMobileFirstTimeSlotandNavigateForward(timeSlotObj) { this.selectedMobileFirstAppointment = true; this.appointmentType = AppointmentTypeStrings.MOBILE; @@ -2264,21 +2375,62 @@ export default { this.forwardButtonAction(); }, updateMultiLocationTimeSlotandNavigateForward({ selectedTimeSlot, appointmentType }) { - if (appointmentType?.toLowerCase() == "mobile") { - this.appointmentType = AppointmentTypeStrings.MOBILE; - } else if (appointmentType?.toLowerCase() == "inshop") { - this.appointmentType = AppointmentTypeStrings.IN_SHOP; - } + if (!selectedTimeSlot) return; this.selectedMultiLocationAppointment = true; this.selectedDate = selectedTimeSlot.date; - this.updateSelectedProvider(); - this.updateTimeSlot(selectedTimeSlot); + if (appointmentType?.toLowerCase() == "mobile") { + this.appointmentType = AppointmentTypeStrings.MOBILE; + this.updateSelectedProvider(); + this.updateTimeSlot(selectedTimeSlot); + } else if (appointmentType?.toLowerCase() == "inshop") { + this.appointmentType = AppointmentTypeStrings.IN_SHOP; + + this.updateSelectedProvider(selectedTimeSlot.provider); + + // UPDATE SELECTED TIME SLOT INFO + this.selectedTimeSlotInfo = { + timeSlot: { + date: selectedTimeSlot.date, + routeCode: selectedTimeSlot.routeCode, + startTime: selectedTimeSlot.timeSlot.startTime, + endTime: selectedTimeSlot.timeSlot.endTime, + jobMaxMinutes: this.estimatedServiceMinutesMaximum.toString(), + jobMinMinutes: this.estimatedServiceMinutesMinimum.toString(), + }, + isPremiumAppointment: selectedTimeSlot.isPremiumAppointment ? true : false, + }; + + // UPDATE APPT TYPE + this.appointmentType = this.getInShopOrDropOffApptType(selectedTimeSlot.routeCode); + } this.forwardButtonAction(); }, updateRecalAcknowledgedAndNavigateForward(isAcknowledged) { this.isRecalAcknowledgedForScheduling = isAcknowledged; this.forwardButtonAction(); }, + getFormattedShopAddress(providerAddress, distanceInMiles) { + const toTitleCase = (str) => { + if (!str) return ""; + return str.replace(/\w\S*/g, function (txt) { + return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); + }); + }; + + if (!providerAddress) return {}; + + const streetAddress = toTitleCase(providerAddress.streetAddress); + const city = toTitleCase(providerAddress.city); + const state = providerAddress.state; + const zipCode = providerAddress.zipCode; + + return { + city: city, + distance: `${distanceInMiles} mi`, + address1: streetAddress, + address2: `${city}, ${state} ${zipCode}`, + }; + }, }, watch: { appointmentTypeFromAppointmentTypeQuestion: {