import { useEffect, useState } from 'react' import { Link, useNavigate, useParams } from 'react-router-dom' import { useAuth } from '../../contexts/AuthContext.jsx' import { api } from '../../api/client.js' import PlayerShell, { honeypotStyle } from './PlayerShell.jsx' import CreateGameAccountForm from '../../components/CreateGameAccountForm.jsx' // Public, token-gated invite acceptance (/invite/:token). Validates the invite, // lets the invitee set a username + password (their email + role are pre-assigned), // creates the account at that role and logs them in. For a player invite it then // offers the built-in "create game account" step before sending them to the portal. export default function AcceptInvite() { const { token } = useParams() const navigate = useNavigate() const { refresh } = useAuth() const [invite, setInvite] = useState(null) // { email, role } const [loadErr, setLoadErr] = useState('') const [signupOk, setSignupOk] = useState(false) const [username, setUsername] = useState('') const [password, setPassword] = useState('') const [company, setCompany] = useState('') // honeypot const [error, setError] = useState('') const [busy, setBusy] = useState(false) const [accepted, setAccepted] = useState(false) useEffect(() => { let active = true api.getInvite(token) .then((iv) => active && setInvite(iv)) .catch((err) => active && setLoadErr(err.status === 404 ? 'This invitation is invalid or has expired.' : 'Could not load this invitation.')) api.publicSettings() .then((s) => active && setSignupOk(Boolean(s?.gameAccountSignup))) .catch(() => {}) return () => { active = false } }, [token]) const dest = invite && invite.role === 'player' ? '/player' : '/admin' async function onSubmit(e) { e.preventDefault() setError('') if (username.trim().length < 3) return setError('Username must be at least 3 characters.') if (password.length < 8) return setError('Password must be at least 8 characters.') setBusy(true) try { await api.acceptInvite(token, username.trim(), password, { company }) await refresh() // pull the freshly-issued session into context setAccepted(true) // Staff invites are web-only — no game step; go straight in. if (!(invite.role === 'player' && signupOk)) navigate(dest, { replace: true }) } catch (err) { if (err.status === 409) setError('That username is already taken, or the invite was already used.') else if (err.status === 404) setError('This invitation is invalid or has expired.') else if (err.status === 400) setError(err.message || 'Please check your details and try again.') else setError('Could not accept the invitation right now.') setBusy(false) } } // ── Loading / invalid ───────────────────────────────────────────────────── if (loadErr) { return (

{loadErr}

Go to sign in

) } if (!invite) { return (
) } // ── Accepted: optional game-account step (player invites) ────────────────── if (accepted) { return (

Your account is ready. Create a game account now to play, or skip and do it later from your portal.

navigate('/player', { replace: true })} />

) } // ── Accept form ──────────────────────────────────────────────────────────── return (

You’ve been invited as {invite.role} {invite.email ? <> for {invite.email} : null}. Choose a username and password to finish.

{error &&

{error}

}
) }