| import React, { useState } from 'react';
|
| import { X, Mail, Lock, ArrowRight, Loader2, CheckCircle } from 'lucide-react';
|
| import { motion, AnimatePresence } from 'framer-motion';
|
|
|
| const LoginModal = ({ isOpen, onClose, onLoginSuccess }) => {
|
| const [isLoading, setIsLoading] = useState(false);
|
| const [email, setEmail] = useState('');
|
| const [password, setPassword] = useState('');
|
|
|
| const handleLogin = (e) => {
|
| e.preventDefault();
|
| setIsLoading(true);
|
|
|
| setTimeout(() => {
|
| setIsLoading(false);
|
| onLoginSuccess();
|
| }, 1500);
|
| };
|
|
|
| const handleGoogleLogin = () => {
|
| setIsLoading(true);
|
|
|
| setTimeout(() => {
|
| setIsLoading(false);
|
| onLoginSuccess();
|
| }, 1500);
|
| };
|
|
|
| return (
|
| <AnimatePresence>
|
| {isOpen && (
|
| <div className="fixed inset-0 z-[100] flex items-center justify-center p-4">
|
| {/* Backdrop */}
|
| <motion.div
|
| initial={{ opacity: 0 }}
|
| animate={{ opacity: 1 }}
|
| exit={{ opacity: 0 }}
|
| onClick={onClose}
|
| className="absolute inset-0 bg-slate-900/40 backdrop-blur-sm"
|
| />
|
|
|
| {/* Modal */}
|
| <motion.div
|
| initial={{ opacity: 0, scale: 0.95, y: 20 }}
|
| animate={{ opacity: 1, scale: 1, y: 0 }}
|
| exit={{ opacity: 0, scale: 0.95, y: 20 }}
|
| className="relative w-full max-w-md bg-white rounded-2xl shadow-2xl overflow-hidden border border-slate-100 z-10"
|
| >
|
| {/* Header */}
|
| <div className="bg-slate-50 px-8 py-6 border-b border-slate-100 flex justify-between items-center">
|
| <div>
|
| <h2 className="text-xl font-bold text-slate-800">Welcome Back</h2>
|
| <p className="text-sm text-slate-500">Sign in to continue your research</p>
|
| </div>
|
| <button
|
| onClick={onClose}
|
| className="p-2 hover:bg-slate-200 rounded-full text-slate-400 hover:text-slate-600 transition-colors"
|
| >
|
| <X size={20} />
|
| </button>
|
| </div>
|
|
|
| {/* Body */}
|
| <div className="p-8">
|
| {/* Google Button */}
|
| <button
|
| onClick={handleGoogleLogin}
|
| disabled={isLoading}
|
| className="w-full flex items-center justify-center gap-3 bg-white border border-slate-200 text-slate-700 font-medium py-3 rounded-xl hover:bg-slate-50 hover:border-slate-300 transition-all shadow-sm group"
|
| >
|
| {isLoading ? (
|
| <Loader2 size={20} className="animate-spin text-slate-400" />
|
| ) : (
|
| <>
|
| <svg className="w-5 h-5" viewBox="0 0 24 24">
|
| <path
|
| 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"
|
| fill="#4285F4"
|
| />
|
| <path
|
| 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"
|
| fill="#34A853"
|
| />
|
| <path
|
| 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"
|
| fill="#FBBC05"
|
| />
|
| <path
|
| 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"
|
| fill="#EA4335"
|
| />
|
| </svg>
|
| <span>Continue with Google</span>
|
| </>
|
| )}
|
| </button>
|
|
|
| <div className="flex items-center gap-4 my-6">
|
| <div className="h-px bg-slate-100 flex-1"></div>
|
| <span className="text-xs text-slate-400 font-medium">OR</span>
|
| <div className="h-px bg-slate-100 flex-1"></div>
|
| </div>
|
|
|
| {/* Email Form */}
|
| <form onSubmit={handleLogin} className="space-y-4">
|
| <div>
|
| <label className="block text-xs font-semibold text-slate-500 mb-1.5 uppercase tracking-wider">Email Address</label>
|
| <div className="relative">
|
| <Mail size={18} className="absolute left-3.5 top-1/2 -translate-y-1/2 text-slate-400" />
|
| <input
|
| type="email"
|
| required
|
| value={email}
|
| onChange={(e) => setEmail(e.target.value)}
|
| className="w-full pl-10 pr-4 py-3 bg-slate-50 border border-slate-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all text-sm text-slate-900 placeholder:text-slate-400"
|
| placeholder="researcher@university.edu"
|
| />
|
| </div>
|
| </div>
|
| <div>
|
| <label className="block text-xs font-semibold text-slate-500 mb-1.5 uppercase tracking-wider">Password</label>
|
| <div className="relative">
|
| <Lock size={18} className="absolute left-3.5 top-1/2 -translate-y-1/2 text-slate-400" />
|
| <input
|
| type="password"
|
| required
|
| value={password}
|
| onChange={(e) => setPassword(e.target.value)}
|
| className="w-full pl-10 pr-4 py-3 bg-slate-50 border border-slate-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all text-sm text-slate-900 placeholder:text-slate-400"
|
| placeholder="••••••••"
|
| />
|
| </div>
|
| </div>
|
|
|
| <button
|
| type="submit"
|
| disabled={isLoading}
|
| className="w-full bg-slate-900 text-white font-medium py-3 rounded-xl hover:bg-slate-800 transition-all shadow-lg shadow-slate-900/20 flex items-center justify-center gap-2 mt-2 disabled:opacity-70 disabled:cursor-not-allowed"
|
| >
|
| {isLoading ? (
|
| <>
|
| <Loader2 size={18} className="animate-spin" />
|
| Verifying...
|
| </>
|
| ) : (
|
| <>
|
| Sign In <ArrowRight size={18} />
|
| </>
|
| )}
|
| </button>
|
| </form>
|
| </div>
|
|
|
| <div className="bg-slate-50 px-8 py-4 border-t border-slate-100 text-center">
|
| <p className="text-xs text-slate-500">
|
| Don't have an account? <button className="text-blue-600 font-semibold hover:underline">Sign up for free</button>
|
| </p>
|
| </div>
|
| </motion.div>
|
| </div>
|
| )}
|
| </AnimatePresence>
|
| );
|
| };
|
|
|
| export default LoginModal;
|
|
|