259 lines
11 KiB
TypeScript
259 lines
11 KiB
TypeScript
import Link from 'next/link'
|
|
import { Trophy, ShieldCheck, Accessibility, Users, Award } from 'lucide-react'
|
|
import { Navigation } from '@/components/navigation'
|
|
import { Footer } from '@/components/footer'
|
|
import { getPublicEvent, getPublicPrizes } from '@/lib/lukenya-api'
|
|
import { findPrize, formatMoney, ordinal, type PrizeCategory, type PrizeRow } from '@/lib/event-types'
|
|
|
|
export const revalidate = 60
|
|
|
|
type PrizeLine = { place: string; amount: string; highlight?: boolean }
|
|
|
|
const PLACE = (n: number) => ordinal(n) || String(n)
|
|
|
|
// Men and women side by side, one row per finishing position.
|
|
function rowsFor(category: PrizeCategory, currency: string): { men: PrizeLine[]; women: PrizeLine[] } {
|
|
const men = category.divisions.find((d) => d.division === 'Men')?.places || []
|
|
const women = category.divisions.find((d) => d.division === 'Women')?.places || []
|
|
const positions = Array.from(new Set([...men, ...women].map((p) => p.position))).sort((x, y) => x - y)
|
|
const fmt = (rows: PrizeRow[], pos: number) => {
|
|
const row = rows.find((r) => r.position === pos)
|
|
return row ? (formatMoney(row.amount, currency)?.replace(`${currency} `, '') || '—') : '—'
|
|
}
|
|
return {
|
|
men: positions.map((pos) => ({ place: PLACE(pos), amount: fmt(men, pos), highlight: pos === 1 })),
|
|
women: positions.map((pos) => ({ place: PLACE(pos), amount: fmt(women, pos), highlight: pos === 1 })),
|
|
}
|
|
}
|
|
|
|
const SPECIAL = [
|
|
{ division: 'Wheelchair', icon: Accessibility, title: 'Wheelchair Category', description: 'Top finishers in the wheelchair division' },
|
|
{ division: 'Corporate Relay', icon: Users, title: 'Corporate Relay', description: 'Fastest corporate relay teams' },
|
|
{ division: 'Age Group', icon: Award, title: 'Age Category Awards', description: 'Top finishers in each age group' },
|
|
]
|
|
|
|
function PrizeTable({
|
|
title,
|
|
men,
|
|
women
|
|
}: {
|
|
title: string
|
|
men: { place: string; amount: string; highlight?: boolean }[]
|
|
women: { place: string; amount: string; highlight?: boolean }[]
|
|
}) {
|
|
return (
|
|
<div className="bg-white rounded-xl shadow-[0_2px_16px_rgba(0,0,0,0.08)] overflow-hidden">
|
|
<div className="bg-[#ffcd05]/10 border-b border-[#ffcd05]/20 px-6 py-4">
|
|
<h3 className="text-xl font-bold text-[#006838]">{title}</h3>
|
|
</div>
|
|
<div className="p-6">
|
|
<table className="w-full">
|
|
<thead>
|
|
<tr className="border-b border-[#e5e7eb]">
|
|
<th className="text-left py-3 font-semibold text-[#111518]">Place</th>
|
|
<th className="text-left py-3 font-semibold text-[#111518]">Men</th>
|
|
<th className="text-left py-3 font-semibold text-[#111518]">Women</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{men.map((prize, index) => (
|
|
<tr
|
|
key={index}
|
|
className={`border-b border-[#e5e7eb] ${
|
|
prize.highlight ? 'bg-[#ffcd05]/5' : ''
|
|
}`}
|
|
>
|
|
<td className={`py-3 font-semibold ${prize.highlight ? 'text-[#ffcd05]' : 'text-[#111518]'}`}>
|
|
{prize.place}
|
|
</td>
|
|
<td className={`py-3 font-extrabold text-lg ${
|
|
prize.highlight ? 'text-[#ffcd05]' : index < 3 ? 'text-[#3bb34a]' : 'text-[#111518]'
|
|
}`}>
|
|
{prize.amount}
|
|
</td>
|
|
<td className={`py-3 font-extrabold text-lg ${
|
|
women[index]?.highlight ? 'text-[#ffcd05]' : index < 3 ? 'text-[#3bb34a]' : 'text-[#111518]'
|
|
}`}>
|
|
{women[index]?.amount}
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function CompactPrizeTable({
|
|
title,
|
|
men,
|
|
women
|
|
}: {
|
|
title: string
|
|
men: { place: string; amount: string }[]
|
|
women: { place: string; amount: string }[]
|
|
}) {
|
|
return (
|
|
<div className="bg-white rounded-xl shadow-[0_2px_16px_rgba(0,0,0,0.08)] overflow-hidden">
|
|
<div className="bg-[#F5F7FC] border-b border-[#e5e7eb] px-6 py-3">
|
|
<h3 className="text-lg font-bold text-[#006838]">{title}</h3>
|
|
</div>
|
|
<div className="p-4">
|
|
<table className="w-full text-sm">
|
|
<thead>
|
|
<tr className="border-b border-[#e5e7eb]">
|
|
<th className="text-left py-2 font-semibold text-[#111518]">Place</th>
|
|
<th className="text-left py-2 font-semibold text-[#111518]">Men</th>
|
|
<th className="text-left py-2 font-semibold text-[#111518]">Women</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{men.map((prize, index) => (
|
|
<tr key={index} className="border-b border-[#e5e7eb] last:border-0">
|
|
<td className="py-2 font-semibold text-[#111518]">{prize.place}</td>
|
|
<td className="py-2 font-bold text-[#3bb34a]">{prize.amount}</td>
|
|
<td className="py-2 font-bold text-[#3bb34a]">{women[index]?.amount}</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default async function PrizeMoneyPage() {
|
|
const [event, prizes] = await Promise.all([getPublicEvent(), getPublicPrizes()])
|
|
const currency = prizes?.currency || event.currency
|
|
// Races with Men/Women prize rules, in the order the backend sends them (featured first).
|
|
const raceTables = (prizes?.categories || []).filter((c) =>
|
|
c.divisions.some((d) => d.division === 'Men' || d.division === 'Women'),
|
|
)
|
|
const [mainRace, ...otherRaces] = raceTables
|
|
const specials = SPECIAL.map((s) => ({ ...s, amount: findPrize(prizes, s.division, 1) }))
|
|
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-[#ffcd05] font-semibold text-xs uppercase tracking-[0.12em] mb-4 block">
|
|
Compete for Real Rewards
|
|
</span>
|
|
<h1 className="text-3xl md:text-4xl lg:text-5xl font-extrabold text-white mb-4">
|
|
Prize Money
|
|
</h1>
|
|
<p className="text-white/80 text-lg max-w-2xl mx-auto">
|
|
{raceTables.length ? `Structured prize money across ${raceTables.length} ${raceTables.length === 1 ? 'race' : 'races'}, paid on race day.` : 'Structured prize money, paid on race day.'}
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Main race */}
|
|
<section className="bg-white py-16 md:py-24">
|
|
<div className="max-w-4xl mx-auto px-4">
|
|
<div className="text-center mb-12">
|
|
<Trophy className="w-12 h-12 text-[#ffcd05] mx-auto mb-4" />
|
|
<span className="text-[#006838] font-semibold text-xs uppercase tracking-[0.12em] mb-2 block">
|
|
Top Prize Category
|
|
</span>
|
|
<h2 className="text-3xl md:text-4xl font-bold text-[#006838]">
|
|
{mainRace ? `${mainRace.category_name}, ${mainRace.distance_label}` : 'Prize structure'}
|
|
</h2>
|
|
</div>
|
|
|
|
{mainRace ? (
|
|
<PrizeTable title={`${mainRace.category_name} prize structure (${currency})`} {...rowsFor(mainRace, currency)} />
|
|
) : (
|
|
<p className="text-center text-[#6b7280] text-lg">
|
|
Prize amounts for each race are published here before entries open.
|
|
</p>
|
|
)}
|
|
</div>
|
|
</section>
|
|
|
|
{otherRaces.length > 0 && (
|
|
<section className="bg-[#F5F7FC] py-16 md:py-24">
|
|
<div className="max-w-5xl mx-auto px-4">
|
|
<div className="text-center mb-12">
|
|
<h2 className="text-2xl md:text-3xl font-bold text-[#006838]">Other races</h2>
|
|
</div>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
{otherRaces.map((race) => (
|
|
<CompactPrizeTable
|
|
key={race.race_category}
|
|
title={`${race.category_name} (${race.distance_label})`}
|
|
{...rowsFor(race, currency)}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
)}
|
|
|
|
{/* Special Categories */}
|
|
<section className="bg-[#006838] py-16 md:py-24">
|
|
<div className="max-w-5xl mx-auto px-4">
|
|
<div className="text-center mb-12">
|
|
<span className="text-[#ffcd05] font-semibold text-xs uppercase tracking-[0.12em] mb-2 block">
|
|
Special Categories
|
|
</span>
|
|
<h2 className="text-3xl md:text-4xl font-bold text-white">
|
|
Additional Prize Categories
|
|
</h2>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
{specials.map((category, index) => (
|
|
<div key={index} className="bg-white rounded-xl p-6 text-center">
|
|
<category.icon className="w-10 h-10 text-[#006838] mx-auto mb-4" />
|
|
<h3 className="font-bold text-[#111518] mb-2">{category.title}</h3>
|
|
<p className="text-[#ffcd05] font-extrabold text-2xl mb-2">{formatMoney(category.amount, currency) || 'To be announced'}</p>
|
|
<p className="text-[#6b7280] text-sm">{category.description}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Prize Payment Info */}
|
|
<section className="bg-[#F5F7FC] py-12">
|
|
<div className="max-w-4xl mx-auto px-4">
|
|
<div className="flex flex-col md:flex-row items-center justify-center gap-4 text-center md:text-left">
|
|
<div className="flex items-center gap-2">
|
|
<ShieldCheck className="w-5 h-5 text-[#006838]" />
|
|
<span className="text-[#111518] font-semibold text-sm">Sporthive Certified Results</span>
|
|
</div>
|
|
<span className="hidden md:block text-[#e5e7eb]">|</span>
|
|
<p className="text-[#6b7280] text-sm">
|
|
Prize money paid on race day. Winners must present valid ID. All prizes subject to race rules.
|
|
</p>
|
|
</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 Compete?
|
|
</h2>
|
|
<p className="text-white/80 text-lg mb-8">
|
|
Register now and compete for prize money at Kenya's 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>
|
|
)
|
|
}
|