Styling updates and multi location time slot retrieval

Added back and forward arrow SVGs
Now we call for all timeslots of three closest stores and mobile if a provider is available
This commit is contained in:
scottkiener-at-safelite 2026-05-06 10:23:01 -04:00
parent 5ec56b11d6
commit 9dbcf1c93d
3 changed files with 96 additions and 58 deletions

View file

@ -1,32 +1,32 @@
<template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm">
<loadingModal notFullScreen ref="loadingModal" />
<funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
<div class="container page-container-grouped-styles">
<div class="row">
<div class="col-12">
<h5 class="fw-normal mb-0 dark-header" :class="headerColor">
<span>
{{ serviceLocationText }}
</span>
</h5>
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" alignLeft />
<datePicker
class="mt-5"
v-model="selectedDate"
:startDate="datePickerStartDate"
:endDate="datePickerEndDate"
:availableDates="availableDates"
:isLoadingDates="isLoadingDates"
@requestMoreDates="handleRequestMoreDates" />
<navbar
cmsWidgetName="FunnelFooterWidget"
ref="navbar"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" />
<loadingModal notFullScreen ref="loadingModal" />
<funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
<div class="container page-container-grouped-styles">
<div class="row">
<div class="col-12">
<h5 class="fw-normal mb-0 dark-header" :class="headerColor">
<span>
{{ serviceLocationText }}
</span>
</h5>
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" alignLeft />
<datePicker
class="mt-5"
v-model="selectedDate"
:startDate="datePickerStartDate"
:endDate="datePickerEndDate"
:availableDates="availableDates"
:isLoadingDates="isLoadingDates"
@requestMoreDates="handleRequestMoreDates" />
<navbar
cmsWidgetName="FunnelFooterWidget"
ref="navbar"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" />
</div>
</div>
</div>
</div>
</Form>
</template>
@ -65,32 +65,48 @@ export default {
payload: { serviceZipCode },
pageNameToLog: to.name,
});
const inShopProviderNumber = shopProviderData?.data?.shopProviders[0]?.providerNumber;
const mobileProviderNumber = shopProviderData?.data?.mobileProviderNumber;
const providers = shopProviderData?.data?.shopProviders?.slice(0, 3) ?? [];
const mobileProviderNumber = shopProviderData?.data?.mobileProviderNumber ?? null;
const startDate = toDateString(0);
const endDate = toDateString(14);
const promiseResultMap = [
{
resultKey: "cmsContent",
promise: fetchCmsContentForPage(to.name),
},
{
resultKey: "inshopOrDropoffTimeSlots",
...providers.map((p, i) => ({
resultKey: `inshopTimeSlots_${i}`,
promise: store.dispatch("getShopTimeSlots", {
payload: {
startDate: toDateString(0),
endDate: toDateString(15),
startDate,
endDate,
shopAppointmentType: "InshopOrDropoff",
providerNumber: inShopProviderNumber,
providerNumber: p.providerNumber,
},
pageNameToLog: to.name,
}),
},
})),
...(mobileProviderNumber
? [
{
resultKey: "mobileTimeSlots",
promise: store.dispatch("getMobileTimeSlots", {
payload: { startDate, endDate, zipCode: serviceZipCode },
pageNameToLog: to.name,
}),
},
]
: []),
];
const resultMap = await settleAllPromises(promiseResultMap);
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
vm.inshopOrDropoffTimeSlots = resultMap.inshopOrDropoffTimeSlots;
vm.inShopProviderNumber = inShopProviderNumber;
vm.inShopProviders = providers;
vm.inshopTimeSlots = providers.map((_, i) => resultMap[`inshopTimeSlots_${i}`] ?? null);
vm.mobileTimeSlots = resultMap.mobileTimeSlots ?? null;
vm.inShopProviderNumber = providers[0]?.providerNumber ?? null;
vm.mobileProviderNumber = mobileProviderNumber;
});
},
@ -99,7 +115,11 @@ export default {
return this.getCmsContent("ServiceLocationText", "Text");
},
availableDates() {
return (this.inshopOrDropoffTimeSlots?.days ?? []).map((d) => d.date);
const inshopDates = this.inshopTimeSlots.flatMap((ts) =>
(ts?.days ?? []).map((d) => d.date)
);
const mobileDates = (this.mobileTimeSlots?.days ?? []).map((d) => d.date);
return [...new Set([...inshopDates, ...mobileDates])].sort();
},
},
data() {
@ -107,8 +127,10 @@ export default {
selectedDate: null,
isLoadingDates: false,
datePickerStartDate: toDateString(0),
datePickerEndDate: toDateString(15),
inshopOrDropoffTimeSlots: null,
datePickerEndDate: toDateString(14),
inShopProviders: [],
inshopTimeSlots: [],
mobileTimeSlots: null,
inShopProviderNumber: null,
mobileProviderNumber: null,
};
@ -166,6 +188,7 @@ export default {
// TODO: call getShopTimeSlots with the next date range and append results
// to this.availableDates and advance this.datePickerEndDate
console.log("handleRequestMoreDates");
this.isLoadingDates = false;
this.$refs.loadingModal.hideModal();
@ -196,7 +219,7 @@ export default {
};
</script>
<style lang="scss" scoped>
.dark-header {
color: $black;
}
</style>
.dark-header {
color: $black;
}
</style>

View file

@ -152,12 +152,7 @@ describe("date-picker.vue", () => {
describe("navigation", () => {
test("goForward advances windowStart by the window size", async () => {
// Use a longer range so forward is possible on desktop (window 5)
const manyDates = [
"2026-01-01",
"2026-01-02",
"2026-01-03",
"2026-01-10",
];
const manyDates = ["2026-01-01", "2026-01-02", "2026-01-03", "2026-01-10"];
const wrapper = mountDesktop({ availableDates: manyDates });
expect(wrapper.vm.canGoForward).toBe(true);
await wrapper.vm.goForward();

View file

@ -5,7 +5,12 @@
:disabled="!canGoBack"
aria-label="Previous dates"
@click="goBack">
<svg v-if="canGoBack" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform: rotate(180deg)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M-6.24658e-06 12C-6.55781e-06 14.3734 0.703782 16.6935 2.02236 18.6668C3.34094 20.6402 5.21508 22.1783 7.40779 23.0866C9.60051 23.9948 12.0133 24.2324 14.3411 23.7694C16.6688 23.3064 18.807 22.1635 20.4853 20.4853C22.1635 18.8071 23.3064 16.6689 23.7694 14.3411C24.2324 12.0133 23.9948 9.60051 23.0865 7.4078C22.1783 5.21509 20.6402 3.34094 18.6668 2.02237C16.6934 0.703788 14.3734 -2.13306e-07 12 -5.24537e-07C8.8174 -9.41884e-07 5.76515 1.26428 3.51472 3.51472C1.26428 5.76515 -5.82924e-06 8.8174 -6.24658e-06 12ZM11.28 5.8872L16.8 11.4072C16.9568 11.5646 17.0449 11.7778 17.0449 12C17.0449 12.2222 16.9568 12.4354 16.8 12.5928L11.28 18.1128C11.1228 18.2719 10.9088 18.3621 10.6851 18.3634C10.4614 18.3648 10.2463 18.2772 10.0872 18.12C9.92806 17.9628 9.83791 17.7488 9.83656 17.5251C9.83521 17.3014 9.92277 17.0863 10.08 16.9272L15.012 12L10.08 7.0728C9.92278 6.91367 9.83521 6.6986 9.83656 6.47491C9.83791 6.25122 9.92807 6.03722 10.0872 5.88C10.2463 5.72278 10.4614 5.63521 10.6851 5.63656C10.9088 5.63791 11.1228 5.72807 11.28 5.8872Z" fill="#0070D1"/>
</svg>
<svg v-else width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M24 12C24 9.62663 23.2962 7.30655 21.9776 5.33316C20.6591 3.35977 18.7849 1.8217 16.5922 0.913447C14.3995 0.00519388 11.9867 -0.232445 9.65892 0.230578C7.33115 0.6936 5.19295 1.83649 3.51472 3.51472C1.83649 5.19295 0.693606 7.33114 0.230583 9.65891C-0.23244 11.9867 0.00519859 14.3995 0.91345 16.5922C1.8217 18.7849 3.35977 20.6591 5.33316 21.9776C7.30655 23.2962 9.62663 24 12 24C15.1826 24 18.2348 22.7357 20.4853 20.4853C22.7357 18.2348 24 15.1826 24 12ZM12.72 18.1128L7.2 12.5928C7.04319 12.4354 6.95514 12.2222 6.95514 12C6.95514 11.7778 7.04319 11.5646 7.2 11.4072L12.72 5.8872C12.8772 5.72807 13.0912 5.63791 13.3149 5.63656C13.5386 5.63521 13.7537 5.72278 13.9128 5.88C14.0719 6.03722 14.1621 6.25122 14.1634 6.47491C14.1648 6.6986 14.0772 6.91367 13.92 7.0728L8.988 12L13.92 16.9272C14.0772 17.0863 14.1648 17.3014 14.1634 17.5251C14.1621 17.7488 14.0719 17.9628 13.9128 18.12C13.7537 18.2772 13.5386 18.3648 13.3149 18.3634C13.0912 18.3621 12.8772 18.2719 12.72 18.1128Z" fill="#B3B4B5"/>
</svg>
</button>
<div class="date-picker__track d-flex flex-grow-1">
<button
@ -29,14 +34,32 @@
:disabled="!canGoForward"
aria-label="Next dates"
@click="goForward">
<svg v-if="canGoForward" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M-6.24658e-06 12C-6.55781e-06 14.3734 0.703782 16.6935 2.02236 18.6668C3.34094 20.6402 5.21508 22.1783 7.40779 23.0866C9.60051 23.9948 12.0133 24.2324 14.3411 23.7694C16.6688 23.3064 18.807 22.1635 20.4853 20.4853C22.1635 18.8071 23.3064 16.6689 23.7694 14.3411C24.2324 12.0133 23.9948 9.60051 23.0865 7.4078C22.1783 5.21509 20.6402 3.34094 18.6668 2.02237C16.6934 0.703788 14.3734 -2.13306e-07 12 -5.24537e-07C8.8174 -9.41884e-07 5.76515 1.26428 3.51472 3.51472C1.26428 5.76515 -5.82924e-06 8.8174 -6.24658e-06 12ZM11.28 5.8872L16.8 11.4072C16.9568 11.5646 17.0449 11.7778 17.0449 12C17.0449 12.2222 16.9568 12.4354 16.8 12.5928L11.28 18.1128C11.1228 18.2719 10.9088 18.3621 10.6851 18.3634C10.4614 18.3648 10.2463 18.2772 10.0872 18.12C9.92806 17.9628 9.83791 17.7488 9.83656 17.5251C9.83521 17.3014 9.92277 17.0863 10.08 16.9272L15.012 12L10.08 7.0728C9.92278 6.91367 9.83521 6.6986 9.83656 6.47491C9.83791 6.25122 9.92807 6.03722 10.0872 5.88C10.2463 5.72278 10.4614 5.63521 10.6851 5.63656C10.9088 5.63791 11.1228 5.72807 11.28 5.8872Z" fill="#0070D1"/>
</svg>
<svg v-else width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform: rotate(180deg)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M24 12C24 9.62663 23.2962 7.30655 21.9776 5.33316C20.6591 3.35977 18.7849 1.8217 16.5922 0.913447C14.3995 0.00519388 11.9867 -0.232445 9.65892 0.230578C7.33115 0.6936 5.19295 1.83649 3.51472 3.51472C1.83649 5.19295 0.693606 7.33114 0.230583 9.65891C-0.23244 11.9867 0.00519859 14.3995 0.91345 16.5922C1.8217 18.7849 3.35977 20.6591 5.33316 21.9776C7.30655 23.2962 9.62663 24 12 24C15.1826 24 18.2348 22.7357 20.4853 20.4853C22.7357 18.2348 24 15.1826 24 12ZM12.72 18.1128L7.2 12.5928C7.04319 12.4354 6.95514 12.2222 6.95514 12C6.95514 11.7778 7.04319 11.5646 7.2 11.4072L12.72 5.8872C12.8772 5.72807 13.0912 5.63791 13.3149 5.63656C13.5386 5.63521 13.7537 5.72278 13.9128 5.88C14.0719 6.03722 14.1621 6.25122 14.1634 6.47491C14.1648 6.6986 14.0772 6.91367 13.92 7.0728L8.988 12L13.92 16.9272C14.0772 17.0863 14.1648 17.3014 14.1634 17.5251C14.1621 17.7488 14.0719 17.9628 13.9128 18.12C13.7537 18.2772 13.5386 18.3648 13.3149 18.3634C13.0912 18.3621 12.8772 18.2719 12.72 18.1128Z" fill="#B3B4B5"/>
</svg>
</button>
</div>
</template>
<script>
const DAY_ABBRS = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
const MONTH_ABBRS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
const MONTH_ABBRS = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
];
const MOBILE_WINDOW_SIZE = 3;
const DESKTOP_WINDOW_SIZE = 5;
const DESKTOP_BREAKPOINT_PX = 768;
@ -138,9 +161,9 @@ export default {
this.updateWindowSize();
window.addEventListener("resize", this.updateWindowSize);
if (this.modelValue && this.allDates.length) {
const idx = this.allDates.findIndex((d) => d.value === this.modelValue);
if (idx !== -1) {
this.windowStart = Math.floor(idx / this.windowSize) * this.windowSize;
const index = this.allDates.findIndex((d) => d.value === this.modelValue);
if (index !== -1) {
this.windowStart = Math.floor(index / this.windowSize) * this.windowSize;
}
}
},
@ -159,6 +182,7 @@ export default {
},
goForward() {
if (this.isLoadingDates) return;
console.log("goForward", this.windowStart, this.windowSize, this.allDates.length);
const isAtEnd = this.windowStart + this.windowSize >= this.allDates.length;
if (isAtEnd) {
this.$emit("requestMoreDates");
@ -188,9 +212,6 @@ export default {
height: 2rem;
border: none;
background: transparent;
font-size: 2rem;
line-height: 1;
color: $blue;
cursor: pointer;
display: flex;
align-items: center;
@ -204,7 +225,6 @@ export default {
}
&:disabled {
color: $gray-300;
cursor: default;
}
}