"use client"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { useEffect, useRef, useState, type FormEvent } from "react"; import { EyeClosedIcon, EyeIcon } from "lucide-react"; import { REGEXP_ONLY_DIGITS } from "input-otp"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Field, FieldGroup, FieldLabel, FieldDescription } from "@/components/ui/field"; import { Input } from "@/components/ui/input"; import { InputOTP, InputOTPGroup, InputOTPSlot } from "@/components/ui/input-otp"; import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from "@/components/ui/input-group"; import { authClient } from "@/lib/auth/client"; declare global { interface Window { turnstile?: { render(container: HTMLElement, options: { sitekey: string; callback: (token: string) => void; "expired-callback": () => void; "error-callback": () => void; }): string; reset(widgetId: string): void; remove(widgetId: string): void; }; } } export function AccountForm({ mode, siteKey, nextPath, verified, verificationError, }: { mode: "login" | "register"; siteKey: string; nextPath: string; verified?: boolean; verificationError?: boolean; }) { const router = useRouter(); const register = mode === "register"; const [busy, setBusy] = useState(false); const [error, setError] = useState(""); const [pendingEmail, setPendingEmail] = useState(""); const [notice, setNotice] = useState(""); const [token, setToken] = useState(""); const [captchaError, setCaptchaError] = useState(""); const [captchaAttempt, setCaptchaAttempt] = useState(0); const [showPassword, setShowPassword] = useState(false); const [resendSeconds, setResendSeconds] = useState(0); const formRef = useRef(null); const container = useRef(null); const widget = useRef(null); const captcha = process.env.NODE_ENV !== "development"; useEffect(() => { if (!resendSeconds) return; const timer = window.setTimeout(() => setResendSeconds((seconds) => seconds - 1), 1000); return () => window.clearTimeout(timer); }, [resendSeconds]); useEffect(() => { if (!captcha || pendingEmail) return; let active = true; const script = document.createElement("script"); script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"; script.async = true; const failed = () => { if (!active) return; window.clearTimeout(timeout); setToken(""); setCaptchaError("โหลดการตรวจสอบความปลอดภัยไม่สำเร็จ กรุณาลองอีกครั้ง"); }; const timeout = window.setTimeout(failed, 15000); script.onerror = failed; script.onload = () => { if (!active) return; if (!container.current || !window.turnstile) return failed(); try { widget.current = window.turnstile.render(container.current, { sitekey: siteKey, callback: (value: string) => { if (!active) return; window.clearTimeout(timeout); setCaptchaError(""); setToken(value); }, "expired-callback": () => { if (!active) return; setToken(""); setCaptchaError("การตรวจสอบหมดอายุ กรุณาตรวจสอบอีกครั้ง"); }, "error-callback": failed, }); window.clearTimeout(timeout); } catch { failed(); } }; if (window.turnstile) script.onload(new Event("load")); else document.head.append(script); return () => { active = false; window.clearTimeout(timeout); script.remove(); if (widget.current) window.turnstile?.remove(widget.current); widget.current = null; }; }, [captcha, siteKey, pendingEmail, captchaAttempt]); async function sendRegistrationOtp() { if (!register || busy || (pendingEmail && resendSeconds > 0)) return; if (pendingEmail) { setBusy(true); setError(""); setNotice(""); try { const result = await authClient.emailOtp.sendVerificationOtp({ email: pendingEmail, type: "email-verification", }); if (result.error) throw new Error("ส่งรหัสใหม่ไม่สำเร็จ กรุณาลองอีกครั้ง"); setNotice("ส่งรหัสใหม่แล้ว กรุณาตรวจสอบอีเมลของคุณ"); setResendSeconds(60); } catch (cause) { setError(cause instanceof Error ? cause.message : "ส่งรหัสใหม่ไม่สำเร็จ"); } finally { setBusy(false); } return; } const form = formRef.current; if (!form?.reportValidity() || (captcha && !token)) return; setBusy(true); setError(""); setNotice(""); try { const fields = new FormData(form); const email = String(fields.get("email") ?? "").trim(); const name = String(fields.get("name") ?? "").trim(); const result = await authClient.signUp.email({ name, email, password: String(fields.get("password") ?? ""), fetchOptions: { headers: captcha ? { "x-captcha-response": token } : undefined }, }); if (result.error) throw new Error(result.error.message ?? "ส่งรหัสไม่สำเร็จ กรุณาลองอีกครั้ง"); setPendingEmail(email); setResendSeconds(60); setNotice("ส่งรหัส 6 หลักแล้ว กรุณาตรวจสอบอีเมลและกรอกรหัสภายใน 5 นาที"); } catch (cause) { setError(cause instanceof Error ? cause.message : "ลองอีกครั้ง"); setToken(""); if (widget.current) window.turnstile?.reset(widget.current); } finally { setBusy(false); } } async function submit(event: FormEvent) { event.preventDefault(); if (register && !pendingEmail) return sendRegistrationOtp(); if (busy || (!register && captcha && !token)) return; setBusy(true); setError(""); try { if (register) { const form = new FormData(event.currentTarget); const result = await authClient.emailOtp.verifyEmail({ email: pendingEmail, otp: String(form.get("otp") ?? "").trim(), }); if (result.error) throw new Error("รหัสไม่ถูกต้องหรือหมดอายุ กรุณาลองอีกครั้ง"); router.replace(`/auth/register?step=profile&next=${encodeURIComponent(nextPath)}`); router.refresh(); return; } const form = new FormData(event.currentTarget); const email = String(form.get("email") ?? "").trim(); const result = await authClient.signIn.email({ email, password: String(form.get("password") ?? ""), fetchOptions: { headers: captcha ? { "x-captcha-response": token } : undefined }, }); if (result.error?.code === "EMAIL_NOT_VERIFIED") { router.push(`/auth/check-email?email=${encodeURIComponent(email)}&next=${encodeURIComponent(nextPath)}`); return; } if (result.error) throw new Error(result.error.message ?? "เข้าสู่ระบบไม่สำเร็จ"); router.push(nextPath); router.refresh(); } catch (cause) { setError(cause instanceof Error ? cause.message : "ลองอีกครั้ง"); if (!register) { setToken(""); if (widget.current) window.turnstile?.reset(widget.current); } } finally { setBusy(false); } } return ( {register ? pendingEmail ? "ยืนยันอีเมล" : "สร้างบัญชี Buzz Guide" : "เข้าสู่ระบบ"} {nextPath.startsWith("/commission") && เข้าสู่ระบบเพื่อบันทึกคำขอ รับผล และพูดคุยกับทีมงาน} {register && ขั้นตอน {pendingEmail ? "2 จาก 3 - ยืนยันอีเมล" : "1 จาก 3 - ข้อมูลบัญชี"}} {verified && ยืนยันอีเมลแล้ว กรุณาเข้าสู่ระบบ} {verificationError && ลิงก์ยืนยันไม่ถูกต้องหรือหมดอายุ กรุณาขอลิงก์ใหม่} {notice && {notice}}
{!pendingEmail ? <> {register && ชื่อที่แสดง } อีเมล รหัสผ่าน setShowPassword((current) => !current)}> {showPassword ? {register && ใช้รหัสผ่านอย่างน้อย 6 ตัวอักษร} {captcha &&

ตรวจสอบความปลอดภัยก่อนดำเนินการ

{captchaError && {captchaError}} {captchaError && } } : <>

ส่งรหัสไปที่ {pendingEmail}

หากกรอกอีเมลผิด ให้เริ่มสมัครด้วยอีเมลที่ใช้งานได้ บัญชีเดิมจะยังไม่ยืนยันอีเมล

รหัสยืนยัน 6 หลัก {Array.from({ length: 6 }, (_, index) => )} รหัสมีอายุ 5 นาที หากไม่พบอีเมล ลองดูในโฟลเดอร์สแปม } {error && {error}} {register && pendingEmail && } {register ? :
} ); }