Vertical.ai / frontend /src /components /LoginModal.jsx
Abhisingh-18's picture
Mirror of github.com/Abhisingh18/Vertical.ai
1f7ead8 verified
Raw
History Blame Contribute Delete
9.63 kB
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);
// Simulate API call
setTimeout(() => {
setIsLoading(false);
onLoginSuccess();
}, 1500);
};
const handleGoogleLogin = () => {
setIsLoading(true);
// Simulate Google Auth
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;