import { createFileRoute } from "@tanstack/react-router"; import { useState, type FormEvent } from "react"; import { Button } from "@executor-js/react/components/button"; 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 [name, setName] = useState(""); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [error, setError] = useState(null); const [busy, setBusy] = useState(false); 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 = "/"; }; 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}

}
); }