import { API_BASE_URL } from '../../lib/config'; import React, { useState, useEffect } from 'react'; import { Users, CheckSquare, Upload, FileCheck, XCircle, CheckCircle, FileText, Search, ChevronRight, Eye, Award } from 'lucide-react'; import { useAuth } from '../../context/AuthContext'; import { useDialog } from '../../context/DialogContext'; import { cn } from '../../lib/utils'; interface Event { id: number; title: string; startDate: string; endDate: string; type: string; location: string; category: string; department: string; status: string; } interface Registration { id: string; userId: string; eventId: number; userName: string; userEmail: string; regNo: string; phone: string; gender: string; dept: string; section: string; year: string; college: string; paymentStatus: 'PENDING' | 'COMPLETED'; teamCode: string | null; teamName: string | null; isTeamLeader: boolean; certificationUrl: string | null; certificationStatus: 'NOT_SUBMITTED' | 'PENDING_APPROVAL' | 'APPROVED'; odUrl: string | null; } export const StudentRegistrationsView: React.FC = () => { const { user } = useAuth(); const { showAlert, showConfirm } = useDialog(); const [events, setEvents] = useState([]); const [selectedEvent, setSelectedEvent] = useState(null); const [registrations, setRegistrations] = useState([]); const [attendanceLogs, setAttendanceLogs] = useState([]); const [isLoading, setIsLoading] = useState(true); // Search and view states const [searchQuery, setSearchQuery] = useState(''); const [activeSubTab, setActiveSubTab] = useState<'roster' | 'attendance' | 'od' | 'certificates'>('roster'); // Audit modals const [inspectCertReg, setInspectCertReg] = useState(null); const [isProcessing, setIsProcessing] = useState(false); // OD Upload states const [odFileBase64, setOdFileBase64] = useState(null); // Attendance checking state (maps registrationId -> dayLabel -> isPresent) const [attendanceGrid, setAttendanceGrid] = useState>>({}); const daysList = ["Day 1", "Day 2", "Day 3"]; // Standard slots const [batches, setBatches] = useState([]); useEffect(() => { fetchEvents(); fetchBatches(); }, []); const fetchBatches = async () => { try { const res = await fetch(API_BASE_URL + '/api/batches'); if (res.ok) { const data = await res.json(); setBatches(data); } } catch (err) { console.error(err); } }; useEffect(() => { if (selectedEvent) { fetchRegistrations(selectedEvent.id); } }, [selectedEvent]); const fetchEvents = async () => { setIsLoading(true); try { const res = await fetch(API_BASE_URL + '/api/events'); if (res.ok) { const data = await res.json(); // Faculty can manage their proposed events, Admins/HODs can manage all let filtered = data.filter((e: Event) => e.status === 'APPROVED' || e.status === 'COMPLETED' || e.status === 'Event Ongoing'); if (user?.role === 'FACULTY') { const userDepts = user.department ? user.department.split(',').map(d => d.trim().toLowerCase()) : []; filtered = filtered.filter((e: Event) => userDepts.includes(e.department?.trim().toLowerCase())); } setEvents(filtered); if (filtered.length > 0) { setSelectedEvent(filtered[0]); } } } catch (err) { console.error(err); } finally { setIsLoading(false); } }; const fetchRegistrations = async (eventId: number) => { setIsLoading(true); try { const [regsRes, attRes] = await Promise.all([ fetch(API_BASE_URL + `/api/registrations?eventId=${eventId}`), fetch(API_BASE_URL + `/api/attendance?eventId=${eventId}`) ]); if (regsRes.ok && attRes.ok) { const regsData = await regsRes.json(); const attData = await attRes.json(); setRegistrations(regsData); setAttendanceLogs(attData); // Pre-populate attendance grid from attendanceLogs const grid: Record> = {}; regsData.forEach((r: Registration) => { grid[r.id] = {}; daysList.forEach(day => { const match = attData.find((a: any) => String(a.registrationId || a.registration_id) === String(r.id) && a.dayLabel === day); grid[r.id][day] = match ? !!match.isPresent : false; }); }); setAttendanceGrid(grid); } } catch (err) { console.error(err); } finally { setIsLoading(false); } }; // Toggle Payment Status Manually const handleTogglePayment = async (reg: Registration) => { setIsProcessing(true); const newStatus = reg.paymentStatus === 'COMPLETED' ? 'PENDING' : 'COMPLETED'; try { const res = await fetch(`${API_BASE_URL}/api/registrations/${reg.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ paymentStatus: newStatus }) }); if (res.ok) { await fetchRegistrations(selectedEvent!.id); } } catch (err) { console.error(err); } finally { setIsProcessing(false); } }; // Dismiss Student from event const handleDismissStudent = async (regId: string, studentName: string) => { showConfirm( "Dismiss Student", `Are you sure you want to dismiss ${studentName} from this event? This will completely remove their registration.`, async () => { setIsProcessing(true); try { const res = await fetch(`${API_BASE_URL}/api/registrations/${regId}`, { method: 'DELETE' }); if (res.ok) { showAlert("Success", `${studentName} has been successfully dismissed.`, "success"); await fetchRegistrations(selectedEvent!.id); } else { showAlert("Error", "Failed to dismiss student.", "error"); } } catch (err) { console.error(err); showAlert("Error", "An error occurred while dismissing the student.", "error"); } finally { setIsProcessing(false); } } ); }; // Toggle Attendance Cell local state const handleToggleAttendance = (regId: string, day: string) => { setAttendanceGrid(prev => ({ ...prev, [regId]: { ...prev[regId], [day]: !prev[regId][day] } })); }; // Save Attendance Grid to Firestore const handleSaveAttendance = async () => { setIsProcessing(true); try { const payload: any[] = []; Object.keys(attendanceGrid).forEach(regId => { daysList.forEach(day => { payload.push({ registrationId: regId, eventId: selectedEvent!.id, dayLabel: day, batchLabel: "Slot 1", isPresent: attendanceGrid[regId][day], date: new Date().toLocaleDateString() }); }); }); const res = await fetch(API_BASE_URL + '/api/attendance', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (res.ok) { showAlert("Success", "Attendance records synchronized successfully!", "success"); await fetchRegistrations(selectedEvent!.id); } else { showAlert("Error", "Failed to save attendance.", "error"); } } catch (err) { console.error(err); } finally { setIsProcessing(false); } }; // Handle OD PDF File selection const handleODFileChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; const reader = new FileReader(); reader.onloadend = () => { setOdFileBase64(reader.result as string); }; reader.readAsDataURL(file); }; // Upload OD File to all registered students const handleUploadODFile = async () => { if (!odFileBase64) { showAlert("Error", "Please select an On-Duty file first.", "error"); return; } setIsProcessing(true); try { // Loop over and update all registrations for this event for (const reg of registrations) { await fetch(`${API_BASE_URL}/api/registrations/${reg.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ odUrl: odFileBase64 }) }); } showAlert("Success", "OD sheet broadcasted to all registered students successfully!", "success"); setOdFileBase64(null); await fetchRegistrations(selectedEvent!.id); } catch (err) { console.error(err); } finally { setIsProcessing(false); } }; // Approve or Reject screenshot proof const handleAuditCertificate = async (regId: string, status: 'APPROVED' | 'NOT_SUBMITTED') => { setIsProcessing(true); try { const res = await fetch(`${API_BASE_URL}/api/registrations/${regId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ certificationStatus: status }) }); if (res.ok) { setInspectCertReg(null); showAlert("Certificate Audited", status === 'APPROVED' ? "Certificate approved!" : "Certificate rejected.", status === 'APPROVED' ? "success" : "info"); await fetchRegistrations(selectedEvent!.id); } } catch (err) { console.error(err); } finally { setIsProcessing(false); } }; const filteredRoster = registrations.filter(r => r.userName.toLowerCase().includes(searchQuery.toLowerCase()) || r.regNo.toLowerCase().includes(searchQuery.toLowerCase()) ); const getStudentBatchName = (reg: any) => { const classStr = `${reg.year} - ${reg.section}`; const match = batches.find(b => b.department === reg.dept && b.classes?.includes(classStr)); return match ? match.name : ''; }; return (
{/* Header Info */}

Student Engagement Hub

Review registration rosters, log session attendance checklists, release OD files, and audit certificate proofs.

{/* Event Selector Dropdown */}
{selectedEvent ? (
{/* Quick Metrics Panels */}
Passes Secured

{registrations.length}

Teamed Up

{registrations.filter(r => r.teamCode).length}

In Review Proofs

{registrations.filter(r => r.certificationStatus === 'PENDING_APPROVAL').length}

Verified Certificates

{registrations.filter(r => r.certificationStatus === 'APPROVED').length}

{/* Sub Navigation Tabs */}
{/* Sub Tab Contents */}
{/* SUB TAB 1: ROSTER & MANUAL FEE */} {activeSubTab === 'roster' && (

Registration Roster

{/* Search Roster */}
setSearchQuery(e.target.value)} />
{filteredRoster.length === 0 ? ( ) : ( filteredRoster.map((reg) => ( )) )}
Student Details Class College Alliance/Team Payment Status Action
No registered students matched the search.

{reg.userName}

{reg.regNo} | {reg.phone}

{reg.year} Year / SEC {reg.section} {getStudentBatchName(reg) && ( {getStudentBatchName(reg)} )}

{reg.dept}

{reg.college} {reg.teamCode ? (

{reg.teamName}

Code: {reg.teamCode}

) : ( Individual )}
)} {/* SUB TAB 2: SESSION ATTENDANCE GRID */} {activeSubTab === 'attendance' && (

Mark Slot Presence

{daysList.map(day => )} {registrations.length === 0 ? ( ) : ( registrations.map((reg) => ( {daysList.map(day => ( ))} )) )}
Student Register No{day}
Mark All 0 && registrations.every(reg => daysList.every(day => attendanceGrid[reg.id]?.[day] || false) ) } onChange={(e) => { const checked = e.target.checked; setAttendanceGrid(prev => { const next = { ...prev }; registrations.forEach(reg => { next[reg.id] = next[reg.id] || {}; daysList.forEach(day => { next[reg.id][day] = checked; }); }); return next; }); }} className="w-4 h-4 rounded border-slate-300 text-brand-indigo focus:ring-brand-indigo cursor-pointer" title="Toggle all days for all students" />
No students registered yet.
{reg.userName} {getStudentBatchName(reg) && ( {getStudentBatchName(reg)} )} {reg.regNo} handleToggleAttendance(reg.id, day)} className="w-4.5 h-4.5 rounded border-slate-300 text-brand-indigo focus:ring-brand-indigo cursor-pointer" /> attendanceGrid[reg.id]?.[day] || false)} onChange={(e) => { const checked = e.target.checked; setAttendanceGrid(prev => ({ ...prev, [reg.id]: daysList.reduce((acc, day) => { acc[day] = checked; return acc; }, {} as Record) })); }} className="w-4.5 h-4.5 rounded border-slate-300 text-brand-indigo focus:ring-brand-indigo cursor-pointer" title="Toggle all days for this student" />
)} {/* SUB TAB 3: ON-DUTY UPLOADER */} {activeSubTab === 'od' && (

On-Duty Official Release

Upload the unified signed On-Duty letter for this event. It will be released to all registered students automatically.

{odFileBase64 && (

File captured ready to broadcast

)}
)} {/* SUB TAB 4: CERTIFICATE AUDITOR */} {activeSubTab === 'certificates' && (

Audit Student Proofs

Inspect uploaded completion screenshot proofs and approve them to unlock certificate downloads.

{registrations.filter(r => r.certificationStatus === 'PENDING_APPROVAL').length === 0 ? (
All certificate proofs have been successfully audited!
) : ( registrations.filter(r => r.certificationStatus === 'PENDING_APPROVAL').map((reg) => (

{reg.userName}

{reg.regNo} | {reg.dept}

)) )}
)}
) : (

No Live Events Available

Create/approve events in your main dashboard first to access student hub audits.

)} {/* INSPECT SCREENSHOT MODAL */} {inspectCertReg && (

Inspect Screenshot

{inspectCertReg.userName} | {inspectCertReg.regNo}

{/* Proof Body */}
{inspectCertReg.certificationUrl ? ( Completion proof ) : (

No proof uploaded

)}
{/* Actions Bar */}
)}
); };