الملفات
dashboard/src/pages/login.tsx
2025-10-08 09:29:32 +00:00

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;