Spaces:
Sleeping
Sleeping
File size: 6,784 Bytes
91d209c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | import { useState, useEffect } from 'react';
import { motion } from 'framer-motion';
import { useAuth } from '@/context/AuthContext';
import { LogoIcon } from './Icons';
export function Login() {
const { login } = useAuth();
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
// Debug: Log error state changes
useEffect(() => {
if (error) {
console.log('Error state updated:', error);
}
}, [error]);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError(null);
setLoading(true);
try {
await login({ username, password });
} catch (err) {
// Extract error message from the error
let errorMessage = 'Login failed. Please check your credentials.';
if (err instanceof Error) {
errorMessage = err.message || errorMessage;
} else if (typeof err === 'string') {
errorMessage = err;
}
console.error('Login error:', err);
console.log('Setting error message:', errorMessage);
setError(errorMessage);
} finally {
setLoading(false);
}
};
return (
<div className="min-h-screen flex items-center justify-center bg-mesh-pattern p-6">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className="w-full max-w-md"
>
<div className="glass-dark rounded-2xl p-8 shadow-2xl">
{/* Logo and Title */}
<div className="text-center mb-8">
<div className="flex justify-center mb-4">
<LogoIcon size={64} />
</div>
<h1 className="text-3xl font-display font-bold text-void-100 mb-2">
Video AdGenesis
</h1>
<p className="text-void-400">Studio</p>
<p className="text-sm text-void-500 mt-4">
Please sign in to continue
</p>
</div>
{/* Login Form */}
<form onSubmit={handleSubmit} className="space-y-6">
{/* Username Field */}
<div>
<label htmlFor="username" className="block text-sm font-medium text-void-300 mb-2">
Username
</label>
<input
id="username"
type="text"
value={username}
onChange={(e) => {
setUsername(e.target.value);
if (error) setError(null);
}}
required
className="w-full px-4 py-3 bg-void-900/50 border border-void-700 rounded-lg
text-void-100 placeholder-void-500 focus:outline-none focus:ring-2
focus:ring-coral-500 focus:border-transparent transition-all"
placeholder="Enter your username"
autoComplete="username"
/>
</div>
{/* Password Field */}
<div>
<label htmlFor="password" className="block text-sm font-medium text-void-300 mb-2">
Password
</label>
<input
id="password"
type="password"
value={password}
onChange={(e) => {
setPassword(e.target.value);
if (error) setError(null);
}}
required
className="w-full px-4 py-3 bg-void-900/50 border border-void-700 rounded-lg
text-void-100 placeholder-void-500 focus:outline-none focus:ring-2
focus:ring-coral-500 focus:border-transparent transition-all"
placeholder="Enter your password"
autoComplete="current-password"
/>
</div>
{/* Error Message */}
{error ? (
<motion.div
key={`error-${error}`}
initial={{ opacity: 0, y: -10, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.2 }}
className="p-4 bg-red-500/40 border-2 border-red-500 rounded-lg text-red-100 text-sm flex items-start gap-3 shadow-lg backdrop-blur-sm"
role="alert"
aria-live="assertive"
style={{ zIndex: 10 }}
>
<svg
className="w-5 h-5 text-red-300 flex-shrink-0 mt-0.5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<span className="flex-1 font-semibold">{error}</span>
</motion.div>
) : null}
{/* Submit Button */}
<button
type="submit"
disabled={loading}
className="w-full py-3 px-4 bg-gradient-to-r from-coral-500 to-coral-600
text-white font-semibold rounded-lg hover:from-coral-600 hover:to-coral-700
focus:outline-none focus:ring-2 focus:ring-coral-500 focus:ring-offset-2
focus:ring-offset-void-900 disabled:opacity-50 disabled:cursor-not-allowed
transition-all transform hover:scale-[1.02] active:scale-[0.98]"
>
{loading ? (
<span className="flex items-center justify-center gap-2">
<svg className="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Signing in...
</span>
) : (
'Sign In'
)}
</button>
</form>
{/* Footer */}
<div className="mt-6 text-center">
<p className="text-xs text-void-500">
Restricted access - Authorized users only
</p>
</div>
</div>
</motion.div>
</div>
);
}
|