import { createFileRoute } from "@tanstack/react-router"; import { useEffect, useState, type FormEvent } from "react"; import { Button } from "@executor-js/react/components/button"; import { Card, CardDescription, CardHeader, CardTitle } from "@executor-js/react/components/card"; import { Input } from "@executor-js/react/components/input"; import { Label } from "@executor-js/react/components/label"; import { authClient } from "../../auth-client"; export const Route = createFileRoute("/join/$code")({ component: JoinPage, }); // Public, chromeless account-creation page. Reached at /join/: the code // is the credential that lets a new person self-register. It rides on the // signup request body; the server's create gate validates + burns it and drops // the new user into the org as a member. The root renders this outside the // auth gate (an un-redeemed visitor has no session yet). function JoinPage() { const { code } = Route.useParams(); const [inviteState, setInviteState] = useState<"checking" | "valid" | "invalid">("checking"); const [name, setName] = useState(""); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [error, setError] = useState(null); const [busy, setBusy] = useState(false); useEffect(() => { let alive = true; setInviteState("checking"); void fetch(`/api/invite-status/${encodeURIComponent(code)}`, { credentials: "same-origin", }).then( async (response) => { const body = response.ok ? ((await response.json().then( (value) => value, () => ({}), )) as { valid?: boolean }) : {}; if (alive) setInviteState(body.valid === true ? "valid" : "invalid"); }, () => { if (alive) setInviteState("invalid"); }, ); return () => { alive = false; }; }, [code]); const submit = async (event: FormEvent) => { event.preventDefault(); setBusy(true); setError(null); // The Better Auth client forwards `inviteCode` (a non-schema field) onto the // signup body the create gate reads; same-origin, so the session cookie // sticks. Returns `{ error }` rather than throwing — no manual fetch. const result = await authClient.signUp.email({ name, email, password, inviteCode: code }); if (result.error) { setBusy(false); setError( result.error.message ?? "Could not create your account. Check your invite link and try again.", ); return; } window.location.href = "/"; }; if (inviteState === "checking") { return (
Loading…
); } if (inviteState === "invalid") { return (
Invite not valid This invite link is invalid or has expired. Ask the person who invited you for a new link.
); } return (

Join Executor

You've been invited — create your account.

setName((e.target as HTMLInputElement).value)} autoComplete="name" required />
setEmail((e.target as HTMLInputElement).value)} autoComplete="email" required />
setPassword((e.target as HTMLInputElement).value)} autoComplete="new-password" required minLength={8} />
{error &&

{error}

}
); }