Files
worldcup/app/layout.tsx
T
valknar a494c80a76 feat: SEO enhancements — server metadata, sitemap, robots, dynamic base URL
- Split all page.tsx files into server wrapper (metadata export) + client.tsx (Apollo/interactive)
- Add robots.ts and sitemap.ts (tournaments, teams, players)
- Add metadataBase, OpenGraph and Twitter card metadata to root layout
- Replace hardcoded worldcup.pivoine.art with NEXT_PUBLIC_SITE_URL env var (sitemap/robots) and relative paths (page metadata, resolved by Next.js against metadataBase)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 20:18:36 +02:00

66 lines
2.6 KiB
TypeScript

import type { Metadata } from 'next'
import { Bebas_Neue, Space_Grotesk } from 'next/font/google'
import Script from 'next/script'
import './globals.css'
import { Nav } from '@/components/nav'
import { AppApolloProvider } from '@/components/apollo-provider'
const bebasNeue = Bebas_Neue({ weight: '400', subsets: ['latin'], variable: '--font-bebas' })
const spaceGrotesk = Space_Grotesk({ subsets: ['latin'], variable: '--font-space' })
const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL ?? 'http://localhost:3000'
export const metadata: Metadata = {
metadataBase: new URL(BASE_URL),
title: { default: 'World Cup Stats', template: '%s · World Cup' },
description: 'Live scores, group standings, results and statistics for every FIFA World Cup from 1930 to 2026.',
keywords: ['World Cup', 'FIFA', 'football', 'soccer', 'statistics', 'live scores', 'standings', '2026'],
openGraph: {
type: 'website',
siteName: 'World Cup Stats',
url: '/',
title: 'World Cup Stats',
description: 'Live scores, group standings, results and statistics for every FIFA World Cup from 1930 to 2026.',
},
twitter: {
card: 'summary',
title: 'World Cup Stats',
description: 'Live scores, group standings, results and statistics for every FIFA World Cup from 1930 to 2026.',
},
icons: {
icon: [
{ url: '/favicon.svg', type: 'image/svg+xml' },
{ url: '/favicon-32x32.png', sizes: '32x32', type: 'image/png' },
],
apple: [{ url: '/apple-touch-icon.png', sizes: '180x180', type: 'image/png' }],
},
}
const umamiId = process.env.UMAMI_ID
const umamiSrc = process.env.UMAMI_SRC
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" data-scroll-behavior="smooth" className={`${bebasNeue.variable} ${spaceGrotesk.variable}`}>
<body>
{umamiId && umamiSrc && (
<Script src={umamiSrc} data-website-id={umamiId} strategy="lazyOnload" />
)}
<AppApolloProvider>
<Nav />
<main className="pt-[60px] min-h-screen">{children}</main>
<footer className="border-t border-green/8 mt-8">
<div className="max-w-[1200px] mx-auto px-7 py-6 flex flex-col sm:flex-row items-center justify-between gap-2 text-[11px] text-green-dark">
<span>© {new Date().getFullYear()} World Cup Statistics.</span>
<a href="https://dev.pivoine.art" target="_blank" rel="noopener noreferrer"
className="text-green-muted hover:text-green transition-colors">
dev.pivoine.art
</a>
</div>
</footer>
</AppApolloProvider>
</body>
</html>
)
}