11<!DOCTYPE html>
22<html lang =" de" >
3+
34<head >
45 <meta charset =" UTF-8" >
56 <title >FDM-Mini-Training 1</title >
67 <!-- Tailwind CSS via CDN -->
78 <script src =" https://cdn.tailwindcss.com" ></script >
89</head >
10+
911<body class =" bg-gray-100" >
1012 <div class =" max-w-5xl mx-auto p-6" >
11- <! -- Header mit Logos -->
13+ {{ -- Header section with logos and training title --}}
1214 <div class =" flex items-center justify-between mb-6" >
1315 <img src =" {{ asset (' images/fhp-logo.png' ) } }" alt =" FHP Logo" class =" h-6" >
1416 <h1 class =" text-3xl font-bold text-center" >FDM-Mini-Training 1</h1 >
1517 <img src =" {{ asset (' images/gfz-logo-en.png' ) } }" alt =" GFZ Logo" class =" h-6" >
1618 </div >
1719 <div class =" bg-white shadow-lg rounded-lg p-6" >
18- < div id = " quiz-container " >
19- <!-- Dynamischer Inhalt: Frage und Antwortoptionen -- >
20- </ div >
21- <! -- Navigationsbereich mit drei Buttons: "Zurück", "Antworten anzeigen" (mit Countdown) und "Weiter" -->
20+ {{-- Container where the current quiz question and its options will be dynamically rendered --}}
21+ < div id = " quiz-container " ></ div >
22+
23+ {{ -- Navigation buttons: Back, Show Results (with countdown), Next --}}
2224 <div class =" mt-6 flex justify-center items-center space-x-4" >
2325 <button id =" back-button" class =" bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded" >
2426 Zurück
2527 </button >
26- <!-- Button ist von Anfang an sichtbar, aber deaktiviert und zeigt den Countdown -->
2728 <button id =" show-results-button" disabled class =" bg-green-500 text-white font-bold py-2 px-4 rounded" >
2829 00:60
2930 </button >
3536 </div >
3637
3738 <script >
38- // Globale Variablen
39+ // Parsed list of quiz questions from the backend
3940 const questions = @json ($questions );
4041 let currentQuestionIndex = 0 ;
4142 let pollInterval;
4243 let countdownInterval;
43- let currentResults = {}; // Zwischenspeicherung der abgefragten Ergebnisse
44- // Status für den "show-results-button": 0 = initial, 1 = Ergebnisse sichtbar
45- let showResultsButtonState = 0 ;
46-
47- // Aktualisiert den aktiven Fragensatz im Backend (optional)
44+ let currentResults = {};
45+ let showResultsButtonState = 0 ; // 0 = before showing results, 1 = results visible
46+
47+ // Update the currently active question in the backend
4848 function setActiveQuestion (questionId ) {
4949 fetch (" {{ route (' quiz.active.update' ) } }" , {
5050 method: " POST" ,
@@ -53,168 +53,146 @@ function setActiveQuestion(questionId) {
5353 " X-CSRF-TOKEN" : " {{ csrf_token () } }"
5454 },
5555 body: JSON .stringify ({ quiz_question_id: questionId })
56- })
57- .then (response => response .json ())
58- .then (data => {
59- if (data .success ) {
60- console .log (" Aktiver Fragewert aktualisiert auf: " + questionId);
61- }
62- })
63- .catch (error => console .error (" Fehler beim Aktualisieren des aktiven Fragewerts:" , error));
56+ }).catch (error => console .error (" Error updating active question:" , error));
6457 }
65-
66- // Rendert die aktuelle Frage, setzt den Countdown zurück und startet das Polling
58+
59+ // Render the question, reset countdown, and start polling for results
6760 function renderQuestion () {
6861 if (currentQuestionIndex >= questions .length ) {
62+ // Redirect to summary when all questions are done
6963 window .location .href = " {{ route (' quiz.summary' ) } }" ;
7064 return ;
7165 }
72- // Reset des Button-Status für die aktuelle Frage
66+
67+ // Reset show-results button and countdown
7368 showResultsButtonState = 0 ;
74- const showResultsButton = document .getElementById (' show-results-button' );
75- showResultsButton .disabled = true ;
76- // Setze den Button-Text auf den Countdown-Startwert
77- showResultsButton .textContent = " 00:60" ;
78- // Falls ein alter Countdown läuft, beenden
69+ const showBtn = document .getElementById (' show-results-button' );
70+ showBtn .disabled = true ;
71+ showBtn .textContent = " 00:60" ;
7972 if (countdownInterval) clearInterval (countdownInterval);
80-
81- // Starte den 60-Sekunden-Countdown
73+
74+ // Start 60-second countdown timer
8275 let timeLeft = 60 ;
8376 countdownInterval = setInterval (() => {
8477 timeLeft-- ;
85- let minutes = Math .floor (timeLeft / 60 );
86- let seconds = timeLeft % 60 ;
87- showResultsButton .textContent =
88- (minutes < 10 ? " 0" : " " ) + minutes + " :" + (seconds < 10 ? " 0" : " " ) + seconds;
89- if (timeLeft <= 0 ) {
78+ const minutes = Math .floor (timeLeft / 60 );
79+ const seconds = timeLeft % 60 ;
80+ showBtn .textContent = ` ${ minutes .toString ().padStart (2 , ' 0' )} :${ seconds .toString ().padStart (2 , ' 0' )} ` ;
81+ if (timeLeft <= 0 ) {
9082 clearInterval (countdownInterval);
91- showResultsButton .textContent = " Antworten anzeigen" ;
92- showResultsButton .disabled = false ;
83+ showBtn .textContent = " Antworten anzeigen" ;
84+ showBtn .disabled = false ;
9385 }
9486 }, 1000 );
95-
96- const currentQuestion = questions[currentQuestionIndex];
87+
88+ const question = questions[currentQuestionIndex];
9789 const container = document .getElementById (' quiz-container' );
9890 container .innerHTML = ' ' ;
99-
100- // Frageanzeige
101- const questionTitle = document .createElement (' h2' );
102- questionTitle .className = " text-2xl font-semibold mb-4" ;
103- questionTitle .textContent = currentQuestion .question_text ;
104- container .appendChild (questionTitle );
105-
106- // "Fertig!"-Badge (wird eingeblendet, wenn alle 6 Stimmen vorliegen)
91+
92+ // Display question text
93+ const title = document .createElement (' h2' );
94+ title .className = " text-2xl font-semibold mb-4" ;
95+ title .textContent = question .question_text ;
96+ container .appendChild (title );
97+
98+ // 'Ready!' badge hidden until all votes are in
10799 const badge = document .createElement (' div' );
108100 badge .id = " ready-badge" ;
109101 badge .className = " hidden inline-block bg-blue-500 text-white text-xs font-bold px-2 py-1 rounded-full mb-4" ;
110102 badge .textContent = " Fertig!" ;
111103 container .appendChild (badge);
112-
113- // Antwortoptionen anzeigen
114- const optionsList = document .createElement (' div' );
115- optionsList .className = " space-y-4" ;
116- currentQuestion .options .forEach (option => {
117- const optionDiv = document .createElement (' div' );
118- optionDiv .className = " option-card p-4 bg-gray-50 rounded shadow flex items-center justify-between" ;
119- optionDiv .setAttribute (' data-correct' , option .is_correct ? " true" : " false" );
120-
121- const optionText = document .createElement (' span' );
122- optionText .className = " font-medium" ;
123- optionText .textContent = ` ${ option .letter } : ${ option .option_text } ` ;
124-
125- const voteCount = document .createElement (' span' );
126- voteCount .className = " text-xl font-bold text-blue-600" ;
127- voteCount .id = ` vote-${ option .letter } ` ;
128- voteCount .textContent = " " ;
129- voteCount .style .visibility = ' hidden' ;
130-
131- optionDiv .appendChild (optionText);
132- optionDiv .appendChild (voteCount);
133- optionsList .appendChild (optionDiv);
104+
105+ // Render each answer option card
106+ const optionsBlock = document .createElement (' div' );
107+ optionsBlock .className = " space-y-4" ;
108+ question .options .forEach (opt => {
109+ const card = document .createElement (' div' );
110+ card .className = " option-card p-4 bg-gray-50 rounded shadow flex items-center justify-between" ;
111+ card .setAttribute (' data-correct' , opt .is_correct );
112+
113+ const textSpan = document .createElement (' span' );
114+ textSpan .className = " font-medium" ;
115+ textSpan .textContent = ` ${ opt .letter } : ${ opt .option_text } ` ;
116+
117+ const countSpan = document .createElement (' span' );
118+ countSpan .id = ` vote-${ opt .letter } ` ;
119+ countSpan .className = " text-xl font-bold text-blue-600" ;
120+ countSpan .style .visibility = ' hidden' ;
121+
122+ card .appendChild (textSpan);
123+ card .appendChild (countSpan);
124+ optionsBlock .appendChild (card);
134125 });
135- container .appendChild (optionsList );
136-
137- // "Zurück"-Button deaktivieren, wenn erste Frage
126+ container .appendChild (optionsBlock );
127+
128+ // Disable back button on the first question
138129 document .getElementById (' back-button' ).disabled = (currentQuestionIndex === 0 );
139- // Aktualisiere den aktiven Fragensatz
140- setActiveQuestion (currentQuestion .id );
141- // Starte das Polling für die Live-Ergebnisse
142- startPolling (currentQuestion .id );
130+ setActiveQuestion (question .id );
131+ startPolling (question .id );
143132 }
144-
145- // Holt per AJAX die aktuellen Ergebnisse für die gegebene Frage
133+
134+ // Poll the backend every 2 seconds for live scan counts
146135 function startPolling (questionId ) {
147136 if (pollInterval) clearInterval (pollInterval);
148137 pollInterval = setInterval (() => {
149- fetch (" {{ url (' /quiz/results' ) } } /" + questionId)
150- .then (response => response .json ())
151- .then (data => {
152- let totalVotes = 0 ;
153- for (const letter in data) {
154- totalVotes += parseInt (data[letter]);
155- }
156- // Falls alle 6 Stimmen vorhanden sind, zeige das "Fertig!"-Badge und aktiviere den Button (falls noch nicht aktiviert)
157- if (totalVotes >= 6 ) {
158- currentResults = data;
159- document .getElementById (' ready-badge' ).classList .remove (' hidden' );
160- const showResultsButton = document .getElementById (' show-results-button' );
161- if (showResultsButton .disabled ) {
162- clearInterval (countdownInterval);
163- showResultsButton .textContent = " Antworten anzeigen" ;
164- showResultsButton .disabled = false ;
138+ fetch (` {{ url (' /quiz/results' ) } } /${ questionId} ` )
139+ .then (res => res .json ())
140+ .then (data => {
141+ const totalVotes = Object .values (data).reduce ((sum , v ) => sum + parseInt (v), 0 );
142+ if (totalVotes >= 6 ) {
143+ currentResults = data;
144+ document .getElementById (' ready-badge' ).classList .remove (' hidden' );
145+ const btn = document .getElementById (' show-results-button' );
146+ if (btn .disabled ) {
147+ clearInterval (countdownInterval);
148+ btn .textContent = " Antworten anzeigen" ;
149+ btn .disabled = false ;
150+ }
165151 }
166- }
167- })
168- .catch (error => console .error (" Fehler beim Abrufen der Ergebnisse:" , error));
152+ }).catch (error => console .error (" Error fetching results:" , error));
169153 }, 2000 );
170154 }
171-
172- // Event Listener für "Antworten anzeigen" / "Lösung anzeigen"
155+
156+ // Handle show-results button click: first show counts, then reveal correct answer
173157 document .getElementById (' show-results-button' ).addEventListener (' click' , () => {
158+ const btn = document .getElementById (' show-results-button' );
174159 if (showResultsButtonState === 0 ) {
175- for (const letter in currentResults) {
176- const voteElement = document .getElementById (' vote-' + letter);
177- if (voteElement) {
178- voteElement .textContent = currentResults[letter];
179- voteElement .style .visibility = ' visible' ;
180- }
181- }
182- document .getElementById (' show-results-button' ).textContent = " Lösung anzeigen" ;
160+ // Show vote counts
161+ Object .entries (currentResults).forEach (([letter , cnt ]) => {
162+ const voteEl = document .getElementById (` vote-${ letter} ` );
163+ voteEl .textContent = cnt;
164+ voteEl .style .visibility = ' visible' ;
165+ });
166+ btn .textContent = " Lösung anzeigen" ;
183167 showResultsButtonState = 1 ;
184- } else if (showResultsButtonState === 1 ) {
185- const optionCards = document .querySelectorAll (' .option-card' );
186- optionCards .forEach (card => {
187- const isCorrect = card .getAttribute (' data-correct' ) === " true" ;
188- if (isCorrect) {
189- card .classList .remove (' bg-gray-50' );
190- card .classList .add (' bg-green-200' );
191- } else {
192- card .classList .remove (' bg-gray-50' );
193- card .classList .add (' bg-red-200' );
194- }
168+ } else {
169+ // Highlight correct vs incorrect options
170+ document .querySelectorAll (' .option-card' ).forEach (card => {
171+ const correct = card .getAttribute (' data-correct' ) === ' true' ;
172+ card .classList .toggle (' bg-green-200' , correct);
173+ card .classList .toggle (' bg-red-200' , ! correct);
195174 });
196- document . getElementById ( ' show-results-button ' ) .disabled = true ;
175+ btn .disabled = true ;
197176 }
198177 });
199-
200- // "Weiter"-Button Event Listener
178+
179+ // Next and Back button event handlers
201180 document .getElementById (' next-button' ).addEventListener (' click' , () => {
202181 if (pollInterval) clearInterval (pollInterval);
203182 currentQuestionIndex++ ;
204183 renderQuestion ();
205184 });
206-
207- // "Zurück"-Button Event Listener
208185 document .getElementById (' back-button' ).addEventListener (' click' , () => {
209186 if (currentQuestionIndex > 0 ) {
210187 if (pollInterval) clearInterval (pollInterval);
211188 currentQuestionIndex-- ;
212189 renderQuestion ();
213190 }
214191 });
215-
216- // Initiale Anzeige der ersten Frage
192+
193+ // Initial render on page load
217194 renderQuestion ();
218- </script >
195+ </script >
219196</body >
220- </html >
197+
198+ </html >
0 commit comments