diff --git a/src/constants/schedule-constants.js b/src/constants/schedule-constants.js index 86c2a793c..5bb784efb 100644 --- a/src/constants/schedule-constants.js +++ b/src/constants/schedule-constants.js @@ -4,7 +4,7 @@ const AppointmentTypeStrings = { DROP_OFF: "Dropoff", }; -const PREMIUM_TIME_SLOT_ID_FLAG = "-premium"; +const PREMIUM_TIME_SLOT_ID_FLAG = "-PREMIUM"; const PREMIUM_FEE_PART_TYPE = "EARLY BIRD"; diff --git a/src/digital-components/dropdown-question/dropdown-question.vue b/src/digital-components/dropdown-question/dropdown-question.vue index 6c032b740..70bf6c90c 100644 --- a/src/digital-components/dropdown-question/dropdown-question.vue +++ b/src/digital-components/dropdown-question/dropdown-question.vue @@ -26,7 +26,7 @@ {{ value }} -
+
{{ errorMessage }}
@@ -56,6 +56,11 @@ export default { required: false, default: false, }, + disableValidationIfElementDisabled: { + type: Boolean, + required: false, + default: false, + }, }, setup(props) { const uuid = uuidv4(); @@ -110,6 +115,9 @@ export default { this.$emit("update:modelValue", newValue); }, }, + disableValidation() { + return this.disableValidationIfElementDisabled ? !this.isDisabled : true; + }, }, watch: { selectedOption(newValue) { diff --git a/src/layouts/schedule/schedule.spec.js b/src/layouts/schedule/schedule.spec.js index b47036c10..e8447f3ab 100644 --- a/src/layouts/schedule/schedule.spec.js +++ b/src/layouts/schedule/schedule.spec.js @@ -6,7 +6,6 @@ import { shallowMount } from "@vue/test-utils"; import { getMountOptions } from "@/helpers/unit-test-helper.js"; import store from "@/store"; import router from "@/router"; -import { nextTick } from "vue"; import baseMixin from "../../mixins/base-mixin"; // Mock basemixin @@ -130,64 +129,67 @@ afterEach(() => { describe("schedule.vue...", () => { describe("initial load", () => { test("should pass arePagePrerequisitesValid with a mobile order and no providerNumber", () => { - //Arrange + // Arrange const { wrapper } = setupMocks({}); store.getters.order.serviceLocation.appointmentType = "Mobile"; store.getters.order.serviceLocation.provider = null; - //Act + // Act const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); - //Assert + // Assert expect(arePagePrerequisitesValid).toBe(true); }); + test("should pass arePagePrerequisitesValid with a inshop order and providerNumber", () => { - //Arrange + // Arrange const { wrapper } = setupMocks({}); - //Act + // Act const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); - //Assert + // Assert expect(arePagePrerequisitesValid).toBe(true); }); + test("should fail arePagePrerequisitesValid with a replace with no glass parts", async () => { - //Arrange + // Arrange const { wrapper } = setupMocks({}); store.getters.order.lineItems.glassParts = []; - //Act + // Act const arePagePrerequisitesValid2 = await wrapper.vm.arePagePrerequisitesValid(); - //Assert + // Assert expect(arePagePrerequisitesValid2).toBe(false); }); + test("should fail arePagePrerequisitesValid without isInsurance", () => { - //Arrange + // Arrange const { wrapper } = setupMocks({}); store.getters.payment.isInsurance = null; - //Act + // Act const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); - //Assert + // Assert expect(arePagePrerequisitesValid).toBe(false); }); test("should return newShopTimeSlots when getAvailableDatesMethod is called", async () => { - //Arrange + // Arrange const { wrapper } = setupMocks({}); wrapper.vm.selectableDatesData = { days: [], }; - //Act + // Act const newShopTimeSlots = await wrapper.vm.getAvailableDatesMethod( "2023-01-01", "2023-01-31" ); - //Assert + // Assert expect(newShopTimeSlots).toStrictEqual({ days: [ { @@ -208,13 +210,13 @@ describe("schedule.vue...", () => { }); test("should call API service in day ranges of 34 or less when getAvailableDatesMethod is called with large date ranges", async () => { - //Arrange + // Arrange const { wrapper } = setupMocks({}); wrapper.vm.selectableDatesData = { days: [], }; - //Act + // Act await wrapper.vm.getAvailableDates.call( wrapper.vm, "2023-01-01", @@ -223,7 +225,7 @@ describe("schedule.vue...", () => { "123" ); - //Assert + // Assert expect(baseMixin.methods.dispatchStoreAction).toHaveBeenCalledTimes(3); expect(baseMixin.methods.dispatchStoreAction).toHaveBeenCalledWith( "getShopTimeSlots", @@ -234,7 +236,7 @@ describe("schedule.vue...", () => { describe("beforeRouteEnter function... ", () => { test("should call next() and call all functions within next", async () => { - //Arrange + // Arrange const { wrapper } = setupMocks({}); wrapper.vm.selectableDatesData = { days: [], @@ -244,7 +246,7 @@ describe("schedule.vue...", () => { c(wrapper.vm); }); - //Act + // Act await schedule.beforeRouteEnter.call( wrapper.vm, { query: { fmgPage: "schedule" } }, @@ -252,7 +254,7 @@ describe("schedule.vue...", () => { nextFunction ); - //Assert + // Assert expect(nextFunction).toHaveBeenCalled(); expect(wrapper.vm.setCmsContent).toHaveBeenCalledWith("content"); expect(wrapper.vm.$refs.datePicker.initializeComponent).toHaveBeenCalledWith( @@ -279,7 +281,7 @@ describe("schedule.vue...", () => { describe("computed properties...", () => { test("timeSlotsForSelectedDate should return timeslots if selected date is available", () => { - //Arrange + // Arrange const { wrapper } = setupMocks({}); wrapper.vm.selectableDatesData = { days: [ @@ -306,10 +308,10 @@ describe("schedule.vue...", () => { selectedDate: "2022-11-11", }); - //Act + // Act const testValue = wrapper.vm.timeSlotsForSelectedDate; - //Assert + // Assert expect(testValue).toStrictEqual( expect.objectContaining({ date: "2022-11-11", @@ -318,7 +320,7 @@ describe("schedule.vue...", () => { }); test("timeSlotsForSelectedDate should be null if no date has been selected", () => { - //Arrange + // Arrange const { wrapper } = setupMocks({}); wrapper.vm.selectableDatesData = { days: [ @@ -345,10 +347,10 @@ describe("schedule.vue...", () => { selectedDate: undefined, }); - //Act + // Act const testValue = wrapper.vm.timeSlotsForSelectedDate; - //Assert + // Assert expect(testValue).toBe(null); }); }); @@ -356,75 +358,67 @@ describe("schedule.vue...", () => { describe("schedule page methods...", () => { test("getServiceZipCtuCodeFromStore should return zipCodeCtu", () => { - //Arrange + // Arrange const { wrapper } = setupMocks({}); wrapper.vm.selectableDatesData = { days: [], }; - //Act + // Act const testValue = wrapper.vm.getServiceZipCtuCodeFromStore(); - //Assert + // Assert expect(testValue).toStrictEqual("01234"); }); test("openInshopTimeSlotsModal should trigger openModal method", () => { - //Arrange + // Arrange const { wrapper } = setupMocks({}); wrapper.vm.selectableDatesData = { days: [], }; - //Act + // Act wrapper.vm.openInshopTimeSlotsModal(); - //Assert + // Assert expect(wrapper.vm.$refs.timeSlotModalQuestion.openModal).toBeCalled(); }); - test("getSelectedRouteCode should return schedule routeCode", () => { - //Arrange - const { wrapper } = setupMocks({}); - - wrapper.vm.selectableDatesData = { - days: [], - }; - - //Act - const testValue = wrapper.vm.getSelectedRouteCode(); - - //Assert - expect(testValue).toStrictEqual("000"); - }); - test("timeSlotModalClosed should null any selected date when there's no route code", () => { - //Arrange + // Arrange const { wrapper } = setupMocks({}); wrapper.vm.selectableDatesData = { days: [], }; + wrapper.setData({ selectedDate: "1980-05-05", }); - wrapper.setData({ - selectedTimeSlot: { + + const selectedTimeSlotInfo = { + timeSlot: { date: "2019-01-01", startTime: "09:00", endTime: "10:00", routeCode: null, }, + isPremiumAppointment: null, + }; + + wrapper.setData({ + selectedTimeSlotInfo: selectedTimeSlotInfo, }); - //Act + // Act wrapper.vm.timeSlotModalClosed(); - //Assert + // Assert expect(wrapper.vm.selectedDate).toBe(null); }); test("getDisplayTextForMilitaryTime should return the correctly formatted string", () => { - //Arrange + // Arrange const { wrapper } = setupMocks({}); wrapper.vm.selectableDatesData = { days: [], @@ -432,13 +426,13 @@ describe("schedule.vue...", () => { const timeInput1 = "15:00"; const timeInput2 = "15:30"; - //Act + // Act const testOutput1 = wrapper.vm.getDisplayTextForMilitaryTime(timeInput1); const testOutput2 = wrapper.vm.getDisplayTextForMilitaryTime(timeInput2); const testOutput3 = wrapper.vm.getDisplayTextForMilitaryTime(timeInput1, true); const testOutput4 = wrapper.vm.getDisplayTextForMilitaryTime(timeInput2, true); - //Assert + // Assert expect(testOutput1).toBe("3:00 PM"); expect(testOutput2).toBe("3:30 PM"); expect(testOutput3).toBe("3 PM"); @@ -446,7 +440,7 @@ describe("schedule.vue...", () => { }); test("getDisplayTextForMilitaryTime should return the correctly formatted string", () => { - //Arrange + // Arrange const { wrapper } = setupMocks({}); wrapper.vm.selectableDatesData = { days: [], @@ -454,10 +448,10 @@ describe("schedule.vue...", () => { wrapper.vm.$router.navigateWithoutSaving = jest.fn(); wrapper.vm.$route = "testRoute"; - //Act + // Act wrapper.vm.backButtonAction(); - //Assert + // Assert expect(wrapper.vm.$router.navigateWithoutSaving).toBeCalledWith( "CLICKED_BACK", "testRoute" @@ -466,7 +460,7 @@ describe("schedule.vue...", () => { }); test("forwardButtonAction should call route method navigateWithoutSaving", async () => { - //Arrange + // Arrange const { wrapper } = setupMocks({}); wrapper.vm.dispatchStoreAction = jest.fn(() => { return { @@ -477,15 +471,15 @@ describe("schedule.vue...", () => { return {}; }); - //Act + // Act await wrapper.vm.forwardButtonAction(); - //Assert + // Assert expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled(); }); test("for mobile appts, updateSupportingItems should call store action to save supporting items", async () => { - //Arrange + // Arrange store.getters.order.serviceLocation.appointmentType = "Mobile"; store.getters.lineItems.supportingItems = [ { @@ -514,10 +508,10 @@ describe("schedule.vue...", () => { }, }); - //Act + // Act await wrapper.vm.updateSupportingItems(); - //Assert + // Assert expect(wrapper.vm.dispatchStoreAction).toBeCalledWith( "saveSupportingItemsSuppressingStateResetting", expect.arrayContaining([ @@ -530,7 +524,7 @@ describe("schedule.vue...", () => { }); test("for Inshop appts, updateSupportingItems should call store action to save supporting items WITHOUT the EARLY BIRD supporting item", async () => { - //Arrange + // Arrange store.getters.order.serviceLocation.appointmentType = "Inshop"; store.getters.lineItems.supportingItems = [ { @@ -559,10 +553,10 @@ describe("schedule.vue...", () => { }, }); - //Act + // Act await wrapper.vm.updateSupportingItems(); - //Assert + // Assert expect(wrapper.vm.dispatchStoreAction).toBeCalledWith( "saveSupportingItemsSuppressingStateResetting", expect.not.arrayContaining([ @@ -575,7 +569,7 @@ describe("schedule.vue...", () => { }); test("if no EARLY BIRD supporting item, then updateSupportingItems should NOT call store action", async () => { - //Arrange + // Arrange store.getters.order.serviceLocation.appointmentType = "Mobile"; store.getters.lineItems.supportingItems = []; const { wrapper } = setupMocks({}); @@ -595,10 +589,10 @@ describe("schedule.vue...", () => { }, }); - //Act + // Act await wrapper.vm.updateSupportingItems(); - //Assert + // Assert expect(wrapper.vm.dispatchStoreAction).not.toBeCalled(); }); }); diff --git a/src/layouts/schedule/schedule.vue b/src/layouts/schedule/schedule.vue index 00b3e796b..156d3696d 100644 --- a/src/layouts/schedule/schedule.vue +++ b/src/layouts/schedule/schedule.vue @@ -24,17 +24,17 @@ { - if ( - value.date == null || - value.startTime == null || - value.routeCode == null || - value.estimatedServiceMinutesMaximum - ) { + if (value?.timeSlot?.routeCode == null) { return errorMessages.DATE_REQUIRED; } return true; @@ -190,7 +185,7 @@ export default { data() { return { selectedDate: this.getSelectedDate(), - selectedTimeSlot: this.getSelectedTimeSlot(), + selectedTimeSlotInfo: this.getSelectedTimeSlotInfo(), selectableDatesData: [], mobilePremiumAppointmentFee: null, }; @@ -265,7 +260,7 @@ export default { vm.mobilePremiumAppointmentFee = resultMap.premiumFeeWithPrice ? resultMap.premiumFeeWithPrice[0] : null; - vm.updateFooterButtonText(vm.selectedTimeSlot); + vm.updateFooterButtonText(vm.selectedTimeSlotInfo); }); }, computed: { @@ -293,18 +288,22 @@ export default { splitCopyOnCMSPlaceHolder, arePagePrerequisitesValid() { const serviceLocation = store.getters.order.serviceLocation; + const serviceLocationPreReqs = serviceLocation.zipCode && serviceLocation.zipCodeCtu && serviceLocation.appointmentType && (serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE || serviceLocation.provider.providerNumber); + const paymentInfo = store.getters.payment.isInsurance !== null; const supportingItems = store.getters.lineItems.supportingItems !== null; + const damageInfo = store.getters.order.damage.isRepair || (store.getters.order.lineItems?.glassParts != null && store.getters.order.lineItems.glassParts.length > 0); + return serviceLocationPreReqs && paymentInfo && supportingItems && damageInfo; }, async getAvailableDatesMethod(startDate, endDate) { @@ -327,45 +326,55 @@ export default { openInshopTimeSlotsModal() { this.$refs["timeSlotModalQuestion"].openModal(); }, - getSelectedTimeSlot() { - return store.getters.order.schedule; - }, getSelectedDate() { return store.getters.order.schedule.date; }, - getSelectedRouteCode() { - return store.getters.order.schedule.routeCode; + getSelectedTimeSlotInfo() { + const supportingItems = this.getSupportingItems(); + const isPremiumAppointment = + !!supportingItems.filter((lineItem) => lineItem.partType === PREMIUM_FEE_PART_TYPE) + .length > 0; + + const selectedTimeSlotInfo = { + timeSlot: store.getters.order.schedule, + isPremiumAppointment: isPremiumAppointment, + }; + + return selectedTimeSlotInfo; }, getSupportingItems() { return store.getters.lineItems.supportingItems; }, timeSlotModalClosed() { // Clear the selectedDate if no timeSlot has been selected - if (this.selectedTimeSlot.routeCode == null) { + if (this.selectedTimeSlotInfo.timeSlot.routeCode == null) { this.selectedDate = null; } }, - updateFooterButtonText(timeSlot) { + updateFooterButtonText(timeSlotInfo) { let funnelFooterButtonText; - if (!timeSlot || !timeSlot.date) { + if (!timeSlotInfo || !timeSlotInfo.timeSlot.date) { funnelFooterButtonText = "Continue"; } else { funnelFooterButtonText = `Select ${this.convertSelectedDateToShortMonthAndDay( - timeSlot.date + timeSlotInfo.timeSlot.date )}`; if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) { funnelFooterButtonText += ` at ${this.getDisplayTextForMilitaryTime( - timeSlot.startTime + timeSlotInfo.timeSlot.startTime )}`; } else if ( this.appointmentType === AppointmentTypeStrings.MOBILE && - !timeSlot.isPremiumAppointment + !timeSlotInfo.isPremiumAppointment ) { funnelFooterButtonText += ` at ${this.getDisplayTextForMilitaryTime( - timeSlot.startTime, + timeSlotInfo.timeSlot.startTime, true - )} - ${this.getDisplayTextForMilitaryTime(timeSlot.endTime, true)}`; + )} - ${this.getDisplayTextForMilitaryTime( + timeSlotInfo.timeSlot.endTime, + true + )}`; } } this.$refs.funnelFooter.updateButtonText(funnelFooterButtonText); @@ -376,14 +385,16 @@ export default { // Ex: April 25 return dateObject.toLocaleDateString("en-us", { month: "short", day: "numeric" }); }, - // Expected input: "HH:MM" getDisplayTextForMilitaryTime(militaryTimeInput, shouldTrimMinutesIfEmpty = false) { + // 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; } + if (shouldTrimMinutesIfEmpty && minutes === "00") { return `${hours} ${meridianNotation}`; } else { @@ -395,7 +406,12 @@ export default { }, forwardButtonAction() { this.updateSupportingItems(); - this.dispatchStoreAction(this.storeActions.SAVE_SCHEDULE, this.selectedTimeSlot, false); + + this.dispatchStoreAction( + this.storeActions.SAVE_SCHEDULE, + this.selectedTimeSlotInfo.timeSlot, + false + ); this.$router.navigateWithSaving(this.navigationScenarios.CLICKED_FORWARD, this.$route); }, @@ -405,7 +421,7 @@ export default { // if we have a premium fee(early bird), then save/update supporting items if ( this.appointmentType === AppointmentTypeStrings.MOBILE && - this.selectedTimeSlot?.isPremiumAppointment + this.selectedTimeSlotInfo?.isPremiumAppointment ) { const earlyBirdIndex = supportingItems.findIndex( (item) => item.partType == PREMIUM_FEE_PART_TYPE @@ -448,17 +464,20 @@ export default { selectedDate(newValue, oldValue) { // Clear time slot selection if date selected changes if (newValue !== oldValue) { - this.selectedTimeSlot = { - date: null, - routeCode: null, - startTime: null, - endTime: null, - jobMaxMinutes: null, - jobMinMinutes: null, + this.selectedTimeSlotInfo = { + timeSlot: { + date: null, + routeCode: null, + startTime: null, + endTime: null, + jobMaxMinutes: null, + jobMinMinutes: null, + }, + isPremiumAppointment: null, }; } }, - selectedTimeSlot(newValue) { + selectedTimeSlotInfo(newValue) { this.updateFooterButtonText(newValue); }, }, diff --git a/src/layouts/schedule/time-slot-modal-question/time-slot-modal-question.spec.js b/src/layouts/schedule/time-slot-modal-question/time-slot-modal-question.spec.js index 93f8cfe01..cbfe355e3 100644 --- a/src/layouts/schedule/time-slot-modal-question/time-slot-modal-question.spec.js +++ b/src/layouts/schedule/time-slot-modal-question/time-slot-modal-question.spec.js @@ -2,30 +2,76 @@ import { shallowMount, flushPromises } from "@vue/test-utils"; import { getMountOptions } from "@/helpers/unit-test-helper.js"; import timeSlotModalQuestion from "@/layouts/schedule/time-slot-modal-question/time-slot-modal-question.vue"; import store from "@/store"; + import { RouteCodeFlags, PREMIUM_TIME_SLOT_ID_FLAG, PREMIUM_FEE_PART_TYPE, } from "@/constants/schedule-constants"; -describe("time-slot-modal-list-button-question.vue", () => { +describe("time-slot-modal-question.vue", () => { + const timeSlotsForSelectedDate = { + date: "2023-09-02", + timeSlots: [ + { + id: "03335-01820-S-B*20334*8 AM", + startTime: "08:00", + endTime: "09:00", + offerPremium: false, + }, + { + id: "03335-01820-S-B*20334*830 AM", + startTime: "08:30", + endTime: "09:30", + offerPremium: false, + }, + { + id: "03335-01820-S-B*20334*9 AM", + startTime: "09:00", + endTime: "10:00", + offerPremium: false, + }, + { + id: "03335-01820-S-B*20334*930 AM", + startTime: "09:30", + endTime: "10:30", + offerPremium: false, + }, + { + id: "03335-01820-S-B*20334*10 AM", + startTime: "10:00", + endTime: "11:00", + offerPremium: false, + }, + { + id: "03335-01820-S-B*20334*1030 AM", + startTime: "10:30", + endTime: "11:30", + offerPremium: false, + }, + ], + }; + test("When selected time slot is changed, a correctly formatted selectedTimeSlot should be emitted", async () => { //Arrange - const date = "2023-07-01"; - const timeSlotId = "testRouteCodeId"; - const startTime = "8:00 AM"; - const endTime = "8:30 AM"; + const date = "2023-09-02"; + const routeCode = "03335-01820-S-B*20334*830 AM"; + const startTime = "08:30"; + const endTime = "09:30"; const jobMaxMinutes = 100; const jobMinMinutes = 50; const expectedEmit = [ [ { - date: date, - routeCode: timeSlotId, - startTime: startTime, - endTime: endTime, - jobMaxMinutes: jobMaxMinutes.toString(), - jobMinMinutes: jobMinMinutes.toString(), + timeSlot: { + date: date, + routeCode: routeCode, + startTime: startTime, + endTime: endTime, + jobMaxMinutes: jobMaxMinutes.toString(), + jobMinMinutes: jobMinMinutes.toString(), + }, + isPremiumAppointment: false, }, ], ]; @@ -35,22 +81,16 @@ describe("time-slot-modal-list-button-question.vue", () => { propsData: { estimatedServiceMinutesMaximum: jobMaxMinutes, estimatedServiceMinutesMinimum: jobMinMinutes, - dateAndTimeSlotData: { - date: date, - timeSlots: [ - { - id: timeSlotId, - startTime: startTime, - endTime: endTime, - }, - ], - }, + timeSlotsForSelectedDate: timeSlotsForSelectedDate, }, }, }); wrapper.vm.$refs.timeSlots.closeModal = jest.fn(); - wrapper.vm.selectedTimeSlotId = timeSlotId; + + await wrapper.setData({ + selectedRouteCode: routeCode, + }); //Act wrapper.vm.setSelectedTimeSlot(); @@ -59,7 +99,8 @@ describe("time-slot-modal-list-button-question.vue", () => { expect(wrapper.emitted()["update:modelValue"]).toEqual(expectedEmit); }); }); -describe("time-slot-modal-list-button-question.vue Supplemental information", () => { + +describe("time-slot-modal-question.vue Supplemental information", () => { test("The correct supplemental information should be obtained from CMS - Mobile - Not Premium ", async () => { //Arrange const mobileCmsWidgetName = "MobileNotPremium"; @@ -72,11 +113,13 @@ describe("time-slot-modal-list-button-question.vue Supplemental information", () }, }, }); + //Assert expect(wrapper.vm.supplementalInformationBlock).toEqual( mockCmsContent[mobileCmsWidgetName]["BodyText"] ); }); + test("The correct supplemental information should be obtained from CMS - Mobile - Premium", async () => { //Arrange const mobileCmsWidgetName = "MobilePremium"; @@ -89,11 +132,13 @@ describe("time-slot-modal-list-button-question.vue Supplemental information", () }, }, }); + //Assert expect(wrapper.vm.supplementalInformationBlock).toEqual( mockCmsContent[mobileCmsWidgetName]["BodyText"] ); }); + test("No supplemental information should be obtained from CMS - Inshop", async () => { //Arrange const inshopWidgetName = "Inshop"; @@ -109,6 +154,7 @@ describe("time-slot-modal-list-button-question.vue Supplemental information", () //Assert expect(wrapper.vm.supplementalInformationBlock).toBeNull(); }); + test("No supplemental information should be obtained from CMS - Drop off, nothing selected", async () => { //Arrange const dropOffWidgetName = "Dropoff"; @@ -124,40 +170,69 @@ describe("time-slot-modal-list-button-question.vue Supplemental information", () //Assert expect(wrapper.vm.supplementalInformationBlock).toBeNull(); }); + test("Correct supplemental information should be retrieved, Dropoff regular", async () => { //Arrange const dropOffWidgetName = "Dropoff"; + const modelValue = { + timeSlot: { + routeCode: `test${RouteCodeFlags.ALL_DAY_DROP_OFF}`, + date: null, + startTime: null, + endTime: null, + jobMaxMinutes: null, + jobMinMinutes: null, + }, + isPremiumAppointment: null, + }; + const { wrapper } = setupMocks({ customMountOptions: { propsData: { - modelValue: { routeCode: "Test" + RouteCodeFlags.ALL_DAY_DROP_OFF }, + modelValue: modelValue, appointmentType: "Dropoff", dropoffCmsWidgetName: dropOffWidgetName, }, }, }); + //Assert expect(wrapper.vm.supplementalInformationBlock).toEqual( mockCmsContent[dropOffWidgetName]["BodyText"] ); }); + test("Correct supplemental information should be retrieved, Dropoff Overnight", async () => { //Arrange const overnightDropOffCmsWidgetName = "Overnight"; + const modelValue = { + timeSlot: { + routeCode: `test${RouteCodeFlags.OVERNIGHT_DROP_OFF}`, + date: null, + startTime: null, + endTime: null, + jobMaxMinutes: null, + jobMinMinutes: null, + }, + isPremiumAppointment: null, + }; + const { wrapper } = setupMocks({ customMountOptions: { propsData: { - modelValue: { routeCode: "Test" + RouteCodeFlags.OVERNIGHT_DROP_OFF }, + modelValue: modelValue, appointmentType: "Dropoff", overnightDropOffCmsWidgetName: overnightDropOffCmsWidgetName, }, }, }); + //Assert expect(wrapper.vm.supplementalInformationBlock).toEqual( mockCmsContent[overnightDropOffCmsWidgetName]["BodyText"] ); }); + test("Correct supplemental information should be retrieved, Dropoff Sameday", async () => { //Arrange const sameDayDropOffCmsWidgetName = "Sameday"; @@ -167,13 +242,24 @@ describe("time-slot-modal-list-button-question.vue Supplemental information", () const endTime = "8:30 AM"; const jobMaxMinutes = 100; const jobMinMinutes = 50; + const modelValue = { + timeSlot: { + routeCode: `test${RouteCodeFlags.ALL_DAY_DROP_OFF}`, + date: null, + startTime: null, + endTime: null, + jobMaxMinutes: null, + jobMinMinutes: null, + }, + isPremiumAppointment: null, + }; const { wrapper } = setupMocks({ customMountOptions: { propsData: { estimatedServiceMinutesMaximum: jobMaxMinutes, estimatedServiceMinutesMinimum: jobMinMinutes, - dateAndTimeSlotData: { + timeSlotsForSelectedDate: { date: date, timeSlots: [ { @@ -183,7 +269,7 @@ describe("time-slot-modal-list-button-question.vue Supplemental information", () }, ], }, - modelValue: { routeCode: "Test" + RouteCodeFlags.ALL_DAY_DROP_OFF }, + modelValue: modelValue, appointmentType: "Dropoff", sameDayDropOffCmsWidgetName: sameDayDropOffCmsWidgetName, }, @@ -208,12 +294,24 @@ describe("time-slot-modal-list-button-question.vue Disclaimer", () => { const jobMaxMinutes = 100; const jobMinMinutes = 50; + const modelValue = { + timeSlot: { + routeCode: `Test${RouteCodeFlags.ALL_DAY_DROP_OFF}`, + date: null, + startTime: null, + endTime: null, + jobMaxMinutes: null, + jobMinMinutes: null, + }, + isPremiumAppointment: null, + }; + const { wrapper } = setupMocks({ customMountOptions: { propsData: { estimatedServiceMinutesMaximum: jobMaxMinutes, estimatedServiceMinutesMinimum: jobMinMinutes, - dateAndTimeSlotData: { + timeSlotsForSelectedDate: { date: date, timeSlots: [ { @@ -223,7 +321,7 @@ describe("time-slot-modal-list-button-question.vue Disclaimer", () => { }, ], }, - modelValue: { routeCode: "Test" + RouteCodeFlags.ALL_DAY_DROP_OFF }, + modelValue: modelValue, appointmentType: "Dropoff", sameDayDropOffCmsWidgetName: sameDayDropOffCmsWidgetName, }, @@ -235,47 +333,88 @@ describe("time-slot-modal-list-button-question.vue Disclaimer", () => { mockCmsContent[sameDayDropOffCmsWidgetName]["FooterText"] ); }); + test("Correct disclaimer information should be retrieved, Dropoff regular", async () => { //Arrange const dropOffWidgetName = "Dropoff"; + const modelValue = { + timeSlot: { + routeCode: `Test${RouteCodeFlags.ALL_DAY_DROP_OFF}`, + date: null, + startTime: null, + endTime: null, + jobMaxMinutes: null, + jobMinMinutes: null, + }, + isPremiumAppointment: null, + }; + const { wrapper } = setupMocks({ customMountOptions: { propsData: { - modelValue: { routeCode: "Test" + RouteCodeFlags.ALL_DAY_DROP_OFF }, + modelValue: modelValue, appointmentType: "Dropoff", dropoffCmsWidgetName: dropOffWidgetName, }, }, }); + //Assert expect(wrapper.vm.disclaimerTextBlockCopy).toEqual( mockCmsContent[dropOffWidgetName]["FooterText"] ); }); + test("Correct disclaimer information should be retrieved, Dropoff overnight", async () => { //Arrange const overnightDropOffCmsWidgetName = "Overnight"; + const modelValue = { + timeSlot: { + routeCode: `Test${RouteCodeFlags.OVERNIGHT_DROP_OFF}`, + date: null, + startTime: null, + endTime: null, + jobMaxMinutes: null, + jobMinMinutes: null, + }, + isPremiumAppointment: null, + }; + const { wrapper } = setupMocks({ customMountOptions: { propsData: { - modelValue: { routeCode: "Test" + RouteCodeFlags.OVERNIGHT_DROP_OFF }, + modelValue: modelValue, appointmentType: "Dropoff", overnightDropOffCmsWidgetName: overnightDropOffCmsWidgetName, }, }, }); + //Assert expect(wrapper.vm.disclaimerTextBlockCopy).toEqual( mockCmsContent[overnightDropOffCmsWidgetName]["FooterText"] ); }); + test("No disclaimer information should be retrieved, non-dropoff", async () => { //Arrange const inshopWidgetName = "Inshop"; + const modelValue = { + timeSlot: { + routeCode: `test`, + date: null, + startTime: null, + endTime: null, + jobMaxMinutes: null, + jobMinMinutes: null, + }, + isPremiumAppointment: null, + }; + const { wrapper } = setupMocks({ customMountOptions: { propsData: { - modelValue: { routeCode: "test" }, + modelValue: modelValue, appointmentType: "Inshop", cmsWidgetName: inshopWidgetName, }, @@ -286,7 +425,7 @@ describe("time-slot-modal-list-button-question.vue Disclaimer", () => { }); }); -describe("time-slot-modal-list-button-question.vue Duration", () => { +describe("time-slot-modal-question.vue Duration", () => { test("The correct duration information should be displayed - Dropoff - Sameday ", async () => { //Arrange const sameDayDropOffCmsWidgetName = "Sameday"; @@ -297,12 +436,24 @@ describe("time-slot-modal-list-button-question.vue Duration", () => { const jobMaxMinutes = 100; const jobMinMinutes = 50; + const modelValue = { + timeSlot: { + routeCode: `test` + RouteCodeFlags.ALL_DAY_DROP_OFF, + date: null, + startTime: null, + endTime: null, + jobMaxMinutes: null, + jobMinMinutes: null, + }, + isPremiumAppointment: null, + }; + const { wrapper } = setupMocks({ customMountOptions: { propsData: { estimatedServiceMinutesMaximum: jobMaxMinutes, estimatedServiceMinutesMinimum: jobMinMinutes, - dateAndTimeSlotData: { + timeSlotsForSelectedDate: { date: date, timeSlots: [ { @@ -312,7 +463,7 @@ describe("time-slot-modal-list-button-question.vue Duration", () => { }, ], }, - modelValue: { routeCode: "Test" + RouteCodeFlags.ALL_DAY_DROP_OFF }, + modelValue: modelValue, appointmentType: "Dropoff", sameDayDropOffCmsWidgetName: sameDayDropOffCmsWidgetName, }, @@ -324,30 +475,59 @@ describe("time-slot-modal-list-button-question.vue Duration", () => { mockCmsContent[sameDayDropOffCmsWidgetName]["SubheaderText"] ); }); + test("Correct duration information should be retrieved, Dropoff overnight", async () => { //Arrange const overnightDropOffCmsWidgetName = "Overnight"; + + const modelValue = { + timeSlot: { + routeCode: `test` + RouteCodeFlags.OVERNIGHT_DROP_OFF, + date: null, + startTime: null, + endTime: null, + jobMaxMinutes: null, + jobMinMinutes: null, + }, + isPremiumAppointment: null, + }; + const { wrapper } = setupMocks({ customMountOptions: { propsData: { - modelValue: { routeCode: "Test" + RouteCodeFlags.OVERNIGHT_DROP_OFF }, + modelValue: modelValue, appointmentType: "Dropoff", overnightDropOffCmsWidgetName: overnightDropOffCmsWidgetName, }, }, }); + //Assert expect(wrapper.vm.durationTextBlockCopy).toEqual( mockCmsContent[overnightDropOffCmsWidgetName]["SubheaderText"] ); }); + test("Correct duration information should be retrieved, Dropoff", async () => { //Arrange const dropoffCmsWidgetName = "Dropoff"; + + const modelValue = { + timeSlot: { + routeCode: `test` + RouteCodeFlags.ALL_DAY_DROP_OFF, + date: null, + startTime: null, + endTime: null, + jobMaxMinutes: null, + jobMinMinutes: null, + }, + isPremiumAppointment: null, + }; + const { wrapper } = setupMocks({ customMountOptions: { propsData: { - modelValue: { routeCode: "Test" + RouteCodeFlags.ALL_DAY_DROP_OFF }, + modelValue: modelValue, appointmentType: "Dropoff", dropoffCmsWidgetName: dropoffCmsWidgetName, }, @@ -358,25 +538,39 @@ describe("time-slot-modal-list-button-question.vue Duration", () => { mockCmsContent[dropoffCmsWidgetName]["SubheaderText"] ); }); + test("Correct duration information should be retrieved, Inshop - convert to hours", async () => { //Arrange const cmsWidgetName = "Inshop"; + + const modelValue = { + timeSlot: { + routeCode: `test`, + date: null, + startTime: null, + endTime: null, + }, + isPremiumAppointment: null, + }; + const { wrapper } = setupMocks({ customMountOptions: { propsData: { estimatedServiceMinutesMaximum: 180, estimatedServiceMinutesMinimum: 120, - modelValue: { routeCode: "Test" }, + modelValue: modelValue, appointmentType: "Inshop", cmsWidgetName: cmsWidgetName, }, }, }); + //Assert expect(wrapper.vm.durationTextBlockCopy).toEqual( mockCmsContent[cmsWidgetName]["SubheaderText"] + " 2 - 3 hours" ); }); + test("Correct duration information should be retrieved, Inshop - convert to minutes", async () => { //Arrange const cmsWidgetName = "Inshop"; @@ -396,6 +590,7 @@ describe("time-slot-modal-list-button-question.vue Duration", () => { mockCmsContent[cmsWidgetName]["SubheaderText"] + " 60 - 90 minutes" ); }); + test("Correct duration information should be retrieved, Inshop - min = maximum time", async () => { //Arrange const cmsWidgetName = "Inshop"; @@ -415,6 +610,7 @@ describe("time-slot-modal-list-button-question.vue Duration", () => { mockCmsContent[cmsWidgetName]["SubheaderText"] + " 4 hours" ); }); + test("No duration information should be displayed, Mobile", async () => { //Arrange const mobileCmsWidgetName = "Mobile"; @@ -450,7 +646,7 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => { propsData: { estimatedServiceMinutesMaximum: jobMaxMinutes, estimatedServiceMinutesMinimum: jobMinMinutes, - dateAndTimeSlotData: { + timeSlotsForSelectedDate: { date: date, timeSlots: [ { @@ -473,6 +669,7 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => { }; expect(wrapper.vm.availableTimeSlots[0]).toEqual(expectedTimeSlotObject); }); + test("Available Timeslots should be formatted correctly - Dropoff - Overnight ", async () => { //Arrange const overnightDropOffCmsWidgetName = "Overnight"; @@ -488,7 +685,7 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => { propsData: { estimatedServiceMinutesMaximum: jobMaxMinutes, estimatedServiceMinutesMinimum: jobMinMinutes, - dateAndTimeSlotData: { + timeSlotsForSelectedDate: { date: date, timeSlots: [ { @@ -511,6 +708,7 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => { }; expect(wrapper.vm.availableTimeSlots[0]).toEqual(expectedTimeSlotObject); }); + test("Available Timeslots should be formatted correctly - Dropoff", async () => { //Arrange const dropoffCmsWidgetName = "Dropoff"; @@ -526,7 +724,7 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => { propsData: { estimatedServiceMinutesMaximum: jobMaxMinutes, estimatedServiceMinutesMinimum: jobMinMinutes, - dateAndTimeSlotData: { + timeSlotsForSelectedDate: { date: date, timeSlots: [ { @@ -549,6 +747,7 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => { }; expect(wrapper.vm.availableTimeSlots[0]).toEqual(expectedTimeSlotObject); }); + test("Available Timeslots should be formatted correctly - Mobile not premium", async () => { //Arrange const mobileCmsWidgetName = "MobileNotPremium"; @@ -564,7 +763,7 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => { propsData: { estimatedServiceMinutesMaximum: jobMaxMinutes, estimatedServiceMinutesMinimum: jobMinMinutes, - dateAndTimeSlotData: { + timeSlotsForSelectedDate: { date: date, timeSlots: [ { @@ -587,6 +786,7 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => { }; expect(wrapper.vm.availableTimeSlots[0]).toEqual(expectedTimeSlotObject); }); + test("Available Timeslots should be formatted correctly - Mobile with premium", async () => { //Arrange const mobilePremiumCmsWidgetName = "MobilePremium"; @@ -605,7 +805,7 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => { premiumAppointmentFee: { partType: PREMIUM_FEE_PART_TYPE, }, - dateAndTimeSlotData: { + timeSlotsForSelectedDate: { date: date, timeSlots: [ { @@ -631,8 +831,10 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => { buttonLabelSubCopy: "+$15.99", value: timeSlotId + PREMIUM_TIME_SLOT_ID_FLAG, }; + expect(wrapper.vm.availableTimeSlots[0]).toEqual(expectedTimeSlotObject); }); + test("Available Timeslots should be formatted correctly - Inshop", async () => { //Arrange const cmsWidgetName = "Inshop"; @@ -648,7 +850,7 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => { propsData: { estimatedServiceMinutesMaximum: jobMaxMinutes, estimatedServiceMinutesMinimum: jobMinMinutes, - dateAndTimeSlotData: { + timeSlotsForSelectedDate: { date: date, timeSlots: [ { @@ -678,7 +880,7 @@ const mockCmsContent = { HeaderText: "Mobile, not premium HeaderText", }, MobilePremium: { - "BodyText:": "Mobile, with premium BodyText", + BodyText: "Mobile, with premium BodyText", FooterText: "Mobile, with premium FooterText", SubheaderText: "Mobile, with premium SubheaderText", HeaderText: "Mobile, with premium HeaderText", 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 cce2c0503..b6cc95365 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 @@ -22,7 +22,7 @@ :answers="availableTimeSlots" groupName="chooseTimeSlot" textPosition="text-center" - v-model="selectedTimeSlotId" + v-model="selectedRouteCode" isRequired validationRules="time-slot-required" />
({ - routeCode: null, - date: null, - startTime: null, - endTime: null, - jobMaxMinutes: null, - jobMinMinutes: null, + timeSlot: { + routeCode: null, + date: null, + startTime: null, + endTime: null, + jobMaxMinutes: null, + jobMinMinutes: null, + }, + isPremiumAppointment: null, }), }, cmsWidgetName: String, @@ -102,7 +105,7 @@ export default { sameDayDropOffCmsWidgetName: String, overnightDropOffCmsWidgetName: String, appointmentType: String, - dateAndTimeSlotData: Object, + timeSlotsForSelectedDate: Object, premiumAppointmentFee: Object, estimatedServiceMinutesMinimum: Number, estimatedServiceMinutesMaximum: Number, @@ -113,7 +116,7 @@ export default { data() { return { isModalOpened: false, - selectedValue: null, + selectedRouteCode: this.getSelectedRouteCode(), timeSlotModalListButton: timeSlotModalListButton, }; }, @@ -150,36 +153,31 @@ export default { modalName() { return "timeSlots"; }, - selectedTimeSlotId: { - get: function () { - return this.modelValue?.routeCode; - }, - set: function (newValue) { - // Button Question only supports Number, or String data types so we must convert to the full object before emitting - this.selectedValue = this.getSelectedTimeSlotObject(newValue); - }, + modal() { + return this.$refs[this.modalName]; }, supplementalInformationBlock() { let appointmentTypeCmsWidgetName; if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) { return null; } else if (this.appointmentType === AppointmentTypeStrings.MOBILE) { - appointmentTypeCmsWidgetName = this.selectedTimeSlotId?.includes( + appointmentTypeCmsWidgetName = this.selectedRouteCode?.includes( PREMIUM_TIME_SLOT_ID_FLAG ) ? this.mobilePremiumCmsWidgetName : this.mobileCmsWidgetName; } else { - if (!this.selectedTimeSlotId) { + if (!this.selectedRouteCode) { return null; } else { appointmentTypeCmsWidgetName = this.getRelevantDropOffCmsWidgetNameForSelectedTimeSlot( - this.selectedTimeSlotId, + this.selectedRouteCode, true ); } } + return this.getCmsContent( appointmentTypeCmsWidgetName, cmsWidgetFieldMappings.SUPPLEMENTAL_INFORMATION @@ -232,13 +230,13 @@ export default { }, disclaimerTextBlockCopy() { if (this.appointmentType === AppointmentTypeStrings.DROP_OFF) { - if (this.selectedTimeSlotId?.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) { + if (this.selectedRouteCode?.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) { if (this.isSameDay) { return this.sameDayDropOffDisclaimerText; } else { return this.dropoffDisclaimerText; } - } else if (this.selectedTimeSlotId?.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) { + } else if (this.selectedRouteCode?.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) { return this.overnightDropOffDisclaimerText; } else { return null; @@ -281,9 +279,9 @@ export default { } else if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) { return this.inshopDurationText; } else { - if (this.selectedTimeSlotId?.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) { + if (this.selectedRouteCode?.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) { return this.overnightDropoffDurationText; - } else if (this.selectedTimeSlotId?.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) { + } else if (this.selectedRouteCode?.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) { if (this.isSameDay) { return this.sameDayDropoffDurationText; } else { @@ -295,21 +293,21 @@ export default { } }, isSameDay() { - if (!this.dateAndTimeSlotData) { + if (!this.timeSlotsForSelectedDate) { return false; } - const selectedDate = this.dateAndTimeSlotData.date; + const selectedDate = this.timeSlotsForSelectedDate.date; const todaysDate = new Date().toISOString().split("T")[0]; return selectedDate === todaysDate; }, dateSelectedReadableDate() { - if (!this.dateAndTimeSlotData) { + if (!this.timeSlotsForSelectedDate) { return null; } // This conversion ensures we don't get get GMT induced date changes - const dateObject = convertDateStringToDate(this.dateAndTimeSlotData.date); + const dateObject = convertDateStringToDate(this.timeSlotsForSelectedDate.date); // Ex: Tuesday, April 22 return dateObject.toLocaleDateString("en-us", { weekday: "long", @@ -318,21 +316,20 @@ export default { }); }, availableTimeSlots() { - if (!this.dateAndTimeSlotData) { + if (!this.timeSlotsForSelectedDate) { return null; } if (this.appointmentType === AppointmentTypeStrings.DROP_OFF) { - return this.getAvailableTimeSlotsForDropOff(this.dateAndTimeSlotData.timeSlots); + return this.getAvailableTimeSlotsForDropOff( + this.timeSlotsForSelectedDate.timeSlots + ); } else if (this.appointmentType === AppointmentTypeStrings.MOBILE) { - return this.getAvailableTimeSlotsForMobile(this.dateAndTimeSlotData.timeSlots); + return this.getAvailableTimeSlotsForMobile(this.timeSlotsForSelectedDate.timeSlots); } else { - return this.getAvailableTimeSlotsForInshop(this.dateAndTimeSlotData.timeSlots); + return this.getAvailableTimeSlotsForInshop(this.timeSlotsForSelectedDate.timeSlots); } }, - modal() { - return this.$refs[this.modalName]; - }, }, methods: { openModal() { @@ -348,16 +345,19 @@ export default { this.$emit("time-slot-modal-closed"); }, async setSelectedTimeSlot() { - this.$emit("update:modelValue", this.selectedValue); + this.$emit( + "update:modelValue", + this.getSelectedTimeSlotInfoObject(this.selectedRouteCode) + ); this.closeModal(); }, getRelevantDropOffCmsWidgetNameForSelectedTimeSlot( - selectedTimeSlotId, + selectedRouteCode, isSameDayRelevant = false ) { - if (selectedTimeSlotId.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) { + if (selectedRouteCode.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) { return this.overnightDropOffCmsWidgetName; - } else if (selectedTimeSlotId.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) { + } else if (selectedRouteCode.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) { return this.isSameDay && isSameDayRelevant ? this.sameDayDropOffCmsWidgetName : this.dropoffCmsWidgetName; @@ -416,8 +416,9 @@ export default { getPremiumAppointmentTimeSlot(timeSlotData) { const formattedPrice = "+$" + this.getTotalLineItemPrice(this.premiumAppointmentFee).toFixed(2); + return { - // Unique value is required for each and the premium appoinment shares a timeSlot ID + // Unique value is required for each and the premium appoinment shares an id value: this.addPremiumFlagToInput(timeSlotData.id), buttonLabel: this.premiumAppointmentButtonText, buttonLabelSubCopy: formattedPrice, @@ -426,41 +427,68 @@ export default { }, }; }, - autoSelectTimeSlotIfOnlyOneIsAvailable(newAvailableTimeSlotsValue) { - const numberOfOptions = newAvailableTimeSlotsValue?.length; + getSelectedRouteCode() { + let selectedRouteCode; + if (!this.modelValue?.timeSlot) { + selectedRouteCode = null; + } + + if (this.modelValue?.isPremiumAppointment) { + selectedRouteCode = this.addPremiumFlagToInput( + this.modelValue?.timeSlot?.routeCode + ); + } else { + selectedRouteCode = this.modelValue?.timeSlot?.routeCode; + } + + return selectedRouteCode; + }, + autoSelectTimeSlotIfOnlyOneIsAvailable() { + const numberOfOptions = this.availableTimeSlots?.length; if (numberOfOptions === 1) { - this.selectedTimeSlotId = newAvailableTimeSlotsValue[0].value; + this.selectedRouteCode = this.availableTimeSlots[0].value; } }, - addPremiumFlagToInput(timeSlotId) { - return (timeSlotId += PREMIUM_TIME_SLOT_ID_FLAG); + addPremiumFlagToInput(routeCode) { + return (routeCode += PREMIUM_TIME_SLOT_ID_FLAG); }, - removePremiumFlagFromInput(timeSlotId) { - return timeSlotId.substring(0, timeSlotId.length - PREMIUM_TIME_SLOT_ID_FLAG.length); + removePremiumFlagFromInput(routeCode) { + return routeCode.replace(PREMIUM_TIME_SLOT_ID_FLAG, ""); }, - getSelectedTimeSlotObject(timeSlotId) { - const timeSlot = this.dateAndTimeSlotData?.timeSlots?.find( - (timeSlot) => timeSlot.id == timeSlotId + getSelectedTimeSlotInfoObject(routeCode) { + const routeCodeIncludesPremium = routeCode?.includes(PREMIUM_TIME_SLOT_ID_FLAG); + if (routeCodeIncludesPremium) { + routeCode = this.removePremiumFlagFromInput(routeCode); + } + + const timeSlot = this.timeSlotsForSelectedDate?.timeSlots?.find( + (timeSlot) => timeSlot.id == routeCode ); if (timeSlot) { return { - date: this.dateAndTimeSlotData.date, - routeCode: timeSlot.id, - startTime: timeSlot.startTime, - endTime: timeSlot.endTime, - jobMaxMinutes: this.estimatedServiceMinutesMaximum.toString(), - jobMinMinutes: this.estimatedServiceMinutesMinimum.toString(), + timeSlot: { + date: this.timeSlotsForSelectedDate.date, + routeCode: timeSlot.id, + startTime: timeSlot.startTime, + endTime: timeSlot.endTime, + jobMaxMinutes: this.estimatedServiceMinutesMaximum.toString(), + jobMinMinutes: this.estimatedServiceMinutesMinimum.toString(), + }, + isPremiumAppointment: routeCodeIncludesPremium ? true : false, }; } return { - date: null, - startTime: null, - endTime: null, - routeCode: null, - jobMaxMinutes: null, - jobMinMinutes: null, + timeSlot: { + date: null, + startTime: null, + endTime: null, + routeCode: null, + jobMaxMinutes: null, + jobMinMinutes: null, + }, + isPremiumAppointment: null, }; }, }, @@ -473,12 +501,10 @@ export default { }, selectedDate: { handler() { - this.selectedTimeSlotId = null; + this.selectedRouteCode = null; + this.autoSelectTimeSlotIfOnlyOneIsAvailable(); }, }, - availableTimeSlots(newValue) { - this.autoSelectTimeSlotIfOnlyOneIsAvailable(newValue); - }, }, components: { modal, diff --git a/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue b/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue index 6873ce294..0fef11cb1 100644 --- a/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue +++ b/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue @@ -5,7 +5,7 @@