"use client"; import { useCallback, useEffect, useState } from "react"; import { createClient } from "@/lib/supabase/client"; import { track } from "@/lib/analytics"; type AuthStep = "pick" | "email-input" | "otp-verify"; export function AuthModal({ onClose, onSuccess, }: { onClose: () => void; onSuccess: () => void; }) { const [step, setStep] = useState("pick"); const [email, setEmail] = useState(""); const [otp, setOtp] = useState(""); const [loading, setLoading] = useState(false); const [error, setError] = useState(""); useEffect(() => { function onKeyDown(e: KeyboardEvent) { if (e.key === "Escape") onClose(); } window.addEventListener("keydown", onKeyDown); return () => window.removeEventListener("keydown", onKeyDown); }, [onClose]); const handleOAuth = useCallback( async (provider: "google" | "github") => { setLoading(true); setError(""); const supabase = createClient(); const { error: oauthError } = await supabase.auth.signInWithOAuth({ provider, options: { redirectTo: `${window.location.origin}/auth/callback?next=${encodeURIComponent(window.location.pathname + window.location.search)}`, }, }); if (oauthError) { setError(oauthError.message); setLoading(false); } }, [], ); const handleSendOtp = useCallback(async () => { const trimmed = email.trim(); if (!trimmed) return; setLoading(true); setError(""); const supabase = createClient(); const { error: otpError } = await supabase.auth.signInWithOtp({ email: trimmed, }); setLoading(false); if (otpError) { setError(otpError.message); } else { setStep("otp-verify"); } }, [email]); const handleVerifyOtp = useCallback(async () => { const trimmedOtp = otp.trim(); if (!trimmedOtp) return; setLoading(true); setError(""); const supabase = createClient(); const { error: verifyError } = await supabase.auth.verifyOtp({ email: email.trim(), token: trimmedOtp, type: "email", }); setLoading(false); if (verifyError) { setError(verifyError.message); } else { track("login_success", { provider: "email" }); onSuccess(); } }, [email, otp, onSuccess]); return (
e.stopPropagation()} style={{ background: "rgba(14, 10, 6, 0.92)", border: "1.5px solid rgba(175, 138, 72, 0.72)", borderRadius: "8px", backdropFilter: "blur(14px)", WebkitBackdropFilter: "blur(14px)", boxShadow: "0 10px 42px rgba(0,0,0,0.62), inset 0 1px 0 rgba(200,165,90,0.12)", }} role="dialog" aria-modal="true" aria-label="登录" > {/* header */}
{step === "pick" && "登录以继续"} {step === "email-input" && "邮箱登录"} {step === "otp-verify" && "验证码"}
{error && (

{error}

)} {step === "pick" && ( <>
)} {step === "email-input" && ( <> setEmail(e.target.value)} onKeyDown={(e) => e.key === "Enter" && handleSendOtp()} placeholder="your@email.com" autoFocus className="w-full rounded-md border border-cream-50/15 bg-cream-50/[0.06] px-3.5 py-2.5 text-[13px] text-cream-50/90 placeholder:text-cream-50/30 outline-none focus:border-[rgba(175,138,72,0.6)]" /> )} {step === "otp-verify" && ( <>

验证码已发送至 {email.trim()}

setOtp(e.target.value.replace(/\D/g, ""))} onKeyDown={(e) => e.key === "Enter" && handleVerifyOtp()} placeholder="6 位验证码" autoFocus className="w-full rounded-md border border-cream-50/15 bg-cream-50/[0.06] px-3.5 py-2.5 text-center text-[16px] tracking-[0.35em] text-cream-50/90 placeholder:text-cream-50/30 placeholder:tracking-normal outline-none focus:border-[rgba(175,138,72,0.6)]" /> )}
); }