Feature: Support both Free-Text and Multiple-Choice question types
This commit is contained in:
@@ -428,6 +428,88 @@ body {
|
||||
box-shadow: 0 0 12px rgba(201, 168, 106, 0.4);
|
||||
}
|
||||
|
||||
/* Multiple Choice Styles */
|
||||
.choice-group {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.choice-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: 14px;
|
||||
width: 100%;
|
||||
max-width: 650px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.choice-btn {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 2px solid var(--border-gold);
|
||||
border-radius: 10px;
|
||||
padding: 14px 18px;
|
||||
color: var(--text-main);
|
||||
font-size: 1.05rem;
|
||||
font-family: inherit;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
transition: all 0.25s ease;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.choice-btn:hover:not(:disabled) {
|
||||
background: rgba(201, 168, 106, 0.18);
|
||||
border-color: var(--gold);
|
||||
box-shadow: 0 0 15px rgba(201, 168, 106, 0.4);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.choice-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.choice-letter {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background: rgba(201, 168, 106, 0.2);
|
||||
border: 1px solid var(--gold);
|
||||
border-radius: 50%;
|
||||
color: var(--gold-bright);
|
||||
font-weight: bold;
|
||||
font-size: 0.9rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.choice-text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.choice-selected {
|
||||
border-color: var(--gold-bright);
|
||||
background: rgba(201, 168, 106, 0.3);
|
||||
}
|
||||
|
||||
.choice-correct {
|
||||
background: rgba(46, 204, 113, 0.25) !important;
|
||||
border-color: #2ecc71 !important;
|
||||
color: #55efc4 !important;
|
||||
box-shadow: 0 0 15px rgba(46, 204, 113, 0.6) !important;
|
||||
}
|
||||
|
||||
.choice-wrong {
|
||||
background: rgba(231, 76, 60, 0.25) !important;
|
||||
border-color: #e74c3c !important;
|
||||
color: #ff7675 !important;
|
||||
box-shadow: 0 0 15px rgba(231, 76, 60, 0.5) !important;
|
||||
}
|
||||
|
||||
.hint-box {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
|
||||
+14
-2
@@ -7,8 +7,9 @@ function showTab(tabId) {
|
||||
targetTab.classList.add("active");
|
||||
}
|
||||
|
||||
// Mark active button
|
||||
event.currentTarget.classList.add("active");
|
||||
if (event && event.currentTarget) {
|
||||
event.currentTarget.classList.add("active");
|
||||
}
|
||||
}
|
||||
|
||||
function toggleEdit(id) {
|
||||
@@ -18,6 +19,17 @@ function toggleEdit(id) {
|
||||
}
|
||||
}
|
||||
|
||||
function toggleAdminNewType(type) {
|
||||
const mcGroup = document.getElementById("adminMcOptions");
|
||||
if (mcGroup) {
|
||||
if (type === "multiple_choice") {
|
||||
mcGroup.classList.remove("hidden");
|
||||
} else {
|
||||
mcGroup.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function approveQuestion(id) {
|
||||
if (!confirm(`Möchtest du Frage #${id} wirklich freischalten und für alle Spieler aktivieren?`)) {
|
||||
return;
|
||||
|
||||
+64
-14
@@ -103,16 +103,42 @@ function renderQuestion(qData) {
|
||||
document.getElementById("attemptBadge").innerText = `Versuch ${attemptsUsed + 1} von ${maxAttempts}`;
|
||||
document.getElementById("frageText").innerText = qData.question;
|
||||
|
||||
const textGroup = document.getElementById("textAnswerGroup");
|
||||
const choiceGroup = document.getElementById("choiceAnswerGroup");
|
||||
const choiceContainer = document.getElementById("choiceOptionsContainer");
|
||||
const input = document.getElementById("answerInput");
|
||||
input.value = "";
|
||||
input.disabled = false;
|
||||
input.focus();
|
||||
|
||||
document.getElementById("checkBtn").disabled = false;
|
||||
document.getElementById("hintBtn").disabled = false;
|
||||
document.getElementById("hintBox").classList.add("hidden");
|
||||
document.getElementById("feedbackBox").innerHTML = "";
|
||||
|
||||
if (qData.question_type === "multiple_choice" && qData.options && qData.options.length > 0) {
|
||||
textGroup.classList.add("hidden");
|
||||
choiceGroup.classList.remove("hidden");
|
||||
choiceContainer.innerHTML = "";
|
||||
|
||||
const letters = ["A", "B", "C", "D", "E", "F"];
|
||||
qData.options.forEach((opt, idx) => {
|
||||
const btn = document.createElement("button");
|
||||
btn.className = "choice-btn";
|
||||
btn.type = "button";
|
||||
btn.innerHTML = `<span class="choice-letter">${letters[idx] || "•"}</span> <span class="choice-text">${opt}</span>`;
|
||||
btn.onclick = () => submitAnswer(opt, btn);
|
||||
choiceContainer.appendChild(btn);
|
||||
});
|
||||
} else {
|
||||
choiceGroup.classList.add("hidden");
|
||||
textGroup.classList.remove("hidden");
|
||||
input.value = "";
|
||||
input.disabled = false;
|
||||
document.getElementById("checkBtn").disabled = false;
|
||||
input.focus();
|
||||
}
|
||||
|
||||
const hintBtn = document.getElementById("hintBtn");
|
||||
if (hintBtn) hintBtn.disabled = false;
|
||||
const hintBtnMC = document.getElementById("hintBtnMC");
|
||||
if (hintBtnMC) hintBtnMC.disabled = false;
|
||||
|
||||
updateProgress();
|
||||
}
|
||||
|
||||
@@ -121,17 +147,28 @@ function updateProgress() {
|
||||
document.getElementById("progressBar").style.width = percent + "%";
|
||||
}
|
||||
|
||||
async function checkAnswer() {
|
||||
function checkAnswer() {
|
||||
const input = document.getElementById("answerInput");
|
||||
const answer = input.value.trim();
|
||||
|
||||
if (!answer) {
|
||||
input.focus();
|
||||
return;
|
||||
}
|
||||
submitAnswer(answer);
|
||||
}
|
||||
|
||||
async function submitAnswer(answer, clickedBtn = null) {
|
||||
const checkBtn = document.getElementById("checkBtn");
|
||||
checkBtn.disabled = true;
|
||||
const input = document.getElementById("answerInput");
|
||||
if (checkBtn) checkBtn.disabled = true;
|
||||
if (input) input.disabled = true;
|
||||
|
||||
// Disable all choice buttons
|
||||
const choiceBtns = document.querySelectorAll(".choice-btn");
|
||||
choiceBtns.forEach(b => b.disabled = true);
|
||||
if (clickedBtn) {
|
||||
clickedBtn.classList.add("choice-selected");
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch("/api/game/check", {
|
||||
@@ -157,9 +194,12 @@ async function checkAnswer() {
|
||||
if (result.correct) {
|
||||
// Success!
|
||||
playSound('magic');
|
||||
if (clickedBtn) {
|
||||
clickedBtn.classList.remove("choice-selected");
|
||||
clickedBtn.classList.add("choice-correct");
|
||||
}
|
||||
frageBox.classList.add("sparkle-effect");
|
||||
feedbackBox.innerHTML = `<span class="feedback-correct">✨ Richtig! 1 Hauspunkt für dein Haus! ✨</span>`;
|
||||
input.disabled = true;
|
||||
|
||||
setTimeout(() => {
|
||||
frageBox.classList.remove("sparkle-effect");
|
||||
@@ -175,6 +215,10 @@ async function checkAnswer() {
|
||||
} else {
|
||||
// Incorrect
|
||||
playSound('wrong');
|
||||
if (clickedBtn) {
|
||||
clickedBtn.classList.remove("choice-selected");
|
||||
clickedBtn.classList.add("choice-wrong");
|
||||
}
|
||||
frageBox.classList.add("shake-effect");
|
||||
setTimeout(() => frageBox.classList.remove("shake-effect"), 500);
|
||||
|
||||
@@ -182,12 +226,11 @@ async function checkAnswer() {
|
||||
document.getElementById("attemptBadge").innerText = `Versuch ${Math.min(attemptsUsed + 1, maxAttempts)} von ${maxAttempts}`;
|
||||
|
||||
if (result.attempts_left === 0) {
|
||||
// Exhausted all 3 attempts
|
||||
// Exhausted all attempts
|
||||
feedbackBox.innerHTML = `
|
||||
<div class="feedback-wrong">Leider nicht richtig (3 Fehlversuche).</div>
|
||||
<div class="feedback-revealed">Richtige Lösung: <strong>${result.revealed_answer}</strong></div>
|
||||
`;
|
||||
input.disabled = true;
|
||||
|
||||
setTimeout(() => {
|
||||
if (result.is_finished) {
|
||||
@@ -202,14 +245,21 @@ async function checkAnswer() {
|
||||
} else {
|
||||
// Still attempts remaining
|
||||
feedbackBox.innerHTML = `<span class="feedback-wrong">Leider falsch! Noch ${result.attempts_left} Versuch(e).</span>`;
|
||||
checkBtn.disabled = false;
|
||||
input.select();
|
||||
if (checkBtn) checkBtn.disabled = false;
|
||||
if (input) {
|
||||
input.disabled = false;
|
||||
input.select();
|
||||
}
|
||||
// Re-enable choice buttons
|
||||
choiceBtns.forEach(b => b.disabled = false);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
alert("Netzwerkfehler: " + err.message);
|
||||
checkBtn.disabled = false;
|
||||
if (checkBtn) checkBtn.disabled = false;
|
||||
if (input) input.disabled = false;
|
||||
choiceBtns.forEach(b => b.disabled = false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
function toggleVorschlagType(type) {
|
||||
const mcGroup = document.getElementById("mcOptionsGroup");
|
||||
const antwortLabel = document.getElementById("antwortLabel");
|
||||
const antwortHelp = document.getElementById("antwortHelp");
|
||||
|
||||
if (type === "multiple_choice") {
|
||||
mcGroup.classList.remove("hidden");
|
||||
antwortLabel.innerText = "Richtige Antwort (die korrekte Option):";
|
||||
antwortHelp.innerText = "Diese Antwort wird zusammen mit den falschen Optionen zufällig für den Spieler angeordnet.";
|
||||
} else {
|
||||
mcGroup.classList.add("hidden");
|
||||
antwortLabel.innerText = "Richtige Antwort:";
|
||||
antwortHelp.innerText = "Unser System gleicht Tippfehler und Groß-/Kleinschreibung automatisch ab.";
|
||||
}
|
||||
}
|
||||
|
||||
async function submitSuggestion(event) {
|
||||
event.preventDefault();
|
||||
|
||||
@@ -5,6 +21,7 @@ async function submitSuggestion(event) {
|
||||
const frage = document.getElementById("frage").value.trim();
|
||||
const antwort = document.getElementById("antwort").value.trim();
|
||||
const tipp = document.getElementById("tipp").value.trim();
|
||||
const qType = document.getElementById("q_type").value;
|
||||
const submitBtn = document.getElementById("submitBtn");
|
||||
const resultBox = document.getElementById("resultMessage");
|
||||
|
||||
@@ -13,19 +30,36 @@ async function submitSuggestion(event) {
|
||||
return;
|
||||
}
|
||||
|
||||
let falscheAntworten = [];
|
||||
if (qType === "multiple_choice") {
|
||||
const w1 = document.getElementById("wrong1").value.trim();
|
||||
const w2 = document.getElementById("wrong2").value.trim();
|
||||
const w3 = document.getElementById("wrong3").value.trim();
|
||||
falscheAntworten = [w1, w2, w3].filter(x => x.length > 0);
|
||||
|
||||
if (falscheAntworten.length === 0) {
|
||||
alert("Bitte gib bei Multiple-Choice mindestens eine falsche Antwortmöglichkeit an.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.innerText = "✨ Vorschlag wird übertragen...";
|
||||
|
||||
try {
|
||||
const payload = {
|
||||
name: name || "Gast",
|
||||
frage: frage,
|
||||
antwort: antwort,
|
||||
tipp: tipp || null,
|
||||
question_type: qType,
|
||||
falsche_antworten: falscheAntworten
|
||||
};
|
||||
|
||||
const res = await fetch("/api/suggestions", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
name: name || "Gast",
|
||||
frage: frage,
|
||||
antwort: antwort,
|
||||
tipp: tipp || null
|
||||
})
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
@@ -39,6 +73,7 @@ async function submitSuggestion(event) {
|
||||
`;
|
||||
resultBox.classList.remove("hidden");
|
||||
document.getElementById("suggestionForm").reset();
|
||||
toggleVorschlagType("text");
|
||||
} else {
|
||||
resultBox.innerHTML = `
|
||||
<div class="alert alert-danger">
|
||||
|
||||
Reference in New Issue
Block a user