TaskFlow - Todo App with Next.js, Prisma, NextAuth

This commit is contained in:
PhongMacbook
2025-12-16 23:48:54 +07:00
parent fecae546e9
commit 81a38eef72
2 changed files with 128 additions and 119 deletions

228
README.md
View File

@@ -1,44 +1,64 @@
# TaskFlow - ng dng Qun lý Công vic Cá nhân # TaskFlow - Ung dung Quan ly Cong viec Ca nhan
ng dng To-do List hin đại vi h thng xác thc người dùng, cho phép mi user qun lý công vic ca riêng mình. Ung dung To-do List hien dai voi he thong xac thuc nguoi dung, cho phep moi user quan ly cong viec cua rieng minh.
## 🚀 Tính năng Demo: https://todo-app-topaz-nine-36.vercel.app
### Xác thực & Phân quyền ## Tinh nang
- ✅ Đăng ký / đăng nhập bằng Email & Password
- ✅ Đăng nhập bằng Google OAuth
- ✅ Mỗi user chỉ xem và thao tác trên công việc của chính mình
- ✅ Đăng xuất
### Quản lý Công việc (CRUD) ### Xac thuc va Phan quyen
- ✅ Tạo công việc mới (tiêu đề, mô tả, trạng thái, deadline) - Dang ky / dang nhap bang Email va Password
- ✅ Sửa công việc - Dang nhap bang Google OAuth
- ✅ Xóa công việc - Moi user chi xem va thao tac tren cong viec cua chinh minh
- ✅ Xem danh sách công việc - Dang xuat
### Tìm kiếm Lọc Sắp xếp ### Quan ly Cong viec (CRUD)
- ✅ Tìm kiếm theo tiêu đề - Tao cong viec moi (tieu de, mo ta, trang thai, deadline)
- ✅ Lọc theo trạng thái (Todo / In Progress / Done) - Sua cong viec
- ✅ Lọc theo deadline (hôm nay, tuần này, quá hạn) - Xoa cong viec
- ✅ Sắp xếp theo deadline, trạng thái, thời gian tạo - Xem danh sach cong viec
### Giao diện ### Tim kiem - Loc - Sap xep
- ✅ Responsive (desktop + mobile) - Tim kiem theo tieu de
- ✅ Màu sắc rõ ràng cho từng trạng thái - Loc theo trang thai (Todo / In Progress / Done)
- ✅ Hiển th deadline sắp đến & quá hạn - Loc theo deadline (hom nay, tuan nay, qua han)
- ✅ Loading states, empty states, thông báo - Sap xep theo deadline, trang thai, thoi gian tao
## 🛠 Tech Stack ### Giao dien
- Responsive (desktop + mobile)
- Mau sac ro rang cho tung trang thai
- Hien thi deadline sap den va qua han
- Kanban Board (keo tha task giua cac cot)
- Calendar view hien thi task theo ngay
- Loading states, empty states, thong bao
| Layer | Technology | ## Tech Stack
|-------|------------|
| Frontend | Next.js 14 (App Router) + TypeScript |
| Styling | Tailwind CSS |
| Auth | NextAuth.js (Credentials + Google OAuth) |
| Database | PostgreSQL + Prisma ORM |
| Validation | Zod |
## 📁 Cấu trúc Project ### Frontend
- Next.js 14 (App Router)
- TypeScript
- Tailwind CSS
- React Hot Toast (thong bao)
- React Icons
### Backend
- Next.js API Routes
- NextAuth.js (xac thuc)
- Credentials Provider (email/password)
- Google OAuth Provider
### Database
- PostgreSQL (Neon - serverless)
- Prisma ORM
### Validation
- Zod
### Deployment
- Vercel (hosting)
- Neon (PostgreSQL database)
## Cau truc Project
``` ```
todo-app/ todo-app/
@@ -70,47 +90,49 @@ todo-app/
└── package.json └── package.json
``` ```
## 🔧 Hướng dn cài đặt ## Huong dan cai dat
### 1. Clone và cài đặt dependencies ### 1. Clone va cai dat dependencies
```bash ```bash
cd todo-app cd todo-app
npm install npm install
``` ```
### 2. Cu hình biến môi trường ### 2. Cau hinh bien moi truong
Copy file `.env.example` thành `.env` và cp nht các giá tr: Copy file .env.example thanh .env va cap nhat cac gia tri:
```bash ```bash
cp .env.example .env cp .env.example .env
``` ```
```env ```env
# Database (PostgreSQL) # Database (PostgreSQL - Neon)
DATABASE_URL="postgresql://user:password@localhost:5432/todoapp?schema=public" DATABASE_URL="postgresql://user:password@host/database?sslmode=require"
DIRECT_URL="postgresql://user:password@host/database?sslmode=require"
# NextAuth # NextAuth
NEXTAUTH_URL="http://localhost:3000" NEXTAUTH_URL="http://localhost:3000"
NEXTAUTH_SECRET="your-super-secret-key-here" NEXTAUTH_SECRET="your-super-secret-key-here"
# Google OAuth (ly t Google Cloud Console) # Google OAuth (lay tu Google Cloud Console)
GOOGLE_CLIENT_ID="your-google-client-id" GOOGLE_CLIENT_ID="your-google-client-id"
GOOGLE_CLIENT_SECRET="your-google-client-secret" GOOGLE_CLIENT_SECRET="your-google-client-secret"
``` ```
### 3. Thiết lp Google OAuth ### 3. Thiet lap Google OAuth
1. Vào [Google Cloud Console](https://console.cloud.google.com/) 1. Vao Google Cloud Console (https://console.cloud.google.com/)
2. To project mi hoc chn project có sn 2. Tao project moi hoac chon project co san
3. Vào **APIs & Services** > **Credentials** 3. Vao APIs & Services > Credentials
4. To **OAuth 2.0 Client IDs** 4. Tao OAuth 2.0 Client IDs
5. Thêm Authorized redirect URIs: 5. Them Authorized redirect URIs:
- `http://localhost:3000/api/auth/callback/google` - http://localhost:3000/api/auth/callback/google
6. Copy Client ID và Client Secret vào file `.env` - https://your-domain.vercel.app/api/auth/callback/google
6. Copy Client ID va Client Secret vao file .env
### 4. Khi to Database ### 4. Khoi tao Database
```bash ```bash
# Generate Prisma client # Generate Prisma client
@@ -123,92 +145,76 @@ npm run db:push
npm run db:seed npm run db:seed
``` ```
### 5. Chy ng dng ### 5. Chay ung dung
```bash ```bash
npm run dev npm run dev
``` ```
Mở [http://localhost:3000](http://localhost:3000) để xem kết qu. Mo http://localhost:3000 de xem ket qua.
## 📊 API Endpoints ## API Endpoints
### Authentication ### Authentication
| Method | Endpoint | Mô t | | Method | Endpoint | Mo ta |
|--------|----------|-------| |--------|----------|-------|
| POST | `/api/auth/register` | Đăng ký tài khon mi | | POST | /api/auth/register | Dang ky tai khoan moi |
| POST | `/api/auth/[...nextauth]` | NextAuth handlers | | POST | /api/auth/[...nextauth] | NextAuth handlers |
### Tasks (Yêu cu xác thc) ### Tasks (Yeu cau xac thuc)
| Method | Endpoint | Mô t | | Method | Endpoint | Mo ta |
|--------|----------|-------| |--------|----------|-------|
| GET | `/api/tasks` | Ly danh sách tasks ca user | | GET | /api/tasks | Lay danh sach tasks cua user |
| POST | `/api/tasks` | To task mi | | POST | /api/tasks | Tao task moi |
| GET | `/api/tasks/:id` | Ly chi tiết task | | GET | /api/tasks/:id | Lay chi tiet task |
| PUT | `/api/tasks/:id` | Cp nht task | | PUT | /api/tasks/:id | Cap nhat task |
| DELETE | `/api/tasks/:id` | Xóa task | | DELETE | /api/tasks/:id | Xoa task |
**Query Parameters cho GET /api/tasks:** Query Parameters cho GET /api/tasks:
- `search` - Tìm kiếm theo tiêu đề - search: Tim kiem theo tieu de
- `status` - Lc theo trng thái (TODO, IN_PROGRESS, DONE) - status: Loc theo trang thai (TODO, IN_PROGRESS, DONE)
- `deadline` - Lc theo deadline (today, this_week, overdue) - deadline: Loc theo deadline (today, this_week, overdue)
- `sortBy` - Sp xếp theo (deadline, status, createdAt) - sortBy: Sap xep theo (deadline, status, createdAt)
- `sortOrder` - Th t (asc, desc) - sortOrder: Thu tu (asc, desc)
## 🔐 Bảo mt ## Bao mat
- **Middleware Protection**: API routes được bo v bng NextAuth middleware - Middleware Protection: API routes duoc bao ve bang NextAuth middleware
- **User Isolation**: Mi task được gn vi `userId`, API ch tr v tasks ca user hin ti - User Isolation: Moi task duoc gan voi userId, API chi tra ve tasks cua user hien tai
- **Password Hashing**: Mt khu được hash bng bcrypt - Password Hashing: Mat khau duoc hash bang bcrypt
- **Input Validation**: Tt c input được validate bng Zod - Input Validation: Tat ca input duoc validate bang Zod
## 🎨 Trng thái Task ## Trang thai Task
| Status | Label | Màu | | Status | Label | Mau |
|--------|-------|-----| |--------|-------|-----|
| TODO | Chưa làm | 🟡 Vàng | | TODO | Chua lam | Vang |
| IN_PROGRESS | Đang làm | 🔵 Xanh dương | | IN_PROGRESS | Dang lam | Xanh duong |
| DONE | Hoàn thành | 🟢 Xanh lá | | DONE | Hoan thanh | Xanh la |
| Overdue | Quá hn | 🔴 Đỏ | | Overdue | Qua han | Do |
## 📱 Screenshots ## Deploy
### Dashboard ### Vercel
- Thống kê tổng quan
- Bộ lọc và tìm kiếm
- Danh sách công việc
### Task Form 1. Push code len Git repository
- Tạo / sửa công việc 2. Import project vao Vercel
- Chọn trạng thái 3. Cau hinh Environment Variables:
- Đặt deadline - DATABASE_URL
- DIRECT_URL
- NEXTAUTH_URL (URL production)
- NEXTAUTH_SECRET
- GOOGLE_CLIENT_ID
- GOOGLE_CLIENT_SECRET
4. Deploy
### Authentication ## Demo Account
- Đăng nhập / Đăng ký
- Google OAuth
## 🚀 Deploy - Email: demo@example.com
- Password: demo123456
### Vercel (Recommended) ## License
1. Push code lên GitHub MIT
2. Import project vào [Vercel](https://vercel.com)
3. Thêm biến môi trường
4. Deploy!
### Docker
```bash
docker-compose up -d
```
## 📝 Demo Account
- **Email**: demo@example.com
- **Password**: demo123456
## 📄 License
MIT License

View File

@@ -7,9 +7,10 @@ import { updateTaskSchema } from '@/lib/validations'
// GET /api/tasks/[id] - Lấy chi tiết task // GET /api/tasks/[id] - Lấy chi tiết task
export async function GET( export async function GET(
request: NextRequest, request: NextRequest,
{ params }: { params: { id: string } } { params }: { params: Promise<{ id: string }> }
) { ) {
try { try {
const { id } = await params
const session = await getServerSession(authOptions) const session = await getServerSession(authOptions)
if (!session?.user?.id) { if (!session?.user?.id) {
@@ -21,7 +22,7 @@ export async function GET(
const task = await prisma.task.findFirst({ const task = await prisma.task.findFirst({
where: { where: {
id: params.id, id: id,
userId: session.user.id, // Chỉ lấy task của user hiện tại userId: session.user.id, // Chỉ lấy task của user hiện tại
}, },
}) })
@@ -49,9 +50,10 @@ export async function GET(
// PUT /api/tasks/[id] - Cập nhật task // PUT /api/tasks/[id] - Cập nhật task
export async function PUT( export async function PUT(
request: NextRequest, request: NextRequest,
{ params }: { params: { id: string } } { params }: { params: Promise<{ id: string }> }
) { ) {
try { try {
const { id } = await params
const session = await getServerSession(authOptions) const session = await getServerSession(authOptions)
if (!session?.user?.id) { if (!session?.user?.id) {
@@ -64,7 +66,7 @@ export async function PUT(
// Check if task belongs to user // Check if task belongs to user
const existingTask = await prisma.task.findFirst({ const existingTask = await prisma.task.findFirst({
where: { where: {
id: params.id, id: id,
userId: session.user.id, userId: session.user.id,
}, },
}) })
@@ -94,7 +96,7 @@ export async function PUT(
// Update task // Update task
const task = await prisma.task.update({ const task = await prisma.task.update({
where: { id: params.id }, where: { id: id },
data: { data: {
...(title && { title }), ...(title && { title }),
...(description !== undefined && { description }), ...(description !== undefined && { description }),
@@ -122,9 +124,10 @@ export async function PUT(
// DELETE /api/tasks/[id] - Xóa task // DELETE /api/tasks/[id] - Xóa task
export async function DELETE( export async function DELETE(
request: NextRequest, request: NextRequest,
{ params }: { params: { id: string } } { params }: { params: Promise<{ id: string }> }
) { ) {
try { try {
const { id } = await params
const session = await getServerSession(authOptions) const session = await getServerSession(authOptions)
if (!session?.user?.id) { if (!session?.user?.id) {
@@ -137,7 +140,7 @@ export async function DELETE(
// Check if task belongs to user // Check if task belongs to user
const existingTask = await prisma.task.findFirst({ const existingTask = await prisma.task.findFirst({
where: { where: {
id: params.id, id: id,
userId: session.user.id, userId: session.user.id,
}, },
}) })
@@ -151,7 +154,7 @@ export async function DELETE(
// Delete task // Delete task
await prisma.task.delete({ await prisma.task.delete({
where: { id: params.id }, where: { id: id },
}) })
return NextResponse.json({ return NextResponse.json({