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:
+2
-3
@@ -3,6 +3,7 @@ import { useQuery, gql } from '@/lib/graphql/hooks'
|
|||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { TeamFlag } from '@/components/team-flag'
|
import { TeamFlag } from '@/components/team-flag'
|
||||||
import { LiveBadge } from '@/components/live-badge'
|
import { LiveBadge } from '@/components/live-badge'
|
||||||
|
import { PageSpinner } from '@/components/page-spinner'
|
||||||
import { MatchCard } from '@/components/match-card'
|
import { MatchCard } from '@/components/match-card'
|
||||||
|
|
||||||
const HOME_QUERY = gql`
|
const HOME_QUERY = gql`
|
||||||
@@ -231,9 +232,7 @@ export function HomeClient() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{loading && !data && (
|
{loading && !data && <PageSpinner />}
|
||||||
<div className="py-16 text-center text-green-muted text-sm">Loading live World Cup data…</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { use, useEffect } from 'react'
|
|||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { TeamFlag } from '@/components/team-flag'
|
import { TeamFlag } from '@/components/team-flag'
|
||||||
import { MatchCard } from '@/components/match-card'
|
import { MatchCard } from '@/components/match-card'
|
||||||
|
import { PageSpinner } from '@/components/page-spinner'
|
||||||
|
|
||||||
const PLAYER_QUERY = gql`
|
const PLAYER_QUERY = gql`
|
||||||
query Player($name: String!) {
|
query Player($name: String!) {
|
||||||
@@ -46,7 +47,7 @@ export function PlayerClient({ params }: { params: Promise<{ name: string }> })
|
|||||||
`)
|
`)
|
||||||
|
|
||||||
if (loading && !data) {
|
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) {
|
if (!player) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { useQuery, gql } from '@/lib/graphql/hooks'
|
import { useQuery, gql } from '@/lib/graphql/hooks'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { TeamFlag } from '@/components/team-flag'
|
import { TeamFlag } from '@/components/team-flag'
|
||||||
|
import { PageSpinner } from '@/components/page-spinner'
|
||||||
import {
|
import {
|
||||||
ChartBarIcon, StarIcon, TrophyIcon, ClockIcon, BoltIcon,
|
ChartBarIcon, StarIcon, TrophyIcon, ClockIcon, BoltIcon,
|
||||||
FireIcon, SparklesIcon, ArrowPathIcon, GlobeEuropeAfricaIcon, TableCellsIcon,
|
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">
|
<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>
|
<h1 className="font-['Bebas_Neue'] text-[52px] tracking-[0.04em] text-green leading-none mb-10">Historical Statistics</h1>
|
||||||
|
|
||||||
{loading && !data && (
|
{loading && !data && <PageSpinner />}
|
||||||
<div className="text-green-muted text-sm py-16 text-center">Loading statistics…</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* ── Goals per tournament bar chart ── */}
|
{/* ── Goals per tournament bar chart ── */}
|
||||||
{tournaments.length > 0 && (
|
{tournaments.length > 0 && (
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { use, useEffect } from 'react'
|
|||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { TeamFlag } from '@/components/team-flag'
|
import { TeamFlag } from '@/components/team-flag'
|
||||||
import { TrophyIcon } from '@heroicons/react/24/outline'
|
import { TrophyIcon } from '@heroicons/react/24/outline'
|
||||||
|
import { PageSpinner } from '@/components/page-spinner'
|
||||||
|
|
||||||
const TEAM_QUERY = gql`
|
const TEAM_QUERY = gql`
|
||||||
query Team($slug: String!) {
|
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)
|
const years = Object.keys(matchesByYear).map(Number).sort((a, b) => b - a)
|
||||||
|
|
||||||
if (loading && !teamData) {
|
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) {
|
if (!team) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useQuery, gql } from '@/lib/graphql/hooks'
|
|||||||
import { use, useEffect } from 'react'
|
import { use, useEffect } from 'react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { TeamFlag } from '@/components/team-flag'
|
import { TeamFlag } from '@/components/team-flag'
|
||||||
|
import { PageSpinner } from '@/components/page-spinner'
|
||||||
import { MatchCard } from '@/components/match-card'
|
import { MatchCard } from '@/components/match-card'
|
||||||
import { LiveBadge } from '@/components/live-badge'
|
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)
|
const maxScorer = Math.max(...(t?.topScorers?.map((s: { goals: number }) => s.goals) ?? [1]), 1)
|
||||||
|
|
||||||
if (loading && !data) {
|
if (loading && !data) {
|
||||||
return (
|
return <PageSpinner label={`${year} World Cup`} />
|
||||||
<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>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!t) return <div className="max-w-[1200px] mx-auto px-7 py-10 text-green-muted">Tournament {year} not found.</div>
|
if (!t) return <div className="max-w-[1200px] mx-auto px-7 py-10 text-green-muted">Tournament {year} not found.</div>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user