CSR-886: refactoring, fixes & set up API calls

This commit is contained in:
Adam Caouette 2023-05-04 14:33:29 -04:00
parent cfb4c73927
commit 32906736dd
3 changed files with 105 additions and 241 deletions

View file

@ -1,11 +1,7 @@
<template>
<div v-if="months" class="date-picker text-center" :class="calendarViewDirection">
<!-- <p style="text-align: left">selectableDatesData: {{ selectableDatesData }}</p> -->
<!-- <p style="text-align: left">months: {{ months }}</p> -->
<!-- <p style="text-align: left">hasPartialMonthInitialView: {{ hasPartialMonthInitialView }}</p> -->
<fieldset id="date-picker-fieldset" ref="datePickerFieldset">
<legend class="sr-only">Date Picker</legend>
<!-- <h4 id="tracker" ref="tracker">currentScrollTop: {{ currentScrollTop }}</h4> -->
<legend class="sr-only">Select a day and time</legend>
<div
v-for="month in months"
:key="`${month.monthLabel}-${month.yearNum?.toString()}`"
@ -36,11 +32,13 @@
<div class="grid-item caption"><span class="sr-only">Saturday</span>S</div>
<div
v-for="date in month.dates"
:key="`${month.monthLabel}-${date.dateNum.toString()}-${months.length}`"
:key="date.inputValue.dateString"
:id="date.inputValue.dateString"
class="grid-item radio-wrapper"
:class="[
date.dateNum === 1 ? 'first-day-' + month.startDateDayIndex : '',
date.dayClasses,
isSelectableDate(date.inputValue) ? 'selectable-day' : '',
]">
<input
:disabled="!isSelectableDate(date.inputValue)"
@ -74,14 +72,16 @@ const SelectableDaysOptions = Object.freeze({
PAST: "past",
});
const requiredParameter = () => { throw new Error('parameter is required'); };
const requiredParameter = () => {
throw new Error("parameter is required");
};
function forceTwoDigitString(monthNum) { // TODO : MOVE THIS SOMEWHERE ELSE; A UTILITIES MIXIN?
let newString = monthNum.toString();
const forceTwoDigitString = (monthNum) => {
const newString = monthNum.toString();
return newString.length === 1 ? "0" + newString : newString;
}
};
const TIMINGFUNC_MAP = { // TODO : MOVE THIS SOMEWHERE ELSE; A CONSTANT?
const TIMINGFUNC_MAP = {
linear: (t) => t,
"ease-in": (t) => t * t,
"ease-out": (t) => t * (2 - t),
@ -97,19 +97,19 @@ export default {
monthsAfterToLoadOffset: 12,
selectableDatesData: [], // NOTE: uses monthNum (1-based), NOT monthIndex (0-based)
hasPartialMonthInitialView: true,
hasPartialMonthInitialViewNEW: true,
initialViewRowsToShow: 5,
initialViewRowsTally: 0,
isAddingNewMonth: false,
currentScrollTop: 0,
months: null,
lastApiCallStartDate: null,
lastApiCallEndDate: null,
};
},
mounted() {
this.setCalendarData();
// this.trackScrollTop(); // TODO - THIS FUNCTION IS TEMPORARY; REMOVE WHEN SCROLLING WORK IS DONE
},
props: {
selectableDatesSetting: {
type: String,
@ -149,27 +149,18 @@ export default {
return this.modelValue;
},
set(newSelectedDate) {
console.log("newSelectedDate ", newSelectedDate);
this.$emit("update:modelValue", newSelectedDate);
},
},
},
methods: {
// trackScrollTop() { // TODO - THIS FUNCTION IS TEMPORARY; REMOVE WHEN SCROLLING WORK IS DONE
// setInterval(() => {
// this.currentScrollTop = this.$refs.datePickerFieldset?.scrollTop.toString();
// }, 250);
// },
scrollToElement(elementId, speed, easing) {
function scrollTopSmooth(wrapper, target, duration = 300, timingName = "linear") {
const initY = wrapper.scrollTop;
const wrapperRect = wrapper.getBoundingClientRect();
// console.log("wrapperRect: ", wrapperRect)
const targetRect = target.getBoundingClientRect();
// console.log("targetRect: ", targetRect)
const targetY = targetRect.top - wrapperRect.top - BUFFER_OFFSET;
// console.log("targetY: ", targetY)
const timingFunc = TIMINGFUNC_MAP[timingName];
let start = null;
@ -181,15 +172,9 @@ export default {
time = Math.min(1, (timestamp - start) / duration);
let percentageNew = timingFunc(time);
let distanceToGo = targetY; // 592 is the goal for June-2023 targetY
let distanceToGo = targetY;
let thisDistance = percentageNew * distanceToGo;
// console.log("", progress.toFixed(2), " - initY: ", initY.toFixed(2))
// console.log(" ", progress.toFixed(2), " - percentageNew: ", percentageNew.toFixed(2))
// console.log(" ", progress.toFixed(2), " - distanceToGo: ", distanceToGo.toFixed(2))
// console.log(" ", progress.toFixed(2), " - thisDistance: ", thisDistance.toFixed(2))
// console.log(" ")
wrapper.scrollTo(0, initY + thisDistance);
if (percentageNew < 1) {
@ -200,13 +185,8 @@ export default {
window.requestAnimationFrame(step);
}
// scrollTopSmooth( window.scrollY, 300, "ease-in-out" );
// const wrapper = document.getElementById("date-picker-fieldset");
const wrapper = this.$refs.datePickerFieldset;
// console.log("wrapper.scrollTop: ", wrapper.scrollTop)
// get Target
let targetMonth = document.getElementById(elementId);
const targetMonth = document.getElementById(elementId);
scrollTopSmooth(wrapper, targetMonth, 800, "ease-in-out");
},
@ -214,23 +194,61 @@ export default {
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.$nextTick(() => {
setTimeout(() => {
this.scrollToElement(monthToScrollTo.id);
}, 0.01);
});
this.hasPartialMonthInitialView = false; // 1st click removes hidden styling
this.scrollToElement(monthToScrollTo.id);
this.hasPartialMonthInitialView = false; // removes hidden styling on days
} else {
this.showAnotherMonth(); // 2nd click adds 1 month data
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();
this.lastApiCallStartDate =
todayYearNum.toString() +
"-" +
forceTwoDigitString(todayMonthIndex) +
"-" +
forceTwoDigitString(todayDateNum);
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);
// GENERATE MONTHS AND PUSH THEM INTO ARRAY
const months = [];
if (this.calendarViewDirection === "future") {
@ -262,10 +280,10 @@ export default {
let currentWeekStartDateNum; // only used for setCalendarData FUTURE
let firstMonthDayTally; // only used for setCalendarData on 1st month generated
const datesArray = [];
const todayDateNum = this.todayDate.getDate();
const todayDateNum = this.todayDate.getDate(); // TODO - DEDUPE?; SAME SET IN setCalendarData
yearNum = this.todayDate.getFullYear();
monthIndex = this.todayDate.getMonth() + offset;
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) {
@ -279,10 +297,10 @@ export default {
}
}
const todayDayIndex = this.todayDate.getDay(); // get day of week index of today (0-6)
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
const monthEndDate = new Date(yearNum, monthIndex + 1, 0); // BOTH // TODO - DEDUPE?; SAME SET IN setCalendarData
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) {
@ -324,11 +342,13 @@ export default {
}
if (Math.abs(offset) === 1) {
if (this.initialViewRowsTally < this.initialViewRowsToShow) { // INDICATES A SPLIT-MONTH INITIAL VIEW
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
} else {
// INDICATES AN INITIAL VIEW SHOWING ONLY ONE MONTH
// monthClass = monthClass + " initial-month-hidden";
monthClass = monthClass + " month-hidden";
this.hasPartialMonthInitialView = false;
@ -344,23 +364,6 @@ export default {
if (Math.abs(offset) > 1) {
monthClass = monthClass + " month-hidden";
}
if (this.selectableDatesSetting === "custom") {
const monthStart = {
year: yearNum,
month: monthIndex + 1,
date: offset === 0 ? todayDateNum : monthStartDateNum,
};
const monthEnd = {
year: monthEndDate.getFullYear(),
month: monthEndDate.getMonth() + 1,
date: monthEndDateNum,
};
// get available dates
await this.updateSelectableDates(monthStart, monthEnd); // v v v v v v
// MAKE API CALL, ADDS NEW DATES TO this.selectableDatesData
}
// populate datesArray
for (let i = monthStartDateNum; i <= monthEndDateNum; i++) {
@ -378,7 +381,6 @@ export default {
"-" +
forceTwoDigitString(i),
};
let isSelectableDate = this.isSelectableDate(thisDate);
if (offset === 0 && i === todayDateNum) {
dayClasses += "current-day";
}
@ -394,16 +396,12 @@ export default {
if (Math.abs(offset) === 1 && direction === "past" && i < initialViewStartDate) {
dayClasses += "day-hidden";
}
if (this.selectableDatesSetting === "custom" && isSelectableDate) {
dayClasses += " selectable-day";
}
const dateObject = {
dateNum: i,
dayClasses: dayClasses,
inputValue: thisDate,
};
// console.log("dateObject: ", dateObject)
datesArray.push(dateObject);
}
@ -416,9 +414,7 @@ export default {
startDateDayIndex: startDateDayIndex,
monthClass: monthClass,
};
// TODO: CHANGE NAME OF MONTH AND DATE's "___String" to "_____Id"; it's better
return monthToAdd;
},
async showAnotherMonth() {
@ -434,72 +430,40 @@ export default {
month.monthClass.includes("month-hidden")
);
}
// console.log("monthToShow: ", monthToShow);
// make new API call with this month's start and end dates
await this.updateSelectableDates(
monthToShow.dates[0].inputValue.dateString,
monthToShow.dates[monthToShow.dates.length - 1].inputValue.dateString
);
monthToShow.monthClass = monthToShow.monthClass.replace(" month-hidden", "");
// scroll the next question into view
this.$nextTick(() => {
setTimeout(() => {
// put call to scroll here
// ID to scroll to: monthToShow.monthString
this.scrollToElement(monthToShow.monthString);
}, 0.01);
});
if (monthToShow) {
// make new API call with this month's start and end dates
await this.updateSelectableDates(
monthToShow.dates[0].inputValue.dateString,
monthToShow.dates[monthToShow.dates.length - 1].inputValue.dateString
);
monthToShow.monthClass = monthToShow.monthClass.replace(" month-hidden", "");
this.scrollToElement(monthToShow.monthString);
}
},
isSelectableDate(thisDate) {
const testForDate = (dateInArray) => {
// console.log(" running isSelectableDate/testForDate... thisDate.month: ", thisDate.month, " / thisDate.date: ", thisDate.date)
// console.log(" running isSelectableDate/testForDate... dateInArray.month: ", dateInArray.month, " / dateInArray.date: ", dateInArray.date)
// debugger; // eslint-disable-line no-debugger
return dateInArray.dateString === thisDate.dateString;
};
const isSelectable =
this.selectableDatesData.findIndex(testForDate) > -1 ? true : false;
// debugger; // eslint-disable-line no-debugger
// console.log("running isSelectableDate... thisDate: ", thisDate, " / isSelectable: ", isSelectable)
return isSelectable;
},
async updateSelectableDates(monthStart, monthEnd) {
// console.log("running DP updateSelectableDates... ")
const test = await this.customSelectableDatesCallback(monthStart, monthEnd);
const responseData = await this.customSelectableDatesCallback(monthStart, monthEnd);
test?.forEach((newObj) => {
responseData?.forEach((newObj) => {
const index = this.selectableDatesData.findIndex(
(obj) => obj.dateString === newObj.dateString
);
if (index === -1) this.selectableDatesData.push(newObj);
});
// console.log("running DP updateSelectableDates... this.selectableDatesData now: ", this.selectableDatesData)
},
},
};
</script>
<style lang="scss" scoped>
#tracker {
position: absolute;
top: 5px;
right: 5px;
z-index: 99999999999;
background: rgba(255, 255, 255, 0.6);
font-size: 12px;
font-weight: bold;
width: 40%;
text-align: left;
padding: 5px;
display: none;
}
.date-picker {
overflow: hidden;
position: relative;

View file

@ -60,14 +60,18 @@ export default {
},
/* istanbul ignore next */
callMockHttpClient({ method, endpoint }) {
callMockHttpClient({ method, endpoint, payload }) {
// For Mock use only!
return new Promise((resolve, reject) => {
axios({
method: method,
url: endpoint,
crossDomain: true,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
responseType: {},
data: payload,
}).then(
(response) => {
resolve(response);

View file

@ -1,6 +1,5 @@
<template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
<!-- <h3>shopTimeSlots: {{ shopTimeSlots }}</h3> -->
<div class="page-container-grouped-styles">
<loadingModal ref="loadingModal" />
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
@ -23,6 +22,7 @@
selectableDatesSetting="custom"
v-model="selectedDate"
:customSelectableDatesCallback="getAvailableDates" />
<!-- todayOverrideDateString="2023-08-06T03:00:00" -->
<funnel-footer
cmsWidgetName="FunnelFooterWidget"
ref="funnelFooter"
@ -64,126 +64,19 @@ export default {
data() {
return {
selectedDate: null,
mockSelectableDatesData: [
{ dateString: "2023-04-28" },
{ dateString: "2023-05-04" },
{ dateString: "2023-05-09" },
{ dateString: "2023-05-11" },
{ dateString: "2023-05-17" },
{ dateString: "2023-05-18" },
{ dateString: "2023-05-19" },
{ dateString: "2023-05-23" },
{ dateString: "2023-05-24" },
{ dateString: "2023-05-25" },
{ dateString: "2023-05-30" },
{ dateString: "2023-05-31" },
{ dateString: "2023-06-01" },
{ dateString: "2023-06-02" },
{ dateString: "2023-06-06" },
{ dateString: "2023-06-07" },
{ dateString: "2023-06-09" },
{ dateString: "2023-06-12" },
{ dateString: "2023-06-13" },
{ dateString: "2023-06-15" },
{ dateString: "2023-06-16" },
{ dateString: "2023-06-17" },
{ dateString: "2023-06-20" },
{ dateString: "2023-06-21" },
{ dateString: "2023-06-22" },
{ dateString: "2023-06-23" },
{ dateString: "2023-06-26" },
{ dateString: "2023-06-27" },
{ dateString: "2023-06-28" },
{ dateString: "2023-06-29" },
{ dateString: "2023-06-30" },
{ dateString: "2023-07-01" },
{ dateString: "2023-07-03" },
{ dateString: "2023-07-04" },
{ dateString: "2023-07-05" },
{ dateString: "2023-07-06" },
{ dateString: "2023-07-07" },
{ dateString: "2023-07-10" },
{ dateString: "2023-07-11" },
{ dateString: "2023-07-12" },
{ dateString: "2023-07-13" },
{ dateString: "2023-07-14" },
{ dateString: "2023-07-15" },
{ dateString: "2023-07-17" },
{ dateString: "2023-07-18" },
{ dateString: "2023-07-19" },
{ dateString: "2023-07-20" },
{ dateString: "2023-07-21" },
{ dateString: "2023-07-24" },
{ dateString: "2023-07-25" },
{ dateString: "2023-07-26" },
{ dateString: "2023-07-27" },
{ dateString: "2023-07-28" },
{ dateString: "2023-07-29" },
{ dateString: "2023-07-31" },
],
selectableDatesData: [],
};
/*
shopTimeSlots: {
estimatedServiceMinutesMinimum: 120,
estimatedServiceMinutesMaximum: 180,
days: [
{
date: "2023-04-25",
timeSlots: [
{
id: 12001400,
startTime: "12:00:00Z",
endTime: "14:00:00Z",
offerPremium: false,
},
],
},
{
date: "2023-04-26",
timeSlots: [
{ id: 12001400,
startTime: "12:00:00Z",
endTime: "14:00:00Z",
offerPremium: false,
},
{ id: 12001400,
startTime: "14:30:00Z",
endTime: "17:30:00Z",
offerPremium: false,
}
],
},
],
}
*/
},
async beforeRouteEnter(to, from, next) {
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
const getShopTimeSlotsPromise = baseMixin.methods.dispatchStoreAction(
storeActions.GET_SHOP_TIME_SLOTS,
{
startDate: "2023-01-01",
endDate: "2023-05-01",
shopAppointmentType: "ZZZZ",
}
);
// Settle promises and get results
const promiseResultMap = [
{
resultKey: "cmsContent",
promise: cmsContentPromise,
},
{
resultKey: "shopTimeSlots",
promise: getShopTimeSlotsPromise,
},
];
const resultMap = await settleAllPromises(promiseResultMap);
@ -191,7 +84,6 @@ export default {
// Call the "next" function to complete the transition to this page.
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
vm.shopTimeSlots = resultMap.shopTimeSlots;
});
},
computed: {
@ -213,15 +105,13 @@ export default {
// NEED TODO - WHAT ARE PAGE REQ'S FOR THIS PAGE?
},
async getAvailableDates(startDate, endDate) {
// console.log("S- async getAvailableDates");
// USING DATES PASSED, MAKE AN API CALL
const newShopTimeSlotsResponse = await baseMixin.methods.dispatchStoreAction(
storeActions.GET_SHOP_TIME_SLOTS,
{
startDate: startDate,
endDate: endDate,
shopAppointmentType: "ZZZZ",
shopAppointmentType: "mobile", // TODO - PASS THIS IN FROM PREVIOUS PAGE?
},
false
);
@ -229,14 +119,13 @@ export default {
// console.log("newShopTimeSlots ", newShopTimeSlots)
// ADD API CALL RESULTS TO EXISTING DATE DATA
const currentData = [
...this.selectableDatesData,
this.convertApiResponse(newShopTimeSlots.days),
];
this.selectableDatesData = this.selectableDatesData.concat(
this.convertApiResponse(newShopTimeSlots.days)
);
console.log("this.selectableDatesData is now: ", this.selectableDatesData);
// RETURN ALL THE DATE DATA (THE AGGREGATE)
return this.mockSelectableDatesData; // TODO REMOVE MOCK DATA
// return currentData;
// RETURN AGGREGATE DATE DATA
return this.selectableDatesData;
},
convertApiResponse(responseData) {
// DATA CONVERSION
@ -257,6 +146,13 @@ export default {
navigateToHeritageFunnel({ loadingModal: this.$refs.loadingModal });
},
},
watch: {
selectedDate(date) {
/* EXAMPLE RETURNED date OBJECT:
{ "year": 2023, "month": 5, "date": 25, "dateString": "2023-05-25" }
*/
},
},
components: {
funnelHeader,
funnelFooter,