@@ -20,29 +19,35 @@
:availableDates="availableDates"
:isLoadingDates="isLoadingDates"
@requestMoreDates="handleRequestMoreDates" />
-
-
+
+
+
+
+
+
({
- resultKey: `inshopTimeSlots_${i}`,
- promise: fetchInshopTimeSlots({
- startDate,
- endDate,
- providerNumber: provider.providerNumber,
+ {
+ resultKey: "providers",
+ promise: store.dispatch("getProviders", {
+ payload: { serviceZipCode },
pageNameToLog: to.name,
}),
- })),
- // Get the mobile time slots if a mobile provider number is available
- ...(mobileProviderNumber
- ? [
- {
- resultKey: "mobileTimeSlots",
- promise: fetchMobileTimeSlots({
- startDate,
- endDate,
- zipCode: serviceZipCode,
- pageNameToLog: to.name,
- }),
- },
- ]
- : []),
+ },
];
const resultMap = await settleAllPromises(promiseResultMap);
- next((vm) => {
+ // Get the first 3 providers from the shopProviderData
+ const providers = resultMap.providers?.shopProviders?.slice(0, 3) ?? [];
+ const mobileProviderNumber = resultMap.providers?.mobileProviderNumber ?? null;
+ next(async (vm) => {
vm.setCmsContent(resultMap.cmsContent);
- vm.inShopProvidersAndTimeslots = providers.map((provider, i) => ({
+ vm.inShopProvidersAndTimeslots = providers.map((provider) => ({
provider,
- timeSlots: resultMap[`inshopTimeSlots_${i}`] ?? null,
+ timeSlots: null,
}));
vm.mobileProviderAndTimeSlot = mobileProviderNumber
- ? {
- providerNumber: mobileProviderNumber,
- timeSlots: resultMap.mobileTimeSlots ?? null,
- }
+ ? { providerNumber: mobileProviderNumber, timeSlots: null }
: null;
+ const startDate = toDateString(0);
+ const endDate = toDateString(SCHEDULE_FETCH_DAYS - 1);
+ const timeSlotsPromiseResultMap = [
+ ...providers.map((provider, i) => ({
+ resultKey: `inshopTimeSlots_${i}`,
+ promise: fetchInshopTimeSlots({
+ startDate,
+ endDate,
+ providerNumber: provider.providerNumber,
+ pageNameToLog: to.name,
+ }),
+ })),
+ // Get the mobile time slots if a mobile provider number is available
+ ...(mobileProviderNumber
+ ? [
+ {
+ resultKey: "mobileTimeSlots",
+ promise: fetchMobileTimeSlots({
+ startDate,
+ endDate,
+ zipCode: serviceZipCode,
+ pageNameToLog: to.name,
+ }),
+ },
+ ]
+ : []),
+ ];
+ const timeSlotsResultMap = await settleAllPromises(timeSlotsPromiseResultMap);
+ vm.inShopProvidersAndTimeslots.forEach((entry, i) => {
+ entry.timeSlots = timeSlotsResultMap[`inshopTimeSlots_${i}`] ?? null;
+ });
+ if (vm.mobileProviderAndTimeSlot) {
+ vm.mobileProviderAndTimeSlot.timeSlots = timeSlotsResultMap.mobileTimeSlots ?? null;
+ }
vm.datesLoaded = true;
vm.mobilePremiumAppointmentFee = resultMap.mobilePremiumFee ?? null;
+ vm.isLoadingDates = false;
});
},
watch: {
@@ -217,7 +229,12 @@ export default {
return store.getters.order.serviceLocation.zipCode;
},
showMobileSchedulingCard() {
- return Boolean(this.mobileProviderAndTimeSlot && this.selectedDate);
+ return Boolean(
+ this.mobileProviderAndTimeSlot && (this.selectedDate || this.isLoadingDates)
+ );
+ },
+ showInshopSchedulingCards() {
+ return this.selectedDate || this.isLoadingDates;
},
mobileTimeSlotsForSelectedDate() {
if (!this.selectedDate) {
@@ -251,7 +268,7 @@ export default {
data() {
return {
selectedDate: null,
- isLoadingDates: false,
+ isLoadingDates: true,
datesLoaded: false,
datePickerStartDate: toDateString(0),
datePickerEndDate: toDateString(SCHEDULE_FETCH_DAYS - 1),
@@ -378,7 +395,6 @@ export default {
datePicker,
mobileSchedulingCard,
inshopSchedulingCard,
- loadingModal,
interceptOverlay,
},
};
@@ -391,4 +407,20 @@ h5 {
line-height: 32px;
font-size: 1.25rem;
}
+
+.card-slide-enter-active {
+ transition:
+ opacity 0.3s ease-out,
+ transform 0.3s ease-out;
+}
+.card-slide-leave-active {
+ transition: opacity 0.32s ease-in;
+}
+.card-slide-enter-from {
+ opacity: 0;
+ transform: translateX(24px);
+}
+.card-slide-leave-to {
+ opacity: 0;
+}
diff --git a/src/ux-components/intercept-overlay/intercept-overlay.spec.js b/src/ux-components/intercept-overlay/intercept-overlay.spec.js
index f3854a47a..0a282b93d 100644
--- a/src/ux-components/intercept-overlay/intercept-overlay.spec.js
+++ b/src/ux-components/intercept-overlay/intercept-overlay.spec.js
@@ -14,4 +14,33 @@ describe("intercept-overlay.vue", () => {
expect(wrapper.text()).toBe("");
wrapper.unmount();
});
+
+ describe("keyboard interception", () => {
+ test("adds a capturing keydown listener on mount and removes it on unmount", () => {
+ const addSpy = jest.spyOn(document, "addEventListener");
+ const removeSpy = jest.spyOn(document, "removeEventListener");
+
+ const wrapper = shallowMount(interceptOverlay);
+ expect(addSpy).toHaveBeenCalledWith("keydown", expect.any(Function), true);
+
+ const [, handler] = addSpy.mock.calls.find(
+ ([type, , capture]) => type === "keydown" && capture === true
+ );
+
+ wrapper.unmount();
+ expect(removeSpy).toHaveBeenCalledWith("keydown", handler, true);
+
+ addSpy.mockRestore();
+ removeSpy.mockRestore();
+ });
+
+ test("blockKey prevents default and stops immediate propagation", () => {
+ const wrapper = shallowMount(interceptOverlay);
+ const event = { preventDefault: jest.fn(), stopImmediatePropagation: jest.fn() };
+ wrapper.vm.blockKey(event);
+ expect(event.preventDefault).toHaveBeenCalled();
+ expect(event.stopImmediatePropagation).toHaveBeenCalled();
+ wrapper.unmount();
+ });
+ });
});
diff --git a/src/ux-components/intercept-overlay/intercept-overlay.vue b/src/ux-components/intercept-overlay/intercept-overlay.vue
index 1a101589a..1269ccb9d 100644
--- a/src/ux-components/intercept-overlay/intercept-overlay.vue
+++ b/src/ux-components/intercept-overlay/intercept-overlay.vue
@@ -1,10 +1,22 @@
-
+