Files
Event-Management-System/RIT-EVENT-MANAGEMENT--main/components/UpcomingEventsSlider.tsx

163 lines
7.8 KiB
TypeScript

import React, { useMemo, useState } from 'react';
import { createPortal } from 'react-dom';
import { Event } from '../types';
import { CLUBS } from '../constants';
const Portal: React.FC<{ children: React.ReactNode }> = ({ children }) => {
return createPortal(children, document.body);
};
interface UpcomingEventsSliderProps {
events: Event[];
}
const UpcomingEventsSlider: React.FC<UpcomingEventsSliderProps> = ({ events }) => {
const [selectedEvent, setSelectedEvent] = useState<Event | null>(null);
const clubInfo = useMemo(() => selectedEvent ? CLUBS.find(c => c.name === selectedEvent.club) : null, [selectedEvent]);
const sortedEvents = useMemo(() => {
return [...events]
.filter(e => e.status !== 'Completed')
.sort((a, b) => {
return new Date(a.date).getTime() - new Date(b.date).getTime();
});
}, [events]);
// Duplicate events for seamless loop only if we have enough to fill the track
const loopEvents = sortedEvents.length > 2 ? [...sortedEvents, ...sortedEvents] : sortedEvents;
if (sortedEvents.length === 0) return null;
return (
<div className="py-24 px-6 md:px-12 lg:px-24 bg-white overflow-hidden">
<style>
{`
@keyframes merryGoRound {
0% { transform: translateX(0); }
100% { transform: translateX(-50%); }
}
.animate-merry-go-round {
animation: merryGoRound 30s linear infinite;
display: flex;
width: max-content;
}
.animate-merry-go-round:hover {
animation-play-state: paused;
}
`}
</style>
<div className="flex flex-col lg:flex-row items-start lg:items-end justify-between mb-16 gap-8">
<div>
<h3 className="text-4xl md:text-5xl font-serif text-[#1A202C] tracking-tight">Upcoming Experiences</h3>
</div>
</div>
<div className="relative overflow-hidden w-full">
{/* Gradient masks removed to eliminate fogginess */}
<div className="animate-merry-go-round gap-6 md:gap-8">
{loopEvents.map((event, idx) => (
<div
key={`${event.id}-${idx}`}
className="w-[280px] md:w-[350px] lg:w-[400px] flex-shrink-0 bg-white rounded-[2.5rem] p-5 group transition-all shadow-xl shadow-gray-900/5 hover:-translate-y-1 border border-gray-200"
>
<div className="h-48 md:h-56 w-full rounded-[2rem] overflow-hidden mb-6 md:mb-8 relative">
<img
src={event.image}
alt={event.title}
className="w-full h-full object-cover transition-transform duration-1000 group-hover:scale-110"
/>
<div className="absolute top-4 right-4 bg-white px-4 py-2 rounded-full border border-gray-200">
<span className="text-[9px] font-black text-[#1A202C] uppercase tracking-widest">{event.category}</span>
</div>
</div>
<div className="px-2 pb-2 md:pb-4">
<h4 className="text-xl md:text-2xl font-serif text-[#1A202C] mb-4 md:mb-6 group-hover:text-[#f97316] transition-colors line-clamp-1">
{event.title}
</h4>
<div className="flex flex-col gap-3 text-gray-500 text-[9px] md:text-[10px] font-bold uppercase tracking-[0.2em]">
<div className="flex items-center gap-3">
<i className="far fa-calendar-alt text-[#f97316]"></i>
<span className="flex items-center gap-2">
{event.date}
<span className="w-1 h-1 bg-gray-300 rounded-full"></span>
<span className="text-[#f97316]">{event.schedule?.[0]?.start_time || ''}</span>
</span>
</div>
<div className="flex items-center gap-3">
<i className="fas fa-location-dot text-[#f97316]"></i>
<span className="truncate">{event.location}</span>
</div>
</div>
<div className="mt-6 md:mt-8 pt-4 md:pt-6 border-t border-gray-200/30 flex justify-between items-center">
<span className="text-[8px] md:text-[9px] text-gray-400 font-black uppercase tracking-widest">Limited Access</span>
<button
onClick={() => setSelectedEvent(event)}
className="text-[#1A202C] text-[9px] md:text-[10px] font-black uppercase tracking-[0.3em] hover:text-[#f97316] transition-colors flex items-center gap-2"
>
Details <i className="fas fa-arrow-right text-[8px]"></i>
</button>
</div>
</div>
</div>
))}
</div>
</div>
{selectedEvent && (
<Portal>
<div className="fixed inset-0 z-[10000] flex items-center justify-center p-4 sm:p-6 bg-slate-900/60 backdrop-blur-sm animate-in fade-in duration-300">
<div className="bg-white rounded-[2rem] w-full max-w-lg shadow-2xl animate-in zoom-in-95 duration-500 max-h-[85vh] flex flex-col relative">
<button
onClick={() => setSelectedEvent(null)}
className="absolute top-6 right-6 w-8 h-8 rounded-full bg-slate-100 text-slate-500 hover:bg-slate-200 hover:text-slate-800 transition-all flex items-center justify-center z-10"
>
<i className="fas fa-times text-sm"></i>
</button>
<div className="p-8 overflow-y-auto flex-1 custom-scrollbar">
<div className="flex items-center gap-4 mb-8 pb-6 border-b border-gray-100 pr-8">
{clubInfo && (
<img src={clubInfo.image} className="w-12 h-12 rounded-full object-cover border border-slate-200 shadow-sm" alt="" />
)}
<div>
<span className="block text-[10px] font-black text-[#f97316] uppercase tracking-[0.2em] mb-1">{selectedEvent.club || 'Organized by'}</span>
<span className="text-[10px] font-bold text-gray-500 uppercase tracking-widest flex items-center gap-2">
<i className="fas fa-user text-[9px]"></i> {selectedEvent.coordinator}
</span>
</div>
</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="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>
</div>
</div>
</Portal>
)}
</div>
);
};
export default UpcomingEventsSlider;