import { useCallback, useEffect, useState } from 'react'
import { Navigate } from 'react-router-dom'
import { useAuth } from '../context/AuthContext'
import api from '../utils/api'
import AdminPageNav from '../components/AdminPageNav'
const CLUB_ROLE_OPTIONS = [
{ code: 'club_admin', label: 'Vereinsadmin' },
{ code: 'trainer', label: 'Trainer' },
{ code: 'division_lead', label: 'Spartenleitung' },
{ code: 'content_editor', label: 'Inhalte bearbeiten' },
]
const PORTAL_ROLE_LABEL = {
user: 'Nutzer',
trainer: 'Portal-Trainer',
admin: 'Portal-Administrator',
superadmin: 'Super-Administrator',
}
function portalRoleSelectOptions(viewerIsSuperadmin, currentRole) {
const base = [
{ value: 'user', label: PORTAL_ROLE_LABEL.user },
{ value: 'trainer', label: `${PORTAL_ROLE_LABEL.trainer} (Legacy)` },
{ value: 'admin', label: PORTAL_ROLE_LABEL.admin },
]
const cur = (currentRole || 'user').toLowerCase()
if (viewerIsSuperadmin) base.push({ value: 'superadmin', label: PORTAL_ROLE_LABEL.superadmin })
const values = new Set(base.map((x) => x.value))
if (cur && !values.has(cur)) {
base.unshift({ value: cur, label: cur })
}
return base
}
/**
* Nur Super-Admins — globale Nutzer- und Portal-Rollen plus Vereinszuordnung pro Profil.
* Vereinsorganisation (Mitglieder, Anträge, Rollen vor Ort): unter Vereine → Mitglieder.
*/
export default function AdminUsersPage() {
const { user } = useAuth()
const isSuperadminViewer = user?.role === 'superadmin'
const [platformUsers, setPlatformUsers] = useState([])
const [clubs, setClubs] = useState([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState('')
const [portalDraft, setPortalDraft] = useState({})
const [assignModal, setAssignModal] = useState(null)
const [assignRoles, setAssignRoles] = useState(['trainer'])
const [clubEditModal, setClubEditModal] = useState(null)
const [pwdModal, setPwdModal] = useState(null)
const [pwdNew, setPwdNew] = useState('')
const [pwdNew2, setPwdNew2] = useState('')
const loadPlatform = useCallback(async () => {
const [u, c] = await Promise.all([api.listAdminUsers(), api.listClubs()])
setPlatformUsers(Array.isArray(u) ? u : [])
setClubs(Array.isArray(c) ? c : [])
const d = {}
for (const row of u || []) {
d[row.id] = { role: (row.role || 'user').toLowerCase() }
}
setPortalDraft(d)
}, [])
useEffect(() => {
if (!isSuperadminViewer) return
let cancelled = false
;(async () => {
setError('')
setLoading(true)
try {
await loadPlatform()
} catch (e) {
if (!cancelled) setError(e.message || String(e))
} finally {
if (!cancelled) setLoading(false)
}
})()
return () => {
cancelled = true
}
}, [isSuperadminViewer, loadPlatform])
if (!isSuperadminViewer) return
const savePortal = async (profileId) => {
const dr = portalDraft[profileId]
if (!dr) return
try {
await api.updateProfile(profileId, { role: dr.role })
await loadPlatform()
} catch (e) {
alert(e.message || String(e))
}
}
const submitAssignClub = async () => {
if (!assignModal) return
const clubId = assignModal.clubId
const profileId = assignModal.profileId
if (!clubId || !assignRoles.length) {
alert('Verein und mindestens eine Rolle wählen.')
return
}
try {
await api.addClubMember(clubId, { profile_id: profileId, roles: assignRoles })
setAssignModal(null)
setAssignRoles(['trainer'])
await loadPlatform()
} catch (e) {
alert(e.message || String(e))
}
}
const saveClubMembership = async () => {
if (!clubEditModal) return
const { clubId, profileId, roles, status } = clubEditModal
try {
await api.updateClubMember(clubId, profileId, { roles, status })
setClubEditModal(null)
await loadPlatform()
} catch (e) {
alert(e.message || String(e))
}
}
const removeClubMembership = async () => {
if (!clubEditModal) return
if (!confirm('Mitgliedschaft in diesem Verein wirklich entfernen?')) return
try {
await api.removeClubMember(clubEditModal.clubId, clubEditModal.profileId)
setClubEditModal(null)
await loadPlatform()
} catch (e) {
alert(e.message || String(e))
}
}
const submitPasswordEmail = async () => {
if (!pwdModal) return
try {
const res = await api.managementPasswordReset(pwdModal.profileId, null)
setPwdModal(null)
setPwdNew('')
setPwdNew2('')
let msg =
'Sofern eine E-Mail-Adresse hinterlegt ist, wurde ein Link zum Setzen eines neuen Passworts versendet. Das bisherige Passwort bleibt bis zur Bestätigung im Link aktiv.'
if (res?.email_sent === false) {
msg += ' Hinweis: Der E-Mail-Versand ist fehlgeschlagen (SMTP prüfen).'
}
alert(msg)
} catch (e) {
alert(e.message || String(e))
}
}
const submitPasswordDirect = async () => {
if (!pwdModal) return
if (pwdNew.length < 8) {
alert('Mindestens 8 Zeichen.')
return
}
if (pwdNew !== pwdNew2) {
alert('Die beiden Passwörter stimmen nicht überein.')
return
}
try {
await api.managementPasswordReset(pwdModal.profileId, pwdNew)
setPwdModal(null)
setPwdNew('')
setPwdNew2('')
alert('Neues Passwort wurde direkt gesetzt.')
} catch (e) {
alert(e.message || String(e))
}
}
return (
Globale Nutzer & Portal-Rollen
<>
Plattformweite Konten und Vereinszuordnungen im Überblick. Vereinsmitgliedschaft vor Ort{' '}
(Mitglieder, Beitrittsanträge, Vereinszugriffe) liegt unter Vereine → Mitglieder. Die
geschützten /admin-Menüpunkte (Hierarchie, Kataloge, …) gibt es nur noch hier für Super-Admins.
Portal-Rollen auf Profil-Ebene:
Nutzer — Standard ohne globale Administrationsbereiche.
Portal-Trainer (Legacy) — historische Kennzeichnung; organisatorisch ist „Trainer“ meist eine{' '}
Vereinsrolle.
Portal-Administrator — erhöhte API-/Operativ-Rechte nach Backend-Policies; die{' '}
Admin-Navigation dieser App sieht jedoch nur noch der Super-Admin.
„Deaktiviert“ betrifft nur den Zugriff auf Inhalte dieses Vereins; Login und andere Vereine bleiben unberührt.
Rollen
{CLUB_ROLE_OPTIONS.map((opt) => (
))}
)}
{pwdModal ? (
Passwort zurücksetzen
{pwdModal.label}
Standard: Es wird ein sicherer Link per E-Mail verschickt (wie „Passwort vergessen“). Das bisherige Passwort
bleibt gültig, bis die Person den Link nutzt und ein neues Passwort wählt.
<>
Ausnahme: Passwort direkt setzen (nur bei Bedarf). Das bisherige Passwort ist danach ungültig.