import React, { useState, useMemo, useEffect } from 'react'; import { createPortal } from 'react-dom'; import { Event } from '../types'; import { supabase } from '../supabase'; interface AdminStatusTrackerViewProps { events: Event[]; onShowToast: (msg: string) => void; studentDocuments: Record; onApproveCertificate?: (regId: string) => Promise; localRegistrations?: any[]; isAdmin?: boolean; currentUserId?: string; } interface FilterState { depts: string[]; years: string[]; } const AdminStatusTrackerView: React.FC = ({ events, onShowToast, onApproveCertificate, localRegistrations = [], isAdmin = false, currentUserId }) => { const [selectedEventId, setSelectedEventId] = useState(null); const [searchTerm, setSearchTerm] = useState(''); const [viewingDoc, setViewingDoc] = useState(null); const [isApproving, setIsApproving] = useState(null); const [liveRegistrations, setLiveRegistrations] = useState(localRegistrations); const [isLoadingRegs, setIsLoadingRegs] = useState(true); const [activeFilters, setActiveFilters] = useState({ depts: [], years: [] }); const [showFilters, setShowFilters] = useState(false); const [viewMode, setViewMode] = useState<'ALL' | 'OD_PENDING' | 'CERT_PENDING'>('ALL'); const [isUploadingOd, setIsUploadingOd] = useState(null); const [externalUserIds, setExternalUserIds] = useState>(new Set()); const [studentTypeFilter, setStudentTypeFilter] = useState<'INTERNAL' | 'EXTERNAL'>('INTERNAL'); const [isLandingMode, setIsLandingMode] = useState(true); const [selectedIds, setSelectedIds] = useState>(new Set()); const [isBulkUploading, setIsBulkUploading] = useState(false); const handleOdUpload = async (e: React.ChangeEvent, regId: string) => { const file = e.target.files?.[0]; if (!file) return; setIsUploadingOd(regId); try { const reader = new FileReader(); reader.onloadend = async () => { try { const base64 = reader.result as string; const isPdf = file.type === 'application/pdf'; const extension = isPdf ? 'pdf' : 'jpg'; const fileName = `OD_${regId}_${Date.now()}.${extension}`; const { uploadToSupabase } = await import('../supabase'); const publicUrl = await uploadToSupabase(base64, fileName, 'OD_PROVIDERS'); const { error } = await supabase.from('registrations').update({ od_url: publicUrl }).eq('id', regId); if (error) throw error; onShowToast("OD Document uploaded successfully."); await refreshRegs(); } catch (uploadErr: any) { console.error("OD Upload processing failed", uploadErr); alert(`OD Upload failed: ${uploadErr.message || 'Unknown error'}`); } finally { setIsUploadingOd(null); } }; reader.readAsDataURL(file); } catch (err: any) { console.error("OD Upload failed", err); alert(`OD Upload failed: ${err.message || 'Unknown error'}`); setIsUploadingOd(null); } }; // Fetch all registrations directly from Supabase (bypasses prop staleness) useEffect(() => { const fetchRegs = async () => { setIsLoadingRegs(true); const { data, error } = await supabase .from('registrations') .select('*') .order('registered_at', { ascending: false }); if (data && !error) { setLiveRegistrations(data); } else if (localRegistrations.length > 0) { setLiveRegistrations(localRegistrations); } const { data: extUsers } = await supabase.from('externalusers').select('id'); if (extUsers) { setExternalUserIds(new Set(extUsers.map(u => u.id))); } setIsLoadingRegs(false); }; fetchRegs(); }, []); const refreshRegs = async () => { const { data, error } = await supabase .from('registrations') .select('*') .order('registered_at', { ascending: false }); if (data && !error) setLiveRegistrations(data); const { data: extUsers } = await supabase.from('externalusers').select('id'); if (extUsers) { setExternalUserIds(new Set(extUsers.map(u => u.id))); } }; const handleApprove = async (regId: string) => { if (onApproveCertificate) { setIsApproving(regId); try { await onApproveCertificate(regId); onShowToast("Certificate verified and student hub updated."); await refreshRegs(); // Refresh so card instantly shows Verified } catch (err) { console.error("Approval failed:", err); } finally { setIsApproving(null); } } }; const handleDownload = async (url: string, studentName: string, eventTitle: string) => { try { const response = await fetch(url); const blob = await response.blob(); const blobUrl = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = blobUrl; link.download = `Cert_${studentName.replace(/\s+/g, '_')}_${eventTitle.replace(/\s+/g, '_')}.jpg`; document.body.appendChild(link); link.click(); document.body.removeChild(link); window.URL.revokeObjectURL(blobUrl); } catch (err) { console.error("Download failed:", err); } }; const filterOptions = { depts: ['CSBS', 'AIDS', 'AIML', 'ECE', 'VLSI', 'H&S', 'CCE', 'CSE', 'MECH', 'BIO-TECH', 'Information Technology (IT)', 'Electrical & Electronics Engineering (EEE)', 'Civil Engineering', 'Biomedical Engineering', 'Chemical Engineering', 'Aeronautical / Aerospace Engineering', 'Mechatronics Engineering', 'Others'], years: ['1st Year', '2nd Year', '3rd Year', '4th Year', '5th Year'] }; const mappedParticipants = useMemo(() => { return liveRegistrations.map(reg => { const event = events.find(e => e.id === reg.event_id); const isEnded = event?.status === 'Completed'; const isExternal = externalUserIds.has(reg.user_id); const isVerified = reg.certification_status === 'APPROVED' || isExternal; const hasUploaded = !!reg.certification_url; const hasUploadedOd = !!reg.od_url; const isFree = event?.pricingType === 'FREE'; const isPaidVerified = reg.payment_status === 'COMPLETED'; const stages = [ { label: 'Registered', status: 'completed' }, { label: 'TEAM', status: !event?.isTeamEvent || reg.team_code ? 'completed' : 'active' }, { label: 'Payment', status: isFree || isPaidVerified ? 'completed' : (reg.team_code || !event?.isTeamEvent ? 'active' : 'pending') }, { label: 'Ticket', status: isFree || isPaidVerified ? 'completed' : 'pending' }, { label: 'Ongoing', status: isEnded ? 'completed' : (event?.status === 'Event Ongoing' ? 'active' : 'pending') }, { label: 'Ended', status: isEnded ? 'completed' : 'pending' }, { label: 'Certification', status: isVerified ? 'completed' : (isEnded ? 'active' : 'pending') }, { label: 'OD', status: hasUploadedOd ? 'completed' : (isVerified ? 'active' : 'pending') } ]; return { ...reg, name: reg.user_name || 'Student', regNo: reg.reg_no || 'N/A', phone: reg.phone || 'No Phone', dept: reg.dept || 'N/A', year: reg.year || 'N/A', college: reg.college || null, eventTitle: event?.title || 'Unknown Event', stages, hasUploaded, isVerified, isExternal, hasUploadedOd, od_url: reg.od_url }; }); }, [liveRegistrations, events]); const filteredParticipants = useMemo(() => { return mappedParticipants.filter(p => { const eventMatch = !selectedEventId || p.event_id === selectedEventId; const searchMatch = p.name.toLowerCase().includes(searchTerm.toLowerCase()) || p.regNo.toLowerCase().includes(searchTerm.toLowerCase()); const deptMatch = activeFilters.depts.length === 0 || activeFilters.depts.includes(p.dept); const yearMatch = activeFilters.years.length === 0 || activeFilters.years.includes(p.year); let viewMatch = true; if (viewMode === 'OD_PENDING') viewMatch = p.isVerified; if (viewMode === 'CERT_PENDING') viewMatch = p.hasUploaded || (p.stages[5].status === 'completed'); // Ended or Uploaded const isExternal = externalUserIds.has(p.user_id); const typeMatch = studentTypeFilter === 'EXTERNAL' ? isExternal : !isExternal; return eventMatch && searchMatch && deptMatch && yearMatch && viewMatch && typeMatch; }).sort((a, b) => { // Sort pending certificates to the top if (a.hasUploaded && !a.isVerified && (!b.hasUploaded || b.isVerified)) return -1; if (b.hasUploaded && !b.isVerified && (!a.hasUploaded || a.isVerified)) return 1; return 0; }); }, [mappedParticipants, selectedEventId, searchTerm, activeFilters, externalUserIds, studentTypeFilter, viewMode]); const handleBulkOdUpload = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file || selectedIds.size === 0) return; setIsBulkUploading(true); try { const reader = new FileReader(); reader.onloadend = async () => { try { const base64 = reader.result as string; const isPdf = file.type === 'application/pdf'; const extension = isPdf ? 'pdf' : 'jpg'; const fileName = `BULK_OD_${Date.now()}.${extension}`; const { uploadToSupabase } = await import('../supabase'); const publicUrl = await uploadToSupabase(base64, fileName, 'OD_PROVIDERS'); const idsArray = Array.from(selectedIds); const { error } = await supabase.from('registrations').update({ od_url: publicUrl }).in('id', idsArray); if (error) throw error; onShowToast(`OD Document assigned to ${selectedIds.size} students successfully.`); setSelectedIds(new Set()); await refreshRegs(); } catch (uploadErr: any) { console.error("Bulk OD Upload failed", uploadErr); alert(`Bulk OD Upload failed: ${uploadErr.message || 'Unknown error'}`); } finally { setIsBulkUploading(false); } }; reader.readAsDataURL(file); } catch (err: any) { console.error("Bulk OD Upload failed", err); alert(`Bulk OD Upload failed: ${err.message || 'Unknown error'}`); setIsBulkUploading(false); } }; const toggleSelectAll = () => { if (selectedIds.size === filteredParticipants.length) { setSelectedIds(new Set()); } else { setSelectedIds(new Set(filteredParticipants.map(p => p.id))); } }; const toggleFilterValue = (category: 'depts' | 'years', value: string) => { setActiveFilters(prev => { const current = prev[category]; const next = current.includes(value) ? current.filter(v => v !== value) : [...current, value]; return { ...prev, [category]: next }; }); }; return (
{isLandingMode ? (

Status Tracker

Choose Verification Category

) : ( <>

{viewMode === 'OD_PENDING' ? 'OD' : (viewMode === 'CERT_PENDING' ? 'Certification' : 'System')} {viewMode === 'ALL' ? 'Monitor' : 'Queue'}

Real-time Tracking & Verification Hub

setSearchTerm(e.target.value)} />
{viewMode !== 'CERT_PENDING' && (
)}
{isLoadingRegs ? (

Syncing Registrations...

) : filteredParticipants.length > 0 ? filteredParticipants.map((p) => (
{/* Selection Checkbox */}
{ const newSet = new Set(selectedIds); if (e.target.checked) newSet.add(p.id); else newSet.delete(p.id); setSelectedIds(newSet); }} />
{/* Student Info */}
{p.name.charAt(0)}

{p.name}

{p.hasUploaded && !p.isVerified && ( PENDING )}

{p.regNo}

{p.team_name && (

{p.team_name}

)}

{p.dept} • {p.year}

{p.college && (

{p.college}

)}

{p.eventTitle}

{/* Timeline View */}
{p.stages.map((stage: any, idx: number) => (
{stage.status === 'completed' ? : {idx + 1}}
{stage.label}
))}
{/* Actions Area */}
{p.certification_url && (
)} {/* Certification Status / Approval */} {(() => { const eventObj = events.find(e => e.id === p.event_id); const isOwner = eventObj && eventObj.created_by === currentUserId; if (p.isExternal) { return (
Auto-Verified (External)
); } if (p.hasUploaded && !p.isVerified) { if (isAdmin || !isOwner) { return (
{isOwner ? "Pending Approval" : "Read Only (Other Coord)"}
); } return ( ); } if (p.isVerified) { return (
Verified Credentials
); } return (
Awaiting Cert Upload
); })()} {/* OD Upload Actions */} {(() => { const eventObj = events.find(e => e.id === p.event_id); const isOwner = eventObj && eventObj.created_by === currentUserId; if (!p.hasUploadedOd) { if (isAdmin || !isOwner) { return (
{isOwner ? "No OD Uploaded" : "Read Only"}
); } return ( ); } return (
OD Uploaded
); })()}
)) : (

No matching student data

Try adjusting your filters or search term

)}
{/* Floating Bulk Action Bar */} {selectedIds.size > 0 && (
{selectedIds.size} Students Active
)} {showFilters && createPortal(
setShowFilters(false)} />

System Filters

Refine Registry View

Departments
{filterOptions.depts.map(dept => ( ))}
Years
{filterOptions.years.map(year => ( ))}
, document.body )} {viewingDoc && createPortal(
setViewingDoc(null)}>
e.stopPropagation()}>
Proof Inspection Mode
Document
, document.body )} )}
); }; export default AdminStatusTrackerView;