import { API_BASE_URL } from '../../lib/config'; import React, { useEffect, useState } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { History, Search, Calendar, MapPin, CheckCircle2, XCircle, Clock, Download, Filter, AlertTriangle, ChevronRight, User, Heart, FileText, X, Building2, Users, Wallet, Ticket, Layers } from 'lucide-react'; import { cn } from '../../lib/utils'; import { format } from 'date-fns'; import { Pagination } from './Pagination'; interface Event { id: number; title: string; startDate: string; endDate: string; type: string; status: string; department: string; location: string; budget: number; conflictMessage?: string; rejectionReason?: string; institution: string; category: string; academicYears: string[]; targetDepartments?: string[]; targetedSections?: string[]; proposer?: { fullName: string; email?: string; department?: string; role?: string; }; description?: string; sponsors?: string[]; hasRegistrationFee: boolean; registrationFee: number; maxParticipants?: number | null; requirements?: string[]; refreshment_expense?: number; transportation_expense?: number; session_coverage_fee?: number; total_expense?: number; paymentLink?: string; isClubEvent?: boolean; eventScope?: string; } export const EventHistory: React.FC = () => { const [events, setEvents] = useState([]); const [searchQuery, setSearchQuery] = useState(''); const [statusFilter, setStatusFilter] = useState('ALL'); const [selectedEvent, setSelectedEvent] = useState(null); const [currentPage, setCurrentPage] = useState(1); const [itemsPerPage, setItemsPerPage] = useState(10); useEffect(() => { fetch(API_BASE_URL + '/api/events') .then(res => res.json()) .then(data => setEvents(data)) .catch(err => console.error('History fetch failed:', err)); }, []); const getStatusStyle = (status: string, hasConflict: boolean) => { if (hasConflict && status !== 'APPROVED') return { bg: 'bg-red-50', text: 'text-red-600', border: 'border-red-100', icon: AlertTriangle, label: 'CONFLICT' }; switch (status) { case 'APPROVED': return { bg: 'bg-emerald-50', text: 'text-emerald-600', border: 'border-emerald-100', icon: CheckCircle2, label: 'APPROVED' }; case 'REQUESTED': return { bg: 'bg-blue-50', text: 'text-blue-600', border: 'border-blue-100', icon: Clock, label: 'REQUESTED' }; case 'PENDING_PR': return { bg: 'bg-purple-50', text: 'text-purple-600', border: 'border-purple-100', icon: ShieldCheck, label: 'PENDING PRINCIPAL' }; case 'HOD_REJECTED': case 'PRINCIPAL_REJECTED': return { bg: 'bg-red-50', text: 'text-red-600', border: 'border-red-100', icon: XCircle, label: 'REJECTED' }; default: return { bg: 'bg-slate-50', text: 'text-slate-600', border: 'border-slate-100', icon: Clock, label: status }; } }; const filteredEvents = events .filter(e => { const matchesSearch = e.title.toLowerCase().includes(searchQuery.toLowerCase()) || e.department.toLowerCase().includes(searchQuery.toLowerCase()); if (statusFilter === 'ALL') return matchesSearch; if (statusFilter === 'PENDING') return matchesSearch && (e.status === 'REQUESTED' || e.status === 'PENDING_PR'); if (statusFilter === 'REJECTED') return matchesSearch && e.status.includes('REJECTED'); return matchesSearch && e.status === statusFilter; }) .sort((a, b) => new Date(b.startDate).getTime() - new Date(a.startDate).getTime()); const currentEvents = filteredEvents.slice( (currentPage - 1) * itemsPerPage, currentPage * itemsPerPage ); return (
{/* Header Section */}

Institutional Event History

Audit-ready comprehensive event ledger

{/* Filters Bar */}
setSearchQuery(e.target.value)} className="w-full h-16 pl-16 pr-8 bg-white border border-slate-100 rounded-3xl text-sm font-bold text-text-dark focus:outline-none focus:ring-4 focus:ring-brand-indigo/5 focus:border-brand-indigo transition-all" />
{['ALL', 'APPROVED', 'PENDING', 'REJECTED'].map(status => ( ))}
{/* Events Ledger */}
{currentEvents.map((event, idx) => { const statusStyle = getStatusStyle(event.status, !!event.conflictMessage); const StatusIcon = statusStyle.icon; return ( setSelectedEvent(event)} className="group hover:bg-slate-50/50 transition-colors cursor-pointer" > ); })}
Event Details Logistics Status Budget
{event.department?.slice(0, 3).toUpperCase()}

{event.title}

{event.type}

{format(new Date(event.startDate), 'MMM d, yyyy')}
{event.location}
{statusStyle.label}
₹{event.budget?.toLocaleString() || '0'}
{ setItemsPerPage(val); setCurrentPage(1); }} itemsPerPageOptions={[5, 10, 20, 50]} />
{/* Detail Modal */} {selectedEvent && (
setSelectedEvent(null)} className="absolute inset-0 bg-slate-900/40 backdrop-blur-sm" />
Event History {selectedEvent.institution}

{selectedEvent.title}

{/* Description */} {selectedEvent.description && (
Description

{selectedEvent.description}

)} {/* Event Classification */}
Event Type

{selectedEvent.type || 'N/A'}

Hub Category

{selectedEvent.category === 'TECHNICAL' ? 'Technical' : selectedEvent.category === 'NON-TECHNICAL' ? 'Non-Technical' : selectedEvent.category === 'WORKSHOP' ? 'Workshop' : selectedEvent.category === 'CENTRE-ACTIVITY' ? 'Centre Based Activity' : selectedEvent.category || 'N/A'}

{/* Dates */}
Start Date & Time

{selectedEvent.startDate ? new Date(selectedEvent.startDate).toLocaleString('en-IN', { timeZone: 'Asia/Kolkata', day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit', hour12: false }) + ' IST' : 'N/A'}

End Date & Time

{selectedEvent.endDate ? new Date(selectedEvent.endDate).toLocaleString('en-IN', { timeZone: 'Asia/Kolkata', day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit', hour12: false }) + ' IST' : 'N/A'}

{/* Location & Capacity */}
Location

{selectedEvent.location || 'N/A'}

Total Capacity

{selectedEvent.maxParticipants ? selectedEvent.maxParticipants.toLocaleString() : 'Unlimited'}

{/* Scope */}
Event Scope

{selectedEvent.department === 'Institutional' ? 'Institutional Event' : 'Departmental Event'}

Proposing Department

{(selectedEvent.proposer?.department && selectedEvent.proposer?.department !== 'N/A') ? selectedEvent.proposer.department : (selectedEvent.proposer?.role === 'ADMIN' ? 'System Administrator' : selectedEvent.department) || 'N/A'}

{/* Target Departments & Batches */}
Target Audience

Departments

{(selectedEvent.targetDepartments && selectedEvent.targetDepartments.length > 0) ? selectedEvent.targetDepartments.join(', ') : selectedEvent.department || 'N/A'}

Batches

{selectedEvent.academicYears?.length > 0 ? selectedEvent.academicYears.join(', ') : 'All'}

Sections

{(selectedEvent.targetedSections && selectedEvent.targetedSections.length > 0) ? selectedEvent.targetedSections.join(', ') : 'All Sections'}

{/* Sponsors */} {selectedEvent.sponsors && selectedEvent.sponsors.length > 0 && (
Event Sponsors
{selectedEvent.sponsors.map((sponsor, idx) => ( {sponsor} ))}
)} {/* Event Amenities / Requirements */} {selectedEvent.requirements && selectedEvent.requirements.length > 0 && (
Event Amenities
{selectedEvent.requirements.map((req, idx) => ( {req} ))}
)} {/* Financial Details */}
Financial Details

Budget

₹{selectedEvent.budget?.toLocaleString() || '0'}

{(selectedEvent.refreshment_expense || 0) > 0 && (

Refreshments

₹{selectedEvent.refreshment_expense?.toLocaleString()}

)} {(selectedEvent.transportation_expense || 0) > 0 && (

Transportation

₹{selectedEvent.transportation_expense?.toLocaleString()}

)} {(selectedEvent.session_coverage_fee || 0) > 0 && (

Session Coverage

₹{selectedEvent.session_coverage_fee?.toLocaleString()}

)}

Total Expense

₹{(selectedEvent.total_expense || selectedEvent.budget || 0).toLocaleString()}

{/* Registration / Free Event */}
Registration

{selectedEvent.hasRegistrationFee ? `Paid — ₹${selectedEvent.registrationFee?.toLocaleString()}` : 'Free Event'}

{selectedEvent.hasRegistrationFee && selectedEvent.paymentLink && ( Payment Link ↗ )}
Status

{selectedEvent.status}

{selectedEvent.rejectionReason && (

Rejection Reason

{selectedEvent.rejectionReason}

)}
)}
); }; const ShieldCheck = (props: any) => ( );