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>
97 lines
2.5 KiB
JavaScript
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>
|
|
)
|
|
}
|