"use client"; import { useEffect, useRef, useState } from "react"; import Link from "next/link"; import { useRouter, useSearchParams } from "next/navigation"; import { Logo } from "@/components/logo"; import { Notice } from "@/components/ui"; export function LoginView() { const sp = useSearchParams(); const router = useRouter(); const next = sp.get("next") && sp.get("next")!.startsWith("/") ? sp.get("next")! : "/membre"; const [step, setStep] = useState<"email" | "code">("email"); const [email, setEmail] = useState(""); const [accept, setAccept] = useState(false); const [challengeId, setChallengeId] = useState(null); const [code, setCode] = useState(""); const [err, setErr] = useState(null); const [busy, setBusy] = useState(false); const [cooldown, setCooldown] = useState(0); const [needTerms, setNeedTerms] = useState(false); const codeRef = useRef(null); useEffect(() => { if (cooldown <= 0) return; const t = setTimeout(() => setCooldown((c) => c - 1), 1000); return () => clearTimeout(t); }, [cooldown]); const request = async () => { setBusy(true); setErr(null); const r = await fetch("/api/auth/request-code", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ email }) }); const d = await r.json(); setBusy(false); if (r.status === 400) { setErr(d.error?.message ?? "Adresse invalide"); return; } if (r.status === 429) { setErr(d.message); setCooldown(d.retryAfterSec ?? 60); if (challengeId) setStep("code"); return; } setChallengeId(d.challengeId); setCooldown(d.retryAfterSec ?? 60); setStep("code"); setTimeout(() => codeRef.current?.focus(), 50); }; const verify = async (c = code) => { if (!challengeId || c.length !== 6) return; setBusy(true); setErr(null); const r = await fetch("/api/auth/verify-code", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ challengeId, code: c, acceptTerms: accept }) }); const d = await r.json(); setBusy(false); if (r.status === 409) { setNeedTerms(true); setErr(d.error?.message ?? "Acceptez les conditions puis confirmez le code."); return; } if (!r.ok) { setErr(d.error?.message ?? "Erreur"); if (["expired", "too_many_attempts", "not_found"].includes(d.error?.code)) { setStep("email"); setChallengeId(null); setCode(""); } return; } router.push(next); router.refresh(); }; return (

{step === "email" ? "Connexion ou création de compte" : "Entrez votre code"}

{step === "email" ? "Une seule identité pour chercher, publier, échanger. Sans mot de passe : un code à usage unique vous est envoyé par courriel." : <>Un code à 6 chiffres a été envoyé à {email} s'il s'agit d'une adresse valide. Valable 10 minutes.}

{step === "email" ? (
{ e.preventDefault(); request(); }} className="space-y-4"> {err && {err}}
) : (
{ e.preventDefault(); verify(); }} className="space-y-4"> {needTerms && } {err && {err}}
)}

Si vous n'avez rien demandé, ignorez le courriel : personne ne peut se connecter sans le code.

); }