update
This commit is contained in:
@@ -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 và 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 và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ử lý...
|
||||
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" và "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
|
||||
|
||||
Reference in New Issue
Block a user