updated the event card in the students dashboard

This commit is contained in:
2026-06-22 09:16:54 +05:30
parent 87e2de7469
commit ef93f4bf48
5 changed files with 144 additions and 26 deletions

View File

@@ -12,7 +12,8 @@ import {
XCircle, XCircle,
Timer, Timer,
ChevronLeft, ChevronLeft,
ChevronDown ChevronDown,
Users
} from 'lucide-react'; } from 'lucide-react';
import { cn } from '../../lib/utils'; import { cn } from '../../lib/utils';
import { format } from 'date-fns'; import { format } from 'date-fns';
@@ -26,6 +27,7 @@ interface Event {
status: 'REQUESTED' | 'APPROVED' | 'COMPLETED' | 'CANCELLED' | 'PENDING_PR' | 'HOD_REJECTED' | 'PRINCIPAL_REJECTED'; status: 'REQUESTED' | 'APPROVED' | 'COMPLETED' | 'CANCELLED' | 'PENDING_PR' | 'HOD_REJECTED' | 'PRINCIPAL_REJECTED';
rejectionReason?: string; rejectionReason?: string;
category?: string; category?: string;
currentParticipants?: number;
} }
interface FacultyEventManagementProps { interface FacultyEventManagementProps {
@@ -159,6 +161,12 @@ export const FacultyEventManagement: React.FC<FacultyEventManagementProps> = ({
{event.location} {event.location}
</span> </span>
</div> </div>
<div className="flex items-center gap-1.5 text-slate-500">
<Users className="w-3.5 h-3.5 text-brand-indigo/60" />
<span className="text-[10px] font-bold">
{event.currentParticipants || 0} Registered
</span>
</div>
</div> </div>
</div> </div>

View File

@@ -64,13 +64,45 @@ export const StudentDashboard: React.FC = () => {
} }
}); });
const mapDepartmentToDomain = (dept: string): string => {
if (!dept) return '';
const d = dept.trim().toUpperCase();
if (d === 'AI&DS' || d === 'AIDS' || d === 'AI & DS') return 'AIDS';
if (d === 'AI&ML' || d === 'AIML' || d === 'AI & ML') return 'AIML';
if (d === 'MECH' || d === 'MECHANICAL') return 'Mechanical';
if (d === 'BIOTECH' || d === 'BIO-TECH') return 'Bio-tech';
if (d === 'EE(VLSI)' || d === 'VLSI') return 'VLSI';
return dept.trim();
};
const mapCategoryToStudentCategory = (dbCat: string, dbType: string): 'TECHNICAL' | 'NON-TECHNICAL' | 'WORKSHOP' | 'CENTRE-ACTIVITY' => {
const cat = (dbCat || '').toUpperCase();
if (cat === 'TECHNICAL' || cat === 'NON-TECHNICAL' || cat === 'WORKSHOP' || cat === 'CENTRE-ACTIVITY') {
return cat as any;
}
if (cat === 'CENTRE') {
return 'CENTRE-ACTIVITY';
}
if (cat === 'ACADEMIC') {
return 'TECHNICAL';
}
const type = (dbType || '').toLowerCase();
if (type === 'workshop') {
return 'WORKSHOP';
}
if (type === 'cultural event' || type === 'cultural') {
return 'NON-TECHNICAL';
}
return 'TECHNICAL';
};
return { return {
id: String(dbEvent.id), id: String(dbEvent.id),
title: dbEvent.title || 'Untitled Event', title: dbEvent.title || 'Untitled Event',
location: dbEvent.location || dbEvent.venue || 'TBD', location: dbEvent.location || dbEvent.venue || 'TBD',
date: dbEvent.startDate || dbEvent.date || new Date().toISOString(), date: dbEvent.startDate || dbEvent.date || new Date().toISOString(),
category: dbEvent.category || 'TECHNICAL', category: mapCategoryToStudentCategory(dbEvent.category, dbEvent.eventType || dbEvent.type || ''),
domain: dbEvent.domain || dbEvent.department || '', domain: mapDepartmentToDomain(dbEvent.domain || dbEvent.department || ''),
pricingType: dbEvent.hasRegistrationFee ? 'PAID' : 'FREE', pricingType: dbEvent.hasRegistrationFee ? 'PAID' : 'FREE',
coordinator: dbEvent.proposer?.fullName || dbEvent.coordinator || 'Faculty Coordinator', coordinator: dbEvent.proposer?.fullName || dbEvent.coordinator || 'Faculty Coordinator',
image: dbEvent.image || 'https://images.unsplash.com/photo-1517694712202-14dd9538aa97?auto=format&fit=crop&q=80&w=800', image: dbEvent.image || 'https://images.unsplash.com/photo-1517694712202-14dd9538aa97?auto=format&fit=crop&q=80&w=800',
@@ -343,7 +375,12 @@ export const StudentDashboard: React.FC = () => {
return ( return (
<> <>
<Hero events={events} /> <Hero events={events} />
<UpcomingEventsSlider events={events} /> <UpcomingEventsSlider
events={events}
bookedEventIds={bookedEventIds}
onToggleBooking={handleToggleBooking}
userRole={user?.role}
/>
<AboutHubSection /> <AboutHubSection />
<StatsSection events={events} /> <StatsSection events={events} />
<HomeDashboard <HomeDashboard

View File

@@ -10,9 +10,17 @@ const Portal: React.FC<{ children: React.ReactNode }> = ({ children }) => {
interface UpcomingEventsSliderProps { interface UpcomingEventsSliderProps {
events: Event[]; events: Event[];
bookedEventIds?: string[];
onToggleBooking?: (id: string) => void;
userRole?: string;
} }
const UpcomingEventsSlider: React.FC<UpcomingEventsSliderProps> = ({ events }) => { const UpcomingEventsSlider: React.FC<UpcomingEventsSliderProps> = ({
events,
bookedEventIds = [],
onToggleBooking,
userRole
}) => {
const [selectedEvent, setSelectedEvent] = useState<Event | null>(null); const [selectedEvent, setSelectedEvent] = useState<Event | null>(null);
const clubInfo = useMemo(() => selectedEvent ? CLUBS.find(c => c.name === selectedEvent.club) : null, [selectedEvent]); const clubInfo = useMemo(() => selectedEvent ? CLUBS.find(c => c.name === selectedEvent.club) : null, [selectedEvent]);
@@ -130,26 +138,60 @@ const UpcomingEventsSlider: React.FC<UpcomingEventsSliderProps> = ({ events }) =
</div> </div>
<div className="space-y-6 pb-6 mt-6 border-t border-gray-100 pt-6"> <div className="space-y-6 pb-6 mt-6 border-t border-gray-100 pt-6">
<div className="flex items-center gap-2 mb-2"> <div className="flex items-center justify-between gap-2 mb-2">
<div className="w-1 h-3 bg-[#f97316] rounded-full"></div> <div className="flex items-center gap-2">
<h4 className="text-[10px] font-black text-slate-800 uppercase tracking-[0.2em]">Summary</h4> <div className="w-1 h-3 bg-[#f97316] rounded-full"></div>
</div> <h4 className="text-[10px] font-black text-slate-800 uppercase tracking-[0.2em]">Summary</h4>
<div className="relative pt-2"> </div>
<i className="fas fa-quote-right absolute top-0 right-0 text-5xl text-slate-50 pointer-events-none -z-10"></i> {userRole === 'STUDENT' && bookedEventIds.includes(selectedEvent.id) && (
<p className="text-[14px] text-slate-600 leading-relaxed whitespace-pre-wrap font-medium"> <span className="px-2.5 py-1 bg-emerald-50 text-emerald-600 border border-emerald-200 text-[8px] font-black rounded-lg uppercase tracking-wider">
{selectedEvent.event_summary || "Details for this session will be provided soon."} Registered
</p> </span>
</div> )}
</div> </div>
<div className="relative pt-2">
<i className="fas fa-quote-right absolute top-0 right-0 text-5xl text-slate-50 pointer-events-none -z-10"></i>
<p className="text-[14px] text-slate-600 leading-relaxed whitespace-pre-wrap font-medium">
{selectedEvent.event_summary || "Details for this session will be provided soon."}
</p>
</div>
</div>
<div className="mt-4 shrink-0 border-t border-gray-100 pt-6"> <div className="mt-4 shrink-0 border-t border-gray-100 pt-6 flex flex-col gap-3">
<button {userRole === 'STUDENT' && onToggleBooking && (
onClick={() => setSelectedEvent(null)} <>
className="w-full py-4 bg-slate-900 text-white rounded-2xl font-black uppercase text-[10px] tracking-widest hover:bg-black transition-all shadow-xl shadow-slate-200 focus:outline-none focus:ring-4 focus:ring-slate-100" {bookedEventIds.includes(selectedEvent.id) ? (
> <button
Close Summary onClick={() => {
</button> if (window.confirm(`Are you sure you want to cancel your registration for "${selectedEvent.title}"?`)) {
</div> onToggleBooking(selectedEvent.id);
setSelectedEvent(null);
}
}}
className="w-full py-4 bg-rose-600 text-white rounded-2xl font-black uppercase text-[10px] tracking-widest hover:bg-rose-700 transition-all shadow-xl shadow-rose-200 focus:outline-none focus:ring-4 focus:ring-rose-100"
>
Cancel Registration
</button>
) : (
<button
onClick={() => {
onToggleBooking(selectedEvent.id);
setSelectedEvent(null);
}}
className="w-full py-4 bg-emerald-600 text-white rounded-2xl font-black uppercase text-[10px] tracking-widest hover:bg-emerald-700 transition-all shadow-xl shadow-emerald-200 focus:outline-none focus:ring-4 focus:ring-emerald-100"
>
Get Tickets
</button>
)}
</>
)}
<button
onClick={() => setSelectedEvent(null)}
className="w-full py-4 bg-slate-900 text-white rounded-2xl font-black uppercase text-[10px] tracking-widest hover:bg-black transition-all shadow-xl shadow-slate-200 focus:outline-none focus:ring-4 focus:ring-slate-100"
>
Close Summary
</button>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -506,8 +506,15 @@ window.fetch = async function(input: RequestInfo | URL, init?: RequestInit): Pro
const snap = await getDocs(collection(db, 'ems_events')); const snap = await getDocs(collection(db, 'ems_events'));
const events = snap.docs.map(d => d.data()); const events = snap.docs.map(d => d.data());
// Populate conflicts // Fetch all registrations to compute current participant counts dynamically
const regsSnap = await getDocs(collection(db, 'ems_registrations'));
const registrations = regsSnap.docs.map(d => d.data());
// Populate conflicts and currentParticipants
for (const event of events) { for (const event of events) {
const count = registrations.filter(r => String(r.eventId || r.event_id) === String(event.id)).length;
event.currentParticipants = count;
if (!['APPROVED', 'COMPLETED', 'CANCELLED'].includes(event.status)) { if (!['APPROVED', 'COMPLETED', 'CANCELLED'].includes(event.status)) {
const conflictMsg = await getConflictMessage(event); const conflictMsg = await getConflictMessage(event);
if (conflictMsg) { if (conflictMsg) {
@@ -1049,6 +1056,11 @@ window.fetch = async function(input: RequestInfo | URL, init?: RequestInit): Pro
}; };
await setDoc(regDocRef, newReg); await setDoc(regDocRef, newReg);
// Update event's currentParticipants
const eventDocRef = doc(db, 'ems_events', String(eventId));
await updateDoc(eventDocRef, { currentParticipants: currentParticipants + 1 });
return jsonResponse(newReg); return jsonResponse(newReg);
} }
@@ -1100,7 +1112,26 @@ window.fetch = async function(input: RequestInfo | URL, init?: RequestInit): Pro
if (path.startsWith('/api/registrations/') && method === 'DELETE') { if (path.startsWith('/api/registrations/') && method === 'DELETE') {
const parts = path.split('/'); const parts = path.split('/');
const id = parts[parts.length - 1]; const id = parts[parts.length - 1];
await deleteDoc(doc(db, 'ems_registrations', String(id))); const regRef = doc(db, 'ems_registrations', String(id));
const regSnap = await getDoc(regRef);
if (regSnap.exists()) {
const regData = regSnap.data();
const eventId = regData.eventId || regData.event_id;
await deleteDoc(regRef);
if (eventId) {
const allRegsSnap = await getDocs(collection(db, 'ems_registrations'));
const eventRegs = allRegsSnap.docs.map(d => d.data()).filter(r => String(r.eventId || r.event_id) === String(eventId));
const currentParticipants = eventRegs.length;
await updateDoc(doc(db, 'ems_events', String(eventId)), { currentParticipants });
}
} else {
await deleteDoc(regRef);
}
return jsonResponse({ message: "Registration deleted successfully" }); return jsonResponse({ message: "Registration deleted successfully" });
} }

BIN
image.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 44 KiB