badges
This commit is contained in:
1 parent
c4844eecff
commit
6c77a8f407
5 files changed
+147
-32
No files matched your search
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"nate": [
|
||||
"Perfect Round",
|
||||
"No Misses",
|
||||
"Accuracy Master",
|
||||
"Practice Streak",
|
||||
"Vocabulary Pro"
|
||||
]
|
||||
}
|
||||
+14
-2
@@ -51,6 +51,7 @@
|
||||
<button onclick="switchTab('tabWords')">📝 Word Lists</button>
|
||||
<button onclick="switchTab('tabAnalytics')">📊 Analytics</button>
|
||||
<button onclick="switchTab('tabResults')">📊 Results</button>
|
||||
<button onclick="switchTab('tabBadges')">🎖 Badges</button>
|
||||
<button onclick="switchTab('tabTheme')">🎨 Theme</button>
|
||||
</div>
|
||||
|
||||
@@ -119,7 +120,15 @@
|
||||
|
||||
<button onclick="refreshAnalytics()">🔄 Refresh</button>
|
||||
</div>
|
||||
|
||||
<!-- 🎖 Badges -->
|
||||
<div id="tabBadges" class="adminTab hidden">
|
||||
<h3>🎖 Badge Viewer</h3>
|
||||
<ul id="badgeUserList"></ul>
|
||||
<div id="badgeDetails" class="hidden">
|
||||
<h4 id="badgeUserTitle"></h4>
|
||||
<ul id="badgeUserBadges"></ul>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 🎨 Theme Tab -->
|
||||
<div id="tabTheme" class="adminTab hidden">
|
||||
<h3>🎨 Select Theme</h3>
|
||||
@@ -146,7 +155,10 @@
|
||||
<input type="text" id="userInput" placeholder="Type your answer" autocomplete="off" />
|
||||
<button onclick="submitAnswer()">Submit</button>
|
||||
</div>
|
||||
|
||||
<div id="badgeDisplay" class="badgeSection hidden">
|
||||
<h3>🎖 Badges Earned</h3>
|
||||
<ul id="badgeList"></ul>
|
||||
</div>
|
||||
<div id="summary" class="hidden"></div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -193,12 +193,18 @@ function switchTab(targetId) {
|
||||
document.querySelectorAll('#adminTabs button').forEach(btn => btn.classList.remove('active'));
|
||||
|
||||
document.getElementById(targetId).classList.remove('hidden');
|
||||
|
||||
const buttons = document.querySelectorAll('#adminTabs button');
|
||||
buttons.forEach(btn => {
|
||||
if (btn.getAttribute('onclick')?.includes(targetId)) {
|
||||
btn.classList.add('active');
|
||||
}
|
||||
});
|
||||
|
||||
// 🏆 Load badge viewer if needed
|
||||
if (targetId === 'tabBadges') {
|
||||
loadBadgeViewer();
|
||||
}
|
||||
}
|
||||
async function loadUserDropdowns() {
|
||||
try {
|
||||
@@ -277,4 +283,43 @@ async function refreshAnalytics() {
|
||||
console.error("📉 Failed to refresh analytics:", err);
|
||||
summary.innerHTML = '<li style="color:red">Error loading analytics.</li>';
|
||||
}
|
||||
}
|
||||
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 = `<button onclick="showUserBadges('${username}')">${username}</button>`;
|
||||
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 = "<li><em>No badges earned yet.</em></li>";
|
||||
} else {
|
||||
badges.forEach(b => {
|
||||
const li = document.createElement("li");
|
||||
li.textContent = `🏅 ${b}`;
|
||||
badgeList.appendChild(li);
|
||||
});
|
||||
}
|
||||
|
||||
details.classList.remove("hidden");
|
||||
});
|
||||
}
|
||||
+50
-29
@@ -10,7 +10,6 @@ function showStudent() {
|
||||
studentPanel?.classList.remove('hidden');
|
||||
adminPanel?.classList.add('hidden');
|
||||
|
||||
// Delay button reveal until DOM is fully settled
|
||||
setTimeout(() => {
|
||||
const btn = document.getElementById('startGameBtn');
|
||||
if (btn) {
|
||||
@@ -41,6 +40,7 @@ async function startGame() {
|
||||
results = [];
|
||||
|
||||
document.getElementById('summary')?.classList.add('hidden');
|
||||
document.getElementById('badgeDisplay')?.classList.add('hidden');
|
||||
showWord();
|
||||
}
|
||||
|
||||
@@ -101,32 +101,57 @@ function showSummary() {
|
||||
|
||||
summary.classList.remove('hidden');
|
||||
|
||||
// 📨 Save results to server
|
||||
const user = JSON.parse(localStorage.getItem('loggedInUser'));
|
||||
if (user) {
|
||||
console.log("📡 Sending results to server:", {
|
||||
username: user.username,
|
||||
results
|
||||
});
|
||||
// 🏆 Badge evaluation
|
||||
const earnedBadges = [];
|
||||
if (correct === words.length) earnedBadges.push("Perfect Round");
|
||||
if (missed.length === 0) earnedBadges.push("No Misses");
|
||||
|
||||
const score = correct;
|
||||
const completed = true;
|
||||
const user = JSON.parse(localStorage.getItem('loggedInUser'));
|
||||
if (user) {
|
||||
// 📡 Save structured results
|
||||
fetch('/saveResults', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
username: user.username,
|
||||
result: {
|
||||
score: correct,
|
||||
completed: true,
|
||||
answers: results.map(({ word, attempt }) => ({
|
||||
word,
|
||||
correct: word.toLowerCase() === attempt.toLowerCase()
|
||||
}))
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
fetch('/saveResults', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
username: user.username,
|
||||
result: {
|
||||
score,
|
||||
completed,
|
||||
answers: results.map(({ word, attempt }) => ({
|
||||
word,
|
||||
correct: word.toLowerCase() === attempt.toLowerCase()
|
||||
}))
|
||||
// 🎖 Send earned badges
|
||||
if (earnedBadges.length) {
|
||||
fetch('/awardBadges', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
username: user.username,
|
||||
badges: earnedBadges
|
||||
})
|
||||
});
|
||||
|
||||
// 🎨 Display badges
|
||||
const badgeSection = document.getElementById("badgeDisplay");
|
||||
const badgeList = document.getElementById("badgeList");
|
||||
|
||||
badgeList.innerHTML = "";
|
||||
earnedBadges.forEach(b => {
|
||||
const li = document.createElement("li");
|
||||
li.textContent = `🏅 ${b}`;
|
||||
badgeList.appendChild(li);
|
||||
});
|
||||
|
||||
badgeSection.classList.remove("hidden");
|
||||
} else {
|
||||
document.getElementById("badgeDisplay")?.classList.add("hidden");
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 🔁 Retry only the missed words
|
||||
@@ -136,10 +161,6 @@ function retryMissed(missedWords) {
|
||||
results = [];
|
||||
|
||||
document.getElementById('summary')?.classList.add('hidden');
|
||||
document.getElementById('badgeDisplay')?.classList.add('hidden');
|
||||
showWord();
|
||||
}
|
||||
|
||||
// added remove later
|
||||
console.log("showStudent fired");
|
||||
// added remove later
|
||||
}
|
||||
@@ -15,7 +15,8 @@ if (!fs.existsSync(DATA_DIR)) fs.mkdirSync(DATA_DIR);
|
||||
const files = {
|
||||
users: 'users.json',
|
||||
wordlists: 'wordlists.json',
|
||||
results: 'results.json'
|
||||
results: 'results.json',
|
||||
badges: 'badges.json'
|
||||
};
|
||||
|
||||
for (const file of Object.values(files)) {
|
||||
@@ -163,4 +164,31 @@ app.post('/saveResults', (req, res) => {
|
||||
app.get('/getUsers', (req, res) => {
|
||||
const users = readJsonSafe(path.join(DATA_DIR, files.users), []);
|
||||
res.json(users);
|
||||
});
|
||||
|
||||
|
||||
const badgePath = path.join(DATA_DIR, files.badges);
|
||||
app.post('/awardBadges', (req, res) => {
|
||||
const { username, badges } = req.body;
|
||||
if (
|
||||
typeof username !== 'string' ||
|
||||
!Array.isArray(badges)
|
||||
) return res.status(400).send("Invalid badge format");
|
||||
|
||||
const allBadges = readJsonSafe(badgePath);
|
||||
if (!allBadges[username]) allBadges[username] = [];
|
||||
|
||||
badges.forEach(b => {
|
||||
if (!allBadges[username].includes(b)) {
|
||||
allBadges[username].push(b);
|
||||
}
|
||||
});
|
||||
|
||||
fs.writeFileSync(badgePath, JSON.stringify(allBadges, null, 2));
|
||||
res.send(`🎉 Badges updated for ${username}`);
|
||||
});
|
||||
app.get('/getBadges', (req, res) => {
|
||||
const badgePath = path.join(DATA_DIR, files.badges);
|
||||
const badges = readJsonSafe(badgePath);
|
||||
res.json(badges);
|
||||
});
|
||||
Reference in new issue
Block a user