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()}> + + + + + + + + + + +
onSubmit())} onReset={() => form.reset()}> + + } + ref={ref} + /> +