From 1c030c8ed19bfde97cc8c5fc1a6e77b1e0df8dd6 Mon Sep 17 00:00:00 2001 From: orfelorfel23 Date: Sun, 26 Apr 2026 12:12:38 +0200 Subject: [PATCH] Fix Wettkampf resume, timer persistence, and navigator flex wrap --- public/js/app.js | 45 ++++++++++++++++++++++++++++++++++----------- server/index.js | 1 + 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/public/js/app.js b/public/js/app.js index 1b0d380..cbc6660 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -874,15 +874,24 @@ function WettkampfQuizPage({ params, navigate }) { if (params.priorAnswers && params.priorAnswers.length > 0) { // Resuming: find the questions that were already answered - const priorIds = new Set(params.priorAnswers.map((a) => a.Frage_ID)); + const priorIds = new Set(params.priorAnswers.map((a) => Number(a.Frage_ID))); const resumeQuestions = qs.filter((q) => priorIds.has(q.id)); - setQuestions(resumeQuestions); + + // Fill the rest to match configure questionCount + const needed = params.questionCount - resumeQuestions.length; + if (needed > 0) { + const unpicked = qs.filter((q) => !priorIds.has(q.id)); + const additional = shuffleArray(unpicked).slice(0, needed); + resumeQuestions.push(...additional); + } + + setQuestions(shuffleArray(resumeQuestions)); // Restore answers state const restored = {}; params.priorAnswers.forEach((a) => { const ansStr = a.Antwort || ''; - restored[a.Frage_ID] = { + restored[Number(a.Frage_ID)] = { selected: ansStr ? ansStr.split(',').map((s) => s.trim()) : [], rowId: a.rowId, }; @@ -1092,10 +1101,21 @@ function WettkampfQuizPage({ params, navigate }) { useEffect(() => { if (!params.timer || questions.length === 0) return; const totalSeconds = params.timer * 60; - setTimeLeft(totalSeconds); - const start = Date.now(); - timerRef.current = setInterval(() => { - const elapsed = (Date.now() - start) / 1000; + + let startTimestamp = Date.now(); + if (params.priorAnswers && params.priorAnswers.length > 0) { + let earliest = null; + for (const a of params.priorAnswers) { + if (a.Zeitstempel) { + const ts = new Date(a.Zeitstempel).getTime(); + if (!earliest || ts < earliest) earliest = ts; + } + } + if (earliest) startTimestamp = earliest; + } + + const runTimer = () => { + const elapsed = (Date.now() - startTimestamp) / 1000; const remaining = Math.max(0, totalSeconds - elapsed); setTimeLeft(remaining); if (remaining <= 0) { @@ -1104,9 +1124,12 @@ function WettkampfQuizPage({ params, navigate }) { // Auto-confirm if (confirmRef.current) confirmRef.current(); } - }, 200); + }; + + runTimer(); // Initial check + timerRef.current = setInterval(runTimer, 200); return () => clearInterval(timerRef.current); - }, [questions.length, params.timer]); + }, [questions.length, params.timer, params.priorAnswers]); if (error) return
{error}
; if (!questions.length) return
Lade Fragen...
; @@ -1153,13 +1176,13 @@ function WettkampfQuizPage({ params, navigate }) { {/* Question navigator */} -
+
{questions.map((question, i) => { const a = answers[question.id]; const isAnswered = a && a.selected && a.selected.length > 0 && a.selected[0] !== ''; const isCurrent = i === index; const style = {}; - let cls = 'w-9 h-9 rounded-lg text-sm font-bold transition-all '; + let cls = 'w-9 h-9 flex-shrink-0 rounded-lg text-sm font-bold transition-all '; if (isCurrent) { cls += 'text-white ring-2'; style.backgroundColor = '#f59e0b'; style.boxShadow = '0 0 0 2px #fcd34d'; } else if (isAnswered) { cls += 'text-white'; style.backgroundColor = accentColor; } else cls += 'bg-gray-700 text-gray-400 hover:bg-gray-600'; diff --git a/server/index.js b/server/index.js index ce81a20..2165068 100644 --- a/server/index.js +++ b/server/index.js @@ -299,6 +299,7 @@ app.get('/api/wettkampf-progress/:name/:setId', async (req, res) => { rowId: a.id, Frage_ID: a['Frage_ID'], Antwort: a['Antwort'] || '', + Zeitstempel: a['Zeitstempel'] || '', })), totalAnswered: best.answers.length, });