✨ Beautiful Animated Fire Glow: - Slow-moving fire gradient background with light/dark modes - Floating ember particles with realistic animation - 12-second background animation cycle with subtle color shifts - Theme-aware gradient colors (warm daylight vs cozy evening) 🎨 Enhanced Login Experience: - Updated branding to Deck Hearth with fire emoji - Backdrop blur effects on form elements - Semi-transparent containers for depth - Orange focus states to match fire theme - Enhanced shadows and glow effects 🌙 Theme Support: - Light mode: Warm daylight fire with golden embers - Dark mode: Cozy evening fire with bright orange flames - RGB color variables for backdrop-blur compatibility - Gradient-bg-ember class for consistent fire theming The login page now perfectly captures the warm, inviting Deck Hearth atmosphere
237 lines
No EOL
9.1 KiB
JavaScript
237 lines
No EOL
9.1 KiB
JavaScript
import { useState } from 'react';
|
|
import { useRouter } from 'next/router';
|
|
import AuthLayout from '../components/AuthLayout';
|
|
|
|
export default function Login() {
|
|
const router = useRouter();
|
|
const [formData, setFormData] = useState({
|
|
email: '',
|
|
password: ''
|
|
});
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState('');
|
|
|
|
const handleInputChange = (field, value) => {
|
|
setFormData(prev => ({
|
|
...prev,
|
|
[field]: value
|
|
}));
|
|
// Clear error when user starts typing
|
|
if (error) setError('');
|
|
};
|
|
|
|
const handleSubmit = async (e) => {
|
|
e.preventDefault();
|
|
setLoading(true);
|
|
setError('');
|
|
|
|
try {
|
|
const response = await fetch('/api/auth/login', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(formData)
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (response.ok) {
|
|
// Store the JWT token in localStorage
|
|
localStorage.setItem('auth_token', data.token);
|
|
|
|
// Redirect to admin panel or dashboard
|
|
if (data.user.role === 'admin') {
|
|
router.push('/admin/card-editor');
|
|
} else {
|
|
router.push('/dashboard');
|
|
}
|
|
} else {
|
|
setError(data.error || 'Login failed');
|
|
}
|
|
} catch (error) {
|
|
console.error('Login error:', error);
|
|
setError('Network error. Please try again.');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleQuickLogin = (email, password) => {
|
|
setFormData({ email, password });
|
|
};
|
|
|
|
return (
|
|
<AuthLayout>
|
|
<div className="min-h-screen flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
|
|
<div className="max-w-md w-full space-y-8">
|
|
<div>
|
|
<div className="text-center">
|
|
<div className="text-6xl mb-4">🔥</div>
|
|
<h2 className="text-3xl font-bold gradient-text-flame mb-2">
|
|
Welcome to Deck Hearth
|
|
</h2>
|
|
<p className="mt-2 text-sm" style={{ color: 'var(--text-secondary)' }}>
|
|
Sign in to access your trading card collection
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
className="p-8 rounded-2xl shadow-2xl backdrop-blur-sm border border-opacity-20"
|
|
style={{
|
|
backgroundColor: 'rgba(var(--bg-secondary-rgb), 0.85)',
|
|
borderColor: 'var(--border)'
|
|
}}
|
|
>
|
|
<form onSubmit={handleSubmit} className="space-y-6">
|
|
{error && (
|
|
<div className="p-4 rounded-lg bg-red-500 bg-opacity-10 border border-red-500 border-opacity-20 backdrop-blur-sm">
|
|
<p className="text-red-400 text-sm">{error}</p>
|
|
</div>
|
|
)}
|
|
|
|
<div>
|
|
<label htmlFor="email" className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
Email Address
|
|
</label>
|
|
<input
|
|
id="email"
|
|
name="email"
|
|
type="email"
|
|
autoComplete="email"
|
|
required
|
|
value={formData.email}
|
|
onChange={(e) => handleInputChange('email', e.target.value)}
|
|
className="w-full px-4 py-3 rounded-lg border transition-all duration-200 focus:ring-2 focus:ring-orange-500 focus:border-transparent backdrop-blur-sm"
|
|
style={{
|
|
backgroundColor: 'rgba(var(--bg-primary-rgb), 0.7)',
|
|
borderColor: 'var(--border)',
|
|
color: 'var(--text-primary)'
|
|
}}
|
|
placeholder="Enter your email"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="password" className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
Password
|
|
</label>
|
|
<input
|
|
id="password"
|
|
name="password"
|
|
type="password"
|
|
autoComplete="current-password"
|
|
required
|
|
value={formData.password}
|
|
onChange={(e) => handleInputChange('password', e.target.value)}
|
|
className="w-full px-4 py-3 rounded-lg border transition-all duration-200 focus:ring-2 focus:ring-orange-500 focus:border-transparent backdrop-blur-sm"
|
|
style={{
|
|
backgroundColor: 'rgba(var(--bg-primary-rgb), 0.7)',
|
|
borderColor: 'var(--border)',
|
|
color: 'var(--text-primary)'
|
|
}}
|
|
placeholder="Enter your password"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<button
|
|
type="submit"
|
|
disabled={loading}
|
|
className={`w-full px-4 py-3 rounded-lg font-medium transition-all duration-200 ${
|
|
loading
|
|
? 'opacity-50 cursor-not-allowed'
|
|
: 'gradient-bg-ember text-white hover:shadow-xl transform hover:scale-105 hover:shadow-orange-500/20'
|
|
}`}
|
|
>
|
|
{loading ? (
|
|
<div className="flex items-center justify-center">
|
|
<div className="animate-spin rounded-full h-5 w-5 border-b-2 border-white mr-2"></div>
|
|
Signing in...
|
|
</div>
|
|
) : (
|
|
'Sign in to Deck Hearth'
|
|
)}
|
|
</button>
|
|
</div>
|
|
|
|
{/* Quick Login for Testing */}
|
|
<div className="mt-6 pt-6 border-t border-opacity-20" style={{ borderColor: 'var(--border)' }}>
|
|
<div className="text-center">
|
|
<p className="text-sm mb-3" style={{ color: 'var(--text-secondary)' }}>
|
|
Quick Login for Testing:
|
|
</p>
|
|
<div className="grid grid-cols-3 gap-2">
|
|
<button
|
|
type="button"
|
|
onClick={() => handleQuickLogin('admin@tcgvault.com', 'admin123')}
|
|
className="px-3 py-2 rounded-lg text-xs font-medium border border-opacity-20 transition-all duration-200 hover:shadow-md backdrop-blur-sm hover:bg-opacity-80"
|
|
style={{
|
|
backgroundColor: 'rgba(var(--bg-primary-rgb), 0.6)',
|
|
borderColor: 'var(--border)',
|
|
color: 'var(--text-primary)'
|
|
}}
|
|
>
|
|
👑 Admin
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => handleQuickLogin('alice@tcgvault.com', 'alice123')}
|
|
className="px-3 py-2 rounded-lg text-xs font-medium border border-opacity-20 transition-all duration-200 hover:shadow-md backdrop-blur-sm hover:bg-opacity-80"
|
|
style={{
|
|
backgroundColor: 'rgba(var(--bg-primary-rgb), 0.6)',
|
|
borderColor: 'var(--border)',
|
|
color: 'var(--text-primary)'
|
|
}}
|
|
>
|
|
👤 Alice
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => handleQuickLogin('bob@tcgvault.com', 'bob123')}
|
|
className="px-3 py-2 rounded-lg text-xs font-medium border border-opacity-20 transition-all duration-200 hover:shadow-md backdrop-blur-sm hover:bg-opacity-80"
|
|
style={{
|
|
backgroundColor: 'rgba(var(--bg-primary-rgb), 0.6)',
|
|
borderColor: 'var(--border)',
|
|
color: 'var(--text-primary)'
|
|
}}
|
|
>
|
|
👤 Bob
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
<div className="mt-6 text-center">
|
|
<p className="text-sm" style={{ color: 'var(--text-secondary)' }}>
|
|
Use the quick login buttons above for testing, or enter credentials manually.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Testing Guide */}
|
|
<div
|
|
className="p-4 rounded-xl border border-opacity-20 backdrop-blur-sm"
|
|
style={{
|
|
backgroundColor: 'rgba(var(--bg-secondary-rgb), 0.6)',
|
|
borderColor: 'var(--border)'
|
|
}}
|
|
>
|
|
<div className="text-center">
|
|
<h3 className="text-sm font-semibold mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
🧪 Testing Accounts
|
|
</h3>
|
|
<div className="text-xs space-y-1" style={{ color: 'var(--text-secondary)' }}>
|
|
<p><strong>Admin:</strong> Full system access</p>
|
|
<p><strong>Alice:</strong> Regular user for testing</p>
|
|
<p><strong>Bob:</strong> Collaborator for testing</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</AuthLayout>
|
|
);
|
|
}
|