// Approve link page (Jay, Oct 30 2026): #/admin/members?pending=, the link in the "new member waiting" admin // email and push. Admin > Members with that person highlighted at the top: handle, when they joined, paid status, and // big Approve / Decline buttons (Decline asks inline, no pop-up). Rules + copy: utils/pending-approve.js. Calls: // BBGroupMembers.fetchMembers / act(approve | reject), the same ones the Members list uses. No modals. (function () { const { useState, useEffect, useCallback } = React; function PendingApproveCard({ pendingId, apiBase, token, withCode, isAdmin, authed, navigate }) { const P = window.BBPendingApprove; const GM = window.BBGroupMembers; const C = P.COPY; const [data, setData] = useState(null); const [done, setDone] = useState(null); // {kind: "approved"|"declined", text} const [busy, setBusy] = useState(""); // "approve" | "reject" const [asking, setAsking] = useState(false); // inline Decline confirm open const [note, setNote] = useState(null); // {kind, text} inline after a failed action const [forbidden, setForbidden] = useState(false); const [handle, setHandle] = useState(""); const load = useCallback(() => { if (!GM || !isAdmin || !token) return Promise.resolve(); return GM.fetchMembers(apiBase, token, withCode).then((d) => { setData(d); const m = P.findMember(d, pendingId); if (m && m.handle) setHandle(m.handle); }); }, [apiBase, token, withCode, isAdmin, pendingId]); // eslint-disable-line react-hooks/exhaustive-deps useEffect(() => { setDone(null); setNote(null); setAsking(false); setData(null); load(); }, [load]); // The Members list below changes too (approve / reject there): re-read so this card never shows a stale row. useEffect(() => { const on = () => { load(); }; window.addEventListener("bb-members-changed", on); return () => window.removeEventListener("bb-members-changed", on); }, [load]); const state = P.stateFor({ authed, isAdmin, pendingId, data, done, forbidden }); const member = P.findMember(data, pendingId); const name = (member && member.handle) || handle || "This person"; const goMembers = (e) => { if (e) e.preventDefault(); navigate("/admin/members", { replace: true }); }; const membersLink = (label, testid) => ( {label} ); const act = async (action) => { setBusy(action); setNote(null); const r = await GM.act(apiBase, token, withCode, pendingId, action, false); setBusy(""); setAsking(false); const o = P.outcomeOf(action, r, name); if (o.kind === "approved" || o.kind === "declined") setDone(o); else if (o.kind === "not-admin") setForbidden(true); else if (o.kind !== "handled") setNote(o); await load(); try { window.dispatchEvent(new CustomEvent("bb-members-changed")); } catch (e) {} }; const shell = (kind, children) => (
{children}
); if (state === "none" || state === "signin") return null; if (state === "not-admin") return shell("not-admin", ( <>

{C.notAdmin}

{C.notAdminHint}

{ e.preventDefault(); navigate("/games", { replace: true }); }}>{C.gamesLink} )); if (state === "loading") return shell("loading",
{C.loading}
); if (state === "unavailable") return shell("unavailable",

{C.unavailable}

); if (state === "error") return shell("error", ( <>

{C.error}

)); if (state === "approved" || state === "declined") return shell(state, ( <>

{done.text}

{membersLink(C.backToMembers, "pending-approve-members")} )); if (state === "handled") return shell("handled", ( <>

{C.handled}

{C.handledHint}

{membersLink(C.membersLink, "pending-approve-members")} )); // state === "pending" const pay = P.payInfo(member); const joined = P.formatJoined(P.joinedAtOf(member)); return shell("pending", ( <>
{C.title}

{member.handle}

{pay.badge && {pay.badge === "paid" ? C.paid : C.unpaid}}
{joined &&
{joined}
}

{C.intro}

{pay.blocked && (

{C.needsPay}

)} {note && (

{note.text}

)} {!asking ? (
) : (

{C.declineAsk(member.handle)}

)} )); } // Admin > Members (#/admin/members). With ?pending=: the approve card on top, then the full Members list. function MembersPage({ pendingId, apiBase, token, withCode, isAdmin, authed, navigate }) { const List = window.GroupMembersSetting; return (
{pendingId && } {isAdmin && List && }
); } window.BBPendingApproveUI = { Card: PendingApproveCard, MembersPage }; })();