-
Notifications
You must be signed in to change notification settings - Fork 131
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
Showing
11 changed files
with
245 additions
and
237 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 |
---|---|---|
@@ -1,143 +1,2 @@ | ||
"use client" | ||
|
||
import TodoService from "@services/TodoService"; | ||
import styles from "./page.module.sass"; | ||
import { useEffect, useState } from "react"; | ||
import { Button, Card, Form, Input, List, Modal, Skeleton, Space, message } from "antd"; | ||
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons'; | ||
import { useSession } from "next-auth/react"; | ||
import TextArea from "antd/es/input/TextArea"; | ||
|
||
export default function Dashboard() { | ||
const CARD_WIDTH = 670; | ||
|
||
const { data: session } = useSession(); | ||
const [messageApi, contextHolder] = message.useMessage(); | ||
|
||
const [form] = Form.useForm(); | ||
const [isBusy, setIsBusy] = useState(false); | ||
const [isLoading, setIsLoading] = useState(true); | ||
const [todos, setTodos] = useState([]); | ||
const [isAddTodoModalOpen, setIsAddTodoModalOpen] = useState(false); | ||
|
||
useEffect(() => { | ||
getAllTodos(); | ||
}, []) | ||
|
||
const getAllTodos = async () => { | ||
setIsLoading(true); | ||
|
||
try { | ||
const response = await TodoService.getAllTodos(); | ||
|
||
// TODO :: Handle errors based on response status | ||
const todos = await response.json(); | ||
|
||
setTodos(Array.isArray(todos) ? todos : []); | ||
|
||
} catch (error) { | ||
console.error(error); | ||
} finally { | ||
setIsLoading(false); | ||
} | ||
}; | ||
|
||
const createTodo = async () => { | ||
setIsBusy(true); | ||
|
||
try { | ||
const values = await form?.validateFields(); | ||
const { todoTitle, todoDescription } = values; | ||
|
||
const response = await TodoService.createTodo(todoTitle, todoDescription); | ||
const newTodo = await response.json(); | ||
const updatedTodos: any = [...(todos ?? []), newTodo]; | ||
|
||
setTodos(updatedTodos); | ||
form?.resetFields(); | ||
setIsAddTodoModalOpen(false) | ||
messageApi.success("Added new todo"); | ||
|
||
} catch (error) { | ||
console.error(error); | ||
} finally { | ||
setIsBusy(false); | ||
} | ||
}; | ||
|
||
const deleteTodo = async (todoId: string) => { | ||
const response = await TodoService.deleteTodo(todoId); | ||
const deletedTodoId = await response.text(); | ||
const filteredTodos = todos?.filter((t: any) => t._id !== deletedTodoId); | ||
messageApi.success("Deleted todo"); | ||
|
||
setTodos(filteredTodos); | ||
}; | ||
|
||
const renderHeader = () => ( | ||
<> | ||
<h1 className={styles.heading}>Hi {session?.user?.name?.split(' ')?.[0]} :)</h1> | ||
<h2 className={styles.sub_heading}>Track your todos with ease.</h2> | ||
</> | ||
) | ||
|
||
return ( | ||
<main className={styles.container}> | ||
{contextHolder} | ||
{renderHeader()} | ||
<Card | ||
title={`Here are your todos`} | ||
extra={<Button type="primary" size="small" shape="circle" onClick={() => setIsAddTodoModalOpen(true)}><PlusOutlined /></Button>} | ||
style={{ width: CARD_WIDTH }} | ||
> | ||
<List | ||
className={styles.list} | ||
loading={isLoading} | ||
itemLayout="horizontal" | ||
loadMore={null} | ||
dataSource={todos} | ||
renderItem={(todo, index) => ( | ||
<List.Item actions={[<Button type="link" danger key="list-delet" onClick={() => deleteTodo(todo._id)}><DeleteOutlined /></Button>]}> | ||
<Skeleton title={false} loading={isLoading} active> | ||
<List.Item.Meta | ||
title={todo?.todoTitle} | ||
description={todo?.todoDescription} | ||
/> | ||
</Skeleton> | ||
</List.Item> | ||
)} | ||
/> | ||
</Card> | ||
<Modal | ||
title="Add new todo" | ||
open={isAddTodoModalOpen} | ||
okText="Add" | ||
okButtonProps={{ loading: isBusy }} | ||
onOk={() => createTodo()} | ||
onCancel={() => { | ||
form?.resetFields(); | ||
setIsAddTodoModalOpen(false) | ||
}} | ||
> | ||
<p>What would you like to add to you todo list?</p> | ||
<Form | ||
form={form} | ||
layout="vertical" | ||
> | ||
<Form.Item | ||
name="todoTitle" | ||
rules={[{ required: true, message: 'Todo title is required!' }]} | ||
> | ||
<Input placeholder="Todo title" /> | ||
</Form.Item> | ||
<Form.Item | ||
name="todoDescription" | ||
rules={[{ required: true, message: 'Todo description is required!' }]} | ||
> | ||
<TextArea rows={4} placeholder="Todo description" /> | ||
</Form.Item> | ||
</Form> | ||
</Modal> | ||
</main> | ||
); | ||
} | ||
import Dashboard from "@components/pages/Dashboard"; | ||
export default Dashboard; |
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 |
---|---|---|
@@ -1,37 +1,2 @@ | ||
import type { Metadata } from "next"; | ||
import { AntdRegistry } from '@ant-design/nextjs-registry'; | ||
import { Inter } from "next/font/google"; | ||
import { getServerSession } from "next-auth"; | ||
import AuthSessionProvider from "@components/providers/AuthSessionProvider"; | ||
import AntdConfigProvider from "@components/providers/AntdConfigProvider"; | ||
import "@styles/globals.css"; | ||
|
||
const inter = Inter({ subsets: ["latin"] }); | ||
|
||
export const metadata: Metadata = { | ||
title: "Full-stack starter template 2024", | ||
description: "NextJs + NextAuth + Typescript + Mongo DB + Ant Design", | ||
icons: { icon: "/logos/next-icon.svg" } | ||
}; | ||
|
||
export default async function RootLayout({ | ||
children, | ||
}: Readonly<{ | ||
children: React.ReactNode; | ||
}>) { | ||
const session = await getServerSession(); | ||
|
||
return ( | ||
<html lang="en"> | ||
<AntdRegistry> | ||
<AntdConfigProvider> | ||
<AuthSessionProvider session={session}> | ||
<body className={inter.className}> | ||
{children} | ||
</body> | ||
</AuthSessionProvider> | ||
</AntdConfigProvider> | ||
</AntdRegistry> | ||
</html> | ||
); | ||
} | ||
import MainLayout from "@layouts/MainLayout"; | ||
export default MainLayout; |
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 |
---|---|---|
@@ -1,57 +1,2 @@ | ||
import Image from "next/image"; | ||
import { Row } from "antd"; | ||
import AuthCard from "@components/molecules/AuthCard"; | ||
import IconCard from "@components/molecules/IconCard"; | ||
import TECH_STACK_INFO from "@constants/TechStackInfo"; | ||
import CustomAvatar from "@components/atoms/CustomAvatar"; | ||
import styles from "./page.module.sass"; | ||
|
||
export default function Home() { | ||
|
||
const renderHeader = () => ( | ||
<> | ||
<Image | ||
src='/logos/next.svg' | ||
alt='Next logo' | ||
width={100} | ||
height={20} | ||
/> | ||
<h1 className={styles.heading}>Full-stack starter template 2024</h1> | ||
<h2 className={styles.sub_heading}>NextJs + NextAuth + Typescript + Mongo DB + Ant Design</h2> | ||
</> | ||
) | ||
|
||
const renderTechStack = () => ( | ||
<Row gutter={16}> | ||
{TECH_STACK_INFO?.map((t) => ( | ||
<IconCard | ||
key={t.name} | ||
text={t.name} | ||
iconPath={t.iconPath} | ||
iconSize={35} | ||
url={t.url} | ||
/> | ||
))} | ||
</Row> | ||
) | ||
|
||
const renderFooter = () => ( | ||
<div className={styles.footer}> | ||
<p className={styles.footer_text}>This template was created on 25th of February 2024.</p> | ||
<CustomAvatar | ||
image='/images/author.png' | ||
mainText='Devon Wijesinghe' | ||
subText='wdevon99' | ||
/> | ||
</div> | ||
) | ||
|
||
return ( | ||
<main className={styles.container}> | ||
{renderHeader()} | ||
{renderTechStack()} | ||
<AuthCard /> | ||
{renderFooter()} | ||
</main> | ||
); | ||
} | ||
import Home from "@components/pages/Home"; | ||
export default Home; |
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,143 @@ | ||
"use client" | ||
|
||
import { useEffect, useState } from "react"; | ||
import { Button, Card, Form, Input, List, Modal, Skeleton, message } from "antd"; | ||
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons'; | ||
import { useSession } from "next-auth/react"; | ||
import TextArea from "antd/es/input/TextArea"; | ||
import TodoService from "@services/TodoService"; | ||
import styles from "./styles.module.sass"; | ||
|
||
export default function Dashboard() { | ||
const CARD_WIDTH = 670; | ||
|
||
const { data: session } = useSession(); | ||
const [messageApi, contextHolder] = message.useMessage(); | ||
|
||
const [form] = Form.useForm(); | ||
const [isBusy, setIsBusy] = useState(false); | ||
const [isLoading, setIsLoading] = useState(true); | ||
const [todos, setTodos] = useState([]); | ||
const [isAddTodoModalOpen, setIsAddTodoModalOpen] = useState(false); | ||
|
||
useEffect(() => { | ||
getAllTodos(); | ||
}, []) | ||
|
||
const getAllTodos = async () => { | ||
setIsLoading(true); | ||
|
||
try { | ||
const response = await TodoService.getAllTodos(); | ||
|
||
// TODO :: Handle errors based on response status | ||
const todos = await response.json(); | ||
|
||
setTodos(Array.isArray(todos) ? todos : []); | ||
|
||
} catch (error) { | ||
console.error(error); | ||
} finally { | ||
setIsLoading(false); | ||
} | ||
}; | ||
|
||
const createTodo = async () => { | ||
setIsBusy(true); | ||
|
||
try { | ||
const values = await form?.validateFields(); | ||
const { todoTitle, todoDescription } = values; | ||
|
||
const response = await TodoService.createTodo(todoTitle, todoDescription); | ||
const newTodo = await response.json(); | ||
const updatedTodos: any = [...(todos ?? []), newTodo]; | ||
|
||
setTodos(updatedTodos); | ||
form?.resetFields(); | ||
setIsAddTodoModalOpen(false) | ||
messageApi.success("Added new todo"); | ||
|
||
} catch (error) { | ||
console.error(error); | ||
} finally { | ||
setIsBusy(false); | ||
} | ||
}; | ||
|
||
const deleteTodo = async (todoId: string) => { | ||
const response = await TodoService.deleteTodo(todoId); | ||
const deletedTodoId = await response.text(); | ||
const filteredTodos = todos?.filter((t: any) => t._id !== deletedTodoId); | ||
messageApi.success("Deleted todo"); | ||
|
||
setTodos(filteredTodos); | ||
}; | ||
|
||
const renderHeader = () => ( | ||
<> | ||
<h1 className={styles.heading}>Hi {session?.user?.name?.split(' ')?.[0]} :)</h1> | ||
<h2 className={styles.sub_heading}>Track your todos with ease.</h2> | ||
</> | ||
) | ||
|
||
return ( | ||
<main className={styles.container}> | ||
{contextHolder} | ||
{renderHeader()} | ||
<Card | ||
title={`Here are your todos`} | ||
extra={<Button type="primary" size="small" shape="circle" onClick={() => setIsAddTodoModalOpen(true)}><PlusOutlined /></Button>} | ||
style={{ width: CARD_WIDTH }} | ||
> | ||
<List | ||
className={styles.list} | ||
loading={isLoading} | ||
itemLayout="horizontal" | ||
loadMore={null} | ||
dataSource={todos} | ||
renderItem={(todo, index) => ( | ||
<List.Item actions={[<Button type="link" danger key="list-delet" onClick={() => deleteTodo(todo._id)}><DeleteOutlined /></Button>]}> | ||
<Skeleton title={false} loading={isLoading} active> | ||
<List.Item.Meta | ||
title={todo?.todoTitle} | ||
description={todo?.todoDescription} | ||
/> | ||
</Skeleton> | ||
</List.Item> | ||
)} | ||
/> | ||
</Card> | ||
<Modal | ||
title="Add new todo" | ||
open={isAddTodoModalOpen} | ||
okText="Add" | ||
okButtonProps={{ loading: isBusy }} | ||
onOk={() => createTodo()} | ||
onCancel={() => { | ||
form?.resetFields(); | ||
setIsAddTodoModalOpen(false) | ||
}} | ||
> | ||
<p>What would you like to add to you todo list?</p> | ||
<Form | ||
form={form} | ||
layout="vertical" | ||
> | ||
<Form.Item | ||
name="todoTitle" | ||
rules={[{ required: true, message: 'Todo title is required!' }]} | ||
> | ||
<Input placeholder="Todo title" /> | ||
</Form.Item> | ||
<Form.Item | ||
name="todoDescription" | ||
rules={[{ required: true, message: 'Todo description is required!' }]} | ||
> | ||
<TextArea rows={4} placeholder="Todo description" /> | ||
</Form.Item> | ||
</Form> | ||
</Modal> | ||
</main> | ||
); | ||
} |
File renamed without changes.
Oops, something went wrong.