diff --git a/.gitignore b/.gitignore
index 594931c..7e8b523 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,6 +37,7 @@ yarn-error.log*
next-env.d.ts
.idea
+.turbo
/private
/.react-email
diff --git a/package-lock.json b/package-lock.json
index 8b00f6f..d310c9e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -24,6 +24,7 @@
"aws-sdk": "^2.1483.0",
"axios": "^1.5.1",
"bcrypt": "^5.1.1",
+ "dayjs": "^1.11.10",
"framer-motion": "^10.16.4",
"jsonwebtoken": "^9.0.2",
"next": "^13.5.6",
@@ -32,6 +33,7 @@
"next-nprogress-bar": "^2.1.2",
"next-themes": "^0.2.1",
"nodemailer": "^6.9.7",
+ "ramda": "^0.29.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-email": "1.9.5",
@@ -49,6 +51,7 @@
"@types/multer": "^1.4.9",
"@types/node": "^20",
"@types/nodemailer": "^6.4.13",
+ "@types/ramda": "^0.29.7",
"@types/react": "^18",
"@types/react-dom": "^18",
"autoprefixer": "^10",
@@ -5482,6 +5485,15 @@
"integrity": "sha512-wYLxw35euwqGvTDx6zfY1vokBFnsK0HNrzc6xNHchxfO2hpuRg74GbkEW7e3sSmPvj0TjCDT1VCa6OtHXnubsg==",
"dev": true
},
+ "node_modules/@types/ramda": {
+ "version": "0.29.7",
+ "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.29.7.tgz",
+ "integrity": "sha512-IUl6U95qwlQtVvZkSX4ODj08oJVtPyWMFRtPVNqhxc2rt+Bh7lCzTrGMYMZ7dmRKcAjtot3xrPnYGwsjdt8gzQ==",
+ "dev": true,
+ "dependencies": {
+ "types-ramda": "^0.29.5"
+ }
+ },
"node_modules/@types/range-parser": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.6.tgz",
@@ -6566,6 +6578,11 @@
"integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
"dev": true
},
+ "node_modules/dayjs": {
+ "version": "1.11.10",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz",
+ "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
+ },
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -10252,6 +10269,15 @@
}
]
},
+ "node_modules/ramda": {
+ "version": "0.29.1",
+ "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.1.tgz",
+ "integrity": "sha512-OfxIeWzd4xdUNxlWhgFazxsA/nl3mS4/jGZI5n00uWOoSSFRhC1b6gl6xvmzUamgmqELraWp0J/qqVlXYPDPyA==",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ramda"
+ }
+ },
"node_modules/react": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
@@ -11398,6 +11424,12 @@
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
},
+ "node_modules/ts-toolbelt": {
+ "version": "9.6.0",
+ "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz",
+ "integrity": "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==",
+ "dev": true
+ },
"node_modules/tsconfig-paths": {
"version": "3.14.2",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz",
@@ -11554,6 +11586,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/types-ramda": {
+ "version": "0.29.5",
+ "resolved": "https://registry.npmjs.org/types-ramda/-/types-ramda-0.29.5.tgz",
+ "integrity": "sha512-u+bAYXHDPJR+amB0qMrMU/NXRB2PG8QqpO2v6j7yK/0mPZhlaaZj++ynYjnVpkPEpCkZEGxNpWY3X7qyLCGE3w==",
+ "dev": true,
+ "dependencies": {
+ "ts-toolbelt": "^9.6.0"
+ }
+ },
"node_modules/typescript": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
diff --git a/package.json b/package.json
index 606fc0d..721b8fe 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,7 @@
"aws-sdk": "^2.1483.0",
"axios": "^1.5.1",
"bcrypt": "^5.1.1",
+ "dayjs": "^1.11.10",
"framer-motion": "^10.16.4",
"jsonwebtoken": "^9.0.2",
"next": "^13.5.6",
@@ -35,6 +36,7 @@
"next-nprogress-bar": "^2.1.2",
"next-themes": "^0.2.1",
"nodemailer": "^6.9.7",
+ "ramda": "^0.29.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-email": "1.9.5",
@@ -52,6 +54,7 @@
"@types/multer": "^1.4.9",
"@types/node": "^20",
"@types/nodemailer": "^6.4.13",
+ "@types/ramda": "^0.29.7",
"@types/react": "^18",
"@types/react-dom": "^18",
"autoprefixer": "^10",
diff --git a/src/app/(site)/(internal)/dashboard/calendar/components/Calendar.tsx b/src/app/(site)/(internal)/dashboard/calendar/components/Calendar.tsx
new file mode 100644
index 0000000..c448d80
--- /dev/null
+++ b/src/app/(site)/(internal)/dashboard/calendar/components/Calendar.tsx
@@ -0,0 +1,93 @@
+"use client"
+
+import {FC, useState} from "react";
+import Select from "@/app/(site)/components/inputs/Select";
+import {
+ createDaysForCurrentMonth,
+ createDaysForNextMonth, createDaysForPreviousMonth,
+ daysOfWeek
+} from "@/app/(site)/(internal)/dashboard/calendar/utils/calendar-utils";
+
+const Calendar: FC = () => {
+ const [[year, month], setCurrentYearAndMonth] = useState<[number, number]>([new Date().getFullYear(), new Date().getMonth() + 1]);
+
+ let currentMonthDays = createDaysForCurrentMonth(year, month);
+ let previousMonthDays = createDaysForPreviousMonth(
+ year,
+ month,
+ currentMonthDays
+ );
+ let nextMonthDays = createDaysForNextMonth(year, month, currentMonthDays);
+ let calendarGridDayObjects = [
+ ...previousMonthDays,
+ ...currentMonthDays,
+ ...nextMonthDays
+ ];
+
+ return (
+
+
+
+
+
+
+
+
+
+ {daysOfWeek.map((day, i) => (
+
+ {day}
+
+
+ ))}
+
+
+ {daysOfWeek.map((day, i) => (
+
+ {day.charAt(0)}
+
+
+ ))}
+
+
+ {
+ calendarGridDayObjects.map(day => (
+
+
+ {day.dayOfMonth}
+
+
+ hi
+
+
+ ))
+ }
+
+
+ )
+}
+
+export default Calendar
\ No newline at end of file
diff --git a/src/app/(site)/(internal)/dashboard/calendar/page.tsx b/src/app/(site)/(internal)/dashboard/calendar/page.tsx
new file mode 100644
index 0000000..fad7eb8
--- /dev/null
+++ b/src/app/(site)/(internal)/dashboard/calendar/page.tsx
@@ -0,0 +1,13 @@
+import {FC, Fragment} from "react";
+import Calendar from "@/app/(site)/(internal)/dashboard/calendar/components/Calendar";
+
+const CalendarPage: FC = () => {
+ return (
+
+ Dream Calendar
+
+
+ )
+}
+
+export default CalendarPage
\ No newline at end of file
diff --git a/src/app/(site)/(internal)/dashboard/calendar/utils/calendar-utils.ts b/src/app/(site)/(internal)/dashboard/calendar/utils/calendar-utils.ts
new file mode 100644
index 0000000..6bb5826
--- /dev/null
+++ b/src/app/(site)/(internal)/dashboard/calendar/utils/calendar-utils.ts
@@ -0,0 +1,100 @@
+import { range } from "ramda";
+import dayjs from "dayjs";
+import weekday from "dayjs/plugin/weekday";
+import weekOfYear from "dayjs/plugin/weekOfYear";
+
+dayjs.extend(weekday);
+dayjs.extend(weekOfYear);
+
+export const daysOfWeek = [
+ "Sunday",
+ "Monday",
+ "Tuesday",
+ "Wednesday",
+ "Thursday",
+ "Friday",
+ "Saturday"
+];
+
+export function getYearDropdownOptions(currentYear: number) {
+ let minYear = currentYear - 4;
+ let maxYear = currentYear + 5;
+ return range(minYear, maxYear + 1).map((y) => ({ label: `${y}`, value: y }));
+}
+
+export function getMonthDropdownOptions() {
+ return range(1, 13).map((m) => ({
+ value: m,
+ label: dayjs()
+ .month(m - 1)
+ .format("MMMM")
+ }));
+}
+
+export function getNumberOfDaysInMonth(year: number, month: number) {
+ return dayjs(`${year}-${month}-01`).daysInMonth();
+}
+
+export function createDaysForCurrentMonth(year: number, month: number) {
+ return [...Array(getNumberOfDaysInMonth(year, month))].map((_, index) => {
+ return {
+ dateString: dayjs(`${year}-${month}-${index + 1}`).format("YYYY-MM-DD"),
+ dayOfMonth: index + 1,
+ isCurrentMonth: true
+ };
+ });
+}
+
+export function createDaysForPreviousMonth(year: number, month: number, currentMonthDays: any[]) {
+ const firstDayOfTheMonthWeekday = getWeekday(currentMonthDays[0].dateString);
+ const previousMonth = dayjs(`${year}-${month}-01`).subtract(1, "month");
+
+ const visibleNumberOfDaysFromPreviousMonth = firstDayOfTheMonthWeekday;
+
+ const previousMonthLastMondayDayOfMonth = dayjs(
+ currentMonthDays[0].dateString
+ )
+ .subtract(visibleNumberOfDaysFromPreviousMonth, "day")
+ .date();
+
+ return [...Array(visibleNumberOfDaysFromPreviousMonth)].map((_, index) => {
+ return {
+ dateString: dayjs(
+ `${previousMonth.year()}-${previousMonth.month() + 1}-${
+ previousMonthLastMondayDayOfMonth + index
+ }`
+ ).format("YYYY-MM-DD"),
+ dayOfMonth: previousMonthLastMondayDayOfMonth + index,
+ isCurrentMonth: false,
+ isPreviousMonth: true
+ };
+ });
+}
+
+export function createDaysForNextMonth(year: number, month: number, currentMonthDays: any) {
+ const lastDayOfTheMonthWeekday = getWeekday(
+ `${year}-${month}-${currentMonthDays.length}`
+ );
+ const nextMonth = dayjs(`${year}-${month}-01`).add(1, "month");
+ const visibleNumberOfDaysFromNextMonth = 6 - lastDayOfTheMonthWeekday;
+
+ return [...Array(visibleNumberOfDaysFromNextMonth)].map((day, index) => {
+ return {
+ dateString: dayjs(
+ `${nextMonth.year()}-${nextMonth.month() + 1}-${index + 1}`
+ ).format("YYYY-MM-DD"),
+ dayOfMonth: index + 1,
+ isCurrentMonth: false,
+ isNextMonth: true
+ };
+ });
+}
+
+// sunday === 0, saturday === 6
+export function getWeekday(dateString: string) {
+ return dayjs(dateString).weekday();
+}
+
+export function isWeekendDay(dateString: string) {
+ return [6, 0].includes(getWeekday(dateString));
+}
diff --git a/src/app/(site)/(internal)/dashboard/components/DashboardSidebar.tsx b/src/app/(site)/(internal)/dashboard/components/DashboardSidebar.tsx
index 63871f7..b11a83c 100644
--- a/src/app/(site)/(internal)/dashboard/components/DashboardSidebar.tsx
+++ b/src/app/(site)/(internal)/dashboard/components/DashboardSidebar.tsx
@@ -21,7 +21,7 @@ const DashboardSidebar: FC = () => {
}
title="Dream Calendar"
- href="/dashboard"
+ href="/dashboard/calendar"
/>
}
diff --git a/src/app/(site)/(internal)/settings/account/components/DeleteAccountButton.tsx b/src/app/(site)/(internal)/settings/account/components/DeleteAccountButton.tsx
index d5170e8..67e33dd 100644
--- a/src/app/(site)/(internal)/settings/account/components/DeleteAccountButton.tsx
+++ b/src/app/(site)/(internal)/settings/account/components/DeleteAccountButton.tsx
@@ -6,7 +6,7 @@ import ConfirmationModal from "@/app/(site)/components/ConfirmationModal";
import {AnimatePresence, motion} from "framer-motion";
import Input from "@/app/(site)/components/inputs/Input";
import {signOut, useSession} from "next-auth/react";
-import {deleteMutator} from "@/utils/client/client-utils";
+import {deleteMutatorWithArgs} from "@/utils/client/client-utils";
import {DeleteSelfDto} from "@/app/api/me/self-user.dto";
import useSWRMutation from "swr/mutation";
import {Member} from "@prisma/client";
@@ -14,7 +14,7 @@ import toast from "react-hot-toast";
import {AxiosError} from "axios";
const DeleteAccount = () => (
- useSWRMutation('/api/me', deleteMutator())
+ useSWRMutation('/api/me', deleteMutatorWithArgs())
)
const DeleteAccountButton: FC = () => {
diff --git a/src/utils/client/client-utils.tsx b/src/utils/client/client-utils.tsx
index cf23fb8..2bbc4ea 100644
--- a/src/utils/client/client-utils.tsx
+++ b/src/utils/client/client-utils.tsx
@@ -21,7 +21,8 @@ export type MutatorArgs = {
export const postMutator = () => (url: string, {arg}: MutatorArgs) => axios.post(url, arg.body)
export const patchMutator = () => (url: string, {arg}: MutatorArgs) => axios.patch(url, arg.body)
-export const deleteMutator = , R>() => (url: string, args?: MutatorArgs) => axios.delete(`${url}${args ? "?" + new URLSearchParams(args.arg.body).toString() : ""}`)
+export const deleteMutatorWithArgs = | undefined, R>() => (url: string, args?: MutatorArgs) => axios.delete(`${url}${args ? "?" + new URLSearchParams(args.arg.body).toString() : ""}`)
+export const deleteMutator = () => (url: string) => axios.delete(url)
export function handleAxiosError(error: any): undefined {
diff --git a/turbo.json b/turbo.json
new file mode 100644
index 0000000..75e9460
--- /dev/null
+++ b/turbo.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "pipeline": {
+ "build": {
+ "outputs": [".next/**", "!.next/cache/**"]
+ },
+ "lint": {}
+ }
+}
\ No newline at end of file