Files
Hotel-Booking/docs/LOGIN_FORM_GUIDE.md
Iliyan Angelov 824eec6190 Hotel Booking
2025-11-16 14:19:13 +02:00

10 KiB

Chức năng 4: Form Đăng Nhập - Hướng Dẫn Sử Dụng

📋 Tổng Quan

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
  • Responsive design

🗂️ Các File Đã Tạo/Cập Nhật

1. LoginPage.tsx - Component form đăng nhập

Đường dẫn: client/src/pages/auth/LoginPage.tsx

// Các tính năng chính:
- React Hook Form với Yup validation
- Show/hide password toggle
- Remember me checkbox
- Loading state với spinner
- Error handling
- Redirect với location state

2. index.ts - Export module

Đường dẫn: client/src/pages/auth/index.ts

export { default as LoginPage } from './LoginPage';

3. App.tsx - Đã cập nhật routing

Đường dẫn: client/src/App.tsx

// Đã thêm:
import { LoginPage } from './pages/auth';

// Route:
<Route path="/login" element={<LoginPage />} />

🎨 Cấu Trúc UI

Layout

┌─────────────────────────────────────┐
│   🏨 Hotel Icon                     │
│   Đăng nhập                         │
│   Chào mừng bạn trở lại...          │
├─────────────────────────────────────┤
│  ┌───────────────────────────────┐  │
│  │ [Error message if any]        │  │
│  ├───────────────────────────────┤  │
│  │ Email                         │  │
│  │ [📧 email@example.com       ] │  │
│  ├───────────────────────────────┤  │
│  │ Mật khẩu                      │  │
│  │ [🔒 ••••••••            👁️] │  │
│  ├───────────────────────────────┤  │
│  │ ☑️ Nhớ đăng nhập              │  │
│  │              Quên mật khẩu? → │  │
│  ├───────────────────────────────┤  │
│  │      [🔐 Đăng nhập]           │  │
│  └───────────────────────────────┘  │
│  Chưa có tài khoản? Đăng ký ngay    │
│                                     │
│  Điều khoản & Chính sách bảo mật    │
└─────────────────────────────────────┘

🔧 Cách Sử Dụng

1. Truy Cập Form

# URL
http://localhost:5173/login

2. Các Trường Trong Form

Trường Type Bắt buộc Validation
Email text Email hợp lệ
Password password Min 8 ký tự
Remember Me checkbox Boolean

3. Validation Rules

Email:

- Required: "Email là bắt buộc"
- Valid email format: "Email không hợp lệ"
- Trim whitespace

Password:

- 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ự"

4. Luồng Đăng Nhập

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()
   - API POST /api/auth/login
5. Nếu thành công:
   - Lưu token vào localStorage
   - Update Zustand state
   - Redirect đến /dashboard
6. Nếu lỗi:
   - Hiển thị error message
   - Button enabled lại

🎯 Tính Năng Chính

1. Show/Hide Password

const [showPassword, setShowPassword] = useState(false);

// Toggle button
<button onClick={() => setShowPassword(!showPassword)}>
  {showPassword ? <EyeOff /> : <Eye />}
</button>

// Input type
<input type={showPassword ? 'text' : 'password'} />

2. Remember Me (7 ngày)

// Checkbox
<input {...register('rememberMe')} type="checkbox" />

// Logic trong authService.login()
if (rememberMe) {
  // Token sẽ được lưu trong localStorage
  // và không bị xóa khi đóng trình duyệt
}

3. Loading State

// Button disabled khi đang loading
<button disabled={isLoading}>
  {isLoading ? (
    <>
      <Loader2 className="animate-spin" />
      Đang xử ...
    </>
  ) : (
    <>
      <LogIn />
      Đăng nhập
    </>
  )}
</button>

4. Error Handling

// Error từ Zustand store
const { error } = useAuthStore();

// Hiển thị error message
{error && (
  <div className="bg-red-50 border border-red-200">
    {error}
  </div>
)}

5. Redirect Logic

// Lấy location state từ ProtectedRoute
const location = useLocation();

// Redirect về trang trước đó hoặc dashboard
const from = location.state?.from?.pathname || '/dashboard';
navigate(from, { replace: true });

🔗 Integration với Zustand Store

// Hook usage
const { 
  login,      // Function để login
  isLoading,  // Loading state
  error,      // Error message
  clearError  // Clear error
} = useAuthStore();

// Login call
await login({
  email: 'user@example.com',
  password: 'password123',
  rememberMe: true
});

🎨 Styling với Tailwind

Color Scheme

- Primary: blue-600, blue-700
- Background: gradient from-blue-50 to-indigo-100
- Error: red-50, red-200, red-600, red-700
- Text: gray-600, gray-700, gray-900

Responsive Design

// Container
className="max-w-md w-full"  // Max width 28rem

// Grid (nếu có)
className="grid grid-cols-1 md:grid-cols-2"

🧪 Testing Scenarios

1. Validation Testing

Test Case 1: Empty form

- Input: Submit form trống
- Expected: Hiển thị lỗi "Email là bắt buộc"

Test Case 2: Invalid email

- Input: Email = "notanemail"
- Expected: "Email không hợp lệ"

Test Case 3: Short password

- Input: Password = "123"
- Expected: "Mật khẩu phải có ít nhất 8 ký tự"

2. Authentication Testing

Test Case 4: Valid credentials

- Input: Valid email + password
- Expected: Redirect to /dashboard

Test Case 5: Invalid credentials

- Input: Wrong password
- Expected: Error message từ server

Test Case 6: Network error

- Input: Server offline
- Expected: Error message "Có lỗi xảy ra"

3. UX Testing

Test Case 7: Show/hide password

- Action: Click eye icon
- Expected: Password text visible/hidden

Test Case 8: Remember me

- Action: Check "Nhớ đăng nhập"
- Expected: Token persist sau khi reload

Test Case 9: Loading state

- Action: Submit form
- Expected: Button disabled, spinner hiển thị

🔐 Security Features

1. Password Visibility

// Default: password hidden
type="password"

// Toggle: user control
onClick={() => setShowPassword(!showPassword)}

2. HTTPS Only (Production)

// Trong .env
VITE_API_URL=https://api.yourdomain.com

3. Token Storage

// LocalStorage cho remember me
if (rememberMe) {
  localStorage.setItem('token', token);
}

// SessionStorage cho session only
else {
  sessionStorage.setItem('token', token);
}

📝 Best Practices

1. Form Validation

 Client-side validation (Yup)
 Server-side validation (Express validator)
 Immediate feedback
 Clear error messages

2. Error Handling

 Try-catch blocks
 User-friendly messages
 Clear error state
 Console logging for debugging

3. UX

 Loading indicators
 Disabled states
 Auto-focus first field
 Enter key submit
 Remember form state

🚀 Next Steps (Chức năng 5-7)

  1. Chức năng 5: Form Register

    • Copy structure từ LoginPage
    • Thêm 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
    • Send reset link
    • Success message
  3. Chức năng 7: Reset Password

    • Form với password + confirmPassword
    • Token từ URL params
    • Redirect to /login after success

🐛 Troubleshooting

Issue 1: "Error: Token expired"

Solution: Check token expiry time
- Access token: 1 hour
- Refresh token: 7 days

Issue 2: Form không submit

Solution: Check console for validation errors
- Open DevTools > Console
- Look for Yup validation errors

Issue 3: Redirect không hoạt động

Solution: Check location state
console.log(location.state?.from);

Issue 4: Remember me không work

Solution: Check localStorage
- Open DevTools > Application > Local Storage
- Check "token"  "refreshToken" keys

📚 Resources

Checklist

  • Create validationSchemas.ts
  • Create LoginPage.tsx
  • Add route to App.tsx
  • Email validation
  • Password validation
  • Show/hide password
  • Remember me checkbox
  • Loading state
  • Error display
  • Redirect after login
  • Responsive design
  • Icons integration
  • E2E testing

Status: Chức năng 4 hoàn thành Next: Chức năng 5 - Form Register