import React, { useState, useEffect, useCallback } from 'react'; import WelcomeScreen from './components/WelcomeScreen'; import LoginForm from './components/LoginForm'; import Dashboard from './components/Dashboard'; import AdminLandingPage from './components/AdminLandingPage'; import FacultyDashboard from './components/FacultyDashboard'; import TicketVerificationView from './components/TicketVerificationView'; import { AppState, UserRole, Event, EventSchedule, Announcement, Ticket } from './types'; import { supabase } from './supabase'; const App: React.FC = () => { const [appState, setAppState] = useState('WELCOME'); const [userRole, setUserRole] = useState(null); const [events, setEvents] = useState([]); const [announcements, setAnnouncements] = useState([]); const [specialEvents, setSpecialEvents] = useState([]); const [domains, setDomains] = useState([]); const [userRegistrations, setUserRegistrations] = useState([]); const [allRegistrations, setAllRegistrations] = useState([]); const [bookedEventIds, setBookedEventIds] = useState([]); const [currentUserName, setCurrentUserName] = useState('User'); const [currentUserDept, setCurrentUserDept] = useState(''); const [currentUserEmail, setCurrentUserEmail] = useState(''); const [currentUserPhone, setCurrentUserPhone] = useState(''); const [currentUserPhoto, setCurrentUserPhoto] = useState(''); const [currentUserId, setCurrentUserId] = useState(''); const [currentUserFacultyRole, setCurrentUserFacultyRole] = useState(''); const [ticketToVerify, setTicketToVerify] = useState<{ ticket: Ticket, event: Event } | null>(null); const [isAuthenticating, setIsAuthenticating] = useState(false); const [globalError, setGlobalError] = useState(null); const [studentDocuments, setStudentDocuments] = useState>({}); const [showStudentHubForAdmin, setShowStudentHubForAdmin] = useState(false); const [intendedRole, setIntendedRole] = useState(null); const mapDbToEvent = useCallback((dbEvent: any, registrations: any[] = []): Event => { const eventRegs = registrations.filter(r => String(r.event_id) === String(dbEvent.id)); const deptCounts: Record = {}; const deptSectionCounts: Record> = {}; eventRegs.forEach(reg => { if (reg.dept) { deptCounts[reg.dept] = (deptCounts[reg.dept] || 0) + 1; if (reg.section) { if (!deptSectionCounts[reg.dept]) { deptSectionCounts[reg.dept] = {}; } deptSectionCounts[reg.dept][reg.section] = (deptSectionCounts[reg.dept][reg.section] || 0) + 1; } } }); return { id: String(dbEvent.id), title: dbEvent.title, location: dbEvent.location, date: dbEvent.date, category: dbEvent.category, domain: dbEvent.domain, pricingType: dbEvent.pricing_type, coordinator: dbEvent.coordinator, image: dbEvent.image, status: dbEvent.status, maxParticipants: dbEvent.max_participants, registrationDeadline: dbEvent.registration_deadline, durationDays: dbEvent.duration_days, club: dbEvent.club, deptLimits: dbEvent.deptLimits || {}, deptSectionLimits: dbEvent.deptSectionLimits || {}, event_summary: dbEvent.event_summary, isTeamEvent: dbEvent.is_team_event, teamSizeLimit: dbEvent.team_size_limit, teamComposition: dbEvent.team_composition, currentParticipants: eventRegs.length, currentDeptCounts: deptCounts, currentDeptSectionCounts: deptSectionCounts, created_by: dbEvent.created_by, participantType: dbEvent.participant_type as any, verificationStatus: dbEvent.verification_status as any, refreshment_expense: dbEvent.refreshment_expense ?? 0, transportation_expense: dbEvent.transportation_expense ?? 0, session_coverage_fee: dbEvent.session_coverage_fee ?? 0, total_expense: dbEvent.total_expense ?? 0, conducting_dept: dbEvent.conducting_dept ?? null, request_by_faculty: dbEvent.request_by_faculty ?? null, request_by_HOD: dbEvent.request_by_hod ?? null, }; }, []); const fetchAnnouncements = useCallback(async () => { try { const { data, error } = await supabase .from('announcements') .select('*') .order('timestamp', { ascending: false }); if (data) { setAnnouncements(data.map(ann => ({ id: String(ann.id), title: ann.title, message: ann.message, type: ann.type, eventId: ann.event_id ? String(ann.event_id) : undefined, timestamp: ann.timestamp || new Date().toISOString(), expiresAt: ann.expires_at }))); } } catch (err) { console.error("Announcement Sync Error:", err); } }, []); const fetchSpecialEvents = useCallback(async () => { try { let query = supabase.from('special_events').select('*').eq('is_active', true); // SECURITY: Public users and Students only see Approved special events if (!userRole || userRole === 'STUDENT') { query = query.eq('verification_status', 'APPROVED'); } const { data } = await query.order('created_at', { ascending: false }); if (data) { setSpecialEvents(data.map((se: any) => ({ ...se, verificationStatus: se.verification_status }))); } } catch (err) { console.error("Special Events Sync Error:", err); } }, [userRole]); const fetchDomains = useCallback(async () => { try { const { data } = await supabase.from('domains').select('*').order('name', { ascending: true }); if (data) setDomains(data); } catch (err) { console.error("Domains Sync Error:", err); } }, []); const fetchEvents = useCallback(async (registrations: any[] = []) => { const { data } = await supabase.from('events').select('*').order('date', { ascending: true }); if (data) { // Fetch all schedule + resource_persons + dept limits in bulk const { data: scheduleData } = await supabase.from('event_schedule').select('*').order('day_idx', { ascending: true }).order('batch_idx', { ascending: true }); const { data: rpData } = await supabase.from('resource_persons').select('*'); const { data: deptLimitsData } = await supabase.from('event_dept_limits').select('*'); const mapped = data.map(e => { const event = mapDbToEvent(e, registrations); // Attach schedule rows for this event const eventSchedule: EventSchedule[] = (scheduleData || []).filter(s => s.event_id === e.id).map(s => { const rp = (rpData || []).find(r => r.schedule_id === s.id); return { ...s, resource_person: rp ? { id: rp.id, type: rp.type, name: rp.name, dept: rp.dept, college_name: rp.college_name, phone: rp.phone, email: rp.email } : undefined, }; }); event.schedule = eventSchedule; // Attach department limits for this event const eventDeptLimits: Record = {}; const eventDeptSectionLimits: Record> = {}; (deptLimitsData || []).filter(dl => dl.event_id === e.id).forEach(dl => { eventDeptLimits[dl.department] = dl.max_seats; eventDeptSectionLimits[dl.department] = dl.section_limits || {}; }); event.deptLimits = eventDeptLimits; event.deptSectionLimits = eventDeptSectionLimits; return event; }); // SECURITY: Public users and Students only see Approved events if (!userRole || userRole === 'STUDENT') { setEvents(mapped.filter(e => e.verificationStatus === 'APPROVED')); } else { setEvents(mapped); } } }, [mapDbToEvent, userRole]); const fetchAllRegistrations = useCallback(async () => { const { data } = await supabase.from('registrations').select('*').order('registered_at', { ascending: false }); if (data) { setAllRegistrations(data); fetchEvents(data); } }, [fetchEvents]); const fetchMyRegs = useCallback(async (userId: string) => { const { data } = await supabase.from('registrations').select('*').eq('user_id', userId); if (data) { setUserRegistrations(data); setBookedEventIds(data.map((r: any) => String(r.event_id))); } }, []); const handleAuthChange = useCallback(async (session: any) => { if (!session?.user) { setAppState(prev => { if (prev !== 'WELCOME' && prev !== 'LOGIN') return 'WELCOME'; return prev; }); setIsAuthenticating(false); return; } setIsAuthenticating(true); const user = session.user; const HIGH_AUTH_ADMINS = [ 'adrit1highauth@gmail.com', 'adrit2highauth@gmail.com', 'adrit3highauth@gmail.com', 'adrit4highauth@gmail.com', 'adrit5highauth@gmail.com' ]; try { let profile = null; let finalRole: UserRole | null = null; // Check for fixed high-auth admins first if (user.email && HIGH_AUTH_ADMINS.includes(user.email)) { const { data: admin } = await supabase.from('Adminusers').select('*').eq('id', user.id).single(); if (admin) { profile = admin; } else { // If they exist in auth but not in Adminusers yet, we create and insert a profile const newProfile = { id: user.id, name: 'System Admin', email: user.email, dept: 'Administration', updated_at: new Date().toISOString() }; const { data: inserted, error: insertError } = await supabase.from('Adminusers').insert(newProfile).select().single(); if (!insertError) { profile = inserted; } else { profile = newProfile; // Fallback to local object if insert fails } } finalRole = 'ADMIN'; // Principal/Director is ADMIN } else { // Check Adminusers (Admin Hub / System Admins) const { data: adminProfile } = await supabase.from('Adminusers').select('*').eq('id', user.id).single(); if (adminProfile) { profile = adminProfile; finalRole = 'ADMIN'; } else { // Check Facultyusers (Event Coordinator Hub) const { data: facultyProfile } = await supabase.from('Facultyusers').select('*').eq('id', user.id).single(); if (facultyProfile) { profile = facultyProfile; finalRole = 'COORDINATOR'; } else { const { data: student } = await supabase.from('Studentusers').select('*').eq('id', user.id).single(); if (student) { profile = student; finalRole = 'STUDENT'; } else { const { data: externalStudent } = await supabase.from('externalusers').select('*').eq('id', user.id).single(); if (externalStudent) { profile = externalStudent; finalRole = 'STUDENT'; } else { // FALLBACK: Auto-sync profile from metadata or auto-register from Google email format const meta = user.user_metadata; const emailLower = (user.email || '').trim().toLowerCase(); const studentMatch = emailLower.match(/^student\.(\d{6})@([a-zA-Z0-9&-_]+)\.ritchennai\.edu\.in$/); if (studentMatch) { const rollNo = studentMatch[1]; const rawDept = studentMatch[2].toLowerCase(); // Calculate joining year and academic year (current local time is 2026-06-19) const joinYear = 2000 + parseInt(rollNo.substring(0, 2)); const currentYear = new Date().getFullYear(); const currentMonth = new Date().getMonth(); const academicYearOffset = currentMonth >= 5 ? 1 : 0; const yearIndex = currentYear - joinYear + academicYearOffset; const years = ["1st Year", "2nd Year", "3rd Year", "4th Year"]; const calculatedYear = years[yearIndex - 1] || "N/A"; // Map department let dept = rawDept.toUpperCase(); if (dept === 'AIDS') dept = 'AI&DS'; else if (dept === 'AIML') dept = 'AI&ML'; else if (dept === 'VLSI') dept = 'EE(VLSI)'; else if (dept === 'BIOTECH' || dept === 'BIO-TECH') dept = 'BIOTECH'; else if (dept === 'H&S') dept = 'H&S Dept'; const syncData = { id: user.id, name: meta?.name || `Student ${rollNo}`, email: emailLower, reg_no: rollNo, phone: '', department: dept, year: calculatedYear, section: 'A', college_name: 'Rajalakshmi Institute of Technology', updated_at: new Date().toISOString() }; const { data: synced } = await supabase.from('Studentusers').upsert(syncData).select().single(); profile = synced || syncData; finalRole = 'STUDENT'; } else if (emailLower && !emailLower.endsWith('@ritchennai.edu.in') && !emailLower.endsWith('@rit.edu') && !HIGH_AUTH_ADMINS.includes(emailLower)) { // Auto-register external user if signing in via Google with a non-campus email const syncData = { id: user.id, name: meta?.name || emailLower.split('@')[0], email: emailLower, reg_no: 'EXT-' + Math.floor(Math.random() * 100000), phone: '', department: 'Others', year: 'N/A', section: 'N/A', college: 'External Institution', college_location: 'N/A', gender: 'Male', updated_at: new Date().toISOString() }; const { data: synced } = await supabase.from('externalusers').upsert(syncData).select().single(); profile = synced || syncData; finalRole = 'STUDENT'; } else if (meta && (meta.role === 'STUDENT' || meta.regNo)) { const isExternal = meta.signUpType === 'EXTERNAL'; const table = isExternal ? 'externalusers' : 'Studentusers'; const syncData: any = { id: user.id, name: meta.name || user.email?.split('@')[0], email: user.email, reg_no: meta.regNo || meta.reg_no, phone: meta.phone, department: meta.department || meta.dept, year: meta.year, section: meta.section, updated_at: new Date().toISOString() }; if (isExternal) { syncData.college = meta.collegeName || meta.college; syncData.college_location = meta.collegeLocation; syncData.gender = meta.gender; } const { data: synced, error: syncError } = await supabase .from(table) .upsert(syncData) .select() .single(); if (!syncError && synced) { profile = synced; finalRole = 'STUDENT'; } else { console.warn("Auto-sync profile failed:", syncError?.message); profile = syncData; finalRole = 'STUDENT'; } } else if (meta && (meta.role === 'ADMIN' || meta.role === 'COORDINATOR')) { // If metadata says Admin/Coordinator but they weren't in tables, check HIGH_AUTH_ADMINS if (user.email && HIGH_AUTH_ADMINS.includes(user.email)) { finalRole = 'ADMIN'; } else { finalRole = 'COORDINATOR'; } // Try to find in Facultyusers or Adminusers as fallback const { data: f } = await supabase.from('Facultyusers').select('*').eq('id', user.id).single(); if (f) profile = f; else { const { data: a } = await supabase.from('Adminusers').select('*').eq('id', user.id).single(); if (a) profile = a; } } } } } } } if (profile && finalRole) { // PORTAL SECURITY GUARD: Ensure actual role matches the intended portal if (intendedRole && finalRole !== intendedRole) { // Exception: ADMINs can access COORDINATOR portal if needed, but not vice-versa const isPermittedOverload = (intendedRole === 'COORDINATOR' && finalRole === 'ADMIN'); if (!isPermittedOverload) { setGlobalError(`Access Denied: Your account (${finalRole}) is not authorized for the ${intendedRole} Portal.`); await supabase.auth.signOut(); setIsAuthenticating(false); return; } } setCurrentUserName(profile.name || 'User'); setCurrentUserDept(profile.department || profile.dept || ''); setCurrentUserEmail(profile.email || user.email || ''); setCurrentUserPhone(profile.phone || ''); setCurrentUserPhoto(profile.profile_photo || ''); setCurrentUserId(user.id); setCurrentUserFacultyRole(profile.role || ''); setUserRole(finalRole); // Only redirect if we are in a state that warrants it (e.g. just logged in) setAppState(prev => { if (prev === 'WELCOME' || prev === 'LOGIN' || prev === 'VERIFY') { if (finalRole === 'ADMIN') { if (intendedRole === 'COORDINATOR') { return 'ADMIN_LANDING'; // Event Co Hub for Admins who chose it } return 'FACULTY_DASHBOARD'; // Default Admin Hub } if (finalRole === 'COORDINATOR') return 'ADMIN_LANDING'; return 'DASHBOARD'; } return prev; }); // Fetch data but don't let it block the UI if it takes too long Promise.all([ fetchAllRegistrations(), fetchMyRegs(user.id), fetchAnnouncements(), fetchSpecialEvents(), fetchDomains() ]).catch(err => console.error("Post-auth fetch error:", err)); } else { setGlobalError("Access Denied: Account structure invalid."); await supabase.auth.signOut(); } } catch (err) { console.error("Auth routing error:", err); } finally { setIsAuthenticating(false); } }, [fetchAllRegistrations, fetchMyRegs, fetchAnnouncements, fetchSpecialEvents, fetchDomains, intendedRole]); useEffect(() => { // Initial public data fetch fetchAllRegistrations(); fetchAnnouncements(); fetchSpecialEvents(); fetchDomains(); // 1. Initial Session Recovery & Error Handling const initAuth = async () => { try { const { data: { session }, error } = await supabase.auth.getSession(); if (error) { console.warn("[Auth System] Session recovery failed:", error.message); // Broaden the error detection for refresh token issues const isRefreshTokenError = error.message.includes('Refresh Token') || error.message.includes('not found') || error.message.includes('Invalid Refresh Token') || error.status === 400 || error.status === 401; if (isRefreshTokenError) { console.log("[Auth System] Stale or invalid session detected. Clearing storage..."); // Explicitly clear the storage key used in supabase.ts localStorage.removeItem('rit-events-hub-auth'); // Attempt a clean sign out await supabase.auth.signOut().catch(() => {}); setAppState('WELCOME'); } } if (session) { handleAuthChange(session); } } catch (err) { console.error("[Auth System] Critical recovery error:", err); } }; initAuth(); // 2. Real-time Auth State Subscription const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => { handleAuthChange(session); }); // 3. Real-time Database Subscriptions const eventsChannel = supabase .channel('events-sync') .on('postgres_changes', { event: '*', schema: 'public', table: 'events' }, () => { fetchAllRegistrations(); }) .subscribe(); const specialEventsChannel = supabase .channel('special-events-sync') .on('postgres_changes', { event: '*', schema: 'public', table: 'special_events' }, () => { fetchSpecialEvents(); }) .subscribe(); const announcementsChannel = supabase .channel('announcements-sync') .on('postgres_changes', { event: '*', schema: 'public', table: 'announcements' }, () => { fetchAnnouncements(); }) .subscribe(); return () => { subscription.unsubscribe(); supabase.removeChannel(eventsChannel); supabase.removeChannel(specialEventsChannel); supabase.removeChannel(announcementsChannel); }; }, [handleAuthChange, fetchAllRegistrations, fetchSpecialEvents, fetchAnnouncements]); // NOTE: CreateEventForm already handles the Supabase insert. // This callback only refreshes the local events list. const handleAddEvent = async (_ev: Event) => { await fetchAllRegistrations(); }; const handleDeleteEvent = async (id: string) => { // Step 1: Delete all registrations for this event first (FK constraint: NO ACTION) const { error: regError } = await supabase .from('registrations') .delete() .eq('event_id', id); if (regError) { console.error('[Delete Event] Failed to delete registrations:', regError); } // Step 2: Delete any announcements linked to this event await supabase.from('announcements').delete().eq('event_id', id); // Step 3: Now delete the event itself const { error } = await supabase.from('events').delete().eq('id', id); if (error) { console.error('[Delete Event] Supabase error:', error); alert(`Failed to delete event: ${error.message}`); } else { await fetchAllRegistrations(); // Refresh so all hubs update } }; // NOTE: CreateEventForm handles event_schedule + resource_persons writes. // This only updates the events table columns. const handleUpdateEvent = async (ev: Event) => { const { error } = await supabase.from('events').update({ title: ev.title, location: ev.location, date: ev.date, category: ev.category, domain: ev.domain, pricing_type: ev.pricingType, coordinator: ev.coordinator, image: ev.image, status: ev.status, max_participants: ev.maxParticipants, registration_deadline: ev.registrationDeadline, duration_days: ev.durationDays, club: ev.club, event_summary: ev.event_summary, is_team_event: ev.isTeamEvent, team_size_limit: ev.teamSizeLimit, team_composition: ev.teamComposition, participant_type: ev.participantType, verification_status: ev.verificationStatus, refreshment_expense: ev.refreshment_expense || 0, transportation_expense: ev.transportation_expense || 0, session_coverage_fee: (ev as any).session_coverage_fee || 0, total_expense: ev.total_expense || 0, request_by_faculty: ev.request_by_faculty || null, request_by_hod: ev.request_by_HOD || null }).eq('id', ev.id); if (error) { console.error('[Update Event] Supabase error:', error); alert(`Failed to update event: ${error.message}`); } else { await fetchAllRegistrations(); } }; const handleUpdateSpecialEvent = async (se: any) => { const { error } = await supabase.from('special_events').update({ title: se.title, description: se.description, link: se.link, is_active: se.is_active, verification_status: se.verificationStatus }).eq('id', se.id); if (error) { console.error('[Update Special Event] Supabase error:', error); alert(`Failed to update special event: ${error.message}`); } else { await fetchSpecialEvents(); } }; const handleAddAnnouncement = async (ann: Announcement) => { const { error } = await supabase.from('announcements').insert({ title: ann.title, message: ann.message, type: ann.type, event_id: ann.eventId, expires_at: ann.expiresAt, timestamp: new Date().toISOString() }); if (!error) { if (ann.type === 'ONGOING' && ann.eventId) { // Sync participants table when event starts const { data: regs } = await supabase .from('registrations') .select('*') .eq('event_id', ann.eventId) .eq('payment_status', 'COMPLETED'); if (regs && regs.length > 0) { const participantData = regs.map(r => ({ user_id: r.user_id, event_id: r.event_id, user_name: r.user_name, reg_no: r.reg_no, dept: r.dept, year: r.year, section: r.section, participation_date: new Date().toISOString() })); await supabase.from('participants').upsert(participantData); } } await fetchAnnouncements(); } }; const handleUpdateAnnouncement = async (ann: Announcement) => { const { error } = await supabase.from('announcements') .update({ title: ann.title, message: ann.message, type: ann.type, event_id: ann.eventId, expires_at: ann.expiresAt }) .eq('id', ann.id); if (!error) { await fetchAnnouncements(); } else { console.error("[Database Hub] Update Rejection:", error); alert(`Update failed: ${error.message}`); } }; const handleDeleteAnnouncement = async (id: string) => { // 1. Retrieve notice details before deletion to check type const { data: targetAnn } = await supabase.from('announcements').select('*').eq('id', id).single(); const { error } = await supabase.from('announcements') .delete() .eq('id', id); if (!error) { // 2. If it was an 'ENDED' or 'ONGOING' notice, revert event status and wipe records if (targetAnn && (targetAnn.type === 'ENDED' || targetAnn.type === 'ONGOING') && targetAnn.event_id) { await supabase.from('events').update({ status: 'Scheduled' }).eq('id', targetAnn.event_id); // Wipe attendance and participants for a clean restart await supabase.from('attendance_records').delete().eq('event_id', targetAnn.event_id); await supabase.from('participants').delete().eq('event_id', targetAnn.event_id); await fetchAllRegistrations(); // Sync the event list } setAnnouncements(prev => prev.filter(ann => ann.id !== id)); await fetchAnnouncements(); } else { console.error("[Database Hub] Delete Rejection:", error); alert(`Delete rejected: ${error.message}`); } }; const toggleBooking = async (eventId: string) => { if (userRole !== 'STUDENT') { alert("Event Coordinators and Admins cannot register for events."); return; } try { const { data: { user } } = await supabase.auth.getUser(); if (!user) { alert("You must be logged in to register."); return; } const registrationId = `${user.id}_${eventId}`; if (bookedEventIds.includes(eventId)) { const { error } = await supabase.from('registrations').delete().eq('id', registrationId); if (error) throw error; setBookedEventIds(prev => prev.filter(id => id !== eventId)); await fetchAllRegistrations(); } else { // Block registration if there is any unfinalized completed event (missing certificate upload or missing OD approval) const unfinalized = userRegistrations.find(reg => { const ev = events.find(e => String(e.id) === String(reg.event_id)); if (ev && ev.status === 'Completed') { const hasUploadedCert = !!reg.certification_url; const hasOd = !!(reg.od_url || reg.od); return !hasUploadedCert || !hasOd; } return false; }); if (unfinalized) { const ev = events.find(e => String(e.id) === String(unfinalized.event_id)); alert(`Registration Blocked: You must upload your certificate and obtain OD approval for your completed event "${ev?.title || 'Past Event'}" before you can register for another event.`); return; } const targetEvent = events.find(e => e.id === eventId); if (!targetEvent) { alert("Event not found."); return; } // Try to fetch from Studentusers first, then externalusers let student = null; const { data: internalStudent } = await supabase.from('Studentusers').select('*').eq('id', user.id).single(); if (internalStudent) { student = internalStudent; } else { const { data: externalStudent } = await supabase.from('externalusers').select('*').eq('id', user.id).single(); if (externalStudent) student = externalStudent; } if (!student) { console.error("Student profile not found for user:", user.id); alert("Your student profile was not found. Please contact support."); return; } // Validate overall registration capacity if (targetEvent.maxParticipants && (targetEvent.currentParticipants || 0) >= targetEvent.maxParticipants) { alert("This event has reached its maximum registration limit."); return; } // Validate department/section quota limits (Internal only, external has no sections) if (student.department && targetEvent.deptLimits?.[student.department]) { const sectionLimits = targetEvent.deptSectionLimits?.[student.department]; const hasSectionLimits = sectionLimits && Object.keys(sectionLimits).length > 0; if (hasSectionLimits) { const studentSection = student.section || ''; const limit = sectionLimits[studentSection]; if (!limit || limit <= 0) { alert(`Registration Blocked: Section ${studentSection} of ${student.department} department is not allowed to register for this event.`); return; } const currentSectionCount = targetEvent.currentDeptSectionCounts?.[student.department]?.[studentSection] || 0; if (currentSectionCount >= limit) { alert(`Registration Blocked: The quota for Section ${studentSection} of department ${student.department} is full.`); return; } } else { const currentDeptCount = targetEvent.currentDeptCounts?.[student.department] || 0; if (currentDeptCount >= targetEvent.deptLimits[student.department]) { alert(`Registration Blocked: The quota for department ${student.department} is full.`); return; } } } const { error } = await supabase.from('registrations').insert({ id: registrationId, user_id: user.id, event_id: eventId, user_email: user.email, user_name: student.name, reg_no: student.reg_no, phone: student.phone, gender: student.gender, dept: student.department, section: student.section, year: student.year, college: student.college_name || student.college || 'Rajalakshmi Institute of Technology', payment_status: targetEvent?.pricingType === 'FREE' ? 'COMPLETED' : 'PENDING' }); if (error) { if (error.code === '23505') { alert("You are already registered for this event."); } else { throw error; } } else { await fetchMyRegs(user.id); await fetchAllRegistrations(); } } } catch (err: any) { console.error("Booking Error:", err); alert(`Registration failed: ${err.message || 'Unknown error'}`); } }; const handleLogout = async () => { await supabase.auth.signOut(); setAppState('WELCOME'); setUserRole(null); setCurrentUserId(''); setCurrentUserFacultyRole(''); }; return (
{isAuthenticating && (

Verifying Portal

Authenticating Academic Credentials

)} {globalError && (
{globalError}
)} {appState === 'WELCOME' && { setIntendedRole(role); setUserRole(role); setAppState('LOGIN'); }} events={events} />} {appState === 'LOGIN' && userRole && ( setIsAuthenticating(true)} onBack={() => setAppState('WELCOME')} /> )} {appState === 'DASHBOARD' && (userRole === 'STUDENT' || userRole === 'COORDINATOR' || userRole === 'ADMIN') && ( ""} studentDocuments={studentDocuments} onUploadDoc={(studentId, data) => setStudentDocuments(prev => ({ ...prev, [studentId]: data }))} onBackToCoordinatorHub={(userRole === 'COORDINATOR' || userRole === 'ADMIN') ? () => setAppState('ADMIN_LANDING') : undefined} specialEvents={specialEvents} /> )} {appState === 'ADMIN_LANDING' && (userRole === 'COORDINATOR' || userRole === 'ADMIN') && ( setAppState('FACULTY_DASHBOARD') : handleLogout} onAddEvent={handleAddEvent} onUpdateEvent={handleUpdateEvent} onDeleteEvent={handleDeleteEvent} events={events} announcements={announcements} onAddAnnouncement={handleAddAnnouncement} onDeleteAnnouncement={handleDeleteAnnouncement} onUpdateAnnouncement={handleUpdateAnnouncement} studentDocuments={studentDocuments} onApproveCertificate={async (id) => { await supabase.from('registrations').update({ certification_status: 'APPROVED' }).eq('id', id); fetchAllRegistrations(); }} localRegistrations={allRegistrations} onViewStudentHub={() => setAppState('DASHBOARD')} currentUserDept={currentUserDept} currentUserEmail={currentUserEmail} currentUserName={currentUserName} currentUserPhone={currentUserPhone} currentUserPhoto={currentUserPhoto} currentUserId={currentUserId} currentUserFacultyRole={currentUserFacultyRole} domains={domains} /> )} {appState === 'FACULTY_DASHBOARD' && userRole === 'ADMIN' && ( { await supabase.from('registrations').update({ certification_status: 'APPROVED' }).eq('id', id); fetchAllRegistrations(); }} onUpdateEvent={handleUpdateEvent} onUpdateSpecialEvent={handleUpdateSpecialEvent} localRegistrations={allRegistrations} onViewCoordinatorHub={() => setAppState('ADMIN_LANDING')} onViewStudentHub={() => setAppState('DASHBOARD')} currentUserName={currentUserName} currentUserDept={currentUserDept} currentUserEmail={currentUserEmail} /> )}
); }; export default App;