# โœ… 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 ``` --- ### 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!** ๐Ÿš€