162 lines
6.8 KiB
TypeScript
162 lines
6.8 KiB
TypeScript
// app/events/page.tsx
|
|
// Events listing page
|
|
|
|
import { query } from '@/lib/db';
|
|
import { Event } from '@/types/racing';
|
|
import Link from 'next/link';
|
|
import { TrophyIcon, UsersIcon, MapPinIcon, ClockIcon, CalendarIcon } from '@/components/ui/icons';
|
|
export const dynamic = "force-dynamic";
|
|
|
|
async function getEvents(): Promise<Event[]> {
|
|
const sql = `
|
|
SELECT
|
|
e.*,
|
|
COUNT(er.registration_id) as registrations_count
|
|
FROM events e
|
|
LEFT JOIN event_registrations er ON e.event_id = er.event_id AND er.status = 'REGISTERED'
|
|
WHERE e.event_status IN ('OPEN', 'CLOSED')
|
|
GROUP BY e.event_id
|
|
ORDER BY e.event_date ASC
|
|
`;
|
|
|
|
const rows = await query(sql);
|
|
return rows as Event[];
|
|
}
|
|
|
|
function formatDate(date: Date): string {
|
|
return new Date(date).toLocaleDateString('en-US', {
|
|
weekday: 'long',
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
});
|
|
}
|
|
|
|
export default async function EventsPage() {
|
|
const events = await getEvents();
|
|
|
|
return (
|
|
<>
|
|
{/* Hero */}
|
|
<div className="relative border-b border-white/10 grid-overlay">
|
|
<div className="max-w-7xl mx-auto px-6 py-16">
|
|
<div className="space-y-4">
|
|
<div className="inline-flex items-center space-x-2 px-3 py-1 border border-white/20 bg-black">
|
|
<TrophyIcon className="w-4 h-4" />
|
|
<span className="text-xs font-medium tracking-wider">RACING EVENTS</span>
|
|
</div>
|
|
<h1 className="text-6xl font-bold tracking-tight">
|
|
EVENTS
|
|
</h1>
|
|
<p className="text-white/60 text-lg max-w-3xl">
|
|
Join competitive racing events, championships, and special races. Register now to secure your spot.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Events List */}
|
|
<div className="max-w-7xl mx-auto px-6 py-12 space-y-6">
|
|
{events.length === 0 ? (
|
|
<div className="border border-white/10 p-16 text-center bg-black">
|
|
<TrophyIcon className="w-16 h-16 mx-auto mb-6 text-white/20" />
|
|
<p className="text-white/40 text-base tracking-wider">NO UPCOMING EVENTS</p>
|
|
<p className="text-white/20 text-sm mt-2">Check back soon for new racing events</p>
|
|
</div>
|
|
) : (
|
|
events.map((event: any) => {
|
|
const isOpen = event.event_status === 'OPEN';
|
|
const isFull = event.registrations_count >= event.max_participants;
|
|
const deadlinePassed = event.registration_deadline && new Date(event.registration_deadline) < new Date();
|
|
const canRegister = isOpen && !isFull && !deadlinePassed;
|
|
|
|
return (
|
|
<div key={event.event_id} className="border border-white/10 sharp-border bg-black">
|
|
|
|
<img
|
|
src="https://openwheels.racing/files/img/EnduranceEvents1-1.png?fckcache=999"
|
|
alt={event.event_name}
|
|
className="w-full h-48 object-cover object-[50%_0%] border-b border-white/10 grayscale hover:grayscale-0 transition duration-300"
|
|
/>
|
|
|
|
{/* Event Header */}
|
|
<div className="border-b border-white/10 p-6 topo-lines-dense">
|
|
<div className="flex items-start justify-between">
|
|
<div className="flex-1 space-y-3">
|
|
<div className="flex items-center space-x-3">
|
|
<div className={`flex items-center space-x-2 px-2 py-1 border text-xs ${
|
|
canRegister
|
|
? 'border-white/30 text-white'
|
|
: 'border-white/10 text-white/40'
|
|
}`}>
|
|
<span className="font-medium tracking-wider">{event.event_status}</span>
|
|
</div>
|
|
<span className="text-xs text-white/40 tracking-wider">
|
|
ID: {event.event_id}
|
|
</span>
|
|
</div>
|
|
<h2 className="text-2xl font-bold tracking-tight">
|
|
{event.event_name}
|
|
</h2>
|
|
<p className="text-white/70 text-sm max-w-3xl">
|
|
{event.event_description}
|
|
</p>
|
|
<div className="flex flex-wrap gap-x-6 gap-y-2">
|
|
<div className="flex items-center space-x-2">
|
|
<MapPinIcon className="w-4 h-4 text-white/60" />
|
|
<span className="text-white/80 text-sm">{event.event_track}</span>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<UsersIcon className="w-4 h-4 text-white/60" />
|
|
<span className="text-white/80 text-sm">
|
|
{event.registrations_count}/{event.max_participants} registered
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<CalendarIcon className="w-4 h-4 text-white/60" />
|
|
<span className="text-white/80 text-sm">
|
|
{formatDate(event.event_date)}
|
|
</span>
|
|
</div>
|
|
{event.event_duration && (
|
|
<div className="flex items-center space-x-2">
|
|
<ClockIcon className="w-4 h-4 text-white/60" />
|
|
<span className="text-white/80 text-sm">
|
|
{event.event_duration} minutes
|
|
</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Event Actions */}
|
|
<div className="p-6 flex items-center justify-between">
|
|
<div className="text-sm text-white/60">
|
|
{deadlinePassed && <span>Registration deadline passed</span>}
|
|
{isFull && !deadlinePassed && <span>Event is full</span>}
|
|
{!canRegister && !deadlinePassed && !isFull && event.event_status === 'CLOSED' && <span>Registration closed</span>}
|
|
</div>
|
|
<Link
|
|
href={`/events/${event.event_id}`}
|
|
className={`px-6 py-3 border text-sm font-medium transition-all ${
|
|
canRegister
|
|
? 'border-white hover:bg-white hover:text-black'
|
|
: 'border-white/20 text-white/60'
|
|
}`}
|
|
>
|
|
{canRegister ? 'REGISTER NOW' : 'VIEW DETAILS'}
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
);
|
|
})
|
|
)}
|
|
</div>
|
|
</>
|
|
);
|
|
}
|