-
-
+
-
Upload Word File
diff --git a/public/js/admin.js b/public/js/admin.js
index e43f9a4..4c504b8 100644
--- a/public/js/admin.js
+++ b/public/js/admin.js
@@ -1,4 +1,81 @@
// Global variables for admin functionality
+// --- Weeks input logic ---
+// Populate active week dropdown from weeksContainer
+window.populateActiveWeekDropdown = function() {
+ const select = document.getElementById('activeWeekSelect');
+ select.innerHTML = '';
+ document.querySelectorAll('#weeksContainer .week-input-card').forEach(card => {
+ const date = card.querySelector('.week-date').value;
+ if (date) {
+ const option = document.createElement('option');
+ option.value = date;
+ option.textContent = date;
+ select.appendChild(option);
+ }
+ });
+};
+
+// Call populateActiveWeekDropdown whenever weeks change
+const observer = new MutationObserver(() => populateActiveWeekDropdown());
+observer.observe(document.getElementById('weeksContainer'), { childList: true, subtree: true });
+
+window.setActiveWeek = async function() {
+ const selectedUser = document.getElementById('wordUserSelect')?.value;
+ const activeWeek = document.getElementById('activeWeekSelect')?.value;
+ if (!selectedUser || !activeWeek) {
+ alert('⚠️ Please select a user and week.');
+ return;
+ }
+ await fetch('/setActiveWeek', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ username: selectedUser, activeWeek })
+ });
+ alert(`✅ Active week set for ${selectedUser}: ${activeWeek}`);
+}
+window.addWeekInput = function() {
+ const container = document.getElementById('weeksContainer');
+ const weekIdx = container.children.length;
+ const weekDiv = document.createElement('div');
+ weekDiv.className = 'week-input-card';
+ weekDiv.style = 'border:1px solid #ccc; padding:1rem; margin-bottom:1rem;';
+ weekDiv.innerHTML = `
+
+
+
+ `;
+ container.appendChild(weekDiv);
+}
+
+window.saveWeeks = async function() {
+ const selectedUser = document.getElementById('wordUserSelect')?.value;
+ if (!selectedUser) {
+ alert('⚠️ Please select a user before saving.');
+ return;
+ }
+ const weeks = [];
+ document.querySelectorAll('#weeksContainer .week-input-card').forEach(card => {
+ const date = card.querySelector('.week-date').value;
+ const words = card.querySelector('.week-words').value.split('\n').map(w => w.trim()).filter(Boolean);
+ if (date && words.length) {
+ weeks.push({ date, words });
+ }
+ });
+ if (!weeks.length) {
+ alert('⚠️ Please enter at least one week with a date and words.');
+ return;
+ }
+ await fetch('/saveWeeksWordList', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ username: selectedUser, weeks })
+ });
+ alert(`✅ Weeks saved for ${selectedUser}`);
+}
+
+window.clearWeeks = function() {
+ document.getElementById('weeksContainer').innerHTML = '';
+}
let allWords = [];
let adminUsers = [];
@@ -675,28 +752,23 @@ function populatePasswordUserDropdown() {
}
// Add event listeners for admin tabs as backup to onclick
-document.addEventListener('DOMContentLoaded', () => {
- // Setup tab click listeners
+window.addEventListener('DOMContentLoaded', () => {
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');
- }
+ const tabId = button.getAttribute('data-tab');
+ if (tabId) {
+ 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);
+ }, 500);
});
\ No newline at end of file
diff --git a/public/js/game.js b/public/js/game.js
index 033389b..89ce94f 100644
--- a/public/js/game.js
+++ b/public/js/game.js
@@ -100,14 +100,24 @@ window.startGame = async function() {
const data = await response.json();
console.log('Received words:', data);
- if (!data.words || data.words.length === 0) {
+ let weekWords = [];
+ // If weeks format and activeWeek is set, use only that week's words
+ if (data.words && Array.isArray(data.words)) {
+ weekWords = data.words;
+ } else if (data.words && data.words.weeks && data.words.activeWeek) {
+ const active = data.words.activeWeek;
+ const found = data.words.weeks.find(w => w.date === active);
+ if (found) weekWords = found.words;
+ }
+
+ if (!weekWords.length) {
alert('No words found for practice');
return;
}
// Start game
- words = [...data.words];
- typingWords = [...data.words]; // Initialize typing words too
+ words = [...weekWords];
+ typingWords = [...weekWords]; // Initialize typing words too
currentIndex = 0;
results = []; // Reset results array
showWord();
diff --git a/public/js/ui.js b/public/js/ui.js
index ea1edd5..fb7bb78 100644
--- a/public/js/ui.js
+++ b/public/js/ui.js
@@ -98,11 +98,12 @@ function showAdmin() {
console.warn("Could not load user dropdowns:", e);
});
- // Set default tab - use a slight delay to ensure DOM is ready
+ // Set default tab to tabWords and enable tab buttons
setTimeout(() => {
+ document.querySelectorAll('#adminTabs button').forEach(btn => btn.disabled = false);
if (typeof window.switchTab === 'function') {
- window.switchTab("tabUsers");
- console.log("✅ Default tab set");
+ window.switchTab("tabWords");
+ console.log("✅ Default tab set to tabWords");
} else {
console.error("switchTab function not available");
}
diff --git a/server.js b/server.js
index 2ca5886..65fa3bc 100644
--- a/server.js
+++ b/server.js
@@ -296,6 +296,46 @@ app.get('/getWordList', (req, res) => {
});
app.post('/saveWordList', (req, res) => {
+// Save multiple weeks with dates for a user
+app.post('/saveWeeksWordList', (req, res) => {
+// Set active week for a user
+app.post('/setActiveWeek', (req, res) => {
+ const { username, activeWeek } = req.body;
+ if (typeof username !== 'string' || typeof activeWeek !== 'string') {
+ return res.status(400).send('Invalid data');
+ }
+ const wordlistsPath = path.join(DATA_DIR, files.wordlists);
+ const wordlists = readJsonSafe(wordlistsPath);
+ if (!wordlists[username]) {
+ return res.status(404).send('User not found');
+ }
+ if (typeof wordlists[username] === 'object') {
+ wordlists[username].activeWeek = activeWeek;
+ } else {
+ wordlists[username] = { weeks: [], activeWeek };
+ }
+ fs.writeFileSync(wordlistsPath, JSON.stringify(wordlists, null, 2));
+ res.send(`✅ Active week set for ${username}: ${activeWeek}`);
+});
+ const { username, weeks } = req.body;
+ if (
+ typeof username !== 'string' ||
+ !Array.isArray(weeks) ||
+ weeks.length > 100 ||
+ weeks.some(w => typeof w.date !== 'string' || !Array.isArray(w.words) || w.words.some(word => typeof word !== 'string'))
+ ) return res.status(400).send('Invalid weeks data');
+
+ const userList = readJsonSafe(path.join(DATA_DIR, files.users), []);
+ if (!userList.find(u => u.username === username)) {
+ return res.status(404).send('User not found');
+ }
+
+ const wordlistsPath = path.join(DATA_DIR, files.wordlists);
+ const wordlists = readJsonSafe(wordlistsPath);
+ wordlists[username] = { weeks };
+ fs.writeFileSync(wordlistsPath, JSON.stringify(wordlists, null, 2));
+ res.send(`✅ Weeks word list saved for ${username}`);
+});
const { username, words } = req.body;
if (
typeof username !== 'string' ||