"use client"; import { useState, useActionState, startTransition } from "react"; import { useTranslations } from "next-intl"; import Container from "@/components/ui/Container"; import Input from "@/components/ui/Input"; import { LightButton } from "@/components/ui/buttons"; import { authClient } from "@/lib/auth-client"; import { signInAction, signUpAction, SignUpState } from "@/modules/auth"; interface AuthFormProps { redirectTo?: string; } export default function AuthForm({ redirectTo }: AuthFormProps) { const t = useTranslations("auth"); const [mode, setMode] = useState<'signin' | 'signup'>('signin'); const [clearSignIn, setClearSignIn] = useState(false); const [clearSignUp, setClearSignUp] = useState(false); const [signInState, signInActionForm, isSignInPending] = useActionState( async (prevState: SignUpState | undefined, formData: FormData) => { if (clearSignIn) { setClearSignIn(false); return undefined; } return signInAction(prevState || {}, formData); }, undefined ); const [signUpState, signUpActionForm, isSignUpPending] = useActionState( async (prevState: SignUpState | undefined, formData: FormData) => { if (clearSignUp) { setClearSignUp(false); return undefined; } return signUpAction(prevState || {}, formData); }, undefined ); const [errors, setErrors] = useState>({}); const validateForm = (formData: FormData): boolean => { const newErrors: Record = {}; const email = formData.get("email") as string; const password = formData.get("password") as string; const name = formData.get("name") as string; const confirmPassword = formData.get("confirmPassword") as string; if (!email) { newErrors.email = t("emailRequired"); } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { newErrors.email = t("invalidEmail"); } if (!password) { newErrors.password = t("passwordRequired"); } else if (password.length < 8) { newErrors.password = t("passwordTooShort"); } if (mode === 'signup') { if (!name) { newErrors.name = t("nameRequired"); } if (!confirmPassword) { newErrors.confirmPassword = t("confirmPasswordRequired"); } else if (password !== confirmPassword) { newErrors.confirmPassword = t("passwordsNotMatch"); } } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const handleFormSubmit = (e: React.FormEvent) => { e.preventDefault(); const formData = new FormData(e.currentTarget); // 基本客户端验证 if (!validateForm(formData)) { return; } // 添加 redirectTo 到 formData if (redirectTo) { formData.append("redirectTo", redirectTo); } // 使用 startTransition 包装 action 调用 startTransition(() => { // 根据模式调用相应的 action if (mode === 'signin') { signInActionForm(formData); } else { signUpActionForm(formData); } }); }; const handleGitHubSignIn = async () => { await authClient.signIn.social({ provider: "github", callbackURL: redirectTo || "/" }); }; const currentError = mode === 'signin' ? signInState : signUpState; return (
{/* 页面标题 */}

{t(mode === 'signin' ? 'signIn' : 'signUp')}

{/* 服务器端错误提示 */} {currentError?.message && (
{currentError.message}
)} {/* 登录/注册表单 */}
{/* 用户名输入(仅注册模式显示) */} {mode === 'signup' && (
{/* 客户端验证错误 */} {errors.name && (

{errors.name}

)} {/* 服务器端验证错误 */} {currentError?.errors?.username && (

{currentError.errors.username[0]}

)}
)} {/* 邮箱输入 */}
{errors.email && (

{errors.email}

)} {currentError?.errors?.email && (

{currentError.errors.email[0]}

)}
{/* 密码输入 */}
{errors.password && (

{errors.password}

)} {currentError?.errors?.password && (

{currentError.errors.password[0]}

)}
{/* 确认密码输入(仅注册模式显示) */} {mode === 'signup' && (
{errors.confirmPassword && (

{errors.confirmPassword}

)}
)} {/* 提交按钮 */} {isSignInPending || isSignUpPending ? t("loading") : t(mode === 'signin' ? 'signInButton' : 'signUpButton') }
{/* 第三方登录区域 */}
{/* 分隔线 */}
{/* GitHub 登录按钮 */} {t(mode === 'signin' ? 'signInWithGitHub' : 'signUpWithGitHub')}
{/* 模式切换链接 */}
); }