This commit is contained in:
Iliyan Angelov
2025-11-16 15:12:43 +02:00
parent 824eec6190
commit 93d4c1df80
54 changed files with 1606 additions and 1612 deletions

View File

@@ -1,34 +1,34 @@
# Chức năng 6: Quên Mật Khẩu (Forgot Password) - Hoàn Thành
# Function 6: Forgot Password - Completed
## 📦 Files Đã Tạo/Cập Nhật
## 📦 Files Created/Updated
### Frontend
1. **`client/src/pages/auth/ForgotPasswordPage.tsx`** - Component form quên mật khẩu
1. **`client/src/pages/auth/ForgotPasswordPage.tsx`** - Forgot password form component
2. **`client/src/pages/auth/index.ts`** - Export ForgotPasswordPage
3. **`client/src/App.tsx`** - Route `/forgot-password`
### Backend
4. **`server/src/controllers/authController.js`** - forgotPassword() & resetPassword()
5. **`server/src/routes/authRoutes.js`** - Routes cho forgot/reset password
5. **`server/src/routes/authRoutes.js`** - Routes for forgot/reset password
## ✨ Tính Năng Chính
## ✨ Main Features
### 1. Form State (Initial)
```
┌─────────────────────────────────────┐
│ 🏨 Hotel Icon (Blue) │
Quên mật khẩu?
Nhập email để nhận link...
Forgot password?
Enter email to receive link... │
├─────────────────────────────────────┤
│ ┌───────────────────────────────┐ │
│ │ Email │ │
│ │ [📧 email@example.com ] │ │
│ ├───────────────────────────────┤ │
│ │ [📤 Gửi link đặt lại MK] │ │
│ │ [📤 Send reset link] │ │
│ ├───────────────────────────────┤ │
│ │ ← Quay lại đăng nhập │ │
│ │ ← Back to login │ │
│ └───────────────────────────────┘ │
Chưa có tài khoản? Đăng ký ngay
Don't have an account? Sign up now
└─────────────────────────────────────┘
```
@@ -37,23 +37,23 @@
┌─────────────────────────────────────┐
│ ✅ Success Icon │
│ │
│ Email đã được gửi!
Chúng tôi đã gửi link đến
│ Email has been sent!
We have sent a link to
│ user@example.com │
├─────────────────────────────────────┤
Lưu ý:
│ • Link có hiệu lực trong 1 giờ
│ • Kiểm tra cả thư mục Spam/Junk
│ • Nếu không nhận được, thử lại
Note:
│ • Link is valid for 1 hour
│ • Check Spam/Junk folder
│ • If not received, try again
├─────────────────────────────────────┤
│ [📧 Gửi lại email] │
│ [← Quay lại đăng nhập]
│ [📧 Resend email]
│ [← Back to login]
└─────────────────────────────────────┘
```
### 3. Two-State Design Pattern
**Form State** - Nhập email
**Success State** - Hiển thị xác nhận & hướng dẫn
**Form State** - Enter email
**Success State** - Display confirmation & instructions
State management:
```typescript
@@ -61,19 +61,19 @@ const [isSuccess, setIsSuccess] = useState(false);
const [submittedEmail, setSubmittedEmail] = useState('');
```
## 🔧 Features Chi Tiết
## 🔧 Detailed Features
### 1. Validation (Yup Schema)
```typescript
email:
- Required: "Email là bắt buộc"
- Valid format: "Email không hợp lệ"
- Required: "Email is required"
- Valid format: "Invalid email format"
- Trim whitespace
```
### 2. Form Field
- **Email input** với Mail icon
- Auto-focus khi load page
- **Email input** with Mail icon
- Auto-focus when page loads
- Validation real-time
- Error message inline
@@ -82,12 +82,12 @@ email:
{isLoading ? (
<>
<Loader2 className="animate-spin" />
Đang xử ...
Processing...
</>
) : (
<>
<Send />
Gửi link đặt lại mật khẩu
Send reset link
</>
)}
```
@@ -104,15 +104,15 @@ email:
- Can resend email
**Action Buttons**
- "Gửi lại email" - Reset to form state
- "Quay lại đăng nhập" - Navigate to /login
- "Resend email" - Reset to form state
- "Back to login" - Navigate to /login
### 5. Help Section
```tsx
<div className="bg-white rounded-lg shadow-sm border">
<h3>Cần trợ giúp?</h3>
<h3>Need help?</h3>
<p>
Liên hệ: support@hotel.com hoặc 1900-xxxx
Contact: support@hotel.com or 1900-xxxx
</p>
</div>
```
@@ -267,7 +267,7 @@ CREATE TABLE password_reset_tokens (
2. Enter email address
3. Click "Gửi link đặt lại mật khẩu"
3. Click "Send reset link"
4. Frontend validation (Yup)
@@ -286,7 +286,7 @@ CREATE TABLE password_reset_tokens (
10. Click link → /reset-password/:token
11. Enter new password (Chức năng 7)
11. Enter new password (Function 7)
```
## 🧪 Test Scenarios
@@ -305,7 +305,7 @@ Expected:
```
Input: email = "notanemail"
Expected:
- Validation error: "Email không hợp lệ"
- Validation error: "Invalid email format"
- Form not submitted
```
@@ -313,7 +313,7 @@ Expected:
```
Input: email = ""
Expected:
- Validation error: "Email là bắt buộc"
- Validation error: "Email is required"
- Form not submitted
```
@@ -323,12 +323,12 @@ Action: Submit form
Expected:
- Button disabled
- Spinner shows
- Text: "Đang xử lý..."
- Text: "Processing..."
```
### Test Case 5: Resend email
```
Action: Click "Gửi lại email" in success state
Action: Click "Resend email" in success state
Expected:
- Return to form state
- Email field cleared
@@ -337,7 +337,7 @@ Expected:
### Test Case 6: Back to login
```
Action: Click "Quay lại đăng nhập"
Action: Click "Back to login"
Expected:
- Navigate to /login
```
@@ -482,7 +482,7 @@ never expose raw reset tokens in logs. To enable email sending:
---
**Status:**Chức năng 6 hoàn thành
**Next:** Chức năng 7 - Reset Password (form to change password with token)
**Status:**Function 6 completed
**Next:** Function 7 - Reset Password (form to change password with token)
**Test URL:** http://localhost:5173/forgot-password
**API:** POST /api/auth/forgot-password

View File

@@ -1,48 +1,48 @@
# Layout Components - Chức năng 1
# Layout Components - Function 1
## Tổng quan
Đã triển khai thành công **Chức năng 1: Layout cơ bản** bao gồm:
## Overview
Successfully implemented **Function 1: Basic Layout** including:
### Components đã tạo
### Components Created
#### 1. **Header** (`src/components/layout/Header.tsx`)
- Logo và tên ứng dụng
- Sticky header với shadow
- Logo and application name
- Sticky header with shadow
- Responsive design
- Links cơ bản (Trang chủ, Phòng, Đặt phòng)
- Basic links (Home, Rooms, Bookings)
#### 2. **Footer** (`src/components/layout/Footer.tsx`)
- Thông tin công ty
- Quick links (Liên kết nhanh)
- Support links (Hỗ trợ)
- Contact info (Thông tin liên hệ)
- Company information
- Quick links
- Support links
- Contact information
- Social media icons
- Copyright info
- Copyright information
- Fully responsive (4 columns → 2 → 1)
#### 3. **Navbar** (`src/components/layout/Navbar.tsx`)
- **Trạng thái chưa đăng nhập**:
- Hiển thị nút "Đăng nhập" và "Đăng ký"
- **Trạng thái đã đăng nhập**:
- Hiển thị avatar/tên user
- Dropdown menu với "Hồ sơ", "Quản trị" (admin), "Đăng xuất"
- Mobile menu với hamburger icon
- Responsive cho desktop mobile
- **Not logged in state**:
- Display "Login" and "Register" buttons
- **Logged in state**:
- Display avatar/user name
- Dropdown menu with "Profile", "Admin" (admin), "Logout"
- Mobile menu with hamburger icon
- Responsive for desktop and mobile
#### 4. **SidebarAdmin** (`src/components/layout/SidebarAdmin.tsx`)
- Chỉ hiển thị cho role = "admin"
- Collapsible sidebar (mở/đóng)
- Only displays for role = "admin"
- Collapsible sidebar (open/close)
- Menu items: Dashboard, Users, Rooms, Bookings, Payments, Services, Promotions, Banners, Reports, Settings
- Active state highlighting
- Responsive design
#### 5. **LayoutMain** (`src/components/layout/LayoutMain.tsx`)
- Tích hợp Header, Navbar, Footer
- Sử dụng `<Outlet />` để render nội dung động
- Integrates Header, Navbar, Footer
- Uses `<Outlet />` to render dynamic content
- Props: `isAuthenticated`, `userInfo`, `onLogout`
- Min-height 100vh với flex layout
- Min-height 100vh with flex layout
### Cấu trúc thư mục
### Directory Structure
```
src/
├── components/
@@ -62,13 +62,13 @@ src/
└── main.tsx
```
### Cách sử dụng
### Usage
#### 1. Import Layout o App
#### 1. Import Layout into App
```tsx
import LayoutMain from './components/layout/LayoutMain';
// Trong Routes
// In Routes
<Route
path="/"
element={
@@ -80,11 +80,11 @@ import LayoutMain from './components/layout/LayoutMain';
}
>
<Route index element={<HomePage />} />
{/* Các route con khác */}
{/* Other child routes */}
</Route>
```
#### 2. Sử dụng SidebarAdmin cho trang Admin
#### 2. Use SidebarAdmin for Admin Pages
```tsx
import SidebarAdmin from '../components/layout/SidebarAdmin';
@@ -98,78 +98,78 @@ const AdminLayout = () => (
);
```
### Tính năng đã hoàn thành
### Completed Features
- [x] Tạo thư mục `src/components/layout/`
- [x] Header.tsx với logo navigation
- [x] Footer.tsx với thông tin đầy đủ
- [x] Navbar.tsx với logic đăng nhập/đăng xuất động
- [x] SidebarAdmin.tsx chỉ hiển thị với role admin
- [x] LayoutMain.tsx sử dụng `<Outlet />`
- [x] Navbar thay đổi theo trạng thái đăng nhập
- [x] Giao diện responsive, tương thích desktop/mobile
- [x] Tích hợp TailwindCSS cho styling
- [x] Export tất cả components qua index.ts
- [x] Create `src/components/layout/` directory
- [x] Header.tsx with logo and navigation
- [x] Footer.tsx with complete information
- [x] Navbar.tsx with dynamic login/logout logic
- [x] SidebarAdmin.tsx only displays with admin role
- [x] LayoutMain.tsx uses `<Outlet />`
- [x] Navbar changes based on login state
- [x] Responsive interface, compatible with desktop/mobile
- [x] TailwindCSS integration for styling
- [x] Export all components via index.ts
### Demo Routes đã tạo
### Demo Routes Created
**Public Routes** (với LayoutMain):
- `/` - Trang chủ
- `/rooms` - Danh sách phòng
- `/bookings` - Đặt phòng
- `/about` - Giới thiệu
**Public Routes** (with LayoutMain):
- `/` - Home
- `/rooms` - Room list
- `/bookings` - Bookings
- `/about` - About
**Auth Routes** (không có layout):
- `/login` - Đăng nhập
- `/register` - Đăng ký
- `/forgot-password` - Quên mật khẩu
**Auth Routes** (no layout):
- `/login` - Login
- `/register` - Register
- `/forgot-password` - Forgot password
**Admin Routes** (với SidebarAdmin):
**Admin Routes** (with SidebarAdmin):
- `/admin/dashboard` - Dashboard
- `/admin/users` - Quản lý người dùng
- `/admin/rooms` - Quản lý phòng
- `/admin/bookings` - Quản lý đặt phòng
- `/admin/payments` - Quản lý thanh toán
- `/admin/services` - Quản lý dịch vụ
- `/admin/promotions` - Quản lý khuyến mãi
- `/admin/banners` - Quản lý banner
- `/admin/users` - User Management
- `/admin/rooms` - Room Management
- `/admin/bookings` - Booking Management
- `/admin/payments` - Payment Management
- `/admin/services` - Service Management
- `/admin/promotions` - Promotion Management
- `/admin/banners` - Banner Management
### Chạy ứng dụng
### Run Application
```bash
# Di chuyển vào thư mục client
# Navigate to client directory
cd client
# Cài đặt dependencies (nếu chưa cài)
# Install dependencies (if not installed)
npm install
# Chạy development server
# Run development server
npm run dev
# Mở trình duyệt tại: http://localhost:5173
# Open browser at: http://localhost:5173
```
### Các bước tiếp theo
### Next Steps
**Chức năng 2**: Cấu hình Routing (react-router-dom)
**Function 2**: Routing Configuration (react-router-dom)
- ProtectedRoute component
- AdminRoute component
- Redirect logic
**Chức năng 3**: useAuthStore (Zustand Store)
- Quản lý authentication state
**Function 3**: useAuthStore (Zustand Store)
- Manage authentication state
- Login/Logout functions
- Persist state trong localStorage
- Persist state in localStorage
**Chức năng 4-8**: Auth Forms
**Function 4-8**: Auth Forms
- LoginPage
- RegisterPage
- ForgotPasswordPage
- ResetPasswordPage
### Notes
- Layout components được thiết kế để tái sử dụng
- Props-based design cho flexibility
- Sẵn sàng tích hợp với Zustand store
- Tailwind classes tuân thủ 80 ký tự/dòng
- Icons sử dụng lucide-react (đã có trong dependencies)
- Layout components designed for reusability
- Props-based design for flexibility
- Ready to integrate with Zustand store
- Tailwind classes follow 80 characters/line
- Icons use lucide-react (already in dependencies)

View File

@@ -1,58 +1,58 @@
# Chức năng 4: Form Đăng Nhập - Hướng Dẫn Sử Dụng
# Function 4: Login Form - Usage Guide
## 📋 Tổng Quan
## 📋 Overview
Form đăng nhập đã được triển khai đầy đủ với:
-Validation form bằng React Hook Form + Yup
-Hiển thị/ẩn mật khẩu
-Checkbox "Nhớ đăng nhập" (7 ngày)
- ✅ Loading state trong quá trình đăng nhập
-Hiển thị lỗi từ server
- ✅ Redirect sau khi đăng nhập thành công
-UI đẹp với Tailwind CSS Lucide Icons
Login form has been fully implemented with:
-Form validation with React Hook Form + Yup
-Show/hide password
-"Remember me" checkbox (7 days)
- ✅ Loading state during login process
-Display errors from server
- ✅ Redirect after successful login
-Beautiful UI with Tailwind CSS and Lucide Icons
- ✅ Responsive design
## 🗂️ Các File Đã Tạo/Cập Nhật
## 🗂️ Files Created/Updated
### 1. **LoginPage.tsx** - Component form đăng nhập
**Đường dẫn:** `client/src/pages/auth/LoginPage.tsx`
### 1. **LoginPage.tsx** - Login form component
**Path:** `client/src/pages/auth/LoginPage.tsx`
```typescript
// Các tính năng chính:
- React Hook Form với Yup validation
// Main features:
- React Hook Form with Yup validation
- Show/hide password toggle
- Remember me checkbox
- Loading state với spinner
- Loading state with spinner
- Error handling
- Redirect với location state
- Redirect with location state
```
### 2. **index.ts** - Export module
**Đường dẫn:** `client/src/pages/auth/index.ts`
**Path:** `client/src/pages/auth/index.ts`
```typescript
export { default as LoginPage } from './LoginPage';
```
### 3. **App.tsx** - Đã cập nhật routing
**Đường dẫn:** `client/src/App.tsx`
### 3. **App.tsx** - Routing updated
**Path:** `client/src/App.tsx`
```typescript
// Đã thêm:
// Added:
import { LoginPage } from './pages/auth';
// Route:
<Route path="/login" element={<LoginPage />} />
```
## 🎨 Cấu Trúc UI
## 🎨 UI Structure
### Layout
```
┌─────────────────────────────────────┐
│ 🏨 Hotel Icon │
Đăng nhập
Chào mừng bạn trở lại...
Login
Welcome back...
├─────────────────────────────────────┤
│ ┌───────────────────────────────┐ │
│ │ [Error message if any] │ │
@@ -60,72 +60,72 @@ import { LoginPage } from './pages/auth';
│ │ Email │ │
│ │ [📧 email@example.com ] │ │
│ ├───────────────────────────────┤ │
│ │ Mật khẩu │ │
│ │ Password │ │
│ │ [🔒 •••••••• 👁️] │ │
│ ├───────────────────────────────┤ │
│ │ ☑️ Nhớ đăng nhập │ │
│ │ Quên mật khẩu? → │ │
│ │ ☑️ Remember me │ │
│ │ Forgot password? → │ │
│ ├───────────────────────────────┤ │
│ │ [🔐 Đăng nhập] │ │
│ │ [🔐 Login] │ │
│ └───────────────────────────────┘ │
Chưa có tài khoản? Đăng ký ngay
Don't have an account? Sign up now
│ │
Điều khoản & Chính sách bảo mật
Terms & Privacy Policy
└─────────────────────────────────────┘
```
## 🔧 Cách Sử Dụng
## 🔧 Usage
### 1. Truy Cập Form
### 1. Access Form
```bash
# URL
http://localhost:5173/login
```
### 2. Các Trường Trong Form
### 2. Form Fields
| Trường | Type | Bắt buộc | Validation |
| Field | Type | Required | Validation |
|--------|------|----------|------------|
| Email | text | ✅ | Email hợp lệ |
| Password | password | ✅ | Min 8 ký tự |
| Email | text | ✅ | Valid email |
| Password | password | ✅ | Min 8 characters |
| Remember Me | checkbox | ❌ | Boolean |
### 3. Validation Rules
**Email:**
```typescript
- Required: "Email là bắt buộc"
- Valid email format: "Email không hợp lệ"
- Required: "Email is required"
- Valid email format: "Invalid email format"
- Trim whitespace
```
**Password:**
```typescript
- Required: "Mật khẩu là bắt buộc"
- Min 8 characters: "Mật khẩu phải có ít nhất 8 ký tự"
- Required: "Password is required"
- Min 8 characters: "Password must be at least 8 characters"
```
### 4. Luồng Đăng Nhập
### 4. Login Flow
```
1. User nhập email + password
2. Click "Đăng nhập"
3. Validation form (client-side)
4. Nếu valid:
- Button disabled + hiển thị loading
- Gọi useAuthStore.login()
1. User enters email + password
2. Click "Login"
3. Form validation (client-side)
4. If valid:
- Button disabled + show loading
- Call useAuthStore.login()
- API POST /api/auth/login
5. Nếu thành công:
- Lưu token o localStorage
5. If successful:
- Save token to localStorage
- Update Zustand state
- Redirect đến /dashboard
6. Nếu lỗi:
- Hiển thị error message
- Button enabled lại
- Redirect to /dashboard
6. If error:
- Display error message
- Button enabled again
```
## 🎯 Tính Năng Chính
## 🎯 Main Features
### 1. Show/Hide Password
@@ -141,33 +141,33 @@ const [showPassword, setShowPassword] = useState(false);
<input type={showPassword ? 'text' : 'password'} />
```
### 2. Remember Me (7 ngày)
### 2. Remember Me (7 days)
```typescript
// Checkbox
<input {...register('rememberMe')} type="checkbox" />
// Logic trong authService.login()
// Logic in authService.login()
if (rememberMe) {
// Token sẽ được lưu trong localStorage
// và không bị xóa khi đóng trình duyệt
// Token will be saved in localStorage
// and won't be deleted when closing browser
}
```
### 3. Loading State
```typescript
// Button disabled khi đang loading
// Button disabled when loading
<button disabled={isLoading}>
{isLoading ? (
<>
<Loader2 className="animate-spin" />
Đang xử ...
Processing...
</>
) : (
<>
<LogIn />
Đăng nhập
Login
</>
)}
</button>
@@ -176,10 +176,10 @@ if (rememberMe) {
### 4. Error Handling
```typescript
// Error từ Zustand store
// Error from Zustand store
const { error } = useAuthStore();
// Hiển thị error message
// Display error message
{error && (
<div className="bg-red-50 border border-red-200">
{error}
@@ -190,20 +190,20 @@ const { error } = useAuthStore();
### 5. Redirect Logic
```typescript
// Lấy location state từ ProtectedRoute
// Get location state from ProtectedRoute
const location = useLocation();
// Redirect về trang trước đó hoặc dashboard
// Redirect to previous page or dashboard
const from = location.state?.from?.pathname || '/dashboard';
navigate(from, { replace: true });
```
## 🔗 Integration với Zustand Store
## 🔗 Integration with Zustand Store
```typescript
// Hook usage
const {
login, // Function để login
login, // Function to login
isLoading, // Loading state
error, // Error message
clearError // Clear error
@@ -217,7 +217,7 @@ await login({
});
```
## 🎨 Styling với Tailwind
## 🎨 Styling with Tailwind
### Color Scheme
```
@@ -232,7 +232,7 @@ await login({
// Container
className="max-w-md w-full" // Max width 28rem
// Grid (nếu có)
// Grid (if any)
className="grid grid-cols-1 md:grid-cols-2"
```
@@ -242,20 +242,20 @@ className="grid grid-cols-1 md:grid-cols-2"
**Test Case 1: Empty form**
```
- Input: Submit form trống
- Expected: Hiển thị lỗi "Email là bắt buộc"
- Input: Submit empty form
- Expected: Display error "Email is required"
```
**Test Case 2: Invalid email**
```
- Input: Email = "notanemail"
- Expected: "Email không hợp lệ"
- Expected: "Invalid email format"
```
**Test Case 3: Short password**
```
- Input: Password = "123"
- Expected: "Mật khẩu phải có ít nhất 8 ký tự"
- Expected: "Password must be at least 8 characters"
```
### 2. Authentication Testing
@@ -269,13 +269,13 @@ className="grid grid-cols-1 md:grid-cols-2"
**Test Case 5: Invalid credentials**
```
- Input: Wrong password
- Expected: Error message từ server
- Expected: Error message from server
```
**Test Case 6: Network error**
```
- Input: Server offline
- Expected: Error message "Có lỗi xảy ra"
- Expected: Error message "An error occurred"
```
### 3. UX Testing
@@ -288,14 +288,14 @@ className="grid grid-cols-1 md:grid-cols-2"
**Test Case 8: Remember me**
```
- Action: Check "Nhớ đăng nhập"
- Expected: Token persist sau khi reload
- Action: Check "Remember me"
- Expected: Token persists after reload
```
**Test Case 9: Loading state**
```
- Action: Submit form
- Expected: Button disabled, spinner hiển thị
- Expected: Button disabled, spinner displayed
```
## 🔐 Security Features
@@ -311,18 +311,18 @@ onClick={() => setShowPassword(!showPassword)}
### 2. HTTPS Only (Production)
```typescript
// Trong .env
// In .env
VITE_API_URL=https://api.yourdomain.com
```
### 3. Token Storage
```typescript
// LocalStorage cho remember me
// LocalStorage for remember me
if (rememberMe) {
localStorage.setItem('token', token);
}
// SessionStorage cho session only
// SessionStorage for session only
else {
sessionStorage.setItem('token', token);
}
@@ -355,22 +355,22 @@ else {
Remember form state
```
## 🚀 Next Steps (Chức năng 5-7)
## 🚀 Next Steps (Function 5-7)
1. **Chức năng 5: Form Register**
- Copy structure từ LoginPage
- Thêm fields: name, phone, confirmPassword
1. **Function 5: Register Form**
- Copy structure from LoginPage
- Add fields: name, phone, confirmPassword
- Use registerSchema
- Redirect to /login after success
2. **Chức năng 6: Forgot Password**
- Simple form với email only
2. **Function 6: Forgot Password**
- Simple form with email only
- Send reset link
- Success message
3. **Chức năng 7: Reset Password**
- Form với password + confirmPassword
- Token từ URL params
3. **Function 7: Reset Password**
- Form with password + confirmPassword
- Token from URL params
- Redirect to /login after success
## 🐛 Troubleshooting
@@ -382,24 +382,24 @@ Solution: Check token expiry time
- Refresh token: 7 days
```
### Issue 2: Form không submit
### Issue 2: Form doesn't submit
```typescript
Solution: Check console for validation errors
- Open DevTools > Console
- Look for Yup validation errors
```
### Issue 3: Redirect không hoạt động
### Issue 3: Redirect doesn't work
```typescript
Solution: Check location state
console.log(location.state?.from);
```
### Issue 4: Remember me không work
### Issue 4: Remember me doesn't work
```typescript
Solution: Check localStorage
- Open DevTools > Application > Local Storage
- Check "token" "refreshToken" keys
- Check "token" and "refreshToken" keys
```
## 📚 Resources
@@ -428,5 +428,5 @@ Solution: Check localStorage
---
**Status:**Chức năng 4 hoàn thành
**Next:** Chức năng 5 - Form Register
**Status:**Function 4 completed
**Next:** Function 5 - Register Form

View File

@@ -1,89 +1,89 @@
# Chức năng 5: Form Đăng Ký - Hoàn Thành
# Function 5: Register Form - Completed
## 📦 Files Đã Tạo/Cập Nhật
## 📦 Files Created/Updated
### 1. **RegisterPage.tsx** - Component form đăng ký
**Đường dẫn:** `client/src/pages/auth/RegisterPage.tsx`
### 1. **RegisterPage.tsx** - Register form component
**Path:** `client/src/pages/auth/RegisterPage.tsx`
### 2. **index.ts** - Export module
**Đường dẫn:** `client/src/pages/auth/index.ts`
- Đã thêm export RegisterPage
**Path:** `client/src/pages/auth/index.ts`
- Added export RegisterPage
### 3. **App.tsx** - Cập nhật routing
**Đường dẫn:** `client/src/App.tsx`
- Đã thêm route `/register`
### 3. **App.tsx** - Routing updated
**Path:** `client/src/App.tsx`
- Added route `/register`
## ✨ Tính Năng Chính
## ✨ Main Features
### 1. Form Fields (5 fields)
**Họ và tên** (name)
- Required, 2-50 ký tự
**Full Name** (name)
- Required, 2-50 characters
- Icon: User
- Placeholder: "Nguyễn Văn A"
- Placeholder: "John Doe"
**Email**
- Required, valid email format
- Icon: Mail
- Placeholder: "email@example.com"
**Số điện thoại** (phone) - Optional
- 10-11 chữ số
**Phone Number** (phone) - Optional
- 10-11 digits
- Icon: Phone
- Placeholder: "0123456789"
**Mật khẩu** (password)
**Password** (password)
- Required, min 8 chars
- Must contain: uppercase, lowercase, number, special char
- Show/hide toggle với Eye icon
- Show/hide toggle with Eye icon
- Icon: Lock
**Xác nhận mật khẩu** (confirmPassword)
**Confirm Password** (confirmPassword)
- Must match password
- Show/hide toggle với Eye icon
- Show/hide toggle with Eye icon
- Icon: Lock
### 2. Password Strength Indicator
**Visual Progress Bar** với 5 levels:
1. 🔴 Rất yếu (0/5)
2. 🟠 Yếu (1/5)
3. 🟡 Trung bình (2/5)
4. 🔵 Mạnh (3/5)
5. 🟢 Rất mạnh (5/5)
**Visual Progress Bar** with 5 levels:
1. 🔴 Very weak (0/5)
2. 🟠 Weak (1/5)
3. 🟡 Medium (2/5)
4. 🔵 Strong (3/5)
5. 🟢 Very strong (5/5)
**Real-time Requirements Checker:**
- ✅/❌ Ít nhất 8 ký tự
- ✅/❌ Chữ thường (a-z)
- ✅/❌ Chữ hoa (A-Z)
- ✅/❌ Số (0-9)
- ✅/❌ Ký tự đặc biệt (@$!%*?&)
- ✅/❌ At least 8 characters
- ✅/❌ Lowercase (a-z)
- ✅/❌ Uppercase (A-Z)
- ✅/❌ Number (0-9)
- ✅/❌ Special character (@$!%*?&)
### 3. Validation Rules (Yup Schema)
```typescript
name:
- Required: "Họ tên là bắt buộc"
- Min 2 chars: "Họ tên phải có ít nhất 2 ký tự"
- Max 50 chars: "Họ tên không được quá 50 ký tự"
- Required: "Full name is required"
- Min 2 chars: "Full name must be at least 2 characters"
- Max 50 chars: "Full name must not exceed 50 characters"
- Trim whitespace
email:
- Required: "Email là bắt buộc"
- Valid format: "Email không hợp lệ"
- Required: "Email is required"
- Valid format: "Invalid email format"
- Trim whitespace
phone (optional):
- Pattern /^[0-9]{10,11}$/
- Error: "Số điện thoại không hợp lệ"
- Error: "Invalid phone number"
password:
- Required: "Mật khẩu là bắt buộc"
- Required: "Password is required"
- Min 8 chars
- Pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])/
- Error: "Mật khẩu phải chứa chữ hoa, chữ thường, số và ký tự đặc biệt"
- Error: "Password must contain uppercase, lowercase, number and special characters"
confirmPassword:
- Required: "Vui lòng xác nhận mật khẩu"
- Must match password: "Mật khẩu không khớp"
- Required: "Please confirm password"
- Must match password: "Passwords do not match"
```
### 4. UX Features
@@ -93,32 +93,32 @@ confirmPassword:
{isLoading ? (
<>
<Loader2 className="animate-spin" />
Đang xử ...
Processing...
</>
) : (
<>
<UserPlus />
Đăng
Register
</>
)}
```
**Show/Hide Password** (2 toggles)
- Eye/EyeOff icons
- Separate toggle cho password confirmPassword
- Visual feedback khi hover
- Separate toggle for password and confirmPassword
- Visual feedback on hover
**Error Display**
- Inline validation errors dưới mỗi field
- Global error message top của form
- Red border cho fields có lỗi
- Inline validation errors under each field
- Global error message at top of form
- Red border for fields with errors
**Success Flow**
```typescript
1. Submit form
2. Validation passes
3. Call useAuthStore.register()
4. Show toast: "Đăng ký thành công! Vui lòng đăng nhập."
4. Show toast: "Registration successful! Please login."
5. Navigate to /login
```
@@ -135,38 +135,38 @@ confirmPassword:
```
┌─────────────────────────────────────┐
│ 🏨 Hotel Icon (Purple) │
Đăng ký tài khoản
Tạo tài khoản mới để đặt phòng...
Register Account
Create a new account to book...
├─────────────────────────────────────┤
│ ┌───────────────────────────────┐ │
│ │ [Error message if any] │ │
│ ├───────────────────────────────┤ │
│ │ Họ và tên │ │
│ │ [👤 Nguyễn Văn A ] │ │
│ │ Full Name │ │
│ │ [👤 John Doe ] │ │
│ ├───────────────────────────────┤ │
│ │ Email │ │
│ │ [📧 email@example.com ] │ │
│ ├───────────────────────────────┤ │
│ │ Số điện thoại (Tùy chọn) │ │
│ │ Phone Number (Optional) │ │
│ │ [📱 0123456789 ] │ │
│ ├───────────────────────────────┤ │
│ │ Mật khẩu │ │
│ │ Password │ │
│ │ [🔒 •••••••• 👁️] │ │
│ │ ▓▓▓▓▓░░░░░ Rất mạnh │ │
│ │ ✅ Ít nhất 8 ký tự │ │
│ │ ✅ Chữ thường (a-z) │ │
│ │ ✅ Chữ hoa (A-Z) │ │
│ │ ✅ Số (0-9) │ │
│ │ ✅ Ký tự đặc biệt │ │
│ │ ▓▓▓▓▓░░░░░ Very strong │ │
│ │ ✅ At least 8 characters │ │
│ │ ✅ Lowercase (a-z) │ │
│ │ ✅ Uppercase (A-Z) │ │
│ │ ✅ Number (0-9) │ │
│ │ ✅ Special character │ │
│ ├───────────────────────────────┤ │
│ │ Xác nhận mật khẩu │ │
│ │ Confirm Password │ │
│ │ [🔒 •••••••• 👁️] │ │
│ ├───────────────────────────────┤ │
│ │ [👤 Đăng ký] │ │
│ │ [👤 Register] │ │
│ └───────────────────────────────┘ │
Đã có tài khoản? Đăng nhập ngay
Already have an account? Login now
│ │
Điều khoản & Chính sách bảo mật
Terms & Privacy Policy
└─────────────────────────────────────┘
```
@@ -180,7 +180,7 @@ POST /api/auth/register
### Request Body
```typescript
{
name: string; // "Nguyễn Văn A"
name: string; // "John Doe"
email: string; // "user@example.com"
password: string; // "Password123@"
phone?: string; // "0123456789" (optional)
@@ -195,7 +195,7 @@ POST /api/auth/register
"data": {
"user": {
"id": 1,
"name": "Nguyễn Văn A",
"name": "John Doe",
"email": "user@example.com",
"phone": "0123456789",
"role": "customer"
@@ -235,20 +235,20 @@ Expected: Show validation errors for name, email, password
**Test Case 2: Invalid email**
```
Input: email = "notanemail"
Expected: "Email không hợp lệ"
Expected: "Invalid email format"
```
**Test Case 3: Short name**
```
Input: name = "A"
Expected: "Họ tên phải có ít nhất 2 ký tự"
Expected: "Full name must be at least 2 characters"
```
**Test Case 4: Weak password**
```
Input: password = "abc123"
Expected: "Mật khẩu phải chứa chữ hoa, chữ thường, số và ký tự đặc biệt"
Password strength: Yếu/Trung bình
Expected: "Password must contain uppercase, lowercase, number and special characters"
Password strength: Weak/Medium
```
**Test Case 5: Password mismatch**
@@ -256,13 +256,13 @@ Password strength: Yếu/Trung bình
Input:
password = "Password123@"
confirmPassword = "Password456@"
Expected: "Mật khẩu không khớp"
Expected: "Passwords do not match"
```
**Test Case 6: Invalid phone**
```
Input: phone = "123"
Expected: "Số điện thoại không hợp lệ"
Expected: "Invalid phone number"
```
### 2. UX Tests
@@ -290,7 +290,7 @@ Action: Submit valid form
Expected:
- Button disabled
- Spinner shows
- Text changes to "Đang xử lý..."
- Text changes to "Processing..."
```
### 3. Integration Tests
@@ -300,7 +300,7 @@ Expected:
Input: All valid data
Expected:
1. API POST /api/auth/register called
2. Toast: "Đăng ký thành công! Vui lòng đăng nhập."
2. Toast: "Registration successful! Please login."
3. Redirect to /login
```
@@ -317,7 +317,7 @@ Expected:
```
Scenario: Server offline
Expected:
- Error message: "Đăng ký thất bại. Vui lòng thử lại."
- Error message: "Registration failed. Please try again."
- Toast error displayed
```
@@ -335,7 +335,7 @@ function getPasswordStrength(pwd: string) {
return {
strength: 0-5,
label: ['Rất yếu', 'Yếu', 'Trung bình', 'Mạnh', 'Rất mạnh'][strength],
label: ['Very weak', 'Weak', 'Medium', 'Strong', 'Very strong'][strength],
color: ['bg-red-500', 'bg-orange-500', 'bg-yellow-500', 'bg-blue-500', 'bg-green-500'][strength]
};
}
@@ -347,7 +347,7 @@ function getPasswordStrength(pwd: string) {
RegisterPage/
├── Header Section
├── Hotel Icon (purple)
├── Title: "Đăng ký tài khoản"
├── Title: "Register Account"
└── Subtitle
├── Form Container (white card)
@@ -364,7 +364,7 @@ RegisterPage/
└── Submit Button (with loading)
├── Login Link
└── "Đã có tài khoản? Đăng nhập ngay"
└── "Already have an account? Login now"
└── Footer Links
├── Terms of Service
@@ -413,7 +413,7 @@ http://localhost:5173/register
### Example Registration
```typescript
Name: "Nguyễn Văn A"
Name: "John Doe"
Email: "nguyenvana@example.com"
Phone: "0123456789"
Password: "Password123@"
@@ -481,6 +481,6 @@ to /login Show errors
---
**Status:**Chức năng 5 hoàn thành
**Next:** Chức năng 6 - Forgot Password
**Status:**Function 5 completed
**Next:** Function 6 - Forgot Password
**Test URL:** http://localhost:5173/register

View File

@@ -1,19 +1,19 @@
# Route Protection Documentation
## Chức năng 8: Phân quyền & Bảo vệ Route
## Function 8: Authorization & Route Protection
Hệ thống sử dụng 2 component để bảo vệ các route:
- **ProtectedRoute**: Yêu cầu user phải đăng nhập
- **AdminRoute**: Yêu cầu user phải là Admin
The system uses 2 components to protect routes:
- **ProtectedRoute**: Requires user to be logged in
- **AdminRoute**: Requires user to be Admin
---
## 1. ProtectedRoute
### Mục đích
Bảo vệ các route yêu cầu authentication (đăng nhập).
### Purpose
Protects routes requiring authentication (login).
### Cách hoạt động
### How It Works
```tsx
// File: client/src/components/auth/ProtectedRoute.tsx
@@ -23,32 +23,32 @@ const ProtectedRoute: React.FC<ProtectedRouteProps> = ({
const location = useLocation();
const { isAuthenticated, isLoading } = useAuthStore();
// 1. Nếu đang loading → hiển thị spinner
// 1. If loading → display spinner
if (isLoading) {
return <LoadingScreen />;
}
// 2. Nếu chưa đăng nhập → redirect /login
// 2. If not logged in → redirect /login
if (!isAuthenticated) {
return (
<Navigate
to="/login"
state={{ from: location }} // Lưu location để quay lại
state={{ from: location }} // Save location to return later
replace
/>
);
}
// 3. Đã đăng nhập → cho phép truy cập
// 3. Logged in → allow access
return <>{children}</>;
};
```
### Sử dụng trong App.tsx
### Usage in App.tsx
```tsx
import { ProtectedRoute } from './components/auth';
// Route yêu cầu đăng nhập
// Route requiring login
<Route
path="/dashboard"
element={
@@ -77,20 +77,20 @@ import { ProtectedRoute } from './components/auth';
/>
```
### Luồng hoạt động
1. User chưa đăng nhập truy cập `/dashboard`
2. ProtectedRoute kiểm tra `isAuthenticated === false`
3. Redirect về `/login` và lưu `state={{ from: '/dashboard' }}`
4. Sau khi login thành công, redirect về `/dashboard`
### Flow
1. User not logged in accesses `/dashboard`
2. ProtectedRoute checks `isAuthenticated === false`
3. Redirect to `/login` and save `state={{ from: '/dashboard' }}`
4. After successful login, redirect to `/dashboard`
---
## 2. AdminRoute
### Mục đích
Bảo vệ các route chỉ dành cho Admin (role-based access).
### Purpose
Protects routes for Admin only (role-based access).
### Cách hoạt động
### How It Works
```tsx
// File: client/src/components/auth/AdminRoute.tsx
@@ -100,12 +100,12 @@ const AdminRoute: React.FC<AdminRouteProps> = ({
const location = useLocation();
const { isAuthenticated, userInfo, isLoading } = useAuthStore();
// 1. Nếu đang loading → hiển thị spinner
// 1. If loading → display spinner
if (isLoading) {
return <LoadingScreen />;
}
// 2. Nếu chưa đăng nhập → redirect /login
// 2. If not logged in → redirect /login
if (!isAuthenticated) {
return (
<Navigate
@@ -116,22 +116,22 @@ const AdminRoute: React.FC<AdminRouteProps> = ({
);
}
// 3. Nếu không phải admin → redirect /
// 3. If not admin → redirect /
const isAdmin = userInfo?.role === 'admin';
if (!isAdmin) {
return <Navigate to="/" replace />;
}
// 4. admin → cho phép truy cập
// 4. Is admin → allow access
return <>{children}</>;
};
```
### Sử dụng trong App.tsx
### Usage in App.tsx
```tsx
import { AdminRoute } from './components/auth';
// Route chỉ dành cho Admin
// Route for Admin only
<Route
path="/admin"
element={
@@ -148,50 +148,50 @@ import { AdminRoute } from './components/auth';
</Route>
```
### Luồng hoạt động
### Flow
#### Case 1: User chưa đăng nhập
1. Truy cập `/admin`
2. AdminRoute kiểm tra `isAuthenticated === false`
3. Redirect về `/login` với `state={{ from: '/admin' }}`
4. Sau login thành công → quay lại `/admin`
5. AdminRoute kiểm tra lại role
#### Case 1: User not logged in
1. Access `/admin`
2. AdminRoute checks `isAuthenticated === false`
3. Redirect to `/login` with `state={{ from: '/admin' }}`
4. After successful login → return to `/admin`
5. AdminRoute checks role again
#### Case 2: User đã đăng nhập nhưng không phải Admin
1. Customer (role='customer') truy cập `/admin`
2. AdminRoute kiểm tra `isAuthenticated === true`
3. AdminRoute kiểm tra `userInfo.role === 'customer'` (không phải 'admin')
4. Redirect về `/` (trang chủ)
#### Case 2: User logged in but not Admin
1. Customer (role='customer') accesses `/admin`
2. AdminRoute checks `isAuthenticated === true`
3. AdminRoute checks `userInfo.role === 'customer'` (not 'admin')
4. Redirect to `/` (homepage)
#### Case 3: User Admin
1. Admin (role='admin') truy cập `/admin`
2. AdminRoute kiểm tra `isAuthenticated === true`
3. AdminRoute kiểm tra `userInfo.role === 'admin'`
4. Cho phép truy cập
#### Case 3: User is Admin
1. Admin (role='admin') accesses `/admin`
2. AdminRoute checks `isAuthenticated === true`
3. AdminRoute checks `userInfo.role === 'admin'`
4. Allow access
---
## 3. Cấu trúc Route trong App.tsx
## 3. Route Structure in App.tsx
```tsx
function App() {
return (
<BrowserRouter>
<Routes>
{/* Public Routes - Không cần đăng nhập */}
{/* Public Routes - No login required */}
<Route path="/" element={<LayoutMain />}>
<Route index element={<HomePage />} />
<Route path="rooms" element={<RoomListPage />} />
<Route path="about" element={<AboutPage />} />
</Route>
{/* Auth Routes - Không cần layout */}
{/* Auth Routes - No layout */}
<Route path="/login" element={<LoginPage />} />
<Route path="/register" element={<RegisterPage />} />
<Route path="/forgot-password" element={<ForgotPasswordPage />} />
<Route path="/reset-password/:token" element={<ResetPasswordPage />} />
{/* Protected Routes - Yêu cầu đăng nhập */}
{/* Protected Routes - Login required */}
<Route path="/" element={<LayoutMain />}>
<Route
path="dashboard"
@@ -219,7 +219,7 @@ function App() {
/>
</Route>
{/* Admin Routes - Chỉ Admin */}
{/* Admin Routes - Admin only */}
<Route
path="/admin"
element={
@@ -251,7 +251,7 @@ function App() {
---
## 4. Tích hợp với Zustand Store
## 4. Integration with Zustand Store
### useAuthStore State
```tsx
@@ -286,17 +286,17 @@ const useAuthStore = create<AuthStore>((set) => ({
```
### User Roles
- **admin**: Quản trị viên (full access)
- **staff**: Nhân viên (limited access)
- **customer**: Khách hàng (customer features only)
- **admin**: Administrator (full access)
- **staff**: Staff (limited access)
- **customer**: Customer (customer features only)
---
## 5. Loading State
Cả 2 component đều xử lý loading state để tránh:
- Flash of redirect (nhấp nháy khi chuyển trang)
- Race condition (auth state chưa load xong)
Both components handle loading state to avoid:
- Flash of redirect (flickering when changing pages)
- Race condition (auth state not loaded yet)
```tsx
if (isLoading) {
@@ -306,7 +306,7 @@ if (isLoading) {
<div className="animate-spin rounded-full h-12 w-12
border-b-2 border-indigo-600 mx-auto"
/>
<p className="mt-4 text-gray-600">Đang xác thực...</p>
<p className="mt-4 text-gray-600">Authenticating...</p>
</div>
</div>
);
@@ -330,12 +330,12 @@ const LoginPage: React.FC = () => {
try {
await login(data);
// Redirect về page ban đầu hoặc /dashboard
// Redirect to original page or /dashboard
navigate(from, { replace: true });
toast.success('Đăng nhập thành công!');
toast.success('Login successful!');
} catch (error) {
toast.error('Đăng nhập thất bại!');
toast.error('Login failed!');
}
};
@@ -348,58 +348,58 @@ const LoginPage: React.FC = () => {
```
### Flow
1. User truy cập `/bookings` (protected)
1. User accesses `/bookings` (protected)
2. Redirect `/login?from=/bookings`
3. Login thành công
4. Redirect về `/bookings` (page ban đầu)
3. Login successful
4. Redirect to `/bookings` (original page)
---
## 7. Testing Route Protection
### Test Case 1: ProtectedRoute - Unauthenticated
**Given**: User chưa đăng nhập
**When**: Truy cập `/dashboard`
**Then**: Redirect về `/login`
**And**: Lưu `from=/dashboard` trong location state
**Given**: User not logged in
**When**: Access `/dashboard`
**Then**: Redirect to `/login`
**And**: Save `from=/dashboard` in location state
### Test Case 2: ProtectedRoute - Authenticated
**Given**: User đã đăng nhập
**When**: Truy cập `/dashboard`
**Then**: Hiển thị DashboardPage thành công
**Given**: User logged in
**When**: Access `/dashboard`
**Then**: Display DashboardPage successfully
### Test Case 3: AdminRoute - Not Admin
**Given**: User role='customer'
**When**: Truy cập `/admin`
**Then**: Redirect về `/` (trang chủ)
**Given**: User has role='customer'
**When**: Access `/admin`
**Then**: Redirect to `/` (homepage)
### Test Case 4: AdminRoute - Is Admin
**Given**: User role='admin'
**When**: Truy cập `/admin`
**Then**: Hiển thị AdminLayout thành công
**Given**: User has role='admin'
**When**: Access `/admin`
**Then**: Display AdminLayout successfully
### Test Case 5: Loading State
**Given**: Auth đang initialize
**Given**: Auth is initializing
**When**: isLoading === true
**Then**: Hiển thị loading spinner
**And**: Không redirect
**Then**: Display loading spinner
**And**: No redirect
---
## 8. Security Best Practices
### ✅ Đã Implement
### ✅ Implemented
1. **Client-side protection**: ProtectedRoute & AdminRoute
2. **Token persistence**: localStorage
3. **Role-based access**: Kiểm tra userInfo.role
4. **Location state**: Lưu "from" để redirect về đúng page
5. **Loading state**: Tránh flash của redirect
6. **Replace navigation**: Không lưu lịch sử redirect
3. **Role-based access**: Check userInfo.role
4. **Location state**: Save "from" to redirect to correct page
5. **Loading state**: Avoid flash of redirect
6. **Replace navigation**: Don't save redirect history
### ⚠️ Lưu Ý
- Client-side protection **không đủ** → Phải có backend validation
- API endpoints phải kiểm tra JWT + role
- Middleware backend: `auth`, `adminOnly`
### ⚠️ Note
- Client-side protection **is not enough** → Must have backend validation
- API endpoints must check JWT + role
- Backend middleware: `auth`, `adminOnly`
- Never trust client-side role → Always verify on server
### Backend Middleware Example
@@ -440,45 +440,45 @@ router.get('/admin/users', auth, adminOnly, getUsers);
## 9. Troubleshooting
### Vấn đề 1: Infinite redirect loop
**Nguyên nhân**: ProtectedRoute check sai logic
**Giải pháp**: Đảm bảo `replace={true}` trong Navigate
### Issue 1: Infinite redirect loop
**Cause**: ProtectedRoute check logic error
**Solution**: Ensure `replace={true}` in Navigate
### Vấn đề 2: Flash of redirect
**Nguyên nhân**: Không handle loading state
**Giải pháp**: Thêm check `if (isLoading)` trước check auth
### Issue 2: Flash of redirect
**Cause**: Not handling loading state
**Solution**: Add check `if (isLoading)` before auth check
### Vấn đề 3: Lost location state
**Nguyên nhân**: Không pass `state={{ from: location }}`
**Giải pháp**: Luôn lưu location khi redirect
### Issue 3: Lost location state
**Cause**: Not passing `state={{ from: location }}`
**Solution**: Always save location when redirecting
### Vấn đề 4: Admin có thể truy cập nhưng API fail
**Nguyên nhân**: Backend không verify role
**Giải pháp**: Thêm middleware `adminOnly` trên API routes
### Issue 4: Admin can access but API fails
**Cause**: Backend doesn't verify role
**Solution**: Add `adminOnly` middleware on API routes
---
## 10. Summary
### ProtectedRoute
-Kiểm tra `isAuthenticated`
- ✅ Redirect `/login` nếu chưa đăng nhập
-Lưu location state để quay lại
-Check `isAuthenticated`
- ✅ Redirect `/login` if not logged in
-Save location state to return
- ✅ Handle loading state
### AdminRoute
-Kiểm tra `isAuthenticated` trước
-Kiểm tra `userInfo.role === 'admin'`
- ✅ Redirect `/login` nếu chưa đăng nhập
- ✅ Redirect `/` nếu không phải admin
-Check `isAuthenticated` first
-Check `userInfo.role === 'admin'`
- ✅ Redirect `/login` if not logged in
- ✅ Redirect `/` if not admin
- ✅ Handle loading state
### Kết quả
- Bảo vệ toàn bộ protected routes
- UX mượt mà, không flash
- Role-based access hoạt động chính xác
- Security tốt (kết hợp backend validation)
### Results
- Protect all protected routes
- Smooth UX, no flash
- Role-based access works correctly
- Good security (combined with backend validation)
---
**Chức năng 8 hoàn thành! ✅**
**Function 8 completed! ✅**

View File

@@ -3,9 +3,9 @@
## 📦 Files Created
### Core Server Files
1. **`.env`** - Environment configuration (với mật khẩu và secrets)
2. **`src/server.js`** - Server entry point với database connection
3. **`src/app.js`** - Express application setup với middleware
1. **`.env`** - Environment configuration (with passwords and secrets)
2. **`src/server.js`** - Server entry point with database connection
3. **`src/app.js`** - Express application setup with middleware
### Controllers
4. **`src/controllers/authController.js`** - Authentication logic
@@ -230,11 +230,11 @@ CLIENT_URL=http://localhost:5173
### 1. Database Setup
```bash
# Tạo database
# Create database
mysql -u root -p
CREATE DATABASE hotel_db;
# Chạy migrations
# Run migrations
cd d:/hotel-booking/server
npm run migrate

View File

@@ -1,4 +1,4 @@
# Test Scenarios - Route Protection (Chức năng 8)
# Test Scenarios - Route Protection (Function 8)
## Test Setup
@@ -205,7 +205,7 @@ Verify that loading state is displayed during auth check.
### Expected Result
- ✅ Loading spinner displayed
- ✅ Text "Đang tải..." or "Đang xác thực..." visible
- ✅ Text "Loading..." or "Authenticating..." visible
- ✅ No flash of redirect
- ✅ Smooth transition after loading
@@ -336,7 +336,7 @@ Verify that logout clears auth and redirects properly.
- ✅ Token removed from localStorage
- ✅ userInfo removed from localStorage
- ✅ Redirected to `/` or `/login`
- ✅ Navbar shows "Đăng nhập" button
- ✅ Navbar shows "Login" button
- ✅ Cannot access protected routes anymore
### Actual Result
@@ -430,7 +430,7 @@ Verify that non-existent routes show 404 page.
### Expected Result
- ✅ 404 page displayed
- ✅ "404 - Không tìm thấy trang" message
- ✅ "404 - Page not found" message
- ✅ URL shows `/non-existent-route`
- ✅ No errors in console

View File

@@ -1,30 +1,30 @@
# 🏨 Hotel Management & Booking System
## Bản Phân Tích Dành Cho Admin (SRS Admin Analysis)
## Admin Analysis Document (SRS Admin Analysis)
---
## 1. Giới thiệu
Tài liệu này phân tích các yêu cầu từ SRS của hệ thống **Hotel Management & Booking Online (e-Hotel)**, tập trung hoàn toàn vào phần **Admin / Manager / Staff** (không bao gồm khách hàng).
Mục tiêu là nắm rõ các chức năng quản trị, vận hành và bảo mật của hệ thống.
## 1. Introduction
This document analyzes the requirements from the SRS of the **Hotel Management & Booking Online (e-Hotel)** system, focusing entirely on the **Admin / Manager / Staff** section (excluding customers).
The goal is to understand the administration, operation, and security functions of the system.
---
# 2. Phân tích chức năng dành cho Admin
# 2. Admin Functionality Analysis
---
## 2.1 Setup Module (Thiết lập hệ thống)
## 2.1 Setup Module (System Setup)
### 2.1.1 Setup Rooms (Quản lý phòng)
**Vai trò sử dụng:** Manager, Admin
### 2.1.1 Setup Rooms (Room Management)
**User Roles:** Manager, Admin
**Các chức năng:**
- Thêm mới phòng
- Chỉnh sửa thông tin phòng
- Xoá phòng *(chỉ khi phòng chưa có booking)*
- Upload hình ảnh phòng
**Functions:**
- Add new room
- Edit room information
- Delete room *(only when room has no bookings)*
- Upload room images
**Thông tin phòng gồm:**
**Room Information:**
- RoomID
- Description
- Type (VIP, DELUX, SUITE, …)
@@ -32,114 +32,114 @@ Mục tiêu là nắm rõ các chức năng quản trị, vận hành và bảo
- Price
- Pictures
**Quy tắc:**
- Validate toàn bộ dữ liệu khi thêm/sửa
- Không cho xoá phòng đã phát sinh booking
**Rules:**
- Validate all data when adding/editing
- Do not allow deletion of rooms that have bookings
---
### 2.1.2 Setup Services (Quản lý dịch vụ)
**Vai trò:** Manager, Admin
### 2.1.2 Setup Services (Service Management)
**Roles:** Manager, Admin
**Chức năng:**
- Thêm dịch vụ
- Chỉnh sửa
- Xoá dịch vụ
**Functions:**
- Add service
- Edit
- Delete service
**Thông tin dịch vụ:**
**Service Information:**
- Service ID
- Service Name
- Description
- Unit (giờ, suất, lần,…)
- Unit (hour, portion, time, …)
- Price
**Quy tắc:**
- Validate tất cả dữ liệu nhập
**Rules:**
- Validate all input data
---
### 2.1.3 Promotion Management (Quản lý khuyến mãi)
**Vai trò:** Manager, Admin
### 2.1.3 Promotion Management
**Roles:** Manager, Admin
**Chức năng:**
**Functions:**
- Add promotion
- Edit promotion
- Delete promotion
- Promotion có thể áp dụng bằng code hoặc tự động trong booking
- Promotion can be applied by code or automatically in booking
**Thông tin:**
**Information:**
- ID
- Name
- Description
- Value (phần trăm hoặc số tiền)
- Value (percentage or fixed amount)
---
# 2.2 Operation Module (Vận hành khách sạn)
# 2.2 Operation Module (Hotel Operations)
---
## 2.2.1 Booking Management
**Vai trò:** Staff, Manager, Admin
**Roles:** Staff, Manager, Admin
**Chức năng:**
- Tìm booking theo tên khách, số booking, ngày đặt
- Xem chi tiết booking
- Xem bill dịch vụ
- Xử lý yêu cầu:
- Hủy booking
**Functions:**
- Search booking by guest name, booking number, booking date
- View booking details
- View service bill
- Process requests:
- Cancel booking
- Checkout
---
## 2.2.2 Check-in
**Vai trò:** Staff, Manager
**Roles:** Staff, Manager
**Quy trình check-in:**
- Khách xuất trình Booking Number
- Nhân viên kiểm tra thông tin booking
- Nhập thông tin từng khách trong phòng
- Gán số phòng thực tế
- Thu thêm phí nếu có trẻ em hoặc extra person
**Check-in Process:**
- Guest presents Booking Number
- Staff verifies booking information
- Enter information for each guest in the room
- Assign actual room number
- Collect additional fees if there are children or extra persons
---
## 2.2.3 Use Services (Khách đăng ký sử dụng dịch vụ)
**Vai trò:** Staff
## 2.2.3 Use Services (Guest Service Registration)
**Roles:** Staff
**Chức năng:**
- Đăng ký dịch vụ cho khách dựa trên Room Number
- In ticket nếu có yêu cầu
**Functions:**
- Register services for guests based on Room Number
- Print ticket if requested
---
## 2.2.4 Check-out
**Vai trò:** Staff, Manager
**Roles:** Staff, Manager
**Chức năng:**
- Tính toán:
- Phí phòng
- Phí dịch vụ
- Phụ phí khác
- Tạo hóa đơn (Invoice)
- Khấu trừ tiền đã đặt cọc (booking value)
- Khách thanh toán phần còn lại
**Functions:**
- Calculate:
- Room fee
- Service fee
- Other surcharges
- Create invoice
- Deduct deposit amount (booking value)
- Guest pays remaining amount
---
# 2.3 Report Module (Báo cáo)
# 2.3 Report Module
**Vai trò:** Manager, Admin
**Roles:** Manager, Admin
**Chức năng:**
- Nhập khoảng thời gian From → To
- Liệt kê toàn bộ booking trong khoảng thời gian
- Tính tổng doanh thu
- Xuất báo cáo:
**Functions:**
- Enter time range From → To
- List all bookings within the time range
- Calculate total revenue
- Export reports:
- Excel
- PDF
**Nội dung báo cáo:**
**Report Content:**
- Booking ID
- Customer Name
- Room
@@ -150,58 +150,58 @@ Mục tiêu là nắm rõ các chức năng quản trị, vận hành và bảo
---
# 2.4 System Administration Module (Quản trị hệ thống)
# 2.4 System Administration Module
---
## 2.4.1 User Management
**Vai trò:** Admin
**Roles:** Admin
**Chức năng:**
**Functions:**
- Add user
- Edit user
- Delete user
- View user detail
- List tất cả user
- n role (Admin, Manager, Staff)
- List all users
- Assign role (Admin, Manager, Staff)
---
## 2.4.2 Security
**Chức năng bảo mật của hệ thống:**
**System Security Functions:**
### Roles được định nghĩa:
| Role | Quyền |
### Defined Roles:
| Role | Permissions |
|------|-------|
| **Customer** | Không cần login |
| **Staff (Sale)** | Truy cập Operation Module |
| **Manager** | Truy cập Setup Module |
| **Admin** | Toàn quyền, bao gồm User & Security |
| **Customer** | No login required |
| **Staff (Sale)** | Access Operation Module |
| **Manager** | Access Setup Module |
| **Admin** | Full access, including User & Security |
### Quy tắc bảo mật:
- Nhân viên & admin bắt buộc phải login
- Quyền thao tác phụ thuộc vào role
- Session timeout sau 30 phút không hoạt động
### Security Rules:
- Staff & admin must login
- Operation permissions depend on role
- Session timeout after 30 minutes of inactivity
---
# 3. Tóm tắt theo góc nhìn Admin
# 3. Summary from Admin Perspective
| Module | Quyền Admin | Nội dung |
| Module | Admin Permissions | Content |
|--------|-------------|----------|
| Room Setup | Full | CRUD phòng |
| Service Setup | Full | CRUD dịch vụ |
| Promotion Setup | Full | CRUD khuyến mãi |
| Booking Management | Full | Xem, duyệt, hủy booking |
| Check-in / Check-out | Full | Quản lý vận hành |
| Service Usage | Full | Ghi log dịch vụ |
| Reports | Full | Thống kê, xuất file |
| User Management | Full | Quản lý nhân viên |
| Security | Full | Role, phân quyền |
| Room Setup | Full | CRUD rooms |
| Service Setup | Full | CRUD services |
| Promotion Setup | Full | CRUD promotions |
| Booking Management | Full | View, approve, cancel bookings |
| Check-in / Check-out | Full | Operations management |
| Service Usage | Full | Service logging |
| Reports | Full | Statistics, export files |
| User Management | Full | Staff management |
| Security | Full | Roles, permissions |
---
# 4. Kết luận
Phân tích trên giúp xác định đầy đủ các chức năng cần triển khai cho **Admin / Manager / Staff** trong hệ thống quản lý khách sạn.
Tài liệu có thể được sử dụng để xây dựng database, API, UI/UX, và phân quyền hệ thống.
# 4. Conclusion
The above analysis helps identify all the functions that need to be implemented for **Admin / Manager / Staff** in the hotel management system.
This document can be used to build the database, API, UI/UX, and system permissions.

View File

@@ -1,41 +1,41 @@
# Authentication
## Chức năng 1: Layout cơ bản (Header, Footer, Navbar, SidebarAdmin)
## Function 1: Basic Layout (Header, Footer, Navbar, SidebarAdmin)
### Mục tiêu
Tạo layout nền tảng cho toàn bộ hệ thống và cấu trúc render nội dung theo route.
### Objective
Create a foundational layout for the entire system and structure content rendering by route.
#### Nhiệm vụ chi tiết
- Tạo thư mục:
#### Detailed Tasks
- Create directory:
```
src/components/layouts/
```
- Bao gồm:
- Include:
+ Header.jsx
+ Footer.jsx
+ Navbar.jsx
+ SidebarAdmin.jsx
+ LayoutMain.jsx
- Dùng <Outlet /> trong LayoutMain để render nội dung động.
- Navbar thay đổi tùy trạng thái đăng nhập:
+ Nếu chưa login → hiển thị nút “Đăng nhập / Đăng ký”.
+ Nếu đã login → hiển thị avatar, tên user và nút “Đăng xuất”.
- SidebarAdmin chỉ hiển thị với role = admin.
- Use <Outlet /> in LayoutMain to render dynamic content.
- Navbar changes based on login status:
+ If not logged in → display "Login / Register" button.
+ If logged in → display avatar, user name and "Logout" button.
- SidebarAdmin only displays with role = admin.
### Kết quả mong đợi
1. Layout tổng thể hiển thị ổn định.
2. Navbar hiển thị nội dung động theo trạng thái người dùng.
3. Giao diện responsive, tương thích desktop/mobile.
### Expected Results
1. Overall layout displays stably.
2. Navbar displays dynamic content based on user status.
3. Responsive interface, compatible with desktop/mobile.
---
## Chức năng 2: Cấu hình Routing (react-router-dom)
## Function 2: Routing Configuration (react-router-dom)
### Mục tiêu
Thiết lập hệ thống định tuyến chuẩn, có bảo vệ route theo role.
### Objective
Set up a standard routing system with role-based route protection.
#### Nhiệm vụ chi tiết
- Cấu trúc route chính:
#### Detailed Tasks
- Main route structure:
```
<Route path="/" element={<LayoutMain />}>
<Route index element={<HomePage />} />
@@ -51,25 +51,25 @@
<Route path="/dashboard" element={<ProtectedRoute><Dashboard /></ProtectedRoute>} />
<Route path="/admin/*" element={<AdminRoute><AdminModule /></AdminRoute>} />
```
- Dùng ProtectedRoute AdminRoute để kiểm tra:
- Use ProtectedRoute and AdminRoute to check:
+ isAuthenticated
+ role === "admin"
### Kết quả mong đợi
1. Người dùng không đăng nhập bị redirect về /login.
2. AdminRoute chỉ cho phép admin truy cập.
3. Tất cả route hoạt động mượt, không lỗi vòng lặp redirect.
### Expected Results
1. Unauthenticated users are redirected to /login.
2. AdminRoute only allows admin access.
3. All routes work smoothly, no redirect loop errors.
---
## Chức năng 3: useAuthStore (Zustand Store)
## Function 3: useAuthStore (Zustand Store)
### Mục tiêu
Quản lý trạng thái xác thực toàn cục (token, userInfo, role).
### Objective
Manage global authentication state (token, userInfo, role).
#### Nhiệm vụ chi tiết
- Tạo src/stores/useAuthStore.js
- Cấu trúc:
#### Detailed Tasks
- Create src/stores/useAuthStore.js
- Structure:
```
const useAuthStore = create((set) => ({
token: localStorage.getItem("token") || null,
@@ -82,132 +82,132 @@
resetPassword: async (payload) => { ... },
}));
```
- Khi đăng nhập thành công:
+ Lưu token + userInfo o localStorage.
- Khi logout:
+ Xóa localStorage reset state.
- When login succeeds:
+ Save token + userInfo to localStorage.
- When logout:
+ Clear localStorage and reset state.
### Kết quả mong đợi
1. Toàn bộ thông tin user được quản lý tập trung.
2. Duy trì đăng nhập sau khi reload trang.
3. Dễ dàng truy cập userInfo trong mọi component.
### Expected Results
1. All user information is managed centrally.
2. Maintain login after page reload.
3. Easy access to userInfo in any component.
---
## Chức năng 4: Form Login
## Function 4: Login Form
### Mục tiêu
Cho phép người dùng đăng nhập hệ thống.
### Objective
Allow users to log into the system.
#### Nhiệm vụ chi tiết
- Tạo LoginPage.jsx
- Dùng React Hook Form + Yup validate:
+ Email hợp lệ
+ Mật khẩu ≥ 8 ký tự
#### Detailed Tasks
- Create LoginPage.jsx
- Use React Hook Form + Yup validation:
+ Valid email
+ Password ≥ 8 characters
- API:
```
POST /api/auth/login
```
- Sau khi đăng nhập thành công:
+ Lưu token o localStorage.
+ Gọi setUser() để cập nhật Zustand.
+ Redirect về /dashboard.
+ Gửi email POST /api/notify/login-success.
- UX nâng cao:
+ Nút loading khi đang gửi form.
+ “Hiện/Ẩn mật khẩu”.
+ “Nhớ đăng nhập” → lưu 7 ngày.
- After successful login:
+ Save token to localStorage.
+ Call setUser() to update Zustand.
+ Redirect to /dashboard.
+ Send email POST /api/notify/login-success.
- Enhanced UX:
+ Loading button when submitting form.
+ "Show/Hide password".
+ "Remember me" → save for 7 days.
### Kết quả mong đợi
1. Đăng nhập hoạt động mượt, hiển thị thông báo lỗi rõ ràng.
2. Email được gửi khi login thành công.
3. Chuyển hướng đúng theo vai trò user.
### Expected Results
1. Login works smoothly, displays clear error messages.
2. Email is sent when login succeeds.
3. Redirect correctly based on user role.
---
## Chức năng 5: Form Register
## Function 5: Register Form
### Mục tiêu
Cho phép người dùng đăng ký tài khoản mới.
### Objective
Allow users to register a new account.
#### Nhiệm vụ chi tiết
- Tạo RegisterPage.jsx
- Dùng React Hook Form + Yup validate:
+ Họ tên không rỗng
+ Email hợp lệ
+ Mật khẩu ≥ 8 ký tự, có ký tự đặc biệt
#### Detailed Tasks
- Create RegisterPage.jsx
- Use React Hook Form + Yup validation:
+ Full name not empty
+ Valid email
+ Password ≥ 8 characters, contains special characters
- API:
```
POST /api/auth/register
```
- Sau khi đăng ký thành công:
+ Hiển thị toast “Đăng ký thành công, vui lòng đăng nhập”.
+ Redirect về /login.
- After successful registration:
+ Display toast "Registration successful, please login".
+ Redirect to /login.
### Kết quả mong đợi
1. Người dùng tạo tài khoản mới thành công.
2. Validate chặt chẽ, UX mượt mà.
3. Giao diện thống nhất với form login.
### Expected Results
1. Users can create new accounts successfully.
2. Strict validation, smooth UX.
3. Interface consistent with login form.
---
## Chức năng 6: Quên mật khẩu (Forgot Password)
## Function 6: Forgot Password
### Mục tiêu
Cung cấp chức năng gửi email reset mật khẩu.
### Objective
Provide functionality to send password reset email.
#### Nhiệm vụ chi tiết
- Tạo ForgotPasswordPage.jsx
#### Detailed Tasks
- Create ForgotPasswordPage.jsx
- API:
```
POST /api/auth/forgot-password
```
- Sau khi gửi thành công:
+ Hiển thị thông báo “Vui lòng kiểm tra email để đặt lại mật khẩu.”
+ Backend gửi link reset có token dạng:
- After successful send:
+ Display message "Please check your email to reset password."
+ Backend sends reset link with token:
```
https://domain.com/reset-password/:token
```
### Kết quả mong đợi
1. Gửi email thành công.
2. UX rõ ràng, có loading và thông báo lỗi.
3. Giao diện thân thiện.
### Expected Results
1. Email sent successfully.
2. Clear UX, with loading and error messages.
3. User-friendly interface.
---
## Chức năng 7: Đặt lại mật khẩu (Reset Password)
## Function 7: Reset Password
### Mục tiêu
Cho phép người dùng đổi mật khẩu thông qua link email.
### Objective
Allow users to change password through email link.
#### Nhiệm vụ chi tiết
- Tạo ResetPasswordPage.jsx
- Validate:
+ Mật khẩu mới ≥ 8 ký tự, chứa ký tự đặc biệt
+ Nhập lại mật khẩu trùng khớp
#### Detailed Tasks
- Create ResetPasswordPage.jsx
- Validation:
+ New password ≥ 8 characters, contains special characters
+ Confirm password matches
- API:
```
POST /api/auth/reset-password
```
- Sau khi đổi mật khẩu thành công:
+ Gửi email xác nhận POST /api/notify/reset-success.
+ Redirect về /login.
- After successful password change:
+ Send confirmation email POST /api/notify/reset-success.
+ Redirect to /login.
### Kết quả mong đợi
1. Mật khẩu được cập nhật thành công.
2. Gửi email thông báo thành công.
3. Bảo vệ token hết hạn (invalid token → redirect về forgot-password).
### Expected Results
1. Password updated successfully.
2. Success notification email sent.
3. Protect expired token (invalid token → redirect to forgot-password).
---
## Chức năng 8: Phân quyền & Bảo vệ route (ProtectedRoute / AdminRoute)
## Function 8: Permissions & Route Protection (ProtectedRoute / AdminRoute)
### Mục tiêu
Chặn truy cập trái phép và bảo vệ các route quan trọng.
### Objective
Block unauthorized access and protect important routes.
#### Nhiệm vụ chi tiết
- Tạo component ProtectedRoute.jsx:
#### Detailed Tasks
- Create ProtectedRoute.jsx component:
```
const ProtectedRoute = ({ children }) => {
const { isAuthenticated } = useAuthStore();
@@ -216,13 +216,13 @@
};
```
- Tạo AdminRoute.jsx:
- Create AdminRoute.jsx:
```
const AdminRoute = ({ children }) => {
const { userInfo } = useAuthStore();
return userInfo?.role === "admin" ? children : <Navigate to="/" replace />;
};
```
### Kết quả mong đợi
1. Chỉ người dùng hợp lệ mới truy cập được route quan trọng.
2. AdminRoute đảm bảo bảo mật cho module quản trị.
### Expected Results
1. Only valid users can access important routes.
2. AdminRoute ensures security for admin module.

View File

@@ -1,182 +1,182 @@
# Review System
## Chức năng 1: HomePage Trang chủ hiển thị phòng nổi bật
## Function 1: HomePage Homepage displaying featured rooms
### Mục tiêu
Tạo giao diện trang chủ giới thiệu phòng nổi bật, banner và điều hướng đến danh sách phòng.
### Objective
Create a homepage interface introducing featured rooms, banner and navigation to room list.
#### Nhiệm vụ chi tiết
#### Detailed Tasks
1. Route: /
2. Banner:
```
GET /api/banners?position=home
```
- Nếu không có banner → hiển thị ảnh mặc định.
- Có thể dùng Carousel hoặc ảnh tĩnh.
3. Phòng nổi bật:
- If no banner → display default image.
- Can use Carousel or static image.
3. Featured rooms:
```
GET /api/rooms?featured=true
```
- Hiển thị 46 phòng bằng component RoomCard.
- Nút “Xem tất cả phòng” → điều hướng /rooms.
4. Loading skeleton trong khi chờ dữ liệu.
- Display 46 rooms using RoomCard component.
- "View all rooms" button → navigate to /rooms.
4. Loading skeleton while waiting for data.
### Kết quả mong đợi
1. Trang chủ hiển thị banner và danh sách phòng nổi bật rõ ràng.
2. Khi không có banner → ảnh fallback được hiển thị.
3. Phòng nổi bật load từ API, giới hạn 46 phòng.
4. UX mượt, có skeleton khi load.
5. Nút “Xem tất cả phòng” điều hướng chính xác đến /rooms.
### Expected Results
1. Homepage displays banner and featured room list clearly.
2. When no banner → fallback image is displayed.
3. Featured rooms load from API, limited to 46 rooms.
4. Smooth UX, with skeleton when loading.
5. "View all rooms" button navigates correctly to /rooms.
---
## Chức năng 2: RoomListPage Danh sách & Bộ lọc phòng
## Function 2: RoomListPage Room List & Filters
### Mục tiêu
Hiển thị danh sách phòng, cho phép người dùng lọc theo loại, giá, số người và phân trang.
### Objective
Display room list, allow users to filter by type, price, number of guests and pagination.
#### Nhiệm vụ chi tiết
#### Detailed Tasks
1. Route: /rooms
2. Bộ lọc (component RoomFilter):
- Trường lọc: loại phòng, giá minmax, số người.
- Khi submit → gọi API:
2. Filters (RoomFilter component):
- Filter fields: room type, minmax price, number of guests.
- On submit → call API:
```
GET /api/rooms?type=&minPrice=&maxPrice=&capacity=&page=
```
- Lưu bộ lọc vào URL query.
- Nút “Reset” để xóa toàn bộ bộ lọc.
3. Phân trang (Pagination component).
4. Hiển thị danh sách bằng RoomCard.
- Save filters to URL query.
- "Reset" button to clear all filters.
3. Pagination (Pagination component).
4. Display list using RoomCard.
### Kết quả mong đợi
1. Danh sách phòng hiển thị chính xác theo filter.
2. Bộ lọc hoạt động mượt, có thể reset dễ dàng.
3. Phân trang hiển thị chính xác số trang.
4. Filter được lưu trong URL (giúp reload không mất).
5. Giao diện responsive, dễ đọc, không bị vỡ.
### Expected Results
1. Room list displays accurately according to filter.
2. Filters work smoothly, can reset easily.
3. Pagination displays correct page numbers.
4. Filters saved in URL (helps reload without losing).
5. Responsive interface, easy to read, no breakage.
---
## Chức năng 3: RoomDetailPage Chi tiết phòng & Đánh giá
## Function 3: RoomDetailPage Room Details & Reviews
### Mục tiêu
Tạo trang chi tiết phòng đầy đủ thông tin, hình ảnh, tiện ích và khu vực đánh giá.
### Objective
Create a complete room detail page with information, images, amenities and review section.
#### Nhiệm vụ chi tiết
#### Detailed Tasks
1. Route: /rooms/:id
2. Phần nội dung:
-Thông tin phòng (ảnh, mô tả, giá, tiện ích)
- RoomGallery: Carousel ảnh
- RoomAmenities: danh sách tiện ích
- Nút “Đặt ngay” → điều hướng /booking/:roomId
2. Content section:
- Room information (images, description, price, amenities)
- RoomGallery: Image carousel
- RoomAmenities: amenities list
- "Book Now" button → navigate to /booking/:roomId
3. Review Section:
- Lấy danh sách review đã duyệt:
- Get approved review list:
```
GET /api/rooms/:id/reviews
```
- Nếu người dùng đã từng đặt phòng:
- If user has booked the room before:
```
POST /api/reviews
```
4. Component RatingStars + ReviewForm.
5. Nếu chưa đăng nhập → hiển thị “Vui lòng đăng nhập để đánh giá”.
6. Tính trung bình điểm review.
7. Loading skeleton khi chờ review.
4. RatingStars + ReviewForm component.
5. If not logged in → display "Please login to review".
6. Calculate average review rating.
7. Loading skeleton when waiting for reviews.
### Kết quả mong đợi
1. Hiển thị đầy đủ ảnh, mô tả, tiện ích phòng.
2. Carousel hoạt động mượt mà.
3. Review hiển thị đúng, có trung bình số sao.
4. Người đã đặt có thể viết review (sau duyệt).
5. Nút “Đặt ngay” điều hướng chính xác đến form booking.
6. Skeleton hiển thị khi chờ dữ liệu.
### Expected Results
1. Displays complete images, description, room amenities.
2. Carousel works smoothly.
3. Reviews display correctly, with average star rating.
4. Users who have booked can write reviews (after approval).
5. "Book Now" button navigates correctly to booking form.
6. Skeleton displays when waiting for data.
---
## Chức năng 4: SearchRoom Tìm phòng trống
## Function 4: SearchRoom Find available rooms
### Mục tiêu
Cho phép người dùng tìm phòng trống theo ngày và loại phòng.
### Objective
Allow users to find available rooms by date and room type.
#### Nhiệm vụ chi tiết
1. Form tìm kiếm (ở HomePage hoặc RoomListPage):
- Input: ngày đến (from), ngày đi (to), loại phòng.
#### Detailed Tasks
1. Search form (on HomePage or RoomListPage):
- Input: arrival date (from), departure date (to), room type.
2. API:
```
GET /api/rooms/available?from=&to=&type=
```
3. Validate:
3. Validation:
- from < to
- from không nhỏ hơn hôm nay.
4. Kết quả:
- Hiển thị danh sách bằng RoomCard.
- Nếu không có kết quả → “Không tìm thấy phòng phù hợp”.
5. Dùng react-datepicker hoặc react-day-picker.
6. Loading spinner khi đang tìm.
- from not less than today.
4. Results:
- Display list using RoomCard.
- If no results → "No matching rooms found".
5. Use react-datepicker or react-day-picker.
6. Loading spinner while searching.
### Kết quả mong đợi
1. Form tìm phòng hoạt động, validate chính xác.
2. Khi bấm tìm → hiển thị danh sách phòng trống.
3. Nếu không có kết quả → thông báo thân thiện.
4. Loading hiển thị rõ trong lúc chờ.
5. Tìm theo ngày & loại phòng chính xác từ backend.
### Expected Results
1. Room search form works, validates correctly.
2. When clicking search → displays available room list.
3. If no results → friendly message.
4. Loading displays clearly while waiting.
5. Search by date & room type accurately from backend.
---
## Chức năng 5: Wishlist Danh sách yêu thích
## Function 5: Wishlist Favorites list
### Mục tiêu
Cho phép người dùng thêm, bỏ hoặc xem danh sách phòng yêu thích.
### Objective
Allow users to add, remove or view favorite rooms list.
#### Nhiệm vụ chi tiết
#### Detailed Tasks
1. API:
```
POST /api/favorites/:roomId # Thêm
DELETE /api/favorites/:roomId # Xóa
GET /api/favorites # Lấy danh sách yêu thích
POST /api/favorites/:roomId # Add
DELETE /api/favorites/:roomId # Remove
GET /api/favorites # Get favorites list
```
2. UI:
- FavoriteButton (icon ❤️):
+ Nếu yêu thích → tô đỏ
+ Nếu chưa → viền xám
- Tooltip: “Thêm vào yêu thích” / “Bỏ yêu thích”
3. Nếu chưa đăng nhập:
- Lưu tạm trong localStorage (guestFavorites)
- Khi đăng nhập → đồng bộ với server.
4. Toast thông báo khi thêm/bỏ yêu thích.
+ If favorited → filled red
+ If not → gray border
- Tooltip: "Add to favorites" / "Remove from favorites"
3. If not logged in:
- Save temporarily in localStorage (guestFavorites)
- When logged in → sync with server.
4. Toast notification when adding/removing favorites.
### Kết quả mong đợi
1. Nút ❤️ hoạt động đúng trạng thái (đỏ / xám).
2. Người chưa đăng nhập vẫn có thể lưu tạm yêu thích.
3. Khi đăng nhập → danh sách đồng bộ với backend.
4. Toast hiển thị “Đã thêm vào yêu thích” / “Đã bỏ yêu thích”.
5. API hoạt động đúng, không lỗi 401 khi đăng nhập hợp lệ.
### Expected Results
1. ❤️ button works correctly (red / gray).
2. Unauthenticated users can still save favorites temporarily.
3. When logged in → list syncs with backend.
4. Toast displays "Added to favorites" / "Removed from favorites".
5. API works correctly, no 401 error when logged in validly.
---
## Chức năng 6: Tối ưu UI/UX & Performance
## Function 6: UI/UX & Performance Optimization
### Mục tiêu
Cải thiện trải nghiệm người dùng, tối ưu tốc độ tải và khả năng hiển thị responsive.
### Objective
Improve user experience, optimize loading speed and responsive display capability.
#### Nhiệm vụ chi tiết
1. Loading skeleton khi fetch phòng hoặc review.
2. Debounce khi nhập giá để tránh gọi API liên tục.
3. Infinite scroll (tùy chọn) thay cho pagination.
#### Detailed Tasks
1. Loading skeleton when fetching rooms or reviews.
2. Debounce when entering price to avoid continuous API calls.
3. Infinite scroll (optional) instead of pagination.
4. Responsive layout:
- Desktop: 34 cột
- Tablet: 2 cột
- Mobile: 1 cột
- Desktop: 34 columns
- Tablet: 2 columns
- Mobile: 1 column
5. Empty states:
- Không có phòng → hiển thị ảnh minh họa + dòng “Không tìm thấy phòng phù hợp”.
- Không có review → “Hãy là người đầu tiên đánh giá!”.
6. Toast thông báo khi thêm yêu thích, gửi review, lỗi mạng.
- No rooms → display illustration + "No matching rooms found" message.
- No reviews"Be the first to review!".
6. Toast notifications when adding favorites, submitting reviews, network errors.
### Kết quả mong đợi
1. Trang hoạt động mượt, có skeleton khi chờ dữ liệu.
2. Tốc độ phản hồi nhanh (debounce hoạt động).
3. Responsive trên mọi kích thước màn hình.
4. Các empty state hiển thị thân thiện.
5. Toast thông báo rõ ràng, UX thân thiện.
### Expected Results
1. Page works smoothly, has skeleton when waiting for data.
2. Fast response speed (debounce works).
3. Responsive on all screen sizes.
4. Empty states display friendly.
5. Toast notifications clear, friendly UX.
---

View File

@@ -1,144 +1,144 @@
# Booking & Payment
## Chức năng 1: BookingPage Form Đặt phòng
## Function 1: BookingPage Booking Form
### Mục tiêu
Xây dựng form đặt phòng đầy đủ thông tin, xác thực dữ liệu, tính tổng tiền theo số ngày, và gửi yêu cầu đặt.
### Objective
Build a complete booking form with information, data validation, calculate total by number of days, and send booking request.
#### Nhiệm vụ chi tiết
#### Detailed Tasks
1. Route:
```
/booking/:roomId
```
2. Khi user click “Đặt ngay” ở RoomDetailPage → chuyển sang BookingPage.
3. Hiển thị:
- Ảnh phòng, tên phòng, giá/đêm
- Thông tin người dùng (tự động điền nếu đã login)
2. When user clicks "Book Now" on RoomDetailPage → navigate to BookingPage.
3. Display:
- Room image, room name, price/night
- User information (auto-fill if logged in)
- Form:
+ Ngày check-in / check-out (DateRangePicker)
+ Số người
+ Ghi chú
+ Phương thức thanh toán:
1. Thanh toán tại chỗ
2. Chuyển khoản (hiển thị QR + hướng dẫn)
4. Validate bằng Yup + React Hook Form:
+ Check-in / check-out date (DateRangePicker)
+ Number of guests
+ Notes
+ Payment method:
1. Pay at hotel
2. Bank transfer (display QR + instructions)
4. Validate using Yup + React Hook Form:
- Check-in < Check-out
- Không bỏ trống ngày
- Có chọn phương thức thanh toán
5. Tính tổng tiền:
- Dates not empty
- Payment method selected
5. Calculate total:
```
total = room.price * (số ngày ở)
total = room.price * (number of nights)
```
6. Nút “Đặt phòng”:
6. "Book" button:
- Loading spinner
- Disable khi đang submit
- Disable when submitting
7. Nếu chưa đăng nhập → redirect /login.
7. If not logged in → redirect to /login.
---
## Chức năng 2: Booking API (Giao tiếp backend)
## Function 2: Booking API (Backend communication)
### Mục tiêu
Kết nối và xử lý API liên quan đến đặt phòng.
### Objective
Connect and handle APIs related to booking.
#### Nhiệm vụ chi tiết
#### Detailed Tasks
🔧 Endpoints:
```
POST /api/bookings → Tạo booking
GET /api/bookings/me → Lấy danh sách booking của user
PATCH /api/bookings/:id/cancel → Hủy booking
GET /api/bookings/:id → Chi tiết booking
GET /api/bookings/check/:bookingNumber → Tra cứu booking
POST /api/bookings → Create booking
GET /api/bookings/me → Get user's booking list
PATCH /api/bookings/:id/cancel → Cancel booking
GET /api/bookings/:id → Booking details
GET /api/bookings/check/:bookingNumber → Look up booking
```
🔄 Luồng xử lý:
1. Frontend gọi POST /api/bookings
2. Backend kiểm tra phòng trống:
🔄 Processing flow:
1. Frontend calls POST /api/bookings
2. Backend checks room availability:
```
GET /api/rooms/available?roomId=...&from=...&to=...
```
3. Nếu trống → tạo booking
- Nếu trùng lịch → trả 409 “Phòng đã được đặt trong thời gian này”
4. Gửi email xác nhận booking (nếu cần)
5. Trả về dữ liệu booking để hiển thị /booking-success/:id.
3. If available → create booking
- If schedule conflict → return 409 "Room already booked during this time"
4. Send booking confirmation email (if needed)
5. Return booking data to display /booking-success/:id.
---
## Chức năng 3: BookingSuccess Trang kết quả sau đặt phòng
## Function 3: BookingSuccess Page after booking
### Mục tiêu
Hiển thị kết quả đặt phòng thành công và các hành động tiếp theo.
### Objective
Display successful booking result and next actions.
#### Nhiệm vụ chi tiết
#### Detailed Tasks
1. Route: /booking-success/:id
2. Gọi GET /api/bookings/:id → hiển thị chi tiết
3. Nút:
- “Xem đơn của tôi” → /my-bookings
- “Về trang chủ” → /
4. Nếu phương thức là Chuyển khoản:
+ Hiển thị QR code ngân hàng
+ Cho phép upload ảnh xác nhận
+ Gọi POST /api/notify/payment khi người dùng xác nhận đã chuyển khoản.
2. Call GET /api/bookings/:id → display details
3. Buttons:
- "View my bookings" → /my-bookings
- "Go to home" → /
4. If payment method is Bank transfer:
+ Display bank QR code
+ Allow upload confirmation image
+ Call POST /api/notify/payment when user confirms transfer.
---
## Chức năng 4: MyBookingsPage Danh sách đơn đặt của người
## Function 4: MyBookingsPage User's booking list
### Mục tiêu
Hiển thị toàn bộ các đơn đặt của user + cho phép hủy đơn.
### Objective
Display all user's bookings + allow canceling bookings.
#### Nhiệm vụ chi tiết
#### Detailed Tasks
1. Route: /my-bookings
2. API: GET /api/bookings/me
3. Hiển thị danh sách booking:
- Phòng, ngày nhận/trả, tổng tiền
- Trạng thái:
3. Display booking list:
- Room, check-in/check-out dates, total amount
- Status:
🟡 pending
🟢 confirmed
🔴 cancelled
4. Nút “Hủy đặt phòng”:
1. window.confirm("Bạn có chắc muốn hủy không?")
2. Gọi PATCH /api/bookings/:id/cancel (hoặc DELETE /api/bookings/:id tùy implement)
3. Logic hủy:
- Giữ 20% giá trị đơn
- Hoàn 80% còn lại cho user
- Cập nhật trạng thái phòng về available
4. Hiển thị toast “Đơn đã được hủy thành công”
5. Cho phép xem chi tiết booking:
4. "Cancel booking" button:
1. window.confirm("Are you sure you want to cancel?")
2. Call PATCH /api/bookings/:id/cancel (or DELETE /api/bookings/:id depending on implementation)
3. Cancel logic:
- Keep 20% of order value
- Refund remaining 80% to user
- Update room status to available
4. Display toast "Booking cancelled successfully"
5. Allow viewing booking details:
- Route: /bookings/:id
- Gọi GET /api/bookings/:id
- Hiển thị chi tiết phòng, thông tin user, tổng tiền, status.
- Call GET /api/bookings/:id
- Display room details, user information, total amount, status.
---
## Chức năng 5: Thanh toán (Giả lập Payment)
## Function 5: Payment (Simulated Payment)
### Mục tiêu
Cho phép người dùng chọn phương thức thanh toán và xác nhận thanh toán.
### Objective
Allow users to select payment method and confirm payment.
#### Nhiệm vụ chi tiết
- Phương thức:
1. Thanh toán tại chỗ
- Booking được tạo với status = "pending"
2. Chuyển khoản
- Hiển thị mã QR ngân hàng (tĩnh hoặc từ API)
- Upload ảnh biên lai (image upload)
- Sau khi upload → gọi POST /api/notify/payment gửi email xác nhận
- Cập nhật status = "confirmed"
#### Detailed Tasks
- Payment methods:
1. Pay at hotel
- Booking created with status = "pending"
2. Bank transfer
- Display bank QR code (static or from API)
- Upload receipt image (image upload)
- After upload → call POST /api/notify/payment send confirmation email
- Update status = "confirmed"
---
## Chức năng 6: UX & Hiệu năng
## Function 6: UX & Performance
### Mục tiêu
Cải thiện trải nghiệm người dùng và tính trực quan.
### Objective
Improve user experience and intuitiveness.
#### Nhiệm vụ chi tiết
1. Toasts (react-hot-toast hoặc sonner)
2. Loading spinner rõ ràng
3. DateRangePicker cho chọn ngày
4. Form được validate đầy đủ (và báo lỗi chi tiết)
5. Focus input đầu tiên
6. Tự động redirect khi đặt thành công / hủy đơn
#### Detailed Tasks
1. Toasts (react-hot-toast or sonner)
2. Clear loading spinner
3. DateRangePicker for date selection
4. Form fully validated (and detailed error messages)
5. Focus first input
6. Auto redirect when booking succeeds / canceling booking
---
---

View File

@@ -1,72 +1,72 @@
# Review System
## Chức năng 1: ReviewPage Trang người dùng đánh giá phòng
## Function 1: ReviewPage User Room Review Page
### Mục tiêu
Cho phép người dùng viết đánh giá cho những phòng họ đã đặt thành công.
### Objective
Allow users to write reviews for rooms they have successfully booked.
#### Nhiệm vụ chi tiết
#### Detailed Tasks
1. Route: /reviews
2. Gọi API:
2. API Calls:
```
GET /api/bookings/me → Lấy danh sách phòng người dùng đã đặt.
POST /api/reviews → Gửi đánh giá.
GET /api/bookings/me → Get list of rooms user has booked.
POST /api/reviews → Submit review.
```
3. Giao diện:
- Hiển thị danh sách phòng đã đặt (tên, ngày ở, trạng thái)
- Nút “Đánh giá” (hiện nếu chưa đánh giá phòng đó)
4. Khi nhấn “Đánh giá” → mở Modal:
- Input chọn số sao (⭐ 15)
- Textarea nhập nội dung bình luận
- Nút “Gửi đánh giá”
5. Validate:
- Rating bắt buộc (15)
- Comment không để trống
6. Sau khi gửi thành công → toast thông báo “Đánh giá của bạn đang chờ duyệt”.
3. Interface:
- Display list of booked rooms (name, stay dates, status)
- "Review" button (shown if room not yet reviewed)
4. When clicking "Review" → open Modal:
- Input to select star rating (⭐ 15)
- Textarea to enter comment content
- "Submit Review" button
5. Validation:
- Rating required (15)
- Comment cannot be empty
6. After successful submission → toast notification "Your review is pending approval".
### Kết quả mong đợi
1. Người dùng chỉ thấy nút “Đánh giá” với phòng đã từng đặt.
2. Modal mở ra và validate chính xác.
3. Gửi thành công → review có trạng thái "pending".
4. Toast hiển thị thông báo hợp lý.
5. Giao diện gọn, trực quan, không lỗi khi chưa có phòng nào đặt.
### Expected Results
1. Users only see "Review" button for rooms they have booked.
2. Modal opens and validates correctly.
3. Successful submission → review has status "pending".
4. Toast displays appropriate notification.
5. Clean, intuitive interface, no errors when no rooms booked.
---
## Chức năng 2: RoomDetailPage Hiển thị danh sách đánh giá
## Function 2: RoomDetailPage Display Review List
### Mục tiêu
Hiển thị danh sách các đánh giá đã được admin duyệt cho từng phòng.
### Objective
Display list of reviews that have been approved by admin for each room.
#### Nhiệm vụ chi tiết
#### Detailed Tasks
1. Route: /rooms/:id
2. API:
```
GET /api/reviews?roomId={id}&status=approved
```
3. Hiển thị danh sách review:
- Avatar + tên người dùng
- Số sao (⭐)
- Nội dung bình luận
- Ngày đăng (createdAt)
4. Tính và hiển thị điểm trung bình rating (VD: ⭐ 4.2 / 5)
5. Nếu chưa có review → hiển thị: “Chưa có đánh giá nào.”
3. Display review list:
- Avatar + user name
- Star rating (⭐)
- Comment content
- Post date (createdAt)
4. Calculate and display average rating (e.g.: ⭐ 4.2 / 5)
5. If no reviewsdisplay: "No reviews yet."
### Kết quả mong đợi
1. Danh sách review hiển thị đúng theo phòng.
2. Chỉ review status = approved được render.
3. Tính điểm trung bình chính xác (làm tròn 1 chữ số thập phân).
4. Hiển thị avatar, tên, sao, và ngày đầy đủ.
5. Có thông báo “Chưa có đánh giá” khi danh sách trống.
### Expected Results
1. Review list displays correctly by room.
2. Only reviews with status = approved are rendered.
3. Calculate average rating accurately (rounded to 1 decimal place).
4. Display avatar, name, stars, and date completely.
5. Show "No reviews yet" message when list is empty.
---
## Chức năng 3: AdminReviewPage Trang quản trị đánh giá
## Function 3: AdminReviewPage Review Management Page
### Mục tiêu
Cho phép Admin xem, duyệt hoặc từ chối các đánh giá người dùng gửi lên.
### Objective
Allow Admin to view, approve or reject reviews submitted by users.
#### Nhiệm vụ chi tiết
#### Detailed Tasks
1. Route: /admin/reviews
2. API:
```
@@ -74,67 +74,67 @@
PATCH /api/reviews/:id/approve
PATCH /api/reviews/:id/reject
```
3. Hành động:
Duyệt → review chuyển sang approved
Từ chối → review chuyển sang rejected
4. Sau khi duyệt → cập nhật giao diện và hiển thị toast thông báo.
5. Có filter theo trạng thái (pending, approved, rejected).
3. Actions:
Approve → review changes to approved
Reject → review changes to rejected
4. After approval → update interface and display toast notification.
5. Filter by status (pending, approved, rejected).
### Kết quả mong đợi
1. Admin thấy đầy đủ danh sách review.
2. Duyệt hoặc từ chối hoạt động đúng API.
3. Bảng tự cập nhật khi thay đổi trạng thái.
4. Toast hiển thị rõ “Đã duyệt” hoặc “Đã từ chối”.
5. Chỉ review approved mới hiển thị công khai cho người dùng.
### Expected Results
1. Admin sees complete review list.
2. Approve or reject works correctly with API.
3. Table automatically updates when status changes.
4. Toast clearly displays "Approved" or "Rejected".
5. Only approved reviews are displayed publicly to users.
---
## Chức năng 4: Bảo mật & Logic hiển thị
## Function 4: Security & Display Logic
### Mục tiêu
Đảm bảo chỉ người hợp lệ mới có thể gửi đánh giá và hệ thống hiển thị đúng dữ liệu.
### Objective
Ensure only valid users can submit reviews and system displays correct data.
#### Nhiệm vụ chi tiết
1. Kiểm tra quyền:
- Người dùng chưa đăng nhập → redirect /login
- Người dùng chưa từng đặt phòng → không hiển thị nút “Đánh giá”
2. Kiểm tra logic:
- Mỗi người chỉ được đánh giá 1 lần / phòng
- Review mặc định status = pending
3. Phân quyền:
- User: chỉ gửi review
- Admin: duyệt / từ chối
- Staff: chỉ xem
#### Detailed Tasks
1. Permission check:
- User not logged in → redirect /login
- User has never booked room → don't display "Review" button
2. Logic check:
- Each person can only review once per room
- Review default status = pending
3. Authorization:
- User: can only submit review
- Admin: approve / reject
- Staff: view only
### Kết quả mong đợi
1. Người chưa đăng nhập không thể gửi review.
2. Mỗi phòng chỉ được review 1 lần bởi 1 user.
3. Dữ liệu hiển thị chính xác theo phân quyền.
4. Review chỉ xuất hiện công khai khi được duyệt.
5. Không có lỗi logic hoặc hiển thị sai trạng thái.
### Expected Results
1. Users not logged in cannot submit reviews.
2. Each room can only be reviewed once by one user.
3. Data displays correctly according to permissions.
4. Reviews only appear publicly when approved.
5. No logic errors or incorrect status display.
---
## Chức năng 5: UX & Hiển thị tổng quan
## Function 5: UX & Overall Display
### Mục tiêu
Cải thiện trải nghiệm người dùng và giao diện hiển thị đánh giá.
### Objective
Improve user experience and review display interface.
#### Nhiệm vụ chi tiết
1. Dùng component đánh giá sao trực quan (ví dụ react-rating-stars-component).
2. Format ngày tạo bằng:
#### Detailed Tasks
1. Use intuitive star rating component (e.g., react-rating-stars-component).
2. Format creation date using:
```
new Date(createdAt).toLocaleDateString('vi-VN')
new Date(createdAt).toLocaleDateString('en-US')
```
3. Thêm hiệu ứng hover nhẹ khi hiển thị danh sách review.
4. Dùng toast (react-hot-toast) cho thông báo gửi / duyệt / từ chối.
5. Loading spinner khi chờ API.
3. Add light hover effect when displaying review list.
4. Use toast (react-hot-toast) for submit / approve / reject notifications.
5. Loading spinner when waiting for API.
### Kết quả mong đợi
1. UI mượt mà, dễ đọc và thân thiện.
2. Loading / toast hiển thị đúng trạng thái.
3. Ngày tháng, sao và bình luận được format đẹp.
4. Giao diện quản trị và người dùng thống nhất phong cách.
5. Trải nghiệm người dùng mượt, không giật lag.
### Expected Results
1. Smooth, readable and user-friendly UI.
2. Loading / toast displays correct status.
3. Dates, stars and comments are formatted nicely.
4. Admin and user interfaces have consistent styling.
5. Smooth user experience, no lag or stuttering.
---