Files
Event-Management-System/RIT-EVENT-MANAGEMENT--main/components/LoginForm.tsx

453 lines
25 KiB
TypeScript

import React, { useState, useEffect, useRef } from 'react';
import { UserRole } from '../types';
import { supabase } from '../supabase';
const DEPARTMENTS = ['AIDS', 'CSBS', 'CSE', 'CCE', 'MECH', 'VLSI', 'BIO-TECH', 'AIML', 'ECE', 'H&S'];
const EXTERNAL_DEPARTMENTS = [
...DEPARTMENTS,
'Information Technology (IT)',
'Electrical & Electronics Engineering (EEE)',
'Civil Engineering',
'Biomedical Engineering',
'Chemical Engineering',
'Aeronautical / Aerospace Engineering',
'Mechatronics Engineering',
'Others'
];
const SECTIONS = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'];
const YEARS = ['1st Year', '2nd Year', '3rd Year', '4th Year', '5th Year'];
interface LoginFormProps {
role: UserRole;
onSuccess: () => void;
onBack: () => void;
}
const GoogleIcon = () => (
<svg className="w-5 h-5 mr-3" viewBox="0 0 24 24" width="24" height="24" xmlns="http://www.w3.org/2000/svg">
<g transform="matrix(1, 0, 0, 1, 0, 0)">
<path d="M21.35,11.1H12v2.7h5.38c-0.24,1.28 -0.96,2.37 -2.04,3.1v2.58h3.3c1.93,-1.78 3.04,-4.4 3.04,-7.48C21.68,11.96 21.56,11.49 21.35,11.1z" fill="#4285F4" />
<path d="M12,20.58c2.43,0 4.47,-0.8 5.96,-2.2l-2.58,-2c-0.72,0.48 -1.64,0.77 -2.58,0.77 -2.37,0 -4.38,-1.6 -5.1,-3.75H4.31v2.1a8.4,8.4 0 0,0 7.69,5.08z" fill="#34A853" />
<path d="M6.9,13.4c-0.18,-0.54 -0.29,-1.11 -0.29,-1.7 0,-0.59 0.11,-1.16 0.29,-1.7V7.9H4.31A8.4,8.4 0 0,0 3.3,11.7c0,1.38 0.33,2.69 1.01,3.8l2.59,-2.1z" fill="#FBBC05" />
<path d="M12,6.85c1.32,0 2.5,0.45 3.44,1.35l2.58,-2.58C16.46,4.1 14.43,3.32 12,3.32c-4.79,0 -8.7,2.82 -10.39,6.9l2.59,2.1c0.72,-2.15 2.73,-3.75 5.1,-3.75z" fill="#EA4335" />
</g>
</svg>
);
const LoginForm: React.FC<LoginFormProps> = ({ role, onSuccess, onBack }) => {
const [isSignUp, setIsSignUp] = useState(false);
const [signUpType, setSignUpType] = useState<'INTERNAL' | 'EXTERNAL' | null>(null);
const [coordinatorLoginType, setCoordinatorLoginType] = useState<'Faculty' | 'HOD' | null>(null);
const [formData, setFormData] = useState({
email: '',
phone: '',
regNo: '',
password: '',
confirmPassword: '',
name: '',
collegeName: 'Rajalakshmi Institute of Technology',
department: '',
section: '',
year: '',
gender: '',
collegeLocation: '',
captchaInput: '',
});
// Auto-set internal for non-student roles
useEffect(() => {
if (role !== 'STUDENT') {
setSignUpType('INTERNAL');
} else if (!isSignUp) {
setSignUpType(null);
}
}, [role, isSignUp]);
const [showPassword, setShowPassword] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const handleGoogleSignIn = async () => {
setErrorMessage(null);
setIsSubmitting(true);
try {
const { data, error } = await supabase.auth.signInWithOAuth({ provider: 'google' });
if (error) throw error;
if (data?.user) {
onSuccess();
}
} catch (err: any) {
console.error("Google Auth error:", err);
if (err.code !== 'auth/popup-closed-by-user') {
setErrorMessage(err.message || "Google Sign-In failed.");
}
} finally {
setIsSubmitting(false);
}
};
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setErrorMessage(null);
setIsSubmitting(true);
try {
if (!isSignUp) {
// Login Logic
const { data: loginData, error } = await supabase.auth.signInWithPassword({
email: formData.email,
password: formData.password
});
if (error) throw error;
if (role === 'COORDINATOR' && coordinatorLoginType && loginData.user) {
const { data: faculty } = await supabase.from('Facultyusers').select('role').eq('id', loginData.user.id).single();
if (!faculty || (faculty.role !== coordinatorLoginType && faculty.role !== 'System Admin')) {
await supabase.auth.signOut();
throw new Error(`Access Denied: You are not provisioned as ${coordinatorLoginType}.`);
}
}
// For students, ensure they have a profile in Studentusers
if (role === 'STUDENT' && signUpType === 'INTERNAL' && loginData.user) {
const { data: existing } = await supabase.from('Studentusers').select('id').eq('id', loginData.user.id).single();
if (!existing) {
const { error: profileError } = await supabase.from('Studentusers').insert({
id: loginData.user.id,
name: formData.name,
email: formData.email,
reg_no: formData.regNo,
phone: formData.phone,
department: formData.department,
year: formData.year,
section: formData.section,
college_name: 'RAJALAKSHMI INSTITUTE OF TECHNOLOGY'
});
if (profileError) console.error("Profile creation error:", profileError);
}
}
onSuccess();
} else {
// Sign-Up Logic
const { data: signUpData, error } = await supabase.auth.signUp({
email: formData.email,
password: formData.password,
options: {
data: {
name: formData.name,
role: signUpType === 'EXTERNAL' ? 'EXTERNAL_STUDENT' : role,
regNo: formData.regNo,
phone: formData.phone,
gender: formData.gender,
college: formData.collegeName,
collegeLocation: formData.collegeLocation,
department: formData.department,
year: formData.year,
section: formData.section
}
}
});
if (error) throw error;
if (signUpData.user) {
if (signUpType === 'EXTERNAL') {
await supabase.from('externalusers').insert({
id: signUpData.user.id,
name: formData.name,
email: formData.email,
reg_no: formData.regNo,
phone: formData.phone,
department: formData.department,
year: formData.year,
section: formData.section,
college: formData.collegeName,
college_location: formData.collegeLocation,
gender: formData.gender
});
} else if (role === 'STUDENT') {
await supabase.from('Studentusers').insert({
id: signUpData.user.id,
name: formData.name,
email: formData.email,
reg_no: formData.regNo,
phone: formData.phone,
department: formData.department,
year: formData.year,
section: formData.section,
college_name: 'RAJALAKSHMI INSTITUTE OF TECHNOLOGY'
});
}
}
alert("Verification link sent to your email. Please verify to continue.");
setIsSignUp(false);
if (role === 'STUDENT') setSignUpType(null);
}
} catch (err: any) {
setErrorMessage(err.message || "An error occurred.");
} finally {
setIsSubmitting(false);
}
};
const RIT_BLUE = 'bg-[#004a99]';
const RIT_BLUE_TEXT = 'text-[#004a99]';
const RIT_BLUE_HOVER = 'hover:bg-[#003366]';
const RIT_BLUE_BORDER = 'border-[#004a99]';
return (
<div className="min-h-screen w-full bg-slate-50 flex items-center justify-center p-4 md:p-10 font-inter">
{/* Main Container */}
<div className="relative w-full max-w-5xl h-[700px] bg-white rounded-[3rem] shadow-[0_50px_100px_-20px_rgba(0,0,0,0.15)] overflow-hidden flex flex-col md:flex-row border border-slate-100">
{/* Back Button */}
<button
onClick={onBack}
className="absolute top-8 left-8 z-[60] w-12 h-12 bg-white/10 backdrop-blur-md border border-white/20 rounded-full flex items-center justify-center text-white hover:bg-white/20 transition-all"
>
<i className="fas fa-arrow-left"></i>
</button>
{/* Forms Container */}
<div className="relative flex-1 flex">
{/* Sign Up Form (Left Side) */}
<div className={`absolute inset-0 w-full md:w-1/2 h-full flex flex-col justify-center p-12 transition-all duration-700 ease-in-out z-10 ${isSignUp ? 'opacity-100 translate-x-0 visible' : 'opacity-0 -translate-x-full invisible pointer-events-none'}`}>
<img
src="https://www.facultyplus.com/wp-content/uploads/2020/11/Rit-logo.png"
alt="RIT Logo"
className="h-12 w-auto mb-6 object-contain"
/>
{!signUpType && role === 'STUDENT' ? (
<div className="space-y-6 text-center animate-in fade-in zoom-in-95 duration-500">
<h2 className="text-3xl font-black text-slate-900 uppercase tracking-tighter mb-8">Choose <span className={RIT_BLUE_TEXT}>Portal</span></h2>
<div className="grid gap-4">
<button
onClick={() => setSignUpType('INTERNAL')}
className={`w-full py-6 ${RIT_BLUE} text-white rounded-2xl font-black uppercase text-xs tracking-[0.2em] shadow-lg transition-all hover:scale-[1.02] active:scale-95`}
>
RIT Student (Internal)
</button>
<button
onClick={() => setSignUpType('EXTERNAL')}
className={`w-full py-6 bg-white border-2 border-slate-200 text-slate-700 rounded-2xl font-black uppercase text-xs tracking-[0.2em] transition-all hover:${RIT_BLUE_BORDER} hover:${RIT_BLUE_TEXT} hover:scale-[1.02] active:scale-95`}
>
Other College (External)
</button>
</div>
</div>
) : (
<form onSubmit={handleSubmit} className="space-y-4 overflow-y-auto pr-3 py-4 scrollbar-hide">
<div className="mb-6">
<div className="flex items-center justify-between">
<h2 className="text-3xl font-black text-slate-900 uppercase tracking-tighter">
{role === 'STUDENT' ? (signUpType === 'INTERNAL' ? 'Internal' : 'External') : (role === 'COORDINATOR' ? 'Event Coordinator' : 'Admin')} <span className={role === 'STUDENT' ? RIT_BLUE_TEXT : 'text-orange-500'}>Sign Up</span>
</h2>
{role === 'STUDENT' && <button type="button" onClick={() => setSignUpType(null)} className={`text-[10px] font-black text-slate-400 uppercase hover:${RIT_BLUE_TEXT}`}>Change</button>}
</div>
<p className="text-slate-400 text-[10px] font-bold uppercase tracking-widest mt-2">Join the RIT Excellence Hub</p>
</div>
<div className="grid grid-cols-2 gap-4">
<input required name="name" placeholder="FULL NAME" className="bg-slate-50 border-none rounded-2xl px-6 py-4 text-xs font-bold outline-none focus:ring-2 focus:ring-[#004a99] transition-all" value={formData.name} onChange={handleInputChange} />
<input required type="email" name="email" placeholder="EMAIL" className="bg-slate-50 border-none rounded-2xl px-6 py-4 text-xs font-bold outline-none focus:ring-2 focus:ring-[#004a99] transition-all" value={formData.email} onChange={handleInputChange} />
</div>
<div className="grid grid-cols-2 gap-4">
<input required name="regNo" placeholder="REG NO" className="bg-slate-50 border-none rounded-2xl px-6 py-4 text-xs font-bold outline-none focus:ring-2 focus:ring-[#004a99] transition-all" value={formData.regNo} onChange={handleInputChange} />
<input required name="phone" placeholder="PHONE NUMBER" className="bg-slate-50 border-none rounded-2xl px-6 py-4 text-xs font-bold outline-none focus:ring-2 focus:ring-[#004a99] transition-all" value={formData.phone} onChange={handleInputChange} />
</div>
<div className="grid grid-cols-2 gap-4">
<input required type="password" name="password" placeholder="PASSWORD" className="bg-slate-50 border-none rounded-2xl px-6 py-4 text-xs font-bold outline-none focus:ring-2 focus:ring-[#004a99] transition-all" value={formData.password} onChange={handleInputChange} />
<input required type="password" name="confirmPassword" placeholder="CONFIRM" className="bg-slate-50 border-none rounded-2xl px-6 py-4 text-xs font-bold outline-none focus:ring-2 focus:ring-[#004a99] transition-all" value={formData.confirmPassword} onChange={handleInputChange} />
</div>
<div className="grid grid-cols-2 gap-4">
<select required name="gender" className="bg-slate-50 border-none rounded-2xl px-6 py-4 text-xs font-bold outline-none focus:ring-2 focus:ring-[#004a99] transition-all" value={formData.gender} onChange={handleInputChange}>
<option value="">SELECT GENDER</option>
<option value="Male">MALE</option>
<option value="Female">FEMALE</option>
<option value="Other">OTHER</option>
</select>
<input required name="collegeLocation" placeholder="COLLEGE LOCATION" className="bg-slate-50 border-none rounded-2xl px-6 py-4 text-xs font-bold outline-none focus:ring-2 focus:ring-[#004a99] transition-all" value={formData.collegeLocation} onChange={handleInputChange} />
</div>
{(role === 'STUDENT' || signUpType === 'EXTERNAL') && (
<>
<input
required
name="collegeName"
placeholder="COLLEGE NAME"
disabled={signUpType === 'INTERNAL'}
className="w-full bg-slate-50 border-none rounded-2xl px-6 py-4 text-xs font-bold outline-none focus:ring-2 focus:ring-[#004a99] transition-all disabled:opacity-60"
value={formData.collegeName}
onChange={handleInputChange}
/>
<div className="grid grid-cols-3 gap-4">
<select required name="department" className="bg-slate-50 border-none rounded-2xl px-4 py-4 text-[10px] font-black outline-none" value={formData.department} onChange={handleInputChange}>
<option value="">DEPT</option>
{(signUpType === 'EXTERNAL' ? EXTERNAL_DEPARTMENTS : DEPARTMENTS).map(d => <option key={d} value={d}>{d}</option>)}
</select>
<select required name="year" className="bg-slate-50 border-none rounded-2xl px-4 py-4 text-[10px] font-black outline-none" value={formData.year} onChange={handleInputChange}>
<option value="">YEAR</option>
{YEARS.map(y => <option key={y} value={y}>{y}</option>)}
</select>
<select required name="section" className="bg-slate-50 border-none rounded-2xl px-4 py-4 text-[10px] font-black outline-none" value={formData.section} onChange={handleInputChange}>
<option value="">SEC</option>
{SECTIONS.map(s => <option key={s} value={s}>{s}</option>)}
</select>
</div>
</>
)}
{errorMessage && <p className="text-rose-500 text-[10px] font-bold uppercase">{errorMessage}</p>}
<button type="submit" disabled={isSubmitting} className={`w-full py-5 ${role === 'STUDENT' ? `${RIT_BLUE} hover:bg-[#003366] shadow-blue-900/20` : 'bg-orange-500 hover:bg-orange-600 shadow-orange-500/20'} text-white text-[10px] font-black uppercase tracking-[0.3em] rounded-2xl transition-all shadow-lg active:scale-95`}>
{isSubmitting ? 'Initializing...' : 'Sign Up Now'}
</button>
</form>
)}
</div>
{/* Sign In Form (Right Side) */}
<div className={`absolute inset-0 w-full md:w-1/2 h-full flex flex-col justify-center p-12 transition-all duration-700 ease-in-out z-10 ml-auto ${!isSignUp ? 'opacity-100 translate-x-0 visible' : 'opacity-0 translate-x-full invisible pointer-events-none'}`}>
{role === 'COORDINATOR' && !coordinatorLoginType ? (
<div className="space-y-6 text-center animate-in fade-in zoom-in-95 duration-500">
<img
src="https://www.facultyplus.com/wp-content/uploads/2020/11/Rit-logo.png"
alt="RIT Logo"
className="h-12 w-auto mb-6 object-contain mx-auto"
/>
<h2 className="text-3xl font-black text-slate-900 uppercase tracking-tighter mb-8">Choose <span className="text-orange-500">Access Level</span></h2>
<div className="grid gap-4">
<button
onClick={() => setCoordinatorLoginType('Faculty')}
type="button"
className={`w-full py-6 ${RIT_BLUE} text-white rounded-2xl font-black uppercase text-xs tracking-[0.2em] shadow-lg transition-all hover:scale-[1.02] active:scale-95`}
>
Faculty Member
</button>
<button
onClick={() => setCoordinatorLoginType('HOD')}
type="button"
className="w-full py-6 bg-orange-500 text-white rounded-2xl font-black uppercase text-xs tracking-[0.2em] shadow-lg transition-all hover:scale-[1.02] active:scale-95"
>
Head of Department
</button>
</div>
</div>
) : (
<form onSubmit={handleSubmit} className="space-y-6">
<div className="mb-8">
<img
src="https://www.facultyplus.com/wp-content/uploads/2020/11/Rit-logo.png"
alt="RIT Logo"
className="h-12 w-auto mb-6 object-contain"
/>
<div className="flex items-center justify-between">
<h2 className="text-4xl font-black text-slate-900 uppercase tracking-tighter">Welcome <span className={RIT_BLUE_TEXT}>Back</span></h2>
{role === 'COORDINATOR' && (
<button type="button" onClick={() => setCoordinatorLoginType(null)} className="text-[10px] font-black text-slate-400 uppercase hover:text-orange-500">Change</button>
)}
</div>
<p className="text-slate-400 text-xs font-bold uppercase tracking-widest mt-2">
Access your {role === 'ADMIN' ? 'Admin' : (role === 'COORDINATOR' ? `${coordinatorLoginType} Event` : 'Student')} portal
</p>
</div>
<div className="space-y-4">
<div className="relative">
<i className="fas fa-envelope absolute left-6 top-1/2 -translate-y-1/2 text-slate-300"></i>
<input required type="email" name="email" placeholder="EMAIL ADDRESS" className="w-full bg-slate-50 border-none rounded-2xl pl-14 pr-6 py-5 text-xs font-bold outline-none focus:ring-2 focus:ring-[#004a99] transition-all" value={formData.email} onChange={handleInputChange} />
</div>
<div className="relative">
<i className="fas fa-lock absolute left-6 top-1/2 -translate-y-1/2 text-slate-300"></i>
<input required type={showPassword ? 'text' : 'password'} name="password" placeholder="PASSWORD" className="w-full bg-slate-50 border-none rounded-2xl pl-14 pr-14 py-5 text-xs font-bold outline-none focus:ring-2 focus:ring-[#004a99] transition-all" value={formData.password} onChange={handleInputChange} />
<button type="button" onClick={() => setShowPassword(!showPassword)} className={`absolute right-6 top-1/2 -translate-y-1/2 text-slate-300 hover:${RIT_BLUE_TEXT} transition-colors`}>
<i className={`fas ${showPassword ? 'fa-eye-slash' : 'fa-eye'}`}></i>
</button>
</div>
</div>
<div className="flex justify-end">
<button type="button" className="text-[10px] font-black text-slate-400 uppercase tracking-widest hover:text-orange-500 transition-colors">Forgot Password?</button>
</div>
{errorMessage && <p className="text-rose-500 text-[10px] font-bold uppercase">{errorMessage}</p>}
<button type="submit" disabled={isSubmitting} className={`w-full py-5 ${RIT_BLUE} text-white text-[10px] font-black uppercase tracking-[0.3em] rounded-2xl ${RIT_BLUE_HOVER} transition-all shadow-lg shadow-blue-900/20 active:scale-95`}>
{isSubmitting ? 'Verifying...' : 'Sign In Now'}
</button>
{/* Separator */}
<div className="w-full flex items-center justify-center gap-4">
<div className="h-px bg-slate-100 flex-1" />
<span className="text-[9px] font-black text-slate-300 uppercase tracking-widest">or use Google</span>
<div className="h-px bg-slate-100 flex-1" />
</div>
{/* Google Sign In Button */}
<button
type="button"
onClick={handleGoogleSignIn}
disabled={isSubmitting}
className="w-full py-4.5 px-6 bg-white border border-slate-200 hover:border-slate-300 rounded-2xl flex items-center justify-center text-[10px] font-black text-slate-700 uppercase tracking-[0.2em] hover:bg-slate-50 transition-all hover:scale-[1.02] active:scale-[0.98] shadow-sm"
>
<GoogleIcon />
Sign in with Google
</button>
</form>
)}
</div>
{/* Sliding Overlay Panel */}
<div className={`absolute top-0 left-0 w-full md:w-1/2 h-full ${RIT_BLUE} z-50 transition-all duration-700 ease-[cubic-bezier(0.7,0,0.3,1)] flex flex-col items-center justify-center text-center p-12 overflow-hidden ${isSignUp ? 'md:translate-x-full' : 'translate-x-0'}`}>
{/* Background Pattern */}
<div className="absolute inset-0 opacity-10 pointer-events-none">
<div className="absolute top-0 left-0 w-full h-full bg-[radial-gradient(circle_at_center,_white_1px,_transparent_1px)] bg-[size:30px_30px]"></div>
</div>
<div className={`absolute inset-0 flex flex-col items-center justify-center p-12 transition-all duration-700 delay-100 ${isSignUp ? 'opacity-0 -translate-y-10 pointer-events-none invisible' : 'opacity-100 translate-y-0 pointer-events-auto visible'}`}>
<h2 className="text-4xl font-black text-white uppercase tracking-tighter mb-4">{role === 'STUDENT' ? 'New Here?' : 'Restricted Access'}</h2>
<p className="text-white/80 text-sm font-medium leading-relaxed mb-10 max-w-xs mx-auto">
{role === 'STUDENT'
? 'Sign up and discover a world of possibilities at RIT Events Hub.'
: 'Sign up is restricted for this portal. Please contact the administrator to provision an account.'}
</p>
{role === 'STUDENT' && (
<button
onClick={() => setIsSignUp(true)}
className="px-12 py-4 border-2 border-white text-white text-[10px] font-black uppercase tracking-[0.3em] rounded-full hover:bg-white hover:text-[#004a99] transition-all active:scale-95"
>
Sign Up
</button>
)}
</div>
<div className={`absolute inset-0 flex flex-col items-center justify-center p-12 transition-all duration-700 delay-100 ${(!isSignUp || role !== 'STUDENT') ? 'opacity-0 translate-y-10 pointer-events-none invisible' : 'opacity-100 translate-y-0 pointer-events-auto visible'}`}>
<h2 className="text-4xl font-black text-white uppercase tracking-tighter mb-4">Welcome <span className="text-white/70">Back!</span></h2>
<p className="text-white/80 text-sm font-medium leading-relaxed mb-10 max-w-xs mx-auto">
To keep connected with us please login with your personal info.
</p>
<button
onClick={() => setIsSignUp(false)}
className="px-12 py-4 border-2 border-white text-white text-[10px] font-black uppercase tracking-[0.3em] rounded-full hover:bg-white hover:text-[#004a99] transition-all active:scale-95"
>
Sign In
</button>
</div>
</div>
</div>
</div>
</div>
);
};
export default LoginForm;