-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6cbdc22
commit 8e764c5
Showing
11 changed files
with
407 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ( | ||
<Container px={0} size="sm"> | ||
{children} | ||
</Container> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ( | ||
<> | ||
<TodoPageActions refetch={refetch} /> | ||
{loading ? <TodoSkelton /> : <Todos todos={data} />} | ||
</> | ||
); | ||
}; | ||
|
||
export default TodosPage; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 }); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 }); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import React from 'react'; | ||
import { ActionIcon, Badge, Group, Paper, Stack, Text } from '@mantine/core'; | ||
import { IconCalendar, IconCircleCheckFilled, IconList, IconStarFilled } from '@tabler/icons-react'; | ||
import dayjs from 'dayjs'; | ||
import { TodoType } from '@/models/Todo'; | ||
|
||
interface Props { | ||
todos: TodoType[]; | ||
} | ||
|
||
const Todos = ({ todos }: Props) => ( | ||
<Stack> | ||
{todos?.map((i: TodoType) => ( | ||
<Paper bg={`${i.color}.3`} p="md" key={String(i._id)}> | ||
<Stack gap="xs"> | ||
<Group gap="xs" wrap="nowrap" justify="space-between"> | ||
<Group gap="xs" wrap="nowrap"> | ||
<ActionIcon color="gray.0" variant="transparent"> | ||
<IconCircleCheckFilled /> | ||
</ActionIcon> | ||
<Text fw={700} c="gray.0" lineClamp={2}> | ||
{i.todo} | ||
</Text> | ||
</Group> | ||
<ActionIcon color="gray.0" variant="transparent"> | ||
<IconStarFilled /> | ||
</ActionIcon> | ||
</Group> | ||
<Group display={i?.list || i?.date ? 'flex' : 'none'}> | ||
<Badge | ||
c={i?.list?.color || 'dark'} | ||
leftSection={<IconList size={14} />} | ||
radius="xs" | ||
variant="white" | ||
display={i?.list ? 'block' : 'none'} | ||
> | ||
{i?.list?.title} | ||
</Badge> | ||
<Badge | ||
c={i?.list?.color || 'dark'} | ||
leftSection={<IconCalendar size={14} />} | ||
radius="xs" | ||
variant="white" | ||
display={i?.date ? 'block' : 'none'} | ||
> | ||
{dayjs(i?.date).format('DD MMM YYYY')} | ||
</Badge> | ||
</Group> | ||
</Stack> | ||
</Paper> | ||
))} | ||
</Stack> | ||
); | ||
|
||
export default Todos; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<any>(); | ||
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 ( | ||
<> | ||
<Group mt="sm" mb="xl" justify="right"> | ||
<ActionIcon variant="subtle" color="gray" onClick={() => window.print()}> | ||
<IconPrinter /> | ||
</ActionIcon> | ||
<ActionIcon variant="subtle" color="gray"> | ||
<IconShare /> | ||
</ActionIcon> | ||
<ActionIcon onClick={open} variant="subtle" color="gray"> | ||
<IconPlus /> | ||
</ActionIcon> | ||
</Group> | ||
<Modal opened={opened} onClose={onClose} title="New todo" centered> | ||
<form onSubmit={form.onSubmit(() => onSubmit())} onReset={() => form.reset()}> | ||
<Stack> | ||
<TextInput | ||
{...form.getInputProps('todo')} | ||
placeholder="Enter todo" | ||
styles={STYLES} | ||
rightSection={<IconCircleCheck />} | ||
ref={ref} | ||
/> | ||
<Select | ||
{...form.getInputProps('list')} | ||
data={todoList.map(({ _id, title }) => ({ label: title, value: _id }))} | ||
placeholder="Select a list (optional)" | ||
rightSection={<IconCaretUpDown />} | ||
clearable | ||
styles={STYLES} | ||
/> | ||
<DatePickerInput | ||
{...form.getInputProps('date')} | ||
placeholder="Pick date" | ||
styles={STYLES} | ||
rightSection={<IconCalendar />} | ||
/> | ||
<Group wrap="nowrap" gap={rem(4)} justify="space-between"> | ||
{COLORS?.map((color) => ( | ||
<ActionIcon | ||
color={`${color}.3`} | ||
size="sm" | ||
radius="xl" | ||
key={color} | ||
style={{ border: '1px solid gray' }} | ||
onClick={() => | ||
form.setFieldValue('color', form.values.color === color ? '' : color) | ||
} | ||
> | ||
{color === form.values.color && ( | ||
<IconCheck stroke={4} style={{ width: rem(14) }} /> | ||
)} | ||
</ActionIcon> | ||
))} | ||
</Group> | ||
<FormButtons /> | ||
</Stack> | ||
</form> | ||
</Modal> | ||
</> | ||
); | ||
}; | ||
|
||
export default TodoPageActions; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { Skeleton } from '@mantine/core'; | ||
import React from 'react'; | ||
|
||
const TodoSkelton = () => | ||
[...Array(4)].map((_, i) => <Skeleton key={String(i)} height={100} mt="md" animate />); | ||
|
||
export default TodoSkelton; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default as TodoPageActions } from './TodoPageActions'; |
Oops, something went wrong.