Files
shadow-gunbuilder-ai-proto/app/beta/confirm/page.tsx
T

94 lines
2.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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<Status>("loading");
const [message, setMessage] = useState<string>("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) {
// Dont assume expired—just say it didnt 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 (
<main className="min-h-screen flex items-center justify-center bg-black text-white px-6">
<div className="max-w-md w-full text-center space-y-4">
{status === "loading" && (
<>
<h1 className="text-xl font-semibold">Confirming email</h1>
<p className="text-sm text-zinc-400">{message}</p>
</>
)}
{status === "success" && (
<>
<h1 className="text-xl font-semibold">Email confirmed </h1>
<p className="text-sm text-zinc-400">{message}</p>
</>
)}
{status === "error" && (
<>
<h1 className="text-xl font-semibold">Link expired or invalid</h1>
<p className="text-sm text-zinc-400">{message}</p>
</>
)}
</div>
</main>
);
}