Skip to content

Commit

Permalink
Merge pull request #2 from vishalkondle45/feature/todos
Browse files Browse the repository at this point in the history
todos page with skelton
  • Loading branch information
vishalkondle-dev authored Jun 22, 2024
2 parents 1b198a5 + 531d69f commit a60336b
Show file tree
Hide file tree
Showing 16 changed files with 537 additions and 22 deletions.
14 changes: 14 additions & 0 deletions app/(private)/todos/layout.tsx
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>
);
}
18 changes: 18 additions & 0 deletions app/(private)/todos/page.tsx
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;
31 changes: 31 additions & 0 deletions app/api/list/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
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(req: NextRequest) {
try {
const session: UserDataTypes | null = await getServerSession(authOptions);
if (!session?.user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
await startDb();
let list: any[] = [];
const schema = req.nextUrl.searchParams.get('schema')?.toString();
switch (schema) {
case 'todos':
list = await TodoList.find({ user: session?.user._id }).sort('-updatedAt');
break;
default:
break;
}
return NextResponse.json(
list.map((i) => ({ path: `/${schema}/${i?._id}`, label: i?.title, color: i?.color })),
{ status: 200 }
);
} catch (error: any) {
return NextResponse.json({ error: error?.message }, { status: 500 });
}
}
45 changes: 45 additions & 0 deletions app/api/todos/route.ts
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 });
}
}
34 changes: 34 additions & 0 deletions app/api/todos/todo-list/route.ts
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 });
}
}
1 change: 1 addition & 0 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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: '' };
Expand Down
2 changes: 1 addition & 1 deletion components/App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const App = ({ app, isCurrent }: Props) => {
ref={ref}
bg={hovered || isCurrent ? `${app.color}.3` : 'transparent'}
key={app.path}
onClick={() => router.push(app.path)}
onClick={() => router.push(app?.path || '')}
style={{ cursor: 'pointer' }}
c={hovered || isCurrent ? 'white' : 'dark'}
>
Expand Down
59 changes: 44 additions & 15 deletions components/Layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,57 @@ import {
Stack,
} from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { IconGridDots } from '@tabler/icons-react';
import { IconGridDots, IconList } from '@tabler/icons-react';
import { signOut, useSession } from 'next-auth/react';
import { usePathname, useRouter } from 'next/navigation';
import React from 'react';
import { nprogress } from '@mantine/nprogress';
import React, { useCallback, useEffect, useState } from 'react';
import axios from 'axios';
import { App } from '../App';
import { APPS } from '@/lib/constants';

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();
const pathname = usePathname();
const APP = APPS.find((app) => `/${pathname.split('/')[1]}` === app.path);
const rootpath = pathname.split('/')[1];
const [APP, setAPP] = useState(APPS.find((app) => `/${rootpath}` === app?.path));

const navigateTo = (path: string) => {
router.push(path);
toggleMobile();
const navigateTo = useCallback(
(path?: string) => {
if (path && path !== pathname) {
router.push(path);
}
toggleMobile();
},
[pathname]
);

const getList = async () => {
setAPP(APPS.find((app) => `/${rootpath}` === app?.path));
const { data } = await axios.get(`/api/list?schema=${rootpath}`);
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 (
<AppShell
header={{ height: 60 }}
Expand Down Expand Up @@ -85,7 +114,7 @@ export default function Layout({ children }: { children: React.ReactNode }) {
<Popover.Dropdown p="xs">
<SimpleGrid spacing="xs" cols={3}>
{APPS.map((app) => (
<App isCurrent={APP?.path === app.path} key={app.path} app={app} />
<App isCurrent={APP?.path === app?.path} key={app?.path} app={app} />
))}
</SimpleGrid>
</Popover.Dropdown>
Expand Down Expand Up @@ -126,19 +155,19 @@ export default function Layout({ children }: { children: React.ReactNode }) {
{isLoggedIn ? (
<>
<AppShell.Navbar>
<Stack gap="xs" my="xs">
{APP?.sidebar.map((item) => (
<Stack gap={0} my="xs">
{APP?.sidebar?.map((item) => (
<Button
key={item.label}
key={item?.label}
justify="left"
onClick={() => navigateTo(item.path)}
leftSection={item.icon}
onClick={() => navigateTo(item?.path)}
leftSection={item?.icon || <IconList />}
radius={0}
variant={pathname === item.path ? 'filled' : 'subtle'}
color={APP.color}
variant={pathname === item?.path ? 'filled' : 'subtle'}
color={item?.color || APP.color}
fullWidth
>
{item.label}
{item?.label}
</Button>
))}
</Stack>
Expand Down
55 changes: 55 additions & 0 deletions components/Todo/Todo.tsx
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;
Loading

0 comments on commit a60336b

Please sign in to comment.