Calender context moved into redux.
This commit is contained in:
@@ -1,262 +0,0 @@
|
|||||||
import React, { createContext, useState, ReactNode } from "react";
|
|
||||||
import {
|
|
||||||
format,
|
|
||||||
startOfMonth,
|
|
||||||
endOfMonth,
|
|
||||||
getDate,
|
|
||||||
add,
|
|
||||||
sub,
|
|
||||||
set,
|
|
||||||
isAfter,
|
|
||||||
isBefore,
|
|
||||||
compareAsc
|
|
||||||
} from "date-fns";
|
|
||||||
|
|
||||||
const CalenderContext = createContext({} as CalenderContextState);
|
|
||||||
|
|
||||||
const CalenderContextProvider = ({
|
|
||||||
children
|
|
||||||
}: {
|
|
||||||
children: ReactNode;
|
|
||||||
}): JSX.Element => {
|
|
||||||
const weekDays: WeekDays = {
|
|
||||||
sunday: [
|
|
||||||
"Sunday",
|
|
||||||
"Monday",
|
|
||||||
"Tuesday",
|
|
||||||
"Wednesday",
|
|
||||||
"Thursday",
|
|
||||||
"Friday",
|
|
||||||
"Saturday"
|
|
||||||
],
|
|
||||||
monday: [
|
|
||||||
"Monday",
|
|
||||||
"Tuesday",
|
|
||||||
"Wednesday",
|
|
||||||
"Thursday",
|
|
||||||
"Friday",
|
|
||||||
"Saturday",
|
|
||||||
"Sunday"
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Using date-fns, this function checks if currDate is within the month of selectedDate or not.
|
|
||||||
* @param {Date} selectedDate The current month.
|
|
||||||
* @param {Date} currDate The date to be compared to the selected month.
|
|
||||||
* @returns True if currDate is outside of the month of selectedDate, false if otherwise.
|
|
||||||
*/
|
|
||||||
const isOverflow = (
|
|
||||||
selectedDate: Date,
|
|
||||||
currDate: Date
|
|
||||||
): {
|
|
||||||
isOverflow: boolean;
|
|
||||||
overflowDirection: "prev" | "next" | null;
|
|
||||||
} => {
|
|
||||||
let flag = false;
|
|
||||||
let direction: "next" | "prev" | null = null;
|
|
||||||
|
|
||||||
const start = startOfMonth(selectedDate);
|
|
||||||
const end = endOfMonth(selectedDate);
|
|
||||||
|
|
||||||
if (isBefore(currDate, start)) {
|
|
||||||
flag = true;
|
|
||||||
direction = "prev";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAfter(currDate, end)) {
|
|
||||||
flag = true;
|
|
||||||
direction = "next";
|
|
||||||
}
|
|
||||||
|
|
||||||
return { isOverflow: flag, overflowDirection: direction };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A function that will return a month layout when given a date. It produces
|
|
||||||
* an object with 6 weeks that include overflow from the previous and next month
|
|
||||||
* with all dates aligned with the day of the week.
|
|
||||||
* @param selectedDate The date of the month to generate a month layout for.
|
|
||||||
*/
|
|
||||||
const populateMonth = (selectedDate: Date): MonthLayout => {
|
|
||||||
const endLastMonth = getDate(endOfMonth(sub(selectedDate, { months: 1 })));
|
|
||||||
const startOfSelectedMonth = format(startOfMonth(selectedDate), "iii");
|
|
||||||
|
|
||||||
const ISOToIndex = {
|
|
||||||
sunday: {
|
|
||||||
Sun: 0,
|
|
||||||
Mon: 1,
|
|
||||||
Tue: 2,
|
|
||||||
Wed: 3,
|
|
||||||
Thu: 4,
|
|
||||||
Fri: 5,
|
|
||||||
Sat: 6
|
|
||||||
},
|
|
||||||
monday: {
|
|
||||||
Mon: -1,
|
|
||||||
Tue: 0,
|
|
||||||
Wed: 1,
|
|
||||||
Thu: 2,
|
|
||||||
Fri: 3,
|
|
||||||
Sat: 4,
|
|
||||||
Sun: 5
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const sundays = {
|
|
||||||
week1: new Array(7).fill(null),
|
|
||||||
week2: new Array(7).fill(null),
|
|
||||||
week3: new Array(7).fill(null),
|
|
||||||
week4: new Array(7).fill(null),
|
|
||||||
week5: new Array(7).fill(null),
|
|
||||||
week6: new Array(7).fill(null)
|
|
||||||
};
|
|
||||||
|
|
||||||
const sunStartDay =
|
|
||||||
endLastMonth - (ISOToIndex.sunday[startOfSelectedMonth] - 1);
|
|
||||||
|
|
||||||
let sunCurrDate = set(sub(selectedDate, { months: 1 }), {
|
|
||||||
date: sunStartDay
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const week in sundays) {
|
|
||||||
const thisWeek = sundays[week];
|
|
||||||
|
|
||||||
thisWeek.forEach((e, i) => {
|
|
||||||
const overflowInfo = isOverflow(selectedDate, sunCurrDate);
|
|
||||||
|
|
||||||
const day: MonthDay = {
|
|
||||||
...overflowInfo,
|
|
||||||
date: sunCurrDate
|
|
||||||
};
|
|
||||||
|
|
||||||
sunCurrDate = add(sunCurrDate, {
|
|
||||||
days: 1
|
|
||||||
});
|
|
||||||
|
|
||||||
sundays[week][i] = day;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const mondays = {
|
|
||||||
week1: new Array(7).fill(null),
|
|
||||||
week2: new Array(7).fill(null),
|
|
||||||
week3: new Array(7).fill(null),
|
|
||||||
week4: new Array(7).fill(null),
|
|
||||||
week5: new Array(7).fill(null),
|
|
||||||
week6: new Array(7).fill(null)
|
|
||||||
};
|
|
||||||
|
|
||||||
const monStartDay = endLastMonth - ISOToIndex.monday[startOfSelectedMonth];
|
|
||||||
|
|
||||||
let monCurrDate = set(sub(selectedDate, { months: 1 }), {
|
|
||||||
date: monStartDay
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const week in mondays) {
|
|
||||||
const thisWeek = mondays[week];
|
|
||||||
|
|
||||||
thisWeek.forEach((e, i) => {
|
|
||||||
const overflowInfo = isOverflow(selectedDate, monCurrDate);
|
|
||||||
|
|
||||||
const day: MonthDay = {
|
|
||||||
...overflowInfo,
|
|
||||||
date: monCurrDate
|
|
||||||
};
|
|
||||||
|
|
||||||
monCurrDate = add(monCurrDate, {
|
|
||||||
days: 1
|
|
||||||
});
|
|
||||||
|
|
||||||
mondays[week][i] = day;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const output = {
|
|
||||||
sunday: {
|
|
||||||
weekdays: weekDays.sunday,
|
|
||||||
month: sundays
|
|
||||||
},
|
|
||||||
monday: {
|
|
||||||
weekdays: weekDays.monday,
|
|
||||||
month: mondays
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return output;
|
|
||||||
};
|
|
||||||
|
|
||||||
const [selectedDate, setSelectedDate] = useState<Date>(new Date());
|
|
||||||
const [selectedDateInfo, setSelectedMonthInfo] = useState<MonthContext>({
|
|
||||||
date: selectedDate,
|
|
||||||
title: format(selectedDate, "LLLL uuuu"),
|
|
||||||
layout: populateMonth(selectedDate)
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the selectedDateInfo state when given a date.
|
|
||||||
* @param {Date} newDate The date to set the selectedDateInfo state to.
|
|
||||||
*/
|
|
||||||
const updateDateInfo = (newDate: Date) => {
|
|
||||||
const output = { ...selectedDateInfo };
|
|
||||||
output.date = newDate;
|
|
||||||
output.title = format(newDate, "LLLL uuuu");
|
|
||||||
output.layout = populateMonth(newDate);
|
|
||||||
|
|
||||||
setSelectedMonthInfo(output);
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Add a function that validated if a date has at least one sticker in it. Use that within the nav function (when filter is enabled).
|
|
||||||
|
|
||||||
// TODO: Add a function that will give the closest date, if available, when the nav func detects an empty month.
|
|
||||||
// Use the chart creation date to aid with this. (When filter is enabled)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: Add logic that prevents navigation to the future and too far in the past. (Use chart creation date)
|
|
||||||
* Update to use a promise and return appropriate errors. Display those errors on the front end.
|
|
||||||
* Update the use of this function on the front to handle the fails of the promise.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// TODO: (When filter is enabled) Update the calender update function that will take in a direction so that the the navigation buttons will take the user to the next month with stickers. Assuming there was a gap with empty months.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updated the selectedDate state when given the appropriate object.
|
|
||||||
* @param {UpdateCalendarProps} input An object with year, month,
|
|
||||||
* and day keys that the selectedDate state will be updated to.
|
|
||||||
*/
|
|
||||||
const updateDate = (input: UpdateCalendarProps) => {
|
|
||||||
const { year, month: inputMonth, day } = input;
|
|
||||||
|
|
||||||
if (!year || !inputMonth || day < 0 || day > 31) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
const month = inputMonth - 1;
|
|
||||||
const customDate: Date = new Date(year, month, day);
|
|
||||||
|
|
||||||
if (compareAsc(customDate, selectedDate) !== 0) {
|
|
||||||
setSelectedDate(customDate);
|
|
||||||
updateDateInfo(customDate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// * Attempting to fix an issue with static generation where the date does not appear to be updating after initial generation.
|
|
||||||
const [currDate, setCurrDate] = useState<Date>(new Date());
|
|
||||||
|
|
||||||
const calenderContextValues: CalenderContextState = {
|
|
||||||
currDate,
|
|
||||||
setCurrDate,
|
|
||||||
selectedDate,
|
|
||||||
title: selectedDateInfo.title,
|
|
||||||
layout: selectedDateInfo.layout,
|
|
||||||
updateDate
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<CalenderContext.Provider value={calenderContextValues}>
|
|
||||||
{children}
|
|
||||||
</CalenderContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export { CalenderContextProvider, CalenderContext };
|
|
||||||
181
lib/populateMonth.ts
Normal file
181
lib/populateMonth.ts
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
import {
|
||||||
|
getDate,
|
||||||
|
endOfMonth,
|
||||||
|
sub,
|
||||||
|
format,
|
||||||
|
startOfMonth,
|
||||||
|
set,
|
||||||
|
add,
|
||||||
|
isAfter,
|
||||||
|
isBefore
|
||||||
|
} from "date-fns";
|
||||||
|
|
||||||
|
const weekDays: WeekDays = {
|
||||||
|
sunday: [
|
||||||
|
"Sunday",
|
||||||
|
"Monday",
|
||||||
|
"Tuesday",
|
||||||
|
"Wednesday",
|
||||||
|
"Thursday",
|
||||||
|
"Friday",
|
||||||
|
"Saturday"
|
||||||
|
],
|
||||||
|
monday: [
|
||||||
|
"Monday",
|
||||||
|
"Tuesday",
|
||||||
|
"Wednesday",
|
||||||
|
"Thursday",
|
||||||
|
"Friday",
|
||||||
|
"Saturday",
|
||||||
|
"Sunday"
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Using date-fns, this function checks if currDate is within the month of selectedDate or not.
|
||||||
|
* @param {Date} selectedDate The current month.
|
||||||
|
* @param {Date} currDate The date to be compared to the selected month.
|
||||||
|
* @returns True if currDate is outside of the month of selectedDate, false if otherwise.
|
||||||
|
*/
|
||||||
|
const isOverflow = (
|
||||||
|
selectedDate: Date,
|
||||||
|
currDate: Date
|
||||||
|
): {
|
||||||
|
isOverflow: boolean;
|
||||||
|
overflowDirection: "prev" | "next" | null;
|
||||||
|
} => {
|
||||||
|
let flag = false;
|
||||||
|
let direction: "next" | "prev" | null = null;
|
||||||
|
|
||||||
|
const start = startOfMonth(selectedDate);
|
||||||
|
const end = endOfMonth(selectedDate);
|
||||||
|
|
||||||
|
if (isBefore(currDate, start)) {
|
||||||
|
flag = true;
|
||||||
|
direction = "prev";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAfter(currDate, end)) {
|
||||||
|
flag = true;
|
||||||
|
direction = "next";
|
||||||
|
}
|
||||||
|
|
||||||
|
return { isOverflow: flag, overflowDirection: direction };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function that will return a month layout when given a date. It produces
|
||||||
|
* an object with 6 weeks that include overflow from the previous and next month
|
||||||
|
* with all dates aligned with the day of the week.
|
||||||
|
* @param selectedDate The date of the month to generate a month layout for.
|
||||||
|
* @returns The month layout object for the provided month.
|
||||||
|
*/
|
||||||
|
const populateMonth = (selectedDate: Date): MonthLayout => {
|
||||||
|
const endLastMonth = getDate(endOfMonth(sub(selectedDate, { months: 1 })));
|
||||||
|
const startOfSelectedMonth = format(startOfMonth(selectedDate), "iii");
|
||||||
|
|
||||||
|
const ISOToIndex = {
|
||||||
|
sunday: {
|
||||||
|
Sun: 0,
|
||||||
|
Mon: 1,
|
||||||
|
Tue: 2,
|
||||||
|
Wed: 3,
|
||||||
|
Thu: 4,
|
||||||
|
Fri: 5,
|
||||||
|
Sat: 6
|
||||||
|
},
|
||||||
|
monday: {
|
||||||
|
Mon: -1,
|
||||||
|
Tue: 0,
|
||||||
|
Wed: 1,
|
||||||
|
Thu: 2,
|
||||||
|
Fri: 3,
|
||||||
|
Sat: 4,
|
||||||
|
Sun: 5
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const sundays = {
|
||||||
|
week1: new Array(7).fill(null),
|
||||||
|
week2: new Array(7).fill(null),
|
||||||
|
week3: new Array(7).fill(null),
|
||||||
|
week4: new Array(7).fill(null),
|
||||||
|
week5: new Array(7).fill(null),
|
||||||
|
week6: new Array(7).fill(null)
|
||||||
|
};
|
||||||
|
|
||||||
|
const sunStartDay =
|
||||||
|
endLastMonth - (ISOToIndex.sunday[startOfSelectedMonth] - 1);
|
||||||
|
|
||||||
|
let sunCurrDate = set(sub(selectedDate, { months: 1 }), {
|
||||||
|
date: sunStartDay
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const week in sundays) {
|
||||||
|
const thisWeek = sundays[week];
|
||||||
|
|
||||||
|
thisWeek.forEach((e, i) => {
|
||||||
|
const overflowInfo = isOverflow(selectedDate, sunCurrDate);
|
||||||
|
|
||||||
|
const day: MonthDay = {
|
||||||
|
...overflowInfo,
|
||||||
|
date: sunCurrDate.toJSON()
|
||||||
|
};
|
||||||
|
|
||||||
|
sunCurrDate = add(sunCurrDate, {
|
||||||
|
days: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
sundays[week][i] = day;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const mondays = {
|
||||||
|
week1: new Array(7).fill(null),
|
||||||
|
week2: new Array(7).fill(null),
|
||||||
|
week3: new Array(7).fill(null),
|
||||||
|
week4: new Array(7).fill(null),
|
||||||
|
week5: new Array(7).fill(null),
|
||||||
|
week6: new Array(7).fill(null)
|
||||||
|
};
|
||||||
|
|
||||||
|
const monStartDay = endLastMonth - ISOToIndex.monday[startOfSelectedMonth];
|
||||||
|
|
||||||
|
let monCurrDate = set(sub(selectedDate, { months: 1 }), {
|
||||||
|
date: monStartDay
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const week in mondays) {
|
||||||
|
const thisWeek = mondays[week];
|
||||||
|
|
||||||
|
thisWeek.forEach((e, i) => {
|
||||||
|
const overflowInfo = isOverflow(selectedDate, monCurrDate);
|
||||||
|
|
||||||
|
const day: MonthDay = {
|
||||||
|
...overflowInfo,
|
||||||
|
date: monCurrDate.toJSON()
|
||||||
|
};
|
||||||
|
|
||||||
|
monCurrDate = add(monCurrDate, {
|
||||||
|
days: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
mondays[week][i] = day;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = {
|
||||||
|
sunday: {
|
||||||
|
weekdays: weekDays.sunday,
|
||||||
|
month: sundays
|
||||||
|
},
|
||||||
|
monday: {
|
||||||
|
weekdays: weekDays.monday,
|
||||||
|
month: mondays
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default populateMonth;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
|
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
|
||||||
import { RootState, AppDispatch } from "./store";
|
import { RootState, AppDispatch } from "./store";
|
||||||
|
|
||||||
export const useAppDiscpatch = () => useDispatch<AppDispatch>();
|
export const useAppDispatch = () => useDispatch<AppDispatch>();
|
||||||
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
|
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
import React, { useContext } from "react";
|
import React from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { HStack, IconButton } from "@chakra-ui/react";
|
import { HStack, IconButton } from "@chakra-ui/react";
|
||||||
import { Icon } from "@iconify/react";
|
import { Icon } from "@iconify/react";
|
||||||
import { format, isSameMonth, addMonths, subMonths } from "date-fns";
|
import { format, isSameMonth, addMonths, subMonths } from "date-fns";
|
||||||
import findValidDateRange from "../../../lib/findValidDateRange";
|
import findValidDateRange from "../../../lib/findValidDateRange";
|
||||||
import DatePicker from "./DatePicker";
|
import DatePicker from "./DatePicker";
|
||||||
import { CalenderContext } from "../../../contexts/CalenderContext";
|
import { useAppSelector } from "../../app/hooks";
|
||||||
|
|
||||||
const CalenderNav = (): JSX.Element => {
|
const CalenderNav = (): JSX.Element => {
|
||||||
const { selectedDate } = useContext(CalenderContext);
|
const selectedDate = useAppSelector(
|
||||||
|
(state) => state.calender.selectedDateInfo
|
||||||
|
);
|
||||||
|
const { date } = selectedDate;
|
||||||
|
|
||||||
|
const selectedDateObj = new Date(date);
|
||||||
|
|
||||||
const validDateRange = findValidDateRange();
|
const validDateRange = findValidDateRange();
|
||||||
const { start: validStart, end: validEnd } = validDateRange;
|
const { start: validStart, end: validEnd } = validDateRange;
|
||||||
@@ -17,14 +22,14 @@ const CalenderNav = (): JSX.Element => {
|
|||||||
|
|
||||||
const handleNavButtons = (direction: "next" | "prev") => {
|
const handleNavButtons = (direction: "next" | "prev") => {
|
||||||
if (direction === "next") {
|
if (direction === "next") {
|
||||||
const newMonth = addMonths(selectedDate, 1);
|
const newMonth = addMonths(selectedDateObj, 1);
|
||||||
|
|
||||||
const year = format(newMonth, "y");
|
const year = format(newMonth, "y");
|
||||||
const month = format(newMonth, "L");
|
const month = format(newMonth, "L");
|
||||||
|
|
||||||
router.push(`/calendar/${year}/${month}`);
|
router.push(`/calendar/${year}/${month}`);
|
||||||
} else if (direction === "prev") {
|
} else if (direction === "prev") {
|
||||||
const newMonth = subMonths(selectedDate, 1);
|
const newMonth = subMonths(selectedDateObj, 1);
|
||||||
|
|
||||||
const year = format(newMonth, "y");
|
const year = format(newMonth, "y");
|
||||||
const month = format(newMonth, "L");
|
const month = format(newMonth, "L");
|
||||||
@@ -36,14 +41,14 @@ const CalenderNav = (): JSX.Element => {
|
|||||||
return (
|
return (
|
||||||
<HStack spacing={10} as="nav" w="auto" h="10vh" textAlign="center">
|
<HStack spacing={10} as="nav" w="auto" h="10vh" textAlign="center">
|
||||||
<IconButton
|
<IconButton
|
||||||
isDisabled={isSameMonth(selectedDate, validStart)}
|
isDisabled={isSameMonth(selectedDateObj, validStart)}
|
||||||
aria-label="Previous Month"
|
aria-label="Previous Month"
|
||||||
icon={<Icon icon="akar-icons:chevron-left" />}
|
icon={<Icon icon="akar-icons:chevron-left" />}
|
||||||
onClick={() => handleNavButtons("prev")}
|
onClick={() => handleNavButtons("prev")}
|
||||||
/>
|
/>
|
||||||
<DatePicker />
|
<DatePicker />
|
||||||
<IconButton
|
<IconButton
|
||||||
isDisabled={isSameMonth(selectedDate, validEnd)}
|
isDisabled={isSameMonth(selectedDateObj, validEnd)}
|
||||||
aria-label="Next Month"
|
aria-label="Next Month"
|
||||||
icon={<Icon icon="akar-icons:chevron-right" />}
|
icon={<Icon icon="akar-icons:chevron-right" />}
|
||||||
onClick={() => handleNavButtons("next")}
|
onClick={() => handleNavButtons("next")}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useContext, useRef, useState } from "react";
|
import React, { useRef, useState } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -27,10 +27,12 @@ import {
|
|||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import findValidDateRange from "../../../lib/findValidDateRange";
|
import findValidDateRange from "../../../lib/findValidDateRange";
|
||||||
import FormValidateEmoji from "./FormValidateEmoji";
|
import FormValidateEmoji from "./FormValidateEmoji";
|
||||||
import { CalenderContext } from "../../../contexts/CalenderContext";
|
import { useAppSelector } from "../../app/hooks";
|
||||||
|
|
||||||
const DatePicker = (): JSX.Element => {
|
const DatePicker = (): JSX.Element => {
|
||||||
const { title } = useContext(CalenderContext);
|
const selectedDate = useAppSelector(
|
||||||
|
(state) => state.calender.selectedDateInfo
|
||||||
|
);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@@ -129,7 +131,7 @@ const DatePicker = (): JSX.Element => {
|
|||||||
<PopoverTrigger>
|
<PopoverTrigger>
|
||||||
<Button border="none" variant="outline">
|
<Button border="none" variant="outline">
|
||||||
<Heading w="100%" h="auto">
|
<Heading w="100%" h="auto">
|
||||||
{title}
|
{selectedDate.title}
|
||||||
</Heading>
|
</Heading>
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ interface DayProps {
|
|||||||
overflowDirection?: "next" | "prev" | null;
|
overflowDirection?: "next" | "prev" | null;
|
||||||
sticker: StickerVal;
|
sticker: StickerVal;
|
||||||
date: Date;
|
date: Date;
|
||||||
selectedDate: Date;
|
selectedDate: string;
|
||||||
currDate: Date;
|
currDate: Date;
|
||||||
isToday: boolean;
|
isToday: boolean;
|
||||||
}
|
}
|
||||||
@@ -42,17 +42,19 @@ const Day = ({
|
|||||||
currDate,
|
currDate,
|
||||||
isToday
|
isToday
|
||||||
}: DayProps): JSX.Element => {
|
}: DayProps): JSX.Element => {
|
||||||
|
const selectedDateObj = new Date(selectedDate);
|
||||||
|
|
||||||
const handleNav = (direction: "next" | "prev") => {
|
const handleNav = (direction: "next" | "prev") => {
|
||||||
if (direction === "next") {
|
if (direction === "next") {
|
||||||
console.log(overflowDirection);
|
console.log(overflowDirection);
|
||||||
const newMonth = add(selectedDate, { months: 1 });
|
const newMonth = add(selectedDateObj, { months: 1 });
|
||||||
|
|
||||||
const year = getYear(newMonth);
|
const year = getYear(newMonth);
|
||||||
const month = getMonth(newMonth) + 1;
|
const month = getMonth(newMonth) + 1;
|
||||||
|
|
||||||
router.push(`/calendar/${year}/${month}`);
|
router.push(`/calendar/${year}/${month}`);
|
||||||
} else if (direction === "prev") {
|
} else if (direction === "prev") {
|
||||||
const newMonth = sub(selectedDate, { months: 1 });
|
const newMonth = sub(selectedDateObj, { months: 1 });
|
||||||
|
|
||||||
const year = getYear(newMonth);
|
const year = getYear(newMonth);
|
||||||
const month = getMonth(newMonth) + 1;
|
const month = getMonth(newMonth) + 1;
|
||||||
|
|||||||
@@ -1,41 +1,50 @@
|
|||||||
import React, { useContext, useEffect } from "react";
|
import React, { useContext, useEffect } from "react";
|
||||||
import { Box, HStack, SimpleGrid, Text, VStack } from "@chakra-ui/react";
|
import { Box, HStack, SimpleGrid, Text, VStack } from "@chakra-ui/react";
|
||||||
import { isSameDay, format } from "date-fns";
|
import { isSameDay, format } from "date-fns";
|
||||||
import { useAppDiscpatch, useAppSelector } from "../../app/hooks";
|
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
||||||
import { updateCurrDate, updateMonth } from '../../features/calender/calender';
|
import { updateCurrDate, updateMonth } from "../../features/calender/calender";
|
||||||
import { CalenderContext } from "../../../contexts/CalenderContext";
|
|
||||||
import { StickersContext } from "../../../contexts/StickerContext";
|
import { StickersContext } from "../../../contexts/StickerContext";
|
||||||
import CalenderNav from "./CalenderNav";
|
import CalenderNav from "./CalenderNav";
|
||||||
import Day from "./Day";
|
import Day from "./Day";
|
||||||
|
|
||||||
const Calender = (newDate?: UpdateCalendarProps): JSX.Element => {
|
const Calender = (newDate?: UpdateCalendarProps): JSX.Element => {
|
||||||
const currDate: Date = useAppSelector(state => state.calender.currDate);
|
const currDate: string = useAppSelector((state) => state.calender.currDate);
|
||||||
const seletedMonth = useAppSelector(state => state.calender.selectedDateInfo);
|
const selectedDate = useAppSelector(
|
||||||
|
(state) => state.calender.selectedDateInfo
|
||||||
|
);
|
||||||
|
const { layout } = selectedDate;
|
||||||
|
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const { selectedDate, layout, updateDate,/* currDate, */setCurrDate } =
|
|
||||||
useContext(CalenderContext);
|
|
||||||
const { stickersMonth } = useContext(StickersContext);
|
const { stickersMonth } = useContext(StickersContext);
|
||||||
|
|
||||||
|
const currDateObj = new Date(currDate);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (newDate && newDate.year && newDate.month && newDate.day) {
|
if (newDate && newDate.year && newDate.month && newDate.day) {
|
||||||
const { year, month, day } = newDate;
|
const { year, month, day } = newDate;
|
||||||
|
|
||||||
if (year > 0 && month > 0 && day > 0) {
|
if (year > 0 && month > 0 && day > 0) {
|
||||||
updateDate(newDate);
|
const generatedDate: Date = new Date(year, month - 1, day);
|
||||||
|
const dateString: string = generatedDate.toJSON();
|
||||||
|
|
||||||
|
dispatch(updateMonth(dateString));
|
||||||
} else {
|
} else {
|
||||||
console.warn("Invalid date format: ", newDate);
|
console.warn("Invalid date format: ", newDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [newDate, updateDate]);
|
}, [dispatch, newDate]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.info("Check to update date.");
|
console.info("Check to update date.");
|
||||||
if (!isSameDay(currDate, new Date())) {
|
|
||||||
|
const currDateObj = new Date(currDate);
|
||||||
|
|
||||||
|
if (!isSameDay(currDateObj, new Date())) {
|
||||||
console.info("Updated date.");
|
console.info("Updated date.");
|
||||||
setCurrDate(new Date());
|
dispatch(updateCurrDate());
|
||||||
}
|
}
|
||||||
}, [currDate, setCurrDate]);
|
}, [currDate, dispatch]);
|
||||||
|
|
||||||
// Simulated user settings context
|
// Simulated user settings context
|
||||||
const userSettings = {
|
const userSettings = {
|
||||||
@@ -87,12 +96,14 @@ const Calender = (newDate?: UpdateCalendarProps): JSX.Element => {
|
|||||||
return thisWeek.map((day: MonthDay) => {
|
return thisWeek.map((day: MonthDay) => {
|
||||||
const { date, isOverflow, overflowDirection } = day;
|
const { date, isOverflow, overflowDirection } = day;
|
||||||
|
|
||||||
|
const toDateObj: Date = new Date(date);
|
||||||
|
|
||||||
let sticker = null;
|
let sticker = null;
|
||||||
|
|
||||||
let id = "";
|
let id = "";
|
||||||
|
|
||||||
stickersMonth.map((stickerDay) => {
|
stickersMonth.map((stickerDay) => {
|
||||||
if (isSameDay(stickerDay.date, date)) {
|
if (isSameDay(stickerDay.date, toDateObj)) {
|
||||||
sticker = stickerDay.sticker;
|
sticker = stickerDay.sticker;
|
||||||
|
|
||||||
id = stickerDay.id;
|
id = stickerDay.id;
|
||||||
@@ -104,15 +115,15 @@ const Calender = (newDate?: UpdateCalendarProps): JSX.Element => {
|
|||||||
isOverflow={isOverflow}
|
isOverflow={isOverflow}
|
||||||
overflowDirection={overflowDirection}
|
overflowDirection={overflowDirection}
|
||||||
sticker={sticker}
|
sticker={sticker}
|
||||||
date={date}
|
date={toDateObj}
|
||||||
selectedDate={selectedDate}
|
selectedDate={selectedDate.date}
|
||||||
currDate={currDate}
|
currDate={currDateObj}
|
||||||
isToday={isSameDay(currDate, date)}
|
isToday={isSameDay(currDateObj, toDateObj)}
|
||||||
key={
|
key={
|
||||||
id.length
|
id.length
|
||||||
? id
|
? id
|
||||||
: format(date, "yyyyddLL") +
|
: format(toDateObj, "yyyyddLL") +
|
||||||
`/${sticker === null ? 0 : sticker}`
|
`/${sticker === null ? 0 : sticker}`
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,43 +1,59 @@
|
|||||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
|
import populate from "../../../lib/populateMonth";
|
||||||
|
|
||||||
interface CalenderSlice {
|
interface CalenderSlice {
|
||||||
currDate: Date;
|
currDate: string;
|
||||||
selectedDateInfo: {
|
selectedDateInfo: {
|
||||||
selectedDate: Date;
|
date: string;
|
||||||
title: string;
|
title: string;
|
||||||
layout: MonthLayout;
|
layout: MonthLayout;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const getCurrDate = (): Date => new Date();
|
const getCurrDate = (): Date => new Date();
|
||||||
|
const dateParse = (date: Date) => date.toJSON();
|
||||||
const dateFormatter = (date: Date): string => format(date, "LLLL uuuu");
|
const dateFormatter = (date: Date): string => format(date, "LLLL uuuu");
|
||||||
|
|
||||||
const initialState: CalenderSlice = {
|
const initialState: CalenderSlice = {
|
||||||
currDate: getCurrDate(),
|
currDate: dateParse(getCurrDate()),
|
||||||
selectedDateInfo: {
|
selectedDateInfo: {
|
||||||
selectedDate: getCurrDate(),
|
date: dateParse(getCurrDate()),
|
||||||
title: dateFormatter(new Date()),
|
title: dateFormatter(getCurrDate()),
|
||||||
layout: {} as MonthLayout
|
layout: populate(getCurrDate())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: Add a function that validated if a month has at least one sticker in it. Use that within the nav function (when filter is enabled).
|
||||||
|
|
||||||
|
// TODO: Add a function that will give the closest date, if available, when the nav func detects an empty month.
|
||||||
|
// Use the chart creation date to aid with this. (When filter is enabled)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Add logic that prevents navigation to the future and too far in the past. (Use chart creation date)
|
||||||
|
* Update to use a promise and return appropriate errors. Display those errors on the front end.
|
||||||
|
* Update the use of this function on the front to handle the fails of the promise.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO: (When filter is enabled) Update the calender update function that will take in a direction so that the the navigation buttons will take the user to the next month with stickers. Assuming there was a gap with empty months.
|
||||||
|
|
||||||
const calenderSlice = createSlice({
|
const calenderSlice = createSlice({
|
||||||
name: "Calender",
|
name: "Calender",
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
// Populate month
|
|
||||||
// Update month info
|
// Update month info
|
||||||
updateMonth(state: CalenderSlice, action: PayloadAction<Date>) {
|
updateMonth(state: CalenderSlice, action: PayloadAction<string>) {
|
||||||
const { payload: newDate } = action;
|
const { payload } = action;
|
||||||
|
|
||||||
state.selectedDateInfo.selectedDate = newDate;
|
const toDateObj: Date = new Date(payload);
|
||||||
state.selectedDateInfo.title = dateFormatter(newDate);
|
|
||||||
// ! Add the layout formatter function
|
state.selectedDateInfo.date = payload;
|
||||||
|
state.selectedDateInfo.title = dateFormatter(toDateObj);
|
||||||
|
state.selectedDateInfo.layout = populate(toDateObj);
|
||||||
},
|
},
|
||||||
// Update current date
|
// Update current date
|
||||||
updateCurrDate(state: CalenderSlice) {
|
updateCurrDate(state: CalenderSlice) {
|
||||||
state.currDate = new Date();
|
state.currDate = dateParse(new Date());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ import {
|
|||||||
// import findValidDateRange from "../../lib/findValidDateRange";
|
// import findValidDateRange from "../../lib/findValidDateRange";
|
||||||
import ErrorPage from "next/error";
|
import ErrorPage from "next/error";
|
||||||
import Calender from "../../components/calender";
|
import Calender from "../../components/calender";
|
||||||
import { CalenderContextProvider } from "../../../contexts/CalenderContext";
|
import { Provider } from "react-redux";
|
||||||
|
import { store } from "../../app/store";
|
||||||
import { StickersContextProvider } from "../../../contexts/StickerContext";
|
import { StickersContextProvider } from "../../../contexts/StickerContext";
|
||||||
|
|
||||||
const DateRoute: React.FC<unknown> = () => {
|
const DateRoute: React.FC<unknown> = () => {
|
||||||
@@ -59,8 +60,6 @@ const DateRoute: React.FC<unknown> = () => {
|
|||||||
} else if (!dateArr[2]) {
|
} else if (!dateArr[2]) {
|
||||||
date.day = 1;
|
date.day = 1;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return date;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return date;
|
return date;
|
||||||
@@ -199,11 +198,11 @@ const DateRoute: React.FC<unknown> = () => {
|
|||||||
<ErrorPage statusCode={404} />
|
<ErrorPage statusCode={404} />
|
||||||
) : (
|
) : (
|
||||||
<Box textAlign="center" w="100%" h="auto" pt="50px" pb="10vh">
|
<Box textAlign="center" w="100%" h="auto" pt="50px" pb="10vh">
|
||||||
<CalenderContextProvider>
|
<StickersContextProvider>
|
||||||
<StickersContextProvider>
|
<Provider store={store}>
|
||||||
<Calender {...date} />
|
<Calender {...date} />
|
||||||
</StickersContextProvider>
|
</Provider>
|
||||||
</CalenderContextProvider>
|
</StickersContextProvider>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { format } from "date-fns";
|
|||||||
import { Provider } from "react-redux";
|
import { Provider } from "react-redux";
|
||||||
import { store } from "../app/store";
|
import { store } from "../app/store";
|
||||||
import { StickersContextProvider } from "../../contexts/StickerContext";
|
import { StickersContextProvider } from "../../contexts/StickerContext";
|
||||||
import { CalenderContextProvider } from "../../contexts/CalenderContext";
|
|
||||||
import Calender from "../components/calender";
|
import Calender from "../components/calender";
|
||||||
|
|
||||||
const IndexPage = (): JSX.Element => {
|
const IndexPage = (): JSX.Element => {
|
||||||
@@ -17,11 +16,9 @@ const IndexPage = (): JSX.Element => {
|
|||||||
return (
|
return (
|
||||||
<Box textAlign="center" w="100%" h="auto" pt="50px" pb="10vh">
|
<Box textAlign="center" w="100%" h="auto" pt="50px" pb="10vh">
|
||||||
<StickersContextProvider>
|
<StickersContextProvider>
|
||||||
<CalenderContextProvider>
|
<Provider store={store}>
|
||||||
<Provider store={store}>
|
<Calender {...date.current} />
|
||||||
<Calender {...date.current} />
|
</Provider>
|
||||||
</Provider>
|
|
||||||
</CalenderContextProvider>
|
|
||||||
</StickersContextProvider>
|
</StickersContextProvider>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
2
types/CalenderContext.d.ts
vendored
2
types/CalenderContext.d.ts
vendored
@@ -15,7 +15,7 @@ interface WeekDays {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface MonthDay {
|
interface MonthDay {
|
||||||
date: Date;
|
date: string;
|
||||||
isOverflow: boolean;
|
isOverflow: boolean;
|
||||||
overflowDirection: "prev" | "next" | null;
|
overflowDirection: "prev" | "next" | null;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user