/**
* Standalone invite acceptance page (not wrapped in admin Shell).
* Validates an invite token, then registers a passkey to complete signup.
*/
import { Button, Input, Loader } from "@cloudflare/kumo";
import { Link, useSearch } from "@tanstack/react-router";
import * as React from "react";
import { validateInviteToken, type InviteVerifyResult } from "../lib/api";
import { PasskeyRegistration } from "./auth/PasskeyRegistration";
import { LogoLockup } from "./Logo.js";
type InviteStep = "verify" | "register" | "error";
interface RegisterStepProps {
inviteData: InviteVerifyResult;
token: string;
}
function handleInviteSuccess() {
window.location.href = "/_emdash/admin";
}
function RegisterStep({ inviteData, token }: RegisterStepProps) {
const [name, setName] = React.useState("");
return (
);
}
interface ErrorStepProps {
message: string;
code?: string;
}
function ErrorStep({ message, code }: ErrorStepProps) {
return (
{code === "TOKEN_EXPIRED"
? "Invite expired"
: code === "INVALID_TOKEN"
? "Invalid invite link"
: code === "USER_EXISTS"
? "Account already exists"
: "Something went wrong"}
{message}
{code === "USER_EXISTS" ? (
) : (
<>
Please ask your administrator to send a new invite.
>
)}
);
}
export function InviteAcceptPage() {
const { token: urlToken } = useSearch({ strict: false });
const [step, setStep] = React.useState("verify");
const [error, setError] = React.useState();
const [errorCode, setErrorCode] = React.useState();
const [isLoading, setIsLoading] = React.useState(true);
const [inviteData, setInviteData] = React.useState(null);
const [token, setToken] = React.useState(null);
React.useEffect(() => {
if (!urlToken) {
setError("No invite token provided");
setStep("error");
setIsLoading(false);
return;
}
setToken(urlToken);
void verifyToken(urlToken);
}, [urlToken]);
const verifyToken = async (tokenToVerify: string) => {
setIsLoading(true);
setError(undefined);
setErrorCode(undefined);
try {
const result = await validateInviteToken(tokenToVerify);
setInviteData(result);
setStep("register");
} catch (err) {
const verifyError = err instanceof Error ? err : new Error(String(err));
const errorWithCode = verifyError as Error & { code?: string };
setError(verifyError.message);
setErrorCode(typeof errorWithCode.code === "string" ? errorWithCode.code : undefined);
setStep("error");
} finally {
setIsLoading(false);
}
};
if (isLoading) {
return (
);
}
return (
{step === "register" && "Accept Invite"}
{step === "error" && "Invite Error"}
{step === "register" && inviteData && token && (
)}
{step === "error" && (
)}
);
}
export default InviteAcceptPage;