377 أسطر
16 KiB
TypeScript
377 أسطر
16 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { Eye, EyeOff, Mail, Lock, User, ArrowLeft, AlertCircle } from 'lucide-react';
|
|
import { useAuth } from './useAuth'; // Adjust the import path as needed
|
|
|
|
const Login = () => {
|
|
const [isLogin, setIsLogin] = useState(true);
|
|
const [showPassword, setShowPassword] = useState(false);
|
|
const [showForgotPassword, setShowForgotPassword] = useState(false);
|
|
const [forgotPasswordEmail, setForgotPasswordEmail] = useState('');
|
|
|
|
const [formData, setFormData] = useState({
|
|
email: '',
|
|
password: '',
|
|
confirmPassword: '',
|
|
username: ''
|
|
});
|
|
|
|
const { isLoading, error, handleLogin, handleSignUp, handleForgotPassword, setError } = useAuth();
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setError(''); // Clear previous errors
|
|
|
|
// Validation
|
|
if (!isLogin && formData.password !== formData.confirmPassword) {
|
|
setError('Passwords do not match');
|
|
return;
|
|
}
|
|
|
|
if (!isLogin && formData.password.length < 6) {
|
|
setError('Password must be at least 6 characters long');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
let result;
|
|
if (isLogin) {
|
|
result = await handleLogin(formData.email, formData.password);
|
|
} else {
|
|
result = await handleSignUp(formData.email, formData.password);
|
|
}
|
|
|
|
if (result.success) {
|
|
// Redirect or handle successful authentication
|
|
console.log('Authentication successful:', result.data);
|
|
// You can redirect here or handle the successful login
|
|
window.location.href = '/'; // Example redirect
|
|
} else {
|
|
// Error is already set in the hook, no need to set it again
|
|
console.error('Authentication failed:', result.error);
|
|
}
|
|
} catch (err) {
|
|
console.error('Authentication error:', err);
|
|
}
|
|
};
|
|
|
|
const handleForgotPasswordSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setError('');
|
|
|
|
if (!forgotPasswordEmail) {
|
|
setError('Please enter your email address');
|
|
return;
|
|
}
|
|
|
|
const result = await handleForgotPassword(forgotPasswordEmail);
|
|
if (result.success) {
|
|
setError(''); // Clear any previous errors
|
|
alert(result.message); // Or show a success message in a better way
|
|
setShowForgotPassword(false);
|
|
setForgotPasswordEmail('');
|
|
}
|
|
};
|
|
|
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
setFormData(prev => ({
|
|
...prev,
|
|
[e.target.name]: e.target.value
|
|
}));
|
|
};
|
|
|
|
// Forgot Password Modal
|
|
if (showForgotPassword) {
|
|
return (
|
|
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center p-4">
|
|
<div className="max-w-md w-full">
|
|
<div className="bg-white rounded-2xl shadow-lg border p-6 sm:p-8">
|
|
{/* Back Button */}
|
|
<button
|
|
onClick={() => setShowForgotPassword(false)}
|
|
className="flex items-center gap-2 text-gray-600 hover:text-gray-800 mb-4 transition-colors"
|
|
>
|
|
<ArrowLeft className="w-4 h-4" />
|
|
Back to {isLogin ? 'Login' : 'Sign Up'}
|
|
</button>
|
|
|
|
{/* Header */}
|
|
<div className="text-center mb-6">
|
|
<h1 className="text-2xl font-extrabold text-[#4285F4] mb-2">
|
|
Reset Your Password
|
|
</h1>
|
|
<p className="text-gray-600 text-sm">
|
|
Enter your email address and we'll send you a link to reset your password.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Error Display */}
|
|
{error && (
|
|
<div className="mb-4 p-3 bg-red-50 border border-red-200 rounded-lg flex items-center gap-2 text-red-700 text-sm">
|
|
<AlertCircle className="w-4 h-4 flex-shrink-0" />
|
|
<span>{error}</span>
|
|
</div>
|
|
)}
|
|
|
|
{/* Forgot Password Form */}
|
|
<form onSubmit={handleForgotPasswordSubmit} className="space-y-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
Email Address
|
|
</label>
|
|
<div className="relative">
|
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
<Mail className="h-5 w-5 text-gray-400" />
|
|
</div>
|
|
<input
|
|
type="email"
|
|
value={forgotPasswordEmail}
|
|
onChange={(e) => setForgotPasswordEmail(e.target.value)}
|
|
className="block w-full pl-10 pr-3 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#4285F4] focus:border-[#4285F4] transition-colors"
|
|
placeholder="Enter your email"
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<button
|
|
type="submit"
|
|
disabled={isLoading}
|
|
className="w-full bg-[#4285F4] text-white py-3 px-4 rounded-lg font-semibold hover:bg-[#1e40af] focus:ring-2 focus:ring-[#4285F4] focus:ring-offset-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
{isLoading ? 'Sending...' : 'Send Reset Link'}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center p-4">
|
|
<div className="max-w-md w-full">
|
|
{/* Header */}
|
|
<div className="text-center mb-8">
|
|
<h1 className="text-3xl font-extrabold text-[#4285F4] mb-2">
|
|
{isLogin ? 'Welcome Back' : 'Create Account'}
|
|
</h1>
|
|
<p className="text-gray-600">
|
|
{isLogin ? 'Sign in to your account' : 'Create your account'}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Auth Card */}
|
|
<div className="bg-white rounded-2xl shadow-lg border p-6 sm:p-8">
|
|
{/* Error Display */}
|
|
{error && (
|
|
<div className="mb-6 p-3 bg-red-50 border border-red-200 rounded-lg flex items-center gap-2 text-red-700 text-sm">
|
|
<AlertCircle className="w-4 h-4 flex-shrink-0" />
|
|
<span>{error}</span>
|
|
</div>
|
|
)}
|
|
|
|
<form onSubmit={handleSubmit} className="space-y-6">
|
|
{/* Username Field - Only for Signup */}
|
|
{!isLogin && (
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
Username
|
|
</label>
|
|
<div className="relative">
|
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
<User className="h-5 w-5 text-gray-400" />
|
|
</div>
|
|
<input
|
|
type="text"
|
|
name="username"
|
|
value={formData.username}
|
|
onChange={handleChange}
|
|
className="block w-full pl-10 pr-3 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#4285F4] focus:border-[#4285F4] transition-colors"
|
|
placeholder="Enter your username"
|
|
required={!isLogin}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Email Field */}
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
Email Address
|
|
</label>
|
|
<div className="relative">
|
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
<Mail className="h-5 w-5 text-gray-400" />
|
|
</div>
|
|
<input
|
|
type="email"
|
|
name="email"
|
|
value={formData.email}
|
|
onChange={handleChange}
|
|
className="block w-full pl-10 pr-3 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#4285F4] focus:border-[#4285F4] transition-colors"
|
|
placeholder="Enter your email"
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Password Field */}
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
Password
|
|
</label>
|
|
<div className="relative">
|
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
<Lock className="h-5 w-5 text-gray-400" />
|
|
</div>
|
|
<input
|
|
type={showPassword ? 'text' : 'password'}
|
|
name="password"
|
|
value={formData.password}
|
|
onChange={handleChange}
|
|
className="block w-full pl-10 pr-10 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#4285F4] focus:border-[#4285F4] transition-colors"
|
|
placeholder="Enter your password"
|
|
required
|
|
/>
|
|
<button
|
|
type="button"
|
|
className="absolute inset-y-0 right-0 pr-3 flex items-center"
|
|
onClick={() => setShowPassword(!showPassword)}
|
|
>
|
|
{showPassword ? (
|
|
<EyeOff className="h-5 w-5 text-gray-400" />
|
|
) : (
|
|
<Eye className="h-5 w-5 text-gray-400" />
|
|
)}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Confirm Password Field - Only for Signup */}
|
|
{!isLogin && (
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
Confirm Password
|
|
</label>
|
|
<div className="relative">
|
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
<Lock className="h-5 w-5 text-gray-400" />
|
|
</div>
|
|
<input
|
|
type={showPassword ? 'text' : 'password'}
|
|
name="confirmPassword"
|
|
value={formData.confirmPassword}
|
|
onChange={handleChange}
|
|
className="block w-full pl-10 pr-3 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#4285F4] focus:border-[#4285F4] transition-colors"
|
|
placeholder="Confirm your password"
|
|
required={!isLogin}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Forgot Password - Only for Login */}
|
|
{isLogin && (
|
|
<div className="flex justify-end">
|
|
<button
|
|
type="button"
|
|
onClick={() => setShowForgotPassword(true)}
|
|
className="text-sm text-[#4285F4] hover:text-[#1e40af] transition-colors"
|
|
>
|
|
Forgot your password?
|
|
</button>
|
|
</div>
|
|
)}
|
|
|
|
{/* Submit Button */}
|
|
<button
|
|
type="submit"
|
|
disabled={isLoading}
|
|
className="w-full bg-[#4285F4] text-white py-3 px-4 rounded-lg font-semibold hover:bg-[#1e40af] focus:ring-2 focus:ring-[#4285F4] focus:ring-offset-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
{isLoading ? (
|
|
<span className="flex items-center justify-center gap-2">
|
|
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
|
{isLogin ? 'Signing In...' : 'Creating Account...'}
|
|
</span>
|
|
) : (
|
|
isLogin ? 'Sign In' : 'Create Account'
|
|
)}
|
|
</button>
|
|
|
|
{/* Divider */}
|
|
<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">Or continue with</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Social Login Buttons */}
|
|
<div className="grid grid-cols-2 gap-3">
|
|
<button
|
|
type="button"
|
|
disabled={isLoading}
|
|
className="w-full inline-flex justify-center py-3 px-4 border border-gray-300 rounded-lg bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
<svg className="w-5 h-5" viewBox="0 0 24 24">
|
|
<path fill="currentColor" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/>
|
|
<path fill="currentColor" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/>
|
|
<path fill="currentColor" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/>
|
|
<path fill="currentColor" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/>
|
|
</svg>
|
|
<span className="ml-2">Google</span>
|
|
</button>
|
|
<button
|
|
type="button"
|
|
disabled={isLoading}
|
|
className="w-full inline-flex justify-center py-3 px-4 border border-gray-300 rounded-lg bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
|
<path d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"/>
|
|
</svg>
|
|
<span className="ml-2">Twitter</span>
|
|
</button>
|
|
</div>
|
|
</form>
|
|
|
|
{/* Toggle between Login and Signup */}
|
|
<div className="mt-6 text-center">
|
|
<p className="text-sm text-gray-600">
|
|
{isLogin ? "Don't have an account? " : "Already have an account? "}
|
|
<button
|
|
type="button"
|
|
onClick={() => {
|
|
setIsLogin(!isLogin);
|
|
setError(''); // Clear errors when switching modes
|
|
}}
|
|
disabled={isLoading}
|
|
className="text-[#4285F4] hover:text-[#1e40af] font-semibold transition-colors disabled:opacity-50"
|
|
>
|
|
{isLogin ? 'Sign up' : 'Sign in'}
|
|
</button>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Footer */}
|
|
<div className="text-center mt-6">
|
|
<p className="text-xs text-gray-500">
|
|
By continuing, you agree to our{' '}
|
|
<button className="text-[#4285F4] hover:text-[#1e40af] transition-colors">
|
|
Terms of Service
|
|
</button>{' '}
|
|
and{' '}
|
|
<button className="text-[#4285F4] hover:text-[#1e40af] transition-colors">
|
|
Privacy Policy
|
|
</button>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Login; |