// ➕ Add a new user async function addNewUser() { const username = document.getElementById("newUsername").value.trim(); const password = document.getElementById("newPassword").value; const role = document.getElementById("newRole").value; const msg = document.getElementById("userAddMessage"); const hashPreview = document.getElementById("hashPreview"); if (!username || !password) { msg.textContent = "Please fill out all fields."; return; } const hash = hashPassword(password); if (hashPreview) hashPreview.textContent = hash; try { await fetch('/addUser', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, hash, role }) }); msg.textContent = `✅ User "${username}" added to server.`; document.getElementById("newUsername").value = ''; document.getElementById("newPassword").value = ''; if (hashPreview) hashPreview.textContent = '[auto]'; await loadUsers(); displayUserDropdown(); } catch (e) { msg.textContent = "❌ Failed to add user."; console.error("Add user error:", e); } } // 🗑 Populate and delete users function displayUserDropdown() { const dropdown = document.getElementById("userDropdown"); dropdown.innerHTML = ``; users.forEach(user => { dropdown.innerHTML += ``; }); } function selectUserAction(username) { if (!username) return; if (!confirm(`Are you sure you want to delete user "${username}"?`)) { document.getElementById("userDropdown").value = ""; return; } fetch("/deleteUser", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username }) }) .then(res => res.text()) .then(msg => { alert(msg); loadUsers().then(displayUserDropdown); }); } // 📚 Save or clear word list for current user async function saveWords() { const selectedUser = document.getElementById('wordUserSelect')?.value; if (!selectedUser) { alert("⚠️ Please select a user before saving."); return; } const text = document.getElementById('wordInput').value.trim(); allWords = text.split('\n').map(w => w.trim()).filter(Boolean); await fetch('/saveWordList', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: selectedUser, words: allWords }) }); displayWordList(); alert(`✅ Word list saved to server for ${selectedUser}`); } async function clearWords() { const selectedUser = document.getElementById('wordUserSelect')?.value; if (!selectedUser) return; allWords = []; await fetch('/saveWordList', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: selectedUser, words: [] }) }); displayWordList(); alert(`🗑 Word list cleared for ${selectedUser}`); } // 🗂 Load and display word lists async function loadWordsForSelectedUser() { const selectedUser = document.getElementById("wordUserSelect").value; if (!selectedUser) return; const res = await fetch(`/getWordList?user=${encodeURIComponent(selectedUser)}`); allWords = await res.json(); displayWordList(); } function displayWordList() { const ul = document.getElementById('wordListDisplay'); ul.innerHTML = allWords.map(word => `
  • ${word}
  • `).join(''); } function populateWordUserDropdown() { const selects = document.querySelectorAll(".word-user-dropdown"); selects.forEach(select => { select.innerHTML = ''; users.forEach(user => { const option = document.createElement("option"); option.value = user.username; option.textContent = user.username; select.appendChild(option); }); }); } async function displayWordListForSelectedUser() { const select = document.getElementById("viewUserSelect"); const username = select.value; const list = document.getElementById("userWordListDisplay"); list.innerHTML = ''; if (!username) return; try { const res = await fetch(`/getWordList?user=${username}`); const words = await res.json(); if (!words.length) { list.innerHTML = "
  • No words found for this user.
  • "; return; } words.forEach(word => { const li = document.createElement("li"); li.textContent = word; list.appendChild(li); }); } catch (err) { console.error("Failed to load word list for user:", username, err); list.innerHTML = `
  • ❌ Error loading words
  • `; } } // 📊 Student result viewer async function loadStudentResults() { const list = document.getElementById("resultsList"); list.innerHTML = ''; try { const res = await fetch('/getResults'); const results = await res.json(); if (!Object.keys(results).length) { list.innerHTML = '
  • No results available yet.
  • '; 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}
    `; }); li.innerHTML = ` ${username} — Score: ${result.score}, Completed: ${result.completed ? '✅' : '❌'}
    Answers:
    ${answersHTML} `; list.appendChild(li); } } catch (err) { console.error("🔴 Failed to load results:", err); list.innerHTML = '
  • Error loading results.
  • '; } } // 🧭 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'); const users = await res.json(); const dropdowns = [ document.getElementById('userDropdown'), document.getElementById('wordUserSelect'), document.getElementById('viewUserSelect') ]; dropdowns.forEach(drop => { if (!drop) return; drop.innerHTML = ''; users.forEach(u => { const opt = document.createElement('option'); opt.value = u.username; opt.textContent = u.username; drop.appendChild(opt); }); }); } catch (err) { console.error("⚠️ Failed to load user list:", err); } } async function refreshAnalytics() { const summary = document.getElementById("analyticsSummary"); const breakdown = document.getElementById("studentBreakdown"); summary.innerHTML = "
  • Loading...
  • "; breakdown.innerHTML = ""; try { const res = await fetch('/getResults'); const data = await res.json(); let totalScore = 0, sessionCount = 0; const missMap = {}; for (const username in data) { const { score, completed, answers } = data[username]; if (!completed || !Array.isArray(answers)) continue; totalScore += score; sessionCount++; // Count missed words answers.forEach(({ word, correct }) => { if (!correct) { missMap[word] = (missMap[word] || 0) + 1; } }); // Per-student breakdown const li = document.createElement("li"); li.innerHTML = `${username}: ${score}/${answers.length} correct`; breakdown.appendChild(li); } const avgScore = sessionCount ? (totalScore / sessionCount).toFixed(2) : "0"; let mostMissed = "-", highestMisses = 0; for (const word in missMap) { if (missMap[word] > highestMisses) { highestMisses = missMap[word]; mostMissed = word; } } summary.innerHTML = `
  • Average Score: ${avgScore}
  • Total Completed Sessions: ${sessionCount}
  • Most Missed Word: ${mostMissed} (${highestMisses} misses)
  • `; } catch (err) { console.error("📉 Failed to refresh analytics:", err); summary.innerHTML = '
  • Error loading analytics.
  • '; } } async function loadBadgeViewer() { const res = await fetch('/getBadges'); const badgeData = await res.json(); const userList = document.getElementById("badgeUserList"); userList.innerHTML = ""; for (const username in badgeData) { const li = document.createElement("li"); li.innerHTML = ``; userList.appendChild(li); } } function showUserBadges(username) { fetch('/getBadges') .then(res => res.json()) .then(data => { const badges = data[username] || []; const title = document.getElementById("badgeUserTitle"); const badgeList = document.getElementById("badgeUserBadges"); const details = document.getElementById("badgeDetails"); title.textContent = `Badges for ${username}`; badgeList.innerHTML = ""; if (!badges.length) { badgeList.innerHTML = "
  • No badges earned yet.
  • "; } else { badges.forEach(b => { const li = document.createElement("li"); li.textContent = `🏅 ${b}`; badgeList.appendChild(li); }); } details.classList.remove("hidden"); }); } async function loadStudentNamesForReport() { const res = await fetch('/getResults'); const data = await res.json(); const select = document.getElementById("reportUser"); select.innerHTML = ``; Object.keys(data).forEach(username => { const opt = document.createElement("option"); opt.value = username; opt.textContent = username; select.appendChild(opt); }); } async function loadStudentReport(username) { if (!username) return; 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] || []; 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); }); 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 = "
  • No badges earned yet.
  • "; } document.getElementById("reportContent").classList.remove("hidden"); } function copyReportToClipboard() { 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"); const badges = Array.from(document.querySelectorAll("#reportBadges li")).map(li => li.textContent).join("\n"); const summary = `${name}\nScore: ${score}\n\nWords:\n${words}\n\nBadges:\n${badges}`; navigator.clipboard.writeText(summary); alert("✅ Report copied to clipboard"); } async function loadSessionHistory() { const res = await fetch('/getResults'); const allResults = await res.json(); const list = document.getElementById("sessionSummaryList"); list.innerHTML = ""; for (const username in allResults) { const sessions = allResults[username]; const header = document.createElement("li"); header.innerHTML = `${username}`; list.appendChild(header); sessions.forEach(({ score, answers, timestamp }, index) => { const li = document.createElement("li"); const dateStr = new Date(timestamp).toLocaleString(); const total = answers.length; li.textContent = `🗓️ ${dateStr} — ${score}/${total} correct`; list.appendChild(li); }); } } async function populateSessionUserDropdown() { const res = await fetch('/getResults'); const allResults = await res.json(); const select = document.getElementById("sessionUserSelect"); select.innerHTML = ``; Object.keys(allResults).forEach(username => { const opt = document.createElement("option"); opt.value = username; opt.textContent = username; select.appendChild(opt); }); } async function loadUserSessions(username) { const res = await fetch('/getResults'); const allResults = await res.json(); const userSessions = allResults[username] || []; const list = document.getElementById("userSessionList"); list.innerHTML = ""; userSessions.forEach(({ score, answers, timestamp }) => { const sessionItem = document.createElement("li"); const dateStr = new Date(timestamp).toLocaleString(); const total = answers.length; let html = `

    🗓️ ${dateStr} — ${score}/${total} correct

    "; sessionItem.innerHTML = html; list.appendChild(sessionItem); }); }