CSR-886: new refactoring changes to use beforeRouteEnter on Schedule page to do heavy lifting

This commit is contained in:
Adam Caouette 2023-05-17 19:19:48 -04:00
parent 53166fc0ea
commit d7f406b2ba
7 changed files with 477 additions and 288 deletions

View file

@ -1,16 +0,0 @@
const monthsOfYear = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
export { monthsOfYear };

View file

@ -1 +0,0 @@
test.todo("some test to be written in the future");

View file

@ -1,5 +1,5 @@
<template>
<div v-if="months" class="date-picker text-center" :class="calendarViewDirection">
<div class="date-picker text-center" :class="calendarViewDirection">
<fieldset id="date-picker-fieldset" ref="datePickerFieldset">
<legend class="sr-only">Select a day and time</legend>
<div
@ -8,7 +8,7 @@
:id="`${month.monthLabel}-${month.yearNum?.toString()}`"
class="calendar-grid-container position-relative"
:class="[
hasPartialMonthInitialView === true ? 'partial-month-initial-view' : '',
hideSomeDaysForInitialView ? 'partial-month-initial-view' : '',
month.monthClass,
]">
<div class="month-year body-small d-flex align-items-center small">
@ -22,7 +22,7 @@
<div class="separator-line"></div>
<div class="nav-back ps-3"><button></button></div>
<div class="nav-forward pe-3"><button></button></div>
<!-- Do the days of the weeek need to be read? -->
<!-- TODO Accessibility: Do the days of the week need to be read? -->
<div class="grid-item caption"><span class="sr-only">Sunday</span>S</div>
<div class="grid-item caption"><span class="sr-only">Monday</span>M</div>
<div class="grid-item caption"><span class="sr-only">Tuesday</span>T</div>
@ -38,10 +38,10 @@
:class="[
date.dateNum === 1 ? 'first-day-' + month.startDateDayIndex : '',
date.dayClasses,
isSelectableDate(date.inputValue) ? 'selectable-day' : '',
date.isSelectable ? 'selectable-day' : '',
]">
<input
:disabled="!isSelectableDate(date.inputValue)"
:disabled="!date.isSelectable"
type="radio"
name="day-of-month"
v-model="selectedDate"
@ -52,12 +52,17 @@
</label>
</div>
</div>
<loader
:class="[!isLoading ? 'date-picker-hidden' : '']"
loaderColor="blue"
loaderPosition="center" />
</fieldset>
<button
v-if="calendarViewDirection === 'future'"
v-if="calendarViewDirection === 'future' && !disableViewMoreDatesButton"
type="button"
class="btn btn-link"
@click="goForward">
:disabled="isLoading"
@click="showAnotherMonth">
View more dates
</button>
</div>
@ -65,58 +70,55 @@
<script>
// Supporting files
import { monthsOfYear } from "@/constants/scheduling.js";
const SelectableDaysOptions = Object.freeze({
CUSTOM: "custom",
PAST: "past",
});
const requiredParameter = () => {
throw new Error("parameter is required");
};
const forceTwoDigitString = (monthNum) => {
const newString = monthNum.toString();
return newString.length === 1 ? "0" + newString : newString;
};
const TIMINGFUNC_MAP = {
linear: (t) => t,
"ease-in": (t) => t * t,
"ease-out": (t) => t * (2 - t),
"ease-in-out": (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),
};
const BUFFER_OFFSET = 10;
import loader from "@/ux-components/loader/loader";
import { TIMINGFUNC_MAP, BUFFER_OFFSET, MONTHS_OF_YEAR } from "./mixins/constants";
import { selectableDaysOptions, requiredParameter, forceTwoDigitString } from "./mixins/helpers";
export default {
name: "datePicker",
data() {
return {
monthsBeforeToLoadOffset: 0,
monthsAfterToLoadOffset: 12,
isLoading: true,
months: null,
disableViewMoreDatesButton: false,
selectableDatesData: [], // NOTE: uses monthNum (1-based), NOT monthIndex (0-based)
hasPartialMonthInitialView: true,
hasPartialMonthInitialViewNEW: true,
today: null,
hideSomeDaysForInitialView: null,
// TO BE MADE "CONSTANTS"
monthsBeforeToLoadOffset: 36,
monthsAfterToLoadOffset: 12,
initialViewRowsToShow: 5,
// TBD OR REMOVED
hasPartialMonthInitialView: true,
hasSplitInitialView: true,
initialViewIncludesFullSecondMonth: false,
initialViewRowsTally: 0,
isAddingNewMonth: false,
currentScrollTop: 0,
months: null,
lastApiCallStartDate: null,
lastApiCallEndDate: null,
newToday: {
dateObj: null,
dateNum: null,
dayIndex: null,
yearNum: null,
monthIndex: null,
monthEndDateNum: null,
},
initialViewStartDateNum: null,
initialViewEndDateNum: null,
daysLeftOver: null,
};
},
mounted() {
this.setCalendarData();
},
props: {
selectableDatesSetting: {
type: String,
validator(value) {
return Object.values(SelectableDaysOptions).includes(value);
return Object.values(selectableDaysOptions).includes(value);
},
default: SelectableDaysOptions.PAST,
default: selectableDaysOptions.PAST,
},
modelValue: {
type: Object,
@ -134,10 +136,25 @@ export default {
},
},
computed: {
todayDate() {
return this.todayOverrideDateString
? new Date(this.todayOverrideDateString)
: new Date();
todayMonthIndex() {
return this.today.getMonth() + 1;
},
todayYearNum() {
return this.today.getFullYear();
},
todayDayIndex() {
return this.today.getDay();
},
todayDateNum() {
return this.today.getDate();
},
currentWeekStartDateNum() {
return this.todayDayIndex >= this.todayDateNum
? 1
: this.todayDateNum - this.todayDayIndex;
},
currentWeekEndDateNum() {
return this.todayDateNum + (6 - this.todayDayIndex);
},
calendarViewDirection() {
if (this.selectableDatesSetting === "past") return "past";
@ -149,12 +166,134 @@ export default {
return this.modelValue;
},
set(newSelectedDate) {
console.log("newSelectedDate ", newSelectedDate);
this.$emit("update:modelValue", newSelectedDate);
},
},
},
methods: {
getWeekStartDate(date) {
// Get the day of the week for date
let dayOfWeek = date.getDay();
// Subtract the day of the week from date to get the date of Sunday
let sunday = new Date(date);
sunday.setDate(sunday.getDate() - dayOfWeek);
// Return the date of Sunday
return sunday;
},
getWeekEndDate(date) {
const currentDay = date.getDay(); // Get the day of the week (0 = Sunday, 1 = Monday, etc.)
const daysUntilSaturday = 6 - currentDay; // Calculate the number of days until Saturday
// Clone the given date and add the remaining days until Saturday
const saturday = new Date(date);
saturday.setDate(date.getDate() + daysUntilSaturday);
return saturday;
},
getNextWeekSunday(date) {
const currentDay = date.getDay(); // Get the day of the week (0 = Sunday, 1 = Monday, etc.)
const daysUntilNextSunday = currentDay === 0 ? 7 : 7 - currentDay; // Calculate the number of days until the next Sunday
// Clone the given date and add the remaining days until Sunday
const nextSunday = new Date(date);
nextSunday.setDate(date.getDate() + daysUntilNextSunday);
return nextSunday;
},
getInitialViewWeeks(today, initialViewRowsToShow) {
// TODO: this only is for future direction; create logic for past direction
let weeks = [];
let weekStartDate = this.getWeekStartDate(today);
let weekEndDate = this.getWeekEndDate(today);
for (let i = 0; i < initialViewRowsToShow; i++) {
if (i > 0) {
weekStartDate = this.getNextWeekSunday(weekEndDate);
weekEndDate = this.getWeekEndDate(weekStartDate);
}
weeks.push({
weekNum: i + 1,
weekStartDate: weekStartDate,
weekEndDate: weekEndDate,
});
}
return weeks;
},
async loadInitialData(config) {
// CALLED FROM CONSUMING COMPONENT BEFORE DATE-PICKER APPEARS
const todayDate = config.todayOverrideDateString
? new Date(config.todayOverrideDateString)
: new Date();
let todayMonthIndex = todayDate.getMonth() + 1;
let todayYearNum = todayDate.getFullYear();
let todayDayIndex = todayDate.getDay();
let todayDateNum = todayDate.getDate();
let currentMonthStart = new Date(todayYearNum, todayMonthIndex - 1, 1);
let currentMonthEnd = new Date(todayYearNum, todayMonthIndex, 0);
let currentWeekStartDate = this.getWeekStartDate(todayDate);
let currentWeekEndDateNum = this.getWeekEndDate(todayDate);
let calendarViewDirection = "none";
if (config.selectableDatesSetting === "past") calendarViewDirection = "past";
if (config.selectableDatesSetting === "custom") calendarViewDirection = "future";
const initialViewWeeks = this.getInitialViewWeeks(
todayDate,
config.initialViewRowsToShow
);
let initialViewStartDate = todayDate; // <<<<<<<<<<<<<
let initialViewEndDate = initialViewWeeks[initialViewWeeks.length - 1].weekEndDate;
let secondMonthEnd;
let saturday1month = initialViewWeeks[0].weekEndDate.getMonth(); // <<<<<<<<<<<<<
let sunday5month =
initialViewWeeks[initialViewWeeks.length - 1].weekStartDate.getMonth(); // <<<<<<<<<<<<<
let hideSomeDaysForInitialView = false; // <<<<<<<<<<<<<
let hideSecondMonth = false; // <<<<<<<<<<<<<
// TODO - NEED TO CREATE OPPOSITE LOGIC FOR "past" DIRECTION LIKE BELOW vvvvv
if (calendarViewDirection === "future") {
if (saturday1month !== sunday5month) {
hideSomeDaysForInitialView = true;
}
if (initialViewStartDate.getMonth() === sunday5month) {
hideSecondMonth = true;
if (currentMonthEnd > initialViewEndDate) {
// should part of 1st month be hidden?
hideSomeDaysForInitialView = true;
}
}
}
let myPromise = new Promise((resolve, reject) => {
const response = config.customSelectableDatesCallback(
initialViewStartDate,
initialViewEndDate
);
resolve(response);
});
return myPromise.then((response) => {
const initialData = {
todayDate: todayDate,
initialViewStartDate: initialViewStartDate,
initialViewEndDate: initialViewEndDate,
calendarViewDirection: calendarViewDirection,
initialShopTimeSlotsResponse: response,
hideSomeDaysForInitialView: hideSomeDaysForInitialView,
hideSecondMonth: hideSecondMonth,
};
return initialData;
});
},
initializeComponent(initialData) {
this.setCalendarData(initialData);
},
scrollToElement(elementId, speed, easing) {
function scrollTopSmooth(wrapper, target, duration = 300, timingName = "linear") {
const initY = wrapper.scrollTop;
@ -190,280 +329,259 @@ export default {
scrollTopSmooth(wrapper, targetMonth, 800, "ease-in-out");
},
goForward() {
const fieldset = document.querySelector("#date-picker-fieldset");
if (this.hasPartialMonthInitialView) {
const monthToScrollTo = document.querySelector(
".partial-month-initial-view:not(.current-month):not(.month-hidden)"
);
this.scrollToElement(monthToScrollTo.id);
this.hasPartialMonthInitialView = false; // removes hidden styling on days
} else {
this.showAnotherMonth();
}
},
async setCalendarData() {
// CAN I SET TODAY AND THE "END DATE OF THE INITIAL VIEW" BEFORE I RUN THROUGH THE MONTHS?
const todayDateNum = this.todayDate.getDate();
const todayDayIndex = this.todayDate.getDay(); // get day of week index of today (0-6)
const todayYearNum = this.todayDate.getFullYear();
const todayMonthIndex = this.todayDate.getMonth() + 1;
const todayMonthEndDateNum = new Date(todayYearNum, todayMonthIndex, 0).getDate();
async setCalendarData(config = {}) {
this.today = config.todayDate;
this.hideSomeDaysForInitialView = config.hideSomeDaysForInitialView;
let hideSecondMonth = config.hideSecondMonth;
const direction = config.calendarViewDirection;
this.lastApiCallStartDate =
todayYearNum.toString() +
"-" +
forceTwoDigitString(todayMonthIndex) +
"-" +
forceTwoDigitString(todayDateNum);
const monthsAfterToLoadOffset = 12; // TO BE MADE "CONSTANTS"
const monthsBeforeToLoadOffset = 36; // TO BE MADE "CONSTANTS"
const week1endDateNum = todayDateNum + (6 - todayDayIndex);
const weeksLeft =
this.initialViewRowsToShow -
1 -
Math.ceil((todayMonthEndDateNum - week1endDateNum) / 7);
const daysLeftover = 7 - ((todayMonthEndDateNum - week1endDateNum) % 7);
const initialViewEndDate = 7 * weeksLeft + daysLeftover;
if (weeksLeft < 1) this.hasPartialMonthInitialViewNEW = false;
const initialViewEndDateYear =
this.hasPartialMonthInitialViewNEW && todayMonthIndex === 12
? todayYearNum + 1
: todayYearNum;
const initialViewEndDateMonth = this.hasPartialMonthInitialViewNEW
? todayMonthIndex + 1
: todayMonthIndex;
const initialViewEndDateDate = this.hasPartialMonthInitialViewNEW
? initialViewEndDate
: todayMonthEndDateNum;
this.lastApiCallEndDate =
initialViewEndDateYear.toString() +
"-" +
forceTwoDigitString(initialViewEndDateMonth) +
"-" +
forceTwoDigitString(initialViewEndDateDate);
// make new API call with this month's start and end dates
await this.updateSelectableDates(this.lastApiCallStartDate, this.lastApiCallEndDate);
config.initialShopTimeSlotsResponse.forEach((selectableDate) => {
this.selectableDatesData.push(selectableDate);
});
// GENERATE MONTHS AND PUSH THEM INTO ARRAY
const months = [];
if (this.calendarViewDirection === "future") {
const options = {
calendarViewDirection: direction,
monthsBeforeToLoadOffset: monthsBeforeToLoadOffset,
monthsAfterToLoadOffset: monthsAfterToLoadOffset,
initialViewStartDate: config.initialViewStartDate,
initialViewEndDate: config.initialViewEndDate,
hideSecondMonth: hideSecondMonth,
};
if (direction === "future") {
// first 0, then 1
for (let i = 0; i <= this.monthsAfterToLoadOffset; i++) {
months.push(await this.getMonthData(i));
for (let i = 0; i <= monthsAfterToLoadOffset; i++) {
months.push(await this.getMonthData(i, options));
}
} else if (this.calendarViewDirection === "past") {
} else if (direction === "past") {
// first 0, then -1
for (let i = 0; i >= 0 - this.monthsAfterToLoadOffset; i--) {
months.unshift(this.getMonthData(i));
for (let i = 0; i >= 0 - monthsBeforeToLoadOffset; i--) {
months.unshift(this.getMonthData(i, options));
}
} else {
// TK - IF A CALENDAR WITH BOTH PAST AND FUTURE WAS EVER NEEDED
// TODO - IF A CALENDAR WITH BOTH PAST AND FUTURE WAS EVER NEEDED
// for (let i = this.monthsAfterToLoadOffset; i >= this.monthsBeforeToLoadOffset; i--) {
// months.push(this.getMonthDataPAST(i));
// }
}
this.months = months;
this.isLoading = false;
},
async getMonthData(offset = requiredParameter()) {
const direction = this.calendarViewDirection; // "past" or "future"
let yearNum;
let monthIndex;
async getMonthData(offset = requiredParameter(), options) {
/* options will contain:
calendarViewDirection (string)
initialViewStartDateNum (number)
initialViewEndDateNum (number)
monthsBeforeToLoadOffset (number),
monthsAfterToLoadOffset (number),
hideSecondMonth (boolean),
data used:
todayDate (date object)
this.hideSomeDaysForInitialView (string)
*/
let monthIndex = this.todayMonthIndex + offset; // mutable
let yearNum = this.todayYearNum; // mutable
const calendarViewDirection = options.calendarViewDirection;
const initialViewStartDate = options.initialViewStartDate;
const initialViewEndDate = options.initialViewEndDate;
const hideSecondMonth = options.hideSecondMonth; // <<<<<<<<<<<<<
const dates = [];
let monthClass = "";
let initialViewEndDate; // only used for setCalendarData FUTURE
let initialViewStartDate; // only used for setCalendarData PAST
let currentWeekEndDateNum; // only used for setCalendarData PAST
let currentWeekStartDateNum; // only used for setCalendarData FUTURE
let firstMonthDayTally; // only used for setCalendarData on 1st month generated
const datesArray = [];
const todayDateNum = this.todayDate.getDate(); // TODO - DEDUPE?; SAME SET IN setCalendarData
let isMonthThatHidesSomeDaysForInitialView;
yearNum = this.todayDate.getFullYear(); // TODO - DEDUPE?; SAME SET IN setCalendarData
monthIndex = this.todayDate.getMonth() + offset; // TODO - DEDUPE?; SAME SET IN setCalendarData
if (direction === "future" && offset > 0) {
while (monthIndex > 11) {
if (calendarViewDirection === "future" && offset > 0) {
while (monthIndex > 12) {
monthIndex = monthIndex - 12;
yearNum++;
}
} else if (direction === "past" && offset < 0) {
while (monthIndex < 0) {
} else if (calendarViewDirection === "past" && offset < 0) {
while (monthIndex < 1) {
monthIndex = 12 + monthIndex;
yearNum--;
}
}
const todayDayIndex = this.todayDate.getDay(); // get day of week index of today (0-6) // TODO - DEDUPE?; SAME SET IN setCalendarData
currentWeekStartDateNum =
todayDayIndex >= todayDateNum ? 1 : todayDateNum - todayDayIndex; // FUTURE
const monthEndDate = new Date(yearNum, monthIndex + 1, 0); // BOTH // TODO - DEDUPE?; SAME SET IN setCalendarData
const monthEndDate = new Date(yearNum, monthIndex, 0); // BOTH
let monthEndDateNum = monthEndDate.getDate(); // BOTH
currentWeekEndDateNum = todayDateNum + 6 - todayDayIndex; // PAST, aka Sat. (ok if it's larger than the month end?)
if (offset === 0 && direction === "past" && monthEndDateNum > currentWeekEndDateNum) {
monthEndDateNum = currentWeekEndDateNum; // PAST
if (
offset === 0 &&
calendarViewDirection === "past" &&
monthEndDateNum > this.currentWeekEndDateNum
) {
monthEndDateNum = this.currentWeekEndDateNum; // PAST
}
const monthStartDateNum =
offset === 0 && direction === "future" ? currentWeekStartDateNum : 1; // FUTURE
const monthStartDate = new Date(yearNum, monthIndex, monthStartDateNum); // BOTH
offset === 0 && calendarViewDirection === "future"
? this.currentWeekStartDateNum
: 1; // FUTURE
const monthStartDate = new Date(yearNum, monthIndex - 1, monthStartDateNum); // BOTH
const startDateDayIndex = monthStartDate.getDay(); // FUTURE
const endDateDayIndex = monthEndDate.getDay(); // PAST
if (offset === 0 && direction === "future") {
firstMonthDayTally = monthStartDateNum - startDateDayIndex; // FUTURE (starts low, counts up)
monthClass = monthClass + " current-month";
}
if (offset === 0 && direction === "past") {
firstMonthDayTally = currentWeekEndDateNum; // PAST (starts high, counts down)
monthClass = monthClass + " current-month";
}
while (
direction === "future" &&
offset === 0 &&
firstMonthDayTally <= monthEndDateNum
) {
// FUTURE
firstMonthDayTally = firstMonthDayTally + 7;
this.initialViewRowsTally++;
}
while (
direction === "past" &&
offset === 0 &&
firstMonthDayTally >= monthStartDateNum
) {
// PAST
firstMonthDayTally = firstMonthDayTally - 7;
this.initialViewRowsTally++;
}
if (Math.abs(offset) === 1) {
if (this.initialViewRowsTally < this.initialViewRowsToShow) {
// INDICATES A SPLIT-MONTH INITIAL VIEW
initialViewEndDate = 6 - startDateDayIndex + monthStartDateNum; // FUTURE
initialViewStartDate = monthEndDateNum - endDateDayIndex; // PAST
this.initialViewRowsTally++;
} else {
// INDICATES AN INITIAL VIEW SHOWING ONLY ONE MONTH
// monthClass = monthClass + " initial-month-hidden";
monthClass = monthClass + " month-hidden";
this.hasPartialMonthInitialView = false;
}
while (this.initialViewRowsTally < this.initialViewRowsToShow) {
initialViewEndDate = initialViewEndDate + 7;
initialViewStartDate = initialViewStartDate - 7;
this.initialViewRowsTally++;
}
}
if (Math.abs(offset) > 1) {
if (Math.abs(offset) === 1 && hideSecondMonth) {
monthClass = monthClass + " month-hidden";
} else if (Math.abs(offset) > 1) {
monthClass = monthClass + " month-hidden";
}
if (
Math.abs(offset) === options.monthsAfterToLoadOffset &&
calendarViewDirection === "future"
) {
monthClass = monthClass + " last-available-month";
}
if (
Math.abs(offset) === options.monthsBeforeToLoadOffset &&
calendarViewDirection === "past"
) {
// TODO - re-check this logic if past direction
monthClass = monthClass + " last-available-month";
}
// populate datesArray
// populate dates array
for (let i = monthStartDateNum; i <= monthEndDateNum; i++) {
let dayClasses = "";
let dateString =
yearNum.toString() +
"-" +
forceTwoDigitString(monthIndex) +
"-" +
forceTwoDigitString(i);
const thisDate = {
// NOTE: uses monthNum (1-based), NOT monthIndex (0-based)
year: yearNum,
month: monthIndex + 1,
month: monthIndex,
date: i,
dateString:
yearNum.toString() +
"-" +
forceTwoDigitString(monthIndex + 1) +
"-" +
forceTwoDigitString(i),
dateString: dateString,
};
if (offset === 0 && i === todayDateNum) {
if (offset === 0 && i === this.todayDateNum) {
dayClasses += "current-day";
}
if (offset === 0 && i < todayDateNum && direction === "future") {
if (offset === 0 && i < this.todayDateNum && calendarViewDirection === "future") {
dayClasses += "unavailable-day";
}
if (offset === 0 && i > todayDateNum && direction === "past") {
if (offset === 0 && i > this.todayDateNum && calendarViewDirection === "past") {
dayClasses += "unavailable-day";
}
if (Math.abs(offset) === 1 && direction === "future" && i > initialViewEndDate) {
if (
this.hideSomeDaysForInitialView &&
initialViewEndDate.getMonth() + 1 === monthIndex &&
initialViewEndDate.getDate() < i
) {
dayClasses += "day-hidden";
isMonthThatHidesSomeDaysForInitialView = true;
}
if (Math.abs(offset) === 1 && direction === "past" && i < initialViewStartDate) {
dayClasses += "day-hidden";
}
const dateObject = {
dateNum: i,
dayClasses: dayClasses,
inputValue: thisDate,
isSelectable:
this.selectableDatesData.findIndex(
(date) => date.dateString === dateString
) > -1
? true
: false,
};
datesArray.push(dateObject);
dates.push(dateObject);
}
const monthToAdd = {
monthLabel: monthsOfYear[monthIndex],
monthLabel: MONTHS_OF_YEAR[monthIndex - 1],
monthIndex: monthIndex,
monthString: monthsOfYear[monthIndex] + "-" + yearNum?.toString(),
monthString: MONTHS_OF_YEAR[monthIndex - 1] + "-" + yearNum?.toString(),
yearNum: yearNum,
dates: datesArray,
dates: dates,
startDateDayIndex: startDateDayIndex,
monthClass: monthClass,
isMonthThatHidesSomeDaysForInitialView: isMonthThatHidesSomeDaysForInitialView,
};
// TODO: CHANGE NAME OF MONTH AND DATE's "___String" to "_____Id"; it's better
return monthToAdd;
},
async showAnotherMonth() {
this.isLoading = true;
let monthToShow;
if (this.calendarViewDirection === "future") {
monthToShow = this.months.find((month) =>
month.monthClass.includes("month-hidden")
);
}
if (this.calendarViewDirection === "past") {
// TODO: UPDATE THIS WITH CORRECT PAST LOOKING LOGIC
monthToShow = this.months.find((month) =>
month.monthClass.includes("month-hidden")
let monthStartDateNum = 0;
if (this.hideSomeDaysForInitialView) {
monthToShow = this.months.find(
({ isMonthThatHidesSomeDaysForInitialView }) =>
isMonthThatHidesSomeDaysForInitialView
);
// find the first day-hidden to become the next api call start date
monthStartDateNum =
monthToShow.dates.find(({ dayClasses }) => dayClasses.includes("day-hidden"))
.dateNum - 1; // TRY 2
} else {
if (this.calendarViewDirection === "future") {
monthToShow = this.months.find((month) =>
month.monthClass.includes("month-hidden")
);
}
if (this.calendarViewDirection === "past") {
// TODO: UPDATE THIS WITH CORRECT PAST LOOKING LOGIC
monthToShow = this.months.find((month) =>
month.monthClass.includes("month-hidden")
);
}
}
if (monthToShow) {
// make new API call with this month's start and end dates
await this.updateSelectableDates(
monthToShow.dates[0].inputValue.dateString,
monthToShow.dates[monthStartDateNum].inputValue.dateString,
monthToShow.dates[monthToShow.dates.length - 1].inputValue.dateString
);
this.isLoading = false;
// this.hasSplitInitialView = false; // if was partial view, this removes hidden styling on days
this.hideSomeDaysForInitialView = false; // if hid days on initial partial view, this removes hidden styling on days
monthToShow.monthClass = monthToShow.monthClass.replace(" month-hidden", "");
this.scrollToElement(monthToShow.monthString);
if (monthToShow.monthClass.includes("last-available-month"))
this.disableViewMoreDatesButton = true;
}
},
isSelectableDate(thisDate) {
const testForDate = (dateInArray) => {
return dateInArray.dateString === thisDate.dateString;
};
const isSelectable =
this.selectableDatesData.findIndex(testForDate) > -1 ? true : false;
return isSelectable;
},
async updateSelectableDates(monthStart, monthEnd) {
const responseData = await this.customSelectableDatesCallback(monthStart, monthEnd);
responseData?.forEach((newObj) => {
const moreSelectableDates = await this.customSelectableDatesCallback(
monthStart,
monthEnd
);
moreSelectableDates.forEach((selectableDate) => {
const index = this.selectableDatesData.findIndex(
(obj) => obj.dateString === newObj.dateString
(obj) => obj.dateString === selectableDate.dateString
);
if (index === -1) this.selectableDatesData.push(newObj);
if (index === -1) this.selectableDatesData.push(selectableDate);
this.months.forEach((month) => {
// TODO: avoid checking all calendar date; maybe only ones between monthStart and monthEnd as defined above?
month.dates.forEach((date) => {
if (date.inputValue.dateString === selectableDate.dateString) {
date["isSelectable"] = true;
}
});
});
});
},
},
components: {
loader,
},
};
</script>
<style lang="scss" scoped>
.date-picker-hidden {
opacity: 0;
max-height: 0;
}
.date-picker {
overflow: hidden;
position: relative;
@ -472,6 +590,17 @@ export default {
fieldset {
overflow-y: auto;
height: 88%;
position: relative;
}
.loader {
position: absolute;
height: 2rem;
width: 2rem;
&::after {
width: 100%;
height: 100%;
}
}
.calendar-grid-container {
margin: 0 auto 2rem auto;
@ -547,7 +676,7 @@ export default {
outline: none;
height: 1.35rem;
opacity: 1;
transition: ease all 250ms;
transition: height ease 250ms, opacity ease 250ms;
input[type="radio"] {
position: absolute; //override bootstrap
@ -685,11 +814,16 @@ export default {
overflow: hidden;
display: flex;
margin: 0;
max-height: 0;
}
}
&.month-hidden {
opacity: 0;
max-height: 0;
margin-bottom: 0;
}
&.last-available-month:not(&.month-hidden) {
margin-bottom: 14rem;
}
}
#bottom-spacer {

View file

@ -0,0 +1,24 @@
const TIMINGFUNC_MAP = {
linear: (t) => t,
"ease-in": (t) => t * t,
"ease-out": (t) => t * (2 - t),
"ease-in-out": (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),
};
const BUFFER_OFFSET = 10;
const MONTHS_OF_YEAR = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
export { TIMINGFUNC_MAP, BUFFER_OFFSET, MONTHS_OF_YEAR };

View file

@ -0,0 +1,15 @@
const selectableDaysOptions = Object.freeze({
CUSTOM: "custom",
PAST: "past",
});
const requiredParameter = () => {
throw new Error("parameter is required");
};
const forceTwoDigitString = (monthNum) => {
const newString = monthNum.toString();
return newString.length === 1 ? "0" + newString : newString;
};
export { selectableDaysOptions, requiredParameter, forceTwoDigitString };

View file

@ -74,7 +74,10 @@ export default {
data: payload,
}).then(
(response) => {
resolve(response);
// simulate a delayed response
setTimeout(() => {
resolve(response);
}, 2000);
},
(error) => {
return reject(error.response);

View file

@ -31,9 +31,9 @@
<date-picker
selectableDatesSetting="custom"
ref="datePicker"
v-model="selectedDate"
:customSelectableDatesCallback="getAvailableDates" />
<!-- todayOverrideDateString="2023-08-06T03:00:00" -->
:customSelectableDatesCallback="getAvailableDatesMethod" />
<funnel-footer
cmsWidgetName="FunnelFooterWidget"
ref="funnelFooter"
@ -73,6 +73,33 @@ import store from "@/store";
defineRule("date-required", required(errorMessages.DATE_REQUIRED));
const getAvailableDates = async (startDate, endDate) => {
// USING DATES PASSED, MAKE AN API CALL
const newShopTimeSlotsResponse = await baseMixin.methods.dispatchStoreAction(
storeActions.GET_SHOP_TIME_SLOTS,
{
startDate: startDate,
endDate: endDate,
shopAppointmentType: "mobile", // TODO - PASS THIS IN FROM PREVIOUS PAGE?
},
false
);
const newShopTimeSlots = newShopTimeSlotsResponse.data;
return convertApiResponse(newShopTimeSlots.days);
};
const convertApiResponse = (responseData) => {
// DATA CONVERSION
responseData?.forEach((date) => {
const dateString = date.date;
date.dateString = dateString;
const dateStringPieces = dateString.split("-");
date.year = Number(dateStringPieces[0]);
date.month = Number(dateStringPieces[1]);
date.date = Number(dateStringPieces[2]);
});
return responseData;
};
export default {
name: "schedule",
data() {
@ -85,6 +112,28 @@ export default {
async beforeRouteEnter(to, from, next) {
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
const datePickerInitialDataPromise = datePicker.methods.loadInitialData({
// setup config options for date-picker
selectableDatesSetting: "custom",
initialViewRowsToShow: 5,
customSelectableDatesCallback: getAvailableDates,
/* vvvvv SAVE THESE FOR TESTING PURPOSES FOR NOW vvvvv
// todayOverrideDateString: "2023-04-29T03:00:00", // show partial
// todayOverrideDateString: "2023-04-30T03:00:00", //
// todayOverrideDateString: "2023-05-02T03:00:00", // ONE MONTH ONLY
// todayOverrideDateString: "2023-05-06T03:00:00", // ONE MONTH ONLY
// todayOverrideDateString: "2023-05-07T03:00:00", // show partial
// todayOverrideDateString: "2023-05-30T03:00:00", //
// todayOverrideDateString: "2023-06-30T03:00:00", //
// todayOverrideDateString: "2023-07-01T03:00:00", // show partial && ONE MONTH ONLY
// todayOverrideDateString: "2023-07-02T03:00:00", // ONE MONTH ONLY
// todayOverrideDateString: "2023-07-12T03:00:00", // show partial
// todayOverrideDateString: "2023-08-31T03:00:00",
// todayOverrideDateString: "2023-09-30T03:00:00", // show partial
*/
});
const alertReasonsPromise = getAlertReasons(store.getters.order.serviceLocation.zipCodeCtu);
@ -98,6 +147,10 @@ export default {
resultKey: "alertReasons",
promise: alertReasonsPromise,
},
{
resultKey: "datePickerInitialData",
promise: datePickerInitialDataPromise,
},
];
const resultMap = await settleAllPromises(promiseResultMap);
@ -106,6 +159,7 @@ export default {
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
vm.setData(resultMap.alertReasons);
vm.$refs.datePicker.initializeComponent(resultMap.datePickerInitialData);
});
},
computed: {
@ -129,40 +183,21 @@ export default {
return true;
// NEED TODO - WHAT ARE PAGE REQ'S FOR THIS PAGE?
},
async getAvailableDates(startDate, endDate) {
// USING DATES PASSED, MAKE AN API CALL
const newShopTimeSlotsResponse = await baseMixin.methods.dispatchStoreAction(
storeActions.GET_SHOP_TIME_SLOTS,
{
startDate: startDate,
endDate: endDate,
shopAppointmentType: "mobile", // TODO - PASS THIS IN FROM PREVIOUS PAGE?
},
false
);
const newShopTimeSlots = newShopTimeSlotsResponse.data;
// console.log("newShopTimeSlots ", newShopTimeSlots)
async getAvailableDatesMethod(startDate, endDate) {
/* TODO - DO WE NEED TO KEEP AN AGGREGATE OF ALL DATES RETURNED
FOR TIMESLOTS in this.selectableDatesData? IS selectableDatesData EVEN NEEDED?
// ADD API CALL RESULTS TO EXISTING DATE DATA
this.selectableDatesData = this.selectableDatesData.concat(
this.convertApiResponse(newShopTimeSlots.days)
);
console.log("this.selectableDatesData is now: ", this.selectableDatesData);
// // ADD API CALL RESULTS TO EXISTING DATE DATA
// this.selectableDatesData = this.selectableDatesData.concat(
// this.convertApiResponse(newShopTimeSlots.days)
// );
// // console.log("this.selectableDatesData is now: ", this.selectableDatesData);
// RETURN AGGREGATE DATE DATA
return this.selectableDatesData;
},
convertApiResponse(responseData) {
// DATA CONVERSION
responseData?.forEach((date) => {
const dateString = date.date;
date.dateString = dateString;
const dateStringPieces = dateString.split("-");
date.year = Number(dateStringPieces[0]);
date.month = Number(dateStringPieces[1]);
date.date = Number(dateStringPieces[2]);
});
return responseData;
// // RETURN AGGREGATE DATE DATA
// return this.selectableDatesData;
*/
return await getAvailableDates(startDate, endDate);
},
setData(alertReasonsData) {
if (alertReasonsData) {
@ -192,9 +227,6 @@ export default {
console.log("error fetching alert reasons..");
});
},
getAvailableDates(startDate, endDate) {
return this.mockSelectableDatesData;
},
getServiceZipCtuCodeFromStore() {
return store.getters.order.serviceLocation.zipCodeCtu;
},
@ -236,5 +268,3 @@ export default {
mounted() {},
};
</script>
<style lang="scss"></style>