diff --git a/app/events/[slug]/page.tsx b/app/events/[slug]/page.tsx new file mode 100644 index 0000000..a8e6089 --- /dev/null +++ b/app/events/[slug]/page.tsx @@ -0,0 +1,363 @@ +import Link from 'next/link'; +import { notFound } from 'next/navigation'; +import { Section, Badge, Button } from '../../../components/UI'; +import { EVENTS } from '../../../data'; +import { + ArrowLeft, Calendar, Clock, MapPin, Ticket, ShieldAlert, + Swords, Trophy, Users, ExternalLink, AlertTriangle +} from 'lucide-react'; + +interface EventPageProps { + params: Promise<{ slug: string }>; +} + +export default async function EventDetailPage({ params }: EventPageProps) { + const { slug } = await params; + const event = EVENTS.find((e) => e.slug === slug); + + if (!event) { + notFound(); + } + + const isTournament = Boolean(event.rules && event.rules.length > 0); + + return ( +
+ {/* Hero Banner */} +
+
+
+ +
+
+ + All Events + + +
+ {event.status === 'upcoming' ? 'Upcoming Event' : 'Past Event'} + {isTournament && ( + + Live 1v1 Knockout + + )} + {event.tags.map((tag) => ( + + {tag} + + ))} +
+ +

+ {event.title} +

+ +

+ {event.description} +

+ + {/* Quick Metadata Bar */} +
+
+
+ +
+
+
Date
+
+ {new Date(event.date).toLocaleDateString('en-UG', { + weekday: 'short', + year: 'numeric', + month: 'short', + day: 'numeric', + timeZone: 'Africa/Kampala' + })} +
+
+
+ +
+
+ +
+
+
Time
+
{event.time} EAT
+
+
+ +
+
+ +
+
+
Venue
+
{event.venue}
+
{event.address}
+
+
+
+ + {/* Action Buttons */} + {event.ticketsUrl && ( +
+ + {event.videoUrl && ( + + Format Demo Video + + )} +
+ )} +
+
+
+ +
+
+ {/* Rules & Integrity Policy (Special for Tournament) */} + {event.rules && event.rules.length > 0 && ( +
+
+ +

+ Tournament Rules & Battle Code +

+
+

+ Strict adherence is required for all competitors. Violations lead to immediate disqualification. +

+ +
+ {event.rules.map((rule, idx) => ( +
+
+
+ + Rule #{idx + 1} + + {rule.highlight && ( + + {rule.highlight} + + )} +
+

+ {rule.title} +

+

+ {rule.description} +

+
+ + {idx === 1 && ( +
+ Zero tolerance: AI tools disabled on all devices. +
+ )} +
+ ))} +
+
+ )} + + {/* Knockout Bracket Visualizer */} + {event.bracket && event.bracket.length > 0 && ( +
+
+
+
+ Championship Pathway +
+

+ Knockout Tournament Tree +

+
+
+ 8 Coders • Single Elimination +
+
+ + {/* Bracket Grid */} +
+ {/* Quarterfinals */} +
+
+ Quarterfinals (10m Sprint) +
+ {event.bracket.slice(0, 4).map((m) => ( +
+
{m.round}
+
+ {m.player1} + TBD +
+
+ {m.player2} + TBD +
+
+ ))} +
+ + {/* Semifinals */} +
+
+ Semifinals (15m Deep Dive) +
+ {event.bracket.slice(4, 6).map((m) => ( +
+
{m.round}
+
+ {m.player1} + TBD +
+
+ {m.player2} + TBD +
+
+ ))} +
+ + {/* Grand Final */} +
+
+ Grand Final (20m Duel) +
+ {event.bracket.slice(6, 7).map((m) => ( +
+
+ Championship Match +
+
+ {m.player1} + FINALIST 1 +
+
+ {m.player2} + FINALIST 2 +
+
+ Winner receives the JSK Code Wars Edition 2 Trophy +
+
+ ))} +
+
+
+ )} + + {/* Agenda Timeline */} + {event.agenda && event.agenda.length > 0 && ( +
+
+ +

+ Battle Schedule (2:00 PM – 5:00 PM) +

+
+ +
+ {event.agenda.map((item, idx) => ( +
+
+
+ {item.time} +
+
+ {item.title} +
+
+ {item.speaker && ( +
+ {item.speaker} +
+ )} +
+ ))} +
+
+ )} + + {/* Venue & Host Section */} +
+
+
+
+ Host & Venue Partner +
+

+ Africa's Talking Uganda Office +

+

+ Join us in-person at Africa's Talking Office. Experience the intense action with live high-definition code screens, live audio commentary, and direct networking with Uganda's top software engineers and architects. +

+
+
+ + {event.address} +
+
+ + In-person spectator seating limited to registered attendees. +
+
+
+ + {event.ticketsUrl && ( +
+
+ Admission +
+
+ Free RSVP +
+

+ Tickets managed through TicketDaddy. Secure your seat before room capacity is reached. +

+ +
+ )} +
+
+
+
+
+ ); +} + +export function generateStaticParams() { + return EVENTS.map((event) => ({ + slug: event.slug, + })); +} diff --git a/app/events/page.tsx b/app/events/page.tsx index b2f1841..17895f2 100644 --- a/app/events/page.tsx +++ b/app/events/page.tsx @@ -1,6 +1,7 @@ +import Link from 'next/link'; import { Section, Heading, Badge, Button } from '../../components/UI'; import { EVENTS } from '../../data'; -import { Calendar, MapPin, Ticket, Clock } from 'lucide-react'; +import { Calendar, MapPin, Ticket, Clock, Swords, ArrowRight } from 'lucide-react'; export default function Events() { const upcomingEvents = EVENTS.filter(e => e.status === 'upcoming'); @@ -19,7 +20,7 @@ export default function Events() { Events

- Join us for meetups, workshops, and conferences. Build skills, make connections, and grow your network. + Join us for meetups, workshops, and tournaments. Build skills, make connections, and test your code under pressure.

@@ -35,15 +36,22 @@ export default function Events() {
{event.coverImage && (
- {event.title} + + {event.title} +
)}
-
+
+ {event.format && ( + + Live 1v1 Tournament + + )} {event.tags.map(tag => ( {tag} @@ -51,13 +59,21 @@ export default function Events() { ))}

- {event.title} + + {event.title} +

{event.description}

-
+
- {new Date(event.date).toLocaleDateString()} + {new Date(event.date).toLocaleDateString('en-UG', { + weekday: 'short', + year: 'numeric', + month: 'short', + day: 'numeric', + timeZone: 'Africa/Kampala' + })}
@@ -68,11 +84,16 @@ export default function Events() { {event.venue}
- {event.ticketsUrl && ( - + )} + - )} +
@@ -102,7 +123,9 @@ export default function Events() { ))} -

{event.title}

+

+ {event.title} +

{new Date(event.date).toLocaleDateString()} • {event.venue}
diff --git a/app/page.tsx b/app/page.tsx index 9f21779..996629c 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -5,37 +5,26 @@ import Link from 'next/link'; import { ArrowRight, Calendar, Zap, Github, CheckCircle, Ticket, MapPin, Terminal, Activity, Users, Monitor, Play, - Star, GitFork + Star, GitFork, X } from 'lucide-react'; import { Button, Section, Card, Heading, Badge } from '../components/UI'; import { EVENTS, BLOG_POSTS, SPONSORS, PROJECTS } from '../data'; import SponsorsComponent from '@/components/sponsers'; const HeroVideo = () => { - const videoRef = useRef(null); const [isPlaying, setIsPlaying] = useState(false); - const togglePlayback = () => { - const video = videoRef.current; - if (!video) return; - if (video.paused) { - video.play().catch(() => {}); // handle autoplay block gracefully - } else { - video.pause(); - } - }; - return (
{/* Header bar – slightly more compact on mobile */}
Spotlight -
Community Reel
+
JSK Dev Quiz Night Recap
-
Runtime
-
02:34
+
Source
+
YouTube
@@ -43,44 +32,61 @@ const HeroVideo = () => {
- {/* Video wrapper with gradient overlay */} -
-