Merge pull request #1331 from Safelite/feature/CSR-1621

Feature/csr 1621
This commit is contained in:
Leah Schumann 2023-09-05 07:26:57 -04:00 committed by GitHub
commit bbb1bce769
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 460 additions and 219 deletions

View file

@ -4,7 +4,7 @@ const AppointmentTypeStrings = {
DROP_OFF: "Dropoff", DROP_OFF: "Dropoff",
}; };
const PREMIUM_TIME_SLOT_ID_FLAG = "-premium"; const PREMIUM_TIME_SLOT_ID_FLAG = "-PREMIUM";
const PREMIUM_FEE_PART_TYPE = "EARLY BIRD"; const PREMIUM_FEE_PART_TYPE = "EARLY BIRD";

View file

@ -6,7 +6,6 @@ import { shallowMount } from "@vue/test-utils";
import { getMountOptions } from "@/helpers/unit-test-helper.js"; import { getMountOptions } from "@/helpers/unit-test-helper.js";
import store from "@/store"; import store from "@/store";
import router from "@/router"; import router from "@/router";
import { nextTick } from "vue";
import baseMixin from "../../mixins/base-mixin"; import baseMixin from "../../mixins/base-mixin";
// Mock basemixin // Mock basemixin
@ -130,64 +129,67 @@ afterEach(() => {
describe("schedule.vue...", () => { describe("schedule.vue...", () => {
describe("initial load", () => { describe("initial load", () => {
test("should pass arePagePrerequisitesValid with a mobile order and no providerNumber", () => { test("should pass arePagePrerequisitesValid with a mobile order and no providerNumber", () => {
//Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
store.getters.order.serviceLocation.appointmentType = "Mobile"; store.getters.order.serviceLocation.appointmentType = "Mobile";
store.getters.order.serviceLocation.provider = null; store.getters.order.serviceLocation.provider = null;
//Act // Act
const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
//Assert // Assert
expect(arePagePrerequisitesValid).toBe(true); expect(arePagePrerequisitesValid).toBe(true);
}); });
test("should pass arePagePrerequisitesValid with a inshop order and providerNumber", () => { test("should pass arePagePrerequisitesValid with a inshop order and providerNumber", () => {
//Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
//Act // Act
const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
//Assert // Assert
expect(arePagePrerequisitesValid).toBe(true); expect(arePagePrerequisitesValid).toBe(true);
}); });
test("should fail arePagePrerequisitesValid with a replace with no glass parts", async () => { test("should fail arePagePrerequisitesValid with a replace with no glass parts", async () => {
//Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
store.getters.order.lineItems.glassParts = []; store.getters.order.lineItems.glassParts = [];
//Act // Act
const arePagePrerequisitesValid2 = await wrapper.vm.arePagePrerequisitesValid(); const arePagePrerequisitesValid2 = await wrapper.vm.arePagePrerequisitesValid();
//Assert // Assert
expect(arePagePrerequisitesValid2).toBe(false); expect(arePagePrerequisitesValid2).toBe(false);
}); });
test("should fail arePagePrerequisitesValid without isInsurance", () => { test("should fail arePagePrerequisitesValid without isInsurance", () => {
//Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
store.getters.payment.isInsurance = null; store.getters.payment.isInsurance = null;
//Act // Act
const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
//Assert // Assert
expect(arePagePrerequisitesValid).toBe(false); expect(arePagePrerequisitesValid).toBe(false);
}); });
test("should return newShopTimeSlots when getAvailableDatesMethod is called", async () => { test("should return newShopTimeSlots when getAvailableDatesMethod is called", async () => {
//Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
wrapper.vm.selectableDatesData = { wrapper.vm.selectableDatesData = {
days: [], days: [],
}; };
//Act // Act
const newShopTimeSlots = await wrapper.vm.getAvailableDatesMethod( const newShopTimeSlots = await wrapper.vm.getAvailableDatesMethod(
"2023-01-01", "2023-01-01",
"2023-01-31" "2023-01-31"
); );
//Assert // Assert
expect(newShopTimeSlots).toStrictEqual({ expect(newShopTimeSlots).toStrictEqual({
days: [ 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 () => { 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({}); const { wrapper } = setupMocks({});
wrapper.vm.selectableDatesData = { wrapper.vm.selectableDatesData = {
days: [], days: [],
}; };
//Act // Act
await wrapper.vm.getAvailableDates.call( await wrapper.vm.getAvailableDates.call(
wrapper.vm, wrapper.vm,
"2023-01-01", "2023-01-01",
@ -223,7 +225,7 @@ describe("schedule.vue...", () => {
"123" "123"
); );
//Assert // Assert
expect(baseMixin.methods.dispatchStoreAction).toHaveBeenCalledTimes(3); expect(baseMixin.methods.dispatchStoreAction).toHaveBeenCalledTimes(3);
expect(baseMixin.methods.dispatchStoreAction).toHaveBeenCalledWith( expect(baseMixin.methods.dispatchStoreAction).toHaveBeenCalledWith(
"getShopTimeSlots", "getShopTimeSlots",
@ -234,7 +236,7 @@ describe("schedule.vue...", () => {
describe("beforeRouteEnter function... ", () => { describe("beforeRouteEnter function... ", () => {
test("should call next() and call all functions within next", async () => { test("should call next() and call all functions within next", async () => {
//Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
wrapper.vm.selectableDatesData = { wrapper.vm.selectableDatesData = {
days: [], days: [],
@ -244,7 +246,7 @@ describe("schedule.vue...", () => {
c(wrapper.vm); c(wrapper.vm);
}); });
//Act // Act
await schedule.beforeRouteEnter.call( await schedule.beforeRouteEnter.call(
wrapper.vm, wrapper.vm,
{ query: { fmgPage: "schedule" } }, { query: { fmgPage: "schedule" } },
@ -252,7 +254,7 @@ describe("schedule.vue...", () => {
nextFunction nextFunction
); );
//Assert // Assert
expect(nextFunction).toHaveBeenCalled(); expect(nextFunction).toHaveBeenCalled();
expect(wrapper.vm.setCmsContent).toHaveBeenCalledWith("content"); expect(wrapper.vm.setCmsContent).toHaveBeenCalledWith("content");
expect(wrapper.vm.$refs.datePicker.initializeComponent).toHaveBeenCalledWith( expect(wrapper.vm.$refs.datePicker.initializeComponent).toHaveBeenCalledWith(
@ -279,7 +281,7 @@ describe("schedule.vue...", () => {
describe("computed properties...", () => { describe("computed properties...", () => {
test("timeSlotsForSelectedDate should return timeslots if selected date is available", () => { test("timeSlotsForSelectedDate should return timeslots if selected date is available", () => {
//Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
wrapper.vm.selectableDatesData = { wrapper.vm.selectableDatesData = {
days: [ days: [
@ -306,10 +308,10 @@ describe("schedule.vue...", () => {
selectedDate: "2022-11-11", selectedDate: "2022-11-11",
}); });
//Act // Act
const testValue = wrapper.vm.timeSlotsForSelectedDate; const testValue = wrapper.vm.timeSlotsForSelectedDate;
//Assert // Assert
expect(testValue).toStrictEqual( expect(testValue).toStrictEqual(
expect.objectContaining({ expect.objectContaining({
date: "2022-11-11", date: "2022-11-11",
@ -318,7 +320,7 @@ describe("schedule.vue...", () => {
}); });
test("timeSlotsForSelectedDate should be null if no date has been selected", () => { test("timeSlotsForSelectedDate should be null if no date has been selected", () => {
//Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
wrapper.vm.selectableDatesData = { wrapper.vm.selectableDatesData = {
days: [ days: [
@ -345,10 +347,10 @@ describe("schedule.vue...", () => {
selectedDate: undefined, selectedDate: undefined,
}); });
//Act // Act
const testValue = wrapper.vm.timeSlotsForSelectedDate; const testValue = wrapper.vm.timeSlotsForSelectedDate;
//Assert // Assert
expect(testValue).toBe(null); expect(testValue).toBe(null);
}); });
}); });
@ -356,75 +358,67 @@ describe("schedule.vue...", () => {
describe("schedule page methods...", () => { describe("schedule page methods...", () => {
test("getServiceZipCtuCodeFromStore should return zipCodeCtu", () => { test("getServiceZipCtuCodeFromStore should return zipCodeCtu", () => {
//Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
wrapper.vm.selectableDatesData = { wrapper.vm.selectableDatesData = {
days: [], days: [],
}; };
//Act // Act
const testValue = wrapper.vm.getServiceZipCtuCodeFromStore(); const testValue = wrapper.vm.getServiceZipCtuCodeFromStore();
//Assert // Assert
expect(testValue).toStrictEqual("01234"); expect(testValue).toStrictEqual("01234");
}); });
test("openInshopTimeSlotsModal should trigger openModal method", () => { test("openInshopTimeSlotsModal should trigger openModal method", () => {
//Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
wrapper.vm.selectableDatesData = { wrapper.vm.selectableDatesData = {
days: [], days: [],
}; };
//Act // Act
wrapper.vm.openInshopTimeSlotsModal(); wrapper.vm.openInshopTimeSlotsModal();
//Assert // Assert
expect(wrapper.vm.$refs.timeSlotModalQuestion.openModal).toBeCalled(); 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", () => { test("timeSlotModalClosed should null any selected date when there's no route code", () => {
//Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
wrapper.vm.selectableDatesData = { wrapper.vm.selectableDatesData = {
days: [], days: [],
}; };
wrapper.setData({ wrapper.setData({
selectedDate: "1980-05-05", selectedDate: "1980-05-05",
}); });
wrapper.setData({
selectedTimeSlot: { const selectedTimeSlotInfo = {
timeSlot: {
date: "2019-01-01", date: "2019-01-01",
startTime: "09:00", startTime: "09:00",
endTime: "10:00", endTime: "10:00",
routeCode: null, routeCode: null,
}, },
isPremiumAppointment: null,
};
wrapper.setData({
selectedTimeSlotInfo: selectedTimeSlotInfo,
}); });
//Act // Act
wrapper.vm.timeSlotModalClosed(); wrapper.vm.timeSlotModalClosed();
//Assert // Assert
expect(wrapper.vm.selectedDate).toBe(null); expect(wrapper.vm.selectedDate).toBe(null);
}); });
test("getDisplayTextForMilitaryTime should return the correctly formatted string", () => { test("getDisplayTextForMilitaryTime should return the correctly formatted string", () => {
//Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
wrapper.vm.selectableDatesData = { wrapper.vm.selectableDatesData = {
days: [], days: [],
@ -432,13 +426,13 @@ describe("schedule.vue...", () => {
const timeInput1 = "15:00"; const timeInput1 = "15:00";
const timeInput2 = "15:30"; const timeInput2 = "15:30";
//Act // Act
const testOutput1 = wrapper.vm.getDisplayTextForMilitaryTime(timeInput1); const testOutput1 = wrapper.vm.getDisplayTextForMilitaryTime(timeInput1);
const testOutput2 = wrapper.vm.getDisplayTextForMilitaryTime(timeInput2); const testOutput2 = wrapper.vm.getDisplayTextForMilitaryTime(timeInput2);
const testOutput3 = wrapper.vm.getDisplayTextForMilitaryTime(timeInput1, true); const testOutput3 = wrapper.vm.getDisplayTextForMilitaryTime(timeInput1, true);
const testOutput4 = wrapper.vm.getDisplayTextForMilitaryTime(timeInput2, true); const testOutput4 = wrapper.vm.getDisplayTextForMilitaryTime(timeInput2, true);
//Assert // Assert
expect(testOutput1).toBe("3:00 PM"); expect(testOutput1).toBe("3:00 PM");
expect(testOutput2).toBe("3:30 PM"); expect(testOutput2).toBe("3:30 PM");
expect(testOutput3).toBe("3 PM"); expect(testOutput3).toBe("3 PM");
@ -446,7 +440,7 @@ describe("schedule.vue...", () => {
}); });
test("getDisplayTextForMilitaryTime should return the correctly formatted string", () => { test("getDisplayTextForMilitaryTime should return the correctly formatted string", () => {
//Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
wrapper.vm.selectableDatesData = { wrapper.vm.selectableDatesData = {
days: [], days: [],
@ -454,10 +448,10 @@ describe("schedule.vue...", () => {
wrapper.vm.$router.navigateWithoutSaving = jest.fn(); wrapper.vm.$router.navigateWithoutSaving = jest.fn();
wrapper.vm.$route = "testRoute"; wrapper.vm.$route = "testRoute";
//Act // Act
wrapper.vm.backButtonAction(); wrapper.vm.backButtonAction();
//Assert // Assert
expect(wrapper.vm.$router.navigateWithoutSaving).toBeCalledWith( expect(wrapper.vm.$router.navigateWithoutSaving).toBeCalledWith(
"CLICKED_BACK", "CLICKED_BACK",
"testRoute" "testRoute"
@ -466,7 +460,7 @@ describe("schedule.vue...", () => {
}); });
test("forwardButtonAction should call route method navigateWithoutSaving", async () => { test("forwardButtonAction should call route method navigateWithoutSaving", async () => {
//Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
wrapper.vm.dispatchStoreAction = jest.fn(() => { wrapper.vm.dispatchStoreAction = jest.fn(() => {
return { return {
@ -477,15 +471,15 @@ describe("schedule.vue...", () => {
return {}; return {};
}); });
//Act // Act
await wrapper.vm.forwardButtonAction(); await wrapper.vm.forwardButtonAction();
//Assert // Assert
expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled(); expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled();
}); });
test("for mobile appts, updateSupportingItems should call store action to save supporting items", async () => { test("for mobile appts, updateSupportingItems should call store action to save supporting items", async () => {
//Arrange // Arrange
store.getters.order.serviceLocation.appointmentType = "Mobile"; store.getters.order.serviceLocation.appointmentType = "Mobile";
store.getters.lineItems.supportingItems = [ store.getters.lineItems.supportingItems = [
{ {
@ -514,10 +508,10 @@ describe("schedule.vue...", () => {
}, },
}); });
//Act // Act
await wrapper.vm.updateSupportingItems(); await wrapper.vm.updateSupportingItems();
//Assert // Assert
expect(wrapper.vm.dispatchStoreAction).toBeCalledWith( expect(wrapper.vm.dispatchStoreAction).toBeCalledWith(
"saveSupportingItemsSuppressingStateResetting", "saveSupportingItemsSuppressingStateResetting",
expect.arrayContaining([ 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 () => { 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.order.serviceLocation.appointmentType = "Inshop";
store.getters.lineItems.supportingItems = [ store.getters.lineItems.supportingItems = [
{ {
@ -559,10 +553,10 @@ describe("schedule.vue...", () => {
}, },
}); });
//Act // Act
await wrapper.vm.updateSupportingItems(); await wrapper.vm.updateSupportingItems();
//Assert // Assert
expect(wrapper.vm.dispatchStoreAction).toBeCalledWith( expect(wrapper.vm.dispatchStoreAction).toBeCalledWith(
"saveSupportingItemsSuppressingStateResetting", "saveSupportingItemsSuppressingStateResetting",
expect.not.arrayContaining([ 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 () => { 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.order.serviceLocation.appointmentType = "Mobile";
store.getters.lineItems.supportingItems = []; store.getters.lineItems.supportingItems = [];
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
@ -595,10 +589,10 @@ describe("schedule.vue...", () => {
}, },
}); });
//Act // Act
await wrapper.vm.updateSupportingItems(); await wrapper.vm.updateSupportingItems();
//Assert // Assert
expect(wrapper.vm.dispatchStoreAction).not.toBeCalled(); expect(wrapper.vm.dispatchStoreAction).not.toBeCalled();
}); });
}); });

View file

@ -24,17 +24,17 @@
<timeSlotModalQuestion <timeSlotModalQuestion
ref="timeSlotModalQuestion" ref="timeSlotModalQuestion"
customComponentId="timeSlotModalQuestion" customComponentId="timeSlotModalQuestion"
v-model="selectedTimeSlotInfo"
cmsWidgetName="TimeSlotModalQuestion" cmsWidgetName="TimeSlotModalQuestion"
mobilePremiumCmsWidgetName="MobilePremiumTimeSlotModal" mobilePremiumCmsWidgetName="MobilePremiumTimeSlotModal"
mobileCmsWidgetName="MobileTimeSlotModal" mobileCmsWidgetName="MobileTimeSlotModal"
dropoffCmsWidgetName="DropOffTimeSlotModal" dropoffCmsWidgetName="DropOffTimeSlotModal"
sameDayDropOffCmsWidgetName="SameDayDropOffTimeSlotModal" sameDayDropOffCmsWidgetName="SameDayDropOffTimeSlotModal"
overnightDropOffCmsWidgetName="OvernightDropOffTimeSlotModal" overnightDropOffCmsWidgetName="OvernightDropOffTimeSlotModal"
v-model="selectedTimeSlot"
:selectedDate="selectedDate" :selectedDate="selectedDate"
:appointmentType="appointmentType" :appointmentType="appointmentType"
:premiumAppointmentFee="mobilePremiumAppointmentFee" :premiumAppointmentFee="mobilePremiumAppointmentFee"
:dateAndTimeSlotData="timeSlotsForSelectedDate" :timeSlotsForSelectedDate="timeSlotsForSelectedDate"
:estimatedServiceMinutesMinimum="selectableDatesData.estimatedServiceMinutesMinimum" :estimatedServiceMinutesMinimum="selectableDatesData.estimatedServiceMinutesMinimum"
:estimatedServiceMinutesMaximum="selectableDatesData.estimatedServiceMinutesMaximum" :estimatedServiceMinutesMaximum="selectableDatesData.estimatedServiceMinutesMaximum"
@time-slot-modal-closed="timeSlotModalClosed" @time-slot-modal-closed="timeSlotModalClosed"
@ -81,12 +81,7 @@ import store from "@/store";
// DEFINE VALIDATION RULES // DEFINE VALIDATION RULES
defineRule("date-required", required(errorMessages.DATE_REQUIRED)); defineRule("date-required", required(errorMessages.DATE_REQUIRED));
defineRule("time-slot-selection-required", (value) => { defineRule("time-slot-selection-required", (value) => {
if ( if (value?.timeSlot?.routeCode == null) {
value.date == null ||
value.startTime == null ||
value.routeCode == null ||
value.estimatedServiceMinutesMaximum
) {
return errorMessages.DATE_REQUIRED; return errorMessages.DATE_REQUIRED;
} }
return true; return true;
@ -190,7 +185,7 @@ export default {
data() { data() {
return { return {
selectedDate: this.getSelectedDate(), selectedDate: this.getSelectedDate(),
selectedTimeSlot: this.getSelectedTimeSlot(), selectedTimeSlotInfo: this.getSelectedTimeSlotInfo(),
selectableDatesData: [], selectableDatesData: [],
mobilePremiumAppointmentFee: null, mobilePremiumAppointmentFee: null,
}; };
@ -265,7 +260,7 @@ export default {
vm.mobilePremiumAppointmentFee = resultMap.premiumFeeWithPrice vm.mobilePremiumAppointmentFee = resultMap.premiumFeeWithPrice
? resultMap.premiumFeeWithPrice[0] ? resultMap.premiumFeeWithPrice[0]
: null; : null;
vm.updateFooterButtonText(vm.selectedTimeSlot); vm.updateFooterButtonText(vm.selectedTimeSlotInfo);
}); });
}, },
computed: { computed: {
@ -293,18 +288,22 @@ export default {
splitCopyOnCMSPlaceHolder, splitCopyOnCMSPlaceHolder,
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
const serviceLocation = store.getters.order.serviceLocation; const serviceLocation = store.getters.order.serviceLocation;
const serviceLocationPreReqs = const serviceLocationPreReqs =
serviceLocation.zipCode && serviceLocation.zipCode &&
serviceLocation.zipCodeCtu && serviceLocation.zipCodeCtu &&
serviceLocation.appointmentType && serviceLocation.appointmentType &&
(serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE || (serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE ||
serviceLocation.provider.providerNumber); serviceLocation.provider.providerNumber);
const paymentInfo = store.getters.payment.isInsurance !== null; const paymentInfo = store.getters.payment.isInsurance !== null;
const supportingItems = store.getters.lineItems.supportingItems !== null; const supportingItems = store.getters.lineItems.supportingItems !== null;
const damageInfo = const damageInfo =
store.getters.order.damage.isRepair || store.getters.order.damage.isRepair ||
(store.getters.order.lineItems?.glassParts != null && (store.getters.order.lineItems?.glassParts != null &&
store.getters.order.lineItems.glassParts.length > 0); store.getters.order.lineItems.glassParts.length > 0);
return serviceLocationPreReqs && paymentInfo && supportingItems && damageInfo; return serviceLocationPreReqs && paymentInfo && supportingItems && damageInfo;
}, },
async getAvailableDatesMethod(startDate, endDate) { async getAvailableDatesMethod(startDate, endDate) {
@ -327,45 +326,55 @@ export default {
openInshopTimeSlotsModal() { openInshopTimeSlotsModal() {
this.$refs["timeSlotModalQuestion"].openModal(); this.$refs["timeSlotModalQuestion"].openModal();
}, },
getSelectedTimeSlot() {
return store.getters.order.schedule;
},
getSelectedDate() { getSelectedDate() {
return store.getters.order.schedule.date; return store.getters.order.schedule.date;
}, },
getSelectedRouteCode() { getSelectedTimeSlotInfo() {
return store.getters.order.schedule.routeCode; 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() { getSupportingItems() {
return store.getters.lineItems.supportingItems; return store.getters.lineItems.supportingItems;
}, },
timeSlotModalClosed() { timeSlotModalClosed() {
// Clear the selectedDate if no timeSlot has been selected // Clear the selectedDate if no timeSlot has been selected
if (this.selectedTimeSlot.routeCode == null) { if (this.selectedTimeSlotInfo.timeSlot.routeCode == null) {
this.selectedDate = null; this.selectedDate = null;
} }
}, },
updateFooterButtonText(timeSlot) { updateFooterButtonText(timeSlotInfo) {
let funnelFooterButtonText; let funnelFooterButtonText;
if (!timeSlot || !timeSlot.date) { if (!timeSlotInfo || !timeSlotInfo.timeSlot.date) {
funnelFooterButtonText = "Continue"; funnelFooterButtonText = "Continue";
} else { } else {
funnelFooterButtonText = `Select ${this.convertSelectedDateToShortMonthAndDay( funnelFooterButtonText = `Select ${this.convertSelectedDateToShortMonthAndDay(
timeSlot.date timeSlotInfo.timeSlot.date
)}`; )}`;
if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) { if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) {
funnelFooterButtonText += ` at ${this.getDisplayTextForMilitaryTime( funnelFooterButtonText += ` at ${this.getDisplayTextForMilitaryTime(
timeSlot.startTime timeSlotInfo.timeSlot.startTime
)}`; )}`;
} else if ( } else if (
this.appointmentType === AppointmentTypeStrings.MOBILE && this.appointmentType === AppointmentTypeStrings.MOBILE &&
!timeSlot.isPremiumAppointment !timeSlotInfo.isPremiumAppointment
) { ) {
funnelFooterButtonText += ` at ${this.getDisplayTextForMilitaryTime( funnelFooterButtonText += ` at ${this.getDisplayTextForMilitaryTime(
timeSlot.startTime, timeSlotInfo.timeSlot.startTime,
true true
)} - ${this.getDisplayTextForMilitaryTime(timeSlot.endTime, true)}`; )} - ${this.getDisplayTextForMilitaryTime(
timeSlotInfo.timeSlot.endTime,
true
)}`;
} }
} }
this.$refs.funnelFooter.updateButtonText(funnelFooterButtonText); this.$refs.funnelFooter.updateButtonText(funnelFooterButtonText);
@ -376,14 +385,16 @@ export default {
// Ex: April 25 // Ex: April 25
return dateObject.toLocaleDateString("en-us", { month: "short", day: "numeric" }); return dateObject.toLocaleDateString("en-us", { month: "short", day: "numeric" });
}, },
// Expected input: "HH:MM"
getDisplayTextForMilitaryTime(militaryTimeInput, shouldTrimMinutesIfEmpty = false) { getDisplayTextForMilitaryTime(militaryTimeInput, shouldTrimMinutesIfEmpty = false) {
// Expected input: "HH:MM"
let hours = parseInt(militaryTimeInput.split(":")[0]); let hours = parseInt(militaryTimeInput.split(":")[0]);
const minutes = militaryTimeInput.split(":")[1]; const minutes = militaryTimeInput.split(":")[1];
const meridianNotation = hours > 11 ? "PM" : "AM"; const meridianNotation = hours > 11 ? "PM" : "AM";
if (hours > 12) { if (hours > 12) {
hours -= 12; hours -= 12;
} }
if (shouldTrimMinutesIfEmpty && minutes === "00") { if (shouldTrimMinutesIfEmpty && minutes === "00") {
return `${hours} ${meridianNotation}`; return `${hours} ${meridianNotation}`;
} else { } else {
@ -395,7 +406,12 @@ export default {
}, },
forwardButtonAction() { forwardButtonAction() {
this.updateSupportingItems(); 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); 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 we have a premium fee(early bird), then save/update supporting items
if ( if (
this.appointmentType === AppointmentTypeStrings.MOBILE && this.appointmentType === AppointmentTypeStrings.MOBILE &&
this.selectedTimeSlot?.isPremiumAppointment this.selectedTimeSlotInfo?.isPremiumAppointment
) { ) {
const earlyBirdIndex = supportingItems.findIndex( const earlyBirdIndex = supportingItems.findIndex(
(item) => item.partType == PREMIUM_FEE_PART_TYPE (item) => item.partType == PREMIUM_FEE_PART_TYPE
@ -448,17 +464,20 @@ export default {
selectedDate(newValue, oldValue) { selectedDate(newValue, oldValue) {
// Clear time slot selection if date selected changes // Clear time slot selection if date selected changes
if (newValue !== oldValue) { if (newValue !== oldValue) {
this.selectedTimeSlot = { this.selectedTimeSlotInfo = {
date: null, timeSlot: {
routeCode: null, date: null,
startTime: null, routeCode: null,
endTime: null, startTime: null,
jobMaxMinutes: null, endTime: null,
jobMinMinutes: null, jobMaxMinutes: null,
jobMinMinutes: null,
},
isPremiumAppointment: null,
}; };
} }
}, },
selectedTimeSlot(newValue) { selectedTimeSlotInfo(newValue) {
this.updateFooterButtonText(newValue); this.updateFooterButtonText(newValue);
}, },
}, },

View file

@ -2,30 +2,76 @@ import { shallowMount, flushPromises } from "@vue/test-utils";
import { getMountOptions } from "@/helpers/unit-test-helper.js"; import { getMountOptions } from "@/helpers/unit-test-helper.js";
import timeSlotModalQuestion from "@/layouts/schedule/time-slot-modal-question/time-slot-modal-question.vue"; import timeSlotModalQuestion from "@/layouts/schedule/time-slot-modal-question/time-slot-modal-question.vue";
import store from "@/store"; import store from "@/store";
import { import {
RouteCodeFlags, RouteCodeFlags,
PREMIUM_TIME_SLOT_ID_FLAG, PREMIUM_TIME_SLOT_ID_FLAG,
PREMIUM_FEE_PART_TYPE, PREMIUM_FEE_PART_TYPE,
} from "@/constants/schedule-constants"; } 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 () => { test("When selected time slot is changed, a correctly formatted selectedTimeSlot should be emitted", async () => {
//Arrange //Arrange
const date = "2023-07-01"; const date = "2023-09-02";
const timeSlotId = "testRouteCodeId"; const routeCode = "03335-01820-S-B*20334*830 AM";
const startTime = "8:00 AM"; const startTime = "08:30";
const endTime = "8:30 AM"; const endTime = "09:30";
const jobMaxMinutes = 100; const jobMaxMinutes = 100;
const jobMinMinutes = 50; const jobMinMinutes = 50;
const expectedEmit = [ const expectedEmit = [
[ [
{ {
date: date, timeSlot: {
routeCode: timeSlotId, date: date,
startTime: startTime, routeCode: routeCode,
endTime: endTime, startTime: startTime,
jobMaxMinutes: jobMaxMinutes.toString(), endTime: endTime,
jobMinMinutes: jobMinMinutes.toString(), jobMaxMinutes: jobMaxMinutes.toString(),
jobMinMinutes: jobMinMinutes.toString(),
},
isPremiumAppointment: false,
}, },
], ],
]; ];
@ -35,22 +81,16 @@ describe("time-slot-modal-list-button-question.vue", () => {
propsData: { propsData: {
estimatedServiceMinutesMaximum: jobMaxMinutes, estimatedServiceMinutesMaximum: jobMaxMinutes,
estimatedServiceMinutesMinimum: jobMinMinutes, estimatedServiceMinutesMinimum: jobMinMinutes,
dateAndTimeSlotData: { timeSlotsForSelectedDate: timeSlotsForSelectedDate,
date: date,
timeSlots: [
{
id: timeSlotId,
startTime: startTime,
endTime: endTime,
},
],
},
}, },
}, },
}); });
wrapper.vm.$refs.timeSlots.closeModal = jest.fn(); wrapper.vm.$refs.timeSlots.closeModal = jest.fn();
wrapper.vm.selectedTimeSlotId = timeSlotId;
await wrapper.setData({
selectedRouteCode: routeCode,
});
//Act //Act
wrapper.vm.setSelectedTimeSlot(); wrapper.vm.setSelectedTimeSlot();
@ -59,7 +99,8 @@ describe("time-slot-modal-list-button-question.vue", () => {
expect(wrapper.emitted()["update:modelValue"]).toEqual(expectedEmit); 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 () => { test("The correct supplemental information should be obtained from CMS - Mobile - Not Premium ", async () => {
//Arrange //Arrange
const mobileCmsWidgetName = "MobileNotPremium"; const mobileCmsWidgetName = "MobileNotPremium";
@ -72,11 +113,13 @@ describe("time-slot-modal-list-button-question.vue Supplemental information", ()
}, },
}, },
}); });
//Assert //Assert
expect(wrapper.vm.supplementalInformationBlock).toEqual( expect(wrapper.vm.supplementalInformationBlock).toEqual(
mockCmsContent[mobileCmsWidgetName]["BodyText"] mockCmsContent[mobileCmsWidgetName]["BodyText"]
); );
}); });
test("The correct supplemental information should be obtained from CMS - Mobile - Premium", async () => { test("The correct supplemental information should be obtained from CMS - Mobile - Premium", async () => {
//Arrange //Arrange
const mobileCmsWidgetName = "MobilePremium"; const mobileCmsWidgetName = "MobilePremium";
@ -89,11 +132,13 @@ describe("time-slot-modal-list-button-question.vue Supplemental information", ()
}, },
}, },
}); });
//Assert //Assert
expect(wrapper.vm.supplementalInformationBlock).toEqual( expect(wrapper.vm.supplementalInformationBlock).toEqual(
mockCmsContent[mobileCmsWidgetName]["BodyText"] mockCmsContent[mobileCmsWidgetName]["BodyText"]
); );
}); });
test("No supplemental information should be obtained from CMS - Inshop", async () => { test("No supplemental information should be obtained from CMS - Inshop", async () => {
//Arrange //Arrange
const inshopWidgetName = "Inshop"; const inshopWidgetName = "Inshop";
@ -109,6 +154,7 @@ describe("time-slot-modal-list-button-question.vue Supplemental information", ()
//Assert //Assert
expect(wrapper.vm.supplementalInformationBlock).toBeNull(); expect(wrapper.vm.supplementalInformationBlock).toBeNull();
}); });
test("No supplemental information should be obtained from CMS - Drop off, nothing selected", async () => { test("No supplemental information should be obtained from CMS - Drop off, nothing selected", async () => {
//Arrange //Arrange
const dropOffWidgetName = "Dropoff"; const dropOffWidgetName = "Dropoff";
@ -124,40 +170,69 @@ describe("time-slot-modal-list-button-question.vue Supplemental information", ()
//Assert //Assert
expect(wrapper.vm.supplementalInformationBlock).toBeNull(); expect(wrapper.vm.supplementalInformationBlock).toBeNull();
}); });
test("Correct supplemental information should be retrieved, Dropoff regular", async () => { test("Correct supplemental information should be retrieved, Dropoff regular", async () => {
//Arrange //Arrange
const dropOffWidgetName = "Dropoff"; 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({ const { wrapper } = setupMocks({
customMountOptions: { customMountOptions: {
propsData: { propsData: {
modelValue: { routeCode: "Test" + RouteCodeFlags.ALL_DAY_DROP_OFF }, modelValue: modelValue,
appointmentType: "Dropoff", appointmentType: "Dropoff",
dropoffCmsWidgetName: dropOffWidgetName, dropoffCmsWidgetName: dropOffWidgetName,
}, },
}, },
}); });
//Assert //Assert
expect(wrapper.vm.supplementalInformationBlock).toEqual( expect(wrapper.vm.supplementalInformationBlock).toEqual(
mockCmsContent[dropOffWidgetName]["BodyText"] mockCmsContent[dropOffWidgetName]["BodyText"]
); );
}); });
test("Correct supplemental information should be retrieved, Dropoff Overnight", async () => { test("Correct supplemental information should be retrieved, Dropoff Overnight", async () => {
//Arrange //Arrange
const overnightDropOffCmsWidgetName = "Overnight"; 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({ const { wrapper } = setupMocks({
customMountOptions: { customMountOptions: {
propsData: { propsData: {
modelValue: { routeCode: "Test" + RouteCodeFlags.OVERNIGHT_DROP_OFF }, modelValue: modelValue,
appointmentType: "Dropoff", appointmentType: "Dropoff",
overnightDropOffCmsWidgetName: overnightDropOffCmsWidgetName, overnightDropOffCmsWidgetName: overnightDropOffCmsWidgetName,
}, },
}, },
}); });
//Assert //Assert
expect(wrapper.vm.supplementalInformationBlock).toEqual( expect(wrapper.vm.supplementalInformationBlock).toEqual(
mockCmsContent[overnightDropOffCmsWidgetName]["BodyText"] mockCmsContent[overnightDropOffCmsWidgetName]["BodyText"]
); );
}); });
test("Correct supplemental information should be retrieved, Dropoff Sameday", async () => { test("Correct supplemental information should be retrieved, Dropoff Sameday", async () => {
//Arrange //Arrange
const sameDayDropOffCmsWidgetName = "Sameday"; const sameDayDropOffCmsWidgetName = "Sameday";
@ -167,13 +242,24 @@ describe("time-slot-modal-list-button-question.vue Supplemental information", ()
const endTime = "8:30 AM"; const endTime = "8:30 AM";
const jobMaxMinutes = 100; const jobMaxMinutes = 100;
const jobMinMinutes = 50; 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({ const { wrapper } = setupMocks({
customMountOptions: { customMountOptions: {
propsData: { propsData: {
estimatedServiceMinutesMaximum: jobMaxMinutes, estimatedServiceMinutesMaximum: jobMaxMinutes,
estimatedServiceMinutesMinimum: jobMinMinutes, estimatedServiceMinutesMinimum: jobMinMinutes,
dateAndTimeSlotData: { timeSlotsForSelectedDate: {
date: date, date: date,
timeSlots: [ 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", appointmentType: "Dropoff",
sameDayDropOffCmsWidgetName: sameDayDropOffCmsWidgetName, sameDayDropOffCmsWidgetName: sameDayDropOffCmsWidgetName,
}, },
@ -208,12 +294,24 @@ describe("time-slot-modal-list-button-question.vue Disclaimer", () => {
const jobMaxMinutes = 100; const jobMaxMinutes = 100;
const jobMinMinutes = 50; 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({ const { wrapper } = setupMocks({
customMountOptions: { customMountOptions: {
propsData: { propsData: {
estimatedServiceMinutesMaximum: jobMaxMinutes, estimatedServiceMinutesMaximum: jobMaxMinutes,
estimatedServiceMinutesMinimum: jobMinMinutes, estimatedServiceMinutesMinimum: jobMinMinutes,
dateAndTimeSlotData: { timeSlotsForSelectedDate: {
date: date, date: date,
timeSlots: [ 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", appointmentType: "Dropoff",
sameDayDropOffCmsWidgetName: sameDayDropOffCmsWidgetName, sameDayDropOffCmsWidgetName: sameDayDropOffCmsWidgetName,
}, },
@ -235,47 +333,88 @@ describe("time-slot-modal-list-button-question.vue Disclaimer", () => {
mockCmsContent[sameDayDropOffCmsWidgetName]["FooterText"] mockCmsContent[sameDayDropOffCmsWidgetName]["FooterText"]
); );
}); });
test("Correct disclaimer information should be retrieved, Dropoff regular", async () => { test("Correct disclaimer information should be retrieved, Dropoff regular", async () => {
//Arrange //Arrange
const dropOffWidgetName = "Dropoff"; 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({ const { wrapper } = setupMocks({
customMountOptions: { customMountOptions: {
propsData: { propsData: {
modelValue: { routeCode: "Test" + RouteCodeFlags.ALL_DAY_DROP_OFF }, modelValue: modelValue,
appointmentType: "Dropoff", appointmentType: "Dropoff",
dropoffCmsWidgetName: dropOffWidgetName, dropoffCmsWidgetName: dropOffWidgetName,
}, },
}, },
}); });
//Assert //Assert
expect(wrapper.vm.disclaimerTextBlockCopy).toEqual( expect(wrapper.vm.disclaimerTextBlockCopy).toEqual(
mockCmsContent[dropOffWidgetName]["FooterText"] mockCmsContent[dropOffWidgetName]["FooterText"]
); );
}); });
test("Correct disclaimer information should be retrieved, Dropoff overnight", async () => { test("Correct disclaimer information should be retrieved, Dropoff overnight", async () => {
//Arrange //Arrange
const overnightDropOffCmsWidgetName = "Overnight"; 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({ const { wrapper } = setupMocks({
customMountOptions: { customMountOptions: {
propsData: { propsData: {
modelValue: { routeCode: "Test" + RouteCodeFlags.OVERNIGHT_DROP_OFF }, modelValue: modelValue,
appointmentType: "Dropoff", appointmentType: "Dropoff",
overnightDropOffCmsWidgetName: overnightDropOffCmsWidgetName, overnightDropOffCmsWidgetName: overnightDropOffCmsWidgetName,
}, },
}, },
}); });
//Assert //Assert
expect(wrapper.vm.disclaimerTextBlockCopy).toEqual( expect(wrapper.vm.disclaimerTextBlockCopy).toEqual(
mockCmsContent[overnightDropOffCmsWidgetName]["FooterText"] mockCmsContent[overnightDropOffCmsWidgetName]["FooterText"]
); );
}); });
test("No disclaimer information should be retrieved, non-dropoff", async () => { test("No disclaimer information should be retrieved, non-dropoff", async () => {
//Arrange //Arrange
const inshopWidgetName = "Inshop"; const inshopWidgetName = "Inshop";
const modelValue = {
timeSlot: {
routeCode: `test`,
date: null,
startTime: null,
endTime: null,
jobMaxMinutes: null,
jobMinMinutes: null,
},
isPremiumAppointment: null,
};
const { wrapper } = setupMocks({ const { wrapper } = setupMocks({
customMountOptions: { customMountOptions: {
propsData: { propsData: {
modelValue: { routeCode: "test" }, modelValue: modelValue,
appointmentType: "Inshop", appointmentType: "Inshop",
cmsWidgetName: inshopWidgetName, 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 () => { test("The correct duration information should be displayed - Dropoff - Sameday ", async () => {
//Arrange //Arrange
const sameDayDropOffCmsWidgetName = "Sameday"; const sameDayDropOffCmsWidgetName = "Sameday";
@ -297,12 +436,24 @@ describe("time-slot-modal-list-button-question.vue Duration", () => {
const jobMaxMinutes = 100; const jobMaxMinutes = 100;
const jobMinMinutes = 50; 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({ const { wrapper } = setupMocks({
customMountOptions: { customMountOptions: {
propsData: { propsData: {
estimatedServiceMinutesMaximum: jobMaxMinutes, estimatedServiceMinutesMaximum: jobMaxMinutes,
estimatedServiceMinutesMinimum: jobMinMinutes, estimatedServiceMinutesMinimum: jobMinMinutes,
dateAndTimeSlotData: { timeSlotsForSelectedDate: {
date: date, date: date,
timeSlots: [ 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", appointmentType: "Dropoff",
sameDayDropOffCmsWidgetName: sameDayDropOffCmsWidgetName, sameDayDropOffCmsWidgetName: sameDayDropOffCmsWidgetName,
}, },
@ -324,30 +475,59 @@ describe("time-slot-modal-list-button-question.vue Duration", () => {
mockCmsContent[sameDayDropOffCmsWidgetName]["SubheaderText"] mockCmsContent[sameDayDropOffCmsWidgetName]["SubheaderText"]
); );
}); });
test("Correct duration information should be retrieved, Dropoff overnight", async () => { test("Correct duration information should be retrieved, Dropoff overnight", async () => {
//Arrange //Arrange
const overnightDropOffCmsWidgetName = "Overnight"; 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({ const { wrapper } = setupMocks({
customMountOptions: { customMountOptions: {
propsData: { propsData: {
modelValue: { routeCode: "Test" + RouteCodeFlags.OVERNIGHT_DROP_OFF }, modelValue: modelValue,
appointmentType: "Dropoff", appointmentType: "Dropoff",
overnightDropOffCmsWidgetName: overnightDropOffCmsWidgetName, overnightDropOffCmsWidgetName: overnightDropOffCmsWidgetName,
}, },
}, },
}); });
//Assert //Assert
expect(wrapper.vm.durationTextBlockCopy).toEqual( expect(wrapper.vm.durationTextBlockCopy).toEqual(
mockCmsContent[overnightDropOffCmsWidgetName]["SubheaderText"] mockCmsContent[overnightDropOffCmsWidgetName]["SubheaderText"]
); );
}); });
test("Correct duration information should be retrieved, Dropoff", async () => { test("Correct duration information should be retrieved, Dropoff", async () => {
//Arrange //Arrange
const dropoffCmsWidgetName = "Dropoff"; 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({ const { wrapper } = setupMocks({
customMountOptions: { customMountOptions: {
propsData: { propsData: {
modelValue: { routeCode: "Test" + RouteCodeFlags.ALL_DAY_DROP_OFF }, modelValue: modelValue,
appointmentType: "Dropoff", appointmentType: "Dropoff",
dropoffCmsWidgetName: dropoffCmsWidgetName, dropoffCmsWidgetName: dropoffCmsWidgetName,
}, },
@ -358,25 +538,39 @@ describe("time-slot-modal-list-button-question.vue Duration", () => {
mockCmsContent[dropoffCmsWidgetName]["SubheaderText"] mockCmsContent[dropoffCmsWidgetName]["SubheaderText"]
); );
}); });
test("Correct duration information should be retrieved, Inshop - convert to hours", async () => { test("Correct duration information should be retrieved, Inshop - convert to hours", async () => {
//Arrange //Arrange
const cmsWidgetName = "Inshop"; const cmsWidgetName = "Inshop";
const modelValue = {
timeSlot: {
routeCode: `test`,
date: null,
startTime: null,
endTime: null,
},
isPremiumAppointment: null,
};
const { wrapper } = setupMocks({ const { wrapper } = setupMocks({
customMountOptions: { customMountOptions: {
propsData: { propsData: {
estimatedServiceMinutesMaximum: 180, estimatedServiceMinutesMaximum: 180,
estimatedServiceMinutesMinimum: 120, estimatedServiceMinutesMinimum: 120,
modelValue: { routeCode: "Test" }, modelValue: modelValue,
appointmentType: "Inshop", appointmentType: "Inshop",
cmsWidgetName: cmsWidgetName, cmsWidgetName: cmsWidgetName,
}, },
}, },
}); });
//Assert //Assert
expect(wrapper.vm.durationTextBlockCopy).toEqual( expect(wrapper.vm.durationTextBlockCopy).toEqual(
mockCmsContent[cmsWidgetName]["SubheaderText"] + " 2 - 3 hours" mockCmsContent[cmsWidgetName]["SubheaderText"] + " 2 - 3 hours"
); );
}); });
test("Correct duration information should be retrieved, Inshop - convert to minutes", async () => { test("Correct duration information should be retrieved, Inshop - convert to minutes", async () => {
//Arrange //Arrange
const cmsWidgetName = "Inshop"; const cmsWidgetName = "Inshop";
@ -396,6 +590,7 @@ describe("time-slot-modal-list-button-question.vue Duration", () => {
mockCmsContent[cmsWidgetName]["SubheaderText"] + " 60 - 90 minutes" mockCmsContent[cmsWidgetName]["SubheaderText"] + " 60 - 90 minutes"
); );
}); });
test("Correct duration information should be retrieved, Inshop - min = maximum time", async () => { test("Correct duration information should be retrieved, Inshop - min = maximum time", async () => {
//Arrange //Arrange
const cmsWidgetName = "Inshop"; const cmsWidgetName = "Inshop";
@ -415,6 +610,7 @@ describe("time-slot-modal-list-button-question.vue Duration", () => {
mockCmsContent[cmsWidgetName]["SubheaderText"] + " 4 hours" mockCmsContent[cmsWidgetName]["SubheaderText"] + " 4 hours"
); );
}); });
test("No duration information should be displayed, Mobile", async () => { test("No duration information should be displayed, Mobile", async () => {
//Arrange //Arrange
const mobileCmsWidgetName = "Mobile"; const mobileCmsWidgetName = "Mobile";
@ -450,7 +646,7 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => {
propsData: { propsData: {
estimatedServiceMinutesMaximum: jobMaxMinutes, estimatedServiceMinutesMaximum: jobMaxMinutes,
estimatedServiceMinutesMinimum: jobMinMinutes, estimatedServiceMinutesMinimum: jobMinMinutes,
dateAndTimeSlotData: { timeSlotsForSelectedDate: {
date: date, date: date,
timeSlots: [ timeSlots: [
{ {
@ -473,6 +669,7 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => {
}; };
expect(wrapper.vm.availableTimeSlots[0]).toEqual(expectedTimeSlotObject); expect(wrapper.vm.availableTimeSlots[0]).toEqual(expectedTimeSlotObject);
}); });
test("Available Timeslots should be formatted correctly - Dropoff - Overnight ", async () => { test("Available Timeslots should be formatted correctly - Dropoff - Overnight ", async () => {
//Arrange //Arrange
const overnightDropOffCmsWidgetName = "Overnight"; const overnightDropOffCmsWidgetName = "Overnight";
@ -488,7 +685,7 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => {
propsData: { propsData: {
estimatedServiceMinutesMaximum: jobMaxMinutes, estimatedServiceMinutesMaximum: jobMaxMinutes,
estimatedServiceMinutesMinimum: jobMinMinutes, estimatedServiceMinutesMinimum: jobMinMinutes,
dateAndTimeSlotData: { timeSlotsForSelectedDate: {
date: date, date: date,
timeSlots: [ timeSlots: [
{ {
@ -511,6 +708,7 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => {
}; };
expect(wrapper.vm.availableTimeSlots[0]).toEqual(expectedTimeSlotObject); expect(wrapper.vm.availableTimeSlots[0]).toEqual(expectedTimeSlotObject);
}); });
test("Available Timeslots should be formatted correctly - Dropoff", async () => { test("Available Timeslots should be formatted correctly - Dropoff", async () => {
//Arrange //Arrange
const dropoffCmsWidgetName = "Dropoff"; const dropoffCmsWidgetName = "Dropoff";
@ -526,7 +724,7 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => {
propsData: { propsData: {
estimatedServiceMinutesMaximum: jobMaxMinutes, estimatedServiceMinutesMaximum: jobMaxMinutes,
estimatedServiceMinutesMinimum: jobMinMinutes, estimatedServiceMinutesMinimum: jobMinMinutes,
dateAndTimeSlotData: { timeSlotsForSelectedDate: {
date: date, date: date,
timeSlots: [ timeSlots: [
{ {
@ -549,6 +747,7 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => {
}; };
expect(wrapper.vm.availableTimeSlots[0]).toEqual(expectedTimeSlotObject); expect(wrapper.vm.availableTimeSlots[0]).toEqual(expectedTimeSlotObject);
}); });
test("Available Timeslots should be formatted correctly - Mobile not premium", async () => { test("Available Timeslots should be formatted correctly - Mobile not premium", async () => {
//Arrange //Arrange
const mobileCmsWidgetName = "MobileNotPremium"; const mobileCmsWidgetName = "MobileNotPremium";
@ -564,7 +763,7 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => {
propsData: { propsData: {
estimatedServiceMinutesMaximum: jobMaxMinutes, estimatedServiceMinutesMaximum: jobMaxMinutes,
estimatedServiceMinutesMinimum: jobMinMinutes, estimatedServiceMinutesMinimum: jobMinMinutes,
dateAndTimeSlotData: { timeSlotsForSelectedDate: {
date: date, date: date,
timeSlots: [ timeSlots: [
{ {
@ -587,6 +786,7 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => {
}; };
expect(wrapper.vm.availableTimeSlots[0]).toEqual(expectedTimeSlotObject); expect(wrapper.vm.availableTimeSlots[0]).toEqual(expectedTimeSlotObject);
}); });
test("Available Timeslots should be formatted correctly - Mobile with premium", async () => { test("Available Timeslots should be formatted correctly - Mobile with premium", async () => {
//Arrange //Arrange
const mobilePremiumCmsWidgetName = "MobilePremium"; const mobilePremiumCmsWidgetName = "MobilePremium";
@ -605,7 +805,7 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => {
premiumAppointmentFee: { premiumAppointmentFee: {
partType: PREMIUM_FEE_PART_TYPE, partType: PREMIUM_FEE_PART_TYPE,
}, },
dateAndTimeSlotData: { timeSlotsForSelectedDate: {
date: date, date: date,
timeSlots: [ timeSlots: [
{ {
@ -631,8 +831,10 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => {
buttonLabelSubCopy: "+$15.99", buttonLabelSubCopy: "+$15.99",
value: timeSlotId + PREMIUM_TIME_SLOT_ID_FLAG, value: timeSlotId + PREMIUM_TIME_SLOT_ID_FLAG,
}; };
expect(wrapper.vm.availableTimeSlots[0]).toEqual(expectedTimeSlotObject); expect(wrapper.vm.availableTimeSlots[0]).toEqual(expectedTimeSlotObject);
}); });
test("Available Timeslots should be formatted correctly - Inshop", async () => { test("Available Timeslots should be formatted correctly - Inshop", async () => {
//Arrange //Arrange
const cmsWidgetName = "Inshop"; const cmsWidgetName = "Inshop";
@ -648,7 +850,7 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => {
propsData: { propsData: {
estimatedServiceMinutesMaximum: jobMaxMinutes, estimatedServiceMinutesMaximum: jobMaxMinutes,
estimatedServiceMinutesMinimum: jobMinMinutes, estimatedServiceMinutesMinimum: jobMinMinutes,
dateAndTimeSlotData: { timeSlotsForSelectedDate: {
date: date, date: date,
timeSlots: [ timeSlots: [
{ {
@ -678,7 +880,7 @@ const mockCmsContent = {
HeaderText: "Mobile, not premium HeaderText", HeaderText: "Mobile, not premium HeaderText",
}, },
MobilePremium: { MobilePremium: {
"BodyText:": "Mobile, with premium BodyText", BodyText: "Mobile, with premium BodyText",
FooterText: "Mobile, with premium FooterText", FooterText: "Mobile, with premium FooterText",
SubheaderText: "Mobile, with premium SubheaderText", SubheaderText: "Mobile, with premium SubheaderText",
HeaderText: "Mobile, with premium HeaderText", HeaderText: "Mobile, with premium HeaderText",

View file

@ -22,7 +22,7 @@
:answers="availableTimeSlots" :answers="availableTimeSlots"
groupName="chooseTimeSlot" groupName="chooseTimeSlot"
textPosition="text-center" textPosition="text-center"
v-model="selectedTimeSlotId" v-model="selectedRouteCode"
isRequired isRequired
validationRules="time-slot-required" /> validationRules="time-slot-required" />
<div <div
@ -47,12 +47,12 @@ import buttonQuestion from "@/digital-components/button-question/button-question
import timeSlotModalListButton from "./time-slot-modal-list-button/time-slot-modal-list-button"; import timeSlotModalListButton from "./time-slot-modal-list-button/time-slot-modal-list-button";
// Helpers // Helpers
import { deepClone } from "@/helpers/object-helper";
import { import {
convertDateStringToDate, convertDateStringToDate,
militaryToTwelveHourTime, militaryToTwelveHourTime,
getDisplayTextForDurationLength, getDisplayTextForDurationLength,
} from "@/layouts/schedule/helpers/schedule-helper"; } from "@/layouts/schedule/helpers/schedule-helper";
import { deepClone } from "@/helpers/object-helper";
// Validation - TODO: Move this somewhere more global? // Validation - TODO: Move this somewhere more global?
import { defineRule, useField } from "vee-validate"; import { defineRule, useField } from "vee-validate";
@ -87,12 +87,15 @@ export default {
modelValue: { modelValue: {
type: Object, type: Object,
default: () => ({ default: () => ({
routeCode: null, timeSlot: {
date: null, routeCode: null,
startTime: null, date: null,
endTime: null, startTime: null,
jobMaxMinutes: null, endTime: null,
jobMinMinutes: null, jobMaxMinutes: null,
jobMinMinutes: null,
},
isPremiumAppointment: null,
}), }),
}, },
cmsWidgetName: String, cmsWidgetName: String,
@ -102,7 +105,7 @@ export default {
sameDayDropOffCmsWidgetName: String, sameDayDropOffCmsWidgetName: String,
overnightDropOffCmsWidgetName: String, overnightDropOffCmsWidgetName: String,
appointmentType: String, appointmentType: String,
dateAndTimeSlotData: Object, timeSlotsForSelectedDate: Object,
premiumAppointmentFee: Object, premiumAppointmentFee: Object,
estimatedServiceMinutesMinimum: Number, estimatedServiceMinutesMinimum: Number,
estimatedServiceMinutesMaximum: Number, estimatedServiceMinutesMaximum: Number,
@ -113,7 +116,7 @@ export default {
data() { data() {
return { return {
isModalOpened: false, isModalOpened: false,
selectedValue: null, selectedRouteCode: this.getSelectedRouteCode(),
timeSlotModalListButton: timeSlotModalListButton, timeSlotModalListButton: timeSlotModalListButton,
}; };
}, },
@ -150,36 +153,31 @@ export default {
modalName() { modalName() {
return "timeSlots"; return "timeSlots";
}, },
selectedTimeSlotId: { modal() {
get: function () { return this.$refs[this.modalName];
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);
},
}, },
supplementalInformationBlock() { supplementalInformationBlock() {
let appointmentTypeCmsWidgetName; let appointmentTypeCmsWidgetName;
if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) { if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) {
return null; return null;
} else if (this.appointmentType === AppointmentTypeStrings.MOBILE) { } else if (this.appointmentType === AppointmentTypeStrings.MOBILE) {
appointmentTypeCmsWidgetName = this.selectedTimeSlotId?.includes( appointmentTypeCmsWidgetName = this.selectedRouteCode?.includes(
PREMIUM_TIME_SLOT_ID_FLAG PREMIUM_TIME_SLOT_ID_FLAG
) )
? this.mobilePremiumCmsWidgetName ? this.mobilePremiumCmsWidgetName
: this.mobileCmsWidgetName; : this.mobileCmsWidgetName;
} else { } else {
if (!this.selectedTimeSlotId) { if (!this.selectedRouteCode) {
return null; return null;
} else { } else {
appointmentTypeCmsWidgetName = appointmentTypeCmsWidgetName =
this.getRelevantDropOffCmsWidgetNameForSelectedTimeSlot( this.getRelevantDropOffCmsWidgetNameForSelectedTimeSlot(
this.selectedTimeSlotId, this.selectedRouteCode,
true true
); );
} }
} }
return this.getCmsContent( return this.getCmsContent(
appointmentTypeCmsWidgetName, appointmentTypeCmsWidgetName,
cmsWidgetFieldMappings.SUPPLEMENTAL_INFORMATION cmsWidgetFieldMappings.SUPPLEMENTAL_INFORMATION
@ -232,13 +230,13 @@ export default {
}, },
disclaimerTextBlockCopy() { disclaimerTextBlockCopy() {
if (this.appointmentType === AppointmentTypeStrings.DROP_OFF) { 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) { if (this.isSameDay) {
return this.sameDayDropOffDisclaimerText; return this.sameDayDropOffDisclaimerText;
} else { } else {
return this.dropoffDisclaimerText; return this.dropoffDisclaimerText;
} }
} else if (this.selectedTimeSlotId?.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) { } else if (this.selectedRouteCode?.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) {
return this.overnightDropOffDisclaimerText; return this.overnightDropOffDisclaimerText;
} else { } else {
return null; return null;
@ -281,9 +279,9 @@ export default {
} else if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) { } else if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) {
return this.inshopDurationText; return this.inshopDurationText;
} else { } else {
if (this.selectedTimeSlotId?.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) { if (this.selectedRouteCode?.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) {
return this.overnightDropoffDurationText; 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) { if (this.isSameDay) {
return this.sameDayDropoffDurationText; return this.sameDayDropoffDurationText;
} else { } else {
@ -295,21 +293,21 @@ export default {
} }
}, },
isSameDay() { isSameDay() {
if (!this.dateAndTimeSlotData) { if (!this.timeSlotsForSelectedDate) {
return false; return false;
} }
const selectedDate = this.dateAndTimeSlotData.date; const selectedDate = this.timeSlotsForSelectedDate.date;
const todaysDate = new Date().toISOString().split("T")[0]; const todaysDate = new Date().toISOString().split("T")[0];
return selectedDate === todaysDate; return selectedDate === todaysDate;
}, },
dateSelectedReadableDate() { dateSelectedReadableDate() {
if (!this.dateAndTimeSlotData) { if (!this.timeSlotsForSelectedDate) {
return null; return null;
} }
// This conversion ensures we don't get get GMT induced date changes // 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 // Ex: Tuesday, April 22
return dateObject.toLocaleDateString("en-us", { return dateObject.toLocaleDateString("en-us", {
weekday: "long", weekday: "long",
@ -318,21 +316,20 @@ export default {
}); });
}, },
availableTimeSlots() { availableTimeSlots() {
if (!this.dateAndTimeSlotData) { if (!this.timeSlotsForSelectedDate) {
return null; return null;
} }
if (this.appointmentType === AppointmentTypeStrings.DROP_OFF) { if (this.appointmentType === AppointmentTypeStrings.DROP_OFF) {
return this.getAvailableTimeSlotsForDropOff(this.dateAndTimeSlotData.timeSlots); return this.getAvailableTimeSlotsForDropOff(
this.timeSlotsForSelectedDate.timeSlots
);
} else if (this.appointmentType === AppointmentTypeStrings.MOBILE) { } else if (this.appointmentType === AppointmentTypeStrings.MOBILE) {
return this.getAvailableTimeSlotsForMobile(this.dateAndTimeSlotData.timeSlots); return this.getAvailableTimeSlotsForMobile(this.timeSlotsForSelectedDate.timeSlots);
} else { } else {
return this.getAvailableTimeSlotsForInshop(this.dateAndTimeSlotData.timeSlots); return this.getAvailableTimeSlotsForInshop(this.timeSlotsForSelectedDate.timeSlots);
} }
}, },
modal() {
return this.$refs[this.modalName];
},
}, },
methods: { methods: {
openModal() { openModal() {
@ -348,16 +345,19 @@ export default {
this.$emit("time-slot-modal-closed"); this.$emit("time-slot-modal-closed");
}, },
async setSelectedTimeSlot() { async setSelectedTimeSlot() {
this.$emit("update:modelValue", this.selectedValue); this.$emit(
"update:modelValue",
this.getSelectedTimeSlotInfoObject(this.selectedRouteCode)
);
this.closeModal(); this.closeModal();
}, },
getRelevantDropOffCmsWidgetNameForSelectedTimeSlot( getRelevantDropOffCmsWidgetNameForSelectedTimeSlot(
selectedTimeSlotId, selectedRouteCode,
isSameDayRelevant = false isSameDayRelevant = false
) { ) {
if (selectedTimeSlotId.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) { if (selectedRouteCode.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) {
return this.overnightDropOffCmsWidgetName; 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 return this.isSameDay && isSameDayRelevant
? this.sameDayDropOffCmsWidgetName ? this.sameDayDropOffCmsWidgetName
: this.dropoffCmsWidgetName; : this.dropoffCmsWidgetName;
@ -416,8 +416,9 @@ export default {
getPremiumAppointmentTimeSlot(timeSlotData) { getPremiumAppointmentTimeSlot(timeSlotData) {
const formattedPrice = const formattedPrice =
"+$" + this.getTotalLineItemPrice(this.premiumAppointmentFee).toFixed(2); "+$" + this.getTotalLineItemPrice(this.premiumAppointmentFee).toFixed(2);
return { return {
// Unique value is required for each <input> and the premium appoinment shares a timeSlot ID // Unique value is required for each <input> and the premium appoinment shares an id
value: this.addPremiumFlagToInput(timeSlotData.id), value: this.addPremiumFlagToInput(timeSlotData.id),
buttonLabel: this.premiumAppointmentButtonText, buttonLabel: this.premiumAppointmentButtonText,
buttonLabelSubCopy: formattedPrice, buttonLabelSubCopy: formattedPrice,
@ -426,41 +427,68 @@ export default {
}, },
}; };
}, },
autoSelectTimeSlotIfOnlyOneIsAvailable(newAvailableTimeSlotsValue) { getSelectedRouteCode() {
const numberOfOptions = newAvailableTimeSlotsValue?.length; 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) { if (numberOfOptions === 1) {
this.selectedTimeSlotId = newAvailableTimeSlotsValue[0].value; this.selectedRouteCode = this.availableTimeSlots[0].value;
} }
}, },
addPremiumFlagToInput(timeSlotId) { addPremiumFlagToInput(routeCode) {
return (timeSlotId += PREMIUM_TIME_SLOT_ID_FLAG); return (routeCode += PREMIUM_TIME_SLOT_ID_FLAG);
}, },
removePremiumFlagFromInput(timeSlotId) { removePremiumFlagFromInput(routeCode) {
return timeSlotId.substring(0, timeSlotId.length - PREMIUM_TIME_SLOT_ID_FLAG.length); return routeCode.replace(PREMIUM_TIME_SLOT_ID_FLAG, "");
}, },
getSelectedTimeSlotObject(timeSlotId) { getSelectedTimeSlotInfoObject(routeCode) {
const timeSlot = this.dateAndTimeSlotData?.timeSlots?.find( const routeCodeIncludesPremium = routeCode?.includes(PREMIUM_TIME_SLOT_ID_FLAG);
(timeSlot) => timeSlot.id == timeSlotId if (routeCodeIncludesPremium) {
routeCode = this.removePremiumFlagFromInput(routeCode);
}
const timeSlot = this.timeSlotsForSelectedDate?.timeSlots?.find(
(timeSlot) => timeSlot.id == routeCode
); );
if (timeSlot) { if (timeSlot) {
return { return {
date: this.dateAndTimeSlotData.date, timeSlot: {
routeCode: timeSlot.id, date: this.timeSlotsForSelectedDate.date,
startTime: timeSlot.startTime, routeCode: timeSlot.id,
endTime: timeSlot.endTime, startTime: timeSlot.startTime,
jobMaxMinutes: this.estimatedServiceMinutesMaximum.toString(), endTime: timeSlot.endTime,
jobMinMinutes: this.estimatedServiceMinutesMinimum.toString(), jobMaxMinutes: this.estimatedServiceMinutesMaximum.toString(),
jobMinMinutes: this.estimatedServiceMinutesMinimum.toString(),
},
isPremiumAppointment: routeCodeIncludesPremium ? true : false,
}; };
} }
return { return {
date: null, timeSlot: {
startTime: null, date: null,
endTime: null, startTime: null,
routeCode: null, endTime: null,
jobMaxMinutes: null, routeCode: null,
jobMinMinutes: null, jobMaxMinutes: null,
jobMinMinutes: null,
},
isPremiumAppointment: null,
}; };
}, },
}, },
@ -473,12 +501,10 @@ export default {
}, },
selectedDate: { selectedDate: {
handler() { handler() {
this.selectedTimeSlotId = null; this.selectedRouteCode = null;
this.autoSelectTimeSlotIfOnlyOneIsAvailable();
}, },
}, },
availableTimeSlots(newValue) {
this.autoSelectTimeSlotIfOnlyOneIsAvailable(newValue);
},
}, },
components: { components: {
modal, modal,