280 lines
12 KiB
TypeScript
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'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>
|
|
)
|
|
}
|