Fix Wettkampf resume, timer persistence, and navigator flex wrap
This commit is contained in:
+34
-11
@@ -874,15 +874,24 @@ function WettkampfQuizPage({ params, navigate }) {
|
|||||||
|
|
||||||
if (params.priorAnswers && params.priorAnswers.length > 0) {
|
if (params.priorAnswers && params.priorAnswers.length > 0) {
|
||||||
// Resuming: find the questions that were already answered
|
// 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));
|
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
|
// Restore answers state
|
||||||
const restored = {};
|
const restored = {};
|
||||||
params.priorAnswers.forEach((a) => {
|
params.priorAnswers.forEach((a) => {
|
||||||
const ansStr = a.Antwort || '';
|
const ansStr = a.Antwort || '';
|
||||||
restored[a.Frage_ID] = {
|
restored[Number(a.Frage_ID)] = {
|
||||||
selected: ansStr ? ansStr.split(',').map((s) => s.trim()) : [],
|
selected: ansStr ? ansStr.split(',').map((s) => s.trim()) : [],
|
||||||
rowId: a.rowId,
|
rowId: a.rowId,
|
||||||
};
|
};
|
||||||
@@ -1092,10 +1101,21 @@ function WettkampfQuizPage({ params, navigate }) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!params.timer || questions.length === 0) return;
|
if (!params.timer || questions.length === 0) return;
|
||||||
const totalSeconds = params.timer * 60;
|
const totalSeconds = params.timer * 60;
|
||||||
setTimeLeft(totalSeconds);
|
|
||||||
const start = Date.now();
|
let startTimestamp = Date.now();
|
||||||
timerRef.current = setInterval(() => {
|
if (params.priorAnswers && params.priorAnswers.length > 0) {
|
||||||
const elapsed = (Date.now() - start) / 1000;
|
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);
|
const remaining = Math.max(0, totalSeconds - elapsed);
|
||||||
setTimeLeft(remaining);
|
setTimeLeft(remaining);
|
||||||
if (remaining <= 0) {
|
if (remaining <= 0) {
|
||||||
@@ -1104,9 +1124,12 @@ function WettkampfQuizPage({ params, navigate }) {
|
|||||||
// Auto-confirm
|
// Auto-confirm
|
||||||
if (confirmRef.current) confirmRef.current();
|
if (confirmRef.current) confirmRef.current();
|
||||||
}
|
}
|
||||||
}, 200);
|
};
|
||||||
|
|
||||||
|
runTimer(); // Initial check
|
||||||
|
timerRef.current = setInterval(runTimer, 200);
|
||||||
return () => clearInterval(timerRef.current);
|
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 (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>;
|
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>
|
</div>
|
||||||
|
|
||||||
{/* Question navigator */}
|
{/* 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) => {
|
{questions.map((question, i) => {
|
||||||
const a = answers[question.id];
|
const a = answers[question.id];
|
||||||
const isAnswered = a && a.selected && a.selected.length > 0 && a.selected[0] !== '';
|
const isAnswered = a && a.selected && a.selected.length > 0 && a.selected[0] !== '';
|
||||||
const isCurrent = i === index;
|
const isCurrent = i === index;
|
||||||
const style = {};
|
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'; }
|
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 if (isAnswered) { cls += 'text-white'; style.backgroundColor = accentColor; }
|
||||||
else cls += 'bg-gray-700 text-gray-400 hover:bg-gray-600';
|
else cls += 'bg-gray-700 text-gray-400 hover:bg-gray-600';
|
||||||
|
|||||||
@@ -299,6 +299,7 @@ app.get('/api/wettkampf-progress/:name/:setId', async (req, res) => {
|
|||||||
rowId: a.id,
|
rowId: a.id,
|
||||||
Frage_ID: a['Frage_ID'],
|
Frage_ID: a['Frage_ID'],
|
||||||
Antwort: a['Antwort'] || '',
|
Antwort: a['Antwort'] || '',
|
||||||
|
Zeitstempel: a['Zeitstempel'] || '',
|
||||||
})),
|
})),
|
||||||
totalAnswered: best.answers.length,
|
totalAnswered: best.answers.length,
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user