Files
homeschool/public/script.js
T
2025-07-02 11:37:34 -04:00

329 lines
9.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
let users = []
// 🔁 Load users from server-side users.json
async function loadUsers() {
try {
const res = await fetch("users.json")
users = await res.json()
} catch (e) {
console.error("Failed to load users.json:", e)
users = []
}
}
// 🔐 Password hashing
function hashPassword(password) {
const shaObj = new jsSHA("SHA-256", "TEXT")
shaObj.update(password)
return shaObj.getHash("HEX")
}
// 👤 Login
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')
const user = users.find(u => u.username === uname && u.hash === hash)
if (user) {
localStorage.setItem('loggedInUser', JSON.stringify(user))
msg.textContent = ''
setupSession(user)
await showAdmin()
} else {
msg.textContent = "Invalid username or password."
}
}
// 🧠 On load
window.addEventListener('DOMContentLoaded', async () => {
await loadUsers()
displayUserDropdown()
const savedTheme = localStorage.getItem('theme') || 'playground'
document.body.className = savedTheme
const dropdown = document.getElementById('themeSelect')
if (dropdown) dropdown.value = savedTheme
const savedUser = localStorage.getItem('loggedInUser')
if (savedUser) setupSession(JSON.parse(savedUser))
})
// 🧑‍🏫 Admin setup
async function setupSession(user) {
document.getElementById('loginPanel').classList.add('hidden')
document.getElementById('nav').classList.remove('hidden')
document.getElementById('userInfo').textContent = `${user.username} (${user.role})`
if (user.role === "admin") {
document.getElementById('adminBtn').classList.remove('hidden')
await showAdmin()
await loadUsers()
displayUserDropdown()
populateWordUserDropdown()
} else {
document.getElementById('adminBtn').classList.add('hidden')
showStudent()
}
}
function logoutUser() {
localStorage.removeItem('loggedInUser')
document.getElementById('loginPanel').classList.remove('hidden')
document.getElementById('nav').classList.add('hidden')
document.getElementById('adminPanel').classList.add('hidden')
document.getElementById('studentPanel').classList.add('hidden')
}
async function showAdmin() {
document.getElementById('adminPanel').classList.remove('hidden')
document.getElementById('studentPanel').classList.add('hidden')
loadWords()
displayWordList()
await loadUsers()
displayUserDropdown()
}
// ➕ Add 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")
if (!username || !password) {
msg.textContent = "Please fill out all fields."
return
}
const hash = hashPassword(password)
await saveUserToServer(username, hash, role)
msg.textContent = `✅ User "${username}" added to server.`
document.getElementById("newUsername").value = ''
document.getElementById("newPassword").value = ''
await loadUsers()
displayUserDropdown()
}
async function saveUserToServer(username, hash, role) {
const res = await fetch('/addUser', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, hash, role })
})
const msg = await res.text()
alert(msg)
}
// 🗑 User deletion
function displayUserDropdown() {
const dropdown = document.getElementById("userDropdown")
dropdown.innerHTML = `<option value="">-- Select a user --</option>`
users.forEach(user => {
dropdown.innerHTML += `<option value="${user.username}">${user.username} (${user.role})</option>`
})
}
function selectUserAction(username) {
if (!username) return
const confirmDelete = confirm(`Are you sure you want to delete user "${username}"?`)
if (!confirmDelete) {
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)
})
}
// 🎨 Theme
function changeTheme(themeName) {
document.body.className = themeName
localStorage.setItem('theme', themeName)
}
// 📚 Word list
function saveWords() {
const text = document.getElementById('wordInput').value.trim()
allWords = text.split('\n').map(w => w.trim()).filter(Boolean)
localStorage.setItem('spellingWords', JSON.stringify(allWords))
displayWordList()
alert("Word list saved!")
}
function clearWords() {
localStorage.removeItem('spellingWords')
allWords = []
displayWordList()
alert("Saved words cleared.")
}
function displayWordList() {
const ul = document.getElementById('wordListDisplay')
ul.innerHTML = allWords.map(word => `<li>${word}</li>`).join('')
}
function handleFileUpload(event) {
const file = event.target.files[0]
if (!file) return
const reader = new FileReader()
reader.onload = function(e) {
const text = e.target.result
const newWords = text.includes(",") ? text.split(",") : text.split("\n")
allWords = newWords.map(w => w.trim()).filter(Boolean)
localStorage.setItem('spellingWords', JSON.stringify(allWords))
displayWordList()
alert("Words uploaded and saved!")
}
reader.readAsText(file)
}
// 🎮 Spelling Game
let allWords = [], words = [], current = 0, results = []
function showStudent() {
document.getElementById('studentPanel').classList.remove('hidden')
document.getElementById('adminPanel').classList.add('hidden')
}
function startGame() {
loadWords()
if (allWords.length === 0) {
alert("No words found. Please add words in Admin Panel.")
return
}
words = [...allWords]
current = 0
results = []
document.getElementById('summary').classList.add('hidden')
showWord()
}
function showWord() {
const wordBox = document.getElementById('wordBox')
const wordEl = document.getElementById('word')
wordBox.classList.remove('hidden')
wordEl.textContent = words[current]
setTimeout(() => {
wordBox.classList.add('hidden')
document.getElementById('inputSection').classList.remove('hidden')
document.getElementById('userInput').value = ''
document.getElementById('userInput').focus()
}, 2000)
}
function submitAnswer() {
const input = document.getElementById('userInput').value.trim()
results.push({ word: words[current], attempt: input })
current++
document.getElementById('inputSection').classList.add('hidden')
if (current < words.length) {
showWord()
} else {
showSummary()
}
}
function showSummary() {
const summary = document.getElementById('summary')
let correct = 0
let missed = []
const report = results.map(({ word, attempt }) => {
const isCorrect = word.toLowerCase() === attempt.toLowerCase()
if (isCorrect) correct++
else missed.push(word)
return `<li>${word} — <strong style="color:${isCorrect ? 'green' : 'red'}">${attempt || '(no answer)'}</strong></li>`
}).join('')
summary.innerHTML = `
<h2>Results</h2>
<p>You got ${correct} out of ${words.length} correct (${Math.round((correct / words.length) * 100)}%)</p>
<ul>${report}</ul>
${missed.length ? `<button onclick="retryMissed(${JSON.stringify(missed)})">Retry Missed Words</button>` : ''}
`
summary.classList.remove('hidden')
}
function retryMissed(missedWords) {
words = missedWords
current = 0
results = []
document.getElementById('summary').classList.add('hidden')
showWord()
}
async function loadWords() {
const user = JSON.parse(localStorage.getItem('loggedInUser'))
if (!user) return allWords = []
const res = await fetch(`/getWordList?user=${encodeURIComponent(user.username)}`)
allWords = await res.json()
displayWordList()
}
async function saveWords() {
const user = JSON.parse(localStorage.getItem('loggedInUser'))
if (!user) 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: user.username, words: allWords })
})
displayWordList()
alert("✅ Word list saved to server")
}
async function clearWords() {
const user = JSON.parse(localStorage.getItem('loggedInUser'))
if (!user) return
allWords = []
await fetch('/saveWordList', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: user.username, words: [] })
})
displayWordList()
alert("🗑 Word list cleared")
}
function populateWordUserDropdown() {
const dropdown = document.getElementById("wordUserSelect")
dropdown.innerHTML = `<option value="">-- Select a user --</option>`
users.forEach(user => {
dropdown.innerHTML += `<option value="${user.username}">${user.username}</option>`
})
}
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()
}