feat: team pages with match/tournament history, mobile padding fixes, linked scorers and nations
- Team page: add Tournament Participations (year pills → /tournaments/[year]) and Match History (grouped by year, W/D/L badge, opponent, score from team's perspective, PSO/AET annotations, each row → match anchor) - GraphQL: extend matches() query with teamId filter (OR team1_id/team2_id) - Match card: link team names to /teams/[slug]; fix ET score display — show scoreEt as headline for AET matches, scoreFt as footnote; winner determination uses scoreP ?? scoreEt ?? scoreFt - Tournament page: scorer names below each match linked to /players/[name] with dotted underline (solid + green on hover) - Stats page: reduce mobile padding on Goals chart, Top Scorers, Titles, Goals by Minute — hide progress bars and trophy emojis on small screens - Homepage: Golden Boot Race same mobile padding/bar treatment; add slug to match team queries Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+25
-23
@@ -94,14 +94,14 @@ export default function StatsPage() {
|
||||
<div className="mb-12">
|
||||
<SectionTitle>⚽ Goals Scored per Tournament</SectionTitle>
|
||||
<Card>
|
||||
<div className="p-7 pb-0">
|
||||
<div className="flex items-end gap-[3px] h-[170px]">
|
||||
<div className="px-3 pt-4 pb-0 sm:px-7 sm:pt-7">
|
||||
<div className="flex items-end gap-[2px] sm:gap-[3px] h-[170px]">
|
||||
{tournaments.map(t => {
|
||||
const h = Math.max(4, Math.round(((t.totalGoals ?? 0) / maxGoals) * 140))
|
||||
const avg = t.avgGoalsPerGame ? Number(t.avgGoalsPerGame).toFixed(1) : null
|
||||
return (
|
||||
<Link key={t.year} href={`/tournaments/${t.year}`} className="flex flex-col items-center flex-1 min-w-[16px] group">
|
||||
<div className="text-[7px] text-[#2a5c35] font-semibold mb-1 leading-none group-hover:text-[#22c55e]">{t.totalGoals}</div>
|
||||
<Link key={t.year} href={`/tournaments/${t.year}`} className="flex flex-col items-center flex-1 min-w-[8px] group">
|
||||
<div className="text-[6px] sm:text-[7px] text-[#2a5c35] font-semibold mb-1 leading-none group-hover:text-[#22c55e]">{t.totalGoals}</div>
|
||||
<div className="w-full rounded-t-sm border-t-2 transition-colors group-hover:bg-[rgba(34,197,94,0.35)]"
|
||||
style={{ height: `${h}px`, background: 'rgba(34,197,94,0.18)', borderColor: 'rgba(34,197,94,0.45)' }}
|
||||
title={`${t.year}: ${t.totalGoals} goals${avg ? ` · ${avg}/game` : ''}`}
|
||||
@@ -110,7 +110,7 @@ export default function StatsPage() {
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div className="flex gap-[3px] pt-1.5 pb-3.5 border-t mt-0" style={{ borderColor: 'rgba(34,197,94,0.06)' }}>
|
||||
<div className="flex gap-[2px] sm:gap-[3px] pt-1.5 pb-3 border-t" style={{ borderColor: 'rgba(34,197,94,0.06)' }}>
|
||||
{tournaments.map(t => (
|
||||
<div key={t.year} className="flex-1 text-center text-[6px] text-[#1a3a22]" style={{ transform: 'rotate(-45deg)', transformOrigin: 'center top' }}>
|
||||
{t.year}
|
||||
@@ -129,15 +129,15 @@ export default function StatsPage() {
|
||||
<Card>
|
||||
{scorers.map((s, i) => (
|
||||
<Link key={s.playerName} href={`/players/${encodeURIComponent(s.playerName)}`}>
|
||||
<div className="flex items-center gap-3 px-4 py-3 border-b hover:bg-[rgba(34,197,94,0.03)] cursor-pointer"
|
||||
<div className="flex items-center gap-2 sm:gap-3 px-3 sm:px-4 py-3 border-b hover:bg-[rgba(34,197,94,0.03)] cursor-pointer"
|
||||
style={{ borderColor: 'rgba(34,197,94,0.05)', background: i === 0 ? 'rgba(34,197,94,0.04)' : undefined }}>
|
||||
<span className="text-[11px] text-[#2a5c35] w-5 text-right font-bold flex-shrink-0">{i + 1}</span>
|
||||
{s.team && <TeamFlag name={s.team.name} iso2={s.team.iso2} size="sm" />}
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className={`text-sm font-semibold truncate ${i < 3 ? 'text-[#dff5e8]' : 'text-[#6abf7a]'}`}>{s.playerName}</div>
|
||||
<div className="text-[10px] text-[#2a5c35]">{s.team?.name} · {s.tournaments} WC{s.tournaments !== 1 ? 's' : ''}{s.penalties > 0 ? ` · ${s.penalties}P` : ''}</div>
|
||||
<div className="text-[10px] text-[#2a5c35] truncate">{s.team?.name} · {s.tournaments} WC{s.tournaments !== 1 ? 's' : ''}{s.penalties > 0 ? ` · ${s.penalties}P` : ''}</div>
|
||||
</div>
|
||||
<div className="w-16 h-1 rounded-full flex-shrink-0" style={{ background: 'rgba(34,197,94,0.1)' }}>
|
||||
<div className="hidden sm:block w-16 h-1 rounded-full flex-shrink-0" style={{ background: 'rgba(34,197,94,0.1)' }}>
|
||||
<div className="h-full rounded-full bg-[#22c55e]" style={{ width: `${(s.goals / maxScorer) * 100}%` }} />
|
||||
</div>
|
||||
<span className="font-['Bebas_Neue'] text-[22px] text-[#22c55e] min-w-[28px] text-right flex-shrink-0">{s.goals}</span>
|
||||
@@ -153,12 +153,12 @@ export default function StatsPage() {
|
||||
<Card>
|
||||
{titlesByNation.map((t, i) => (
|
||||
<Link key={t.name} href={`/teams/${t.slug}`}>
|
||||
<div className="flex items-center gap-3 px-4 py-3.5 border-b hover:bg-[rgba(34,197,94,0.03)] cursor-pointer"
|
||||
<div className="flex items-center gap-2 sm:gap-3 px-3 sm:px-4 py-3.5 border-b hover:bg-[rgba(34,197,94,0.03)] cursor-pointer"
|
||||
style={{ borderColor: 'rgba(34,197,94,0.05)' }}>
|
||||
<span className="text-[11px] text-[#2a5c35] w-5 text-right font-bold flex-shrink-0">{i + 1}</span>
|
||||
<TeamFlag name={t.name} iso2={t.iso2} size="sm" />
|
||||
<div className="flex-1 text-sm font-semibold text-[#dff5e8]">{t.name}</div>
|
||||
<div className="flex gap-0.5 flex-shrink-0">
|
||||
<div className="flex-1 min-w-0 text-sm font-semibold text-[#dff5e8] truncate">{t.name}</div>
|
||||
<div className="hidden sm:flex gap-0.5 flex-shrink-0">
|
||||
{Array.from({ length: t.stats?.titles ?? 0 }).map((_, j) => (
|
||||
<span key={j} className="text-sm">🏆</span>
|
||||
))}
|
||||
@@ -175,18 +175,20 @@ export default function StatsPage() {
|
||||
{minuteBuckets.length > 0 && (
|
||||
<div className="mb-12">
|
||||
<SectionTitle>⏱ Goals by Minute (All-Time)</SectionTitle>
|
||||
<Card className="p-6">
|
||||
<div className="flex items-end gap-3 h-24">
|
||||
{minuteBuckets.map(b => {
|
||||
const h = Math.max(8, Math.round((b.count / maxMinute) * 80))
|
||||
return (
|
||||
<div key={b.bucket} className="flex-1 flex flex-col items-center gap-1.5">
|
||||
<span className="text-[9px] text-[#2a5c35] font-bold">{b.count}</span>
|
||||
<div className="w-full rounded-t" style={{ height: `${h}px`, background: 'rgba(34,197,94,0.3)', border: '1px solid rgba(34,197,94,0.5)' }} />
|
||||
<span className="text-[9px] text-[#1a3a22]">{b.bucket}</span>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
<Card>
|
||||
<div className="px-3 py-4 sm:p-6">
|
||||
<div className="flex items-end gap-1 sm:gap-3 h-24">
|
||||
{minuteBuckets.map(b => {
|
||||
const h = Math.max(8, Math.round((b.count / maxMinute) * 80))
|
||||
return (
|
||||
<div key={b.bucket} className="flex-1 flex flex-col items-center gap-1">
|
||||
<span className="text-[7px] sm:text-[9px] text-[#2a5c35] font-bold leading-none">{b.count}</span>
|
||||
<div className="w-full rounded-t" style={{ height: `${h}px`, background: 'rgba(34,197,94,0.3)', border: '1px solid rgba(34,197,94,0.5)' }} />
|
||||
<span className="text-[7px] sm:text-[9px] text-[#1a3a22] leading-none">{b.bucket}</span>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user