Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Sam's Birthday - Interactive Quiz π</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> | |
<style> | |
body { | |
font-family: 'Inter', sans-serif; | |
background-color: #f0f4f8; | |
} | |
.game-title { color: #3730a3; } | |
.question-card { | |
background-color: #ffffff; | |
border-radius: 12px; | |
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); | |
page-break-inside: avoid; | |
} | |
.question-text-strong { color: #4338ca; } | |
.answer-label { color: #3f3f46; } | |
input[type="text"].answer-input, input[type="text"].name-input { | |
border: 1px solid #d1d5db; | |
padding: 0.5rem 0.75rem; | |
border-radius: 0.375rem; | |
width: 100%; | |
box-sizing: border-box; | |
} | |
input[type="text"].answer-input:focus, input[type="text"].name-input:focus { | |
outline: none; | |
border-color: #4f46e5; | |
box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.3); | |
} | |
.correct-answer-section { | |
background-color: #f0fdf4; | |
border-left: 4px solid #22c55e; | |
padding: 0.5rem 0.75rem; | |
border-radius: 0.25rem; | |
margin-top: 0.5rem; /* Space between answer and its key */ | |
} | |
.correct-answer-text { color: #15803d; } | |
.btn-action { | |
background-color: #4f46e5; | |
color: white; | |
transition: background-color 0.3s ease; | |
} | |
.btn-action:hover { background-color: #4338ca; } | |
.btn-save { | |
background-color: #10b981; /* Emerald-500 */ | |
} | |
.btn-save:hover { | |
background-color: #059669; /* Emerald-600 */ | |
} | |
.feedback-emoji { | |
font-size: 1.25rem; /* Make emojis a bit bigger */ | |
margin-left: 0.5rem; | |
} | |
.score-display { | |
background-color: #e0e7ff; /* Indigo-100 */ | |
color: #3730a3; /* Indigo-800 */ | |
padding: 0.75rem 1rem; | |
border-radius: 8px; | |
font-weight: 600; | |
text-align: center; | |
} | |
/* Initially hide answer sections and feedback emojis */ | |
.correct-answer-container.hidden-answers .correct-answer-section { display: none; } | |
.feedback-emoji { display: none; } | |
.correct-answer-container.checked-answers .feedback-emoji { display: inline-block; } | |
@media print { | |
body { background-color: #ffffff; -webkit-print-color-adjust: exact; print-color-adjust: exact; } | |
.btn-action, .btn-save, .print-hide, #nameInputSection, #scoreDisplaySection { display: none ; } | |
.question-card { box-shadow: none; border: 1px solid #e5e7eb; margin-bottom: 1rem; } | |
.correct-answer-section { display: block ; background-color: #f9fafb ; border-left: 2px solid #6b7280 ; padding: 0.25rem 0.5rem; } | |
.correct-answer-text { color: #1f2937 ; } | |
input[type="text"].answer-input { border-bottom: 1px solid #9ca3af; border-top: none; border-left: none; border-right: none; border-radius: 0; padding-left: 0; padding-right: 0; } | |
input[type="text"].answer-input::placeholder { color: transparent; } | |
.cards-container-grid { grid-template-columns: repeat(1, minmax(0, 1fr)) ; } | |
.page-container { padding: 0.5in ; max-width: 100% ; } | |
.game-title, .footer-text { text-align: center; } | |
.feedback-emoji { display: inline-block ; } /* Show emojis on print if answers are checked */ | |
} | |
</style> | |
</head> | |
<body class="p-4 sm:p-6 md:p-8"> | |
<div id="pageContainer" class="page-container max-w-5xl mx-auto"> | |
<header class="text-center mb-6"> | |
<h1 class="game-title text-3xl sm:text-4xl font-bold">Sam's Birthday - Interactive Quiz π</h1> | |
</header> | |
<div id="nameInputSection" class="mb-6 max-w-md mx-auto"> | |
<label for="playerNameInput" class="block text-sm font-medium text-gray-700 mb-1">Player Name:</label> | |
<input type="text" id="playerNameInput" class="name-input" placeholder="Enter your name (e.g., Aaron)"> | |
</div> | |
<div id="scoreDisplaySection" class="mb-6 text-center" style="display: none;"> | |
<p id="scoreText" class="score-display">Score: 0 / 13 (0%)</p> | |
</div> | |
<div class="text-center mb-8 space-x-4 print-hide"> | |
<button id="checkAnswersBtn" class="btn-action py-2 px-6 rounded-lg font-semibold text-lg"> | |
Reveal Answers | |
</button> | |
<button id="saveProgressBtn" class="btn-action btn-save py-2 px-6 rounded-lg font-semibold text-lg"> | |
Save Progress | |
</button> | |
</div> | |
<div id="cardsContainer" class="correct-answer-container hidden-answers cards-container-grid grid grid-cols-1 md:grid-cols-2 gap-6"> | |
<!-- Question cards will be injected here by JavaScript --> | |
</div> | |
<footer class="mt-12 text-center footer-text"> | |
<p class="text-xs text-gray-500">© 2025 Fun Times</p> | |
</footer> | |
</div> | |
<script type="module"> | |
// Firebase Imports | |
import { initializeApp } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-app.js"; | |
import { getAuth, signInAnonymously, onAuthStateChanged, signInWithCustomToken } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-auth.js"; | |
import { getFirestore, doc, setDoc, getDoc, serverTimestamp, updateDoc } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-firestore.js"; | |
// IMPORTANT: Replace with your actual Firebase config | |
const firebaseConfig = typeof __firebase_config !== 'undefined' ? JSON.parse(__firebase_config) : { | |
apiKey: "YOUR_API_KEY", | |
authDomain: "YOUR_AUTH_DOMAIN", | |
projectId: "YOUR_PROJECT_ID", | |
storageBucket: "YOUR_STORAGE_BUCKET", | |
messagingSenderId: "YOUR_MESSAGING_SENDER_ID", | |
appId: "YOUR_APP_ID" | |
}; | |
const appId = typeof __app_id !== 'undefined' ? __app_id : 'sams-birthday-quiz-default'; | |
// Initialize Firebase | |
const app = initializeApp(firebaseConfig); | |
const auth = getAuth(app); | |
const db = getFirestore(app); | |
let userId = null; | |
let gameDocRef = null; | |
let hasUserTyped = false; // Track if user has typed anything | |
const gameData = [ | |
{ question: "Name Sam's all time favorite musical group? Hint: Their debut album was in 1986.", answer: "Beastie Boys", id: "q1" }, | |
{ question: "What is Sam's favorite game? Hint: She grew up playing this game with her Grandma.", answer: "Yahtzee", id: "q2" }, | |
{ | |
question: "In her 20's, Sam lived briefly in which two states?", | |
answer: ["California", "Texas"], answerLabels: ["First State:", "Second State:"], id: "q3", parts: 2 | |
}, | |
{ question: "Name Sam's longest Employer. Hint: Close to 20 years employment.", answer: "MSU", alternatives: ["Metropolitan State University"], id: "q4" }, | |
{ | |
question: "Sam holds a BA degree in _____, and Masters degree in _____?", | |
answer: ["Political Science", "Public Administration"], answerLabels: ["BA Degree:", "Masters Degree:"], id: "q5", parts: 2 | |
}, | |
{ question: "What is Sam's biggest fear?", answer: "Snakes", id: "q6" }, | |
{ question: "Name the first National Park that Sam and Aaron travelled to. Hint: It was during COVID.", answer: "Death Valley", alternatives: ["Death Valley National Park"], id: "q7" }, | |
{ question: "Sam and her BFF have matching tattoos which is the title of a song. What is the Paul McCartney song?", answer: "Let It Be", id: "q8" }, | |
{ question: "What animal would Sam like to have as an indoor pet?", answer: "Raccoon", id: "q9" }, | |
{ | |
question: "If Sam won $1000 shopping spree at the store of her choice, which store would it be?", | |
answer: "Menards", bonusPrompt: "Bonus: Why would she choose this store?", bonusAnswer: "Rebate", id: "q10", parts: 1 // Main answer is 1 part, bonus is separate | |
} | |
]; | |
const TOTAL_POSSIBLE_POINTS = 13; // 9 single + 2 for q3 + 2 for q5 = 13 (bonus is one of these) | |
const cardsContainerEl = document.getElementById('cardsContainer'); | |
const checkAnswersBtn = document.getElementById('checkAnswersBtn'); | |
const saveProgressBtn = document.getElementById('saveProgressBtn'); | |
const playerNameInputEl = document.getElementById('playerNameInput'); | |
const scoreTextEl = document.getElementById('scoreText'); | |
const scoreDisplaySectionEl = document.getElementById('scoreDisplaySection'); | |
function normalizeAnswer(answer) { | |
return answer.trim().toLowerCase(); | |
} | |
function renderCards(savedAnswers = {}, savedFeedback = {}) { | |
cardsContainerEl.innerHTML = ''; | |
hasUserTyped = Object.keys(savedAnswers).length > 0 || !!playerNameInputEl.value; | |
gameData.forEach((item, index) => { | |
const card = document.createElement('div'); | |
card.className = 'question-card p-5 sm:p-6 flex flex-col'; | |
let inputFieldsHTML = ''; | |
let answerSectionsHTML = ''; | |
if (item.parts === 2 && item.answerLabels) { // Multi-part questions (Q3, Q5) | |
inputFieldsHTML = ` | |
<div class="mb-3 mt-3"> | |
<label for="${item.id}-part1" class="block text-sm font-medium answer-label mb-1">${item.answerLabels[0]}</label> | |
<div class="flex items-center"> | |
<input type="text" id="${item.id}-part1" data-question-id="${item.id}" data-part-index="0" class="answer-input w-full" placeholder="${item.answerLabels[0].replace(':', '...')}" value="${savedAnswers[`${item.id}-part1`] || ''}"> | |
<span id="feedback-${item.id}-part1" class="feedback-emoji">${savedFeedback[`${item.id}-part1`] || ''}</span> | |
</div> | |
</div> | |
<div class="mb-3"> | |
<label for="${item.id}-part2" class="block text-sm font-medium answer-label mb-1">${item.answerLabels[1]}</label> | |
<div class="flex items-center"> | |
<input type="text" id="${item.id}-part2" data-question-id="${item.id}" data-part-index="1" class="answer-input w-full" placeholder="${item.answerLabels[1].replace(':', '...')}" value="${savedAnswers[`${item.id}-part2`] || ''}"> | |
<span id="feedback-${item.id}-part2" class="feedback-emoji">${savedFeedback[`${item.id}-part2`] || ''}</span> | |
</div> | |
</div>`; | |
answerSectionsHTML = ` | |
<div class="correct-answer-section mb-2"> | |
<p class="text-xs font-medium text-gray-600">Correct Answer (${item.answerLabels[0].replace(':', '')}):</p> | |
<p class="correct-answer-text font-semibold">${item.answer[0]}</p> | |
</div> | |
<div class="correct-answer-section"> | |
<p class="text-xs font-medium text-gray-600">Correct Answer (${item.answerLabels[1].replace(':', '')}):</p> | |
<p class="correct-answer-text font-semibold">${item.answer[1]}</p> | |
</div>`; | |
} else if (item.bonusPrompt) { // Bonus question (Q10) | |
inputFieldsHTML = ` | |
<div class="mb-3 mt-3"> | |
<label for="${item.id}-main" class="block text-sm font-medium answer-label mb-1">Your Answer (Main):</label> | |
<div class="flex items-center"> | |
<input type="text" id="${item.id}-main" data-question-id="${item.id}" data-part-type="main" class="answer-input w-full" placeholder="Main answer..." value="${savedAnswers[`${item.id}-main`] || ''}"> | |
<span id="feedback-${item.id}-main" class="feedback-emoji">${savedFeedback[`${item.id}-main`] || ''}</span> | |
</div> | |
</div> | |
<p class="text-sm font-semibold bonus-prompt-text mt-4 mb-1">${item.bonusPrompt}</p> | |
<div class="mb-3"> | |
<label for="${item.id}-bonus" class="block text-sm font-medium answer-label mb-1">Your Answer (Bonus):</label> | |
<div class="flex items-center"> | |
<input type="text" id="${item.id}-bonus" data-question-id="${item.id}" data-part-type="bonus" class="answer-input w-full" placeholder="Bonus answer..." value="${savedAnswers[`${item.id}-bonus`] || ''}"> | |
<span id="feedback-${item.id}-bonus" class="feedback-emoji">${savedFeedback[`${item.id}-bonus`] || ''}</span> | |
</div> | |
</div>`; | |
answerSectionsHTML = ` | |
<div class="correct-answer-section mb-2"> | |
<p class="text-xs font-medium text-gray-600">Correct Answer (Main):</p> | |
<p class="correct-answer-text font-semibold">${item.answer}</p> | |
</div> | |
<div class="correct-answer-section"> | |
<p class="text-xs font-medium text-gray-600">Correct Answer (Bonus):</p> | |
<p class="correct-answer-text font-semibold">${item.bonusAnswer}</p> | |
</div>`; | |
} else { // Single-part questions | |
inputFieldsHTML = ` | |
<div class="mb-3 mt-3"> | |
<label for="${item.id}" class="block text-sm font-medium answer-label mb-1">Your Answer:</label> | |
<div class="flex items-center"> | |
<input type="text" id="${item.id}" data-question-id="${item.id}" class="answer-input w-full" placeholder="Type your answer here..." value="${savedAnswers[item.id] || ''}"> | |
<span id="feedback-${item.id}" class="feedback-emoji">${savedFeedback[item.id] || ''}</span> | |
</div> | |
</div>`; | |
answerSectionsHTML = ` | |
<div class="correct-answer-section"> | |
<p class="text-xs font-medium text-gray-600">Correct Answer:</p> | |
<p class="correct-answer-text font-semibold">${item.answer}</p> | |
</div>`; | |
} | |
card.innerHTML = ` | |
<div class="flex-grow"> | |
<h3 class="text-lg font-semibold question-text-strong mb-2"> | |
<span class="question-number">${index + 1}.</span> | |
${item.question} | |
</h3> | |
${inputFieldsHTML} | |
</div> | |
<div class="mt-auto answer-key-container"> | |
${answerSectionsHTML} | |
</div>`; | |
cardsContainerEl.appendChild(card); | |
}); | |
updateButtonStates(); | |
attachInputListeners(); // Attach listeners after rendering | |
if (Object.keys(savedFeedback).length > 0) { // If feedback exists, means answers were checked | |
cardsContainerEl.classList.remove('hidden-answers'); | |
cardsContainerEl.classList.add('checked-answers'); | |
checkAnswersBtn.textContent = 'Answers Checked'; | |
} else { | |
cardsContainerEl.classList.add('hidden-answers'); | |
cardsContainerEl.classList.remove('checked-answers'); | |
} | |
} | |
function attachInputListeners() { | |
document.querySelectorAll('.answer-input, #playerNameInput').forEach(input => { | |
input.addEventListener('input', () => { | |
hasUserTyped = true; | |
updateButtonStates(); | |
}); | |
}); | |
} | |
function updateButtonStates() { | |
if (hasUserTyped || playerNameInputEl.value.trim() !== "") { | |
checkAnswersBtn.textContent = 'Check Answers with Key'; | |
} else { | |
checkAnswersBtn.textContent = 'Reveal Answers'; | |
} | |
// If answers are already checked, reflect that | |
if (cardsContainerEl.classList.contains('checked-answers') && !cardsContainerEl.classList.contains('hidden-answers')) { | |
checkAnswersBtn.textContent = 'Answers Checked'; | |
} | |
} | |
function collectAnswers() { | |
const userAnswers = {}; | |
document.querySelectorAll('.answer-input').forEach(input => { | |
const questionId = input.dataset.questionId; | |
if (input.dataset.partIndex) { // Multi-part (Q3, Q5) | |
userAnswers[`${questionId}-part${parseInt(input.dataset.partIndex) + 1}`] = input.value; | |
} else if (input.dataset.partType) { // Bonus (Q10) | |
userAnswers[`${questionId}-${input.dataset.partType}`] = input.value; | |
} else { // Single part | |
userAnswers[questionId] = input.value; | |
} | |
}); | |
return userAnswers; | |
} | |
function handleCheckAnswers() { | |
cardsContainerEl.classList.remove('hidden-answers'); | |
cardsContainerEl.classList.add('checked-answers'); // To show emojis | |
let currentScore = 0; | |
const userAnswers = collectAnswers(); | |
const feedbackEmojis = {}; | |
gameData.forEach(item => { | |
if (item.parts === 2) { // Q3, Q5 | |
for (let i = 0; i < 2; i++) { | |
const userAnswer = normalizeAnswer(userAnswers[`${item.id}-part${i + 1}`] || ''); | |
const correctAnswer = normalizeAnswer(item.answer[i]); | |
if (userAnswer === correctAnswer) { | |
currentScore++; | |
feedbackEmojis[`${item.id}-part${i+1}`] = 'β '; | |
} else { | |
feedbackEmojis[`${item.id}-part${i+1}`] = 'β'; | |
} | |
document.getElementById(`feedback-${item.id}-part${i+1}`).textContent = feedbackEmojis[`${item.id}-part${i+1}`]; | |
} | |
} else if (item.bonusPrompt) { // Q10 | |
const mainUserAnswer = normalizeAnswer(userAnswers[`${item.id}-main`] || ''); | |
const mainCorrectAnswer = normalizeAnswer(item.answer); | |
let mainCorrect = false; | |
if (mainUserAnswer === mainCorrectAnswer || (item.alternatives && item.alternatives.map(normalizeAnswer).includes(mainUserAnswer))) { | |
currentScore++; | |
feedbackEmojis[`${item.id}-main`] = 'β '; | |
mainCorrect = true; | |
} else { | |
feedbackEmojis[`${item.id}-main`] = 'β'; | |
} | |
document.getElementById(`feedback-${item.id}-main`).textContent = feedbackEmojis[`${item.id}-main`]; | |
// Only score bonus if main was correct (optional rule, implementing as strict for now) | |
// if (mainCorrect) { | |
const bonusUserAnswer = normalizeAnswer(userAnswers[`${item.id}-bonus`] || ''); | |
const bonusCorrectAnswer = normalizeAnswer(item.bonusAnswer); | |
if (bonusUserAnswer === bonusCorrectAnswer) { | |
currentScore++; //This makes it 13 points total | |
feedbackEmojis[`${item.id}-bonus`] = 'β '; | |
} else { | |
feedbackEmojis[`${item.id}-bonus`] = 'β'; | |
} | |
// } else { | |
// feedbackEmojis[`${item.id}-bonus`] = 'β'; // Indicate bonus not attempted or main was wrong | |
// } | |
document.getElementById(`feedback-${item.id}-bonus`).textContent = feedbackEmojis[`${item.id}-bonus`]; | |
} else { // Single answer questions | |
const userAnswer = normalizeAnswer(userAnswers[item.id] || ''); | |
const correctAnswer = normalizeAnswer(item.answer); | |
if (userAnswer === correctAnswer || (item.alternatives && item.alternatives.map(normalizeAnswer).includes(userAnswer))) { | |
currentScore++; | |
feedbackEmojis[item.id] = 'β '; | |
} else { | |
feedbackEmojis[item.id] = 'β'; | |
} | |
document.getElementById(`feedback-${item.id}`).textContent = feedbackEmojis[item.id]; | |
} | |
}); | |
const percentage = TOTAL_POSSIBLE_POINTS > 0 ? Math.round((currentScore / TOTAL_POSSIBLE_POINTS) * 100) : 0; | |
scoreTextEl.textContent = `Score: ${currentScore} / ${TOTAL_POSSIBLE_POINTS} (${percentage}%)`; | |
scoreDisplaySectionEl.style.display = 'block'; | |
checkAnswersBtn.textContent = 'Answers Checked'; | |
hasUserTyped = true; // Ensure button state remains "checked" | |
saveGameState(true, currentScore, percentage, feedbackEmojis); // Save with checked state | |
} | |
async function saveGameState(isChecked = false, score = 0, percentage = 0, feedback = {}) { | |
if (!userId || !gameDocRef) { | |
console.warn("User not authenticated or gameDocRef not set. Cannot save yet. Will retry on auth."); | |
return; | |
} | |
const playerName = playerNameInputEl.value.trim() || "Anonymous Player"; | |
const answers = collectAnswers(); | |
const gameState = { | |
playerName: playerName, | |
answers: answers, | |
lastUpdated: serverTimestamp(), | |
isChecked: isChecked, // New field to indicate if answers were checked | |
score: isChecked ? score : null, | |
percentage: isChecked ? percentage : null, | |
feedback: isChecked ? feedback : {} | |
}; | |
try { | |
await setDoc(gameDocRef, gameState, { merge: true }); | |
console.log("Game state saved for player:", playerName); | |
} catch (error) { | |
console.error("Error saving game state: ", error); | |
} | |
} | |
async function loadGameState() { | |
if (!userId) { | |
console.warn("User ID not available for loading game state."); | |
renderCards(); // Render empty cards if no user ID | |
updateButtonStates(); | |
return; | |
} | |
// Corrected path: collection "samsFavoritesGameData", document "userGameState" | |
gameDocRef = doc(db, "artifacts", appId, "users", userId, "samsFavoritesGameData", "userGameState"); | |
console.log("Attempting to load game state from:", gameDocRef.path); | |
try { | |
const docSnap = await getDoc(gameDocRef); | |
if (docSnap.exists()) { | |
const data = docSnap.data(); | |
playerNameInputEl.value = data.playerName || 'Aaron'; // Default to Aaron if no name | |
renderCards(data.answers || {}, data.feedback || {}); | |
if (data.isChecked) { | |
scoreTextEl.textContent = `Score: ${data.score || 0} / ${TOTAL_POSSIBLE_POINTS} (${data.percentage || 0}%)`; | |
scoreDisplaySectionEl.style.display = 'block'; | |
cardsContainerEl.classList.remove('hidden-answers'); | |
cardsContainerEl.classList.add('checked-answers'); | |
checkAnswersBtn.textContent = 'Answers Checked'; | |
hasUserTyped = true; // If data loaded, assume interaction | |
} | |
} else { | |
console.log("No saved game state found. Starting fresh."); | |
playerNameInputEl.value = 'Aaron'; // Default for new users | |
renderCards(); // Render empty cards | |
} | |
} catch (error) { | |
console.error("Error loading game state: ", error); | |
renderCards(); // Render empty cards on error | |
} | |
updateButtonStates(); | |
} | |
// Authentication and Initialization | |
onAuthStateChanged(auth, async (user) => { | |
if (user) { | |
userId = user.uid; | |
console.log("User authenticated with UID:", userId); | |
// Define gameDocRef here, now that userId is available | |
gameDocRef = doc(db, "artifacts", appId, "users", userId, "samsFavoritesGameData", "userGameState"); | |
await loadGameState(); | |
} else { | |
// User is signed out or not yet signed in. Try custom token or anonymous. | |
console.log("No user signed in. Attempting sign-in."); | |
if (typeof __initial_auth_token !== 'undefined' && __initial_auth_token) { | |
try { | |
await signInWithCustomToken(auth, __initial_auth_token); | |
console.log("Signed in with custom token."); | |
// onAuthStateChanged will trigger again with the new user. | |
} catch (error) { | |
console.error("Error signing in with custom token, falling back to anonymous:", error); | |
await signInAnonymously(auth).catch(anonError => console.error("Anonymous sign-in also failed:", anonError)); | |
} | |
} else { | |
try { | |
await signInAnonymously(auth); | |
console.log("Signed in anonymously."); | |
// onAuthStateChanged will trigger again with the new user. | |
} catch (error) { | |
console.error("Error signing in anonymously: ", error); | |
renderCards(); | |
updateButtonStates(); | |
} | |
} | |
} | |
}); | |
checkAnswersBtn.addEventListener('click', () => { | |
if (checkAnswersBtn.textContent === 'Answers Checked') return; // Don't re-check | |
if (cardsContainerEl.classList.contains('hidden-answers') && !hasUserTyped) { | |
// If revealing answers without typing anything | |
cardsContainerEl.classList.remove('hidden-answers'); | |
checkAnswersBtn.textContent = 'Check Answers with Key'; // Prompt to type then check | |
} else { | |
handleCheckAnswers(); | |
} | |
}); | |
saveProgressBtn.addEventListener('click', () => { | |
// Determine if answers have been checked to pass correct state to saveGameState | |
const isChecked = cardsContainerEl.classList.contains('checked-answers') && !cardsContainerEl.classList.contains('hidden-answers'); | |
let score = 0; | |
let percentage = 0; | |
let feedback = {}; | |
if (isChecked) { | |
// If checked, parse score from display or re-calculate (safer to re-calculate if needed) | |
// For simplicity, assume scoreTextEl is up-to-date if isChecked is true | |
const scoreMatch = scoreTextEl.textContent.match(/Score: (\d+) \/ \d+ \((\d+)%\)/); | |
if (scoreMatch) { | |
score = parseInt(scoreMatch[1]); | |
percentage = parseInt(scoreMatch[2]); | |
} | |
// Collect feedback emojis again | |
document.querySelectorAll('.feedback-emoji').forEach(el => { | |
const idParts = el.id.split('-'); // e.g., feedback-q1 or feedback-q3-part1 | |
let key = idParts[1]; | |
if (idParts.length > 2 && (idParts[2] === 'part1' || idParts[2] === 'part2' || idParts[2] === 'main' || idParts[2] === 'bonus')) { | |
key += `-${idParts[2]}`; | |
} | |
if(el.textContent && el.textContent !== 'β') feedback[key] = el.textContent; | |
}); | |
} | |
saveGameState(isChecked, score, percentage, feedback); | |
}); | |
// Initial setup | |
// renderCards(); // Render empty cards initially, loadGameState will populate if data exists | |
// updateButtonStates(); // Called within loadGameState or auth callback | |
</script> | |
</body> | |
</html> | |