Added time logic and edited several buttons in hod panel

This commit is contained in:
2026-07-02 08:49:32 +05:30
parent 31fcc27658
commit 2f18d432e7
4 changed files with 333 additions and 11 deletions

View File

@@ -1,6 +1,6 @@
import { API_BASE_URL } from '../../lib/config';
import React, { useEffect, useState } from 'react';
import { motion } from 'framer-motion';
import { motion, AnimatePresence } from 'framer-motion';
import {
Calendar,
MapPin,
@@ -8,22 +8,46 @@ import {
Filter,
Search,
Trash2,
FileSpreadsheet
FileSpreadsheet,
FileText,
Users,
Ticket,
Wallet,
Heart,
XCircle,
AlertTriangle,
Split,
Layers,
CheckCircle
} from 'lucide-react';
import { cn } from '../../lib/utils';
import { useAuth } from '../../context/AuthContext';
import { StatusTimeline, type Event } from './EventStatusTimeline';
import { Pagination } from './Pagination';
interface ExtendedEvent extends Event {
description?: string;
sponsors?: string[];
academicYears?: string[];
dayConfigs?: any[];
deptLimits?: Record<string, any>;
deptSectionLimits?: Record<string, any>;
budget?: number;
hasRegistrationFee?: boolean;
registrationFee?: number;
conflictMessage?: string;
}
interface AllEventsProps {
onEditEvent?: (event: any) => void;
}
export const AllEvents: React.FC<AllEventsProps> = ({ onEditEvent }) => {
const { user } = useAuth();
const [events, setEvents] = useState<Event[]>([]);
const [events, setEvents] = useState<ExtendedEvent[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [searchTerm, setSearchTerm] = useState('');
const [selectedEvent, setSelectedEvent] = useState<ExtendedEvent | null>(null);
useEffect(() => {
fetchEvents();
@@ -78,7 +102,6 @@ export const AllEvents: React.FC<AllEventsProps> = ({ onEditEvent }) => {
}
};
const [currentPage, setCurrentPage] = useState(1);
const [itemsPerPage, setItemsPerPage] = useState(10);
@@ -209,6 +232,12 @@ export const AllEvents: React.FC<AllEventsProps> = ({ onEditEvent }) => {
</td>
<td className="px-8 py-6 text-right">
<div className="flex items-center justify-end gap-2">
<button
onClick={() => setSelectedEvent(event)}
className="px-4 py-2 bg-slate-50 text-brand-indigo hover:bg-brand-indigo hover:text-white rounded-xl text-[9px] font-black uppercase tracking-widest transition-all border border-brand-indigo/10"
>
View
</button>
{(user?.role === 'ADMIN' || (event.status === 'REQUESTED' && event.proposer?.email === user?.email)) && onEditEvent && (
<button
onClick={() => onEditEvent(event)}
@@ -248,6 +277,262 @@ export const AllEvents: React.FC<AllEventsProps> = ({ onEditEvent }) => {
</>
)}
</div>
{/* Details Modal */}
<AnimatePresence>
{selectedEvent && (
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4">
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={() => setSelectedEvent(null)}
className="absolute inset-0 bg-slate-900/40 backdrop-blur-sm"
/>
<motion.div
initial={{ opacity: 0, scale: 0.9, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.9, y: 20 }}
className="relative w-full max-w-2xl bg-white rounded-[2.5rem] premium-shadow overflow-hidden max-h-[90vh] overflow-y-auto"
>
<div className={cn(
"p-8 text-white flex justify-between items-start",
selectedEvent.status === 'CANCELLED' ? "bg-red-600" : "bg-brand-navy"
)}>
<div>
<div className="flex items-center gap-2 mb-2">
<span className="text-[10px] font-black uppercase tracking-[0.2em] text-white/60">Event Details</span>
<span className="w-1 h-1 bg-white/30 rounded-full" />
<span className="text-[10px] font-black uppercase tracking-[0.2em] text-white/60">{selectedEvent.institution}</span>
{selectedEvent.category === 'CLUB' && (
<>
<span className="w-1 h-1 bg-white/30 rounded-full" />
<span className="text-[10px] font-black uppercase tracking-[0.2em] text-emerald-400">Institutional Club Event</span>
</>
)}
</div>
<h3 className="text-2xl font-black tracking-tight">{selectedEvent.title}</h3>
</div>
<button
onClick={() => setSelectedEvent(null)}
className="p-2 hover:bg-white/10 rounded-xl transition-all"
>
<XCircle className="w-6 h-6" />
</button>
</div>
<div className="p-8 space-y-8">
{/* Description Section */}
{selectedEvent.description && (
<div className="space-y-2">
<div className="flex items-center gap-2 text-[10px] font-black uppercase tracking-widest text-text-muted">
<FileText className="w-3.5 h-3.5" />
Event Description
</div>
<p className="text-sm font-medium text-text-dark leading-relaxed bg-slate-50 p-4 rounded-2xl border border-slate-100">
{selectedEvent.description}
</p>
</div>
)}
<div className="grid grid-cols-2 gap-8">
<div className="space-y-6">
<div className="space-y-1">
<div className="flex items-center gap-2 text-[10px] font-black uppercase tracking-widest text-text-muted">
<Calendar className="w-3.5 h-3.5" />
Schedule
</div>
<p className="text-sm font-bold text-text-dark">
{parseDate(selectedEvent.startDate) ? parseDate(selectedEvent.startDate)!.toLocaleString('en-IN') : 'N/A'}
</p>
</div>
<div className="space-y-1">
<div className="flex items-center gap-2 text-[10px] font-black uppercase tracking-widest text-text-muted">
<MapPin className="w-3.5 h-3.5" />
Venue
</div>
<p className="text-sm font-bold text-text-dark">{selectedEvent.location}</p>
</div>
</div>
<div className="space-y-6">
<div className="space-y-1">
<div className="flex items-center gap-2 text-[10px] font-black uppercase tracking-widest text-text-muted">
<Users className="w-3.5 h-3.5" />
Proposed By
</div>
<p className="text-sm font-bold text-text-dark">{selectedEvent.proposer?.fullName || 'Faculty'}</p>
<p className="text-[10px] font-black text-brand-indigo uppercase">{selectedEvent.department}</p>
</div>
{selectedEvent.academicYears && selectedEvent.academicYears.length > 0 && (
<div className="space-y-1">
<div className="flex items-center gap-2 text-[10px] font-black uppercase tracking-widest text-text-muted">
<Users className="w-3.5 h-3.5" />
Target Audience
</div>
<p className="text-sm font-bold text-text-dark">{selectedEvent.academicYears.join(', ')} Batches</p>
</div>
)}
</div>
</div>
{/* Sponsors Section */}
{selectedEvent.sponsors && selectedEvent.sponsors.length > 0 && (
<div className="space-y-3">
<div className="flex items-center gap-2 text-[10px] font-black uppercase tracking-widest text-text-muted">
<Heart className="w-3.5 h-3.5 text-brand-indigo" />
Event Sponsors
</div>
<div className="flex flex-wrap gap-2">
{selectedEvent.sponsors.map((sponsor, idx) => (
<span key={idx} className="px-3 py-1 bg-brand-glow text-brand-indigo rounded-lg text-[10px] font-black uppercase tracking-widest border border-brand-indigo/10">
{sponsor}
</span>
))}
</div>
</div>
)}
{/* Itinerary & Day Configs */}
{selectedEvent.dayConfigs && selectedEvent.dayConfigs.length > 0 && (
<div className="space-y-3">
<div className="flex items-center gap-2 text-[10px] font-black uppercase tracking-widest text-text-muted">
<Calendar className="w-3.5 h-3.5 text-brand-indigo" />
Detailed Itinerary & Schedule
</div>
<div className="space-y-4">
{selectedEvent.dayConfigs.map((day: any, dIdx: number) => {
const dayDate = day.date ? new Date(day.date) : null;
return (
<div key={dIdx} className="p-4 rounded-2xl bg-slate-50 border border-slate-100 space-y-3">
<div className="flex justify-between items-center border-b border-slate-200 pb-1.5">
<span className="text-[11px] font-black text-brand-navy uppercase tracking-wider">
Day {dIdx + 1}: {dayDate && !isNaN(dayDate.getTime()) ? dayDate.toLocaleDateString() : day.date || 'TBD'}
</span>
<span className="px-2 py-0.5 bg-brand-glow text-brand-indigo text-[8px] font-black uppercase tracking-widest rounded">
{day.batches?.length || 0} {(day.batches?.length || 0) === 1 ? 'Batch' : 'Batches'}
</span>
</div>
<div className="space-y-3 divide-y divide-slate-200/50">
{day.batches?.map((batch: any, bIdx: number) => (
<div key={bIdx} className="pt-3 first:pt-0 space-y-2">
<div className="flex justify-between items-center">
<span className="text-[9px] font-black text-slate-400 uppercase tracking-widest">
Batch {batch.id || bIdx + 1}
</span>
<span className="text-[10px] font-bold text-brand-indigo bg-white px-2 py-0.5 rounded-lg border border-slate-200 shadow-sm flex items-center gap-1">
<Clock className="w-3.5 h-3.5" />
{batch.startTime || 'TBD'} - {batch.endTime || 'TBD'}
</span>
</div>
{batch.resourcePerson && batch.resourcePerson.name && (
<div className="p-3 bg-white rounded-xl border border-slate-100 space-y-1.5">
<div className="flex justify-between items-start">
<div>
<p className="text-[8px] font-black text-slate-400 uppercase tracking-widest">Resource Person</p>
<p className="text-xs font-bold text-text-dark">{batch.resourcePerson.name}</p>
</div>
<span className={cn(
"px-2 py-0.5 rounded text-[8px] font-black uppercase tracking-wider",
batch.resourcePerson.type === 'EXTERNAL' ? "bg-amber-50 text-amber-600 border border-amber-100" : "bg-brand-glow text-brand-indigo border border-brand-indigo/10"
)}>
{batch.resourcePerson.type || 'INTERNAL'}
</span>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-1.5 text-[9px] text-text-muted font-semibold">
{batch.resourcePerson.dept && (
<div>
<span className="font-bold text-slate-400 uppercase tracking-wider">Dept:</span> {batch.resourcePerson.dept}
</div>
)}
{batch.resourcePerson.college_name && (
<div className="col-span-2">
<span className="font-bold text-slate-400 uppercase tracking-wider">Inst:</span> {batch.resourcePerson.college_name}
</div>
)}
{batch.resourcePerson.phone && (
<div>
<span className="font-bold text-slate-400 tracking-wider uppercase">Phone:</span> {batch.resourcePerson.phone}
</div>
)}
{batch.resourcePerson.email && (
<div>
<span className="font-bold text-slate-400 tracking-wider uppercase">Email:</span> {batch.resourcePerson.email}
</div>
)}
</div>
</div>
)}
</div>
))}
</div>
</div>
);
})}
</div>
</div>
)}
{/* Department limits and quotas */}
{selectedEvent.deptLimits && Object.keys(selectedEvent.deptLimits).length > 0 && (
<div className="space-y-3">
<div className="flex items-center gap-2 text-[10px] font-black uppercase tracking-widest text-text-muted">
<Users className="w-3.5 h-3.5 text-brand-indigo" />
Department Limits & Quotas
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{Object.entries(selectedEvent.deptLimits).map(([dept, maxSeats]) => {
const sectionLimits = selectedEvent.deptSectionLimits?.[dept];
const hasSectionLimits = sectionLimits && Object.keys(sectionLimits).length > 0;
return (
<div key={dept} className="bg-slate-50 border border-slate-100 rounded-2xl p-4 space-y-3">
<div className="flex justify-between items-center">
<span className="text-xs font-black text-brand-navy uppercase tracking-wider">{dept}</span>
<span className="px-2 py-0.5 bg-brand-indigo text-white text-[9px] font-black uppercase rounded-md">
Quota: {maxSeats} Seats
</span>
</div>
{hasSectionLimits && (
<div className="pt-2 border-t border-slate-200/60 grid grid-cols-3 gap-2">
{Object.entries(sectionLimits).map(([sec, limit]) => (
<div key={sec} className="bg-white px-2 py-1 rounded-xl border border-slate-100 text-center">
<span className="block text-[8px] font-black text-slate-400 uppercase mb-0.5">Sec {sec}</span>
<span className="text-[10px] font-black text-brand-navy">{limit}</span>
</div>
))}
</div>
)}
</div>
);
})}
</div>
</div>
)}
<div className="grid grid-cols-2 gap-8 pt-4 border-t border-slate-50">
<div className="p-4 bg-slate-50 rounded-2xl">
<div className="flex items-center gap-2 text-[10px] font-black uppercase tracking-widest text-text-muted mb-1">
<Wallet className="w-3.5 h-3.5 text-brand-indigo" />
Estimated Budget
</div>
<p className="text-lg font-black text-text-dark">{selectedEvent.budget?.toLocaleString() || '0'}</p>
</div>
<div className="p-4 bg-slate-50 rounded-2xl">
<div className="flex items-center gap-2 text-[10px] font-black uppercase tracking-widest text-text-muted mb-1">
<Ticket className="w-3.5 h-3.5 text-brand-indigo" />
Registration
</div>
<p className="text-lg font-black text-text-dark">
{selectedEvent.hasRegistrationFee ? `${selectedEvent.registrationFee}` : 'FREE'}
</p>
</div>
</div>
</div>
</motion.div>
</div>
)}
</AnimatePresence>
</div>
);
};