Admin panel sync improvements, Baserow field handling (Single Select support), and UI emoji swap
This commit is contained in:
+27
-13
@@ -26,6 +26,23 @@ function shuffleArray(arr) {
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract value from Baserow Single Select field.
|
||||
* Can be { id: 6, value: "MC", color: "..." } or plain string or null.
|
||||
*/
|
||||
function fieldVal(field, fallback) {
|
||||
if (!field) return fallback || '';
|
||||
if (typeof field === 'string') return field;
|
||||
if (typeof field === 'object' && field.value !== undefined) return field.value;
|
||||
return String(field);
|
||||
}
|
||||
|
||||
function normalizeText(t) {
|
||||
return (t || '').toLowerCase()
|
||||
.replace(/[äÄ]/g, 'ae').replace(/[öÖ]/g, 'oe').replace(/[üÜ]/g, 'ue').replace(/ß/g, 'ss')
|
||||
.replace(/[^a-z0-9]/g, '');
|
||||
}
|
||||
|
||||
/* ================================================================== */
|
||||
/* Shared Components */
|
||||
/* ================================================================== */
|
||||
@@ -123,7 +140,7 @@ function AnswerGrid({ question, selected, onToggle, disabled }) {
|
||||
function HomePage({ navigate }) {
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col items-center justify-center p-6">
|
||||
<img src="/img/logo.png" alt="Quizalarm" className="w-24 h-24 mb-2" />
|
||||
<div className="text-6xl mb-2">🚨</div>
|
||||
<h1 className="text-5xl font-extrabold mb-2 fire-gradient">Quizalarm</h1>
|
||||
<p className="text-gray-400 mb-10 text-lg">Lernen. Quizzen. Wissen.</p>
|
||||
<div className="w-full max-w-sm space-y-4">
|
||||
@@ -402,6 +419,7 @@ function SoloQuizPage({ params, navigate }) {
|
||||
|
||||
useEffect(() => {
|
||||
api.get(`/api/sets/${params.setId}/questions`).then((qs) => {
|
||||
console.log(`Loaded ${qs.length} questions, sample Typ:`, qs[0]?.['Typ']);
|
||||
if (params.shuffle) {
|
||||
for (let i = qs.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
@@ -412,7 +430,6 @@ function SoloQuizPage({ params, navigate }) {
|
||||
}).catch((e) => setError(e.message));
|
||||
}, []);
|
||||
|
||||
// Shuffle answers when question changes
|
||||
useEffect(() => {
|
||||
if (questions.length === 0 || index >= questions.length) return;
|
||||
const q = questions[index];
|
||||
@@ -427,16 +444,14 @@ function SoloQuizPage({ params, navigate }) {
|
||||
if (!questions.length) return <div className="min-h-screen flex items-center justify-center animate-pulse text-xl text-amber-400">Lade Fragen...</div>;
|
||||
|
||||
const q = questions[index];
|
||||
const typ = (q['Typ'] || 'MC').toLowerCase();
|
||||
const typ = fieldVal(q['Typ'], 'MC').toLowerCase();
|
||||
const sanitized = {
|
||||
frage: q['Frage'] || q['frage'] || '(Kein Fragetext)',
|
||||
typ: q['Typ'] || q['typ'] || 'MC',
|
||||
frage: q['Frage'] || '(Kein Fragetext)',
|
||||
typ: fieldVal(q['Typ'], 'MC'),
|
||||
bild: q['Bild'] && Array.isArray(q['Bild']) && q['Bild'].length ? q['Bild'][0].url : null,
|
||||
antworten: shuffledAnswers,
|
||||
};
|
||||
|
||||
const normalizeText = (t) => (t || '').toLowerCase().replace(/[äÄ]/g, 'ae').replace(/[öÖ]/g, 'oe').replace(/[üÜ]/g, 'ue').replace(/ß/g, 'ss').replace(/[^a-z0-9]/g, '');
|
||||
|
||||
const checkAnswer = () => {
|
||||
const rawCorrect = (q['Richtige Antwort'] || '').trim();
|
||||
let correct, ok;
|
||||
@@ -520,11 +535,10 @@ function SoloResultPage({ params, navigate }) {
|
||||
const byCategory = {};
|
||||
results.forEach((r, i) => {
|
||||
const q = questions[i];
|
||||
const cat = (q && (q['Kategorie'] || q['kategorie'])) || 'Ohne Kategorie';
|
||||
const catName = typeof cat === 'object' && cat.value ? cat.value : cat;
|
||||
if (!byCategory[catName]) byCategory[catName] = { total: 0, correct: 0 };
|
||||
byCategory[catName].total++;
|
||||
if (r.Richtig) byCategory[catName].correct++;
|
||||
const cat = fieldVal(q?.['Kategorie'], 'Ohne Kategorie') || 'Ohne Kategorie';
|
||||
if (!byCategory[cat]) byCategory[cat] = { total: 0, correct: 0 };
|
||||
byCategory[cat].total++;
|
||||
if (r.Richtig) byCategory[cat].correct++;
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -573,7 +587,7 @@ function MyResultsPage({ navigate }) {
|
||||
if (results) {
|
||||
results.forEach((r) => {
|
||||
const sid = r['Session_ID'] || 'Unbekannt';
|
||||
if (!sessions[sid]) sessions[sid] = { modus: r['Modus'], fragenset: r['Fragenset'], date: r['Zeitstempel'], answers: [] };
|
||||
if (!sessions[sid]) sessions[sid] = { modus: fieldVal(r['Modus'], ''), fragenset: r['Fragenset'], date: r['Zeitstempel'], answers: [] };
|
||||
sessions[sid].answers.push(r);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user