diff --git a/src/layouts/review/review-block/review-block.vue b/src/layouts/review/review-block/review-block.vue index 67e639da9..497aa3634 100644 --- a/src/layouts/review/review-block/review-block.vue +++ b/src/layouts/review/review-block/review-block.vue @@ -3,6 +3,7 @@
+ + + + diff --git a/src/layouts/review/review-sections/service-location-review/service-location-review.vue b/src/layouts/review/review-sections/service-location-review/service-location-review.vue index db4f8188a..0da8361c3 100644 --- a/src/layouts/review/review-sections/service-location-review/service-location-review.vue +++ b/src/layouts/review/review-sections/service-location-review/service-location-review.vue @@ -10,7 +10,7 @@ import reviewBlock from "@/layouts/review/review-block/review-block"; import { AppointmentTypeStrings } from "@/constants/schedule-constants"; export default { - name: "service-package-review", + name: "service-location-review", props: { cmsWidgetName: String, serviceLocation: Object, diff --git a/src/layouts/review/review.vue b/src/layouts/review/review.vue index 59320bce9..78f24c35f 100644 --- a/src/layouts/review/review.vue +++ b/src/layouts/review/review.vue @@ -69,6 +69,13 @@ cmsWidgetName="ServiceLocationTitleWidget" :serviceLocation="serviceLocationInfo" @edit-clicked="editServiceLocation" /> + +
+ +
@@ -94,22 +101,16 @@ import vehicleReview from "@/layouts/review/review-sections/vehicle-review/vehic import damageReview from "@/layouts/review/review-sections/damage-review/damage-review"; import servicePackageReview from "@/layouts/review/review-sections/service-package-review/service-package-review"; import serviceLocationReview from "@/layouts/review/review-sections/service-location-review/service-location-review"; +import scheduleReview from "@/layouts/review/review-sections/schedule-review/schedule-review"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { settleAllPromises } from "@/helpers/layout-helper"; -import baseMixin from "@/mixins/base-mixin.js"; -import { storeActions } from "@/constants/store-actions"; export default { name: "review", async beforeRouteEnter(to, from, next) { // Call APIs const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage); - // Get rain defense and wiper availability for package review section. - const wipersPromise = baseMixin.methods.dispatchStoreAction(storeActions.GET_WIPERS); - const rainDefensePromise = baseMixin.methods.dispatchStoreAction( - storeActions.GET_RAIN_DEFENSE - ); const servicePackageInitialDataPromise = servicePackageReview.methods.loadInitialData(); @@ -168,6 +169,12 @@ export default { this.$route ); }, + editSchedule() { + this.$router.navigateWithoutSaving( + this.navigationScenarios.CLICKED_SCHEDULE_EDIT, + this.$route + ); + }, }, computed: { subHeaderTitle() { @@ -191,6 +198,12 @@ export default { serviceLocationInfo() { return this.$store.getters.order.serviceLocation; }, + appointmentType() { + return this.$store.getters.order.serviceLocation.appointmentType; + }, + exposeCms() { + return this.$root.cmsContentByWidget; + }, }, components: { funnelHeader, @@ -202,6 +215,7 @@ export default { damageReview, servicePackageReview, serviceLocationReview, + scheduleReview, }, }; diff --git a/src/layouts/schedule/helpers/schedule-helper.js b/src/layouts/schedule/helpers/schedule-helper.js index 82c756b5b..0f595b435 100644 --- a/src/layouts/schedule/helpers/schedule-helper.js +++ b/src/layouts/schedule/helpers/schedule-helper.js @@ -45,3 +45,33 @@ export function sumDateString(dateString, daysToAdd) { date.setDate(date.getDate() + daysToAdd); return convertDateToDateString(date); } + +export function militaryToTwelveHourTime(timeString) { + // Expected input: "HH:MM" + if (typeof timeString !== "string") return; + let hours = parseInt(timeString.split(":")[0]); + const minutes = timeString.split(":")[1]; + const meridianNotation = hours > 11 ? "PM" : "AM"; + + if (hours > 12) { + hours -= 12; + } + + return `${hours}:${minutes} ${meridianNotation}`; +} + +export function getDisplayTextForDurationLength(durationMinimum, durationMaximum) { + const isLongAppointment = durationMaximum >= 120; + const isDurationRange = durationMinimum !== durationMaximum; + + const adjustedMinimum = isLongAppointment ? durationMinimum / 60 : durationMinimum; + const adjustedMaximum = isLongAppointment ? durationMaximum / 60 : durationMaximum; + + const durationText = isDurationRange + ? `${adjustedMinimum} - ${adjustedMaximum}` + : adjustedMinimum; + + const unitText = isLongAppointment ? "hours" : "minutes"; + + return `${durationText} ${unitText}`; +} diff --git a/src/layouts/schedule/schedule.vue b/src/layouts/schedule/schedule.vue index 5bebe26fb..6a42b7a92 100644 --- a/src/layouts/schedule/schedule.vue +++ b/src/layouts/schedule/schedule.vue @@ -459,6 +459,7 @@ export default { startTime: null, endTime: null, jobMaxMinutes: null, + jobMinMinutes: null, }; } }, diff --git a/src/layouts/schedule/time-slot-modal-question/time-slot-modal-question.vue b/src/layouts/schedule/time-slot-modal-question/time-slot-modal-question.vue index 724facf6c..cce2c0503 100644 --- a/src/layouts/schedule/time-slot-modal-question/time-slot-modal-question.vue +++ b/src/layouts/schedule/time-slot-modal-question/time-slot-modal-question.vue @@ -47,7 +47,11 @@ import buttonQuestion from "@/digital-components/button-question/button-question import timeSlotModalListButton from "./time-slot-modal-list-button/time-slot-modal-list-button"; // Helpers -import { convertDateStringToDate } from "@/layouts/schedule/helpers/schedule-helper"; +import { + convertDateStringToDate, + militaryToTwelveHourTime, + getDisplayTextForDurationLength, +} from "@/layouts/schedule/helpers/schedule-helper"; import { deepClone } from "@/helpers/object-helper"; // Validation - TODO: Move this somewhere more global? @@ -88,6 +92,7 @@ export default { startTime: null, endTime: null, jobMaxMinutes: null, + jobMinMinutes: null, }), }, cmsWidgetName: String, @@ -263,7 +268,7 @@ export default { cmsWidgetFieldMappings.DURATION ); - const inshopDurationTime = this.getDisplayTextForDurationLength( + const inshopDurationTime = getDisplayTextForDurationLength( this.estimatedServiceMinutesMinimum, this.estimatedServiceMinutesMaximum ); @@ -346,33 +351,6 @@ export default { this.$emit("update:modelValue", this.selectedValue); this.closeModal(); }, - getDisplayTextForMilitaryTime(militaryTimeInput) { - // Expected input: "HH:MM" - let hours = parseInt(militaryTimeInput.split(":")[0]); - const minutes = militaryTimeInput.split(":")[1]; - const meridianNotation = hours > 11 ? "PM" : "AM"; - - if (hours > 12) { - hours -= 12; - } - - return `${hours}:${minutes} ${meridianNotation}`; - }, - getDisplayTextForDurationLength(durationMinimum, durationMaximum) { - const isLongAppointment = durationMaximum >= 120; - const isDurationRange = durationMinimum !== durationMaximum; - - const adjustedMinimum = isLongAppointment ? durationMinimum / 60 : durationMinimum; - const adjustedMaximum = isLongAppointment ? durationMaximum / 60 : durationMaximum; - - const durationText = isDurationRange - ? `${adjustedMinimum} - ${adjustedMaximum}` - : adjustedMinimum; - - const unitText = isLongAppointment ? "hours" : "minutes"; - - return `${durationText} ${unitText}`; - }, getRelevantDropOffCmsWidgetNameForSelectedTimeSlot( selectedTimeSlotId, isSameDayRelevant = false @@ -387,7 +365,7 @@ export default { }, getAvailableTimeSlotsForInshop(timeSlotsForSelectedDate) { return timeSlotsForSelectedDate.map((timeSlot) => { - const readableTime = this.getDisplayTextForMilitaryTime(timeSlot.startTime); + const readableTime = militaryToTwelveHourTime(timeSlot.startTime); return { value: timeSlot.id, buttonLabel: readableTime, @@ -415,9 +393,9 @@ export default { }, getAvailableTimeSlotsForMobile(timeSlotsForSelectedDate) { const availableTimeSlots = timeSlotsForSelectedDate.map((timeSlot) => { - const readableTime = `${this.getDisplayTextForMilitaryTime( + const readableTime = `${militaryToTwelveHourTime( timeSlot.startTime - )} - ${this.getDisplayTextForMilitaryTime(timeSlot.endTime)}`; + )} - ${militaryToTwelveHourTime(timeSlot.endTime)}`; return { value: timeSlot.id, buttonLabel: readableTime, @@ -472,6 +450,7 @@ export default { startTime: timeSlot.startTime, endTime: timeSlot.endTime, jobMaxMinutes: this.estimatedServiceMinutesMaximum.toString(), + jobMinMinutes: this.estimatedServiceMinutesMinimum.toString(), }; } @@ -481,6 +460,7 @@ export default { endTime: null, routeCode: null, jobMaxMinutes: null, + jobMinMinutes: null, }; }, }, diff --git a/src/router/router-constants/navigation-scenarios.js b/src/router/router-constants/navigation-scenarios.js index ae1c880ae..ce2f32134 100644 --- a/src/router/router-constants/navigation-scenarios.js +++ b/src/router/router-constants/navigation-scenarios.js @@ -51,6 +51,7 @@ const navigationScenarios = { CLICKED_DAMAGE_EDIT: "CLICKED_DAMAGE_EDIT", CLICKED_SERVICE_PACKAGE_EDIT: "CLICKED_SERVICE_PACKAGE_EDIT", CLICKED_SERVICE_LOCATION_EDIT: "CLICKED_SERVICE_LOCATION_EDIT", + CLICKED_SCHEDULE_EDIT: "CLICKED_SCHEDULE_EDIT", }; export { navigationScenarios }; diff --git a/src/router/router-constants/routing-table.js b/src/router/router-constants/routing-table.js index 017e1372a..f1f83b445 100644 --- a/src/router/router-constants/routing-table.js +++ b/src/router/router-constants/routing-table.js @@ -484,6 +484,10 @@ const routingTable = function (store) { scenario: navigationScenarios.CLICKED_SERVICE_LOCATION_EDIT, destinationFmgPageValue: fmgPageValues.SERVICE_LOCATION, }, + { + scenario: navigationScenarios.CLICKED_SCHEDULE_EDIT, + destinationFmgPageValue: fmgPageValues.SCHEDULE, + }, ], }, ]; diff --git a/src/store/index.js b/src/store/index.js index 7d1caf59a..635d685b4 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -14,6 +14,11 @@ import { deleteFunnelCookie } from "@/helpers/heritage-integration/cookie-helper import { deepEqual } from "@/helpers/object-helper"; import { AppointmentTypeStrings, PREMIUM_FEE_PART_TYPE } from "@/constants/schedule-constants"; import { partTypeStrings } from "@/constants/part-type-strings"; +import { + convertDateStringToDate, + militaryToTwelveHourTime, + getDisplayTextForDurationLength, +} from "@/layouts/schedule/helpers/schedule-helper"; // Export State const getDefaultState = () => { @@ -96,6 +101,7 @@ const getDefaultState = () => { endTime: null, routeCode: null, jobMaxMinutes: null, + jobMinMinutes: null, }, referralNumber: null, referralDate: null, @@ -283,6 +289,7 @@ export const mutations = { state.order.schedule.endTime = scheduleInfo.endTime; state.order.schedule.routeCode = scheduleInfo.routeCode; state.order.schedule.jobMaxMinutes = scheduleInfo.jobMaxMinutes; + state.order.schedule.jobMinMinutes = scheduleInfo.jobMinMinutes; } }, updateCustomerDetails(state, customerDetails) { @@ -368,6 +375,7 @@ export const mutations = { state.order.schedule.endTime = null; state.order.schedule.routeCode = null; state.order.schedule.jobMaxMinutes = null; + state.order.schedule.jobMinMinutes = null; //premium appointment fee used on schedule page also needs reset when schedule is reset const supportingItems = state.order.lineItems.supportingItems; @@ -506,6 +514,7 @@ export const mutations = { state.order.schedule.endTime = sessionInformation.order.schedule?.endTime; state.order.schedule.routeCode = sessionInformation.order.schedule?.routeCode; state.order.schedule.jobMaxMinutes = sessionInformation.order.schedule?.jobMaxMinutes; + state.order.schedule.jobMinMinutes = sessionInformation.order.schedule?.jobMinMinutes; }, updateExperiments(state, experiments) { state.applicationUser.experiments = experiments; @@ -540,6 +549,9 @@ export const getters = { isMobileAppointment: (state) => { return state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE; }, + isDropOffAppointment: (state) => { + return state.order.serviceLocation.appointmentType === AppointmentTypeStrings.DROP_OFF; + }, isRecalibrationOnOrder: (state) => { return getHasRecalibrationPart(state); }, @@ -548,6 +560,30 @@ export const getters = { (vap) => vap.partType.toUpperCase() === partTypeStrings.REAR_WIPER.toUpperCase() ); }, + scheduleDisplayText: (state) => { + const dateModel = convertDateStringToDate(state.order.schedule.date); + const readableDate = dateModel?.toLocaleDateString("en-us", { + weekday: "long", + month: "long", + day: "numeric", + year: "numeric", + }); + + const readableStartTime = militaryToTwelveHourTime(state.order.schedule.startTime); + const readableEndTime = militaryToTwelveHourTime(state.order.schedule.endTime); + + const readableDuration = getDisplayTextForDurationLength( + state.order.schedule.jobMinMinutes, + state.order.schedule.jobMaxMinutes + ); + + return { + date: readableDate, + startTime: readableStartTime, + endTime: readableEndTime, + duration: readableDuration, + }; + }, lineItems: (state) => state.order.lineItems, pageData: (state) => (page) => { return state.applicationUser.pageData[page]; @@ -1458,6 +1494,7 @@ export const actions = { endTime: order.schedule?.endTime, routeCode: order.schedule?.routeCode, jobMaxMinutes: order.schedule?.jobMaxMinutes, + jobMinMinutes: order.schedule?.jobMinMinutes, }, existingPromoCode: null, referralCorrelationId: order.referralCorrelationId,