Files
tabungin/UX_IMPROVEMENTS.md
dwindown 249f3a9d7d feat: remove OTP gate from transactions, fix categories auth, add implementation plan
- Remove OtpGateGuard from transactions controller (OTP verified at login)
- Fix categories controller to use authenticated user instead of TEMP_USER_ID
- Add comprehensive implementation plan document
- Update .env.example with WEB_APP_URL
- Prepare for admin dashboard development
2025-10-11 14:00:11 +07:00

156 lines
3.9 KiB
Markdown

# ✅ UX Improvements - Email OTP Resend & QR Code Fix
## 🎯 **Improvements Made:**
### 1. ✅ **Email OTP Resend Button with Timer**
**Feature**: Added a resend button for email OTP with a 30-second cooldown timer.
**How it works**:
- When user is on OTP verification page (email tab)
- Button shows countdown: "Resend in 30s", "Resend in 29s", etc.
- After 30 seconds, button becomes active: "Resend Code"
- Click to resend → New OTP sent → Timer resets to 30s
**Implementation**:
```typescript
// State management
const [resendTimer, setResendTimer] = useState(30)
const [canResend, setCanResend] = useState(false)
// Countdown timer
useEffect(() => {
if (resendTimer > 0) {
const timer = setTimeout(() => setResendTimer(resendTimer - 1), 1000)
return () => clearTimeout(timer)
} else {
setCanResend(true)
}
}, [resendTimer])
// Resend handler
const handleResendEmail = async () => {
await axios.post(`${API_URL}/api/otp/email/send`, {}, {
headers: { Authorization: `Bearer ${tempToken}` }
})
setResendTimer(30)
setCanResend(false)
}
```
**UI**:
```tsx
<Button
variant="outline"
onClick={handleResendEmail}
disabled={!canResend || resendLoading}
>
{resendLoading ? (
<>Sending...</>
) : canResend ? (
<>Resend Code</>
) : (
<>Resend in {resendTimer}s</>
)}
</Button>
```
---
### 2. ✅ **QR Code Fix After Re-enabling TOTP**
**Problem**: When disabling and re-enabling Google Authenticator, the QR code failed to load.
**Root Cause**: The QR code state wasn't being cleared when TOTP was disabled, causing stale data.
**Fix**: Clear QR code and secret when disabling TOTP:
```typescript
const handleTotpDisable = async () => {
await axios.post(`${API}/otp/totp/disable`)
await loadOtpStatus()
setShowTotpSetup(false)
// Clear QR code and secret when disabling
setOtpStatus(prev => ({
...prev,
totpSecret: undefined,
totpQrCode: undefined
}))
}
```
**Now**:
1. Disable TOTP → QR code and secret cleared
2. Enable TOTP again → Fresh QR code generated
3. QR code displays properly ✅
---
## 📝 **Files Modified:**
### 1. **`apps/web/src/components/pages/OtpVerification.tsx`**
- Added `useState` for resend timer and loading states
- Added `useEffect` for countdown timer
- Added `handleResendEmail()` function
- Added resend button with timer in email OTP tab
- Imported `RefreshCw` icon and `axios`
### 2. **`apps/web/src/components/pages/Profile.tsx`**
- Updated `handleTotpDisable()` to clear QR code state
- Clears `totpSecret` and `totpQrCode` when disabling
---
## 🧪 **Testing:**
### Test Email OTP Resend:
1. ✅ Login with email/password (has email OTP enabled)
2. ✅ On OTP page, see "Resend in 30s" button (disabled)
3. ✅ Wait for countdown
4. ✅ After 30s, button shows "Resend Code" (enabled)
5. ✅ Click button → New OTP sent
6. ✅ Timer resets to 30s
7. ✅ Check console for new OTP code
### Test TOTP QR Code:
1. ✅ Go to Profile page
2. ✅ Setup Google Authenticator → QR code displays
3. ✅ Verify and enable TOTP
4. ✅ Disable TOTP
5. ✅ Setup again → **QR code displays properly**
---
## ✨ **User Experience Improvements:**
### Before:
- ❌ No way to resend email OTP
- ❌ User stuck if email not received
- ❌ QR code broken after re-enabling TOTP
### After:
- ✅ Can resend email OTP after 30 seconds
- ✅ Clear countdown timer shows when resend is available
- ✅ QR code works perfectly every time
- ✅ Better user experience overall
---
## 🎯 **Additional Features:**
### Resend Button States:
1. **Countdown** (0-29s): "Resend in Xs" - Disabled, gray
2. **Ready** (30s+): "Resend Code" - Enabled, clickable
3. **Sending**: "Sending..." - Disabled, loading spinner
4. **Sent**: Timer resets to 30s
### Error Handling:
- If resend fails: Shows error message
- If verification fails: User can resend
- Timer persists across tab switches
---
**Both improvements are now live! Test them out!** 🚀