Fix Wettkampf resume, timer persistence, and navigator flex wrap

This commit is contained in:
2026-04-26 12:12:38 +02:00
parent a6fd9d26f8
commit 1c030c8ed1
2 changed files with 35 additions and 11 deletions
+34 -11
View File
@@ -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 <div className="min-h-screen flex items-center justify-center text-red-400 text-xl p-6 text-center">{error}</div>;
if (!questions.length) return <div className="min-h-screen flex items-center justify-center animate-pulse text-xl text-emerald-400">Lade Fragen...</div>;
@@ -1153,13 +1176,13 @@ function WettkampfQuizPage({ params, navigate }) {
</div>
{/* Question navigator */}
<div className="flex flex-wrap gap-1.5 mb-4 justify-center">
<div className="flex gap-1.5 mb-4 overflow-x-auto pb-2 justify-start sm:justify-center">
{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';