This commit is contained in:
@@ -108,7 +108,8 @@
|
|||||||
"nameRequired": "Please enter your name",
|
"nameRequired": "Please enter your name",
|
||||||
"emailRequired": "Please enter your email",
|
"emailRequired": "Please enter your email",
|
||||||
"passwordRequired": "Please enter your password",
|
"passwordRequired": "Please enter your password",
|
||||||
"confirmPasswordRequired": "Please confirm your password"
|
"confirmPasswordRequired": "Please confirm your password",
|
||||||
|
"loading": "Loading..."
|
||||||
},
|
},
|
||||||
"memorize": {
|
"memorize": {
|
||||||
"folder_selector": {
|
"folder_selector": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useActionState } from "react";
|
import { useState, useActionState, startTransition } from "react";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { signInAction, signUpAction, SignUpState } from "@/lib/actions/auth";
|
import { signInAction, signUpAction, SignUpState } from "@/lib/actions/auth";
|
||||||
import Container from "@/components/ui/Container";
|
import Container from "@/components/ui/Container";
|
||||||
@@ -16,13 +16,27 @@ interface AuthFormProps {
|
|||||||
export default function AuthForm({ redirectTo }: AuthFormProps) {
|
export default function AuthForm({ redirectTo }: AuthFormProps) {
|
||||||
const t = useTranslations("auth");
|
const t = useTranslations("auth");
|
||||||
const [mode, setMode] = useState<'signin' | 'signup'>('signin');
|
const [mode, setMode] = useState<'signin' | 'signup'>('signin');
|
||||||
|
const [clearSignIn, setClearSignIn] = useState(false);
|
||||||
|
const [clearSignUp, setClearSignUp] = useState(false);
|
||||||
|
|
||||||
const [signInState, signInActionForm, isSignInPending] = useActionState(
|
const [signInState, signInActionForm, isSignInPending] = useActionState(
|
||||||
async (prevState: SignUpState | undefined, formData: FormData) => signInAction(prevState || {}, formData),
|
async (prevState: SignUpState | undefined, formData: FormData) => {
|
||||||
|
if (clearSignIn) {
|
||||||
|
setClearSignIn(false);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return signInAction(prevState || {}, formData);
|
||||||
|
},
|
||||||
undefined
|
undefined
|
||||||
);
|
);
|
||||||
const [signUpState, signUpActionForm, isSignUpPending] = useActionState(
|
const [signUpState, signUpActionForm, isSignUpPending] = useActionState(
|
||||||
async (prevState: SignUpState | undefined, formData: FormData) => signUpAction(prevState || {}, formData),
|
async (prevState: SignUpState | undefined, formData: FormData) => {
|
||||||
|
if (clearSignUp) {
|
||||||
|
setClearSignUp(false);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return signUpAction(prevState || {}, formData);
|
||||||
|
},
|
||||||
undefined
|
undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -64,21 +78,29 @@ export default function AuthForm({ redirectTo }: AuthFormProps) {
|
|||||||
return Object.keys(newErrors).length === 0;
|
return Object.keys(newErrors).length === 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFormSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
const handleFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const formData = new FormData(e.currentTarget);
|
const formData = new FormData(e.currentTarget);
|
||||||
|
|
||||||
if (validateForm(formData)) {
|
// 基本客户端验证
|
||||||
if (redirectTo) {
|
if (!validateForm(formData)) {
|
||||||
formData.append("redirectTo", redirectTo);
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (mode === 'signin') {
|
|
||||||
await signInActionForm(formData);
|
|
||||||
} else {
|
|
||||||
await signUpActionForm(formData);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加 redirectTo 到 formData
|
||||||
|
if (redirectTo) {
|
||||||
|
formData.append("redirectTo", redirectTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 startTransition 包装 action 调用
|
||||||
|
startTransition(() => {
|
||||||
|
// 根据模式调用相应的 action
|
||||||
|
if (mode === 'signin') {
|
||||||
|
signInActionForm(formData);
|
||||||
|
} else {
|
||||||
|
signUpActionForm(formData);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleGitHubSignIn = async () => {
|
const handleGitHubSignIn = async () => {
|
||||||
@@ -115,6 +137,9 @@ export default function AuthForm({ redirectTo }: AuthFormProps) {
|
|||||||
{errors.name && (
|
{errors.name && (
|
||||||
<p className="text-red-500 text-sm mt-1">{errors.name}</p>
|
<p className="text-red-500 text-sm mt-1">{errors.name}</p>
|
||||||
)}
|
)}
|
||||||
|
{currentError?.errors?.username && (
|
||||||
|
<p className="text-red-500 text-sm mt-1">{currentError.errors.username[0]}</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -128,6 +153,9 @@ export default function AuthForm({ redirectTo }: AuthFormProps) {
|
|||||||
{errors.email && (
|
{errors.email && (
|
||||||
<p className="text-red-500 text-sm mt-1">{errors.email}</p>
|
<p className="text-red-500 text-sm mt-1">{errors.email}</p>
|
||||||
)}
|
)}
|
||||||
|
{currentError?.errors?.email && (
|
||||||
|
<p className="text-red-500 text-sm mt-1">{currentError.errors.email[0]}</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -140,6 +168,9 @@ export default function AuthForm({ redirectTo }: AuthFormProps) {
|
|||||||
{errors.password && (
|
{errors.password && (
|
||||||
<p className="text-red-500 text-sm mt-1">{errors.password}</p>
|
<p className="text-red-500 text-sm mt-1">{errors.password}</p>
|
||||||
)}
|
)}
|
||||||
|
{currentError?.errors?.password && (
|
||||||
|
<p className="text-red-500 text-sm mt-1">{currentError.errors.password[0]}</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{mode === 'signup' && (
|
{mode === 'signup' && (
|
||||||
@@ -194,6 +225,12 @@ export default function AuthForm({ redirectTo }: AuthFormProps) {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setMode(mode === 'signin' ? 'signup' : 'signin');
|
setMode(mode === 'signin' ? 'signup' : 'signin');
|
||||||
setErrors({});
|
setErrors({});
|
||||||
|
// 清除服务器端错误状态
|
||||||
|
if (mode === 'signin') {
|
||||||
|
setClearSignIn(true);
|
||||||
|
} else {
|
||||||
|
setClearSignUp(true);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
className="text-[#35786f] hover:underline"
|
className="text-[#35786f] hover:underline"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -26,6 +26,36 @@ export async function signUpAction(prevState: SignUpState, formData: FormData) {
|
|||||||
const password = formData.get("password") as string;
|
const password = formData.get("password") as string;
|
||||||
const redirectTo = formData.get("redirectTo") as string;
|
const redirectTo = formData.get("redirectTo") as string;
|
||||||
|
|
||||||
|
// 服务器端验证
|
||||||
|
const errors: SignUpState['errors'] = {};
|
||||||
|
|
||||||
|
if (!email) {
|
||||||
|
errors.email = ["邮箱是必填项"];
|
||||||
|
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
||||||
|
errors.email = ["请输入有效的邮箱地址"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!name) {
|
||||||
|
errors.username = ["姓名是必填项"];
|
||||||
|
} else if (name.length < 2) {
|
||||||
|
errors.username = ["姓名至少需要2个字符"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!password) {
|
||||||
|
errors.password = ["密码是必填项"];
|
||||||
|
} else if (password.length < 8) {
|
||||||
|
errors.password = ["密码至少需要8个字符"];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有验证错误,返回错误状态
|
||||||
|
if (Object.keys(errors).length > 0) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "请修正表单中的错误",
|
||||||
|
errors
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await auth.api.signUpEmail({
|
await auth.api.signUpEmail({
|
||||||
body: {
|
body: {
|
||||||
@@ -49,6 +79,28 @@ export async function signInAction(prevState: SignUpState, formData: FormData) {
|
|||||||
const password = formData.get("password") as string;
|
const password = formData.get("password") as string;
|
||||||
const redirectTo = formData.get("redirectTo") as string;
|
const redirectTo = formData.get("redirectTo") as string;
|
||||||
|
|
||||||
|
// 服务器端验证
|
||||||
|
const errors: SignUpState['errors'] = {};
|
||||||
|
|
||||||
|
if (!email) {
|
||||||
|
errors.email = ["邮箱是必填项"];
|
||||||
|
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
||||||
|
errors.email = ["请输入有效的邮箱地址"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!password) {
|
||||||
|
errors.password = ["密码是必填项"];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有验证错误,返回错误状态
|
||||||
|
if (Object.keys(errors).length > 0) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "请修正表单中的错误",
|
||||||
|
errors
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await auth.api.signInEmail({
|
await auth.api.signInEmail({
|
||||||
body: {
|
body: {
|
||||||
|
|||||||
Reference in New Issue
Block a user