import React, { useState, useEffect, useMemo } from ‘react’;
import { initializeApp } from ‘firebase/app’;
import {
getAuth,
signInAnonymously,
signInWithCustomToken,
onAuthStateChanged,
signOut
} from ‘firebase/auth’;
import {
getFirestore,
collection,
doc,
setDoc,
addDoc,
updateDoc,
deleteDoc,
onSnapshot
} from ‘firebase/firestore’;
import {
Trophy,
Shield,
Play,
Pause,
RotateCcw,
Plus,
Edit2,
Trash2,
UserCheck,
Users,
Calendar,
Activity,
BarChart3,
CheckCircle2,
Clock,
AlertCircle,
Star,
Settings,
Zap,
Flame,
Award,
ChevronRight,
Filter,
RefreshCw,
LogOut,
User,
Check,
ShieldAlert
} from ‘lucide-react’;
// — Firebase Configuration & Initialization —
const firebaseConfig = typeof __firebase_config !== ‘undefined’
? JSON.parse(__firebase_config)
: {
apiKey: “demo-key”,
authDomain: “demo-app.firebaseapp.com”,
projectId: “demo-app”,
storageBucket: “demo-app.appspot.com”,
messagingSenderId: “123456789”,
appId: “1:123456789:web:demo”
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
const appId = typeof __app_id !== ‘undefined’ ? __app_id : ‘live-tracker-app’;
// Initial Mock Data for first initialization if db is empty
const INITIAL_TEAMS = [
{ id: ‘team1’, name: ‘Milan’, code: ‘MIL’, logo: ‘🔴🖤’, points: 0 },
{ id: ‘team2’, name: ‘Inter’, code: ‘INT’, logo: ‘🔵🖤’, points: 0 },
{ id: ‘team3’, name: ‘Juventus’, code: ‘JUV’, logo: ‘⚪🖤’, points: 0 },
{ id: ‘team4’, name: ‘Napoli’, code: ‘NAP’, logo: ‘🔵⚪’, points: 0 },
{ id: ‘team5’, name: ‘Roma’, code: ‘ROM’, logo: ‘🟡🔴’, points: 0 },
{ id: ‘team6’, name: ‘Atalanta’, code: ‘ATA’, logo: ‘🔵⚫’, points: 0 },
];
const INITIAL_MATCHES = [
{
id: ‘match1’,
round: 1,
homeTeamId: ‘team1’,
awayTeamId: ‘team2’,
homeScore: 2,
awayScore: 1,
status: ‘LIVE’, // ‘UPCOMING’, ‘LIVE’, ‘HALF_TIME’, ‘FINISHED’
minute: 68,
events: [
{ id: ‘e1’, minute: 14, type: ‘GOAL’, teamId: ‘team1’, player: ‘Rafa Leão’, detail: ‘Azione personale’ },
{ id: ‘e2’, minute: 32, type: ‘GOAL’, teamId: ‘team2’, player: ‘Lautaro Martínez’, detail: ‘Colpo di testa’ },
{ id: ‘e3’, minute: 55, type: ‘GOAL’, teamId: ‘team1’, player: ‘Theo Hernández’, detail: ‘Rigore’ },
{ id: ‘e4’, minute: 61, type: ‘CARD_YELLOW’, teamId: ‘team2’, player: ‘Barella’, detail: ‘Fallo tattico’ }
]
},
{
id: ‘match2’,
round: 1,
homeTeamId: ‘team3’,
awayTeamId: ‘team4’,
homeScore: 0,
awayScore: 0,
status: ‘FINISHED’,
minute: 90,
events: []
},
{
id: ‘match3’,
round: 1,
homeTeamId: ‘team5’,
awayTeamId: ‘team6’,
homeScore: 3,
awayScore: 2,
status: ‘FINISHED’,
minute: 90,
events: [
{ id: ‘e5’, minute: 22, type: ‘GOAL’, teamId: ‘team5’, player: ‘Dybala’, detail: ‘Punizione’ }
]
},
{
id: ‘match4’,
round: 2,
homeTeamId: ‘team2’,
awayTeamId: ‘team3’,
homeScore: 0,
awayScore: 0,
status: ‘UPCOMING’,
minute: 0,
events: []
},
{
id: ‘match5’,
round: 2,
homeTeamId: ‘team4’,
awayTeamId: ‘team1’,
homeScore: 0,
awayScore: 0,
status: ‘UPCOMING’,
minute: 0,
events: []
}
];
export default function App() {
// — States —
const [user, setUser] = useState(null);
const [role, setRole] = useState(‘fan’); // ‘admin’ or ‘fan’
const [activeTab, setActiveTab] = useState(‘live’); // ‘live’, ‘standings’, ‘matches’, ‘teams’, ‘admin’
const [selectedRound, setSelectedRound] = useState(‘ALL’);
const [favorites, setFavorites] = useState([]);
// Data from Firestore
const [teams, setTeams] = useState([]);
const [matches, setMatches] = useState([]);
const [loading, setLoading] = useState(true);
// Modals & Active Edit Forms
const [selectedMatch, setSelectedMatch] = useState(null);
const [editingMatch, setEditingMatch] = useState(null);
const [newEvent, setNewEvent] = useState({ minute: 1, type: ‘GOAL’, teamId: ”, player: ”, detail: ” });
const [newTeamName, setNewTeamName] = useState(”);
const [newTeamCode, setNewTeamCode] = useState(”);
const [newTeamLogo, setNewTeamLogo] = useState(‘⚽’);
// — Auth Setup —
useEffect(() => {
const initAuth = async () => {
try {
if (typeof __initial_auth_token !== ‘undefined’ && __initial_auth_token) {
await signInWithCustomToken(auth, __initial_auth_token);
} else {
await signInAnonymously(auth);
}
} catch (err) {
console.error(“Auth initialization error:”, err);
}
};
initAuth();
const unsubscribe = onAuthStateChanged(auth, (currUser) => {
setUser(currUser);
});
return () => unsubscribe();
}, []);
// — Firestore Sync —
useEffect(() => {
if (!user) return;
// References to public collections
const teamsColRef = collection(db, ‘artifacts’, appId, ‘public’, ‘data’, ‘teams’);
const matchesColRef = collection(db, ‘artifacts’, appId, ‘public’, ‘data’, ‘matches’);
// Subscribe to Teams
const unsubTeams = onSnapshot(teamsColRef, (snapshot) => {
const loadedTeams = [];
snapshot.forEach(doc => {
loadedTeams.push({ id: doc.id, …doc.data() });
});
if (loadedTeams.length === 0) {
// Initialize default teams if Firestore is empty
INITIAL_TEAMS.forEach(async (t) => {
await setDoc(doc(db, ‘artifacts’, appId, ‘public’, ‘data’, ‘teams’, t.id), t);
});
} else {
setTeams(loadedTeams);
}
}, (error) => console.error(“Teams listener error:”, error));
// Subscribe to Matches
const unsubMatches = onSnapshot(matchesColRef, (snapshot) => {
const loadedMatches = [];
snapshot.forEach(doc => {
loadedMatches.push({ id: doc.id, …doc.data() });
});
if (loadedMatches.length === 0) {
// Initialize default matches if Firestore is empty
INITIAL_MATCHES.forEach(async (m) => {
await setDoc(doc(db, ‘artifacts’, appId, ‘public’, ‘data’, ‘matches’, m.id), m);
});
} else {
setMatches(loadedMatches.sort((a, b) => a.round – b.round));
}
setLoading(false);
}, (error) => console.error(“Matches listener error:”, error));
return () => {
unsubTeams();
unsubMatches();
};
}, [user]);
// — Live Timer Simulation Loop for Matches marked LIVE —
useEffect(() => {
const timer = setInterval(() => {
if (role === ‘admin’) {
// Increment minutes for live games automatically
matches.forEach(m => {
if (m.status === ‘LIVE’ && m.minute < 90) {
const matchRef = doc(db, ‘artifacts’, appId, ‘public’, ‘data’, ‘matches’, m.id);
updateDoc(matchRef, { minute: m.minute + 1 }).catch(() => {});
}
});
}
}, 15000); // advance minute every 15 seconds for realistic feel
return () => clearInterval(timer);
}, [matches, role]);
// — Automatic Standings Calculation —
const standings = useMemo(() => {
const stats = {};
// Initialize stats for each team
teams.forEach(t => {
stats[t.id] = {
id: t.id,
name: t.name,
code: t.code,
logo: t.logo,
played: 0,
won: 0,
drawn: 0,
lost: 0,
gf: 0, // Goals For
ga: 0, // Goals Against
gd: 0, // Goal Difference
points: 0
};
});
// Compute stats based on completed or live matches
matches.forEach(m => {
if (m.status === ‘FINISHED’ || m.status === ‘LIVE’ || m.status === ‘HALF_TIME’) {
const home = stats[m.homeTeamId];
const away = stats[m.awayTeamId];
if (home && away) {
home.played += 1;
away.played += 1;
home.gf += m.homeScore;
home.ga += m.awayScore;
away.gf += m.awayScore;
away.ga += m.homeScore;
if (m.homeScore > m.awayScore) {
home.won += 1;
home.points += 3;
away.lost += 1;
} else if (m.homeScore < m.awayScore) {
away.won += 1;
away.points += 3;
home.lost += 1;
} else {
home.drawn += 1;
home.points += 1;
away.drawn += 1;
away.points += 1;
}
}
}
});
// Calculate Goal Difference and convert to array
const standingsList = Object.values(stats).map(s => ({
…s,
gd: s.gf – s.ga
}));
// Sorting Order: Points -> Goal Difference -> Goals For -> Name
standingsList.sort((a, b) => {
if (b.points !== a.points) return b.points – a.points;
if (b.gd !== a.gd) return b.gd – a.gd;
if (b.gf !== a.gf) return b.gf – a.gf;
return a.name.localeCompare(b.name);
});
return standingsList;
}, [teams, matches]);
// — Helpers & Actions —
const toggleFavorite = (teamId) => {
setFavorites(prev =>
prev.includes(teamId) ? prev.filter(id => id !== teamId) : […prev, teamId]
);
};
const updateScore = async (matchId, homeChange, awayChange) => {
const match = matches.find(m => m.id === matchId);
if (!match) return;
const newHome = Math.max(0, match.homeScore + homeChange);
const newAway = Math.max(0, match.awayScore + awayChange);
const matchRef = doc(db, ‘artifacts’, appId, ‘public’, ‘data’, ‘matches’, matchId);
await updateDoc(matchRef, {
homeScore: newHome,
awayScore: newAway
});
};
const updateMatchStatus = async (matchId, newStatus) => {
const matchRef = doc(db, ‘artifacts’, appId, ‘public’, ‘data’, ‘matches’, matchId);
const updates = { status: newStatus };
if (newStatus === ‘LIVE’ && matches.find(m => m.id === matchId)?.minute === 0) {
updates.minute = 1;
}
await updateDoc(matchRef, updates);
};
const addMatchEvent = async (matchId) => {
if (!newEvent.player.trim()) return;
const match = matches.find(m => m.id === matchId);
if (!match) return;
const updatedEvents = […(match.events || []), { …newEvent, id: ‘ev_’ + Date.now() }];
// Auto increment score if event is a goal
let homeScore = match.homeScore;
let awayScore = match.awayScore;
if (newEvent.type === ‘GOAL’) {
if (newEvent.teamId === match.homeTeamId) homeScore++;
if (newEvent.teamId === match.awayTeamId) awayScore++;
}
const matchRef = doc(db, ‘artifacts’, appId, ‘public’, ‘data’, ‘matches’, matchId);
await updateDoc(matchRef, {
events: updatedEvents,
homeScore,
awayScore
});
setNewEvent({ minute: match.minute || 1, type: ‘GOAL’, teamId: match.homeTeamId, player: ”, detail: ” });
};
const deleteMatchEvent = async (matchId, eventId) => {
const match = matches.find(m => m.id === matchId);
if (!match) return;
const updatedEvents = match.events.filter(e => e.id !== eventId);
const matchRef = doc(db, ‘artifacts’, appId, ‘public’, ‘data’, ‘matches’, matchId);
await updateDoc(matchRef, { events: updatedEvents });
};
const createTeam = async (e) => {
e.preventDefault();
if (!newTeamName.trim() || !newTeamCode.trim()) return;
const newTeam = {
name: newTeamName,
code: newTeamCode.toUpperCase().slice(0, 3),
logo: newTeamLogo || ‘⚽’,
points: 0
};
const teamsColRef = collection(db, ‘artifacts’, appId, ‘public’, ‘data’, ‘teams’);
await addDoc(teamsColRef, newTeam);
setNewTeamName(”);
setNewTeamCode(”);
setNewTeamLogo(‘⚽’);
};
const getTeam = (id) => teams.find(t => t.id === id) || { name: id, logo: ‘🛡️’, code: ‘—‘ };
const roundsList = useMemo(() => {
const rounds = […new Set(matches.map(m => m.round))];
return rounds.sort((a, b) => a – b);
}, [matches]);
const filteredMatches = useMemo(() => {
if (selectedRound === ‘ALL’) return matches;
return matches.filter(m => m.round === Number(selectedRound));
}, [matches, selectedRound]);
const liveMatches = useMemo(() => {
return matches.filter(m => m.status === ‘LIVE’ || m.status === ‘HALF_TIME’);
}, [matches]);
return (
<div className=”min-h-screen bg-slate-950 text-slate-100 font-sans pb-12″>
{/* — Top Navigation Header — */}
<header className=”sticky top-0 z-40 bg-slate-900/90 backdrop-blur-md border-b border-slate-800 shadow-lg”>
<div className=”max-w-7xl mx-auto px-4 sm:px-6 lg:px-8″>
<div className=”flex items-center justify-between h-16″>
{/* Logo and App Name */}
<div className=”flex items-center space-x-3″>
<div className=”bg-gradient-to-tr from-emerald-500 to-teal-400 p-2 rounded-xl text-slate-950 shadow-emerald-500/20 shadow-lg”>
<Trophy className=”w-6 h-6″ />
</div>
<div>
<span className=”text-xl font-extrabold tracking-wider bg-gradient-to-r from-white via-slate-200 to-emerald-400 bg-clip-text text-transparent”>
SERIE LIVE
</span>
<span className=”hidden sm:inline-block ml-2 text-xs font-semibold px-2 py-0.5 rounded bg-emerald-500/10 text-emerald-400 border border-emerald-500/20″>
TEMPO REALE
</span>
</div>
</div>
{/* Quick Navigation Tabs */}
<nav className=”hidden md:flex space-x-1 bg-slate-950/60 p-1 rounded-xl border border-slate-800″>
<button
onClick={() => setActiveTab(‘live’)}
className={`flex items-center space-x-2 px-4 py-2 rounded-lg text-sm font-medium transition-all ${
activeTab === ‘live’
? ‘bg-emerald-500 text-slate-950 font-bold shadow-md’
: ‘text-slate-400 hover:text-slate-200 hover:bg-slate-800/50’
}`}
>
<Activity className=”w-4 h-4″ />
<span>Risultati & Live</span>
{liveMatches.length > 0 && (
<span className=”flex h-2 w-2 relative”>
<span className=”animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75″></span>
<span className=”relative inline-flex rounded-full h-2 w-2 bg-red-500″></span>
</span>
)}
</button>
<button
onClick={() => setActiveTab(‘standings’)}
className={`flex items-center space-x-2 px-4 py-2 rounded-lg text-sm font-medium transition-all ${
activeTab === ‘standings’
? ‘bg-emerald-500 text-slate-950 font-bold shadow-md’
: ‘text-slate-400 hover:text-slate-200 hover:bg-slate-800/50’
}`}
>
<BarChart3 className=”w-4 h-4″ />
<span>Classifica</span>
</button>
<button
onClick={() => setActiveTab(‘matches’)}
className={`flex items-center space-x-2 px-4 py-2 rounded-lg text-sm font-medium transition-all ${
activeTab === ‘matches’
? ‘bg-emerald-500 text-slate-950 font-bold shadow-md’
: ‘text-slate-400 hover:text-slate-200 hover:bg-slate-800/50’
}`}
>
<Calendar className=”w-4 h-4″ />
<span>Calendario</span>
</button>
<button
onClick={() => setActiveTab(‘teams’)}
className={`flex items-center space-x-2 px-4 py-2 rounded-lg text-sm font-medium transition-all ${
activeTab === ‘teams’
? ‘bg-emerald-500 text-slate-950 font-bold shadow-md’
: ‘text-slate-400 hover:text-slate-200 hover:bg-slate-800/50’
}`}
>
<Shield className=”w-4 h-4″ />
<span>Squadre</span>
</button>
{role === ‘admin’ && (
<button
onClick={() => setActiveTab(‘admin’)}
className={`flex items-center space-x-2 px-4 py-2 rounded-lg text-sm font-medium transition-all ${
activeTab === ‘admin’
? ‘bg-amber-500 text-slate-950 font-bold shadow-md’
: ‘text-amber-400 hover:bg-amber-500/10’
}`}
>
<Settings className=”w-4 h-4″ />
<span>Pannello Admin</span>
</button>
)}
</nav>
{/* Role Switcher & User Profile */}
<div className=”flex items-center space-x-3″>
<div className=”bg-slate-950/80 p-1 rounded-xl border border-slate-800 flex items-center”>
<button
onClick={() => setRole(‘fan’)}
className={`px-3 py-1.5 rounded-lg text-xs font-semibold transition-all ${
role === ‘fan’ ? ‘bg-slate-800 text-emerald-400 shadow’ : ‘text-slate-400 hover:text-slate-200’
}`}
>
Tifoso
</button>
<button
onClick={() => setRole(‘admin’)}
className={`px-3 py-1.5 rounded-lg text-xs font-semibold transition-all flex items-center space-x-1 ${
role === ‘admin’ ? ‘bg-amber-500/20 text-amber-400 border border-amber-500/30’ : ‘text-slate-400 hover:text-slate-200’
}`}
>
<ShieldAlert className=”w-3 h-3″ />
<span>Admin</span>
</button>
</div>
</div>
</div>
</div>
{/* Mobile Navigation */}
<div className=”md:hidden flex justify-around border-t border-slate-800 bg-slate-900/95 py-2 px-2 overflow-x-auto”>
<button
onClick={() => setActiveTab(‘live’)}
className={`flex flex-col items-center px-3 py-1 rounded-lg text-xs ${activeTab === ‘live’ ? ‘text-emerald-400 font-bold’ : ‘text-slate-400’}`}
>
<Activity className=”w-4 h-4 mb-1″ />
<span>Live</span>
</button>
<button
onClick={() => setActiveTab(‘standings’)}
className={`flex flex-col items-center px-3 py-1 rounded-lg text-xs ${activeTab === ‘standings’ ? ‘text-emerald-400 font-bold’ : ‘text-slate-400’}`}
>
<BarChart3 className=”w-4 h-4 mb-1″ />
<span>Classifica</span>
</button>
<button
onClick={() => setActiveTab(‘matches’)}
className={`flex flex-col items-center px-3 py-1 rounded-lg text-xs ${activeTab === ‘matches’ ? ‘text-emerald-400 font-bold’ : ‘text-slate-400’}`}
>
<Calendar className=”w-4 h-4 mb-1″ />
<span>Partite</span>
</button>
<button
onClick={() => setActiveTab(‘teams’)}
className={`flex flex-col items-center px-3 py-1 rounded-lg text-xs ${activeTab === ‘teams’ ? ‘text-emerald-400 font-bold’ : ‘text-slate-400’}`}
>
<Shield className=”w-4 h-4 mb-1″ />
<span>Squadre</span>
</button>
{role === ‘admin’ && (
<button
onClick={() => setActiveTab(‘admin’)}
className={`flex flex-col items-center px-3 py-1 rounded-lg text-xs ${activeTab === ‘admin’ ? ‘text-amber-400 font-bold’ : ‘text-slate-400’}`}
>
<Settings className=”w-4 h-4 mb-1″ />
<span>Admin</span>
</button>
)}
</div>
</header>
{/* — Main Content Body — */}
<main className=”max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 mt-6″>
{/* Live Banner / Status Indicator */}
<div className=”mb-6 flex flex-wrap items-center justify-between gap-4 bg-slate-900/60 p-4 rounded-2xl border border-slate-800/80″>
<div className=”flex items-center space-x-3″>
<div className=”relative flex items-center justify-center”>
<span className=”animate-ping absolute inline-flex h-3 w-3 rounded-full bg-emerald-400 opacity-75″></span>
<span className=”relative inline-flex rounded-full h-3 w-3 bg-emerald-500″></span>
</div>
<span className=”text-sm font-medium text-slate-300″>
Sincronizzazione in tempo reale attiva
</span>
</div>
<div className=”flex items-center space-x-3″>
<label className=”text-xs text-slate-400 font-medium”>Filtra Giornata:</label>
<select
value={selectedRound}
onChange={(e) => setSelectedRound(e.target.value)}
className=”bg-slate-950 text-slate-200 text-xs rounded-lg px-3 py-1.5 border border-slate-800 focus:outline-none focus:border-emerald-500″
>
<option value=”ALL”>Tutte le Giornate</option>
{roundsList.map(r => (
<option key={r} value={r}>Giornata {r}</option>
))}
</select>
</div>
</div>
{/* — TAB 1: RISULTATI & LIVE — */}
{activeTab === ‘live’ && (
<div className=”space-y-6″>
{/* Live Matches Hero Section */}
{liveMatches.length > 0 && (
<div className=”space-y-3″>
<div className=”flex items-center space-x-2 text-red-400 font-semibold text-sm uppercase tracking-wider”>
<Flame className=”w-4 h-4 animate-pulse” />
<span>In Corso ({liveMatches.length})</span>
</div>
<div className=”grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4″>
{liveMatches.map((m) => {
const home = getTeam(m.homeTeamId);
const away = getTeam(m.awayTeamId);
return (
<div
key={m.id}
onClick={() => setSelectedMatch(m)}
className=”bg-slate-900/90 hover:bg-slate-900 border border-emerald-500/40 hover:border-emerald-500 rounded-2xl p-4 transition-all shadow-lg hover:shadow-emerald-500/10 cursor-pointer relative overflow-hidden”
>
<div className=”absolute top-0 right-0 bg-red-500 text-white font-black text-[10px] px-3 py-0.5 rounded-bl-xl tracking-widest flex items-center space-x-1″>
<span>LIVE</span>
<span>{m.status === ‘HALF_TIME’ ? ‘INT’ : `${m.minute}’`}</span>
</div>
<div className=”text-xs text-slate-400 mb-3 font-semibold”>Giornata {m.round}</div>
<div className=”flex items-center justify-between my-2″>
{/* Home Team */}
<div className=”flex flex-col items-center flex-1″>
<span className=”text-3xl mb-1″>{home.logo}</span>
<span className=”font-bold text-sm text-center text-slate-100″>{home.name}</span>
</div>
{/* Score Board */}
<div className=”px-4 py-2 bg-slate-950 rounded-xl border border-slate-800 text-2xl font-black tracking-widest text-emerald-400 flex items-center space-x-2″>
<span>{m.homeScore}</span>
<span className=”text-slate-600 text-sm”>-</span>
<span>{m.awayScore}</span>
</div>
{/* Away Team */}
<div className=”flex flex-col items-center flex-1″>
<span className=”text-3xl mb-1″>{away.logo}</span>
<span className=”font-bold text-sm text-center text-slate-100″>{away.name}</span>
</div>
</div>
{/* Recent Event Snippet */}
{m.events && m.events.length > 0 && (
<div className=”mt-3 pt-3 border-t border-slate-800/80 text-xs text-slate-400 flex items-center justify-center space-x-1 truncate”>
<Zap className=”w-3 h-3 text-amber-400 flex-shrink-0″ />
<span className=”truncate”>
{m.events[m.events.length – 1].minute}’ {m.events[m.events.length – 1].player} ({m.events[m.events.length – 1].type === ‘GOAL’ ? ‘⚽ Gol’ : ‘🟨 Cartellino’})
</span>
</div>
)}
</div>
);
})}
</div>
</div>
)}
{/* All Filtered Matches List */}
<div className=”space-y-4″>
<h2 className=”text-lg font-bold text-slate-200 flex items-center space-x-2″>
<Calendar className=”w-5 h-5 text-emerald-400″ />
<span>Tutti i Risultati e Programma</span>
</h2>
<div className=”grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4″>
{filteredMatches.map((m) => {
const home = getTeam(m.homeTeamId);
const away = getTeam(m.awayTeamId);
const isFav = favorites.includes(m.homeTeamId) || favorites.includes(m.awayTeamId);
return (
<div
key={m.id}
className={`bg-slate-900/80 rounded-2xl p-4 border transition-all ${
isFav ? ‘border-amber-500/50 bg-amber-500/5’ : ‘border-slate-800 hover:border-slate-700’
}`}
>
<div className=”flex items-center justify-between text-xs text-slate-400 mb-2″>
<span className=”font-medium”>Giornata {m.round}</span>
<div className=”flex items-center space-x-2″>
{m.status === ‘LIVE’ && (
<span className=”px-2 py-0.5 rounded bg-red-500/20 text-red-400 font-bold border border-red-500/30 text-[10px]”>
LIVE {m.minute}’
</span>
)}
{m.status === ‘HALF_TIME’ && (
<span className=”px-2 py-0.5 rounded bg-amber-500/20 text-amber-400 font-bold border border-amber-500/30 text-[10px]”>
INTERVALLO
</span>
)}
{m.status === ‘FINISHED’ && (
<span className=”px-2 py-0.5 rounded bg-slate-800 text-slate-400 font-semibold text-[10px]”>
FINALE
</span>
)}
{m.status === ‘UPCOMING’ && (
<span className=”px-2 py-0.5 rounded bg-slate-800 text-slate-400 text-[10px]”>
DA GIOCARE
</span>
)}
</div>
</div>
<div className=”flex items-center justify-between my-3″>
{/* Home */}
<div className=”flex items-center space-x-3 flex-1″>
<span className=”text-2xl”>{home.logo}</span>
<span className={`text-sm font-semibold truncate ${favorites.includes(home.id) ? ‘text-amber-400 font-bold’ : ‘text-slate-200’}`}>
{home.name}
</span>
</div>
{/* Score or Status */}
<div className=”px-3 py-1 bg-slate-950 rounded-lg text-sm font-black border border-slate-800 text-center min-w-[60px]”>
{m.status === ‘UPCOMING’ ? (
<span className=”text-slate-500 text-xs”>VS</span>
) : (
<span className={m.status === ‘LIVE’ ? ‘text-emerald-400’ : ‘text-slate-200’}>
{m.homeScore} – {m.awayScore}
</span>
)}
</div>
{/* Away */}
<div className=”flex items-center justify-end space-x-3 flex-1 text-right”>
<span className={`text-sm font-semibold truncate ${favorites.includes(away.id) ? ‘text-amber-400 font-bold’ : ‘text-slate-200’}`}>
{away.name}
</span>
<span className=”text-2xl”>{away.logo}</span>
</div>
</div>
{/* Admin Live Controls Quick Access */}
{role === ‘admin’ && (
<div className=”mt-3 pt-3 border-t border-slate-800 flex items-center justify-between gap-2″>
<div className=”flex items-center space-x-1″>
<button
onClick={() => updateScore(m.id, 1, 0)}
className=”px-2 py-1 bg-slate-800 hover:bg-slate-700 text-xs rounded text-emerald-400 font-bold”
>
+1 Casa
</button>
<button
onClick={() => updateScore(m.id, 0, 1)}
className=”px-2 py-1 bg-slate-800 hover:bg-slate-700 text-xs rounded text-emerald-400 font-bold”
>
+1 Fuori
</button>
</div>
<button
onClick={() => setSelectedMatch(m)}
className=”text-xs text-amber-400 hover:underline font-semibold flex items-center space-x-1″
>
<Edit2 className=”w-3 h-3″ />
<span>Gestisci</span>
</button>
</div>
)}
{/* Fan Details Link */}
{role === ‘fan’ && (
<button
onClick={() => setSelectedMatch(m)}
className=”mt-2 w-full py-1 text-xs text-slate-400 hover:text-emerald-400 text-center font-medium border-t border-slate-800/60 pt-2″
>
Vedi Dettagli & Cronaca →
</button>
)}
</div>
);
})}
</div>
</div>
</div>
)}
{/* — TAB 2: CLASSIFICA LIVE — */}
{activeTab === ‘standings’ && (
<div className=”space-y-4″>
<div className=”flex items-center justify-between”>
<div>
<h2 className=”text-xl font-black text-slate-100 flex items-center space-x-2″>
<Trophy className=”w-6 h-6 text-amber-400″ />
<span>Classifica in Tempo Reale</span>
</h2>
<p className=”text-xs text-slate-400 mt-1″>
Aggiornata automaticamente ad ogni variazione dei punteggi
</p>
</div>
{/* Legend */}
<div className=”hidden sm:flex items-center space-x-4 text-xs text-slate-400″>
<span className=”flex items-center space-x-1″>
<span className=”w-2.5 h-2.5 rounded-full bg-emerald-500″></span>
<span>Champions</span>
</span>
<span className=”flex items-center space-x-1″>
<span className=”w-2.5 h-2.5 rounded-full bg-blue-500″></span>
<span>Europa League</span>
</span>
<span className=”flex items-center space-x-1″>
<span className=”w-2.5 h-2.5 rounded-full bg-red-500″></span>
<span>Retrocessione</span>
</span>
</div>
</div>
{/* Standings Table */}
<div className=”bg-slate-900 border border-slate-800 rounded-2xl overflow-hidden shadow-xl”>
<div className=”overflow-x-auto”>
<table className=”w-full text-left text-sm text-slate-300″>
<thead className=”bg-slate-950/80 text-xs uppercase text-slate-400 border-b border-slate-800″>
<tr>
<th className=”py-3 px-4 text-center”>Pos</th>
<th className=”py-3 px-4″>Squadra</th>
<th className=”py-3 px-2 text-center”>PG</th>
<th className=”py-3 px-2 text-center”>V</th>
<th className=”py-3 px-2 text-center”>P</th>
<th className=”py-3 px-2 text-center”>S</th>
<th className=”py-3 px-2 text-center hidden sm:table-cell”>GF</th>
<th className=”py-3 px-2 text-center hidden sm:table-cell”>GS</th>
<th className=”py-3 px-2 text-center”>DR</th>
<th className=”py-3 px-4 text-center font-black text-emerald-400″>PT</th>
<th className=”py-3 px-2 text-center”>Pref</th>
</tr>
</thead>
<tbody className=”divide-y divide-slate-800/60″>
{standings.map((team, idx) => {
const pos = idx + 1;
const isFav = favorites.includes(team.id);
let posBg = “border-l-4 border-transparent”;
if (pos <= 2) posBg = “border-l-4 border-emerald-500 bg-emerald-500/5”;
else if (pos === 3 || pos === 4) posBg = “border-l-4 border-blue-500 bg-blue-500/5”;
else if (pos >= standings.length – 1) posBg = “border-l-4 border-red-500 bg-red-500/5”;
return (
<tr key={team.id} className={`hover:bg-slate-800/40 transition-colors ${posBg}`}>
{/* Position */}
<td className=”py-3.5 px-4 text-center font-bold text-slate-400″>
{pos}
</td>
{/* Team Name & Emblem */}
<td className=”py-3.5 px-4 font-semibold text-slate-100″>
<div className=”flex items-center space-x-3″>
<span className=”text-xl”>{team.logo}</span>
<div>
<span>{team.name}</span>
<span className=”text-xs text-slate-500 ml-2 font-mono”>({team.code})</span>
</div>
</div>
</td>
{/* Played */}
<td className=”py-3.5 px-2 text-center text-slate-300 font-mono”>{team.played}</td>
{/* Won */}
<td className=”py-3.5 px-2 text-center text-slate-400 font-mono”>{team.won}</td>
{/* Drawn */}
<td className=”py-3.5 px-2 text-center text-slate-400 font-mono”>{team.drawn}</td>
{/* Lost */}
<td className=”py-3.5 px-2 text-center text-slate-400 font-mono”>{team.lost}</td>
{/* GF */}
<td className=”py-3.5 px-2 text-center text-slate-500 font-mono hidden sm:table-cell”>{team.gf}</td>
{/* GS */}
<td className=”py-3.5 px-2 text-center text-slate-500 font-mono hidden sm:table-cell”>{team.ga}</td>
{/* Goal Difference */}
<td className={`py-3.5 px-2 text-center font-mono font-bold ${team.gd > 0 ? ‘text-emerald-400’ : team.gd < 0 ? ‘text-red-400’ : ‘text-slate-400’}`}>
{team.gd > 0 ? `+${team.gd}` : team.gd}
</td>
{/* Points */}
<td className=”py-3.5 px-4 text-center font-black text-base text-emerald-400 font-mono bg-slate-950/40″>
{team.points}
</td>
{/* Favorite Star */}
<td className=”py-3.5 px-2 text-center”>
<button
onClick={() => toggleFavorite(team.id)}
className=”text-slate-600 hover:text-amber-400 transition-colors”
>
<Star className={`w-4 h-4 ${isFav ? ‘text-amber-400 fill-amber-400’ : ”}`} />
</button>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
</div>
</div>
)}
{/* — TAB 3: CALENDARIO — */}
{activeTab === ‘matches’ && (
<div className=”space-y-6″>
<div className=”flex items-center justify-between”>
<h2 className=”text-xl font-bold text-slate-100 flex items-center space-x-2″>
<Calendar className=”w-5 h-5 text-emerald-400″ />
<span>Calendario Completo</span>
</h2>
</div>
{roundsList.map(roundNum => {
const roundMatches = matches.filter(m => m.round === roundNum);
return (
<div key={roundNum} className=”bg-slate-900 border border-slate-800 rounded-2xl p-4 space-y-3″>
<div className=”text-sm font-bold text-emerald-400 border-b border-slate-800 pb-2 flex justify-between items-center”>
<span>GIORNATA {roundNum}</span>
<span className=”text-xs text-slate-500 font-normal”>{roundMatches.length} Partite</span>
</div>
<div className=”grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3″>
{roundMatches.map(m => {
const home = getTeam(m.homeTeamId);
const away = getTeam(m.awayTeamId);
return (
<div key={m.id} className=”bg-slate-950 p-3 rounded-xl border border-slate-800/80 flex items-center justify-between”>
<div className=”flex items-center space-x-2 flex-1″>
<span>{home.logo}</span>
<span className=”text-xs font-semibold text-slate-200 truncate”>{home.name}</span>
</div>
<div className=”px-2 py-1 bg-slate-900 rounded font-black text-xs text-emerald-400 mx-2 min-w-[50px] text-center”>
{m.status === ‘UPCOMING’ ? ‘VS’ : `${m.homeScore} – ${m.awayScore}`}
</div>
<div className=”flex items-center space-x-2 flex-1 justify-end”>
<span className=”text-xs font-semibold text-slate-200 truncate”>{away.name}</span>
<span>{away.logo}</span>
</div>
</div>
);
})}
</div>
</div>
);
})}
</div>
)}
{/* — TAB 4: SQUADRE — */}
{activeTab === ‘teams’ && (
<div className=”space-y-6″>
<div className=”flex items-center justify-between”>
<h2 className=”text-xl font-bold text-slate-100 flex items-center space-x-2″>
<Shield className=”w-5 h-5 text-emerald-400″ />
<span>Squadre del Campionato</span>
</h2>
</div>
<div className=”grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4″>
{teams.map(t => {
const teamStats = standings.find(s => s.id === t.id) || {};
const isFav = favorites.includes(t.id);
return (
<div key={t.id} className=”bg-slate-900 border border-slate-800 rounded-2xl p-5 flex flex-col justify-between hover:border-slate-700 transition-all”>
<div>
<div className=”flex items-center justify-between mb-3″>
<span className=”text-4xl”>{t.logo}</span>
<button onClick={() => toggleFavorite(t.id)}>
<Star className={`w-5 h-5 ${isFav ? ‘text-amber-400 fill-amber-400’ : ‘text-slate-600’}`} />
</button>
</div>
<h3 className=”text-lg font-bold text-slate-100″>{t.name}</h3>
<p className=”text-xs text-slate-400 font-mono”>Codice: {t.code}</p>
</div>
<div className=”mt-4 pt-4 border-t border-slate-800 grid grid-cols-3 gap-2 text-center”>
<div className=”bg-slate-950 p-2 rounded-lg”>
<div className=”text-[10px] text-slate-500 font-bold uppercase”>Punti</div>
<div className=”text-base font-black text-emerald-400″>{teamStats.points || 0}</div>
</div>
<div className=”bg-slate-950 p-2 rounded-lg”>
<div className=”text-[10px] text-slate-500 font-bold uppercase”>Vittorie</div>
<div className=”text-base font-bold text-slate-200″>{teamStats.won || 0}</div>
</div>
<div className=”bg-slate-950 p-2 rounded-lg”>
<div className=”text-[10px] text-slate-500 font-bold uppercase”>Gol Diff</div>
<div className=”text-base font-bold text-slate-200″>{teamStats.gd || 0}</div>
</div>
</div>
</div>
);
})}
</div>
</div>
)}
{/* — TAB 5: PANNELLO ADMIN — */}
{activeTab === ‘admin’ && role === ‘admin’ && (
<div className=”space-y-8″>
<div className=”bg-amber-500/10 border border-amber-500/30 p-4 rounded-2xl flex items-center space-x-3″>
<ShieldAlert className=”w-6 h-6 text-amber-400 flex-shrink-0″ />
<div>
<h3 className=”font-bold text-amber-400 text-sm”>Modalità Amministratore Attiva</h3>
<p className=”text-xs text-slate-300″>
Da questo pannello puoi aggiungere squadre, aggiornare i punteggi in tempo reale e gestire i match live.
</p>
</div>
</div>
{/* Section 1: Add New Team */}
<div className=”bg-slate-900 border border-slate-800 rounded-2xl p-5 space-y-4″>
<h3 className=”font-bold text-slate-200 text-base flex items-center space-x-2″>
<Plus className=”w-4 h-4 text-emerald-400″ />
<span>Aggiungi Nuova Squadra</span>
</h3>
<form onSubmit={createTeam} className=”grid grid-cols-1 sm:grid-cols-4 gap-3″>
<input
type=”text”
placeholder=”Nome Squadra (es. Napoli)”
value={newTeamName}
onChange={(e) => setNewTeamName(e.target.value)}
className=”bg-slate-950 border border-slate-800 text-slate-100 px-3 py-2 rounded-xl text-sm focus:outline-none focus:border-emerald-500″
required
/>
<input
type=”text”
placeholder=”Codice 3 Lettere (es. NAP)”
value={newTeamCode}
maxLength={3}
onChange={(e) => setNewTeamCode(e.target.value)}
className=”bg-slate-950 border border-slate-800 text-slate-100 px-3 py-2 rounded-xl text-sm focus:outline-none focus:border-emerald-500 uppercase”
required
/>
<input
type=”text”
placeholder=”Emoji/Icona Logo (es. 🔵⚪)”
value={newTeamLogo}
onChange={(e) => setNewTeamLogo(e.target.value)}
className=”bg-slate-950 border border-slate-800 text-slate-100 px-3 py-2 rounded-xl text-sm focus:outline-none focus:border-emerald-500″
/>
<button
type=”submit”
className=”bg-emerald-500 hover:bg-emerald-400 text-slate-950 font-bold px-4 py-2 rounded-xl text-sm transition-all shadow-md”
>
Crea Squadra
</button>
</form>
</div>
{/* Section 2: Manage Matches */}
<div className=”bg-slate-900 border border-slate-800 rounded-2xl p-5 space-y-4″>
<h3 className=”font-bold text-slate-200 text-base flex items-center space-x-2″>
<Settings className=”w-4 h-4 text-emerald-400″ />
<span>Gestione Partite & Risultati Live</span>
</h3>
<div className=”space-y-3″>
{matches.map((m) => {
const home = getTeam(m.homeTeamId);
const away = getTeam(m.awayTeamId);
return (
<div key={m.id} className=”bg-slate-950 p-4 rounded-xl border border-slate-800 flex flex-wrap items-center justify-between gap-4″>
{/* Match Info */}
<div className=”flex items-center space-x-3″>
<span className=”text-xs bg-slate-900 px-2.5 py-1 rounded-md text-slate-400 font-bold”>
G{m.round}
</span>
<div className=”text-sm font-semibold text-slate-200″>
{home.name} <span className=”text-emerald-400 font-bold mx-1″>{m.homeScore} – {m.awayScore}</span> {away.name}
</div>
</div>
{/* Status Change Selector */}
<div className=”flex items-center space-x-2″>
<select
value={m.status}
onChange={(e) => updateMatchStatus(m.id, e.target.value)}
className=”bg-slate-900 border border-slate-800 text-xs text-slate-200 rounded-lg px-2.5 py-1.5 focus:outline-none”
>
<option value=”UPCOMING”>Da Giocare</option>
<option value=”LIVE”>LIVE (In corso)</option>
<option value=”HALF_TIME”>Intervallo</option>
<option value=”FINISHED”>Finita</option>
</select>
<button
onClick={() => setSelectedMatch(m)}
className=”px-3 py-1.5 bg-amber-500/20 hover:bg-amber-500/30 text-amber-400 rounded-lg text-xs font-semibold border border-amber-500/30″
>
Aggiungi Eventi
</button>
</div>
</div>
);
})}
</div>
</div>
</div>
)}
</main>
{/* — MATCH DETAIL & EVENT MODAL — */}
{selectedMatch && (
<div className=”fixed inset-0 z-50 bg-slate-950/80 backdrop-blur-sm flex items-center justify-center p-4″>
<div className=”bg-slate-900 border border-slate-800 rounded-3xl max-w-lg w-full overflow-hidden shadow-2xl space-y-4 p-6 relative”>
<button
onClick={() => setSelectedMatch(null)}
className=”absolute top-4 right-4 text-slate-400 hover:text-slate-100 bg-slate-800 p-2 rounded-full text-xs”
>
✕
</button>
<div className=”text-center space-y-1″>
<span className=”text-xs text-slate-400 font-semibold uppercase tracking-wider”>
Giornata {selectedMatch.round} • {selectedMatch.status}
</span>
<div className=”flex items-center justify-center space-x-4 my-3″>
<div className=”text-center”>
<div className=”text-4xl mb-1″>{getTeam(selectedMatch.homeTeamId).logo}</div>
<div className=”font-bold text-sm text-slate-200″>{getTeam(selectedMatch.homeTeamId).name}</div>
</div>
<div className=”px-4 py-2 bg-slate-950 rounded-2xl border border-slate-800 text-3xl font-black text-emerald-400″>
{selectedMatch.homeScore} – {selectedMatch.awayScore}
</div>
<div className=”text-center”>
<div className=”text-4xl mb-1″>{getTeam(selectedMatch.awayTeamId).logo}</div>
<div className=”font-bold text-sm text-slate-200″>{getTeam(selectedMatch.awayTeamId).name}</div>
</div>
</div>
</div>
{/* Events Timeline */}
<div className=”space-y-3 pt-2″>
<h4 className=”text-xs font-bold text-slate-400 uppercase tracking-wider border-b border-slate-800 pb-1″>
Cronaca Partita & Eventi
</h4>
<div className=”max-h-48 overflow-y-auto space-y-2 pr-1″>
{(!selectedMatch.events || selectedMatch.events.length === 0) ? (
<p className=”text-xs text-slate-500 text-center py-4″>Nessun evento ancora registrato.</p>
) : (
selectedMatch.events.map((ev) => {
const isHome = ev.teamId === selectedMatch.homeTeamId;
return (
<div
key={ev.id}
className={`flex items-center justify-between bg-slate-950 p-2.5 rounded-xl border border-slate-800 text-xs ${
isHome ? ‘border-l-4 border-l-emerald-500’ : ‘border-r-4 border-r-blue-500’
}`}
>
<div className=”flex items-center space-x-2″>
<span className=”font-mono font-bold text-amber-400″>{ev.minute}'</span>
<span>{ev.type === ‘GOAL’ ? ‘⚽ Gol’ : ‘🟨 Cartellino’}</span>
<span className=”font-semibold text-slate-200″>{ev.player}</span>
{ev.detail && <span className=”text-slate-500″>({ev.detail})</span>}
</div>
{role === ‘admin’ && (
<button
onClick={() => deleteMatchEvent(selectedMatch.id, ev.id)}
className=”text-red-400 hover:text-red-300 ml-2″
>
<Trash2 className=”w-3.5 h-3.5″ />
</button>
)}
</div>
);
})
)}
</div>
</div>
{/* Admin Add Event Section inside Modal */}
{role === ‘admin’ && (
<div className=”pt-3 border-t border-slate-800 space-y-2″>
<h5 className=”text-xs font-bold text-amber-400 uppercase”>Aggiungi Evento Live</h5>
<div className=”grid grid-cols-2 gap-2″>
<select
value={newEvent.teamId || selectedMatch.homeTeamId}
onChange={(e) => setNewEvent({ …newEvent, teamId: e.target.value })}
className=”bg-slate-950 border border-slate-800 text-xs rounded-lg p-2 text-slate-200″
>
<option value={selectedMatch.homeTeamId}>{getTeam(selectedMatch.homeTeamId).name}</option>
<option value={selectedMatch.awayTeamId}>{getTeam(selectedMatch.awayTeamId).name}</option>
</select>
<select
value={newEvent.type}
onChange={(e) => setNewEvent({ …newEvent, type: e.target.value })}
className=”bg-slate-950 border border-slate-800 text-xs rounded-lg p-2 text-slate-200″
>
<option value=”GOAL”>⚽ Gol</option>
<option value=”CARD_YELLOW”>🟨 Cartellino Giallo</option>
<option value=”CARD_RED”>🟥 Cartellino Rosso</option>
</select>
</div>
<div className=”grid grid-cols-3 gap-2″>
<input
type=”number”
placeholder=”Minuto”
value={newEvent.minute}
onChange={(e) => setNewEvent({ …newEvent, minute: Number(e.target.value) })}
className=”bg-slate-950 border border-slate-800 text-xs rounded-lg p-2 text-slate-200″
/>
<input
type=”text”
placeholder=”Giocatore”
value={newEvent.player}
onChange={(e) => setNewEvent({ …newEvent, player: e.target.value })}
className=”col-span-2 bg-slate-950 border border-slate-800 text-xs rounded-lg p-2 text-slate-200″
/>
</div>
<button
onClick={() => addMatchEvent(selectedMatch.id)}
className=”w-full bg-amber-500 hover:bg-amber-400 text-slate-950 font-bold py-2 rounded-xl text-xs transition-all mt-2″
>
Registra Evento
</button>
</div>
)}
<button
onClick={() => setSelectedMatch(null)}
className=”w-full py-2 bg-slate-800 hover:bg-slate-700 text-slate-200 text-xs font-bold rounded-xl”
>
Chiudi
</button>
</div>
</div>
)}
</div>
);
}


