Files
catdoku/leaderboard.html

405 lines
11 KiB
HTML

<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CatDoku - Classifica</title>
<link rel="stylesheet" href="style.css">
<style>
.leaderboard-container {
max-width: 800px;
width: 100%;
padding: 20px;
}
.leaderboard-header {
text-align: center;
margin-bottom: 30px;
}
.leaderboard-header h1 {
font-size: 36px;
color: #5d4e37;
margin-bottom: 10px;
}
.leaderboard-header p {
color: #8b7355;
font-size: 16px;
}
.back-btn {
display: inline-block;
padding: 10px 20px;
background: #5d4e37;
color: #fff;
text-decoration: none;
border-radius: 10px;
margin-bottom: 20px;
transition: background 0.2s;
}
.back-btn:hover {
background: #7a6750;
}
.filter-section {
display: flex;
gap: 15px;
margin-bottom: 20px;
flex-wrap: wrap;
justify-content: center;
}
.filter-section label {
font-size: 14px;
color: #8b7355;
font-weight: 500;
}
.filter-section select,
.filter-section input {
padding: 8px 12px;
border-radius: 8px;
border: 2px solid #d4c5b0;
font-size: 14px;
color: #5d4e37;
background: #fff;
}
.leaderboard-table {
width: 100%;
background: #fff;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 16px rgba(0,0,0,0.1);
}
.leaderboard-table table {
width: 100%;
border-collapse: collapse;
}
.leaderboard-table th {
background: #5d4e37;
color: #fff;
padding: 15px 10px;
text-align: left;
font-size: 14px;
font-weight: 600;
}
.leaderboard-table td {
padding: 12px 10px;
border-bottom: 1px solid #f0e8dc;
font-size: 14px;
color: #5d4e37;
}
.leaderboard-table tr:last-child td {
border-bottom: none;
}
.leaderboard-table tr:hover td {
background: #f8f4ff;
}
.rank {
font-weight: 700;
font-size: 18px;
}
.rank-1 { color: #d4af37; }
.rank-2 { color: #c0c0c0; }
.rank-3 { color: #cd7f32; }
.player-name-cell {
font-weight: 600;
color: #4a90d9;
}
.time-cell {
font-weight: 600;
font-family: monospace;
font-size: 16px;
}
.score-cell {
font-weight: 700;
color: #e74c3c;
}
.empty-state {
text-align: center;
padding: 60px 20px;
color: #8b7355;
}
.empty-state-icon {
font-size: 60px;
margin-bottom: 20px;
}
.stats-summary {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 15px;
margin-bottom: 30px;
}
.stat-card {
background: #fff;
padding: 20px;
border-radius: 12px;
text-align: center;
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
}
.stat-card-value {
font-size: 28px;
font-weight: 700;
color: #5d4e37;
}
.stat-card-label {
font-size: 12px;
color: #8b7355;
margin-top: 5px;
}
.clear-btn {
padding: 8px 16px;
background: #e74c3c;
color: #fff;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
transition: background 0.2s;
}
.clear-btn:hover {
background: #c0392b;
}
</style>
</head>
<body>
<div id="app">
<div class="leaderboard-container">
<a href="index.html" class="back-btn">&#8592; Torna al Gioco</a>
<div class="leaderboard-header">
<h1>&#127942; Classifica CatDoku</h1>
<p>I migliori tempi e punteggi di tutti i giocatori</p>
</div>
<div class="stats-summary" id="stats-summary"></div>
<div class="filter-section">
<div>
<label for="filter-player">Giocatore:</label>
<select id="filter-player">
<option value="">Tutti i giocatori</option>
</select>
</div>
<div>
<label for="filter-level">Livello:</label>
<select id="filter-level">
<option value="">Tutti i livelli</option>
</select>
</div>
<div>
<label for="filter-size">Dimensione:</label>
<select id="filter-size">
<option value="">Tutte le dimensioni</option>
</select>
</div>
<button class="clear-btn" id="clear-records">Cancella Tutto</button>
</div>
<div class="leaderboard-table" id="leaderboard-table"></div>
</div>
</div>
<script>
(function() {
'use strict';
const STORAGE_KEY = 'catdoku_records';
function loadRecords() {
return JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
}
function saveRecords(records) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(records));
}
function formatTime(seconds) {
const mins = Math.floor(seconds / 60);
const secs = seconds % 60;
return `${mins}:${secs.toString().padStart(2, '0')}`;
}
function formatDate(dateStr) {
const date = new Date(dateStr);
return date.toLocaleDateString('it-IT', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
}
function getUniquePlayers(records) {
return [...new Set(records.map(r => r.playerName))].sort();
}
function getUniqueLevels(records) {
return [...new Set(records.map(r => r.level))].sort((a, b) => a - b);
}
function getUniqueSizes(records) {
return [...new Set(records.map(r => r.gridSize))].sort((a, b) => a - b);
}
function renderStats(records) {
const statsContainer = document.getElementById('stats-summary');
const totalGames = records.length;
const uniquePlayers = getUniquePlayers(records).length;
const bestTime = records.length > 0 ? Math.min(...records.map(r => r.time)) : 0;
const bestScore = records.length > 0 ? Math.max(...records.map(r => r.score)) : 0;
statsContainer.innerHTML = `
<div class="stat-card">
<div class="stat-card-value">${totalGames}</div>
<div class="stat-card-label">Partite Giocate</div>
</div>
<div class="stat-card">
<div class="stat-card-value">${uniquePlayers}</div>
<div class="stat-card-label">Giocatori</div>
</div>
<div class="stat-card">
<div class="stat-card-value">${formatTime(bestTime)}</div>
<div class="stat-card-label">Miglior Tempo</div>
</div>
<div class="stat-card">
<div class="stat-card-value">${bestScore}</div>
<div class="stat-card-label">Miglior Punteggio</div>
</div>
`;
}
function renderFilters(records) {
const playerSelect = document.getElementById('filter-player');
const levelSelect = document.getElementById('filter-level');
const sizeSelect = document.getElementById('filter-size');
const players = getUniquePlayers(records);
const levels = getUniqueLevels(records);
const sizes = getUniqueSizes(records);
playerSelect.innerHTML = '<option value="">Tutti i giocatori</option>' +
players.map(p => `<option value="${p}">${p}</option>`).join('');
levelSelect.innerHTML = '<option value="">Tutti i livelli</option>' +
levels.map(l => `<option value="${l}">Livello ${l}</option>`).join('');
sizeSelect.innerHTML = '<option value="">Tutte le dimensioni</option>' +
sizes.map(s => `<option value="${s}">${s}x${s}</option>`).join('');
}
function renderLeaderboard(records) {
const container = document.getElementById('leaderboard-table');
if (records.length === 0) {
container.innerHTML = `
<div class="empty-state">
<div class="empty-state-icon">&#128049;</div>
<h3>Nessuna partita registrata</h3>
<p>Completa un livello per apparire in classifica!</p>
</div>
`;
return;
}
// Ordina per tempo (crescente)
const sorted = [...records].sort((a, b) => a.time - b.time);
let html = '<table><thead><tr>';
html += '<th>#</th>';
html += '<th>Giocatore</th>';
html += '<th>Livello</th>';
html += '<th>Dimensione</th>';
html += '<th>Tempo</th>';
html += '<th>Punteggio</th>';
html += '<th>Errori</th>';
html += '<th>Data</th>';
html += '</tr></thead><tbody>';
sorted.forEach((record, index) => {
const rank = index + 1;
const rankClass = rank <= 3 ? `rank rank-${rank}` : 'rank';
html += '<tr>';
html += `<td class="${rankClass}">${rank <= 3 ? ['&#129351;', '&#129352;', '&#129353;'][rank-1] : rank}</td>`;
html += `<td class="player-name-cell">${record.playerName}</td>`;
html += `<td>${record.level}</td>`;
html += `<td>${record.gridSize}x${record.gridSize}</td>`;
html += `<td class="time-cell">${formatTime(record.time)}</td>`;
html += `<td class="score-cell">${record.score}</td>`;
html += `<td>${record.errors}</td>`;
html += `<td>${formatDate(record.date)}</td>`;
html += '</tr>';
});
html += '</tbody></table>';
container.innerHTML = html;
}
function applyFilters() {
const records = loadRecords();
const playerFilter = document.getElementById('filter-player').value;
const levelFilter = document.getElementById('filter-level').value;
const sizeFilter = document.getElementById('filter-size').value;
let filtered = records;
if (playerFilter) {
filtered = filtered.filter(r => r.playerName === playerFilter);
}
if (levelFilter) {
filtered = filtered.filter(r => r.level === parseInt(levelFilter));
}
if (sizeFilter) {
filtered = filtered.filter(r => r.gridSize === parseInt(sizeFilter));
}
renderStats(filtered);
renderLeaderboard(filtered);
}
// Event listeners
document.getElementById('filter-player').addEventListener('change', applyFilters);
document.getElementById('filter-level').addEventListener('change', applyFilters);
document.getElementById('filter-size').addEventListener('change', applyFilters);
document.getElementById('clear-records').addEventListener('click', () => {
if (confirm('Sei sicuro di voler cancellare tutti i record? Questa azione non può essere annullata.')) {
saveRecords([]);
applyFilters();
}
});
// Init
const records = loadRecords();
renderStats(records);
renderFilters(records);
renderLeaderboard(records);
})();
</script>
</body>
</html>