diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index 6b9421f..75e3a4b 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -8,6 +8,8 @@ import { Avatar } from './pages/ProfileSelect'
import SetupScreen from './pages/SetupScreen'
import { ResetPassword } from './pages/PasswordRecovery'
import LoginScreen from './pages/LoginScreen'
+import Register from './pages/Register'
+import Verify from './pages/Verify'
import Dashboard from './pages/Dashboard'
import CaptureHub from './pages/CaptureHub'
import WeightScreen from './pages/WeightScreen'
@@ -59,9 +61,26 @@ function AppShell() {
}
}, [session?.profile_id])
- // Handle password reset link
+ // Handle public pages (register, verify, reset-password)
const urlParams = new URLSearchParams(window.location.search)
- const resetToken = urlParams.get('reset-password') || (window.location.pathname === '/reset-password' ? urlParams.get('token') : null)
+ const currentPath = window.location.pathname
+
+ // Register page
+ if (currentPath === '/register') return (
+
diff --git a/frontend/src/pages/Register.jsx b/frontend/src/pages/Register.jsx
new file mode 100644
index 0000000..14c49c8
--- /dev/null
+++ b/frontend/src/pages/Register.jsx
@@ -0,0 +1,182 @@
+import { useState } from 'react'
+import { Link } from 'react-router-dom'
+import { api } from '../utils/api'
+
+export default function Register() {
+ const [name, setName] = useState('')
+ const [email, setEmail] = useState('')
+ const [password, setPassword] = useState('')
+ const [passwordConfirm, setPasswordConfirm] = useState('')
+ const [loading, setLoading] = useState(false)
+ const [error, setError] = useState(null)
+ const [success, setSuccess] = useState(false)
+
+ const handleSubmit = async (e) => {
+ e.preventDefault()
+ setError(null)
+
+ // Validation
+ if (!name || name.length < 2) {
+ setError('Name muss mindestens 2 Zeichen lang sein')
+ return
+ }
+ if (!email || !email.includes('@')) {
+ setError('Ungültige E-Mail-Adresse')
+ return
+ }
+ if (password.length < 8) {
+ setError('Passwort muss mindestens 8 Zeichen lang sein')
+ return
+ }
+ if (password !== passwordConfirm) {
+ setError('Passwörter stimmen nicht überein')
+ return
+ }
+
+ setLoading(true)
+ try {
+ await api.register(name, email, password)
+ setSuccess(true)
+ } catch (err) {
+ setError(err.message || 'Registrierung fehlgeschlagen')
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ if (success) {
+ return (
+
+
+
✓
+
+ Registrierung erfolgreich!
+
+
+ Wir haben dir eine E-Mail mit einem Bestätigungslink gesendet.
+ Bitte prüfe dein Postfach und bestätige deine E-Mail-Adresse.
+
+
+
+
+ Zum Login
+
+
+
+ Keine E-Mail erhalten? Prüfe auch deinen Spam-Ordner.
+
+
+ )
+ }
+
+ return (
+
+
Registrierung
+
+ Erstelle deinen Mitai Jinkendo Account
+
+
+
+
+
+ Mit der Registrierung akzeptierst du unsere Nutzungsbedingungen
+ und Datenschutzerklärung.
+
+
+ )
+}
diff --git a/frontend/src/pages/Verify.jsx b/frontend/src/pages/Verify.jsx
new file mode 100644
index 0000000..32fc2ca
--- /dev/null
+++ b/frontend/src/pages/Verify.jsx
@@ -0,0 +1,115 @@
+import { useState, useEffect, useContext } from 'react'
+import { useSearchParams, useNavigate } from 'react-router-dom'
+import { AuthContext } from '../context/AuthContext'
+import { api } from '../utils/api'
+
+export default function Verify() {
+ const [searchParams] = useSearchParams()
+ const token = searchParams.get('token')
+ const navigate = useNavigate()
+ const { login } = useContext(AuthContext)
+
+ const [status, setStatus] = useState('loading') // loading | success | error
+ const [error, setError] = useState(null)
+
+ useEffect(() => {
+ const verify = async () => {
+ if (!token) {
+ setStatus('error')
+ setError('Kein Verifikations-Token gefunden')
+ return
+ }
+
+ try {
+ const result = await api.verifyEmail(token)
+
+ // Auto-login with returned token
+ if (result.token) {
+ login(result.token, result.profile)
+ setStatus('success')
+
+ // Redirect to dashboard after 2 seconds
+ setTimeout(() => {
+ navigate('/dashboard')
+ }, 2000)
+ } else {
+ setStatus('error')
+ setError('Verifizierung erfolgreich, aber Login fehlgeschlagen')
+ }
+ } catch (err) {
+ setStatus('error')
+ setError(err.message || 'Verifizierung fehlgeschlagen')
+ }
+ }
+
+ verify()
+ }, [token, login, navigate])
+
+ if (status === 'loading') {
+ return (
+
+
+
E-Mail wird bestätigt...
+
Einen Moment bitte
+
+ )
+ }
+
+ if (status === 'error') {
+ return (
+
+
+
✗
+
+ Verifizierung fehlgeschlagen
+
+
+ {error}
+
+
+
+
+
+ )
+ }
+
+ // Success
+ return (
+
+
+
✓
+
+ E-Mail bestätigt!
+
+
+ Dein Account wurde erfolgreich aktiviert.
+ Du wirst gleich zum Dashboard weitergeleitet...
+
+
+
+
+
+ )
+}
diff --git a/frontend/src/utils/api.js b/frontend/src/utils/api.js
index bc4701e..0204445 100644
--- a/frontend/src/utils/api.js
+++ b/frontend/src/utils/api.js
@@ -142,6 +142,8 @@ export const api = {
adminDeleteProfile: (id) => req(`/admin/profiles/${id}`,{method:'DELETE'}),
adminSetPermissions: (id,d) => req(`/admin/profiles/${id}/permissions`,jput(d)),
changePin: (pin) => req('/auth/pin',json({pin})),
+ register: (name,email,password) => req('/auth/register',json({name,email,password})),
+ verifyEmail: (token) => req(`/auth/verify/${token}`),
// v9c Subscription System
// User-facing