148 lines
5.3 KiB
TypeScript
148 lines
5.3 KiB
TypeScript
'use client';
|
|||
|
|
|
||
|
|
import { useState, useEffect } from 'react';
|
||
|
|
import Link from 'next/link';
|
||
|
|
import { Settings } from 'lucide-react';
|
||
|
|
import LiveMatches from './_components/live-matches';
|
||
|
|
import Standings from './_components/standings';
|
||
|
|
import TopScorers from './_components/top-scorers';
|
||
|
|
import RecentResults from './_components/recent-results';
|
||
|
|
import UpcomingFixtures from './_components/upcoming-fixtures';
|
||
|
|
import CountdownTimer from './_components/countdown-timer';
|
||
|
|
import { SettingsContext, defaultSettings, type AppSettings } from '@/lib/settings-context';
|
||
|
|
|
||
|
|
export default function HomePage() {
|
||
|
|
const [settings, setSettings] = useState<AppSettings>(defaultSettings);
|
||
|
|
const [loading, setLoading] = useState(true);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const fetchSettings = async () => {
|
||
|
|
try {
|
||
|
|
const res = await fetch('/api/settings');
|
||
|
|
if (res.ok) {
|
||
|
|
const data = await res.json();
|
||
|
|
setSettings(data);
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Failed to fetch settings:', error);
|
||
|
|
} finally {
|
||
|
|
setLoading(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
fetchSettings();
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
const leagueName =
|
||
|
|
settings.selectedLeague === 'euro_2026'
|
||
|
|
? 'UEFA Euro 2024'
|
||
|
|
: 'Primeira Liga';
|
||
|
|
|
||
|
|
const leagueSubtitle =
|
||
|
|
settings.selectedLeague === 'euro_2026'
|
||
|
|
? 'European Championship'
|
||
|
|
: 'Portuguese Championship 2024/2025';
|
||
|
|
|
||
|
|
const heroText =
|
||
|
|
settings.selectedLeague === 'euro_2026'
|
||
|
|
? 'European Football at Your Fingertips'
|
||
|
|
: 'Portuguese Football at Your Fingertips';
|
||
|
|
|
||
|
|
if (loading) {
|
||
|
|
return (
|
||
|
|
<div className="min-h-screen flex items-center justify-center">
|
||
|
|
<div className="animate-spin rounded-full h-12 w-12 border-4 border-[#006600] border-t-transparent"></div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<SettingsContext.Provider value={settings}>
|
||
|
|
<main className="min-h-screen pb-16">
|
||
|
|
{/* Header */}
|
||
|
|
<header className="sticky top-0 z-50 backdrop-blur-md bg-white/80 border-b border-gray-200 shadow-sm">
|
||
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||
|
|
<div className="flex items-center justify-between h-20">
|
||
|
|
<div className="flex items-center gap-3">
|
||
|
|
<div
|
||
|
|
className="w-12 h-12 rounded-full flex items-center justify-center shadow-lg"
|
||
|
|
style={{
|
||
|
|
background: `linear-gradient(135deg, ${settings.primaryColor}, ${settings.secondaryColor})`,
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<svg
|
||
|
|
className="w-7 h-7 text-white"
|
||
|
|
fill="none"
|
||
|
|
stroke="currentColor"
|
||
|
|
viewBox="0 0 24 24"
|
||
|
|
>
|
||
|
|
<circle cx="12" cy="12" r="10" strokeWidth="2" />
|
||
|
|
<path d="M12 2v20M2 12h20" strokeWidth="2" />
|
||
|
|
</svg>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<h1
|
||
|
|
className="text-2xl font-bold bg-clip-text text-transparent"
|
||
|
|
style={{
|
||
|
|
backgroundImage: `linear-gradient(to right, ${settings.primaryColor}, ${settings.secondaryColor})`,
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
{leagueName}
|
||
|
|
</h1>
|
||
|
|
<p className="text-sm text-gray-600">{leagueSubtitle}</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div className="flex items-center gap-4">
|
||
|
|
<CountdownTimer refreshInterval={settings.refreshInterval} />
|
||
|
|
<Link
|
||
|
|
href="/admin"
|
||
|
|
className="p-2 rounded-lg hover:bg-gray-100 transition-colors"
|
||
|
|
title="Admin Panel"
|
||
|
|
>
|
||
|
|
<Settings className="h-5 w-5 text-gray-500" />
|
||
|
|
</Link>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</header>
|
||
|
|
|
||
|
|
{/* Hero Section */}
|
||
|
|
<section
|
||
|
|
className="text-white py-12"
|
||
|
|
style={{
|
||
|
|
background: `linear-gradient(135deg, ${settings.primaryColor}, ${settings.secondaryColor})`,
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
|
||
|
|
<h2 className="text-4xl font-bold mb-3">{heroText}</h2>
|
||
|
|
<p className="text-xl opacity-90">
|
||
|
|
Real-time scores, standings, and statistics
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
{/* Main Content */}
|
||
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 space-y-8">
|
||
|
|
{settings.showLiveMatches && <LiveMatches />}
|
||
|
|
{settings.showStandings && <Standings />}
|
||
|
|
{settings.showTopScorers && <TopScorers />}
|
||
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||
|
|
{settings.showRecentResults && <RecentResults />}
|
||
|
|
{settings.showUpcoming && <UpcomingFixtures />}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Footer */}
|
||
|
|
<footer className="mt-16 py-8 border-t border-gray-200 bg-white">
|
||
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center text-gray-600">
|
||
|
|
<p className="text-sm">
|
||
|
|
Data updates every {Math.round(settings.refreshInterval / 60)}{' '}
|
||
|
|
minutes • Powered by API-Football
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
</footer>
|
||
|
|
</main>
|
||
|
|
</SettingsContext.Provider>
|
||
|
|
);
|
||
|
|
}
|