"use client"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { useEffect, useRef, useState, type FormEvent } from "react"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Field, FieldGroup, FieldLabel } from "@/components/ui/field"; import { Input } from "@/components/ui/input"; import { authClient } from "@/lib/auth/client"; import { ProfileImageInput } from "./profile-image-input"; 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; }; } } export function AccountForm({ mode, siteKey, nextPath, verified, verificationError, oidcEnabled, oidcOnly, oidcError, }: { mode: "login" | "register"; siteKey: string; nextPath: string; verified?: boolean; verificationError?: boolean; oidcEnabled?: boolean; oidcOnly?: boolean; oidcError?: string; }) { const router = useRouter(); const register = mode === "register"; const [busy, setBusy] = useState(false); const [error, setError] = useState(""); const [pendingEmail, setPendingEmail] = useState(""); const [pendingName, setPendingName] = useState(""); const [notice, setNotice] = useState(""); const [token, setToken] = useState(""); const [profileImage, setProfileImage] = useState(null); const [profilePreview, setProfilePreview] = useState(null); const formRef = useRef(null); const container = useRef(null); const widget = useRef(null); const captcha = !oidcOnly && process.env.NODE_ENV !== "development"; useEffect(() => () => { if (profilePreview) URL.revokeObjectURL(profilePreview); }, [profilePreview]); useEffect(() => { if (!captcha) return; const script = document.createElement("script"); script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"; script.async = true; script.onload = () => { if (container.current && window.turnstile && !widget.current) widget.current = window.turnstile.render(container.current, { sitekey: siteKey, callback: (value: string) => setToken(value), "expired-callback": () => setToken(""), "error-callback": () => setToken(""), }); }; document.head.append(script); return () => { script.remove(); }; }, [captcha, siteKey]); async function sendRegistrationOtp() { if (!register || busy) 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("ส่งรหัสใหม่แล้ว กรุณาตรวจสอบอีเมลของคุณ"); } 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); setPendingName(name); 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("รหัสไม่ถูกต้องหรือหมดอายุ กรุณาลองอีกครั้ง"); if (profileImage) { const profile = new FormData(); profile.set("name", pendingName); profile.set("image", profileImage); const response = await fetch("/api/profile", { method: "POST", body: profile }); if (!response.ok) { router.replace(`/profile?setup=1&upload=failed&next=${encodeURIComponent(nextPath)}`); router.refresh(); return; } } router.replace(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); } } async function signInWithSudloh() { if (busy) return; setBusy(true); setError(""); try { const errorCallbackURL = `/auth/login?next=${encodeURIComponent(nextPath)}&sudloh=1`; const result = await authClient.signIn.social({ provider: "sudloh", callbackURL: nextPath, errorCallbackURL, }); if (result.error) throw new Error("เริ่มเข้าสู่ระบบด้วย Sudloh ไม่สำเร็จ กรุณาลองอีกครั้ง"); } catch { setError("เริ่มเข้าสู่ระบบด้วย Sudloh ไม่สำเร็จ กรุณาลองอีกครั้ง"); setBusy(false); } } return ( {register ? "สมัครสมาชิก" : "เข้าสู่ระบบ"} {oidcEnabled && } {oidcError && {oidcError === "account_not_linked" ? oidcOnly ? "บัญชีนี้ยังไม่เชื่อมต่อกับ Buzz Guide กรุณาติดต่อผู้ดูแลเพื่อยืนยันตัวตน" : "มีบัญชี Buzz Guide ที่ใช้อีเมลนี้แล้ว กรุณาเข้าสู่ระบบด้วยอีเมลและเชื่อมต่อ Sudloh จากหน้าโปรไฟล์" : "เข้าสู่ระบบด้วย Sudloh ไม่สำเร็จ กรุณาลองอีกครั้ง"} } {!oidcOnly && <> {verified && ยืนยันอีเมลแล้ว กรุณาเข้าสู่ระบบ} {verificationError && ลิงก์ยืนยันไม่ถูกต้องหรือหมดอายุ กรุณาขอลิงก์ใหม่} {notice && {notice}} {register && !pendingEmail &&

กรอกชื่อ อีเมล และรหัสผ่าน แล้วกดส่ง OTP

}
{register && ( ชื่อที่แสดง )} {register && รูปโปรไฟล์ (ไม่บังคับ)
{profilePreview && } ? { setProfileImage(cropped); setProfilePreview(URL.createObjectURL(cropped)); setError(""); }} onError={setError} />
} อีเมล
{register && }
รหัสผ่าน {register && รหัส OTP } {captcha && !pendingEmail &&
} {register ? :
} {error && ( {error} )} } ); }