ffffffff

0
5

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>

);

}

No comments

Leave a reply

Please enter your comment!
Please enter your name here