diff --git a/app/(private)/todos/layout.tsx b/app/(private)/todos/layout.tsx
new file mode 100644
index 0000000..d56e4d6
--- /dev/null
+++ b/app/(private)/todos/layout.tsx
@@ -0,0 +1,14 @@
+import { Container } from '@mantine/core';
+import { ReactNode } from 'react';
+
+interface Props {
+ children: ReactNode;
+}
+
+export default async function RootLayout({ children }: Props) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/app/(private)/todos/page.tsx b/app/(private)/todos/page.tsx
new file mode 100644
index 0000000..7bfe723
--- /dev/null
+++ b/app/(private)/todos/page.tsx
@@ -0,0 +1,18 @@
+'use client';
+
+import { TodoPageActions } from '@/components/Todo';
+import Todos from '@/components/Todo/Todo';
+import TodoSkelton from '@/components/Todo/TodoSkelton';
+import useFetchData from '@/hooks/useFetchData';
+
+const TodosPage = () => {
+ const { data, refetch, loading } = useFetchData('/api/todos');
+ return (
+ <>
+
+ {loading ? : }
+ >
+ );
+};
+
+export default TodosPage;
diff --git a/app/api/todos/route.ts b/app/api/todos/route.ts
new file mode 100644
index 0000000..97e2bbd
--- /dev/null
+++ b/app/api/todos/route.ts
@@ -0,0 +1,45 @@
+import { getServerSession } from 'next-auth';
+import { NextRequest, NextResponse } from 'next/server';
+import mongoose from 'mongoose';
+import startDb from '@/lib/db';
+import Todo from '@/models/Todo';
+import { authOptions } from '../auth/[...nextauth]/authOptions';
+import { UserDataTypes } from '../auth/[...nextauth]/next-auth.interfaces';
+
+export async function GET() {
+ try {
+ const session: UserDataTypes | null = await getServerSession(authOptions);
+ if (!session?.user) {
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
+ }
+ await startDb();
+ const todoList = await Todo.find({ user: session?.user._id })
+ .populate({
+ path: 'list',
+ select: 'title color -_id',
+ })
+ .sort('-updatedAt');
+ return NextResponse.json(todoList, { status: 200 });
+ } catch (error: any) {
+ return NextResponse.json({ error: error?.message }, { status: 500 });
+ }
+}
+export async function POST(req: NextRequest) {
+ try {
+ const session: UserDataTypes | null = await getServerSession(authOptions);
+ if (!session?.user) {
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
+ }
+ const body = await req.json();
+ await startDb();
+ if (body.list) {
+ body.list = new mongoose.Types.ObjectId(String(body.list));
+ } else {
+ body.list = null;
+ }
+ const todoList = await Todo.create({ ...body, user: session?.user._id });
+ return NextResponse.json(todoList, { status: 200 });
+ } catch (error: any) {
+ return NextResponse.json({ error: error?.message }, { status: 500 });
+ }
+}
diff --git a/app/api/todos/todo-list/route.ts b/app/api/todos/todo-list/route.ts
new file mode 100644
index 0000000..4afc7c5
--- /dev/null
+++ b/app/api/todos/todo-list/route.ts
@@ -0,0 +1,34 @@
+import { getServerSession } from 'next-auth';
+import { NextRequest, NextResponse } from 'next/server';
+import startDb from '@/lib/db';
+import TodoList from '@/models/TodoList';
+import { authOptions } from '../../auth/[...nextauth]/authOptions';
+import { UserDataTypes } from '../../auth/[...nextauth]/next-auth.interfaces';
+
+export async function GET() {
+ try {
+ const session: UserDataTypes | null = await getServerSession(authOptions);
+ if (!session?.user) {
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
+ }
+ await startDb();
+ const todoList = await TodoList.find({ user: session?.user._id }).sort('-updatedAt');
+ return NextResponse.json(todoList, { status: 200 });
+ } catch (error: any) {
+ return NextResponse.json({ error: error?.message }, { status: 500 });
+ }
+}
+export async function POST(req: NextRequest) {
+ try {
+ const session: UserDataTypes | null = await getServerSession(authOptions);
+ if (!session?.user) {
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
+ }
+ const body = await req.json();
+ await startDb();
+ const todoList = await TodoList.create({ ...body, user: session?.user._id });
+ return NextResponse.json(todoList, { status: 200 });
+ } catch (error: any) {
+ return NextResponse.json({ error: error?.message }, { status: 500 });
+ }
+}
diff --git a/app/layout.tsx b/app/layout.tsx
index eeb2a70..2ee876e 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -7,6 +7,7 @@ import AuthProvider from '@/Providers/AuthProvider';
import '@mantine/core/styles.css';
import '@mantine/notifications/styles.css';
import '@mantine/nprogress/styles.css';
+import '@mantine/dates/styles.css';
import { theme } from '../theme';
export const metadata = { title: 'Dream', description: '' };
diff --git a/components/Layout/Layout.tsx b/components/Layout/Layout.tsx
index 9832e2d..90982dc 100644
--- a/components/Layout/Layout.tsx
+++ b/components/Layout/Layout.tsx
@@ -16,6 +16,7 @@ import { useDisclosure } from '@mantine/hooks';
import { IconGridDots, IconList } from '@tabler/icons-react';
import { signOut, useSession } from 'next-auth/react';
import { usePathname, useRouter } from 'next/navigation';
+import { nprogress } from '@mantine/nprogress';
import React, { useCallback, useEffect, useState } from 'react';
import axios from 'axios';
import { App } from '../App';
@@ -25,6 +26,7 @@ export default function Layout({ children }: { children: React.ReactNode }) {
const [mobileOpened, { toggle: toggleMobile }] = useDisclosure();
const [desktopOpened, { toggle: toggleDesktop }] = useDisclosure(true);
const session = useSession();
+ const loading = session?.status === 'loading';
const isLoggedIn = session?.status === 'authenticated';
const isLoggedOff = session?.status === 'unauthenticated';
const router = useRouter();
@@ -34,8 +36,8 @@ export default function Layout({ children }: { children: React.ReactNode }) {
const navigateTo = useCallback(
(path?: string) => {
- if (path !== pathname) {
- router.push(path || '');
+ if (path && path !== pathname) {
+ router.push(path);
}
toggleMobile();
},
@@ -48,10 +50,20 @@ export default function Layout({ children }: { children: React.ReactNode }) {
setAPP((old = { sidebar: [] }) => ({ ...old, sidebar: [...old.sidebar, ...data] }));
};
+ if (isLoggedOff) {
+ router.push('/auth/login');
+ }
+
useEffect(() => {
+ nprogress.start();
getList();
+ nprogress.complete();
}, [rootpath]);
+ if (loading) {
+ return <>>;
+ }
+
return (
(
+
+ {todos?.map((i: TodoType) => (
+
+
+
+
+
+
+
+
+ {i.todo}
+
+
+
+
+
+
+
+ }
+ radius="xs"
+ variant="white"
+ display={i?.list ? 'block' : 'none'}
+ >
+ {i?.list?.title}
+
+ }
+ radius="xs"
+ variant="white"
+ display={i?.date ? 'block' : 'none'}
+ >
+ {dayjs(i?.date).format('DD MMM YYYY')}
+
+
+
+
+ ))}
+
+);
+
+export default Todos;
diff --git a/components/Todo/TodoPageActions.tsx b/components/Todo/TodoPageActions.tsx
new file mode 100644
index 0000000..274fd82
--- /dev/null
+++ b/components/Todo/TodoPageActions.tsx
@@ -0,0 +1,155 @@
+'use client';
+
+import { ActionIcon, Group, Modal, rem, Select, Stack, TextInput } from '@mantine/core';
+import { useForm } from '@mantine/form';
+import { useDisclosure } from '@mantine/hooks';
+import {
+ IconCalendar,
+ IconCaretUpDown,
+ IconCheck,
+ IconCircleCheck,
+ IconPlus,
+ IconPrinter,
+ IconShare,
+} from '@tabler/icons-react';
+import axios from 'axios';
+import React, { useEffect, useRef, useState } from 'react';
+import { nprogress } from '@mantine/nprogress';
+import { DatePickerInput } from '@mantine/dates';
+import { failure } from '@/lib/client_functions';
+import { COLORS } from '@/lib/constants';
+import FormButtons from '../FormButtons';
+
+const STYLES = {
+ input: {
+ backgroundColor: 'transparent',
+ border: 'none',
+ fontSize: 16,
+ paddingInline: 0,
+ fontWeight: 'bold',
+ },
+};
+
+interface Props {
+ refetch: () => void;
+}
+
+const TodoPageActions = ({ refetch }: Props) => {
+ const ref = useRef();
+ const [opened, { open, close }] = useDisclosure(false);
+ const [todoList, setTodoList] = useState([]);
+
+ const form = useForm({
+ initialValues: {
+ todo: '',
+ list: '',
+ date: null,
+ color: 'blue',
+ },
+ validate: {
+ todo: (value) => {
+ if (value.length === 0) {
+ ref.current.focus();
+ return 'Please enter a todo';
+ }
+ return null;
+ },
+ },
+ });
+
+ const getTodoLists = async () => {
+ try {
+ const res = await axios.get('/api/todos/todo-list');
+ setTodoList(res?.data);
+ } catch (error) {
+ failure('Something went wrong');
+ }
+ };
+
+ const onSubmit = async () => {
+ if (!form.values.todo) {
+ ref.current.focus();
+ failure('Please enter a todo');
+ return;
+ }
+ nprogress.start();
+ await axios.post('/api/todos', form.values);
+ refetch();
+ nprogress.complete();
+ onClose();
+ getTodoLists();
+ };
+
+ const onClose = () => {
+ form.reset();
+ close();
+ };
+
+ useEffect(() => {
+ getTodoLists();
+ }, []);
+
+ return (
+ <>
+
+ window.print()}>
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default TodoPageActions;
diff --git a/components/Todo/TodoSkelton.tsx b/components/Todo/TodoSkelton.tsx
new file mode 100644
index 0000000..7da4f27
--- /dev/null
+++ b/components/Todo/TodoSkelton.tsx
@@ -0,0 +1,7 @@
+import { Skeleton } from '@mantine/core';
+import React from 'react';
+
+const TodoSkelton = () =>
+ [...Array(4)].map((_, i) => );
+
+export default TodoSkelton;
diff --git a/components/Todo/index.ts b/components/Todo/index.ts
new file mode 100644
index 0000000..85e91fd
--- /dev/null
+++ b/components/Todo/index.ts
@@ -0,0 +1 @@
+export { default as TodoPageActions } from './TodoPageActions';
diff --git a/models/Todo.ts b/models/Todo.ts
new file mode 100644
index 0000000..dff4fe0
--- /dev/null
+++ b/models/Todo.ts
@@ -0,0 +1,63 @@
+import mongoose, { Model, model, models, Schema, Types } from 'mongoose';
+import { TodoListDocument } from './TodoList';
+
+export interface TodoDocument extends Document {
+ _id?: Types.ObjectId;
+ todo: string;
+ list: mongoose.Types.ObjectId;
+ isCompleted: boolean;
+ date: Date;
+ user: mongoose.Types.ObjectId;
+ color: string;
+}
+
+const todoSchema = new Schema(
+ {
+ todo: {
+ type: String,
+ index: true,
+ },
+ list: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'TodoList',
+ default: null,
+ },
+ date: {
+ type: Date,
+ default: null,
+ },
+ isCompleted: {
+ type: Boolean,
+ default: false,
+ },
+ user: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'User',
+ default: null,
+ },
+ color: {
+ type: String,
+ enum: [
+ 'gray.0',
+ 'blue',
+ 'red',
+ 'green',
+ 'indigo',
+ 'teal',
+ 'violet',
+ 'pink',
+ 'cyan',
+ 'grape',
+ 'lime',
+ ],
+ default: 'gray.0',
+ },
+ },
+ { timestamps: true }
+);
+
+const Todo = models.Todo || model('Todo', todoSchema);
+
+export default Todo as Model;
+
+export type TodoType = TodoDocument & { list: TodoListDocument };