// app/beta/confirm/page.tsx "use client"; import { useEffect, useRef, useState } from "react"; import { useSearchParams } from "next/navigation"; const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:8080"; type Status = "loading" | "success" | "error"; export default function BetaConfirmPage() { const searchParams = useSearchParams(); const token = searchParams.get("token"); const [status, setStatus] = useState("loading"); const [message, setMessage] = useState("Please hang tight."); // Prevent double-fire in React Strict Mode (dev) const ran = useRef(false); useEffect(() => { if (!token) { setStatus("error"); setMessage("Missing token."); return; } // StrictMode can run effects twice in dev; bail out after first if (ran.current) return; ran.current = true; const controller = new AbortController(); (async () => { try { setStatus("loading"); setMessage("Confirming your email…"); const res = await fetch(`${API_BASE_URL}/api/auth/beta/confirm`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ token }), signal: controller.signal, }); // If request was aborted, do nothing if (controller.signal.aborted) return; if (!res.ok) { // Don’t assume expired—just say it didn’t work throw new Error("Confirm failed"); } setStatus("success"); setMessage("Check your inbox for your secure sign-in link."); } catch (err) { if (controller.signal.aborted) return; setStatus("error"); setMessage("This link is invalid or expired. Please request a new one."); } })(); return () => controller.abort(); }, [token]); return (
{status === "loading" && ( <>

Confirming email…

{message}

)} {status === "success" && ( <>

Email confirmed ✅

{message}

)} {status === "error" && ( <>

Link expired or invalid

{message}

)}
); }