DigitalConsumer.ISS/src/digital-components/date-picker/date-picker.vue
Bill Richardson ff6dca7650 drop off updates for schedule page
Morning Drop off.
2026-02-13 11:10:33 -05:00

987 lines
37 KiB
Vue

<template>
<div
class="date-picker text-center"
:class="[calendarViewDirection, { 'has-error': errors.length > 0 }]">
<fieldset id="date-picker-fieldset">
<legend class="sr-only">
When would you like service?
</legend>
<div class="appointment-date-picker-header">
When would you like service?
</div>
<div class="appointment-estimate-header">
Your service will take approximately <span>{{ appointmentEstimate }}</span>
</div>
<div
class="appointment-date-picker-container"
:class="isMobileView ? 'mobile' : 'desktop'">
<div class="header-cell header-start">
<button
v-if="activePageIndex !== 0"
type="button"
class="span-block-flex"
@click="gotoPreviousPage()">
<img
class="arrow-icon"
src="~@/assets/img/icons/icon-ablue-previous.png"
alt="Left arrow icon" />
Previous
</button>
</div>
<div class="header-cell header-middle">
{{ headerDateString }}
</div>
<div class="header-cell header-end">
<button
type="button"
class="span-block-flex"
@click="gotoNextPage()">
More
<img
class="arrow-icon"
src="~@/assets/img/icons/icon-ablue-next.png"
alt="Right arrow icon" />
</button>
</div>
<div class="week-row-container date-picker-row">
<template
v-for="n in daysToAdd"
:key="n">
<div class="day-of-week-header">
<span class="span-block day-of-week-caption">{{ showDayOfWeekForIndex(n - 1) }}</span>
<span class="span-block month-day-caption">{{ showMonthAndDayForIndex(n - 1) }}</span>
</div>
</template>
</div>
<div class="morning-row-container date-picker-row">
<template
v-for="n in daysToAdd"
:key="n">
<div
tabindex="0"
class="morning-row-cell"
:class="[showMorningAvailabilityForIndex(n - 1) > 0 ? 'has-appointments' : 'no-appointments'
, checkIsSelectedDay(n - 1, 'morning') ? 'selected-date' : '']"
@click="showMorningTimes(n - 1)"
@keydown.enter="showMorningTimes(n - 1)">
<span class="span-block top-caption">Morning</span>
<span class="span-block bottom-caption">
{{ showMorningAvailabilityForIndex(n - 1) }} available
</span>
</div>
</template>
</div>
<div class="afternoon-row-container date-picker-row">
<template
v-for="n in daysToAdd"
:key="n">
<div
tabindex="0"
class="afternoon-row-cell"
:class="[showAfternoonAvailabilityForIndex(n - 1) > 0 ? 'has-appointments' : 'no-appointments'
, checkIsSelectedDay(n - 1, 'afternoon') ? 'selected-date' : '']"
@click="showAfternoonTimes(n - 1)"
@keydown.enter="showAfternoonTimes(n - 1)">
<span class="span-block top-caption">Afternoon</span>
<span class="span-block bottom-caption">
{{ showAfternoonAvailabilityForIndex(n - 1) }} available
</span>
</div>
</template>
</div>
</div>
<div
v-if="selectedDate"
class="appointment-time-picker-container"
:class="isMobileView ? 'mobile' : 'desktop'">
<div class="appointment-time-picker-header">
Available times:
</div>
<template
v-for="timeSlot in selectableTimeSlotsData"
:key="`${timeSlot.startTime}-${timeSlot.isDropoff}`">
<input
:id="`timeslot${timeSlot.startTime}-${timeSlot.isDropoff}`"
v-model="selectedTime"
type="radio"
name="time-slot"
class="sr-only"
:value="timeSlot.startTime"
@focus="timeSlotFocused(timeSlot)"
@change="timeSlotInputChanged(timeSlot)" />
<label
:for="`timeslot${timeSlot.startTime}`"
class="time-slot-button"
:class="[checkIsSelectedTimeSlot(timeSlot) ? 'selected-time-slot' : ''
, {'has-date-error': showTimeSlotError}]"
@click="selectTimeSlotForDay(timeSlot)"
v-html="displayTimeSlotTime(timeSlot)">
</label>
<div
v-if="timeSlot.isDropoff"
class="time-slot-drop-off-information"
v-html="displayDropOffInformation(timeSlot)">
</div>
</template>
</div>
<div class="row form-test-error">
<div v-if="showTimeSlotError">
{{ displayErrorMessage }}
</div>
</div>
</fieldset>
</div>
</template>
<script>
// Supporting files
import { useMainStore } from '@/store';
import { useField } from 'vee-validate';
import { deepClone } from '@/helpers/object-helper';
import {
convertDateToDateString,
convertDateToShortMonth,
convertDateToTwoDigitDay,
convertDateToTwoDigitMonth,
getDisplayTextForDurationLength,
isAfternoon,
militaryToTwelveHourTime
} from '@/helpers/date-helper';
import {
AppointmentTypeStrings,
PREMIUM_TIME_SLOT_ID_FLAG
} from '@/constants/schedule-constants';
import showIssLoadingModal from '@/helpers/loading-modal-helper.js';
import errorMessages from '@/constants/error-messages.js';
import { selectableDaysOptions } from './mixins/helpers';
export default {
name: 'date-picker',
props: {
activeAppointmentType: {
type: String,
default: null
},
customComponentId: String,
selectableDatesSetting: {
type: String,
validator(value) {
return Object.values(selectableDaysOptions).includes(value);
},
default: selectableDaysOptions.PAST
},
isMobileView: {
type: Boolean,
default: false
},
modelValue: {
type: Object
},
todayOverrideDateString: {
// keep for use in unit tests to override today's date
type: String,
default: null
},
customSelectableDatesCallback: {
type: Function,
default() {
return [];
}
},
showTimeSlotError: {
type: Boolean,
default: false
},
validationRules: {
type: String,
default: ''
}
},
emits: ['dateSelected', 'timeSlotSelected'],
setup(props) {
const uuid = crypto.randomUUID();
const componentId = !props.customComponentId
? `component-${uuid}`
: props.customComponentId;
const mainStore = useMainStore();
const { modelValue } = deepClone(props);
const initialValue = modelValue;
const fieldOptions = {
value: modelValue,
initialValue
};
const {
errorMessage,
handleBlur,
handleChange,
meta,
validate,
errors,
resetField
} = useField(componentId, props.validationRules, fieldOptions);
return {
componentId,
mainStore,
errorMessage,
handleBlur,
handleChange,
validate,
meta,
errors,
resetField
};
},
data() {
return {
isLoading: true,
selectableDatesData: [], // NOTE: uses monthNum (1-based), NOT monthIndex (0-based)
selectableTimeSlotsData: [],
selectedTimeOfDayGrouping: null,
selectedDate: null,
selectedTime: null,
selectedTimeSlot: null,
activeDate: new Date(),
activePageIndex: 0,
todaysDate: new Date(),
mql: null,
daysInViewMobile: 3,
daysInViewStandard: 5,
daysLoaded: 0,
appointmentDurationMinutesMinimum: 90,
appointmentDurationMinutesMaximum: 120,
initialDaysToLoad: 15
};
},
computed: {
appointmentEstimate() {
return getDisplayTextForDurationLength(this.appointmentDurationMinutesMinimum, this.appointmentDurationMinutesMaximum);
},
calendarViewDirection() {
if (this.selectableDatesSetting === 'custom') return 'future';
return 'past';
},
daysToAdd() {
return this.isMobileView ? this.daysInViewMobile : this.daysInViewStandard;
},
displayErrorMessage() {
return errorMessages.TIME_REQUIRED;
},
headerDateString() {
const dayOffset = this.activePageIndex * this.daysToAdd;
const startDateToShow = new Date(this.activeDate);
startDateToShow.setDate(startDateToShow.getDate() + dayOffset);
const startMonthDisplay = convertDateToShortMonth(startDateToShow);
const endDateToShow = new Date(startDateToShow);
endDateToShow.setDate(endDateToShow.getDate() + (this.daysToAdd - 1));
const endMonthDisplay = convertDateToShortMonth(endDateToShow);
if (startMonthDisplay !== endMonthDisplay) {
return `${startMonthDisplay}. ${startDateToShow.getDate()} - ${endMonthDisplay}. ${endDateToShow.getDate()}`;
}
return `${startMonthDisplay}. ${startDateToShow.getDate()}-${endDateToShow.getDate()}`;
},
shortMonth() {
return this.todaysDate.toLocaleString('en-US', { month: 'short' });
},
todayString() {
return (
this.todayOverrideDateString
|| convertDateToDateString(new Date())
);
}
},
watch: {
isMobileView(newValue, oldValue) {
if (newValue !== oldValue) {
this.activePageIndex = 0;
this.selectedDate = null;
this.selectedTimeOfDayGrouping = null;
this.selectedTime = null;
this.selectedTimeSlot = null;
this.findFirstAvailableDateInView();
}
},
selectedDate(newValue, oldValue) {
if (newValue !== oldValue) {
this.$emit('dateSelected', newValue);
}
},
selectedTimeSlot(newValue, oldValue) {
if (newValue !== oldValue) {
const testObj = this.getSelectedTimeSlotInfoObject(newValue);
this.$emit('timeSlotSelected', testObj);
}
}
},
methods: {
initializeComponent(initialData) {
this.resetComponent();
this.setCalendarData(initialData);
},
resetComponent() {
this.isLoading = true;
this.selectableDatesData = []; // NOTE: uses monthNum (1-based), NOT monthIndex (0-based)
this.selectableTimeSlotsData = [];
this.selectedTimeOfDayGrouping = null;
this.selectedDate = null;
this.selectedTime = null;
this.selectedTimeSlot = null;
this.activeDate = new Date();
this.activePageIndex = 0;
this.todaysDate = new Date();
this.mql = null;
this.daysInViewMobile = 3;
this.daysInViewStandard = 5;
this.daysLoaded = 0;
this.appointmentDurationMinutesMinimum = 90;
this.appointmentDurationMinutesMaximum = 120;
this.initialDaysToLoad = 15;
},
addPremiumFlagToInput(routeCode) {
return (`${routeCode}${PREMIUM_TIME_SLOT_ID_FLAG}`);
},
checkIsSelectedDay(index, timeOfDayGrouping) {
const dateToShowString = this.getDateToShowString(index);
return this.selectedDate === dateToShowString && this.selectedTimeOfDayGrouping === timeOfDayGrouping;
},
checkIsSelectedTimeSlot(timeSlot) {
if (!this.selectedTimeSlot) {
return false;
}
return (this.selectedTimeSlot.isDropoff === timeSlot.isDropoff
&& this.selectedTimeSlot.startTime === timeSlot.startTime
&& this.selectedTimeSlot.timeOfDay === timeSlot.timeOfDay);
},
displayDropOffInformation(timeSlot) {
if (timeSlot.isDropoff) {
if (timeSlot.timeOfDay === 'morning') {
return this.getCmsContent(
'DropOffTimeSlotModal',
'BodyText'
);
}
return this.getCmsContent(
'OvernightDropOffTimeSlotModal',
'BodyText'
);
}
return '';
},
displayTimeSlotTime(timeSlot) {
const appointmentType = this.activeAppointmentType || AppointmentTypeStrings.IN_SHOP;
if (appointmentType === AppointmentTypeStrings.MOBILE || appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP) {
return `${militaryToTwelveHourTime(timeSlot.startTime)} - ${militaryToTwelveHourTime(timeSlot.endTime)}`;
}
if (timeSlot.isDropoff) {
if (timeSlot.timeOfDay === 'morning') {
return '<span class="dropoff-label">Drop & Go<sup>&trade;</sup></span>';
}
return '<span class="dropoff-label">Overnight drop off</span>';
}
return militaryToTwelveHourTime(timeSlot.startTime);
},
findFirstAvailableDateInView() {
for (let i = 0; i < this.daysToAdd; i += 1) {
const dateInView = this.getDateToShow(i);
const dateInViewString = convertDateToDateString(dateInView);
const selectableDateObj = this.selectableDatesData.find((dateObj) => dateObj.date === dateInViewString);
if (selectableDateObj && selectableDateObj.timeSlots && selectableDateObj.timeSlots.length > 0) {
if (selectableDateObj.morningTimeSlots && selectableDateObj.morningTimeSlots.length > 0) {
this.showMorningTimes(i);
} else {
this.showAfternoonTimes(i);
}
break;
}
}
},
findInitialPageIndex(daysFromStart) {
if (daysFromStart !== null && daysFromStart !== undefined) {
return {
initialPageIndex: Math.floor(daysFromStart / this.daysToAdd),
initialDayIndex: daysFromStart % this.daysToAdd
};
}
return {
initialPageIndex: 0,
initialDayIndex: null
};
},
findInitialDate(myDateString, initialDayIndex) {
const dateInView = this.getDateToShow(initialDayIndex);
const dateInViewString = convertDateToDateString(dateInView);
if (dateInViewString === myDateString) {
const selectableDateObj = this.selectableDatesData.find((dateObj) => dateObj.date === dateInViewString);
if (selectableDateObj) {
const initialTimeSlot = this.mainStore.order.schedule;
const isAfternoonSlot = isAfternoon(initialTimeSlot?.startTime);
if (isAfternoonSlot) {
this.showAfternoonTimes(initialDayIndex);
this.findInitialTimeByStore(selectableDateObj.afternoonTimeSlots);
} else {
this.showMorningTimes(initialDayIndex);
this.findInitialTimeByStore(selectableDateObj.morningTimeSlots);
}
}
}
},
findInitialTimeByStore(timeSlots) {
const initialTimeSlot = this.mainStore.order.schedule;
if (!initialTimeSlot || !initialTimeSlot.startTime) {
return;
}
const foundTimeSlot = timeSlots.find((ts) => ts.startTime === initialTimeSlot.startTime && ts.endTime === initialTimeSlot.endTime);
if (foundTimeSlot) {
this.selectTimeSlotForDay(foundTimeSlot);
}
},
getDateToShow(index) {
const dayOffset = this.activePageIndex * this.daysToAdd;
const dateToShow = new Date(this.activeDate);
dateToShow.setDate(dateToShow.getDate() + index + dayOffset);
return dateToShow;
},
getDateToShowString(index) {
const dateToShow = this.getDateToShow(index);
return convertDateToDateString(dateToShow);
},
getSelectedTimeSlotInfoObject(timeSlot) {
const routeCode = timeSlot?.id;
let routeCodeToUse = routeCode;
const routeCodeIncludesPremium = routeCode?.includes(PREMIUM_TIME_SLOT_ID_FLAG);
if (routeCodeIncludesPremium) {
routeCodeToUse = this.removePremiumFlagFromInput(routeCode);
}
const fullTimeSlot = this.selectableTimeSlotsData?.find((ts) => ts.id === routeCodeToUse);
if (fullTimeSlot) {
return {
timeSlot: {
date: this.selectedDate,
routeCode: fullTimeSlot.id,
startTime: fullTimeSlot.startTime,
endTime: fullTimeSlot.endTime,
isDropoff: fullTimeSlot.isDropoff,
jobMaxMinutes: this.appointmentDurationMinutesMaximum.toString(),
jobMinMinutes: this.appointmentDurationMinutesMinimum.toString()
},
isPremiumAppointment: !!routeCodeIncludesPremium
};
}
return {
timeSlot: {
date: null,
routeCode: null,
startTime: null,
endTime: null,
isDropoff: null,
jobMaxMinutes: null,
jobMinMinutes: null
},
isPremiumAppointment: null
};
},
async gotoNextPage() {
const maxPageIndex = Math.floor((this.daysLoaded - 1) / this.daysToAdd);
if (this.activePageIndex >= maxPageIndex) {
// do not go to next page right away if at end of loaded days
await this.loadMoreDays(maxPageIndex, this.daysToAdd, this.initialDaysToLoad).then(() => {
this.daysLoaded += this.initialDaysToLoad;
this.activePageIndex += 1;
this.selectedDate = null;
this.selectedTime = null;
this.selectedTimeSlot = null;
this.findFirstAvailableDateInView();
});
return;
}
this.activePageIndex += 1;
this.selectedDate = null;
this.selectedTime = null;
this.selectedTimeSlot = null;
this.findFirstAvailableDateInView();
},
gotoPreviousPage() {
this.activePageIndex -= 1;
this.selectedDate = null;
this.selectedTime = null;
this.selectedTimeSlot = null;
this.findFirstAvailableDateInView();
},
mapTimeSlot(timeSlot, timeOfDay) {
return {
...timeSlot,
isDropoff: timeSlot.id.endsWith('DROP OFF'),
timeOfDay
};
},
removePremiumFlagFromInput(routeCode) {
return routeCode.replace(PREMIUM_TIME_SLOT_ID_FLAG, '');
},
selectTimeSlotForDay(timeSlot) {
if (timeSlot) {
this.selectedTime = timeSlot.startTime;
this.selectedTimeSlot = timeSlot;
}
},
showDayOfWeekForIndex(index) {
const dateToShow = this.getDateToShow(index);
return dateToShow.toLocaleDateString('en-US', { weekday: 'short' });
},
showMonthAndDayForIndex(index) {
const dateToShow = this.getDateToShow(index);
return `${convertDateToTwoDigitMonth(dateToShow)}/${convertDateToTwoDigitDay(dateToShow)}`;
},
showMorningAvailabilityForIndex(index) {
const morningDateString = this.getDateToShowString(index);
const selectableDateObj = this.selectableDatesData.find((dateObj) => dateObj.date === morningDateString);
if (selectableDateObj && selectableDateObj.morningTimeSlots && selectableDateObj.morningTimeSlots.length > 0) {
return selectableDateObj.morningTimeSlots.length;
}
return 0;
},
showMorningTimes(index) {
const morningDateString = this.getDateToShowString(index);
const selectableDateObj = this.selectableDatesData.find((dateObj) => dateObj.date === morningDateString);
if (selectableDateObj && selectableDateObj.morningTimeSlots && selectableDateObj.morningTimeSlots.length > 0) {
if (this.selectedDate === morningDateString && this.selectedTimeOfDayGrouping === 'morning') {
// already selected, do nothing
return;
}
this.selectedDate = morningDateString;
this.selectedTimeOfDayGrouping = 'morning';
this.selectableTimeSlotsData = selectableDateObj.morningTimeSlots;
this.selectedTime = null;
this.selectedTimeSlot = null;
}
},
showAfternoonAvailabilityForIndex(index) {
const afternoonDateString = this.getDateToShowString(index);
const selectableDateObj = this.selectableDatesData.find((dateObj) => dateObj.date === afternoonDateString);
if (selectableDateObj && selectableDateObj.afternoonTimeSlots && selectableDateObj.afternoonTimeSlots.length > 0) {
return selectableDateObj.afternoonTimeSlots.length;
}
return 0;
},
showAfternoonTimes(index) {
const afternoonDateString = this.getDateToShowString(index);
const selectableDateObj = this.selectableDatesData.find((dateObj) => dateObj.date === afternoonDateString);
if (selectableDateObj && selectableDateObj.afternoonTimeSlots && selectableDateObj.afternoonTimeSlots.length > 0) {
if (this.selectedDate === afternoonDateString
&& this.selectedTimeOfDayGrouping === 'afternoon') {
// already selected, do nothing
return;
}
this.selectedDate = afternoonDateString;
this.selectedTimeOfDayGrouping = 'afternoon';
this.selectableTimeSlotsData = selectableDateObj.afternoonTimeSlots;
this.selectedTimeSlot = null;
this.selectedTime = null;
}
},
timeSlotFocused(timeSlot) {
if (timeSlot && this.selectedTime === null) {
this.selectTimeSlotForDay(timeSlot);
}
},
timeSlotInputChanged(timeSlot) {
this.selectTimeSlotForDay(timeSlot);
},
async setCalendarData(config = {}) {
// set selectable dates data
config.initialShopTimeSlotsResponse.days.forEach((selectableDate) => {
const dateObjectToPush = {
date: selectableDate.date,
timeSlots: selectableDate.timeSlots,
morningTimeSlots: selectableDate.timeSlots.filter((timeSlot) => {
const timeHour = parseInt(timeSlot.startTime.split(':')[0], 10);
return timeHour < 12;
}).map((timeSlot) => this.mapTimeSlot(timeSlot, 'morning')),
afternoonTimeSlots: selectableDate.timeSlots.filter((timeSlot) => {
const timeHour = parseInt(timeSlot.startTime.split(':')[0], 10);
return timeHour >= 12;
}).map((timeSlot) => this.mapTimeSlot(timeSlot, 'afternoon')),
isSelected: false
};
if (dateObjectToPush.morningTimeSlots.length > 0) {
const findIndex = dateObjectToPush.morningTimeSlots.findIndex((timeSlot) => timeSlot.id.endsWith('DROP OFF'));
if (findIndex !== -1) {
const dropOffSlot = dateObjectToPush.morningTimeSlots.splice(findIndex, 1)[0];
dateObjectToPush.morningTimeSlots.unshift(dropOffSlot);
}
}
if (dateObjectToPush.afternoonTimeSlots.length > 0) {
const findIndex = dateObjectToPush.afternoonTimeSlots.findIndex((timeSlot) => timeSlot.id.endsWith('DROP OFF'));
if (findIndex !== -1) {
const dropOffSlot = dateObjectToPush.afternoonTimeSlots.splice(findIndex, 1)[0];
dateObjectToPush.afternoonTimeSlots.push(dropOffSlot);
}
}
this.selectableDatesData.push(dateObjectToPush);
});
this.daysLoaded = config.initialDaysLoaded || this.initialDaysToLoad;
this.appointmentDurationMinutesMaximum = config.initialShopTimeSlotsResponse.estimatedServiceMinutesMaximum;
this.appointmentDurationMinutesMinimum = config.initialShopTimeSlotsResponse.estimatedServiceMinutesMinimum;
let pageIndexObj = { initialPageIndex: 0, initialDayIndex: null };
if (config.daysFromStart !== null && config.daysFromStart !== undefined) {
pageIndexObj = this.findInitialPageIndex(config.daysFromStart);
this.activePageIndex = pageIndexObj.initialPageIndex;
}
if (config.preSelectedDate) {
this.findInitialDate(config.preSelectedDate, pageIndexObj.initialDayIndex);
} else {
this.findFirstAvailableDateInView();
let attempts = 0;
while (!this.selectedDate && attempts < 5) {
await this.gotoNextPage();
attempts += 1;
}
}
this.isLoading = false;
},
async loadMoreDays(maxPageIndex, daysInView, daysToFetch) {
showIssLoadingModal(true);
const startDateForFetch = new Date(this.activeDate);
startDateForFetch.setDate(startDateForFetch.getDate() + (maxPageIndex + 1) * daysInView);
const startDateToFetchString = convertDateToDateString(startDateForFetch);
const endDateForFetch = new Date(startDateForFetch);
endDateForFetch.setDate(endDateForFetch.getDate() + (daysToFetch - 1));
const endDateToFetchString = convertDateToDateString(endDateForFetch);
// make new API call with this month's start and end dates
await this.updateSelectableDates(
startDateToFetchString,
endDateToFetchString
).catch(() => {
showIssLoadingModal(false);
});
showIssLoadingModal(false);
},
async updateSelectableDates(dateStart, dateEnd) {
const moreSelectableDates =
await this.customSelectableDatesCallback(
dateStart,
dateEnd
);
moreSelectableDates.days.forEach((selectableDate) => {
const index = this.selectableDatesData.findIndex((dateObj) => dateObj.date === selectableDate.date);
if (index === -1) {
const dateObjectToPush = {
date: selectableDate.date,
timeSlots: selectableDate.timeSlots,
morningTimeSlots: selectableDate.timeSlots.filter((timeSlot) => {
const timeHour = parseInt(timeSlot.startTime.split(':')[0], 10);
return timeHour < 12;
}).map((timeSlot) => this.mapTimeSlot(timeSlot, 'morning')),
afternoonTimeSlots: selectableDate.timeSlots.filter((timeSlot) => {
const timeHour = parseInt(timeSlot.startTime.split(':')[0], 10);
return timeHour >= 12;
}).map((timeSlot) => this.mapTimeSlot(timeSlot, 'afternoon')),
isSelected: false
};
if (dateObjectToPush.morningTimeSlots.length > 0) {
const findIndex = dateObjectToPush.morningTimeSlots.findIndex((timeSlot) => timeSlot.id.endsWith('DROP OFF'));
if (findIndex !== -1) {
const dropOffSlot = dateObjectToPush.morningTimeSlots.splice(findIndex, 1)[0];
dateObjectToPush.morningTimeSlots.unshift(dropOffSlot);
}
}
if (dateObjectToPush.afternoonTimeSlots.length > 0) {
const findIndex = dateObjectToPush.afternoonTimeSlots.findIndex((timeSlot) => timeSlot.id.endsWith('DROP OFF'));
if (findIndex !== -1) {
const dropOffSlot = dateObjectToPush.afternoonTimeSlots.splice(findIndex, 1)[0];
dateObjectToPush.afternoonTimeSlots.push(dropOffSlot);
}
}
this.selectableDatesData.push(dateObjectToPush);
}
});
}
}
};
</script>
<style lang="scss" scoped>
@import '@/styles/ux-variables-svg-strings.scss';
.date-picker-hidden {
opacity: 0;
max-height: 0;
}
.date-picker {
position: relative;
.appointment-date-picker-header {
font-weight: 600;
color: #000;
font-size: 1rem;
text-align: left;
margin-top: 1.25rem;
}
.appointment-estimate-header {
font-weight: 400;
color: #525656;
font-size: 1rem;
text-align: left;
margin-top: 0.25rem;
span {
font-weight: 600;
color:#000;
}
}
.appointment-date-picker-container {
display: grid;
grid-column-gap: 0px;
grid-row-gap: 0px;
margin-top: 1.25rem;
border-radius: .3125rem;
box-shadow: 0 0 .625rem 0 rgba(0, 0, 0, 0.2);
@include media-breakpoint-up(xs) {
grid-template-columns: repeat(3, 33.33%);
}
@include media-breakpoint-up(xl) {
grid-template-columns: repeat(5, 20%);
}
.date-picker-row {
display: contents;
}
.header-cell {
font-size: 1rem;
padding: .3125rem;
.span-block-flex {
display: flex;
align-items: center;
border: none;
background-color: #fff;
color: #525656;
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
}
.header-end {
text-align: end;
.span-block-flex {
justify-self: flex-end;
justify-content: flex-end;
img {
margin-left: 0.25rem;
}
}
}
.header-middle {
font-weight: 600;
color: #000;
@include media-breakpoint-up(xs) {
grid-column: 2 / span 1;
}
@include media-breakpoint-up(xl) {
grid-column: 2 / span 3;
}
text-align: center;
}
.header-start {
text-align: start;
.span-block-flex {
justify-self: flex-start;
justify-content: flex-start;
img {
margin-right: 0.25rem;
}
}
}
.day-of-week-header {
background-color: #0070d1;
color: $white;
padding: 0.5rem 0.25rem;
line-height: 1.5rem;
.day-of-week-caption {
font-weight: 600;
font-size: 1rem;
}
.month-day-caption {
font-weight: 300;
font-size: 0.8125rem;
}
}
.morning-row-cell, .afternoon-row-cell {
padding: 0.8125rem 0.25rem;
line-height: 1.5rem;
border: solid 1px #cacbcc;
.top-caption {
font-weight: 400;
font-size: 1rem;
color: #000;
}
.bottom-caption {
font-weight: 400;
font-size: 0.75rem;
}
&.has-appointments {
&:hover {
border: solid 1px #0070d1;
cursor: pointer;
}
.bottom-caption {
color: #1574a1;
font-weight: 500;
}
}
&.selected-date {
background-color: $background-color-selected;
border: solid 1px #0070d1;
}
&:focus, &:focus-visible {
outline: none;
border: solid 2px #0070d1;
}
}
.span-block {
display: block;
}
}
.appointment-time-picker-container {
margin-top: 1rem;
margin-bottom: 0.5rem;
.appointment-time-picker-header {
font-weight: 600;
color: #000;
font-size: 1rem;
text-align: left;
}
.time-slot-button {
width: 100%;
border: 1px solid #b3b4b5;
border-radius: $border-radius-list-button;
box-shadow: 0 1px 5px 0 rgba(0, 0, 0, .2);
color: #525656;
padding: 0.625rem 0;
margin-top: 0.625rem;
text-align: center;
cursor: pointer;
:deep(.dropoff-label) {
sup {
top: -0.375rem;
font-size: 0.75rem;
}
}
& + .time-slot-drop-off-information {
max-height: 0;
overflow: hidden;
transition: max-height 0.75s ease-in-out;
font-weight: 400;
text-align: left;
color: $darker-gray;
:deep(ul) {
margin: 0;
margin-bottom: 0.375rem;
> li {
padding-left: 0.625rem;
margin-top: 0.625rem;
}
}
}
&.selected-time-slot {
background-color: $background-color-selected;
border: solid 1px #0070d1;
color:#000;
font-weight: 500;
:deep(.dropoff-label) {
sup {
top: .25rem;
font-size: 1.625rem;
}
}
& + .time-slot-drop-off-information {
max-height: 12rem;
transition: max-height 0.75s ease-in-out;
}
}
&.has-date-error {
border: 1px solid #d93025;
}
}
}
fieldset {
flex-grow: 1;
position: relative;
}
.btn-link {
font-weight: 500;
text-underline-offset: 4px;
flex-grow: 0;
&:focus {
box-shadow: none;
}
}
}
.btn-link {
display: block;
position: relative;
height: 2rem;
width: 100%;
justify-content: center;
background: transparent;
border: none;
font-weight: 500;
letter-spacing: inherit;
text-underline-offset: 4px;
z-index: 2;
}
.form-test-error {
text-align: left;
margin-top: 0;
color: #db0020;
font-weight: 400;
font-size: 1rem;
}
</style>