@@ -1,4 +1,16 @@
|
||||
{
|
||||
"rules": {
|
||||
"comma-dangle": [
|
||||
"error",
|
||||
{
|
||||
"arrays": "never",
|
||||
"objects": "never",
|
||||
"imports": "never",
|
||||
"exports": "never",
|
||||
"functions": "never"
|
||||
}
|
||||
]
|
||||
},
|
||||
"extends": [
|
||||
"next",
|
||||
"next/core-web-vitals",
|
||||
|
||||
5
.prettierrc
Normal file
5
.prettierrc
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"trailingComma": "none",
|
||||
"tabWidth": 2,
|
||||
"bracketSameLine": false
|
||||
}
|
||||
@@ -3,8 +3,8 @@ import { useRouter } from "next/router";
|
||||
import { HStack, IconButton } from "@chakra-ui/react";
|
||||
import { Icon } from "@iconify/react";
|
||||
import { sub, add, format } from "date-fns";
|
||||
import { CalenderContext } from "../../contexts/CalenderContext";
|
||||
import DatePicker from "./DatePicker";
|
||||
import { CalenderContext } from "../../contexts/CalenderContext";
|
||||
|
||||
const CalenderNav = (): JSX.Element => {
|
||||
const { selectedDate } = useContext(CalenderContext);
|
||||
@@ -14,7 +14,7 @@ const CalenderNav = (): JSX.Element => {
|
||||
const handleNavButtons = (direction: "next" | "prev") => {
|
||||
if (direction === "next") {
|
||||
const newMonth = add(selectedDate, {
|
||||
months: 1,
|
||||
months: 1
|
||||
});
|
||||
|
||||
const year = format(newMonth, "y");
|
||||
@@ -23,7 +23,7 @@ const CalenderNav = (): JSX.Element => {
|
||||
router.push(`/calendar/${year}/${month}`);
|
||||
} else if (direction === "prev") {
|
||||
const newMonth = sub(selectedDate, {
|
||||
months: 1,
|
||||
months: 1
|
||||
});
|
||||
|
||||
const year = format(newMonth, "y");
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
PopoverContent,
|
||||
PopoverHeader,
|
||||
PopoverTrigger,
|
||||
VStack,
|
||||
VStack
|
||||
} from "@chakra-ui/react";
|
||||
import {
|
||||
Formik,
|
||||
@@ -22,11 +22,10 @@ import {
|
||||
FormikProps,
|
||||
Form,
|
||||
Field,
|
||||
FieldProps,
|
||||
FieldProps
|
||||
} from "formik";
|
||||
import { format } from "date-fns";
|
||||
import { CalenderContext } from "../../contexts/CalenderContext";
|
||||
import FormValidateEmoji from "./FormValidateEmoji";
|
||||
import { CalenderContext } from "../../contexts/CalenderContext";
|
||||
|
||||
interface UpdateCalendarProps {
|
||||
year: number;
|
||||
@@ -35,9 +34,7 @@ interface UpdateCalendarProps {
|
||||
}
|
||||
|
||||
const DatePicker = (): JSX.Element => {
|
||||
const { selectedDate } = useContext(CalenderContext);
|
||||
|
||||
const currentMonth = format(selectedDate, "LLLL uuuu");
|
||||
const { title } = useContext(CalenderContext);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
@@ -57,7 +54,7 @@ const DatePicker = (): JSX.Element => {
|
||||
const date: UpdateCalendarProps = {
|
||||
year: parseInt(dateArr[0]),
|
||||
month: parseInt(dateArr[1]),
|
||||
day: parseInt(dateArr[2]),
|
||||
day: parseInt(dateArr[2])
|
||||
};
|
||||
|
||||
if (!/^(19|20)\d{2}$/.test(`${date.year}`)) {
|
||||
@@ -97,7 +94,7 @@ const DatePicker = (): JSX.Element => {
|
||||
const date: UpdateCalendarProps = {
|
||||
year: parseInt(dateArr[0]),
|
||||
month: parseInt(dateArr[1]),
|
||||
day: parseInt(dateArr[2]),
|
||||
day: parseInt(dateArr[2])
|
||||
};
|
||||
|
||||
return resolve(router.push(`/calendar/${date.year}/${date.month}`));
|
||||
@@ -116,15 +113,15 @@ const DatePicker = (): JSX.Element => {
|
||||
bg: "gray.900",
|
||||
borderColor: "white",
|
||||
_placeholder: {
|
||||
color: "white",
|
||||
color: "white"
|
||||
},
|
||||
_focus: {
|
||||
bg: "#000",
|
||||
color: "#FFF",
|
||||
borderColor: "#63b3ed",
|
||||
boxShadow: "0 0 0 1px #63b3ed",
|
||||
zIndex: "1",
|
||||
},
|
||||
zIndex: "1"
|
||||
}
|
||||
};
|
||||
|
||||
const initRef = useRef();
|
||||
@@ -134,7 +131,7 @@ const DatePicker = (): JSX.Element => {
|
||||
<PopoverTrigger>
|
||||
<Button border="none" variant="outline">
|
||||
<Heading w="100%" h="auto">
|
||||
{currentMonth}
|
||||
{title}
|
||||
</Heading>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
@@ -148,7 +145,7 @@ const DatePicker = (): JSX.Element => {
|
||||
<PopoverBody textAlign="center">
|
||||
<Formik
|
||||
initialValues={{
|
||||
date: "",
|
||||
date: ""
|
||||
}}
|
||||
onSubmit={(data, actions) => {
|
||||
handleSubmit(data)
|
||||
@@ -156,8 +153,8 @@ const DatePicker = (): JSX.Element => {
|
||||
actions.setSubmitting(false);
|
||||
actions.resetForm({
|
||||
values: {
|
||||
date: "",
|
||||
},
|
||||
date: ""
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
@@ -165,11 +162,15 @@ const DatePicker = (): JSX.Element => {
|
||||
});
|
||||
}}
|
||||
>
|
||||
{(formProps: FormikProps<{ date: string }>) => (
|
||||
{(
|
||||
formProps: FormikProps<{
|
||||
date: string;
|
||||
}>
|
||||
) => (
|
||||
<Form
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "auto",
|
||||
height: "auto"
|
||||
}}
|
||||
>
|
||||
<VStack
|
||||
@@ -223,8 +224,8 @@ const DatePicker = (): JSX.Element => {
|
||||
boxShadow: "0 0 0 1px #00c17c",
|
||||
_hover: {
|
||||
borderColor: "brand.valid",
|
||||
boxShadow: "0 0 0 1px #00c17c",
|
||||
},
|
||||
boxShadow: "0 0 0 1px #00c17c"
|
||||
}
|
||||
}
|
||||
: "")}
|
||||
/>
|
||||
|
||||
@@ -5,7 +5,7 @@ interface FormValidateEmojiProps {
|
||||
}
|
||||
|
||||
const FormValidateEmoji: FC<FormValidateEmojiProps> = ({
|
||||
type,
|
||||
type
|
||||
}: FormValidateEmojiProps) => {
|
||||
interface Validations {
|
||||
[key: string]: JSX.Element;
|
||||
@@ -26,7 +26,7 @@ const FormValidateEmoji: FC<FormValidateEmojiProps> = ({
|
||||
<span role="img" aria-label="Check">
|
||||
✔
|
||||
</span>
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
return validations[`${type}`];
|
||||
|
||||
@@ -2,6 +2,9 @@ import React, { useContext, useEffect } from "react";
|
||||
import { Box, HStack, SimpleGrid, Text, VStack } from "@chakra-ui/react";
|
||||
import CalenderNav from "./CalenderNav";
|
||||
import { CalenderContext } from "../../contexts/CalenderContext";
|
||||
import { getDate, sub, add, getYear, getMonth } from "date-fns";
|
||||
import { useRouter } from "next/router";
|
||||
// TODO: import types
|
||||
|
||||
interface UpdateCalendarProps {
|
||||
year: number;
|
||||
@@ -10,28 +13,32 @@ interface UpdateCalendarProps {
|
||||
}
|
||||
|
||||
const Calender = (newDate?: UpdateCalendarProps): JSX.Element => {
|
||||
const { daysOfMonth, daysOfWeek, setDate } = useContext(CalenderContext);
|
||||
const { selectedDate, layout, updateDate } = useContext(CalenderContext);
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
if (newDate) {
|
||||
const { year, month, day } = newDate;
|
||||
|
||||
if (year > 0 && month > 0 && day > 0) {
|
||||
setDate(newDate);
|
||||
updateDate(newDate);
|
||||
} else {
|
||||
console.warn("Invalid date format: ", newDate);
|
||||
}
|
||||
}
|
||||
}, [daysOfMonth, daysOfWeek, newDate, setDate]);
|
||||
}, [newDate, updateDate]);
|
||||
|
||||
// Simulated user settings context
|
||||
const userSettings = {
|
||||
theme: "default",
|
||||
startOfWeek: "Sunday",
|
||||
startOfWeek: "Sunday"
|
||||
};
|
||||
|
||||
const currMonth = layout[`${userSettings.startOfWeek.toLowerCase()}`];
|
||||
const { month, weekdays } = currMonth;
|
||||
|
||||
return (
|
||||
<VStack h="100vh" w="100%">
|
||||
<VStack h="91vh" w="100%">
|
||||
<CalenderNav />
|
||||
<HStack
|
||||
px={6}
|
||||
@@ -42,7 +49,7 @@ const Calender = (newDate?: UpdateCalendarProps): JSX.Element => {
|
||||
alignContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
{daysOfWeek.startOfWeek[userSettings.startOfWeek].map((weekDay) => {
|
||||
{weekdays.map((weekDay) => {
|
||||
return (
|
||||
<Box
|
||||
d="flex"
|
||||
@@ -71,20 +78,52 @@ const Calender = (newDate?: UpdateCalendarProps): JSX.Element => {
|
||||
// alignContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
{daysOfMonth.map((monthDay) => {
|
||||
return (
|
||||
<Box
|
||||
bg="transparent"
|
||||
border="2px solid #0068ff"
|
||||
w="100%"
|
||||
h="100%"
|
||||
key={monthDay}
|
||||
>
|
||||
<Text w="100%" h="100%">
|
||||
{`Day ${monthDay}`}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
{Object.keys(month).map((week) => {
|
||||
const thisWeek = month[week];
|
||||
|
||||
return thisWeek.map((day) => {
|
||||
const { date, isOverflow, overflowDirection } = day;
|
||||
|
||||
return (
|
||||
<Box
|
||||
bg="transparent"
|
||||
color={isOverflow ? "gray.600" : "whiteAlpha"}
|
||||
border={isOverflow ? "2px solid #181d8f" : "2px solid #0068ff"}
|
||||
w="100%"
|
||||
h="100%"
|
||||
key={date}
|
||||
{...(isOverflow && {
|
||||
_hover: {
|
||||
cursor: "pointer"
|
||||
}
|
||||
})}
|
||||
{...(isOverflow && {
|
||||
onClick: () => {
|
||||
if (overflowDirection === "next") {
|
||||
console.log(overflowDirection);
|
||||
const newMonth = add(selectedDate, { months: 1 });
|
||||
|
||||
const year = getYear(newMonth);
|
||||
const month = getMonth(newMonth) + 1;
|
||||
|
||||
router.push(`/calendar/${year}/${month}`);
|
||||
} else if (overflowDirection === "prev") {
|
||||
const newMonth = sub(selectedDate, { months: 1 });
|
||||
|
||||
const year = getYear(newMonth);
|
||||
const month = getMonth(newMonth) + 1;
|
||||
|
||||
router.push(`/calendar/${year}/${month}`);
|
||||
}
|
||||
}
|
||||
})}
|
||||
>
|
||||
<Text w="100%" h="100%">
|
||||
{`Day ${getDate(date)}`}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
})}
|
||||
</SimpleGrid>
|
||||
</VStack>
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
import React, { createContext, useState, ReactNode, useEffect } from "react";
|
||||
import { endOfMonth, getDate, sub, compareAsc } from "date-fns";
|
||||
import React, { createContext, useState, ReactNode } from "react";
|
||||
import {
|
||||
format,
|
||||
startOfMonth,
|
||||
endOfMonth,
|
||||
getDate,
|
||||
add,
|
||||
sub,
|
||||
set,
|
||||
isAfter,
|
||||
isBefore,
|
||||
compareAsc
|
||||
} from "date-fns";
|
||||
// TODO: import types
|
||||
|
||||
type days =
|
||||
type Days =
|
||||
| "Sunday"
|
||||
| "Monday"
|
||||
| "Tuesday"
|
||||
@@ -10,11 +21,47 @@ type days =
|
||||
| "Thursday"
|
||||
| "Friday"
|
||||
| "Saturday";
|
||||
interface DaysOfWeek {
|
||||
startOfWeek: {
|
||||
Sunday: days[];
|
||||
Monday: days[];
|
||||
|
||||
type DaysOfWeek = Days[];
|
||||
|
||||
interface WeekDays {
|
||||
sunday: DaysOfWeek;
|
||||
monday: DaysOfWeek;
|
||||
}
|
||||
|
||||
interface MonthDay {
|
||||
isOverflow: boolean;
|
||||
overflowDirection: "prev" | "next" | null;
|
||||
date: Date;
|
||||
}
|
||||
|
||||
interface Month {
|
||||
week1: MonthDay[];
|
||||
week2: MonthDay[];
|
||||
week3: MonthDay[];
|
||||
week4: MonthDay[];
|
||||
week5: MonthDay[];
|
||||
week6: MonthDay[];
|
||||
}
|
||||
|
||||
interface MonthInfo {
|
||||
date: Date;
|
||||
title: string;
|
||||
}
|
||||
|
||||
interface MonthLayout {
|
||||
sunday: {
|
||||
weekdays: DaysOfWeek;
|
||||
month: Month;
|
||||
};
|
||||
monday: {
|
||||
weekdays: DaysOfWeek;
|
||||
month: Month;
|
||||
};
|
||||
}
|
||||
|
||||
interface MonthContext extends MonthInfo {
|
||||
layout: MonthLayout;
|
||||
}
|
||||
|
||||
interface UpdateCalendarProps {
|
||||
@@ -23,158 +70,217 @@ interface UpdateCalendarProps {
|
||||
day: number;
|
||||
}
|
||||
|
||||
interface Month {
|
||||
week1: Date[];
|
||||
week2: Date[];
|
||||
week3: Date[];
|
||||
week4: Date[];
|
||||
week5: Date[];
|
||||
week6: Date[];
|
||||
}
|
||||
|
||||
interface Calendar {
|
||||
startOfWeek: {
|
||||
Sunday: Month;
|
||||
Monday: Month;
|
||||
};
|
||||
}
|
||||
|
||||
// Will replace all states and be used in redis as a form of memoization.
|
||||
interface MonthInfo {
|
||||
date: Date;
|
||||
layout: Calendar;
|
||||
startWeekday: string;
|
||||
endWeekday: string;
|
||||
days: number;
|
||||
}
|
||||
|
||||
interface CurrentMonth {
|
||||
prev: MonthInfo;
|
||||
curr: MonthInfo;
|
||||
next: MonthInfo;
|
||||
}
|
||||
|
||||
interface CalenderMemoize {
|
||||
String: CurrentMonth;
|
||||
}
|
||||
|
||||
interface CalenderContextState {
|
||||
selectedDate: Date;
|
||||
daysOfMonth: [number];
|
||||
daysOfWeek: DaysOfWeek;
|
||||
setDate: (date: UpdateCalendarProps) => boolean;
|
||||
title: string;
|
||||
layout: MonthLayout;
|
||||
updateDate: (input: UpdateCalendarProps) => void;
|
||||
}
|
||||
|
||||
const CalenderContext = createContext({} as CalenderContextState);
|
||||
|
||||
const CalenderContextProvider = ({
|
||||
children,
|
||||
children
|
||||
}: {
|
||||
children: ReactNode;
|
||||
}): JSX.Element => {
|
||||
const indexToDay = {
|
||||
startOfWeek: {
|
||||
Sunday: {
|
||||
0: "Sunday",
|
||||
1: "Monday",
|
||||
2: "Tuesday",
|
||||
3: "Wednesday",
|
||||
4: "Thursday",
|
||||
5: "Friday",
|
||||
6: "Saturday",
|
||||
},
|
||||
Monday: {
|
||||
0: "Monday",
|
||||
1: "Tuesday",
|
||||
2: "Wednesday",
|
||||
3: "Thursday",
|
||||
4: "Friday",
|
||||
5: "Saturday",
|
||||
6: "Sunday",
|
||||
},
|
||||
},
|
||||
const weekDays: WeekDays = {
|
||||
sunday: [
|
||||
"Sunday",
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday"
|
||||
],
|
||||
monday: [
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday",
|
||||
"Sunday"
|
||||
]
|
||||
};
|
||||
|
||||
// Selected month & year
|
||||
const [selectedDate, setSelectedDate] = useState<Date>(new Date());
|
||||
// Update this to have the day of week and the last numerical day.
|
||||
const [endOfSelectedMonth, SetEndOfSelectedDMonth] = useState<number>(
|
||||
getDate(endOfMonth(selectedDate))
|
||||
);
|
||||
// Update this to have the day of week and the last numerical day.
|
||||
const [endOfPrevMonth, setEndOfPrevMonth] = useState<number>(
|
||||
getDate(endOfMonth(sub(selectedDate, { months: 1 })))
|
||||
);
|
||||
// Add start of selected month and start of next month, including day of week and numerical day.
|
||||
//TODO Add a function that will populate the "MONTH" layout for the context. It should take in the start of the week (Sunday, Monday) and output the appropriate layout based on that preference.
|
||||
|
||||
// TODO: Remove this state and all referenced to it once the date alignment algorithm is complete.
|
||||
const [daysOfMonth, setDaysOfMonth] = useState<[number]>([1]);
|
||||
/**
|
||||
* 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;
|
||||
|
||||
// TODO: Repalce this with the new date alignment algorithm.
|
||||
// Update or populate the days of the month.
|
||||
const populateDays = (): void => {
|
||||
let newDaysOfMonth: [number] = [...daysOfMonth];
|
||||
const start = startOfMonth(selectedDate);
|
||||
const end = endOfMonth(selectedDate);
|
||||
|
||||
if (newDaysOfMonth.length > 1) {
|
||||
newDaysOfMonth = [1];
|
||||
if (isBefore(currDate, start)) {
|
||||
flag = true;
|
||||
direction = "prev";
|
||||
}
|
||||
|
||||
for (let i = 1; i < endOfSelectedMonth; i++) {
|
||||
newDaysOfMonth.push(i + 1);
|
||||
if (isAfter(currDate, end)) {
|
||||
flag = true;
|
||||
direction = "next";
|
||||
}
|
||||
|
||||
setDaysOfMonth(newDaysOfMonth);
|
||||
return { isOverflow: flag, overflowDirection: direction };
|
||||
};
|
||||
|
||||
// TODO: Update new referenced once they are added.
|
||||
// Update selected month sates when the selected month is updated.
|
||||
/**
|
||||
* 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");
|
||||
|
||||
// Update days of month.
|
||||
useEffect(() => {
|
||||
if (daysOfMonth === null) {
|
||||
populateDays();
|
||||
} else {
|
||||
if (daysOfMonth[daysOfMonth.length - 1] !== endOfSelectedMonth) {
|
||||
populateDays();
|
||||
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
|
||||
}
|
||||
}
|
||||
}, [selectedDate, endOfSelectedMonth]);
|
||||
};
|
||||
|
||||
// Update end of month.
|
||||
useEffect(() => {
|
||||
if (endOfSelectedMonth !== getDate(endOfMonth(selectedDate))) {
|
||||
SetEndOfSelectedDMonth(getDate(endOfMonth(selectedDate)));
|
||||
}
|
||||
}, [selectedDate]);
|
||||
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)
|
||||
};
|
||||
|
||||
// Calender Layout
|
||||
const daysOfWeek: DaysOfWeek = {
|
||||
startOfWeek: {
|
||||
Sunday: [
|
||||
"Sunday",
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday",
|
||||
],
|
||||
Monday: [
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday",
|
||||
"Sunday",
|
||||
],
|
||||
},
|
||||
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, sunCurrDate);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
//TODO: Create an object of arrays that will align with the days on the week. Make two sets for each start of the week setting.
|
||||
const [selectedDate, setSelectedDate] = useState<Date>(new Date());
|
||||
const [selectedDateInfo, setSelectedMonthInfo] = useState<MonthContext>({
|
||||
date: selectedDate,
|
||||
title: format(selectedDate, "LLLL uuuu"),
|
||||
layout: populateMonth(selectedDate)
|
||||
});
|
||||
|
||||
// Navigation
|
||||
const setDate = (input: UpdateCalendarProps): boolean => {
|
||||
//TODO Update the MonthInfo to use the new month population function on first render.
|
||||
|
||||
//TODO Add a function that will update the MonthInfo state when the selected month changes. This should use the populate month function that will be made above.
|
||||
/**
|
||||
* 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 new navigation function that will take in either a direction (next, prev) or a date to go directly to. That will update the selected month and trigger the use effects below.
|
||||
/**
|
||||
* 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) {
|
||||
@@ -185,15 +291,16 @@ const CalenderContextProvider = ({
|
||||
|
||||
if (compareAsc(customDate, selectedDate) !== 0) {
|
||||
setSelectedDate(customDate);
|
||||
updateDateInfo(customDate);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const calenderContextValues = {
|
||||
selectedDate,
|
||||
daysOfMonth,
|
||||
daysOfWeek,
|
||||
setDate,
|
||||
title: selectedDateInfo.title,
|
||||
layout: selectedDateInfo.layout,
|
||||
updateDate
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"private": true,
|
||||
"name": "lucid-creations-media-potty-chart",
|
||||
"homepage": "https://lucidcreations.media/introducing-code-name-potty-chart/",
|
||||
"version": "v0.0.5.2-pre-alpha",
|
||||
"version": "v0.0.6.0-pre-alpha",
|
||||
"author": {
|
||||
"name": "Lucid Creations Media",
|
||||
"url": "https://lucidcreations.media",
|
||||
|
||||
@@ -13,8 +13,8 @@ class Document extends NextDocument {
|
||||
<Html>
|
||||
<Head>
|
||||
<meta name="theme-color" content="#3138dc" />
|
||||
<link rel="icon" href="/images/favicon.svg" sizes="32x32 192x192" />
|
||||
<link rel="apple-touch-icon" href="/images/favicon.svg" />
|
||||
<link rel="icon" href="/images/logo.svg" sizes="32x32 192x192" />
|
||||
<link rel="apple-touch-icon" href="/images/logo.svg" />
|
||||
<meta property="og:title" content="LCM Potty Chart" />
|
||||
<meta name="og:description" content={description} />
|
||||
<meta property="og:type" content="Progress Tracking" />
|
||||
|
||||
@@ -22,14 +22,14 @@ const DateRoute: React.FC<unknown> = () => {
|
||||
return {
|
||||
year: 0,
|
||||
month: 0,
|
||||
day: 0,
|
||||
day: 0
|
||||
};
|
||||
}
|
||||
|
||||
const date = {
|
||||
year: 0,
|
||||
month: 0,
|
||||
day: 0,
|
||||
day: 0
|
||||
};
|
||||
|
||||
if (/^(19|20)\d{2}$/.test(`${dateArr[0]}`)) {
|
||||
@@ -58,7 +58,9 @@ const DateRoute: React.FC<unknown> = () => {
|
||||
const parsedSlug = slug.map((e) => {
|
||||
return parseInt(e);
|
||||
});
|
||||
setDate({ ...validateDateInput(parsedSlug) });
|
||||
setDate({
|
||||
...validateDateInput(parsedSlug)
|
||||
});
|
||||
}
|
||||
}, [slug]);
|
||||
|
||||
|
||||
@@ -14,8 +14,9 @@ const IndexPage = (): JSX.Element => {
|
||||
const date = useRef<UpdateCalendarProps>({
|
||||
year: parseInt(format(new Date(), "y")),
|
||||
month: parseInt(format(new Date(), "M")),
|
||||
day: parseInt(format(new Date(), "d")),
|
||||
day: parseInt(format(new Date(), "d"))
|
||||
});
|
||||
|
||||
return (
|
||||
<Box textAlign="center" w="100%" h="auto" pt="50px" pb="10vh">
|
||||
<CalenderContextProvider>
|
||||
|
||||
1
public/images/logo.svg
Normal file
1
public/images/logo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 144.01 157.2"><defs><style>.cls-1{fill:#61a3d7;}</style></defs><path class="cls-1" d="M90.37,123A3.86,3.86,0,0,0,85,124.63q-7.68,13.26-15.31,26.55a11.53,11.53,0,0,0-.75,2,4.09,4.09,0,0,0,3,3.86,3.78,3.78,0,0,0,4.39-1.93q7.71-13.32,15.38-26.65A3.86,3.86,0,0,0,90.37,123Z"/><path class="cls-1" d="M116.26,123a3.84,3.84,0,0,0-5.34,1.59q-6.39,11-12.73,22.06a14.1,14.1,0,0,0-.8,2.11,4.26,4.26,0,0,0,2.93,3.76,3.86,3.86,0,0,0,4.54-1.89q6.4-11.07,12.79-22.16A3.84,3.84,0,0,0,116.26,123Z"/><path class="cls-1" d="M63.53,122.65a3.79,3.79,0,0,0-4.44,1.84q-6.48,11.17-12.89,22.37a4.49,4.49,0,0,0-.51,2.42,3.71,3.71,0,0,0,2.94,3.3,3.76,3.76,0,0,0,4.25-1.81q6.52-11.21,13-22.46a10,10,0,0,0,.62-1.82A4.18,4.18,0,0,0,63.53,122.65Z"/><path class="cls-1" d="M142.6,84.35C139,74,131.3,68.43,120.54,66.76c-.23-1.85-.35-3.69-.69-5.49A34.64,34.64,0,0,0,111.21,44l-5.49,5.48a27.13,27.13,0,0,1,7,20.22c-.19,3,1.76,4.82,4.68,4.78,7.72-.11,13.62,3.21,16.94,10.2a18,18,0,0,1-16.26,25.91c-10.16,0-20.32,0-30.48,0h-30a23,23,0,0,1-10.51-2.5l-5.68,5.68a30.08,30.08,0,0,0,16.13,4.63q30.14,0,60.28,0a29.86,29.86,0,0,0,4.81-.43C138.1,115.3,147.78,99.14,142.6,84.35Z"/><path class="cls-1" d="M139.83,3.83,136.54.54a1.82,1.82,0,0,0-2.59,0L.54,134a1.82,1.82,0,0,0,0,2.59l3.29,3.29a1.83,1.83,0,0,0,1.3.54,1.81,1.81,0,0,0,1.29-.54L139.83,6.42A1.82,1.82,0,0,0,139.83,3.83Z"/><path class="cls-1" d="M36.62,72.45A2.22,2.22,0,0,1,37.29,74c-.7,4.86-1.44,9.72-2.18,14.57L48.59,75.09h0l25.79-25.8h0L84,39.65a1.94,1.94,0,0,1-1.46-1.18C79.06,31.52,75.49,24.6,72,17.64c-1.34-2.68-3.35-4.34-6.35-4.35s-5,1.64-6.37,4.31c-3.5,7-7.08,14-10.59,20.94a1.84,1.84,0,0,1-1.52,1.14c-5.64.9-11.27,1.86-16.91,2.79-2.16.35-4.33.67-6.48,1a6.68,6.68,0,0,0-5.62,4.79A6.77,6.77,0,0,0,20,55.45Q28.33,63.94,36.62,72.45Z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -1,10 +1,10 @@
|
||||
import { extendTheme, ThemeConfig } from "@chakra-ui/react";
|
||||
import { createBreakpoints } from "@chakra-ui/theme-tools";
|
||||
// import { createBreakpoints } from "@chakra-ui/theme-tools";
|
||||
import buttons from "./components/buttonStyles";
|
||||
|
||||
const config: ThemeConfig = {
|
||||
initialColorMode: "dark",
|
||||
useSystemColorMode: false,
|
||||
useSystemColorMode: false
|
||||
};
|
||||
|
||||
// const breakpoints = createBreakpoints({
|
||||
@@ -29,19 +29,19 @@ const AppTheme = extendTheme({
|
||||
footer: "#0097a7",
|
||||
footerText: "black",
|
||||
content: "#2d3748",
|
||||
patreon: "#FF424D",
|
||||
},
|
||||
patreon: "#FF424D"
|
||||
}
|
||||
},
|
||||
styles: {
|
||||
global: {
|
||||
body: {
|
||||
bg: "gray.900",
|
||||
},
|
||||
},
|
||||
bg: "gray.900"
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Button: buttons,
|
||||
},
|
||||
Button: buttons
|
||||
}
|
||||
// breakpoints,
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
darken,
|
||||
mode,
|
||||
StyleFunctionProps,
|
||||
whiten,
|
||||
whiten
|
||||
} from "@chakra-ui/theme-tools";
|
||||
import { Dict } from "@chakra-ui/utils";
|
||||
|
||||
@@ -22,8 +22,8 @@ const buttonStyles = {
|
||||
bg: mode(
|
||||
whiten("brand.primary", 20),
|
||||
darken("brand.primary", 20)
|
||||
)(props),
|
||||
},
|
||||
)(props)
|
||||
}
|
||||
}),
|
||||
contactSecondary: (props: Dict<never> | StyleFunctionProps) => ({
|
||||
bg: "brand.primary",
|
||||
@@ -33,8 +33,8 @@ const buttonStyles = {
|
||||
bg: mode(
|
||||
whiten("brand.primary", 20),
|
||||
darken("brand.primary", 20)
|
||||
)(props),
|
||||
},
|
||||
)(props)
|
||||
}
|
||||
}),
|
||||
project: (props: Dict<never> | StyleFunctionProps) => ({
|
||||
bg: "transparent",
|
||||
@@ -50,8 +50,8 @@ const buttonStyles = {
|
||||
darken("brand.secondary", 20)
|
||||
)(props),
|
||||
boxShadow:
|
||||
"rgba(0, 104, 255, 0.5) 0px 0px 15px, rgba(0, 104, 255, 0.3) 0px 0px 3px 1px",
|
||||
},
|
||||
"rgba(0, 104, 255, 0.5) 0px 0px 15px, rgba(0, 104, 255, 0.3) 0px 0px 3px 1px"
|
||||
}
|
||||
}),
|
||||
nav: (props: Dict<never> | StyleFunctionProps) => ({
|
||||
bg: "transparent",
|
||||
@@ -61,16 +61,16 @@ const buttonStyles = {
|
||||
bg: mode(
|
||||
whiten("brand.secondary", 20),
|
||||
darken("brand.secondary", 20)
|
||||
)(props),
|
||||
},
|
||||
)(props)
|
||||
}
|
||||
}),
|
||||
stickyNav: (/* props: Dict<never> | StyleFunctionProps */) => ({
|
||||
bg: "transparent",
|
||||
fontSize: "md",
|
||||
px: "2",
|
||||
_hover: {
|
||||
textDecoration: "underline",
|
||||
},
|
||||
textDecoration: "underline"
|
||||
}
|
||||
}),
|
||||
credits: (props: Dict<never> | StyleFunctionProps) => ({
|
||||
bg: "brand.main",
|
||||
@@ -78,8 +78,8 @@ const buttonStyles = {
|
||||
p: 3,
|
||||
color: "whiteAlpha",
|
||||
_hover: {
|
||||
bg: mode(whiten("brand.main", 20), darken("brand.main", 20))(props),
|
||||
},
|
||||
bg: mode(whiten("brand.main", 20), darken("brand.main", 20))(props)
|
||||
}
|
||||
}),
|
||||
backToTop: (props: Dict<never> | StyleFunctionProps) => ({
|
||||
bg: "rgba(23, 25, 35, 0.5)",
|
||||
@@ -98,8 +98,8 @@ const buttonStyles = {
|
||||
boxShadow:
|
||||
"rgba(0, 104, 255, 0.5) 0px 0px 15px, rgba(0, 104, 255, 0.3) 0px 0px 3px 1px",
|
||||
color: "whiteAlpha.900",
|
||||
border: "1px solid rgba(0, 134, 255, 1)",
|
||||
},
|
||||
border: "1px solid rgba(0, 134, 255, 1)"
|
||||
}
|
||||
}),
|
||||
collapse: (props: Dict<never> | StyleFunctionProps) => ({
|
||||
bg: "transparent",
|
||||
@@ -114,8 +114,8 @@ const buttonStyles = {
|
||||
darken("brand.secondary", 20)
|
||||
)(props),
|
||||
color: "whiteAlpha.900",
|
||||
textDecoration: "none",
|
||||
},
|
||||
textDecoration: "none"
|
||||
}
|
||||
}),
|
||||
submit: (props: Dict<never> | StyleFunctionProps) => ({
|
||||
fontSize: "lg",
|
||||
@@ -132,9 +132,9 @@ const buttonStyles = {
|
||||
)(props),
|
||||
boxShadow:
|
||||
"rgba(252, 129, 129, .95) 0px 0px 15px, rgba(252, 129, 129, 0.75) 0px 0px 3px 1px",
|
||||
border: "1px solid #FC8181",
|
||||
},
|
||||
},
|
||||
border: "1px solid #FC8181"
|
||||
}
|
||||
}
|
||||
}),
|
||||
mobileNav: (props: Dict<never> | StyleFunctionProps) => ({
|
||||
// bg: "transparent",
|
||||
@@ -148,14 +148,14 @@ const buttonStyles = {
|
||||
darken("brand.secondary", 20)
|
||||
)(props),
|
||||
boxShadow:
|
||||
"rgba(0, 134, 255, 0.5) 0px 0px 15px, rgba(0, 134, 255, 0.3) 0px 0px 3px 1px",
|
||||
"rgba(0, 134, 255, 0.5) 0px 0px 15px, rgba(0, 134, 255, 0.3) 0px 0px 3px 1px"
|
||||
},
|
||||
_expanded: {
|
||||
bg: "brand.primary",
|
||||
boxShadow:
|
||||
"rgba(0, 134, 255, 0.5) 0px 0px 15px, rgba(0, 134, 255, 0.3) 0px 0px 3px 1px",
|
||||
border: "1px solid #0068ff",
|
||||
},
|
||||
border: "1px solid #0068ff"
|
||||
}
|
||||
}),
|
||||
patreon: (props: Dict<never> | StyleFunctionProps) => ({
|
||||
bg: "brand.patreon",
|
||||
@@ -166,12 +166,12 @@ const buttonStyles = {
|
||||
bg: mode(
|
||||
whiten("brand.patreon", 20),
|
||||
darken("brand.patreon", 20)
|
||||
)(props),
|
||||
},
|
||||
}),
|
||||
)(props)
|
||||
}
|
||||
})
|
||||
},
|
||||
// default values for `size` and `variant`
|
||||
defaultProps: {},
|
||||
defaultProps: {}
|
||||
};
|
||||
|
||||
export default buttonStyles;
|
||||
|
||||
@@ -7,14 +7,18 @@ interface BackToTopButtonProps {
|
||||
}
|
||||
|
||||
const BackToTopButton: FC<BackToTopButtonProps> = ({
|
||||
show,
|
||||
show
|
||||
}: BackToTopButtonProps) => {
|
||||
return (
|
||||
<Flex
|
||||
d={show ? "flex" : "none"}
|
||||
pos="fixed"
|
||||
top="85vh"
|
||||
right={{ base: "1.25rem", sm: "2rem", md: "3rem" }}
|
||||
right={{
|
||||
base: "1.25rem",
|
||||
sm: "2rem",
|
||||
md: "3rem"
|
||||
}}
|
||||
>
|
||||
<Link href="/#top">
|
||||
<Button variant="backToTop">
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
HStack,
|
||||
// Image,
|
||||
Button,
|
||||
BoxProps,
|
||||
BoxProps
|
||||
} from "@chakra-ui/react";
|
||||
import { Icon } from "@iconify/react";
|
||||
// import BackToTopButton from "./BackToTopButton";
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import Image from "next/image";
|
||||
import {
|
||||
Heading,
|
||||
HStack,
|
||||
Box,
|
||||
IconButton,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuButton
|
||||
} from "@chakra-ui/react";
|
||||
import { Icon } from "@iconify/react";
|
||||
import DesktopNav from "./DesktopNav";
|
||||
import MobileNav from "./MobileNav";
|
||||
import appLogo from "../../public/images/logo.svg";
|
||||
|
||||
const Header = (): JSX.Element => {
|
||||
const appName = "LCM Potty Chart";
|
||||
const appVersion = "v0.0.5.2-pre-alpha";
|
||||
const appVersion = "v0.0.6.0-pre-alpha";
|
||||
|
||||
// Add transparency while not at the top of the page.
|
||||
const [transparentNavbar, setTransparentNavbar] = useState<boolean>(false);
|
||||
@@ -54,7 +56,7 @@ const Header = (): JSX.Element => {
|
||||
const iconType = {
|
||||
default: <Icon icon="bx:bx-menu-alt-right" />,
|
||||
hover: <Icon icon="bx:bx-menu" />,
|
||||
open: <Icon icon="bx:bx-x" />,
|
||||
open: <Icon icon="bx:bx-x" />
|
||||
};
|
||||
|
||||
if (open) {
|
||||
@@ -91,14 +93,17 @@ const Header = (): JSX.Element => {
|
||||
bg: "brand.main",
|
||||
boxShadow: open
|
||||
? "none"
|
||||
: "rgba(0, 134, 255, 0.9) 0px 0px 15px, rgba(0, 134, 255, 0.7) 0px 0px 3px 1px",
|
||||
: "rgba(0, 134, 255, 0.9) 0px 0px 15px, rgba(0, 134, 255, 0.7) 0px 0px 3px 1px"
|
||||
}}
|
||||
h={open ? "125px" : "auto"}
|
||||
>
|
||||
{/* Logo | Site Name */}
|
||||
<HStack
|
||||
width="100%"
|
||||
justifyContent={{ base: "flex-start", sm: "center" }}
|
||||
justifyContent={{
|
||||
base: "flex-start",
|
||||
sm: "center"
|
||||
}}
|
||||
alignItems="center"
|
||||
height={12}
|
||||
top={0}
|
||||
@@ -107,9 +112,11 @@ const Header = (): JSX.Element => {
|
||||
d={{ base: "flex", lg: "none" }}
|
||||
spacing="5px"
|
||||
_hover={{
|
||||
cursor: "default",
|
||||
cursor: "default"
|
||||
}}
|
||||
>
|
||||
<Image height="30px" width="30px" src={appLogo} alt="App Logo" />
|
||||
|
||||
<Heading as="h1" size="md">
|
||||
{appName}
|
||||
</Heading>
|
||||
@@ -140,9 +147,10 @@ const Header = (): JSX.Element => {
|
||||
height="auto"
|
||||
spacing="5px"
|
||||
_hover={{
|
||||
cursor: "default",
|
||||
cursor: "default"
|
||||
}}
|
||||
>
|
||||
<Image height="30px" width="30px" src={appLogo} alt="App Logo" />
|
||||
<Heading as="h1" size="md">
|
||||
{appName}
|
||||
</Heading>
|
||||
@@ -161,7 +169,10 @@ const Header = (): JSX.Element => {
|
||||
onClick={() => setOpen(!open)}
|
||||
onMouseEnter={() => setHover(true)}
|
||||
onMouseLeave={() => setHover(false)}
|
||||
d={{ base: "inline-flex", lg: "none" }}
|
||||
d={{
|
||||
base: "inline-flex",
|
||||
lg: "none"
|
||||
}}
|
||||
variant="mobileNav"
|
||||
bg={transparentNavbar ? "transparent" : "rgba(255, 255, 255, .15)"}
|
||||
type="button"
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
Link,
|
||||
MenuDivider,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
MenuList
|
||||
} from "@chakra-ui/react";
|
||||
import navItems, { NavItem } from "./navItems";
|
||||
|
||||
@@ -33,10 +33,10 @@ const MobileNav: FC<MobileNavProps> = ({ updateOpen }: MobileNavProps) => {
|
||||
h="auto"
|
||||
p={0}
|
||||
_hover={{
|
||||
backgroundColor: "none",
|
||||
backgroundColor: "none"
|
||||
}}
|
||||
_focus={{
|
||||
backgroundColor: "none",
|
||||
backgroundColor: "none"
|
||||
}}
|
||||
>
|
||||
<Link onClick={() => updateOpen(false)} href={navItem[1]}>
|
||||
|
||||
Reference in New Issue
Block a user