433 lines
9.5 KiB
Markdown
433 lines
9.5 KiB
Markdown
# Function 4: Login Form - Usage Guide
|
|
|
|
## 📋 Overview
|
|
|
|
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
|
|
|
|
## 🗂️ Files Created/Updated
|
|
|
|
### 1. **LoginPage.tsx** - Login form component
|
|
**Path:** `client/src/pages/auth/LoginPage.tsx`
|
|
|
|
```typescript
|
|
// Main features:
|
|
- React Hook Form with Yup validation
|
|
- Show/hide password toggle
|
|
- Remember me checkbox
|
|
- Loading state with spinner
|
|
- Error handling
|
|
- Redirect with location state
|
|
```
|
|
|
|
### 2. **index.ts** - Export module
|
|
**Path:** `client/src/pages/auth/index.ts`
|
|
|
|
```typescript
|
|
export { default as LoginPage } from './LoginPage';
|
|
```
|
|
|
|
### 3. **App.tsx** - Routing updated
|
|
**Path:** `client/src/App.tsx`
|
|
|
|
```typescript
|
|
// Added:
|
|
import { LoginPage } from './pages/auth';
|
|
|
|
// Route:
|
|
<Route path="/login" element={<LoginPage />} />
|
|
```
|
|
|
|
## 🎨 UI Structure
|
|
|
|
### Layout
|
|
```
|
|
┌─────────────────────────────────────┐
|
|
│ 🏨 Hotel Icon │
|
|
│ Login │
|
|
│ Welcome back... │
|
|
├─────────────────────────────────────┤
|
|
│ ┌───────────────────────────────┐ │
|
|
│ │ [Error message if any] │ │
|
|
│ ├───────────────────────────────┤ │
|
|
│ │ Email │ │
|
|
│ │ [📧 email@example.com ] │ │
|
|
│ ├───────────────────────────────┤ │
|
|
│ │ Password │ │
|
|
│ │ [🔒 •••••••• 👁️] │ │
|
|
│ ├───────────────────────────────┤ │
|
|
│ │ ☑️ Remember me │ │
|
|
│ │ Forgot password? → │ │
|
|
│ ├───────────────────────────────┤ │
|
|
│ │ [🔐 Login] │ │
|
|
│ └───────────────────────────────┘ │
|
|
│ Don't have an account? Sign up now │
|
|
│ │
|
|
│ Terms & Privacy Policy │
|
|
└─────────────────────────────────────┘
|
|
```
|
|
|
|
## 🔧 Usage
|
|
|
|
### 1. Access Form
|
|
|
|
```bash
|
|
# URL
|
|
http://localhost:5173/login
|
|
```
|
|
|
|
### 2. Form Fields
|
|
|
|
| Field | Type | Required | Validation |
|
|
|--------|------|----------|------------|
|
|
| Email | text | ✅ | Valid email |
|
|
| Password | password | ✅ | Min 8 characters |
|
|
| Remember Me | checkbox | ❌ | Boolean |
|
|
|
|
### 3. Validation Rules
|
|
|
|
**Email:**
|
|
```typescript
|
|
- Required: "Email is required"
|
|
- Valid email format: "Invalid email format"
|
|
- Trim whitespace
|
|
```
|
|
|
|
**Password:**
|
|
```typescript
|
|
- Required: "Password is required"
|
|
- Min 8 characters: "Password must be at least 8 characters"
|
|
```
|
|
|
|
### 4. Login Flow
|
|
|
|
```
|
|
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. If successful:
|
|
- Save token to localStorage
|
|
- Update Zustand state
|
|
- Redirect to /dashboard
|
|
6. If error:
|
|
- Display error message
|
|
- Button enabled again
|
|
```
|
|
|
|
## 🎯 Main Features
|
|
|
|
### 1. Show/Hide Password
|
|
|
|
```typescript
|
|
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 days)
|
|
|
|
```typescript
|
|
// Checkbox
|
|
<input {...register('rememberMe')} type="checkbox" />
|
|
|
|
// Logic in authService.login()
|
|
if (rememberMe) {
|
|
// Token will be saved in localStorage
|
|
// and won't be deleted when closing browser
|
|
}
|
|
```
|
|
|
|
### 3. Loading State
|
|
|
|
```typescript
|
|
// Button disabled when loading
|
|
<button disabled={isLoading}>
|
|
{isLoading ? (
|
|
<>
|
|
<Loader2 className="animate-spin" />
|
|
Processing...
|
|
</>
|
|
) : (
|
|
<>
|
|
<LogIn />
|
|
Login
|
|
</>
|
|
)}
|
|
</button>
|
|
```
|
|
|
|
### 4. Error Handling
|
|
|
|
```typescript
|
|
// Error from Zustand store
|
|
const { error } = useAuthStore();
|
|
|
|
// Display error message
|
|
{error && (
|
|
<div className="bg-red-50 border border-red-200">
|
|
{error}
|
|
</div>
|
|
)}
|
|
```
|
|
|
|
### 5. Redirect Logic
|
|
|
|
```typescript
|
|
// Get location state from ProtectedRoute
|
|
const location = useLocation();
|
|
|
|
// Redirect to previous page or dashboard
|
|
const from = location.state?.from?.pathname || '/dashboard';
|
|
navigate(from, { replace: true });
|
|
```
|
|
|
|
## 🔗 Integration with Zustand Store
|
|
|
|
```typescript
|
|
// Hook usage
|
|
const {
|
|
login, // Function to login
|
|
isLoading, // Loading state
|
|
error, // Error message
|
|
clearError // Clear error
|
|
} = useAuthStore();
|
|
|
|
// Login call
|
|
await login({
|
|
email: 'user@example.com',
|
|
password: 'password123',
|
|
rememberMe: true
|
|
});
|
|
```
|
|
|
|
## 🎨 Styling with 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
|
|
```typescript
|
|
// Container
|
|
className="max-w-md w-full" // Max width 28rem
|
|
|
|
// Grid (if any)
|
|
className="grid grid-cols-1 md:grid-cols-2"
|
|
```
|
|
|
|
## 🧪 Testing Scenarios
|
|
|
|
### 1. Validation Testing
|
|
|
|
**Test Case 1: Empty form**
|
|
```
|
|
- Input: Submit empty form
|
|
- Expected: Display error "Email is required"
|
|
```
|
|
|
|
**Test Case 2: Invalid email**
|
|
```
|
|
- Input: Email = "notanemail"
|
|
- Expected: "Invalid email format"
|
|
```
|
|
|
|
**Test Case 3: Short password**
|
|
```
|
|
- Input: Password = "123"
|
|
- Expected: "Password must be at least 8 characters"
|
|
```
|
|
|
|
### 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 from server
|
|
```
|
|
|
|
**Test Case 6: Network error**
|
|
```
|
|
- Input: Server offline
|
|
- Expected: Error message "An error occurred"
|
|
```
|
|
|
|
### 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 "Remember me"
|
|
- Expected: Token persists after reload
|
|
```
|
|
|
|
**Test Case 9: Loading state**
|
|
```
|
|
- Action: Submit form
|
|
- Expected: Button disabled, spinner displayed
|
|
```
|
|
|
|
## 🔐 Security Features
|
|
|
|
### 1. Password Visibility
|
|
```typescript
|
|
// Default: password hidden
|
|
type="password"
|
|
|
|
// Toggle: user control
|
|
onClick={() => setShowPassword(!showPassword)}
|
|
```
|
|
|
|
### 2. HTTPS Only (Production)
|
|
```typescript
|
|
// In .env
|
|
VITE_API_URL=https://api.yourdomain.com
|
|
```
|
|
|
|
### 3. Token Storage
|
|
```typescript
|
|
// LocalStorage for remember me
|
|
if (rememberMe) {
|
|
localStorage.setItem('token', token);
|
|
}
|
|
|
|
// SessionStorage for session only
|
|
else {
|
|
sessionStorage.setItem('token', token);
|
|
}
|
|
```
|
|
|
|
## 📝 Best Practices
|
|
|
|
### 1. Form Validation
|
|
```typescript
|
|
✅ Client-side validation (Yup)
|
|
✅ Server-side validation (Express validator)
|
|
✅ Immediate feedback
|
|
✅ Clear error messages
|
|
```
|
|
|
|
### 2. Error Handling
|
|
```typescript
|
|
✅ Try-catch blocks
|
|
✅ User-friendly messages
|
|
✅ Clear error state
|
|
✅ Console logging for debugging
|
|
```
|
|
|
|
### 3. UX
|
|
```typescript
|
|
✅ Loading indicators
|
|
✅ Disabled states
|
|
✅ Auto-focus first field
|
|
✅ Enter key submit
|
|
✅ Remember form state
|
|
```
|
|
|
|
## 🚀 Next Steps (Function 5-7)
|
|
|
|
1. **Function 5: Register Form**
|
|
- Copy structure from LoginPage
|
|
- Add fields: name, phone, confirmPassword
|
|
- Use registerSchema
|
|
- Redirect to /login after success
|
|
|
|
2. **Function 6: Forgot Password**
|
|
- Simple form with email only
|
|
- Send reset link
|
|
- Success message
|
|
|
|
3. **Function 7: Reset Password**
|
|
- Form with password + confirmPassword
|
|
- Token from URL params
|
|
- Redirect to /login after success
|
|
|
|
## 🐛 Troubleshooting
|
|
|
|
### Issue 1: "Error: Token expired"
|
|
```typescript
|
|
Solution: Check token expiry time
|
|
- Access token: 1 hour
|
|
- Refresh token: 7 days
|
|
```
|
|
|
|
### Issue 2: Form doesn't submit
|
|
```typescript
|
|
Solution: Check console for validation errors
|
|
- Open DevTools > Console
|
|
- Look for Yup validation errors
|
|
```
|
|
|
|
### Issue 3: Redirect doesn't work
|
|
```typescript
|
|
Solution: Check location state
|
|
console.log(location.state?.from);
|
|
```
|
|
|
|
### Issue 4: Remember me doesn't work
|
|
```typescript
|
|
Solution: Check localStorage
|
|
- Open DevTools > Application > Local Storage
|
|
- Check "token" and "refreshToken" keys
|
|
```
|
|
|
|
## 📚 Resources
|
|
|
|
- [React Hook Form Docs](https://react-hook-form.com/)
|
|
- [Yup Validation](https://github.com/jquense/yup)
|
|
- [Zustand Guide](https://github.com/pmndrs/zustand)
|
|
- [Tailwind CSS](https://tailwindcss.com/)
|
|
- [Lucide Icons](https://lucide.dev/)
|
|
|
|
## ✅ Checklist
|
|
|
|
- [x] ✅ Create validationSchemas.ts
|
|
- [x] ✅ Create LoginPage.tsx
|
|
- [x] ✅ Add route to App.tsx
|
|
- [x] ✅ Email validation
|
|
- [x] ✅ Password validation
|
|
- [x] ✅ Show/hide password
|
|
- [x] ✅ Remember me checkbox
|
|
- [x] ✅ Loading state
|
|
- [x] ✅ Error display
|
|
- [x] ✅ Redirect after login
|
|
- [x] ✅ Responsive design
|
|
- [x] ✅ Icons integration
|
|
- [ ] ⏳ E2E testing
|
|
|
|
---
|
|
|
|
**Status:** ✅ Function 4 completed
|
|
**Next:** Function 5 - Register Form
|