CASH-2862 - Scheduling - Adjust grouping
This commit is contained in:
parent
b81972f401
commit
58da49e8f3
6 changed files with 183 additions and 109 deletions
|
|
@ -40,6 +40,7 @@ const MOCK_PROVIDER = {
|
||||||
|
|
||||||
const MOCK_CMS_CONTENT = {
|
const MOCK_CMS_CONTENT = {
|
||||||
InshopCardWidget: {
|
InshopCardWidget: {
|
||||||
|
HeaderText: "Visit us at a shop:",
|
||||||
BodyText: "Morning:",
|
BodyText: "Morning:",
|
||||||
BodyText2: "Afternoon:",
|
BodyText2: "Afternoon:",
|
||||||
FooterText: "View more times",
|
FooterText: "View more times",
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,6 @@
|
||||||
class="inshop-scheduling-card__header"
|
class="inshop-scheduling-card__header"
|
||||||
:aria-expanded="isExpanded"
|
:aria-expanded="isExpanded"
|
||||||
@click="toggleExpanded">
|
@click="toggleExpanded">
|
||||||
<img
|
|
||||||
class="inshop-scheduling-card__shop-icon"
|
|
||||||
src="@/assets/img/shop.svg"
|
|
||||||
alt=""
|
|
||||||
aria-hidden="true" />
|
|
||||||
<span class="inshop-scheduling-card__header-title">{{ cityLabel }}</span>
|
<span class="inshop-scheduling-card__header-title">{{ cityLabel }}</span>
|
||||||
<span class="inshop-scheduling-card__distance">{{ formattedDistance }}</span>
|
<span class="inshop-scheduling-card__distance">{{ formattedDistance }}</span>
|
||||||
<span
|
<span
|
||||||
|
|
@ -356,12 +351,6 @@ export default {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__shop-icon {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__header-title {
|
&__header-title {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,11 @@ const MOCK_TIME_SLOTS = [
|
||||||
|
|
||||||
const MOCK_CMS_CONTENT = {
|
const MOCK_CMS_CONTENT = {
|
||||||
MobileCardWidget: {
|
MobileCardWidget: {
|
||||||
HeaderText: "We'll come to you",
|
HeaderText: "We'll come to you (for free!):",
|
||||||
SubheaderText: "We'll come to you for free!",
|
SubheaderText: "At your location",
|
||||||
BodyText: "Enter address on the next page",
|
BodyText: "Enter address on the next page",
|
||||||
FooterText: "First stop of the day",
|
FooterText: "First stop of the day",
|
||||||
FooterText2: "8:00 am - 12:00 pm arrival window",
|
FooterText2: "8:00 am - 12:00 pm arrival",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -66,13 +66,16 @@ function mountComponent(props = {}, cmsContent = {}) {
|
||||||
describe("mobile-scheduling-card.vue", () => {
|
describe("mobile-scheduling-card.vue", () => {
|
||||||
test("renders header, zip code, and time slot options", () => {
|
test("renders header, zip code, and time slot options", () => {
|
||||||
const wrapper = mountComponent();
|
const wrapper = mountComponent();
|
||||||
expect(wrapper.text()).toContain("We'll come to you");
|
expect(wrapper.text()).toContain("At your location");
|
||||||
expect(wrapper.text()).toContain("43235");
|
expect(wrapper.text()).toContain("43235");
|
||||||
expect(wrapper.text()).toContain("Enter address on the next page");
|
expect(wrapper.text()).toContain("Enter address on the next page");
|
||||||
expect(wrapper.text()).toContain("First stop of the day");
|
expect(wrapper.text()).toContain("First stop of the day");
|
||||||
expect(wrapper.text()).not.toContain("8:00 am - 12:00 pm arrival window");
|
expect(wrapper.text()).toContain("8:00 am - 12:00 pm arrival");
|
||||||
|
expect(wrapper.text()).toContain("12:00 pm - 5:00 pm arrival");
|
||||||
expect(wrapper.text()).toContain("+$29.99");
|
expect(wrapper.text()).toContain("+$29.99");
|
||||||
expect(wrapper.text()).toContain("3 appts");
|
expect(wrapper.text()).toContain("3 appts");
|
||||||
|
expect(wrapper.find(".mobile-scheduling-card__free-flag").exists()).toBe(false);
|
||||||
|
expect(wrapper.find(".mobile-scheduling-card__van-icon").exists()).toBe(false);
|
||||||
wrapper.unmount();
|
wrapper.unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -83,12 +86,6 @@ describe("mobile-scheduling-card.vue", () => {
|
||||||
wrapper.unmount();
|
wrapper.unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("shows free flag when showFreeFlag is true", () => {
|
|
||||||
const wrapper = mountComponent({ showFreeFlag: true });
|
|
||||||
expect(wrapper.text()).toContain("We'll come to you for free!");
|
|
||||||
wrapper.unmount();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("emits zip-code-clicked when zip link is clicked", async () => {
|
test("emits zip-code-clicked when zip link is clicked", async () => {
|
||||||
const wrapper = mountComponent();
|
const wrapper = mountComponent();
|
||||||
await wrapper.find(".mobile-scheduling-card__zip-link").trigger("click");
|
await wrapper.find(".mobile-scheduling-card__zip-link").trigger("click");
|
||||||
|
|
@ -113,7 +110,7 @@ describe("mobile-scheduling-card.vue", () => {
|
||||||
|
|
||||||
test("shows premium sublabel only when the premium option is selected", async () => {
|
test("shows premium sublabel only when the premium option is selected", async () => {
|
||||||
const wrapper = mountComponent();
|
const wrapper = mountComponent();
|
||||||
expect(wrapper.text()).not.toContain("8:00 am - 12:00 pm arrival window");
|
expect(wrapper.find(".mobile-scheduling-card__slot-label-secondary").exists()).toBe(false);
|
||||||
|
|
||||||
const premiumInput = wrapper.find(
|
const premiumInput = wrapper.find(
|
||||||
`input[value="${MOCK_TIME_SLOTS[0].id}${PREMIUM_TIME_SLOT_ID_FLAG}"]`
|
`input[value="${MOCK_TIME_SLOTS[0].id}${PREMIUM_TIME_SLOT_ID_FLAG}"]`
|
||||||
|
|
@ -128,7 +125,9 @@ describe("mobile-scheduling-card.vue", () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(wrapper.text()).toContain("8:00 am - 12:00 pm arrival window");
|
const subLabel = wrapper.find(".mobile-scheduling-card__slot-label-secondary");
|
||||||
|
expect(subLabel.exists()).toBe(true);
|
||||||
|
expect(subLabel.text()).toBe("8:00 am - 12:00 pm arrival");
|
||||||
wrapper.unmount();
|
wrapper.unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -183,12 +182,6 @@ describe("mobile-scheduling-card.vue", () => {
|
||||||
wrapper.unmount();
|
wrapper.unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("hides the free flag when isLoading is true even if showFreeFlag is true", () => {
|
|
||||||
const wrapper = mountComponent({ isLoading: true, showFreeFlag: true });
|
|
||||||
expect(wrapper.find(".mobile-scheduling-card__free-flag").exists()).toBe(false);
|
|
||||||
wrapper.unmount();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("defaults isLoading to false", () => {
|
test("defaults isLoading to false", () => {
|
||||||
const wrapper = mountComponent();
|
const wrapper = mountComponent();
|
||||||
expect(wrapper.props("isLoading")).toBe(false);
|
expect(wrapper.props("isLoading")).toBe(false);
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mobile-scheduling-card">
|
<div class="mobile-scheduling-card">
|
||||||
<div v-if="showFreeFlag && !isLoading" class="mobile-scheduling-card__free-flag">
|
|
||||||
<img
|
|
||||||
class="mobile-scheduling-card__free-flag-icon"
|
|
||||||
src="@/assets/img/party.svg"
|
|
||||||
alt=""
|
|
||||||
aria-hidden="true" />
|
|
||||||
<span class="mobile-scheduling-card__free-flag-text">{{ freeFlagText }}</span>
|
|
||||||
</div>
|
|
||||||
<div v-if="isLoading" class="mobile-scheduling-card__panel">
|
<div v-if="isLoading" class="mobile-scheduling-card__panel">
|
||||||
<schedulingCardLoader />
|
<schedulingCardLoader />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div v-else class="mobile-scheduling-card__panel">
|
||||||
v-else
|
|
||||||
class="mobile-scheduling-card__panel"
|
|
||||||
:class="{ 'mobile-scheduling-card__panel--with-free-flag': showFreeFlag }">
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="mobile-scheduling-card__header"
|
class="mobile-scheduling-card__header"
|
||||||
:aria-expanded="isExpanded"
|
:aria-expanded="isExpanded"
|
||||||
@click="toggleExpanded">
|
@click="toggleExpanded">
|
||||||
<img
|
|
||||||
class="mobile-scheduling-card__van-icon"
|
|
||||||
src="@/assets/img/van.svg"
|
|
||||||
alt=""
|
|
||||||
aria-hidden="true" />
|
|
||||||
<span class="mobile-scheduling-card__header-title">{{ headerTitle }}</span>
|
<span class="mobile-scheduling-card__header-title">{{ headerTitle }}</span>
|
||||||
<span
|
<span
|
||||||
class="mobile-scheduling-card__appt-badge"
|
class="mobile-scheduling-card__appt-badge"
|
||||||
|
|
@ -120,10 +104,6 @@ export default {
|
||||||
type: String,
|
type: String,
|
||||||
default: "schedulingTimeSlot",
|
default: "schedulingTimeSlot",
|
||||||
},
|
},
|
||||||
showFreeFlag: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
cmsWidgetName: {
|
cmsWidgetName: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "MobileCardWidget",
|
default: "MobileCardWidget",
|
||||||
|
|
@ -142,9 +122,6 @@ export default {
|
||||||
headerTitle() {
|
headerTitle() {
|
||||||
return this.getCmsContent(this.cmsWidgetName, "HeaderText");
|
return this.getCmsContent(this.cmsWidgetName, "HeaderText");
|
||||||
},
|
},
|
||||||
freeFlagText() {
|
|
||||||
return this.getCmsContent(this.cmsWidgetName, "SubheaderText");
|
|
||||||
},
|
|
||||||
addressHintText() {
|
addressHintText() {
|
||||||
return this.getCmsContent(this.cmsWidgetName, "BodyText");
|
return this.getCmsContent(this.cmsWidgetName, "BodyText");
|
||||||
},
|
},
|
||||||
|
|
@ -218,9 +195,9 @@ export default {
|
||||||
this.$emit("zip-code-clicked");
|
this.$emit("zip-code-clicked");
|
||||||
},
|
},
|
||||||
formatArrivalWindow(startTime, endTime) {
|
formatArrivalWindow(startTime, endTime) {
|
||||||
return `${militaryToTwelveHourTime(startTime)} - ${militaryToTwelveHourTime(
|
const start = militaryToTwelveHourTime(startTime)?.toLowerCase();
|
||||||
endTime
|
const end = militaryToTwelveHourTime(endTime)?.toLowerCase();
|
||||||
)} arrival window`;
|
return `${start} - ${end} arrival`;
|
||||||
},
|
},
|
||||||
isSlotSelected(slot) {
|
isSlotSelected(slot) {
|
||||||
return this.selectedRouteCodeValue === slot.value;
|
return this.selectedRouteCodeValue === slot.value;
|
||||||
|
|
@ -249,35 +226,12 @@ export default {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
|
||||||
&__free-flag {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 2px;
|
|
||||||
background: $green;
|
|
||||||
color: $green-600;
|
|
||||||
border-radius: 8px 8px 0 0;
|
|
||||||
padding: 2px 8px 2px 4px;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__free-flag-icon {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__panel {
|
&__panel {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: 1px solid $gray-1000;
|
border: 1px solid $gray-1000;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
background: $white;
|
background: $white;
|
||||||
|
|
||||||
&--with-free-flag {
|
|
||||||
border-radius: 0 16px 16px 16px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
|
|
@ -291,12 +245,6 @@ export default {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__van-icon {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__header-title {
|
&__header-title {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
|
|
@ -413,12 +361,12 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
&__slot-price {
|
&__slot-price {
|
||||||
background: $green;
|
background: $green-100;
|
||||||
color: $green-600;
|
color: $green-700;
|
||||||
border-radius: 72px;
|
border-radius: 72px;
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
font-weight: 400;
|
font-weight: 500;
|
||||||
line-height: 1.25rem;
|
line-height: 1.25rem;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|
|
||||||
|
|
@ -78,11 +78,13 @@ jest.mock(
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
function setupMocks({ isAppleBrowser = false } = {}) {
|
function setupMocks({ isAppleBrowser = false, cmsContent = {} } = {}) {
|
||||||
const dispatchStoreAction = jest.fn().mockResolvedValue(null);
|
const dispatchStoreAction = jest.fn().mockResolvedValue(null);
|
||||||
const baseMixin = {
|
const baseMixin = {
|
||||||
methods: {
|
methods: {
|
||||||
getCmsContent: jest.fn(() => ""),
|
getCmsContent: jest.fn((widgetName, fieldName) => {
|
||||||
|
return cmsContent[widgetName]?.[fieldName] ?? "";
|
||||||
|
}),
|
||||||
setCmsContent: jest.fn(),
|
setCmsContent: jest.fn(),
|
||||||
getTotalLineItemPrice: jest.fn(() => 0),
|
getTotalLineItemPrice: jest.fn(() => 0),
|
||||||
isAppleBrowser: jest.fn(() => isAppleBrowser),
|
isAppleBrowser: jest.fn(() => isAppleBrowser),
|
||||||
|
|
@ -128,6 +130,80 @@ function setupMocks({ isAppleBrowser = false } = {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("scheduling.vue", () => {
|
describe("scheduling.vue", () => {
|
||||||
|
describe("estimatedTimeText", () => {
|
||||||
|
test("replaces {custom:DURATION} with bracketed duration range", async () => {
|
||||||
|
const { wrapper } = setupMocks({
|
||||||
|
cmsContent: {
|
||||||
|
FunnelSubHeaderWidget: {
|
||||||
|
HeaderSubText: "Estimated service time: {custom:DURATION}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await wrapper.setData({
|
||||||
|
estimatedServiceMinutesMinimum: 60,
|
||||||
|
estimatedServiceMinutesMaximum: 120,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.vm.estimatedTimeText).toBe("Estimated service time: [1-2] hours");
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("section headers", () => {
|
||||||
|
test("renders mobile and inshop section headers from CMS", async () => {
|
||||||
|
const { wrapper } = setupMocks({
|
||||||
|
cmsContent: {
|
||||||
|
MobileCardWidget: {
|
||||||
|
HeaderText: "We'll come to you (for free!):",
|
||||||
|
},
|
||||||
|
InshopCardWidget: {
|
||||||
|
HeaderText: "Visit us at a shop:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await wrapper.setData({
|
||||||
|
isLoadingDates: false,
|
||||||
|
selectedDate: "2026-01-21",
|
||||||
|
mobileProviderAndTimeSlot: { providerNumber: "03357", timeSlots: { days: [] } },
|
||||||
|
inshopProvidersAndTimeSlots: [
|
||||||
|
{
|
||||||
|
provider: { providerNumber: "001820", address: { city: "Worthington" } },
|
||||||
|
timeSlots: { days: [] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const headers = wrapper.findAll(".scheduling-section-header");
|
||||||
|
expect(headers).toHaveLength(2);
|
||||||
|
expect(headers[0].text()).toContain("We'll come to you");
|
||||||
|
expect(headers[0].find(".scheduling-section-header__icon").exists()).toBe(true);
|
||||||
|
expect(headers[0].find(".scheduling-section-header__free").text()).toBe("(for free!):");
|
||||||
|
expect(headers[1].text()).toContain("Visit us at a shop:");
|
||||||
|
expect(headers[1].find(".scheduling-section-header__icon").exists()).toBe(true);
|
||||||
|
expect(wrapper.find("mobile-scheduling-card-stub").exists()).toBe(true);
|
||||||
|
expect(wrapper.find("inshop-scheduling-card-stub").exists()).toBe(true);
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("hides section headers when CMS content is empty", async () => {
|
||||||
|
const { wrapper } = setupMocks();
|
||||||
|
await wrapper.setData({
|
||||||
|
isLoadingDates: false,
|
||||||
|
selectedDate: "2026-01-21",
|
||||||
|
mobileProviderAndTimeSlot: { providerNumber: "03357", timeSlots: { days: [] } },
|
||||||
|
inshopProvidersAndTimeSlots: [
|
||||||
|
{
|
||||||
|
provider: { providerNumber: "001820", address: { city: "Worthington" } },
|
||||||
|
timeSlots: { days: [] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.find(".scheduling-section-header").exists()).toBe(false);
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("intercept overlay", () => {
|
describe("intercept overlay", () => {
|
||||||
test("does not render interceptOverlay when isLoadingDates is false", async () => {
|
test("does not render interceptOverlay when isLoadingDates is false", async () => {
|
||||||
const { wrapper } = setupMocks();
|
const { wrapper } = setupMocks();
|
||||||
|
|
|
||||||
|
|
@ -26,23 +26,53 @@
|
||||||
@requestMoreDates="handleRequestMoreDates" />
|
@requestMoreDates="handleRequestMoreDates" />
|
||||||
<Transition name="card-slide" mode="out-in">
|
<Transition name="card-slide" mode="out-in">
|
||||||
<div :key="selectedDate">
|
<div :key="selectedDate">
|
||||||
|
<p
|
||||||
|
v-if="showMobileSchedulingCard && mobileSectionHeaderMainText"
|
||||||
|
class="scheduling-section-header scheduling-section-header--mobile mt-5">
|
||||||
|
<img
|
||||||
|
class="scheduling-section-header__icon"
|
||||||
|
src="@/assets/img/van.svg"
|
||||||
|
alt=""
|
||||||
|
aria-hidden="true" />
|
||||||
|
<span class="scheduling-section-header__text">
|
||||||
|
{{ mobileSectionHeaderMainText }}
|
||||||
|
<span
|
||||||
|
v-if="mobileSectionHeaderFreeText"
|
||||||
|
class="scheduling-section-header__free">
|
||||||
|
{{ mobileSectionHeaderFreeText }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
<mobileSchedulingCard
|
<mobileSchedulingCard
|
||||||
v-if="showMobileSchedulingCard"
|
v-if="showMobileSchedulingCard"
|
||||||
class="mt-4"
|
class="mt-2"
|
||||||
v-model="selectedScheduling"
|
v-model="selectedScheduling"
|
||||||
:providerNumber="mobileProviderAndTimeSlot.providerNumber"
|
:providerNumber="mobileProviderAndTimeSlot.providerNumber"
|
||||||
:timeSlots="mobileTimeSlotsForSelectedDate"
|
:timeSlots="mobileTimeSlotsForSelectedDate"
|
||||||
:premiumTimeSlotPrice="premiumTimeSlotPrice"
|
:premiumTimeSlotPrice="premiumTimeSlotPrice"
|
||||||
:zipCode="serviceZipCode"
|
:zipCode="serviceZipCode"
|
||||||
:showFreeFlag="showMobileFreeFlag"
|
|
||||||
:radioGroupName="schedulingRadioGroupName"
|
:radioGroupName="schedulingRadioGroupName"
|
||||||
:isLoading="isLoadingDates"
|
:isLoading="isLoadingDates"
|
||||||
@zip-code-clicked="onMobileZipCodeClicked" />
|
@zip-code-clicked="onMobileZipCodeClicked" />
|
||||||
|
<p
|
||||||
|
v-if="
|
||||||
|
showInshopSchedulingCards &&
|
||||||
|
inshopSectionHeaderText &&
|
||||||
|
inshopProvidersAndTimeSlots.length
|
||||||
|
"
|
||||||
|
class="scheduling-section-header scheduling-section-header--inshop mt-5">
|
||||||
|
<img
|
||||||
|
class="scheduling-section-header__icon"
|
||||||
|
src="@/assets/img/shop.svg"
|
||||||
|
alt=""
|
||||||
|
aria-hidden="true" />
|
||||||
|
<span>{{ inshopSectionHeaderText }}</span>
|
||||||
|
</p>
|
||||||
<inshopSchedulingCard
|
<inshopSchedulingCard
|
||||||
v-for="entry in inshopProvidersAndTimeSlots"
|
v-for="(entry, index) in inshopProvidersAndTimeSlots"
|
||||||
v-show="showInshopSchedulingCards"
|
v-show="showInshopSchedulingCards"
|
||||||
:key="entry.provider.providerNumber"
|
:key="entry.provider.providerNumber"
|
||||||
class="mt-4"
|
:class="index === 0 ? 'mt-2' : 'mt-4'"
|
||||||
v-model="selectedScheduling"
|
v-model="selectedScheduling"
|
||||||
:provider="entry.provider"
|
:provider="entry.provider"
|
||||||
:timeSlots="
|
:timeSlots="
|
||||||
|
|
@ -362,8 +392,27 @@ export default {
|
||||||
schedulingRadioGroupName() {
|
schedulingRadioGroupName() {
|
||||||
return SCHEDULING_RADIO_GROUP_NAME;
|
return SCHEDULING_RADIO_GROUP_NAME;
|
||||||
},
|
},
|
||||||
serviceLocationText() {
|
mobileSectionHeaderText() {
|
||||||
return this.getCmsContent("ServiceLocationText", "Text");
|
return this.getCmsContent("MobileCardWidget", "SubheaderText");
|
||||||
|
},
|
||||||
|
mobileSectionHeaderMainText() {
|
||||||
|
const text = this.mobileSectionHeaderText?.trim();
|
||||||
|
if (!text) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const freeTextIndex = text.indexOf("(");
|
||||||
|
return freeTextIndex >= 0 ? text.slice(0, freeTextIndex).trimEnd() : text;
|
||||||
|
},
|
||||||
|
mobileSectionHeaderFreeText() {
|
||||||
|
const text = this.mobileSectionHeaderText?.trim();
|
||||||
|
if (!text) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const freeTextIndex = text.indexOf("(");
|
||||||
|
return freeTextIndex >= 0 ? text.slice(freeTextIndex) : "";
|
||||||
|
},
|
||||||
|
inshopSectionHeaderText() {
|
||||||
|
return this.getCmsContent("InshopCardWidget", "HeaderText");
|
||||||
},
|
},
|
||||||
viewMoreShopsText() {
|
viewMoreShopsText() {
|
||||||
return this.getCmsContent("ViewMoreShopsWidget", "Text");
|
return this.getCmsContent("ViewMoreShopsWidget", "Text");
|
||||||
|
|
@ -372,13 +421,16 @@ export default {
|
||||||
if (!this.estimatedServiceMinutesMinimum || !this.estimatedServiceMinutesMaximum) {
|
if (!this.estimatedServiceMinutesMinimum || !this.estimatedServiceMinutesMaximum) {
|
||||||
return " ";
|
return " ";
|
||||||
}
|
}
|
||||||
const durationText = getDisplayTextForDurationLength(
|
// "1 - 2 hours" → "[1-2] hours"
|
||||||
|
const formattedDuration = getDisplayTextForDurationLength(
|
||||||
this.estimatedServiceMinutesMinimum,
|
this.estimatedServiceMinutesMinimum,
|
||||||
this.estimatedServiceMinutesMaximum
|
this.estimatedServiceMinutesMaximum
|
||||||
);
|
)
|
||||||
return this.getCmsContent("FunnelSubHeaderWidget", "HeaderSubText").replace(
|
.replace(" - ", "-")
|
||||||
|
.replace(/(\S+) (hours|minutes)/i, "[$1] $2");
|
||||||
|
return (this.getCmsContent("FunnelSubHeaderWidget", "HeaderSubText") || "").replace(
|
||||||
"{custom:DURATION}",
|
"{custom:DURATION}",
|
||||||
durationText
|
formattedDuration
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
serviceZipCode() {
|
serviceZipCode() {
|
||||||
|
|
@ -407,9 +459,6 @@ export default {
|
||||||
}
|
}
|
||||||
return this.getTotalLineItemPrice(this.mobilePremiumAppointmentFee);
|
return this.getTotalLineItemPrice(this.mobilePremiumAppointmentFee);
|
||||||
},
|
},
|
||||||
showMobileFreeFlag() {
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
availableDates() {
|
availableDates() {
|
||||||
if (!this.datesLoaded) return null;
|
if (!this.datesLoaded) return null;
|
||||||
const inshopDates = this.inshopProvidersAndTimeSlots.flatMap(({ timeSlots }) =>
|
const inshopDates = this.inshopProvidersAndTimeSlots.flatMap(({ timeSlots }) =>
|
||||||
|
|
@ -1102,12 +1151,30 @@ export default {
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.dark-header {
|
.scheduling-section-header {
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.5rem;
|
||||||
color: $black;
|
color: $black;
|
||||||
}
|
|
||||||
h5 {
|
&--mobile,
|
||||||
line-height: 32px;
|
&--inshop {
|
||||||
font-size: 1.25rem;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__free {
|
||||||
|
font-weight: 400;
|
||||||
|
color: $gray-600;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-slide-enter-active {
|
.card-slide-enter-active {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue