feat: replace text loading messages with PageSpinner component

Add a shared PageSpinner (spinning ring + optional label) and use it
in all pages that previously showed a plain text loading message:
home, stats, player, team, and tournament pages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 09:54:39 +02:00
parent 0d415baf95
commit c96c683580
6 changed files with 18 additions and 14 deletions
+2 -3
View File
@@ -3,6 +3,7 @@ import { useQuery, gql } from '@/lib/graphql/hooks'
import Link from 'next/link'
import { TeamFlag } from '@/components/team-flag'
import { LiveBadge } from '@/components/live-badge'
import { PageSpinner } from '@/components/page-spinner'
import { MatchCard } from '@/components/match-card'
const HOME_QUERY = gql`
@@ -231,9 +232,7 @@ export function HomeClient() {
</div>
)}
{loading && !data && (
<div className="py-16 text-center text-green-muted text-sm">Loading live World Cup data</div>
)}
{loading && !data && <PageSpinner />}
</div>
</div>
)
+2 -1
View File
@@ -4,6 +4,7 @@ import { use, useEffect } from 'react'
import Link from 'next/link'
import { TeamFlag } from '@/components/team-flag'
import { MatchCard } from '@/components/match-card'
import { PageSpinner } from '@/components/page-spinner'
const PLAYER_QUERY = gql`
query Player($name: String!) {
@@ -46,7 +47,7 @@ export function PlayerClient({ params }: { params: Promise<{ name: string }> })
`)
if (loading && !data) {
return <div className="max-w-[1200px] mx-auto px-7 py-10 text-green-muted">Loading player</div>
return <PageSpinner />
}
if (!player) {
+2 -3
View File
@@ -2,6 +2,7 @@
import { useQuery, gql } from '@/lib/graphql/hooks'
import Link from 'next/link'
import { TeamFlag } from '@/components/team-flag'
import { PageSpinner } from '@/components/page-spinner'
import {
ChartBarIcon, StarIcon, TrophyIcon, ClockIcon, BoltIcon,
FireIcon, SparklesIcon, ArrowPathIcon, GlobeEuropeAfricaIcon, TableCellsIcon,
@@ -92,9 +93,7 @@ export function StatsClient() {
<div className="max-w-[1200px] mx-auto px-7 py-10 pb-16">
<h1 className="font-['Bebas_Neue'] text-[52px] tracking-[0.04em] text-green leading-none mb-10">Historical Statistics</h1>
{loading && !data && (
<div className="text-green-muted text-sm py-16 text-center">Loading statistics</div>
)}
{loading && !data && <PageSpinner />}
{/* ── Goals per tournament bar chart ── */}
{tournaments.length > 0 && (
+2 -1
View File
@@ -4,6 +4,7 @@ import { use, useEffect } from 'react'
import Link from 'next/link'
import { TeamFlag } from '@/components/team-flag'
import { TrophyIcon } from '@heroicons/react/24/outline'
import { PageSpinner } from '@/components/page-spinner'
const TEAM_QUERY = gql`
query Team($slug: String!) {
@@ -76,7 +77,7 @@ export function TeamClient({ params }: { params: Promise<{ slug: string }> }) {
const years = Object.keys(matchesByYear).map(Number).sort((a, b) => b - a)
if (loading && !teamData) {
return <div className="max-w-[1200px] mx-auto px-7 py-10 text-green-muted">Loading team</div>
return <PageSpinner />
}
if (!team) {
+2 -6
View File
@@ -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 { PageSpinner } from '@/components/page-spinner'
import { MatchCard } from '@/components/match-card'
import { LiveBadge } from '@/components/live-badge'
@@ -114,12 +115,7 @@ export function TournamentClient({ params }: { params: Promise<{ year: string }>
const maxScorer = Math.max(...(t?.topScorers?.map((s: { goals: number }) => s.goals) ?? [1]), 1)
if (loading && !data) {
return (
<div className="max-w-[1200px] mx-auto px-7 py-10">
<div className="h-24 w-48 rounded-xl animate-pulse mb-6 bg-card" />
<div className="text-green-muted text-sm">Loading {year} World Cup</div>
</div>
)
return <PageSpinner label={`${year} World Cup`} />
}
if (!t) return <div className="max-w-[1200px] mx-auto px-7 py-10 text-green-muted">Tournament {year} not found.</div>
+8
View File
@@ -0,0 +1,8 @@
export function PageSpinner({ label }: { label?: string }) {
return (
<div className="flex flex-col items-center justify-center gap-4 py-24 text-green-muted">
<div className="w-8 h-8 rounded-full border-2 border-green/20 border-t-green animate-spin" />
{label && <span className="text-sm tracking-wide">{label}</span>}
</div>
)
}