updates for schedule
This commit is contained in:
parent
9fb3be5f44
commit
b15ea179b9
6 changed files with 226 additions and 49 deletions
|
|
@ -16,7 +16,7 @@
|
||||||
class="appointment-date-picker-container"
|
class="appointment-date-picker-container"
|
||||||
:class="isMobileView ? 'mobile' : 'desktop'">
|
:class="isMobileView ? 'mobile' : 'desktop'">
|
||||||
<div class="header-cell header-start">
|
<div class="header-cell header-start">
|
||||||
<span
|
<button
|
||||||
v-if="activePageIndex !== 0"
|
v-if="activePageIndex !== 0"
|
||||||
class="span-block-flex"
|
class="span-block-flex"
|
||||||
@click="gotoPreviousPage()">
|
@click="gotoPreviousPage()">
|
||||||
|
|
@ -25,13 +25,13 @@
|
||||||
src="~@/assets/img/icons/icon-ablue-previous.png"
|
src="~@/assets/img/icons/icon-ablue-previous.png"
|
||||||
alt="Left arrow icon" />
|
alt="Left arrow icon" />
|
||||||
Previous
|
Previous
|
||||||
</span>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-cell header-middle">
|
<div class="header-cell header-middle">
|
||||||
{{ headerDateString }}
|
{{ headerDateString }}
|
||||||
</div>
|
</div>
|
||||||
<div class="header-cell header-end">
|
<div class="header-cell header-end">
|
||||||
<span
|
<button
|
||||||
class="span-block-flex"
|
class="span-block-flex"
|
||||||
@click="gotoNextPage()">
|
@click="gotoNextPage()">
|
||||||
More
|
More
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
class="arrow-icon"
|
class="arrow-icon"
|
||||||
src="~@/assets/img/icons/icon-ablue-next.png"
|
src="~@/assets/img/icons/icon-ablue-next.png"
|
||||||
alt="Right arrow icon" />
|
alt="Right arrow icon" />
|
||||||
</span>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="week-row-container date-picker-row">
|
<div class="week-row-container date-picker-row">
|
||||||
<template
|
<template
|
||||||
|
|
@ -56,6 +56,7 @@
|
||||||
v-for="n in daysToAdd"
|
v-for="n in daysToAdd"
|
||||||
:key="n">
|
:key="n">
|
||||||
<div
|
<div
|
||||||
|
tabindex="0"
|
||||||
class="morning-row-cell"
|
class="morning-row-cell"
|
||||||
:class="[showMorningAvailabilityForIndex(n - 1) > 0 ? 'has-appointments' : 'no-appointments'
|
:class="[showMorningAvailabilityForIndex(n - 1) > 0 ? 'has-appointments' : 'no-appointments'
|
||||||
, checkIsSelectedDay(n - 1, 'morning') ? 'selected-date' : '']"
|
, checkIsSelectedDay(n - 1, 'morning') ? 'selected-date' : '']"
|
||||||
|
|
@ -72,6 +73,7 @@
|
||||||
v-for="n in daysToAdd"
|
v-for="n in daysToAdd"
|
||||||
:key="n">
|
:key="n">
|
||||||
<div
|
<div
|
||||||
|
tabindex="0"
|
||||||
class="afternoon-row-cell"
|
class="afternoon-row-cell"
|
||||||
:class="[showAfternoonAvailabilityForIndex(n - 1) > 0 ? 'has-appointments' : 'no-appointments'
|
:class="[showAfternoonAvailabilityForIndex(n - 1) > 0 ? 'has-appointments' : 'no-appointments'
|
||||||
, checkIsSelectedDay(n - 1, 'afternoon') ? 'selected-date' : '']"
|
, checkIsSelectedDay(n - 1, 'afternoon') ? 'selected-date' : '']"
|
||||||
|
|
@ -89,17 +91,28 @@
|
||||||
class="appointment-time-picker-container"
|
class="appointment-time-picker-container"
|
||||||
:class="isMobileView ? 'mobile' : 'desktop'">
|
:class="isMobileView ? 'mobile' : 'desktop'">
|
||||||
<div class="appointment-time-picker-header">
|
<div class="appointment-time-picker-header">
|
||||||
Available Times:
|
Available times:
|
||||||
</div>
|
</div>
|
||||||
<div
|
<template
|
||||||
v-for="timeSlot in selectableTimeSlotsData"
|
v-for="timeSlot in selectableTimeSlotsData"
|
||||||
:key="timeSlot.startTime"
|
:key="timeSlot.startTime">
|
||||||
class="time-slot-button"
|
<label
|
||||||
:class="[checkIsSelectedTimeSlot(timeSlot) ? 'selected-time-slot' : ''
|
:for="`timeslot${timeSlot.startTime}`"
|
||||||
, {'has-date-error': showTimeSlotError}]"
|
class="time-slot-button"
|
||||||
@click="selectTimeSlotForDay(timeSlot)">
|
:class="[checkIsSelectedTimeSlot(timeSlot) ? 'selected-time-slot' : ''
|
||||||
{{ displayTimeSlotTime(timeSlot.startTime) }}
|
, {'has-date-error': showTimeSlotError}]"
|
||||||
</div>
|
@click="selectTimeSlotForDay(timeSlot)">
|
||||||
|
{{ displayTimeSlotTime(timeSlot.startTime) }}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
:id="`timeslot${timeSlot.startTime}`"
|
||||||
|
v-model="selectedTime"
|
||||||
|
type="radio"
|
||||||
|
name="time-slot"
|
||||||
|
class="sr-only"
|
||||||
|
:value="timeSlot.startTime"
|
||||||
|
@change="timeSlotInputChanged(timeSlot)" />
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="row form-test-error">
|
<div class="row form-test-error">
|
||||||
<div v-if="showTimeSlotError">
|
<div v-if="showTimeSlotError">
|
||||||
|
|
@ -121,8 +134,12 @@ import {
|
||||||
convertDateToTwoDigitDay,
|
convertDateToTwoDigitDay,
|
||||||
convertDateToTwoDigitMonth,
|
convertDateToTwoDigitMonth,
|
||||||
getDisplayTextForDurationLength,
|
getDisplayTextForDurationLength,
|
||||||
|
isAfternoon,
|
||||||
militaryToTwelveHourTime
|
militaryToTwelveHourTime
|
||||||
} from '@/helpers/date-helper';
|
} from '@/helpers/date-helper';
|
||||||
|
import {
|
||||||
|
PREMIUM_TIME_SLOT_ID_FLAG
|
||||||
|
} from '@/constants/schedule-constants';
|
||||||
import showIssLoadingModal from '@/helpers/loading-modal-helper.js';
|
import showIssLoadingModal from '@/helpers/loading-modal-helper.js';
|
||||||
import errorMessages from '@/constants/error-messages.js';
|
import errorMessages from '@/constants/error-messages.js';
|
||||||
import { selectableDaysOptions } from './mixins/helpers';
|
import { selectableDaysOptions } from './mixins/helpers';
|
||||||
|
|
@ -207,6 +224,7 @@ export default {
|
||||||
selectableTimeSlotsData: [],
|
selectableTimeSlotsData: [],
|
||||||
selectedTimeOfDayGrouping: null,
|
selectedTimeOfDayGrouping: null,
|
||||||
selectedDate: null,
|
selectedDate: null,
|
||||||
|
selectedTime: null,
|
||||||
selectedTimeSlot: null,
|
selectedTimeSlot: null,
|
||||||
activeDate: new Date(),
|
activeDate: new Date(),
|
||||||
activePageIndex: 0,
|
activePageIndex: 0,
|
||||||
|
|
@ -262,7 +280,8 @@ export default {
|
||||||
watch: {
|
watch: {
|
||||||
selectedTimeSlot(newValue, oldValue) {
|
selectedTimeSlot(newValue, oldValue) {
|
||||||
if (newValue !== oldValue) {
|
if (newValue !== oldValue) {
|
||||||
this.$emit('timeSlotSelected', newValue);
|
const testObj = this.getSelectedTimeSlotInfoObject(newValue);
|
||||||
|
this.$emit('timeSlotSelected', testObj);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
selectedDate(newValue, oldValue) {
|
selectedDate(newValue, oldValue) {
|
||||||
|
|
@ -285,11 +304,54 @@ export default {
|
||||||
initializeComponent(initialData) {
|
initializeComponent(initialData) {
|
||||||
this.setCalendarData(initialData);
|
this.setCalendarData(initialData);
|
||||||
},
|
},
|
||||||
|
addPremiumFlagToInput(routeCode) {
|
||||||
|
return (`${routeCode}${PREMIUM_TIME_SLOT_ID_FLAG}`);
|
||||||
|
},
|
||||||
|
removePremiumFlagFromInput(routeCode) {
|
||||||
|
return routeCode.replace(PREMIUM_TIME_SLOT_ID_FLAG, '');
|
||||||
|
},
|
||||||
|
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,
|
||||||
|
jobMaxMinutes: this.appointmentDurationMinutesMaximum.toString(),
|
||||||
|
jobMinMinutes: this.appointmentDurationMinutesMinimum.toString()
|
||||||
|
},
|
||||||
|
isPremiumAppointment: !!routeCodeIncludesPremium
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
timeSlot: {
|
||||||
|
date: null,
|
||||||
|
startTime: null,
|
||||||
|
endTime: null,
|
||||||
|
routeCode: null,
|
||||||
|
jobMaxMinutes: null,
|
||||||
|
jobMinMinutes: null
|
||||||
|
},
|
||||||
|
isPremiumAppointment: null
|
||||||
|
};
|
||||||
|
},
|
||||||
handleMqlChange(e) {
|
handleMqlChange(e) {
|
||||||
this.isMobileView = !e.matches;
|
this.isMobileView = !e.matches;
|
||||||
this.activePageIndex = 0;
|
this.activePageIndex = 0;
|
||||||
this.selectedDate = null;
|
this.selectedDate = null;
|
||||||
this.selectedTimeOfDayGrouping = null;
|
this.selectedTimeOfDayGrouping = null;
|
||||||
|
this.selectedTime = null;
|
||||||
this.selectedTimeSlot = null;
|
this.selectedTimeSlot = null;
|
||||||
this.findFirstAvailableDateInView();
|
this.findFirstAvailableDateInView();
|
||||||
},
|
},
|
||||||
|
|
@ -311,6 +373,46 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
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 isAfternoonSlot = isAfternoon(myDateString);
|
||||||
|
const selectableDateObj = this.selectableDatesData.find((dateObj) => dateObj.date === dateInViewString);
|
||||||
|
if (selectableDateObj) {
|
||||||
|
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 initialStartTime = initialTimeSlot.startTime;
|
||||||
|
const foundTimeSlot = timeSlots.find((ts) => ts.startTime === initialStartTime);
|
||||||
|
if (foundTimeSlot) {
|
||||||
|
this.selectTimeSlotForDay(foundTimeSlot);
|
||||||
|
}
|
||||||
|
},
|
||||||
getDateToShow(index) {
|
getDateToShow(index) {
|
||||||
const dayOffset = this.activePageIndex * this.daysToAdd;
|
const dayOffset = this.activePageIndex * this.daysToAdd;
|
||||||
const dateToShow = new Date(this.activeDate);
|
const dateToShow = new Date(this.activeDate);
|
||||||
|
|
@ -321,6 +423,38 @@ export default {
|
||||||
const dateToShow = this.getDateToShow(index);
|
const dateToShow = this.getDateToShow(index);
|
||||||
return convertDateToDateString(dateToShow);
|
return convertDateToDateString(dateToShow);
|
||||||
},
|
},
|
||||||
|
getEndDateForExistingDate(startDate, selectedDateString, daysInView) {
|
||||||
|
const endDate = new Date(startDate);
|
||||||
|
endDate.setDate(endDate.getDate() + (daysInView - 1));
|
||||||
|
|
||||||
|
if (selectedDateString) {
|
||||||
|
const selectedDate = new Date(`${selectedDateString}T00:00:00`);
|
||||||
|
const daysDifferenceComparedToStartDate = Math.ceil((selectedDate - startDate) / (1000 * 60 * 60 * 24));
|
||||||
|
if (selectedDate > endDate) {
|
||||||
|
const daysDifferenceComparedToStartDateAsPages = Math.ceil(daysDifferenceComparedToStartDate / daysInView);
|
||||||
|
const newEndDate = new Date(startDate);
|
||||||
|
newEndDate.setDate(newEndDate.getDate() + (daysDifferenceComparedToStartDateAsPages * daysInView) - 1);
|
||||||
|
|
||||||
|
return {
|
||||||
|
endDateString: convertDateToDateString(newEndDate),
|
||||||
|
newDaysLoaded: (daysDifferenceComparedToStartDateAsPages * daysInView),
|
||||||
|
daysFromStart: daysDifferenceComparedToStartDate
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
endDateString: convertDateToDateString(endDate),
|
||||||
|
newDaysLoaded: null,
|
||||||
|
daysFromStart: daysDifferenceComparedToStartDate
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
endDateString: convertDateToDateString(endDate),
|
||||||
|
newDaysLoaded: null,
|
||||||
|
daysFromStart: null
|
||||||
|
};
|
||||||
|
},
|
||||||
gotoNextPage() {
|
gotoNextPage() {
|
||||||
const maxPageIndex = Math.floor((this.daysLoaded - 1) / this.daysToAdd);
|
const maxPageIndex = Math.floor((this.daysLoaded - 1) / this.daysToAdd);
|
||||||
if (this.activePageIndex >= maxPageIndex) {
|
if (this.activePageIndex >= maxPageIndex) {
|
||||||
|
|
@ -329,6 +463,7 @@ export default {
|
||||||
this.daysLoaded += this.initialDaysToLoad;
|
this.daysLoaded += this.initialDaysToLoad;
|
||||||
this.activePageIndex += 1;
|
this.activePageIndex += 1;
|
||||||
this.selectedDate = null;
|
this.selectedDate = null;
|
||||||
|
this.selectedTime = null;
|
||||||
this.selectedTimeSlot = null;
|
this.selectedTimeSlot = null;
|
||||||
this.findFirstAvailableDateInView();
|
this.findFirstAvailableDateInView();
|
||||||
});
|
});
|
||||||
|
|
@ -336,12 +471,14 @@ export default {
|
||||||
}
|
}
|
||||||
this.activePageIndex += 1;
|
this.activePageIndex += 1;
|
||||||
this.selectedDate = null;
|
this.selectedDate = null;
|
||||||
|
this.selectedTIme = null;
|
||||||
this.selectedTimeSlot = null;
|
this.selectedTimeSlot = null;
|
||||||
this.findFirstAvailableDateInView();
|
this.findFirstAvailableDateInView();
|
||||||
},
|
},
|
||||||
gotoPreviousPage() {
|
gotoPreviousPage() {
|
||||||
this.activePageIndex -= 1;
|
this.activePageIndex -= 1;
|
||||||
this.selectedDate = null;
|
this.selectedDate = null;
|
||||||
|
this.selectedTime = null;
|
||||||
this.selectedTimeSlot = null;
|
this.selectedTimeSlot = null;
|
||||||
this.findFirstAvailableDateInView();
|
this.findFirstAvailableDateInView();
|
||||||
},
|
},
|
||||||
|
|
@ -357,6 +494,7 @@ export default {
|
||||||
},
|
},
|
||||||
selectTimeSlotForDay(timeSlot) {
|
selectTimeSlotForDay(timeSlot) {
|
||||||
if (timeSlot) {
|
if (timeSlot) {
|
||||||
|
this.selectedTime = timeSlot.startTime;
|
||||||
this.selectedTimeSlot = timeSlot;
|
this.selectedTimeSlot = timeSlot;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -387,6 +525,7 @@ export default {
|
||||||
this.selectedDate = morningDateString;
|
this.selectedDate = morningDateString;
|
||||||
this.selectedTimeOfDayGrouping = 'morning';
|
this.selectedTimeOfDayGrouping = 'morning';
|
||||||
this.selectableTimeSlotsData = selectableDateObj.morningTimeSlots;
|
this.selectableTimeSlotsData = selectableDateObj.morningTimeSlots;
|
||||||
|
this.selectedTime = null;
|
||||||
this.selectedTimeSlot = null;
|
this.selectedTimeSlot = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -411,13 +550,18 @@ export default {
|
||||||
this.selectedTimeOfDayGrouping = 'afternoon';
|
this.selectedTimeOfDayGrouping = 'afternoon';
|
||||||
this.selectableTimeSlotsData = selectableDateObj.afternoonTimeSlots;
|
this.selectableTimeSlotsData = selectableDateObj.afternoonTimeSlots;
|
||||||
this.selectedTimeSlot = null;
|
this.selectedTimeSlot = null;
|
||||||
|
this.selectedTime = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
timeSlotInputChanged(timeSlot) {
|
||||||
|
this.selectTimeSlotForDay(timeSlot);
|
||||||
|
},
|
||||||
async loadInitialData(config) {
|
async loadInitialData(config) {
|
||||||
/*
|
/*
|
||||||
** NOTE: this _could_ be called by a parent before fully loaded, so data or computeds might not be available
|
** NOTE: this _could_ be called by a parent before fully loaded, so data or computeds might not be available
|
||||||
*/
|
*/
|
||||||
let todayDateString;
|
let todayDateString;
|
||||||
|
const todayDateObject = new Date();
|
||||||
const calendarViewDirection = 'future';
|
const calendarViewDirection = 'future';
|
||||||
const initialDays = 15;
|
const initialDays = 15;
|
||||||
|
|
||||||
|
|
@ -426,13 +570,23 @@ export default {
|
||||||
} else if (config.todayOverrideDateString) {
|
} else if (config.todayOverrideDateString) {
|
||||||
todayDateString = config.todayOverrideDateString;
|
todayDateString = config.todayOverrideDateString;
|
||||||
} else {
|
} else {
|
||||||
todayDateString = convertDateToDateString(new Date());
|
todayDateString = convertDateToDateString(todayDateObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialViewStartDate = todayDateString;
|
const initialViewStartDate = todayDateString;
|
||||||
const initialEndDate = new Date();
|
const initialEndDate = new Date();
|
||||||
initialEndDate.setDate(initialEndDate.getDate() + (initialDays - 1));
|
initialEndDate.setDate(initialEndDate.getDate() + (initialDays - 1));
|
||||||
const initialViewEndDate = convertDateToDateString(initialEndDate);
|
let initialViewEndDate = convertDateToDateString(initialEndDate);
|
||||||
|
|
||||||
|
const endDateObject = this.getEndDateForExistingDate(
|
||||||
|
todayDateObject,
|
||||||
|
config.preSelectedDate,
|
||||||
|
initialDays
|
||||||
|
);
|
||||||
|
|
||||||
|
if (endDateObject.endDateString !== initialViewEndDate) {
|
||||||
|
initialViewEndDate = endDateObject.endDateString;
|
||||||
|
}
|
||||||
|
|
||||||
const loadInitialDataPromise = new Promise((resolve) => {
|
const loadInitialDataPromise = new Promise((resolve) => {
|
||||||
const response = config.customSelectableDatesCallback(
|
const response = config.customSelectableDatesCallback(
|
||||||
|
|
@ -452,7 +606,8 @@ export default {
|
||||||
calendarViewDirection,
|
calendarViewDirection,
|
||||||
initialShopTimeSlotsResponse: response,
|
initialShopTimeSlotsResponse: response,
|
||||||
preSelectedDate: config.preSelectedDate,
|
preSelectedDate: config.preSelectedDate,
|
||||||
initialDaysLoaded: initialDays
|
initialDaysLoaded: endDateObject.newDaysLoaded || initialDays,
|
||||||
|
daysFromStart: endDateObject.daysFromStart || null
|
||||||
};
|
};
|
||||||
|
|
||||||
return initialData;
|
return initialData;
|
||||||
|
|
@ -480,7 +635,18 @@ export default {
|
||||||
this.appointmentDurationMinutesMaximum = config.initialShopTimeSlotsResponse.estimatedServiceMinutesMaximum;
|
this.appointmentDurationMinutesMaximum = config.initialShopTimeSlotsResponse.estimatedServiceMinutesMaximum;
|
||||||
this.appointmentDurationMinutesMinimum = config.initialShopTimeSlotsResponse.estimatedServiceMinutesMinimum;
|
this.appointmentDurationMinutesMinimum = config.initialShopTimeSlotsResponse.estimatedServiceMinutesMinimum;
|
||||||
|
|
||||||
this.findFirstAvailableDateInView();
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
},
|
},
|
||||||
async loadMoreDays(maxPageIndex, daysInView, daysToFetch) {
|
async loadMoreDays(maxPageIndex, daysInView, daysToFetch) {
|
||||||
|
|
@ -590,6 +756,9 @@ export default {
|
||||||
.span-block-flex {
|
.span-block-flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
border: none;
|
||||||
|
background-color: #fff;
|
||||||
|
color: #525656;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|
@ -601,6 +770,7 @@ export default {
|
||||||
text-align: end;
|
text-align: end;
|
||||||
|
|
||||||
.span-block-flex {
|
.span-block-flex {
|
||||||
|
justify-self: flex-end;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
|
|
@ -610,6 +780,7 @@ export default {
|
||||||
}
|
}
|
||||||
.header-middle {
|
.header-middle {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
color: #000;
|
||||||
@include media-breakpoint-up(xs) {
|
@include media-breakpoint-up(xs) {
|
||||||
grid-column: 2 / span 1;
|
grid-column: 2 / span 1;
|
||||||
}
|
}
|
||||||
|
|
@ -623,6 +794,7 @@ export default {
|
||||||
text-align: start;
|
text-align: start;
|
||||||
|
|
||||||
.span-block-flex {
|
.span-block-flex {
|
||||||
|
justify-self: flex-start;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
|
|
@ -677,6 +849,10 @@ export default {
|
||||||
border: solid 1px #0070d1;
|
border: solid 1px #0070d1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:focus, &:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
border: solid 2px #0070d1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.span-block {
|
.span-block {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
@ -697,7 +873,7 @@ export default {
|
||||||
.time-slot-button {
|
.time-slot-button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: 1px solid #b3b4b5;
|
border: 1px solid #b3b4b5;
|
||||||
border-radius: 1.375rem;
|
border-radius: $border-radius-list-button;
|
||||||
box-shadow: 0 1px 5px 0 rgba(0, 0, 0, .2);
|
box-shadow: 0 1px 5px 0 rgba(0, 0, 0, .2);
|
||||||
color: #525656;
|
color: #525656;
|
||||||
padding: 0.5rem 0;
|
padding: 0.5rem 0;
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,12 @@ export function getDisplayTextForDurationLength(durationMinimum, durationMaximum
|
||||||
return `${durationText} ${unitText}`;
|
return `${durationText} ${unitText}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isAfternoon(timeString) {
|
||||||
|
if (typeof timeString !== 'string') return false;
|
||||||
|
const hours = parseInt(timeString.split(':')[0], 10);
|
||||||
|
return hours >= 12;
|
||||||
|
}
|
||||||
|
|
||||||
export function militaryToTwelveHourTime(timeString) {
|
export function militaryToTwelveHourTime(timeString) {
|
||||||
// Expected input: "HH:MM"
|
// Expected input: "HH:MM"
|
||||||
if (typeof timeString !== 'string') return null;
|
if (typeof timeString !== 'string') return null;
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import { createTestingPinia } from '@pinia/testing';
|
||||||
import { shallowMount } from '@vue/test-utils';
|
import { shallowMount } from '@vue/test-utils';
|
||||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
||||||
import { useMainStore } from '@/store/index.js';
|
import { useMainStore } from '@/store/index.js';
|
||||||
import { AppointmentTypeStrings } from '@/constants/schedule-constants';
|
|
||||||
|
|
||||||
// Mock fetchCmsContentForPage
|
// Mock fetchCmsContentForPage
|
||||||
jest.mock('@/helpers/cms-content-helper', () => ({
|
jest.mock('@/helpers/cms-content-helper', () => ({
|
||||||
|
|
@ -214,7 +213,7 @@ describe('schedule-page.vue', () => {
|
||||||
// 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-15'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -236,7 +235,7 @@ describe('schedule-page.vue', () => {
|
||||||
estimatedServiceMinutesMaximum: 120
|
estimatedServiceMinutesMaximum: 120
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
test('Should call API service in days of 34 or less when getAvailableDatesMethod is called with large date ranges', async () => {
|
test('Should call API service in days of 15 or less when getAvailableDatesMethod is called with large date ranges', async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const { wrapper } = getShallowMountedComponent();
|
const { wrapper } = getShallowMountedComponent();
|
||||||
wrapper.vm.selectableDatesData = {
|
wrapper.vm.selectableDatesData = {
|
||||||
|
|
@ -264,7 +263,7 @@ describe('schedule-page.vue', () => {
|
||||||
// 2023-01-01 --> 2023-02-05
|
// 2023-01-01 --> 2023-02-05
|
||||||
// 2023-02-06 --> 2023-03-12
|
// 2023-02-06 --> 2023-03-12
|
||||||
// 2023-03-13 --> 2023-03-31
|
// 2023-03-13 --> 2023-03-31
|
||||||
expect(store.getShopTimeSlots).toHaveBeenCalledTimes(3);
|
expect(store.getShopTimeSlots).toHaveBeenCalledTimes(6);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('Rendering', () => {
|
describe('Rendering', () => {
|
||||||
|
|
@ -296,7 +295,9 @@ describe('schedule-page.vue', () => {
|
||||||
const { wrapper } = getShallowMountedComponent();
|
const { wrapper } = getShallowMountedComponent();
|
||||||
wrapper.vm.$router.navigate = jest.fn(() => ({}));
|
wrapper.vm.$router.navigate = jest.fn(() => ({}));
|
||||||
wrapper.vm.selectedTimeSlotInfo = {
|
wrapper.vm.selectedTimeSlotInfo = {
|
||||||
id: 'test-id'
|
timeSlot: {
|
||||||
|
routeCode: 'test-id'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ import BaseFormMixin from '@/mixins/base-form-mixin.js';
|
||||||
import { useMainStore } from '@/store';
|
import { useMainStore } from '@/store';
|
||||||
|
|
||||||
// Define constants
|
// Define constants
|
||||||
const TIME_SLOTS_CALL_DAYS_LIMIT = 34; // needs to be 34 for API limits (35 does not consistently work)
|
const TIME_SLOTS_CALL_DAYS_LIMIT = 15;
|
||||||
|
|
||||||
const getAvailableDates = async (
|
const getAvailableDates = async (
|
||||||
startDateString,
|
startDateString,
|
||||||
|
|
@ -83,7 +83,7 @@ const getAvailableDates = async (
|
||||||
) => {
|
) => {
|
||||||
const apiEndDateLimit = sumDateString(
|
const apiEndDateLimit = sumDateString(
|
||||||
startDateString,
|
startDateString,
|
||||||
TIME_SLOTS_CALL_DAYS_LIMIT
|
TIME_SLOTS_CALL_DAYS_LIMIT - 1
|
||||||
);
|
);
|
||||||
const difference = calcDaysBetweenDates(startDateString, endDateString);
|
const difference = calcDaysBetweenDates(startDateString, endDateString);
|
||||||
const apiCallsCount = Math.ceil(difference / TIME_SLOTS_CALL_DAYS_LIMIT);
|
const apiCallsCount = Math.ceil(difference / TIME_SLOTS_CALL_DAYS_LIMIT);
|
||||||
|
|
@ -98,7 +98,7 @@ const getAvailableDates = async (
|
||||||
apiStartDate = sumDateString(apiEndDate, 1);
|
apiStartDate = sumDateString(apiEndDate, 1);
|
||||||
apiEndDate = sumDateString(
|
apiEndDate = sumDateString(
|
||||||
apiStartDate,
|
apiStartDate,
|
||||||
TIME_SLOTS_CALL_DAYS_LIMIT
|
TIME_SLOTS_CALL_DAYS_LIMIT - 1
|
||||||
);
|
);
|
||||||
|
|
||||||
if (i === apiCallsCount) {
|
if (i === apiCallsCount) {
|
||||||
|
|
@ -108,11 +108,7 @@ const getAvailableDates = async (
|
||||||
apiEndDate = apiEndDateLimit;
|
apiEndDate = apiEndDateLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (appointmentType === AppointmentTypeStrings.MOBILE || appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP) {
|
||||||
appointmentType === AppointmentTypeStrings.MOBILE
|
|
||||||
|| appointmentType
|
|
||||||
=== AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP
|
|
||||||
) {
|
|
||||||
storeActionConfig = {
|
storeActionConfig = {
|
||||||
storeAction: GET_MOBILE_TIME_SLOTS,
|
storeAction: GET_MOBILE_TIME_SLOTS,
|
||||||
payload: {
|
payload: {
|
||||||
|
|
@ -278,7 +274,7 @@ export default {
|
||||||
return useMainStore().order.serviceLocation.appointmentType;
|
return useMainStore().order.serviceLocation.appointmentType;
|
||||||
},
|
},
|
||||||
isFormValid() {
|
isFormValid() {
|
||||||
const hasTimeSlotSelected = this.selectedTimeSlotInfo?.id != null;
|
const hasTimeSlotSelected = this.selectedTimeSlotInfo?.timeSlot?.routeCode != null;
|
||||||
return hasTimeSlotSelected;
|
return hasTimeSlotSelected;
|
||||||
},
|
},
|
||||||
supportingItems() {
|
supportingItems() {
|
||||||
|
|
@ -328,9 +324,6 @@ export default {
|
||||||
getServiceZipCtuCodeFromStore() {
|
getServiceZipCtuCodeFromStore() {
|
||||||
return this.mainStore.order.serviceLocation.zipCodeCtu;
|
return this.mainStore.order.serviceLocation.zipCodeCtu;
|
||||||
},
|
},
|
||||||
dateTimeSelected() {
|
|
||||||
console.log('dateTimeSelected called');
|
|
||||||
},
|
|
||||||
getSelectedDate() {
|
getSelectedDate() {
|
||||||
return this.mainStore.order.schedule.date;
|
return this.mainStore.order.schedule.date;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -2374,18 +2374,18 @@ export const useMainStore = defineStore({
|
||||||
method: endpoints.TaxOrderItems.method,
|
method: endpoints.TaxOrderItems.method,
|
||||||
endpoint: endpoints.TaxOrderItems.url,
|
endpoint: endpoints.TaxOrderItems.url,
|
||||||
payload: {
|
payload: {
|
||||||
ParentAccountNumber: this.order.parentAccountNumber,
|
ParentAccountNumber: this.order.parentAccountNumber,
|
||||||
BillToAccountNumber: this.billToAccountNumber,
|
BillToAccountNumber: this.billToAccountNumber,
|
||||||
ProviderNumber: this.providerNumber,
|
ProviderNumber: this.providerNumber,
|
||||||
AppointmentType: appointmentType,
|
AppointmentType: appointmentType,
|
||||||
PricedLineItems: getLineItemsFlattened(pricedLineItems),
|
PricedLineItems: getLineItemsFlattened(pricedLineItems),
|
||||||
ServiceLocation: {
|
ServiceLocation: {
|
||||||
City: isMobileApt ? serviceLocationCity : null,
|
City: isMobileApt ? serviceLocationCity : null,
|
||||||
State: isMobileApt ? serviceLocationState : null,
|
State: isMobileApt ? serviceLocationState : null,
|
||||||
ZipCode: isMobileApt ? serviceLocationZipCode : null,
|
ZipCode: isMobileApt ? serviceLocationZipCode : null
|
||||||
},
|
},
|
||||||
ServerData: lineItemServerData ? lineItemServerData : "",
|
ServerData: lineItemServerData || ''
|
||||||
},
|
}
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
this.order.lineItems.serverData = response.data.serverData;
|
this.order.lineItems.serverData = response.data.serverData;
|
||||||
return addTaxesToPricedLineItems(pricedLineItems, response.data.taxedLineItems);
|
return addTaxesToPricedLineItems(pricedLineItems, response.data.taxedLineItems);
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,7 @@ $border-radius: 0.25rem;
|
||||||
$border-radius-sm: 0.2rem;
|
$border-radius-sm: 0.2rem;
|
||||||
$border-radius-lg: 0.5rem; //Used for buttons. Can be used for other things, of course.
|
$border-radius-lg: 0.5rem; //Used for buttons. Can be used for other things, of course.
|
||||||
$border-radius-pill: 50rem;
|
$border-radius-pill: 50rem;
|
||||||
|
$border-radius-list-button: 1.375rem; // Used for list buttons
|
||||||
|
|
||||||
//Progress Bar Styling
|
//Progress Bar Styling
|
||||||
$progress-bar-success-color: $green;
|
$progress-bar-success-color: $green;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue