login page

This commit is contained in:
2025-04-01 14:57:22 +08:00
parent b8e6180212
commit 696a434b95
11 changed files with 1104 additions and 76 deletions

250
app/login/page.tsx Normal file
View File

@@ -0,0 +1,250 @@
'use client';
import { useState, FormEvent, useEffect } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import Link from 'next/link';
import { useAuth } from '@/lib/auth';
import supabase from '@/lib/supabase';
export default function LoginPage() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [message, setMessage] = useState<string | null>(null);
const [serverInfo, setServerInfo] = useState<string | null>(null);
const router = useRouter();
const searchParams = useSearchParams();
const { signIn, signInWithGoogle, user, autoRegisterTestUser } = useAuth();
// 从URL参数中获取消息
useEffect(() => {
const message = searchParams.get('message');
if (message) {
setMessage(message);
}
// 如果用户已登录跳转到dashboard
if (user) {
router.push('/dashboard');
}
// 检查服务器状态
const checkServerStatus = async () => {
try {
const { data, error } = await supabase.auth.getSession();
if (error) {
console.log('Session check error:', error);
setServerInfo(`服务器连接状态: 错误 (${error.message})`);
} else {
console.log('Session check success:', data);
setServerInfo('服务器连接状态: 正常');
}
} catch (error) {
console.error('Server check error:', error);
setServerInfo('服务器连接状态: 无法连接');
}
};
checkServerStatus();
}, [searchParams, user, router]);
// 处理登录表单提交
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
setError(null);
setIsLoading(true);
try {
console.log('Attempting to sign in with:', { email, password });
await signIn(email, password);
// 登录成功后会自动跳转到dashboard
} catch (error: unknown) {
console.error('Login error:', error);
// 显示更详细的错误信息
if (error instanceof Error) {
setError(`登录失败: ${error.message}`);
} else {
setError('登录失败,请检查邮箱和密码是否正确');
}
} finally {
setIsLoading(false);
}
};
// 处理Google登录
const handleGoogleSignIn = async () => {
setError(null);
try {
await signInWithGoogle();
// 登录流程会重定向到Google然后回到应用
} catch (error: unknown) {
console.error('Google sign in error:', error);
if (error instanceof Error) {
setError(`Google登录失败: ${error.message}`);
} else {
setError('Google登录失败请稍后再试');
}
}
};
// 使用测试账户
const handleUseTestAccount = async () => {
setError(null);
setIsLoading(true);
try {
// 使用AuthContext中的自动注册测试账户功能
await autoRegisterTestUser();
} catch (error) {
console.error('测试账户处理错误:', error);
setError(error instanceof Error ? error.message : '测试账户创建失败');
} finally {
setIsLoading(false);
}
};
return (
<div className="flex items-center justify-center min-h-screen bg-gray-100 dark:bg-gray-900">
<div className="w-full max-w-md p-8 space-y-8 bg-white dark:bg-gray-800 rounded-lg shadow-md">
<div className="text-center">
<h1 className="text-2xl font-bold text-gray-900 dark:text-gray-100"></h1>
<p className="mt-2 text-sm text-gray-600 dark:text-gray-400">
访
</p>
{serverInfo && (
<div className="mt-2 text-xs text-gray-500 dark:text-gray-500">
{serverInfo}
</div>
)}
</div>
{/* 消息提示 */}
{message && (
<div className="p-4 mb-4 text-sm text-blue-700 bg-blue-100 dark:bg-blue-900 dark:text-blue-200 rounded-lg">
{message}
</div>
)}
{/* 错误提示 */}
{error && (
<div className="p-4 mb-4 text-sm text-red-700 bg-red-100 dark:bg-red-900 dark:text-red-200 rounded-lg">
{error}
</div>
)}
<form className="mt-8 space-y-6" onSubmit={handleSubmit}>
<div className="space-y-4">
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700 dark:text-gray-300">
</label>
<input
id="email"
name="email"
type="email"
autoComplete="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
className="mt-1 block w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm placeholder-gray-400 dark:placeholder-gray-500 dark:bg-gray-700 dark:text-white focus:outline-none focus:ring-blue-500 focus:border-blue-500"
placeholder="your@email.com"
/>
</div>
<div>
<label htmlFor="password" className="block text-sm font-medium text-gray-700 dark:text-gray-300">
</label>
<input
id="password"
name="password"
type="password"
autoComplete="current-password"
required
value={password}
onChange={(e) => setPassword(e.target.value)}
className="mt-1 block w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm placeholder-gray-400 dark:placeholder-gray-500 dark:bg-gray-700 dark:text-white focus:outline-none focus:ring-blue-500 focus:border-blue-500"
placeholder="********"
/>
</div>
</div>
<div>
<button
type="submit"
disabled={isLoading}
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
>
{isLoading ? '登录中...' : '登录'}
</button>
</div>
<div className="relative">
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-gray-300 dark:border-gray-600"></div>
</div>
<div className="relative flex justify-center text-sm">
<span className="px-2 bg-white dark:bg-gray-800 text-gray-500 dark:text-gray-400"></span>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<button
type="button"
onClick={handleGoogleSignIn}
className="flex justify-center items-center py-2 px-4 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
<svg
className="h-5 w-5 mr-2"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
>
<g transform="matrix(1, 0, 0, 1, 27.009001, -39.238998)">
<path
fill="#4285F4"
d="M -3.264 51.509 C -3.264 50.719 -3.334 49.969 -3.454 49.239 L -14.754 49.239 L -14.754 53.749 L -8.284 53.749 C -8.574 55.229 -9.424 56.479 -10.684 57.329 L -10.684 60.329 L -6.824 60.329 C -4.564 58.239 -3.264 55.159 -3.264 51.509 Z"
/>
<path
fill="#34A853"
d="M -14.754 63.239 C -11.514 63.239 -8.804 62.159 -6.824 60.329 L -10.684 57.329 C -11.764 58.049 -13.134 58.489 -14.754 58.489 C -17.884 58.489 -20.534 56.379 -21.484 53.529 L -25.464 53.529 L -25.464 56.619 C -23.494 60.539 -19.444 63.239 -14.754 63.239 Z"
/>
<path
fill="#FBBC05"
d="M -21.484 53.529 C -21.734 52.809 -21.864 52.039 -21.864 51.239 C -21.864 50.439 -21.724 49.669 -21.484 48.949 L -21.484 45.859 L -25.464 45.859 C -26.284 47.479 -26.754 49.299 -26.754 51.239 C -26.754 53.179 -26.284 54.999 -25.464 56.619 L -21.484 53.529 Z"
/>
<path
fill="#EA4335"
d="M -14.754 43.989 C -12.984 43.989 -11.404 44.599 -10.154 45.789 L -6.734 42.369 C -8.804 40.429 -11.514 39.239 -14.754 39.239 C -19.444 39.239 -23.494 41.939 -25.464 45.859 L -21.484 48.949 C -20.534 46.099 -17.884 43.989 -14.754 43.989 Z"
/>
</g>
</svg>
Google登录
</button>
<button
type="button"
onClick={handleUseTestAccount}
className="flex justify-center items-center py-2 px-4 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
使
</button>
</div>
</form>
<div className="mt-6 text-center">
<p className="text-sm text-gray-600 dark:text-gray-400">
{' '}
<Link
href="/register"
className="font-medium text-blue-600 hover:text-blue-500 dark:text-blue-400 dark:hover:text-blue-300"
>
</Link>
</p>
</div>
</div>
</div>
);
}