| 1 | "use client"; |
| 2 | |
| 3 | import { useEffect, useState } from "react"; |
| 4 | import Link from "next/link"; |
| 5 | import { useRouter } from "next/navigation"; |
| 6 | import { authApi } from "../lib/portfolio-api"; |
| 7 | |
| 8 | type VerificationState = "VERIFYING" | "SUCCESS" | "INVALID"; |
| 9 | |
| 10 | export default function VerifyEmailPage() { |
| 11 | const router = useRouter(); |
| 12 | const [state, setState] = useState<VerificationState>("VERIFYING"); |
| 13 | const [email, setEmail] = useState(""); |
| 14 | const [resendMessage, setResendMessage] = useState(""); |
| 15 | |
| 16 | useEffect(() => { |
| 17 | const token = new URLSearchParams(window.location.search).get("token"); |
| 18 | if (!token) { |
| 19 | queueMicrotask(() => setState("INVALID")); |
| 20 | return; |
| 21 | } |
| 22 | |
| 23 | let active = true; |
| 24 | let redirectTimer: number | undefined; |
| 25 | void authApi.verifyEmail(token) |
| 26 | .then(() => { |
| 27 | if (!active) return; |
| 28 | setState("SUCCESS"); |
| 29 | redirectTimer = window.setTimeout(() => router.push("/"), 1500); |
| 30 | }) |
| 31 | .catch(() => { |
| 32 | if (active) setState("INVALID"); |
| 33 | }); |
| 34 | |
| 35 | return () => { |
| 36 | active = false; |
| 37 | if (redirectTimer) window.clearTimeout(redirectTimer); |
| 38 | }; |
| 39 | }, [router]); |
| 40 | |
| 41 | if (state === "SUCCESS") { |
| 42 | return ( |
| 43 | <main className="signin-shell"> |
| 44 | <section className="signin-panel"> |
| 45 | <h1>Email verified</h1> |
| 46 | <p>Email verified successfully.</p> |
| 47 | <p>Redirecting you to sign in...</p> |
| 48 | <Link className="button" href="/">Sign in now</Link> |
| 49 | </section> |
| 50 | </main> |
| 51 | ); |
| 52 | } |
| 53 | |
| 54 | if (state === "INVALID") { |
| 55 | return ( |
| 56 | <main className="signin-shell"> |
| 57 | <section className="signin-panel"> |
| 58 | <h1>Verification link expired</h1> |
| 59 | <p>This verification link is invalid or expired.</p> |
| 60 | <form className="signin-actions" onSubmit={(event) => { |
| 61 | event.preventDefault(); |
| 62 | void authApi.resendVerification(email).then((result) => setResendMessage(result.message)); |
| 63 | }}> |
| 64 | <input type="email" value={email} onChange={(event) => setEmail(event.target.value)} required placeholder="Email" /> |
| 65 | <button type="submit">Resend verification email</button> |
| 66 | </form> |
| 67 | {resendMessage ? <p role="status">{resendMessage}</p> : null} |
| 68 | <Link href="/">Back to sign in</Link> |
| 69 | </section> |
| 70 | </main> |
| 71 | ); |
| 72 | } |
| 73 | |
| 74 | return ( |
| 75 | <main className="signin-shell"> |
| 76 | <section className="signin-panel"> |
| 77 | <h1>Verifying your email...</h1> |
| 78 | </section> |
| 79 | </main> |
| 80 | ); |
| 81 | } |