// -- GESTION DU FORMULAIRE PERMETTANT D'AFFICHER ET RÉPONDRE À UN GROUPE DE QUIZS
/// Il n'est pas nécessaire d'être connecté pour répondre au quiz et voir son résultat.
/// Mais si pas connecté, on propose à l'internaute de se connecter ou de créer un compte pour sauvegarder son résultat.
/// Dans ce but son résultat est stocké dans son navigateur.
/// Si il est connecté, l'enregistrement de son résultat se fait automatiquement côté serveur et ses éventuels précédents résultats sont affichés.
// Fichier de configuration tirés du backend :
import { apiUrl, availableLangs, theme } from "../../config/instance.js";
const lang=availableLangs[0];
import { getPreviousAnswers, groupRoutes, saveAnswersRoute } from "../../config/questionnaires.js";
const configTemplate = require("../../views/"+theme+"/config/"+lang+".js");
import { checkAnswerOuput, saveAnswer } from "./tools/answers.js";
import { addElement } from "./tools/dom.js";
import { helloDev, updateAccountLink } from "./tools/everywhere.js";
import { getLocaly } from "./tools/clientstorage.js";
import { getDatasFromInputs } from "./tools/forms.js";
import { dateFormat, replaceAll } from "../../tools/main";
import { loadMatomo } from "./tools/matomo.js";
import { checkSession, getTimeDifference } from "./tools/users.js";
// Dictionnaires :
const { noPreviousAnswer, previousAnswersLine, previousAnswersStats, previousAnswersTitle, responseSavedError, wantToSaveResponses } = require("../../lang/"+lang+"/answer");
const { serverError } = require("../../lang/"+lang+"/general");
// Principaux éléments du DOM manipulés :
const btnSubmit = document.getElementById("checkResponses");
const divResponse = document.getElementById("response");
const explanationsTitle = document.getElementById("explanationsTitle");
const explanationsContent = document.getElementById("explanationsContent");
const myForm = document.getElementById("group");
// Affiche le bouton de soumission + déclenche le chronomètre mesurant la durée de la réponse.
let chronoBegin=0;
const beginAnswer = () =>
{
chronoBegin=Date.now();
btnSubmit.style.display="block";
const here=window.location;// window.location à ajouter pour ne pas quitter la page en mode "preview".
}
let isConnected, user;
const initialise = async () =>
{
try
{
// Si JS activé, on affiche le bouton de soumission du formulaire :
beginAnswer();
isConnected=await checkSession(["user"]);// "user" car seuls les utilisateurs de base peuvent enregistrer leurs réponses aux quizs
// Si l'utilisateur est connecté et a déjà répondu à ce quiz, on affiche ses précédentes réponses à la place du texte servant à expliquer le topo aux nouveaux
if(isConnected)
{
user=getLocaly("user", true);
updateAccountLink(user.status, configTemplate);// lien vers le compte adapté pour les utilisateurs connectés
checkPreviousResponses(user);
}
else
loadMatomo();
}
catch(e)
{
console.error(e);
}
}
initialise();
helloDev();
// Traitement de l'envoi de la réponse de l'utilisateur :
let answer = {};
myForm.addEventListener("submit", function(e)
{
try
{
e.preventDefault();
btnSubmit.style.display="none";// seulement un envoi à la fois, SVP :)
divResponse.innerHTML="";// supprime les éventuels messages déjà affichés.
const userResponses=getDatasFromInputs(myForm);
answer.duration=Math.round((Date.now()-chronoBegin)/1000);
answer.nbQuestions=0;
answer.nbCorrectAnswers=0;
answer.GroupId=document.getElementById("groupId").value;
// Les réponses sont regroupées par question, donc quand idQuestion change, on connaît le résultat pour la question précédente.
// Pour qu'une réponse soit bonne, il faut cocher toutes les bonnes réponses (si QCM) à la question ET cocher aucune des mauvaises.
let idChoice, idQuestion="", goodResponse=false;
for(let item in userResponses)
{
if(item.startsWith("isCorrect_response_"))// = Nouvelle réponse possible.
{
idChoice = item.substring(item.lastIndexOf("_") + 1);
if(userResponses["question_id_response_"+idChoice] != idQuestion) // = on commence à traiter une nouvelle question.
{
idQuestion=userResponses["question_id_response_"+idChoice];
answer.nbQuestions++;
if(goodResponse) // = pas d'erreur à la question précédente
answer.nbCorrectAnswers++;
goodResponse=true;// La réponse est considérée comme bonne, jusqu'à la première erreur...
}
if(userResponses[item] == "true")
{
document.getElementById("response_"+idChoice).parentNode.classList.add("isCorrect");
if(userResponses["response_"+idChoice] === undefined)// = une bonne réponse n'a pas été sélectionnée
goodResponse=false;
}
else
{
if(userResponses["response_"+idChoice] === "on")
{
goodResponse=false; // = une mauvaise réponse a été sélectionnée
document.getElementById("response_"+idChoice).parentNode.classList.add("isNotCorrect");
}
}
}
}
// Si j'ai bien répondu à la dernière question, il faut le compter ici, car on est sorti de la boucle :
if(goodResponse)
answer.nbCorrectAnswers++;
// Affichage du résultat, suivant si l'utilisateur est connecté ou pas et son score :
let getOuput=checkAnswerOuput(answer);
if(isConnected)
{
// Si l'utilisateur est connecté, on passe son résultat au serveur pour le sauvegarder.
const xhrSaveAnswer = new XMLHttpRequest();
xhrSaveAnswer.open("POST", apiUrl+groupRoutes+saveAnswersRoute);
xhrSaveAnswer.onreadystatechange = function()
{
if (this.readyState == XMLHttpRequest.DONE)
{
let xhrResponse=JSON.parse(this.responseText);
if (this.status === 201 && (xhrResponse.message))
{
getOuput+="
"+xhrResponse.message.replace("#URL", configTemplate.userHomePage);
checkPreviousResponses(user);
}
else
getOuput+="
"+responseSavedError.replace("#URL", configTemplate.userHomePage);
// Puis on le redirige vers son résultat :
window.location.hash="";
const here=window.location;// window.location à ajouter pour ne pas quitter la page en mode "preview"...
window.location.assign(here+"explanations");
}
}
xhrSaveAnswer.setRequestHeader("Authorization", "Bearer "+user.token);
xhrSaveAnswer.setRequestHeader("Content-Type", "application/json");
answer.timeDifference=getTimeDifference();// On en profite pour mettre les pendules à l'heure.
xhrSaveAnswer.send(JSON.stringify(answer));
}
else
{ // Si internaute non connecté, on enregistre le résultat côté client pour permettre de le retrouver au moment de la création du compte ou de la connexion.
if(saveAnswer(answer))
{
getOuput+="
"+wantToSaveResponses+"
"; addElement(divResponse, "p", getOuput, "", ["success"]); document.querySelector(".subscribeBtns").style.display="block"; } else // Mais inutile de proposer de créer un compte si le stockage local ne fonctionne pas addElement(divResponse, "p", getOuput, "", ["success"]); // Puis on le redirige vers son résultat : window.location.hash=""; const here=window.location;// window.location à ajouter pour ne pas quitter la page en mode "preview"... window.location.assign(here+"response"); } // + Affichage des textes d'explications pour chaque question const explanations=document.querySelectorAll(".help"); for(let i in explanations) if(explanations[i].style !== undefined) // sinon, la console affiche une erreur "TypeError: explanations[i].style is undefined", bien que tout fonctionne (?) explanations[i].style.display="block"; } catch(e) { addElement(divResponse, "p", serverError, "", ["error"]); console.error(e); } }) // Fonction vérifiant les précédentes réponses de l'utilisateur. // Utile si connecté lors du premier chargement de la page, puis après une nouvelle réponse. const checkPreviousResponses = (user) => { const xhrPreviousRes = new XMLHttpRequest(); xhrPreviousRes.open("GET", apiUrl+groupRoutes+getPreviousAnswers+user.id+"/"+document.getElementById("groupId").value); xhrPreviousRes.onreadystatechange = function() { if (this.readyState == XMLHttpRequest.DONE) { let response=JSON.parse(this.responseText); if (this.status === 200) { const nbResponses=response.length; let previousAnswersContent=""; addElement(explanationsTitle, "span", previousAnswersTitle.replace("#NOM", user.name)); if(nbResponses!==0) { let totNbQuestions=0, totNbCorrectAnswers=0, totDuration=0, mapLineContent; for(let i in response) { totNbQuestions+=response[i].nbQuestions;// ! on ne peut se baser sur la version actuelle du quiz, car le nombre de questions a pu évoluer. totNbCorrectAnswers+=response[i].nbCorrectAnswers; totDuration+=response[i].duration; mapLineContent = { DATEANSWER : dateFormat(response[i].createdAt, lang), NBCORRECTANSWERS : response[i].nbCorrectAnswers, NBQUESTIONS : response[i].nbQuestions, AVGDURATION : response[i].duration }; previousAnswersContent+="