'use client'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import { DEFAULT_CLUB_RANKS, fetchClubMembers, sanitizePortalError, updateMemberRank } from '../../lib/club-data'; import { fetchMemberRoster, removeClubMember } from '../../lib/club-community'; import type { FounderClubContext } from '../../lib/club-data'; import type { ClubMember } from './types'; import { CommunityDialog, communityButton, communityInput } from './community-dialog'; export function FounderMembers({ context }: { context: FounderClubContext }) { const [members, setMembers] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [hasMore, setHasMore] = useState(false); const [loadingMore, setLoadingMore] = useState(false); const [notice, setNotice] = useState(''); const [search, setSearch] = useState(''); const [filter, setFilter] = useState('all'); const [selection, setSelection] = useState<{ member: ClubMember; action: 'rank' | 'remove' } | null>(null); const [rank, setRank] = useState(''); const [busy, setBusy] = useState(false); const busyRef = useRef(false); const [actionError, setActionError] = useState(null); const load = useCallback(async () => { setLoading(true); setError(null); try { const roster = context.isDemoFixture ? await fetchClubMembers(context.club.id, context.currentUser.uid, { allowDemoPreview: true }) : await fetchMemberRoster(context.club.id); setMembers(roster); setHasMore(!context.isDemoFixture && roster.length === 50); } catch (err) { setError(sanitizePortalError(err, 'Members could not be loaded.')); } finally { setLoading(false); } }, [context]); useEffect(() => { void load(); }, [load]); async function loadMore() { if (loadingMore || !hasMore) return; setLoadingMore(true); setError(null); try { const roster = await fetchMemberRoster(context.club.id, members[members.length - 1]?.uid); setMembers((current) => [...current, ...roster.filter((member) => !current.some((item) => item.uid === member.uid))]); setHasMore(roster.length === 50); } catch (err) { setError(sanitizePortalError(err, 'More members could not be loaded.')); } finally { setLoadingMore(false); } } const term = search.trim().toLowerCase(); const visible = members.filter((member) => (filter === 'all' || member.role === filter) && [member.displayName, member.handle, member.displayRank, member.uid].some((value) => value?.toLowerCase().includes(term))); function canRemove(member: ClubMember) { return !context.isDemoFixture && member.uid !== context.currentUser.uid && member.uid !== context.club.ownerUid && member.role !== 'owner' && (context.currentUser.role === 'owner' || member.role !== 'admin'); } async function confirm(event: React.FormEvent) { event.preventDefault(); if (!selection || busyRef.current || context.isDemoFixture) return; busyRef.current = true; setBusy(true); setActionError(null); try { if (selection.action === 'rank') { await updateMemberRank(context.club.id, selection.member.uid, rank); setMembers((current) => current.map((member) => member.uid === selection.member.uid ? { ...member, displayRank: rank.trim() } : member)); setNotice('Display rank updated. Administrative access is unchanged.'); } else { await removeClubMember(context.club.id, selection.member.uid); setMembers((current) => current.filter((member) => member.uid !== selection.member.uid)); setNotice('Member removed from the club. Membership history is preserved.'); } setSelection(null); } catch (err) { setActionError(sanitizePortalError(err, 'The member could not be updated. Refresh the roster and try again.')); } finally { busyRef.current = false; setBusy(false); } } return (

CLUB MANAGEMENT / ROSTER

Members

Your active roster, officer ranks, and membership controls.

{context.isDemoFixture &&

Development preview • changes are disabled.

}
{([['Members loaded', members.length], ['Administrators loaded', members.filter((m) => m.role === 'admin' || m.role === 'owner').length], ['Showing', visible.length]] as const).map(([label, count]) => (

{label}

{loading || error ? '—' : count}

))}
{hasMore &&

Search and counts cover loaded members. Load more to include the rest of your roster.

}

{notice}

{loading ?

Loading members…

: error ?

{error}

: visible.length === 0 ?

{members.length ? 'No members match your search.' : 'There are no active members to display.'}

:
{visible.map((member) => )}
RiderDisplay rankAccessActions

{member.displayName || 'Rider'}

{member.handle || member.uid}

{member.displayRank || 'Member'} {member.role}
{canRemove(member) && }
}

Display ranks are club titles. Owner and Admin roles control administrative access.

{selection && setSelection(null)}>

{selection.member.displayName || selection.member.uid}

{selection.action === 'rank' ? <> {DEFAULT_CLUB_RANKS.map((item) =>

This title does not grant administrative permissions.

:

This removes club access. The rider account and membership history will remain.

} {actionError &&

{actionError}

}
}
); }