import React, { useState, useEffect, useMemo } from 'react' import api from '../utils/api' import { notifyOrgInboxChanged } from '../context/OrgInboxContext' import { useAuth } from '../context/AuthContext' import { activeClubMemberships } from '../utils/activeClub' import PageSectionNav from '../components/PageSectionNav' const CLUB_ROLE_OPTIONS = [ { code: 'club_admin', label: 'Vereinsadmin' }, { code: 'trainer', label: 'Trainer' }, { code: 'division_lead', label: 'Spartenleitung' }, { code: 'content_editor', label: 'Inhalte bearbeiten' }, ] function ClubsPage() { const { user } = useAuth() const [activeTab, setActiveTab] = useState('clubs') const [clubs, setClubs] = useState([]) const [divisions, setDivisions] = useState([]) const [groups, setGroups] = useState([]) const [loading, setLoading] = useState(true) const [showModal, setShowModal] = useState(false) const [editing, setEditing] = useState(null) const [modalType, setModalType] = useState('club') const [membersAdminClubId, setMembersAdminClubId] = useState(null) const [clubMembersAdmin, setClubMembersAdmin] = useState([]) const [joinRequestsAdmin, setJoinRequestsAdmin] = useState([]) const [membersAdminLoading, setMembersAdminLoading] = useState(false) const [groupMemberDirectory, setGroupMemberDirectory] = useState([]) const [showAddMemberModal, setShowAddMemberModal] = useState(false) const [profilesAdminList, setProfilesAdminList] = useState([]) const [addMemberForm, setAddMemberForm] = useState({ profile_id: '', roles: ['trainer'] }) const [editMemberModal, setEditMemberModal] = useState(null) const [acceptJoinModal, setAcceptJoinModal] = useState(null) // Form state const [formData, setFormData] = useState({}) const isPlatformAdmin = user?.role === 'admin' || user?.role === 'superadmin' const isSuperAdmin = user?.role === 'superadmin' const clubAdminClubIds = new Set( activeClubMemberships(user?.clubs) .filter((c) => (c.roles || []).includes('club_admin')) .map((c) => c.id) ) const canManageClub = (clubId) => isPlatformAdmin || clubAdminClubIds.has(clubId) const canCreateClub = isPlatformAdmin const canManageOrgSomewhere = isPlatformAdmin || clubAdminClubIds.size > 0 const canCreateTrainingGroup = isPlatformAdmin || activeClubMemberships(user?.clubs).length > 0 const canEditGroup = (g) => isPlatformAdmin || clubAdminClubIds.has(g.club_id) || g.trainer_id === user?.id || (Array.isArray(g.co_trainer_ids) && g.co_trainer_ids.includes(user?.id)) const canDeleteGroup = (g) => isPlatformAdmin || clubAdminClubIds.has(g.club_id) useEffect(() => { loadData() }, []) const loadData = async () => { try { const [clubsData, divisionsData, groupsData] = await Promise.all([ api.listClubs(), api.listDivisions(), api.listTrainingGroups() ]) setClubs(clubsData) setDivisions(divisionsData) setGroups(groupsData) } catch (err) { console.error('Failed to load data:', err) alert('Fehler beim Laden: ' + err.message) } finally { setLoading(false) } } useEffect(() => { if (!clubs.length) { setMembersAdminClubId(null) return } const mcl = clubs.filter((c) => isPlatformAdmin || clubAdminClubIds.has(c.id)) if (!mcl.length) { setMembersAdminClubId(null) return } setMembersAdminClubId((prev) => prev != null && mcl.some((x) => x.id === prev) ? prev : mcl[0].id ) }, [clubs, isPlatformAdmin, user?.clubs]) useEffect(() => { if (activeTab !== 'members' || !membersAdminClubId || !canManageClub(membersAdminClubId)) return let cancelled = false setMembersAdminLoading(true) Promise.all([ api.listClubMembers(membersAdminClubId, { includeInactive: true }), api.listClubJoinRequests(membersAdminClubId), ]) .then(([m, j]) => { if (!cancelled) { setClubMembersAdmin(m) setJoinRequestsAdmin(j) } }) .catch((err) => { if (!cancelled) alert('Mitglieder/Anträge: ' + err.message) }) .finally(() => { if (!cancelled) setMembersAdminLoading(false) }) return () => { cancelled = true } }, [activeTab, membersAdminClubId]) useEffect(() => { if (!showModal || modalType !== 'group' || !formData.club_id) { setGroupMemberDirectory([]) return } let cancelled = false api .clubMembersDirectory(formData.club_id) .then((rows) => { if (!cancelled) setGroupMemberDirectory(rows) }) .catch(() => { if (!cancelled) setGroupMemberDirectory([]) }) return () => { cancelled = true } }, [showModal, modalType, formData.club_id]) useEffect(() => { if (!showAddMemberModal || !isPlatformAdmin) return api.listProfiles().then(setProfilesAdminList).catch(() => setProfilesAdminList([])) }, [showAddMemberModal, isPlatformAdmin]) const handleCreate = (type) => { setEditing(null) setModalType(type) if (type === 'club') { setFormData({ name: '', abbreviation: '', description: '', status: 'active', primary_admin_profile_id: user?.id ?? '', }) } else if (type === 'division') { setFormData({ club_id: '', name: '', focus_area: '' }) } else if (type === 'group') { setFormData({ club_id: '', division_id: '', name: '', focus: '', level: '', age_group: '', weekday: '', time_start: '', time_end: '', location: '', trainer_id: user?.id ?? '', co_trainer_ids: [], status: 'active' }) } setShowModal(true) } const manageableClubs = clubs.filter((c) => canManageClub(c.id)) const reloadMembersAdmin = async () => { if (!membersAdminClubId || !canManageClub(membersAdminClubId)) return try { const [m, j] = await Promise.all([ api.listClubMembers(membersAdminClubId, { includeInactive: true }), api.listClubJoinRequests(membersAdminClubId), ]) setClubMembersAdmin(m) setJoinRequestsAdmin(j) } catch (err) { console.error(err) } } const toggleMembersAdminClubAccess = async (m, activate) => { if (!membersAdminClubId || !canManageClub(membersAdminClubId)) return const st = activate ? 'active' : 'inactive' if ( !activate && !confirm( `Vereinszugang für „${m.name || m.email || '#' + m.profile_id}“ hier deaktivieren? Login bleibt möglich, Vereinsinhalte nicht — auch bei Super-Admins.`, ) ) { return } try { await api.updateClubMember(membersAdminClubId, m.profile_id, { roles: [...(m.roles || [])], status: st, }) await reloadMembersAdmin() } catch (err) { alert(err.message || String(err)) } } const handleEdit = (item, type) => { setEditing(item) setModalType(type) if (type === 'group') { const co = item.co_trainer_ids setFormData({ ...item, co_trainer_ids: Array.isArray(co) ? co : [], }) } else { setFormData({ ...item }) } setShowModal(true) } const handleDelete = async (item, type) => { const confirmMsg = { club: `Verein "${item.name}" wirklich löschen? Alle Sparten und Gruppen werden auch gelöscht!`, division: `Sparte "${item.name}" wirklich löschen?`, group: `Trainingsgruppe "${item.name}" wirklich löschen?` } if (!confirm(confirmMsg[type])) return try { if (type === 'club') await api.deleteClub(item.id) else if (type === 'division') await api.deleteDivision(item.id) else if (type === 'group') await api.deleteTrainingGroup(item.id) await loadData() } catch (err) { alert('Fehler beim Löschen: ' + err.message) } } const handleSubmit = async (e) => { e.preventDefault() try { if (modalType === 'club') { if (editing) { await api.updateClub(editing.id, formData) } else { const payload = { ...formData, primary_admin_profile_id: Number(formData.primary_admin_profile_id), } if (!payload.primary_admin_profile_id) { alert('Hauptverwalter (Profil-ID) ist Pflicht.') return } await api.createClub(payload) } } else if (modalType === 'division') { if (editing) { await api.updateDivision(editing.id, formData) } else { await api.createDivision(formData) } } else if (modalType === 'group') { const trainerRaw = formData.trainer_id const trainer_id = trainerRaw === '' || trainerRaw === undefined || trainerRaw === null ? null : Number(trainerRaw) const coRaw = formData.co_trainer_ids const co_trainer_ids = Array.isArray(coRaw) ? coRaw.map((x) => Number(x)).filter((n) => Number.isFinite(n)) : [] const payload = { ...formData, trainer_id: Number.isFinite(trainer_id) ? trainer_id : null, co_trainer_ids, } if (editing) { await api.updateTrainingGroup(editing.id, payload) } else { await api.createTrainingGroup(payload) } } setShowModal(false) await loadData() } catch (err) { alert('Fehler beim Speichern: ' + err.message) } } const updateFormField = (field, value) => { setFormData(prev => ({ ...prev, [field]: value })) } const clubTabItems = useMemo(() => { const ids = canManageOrgSomewhere ? ['clubs', 'divisions', 'groups', 'members'] : ['clubs', 'divisions', 'groups'] const labels = { clubs: 'Vereine', divisions: 'Sparten', groups: 'Trainingsgruppen', members: 'Mitglieder', } return ids.map((id) => ({ id, label: labels[id] })) }, [canManageOrgSomewhere]) if (loading) { return (

Laden...

) } return (

Vereinsverwaltung

Für die Trainingsplanung wird mindestens ein Verein und eine Trainingsgruppe gebraucht. Sparten sind optional — typische Eckdaten einer Gruppe (Wochentag, Zeit, Ort) kannst du schrittweise eintragen.

{/* Clubs Tab */} {activeTab === 'clubs' && ( <>

Vereine

{canCreateClub && ( )}
{clubs.length === 0 ? (

Noch kein Verein angelegt. {canCreateClub ? ' Nutze „+ Neuer Verein“ — ein Name reicht zum Start.' : ' Bitte einen Administrator oder Support um Anlage.'}

{canCreateClub && (

Danach im Tab Trainingsgruppen eine Gruppe diesem Verein zuordnen; Details sind optional.

)}
) : (
{clubs.map(club => (

{club.name} {club.abbreviation && ( ({club.abbreviation}) )}

{club.description && (

{club.description}

)} {club.status}
{canManageClub(club.id) && (
{isSuperAdmin && ( )}
)}
))}
)} )} {/* Divisions Tab */} {activeTab === 'divisions' && ( <>

Sparten

{canManageOrgSomewhere && ( )}
{divisions.length === 0 ? (

Keine Sparten gefunden

) : (
{divisions.map(division => (

{division.name}

Verein: {division.club_name}

{division.focus_area && ( {division.focus_area} )}
{canManageClub(division.club_id) && (
)}
))}
)} )} {/* Training Groups Tab */} {activeTab === 'groups' && ( <>

Trainingsgruppen

{canCreateTrainingGroup && ( )}
{groups.length === 0 ? (

Keine Trainingsgruppen gefunden

) : (
{groups.map(group => (

{group.name}

{group.club_name} {group.division_name && ` · ${group.division_name}`}

{group.weekday && group.time_start && (
📅 {group.weekday}, {group.time_start.slice(0,5)} - {group.time_end?.slice(0,5)}
)} {group.location &&
📍 {group.location}
} {group.trainer_name &&
👤 {group.trainer_name}
} {group.level &&
⭐ {group.level}
} {group.age_group &&
👶 {group.age_group}
}
{canEditGroup(group) && (
{canDeleteGroup(group) && ( )}
)}
))}
)} )} {activeTab === 'members' && canManageOrgSomewhere && ( <>

Mitglieder & Beitrittsanträge

{manageableClubs.length === 0 ? (

Keine Vereine, für die du Mitglieder verwalten darfst.

) : membersAdminLoading ? (

Laden…

) : ( <> {joinRequestsAdmin.length > 0 && (

Offene Beitrittsanträge

{joinRequestsAdmin.map((req) => (
{req.applicant_name || req.applicant_email || 'Nutzer'}
{req.applicant_email} · Profil #{req.profile_id}
{req.message ? (
{req.message}
) : null}
))}
)}

Mitglieder

{isPlatformAdmin ? (

Liste enthält aktive und deaktivierte Vereinszugänge. Deaktiviert gilt pro Verein (ohne Kontosperre) — auch für Super-Admins ohne aktive Mitgliedschaft in diesem Verein kein Zugriff auf dessen Vereinsinhalte. Wiederherstellen über die Schaltflächen oder Mitglied bearbeiten.

) : (

Deaktivierte Vereinszugänge sind hervorgehoben —{' '} Anmeldung bleibt möglich, Vereinsinhalte dieser Zuordnung nicht.

)} {clubMembersAdmin.length === 0 ? (

Noch keine Mitglieder erfasst.

) : (
{clubMembersAdmin.map((m) => { const memStatus = (m.status || 'active').toLowerCase() const inactiveRow = memStatus === 'inactive' const portalLabel = (m.portal_role || '').trim() return (
{m.name || m.email}
{m.email} · #{m.profile_id} {' · '} Vereinszugang:{' '} {inactiveRow ? 'deaktiviert' : 'aktiv'} {portalLabel ? ( · Portal: {portalLabel} ) : null}
Rollen: {(m.roles || []).join(', ') || '—'}
{m.profile_id !== user?.id ? ( inactiveRow ? ( ) : ( ) ) : null}
) })}
)}
)} )} {/* Modal */} {showModal && (

{editing ? (modalType === 'club' ? 'Verein bearbeiten' : modalType === 'division' ? 'Sparte bearbeiten' : 'Gruppe bearbeiten') : (modalType === 'club' ? 'Neuer Verein' : modalType === 'division' ? 'Neue Sparte' : 'Neue Gruppe') }

{/* Club Form */} {modalType === 'club' && ( <>
updateFormField('name', e.target.value)} required />
updateFormField('abbreviation', e.target.value)} />