deckhearth/pages/login.js
Randall Stillwell e75a4a6649 Major redesign: Enhanced card display with particle effects, improved filters, and search functionality
- 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
2025-07-23 21:26:54 -05:00

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>
);
}