100 lines
3.6 KiB
TypeScript
100 lines
3.6 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useState } from "react"
|
|
import Link from "next/link"
|
|
import { useSearchParams } from "next/navigation"
|
|
import { CheckCircle2, XCircle } from "lucide-react"
|
|
|
|
type Status = "verifying" | "success" | "error"
|
|
|
|
export function VerifyEmailForm() {
|
|
const searchParams = useSearchParams()
|
|
const token = searchParams.get("token")
|
|
const [status, setStatus] = useState<Status>(token ? "verifying" : "error")
|
|
const [email, setEmail] = useState<string | null>(null)
|
|
const [error, setError] = useState<string | null>(null)
|
|
|
|
useEffect(() => {
|
|
if (!token) {
|
|
setError("Mangler kode i lenken.")
|
|
return
|
|
}
|
|
async function verify() {
|
|
try {
|
|
const res = await fetch("/auth/profile/email/confirm", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
credentials: "include",
|
|
body: JSON.stringify({ token }),
|
|
})
|
|
if (!res.ok) {
|
|
const body = await res.json().catch(() => null)
|
|
throw new Error(body?.detail?.message ?? "Lenken er ugyldig eller utløpt.")
|
|
}
|
|
const result: { email: string } = await res.json()
|
|
setEmail(result.email)
|
|
setStatus("success")
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : "Noe gikk galt. Prøv igjen.")
|
|
setStatus("error")
|
|
}
|
|
}
|
|
void verify()
|
|
}, [token])
|
|
|
|
if (status === "verifying") {
|
|
return (
|
|
<div className="flex w-full flex-col items-center gap-4 rounded-3xl border border-border bg-card p-8 text-center shadow-lg shadow-black/5">
|
|
<div
|
|
aria-hidden="true"
|
|
className="size-10 animate-spin rounded-full border-4 border-primary/20 border-t-primary"
|
|
/>
|
|
<p className="text-sm text-muted-foreground" role="status">
|
|
Bekrefter ny e-postadresse …
|
|
</p>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (status === "success") {
|
|
return (
|
|
<div className="flex w-full flex-col items-center gap-5 rounded-3xl border border-border bg-card p-6 text-center shadow-lg shadow-black/5 sm:p-8">
|
|
<div className="flex size-16 items-center justify-center rounded-2xl bg-primary/15">
|
|
<CheckCircle2 aria-hidden="true" className="size-8 text-primary" />
|
|
</div>
|
|
<div className="flex flex-col gap-2">
|
|
<h2 className="text-2xl font-extrabold tracking-tight text-balance">E-post oppdatert</h2>
|
|
{email && (
|
|
<p className="text-sm leading-relaxed text-muted-foreground text-pretty">
|
|
Du logger nå inn med <span className="font-semibold text-foreground">{email}</span>.
|
|
</p>
|
|
)}
|
|
</div>
|
|
<Link
|
|
href="/account"
|
|
className="text-sm font-semibold text-primary transition-colors hover:text-primary/80"
|
|
>
|
|
Til kontoinnstillinger
|
|
</Link>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="flex w-full flex-col items-center gap-5 rounded-3xl border border-border bg-card p-6 text-center shadow-lg shadow-black/5 sm:p-8">
|
|
<div className="flex size-16 items-center justify-center rounded-2xl bg-destructive/15">
|
|
<XCircle aria-hidden="true" className="size-8 text-destructive" />
|
|
</div>
|
|
<div className="flex flex-col gap-2">
|
|
<h2 className="text-2xl font-extrabold tracking-tight text-balance">Kunne ikke bekrefte</h2>
|
|
<p className="text-sm leading-relaxed text-muted-foreground text-pretty">{error}</p>
|
|
</div>
|
|
<Link
|
|
href="/account"
|
|
className="text-sm font-semibold text-primary transition-colors hover:text-primary/80"
|
|
>
|
|
Til kontoinnstillinger
|
|
</Link>
|
|
</div>
|
|
)
|
|
}
|