CSR-886: new refactoring changes to use beforeRouteEnter on Schedule page to do heavy lifting
This commit is contained in:
parent
53166fc0ea
commit
d7f406b2ba
7 changed files with 477 additions and 288 deletions
|
|
@ -1,16 +0,0 @@
|
|||
const monthsOfYear = [
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
];
|
||||
|
||||
export { monthsOfYear };
|
||||
|
|
@ -1 +0,0 @@
|
|||
test.todo("some test to be written in the future");
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
24
src/digital-components/date-picker/mixins/constants.js
Normal file
24
src/digital-components/date-picker/mixins/constants.js
Normal 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 };
|
||||
15
src/digital-components/date-picker/mixins/helpers.js
Normal file
15
src/digital-components/date-picker/mixins/helpers.js
Normal 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 };
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue