"use client" import type React from "react" import { useEffect, useRef, useState } from "react" import { useRouter } from "next/navigation" import { Mail, ArrowLeft, CheckCircle2, KeyRound } from "lucide-react" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" const RESEND_COOLDOWN = 30 // seconds function isValidEmail(value: string) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value.trim()) } export function LoginForm() { const [mode, setMode] = useState<"email" | "code">("email") const [email, setEmail] = useState("") const [touched, setTouched] = useState(false) const [sent, setSent] = useState(false) const [sending, setSending] = useState(false) const [cooldown, setCooldown] = useState(0) const [error, setError] = useState(null) const emailValid = isValidEmail(email) const showError = touched && email.length > 0 && !emailValid // Countdown timer for the resend cooldown. useEffect(() => { if (cooldown <= 0) return const id = setInterval(() => { setCooldown((c) => (c <= 1 ? 0 : c - 1)) }, 1000) return () => clearInterval(id) }, [cooldown]) async function sendLink() { setSending(true) setError(null) try { // Alltid samme suksess-respons uansett om e-posten finnes (anti- // enumerering, se ADR-009) -- kun nettverks-/serverfeil havner i catch. const res = await fetch("/auth/request-link", { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify({ email: email.trim(), locale: "nb" }), }) if (!res.ok) throw new Error(`request-link: ${res.status}`) setSent(true) setCooldown(RESEND_COOLDOWN) } catch { setError("Klarte ikke å sende lenken. Sjekk tilkoblingen og prøv igjen.") } finally { setSending(false) } } function handleSubmit(e: React.FormEvent) { e.preventDefault() setTouched(true) if (!emailValid || sending) return void sendLink() } function handleResend() { if (cooldown > 0 || sending) return void sendLink() } function handleReset() { setSent(false) setTouched(false) } return (
{sent ? ( ) : mode === "code" ? ( setMode("email")} /> ) : (
{showError && (

Skriv inn en gyldig e-postadresse.

)}
{error && (

{error}

)}

Ingen passord. Vi sender deg en sikker lenke på e-post.

)}
) } // --- Kode-innlogging (ADR-020): tar deg rett til en turnering via en kort, // menneske-skrivbar kode -- FØR innlogging, og uansett turneringens // synlighet (koden ER selve invitasjonen, se ADR-020 Beslutning A). --------- function JoinByCode({ onBack }: { onBack: () => void }) { const router = useRouter() const [code, setCode] = useState("") const [loading, setLoading] = useState(false) const [error, setError] = useState(null) async function handleSubmit(e: React.FormEvent) { e.preventDefault() const trimmed = code.trim() if (!trimmed || loading) return setLoading(true) setError(null) try { const res = await fetch(`/public/tournaments/by-code/${encodeURIComponent(trimmed)}`) if (!res.ok) { const body = await res.json().catch(() => null) setError(body?.detail?.message ?? "Fant ingen turnering med denne koden.") return } const data: { tournament_id: string } = await res.json() router.push(`/t/${data.tournament_id}?code=${encodeURIComponent(trimmed)}`) } catch { setError("Klarte ikke å slå opp koden. Sjekk tilkoblingen og prøv igjen.") } finally { setLoading(false) } } return (
{error && (

{error}

)}

Fått en kode muntlig eller på en lapp? Skriv den inn her — du trenger ikke logge inn for å se turneringen eller melde deg på.

) } function ConfirmationState({ email, cooldown, sending, error, onResend, onReset, }: { email: string cooldown: number sending: boolean error: string | null onResend: () => void onReset: () => void }) { const liveRef = useRef(null) useEffect(() => { liveRef.current?.focus() }, []) return (

Sjekk innboksen din

Vi har sendt en innloggingslenke til{" "} {email}. Åpne den på denne enheten for å logge inn.

{error && (

{error}

)}
{cooldown > 0 ? (

Fikk du ingen e-post? Send på nytt om{" "} {cooldown}s

) : ( )}
) }