<!
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Professional Cricket Stats Tracker</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
:root {
--primary-color: #1a73e8; --secondary-color: #5f6368; --bg-color:
#f1f3f4;
--text-color: #202124; --light-grey: #dadce0; --white: #ffffff;
--danger-color: #d93025; --success-color: #1e8e3e; --warning-color:
#ff9800;
}
body {
font-family: "Google Sans", -apple-system, BlinkMacSystemFont, "Segoe
UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background-color: var(--bg-color); color: var(--text-color);
line-height: 1.6; margin: 0; transition: background-color 0.3s, color
0.3s;
}
.header { background-color: var(--white); padding: 0 2rem; box-shadow: 0
2px 4px rgba(0,0,0,0.1); position: sticky; top: 0; z-index: 100; transition:
background-color 0.3s; }
.container { max-width: 1300px; margin: 0 auto; padding: 2rem; }
.card { background-color: var(--white); border-radius: 8px; box-shadow: 0
1px 2px 0 rgba(60,64,67,.3), 0 2px 6px 2px rgba(60,64,67,.15); padding: 1.5rem
2rem; margin-bottom: 2rem; transition: background-color 0.3s; }
h1, h2, h3 { color: var(--secondary-color); border-bottom: 1px solid var(--
light-grey); padding-bottom: 10px; margin-top: 0; transition: color 0.3s, border-
color 0.3s; }
h1 { text-align: center; border-bottom: none; margin: 1rem 0 2rem 0; color:
#3c4043; }
h3 { margin-top: 2rem; }
table { width: 100%; border-collapse: collapse; margin-top: 1rem; table-
layout: auto; }
th, td { border: 1px solid var(--light-grey); padding: 12px; text-align:
center; vertical-align: middle; transition: border-color 0.3s; }
td:first-child { text-align: left; }
thead th { background-color: #e9ecef; color: var(--secondary-color); font-
weight: 600; transition: background-color 0.3s, color 0.3s; }
tbody tr:nth-child(even) { background-color: #f8f9fa; }
.form-grid { display: grid; grid-template-columns: repeat(auto-fit,
minmax(200px, 1fr)); gap: 1rem; }
input, select, button { width: 100%; padding: 12px; border-radius: 5px;
border: 1px solid #ced4da; box-sizing: border-box; font-size: 1rem; }
button { color: var(--white); background-color: var(--primary-color);
border: none; cursor: pointer; font-weight: 600; transition: background-color 0.2s,
box-shadow 0.2s; }
.tab-nav { display: flex; flex-wrap: nowrap; overflow-x: auto; border-
bottom: 1px solid var(--light-grey); margin: 0 auto; max-width: 1300px; }
.tab-button { background: none; border: none; color: var(--secondary-
color); padding: 1rem 1.5rem; font-size: 1.1rem; font-weight: 500; cursor: pointer;
border-bottom: 3px solid transparent; margin-bottom: -1px; white-space: nowrap; }
.tab-button.active { color: var(--primary-color); border-bottom-color:
var(--primary-color); font-weight: 600; }
.tab-pane { display: none; } .tab-pane.active { display: block; }
.settings-actions { display: flex; flex-direction: column; gap: 1rem; max-
width: 400px; margin-top: 1.5rem;}
.stats-header { display: flex; align-items: center; gap: 1.5rem; flex-wrap:
wrap; }
.stats-header img, #playerListTable img { width: 80px; height: 80px;
border-radius: 50%; object-fit: cover; background-color: var(--light-grey); }
.player-bio { display: flex; flex-direction: column; }
.player-bio p { margin: 2px 0; font-size: 0.9rem; }
.stats-filter, .h2h-filters-container { margin: 1.5rem 0; display: flex;
flex-wrap: wrap; gap: 1.5rem; align-items: center; }
.stats-filter > div, .h2h-filters > div { display: flex; gap: 0.5rem;
align-items: center;}
.stats-filter label, .h2h-filters label { font-weight: bold; }
.stats-tables-grid { display: grid; grid-template-columns: repeat(auto-fit,
minmax(350px, 1fr)); gap: 1rem; }
/* Redesigned Performance Row */
.performance-row { border: 1px solid var(--light-grey); border-radius: 8px;
padding: 1rem; margin-top: 1rem; }
.performance-row-header { display: flex; align-items: center; gap: 1rem;
border-bottom: 1px solid var(--light-grey); padding-bottom: 1rem; margin-bottom:
1rem; }
.performance-row-header select { flex-grow: 1; }
.perf-stats-grid { display: grid; grid-template-columns: repeat(auto-fit,
minmax(250px, 1fr)); gap: 1.5rem; }
.perf-group h4 { margin: 0 0 0.5rem 0; font-size: 1rem; color: var(--
primary-color); border-bottom: none; }
.perf-group .input-pair { display: grid; grid-template-columns: 80px 1fr;
gap: 0.5rem; align-items: center; margin-bottom: 0.5rem;}
.perf-group .input-pair label { font-size: 0.85rem; text-align: right; }
.dnb-group { display: flex; align-items: center; justify-content: center;
gap: 0.5rem; }
.dnb-group label { font-size: 0.8rem; }
/* Dark Mode */
body.dark-mode { --bg-color: #202124; --text-color: #e8eaed; --secondary-
color: #bdc1c6; --light-grey: #3c4043; --white: #28292d; }
body.dark-mode thead th { background-color: #3c4043; }
body.dark-mode tbody tr:nth-child(even) { background-color: #2a2a2a; }
body.dark-mode input, body.dark-mode select { background-color: #3c4043;
border-color: #5f6368; color: var(--text-color); }
</style>
</head>
<body>
<header class="header">
<nav class="tab-nav">
<button class="tab-button active" data-tab="home">Home</button>
<button class="tab-button" data-tab="log-match">Log Match</button>
<button class="tab-button" data-tab="h2h-stats">H2H Stats</button>
<button class="tab-button" data-tab="match-history">Match
History</button>
<button class="tab-button" data-tab="settings">Settings</button>
</nav>
</header>
<main class="container">
<div id="home" class="tab-pane active"><div class="card"><h2>Player
Roster</h2><form id="addPlayerForm" class="form-grid"><input type="text"
id="playerName" placeholder="Player Name" required><input type="date"
id="playerDob" title="Date of Birth"><input type="text" id="playerRole"
placeholder="Playing Role"><input type="text" id="battingStyle"
placeholder="Batting Style"><input type="text" id="bowlingStyle"
placeholder="Bowling Style"><input type="url" id="playerAvatar" placeholder="Avatar
Image URL"></form><button type="submit" form="addPlayerForm" class="btn-success"
style="margin-top: 1rem;">Add Player</button><table
id="playerListTable"><thead><tr><th>Avatar</th><th>Name</th><th>Role</
th><th>Actions</th></tr></thead><tbody></tbody></table></div></div>
<div id="log-match" class="tab-pane"><div class="card"><h2>Log a New
Match</h2><form id="logMatchForm"><div class="form-grid"><input type="date"
id="matchDate" required><input type="text" id="matchOpponent" placeholder="Opponent
Name"><select id="matchFormat" required><option value="">-- Select Format
--</option><option value="Test">Test</option><option
value="ODI">ODI</option><option value="T20">T20</option><option value="10 Overs">10
Overs</option><option value="5 Overs">5 Overs</option><option
value="Other">Other</option></select><select id="manOfTheMatch"><option value="">--
Man of the Match --</option></select></div><h3 style="margin-top:2rem">Player
Performances</h3><div id="performanceEntries"></div><button type="button"
id="addPerformanceBtn" style="margin-top:1rem">Add Player
Performance</button><button type="submit" class="btn-success" style="margin-
top:1rem;float:right">Save Match</button></form></div></div>
<div id="h2h-stats" class="tab-pane"><div class="card"><h3>Select Players
and Filters for Comparison</h3><div class="h2h-filters-container"><div class="h2h-
filters"><div><label>Player A:</label><select
id="h2hPlayerA"></select></div><div><label>Player B:</label><select
id="h2hPlayerB"></select></div></div><div class="h2h-
filters"><div><label>Format:</label><select
id="h2hFormatFilter"></select></div><div><label>Year:</label><select
id="h2hYearFilter"></select></div></div></div><button id="compareBtn"
style="margin-top:1rem; max-width: 200px;">Compare Careers</button><div
id="h2hResults" style="margin-top: 2rem;"></div></div></div>
<div id="match-history" class="tab-pane"><div class="card"><h2>Match
History</h2><table
id="matchHistoryTable"><thead><tr><th>Date</th><th>Format</th><th>Opponent</
th><th>Players</th><th>MOTM</th><th>Actions</th></tr></thead><tbody></tbody></
table></div></div>
<div id="settings" class="tab-pane"><div
class="card"><h2>Settings</h2><h3>Data Management</h3><div class="settings-
actions"><button id="exportDataBtn" class="btn-warning">Export Data</button><button
id="importDataBtn" class="btn-warning">Import Data</button><input type="file"
id="importFile" accept=".json" style="display:none"></div><h3 style="margin-top:
2rem;">Appearance</h3><div class="settings-actions"><button
id="darkModeToggle">Toggle Dark Mode</button></div><h3 style="margin-top:
2rem;">Archive (Trash)</h3><p>Items here are deleted permanently after 3
days.</p><table id="archiveTable"><thead><tr><th>Item</th><th>Type</th><th>Deleted
On</th><th>Actions</th></tr></thead><tbody></tbody></table></div></div>
<div id="career-stats" class="tab-pane">
<div class="card">
<div class="stats-header">
<img id="statsAvatar"
src="https://www.gstatic.com/images/branding/product/1x/avatar_circle_grey_512dp.pn
g" alt="Player Avatar">
<div>
<h2 id="statsPlayerName" style="border-bottom:none; margin:
0;"></h2>
<div class="player-bio">
<p id="statsPlayerAge"></p><p id="statsPlayerRole"></p>
<p id="statsPlayerBatting"></p><p
id="statsPlayerBowling"></p>
</div>
</div>
</div>
<div class="stats-filter">
<div><label for="formatFilter">Format:</label><select
id="formatFilter"></select></div>
<div><label for="yearFilter">Year:</label><select
id="yearFilter"></select></div>
</div>
<h3>Batting</h3>
<table><thead><tr><th>M</th><th>I</th><th>NO</th><th>Runs</th><th>HS</th><th>Avg</
th><th>BF</th><th>SR</th></tr></thead>
<tbody><tr><td id="c_m_bat">-</td><td id="c_i_bat">-</td><td
id="c_no">-</td><td id="c_runs">-</td><td id="c_hs">-</td><td
id="c_avg_bat">-</td><td id="c_bf">-</td><td
id="c_sr_bat">-</td></tr></tbody></table>
<div class="stats-tables-grid">
<div><h3>Milestones &
Awards</h3><table><thead><tr><th>100s</th><th>50s</th><th>5W</th><th>MOTM</th></
tr></thead><tbody><tr><td id="c_100s">-</td><td id="c_50s">-</td><td
id="c_5w">-</td><td id="c_motm">-</td></tr></tbody></table></div>
<div><h3>Fielding</h3><table><thead><tr><th>Catches</th><th>Stumpings</th><th>Run
Outs</th></tr></thead><tbody><tr><td id="c_ct">-</td><td id="c_st">-</td><td
id="c_ro">-</td></tr></tbody></table></div>
</div>
<h3>Bowling</h3>
<table><thead><tr><th>M</th><th>I</th><th>Overs</th><th>Balls</th><th>Runs</
th><th>Wkts</th><th>BBI</th><th>Avg</th><th>Econ</th><th>SR</th></tr></thead>
<tbody><tr><td id="c_m_bowl">-</td><td id="c_i_bowl">-</td><td
id="c_overs">-</td><td id="c_balls_bowl">-</td><td id="c_runs_con">-</td><td
id="c_wkts">-</td><td id="c_bbi">-</td><td id="c_avg_bowl">-</td><td id="c_econ">-
</td><td id="c_sr_bowl">-</td></tr></tbody></table>
<h3 style="margin-top: 2rem;">Runs per Innings Chart</h3>
<canvas id="runsChart"></canvas>
</div>
</div>
</main>
<script>
document.addEventListener('DOMContentLoaded', () => {
// --- DOM Elements ---
const tabNav = document.querySelector('.tab-nav');
const tabPanes = document.querySelectorAll('.tab-pane');
const addPlayerForm = document.getElementById('addPlayerForm');
const playerListTableBody = document.querySelector('#playerListTable tbody');
const logMatchForm = document.getElementById('logMatchForm');
const addPerformanceBtn = document.getElementById('addPerformanceBtn');
const performanceEntries = document.getElementById('performanceEntries');
const manOfTheMatchSelect = document.getElementById('manOfTheMatch');
const formatFilterSelect = document.getElementById('formatFilter');
const yearFilterSelect = document.getElementById('yearFilter');
const matchHistoryTableBody = document.querySelector('#matchHistoryTable
tbody');
const archiveTableBody = document.querySelector('#archiveTable tbody');
const exportDataBtn = document.getElementById('exportDataBtn');
const importDataBtn = document.getElementById('importDataBtn');
const importFile = document.getElementById('importFile');
const darkModeToggle = document.getElementById('darkModeToggle');
const h2hPlayerASelect = document.getElementById('h2hPlayerA');
const h2hPlayerBSelect = document.getElementById('h2hPlayerB');
const h2hFormatFilter = document.getElementById('h2hFormatFilter');
const h2hYearFilter = document.getElementById('h2hYearFilter');
const compareBtn = document.getElementById('compareBtn');
const h2hResultsDiv = document.getElementById('h2hResults');
const runsChartCanvas = document.getElementById('runsChart');
let runsChart;
// --- App State ---
let db = { players: [], matches: [], trash: [] };
let currentStatsPlayerId = null;
// --- INITIALIZATION ---
loadData();
if (localStorage.getItem('darkMode') === 'enabled')
document.body.classList.add('dark-mode');
// --- EVENT LISTENERS ---
tabNav.addEventListener('click', handleTabSwitch);
addPlayerForm.addEventListener('submit', handleAddPlayer);
playerListTableBody.addEventListener('click', handlePlayerListClick);
addPerformanceBtn.addEventListener('click', addPerformanceRow);
logMatchForm.addEventListener('submit', handleLogMatch);
formatFilterSelect.addEventListener('change', handleFilterChange);
yearFilterSelect.addEventListener('change', handleFilterChange);
matchHistoryTableBody.addEventListener('click', handleMatchHistoryClick);
archiveTableBody.addEventListener('click', handleArchiveClick);
exportDataBtn.addEventListener('click', exportData);
importDataBtn.addEventListener('click', () => importFile.click());
importFile.addEventListener('change', importData);
darkModeToggle.addEventListener('click', toggleDarkMode);
compareBtn.addEventListener('click', displayH2HStats);
// --- TAB SWITCHING ---
function switchTab(targetTabId) {
if (targetTabId === 'h2h-stats') populateH2HDropdowns();
if (targetTabId === 'log-match') {
populateMOTMSelect();
if (performanceEntries.querySelectorAll('.performance-row').length ===
0) addPerformanceRow();
}
tabPanes.forEach(pane => pane.classList.remove('active'));
document.getElementById(targetTabId)?.classList.add('active');
tabNav.querySelectorAll('.tab-button').forEach(btn =>
btn.classList.remove('active'));
const targetButton = tabNav.querySelector(`[data-tab='${targetTabId}']`);
if (targetButton) targetButton.classList.add('active');
}
function handleTabSwitch(e) {
const tabButton = e.target.closest('.tab-button');
if (!tabButton) return;
switchTab(tabButton.dataset.tab);
}
// --- DARK MODE ---
function toggleDarkMode() {
document.body.classList.toggle('dark-mode');
localStorage.setItem('darkMode', document.body.classList.contains('dark-
mode') ? 'enabled' : 'disabled');
}
// --- DATA HANDLING ---
function saveData() { localStorage.setItem('cricketAppDb', JSON.stringify(db));
}
function loadData() {
const storedDb = localStorage.getItem('cricketAppDb');
if (storedDb) db = JSON.parse(storedDb);
if (!db.trash) db.trash = [];
purgeOldTrash();
renderAll();
}
function renderAll() {
renderPlayerList();
renderMatchHistory();
renderArchive();
updateAllPlayerDropdowns();
}
function exportData() {
if (db.players.length === 0 && db.matches.length === 0) return alert("No
data to export.");
const dataStr = JSON.stringify(db, null, 2);
const dataBlob = new Blob([dataStr], {type: "application/json"});
const url = URL.createObjectURL(dataBlob);
const link = document.createElement('a');
link.href = url;
link.download = `cricket_stats_backup_${new
Date().toISOString().slice(0,10)}.json`;
link.click();
URL.revokeObjectURL(url);
}
function importData(event) {
const file = event.target.files[0]; if (!file) return;
const reader = new FileReader();
reader.onload = function(e) {
try {
const importedDb = JSON.parse(e.target.result);
if (importedDb && Array.isArray(importedDb.players) &&
Array.isArray(importedDb.matches)) {
if (confirm("This will overwrite all current data. Are you
sure?")) {
db = importedDb;
if(!db.trash) db.trash = [];
saveData();
renderAll();
switchTab('home');
alert("Data imported successfully!");
}
} else { alert("Invalid data file format."); }
} catch (error) { alert("Error reading file."); }
};
reader.readAsText(file);
event.target.value = '';
}
// --- PLAYER MANAGEMENT ---
function handleAddPlayer(e) {
e.preventDefault();
db.players.push({
id: Date.now(), name:
document.getElementById('playerName').value.trim() || 'Unnamed Player',
role: document.getElementById('playerRole').value.trim(),
battingStyle: document.getElementById('battingStyle').value.trim(),
bowlingStyle: document.getElementById('bowlingStyle').value.trim(),
avatarUrl: document.getElementById('playerAvatar').value.trim(),
dob: addPlayerForm.querySelector('#playerDob').value
});
saveData();
renderPlayerList();
addPlayerForm.reset();
updateAllPlayerDropdowns();
}
function renderPlayerList() {
playerListTableBody.innerHTML = '';
const defaultAvatar =
'https://www.gstatic.com/images/branding/product/1x/avatar_circle_grey_512dp.png';
db.players.forEach(player => {
const row = document.createElement('tr');
row.innerHTML = `<td><img src="${player.avatarUrl || defaultAvatar}"
alt="Avatar"></td>
<td>${player.name}</td><td>${player.role || '-'}</td>
<td><button class="btn-small" data-player-id="${player.id}" data-
action="view-stats">View Stats</button>
<button class="btn-small btn-danger" data-player-id="$
{player.id}" data-action="delete-player">Delete</button></td>`;
playerListTableBody.appendChild(row);
});
}
function handlePlayerListClick(e) {
const targetButton = e.target.closest('button');
if (!targetButton) return;
const { action, playerId } = targetButton.dataset;
if (action === 'view-stats') openStatsPage(parseInt(playerId));
else if (action === 'delete-player') if (confirm('Are you sure? This will
move the player to the Archive.')) deleteToArchive('player', parseInt(play