Files
KabundiNjuki ff743a1397 Initial commit
2026-10-05 16:40:36 +03:00

280 lines
12 KiB
TypeScript

import Link from 'next/link'
import { Clock, MapPin, Users, ChevronDown, Route, Droplets, Award, ShieldCheck } from 'lucide-react'
import { Navigation } from '@/components/navigation'
import { Footer } from '@/components/footer'
import { getPublicEvent } from '@/lib/lukenya-api'
import {
displayPrice,
formatClock,
formatCutoff,
formatMoney,
type PublicCategory,
} from '@/lib/event-types'
export const revalidate = 60
function startTimesAnswer(categories: PublicCategory[]): string {
const timed = categories.filter((c) => c.start_time)
if (!timed.length) return 'Start times are published with the race categories closer to race day.'
return timed.map((c) => `${c.category_name} (${c.distance_label}): ${formatClock(c.start_time)}`).join('. ') + '.'
}
function ageAnswer(categories: PublicCategory[]): string {
const parts = categories.map((c) => {
if (c.min_age) return `${c.category_name}: ${c.min_age}+`
if (c.eligibility) return `${c.category_name}: ${c.eligibility}`
return null
}).filter(Boolean)
if (!parts.length) return 'Age limits are listed with each race category. Runners under 18 need a parent or guardian to consent.'
return parts.join('. ') + '. Runners under 18 need a parent or guardian to consent.'
}
const routeHighlights = [
{ icon: Route, title: 'Course Type', description: 'Mixed terrain: tarmac and scenic trails' },
{ icon: Droplets, title: 'Water Stations', description: 'Every 2.5km with hydration and first aid' },
{ icon: Award, title: 'Timing', description: 'Sporthive certified chip timing' },
]
const staticFaqs = [
{
question: 'Is parking available?',
answer: 'Yes, free parking is available at the venue. Follow the signage to the designated parking areas. Arrive early for the best spots.'
},
{
question: 'Where do I collect my race pack?',
answer: 'Race packs can be collected at the Expo on the day before the race (10 AM - 6 PM) or on race morning from 4:30 AM at the registration tent.'
},
{
question: 'Is bag drop available?',
answer: 'Yes, secure bag drop is available near the start line. Please label your bag with your bib number. Bags can be collected after you finish.'
},
{
question: 'What is included in my entry fee?',
answer: 'Race bib with chip timing, finisher medal, event t-shirt, access to water stations and post-race refreshments. Inclusions for each race are listed on the registration page.'
},
{
question: 'Can I transfer my entry to someone else?',
answer: 'Entry transfers are allowed up to 7 days before the race for a small admin fee. Contact info@lukenyamarathon.co.ke to arrange a transfer.'
},
{
question: 'What happens if it rains?',
answer: 'The race proceeds rain or shine. Kenya\'s climate is generally favorable, but come prepared with appropriate gear.'
},
]
export default async function RaceInfoPage() {
const event = await getPublicEvent()
const categories = event.categories
const edition = event.edition
const featured = categories.find((c) => c.is_featured) || categories[0]
const featuredStart = featured ? formatClock(featured.start_time) : null
const routeItems = [
{ icon: MapPin, title: 'Start/Finish', description: edition?.venue || 'Venue to be announced' },
...routeHighlights,
]
const faqs = [
{ question: 'What time does the race start?', answer: startTimesAnswer(categories) },
staticFaqs[0],
staticFaqs[1],
staticFaqs[2],
{ question: 'Are there age requirements?', answer: ageAnswer(categories) },
...staticFaqs.slice(3),
]
return (
<div className="min-h-screen">
<Navigation />
{/* Page Hero */}
<section className="bg-[#006838] pt-20">
<div className="max-w-5xl mx-auto px-4 text-center py-16 md:py-20">
<span className="text-[#006838] bg-white/10 backdrop-blur font-semibold text-xs uppercase tracking-[0.12em] px-4 py-2 rounded-full inline-block mb-4">
Everything You Need to Know
</span>
<h1 className="text-3xl md:text-4xl lg:text-5xl font-extrabold text-white mb-4">
Race Information
</h1>
<p className="text-white/80 text-lg max-w-2xl mx-auto">
Categories, route details, timing, and everything you need for race day{edition?.venue ? ` at ${edition.venue}` : ''}.
</p>
</div>
</section>
{/* Quick Info Bar */}
<section className="bg-[#005030] py-6">
<div className="max-w-5xl mx-auto px-4">
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
<div className="text-center">
<Clock className="w-6 h-6 text-[#ffcd05] mx-auto mb-2" />
<p className="text-white font-bold">{featuredStart ? `${featuredStart} Start` : 'Start time TBA'}</p>
<p className="text-white/60 text-sm">{featured?.category_name || 'First race'}</p>
</div>
<div className="text-center">
<MapPin className="w-6 h-6 text-[#ffcd05] mx-auto mb-2" />
<p className="text-white font-bold">{edition?.venue || 'Venue TBA'}</p>
<p className="text-white/60 text-sm">{edition?.county || 'Kenya'}</p>
</div>
<div className="text-center">
<Users className="w-6 h-6 text-[#ffcd05] mx-auto mb-2" />
<p className="text-white font-bold">{categories.length || 'All'} Categories</p>
<p className="text-white/60 text-sm">All abilities</p>
</div>
<div className="text-center">
<ShieldCheck className="w-6 h-6 text-[#ffcd05] mx-auto mb-2" />
<p className="text-white font-bold">Sporthive Timing</p>
<p className="text-white/60 text-sm">Certified results</p>
</div>
</div>
</div>
</section>
{/* Race Categories */}
<section className="bg-white py-16 md:py-24">
<div className="max-w-6xl mx-auto px-4">
<div className="text-center mb-12">
<span className="text-[#006838] font-semibold text-xs uppercase tracking-[0.12em] mb-2 block">
Race Categories
</span>
<h2 className="text-3xl md:text-4xl font-bold text-[#006838]">
Choose Your Challenge
</h2>
</div>
<div className="overflow-x-auto">
<table className="w-full min-w-[600px]">
<thead>
<tr className="border-b-2 border-[#e5e7eb]">
<th className="text-left py-4 px-4 font-semibold text-[#111518]">Category</th>
<th className="text-left py-4 px-4 font-semibold text-[#111518]">Distance</th>
<th className="text-left py-4 px-4 font-semibold text-[#111518]">Cut-off Time</th>
<th className="text-left py-4 px-4 font-semibold text-[#111518]">Elevation</th>
<th className="text-left py-4 px-4 font-semibold text-[#111518]">Entry Fee (Kenyan resident)</th>
</tr>
</thead>
<tbody>
{categories.length === 0 && (
<tr>
<td colSpan={5} className="py-6 px-4 text-[#6b7280]">
Race categories for the next edition will be published here soon.
</td>
</tr>
)}
{categories.map((category) => (
<tr key={category.name} className="border-b border-[#e5e7eb] hover:bg-[#F5F7FC]/50">
<td className="py-4 px-4 font-semibold text-[#111518]">
{category.category_name}
{category.sold_out && <span className="ml-2 text-xs font-bold text-[#c0392b]">Sold out</span>}
</td>
<td className="py-4 px-4 text-[#006838] font-bold">
{category.distance_km ? `${category.distance_km}km` : category.distance_label}
</td>
<td className="py-4 px-4 text-[#6b7280]">{formatCutoff(category.cutoff_hours) || '—'}</td>
<td className="py-4 px-4 text-[#6b7280]">{category.elevation_gain_m ? `+${category.elevation_gain_m}m` : '—'}</td>
<td className="py-4 px-4 text-[#3bb34a] font-bold">
{formatMoney(displayPrice(category, 'resident'), event.currency) || 'Announced soon'}
</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="text-center mt-8">
<Link
href="/registration"
className="inline-block bg-[#3bb34a] text-white font-semibold text-sm px-8 py-3 rounded-lg hover:bg-[#2a9a3a] transition-colors duration-200"
>
REGISTER NOW
</Link>
</div>
</div>
</section>
{/* Route Information */}
<section className="bg-[#F5F7FC] py-16 md:py-24">
<div className="max-w-6xl mx-auto px-4">
<div className="text-center mb-12">
<span className="text-[#006838] font-semibold text-xs uppercase tracking-[0.12em] mb-2 block">
The Course
</span>
<h2 className="text-3xl md:text-4xl font-bold text-[#006838]">
Route Information
</h2>
</div>
{/* Route Map Placeholder */}
<div className="bg-white rounded-xl shadow-[0_2px_16px_rgba(0,0,0,0.08)] p-8 mb-12">
<div className="aspect-video bg-[#e5e7eb] rounded-lg flex items-center justify-center">
<div className="text-center">
<Route className="w-12 h-12 text-[#6b7280] mx-auto mb-2" />
<p className="text-[#6b7280]">Course Map Coming Soon</p>
</div>
</div>
</div>
{/* Route Highlights */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{routeItems.map((item, index) => (
<div key={index} className="bg-white rounded-xl p-6 shadow-[0_2px_16px_rgba(0,0,0,0.08)]">
<item.icon className="w-8 h-8 text-[#006838] mb-4" />
<h3 className="font-bold text-[#111518] mb-2">{item.title}</h3>
<p className="text-[#6b7280] text-sm">{item.description}</p>
</div>
))}
</div>
</div>
</section>
{/* FAQ Section */}
<section className="bg-white py-16 md:py-24">
<div className="max-w-3xl mx-auto px-4">
<div className="text-center mb-12">
<span className="text-[#006838] font-semibold text-xs uppercase tracking-[0.12em] mb-2 block">
Common Questions
</span>
<h2 className="text-3xl md:text-4xl font-bold text-[#006838]">
Frequently Asked Questions
</h2>
</div>
<div className="space-y-4">
{faqs.map((faq, index) => (
<details
key={index}
className="group bg-[#F5F7FC] rounded-xl overflow-hidden"
>
<summary className="flex items-center justify-between cursor-pointer p-6 font-semibold text-[#111518]">
{faq.question}
<ChevronDown className="w-5 h-5 text-[#6b7280] group-open:rotate-180 transition-transform" />
</summary>
<div className="px-6 pb-6 text-[#6b7280]">
{faq.answer}
</div>
</details>
))}
</div>
</div>
</section>
{/* CTA Section */}
<section className="bg-[#006838] py-16">
<div className="max-w-3xl mx-auto px-4 text-center">
<h2 className="text-3xl md:text-4xl font-extrabold text-white mb-4">
Ready to Run?
</h2>
<p className="text-white/80 text-lg mb-8">
Secure your place in Kenya&apos;s only environmental marathon.
</p>
<Link
href="/registration"
className="inline-block bg-[#ffcd05] text-[#111518] font-bold text-sm px-10 py-4 rounded-lg hover:bg-[#c4930f] transition-colors duration-200"
>
REGISTER NOW
</Link>
</div>
</section>
<Footer />
</div>
)
}