pdf-quiz-generator/frontend/src/pages/VerifyEmailPage.jsx
Daniel 47ba213ae3 Major platform update: pgvector search, multi-provider TTS, settings page, CLI
Features:
- Hybrid semantic + keyword quiz search (pgvector HNSW + PostgreSQL ILIKE)
- AWS Bedrock Titan Embed V2 embeddings via LiteLLM proxy (0.71 cosine sim)
- Multi-provider TTS: OpenAI, AWS Polly (neural), ElevenLabs, Google Cloud TTS
- Unified Settings page (profile, theme, Nextcloud integration, admin shortcuts)
- Good morning/afternoon greeting on dashboard
- manage.py CLI: reset-password, list-users, reembed
- Email verification enforced: register no longer returns JWT for unverified users
- Quiz search with debounced input, semantic/keyword/title modes, highlighted snippets
- TTS button: loading/playing states, voice selector locked during playback
- TTS auto-stops when navigating between questions
- Footer added; mobile quiz nav overflow fixed; markdown theme body selector fixed
- OpenAI Alloy as default TTS voice; favicon added
- SMTP configured via smtp2go; password reset rate limiting (3/hour)
- PostgreSQL upgraded to pgvector/pgvector:pg16

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 18:03:10 +02:00

43 lines
1.9 KiB
JavaScript

import { useState, useEffect } from 'react'
import { useSearchParams, Link } from 'react-router-dom'
import api from '../api/client'
export default function VerifyEmailPage() {
const [searchParams] = useSearchParams()
const token = searchParams.get('token')
const [status, setStatus] = useState('verifying') // verifying | success | error
const [message, setMessage] = useState('')
useEffect(() => {
if (!token) { setStatus('error'); setMessage('No verification token provided.'); return }
api.get(`/auth/verify-email?token=${token}`)
.then(res => { setStatus('success'); setMessage(res.data.message) })
.catch(err => { setStatus('error'); setMessage(err.response?.data?.detail || 'Verification failed.') })
}, [token])
return (
<div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#f1f5f9' }}>
<div className="card" style={{ maxWidth: 420, width: '100%', textAlign: 'center' }}>
{status === 'verifying' && (
<><div className="spinner" style={{ margin: '0 auto 16px' }}></div><p>Verifying your email...</p></>
)}
{status === 'success' && (
<>
<div style={{ fontSize: '3rem', marginBottom: 12 }}></div>
<h2 style={{ color: '#166534', marginBottom: 8 }}>Email Verified!</h2>
<p style={{ color: '#64748b', marginBottom: 24 }}>{message}</p>
<Link to="/login" className="btn btn-primary">Log In</Link>
</>
)}
{status === 'error' && (
<>
<div style={{ fontSize: '3rem', marginBottom: 12 }}></div>
<h2 style={{ color: '#dc2626', marginBottom: 8 }}>Verification Failed</h2>
<p style={{ color: '#64748b', marginBottom: 24 }}>{message}</p>
<Link to="/login" className="btn btn-secondary">Back to Login</Link>
</>
)}
</div>
</div>
)
}