From f06332c066c1fb661be90564bd6a1c3d72d70e3a Mon Sep 17 00:00:00 2001 From: Bill Richardson Date: Thu, 22 Jan 2026 13:19:36 -0500 Subject: [PATCH 01/10] initial check in --- src/constants/progress-bar-mapper.js | 3 - .../date-picker/date-picker.vue | 151 ++---- src/helpers/cms-content-helper.js | 1 + src/helpers/service-location-helper.js | 7 +- src/layouts/schedule-page/schedule-page.vue | 345 +++++++++--- .../appointment-type-question.vue | 11 +- .../mobile-location-modal-questions.vue | 7 +- .../vehicle-protected-question.vue | 0 .../service-location/service-location.spec.js | 81 ++- .../service-location/service-location.vue | 491 +++++++----------- .../service-zip-modal-question.spec.js | 2 +- .../service-zip-modal-question.vue | 5 +- .../service-zip-question.spec.js | 2 +- .../service-zip-question.vue | 19 +- .../shop-address/shop-address.vue | 56 +- src/mixins/vehicle-questions-mixin.spec.js | 2 +- src/router/router-constants/issPage-values.js | 1 - src/router/router-constants/routing-table.js | 25 +- 18 files changed, 622 insertions(+), 587 deletions(-) rename src/layouts/{ => schedule-page}/service-location/appointment-type-question/appointment-type-question.vue (93%) rename src/layouts/{service-location/mobile-location-modal-questions => schedule-page/service-location/mobile-location-modal-question}/mobile-location-modal-questions.vue (98%) rename src/layouts/{service-location/mobile-location-modal-questions => schedule-page/service-location/mobile-location-modal-question}/vehicle-protected-question/vehicle-protected-question.vue (100%) rename src/layouts/{ => schedule-page}/service-location/service-location.spec.js (78%) rename src/layouts/{ => schedule-page}/service-location/service-location.vue (52%) rename src/layouts/{ => schedule-page}/service-location/service-zip-modal-question/service-zip-modal-question.spec.js (97%) rename src/layouts/{ => schedule-page}/service-location/service-zip-modal-question/service-zip-modal-question.vue (80%) rename src/layouts/{ => schedule-page}/service-location/service-zip-question/service-zip-question.spec.js (89%) rename src/layouts/{ => schedule-page}/service-location/service-zip-question/service-zip-question.vue (93%) rename src/layouts/{ => schedule-page}/service-location/shop-address/shop-address.vue (91%) diff --git a/src/constants/progress-bar-mapper.js b/src/constants/progress-bar-mapper.js index c12bdf13..e80fbb5c 100644 --- a/src/constants/progress-bar-mapper.js +++ b/src/constants/progress-bar-mapper.js @@ -50,9 +50,6 @@ export const pageProgressMapper = { 'provider-preference': { percent: 60 }, - 'service-location': { - percent: 65 - }, 'schedule-page': { percent: 70 }, diff --git a/src/digital-components/date-picker/date-picker.vue b/src/digital-components/date-picker/date-picker.vue index bdd971cf..cf24627b 100644 --- a/src/digital-components/date-picker/date-picker.vue +++ b/src/digital-components/date-picker/date-picker.vue @@ -161,6 +161,10 @@ export default { }, default: selectableDaysOptions.PAST }, + isMobileView: { + type: Boolean, + default: false + }, modelValue: { type: Object }, @@ -241,8 +245,7 @@ export default { daysLoaded: 0, appointmentDurationMinutesMinimum: 90, appointmentDurationMinutesMaximum: 120, - initialDaysToLoad: 15, - isMobileView: false + initialDaysToLoad: 15 }; }, computed: { @@ -284,6 +287,16 @@ export default { } }, watch: { + isMobileView(newValue, oldValue) { + if (newValue !== oldValue) { + this.activePageIndex = 0; + this.selectedDate = null; + this.selectedTimeOfDayGrouping = null; + this.selectedTime = null; + this.selectedTimeSlot = null; + this.findFirstAvailableDateInView(); + } + }, selectedTimeSlot(newValue, oldValue) { if (newValue !== oldValue) { const testObj = this.getSelectedTimeSlotInfoObject(newValue); @@ -296,20 +309,30 @@ export default { } } }, - mounted() { - this.mql = window.matchMedia('(min-width: 1200px)'); - this.isMobileView = !this.mql.matches; - this.mql.addEventListener('change', this.handleMqlChange); - }, - unmounted() { - if (this.mql) { - this.mql.removeEventListener('change', this.handleMqlChange); - } - }, methods: { initializeComponent(initialData) { + this.resetComponent(); this.setCalendarData(initialData); }, + resetComponent() { + this.isLoading = true; + this.selectableDatesData = []; // NOTE: uses monthNum (1-based), NOT monthIndex (0-based) + this.selectableTimeSlotsData = []; + this.selectedTimeOfDayGrouping = null; + this.selectedDate = null; + this.selectedTime = null; + this.selectedTimeSlot = null; + this.activeDate = new Date(); + this.activePageIndex = 0; + this.todaysDate = new Date(); + this.mql = null; + this.daysInViewMobile = 3; + this.daysInViewStandard = 5; + this.daysLoaded = 0; + this.appointmentDurationMinutesMinimum = 90; + this.appointmentDurationMinutesMaximum = 120; + this.initialDaysToLoad = 15; + }, addPremiumFlagToInput(routeCode) { return (`${routeCode}${PREMIUM_TIME_SLOT_ID_FLAG}`); }, @@ -352,15 +375,6 @@ export default { isPremiumAppointment: null }; }, - handleMqlChange(e) { - this.isMobileView = !e.matches; - this.activePageIndex = 0; - this.selectedDate = null; - this.selectedTimeOfDayGrouping = null; - this.selectedTime = null; - this.selectedTimeSlot = null; - this.findFirstAvailableDateInView(); - }, displayTimeSlotTime(timeSlot) { const { appointmentType } = this.mainStore.order.serviceLocation; if (appointmentType === AppointmentTypeStrings.MOBILE || appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP) { @@ -434,38 +448,6 @@ export default { const dateToShow = this.getDateToShow(index); return convertDateToDateString(dateToShow); }, - getEndDateForExistingDate(startDate, selectedDateString, daysInView) { - const endDate = new Date(startDate); - endDate.setDate(endDate.getDate() + (daysInView - 1)); - - if (selectedDateString) { - const selectedDate = new Date(`${selectedDateString}T00:00:00`); - const daysDifferenceComparedToStartDate = Math.ceil((selectedDate - startDate) / (1000 * 60 * 60 * 24)); - if (selectedDate > endDate) { - const daysDifferenceComparedToStartDateAsPages = Math.ceil(daysDifferenceComparedToStartDate / daysInView); - const newEndDate = new Date(startDate); - newEndDate.setDate(newEndDate.getDate() + (daysDifferenceComparedToStartDateAsPages * daysInView) - 1); - - return { - endDateString: convertDateToDateString(newEndDate), - newDaysLoaded: (daysDifferenceComparedToStartDateAsPages * daysInView), - daysFromStart: daysDifferenceComparedToStartDate - }; - } - - return { - endDateString: convertDateToDateString(endDate), - newDaysLoaded: null, - daysFromStart: daysDifferenceComparedToStartDate - }; - } - - return { - endDateString: convertDateToDateString(endDate), - newDaysLoaded: null, - daysFromStart: null - }; - }, async gotoNextPage() { const maxPageIndex = Math.floor((this.daysLoaded - 1) / this.daysToAdd); if (this.activePageIndex >= maxPageIndex) { @@ -572,66 +554,9 @@ export default { timeSlotInputChanged(timeSlot) { this.selectTimeSlotForDay(timeSlot); }, - async loadInitialData(config) { - /* - ** NOTE: this _could_ be called by a parent before fully loaded, so data or computeds might not be available - */ - let todayDateString; - const todayDateObject = new Date(); - const calendarViewDirection = 'future'; - const initialDays = 15; - - if (this.todayString) { - todayDateString = this.todayString; - } else if (config.todayOverrideDateString) { - todayDateString = config.todayOverrideDateString; - } else { - todayDateString = convertDateToDateString(todayDateObject); - } - - const initialViewStartDate = todayDateString; - const initialEndDate = new Date(); - initialEndDate.setDate(initialEndDate.getDate() + (initialDays - 1)); - let initialViewEndDate = convertDateToDateString(initialEndDate); - - const endDateObject = this.getEndDateForExistingDate( - todayDateObject, - config.preSelectedDate, - initialDays - ); - - if (endDateObject.endDateString !== initialViewEndDate) { - initialViewEndDate = endDateObject.endDateString; - } - - const loadInitialDataPromise = new Promise((resolve) => { - const response = config.customSelectableDatesCallback( - initialViewStartDate, - initialViewEndDate, - useMainStore().order.serviceLocation.appointmentType, - useMainStore().order.serviceLocation.provider.providerNumber - ); - resolve(response); - }); - - return loadInitialDataPromise.then((response) => { - const initialData = { - todayDate: todayDateString, - initialViewStartDate, - initialViewEndDate, - calendarViewDirection, - initialShopTimeSlotsResponse: response, - preSelectedDate: config.preSelectedDate, - initialDaysLoaded: endDateObject.newDaysLoaded || initialDays, - daysFromStart: endDateObject.daysFromStart || null - }; - - return initialData; - }); - }, async setCalendarData(config = {}) { - const initialMql = window.matchMedia('(min-width: 1200px)'); - this.isMobileView = !initialMql.matches; + // const initialMql = window.matchMedia('(min-width: 1200px)'); + // this.isMobileView = !initialMql.matches; config.initialShopTimeSlotsResponse.days.forEach((selectableDate) => { const dateObjectToPush = { date: selectableDate.date, @@ -899,7 +824,7 @@ export default { border-radius: $border-radius-list-button; box-shadow: 0 1px 5px 0 rgba(0, 0, 0, .2); color: #525656; - padding: 0.5rem 0; + padding: 0.625rem 0; margin-top: 0.625rem; text-align: center; cursor: pointer; diff --git a/src/helpers/cms-content-helper.js b/src/helpers/cms-content-helper.js index b5dd2e97..bc550684 100644 --- a/src/helpers/cms-content-helper.js +++ b/src/helpers/cms-content-helper.js @@ -306,6 +306,7 @@ function mapStringToState(str) { const valueFromStore = getStoreValueFromString(match[2]); if (!valueFromStore) { console.warn('Unable to resolve global state data.'); + console.warn(`Tried to resolve: ${match[2]}`); return ''; // if we can't map our string to state data, return an empty string. } const stringWithReplacement = str.replace(match[0], valueFromStore); diff --git a/src/helpers/service-location-helper.js b/src/helpers/service-location-helper.js index 7e10ccd2..106aa34c 100644 --- a/src/helpers/service-location-helper.js +++ b/src/helpers/service-location-helper.js @@ -58,11 +58,16 @@ export async function getAvailabilityRating( providerNumber ) { // For a given shop provider number and date range, get the appointment time slots available + console.log('Getting shop time slots for availability rating...'); + console.log(`Provider Number: ${providerNumber}`); + console.log(`Start Date: ${startDate}`); + console.log(`End Date: ${endDate}`); + console.log(`Shop Appointment Type: ${shopAppointmentType}`); const shopTimeSlots = await useMainStore().getShopTimeSlots(startDate, endDate, shopAppointmentType, providerNumber); const numberOfDaysToEvaluate = 2; const isGoodAvailability = - shopTimeSlots.data.days.filter((x) => x.timeSlots.length > 0).length + shopTimeSlots.data.days.filter((x) => x.timeSlots.length > 6).length >= numberOfDaysToEvaluate; const shopStatus = isGoodAvailability ? 'high' : 'low'; diff --git a/src/layouts/schedule-page/schedule-page.vue b/src/layouts/schedule-page/schedule-page.vue index aa08e79a..f800cb62 100644 --- a/src/layouts/schedule-page/schedule-page.vue +++ b/src/layouts/schedule-page/schedule-page.vue @@ -15,7 +15,19 @@ cmsWidgetName="ScheduleSubHeaderWidget" secondaryTextClasses="text-center small sub-text" class="mt-4" /> -
+
+ +
+
+
+
@@ -25,21 +37,23 @@ customComponentId="dateQuestion" selectableDatesSetting="custom" class="text-link-small" + :isMobileView="isMobileView" :showTimeSlotError="showDatePickerError" - :customSelectableDatesCallback=" - getAvailableDatesMethod - " + :customSelectableDatesCallback="getAvailableDatesMethod" @dateSelected="dateSelectedFromPicker" @timeSlotSelected="timeSlotSelectedFromPicker" /> -
+ @@ -50,6 +64,7 @@ import siteHeader from '@/iss-components/site-header/site-header.vue'; import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header.vue'; import locationAlerts from '@/layouts/schedule-page/location-alerts/location-alerts.vue'; import datePicker from '@/digital-components/date-picker/date-picker.vue'; +import serviceLocation from '@/layouts/schedule-page/service-location/service-location.vue'; import siteFooter from '@/iss-components/site-footer/site-footer.vue'; // Supporting files @@ -65,9 +80,22 @@ import { } from '@/helpers/cms-content-helper'; import { calcDaysBetweenDates, + convertDateToDateString, + // convertDateToShortMonth, + // convertDateToTwoDigitDay, + // convertDateToTwoDigitMonth, + // getDisplayTextForDurationLength, + // isAfternoon, + // militaryToTwelveHourTime, sumDateString } from '@/helpers/date-helper'; import settleAllPromises from '@/helpers/layout-helper'; +import { + getPricedMobileFeePart, + getServiceabilityDetails, + getZipCodeData, + getMobileZipCodeData +} from '@/helpers/service-location-helper'; import { Form } from 'vee-validate'; import BaseFormMixin from '@/mixins/base-form-mixin.js'; import { useMainStore } from '@/store'; @@ -182,6 +210,7 @@ export default { siteSubHeader, locationAlerts, datePicker, + serviceLocation, siteFooter, // eslint-disable-next-line vue/no-reserved-component-names Form @@ -195,17 +224,14 @@ export default { } const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); - - const datePickerInitialDataPromise = - await datePicker.methods.loadInitialData({ - selectableDatesSetting: 'custom', - initialViewRowsToShow: 5, - customSelectableDatesCallback: getAvailableDates, - preSelectedDate - }); + const serviceZipCode = useMainStore().order.serviceLocation.zipCode || useMainStore().order.customer.address.zipCode; + const zipCodeData = getZipCodeData(serviceZipCode); + const mobileFeePartPromise = getPricedMobileFeePart(serviceZipCode); + const serviceabilityDetailsPromise = getServiceabilityDetails(serviceZipCode); + const getGlassFeesPromise = useMainStore().getGlassFees(); + const providersPromise = useMainStore().getProviders(serviceZipCode); const premiumFeePromise = useMainStore().getMobilePremiumFee(); - const premiumFeeWithPricePromise = premiumFeePromise.then((result) => { if (result.data) { return useMainStore().getCombinedQuote(result.data); @@ -213,24 +239,35 @@ export default { return result.data; }); - const alertReasonsPromise = locationAlerts.methods.loadInitialData( - useMainStore().order.serviceLocation.zipCodeCtu, - useMainStore().order.serviceLocation.provider?.address?.zipCodeCtu - ); - // Settle promises and get results const promiseResultMap = [ { resultKey: 'cmsContent', promise: cmsContentPromise }, + // { + // resultKey: 'datePickerInitialData', + // promise: datePickerInitialDataPromise + // }, { - resultKey: 'alertReasons', - promise: alertReasonsPromise + resultKey: 'mobileFeePart', + promise: mobileFeePartPromise }, { - resultKey: 'datePickerInitialData', - promise: datePickerInitialDataPromise + resultKey: 'glassFees', + promise: getGlassFeesPromise + }, + { + resultKey: 'serviceabilityDetails', + promise: serviceabilityDetailsPromise + }, + { + resultKey: 'zipCodeData', + promise: zipCodeData + }, + { + resultKey: 'providers', + promise: providersPromise }, { resultKey: 'premiumFeeWithPrice', @@ -240,14 +277,26 @@ export default { // use resultMap to populate layout content. const resultMap = await settleAllPromises(promiseResultMap); - next((vm) => { + const serviceLocationData = { + glassFees: resultMap.glassFees, + mobileFeePart: resultMap.mobileFeePart, + providers: resultMap.providers, + serviceabilityDetails: resultMap.serviceabilityDetails, + zipCodeData: resultMap.zipCodeData + }; + useMainStore().updateIsSafeliteProvider(true); + next(async (vm) => { + const initialServiceLocationObj = { + provider: resultMap.providers?.shopProviders[0], + zipCode: useMainStore().order.serviceLocation.zipCode || useMainStore().order.customer.address.zipCode, + zipCodeCtu: resultMap.zipCodeData?.zipCodeCtu + }; + vm.setCmsContent(resultMap.cmsContent); - vm.$refs.datePicker.initializeComponent(resultMap.datePickerInitialData); - vm.$refs.locationAlerts.initializeComponent(resultMap.alertReasons); - vm.setData( - resultMap.datePickerInitialData.initialShopTimeSlotsResponse, - resultMap.premiumFeeWithPrice - ); + // vm.$refs.datePicker.initializeComponent(resultMap.datePickerInitialData); + // vm.$refs.locationAlerts.initializeComponent(resultMap.alertReasons); + vm.$refs.serviceLocation.initializeComponent(serviceLocationData); + await vm.setData(resultMap.premiumFeeWithPrice, initialServiceLocationObj); }); }, setup() { @@ -256,23 +305,51 @@ export default { }, data() { return { - selectedDate: this.getSelectedDate(), - selectedTimeSlotInfo: this.getSelectedTimeSlotInfo(), + isMobileView: false, + mobilePremiumAppointmentFee: null, selectableDatesData: [], - showDatePickerError: false, - mobilePremiumAppointmentFee: null + selectedAppointmentType: this.getAppointmentType(), + selectedDate: this.getSelectedDate(), + selectedProvider: this.getSelectedProvider(), + selectedServiceLocation: this.getServiceLocation(), + selectedTimeSlotInfo: this.getSelectedTimeSlotInfo(), + showDatePickerError: false }; }, computed: { - ChangeShopLinkText() { - return this.getCmsContent('ChangeShopLink', 'Text'); - }, - ChangeShopLink() { - return this.splitCopyOnCMSPlaceHolder(this.ChangeShopLinkText); - }, appointmentType() { return useMainStore().order.serviceLocation.appointmentType; }, + daysToAdd() { + return this.isMobileView ? this.daysInViewMobile : this.daysInViewStandard; + }, + hasNeededServiceLocationData() { + const serviceLocationToUse = this.selectedServiceLocation; + if (!serviceLocationToUse || !this.selectedAppointmentType) { + return false; + } + const serviceLocationPreReqs = serviceLocationToUse.zipCode !== null + && serviceLocationToUse.zipCodeCtu !== null + && serviceLocationToUse.appointmentType !== null + && (serviceLocationToUse.appointmentType === AppointmentTypeStrings.MOBILE + || serviceLocationToUse.appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP + || serviceLocationToUse.provider.providerNumber); + + const damageInfo = useMainStore().order.damage.isRepair + || (useMainStore().order.lineItems?.glassParts != null && useMainStore().order.lineItems.glassParts.length > 0); + + const supportingItems = this.supportingItems !== null; + + console.log('Has Needed Service Location Data Check:', { + serviceLocationPreReqs, + supportingItems, + damageInfo + }); + + console.log('Result:', (serviceLocationPreReqs && supportingItems && damageInfo)); + + return (serviceLocationPreReqs && supportingItems && damageInfo); + }, isFormValid() { const hasTimeSlotSelected = this.selectedTimeSlotInfo?.timeSlot?.routeCode != null; return hasTimeSlotSelected; @@ -281,34 +358,50 @@ export default { return useMainStore().lineItems.supportingItems; } }, + mounted() { + this.mql = window.matchMedia('(min-width: 1200px)'); + this.isMobileView = !this.mql.matches; + this.mql.addEventListener('change', this.handleMqlChange); + }, + unmounted() { + if (this.mql) { + this.mql.removeEventListener('change', this.handleMqlChange); + } + }, methods: { splitCopyOnCMSPlaceHolder, arePagePrerequisitesValid() { - const { serviceLocation } = useMainStore().order; - const serviceLocationPreReqs = - serviceLocation.zipCode - && serviceLocation.zipCodeCtu - && serviceLocation.appointmentType - && (serviceLocation.appointmentType - === AppointmentTypeStrings.MOBILE - || serviceLocation.appointmentType - === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP - || serviceLocation.provider.providerNumber); - const supportingItems = this.supportingItems !== null; - const damageInfo = - useMainStore().order.damage.isRepair - || (useMainStore().order.lineItems?.glassParts != null - && useMainStore().order.lineItems.glassParts.length > 0); - - return serviceLocationPreReqs && supportingItems && damageInfo; + return useMainStore().order.serviceLocation.zipCode !== null; }, - setData(initialShopTimeSlotsResponse, premiumFeeWithPriceResponse) { - this.selectableDatesData = initialShopTimeSlotsResponse; + async setData(premiumFeeWithPriceResponse, initialServiceLocationObj) { + // this.selectableDatesData = initialShopTimeSlotsResponse; this.mobilePremiumAppointmentFee = premiumFeeWithPriceResponse ? premiumFeeWithPriceResponse[0] : null; + this.selectedServiceLocation = initialServiceLocationObj; + + if (this.selectedAppointmentType !== AppointmentTypeStrings.MOBILE + && this.selectedAppointmentType !== AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP) { + const initialData = await this.getDatePickerInitialData(initialServiceLocationObj.provider?.providerNumber); + this.selectableDatesData = initialData.initialShopTimeSlotsResponse; + this.$refs.datePicker.setCalendarData(initialData); + } + }, + appointmentTypeChangedFromServiceLocation(newAppointmentType) { + console.log('Parent - Appointment Type Changed from Service Location:', newAppointmentType); + this.selectedAppointmentType = newAppointmentType; + this.shouldRefreshDatePicker(); + }, + dateSelectedFromPicker(date) { + this.selectedDate = date; + this.showDatePickerError = false; + }, + getAppointmentType() { + return this.mainStore.order?.serviceLocation?.appointmentType; }, async getAvailableDatesMethod(startDate, endDate) { + console.log('Fetching Available Dates from Method...'); + console.log('More Data concatenation called with Start Date:', startDate, 'End Date:', endDate); const newShopTimeSlots = await getAvailableDates( startDate, endDate, @@ -321,12 +414,103 @@ export default { return newShopTimeSlots; }, getAvailableDates, + async getDatePickerInitialData(defaultProviderNumber = null) { + console.log('Fetching Date Picker Initial Data...'); + console.log('defaultProviderNumber:', defaultProviderNumber); + let todayDateString; + const todayDateObject = new Date(); + const calendarViewDirection = 'future'; + const initialDays = 15; + + if (this.todayString) { + todayDateString = this.todayString; + } else { + todayDateString = convertDateToDateString(todayDateObject); + } + + const initialViewStartDate = todayDateString; + const initialEndDate = new Date(); + initialEndDate.setDate(initialEndDate.getDate() + (initialDays - 1)); + let initialViewEndDate = convertDateToDateString(initialEndDate); + const preSelectedDate = this.mainStore.order.schedule.date; + + const endDateObject = this.getEndDateForExistingDate( + todayDateObject, + preSelectedDate, + initialDays + ); + + if (endDateObject.endDateString !== initialViewEndDate) { + initialViewEndDate = endDateObject.endDateString; + } + + const initialDataPickerDataPromise = new Promise((resolve) => { + const response = getAvailableDates( + initialViewStartDate, + initialViewEndDate, + this.selectedAppointmentType || AppointmentTypeStrings.IN_SHOP, + this.selectedProvider.providerNumber || defaultProviderNumber + ); + resolve(response); + }); + + const initialData = await initialDataPickerDataPromise.then((response) => ({ + todayDate: todayDateString, + initialViewStartDate, + initialViewEndDate, + calendarViewDirection, + initialShopTimeSlotsResponse: response, + preSelectedDate, + initialDaysLoaded: endDateObject.newDaysLoaded || initialDays, + daysFromStart: endDateObject.daysFromStart || null + })); + + console.log('Date Picker Initial Data:'); + console.log(initialData); + + return initialData; + }, + getEndDateForExistingDate(startDate, selectedDateString, daysInView) { + const endDate = new Date(startDate); + endDate.setDate(endDate.getDate() + (daysInView - 1)); + + if (selectedDateString) { + const selectedDate = new Date(`${selectedDateString}T00:00:00`); + const daysDifferenceComparedToStartDate = Math.ceil((selectedDate - startDate) / (1000 * 60 * 60 * 24)); + if (selectedDate > endDate) { + const daysDifferenceComparedToStartDateAsPages = Math.ceil(daysDifferenceComparedToStartDate / daysInView); + const newEndDate = new Date(startDate); + newEndDate.setDate(newEndDate.getDate() + (daysDifferenceComparedToStartDateAsPages * daysInView) - 1); + + return { + endDateString: convertDateToDateString(newEndDate), + newDaysLoaded: (daysDifferenceComparedToStartDateAsPages * daysInView), + daysFromStart: daysDifferenceComparedToStartDate + }; + } + + return { + endDateString: convertDateToDateString(endDate), + newDaysLoaded: null, + daysFromStart: daysDifferenceComparedToStartDate + }; + } + + return { + endDateString: convertDateToDateString(endDate), + newDaysLoaded: null, + daysFromStart: null + }; + }, getServiceZipCtuCodeFromStore() { return this.mainStore.order.serviceLocation.zipCodeCtu; }, getSelectedDate() { return this.mainStore.order.schedule.date; }, + getSelectedProvider() { + return this.mainStore.order.serviceLocation.provider; + }, getSelectedTimeSlotInfo() { const isPremiumAppointment = !!( @@ -341,9 +525,29 @@ export default { return selectedTimeSlotInfo; }, - dateSelectedFromPicker(date) { - this.selectedDate = date; - this.showDatePickerError = false; + getServiceLocation() { + return this.mainStore.order.serviceLocation; + }, + handleMqlChange(e) { + this.isMobileView = !e.matches; + }, + mobileZipUpdatedFromChild(newMobileZip) { + console.log('Parent - Mobile Zip Updated from Child:', newMobileZip); + }, + providerChangedFromChild(newProvider) { + console.log('Parent - Provider Changed from Child:', newProvider); + this.selectedProvider = newProvider; + // this.shouldRefreshDatePicker(); + }, + serviceLocationUpdatedFromChild(serviceLocationFromChild) { + this.selectedServiceLocation = serviceLocationFromChild; + }, + async shouldRefreshDatePicker() { + if (this.selectedAppointmentType === AppointmentTypeStrings.IN_SHOP && 1 === 2) { + const initialData = await this.getDatePickerInitialData(); + this.selectableDatesData = initialData.initialShopTimeSlotsResponse; + this.$refs.datePicker.initializeComponent(initialData); + } }, timeSlotSelectedFromPicker(timeSlot) { this.selectedTimeSlotInfo = timeSlot; @@ -369,11 +573,12 @@ export default { $page-side-padding: 1.5rem; .iss-heritage-container-width { + padding-right: 0.9375rem; + padding-left: 0.9375rem; + .schedule-page-container { position: relative; min-height: 1px; - padding-left: .9375rem; - padding-right: .9375rem; } } diff --git a/src/layouts/service-location/appointment-type-question/appointment-type-question.vue b/src/layouts/schedule-page/service-location/appointment-type-question/appointment-type-question.vue similarity index 93% rename from src/layouts/service-location/appointment-type-question/appointment-type-question.vue rename to src/layouts/schedule-page/service-location/appointment-type-question/appointment-type-question.vue index f14c51c4..1883d346 100644 --- a/src/layouts/service-location/appointment-type-question/appointment-type-question.vue +++ b/src/layouts/schedule-page/service-location/appointment-type-question/appointment-type-question.vue @@ -19,12 +19,10 @@ diff --git a/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue b/src/layouts/schedule-page/service-location/mobile-location-modal-question/mobile-location-modal-questions.vue similarity index 98% rename from src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue rename to src/layouts/schedule-page/service-location/mobile-location-modal-question/mobile-location-modal-questions.vue index 9647c322..a82fbc52 100644 --- a/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue +++ b/src/layouts/schedule-page/service-location/mobile-location-modal-question/mobile-location-modal-questions.vue @@ -79,8 +79,6 @@ import modal from '@/digital-components/modal/modal.vue'; import alert from '@/ux-components/alert/alert.vue'; import { useField } from 'vee-validate'; import addressQuestions from '@/iss-components/address-questions/address-questions.vue'; -// eslint-disable-next-line max-len -import vehicleProtectedQuestion from '@/layouts/service-location/mobile-location-modal-questions/vehicle-protected-question/vehicle-protected-question.vue'; // Constants import { experimentSettings } from '@/constants/experiments'; @@ -90,10 +88,11 @@ import { useMainStore } from '@/store'; import { getMobileZipCodeData, getPricedMobileFeePart, - getServiceabilityDetails, - getZipCodeData + getServiceabilityDetails } from '@/helpers/service-location-helper'; import { deepClone } from '@/helpers/object-helper.js'; +// eslint-disable-next-line max-len +import vehicleProtectedQuestion from '@/layouts/schedule-page/service-location/mobile-location-modal-question/vehicle-protected-question/vehicle-protected-question.vue'; export default { name: 'mobile-location-modal-questions', diff --git a/src/layouts/service-location/mobile-location-modal-questions/vehicle-protected-question/vehicle-protected-question.vue b/src/layouts/schedule-page/service-location/mobile-location-modal-question/vehicle-protected-question/vehicle-protected-question.vue similarity index 100% rename from src/layouts/service-location/mobile-location-modal-questions/vehicle-protected-question/vehicle-protected-question.vue rename to src/layouts/schedule-page/service-location/mobile-location-modal-question/vehicle-protected-question/vehicle-protected-question.vue diff --git a/src/layouts/service-location/service-location.spec.js b/src/layouts/schedule-page/service-location/service-location.spec.js similarity index 78% rename from src/layouts/service-location/service-location.spec.js rename to src/layouts/schedule-page/service-location/service-location.spec.js index a09c0542..20e0596b 100644 --- a/src/layouts/service-location/service-location.spec.js +++ b/src/layouts/schedule-page/service-location/service-location.spec.js @@ -1,13 +1,12 @@ /* eslint-env jest */ import baseMixin from '@/mixins/base-mixin'; -import { mount, flushPromises } from '@vue/test-utils'; +import { mount } from '@vue/test-utils'; import { createTestingPinia } from '@pinia/testing'; import { AppointmentTypeStrings } from '@/constants/schedule-constants'; import navigationScenarios from '@/router/router-constants/navigation-scenarios'; import routerParams from '@/router/router-constants/router-params'; import { useMainStore } from '@/store'; -import serviceLocation from '@/layouts/service-location/service-location.vue'; -import { getZipCodeData } from '@/helpers/service-location-helper'; +import serviceLocation from '@/layouts/schedule-page/service-location/service-location.vue'; const mockGetServiceabilityDetails = () => { const serviceabilityDetails = { @@ -50,43 +49,41 @@ const mockZipcodeData = (zip) => { state: null, zipCodeCtu: null }); -} -const mockProviders = () => { - return Promise.resolve([ - { - "address": { - "city": "COLUMBUS", - "country": "US", - "state": "OH", - "streetAddress": "6826 Sawmill Rd", - "streetAddress2": "", - "zipCode": "43235", - "zipCodeCtu": "03357" - }, - "distanceInMiles": 4.136335989015438, - "providerNumber": "003357", - "companyName": "SAFELITE AUTOGLASS - COLUMBUS, OH", - "phoneNumber": "6142336400", - "isSafeliteShop": true +}; +const mockProviders = () => Promise.resolve([ + { + address: { + city: 'COLUMBUS', + country: 'US', + state: 'OH', + streetAddress: '6826 Sawmill Rd', + streetAddress2: '', + zipCode: '43235', + zipCodeCtu: '03357' }, - { - "address": { - "city": "Lewis Center", - "country": "US", - "state": "OH", - "streetAddress": "1343 Cameron Ave", - "streetAddress2": "", - "zipCode": "43035", - "zipCodeCtu": "03357" - }, - "distanceInMiles": 8.193072412262042, - "providerNumber": "003417", - "companyName": "SAFELITE AUTOGLASS - LEWIS CENTER, OH", - "phoneNumber": "6147815433", - "isSafeliteShop": true - } - ]) -} + distanceInMiles: 4.136335989015438, + providerNumber: '003357', + companyName: 'SAFELITE AUTOGLASS - COLUMBUS, OH', + phoneNumber: '6142336400', + isSafeliteShop: true + }, + { + address: { + city: 'Lewis Center', + country: 'US', + state: 'OH', + streetAddress: '1343 Cameron Ave', + streetAddress2: '', + zipCode: '43035', + zipCodeCtu: '03357' + }, + distanceInMiles: 8.193072412262042, + providerNumber: '003417', + companyName: 'SAFELITE AUTOGLASS - LEWIS CENTER, OH', + phoneNumber: '6147815433', + isSafeliteShop: true + } +]); jest.mock( '@/helpers/service-location-helper', () => ({ @@ -138,13 +135,13 @@ const mountOptions = { } } }; -function setupMocks({ appointmentType = AppointmentTypeStrings.IN_SHOP }) { +function setupMocks({ appointmentType = AppointmentTypeStrings.IN_SHOP }) { mountOptions.global.plugins = [createTestingPinia({ initialState: { main: { order: { serviceLocation: { - appointmentType: appointmentType + appointmentType } } } @@ -204,7 +201,7 @@ describe('service-location.vue', () => { expect(wrapper.vm.zipContainsMilitaryBase).toBe(false); - const newMobileServiceZipCode = '45433' + const newMobileServiceZipCode = '45433'; // Act mobileServiceZipCodeQuestion.vm.$emit('update:modelValue', newMobileServiceZipCode); diff --git a/src/layouts/service-location/service-location.vue b/src/layouts/schedule-page/service-location/service-location.vue similarity index 52% rename from src/layouts/service-location/service-location.vue rename to src/layouts/schedule-page/service-location/service-location.vue index 2084a282..975accdc 100644 --- a/src/layouts/service-location/service-location.vue +++ b/src/layouts/schedule-page/service-location/service-location.vue @@ -1,166 +1,142 @@