mitai-jinkendo/frontend/src/components/EmailVerificationBanner.jsx
Lars f843d71d6b
All checks were successful
Deploy Development / deploy (push) Successful in 36s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
feat: resend verification email functionality
Backend:
- Added POST /api/auth/resend-verification endpoint
- Rate limited to 3/hour to prevent abuse
- Generates new verification token (24h validity)
- Sends new verification email

Frontend:
- Verify.jsx: Added "expired" status with resend flow
- Email input + "Neue Bestätigungs-E-Mail senden" button
- EmailVerificationBanner: Added "Neue E-Mail senden" button
- Shows success/error feedback inline
- api.js: Added resendVerification() helper

User flows:
1. Expired token → Verify page shows resend form
2. Email lost → Dashboard banner has resend button
3. Both flows use same backend endpoint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 10:23:38 +01:00

97 lines
2.5 KiB
JavaScript

import { useState } from 'react'
import { api } from '../utils/api'
export default function EmailVerificationBanner({ profile }) {
const [resending, setResending] = useState(false)
const [success, setSuccess] = useState(false)
const [error, setError] = useState(null)
// Only show if email is not verified
if (!profile || profile.email_verified !== false) return null
const handleResend = async () => {
if (!profile.email) return
setResending(true)
setError(null)
setSuccess(false)
try {
await api.resendVerification(profile.email)
setSuccess(true)
setTimeout(() => setSuccess(false), 5000)
} catch (err) {
setError(err.message || 'Fehler beim Versenden')
setTimeout(() => setError(null), 5000)
} finally {
setResending(false)
}
}
return (
<div style={{
background: '#FFF4E6',
border: '2px solid #F59E0B',
borderRadius: 12,
padding: '16px 20px',
marginBottom: 20,
display: 'flex',
alignItems: 'center',
gap: 16,
flexWrap: 'wrap'
}}>
<div style={{
fontSize: 32,
lineHeight: 1
}}>
📧
</div>
<div style={{flex: 1, minWidth: 200}}>
<div style={{
fontWeight: 700,
fontSize: 15,
color: '#D97706',
marginBottom: 4
}}>
E-Mail-Adresse noch nicht bestätigt
</div>
<div style={{
fontSize: 13,
color: 'var(--text2)',
lineHeight: 1.4
}}>
Bitte prüfe dein Postfach und klicke auf den Bestätigungslink.
{success && (
<span style={{color:'var(--accent)', fontWeight:600, marginLeft:4}}>
Neue E-Mail versendet!
</span>
)}
{error && (
<span style={{color:'var(--danger)', fontWeight:600, marginLeft:4}}>
{error}
</span>
)}
</div>
</div>
<button
onClick={handleResend}
disabled={resending || success}
style={{
padding: '8px 16px',
borderRadius: 8,
background: success ? 'var(--accent)' : '#F59E0B',
color: 'white',
fontWeight: 600,
fontSize: 13,
border: 'none',
cursor: resending || success ? 'not-allowed' : 'pointer',
whiteSpace: 'nowrap',
opacity: resending || success ? 0.6 : 1
}}
>
{resending ? 'Sende...' : success ? '✓ Versendet' : 'Neue E-Mail senden'}
</button>
</div>
)
}