ajoute Quiz flash (chantier vrai cockpit, étape 2) : QCM et vrai/faux, lancé en direct, indépendant d'une séance de lab
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
45f8a69e26
commit
16d1293911
24 changed files with 1249 additions and 5 deletions
|
|
@ -15,6 +15,14 @@
|
|||
<a href="{{ route('labs.essai') }}" class="inline-flex items-center gap-4 border border-[#e3e5e8] rounded-2xl bg-black px-6 py-4 text-lg font-extrabold text-white transition-transform hover:bg-[#ec3013].5.5 active:shadow-none">Reprendre mon TP <span class="font-mono" aria-hidden="true">→</span></a>
|
||||
</div>
|
||||
</section>
|
||||
{{-- Chantier « vrai cockpit », étape 2 : Quiz flash, réel, pas de démo.
|
||||
Caché par défaut ; rempli et affiché par le sondage ci-dessous. --}}
|
||||
<section id="quiz-flash" hidden class="mx-auto max-w-7xl px-5 pt-10 lg:px-10" aria-labelledby="t-quiz-flash">
|
||||
<div class="rounded-2xl border border-[#e3e5e8] bg-[#eef0fd] p-6">
|
||||
<h2 id="t-quiz-flash" class="text-2xl font-extrabold">Quiz flash</h2>
|
||||
<div data-quiz-contenu class="mt-4"></div>
|
||||
</div>
|
||||
</section>
|
||||
@php($seancesOuvertes = $eleve->classe->seances()->whereIn('statut', [\App\Models\Seance::SALLE_ATTENTE, \App\Models\Seance::EN_COURS])->with('lab')->get())
|
||||
@if ($seancesOuvertes->isNotEmpty())
|
||||
<section class="mx-auto max-w-7xl px-5 pt-10 lg:px-10" aria-labelledby="t-seance-ouverte">
|
||||
|
|
@ -82,4 +90,86 @@
|
|||
</section>
|
||||
</aside>
|
||||
</div>
|
||||
<script nonce="{{ Vite::cspNonce() }}">
|
||||
(function () {
|
||||
var URL_ETAT = @json(route('quiz.eleve.etat'));
|
||||
var CSRF = @json(csrf_token());
|
||||
var section = document.getElementById('quiz-flash');
|
||||
var contenu = section.querySelector('[data-quiz-contenu]');
|
||||
var questionAffichee = null, repondu = false;
|
||||
|
||||
function echapper(s) {
|
||||
return s.replace(/[&<>"]/g, function (c) { return { '&': '&', '<': '<', '>': '>', '"': '"' }[c]; });
|
||||
}
|
||||
|
||||
function rendreQuestion(q) {
|
||||
var champs = q.type === 'vrai_faux'
|
||||
? '<label class="flex items-center gap-2 font-semibold"><input type="radio" name="reponse" value="vrai"> Vrai</label>' +
|
||||
'<label class="flex items-center gap-2 font-semibold"><input type="radio" name="reponse" value="faux"> Faux</label>'
|
||||
: q.choix.map(function (c) {
|
||||
return '<label class="flex items-center gap-2 font-semibold"><input type="radio" name="reponse" value="' + c.id + '"> ' + echapper(c.texte) + '</label>';
|
||||
}).join('');
|
||||
|
||||
contenu.innerHTML =
|
||||
'<p class="text-xl font-extrabold">' + echapper(q.enonce) + '</p>' +
|
||||
'<form data-form-reponse class="mt-4 grid gap-2">' + champs +
|
||||
'<button type="submit" class="mt-2 inline-flex h-11 w-fit items-center rounded-full bg-black px-5 font-bold text-white">Valider ma réponse</button></form>' +
|
||||
'<p data-resultat class="mt-3 hidden font-extrabold"></p>';
|
||||
|
||||
contenu.querySelector('[data-form-reponse]').addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
var choisi = contenu.querySelector('input[name="reponse"]:checked');
|
||||
if (!choisi) return;
|
||||
var corps = q.type === 'vrai_faux'
|
||||
? { reponse_vrai_faux: choisi.value === 'vrai' }
|
||||
: { choix_id: parseInt(choisi.value, 10) };
|
||||
|
||||
fetch('/sessions-quiz/' + sessionActuelle + '/repondre', {
|
||||
method: 'POST',
|
||||
headers: { 'X-CSRF-TOKEN': CSRF, 'Content-Type': 'application/json', 'Accept': 'application/json' },
|
||||
body: JSON.stringify(corps),
|
||||
}).then(function (r) { return r.json(); }).then(function (d) {
|
||||
repondu = true;
|
||||
var form = contenu.querySelector('[data-form-reponse]');
|
||||
form.hidden = true;
|
||||
var p = contenu.querySelector('[data-resultat]');
|
||||
p.hidden = false;
|
||||
p.textContent = d.correcte ? 'Bonne réponse !' : 'Réponse envoyée.';
|
||||
p.className = 'mt-3 font-extrabold ' + (d.correcte ? 'text-[#3C8A5A]' : '');
|
||||
}).catch(function () {});
|
||||
});
|
||||
}
|
||||
|
||||
var sessionActuelle = null;
|
||||
|
||||
function sonder() {
|
||||
fetch(URL_ETAT, { headers: { Accept: 'application/json' } })
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (d) {
|
||||
var s = d.session;
|
||||
if (!s) { section.hidden = true; questionAffichee = null; sessionActuelle = null; return; }
|
||||
sessionActuelle = s.id;
|
||||
section.hidden = false;
|
||||
|
||||
if (s.statut === 'en_attente' || !s.question) {
|
||||
questionAffichee = null;
|
||||
contenu.innerHTML = '<p class="font-semibold">Un quiz va commencer. Garde cette page ouverte.</p>';
|
||||
return;
|
||||
}
|
||||
if (s.deja_repondu) {
|
||||
questionAffichee = s.question.id;
|
||||
contenu.innerHTML = '<p class="font-extrabold">' + echapper(s.question.enonce) + '</p><p class="mt-3 font-extrabold">Réponse envoyée. En attente de la suite…</p>';
|
||||
return;
|
||||
}
|
||||
if (s.question.id !== questionAffichee) {
|
||||
questionAffichee = s.question.id;
|
||||
repondu = false;
|
||||
rendreQuestion(s.question);
|
||||
}
|
||||
}).catch(function () {});
|
||||
}
|
||||
sonder();
|
||||
setInterval(sonder, 2000);
|
||||
})();
|
||||
</script>
|
||||
@endsection
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue