bunch of fixes

This commit is contained in:
nate@necloud.us committed 2025-07-15 10:16:59 -04:00
1 parent 462c7908a1
commit 266a064330
14 files changed
+1211 -868

No files matched your search

+229 -86
View File
@@ -1,3 +1,84 @@
// Global variables for admin functionality
let allWords = [];
let adminUsers = [];
// 🧭 Admin tab switcher (make it globally accessible)
window.switchTab = function switchTab(targetId) {
console.log('🔄 Switching to tab:', targetId);
// Hide all tabs
document.querySelectorAll('.adminTab').forEach(tab => {
tab.classList.add('hidden');
});
// Remove active class from all buttons
document.querySelectorAll('#adminTabs button').forEach(btn => {
btn.classList.remove('active');
});
// Show the selected tab
const targetTab = document.getElementById(targetId);
if (targetTab) {
targetTab.classList.remove('hidden');
console.log('✅ Showing tab:', targetId);
} else {
console.error('❌ Tab not found:', targetId);
}
// Highlight the active button
document.querySelectorAll('#adminTabs button').forEach(btn => {
const onclick = btn.getAttribute('onclick');
if (onclick && onclick.includes(targetId)) {
btn.classList.add('active');
}
});
// 🔁 Trigger tab-specific logic
switch(targetId) {
case 'tabSessions':
if (typeof populateSessionUserDropdown === 'function') {
populateSessionUserDropdown();
}
const sessionList = document.getElementById("userSessionList");
if (sessionList) sessionList.innerHTML = "";
break;
case 'tabReports':
if (typeof loadStudentNamesForReport === 'function') {
loadStudentNamesForReport();
}
break;
case 'tabBadges':
if (typeof loadBadgeViewer === 'function') {
loadBadgeViewer();
}
break;
case 'tabAnalytics':
if (typeof refreshAnalytics === 'function') {
refreshAnalytics();
}
break;
case 'tabResults':
if (typeof loadStudentResults === 'function') {
loadStudentResults();
}
break;
case 'tabWords':
loadUserDropdowns();
break;
}
};
// Load users from server
async function loadUsers() {
try {
const res = await fetch('/getUsers');
adminUsers = await res.json();
} catch (err) {
console.error("Failed to load users:", err);
adminUsers = [];
}
}
// ➕ Add a new user
async function addNewUser() {
const username = document.getElementById("newUsername").value.trim();
@@ -26,8 +107,9 @@ async function addNewUser() {
document.getElementById("newPassword").value = '';
if (hashPreview) hashPreview.textContent = '[auto]';
await loadUsers();
displayUserDropdown();
await loadUsers();
displayUserDropdown();
populateWordUserDropdown();
} catch (e) {
msg.textContent = "❌ Failed to add user.";
console.error("Add user error:", e);
@@ -38,7 +120,7 @@ async function addNewUser() {
function displayUserDropdown() {
const dropdown = document.getElementById("userDropdown");
dropdown.innerHTML = `<option value="">-- Select a user --</option>`;
users.forEach(user => {
adminUsers.forEach(user => {
dropdown.innerHTML += `<option value="${user.username}">${user.username} (${user.role})</option>`;
});
}
@@ -104,8 +186,9 @@ async function loadWordsForSelectedUser() {
const selectedUser = document.getElementById("wordUserSelect").value;
if (!selectedUser) return;
const res = await fetch(`/getWordList?user=${encodeURIComponent(selectedUser)}`);
allWords = await res.json();
const res = await fetch(`/getWordList?username=${encodeURIComponent(selectedUser)}`);
const data = await res.json();
allWords = data.words || [];
displayWordList();
}
@@ -118,7 +201,7 @@ function populateWordUserDropdown() {
const selects = document.querySelectorAll(".word-user-dropdown");
selects.forEach(select => {
select.innerHTML = '<option value="">-- Select a user --</option>';
users.forEach(user => {
adminUsers.forEach(user => {
const option = document.createElement("option");
option.value = user.username;
option.textContent = user.username;
@@ -136,8 +219,9 @@ async function displayWordListForSelectedUser() {
if (!username) return;
try {
const res = await fetch(`/getWordList?user=${username}`);
const words = await res.json();
const res = await fetch(`/getWordList?username=${username}`);
const data = await res.json();
const words = data.words || [];
if (!words.length) {
list.innerHTML = "<li><em>No words found for this user.</em></li>";
return;
@@ -154,7 +238,7 @@ async function displayWordListForSelectedUser() {
}
// 📊 Student result viewer
async function loadStudentResults() {
window.loadStudentResults = async function() {
const list = document.getElementById("resultsList");
list.innerHTML = '';
@@ -167,19 +251,38 @@ async function loadStudentResults() {
return;
}
for (const [username, result] of Object.entries(results)) {
const li = document.createElement("li");
let answersHTML = '';
result.answers.forEach(entry => {
const status = entry.correct ? '✅' : '❌';
answersHTML += `${status} ${entry.word}<br>`;
});
for (const [username, userResults] of Object.entries(results)) {
// Handle array of results per user
if (Array.isArray(userResults) && userResults.length > 0) {
userResults.forEach((result, index) => {
const li = document.createElement("li");
let answersHTML = '';
// Check if result and answers exist and are valid
if (result && result.answers && Array.isArray(result.answers)) {
result.answers.forEach(entry => {
const status = entry.correct ? '✅' : '❌';
answersHTML += `${status} ${entry.word}<br>`;
});
} else {
answersHTML = '<em>No answer data available</em>';
}
li.innerHTML = `
<strong>${username}</strong> — Score: ${result.score}, Completed: ${result.completed ? '✅' : '❌'}<br>
<em>Answers:</em><br>${answersHTML}
`;
list.appendChild(li);
const timestamp = result.timestamp ? new Date(result.timestamp).toLocaleString() : 'Unknown time';
li.innerHTML = `
<strong>${username}</strong> (Session ${index + 1}) — Score: ${result?.score || 'N/A'}, Completed: ${result?.completed ? '✅' : '❌'}<br>
<em>Date:</em> ${timestamp}<br>
<em>Answers:</em><br>${answersHTML}
<hr style="margin: 1rem 0; opacity: 0.3;">
`;
list.appendChild(li);
});
} else {
// Fallback for single result format
const li = document.createElement("li");
li.innerHTML = `<strong>${username}</strong> — No valid results found`;
list.appendChild(li);
}
}
} catch (err) {
console.error("🔴 Failed to load results:", err);
@@ -187,40 +290,6 @@ async function loadStudentResults() {
}
}
// 🧭 Admin tab switcher
function switchTab(targetId) {
document.querySelectorAll('.adminTab').forEach(tab =>
tab.classList.add('hidden')
);
document.querySelectorAll('#adminTabs button').forEach(btn =>
btn.classList.remove('active')
);
// Show the selected tab
document.getElementById(targetId).classList.remove('hidden');
// Highlight the active button
document.querySelectorAll('#adminTabs button').forEach(btn => {
if (btn.getAttribute('onclick')?.includes(targetId)) {
btn.classList.add('active');
}
});
// 🔁 Trigger tab-specific logic
if (targetId === 'tabSessions') {
loadSessionHistory();
}
if (targetId === 'tabReports') {
loadStudentNamesForReport();
}
if (targetId === 'tabBadges') {
loadBadgeViewer();
}
if (targetId === 'tabSessions') {
populateSessionUserDropdown();
document.getElementById("userSessionList").innerHTML = "";
}
}
async function loadUserDropdowns() {
try {
const res = await fetch('/getUsers');
@@ -243,6 +312,8 @@ async function loadUserDropdowns() {
drop.appendChild(opt);
});
});
console.log('✅ User dropdowns loaded');
} catch (err) {
console.error("⚠️ Failed to load user list:", err);
}
@@ -352,44 +423,73 @@ async function loadStudentNamesForReport() {
});
}
async function loadStudentReport(username) {
window.loadStudentReport = async function(username) {
if (!username) return;
const res1 = await fetch('/getResults');
const results = await res1.json();
const res2 = await fetch('/getBadges');
const badges = await res2.json();
try {
const res1 = await fetch('/getResults');
const results = await res1.json();
const res2 = await fetch('/getBadges');
const badges = await res2.json();
const report = results[username];
const badgeList = badges[username] || [];
const userResults = results[username];
const badgeList = badges[username] || [];
document.getElementById("reportTitle").textContent = `Report for ${username}`;
document.getElementById("reportScore").textContent = `${report.score}/${report.answers.length} correct`;
const wordList = document.getElementById("reportWords");
wordList.innerHTML = "";
report.answers.forEach(({ word, correct }) => {
const li = document.createElement("li");
li.textContent = `${word} — ${correct ? '✅' : '❌'}`;
wordList.appendChild(li);
});
// Handle case where userResults might be an array or undefined
let latestReport = null;
if (Array.isArray(userResults) && userResults.length > 0) {
// Get the most recent result
latestReport = userResults[userResults.length - 1];
} else if (userResults && typeof userResults === 'object') {
// Handle single result object
latestReport = userResults;
}
const badgeContainer = document.getElementById("reportBadges");
badgeContainer.innerHTML = "";
if (badgeList.length) {
badgeList.forEach(b => {
const li = document.createElement("li");
li.textContent = `🏅 ${b}`;
badgeContainer.appendChild(li);
});
} else {
badgeContainer.innerHTML = "<li><em>No badges earned yet.</em></li>";
if (!latestReport) {
document.getElementById("reportContent").classList.add("hidden");
alert(`No results found for ${username}`);
return;
}
document.getElementById("reportTitle").textContent = `Report for ${username}`;
// Safe access to score and answers
const score = latestReport.score || 0;
const answers = latestReport.answers || [];
document.getElementById("reportScore").textContent = `${score}/${answers.length} correct`;
const wordList = document.getElementById("reportWords");
wordList.innerHTML = "";
if (answers.length > 0) {
answers.forEach(({ word, correct }) => {
const li = document.createElement("li");
li.textContent = `${word} — ${correct ? '✅' : '❌'}`;
wordList.appendChild(li);
});
} else {
wordList.innerHTML = "<li><em>No word data available.</em></li>";
}
const badgeContainer = document.getElementById("reportBadges");
badgeContainer.innerHTML = "";
if (badgeList.length) {
badgeList.forEach(b => {
const li = document.createElement("li");
li.textContent = `🏅 ${b}`;
badgeContainer.appendChild(li);
});
} else {
badgeContainer.innerHTML = "<li><em>No badges earned yet.</em></li>";
}
document.getElementById("reportContent").classList.remove("hidden");
} catch (error) {
console.error("Error loading student report:", error);
alert("Error loading student report. Please try again.");
}
document.getElementById("reportContent").classList.remove("hidden");
}
function copyReportToClipboard() {
window.copyReportToClipboard = function() {
const name = document.getElementById("reportTitle").textContent;
const score = document.getElementById("reportScore").textContent;
const words = Array.from(document.querySelectorAll("#reportWords li")).map(li => li.textContent).join("\n");
@@ -457,4 +557,47 @@ async function loadUserSessions(username) {
sessionItem.innerHTML = html;
list.appendChild(sessionItem);
});
}
}
// File upload handler for word lists
function handleFileUpload(event) {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(e) {
const content = e.target.result;
const words = content.split(/\r?\n/).map(w => w.trim()).filter(Boolean);
document.getElementById('wordInput').value = words.join('\n');
showToast(`📁 Loaded ${words.length} words from file`);
};
reader.readAsText(file);
}
// Add event listeners for admin tabs as backup to onclick
document.addEventListener('DOMContentLoaded', () => {
// Setup tab click listeners
setTimeout(() => {
const tabButtons = document.querySelectorAll('#adminTabs button');
tabButtons.forEach(button => {
button.addEventListener('click', (e) => {
e.preventDefault();
const onclick = button.getAttribute('onclick');
if (onclick) {
const match = onclick.match(/switchTab\('(.+?)'\)/);
if (match) {
const tabId = match[1];
console.log('Tab clicked:', tabId);
if (typeof window.switchTab === 'function') {
window.switchTab(tabId);
} else {
console.error('switchTab function not available');
}
}
}
});
});
console.log('✅ Tab event listeners added to', tabButtons.length, 'buttons');
}, 300);
});
+92 -19
View File
@@ -1,8 +1,24 @@
// 🔐 Password hashing using jsSHA
function hashPassword(password) {
const shaObj = new jsSHA("SHA-256", "TEXT");
shaObj.update(password);
return shaObj.getHash("HEX");
try {
if (typeof jsSHA === 'undefined') {
console.error('jsSHA library not loaded');
throw new Error('jsSHA library not available');
}
const shaObj = new jsSHA("SHA-256", "TEXT");
shaObj.update(password);
return shaObj.getHash("HEX");
} catch (error) {
console.error('Error hashing password:', error);
// Fallback: simple hash (not secure, but for debugging)
let hash = 0;
for (let i = 0; i < password.length; i++) {
const char = password.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // Convert to 32bit integer
}
return Math.abs(hash).toString(16);
}
}
// 🌐 Load users from server
@@ -12,28 +28,43 @@ let users = [];
async function verifyLogin() {
const uname = document.getElementById('username').value.trim();
const pw = document.getElementById('password').value;
const hash = hashPassword(pw);
const msg = document.getElementById('loginMessage');
if (!uname || !pw) {
msg.textContent = "Please enter both username and password.";
msg.style.color = 'var(--error)';
return;
}
try {
console.log('Attempting login for:', uname);
const hash = hashPassword(pw);
console.log('Generated hash:', hash);
const res = await fetch('/verifyUser', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: uname, hash })
});
const responseText = await res.text();
console.log('Server response:', res.status, responseText);
if (!res.ok) {
msg.textContent = "Invalid username or password.";
msg.style.color = 'var(--error)';
return;
}
const user = await res.json();
const user = JSON.parse(responseText);
console.log('Login successful for user:', user);
localStorage.setItem('loggedInUser', JSON.stringify(user));
msg.textContent = '';
setupSession(user);
} catch (err) {
console.error("Login error:", err);
msg.textContent = "Login failed. Please try again.";
msg.style.color = 'var(--error)';
}
}
@@ -41,18 +72,60 @@ async function verifyLogin() {
function logoutUser() {
localStorage.removeItem('loggedInUser');
document.getElementById('adminPanel').classList.add('hidden');
document.getElementById('studentPanel').classList.add('hidden');
document.getElementById('nav').classList.add('hidden');
document.getElementById('loginPanel').classList.remove('hidden');
document.getElementById('username').value = '';
document.getElementById('password').value = '';
const roleBanner = document.getElementById('roleBanner');
roleBanner.classList.add('hidden');
roleBanner.classList.remove('admin', 'student');
roleBanner.textContent = '';
// Hide all panels
document.getElementById('adminPanel')?.classList.add('hidden');
document.getElementById('studentPanel')?.classList.add('hidden');
document.getElementById('nav')?.classList.add('hidden');
// Show login panel
const loginPanel = document.getElementById('loginPanel');
loginPanel?.classList.remove('hidden');
// Clear login form
const usernameField = document.getElementById('username');
const passwordField = document.getElementById('password');
const loginMessage = document.getElementById('loginMessage');
if (loginMessage) loginMessage.textContent = '';
}
if (usernameField) usernameField.value = '';
if (passwordField) passwordField.value = '';
if (loginMessage) {
loginMessage.textContent = '';
loginMessage.style.color = '';
}
// Reset banner
const roleBanner = document.getElementById('roleBanner');
if (roleBanner) {
roleBanner.classList.add('hidden');
roleBanner.classList.remove('admin', 'student');
roleBanner.textContent = '';
}
// Focus on username field
if (usernameField) {
setTimeout(() => usernameField.focus(), 100);
}
showToast('Logged out successfully');
}
// Add Enter key support for login form
document.addEventListener('DOMContentLoaded', () => {
const loginForm = document.querySelector('#loginPanel form');
const usernameField = document.getElementById('username');
const passwordField = document.getElementById('password');
if (loginForm && usernameField && passwordField) {
// Focus username field on page load
usernameField.focus();
// Add Enter key support
[usernameField, passwordField].forEach(field => {
field.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
verifyLogin();
}
});
});
}
});
+42 -2
View File
@@ -3,7 +3,46 @@ let words = [];
let currentWord = '';
let currentIndex = 0;
let results = [];
let typingWords = [];
// 📝 Check user's answer and move to next
window.submitAnswer = function() {
const input = document.getElementById('userInput').value.trim();
// Record the attempt
results.push({
word: currentWord,
attempt: input
});
if (input.toLowerCase() === currentWord.toLowerCase()) {
currentIndex++;
document.getElementById('inputSection').classList.add('hidden');
setTimeout(() => showWord(), 500); // Brief pause before next word
} else {
// Show feedback for incorrect answer
const inputEl = document.getElementById('userInput');
inputEl.style.borderColor = 'var(--error)';
inputEl.style.backgroundColor = '#fef2f2';
setTimeout(() => {
inputEl.style.borderColor = '';
inputEl.style.backgroundColor = '';
inputEl.focus();
}, 1000);
}
}
// Add Enter key support for game input
document.addEventListener('DOMContentLoaded', () => {
const gameInput = document.getElementById('userInput');
if (gameInput) {
gameInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
submitAnswer();
}
});
}
});[];
let currentSentence = '';
function showStudent() {
console.log("🔊 Showing student panel");
@@ -34,7 +73,7 @@ function showStudent() {
}
// 🚀 Begin game session
async function startGame() {
window.startGame = async function() {
console.log('🎮 Starting spelling game');
const user = JSON.parse(localStorage.getItem('loggedInUser'));
@@ -62,6 +101,7 @@ async function startGame() {
// Start game
words = [...data.words];
typingWords = [...data.words]; // Initialize typing words too
currentIndex = 0;
results = []; // Reset results array
showWord();
+46 -14
View File
@@ -1,22 +1,54 @@
document.addEventListener("DOMContentLoaded", async () => {
// 🌐 Load users.json for login validation
// await loadUsers();
displayUserDropdown();
populateWordUserDropdown();
// 🎨 Restore saved theme
applySavedTheme();
// 🔁 Restore session if user was logged in
const savedUser = localStorage.getItem("loggedInUser");
if (savedUser) {
setupSession(JSON.parse(savedUser));
console.log('🚀 Application starting...');
// Load users for admin functionality
try {
await loadUsers();
displayUserDropdown();
populateWordUserDropdown();
} catch (e) {
console.warn('Could not load users (normal if not admin):', e);
}
// 🧭 Tab switching for Admin Panel
// Restore saved theme
applySavedTheme();
// Restore session if user was logged in
const savedUser = localStorage.getItem("loggedInUser");
if (savedUser) {
try {
const user = JSON.parse(savedUser);
console.log('👤 Restoring session for:', user.username);
setupSession(user);
} catch (e) {
console.error('Error restoring session:', e);
localStorage.removeItem("loggedInUser");
}
} else {
console.log('👤 No saved session found');
// Focus on login form
const usernameField = document.getElementById('username');
if (usernameField) {
setTimeout(() => usernameField.focus(), 100);
}
}
// Set default admin tab
const defaultTab = document.querySelector('#adminTabs button.active');
if (defaultTab) {
const targetId = defaultTab.getAttribute("onclick")?.match(/'(.*?)'/)?.[1];
if (targetId) switchTab(targetId);
if (targetId) {
console.log('🔧 Setting default admin tab:', targetId);
// Use a small delay to ensure all scripts are loaded
setTimeout(() => {
if (typeof window.switchTab === 'function') {
window.switchTab(targetId);
} else {
console.warn('switchTab function not yet available');
}
}, 200);
}
}
console.log('✅ Application initialized');
});
+64 -5
View File
@@ -1,14 +1,73 @@
// 🎨 Apply saved theme on load
// Modern theme system
function applySavedTheme() {
const savedTheme = localStorage.getItem('theme') || 'playground';
document.body.className = savedTheme;
const savedTheme = localStorage.getItem('theme') || 'default';
applyTheme(savedTheme);
const themeSelector = document.getElementById('themeSelect');
if (themeSelector) themeSelector.value = savedTheme;
}
// 🎨 Change theme dynamically
function changeTheme(themeName) {
document.body.className = themeName;
applyTheme(themeName);
localStorage.setItem('theme', themeName);
showToast(`🎨 Theme changed to ${themeName}`);
}
function applyTheme(themeName) {
// Remove existing theme classes
document.body.className = '';
// Apply theme-specific styles
switch(themeName) {
case 'playground':
document.body.className = 'theme-playground';
document.documentElement.style.setProperty('--primary', '#ff6b6b');
document.documentElement.style.setProperty('--accent', '#4ecdc4');
document.documentElement.style.setProperty('--bg-primary', '#fff5f5');
document.documentElement.style.setProperty('--bg-secondary', '#ffebee');
break;
case 'chalkboard':
document.body.className = 'theme-chalkboard';
document.documentElement.style.setProperty('--primary', '#4ade80');
document.documentElement.style.setProperty('--accent', '#22d3ee');
document.documentElement.style.setProperty('--bg-primary', '#1f2937');
document.documentElement.style.setProperty('--bg-secondary', '#374151');
document.documentElement.style.setProperty('--text-primary', '#f9fafb');
document.documentElement.style.setProperty('--text-secondary', '#d1d5db');
document.documentElement.style.setProperty('--border', '#4b5563');
break;
case 'galaxy':
document.body.className = 'theme-galaxy';
document.documentElement.style.setProperty('--primary', '#8b5cf6');
document.documentElement.style.setProperty('--accent', '#06b6d4');
document.documentElement.style.setProperty('--bg-primary', '#0f0f23');
document.documentElement.style.setProperty('--bg-secondary', '#1a1a3f');
document.documentElement.style.setProperty('--text-primary', '#e0e7ff');
document.documentElement.style.setProperty('--text-secondary', '#a5b4fc');
document.documentElement.style.setProperty('--border', '#3730a3');
break;
case 'nature':
document.body.className = 'theme-nature';
document.documentElement.style.setProperty('--primary', '#059669');
document.documentElement.style.setProperty('--accent', '#84cc16');
document.documentElement.style.setProperty('--bg-primary', '#f0fdf4');
document.documentElement.style.setProperty('--bg-secondary', '#dcfce7');
document.documentElement.style.setProperty('--text-primary', '#14532d');
break;
default: // Modern default theme
document.body.className = 'theme-default';
// Reset to original CSS custom properties
document.documentElement.style.setProperty('--primary', '#3b82f6');
document.documentElement.style.setProperty('--accent', '#06b6d4');
document.documentElement.style.setProperty('--bg-primary', '#ffffff');
document.documentElement.style.setProperty('--bg-secondary', '#f8fafc');
document.documentElement.style.setProperty('--text-primary', '#1e293b');
document.documentElement.style.setProperty('--text-secondary', '#64748b');
document.documentElement.style.setProperty('--border', '#e2e8f0');
break;
}
}
+4 -9
View File
@@ -5,7 +5,8 @@ const typingState = {
currentWord: ''
};
async function startTypingPractice() {
// Make functions globally accessible
window.startTypingPractice = async function() {
console.log('Starting typing practice');
const user = JSON.parse(localStorage.getItem('loggedInUser'));
@@ -34,7 +35,7 @@ async function startTypingPractice() {
}
}
function showNextWord() {
window.showNextWord = function() {
const promptEl = document.getElementById('typingPrompt');
const inputEl = document.getElementById('typingInput');
if (!promptEl || !inputEl) return;
@@ -58,14 +59,8 @@ function showNextWord() {
inputEl.focus();
}
// Reset input
inputEl.value = '';
inputEl.disabled = false;
inputEl.focus();
function submitTyping() {
window.submitTyping = function() {
const input = document.getElementById('typingInput')?.value.trim() || '';
const feedback = document.getElementById('typingFeedback');
if (!feedback) return;
+51 -9
View File
@@ -46,16 +46,58 @@ function showToast(message, duration = 3000) {
toast.classList.remove("show");
}, duration);
}
function showAdmin() {
console.log("showAdmin fired");
const adminPanel = document.getElementById("adminPanel");
const studentPanel = document.getElementById("studentPanel");
function showStudent() {
console.log("🎓 Showing student panel");
loadUserDropdowns();
// Hide other panels
document.getElementById('adminPanel')?.classList.add('hidden');
document.getElementById('loginPanel')?.classList.add('hidden');
// Show student panel
const studentPanel = document.getElementById('studentPanel');
if (studentPanel) {
studentPanel.classList.remove('hidden');
// Ensure buttons are visible and functional
const gameBtn = document.getElementById('startGameBtn');
const typingBtn = document.getElementById('startTypingBtn');
if (gameBtn && typingBtn) {
gameBtn.classList.remove('hidden');
typingBtn.classList.remove('hidden');
console.log("✅ Student buttons are now visible");
}
}
}
adminPanel?.classList.remove("hidden");
studentPanel?.classList.add("hidden");
function showAdmin() {
console.log("👨‍💼 Showing admin panel");
switchTab("tabUsers"); // optional default tab
// Hide other panels
document.getElementById('studentPanel')?.classList.add('hidden');
document.getElementById('loginPanel')?.classList.add('hidden');
const adminPanel = document.getElementById("adminPanel");
if (adminPanel) {
adminPanel.classList.remove("hidden");
// Load user data for dropdowns
loadUserDropdowns().then(() => {
console.log("✅ User dropdowns loaded");
}).catch(e => {
console.warn("Could not load user dropdowns:", e);
});
// Set default tab - use a slight delay to ensure DOM is ready
setTimeout(() => {
if (typeof window.switchTab === 'function') {
window.switchTab("tabUsers");
console.log("✅ Default tab set");
} else {
console.error("switchTab function not available");
}
}, 100);
} else {
console.error("Admin panel not found");
}
}