import { useState, useEffect } from 'react' import { Link } from 'react-router-dom' import { useAuth } from '../context/AuthContext' import api from '../utils/api' /** * Persönliche Einstellungen (Anzeige/Name, Kontostatus, Passwort). */ function AccountSettingsPage() { const { user, checkAuth } = useAuth() const [name, setName] = useState('') const [savingProfile, setSavingProfile] = useState(false) const [publicClubsDir, setPublicClubsDir] = useState([]) const [myJoinRequests, setMyJoinRequests] = useState([]) const [joinClubId, setJoinClubId] = useState('') const [joinMessage, setJoinMessage] = useState('') const [joinBusy, setJoinBusy] = useState(false) const [newPw1, setNewPw1] = useState('') const [newPw2, setNewPw2] = useState('') const [savingPw, setSavingPw] = useState(false) const [resendingVerify, setResendingVerify] = useState(false) const [message, setMessage] = useState('') const [error, setError] = useState('') useEffect(() => { setName(typeof user?.name === 'string' ? user.name : '') }, [user]) const refreshJoinRequests = () => { api.getMyClubJoinRequests().then(setMyJoinRequests).catch(() => {}) } useEffect(() => { if (!user?.id) return api.listPublicClubsDirectory().then(setPublicClubsDir).catch(() => {}) refreshJoinRequests() }, [user?.id]) const memberClubIds = new Set((user?.clubs || []).map((c) => c.id)) const pendingClubIds = new Set( myJoinRequests.filter((r) => r.status === 'pending').map((r) => r.club_id) ) const joinClubChoices = publicClubsDir.filter( (c) => !memberClubIds.has(c.id) && !pendingClubIds.has(c.id) ) const joinStatusLabel = (s) => ({ pending: 'ausstehend', accepted: 'angenommen', rejected: 'abgelehnt', withdrawn: 'zurückgezogen', })[s] || s /** API: boolean true / Legacy: fehlt oder false → als „nicht verifiziert“ behandeln */ const emailExplicitlyVerified = user?.email_verified === true || user?.email_verified === 't' || user?.email_verified === 1 || user?.email_verified === 'true' const showOk = (text) => { setMessage(text) setError('') setTimeout(() => setMessage(''), 5000) } const showErr = (text) => { setError(text) setMessage('') } const handleSaveName = async (e) => { e.preventDefault() if (!user?.id) return const trimmed = (name || '').trim() if (trimmed.length < 2) { showErr('Name sollte mindestens 2 Zeichen haben.') return } setSavingProfile(true) try { await api.updateProfile(user.id, { name: trimmed }) await checkAuth() showOk('Profilname gespeichert.') } catch (err) { showErr(err.message || 'Speichern fehlgeschlagen.') } finally { setSavingProfile(false) } } const handleResendVerification = async () => { const em = user?.email if (!em) return setResendingVerify(true) try { await api.resendVerification(em) showOk('Falls diese Adresse einen unbestätigten Account hat: E-Mail ist unterwegs — Postfach prüfen.') } catch (err) { showErr(err.message || 'Konnte keine E-Mail senden.') } finally { setResendingVerify(false) } } const handleChangePassword = async (e) => { e.preventDefault() if (newPw1.length < 4) { showErr('Neues Passwort: mindestens 4 Zeichen.') return } if (newPw1 !== newPw2) { showErr('Die Passwörter stimmen nicht überein.') return } setSavingPw(true) try { await api.changePassword(newPw1) setNewPw1('') setNewPw2('') showOk('Passwort aktualisiert.') } catch (err) { showErr(err.message || 'Passwort konnte nicht geändert werden.') } finally { setSavingPw(false) } } return (

Einstellungen

Konto & Sicherheit

{message && (
{message}
)} {error && (
{error}
)}

Profil

E-Mail
{user?.email || '—'}{' '} {emailExplicitlyVerified ? ( bestätigt ) : ( noch nicht bestätigt )} {!emailExplicitlyVerified && user?.email ? (
) : null}
setName(e.target.value)} placeholder="Dein Name in der App" autoComplete="nickname" />

Rollen & Tarif

Rolle {user?.role === 'admin' ? 'Administrator' : user?.role || 'trainer'} Tier {user?.tier || 'free'} Vereine {user?.clubs?.length ? ( <> {user.clubs.map((c) => (
{c.name} {': '} {(c.roles || []).length ? (c.roles || []).join(', ') : '—'}
))} ) : ( '—' )}

Vereinsbeitritt

Beantrage die Mitgliedschaft in einem Verein. Vereinsadministratoren können den Antrag unter „Vereinsverwaltung → Mitglieder“ annehmen oder ablehnen.

{myJoinRequests.length > 0 && (
Meine Anträge
    {myJoinRequests.map((r) => (
  • {r.club_name || `Verein #${r.club_id}`} — {joinStatusLabel(r.status)} {r.status === 'pending' ? ( <> {' '} ) : null}
  • ))}
)}
{ e.preventDefault() if (!joinClubId) { showErr('Bitte einen Verein auswählen.') return } setJoinBusy(true) try { await api.createClubJoinRequest({ club_id: parseInt(joinClubId, 10), message: (joinMessage || '').trim() || undefined, }) setJoinMessage('') setJoinClubId('') refreshJoinRequests() await checkAuth() showOk('Antrag gesendet.') } catch (err) { showErr(err.message || 'Antrag fehlgeschlagen.') } finally { setJoinBusy(false) } }} >