221 lines
9.2 KiB
TypeScript
221 lines
9.2 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { useRouter } from 'next/navigation';
|
|
import Link from 'next/link';
|
|
import { useAuth } from '@/lib/auth';
|
|
|
|
export default function LoginPage() {
|
|
const router = useRouter();
|
|
const { signIn, signInWithGitHub, signInWithGoogle, user } = useAuth();
|
|
|
|
const [email, setEmail] = useState('');
|
|
const [password, setPassword] = useState('');
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [message, setMessage] = useState({ type: '', content: '' });
|
|
|
|
// 如果用户已登录,重定向到首页
|
|
useEffect(() => {
|
|
if (user) {
|
|
router.push('/');
|
|
}
|
|
}, [user, router]);
|
|
|
|
const handleEmailSignIn = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
|
|
if (!email || !password) {
|
|
setMessage({
|
|
type: 'error',
|
|
content: 'Please enter both email and password'
|
|
});
|
|
return;
|
|
}
|
|
|
|
try {
|
|
setIsLoading(true);
|
|
setMessage({ type: '', content: '' });
|
|
|
|
const { error } = await signIn(email, password);
|
|
|
|
if (error) {
|
|
throw new Error(error.message);
|
|
}
|
|
|
|
// 登录成功后,会通过 useEffect 重定向
|
|
} catch (error) {
|
|
console.error('Login error:', error);
|
|
setMessage({
|
|
type: 'error',
|
|
content: error instanceof Error ? error.message : 'Failed to sign in'
|
|
});
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleGitHubSignIn = async () => {
|
|
try {
|
|
setIsLoading(true);
|
|
setMessage({ type: '', content: '' });
|
|
|
|
const { error } = await signInWithGitHub();
|
|
|
|
if (error) {
|
|
throw new Error(error.message);
|
|
}
|
|
|
|
// 登录成功后,会通过 useEffect 重定向
|
|
} catch (error) {
|
|
console.error('GitHub login error:', error);
|
|
setMessage({
|
|
type: 'error',
|
|
content: error instanceof Error ? error.message : 'Failed to sign in with GitHub'
|
|
});
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleGoogleSignIn = async () => {
|
|
try {
|
|
setIsLoading(true);
|
|
setMessage({ type: '', content: '' });
|
|
|
|
const { error } = await signInWithGoogle();
|
|
|
|
if (error) {
|
|
throw new Error(error.message);
|
|
}
|
|
|
|
// 登录成功后,会通过 useEffect 重定向
|
|
} catch (error) {
|
|
console.error('Google login error:', error);
|
|
setMessage({
|
|
type: 'error',
|
|
content: error instanceof Error ? error.message : 'Failed to sign in with Google'
|
|
});
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="flex items-center justify-center min-h-screen bg-gray-100">
|
|
<div className="w-full max-w-md p-8 space-y-8 bg-white rounded-lg shadow-md">
|
|
<div className="text-center">
|
|
<h1 className="text-2xl font-bold text-gray-900">Login</h1>
|
|
<p className="mt-2 text-sm text-gray-600">
|
|
Sign in to your account to access analytics
|
|
</p>
|
|
<div className="mt-2 text-xs text-gray-500">
|
|
Welcome to ShortURL Analytics
|
|
</div>
|
|
</div>
|
|
|
|
{/* Message display */}
|
|
{message.content && (
|
|
<div className={`p-4 mb-4 text-sm ${
|
|
message.type === 'error'
|
|
? 'text-red-700 bg-red-100 rounded-lg'
|
|
: 'text-blue-700 bg-blue-100 rounded-lg'
|
|
}`}>
|
|
{message.content}
|
|
</div>
|
|
)}
|
|
|
|
<form onSubmit={handleEmailSignIn} className="mt-8 space-y-6">
|
|
<div>
|
|
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
|
|
Email address
|
|
</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 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
|
placeholder="your@email.com"
|
|
disabled={isLoading}
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="password" className="block text-sm font-medium text-gray-700">
|
|
Password
|
|
</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 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
|
placeholder="••••••••"
|
|
disabled={isLoading}
|
|
/>
|
|
</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"
|
|
>
|
|
{isLoading ? 'Signing in...' : 'Sign in'}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
|
|
<div className="mt-6">
|
|
<div className="relative">
|
|
<div className="absolute inset-0 flex items-center">
|
|
<div className="w-full border-t border-gray-300"></div>
|
|
</div>
|
|
<div className="relative flex justify-center text-sm">
|
|
<span className="px-2 bg-white text-gray-500">Or</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-6 grid grid-cols-2 gap-3">
|
|
<button
|
|
type="button"
|
|
onClick={handleGitHubSignIn}
|
|
disabled={isLoading}
|
|
className="flex justify-center items-center py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
|
>
|
|
<svg className="h-5 w-5 mr-2" fill="currentColor" viewBox="0 0 24 24">
|
|
<path fillRule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clipRule="evenodd" />
|
|
</svg>
|
|
GitHub
|
|
</button>
|
|
|
|
<button
|
|
type="button"
|
|
onClick={handleGoogleSignIn}
|
|
disabled={isLoading}
|
|
className="flex justify-center items-center py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
|
>
|
|
<svg className="h-5 w-5 mr-2" viewBox="0 0 24 24">
|
|
<path d="M12.545 12.151L12.545 12.151L12.545 12.151C12.545 9.85553 14.0905 7.98375 16.088 7.98375C17.0865 7.98375 17.938 8.43025 18.5592 9.0514L21.3404 6.27019C19.7172 4.75612 18.0026 4 16.088 4C12.5405 4 9.5 6.67528 9.5 10.2505C9.5 12.0582 10.1533 13.4581 10.8634 14.4685C12.1453 16.3618 14.4737 18.501 16.088 18.501C19.9265 18.501 22 16.0057 22 12.4071C22 11.4245 21.9318 10.9113 21.7953 10.2505H16.088V12.151H12.545Z" fill="#4285F4" />
|
|
<path d="M5.90607 10.2197C5.40834 11.1993 5.12343 12.2959 5.12343 13.4564C5.12343 14.6646 5.41958 15.782 5.92853 16.7831L5.92786 16.7818C6.91998 18.6136 8.81431 19.8018 11.0008 19.8018C12.5581 19.8018 13.8262 19.318 14.7997 18.5825L14.7976 18.5845C15.6806 17.9139 16.401 16.9218 16.6662 15.7257L16.6657 15.7276C16.7331 15.3933 16.7688 15.0493 16.7688 14.6895H11.0008C10.3375 14.6895 9.80078 14.1523 9.80078 13.4882V10.2197H5.90607Z" fill="#34A853" />
|
|
<path d="M5.12207 6.25024C4 7.86024 3.33789 9.81535 3.33789 11.9339C3.33789 12.9995 3.55215 14.0269 3.94853 14.9805L5.90673 10.2197H9.80143V6.25024H5.12207Z" fill="#FBBC05" />
|
|
<path d="M11.001 3.57764C12.4571 3.57764 13.778 4.11181 14.8023 5.06959L14.8028 5.0692L17.2711 2.60092L17.271 2.60082C15.5041 0.97625 13.3649 0 11.001 0C8.81453 0 6.91994 1.18824 5.92853 3.02125L9.80224 6.25031V6.25031H11.001V3.57764Z" fill="#EA4335" />
|
|
</svg>
|
|
Google
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<p className="text-sm text-gray-600">
|
|
Don't have an account?{' '}
|
|
<Link href="/register" className="font-medium text-blue-600 hover:text-blue-500">
|
|
Register
|
|
</Link>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|