iris_backend / src /pages /AppliLogin.jsx
sameer2026's picture
fix: ats resume builder UI and backend extraction handling
9d6cc86
import { supabase } from '../supabaseClient';
import React, { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
// --- SVG Icon Components ---
const EyeIcon = () => (<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></svg>);
const EyeOffIcon = () => (<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path><line x1="1" y1="1" x2="23" y2="23"></line></svg>);
export default function AppliLogin({ onNavigate }) {
const [mode, setMode] = useState('login');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
const [errors, setErrors] = useState({});
const [loading, setLoading] = useState(false);
// Notification State
const [notification, setNotification] = useState(null);
const validate = () => {
const newErrors = {};
if (!email.trim()) newErrors.email = 'Email is required.';
else if (!/\S+@\S+\.\S+/.test(email)) newErrors.email = 'Email is invalid.';
if (mode !== 'forgot') {
if (!password) newErrors.password = 'Password is required.';
}
if (mode === 'register') {
if (password !== confirmPassword) newErrors.confirmPassword = 'Passwords do not match.';
}
return newErrors;
};
const handleSubmit = async (e) => {
e.preventDefault();
const formErrors = validate();
if (Object.keys(formErrors).length > 0) {
setErrors(formErrors);
return;
}
setErrors({});
setLoading(true);
setNotification(null);
try {
if (mode === 'login') {
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
});
if (error) throw error;
// Check role
const role = data.user?.user_metadata?.role;
if (role === 'applicant') {
onNavigate('dashboard');
} else {
setNotification({ type: 'error', message: 'Unauthorized role detected.' });
await supabase.auth.signOut();
}
} else if (mode === 'register') {
const { error } = await supabase.auth.signUp({
email,
password,
options: { data: { role: 'applicant' } }
});
if (error) throw error;
setNotification({ type: 'success', message: 'Registration successful! Please confirm your Email and login.' });
setMode('login');
} else if (mode === 'forgot') {
const { error } = await supabase.auth.resetPasswordForEmail(email, {
redirectTo: `${window.location.origin}/reset-password`,
});
if (error) throw error;
setNotification({ type: 'success', message: 'Password reset link sent! Check your email.' });
setMode('login');
}
} catch (error) {
setNotification({ type: 'error', message: error.message });
} finally {
setLoading(false);
}
};
const formVariants = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 },
exit: { opacity: 0, y: -20 }
};
const notificationStyles = {
padding: '0.75rem 1rem', marginBottom: '1rem', borderRadius: '0.5rem',
fontSize: '0.875rem', textAlign: 'center', border: '1px solid',
};
return (
<div style={{ position: 'relative', minHeight: '100vh', width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '1rem', overflow: 'hidden', backgroundColor: '#020617', color: 'white', fontFamily: "'Montserrat', sans-serif" }}>
<style>{`@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&display=swap'); input:-webkit-autofill, input:-webkit-autofill:hover, input:-webkit-autofill:focus, input:-webkit-autofill:active { transition: background-color 5000s ease-in-out 0s; -webkit-text-fill-color: #fff !important; }`}</style>
{/* --- 🔴 FIXED BACK BUTTON (Z-INDEX 999) --- */}
<button
onClick={() => onNavigate('login')} // Ensure this matches your prop name
style={{
position: 'fixed', // Changed from absolute to fixed
top: '20px',
left: '20px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '8px',
padding: '12px 20px',
backgroundColor: '#3a2f04',
color: '#cebe65',
border: '2px solid #3a2f04',
borderRadius: '10px',
fontWeight: '700',
cursor: 'pointer',
zIndex: 9999, // Super high Z-Index
boxShadow: '0 4px 10px rgba(0,0,0,0.3)'
}}
>
<span></span> Back
</button>
{/* Background Orbs */}
<>
<div style={{ position: 'absolute', borderRadius: '50%', filter: 'blur(80px)', opacity: 0.4, width: '384px', height: '384px', backgroundColor: '#FBBF24', top: '-50px', left: '-100px' }}></div>
<div style={{ position: 'absolute', borderRadius: '50%', filter: 'blur(80px)', opacity: 0.4, width: '320px', height: '320px', backgroundColor: '#F59E0B', bottom: '-80px', right: '-120px' }}></div>
</>
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.4 }}
style={{ width: '100%', maxWidth: '512px', borderRadius: '1rem', backgroundColor: 'rgba(251, 191, 36, 0.1)', backdropFilter: 'blur(12px)', border: '1px solid rgba(251, 191, 36, 0.3)', padding: '2rem', zIndex: 10 }}>
{/* Notification */}
<AnimatePresence>
{notification && (
<motion.div
initial={{ opacity: 0, y: -10 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -10 }}
style={{
...notificationStyles,
backgroundColor: notification.type === 'success' ? 'rgba(16, 185, 129, 0.1)' : 'rgba(239, 68, 68, 0.1)',
borderColor: notification.type === 'success' ? 'rgba(5, 150, 105, 0.3)' : 'rgba(220, 38, 38, 0.3)',
color: notification.type === 'success' ? '#34D399' : '#F87171'
}}
>
{notification.message}
</motion.div>
)}
</AnimatePresence>
<AnimatePresence mode="wait">
<motion.div key={mode} variants={formVariants} initial="hidden" animate="visible" exit="exit" transition={{ duration: 0.3 }}>
{/* --- Login Form --- */}
{mode === 'login' && (
<div>
<h1 style={{ fontSize: '0.875rem', fontWeight: 'bold', color: '#FCD34D', marginBottom: '0.5rem' }}>IRIS</h1>
<h2 style={{ fontSize: '1.875rem', fontWeight: 'bold', marginBottom: '1.5rem', textAlign: 'center' }}>Applicant Portal</h2>
<form onSubmit={handleSubmit}>
<div style={{ marginBottom: '1rem' }}>
<label style={{ display: 'block', fontSize: '0.875rem', fontWeight: '500', marginBottom: '0.5rem', color: '#d1d5db' }}>Email</label>
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="lead@example.com" disabled={loading} style={{ width: '100%', padding: '0.75rem 1rem', backgroundColor: 'rgba(255, 255, 255, 0.1)', border: `1px solid ${errors.email ? '#EF4444' : 'rgba(251, 191, 36, 0.3)'}`, borderRadius: '0.5rem', color: 'white' }} />
{errors.email && <p style={{ color: '#F87171', fontSize: '0.75rem', marginTop: '0.5rem' }}>{errors.email}</p>}
</div>
<div style={{ marginBottom: '1.5rem' }}>
<label style={{ display: 'block', fontSize: '0.875rem', fontWeight: '500', marginBottom: '0.5rem', color: '#d1d5db' }}>Password</label>
<div style={{ position: 'relative' }}>
<input type={isPasswordVisible ? 'text' : 'password'} value={password} onChange={(e) => setPassword(e.target.value)} placeholder="••••••••" disabled={loading} style={{ width: '100%', padding: '0.75rem 3rem 0.75rem 1rem', backgroundColor: 'rgba(255, 255, 255, 0.1)', border: `1px solid ${errors.password ? '#EF4444' : 'rgba(251, 191, 36, 0.3)'}`, borderRadius: '0.5rem', color: 'white' }} />
<button type="button" onClick={() => setIsPasswordVisible(!isPasswordVisible)} style={{ position: 'absolute', right: '0.5rem', top: '50%', transform: 'translateY(-50%)', background: 'none', border: 'none', color: '#FCD34D', cursor: 'pointer' }}>{isPasswordVisible ? <EyeOffIcon /> : <EyeIcon />}</button>
</div>
{errors.password ? <p style={{ color: '#F87171', fontSize: '0.75rem', marginTop: '0.5rem' }}>{errors.password}</p> : <button type="button" onClick={() => setMode('forgot')} style={{ fontSize: '0.75rem', color: '#FCD34D', background: 'none', border: 'none', cursor: 'pointer', float: 'right', marginTop: '0.5rem' }}>Forgot Password?</button>}
</div>
<motion.button type="submit" whileHover={{ scale: 1.03 }} whileTap={{ scale: 0.98 }} disabled={loading} style={{ width: '100%', backgroundColor: '#FBBF24', color: '#1a202c', fontWeight: 'bold', padding: '0.75rem 0', borderRadius: '0.5rem', border: 'none', cursor: 'pointer', marginTop: '1rem' }}>{loading ? 'Signing In...' : 'Sign In'}</motion.button>
</form>
<p style={{ fontSize: '0.75rem', textAlign: 'center', color: '#9ca3af', marginTop: '2rem' }}>Don't have an account? <button onClick={() => setMode('register')} style={{ color: '#FCD34D', fontWeight: '600', background: 'none', border: 'none', cursor: 'pointer' }}>Register For Free</button></p>
</div>
)}
{/* --- Register Form --- */}
{mode === 'register' && (
<div>
<h2 style={{ fontSize: '1.875rem', fontWeight: 'bold', marginBottom: '1.5rem', textAlign: 'center' }}>Create Account</h2>
<form onSubmit={handleSubmit}>
<div style={{ marginBottom: '1rem' }}>
<label style={{ display: 'block', fontSize: '0.875rem', fontWeight: '500', marginBottom: '0.5rem', color: '#d1d5db' }}>Email</label>
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} disabled={loading} style={{ width: '100%', padding: '0.75rem 1rem', backgroundColor: 'rgba(255, 255, 255, 0.1)', border: `1px solid ${errors.email ? '#EF4444' : 'rgba(251, 191, 36, 0.3)'}`, borderRadius: '0.5rem', color: 'white' }} />
</div>
<div style={{ marginBottom: '1rem' }}>
<label style={{ display: 'block', fontSize: '0.875rem', fontWeight: '500', marginBottom: '0.5rem', color: '#d1d5db' }}>Password</label>
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} disabled={loading} style={{ width: '100%', padding: '0.75rem 1rem', backgroundColor: 'rgba(255, 255, 255, 0.1)', border: `1px solid ${errors.password ? '#EF4444' : 'rgba(251, 191, 36, 0.3)'}`, borderRadius: '0.5rem', color: 'white' }} />
</div>
<div style={{ marginBottom: '1.5rem' }}>
<label style={{ display: 'block', fontSize: '0.875rem', fontWeight: '500', marginBottom: '0.5rem', color: '#d1d5db' }}>Confirm Password</label>
<input type="password" value={confirmPassword} onChange={(e) => setConfirmPassword(e.target.value)} disabled={loading} style={{ width: '100%', padding: '0.75rem 1rem', backgroundColor: 'rgba(255, 255, 255, 0.1)', border: `1px solid ${errors.confirmPassword ? '#EF4444' : 'rgba(251, 191, 36, 0.3)'}`, borderRadius: '0.5rem', color: 'white' }} />
</div>
<motion.button type="submit" whileHover={{ scale: 1.03 }} whileTap={{ scale: 0.98 }} disabled={loading} style={{ width: '100%', backgroundColor: '#FBBF24', color: '#1a202c', fontWeight: 'bold', padding: '0.75rem 0', borderRadius: '0.5rem', border: 'none', cursor: 'pointer' }}>{loading ? 'Registering...' : 'Register'}</motion.button>
</form>
<p style={{ fontSize: '0.75rem', textAlign: 'center', color: '#9ca3af', marginTop: '2rem' }}>Already have an account? <button onClick={() => setMode('login')} style={{ color: '#FCD34D', fontWeight: '600', background: 'none', border: 'none', cursor: 'pointer' }}>Sign In</button></p>
</div>
)}
{/* --- Forgot Password --- */}
{mode === 'forgot' && (
<div>
<h2 style={{ fontSize: '1.875rem', fontWeight: 'bold', marginBottom: '1.5rem', textAlign: 'center' }}>Forgot Password</h2>
<form onSubmit={handleSubmit}>
<div style={{ marginBottom: '1.5rem' }}>
<label style={{ display: 'block', fontSize: '0.875rem', fontWeight: '500', marginBottom: '0.5rem', color: '#d1d5db' }}>Email</label>
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} disabled={loading} style={{ width: '100%', padding: '0.75rem 1rem', backgroundColor: 'rgba(255, 255, 255, 0.1)', border: `1px solid ${errors.email ? '#EF4444' : 'rgba(251, 191, 36, 0.3)'}`, borderRadius: '0.5rem', color: 'white' }} />
</div>
<motion.button type="submit" whileHover={{ scale: 1.03 }} whileTap={{ scale: 0.98 }} disabled={loading} style={{ width: '100%', backgroundColor: '#FBBF24', color: '#1a202c', fontWeight: 'bold', padding: '0.75rem 0', borderRadius: '0.5rem', border: 'none', cursor: 'pointer' }}>{loading ? 'Sending...' : 'Send Reset Link'}</motion.button>
</form>
<p style={{ fontSize: '0.75rem', textAlign: 'center', color: '#9ca3af', marginTop: '2rem' }}><button onClick={() => setMode('login')} style={{ color: '#FCD34D', fontWeight: '600', background: 'none', border: 'none', cursor: 'pointer' }}>Back to Sign In</button></p>
</div>
)}
</motion.div>
</AnimatePresence>
</motion.div>
</div>
);
}