Spaces:
Running
Running
connect the code to my php
Browse files$host = 'localhost';
$user = 'root';
$pass = '';
$db = 'code_typing_game';
- components/header.js +3 -4
- db.php +60 -0
- leaderboard.html +85 -0
- script.js +44 -0
components/header.js
CHANGED
|
@@ -30,10 +30,9 @@ class CustomHeader extends HTMLElement {
|
|
| 30 |
CodeFlow
|
| 31 |
</a>
|
| 32 |
<nav class="hidden md:flex space-x-6">
|
| 33 |
-
<a href="
|
| 34 |
-
<a href="
|
| 35 |
-
|
| 36 |
-
</nav>
|
| 37 |
<button class="md:hidden">
|
| 38 |
<i data-feather="menu"></i>
|
| 39 |
</button>
|
|
|
|
| 30 |
CodeFlow
|
| 31 |
</a>
|
| 32 |
<nav class="hidden md:flex space-x-6">
|
| 33 |
+
<a href="/" class="nav-link">Home</a>
|
| 34 |
+
<a href="/leaderboard.html" class="nav-link">Leaderboard</a>
|
| 35 |
+
</nav>
|
|
|
|
| 36 |
<button class="md:hidden">
|
| 37 |
<i data-feather="menu"></i>
|
| 38 |
</button>
|
db.php
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```php
|
| 2 |
+
<?php
|
| 3 |
+
header('Content-Type: application/json');
|
| 4 |
+
header('Access-Control-Allow-Origin: *');
|
| 5 |
+
header('Access-Control-Allow-Methods: POST');
|
| 6 |
+
header('Access-Control-Allow-Headers: Content-Type');
|
| 7 |
+
|
| 8 |
+
$host = 'localhost';
|
| 9 |
+
$user = 'root';
|
| 10 |
+
$pass = '';
|
| 11 |
+
$db = 'code_typing_game';
|
| 12 |
+
|
| 13 |
+
// Create connection
|
| 14 |
+
$conn = new mysqli($host, $user, $pass, $db);
|
| 15 |
+
|
| 16 |
+
// Check connection
|
| 17 |
+
if ($conn->connect_error) {
|
| 18 |
+
die(json_encode(['success' => false, 'error' => 'Connection failed: ' . $conn->connect_error]));
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
// Handle POST request to save results
|
| 22 |
+
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
| 23 |
+
$data = json_decode(file_get_contents('php://input'), true);
|
| 24 |
+
|
| 25 |
+
$language = $conn->real_escape_string($data['language'] ?? '');
|
| 26 |
+
$wpm = intval($data['wpm'] ?? 0);
|
| 27 |
+
$accuracy = intval($data['accuracy'] ?? 0);
|
| 28 |
+
$time = intval($data['time'] ?? 0);
|
| 29 |
+
$timestamp = date('Y-m-d H:i:s');
|
| 30 |
+
|
| 31 |
+
$sql = "INSERT INTO results (language, wpm, accuracy, time_taken, created_at)
|
| 32 |
+
VALUES ('$language', $wpm, $accuracy, $time, '$timestamp')";
|
| 33 |
+
|
| 34 |
+
if ($conn->query($sql) === TRUE) {
|
| 35 |
+
echo json_encode(['success' => true]);
|
| 36 |
+
} else {
|
| 37 |
+
echo json_encode(['success' => false, 'error' => $conn->error]);
|
| 38 |
+
}
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
// Handle GET request to fetch leaderboard
|
| 42 |
+
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
| 43 |
+
$sql = "SELECT * FROM results ORDER BY wpm DESC LIMIT 10";
|
| 44 |
+
$result = $conn->query($sql);
|
| 45 |
+
|
| 46 |
+
$leaderboard = [];
|
| 47 |
+
if ($result->num_rows > 0) {
|
| 48 |
+
while($row = $result->fetch_assoc()) {
|
| 49 |
+
$leaderboard[] = $row;
|
| 50 |
+
}
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
echo json_encode(['success' => true, 'data' => $leaderboard]);
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
$conn->close();
|
| 57 |
+
?>
|
| 58 |
+
```
|
| 59 |
+
|
| 60 |
+
Now let's update the JavaScript to save results to the database:
|
leaderboard.html
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Leaderboard - CodeFlow Typing Challenge</title>
|
| 7 |
+
<link rel="stylesheet" href="style.css">
|
| 8 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 9 |
+
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
|
| 10 |
+
</head>
|
| 11 |
+
<body class="bg-gray-900 text-gray-100 min-h-screen">
|
| 12 |
+
<custom-header></custom-header>
|
| 13 |
+
<main class="container mx-auto px-4 py-8 max-w-4xl">
|
| 14 |
+
<div class="text-center mb-12">
|
| 15 |
+
<h1 class="text-4xl font-bold mb-2 text-indigo-400">Leaderboard</h1>
|
| 16 |
+
<p class="text-lg text-gray-400">Top typing speeds from our community</p>
|
| 17 |
+
</div>
|
| 18 |
+
|
| 19 |
+
<div class="bg-gray-800 rounded-xl p-6 shadow-lg mb-8">
|
| 20 |
+
<div id="leaderboard-container" class="overflow-x-auto">
|
| 21 |
+
<table class="min-w-full divide-y divide-gray-700">
|
| 22 |
+
<thead>
|
| 23 |
+
<tr>
|
| 24 |
+
<th class="px-6 py-3 text-left text-xs font-medium text-indigo-300 uppercase tracking-wider">Rank</th>
|
| 25 |
+
<th class="px-6 py-3 text-left text-xs font-medium text-indigo-300 uppercase tracking-wider">Language</th>
|
| 26 |
+
<th class="px-6 py-3 text-left text-xs font-medium text-indigo-300 uppercase tracking-wider">WPM</th>
|
| 27 |
+
<th class="px-6 py-3 text-left text-xs font-medium text-indigo-300 uppercase tracking-wider">Accuracy</th>
|
| 28 |
+
<th class="px-6 py-3 text-left text-xs font-medium text-indigo-300 uppercase tracking-wider">Time</th>
|
| 29 |
+
<th class="px-6 py-3 text-left text-xs font-medium text-indigo-300 uppercase tracking-wider">Date</th>
|
| 30 |
+
</tr>
|
| 31 |
+
</thead>
|
| 32 |
+
<tbody id="leaderboard-body" class="divide-y divide-gray-700">
|
| 33 |
+
<!-- Leaderboard data will be inserted here -->
|
| 34 |
+
</tbody>
|
| 35 |
+
</table>
|
| 36 |
+
</div>
|
| 37 |
+
</div>
|
| 38 |
+
</main>
|
| 39 |
+
|
| 40 |
+
<custom-footer></custom-footer>
|
| 41 |
+
|
| 42 |
+
<script src="components/header.js"></script>
|
| 43 |
+
<script src="components/footer.js"></script>
|
| 44 |
+
<script>
|
| 45 |
+
feather.replace();
|
| 46 |
+
|
| 47 |
+
// Fetch and display leaderboard
|
| 48 |
+
async function loadLeaderboard() {
|
| 49 |
+
const leaderboard = await fetchLeaderboard();
|
| 50 |
+
const tbody = document.getElementById('leaderboard-body');
|
| 51 |
+
tbody.innerHTML = '';
|
| 52 |
+
|
| 53 |
+
leaderboard.forEach((entry, index) => {
|
| 54 |
+
const row = document.createElement('tr');
|
| 55 |
+
row.innerHTML = `
|
| 56 |
+
<td class="px-6 py-4 whitespace-nowrap">${index + 1}</td>
|
| 57 |
+
<td class="px-6 py-4 whitespace-nowrap">${entry.language.toUpperCase()}</td>
|
| 58 |
+
<td class="px-6 py-4 whitespace-nowrap">${entry.wpm}</td>
|
| 59 |
+
<td class="px-6 py-4 whitespace-nowrap">${entry.accuracy}%</td>
|
| 60 |
+
<td class="px-6 py-4 whitespace-nowrap">${entry.time_taken}s</td>
|
| 61 |
+
<td class="px-6 py-4 whitespace-nowrap">${new Date(entry.created_at).toLocaleString()}</td>
|
| 62 |
+
`;
|
| 63 |
+
tbody.appendChild(row);
|
| 64 |
+
});
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
// Call fetchLeaderboard from script.js
|
| 68 |
+
async function fetchLeaderboard() {
|
| 69 |
+
try {
|
| 70 |
+
const response = await fetch('db.php');
|
| 71 |
+
const data = await response.json();
|
| 72 |
+
if (data.success) {
|
| 73 |
+
return data.data;
|
| 74 |
+
}
|
| 75 |
+
return [];
|
| 76 |
+
} catch (error) {
|
| 77 |
+
console.error('Error fetching leaderboard:', error);
|
| 78 |
+
return [];
|
| 79 |
+
}
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
document.addEventListener('DOMContentLoaded', loadLeaderboard);
|
| 83 |
+
</script>
|
| 84 |
+
</body>
|
| 85 |
+
</html>
|
script.js
CHANGED
|
@@ -249,6 +249,50 @@ function endGame(isCompleted = false) {
|
|
| 249 |
finalAccuracy.textContent = `${accuracy}%`;
|
| 250 |
finalTime.textContent = `${timeTaken}s`;
|
| 251 |
resultsSection.classList.remove('hidden');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 252 |
}
|
| 253 |
}
|
| 254 |
// Event listeners
|
|
|
|
| 249 |
finalAccuracy.textContent = `${accuracy}%`;
|
| 250 |
finalTime.textContent = `${timeTaken}s`;
|
| 251 |
resultsSection.classList.remove('hidden');
|
| 252 |
+
|
| 253 |
+
// Save results to database
|
| 254 |
+
const language = document.getElementById('language-select').value;
|
| 255 |
+
saveResults(language, wpm, accuracy, timeTaken);
|
| 256 |
+
}
|
| 257 |
+
}
|
| 258 |
+
|
| 259 |
+
// Save results to database
|
| 260 |
+
async function saveResults(language, wpm, accuracy, timeTaken) {
|
| 261 |
+
try {
|
| 262 |
+
const response = await fetch('db.php', {
|
| 263 |
+
method: 'POST',
|
| 264 |
+
headers: {
|
| 265 |
+
'Content-Type': 'application/json',
|
| 266 |
+
},
|
| 267 |
+
body: JSON.stringify({
|
| 268 |
+
language,
|
| 269 |
+
wpm,
|
| 270 |
+
accuracy,
|
| 271 |
+
time: timeTaken
|
| 272 |
+
})
|
| 273 |
+
});
|
| 274 |
+
|
| 275 |
+
const data = await response.json();
|
| 276 |
+
if (!data.success) {
|
| 277 |
+
console.error('Failed to save results:', data.error);
|
| 278 |
+
}
|
| 279 |
+
} catch (error) {
|
| 280 |
+
console.error('Error saving results:', error);
|
| 281 |
+
}
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
+
// Fetch leaderboard data
|
| 285 |
+
async function fetchLeaderboard() {
|
| 286 |
+
try {
|
| 287 |
+
const response = await fetch('db.php');
|
| 288 |
+
const data = await response.json();
|
| 289 |
+
if (data.success) {
|
| 290 |
+
return data.data;
|
| 291 |
+
}
|
| 292 |
+
return [];
|
| 293 |
+
} catch (error) {
|
| 294 |
+
console.error('Error fetching leaderboard:', error);
|
| 295 |
+
return [];
|
| 296 |
}
|
| 297 |
}
|
| 298 |
// Event listeners
|