From 81a38eef723fb71fe510d9c3255ac6e4533a9cf5 Mon Sep 17 00:00:00 2001 From: PhongMacbook Date: Tue, 16 Dec 2025 23:48:54 +0700 Subject: [PATCH] TaskFlow - Todo App with Next.js, Prisma, NextAuth --- README.md | 228 ++++++++++++++++---------------- src/app/api/tasks/[id]/route.ts | 19 +-- 2 files changed, 128 insertions(+), 119 deletions(-) diff --git a/README.md b/README.md index ca4720c..7d79de4 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,64 @@ -# TaskFlow - Ứng dụng Quản lý Công việc Cá nhân +# TaskFlow - Ung dung Quan ly Cong viec Ca nhan -Ứng dụng To-do List hiện đại với hệ thống xác thực người dùng, cho phép mỗi user quản lý công việc của 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 -- ✅ Đă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 +## Tinh nang -### Quản lý Công việc (CRUD) -- ✅ Tạo công việc mới (tiêu đề, mô tả, trạng thái, deadline) -- ✅ Sửa công việc -- ✅ Xóa công việc -- ✅ Xem danh sách công việc +### Xac thuc va Phan quyen +- Dang ky / dang nhap bang Email va Password +- Dang nhap bang Google OAuth +- Moi user chi xem va thao tac tren cong viec cua chinh minh +- Dang xuat -### Tìm kiếm – Lọc – Sắp xếp -- ✅ Tìm kiếm theo tiêu đề -- ✅ Lọc theo trạng thái (Todo / In Progress / Done) -- ✅ Lọc theo deadline (hôm nay, tuần này, quá hạn) -- ✅ Sắp xếp theo deadline, trạng thái, thời gian tạo +### Quan ly Cong viec (CRUD) +- Tao cong viec moi (tieu de, mo ta, trang thai, deadline) +- Sua cong viec +- Xoa cong viec +- Xem danh sach cong viec -### Giao diện -- ✅ Responsive (desktop + mobile) -- ✅ Màu sắc rõ ràng cho từng trạng thái -- ✅ Hiển thị deadline sắp đến & quá hạn -- ✅ Loading states, empty states, thông báo +### Tim kiem - Loc - Sap xep +- Tim kiem theo tieu de +- Loc theo trang thai (Todo / In Progress / Done) +- Loc theo deadline (hom nay, tuan nay, qua han) +- 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 | -|-------|------------| -| Frontend | Next.js 14 (App Router) + TypeScript | -| Styling | Tailwind CSS | -| Auth | NextAuth.js (Credentials + Google OAuth) | -| Database | PostgreSQL + Prisma ORM | -| Validation | Zod | +## Tech Stack -## 📁 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/ @@ -70,47 +90,49 @@ todo-app/ └── package.json ``` -## 🔧 Hướng dẫn cài đặt +## Huong dan cai dat -### 1. Clone và cài đặt dependencies +### 1. Clone va cai dat dependencies ```bash cd todo-app npm install ``` -### 2. Cấu hình biến môi trường +### 2. Cau hinh bien moi truong -Copy file `.env.example` thành `.env` và cập nhật các giá trị: +Copy file .env.example thanh .env va cap nhat cac gia tri: ```bash cp .env.example .env ``` ```env -# Database (PostgreSQL) -DATABASE_URL="postgresql://user:password@localhost:5432/todoapp?schema=public" +# Database (PostgreSQL - Neon) +DATABASE_URL="postgresql://user:password@host/database?sslmode=require" +DIRECT_URL="postgresql://user:password@host/database?sslmode=require" # NextAuth NEXTAUTH_URL="http://localhost:3000" NEXTAUTH_SECRET="your-super-secret-key-here" -# Google OAuth (lấy từ Google Cloud Console) +# Google OAuth (lay tu Google Cloud Console) GOOGLE_CLIENT_ID="your-google-client-id" GOOGLE_CLIENT_SECRET="your-google-client-secret" ``` -### 3. Thiết lập Google OAuth +### 3. Thiet lap Google OAuth -1. Vào [Google Cloud Console](https://console.cloud.google.com/) -2. Tạo project mới hoặc chọn project có sẵn -3. Vào **APIs & Services** > **Credentials** -4. Tạo **OAuth 2.0 Client IDs** -5. Thêm Authorized redirect URIs: - - `http://localhost:3000/api/auth/callback/google` -6. Copy Client ID và Client Secret vào file `.env` +1. Vao Google Cloud Console (https://console.cloud.google.com/) +2. Tao project moi hoac chon project co san +3. Vao APIs & Services > Credentials +4. Tao OAuth 2.0 Client IDs +5. Them Authorized redirect URIs: + - http://localhost:3000/api/auth/callback/google + - https://your-domain.vercel.app/api/auth/callback/google +6. Copy Client ID va Client Secret vao file .env -### 4. Khởi tạo Database +### 4. Khoi tao Database ```bash # Generate Prisma client @@ -123,92 +145,76 @@ npm run db:push npm run db:seed ``` -### 5. Chạy ứng dụng +### 5. Chay ung dung ```bash 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 -| Method | Endpoint | Mô tả | +| Method | Endpoint | Mo ta | |--------|----------|-------| -| POST | `/api/auth/register` | Đăng ký tài khoản mới | -| POST | `/api/auth/[...nextauth]` | NextAuth handlers | +| POST | /api/auth/register | Dang ky tai khoan moi | +| POST | /api/auth/[...nextauth] | NextAuth handlers | -### Tasks (Yêu cầu xác thực) +### Tasks (Yeu cau xac thuc) -| Method | Endpoint | Mô tả | +| Method | Endpoint | Mo ta | |--------|----------|-------| -| GET | `/api/tasks` | Lấy danh sách tasks của user | -| POST | `/api/tasks` | Tạo task mới | -| GET | `/api/tasks/:id` | Lấy chi tiết task | -| PUT | `/api/tasks/:id` | Cập nhật task | -| DELETE | `/api/tasks/:id` | Xóa task | +| GET | /api/tasks | Lay danh sach tasks cua user | +| POST | /api/tasks | Tao task moi | +| GET | /api/tasks/:id | Lay chi tiet task | +| PUT | /api/tasks/:id | Cap nhat task | +| DELETE | /api/tasks/:id | Xoa task | -**Query Parameters cho GET /api/tasks:** -- `search` - Tìm kiếm theo tiêu đề -- `status` - Lọc theo trạng thái (TODO, IN_PROGRESS, DONE) -- `deadline` - Lọc theo deadline (today, this_week, overdue) -- `sortBy` - Sắp xếp theo (deadline, status, createdAt) -- `sortOrder` - Thứ tự (asc, desc) +Query Parameters cho GET /api/tasks: +- search: Tim kiem theo tieu de +- status: Loc theo trang thai (TODO, IN_PROGRESS, DONE) +- deadline: Loc theo deadline (today, this_week, overdue) +- sortBy: Sap xep theo (deadline, status, createdAt) +- sortOrder: Thu tu (asc, desc) -## 🔐 Bảo mật +## Bao mat -- **Middleware Protection**: API routes được bảo vệ bằng NextAuth middleware -- **User Isolation**: Mỗi task được gắn với `userId`, API chỉ trả về tasks của user hiện tại -- **Password Hashing**: Mật khẩu được hash bằng bcrypt -- **Input Validation**: Tất cả input được validate bằng Zod +- Middleware Protection: API routes duoc bao ve bang NextAuth middleware +- User Isolation: Moi task duoc gan voi userId, API chi tra ve tasks cua user hien tai +- Password Hashing: Mat khau duoc hash bang bcrypt +- Input Validation: Tat ca input duoc validate bang Zod -## 🎨 Trạng thái Task +## Trang thai Task -| Status | Label | Màu | +| Status | Label | Mau | |--------|-------|-----| -| TODO | Chưa làm | 🟡 Vàng | -| IN_PROGRESS | Đang làm | 🔵 Xanh dương | -| DONE | Hoàn thành | 🟢 Xanh lá | -| Overdue | Quá hạn | 🔴 Đỏ | +| TODO | Chua lam | Vang | +| IN_PROGRESS | Dang lam | Xanh duong | +| DONE | Hoan thanh | Xanh la | +| Overdue | Qua han | Do | -## 📱 Screenshots +## Deploy -### Dashboard -- Thống kê tổng quan -- Bộ lọc và tìm kiếm -- Danh sách công việc +### Vercel -### Task Form -- Tạo / sửa công việc -- Chọn trạng thái -- Đặt deadline +1. Push code len Git repository +2. Import project vao Vercel +3. Cau hinh Environment Variables: + - DATABASE_URL + - DIRECT_URL + - NEXTAUTH_URL (URL production) + - NEXTAUTH_SECRET + - GOOGLE_CLIENT_ID + - GOOGLE_CLIENT_SECRET +4. Deploy -### Authentication -- Đăng nhập / Đăng ký -- Google OAuth +## Demo Account -## 🚀 Deploy +- Email: demo@example.com +- Password: demo123456 -### Vercel (Recommended) +## License -1. Push code lên GitHub -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 +MIT diff --git a/src/app/api/tasks/[id]/route.ts b/src/app/api/tasks/[id]/route.ts index 31d4f8d..93964bd 100644 --- a/src/app/api/tasks/[id]/route.ts +++ b/src/app/api/tasks/[id]/route.ts @@ -7,9 +7,10 @@ import { updateTaskSchema } from '@/lib/validations' // GET /api/tasks/[id] - Lấy chi tiết task export async function GET( request: NextRequest, - { params }: { params: { id: string } } + { params }: { params: Promise<{ id: string }> } ) { try { + const { id } = await params const session = await getServerSession(authOptions) if (!session?.user?.id) { @@ -21,7 +22,7 @@ export async function GET( const task = await prisma.task.findFirst({ where: { - id: params.id, + id: id, 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 export async function PUT( request: NextRequest, - { params }: { params: { id: string } } + { params }: { params: Promise<{ id: string }> } ) { try { + const { id } = await params const session = await getServerSession(authOptions) if (!session?.user?.id) { @@ -64,7 +66,7 @@ export async function PUT( // Check if task belongs to user const existingTask = await prisma.task.findFirst({ where: { - id: params.id, + id: id, userId: session.user.id, }, }) @@ -94,7 +96,7 @@ export async function PUT( // Update task const task = await prisma.task.update({ - where: { id: params.id }, + where: { id: id }, data: { ...(title && { title }), ...(description !== undefined && { description }), @@ -122,9 +124,10 @@ export async function PUT( // DELETE /api/tasks/[id] - Xóa task export async function DELETE( request: NextRequest, - { params }: { params: { id: string } } + { params }: { params: Promise<{ id: string }> } ) { try { + const { id } = await params const session = await getServerSession(authOptions) if (!session?.user?.id) { @@ -137,7 +140,7 @@ export async function DELETE( // Check if task belongs to user const existingTask = await prisma.task.findFirst({ where: { - id: params.id, + id: id, userId: session.user.id, }, }) @@ -151,7 +154,7 @@ export async function DELETE( // Delete task await prisma.task.delete({ - where: { id: params.id }, + where: { id: id }, }) return NextResponse.json({