feat: replace emoji icons with Heroicons SVG set
Install @heroicons/react and replace all emoji usage across stats, history, search, and team pages with proper SVG icons (outline style, w-3 to w-4). SectionTitle in stats page refactored to accept an icon component prop. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import { useQuery, gql } from '@/lib/graphql/hooks'
|
||||
import { useEffect } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { TeamFlag } from '@/components/team-flag'
|
||||
import { FireIcon, CalendarDaysIcon, TrophyIcon } from '@heroicons/react/24/outline'
|
||||
|
||||
const HISTORY_QUERY = gql`
|
||||
query History {
|
||||
@@ -92,14 +93,14 @@ export default function HistoryPage() {
|
||||
)}
|
||||
|
||||
<div className="flex gap-3.5 text-[11px] text-[#2a5c35] flex-wrap">
|
||||
{t.totalGoals != null && <span>⚽ {t.totalGoals}</span>}
|
||||
{t.matchesCount != null && <span>🗓 {t.matchesCount} games</span>}
|
||||
{t.totalGoals != null && <span className="inline-flex items-center gap-1"><FireIcon className="w-3 h-3" />{t.totalGoals}</span>}
|
||||
{t.matchesCount != null && <span className="inline-flex items-center gap-1"><CalendarDaysIcon className="w-3 h-3" />{t.matchesCount} games</span>}
|
||||
{t.teamsCount != null && <span>🏳 {t.teamsCount} teams</span>}
|
||||
</div>
|
||||
|
||||
{topScorer && (
|
||||
<div className="mt-2 text-[10px] text-[#1a3a22]">
|
||||
Golden Boot: <span className="text-[#2a5c35]">{topScorer.playerName} ({topScorer.goals}⚽)</span>
|
||||
Golden Boot: <span className="text-[#2a5c35]">{topScorer.playerName} (<span className="inline-flex items-center gap-0.5"><FireIcon className="w-2.5 h-2.5 inline" />{topScorer.goals}</span>)</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
+5
-4
@@ -4,6 +4,7 @@ import { useSearchParams, useRouter } from 'next/navigation'
|
||||
import { useState, useEffect, Suspense } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { TeamFlag } from '@/components/team-flag'
|
||||
import { TrophyIcon, FireIcon } from '@heroicons/react/24/outline'
|
||||
|
||||
const SEARCH_QUERY = gql`
|
||||
query Search($q: String!) {
|
||||
@@ -111,7 +112,7 @@ function SearchContent() {
|
||||
<div>
|
||||
<div className="text-sm font-semibold text-[#dff5e8]">{t.name}</div>
|
||||
<div className="text-[10px] text-[#2a5c35]">
|
||||
{t.stats?.appearances ?? 0} WCs{t.stats?.titles ? ` · ${t.stats.titles} 🏆` : ''}
|
||||
{t.stats?.appearances ?? 0} WCs{t.stats?.titles ? <span className="inline-flex items-center gap-0.5 ml-1">· {t.stats.titles}<TrophyIcon className="w-3 h-3 inline" /></span> : ''}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -135,7 +136,7 @@ function SearchContent() {
|
||||
<div className="text-sm font-semibold text-[#dff5e8] truncate">{p.playerName}</div>
|
||||
<div className="text-[10px] text-[#2a5c35]">{p.team?.name} · {p.tournaments} WC{p.tournaments !== 1 ? 's' : ''}</div>
|
||||
</div>
|
||||
<span className="font-['Bebas_Neue'] text-xl text-[#22c55e] flex-shrink-0">{p.goals}⚽</span>
|
||||
<span className="font-['Bebas_Neue'] text-xl text-[#22c55e] flex-shrink-0 inline-flex items-center gap-0.5">{p.goals}<FireIcon className="w-3.5 h-3.5" /></span>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
@@ -154,8 +155,8 @@ function SearchContent() {
|
||||
style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.12)' }}>
|
||||
<div className="font-['Bebas_Neue'] text-3xl text-[#22c55e]">{t.year}</div>
|
||||
<div className="text-sm text-[#dff5e8]">{t.host}</div>
|
||||
{t.winner && <div className="text-[10px] text-[#2a5c35] mt-1">🏆 {t.winner}</div>}
|
||||
{t.totalGoals && <div className="text-[10px] text-[#1a3a22]">⚽ {t.totalGoals} goals</div>}
|
||||
{t.winner && <div className="text-[10px] text-[#2a5c35] mt-1 flex items-center gap-1"><TrophyIcon className="w-3 h-3 flex-shrink-0" />{t.winner}</div>}
|
||||
{t.totalGoals && <div className="text-[10px] text-[#1a3a22] flex items-center gap-1"><FireIcon className="w-3 h-3 flex-shrink-0" />{t.totalGoals} goals</div>}
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
|
||||
+22
-13
@@ -3,6 +3,10 @@ import { useQuery, gql } from '@/lib/graphql/hooks'
|
||||
import { useEffect } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { TeamFlag } from '@/components/team-flag'
|
||||
import {
|
||||
ChartBarIcon, StarIcon, TrophyIcon, ClockIcon, BoltIcon,
|
||||
FireIcon, SparklesIcon, ArrowPathIcon, GlobeEuropeAfricaIcon, TableCellsIcon,
|
||||
} from '@heroicons/react/24/outline'
|
||||
|
||||
const STATS_QUERY = gql`
|
||||
query Stats {
|
||||
@@ -36,8 +40,13 @@ const STATS_QUERY = gql`
|
||||
}
|
||||
`
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return <h2 className="text-[11px] font-bold tracking-[0.14em] uppercase text-[#2a5c35] mb-4">{children}</h2>
|
||||
function SectionTitle({ children, icon: Icon }: { children: React.ReactNode; icon: React.ComponentType<{ className?: string }> }) {
|
||||
return (
|
||||
<h2 className="flex items-center gap-1.5 text-[11px] font-bold tracking-[0.14em] uppercase text-[#2a5c35] mb-4">
|
||||
<Icon className="w-3.5 h-3.5 flex-shrink-0" />
|
||||
{children}
|
||||
</h2>
|
||||
)
|
||||
}
|
||||
|
||||
function Card({ children, className = '' }: { children: React.ReactNode; className?: string }) {
|
||||
@@ -92,7 +101,7 @@ export default function StatsPage() {
|
||||
{/* ── Goals per tournament bar chart ── */}
|
||||
{tournaments.length > 0 && (
|
||||
<div className="mb-12">
|
||||
<SectionTitle>⚽ Goals Scored per Tournament</SectionTitle>
|
||||
<SectionTitle icon={ChartBarIcon}>Goals Scored per Tournament</SectionTitle>
|
||||
<Card>
|
||||
<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]">
|
||||
@@ -125,7 +134,7 @@ export default function StatsPage() {
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-12">
|
||||
{/* ── All-time top scorers ── */}
|
||||
<div>
|
||||
<SectionTitle>🏅 All-Time Top Scorers</SectionTitle>
|
||||
<SectionTitle icon={StarIcon}>All-Time Top Scorers</SectionTitle>
|
||||
<Card>
|
||||
{scorers.map((s, i) => (
|
||||
<Link key={s.playerName} href={`/players/${encodeURIComponent(s.playerName)}`}>
|
||||
@@ -149,7 +158,7 @@ export default function StatsPage() {
|
||||
|
||||
{/* ── World Cup titles ── */}
|
||||
<div>
|
||||
<SectionTitle>🏆 World Cup Titles by Nation</SectionTitle>
|
||||
<SectionTitle icon={TrophyIcon}>World Cup Titles by Nation</SectionTitle>
|
||||
<Card>
|
||||
{titlesByNation.map((t, i) => (
|
||||
<Link key={t.name} href={`/teams/${t.slug}`}>
|
||||
@@ -160,7 +169,7 @@ export default function StatsPage() {
|
||||
<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>
|
||||
<TrophyIcon key={j} className="w-4 h-4 text-[#22c55e]" />
|
||||
))}
|
||||
</div>
|
||||
<span className="font-['Bebas_Neue'] text-[28px] text-[#22c55e] flex-shrink-0">{t.stats?.titles}</span>
|
||||
@@ -174,7 +183,7 @@ export default function StatsPage() {
|
||||
{/* ── Goals by minute heatmap ── */}
|
||||
{minuteBuckets.length > 0 && (
|
||||
<div className="mb-12">
|
||||
<SectionTitle>⏱ Goals by Minute (All-Time)</SectionTitle>
|
||||
<SectionTitle icon={ClockIcon}>Goals by Minute (All-Time)</SectionTitle>
|
||||
<Card>
|
||||
<div className="px-3 py-4 sm:p-6">
|
||||
<div className="flex items-end gap-1 sm:gap-3 h-24">
|
||||
@@ -197,7 +206,7 @@ export default function StatsPage() {
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-12">
|
||||
{/* ── Biggest wins ── */}
|
||||
<div>
|
||||
<SectionTitle>💥 Biggest Victories</SectionTitle>
|
||||
<SectionTitle icon={BoltIcon}>Biggest Victories</SectionTitle>
|
||||
<Card>
|
||||
{biggestWins.map(m => (
|
||||
<Link key={m.id} href={`/tournaments/${m.year}#match-${m.id}`}>
|
||||
@@ -220,7 +229,7 @@ export default function StatsPage() {
|
||||
|
||||
{/* ── Highest scoring matches ── */}
|
||||
<div>
|
||||
<SectionTitle>🔥 Highest Scoring Matches</SectionTitle>
|
||||
<SectionTitle icon={FireIcon}>Highest Scoring Matches</SectionTitle>
|
||||
<Card>
|
||||
{highScoring.map(m => (
|
||||
<Link key={m.id} href={`/tournaments/${m.year}#match-${m.id}`}>
|
||||
@@ -245,7 +254,7 @@ export default function StatsPage() {
|
||||
{/* ── Hat-tricks ── */}
|
||||
{hatTricks.length > 0 && (
|
||||
<div className="mb-12">
|
||||
<SectionTitle>🎩 Hat-Tricks</SectionTitle>
|
||||
<SectionTitle icon={SparklesIcon}>Hat-Tricks</SectionTitle>
|
||||
<div className="grid grid-cols-[repeat(auto-fill,minmax(240px,1fr))] gap-3">
|
||||
{hatTricks.map((h, i) => (
|
||||
<div key={i} className="rounded-xl p-4" style={{ background: '#0a1810', border: '1px solid rgba(34,197,94,0.12)' }}>
|
||||
@@ -270,7 +279,7 @@ export default function StatsPage() {
|
||||
{/* ── ET & Penalty stats ── */}
|
||||
{etStats && (
|
||||
<div className="mb-12">
|
||||
<SectionTitle>⚡ Extra Time & Penalty Shootouts</SectionTitle>
|
||||
<SectionTitle icon={ArrowPathIcon}>Extra Time & Penalty Shootouts</SectionTitle>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
|
||||
{[
|
||||
{ label: 'Knockout Matches', value: etStats.totalKnockoutMatches },
|
||||
@@ -290,7 +299,7 @@ export default function StatsPage() {
|
||||
{/* ── Confederation stats ── */}
|
||||
{confStats.length > 0 && (
|
||||
<div className="mb-12">
|
||||
<SectionTitle>🌍 Performance by Confederation</SectionTitle>
|
||||
<SectionTitle icon={GlobeEuropeAfricaIcon}>Performance by Confederation</SectionTitle>
|
||||
<Card>
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
@@ -319,7 +328,7 @@ export default function StatsPage() {
|
||||
{/* ── All-time team table ── */}
|
||||
{teams.length > 0 && (
|
||||
<div>
|
||||
<SectionTitle>📊 All-Time Team Table</SectionTitle>
|
||||
<SectionTitle icon={TableCellsIcon}>All-Time Team Table</SectionTitle>
|
||||
<Card>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full" style={{ minWidth: '560px' }}>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useQuery, gql } from '@/lib/graphql/hooks'
|
||||
import { use, useEffect } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { TeamFlag } from '@/components/team-flag'
|
||||
import { TrophyIcon } from '@heroicons/react/24/outline'
|
||||
|
||||
const TEAM_QUERY = gql`
|
||||
query Team($slug: String!) {
|
||||
@@ -103,8 +104,9 @@ export default function TeamPage({ params }: { params: Promise<{ slug: string }>
|
||||
{team.confederation && <span className="text-[11px] text-[#2a5c35]">{team.confederation}</span>}
|
||||
{team.continent && <span className="text-[11px] text-[#2a5c35]">{team.continent}</span>}
|
||||
{(s?.titles ?? 0) > 0 && (
|
||||
<span className="text-[11px] text-[#22c55e] font-bold">
|
||||
{Array.from({ length: s?.titles ?? 0 }).map(() => '🏆').join('')} {s?.titles} title{(s?.titles ?? 0) !== 1 ? 's' : ''}
|
||||
<span className="inline-flex items-center gap-1 text-[11px] text-[#22c55e] font-bold">
|
||||
{Array.from({ length: s?.titles ?? 0 }).map((_, i) => <TrophyIcon key={i} className="w-3.5 h-3.5" />)}
|
||||
{s?.titles} title{(s?.titles ?? 0) !== 1 ? 's' : ''}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Generated
+8097
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@
|
||||
"dependencies": {
|
||||
"@apollo/client": "^4.2.3",
|
||||
"@graphql-tools/schema": "^10.0.33",
|
||||
"@heroicons/react": "^2.2.0",
|
||||
"drizzle-orm": "^0.45.2",
|
||||
"flag-icons": "^7.5.0",
|
||||
"graphql": "^16.14.2",
|
||||
|
||||
Reference in New Issue
Block a user