- Redesigned card display with 2.5:3.5 aspect ratio and image-only view - Added infinite scroll to replace pagination - Implemented authentic card back placeholders for MTG, Pokemon, and Lorcana - Added rarity-based particle effects with tiered intensity (mythic/enchanted/rare/uncommon) - Enhanced hover details panel with structured card information - Fixed search functionality with debouncing and Enter key support - Improved filter system with working TCG, rarity, set, and price filters - Added favorite system for cards in both hover and detail views - Updated card detail page with comprehensive metadata and actions - Fixed API filtering with proper Vercel Postgres implementation - Added particle animations and rarity glow effects - Improved overall UX with better visual hierarchy and interactions
132 lines
No EOL
4.3 KiB
JavaScript
132 lines
No EOL
4.3 KiB
JavaScript
import { useState } from 'react';
|
|
import { useRouter } from 'next/router';
|
|
import { useAuth } from '../lib/auth-context.js';
|
|
|
|
export default function Login() {
|
|
const [email, setEmail] = useState('');
|
|
const [password, setPassword] = useState('');
|
|
const [error, setError] = useState('');
|
|
const [loading, setLoading] = useState(false);
|
|
const [isRegister, setIsRegister] = useState(false);
|
|
|
|
const { login, register } = useAuth();
|
|
const router = useRouter();
|
|
|
|
const handleSubmit = async (e) => {
|
|
e.preventDefault();
|
|
setError('');
|
|
setLoading(true);
|
|
|
|
try {
|
|
const result = isRegister
|
|
? await register(email, password)
|
|
: await login(email, password);
|
|
|
|
if (result.success) {
|
|
router.push('/dashboard');
|
|
} else {
|
|
setError(result.error);
|
|
}
|
|
} catch (error) {
|
|
setError('An unexpected error occurred');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gray-50 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
|
|
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
|
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
|
|
{isRegister ? 'Create your account' : 'Sign in to your account'}
|
|
</h2>
|
|
<p className="mt-2 text-center text-sm text-gray-600">
|
|
{isRegister ? 'Already have an account?' : "Don't have an account?"}{' '}
|
|
<button
|
|
onClick={() => setIsRegister(!isRegister)}
|
|
className="font-medium text-blue-600 hover:text-blue-500"
|
|
>
|
|
{isRegister ? 'Sign in' : 'Sign up'}
|
|
</button>
|
|
</p>
|
|
</div>
|
|
|
|
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
|
<div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
|
<form className="space-y-6" onSubmit={handleSubmit}>
|
|
{error && (
|
|
<div className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded">
|
|
{error}
|
|
</div>
|
|
)}
|
|
|
|
<div>
|
|
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
|
|
Email address
|
|
</label>
|
|
<div className="mt-1">
|
|
<input
|
|
id="email"
|
|
name="email"
|
|
type="email"
|
|
autoComplete="email"
|
|
required
|
|
value={email}
|
|
onChange={(e) => setEmail(e.target.value)}
|
|
className="input-field"
|
|
placeholder="Enter your email"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="password" className="block text-sm font-medium text-gray-700">
|
|
Password
|
|
</label>
|
|
<div className="mt-1">
|
|
<input
|
|
id="password"
|
|
name="password"
|
|
type="password"
|
|
autoComplete="current-password"
|
|
required
|
|
value={password}
|
|
onChange={(e) => setPassword(e.target.value)}
|
|
className="input-field"
|
|
placeholder="Enter your password"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<button
|
|
type="submit"
|
|
disabled={loading}
|
|
className="w-full btn-primary disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
{loading ? 'Loading...' : (isRegister ? 'Create Account' : 'Sign In')}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
|
|
{!isRegister && (
|
|
<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 className="relative flex justify-center text-sm">
|
|
<span className="px-2 bg-white text-gray-500">Demo Account</span>
|
|
</div>
|
|
</div>
|
|
<div className="mt-6 text-center text-sm text-gray-600">
|
|
<p>Email: admin@tcgvault.com</p>
|
|
<p>Password: admin123</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|