updated the event card in the students dashboard
This commit is contained in:
@@ -12,7 +12,8 @@ import {
|
||||
XCircle,
|
||||
Timer,
|
||||
ChevronLeft,
|
||||
ChevronDown
|
||||
ChevronDown,
|
||||
Users
|
||||
} from 'lucide-react';
|
||||
import { cn } from '../../lib/utils';
|
||||
import { format } from 'date-fns';
|
||||
@@ -26,6 +27,7 @@ interface Event {
|
||||
status: 'REQUESTED' | 'APPROVED' | 'COMPLETED' | 'CANCELLED' | 'PENDING_PR' | 'HOD_REJECTED' | 'PRINCIPAL_REJECTED';
|
||||
rejectionReason?: string;
|
||||
category?: string;
|
||||
currentParticipants?: number;
|
||||
}
|
||||
|
||||
interface FacultyEventManagementProps {
|
||||
@@ -159,6 +161,12 @@ export const FacultyEventManagement: React.FC<FacultyEventManagementProps> = ({
|
||||
{event.location}
|
||||
</span>
|
||||
</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>
|
||||
|
||||
|
||||
@@ -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 {
|
||||
id: String(dbEvent.id),
|
||||
title: dbEvent.title || 'Untitled Event',
|
||||
location: dbEvent.location || dbEvent.venue || 'TBD',
|
||||
date: dbEvent.startDate || dbEvent.date || new Date().toISOString(),
|
||||
category: dbEvent.category || 'TECHNICAL',
|
||||
domain: dbEvent.domain || dbEvent.department || '',
|
||||
category: mapCategoryToStudentCategory(dbEvent.category, dbEvent.eventType || dbEvent.type || ''),
|
||||
domain: mapDepartmentToDomain(dbEvent.domain || dbEvent.department || ''),
|
||||
pricingType: dbEvent.hasRegistrationFee ? 'PAID' : 'FREE',
|
||||
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',
|
||||
@@ -343,7 +375,12 @@ export const StudentDashboard: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
<Hero events={events} />
|
||||
<UpcomingEventsSlider events={events} />
|
||||
<UpcomingEventsSlider
|
||||
events={events}
|
||||
bookedEventIds={bookedEventIds}
|
||||
onToggleBooking={handleToggleBooking}
|
||||
userRole={user?.role}
|
||||
/>
|
||||
<AboutHubSection />
|
||||
<StatsSection events={events} />
|
||||
<HomeDashboard
|
||||
|
||||
@@ -10,9 +10,17 @@ const Portal: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
|
||||
interface UpcomingEventsSliderProps {
|
||||
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 clubInfo = useMemo(() => selectedEvent ? CLUBS.find(c => c.name === selectedEvent.club) : null, [selectedEvent]);
|
||||
|
||||
@@ -130,26 +138,60 @@ const UpcomingEventsSlider: React.FC<UpcomingEventsSliderProps> = ({ events }) =
|
||||
</div>
|
||||
|
||||
<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="w-1 h-3 bg-[#f97316] rounded-full"></div>
|
||||
<h4 className="text-[10px] font-black text-slate-800 uppercase tracking-[0.2em]">Summary</h4>
|
||||
</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="flex items-center justify-between gap-2 mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-1 h-3 bg-[#f97316] rounded-full"></div>
|
||||
<h4 className="text-[10px] font-black text-slate-800 uppercase tracking-[0.2em]">Summary</h4>
|
||||
</div>
|
||||
{userRole === 'STUDENT' && bookedEventIds.includes(selectedEvent.id) && (
|
||||
<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">
|
||||
Registered
|
||||
</span>
|
||||
)}
|
||||
</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">
|
||||
<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 className="mt-4 shrink-0 border-t border-gray-100 pt-6 flex flex-col gap-3">
|
||||
{userRole === 'STUDENT' && onToggleBooking && (
|
||||
<>
|
||||
{bookedEventIds.includes(selectedEvent.id) ? (
|
||||
<button
|
||||
onClick={() => {
|
||||
if (window.confirm(`Are you sure you want to cancel your registration for "${selectedEvent.title}"?`)) {
|
||||
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>
|
||||
|
||||
@@ -506,8 +506,15 @@ window.fetch = async function(input: RequestInfo | URL, init?: RequestInit): Pro
|
||||
const snap = await getDocs(collection(db, 'ems_events'));
|
||||
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) {
|
||||
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)) {
|
||||
const conflictMsg = await getConflictMessage(event);
|
||||
if (conflictMsg) {
|
||||
@@ -1049,6 +1056,11 @@ window.fetch = async function(input: RequestInfo | URL, init?: RequestInit): Pro
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1100,7 +1112,26 @@ window.fetch = async function(input: RequestInfo | URL, init?: RequestInit): Pro
|
||||
if (path.startsWith('/api/registrations/') && method === 'DELETE') {
|
||||
const parts = path.split('/');
|
||||
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" });
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user