WikiLerni/front/src/quiz.js

219 lines
8.5 KiB
JavaScript

// -- GESTION DU FORMULAIRE PERMETTANT D'AFFICHER ET RÉPONDRE À UN QUIZ
/// Il n'est pas nécessaire d'avoir une base de données locale active pour répondre à un quiz.
/// Mais si ce n'est pas déjà le cas et que cela semble techniquement possible, on propose au répondant de sauvegarder son résultat.
/// Dans ce but son résultat est stocké temporairement.
/// Si la base de donnée locale existe déjà, l'enregistrement de son résultat se fait automatiquement dans IndexedDB et ses éventuels précédents résultats sont affichés.
// Configurations générales provenant du backend :
import { availableLangs, theme } from "../../config/instance.js";
const lang=availableLangs[0];
const configTemplate=require("../../views/"+theme+"/config/"+lang+".js");
// Fonctions :
import { checkAllPreviousResults, checkUserAnswers, getResultOutput, resultsOpenDb, saveNewQuiz, saveResult, saveResultTemp, showPreviousResults } from "./tools/answers.js";
import { saveIsReady } from "./tools/clientstorage.js";
import { addElement } from "./tools/dom.js";
import { helloDev } from "./tools/everywhere.js";
import { isEmpty } from "../../tools/main";
import { loadMatomo } from "./tools/matomo.js";
// Textes :
const { wantToSaveResponses, wantToSeaPreviousResults }=require("../../lang/"+lang+"/answer");
const { localDBConnexionFail, serverError }=require("../../lang/"+lang+"/general");
// Informations du quiz, utile pour enregistrement dans base de donnée locale :
const myForm=document.getElementById("quiz");// quiz
const quizInfos=
{
url: window.location.pathname,
GroupId: document.getElementById("groupId").value,
QuestionnaireId: document.getElementById("questionnaireId").value,
title: myForm.dataset.title
};
// Autres éléments du DOM manipulés :
const propose2Save=document.getElementById("propose2Save");
const btnSave=document.getElementById("want2Save");
const btnSubmit=document.getElementById("checkResponses");
const divResponse=document.getElementById("response");
// L'url permet de savoir si nous sommes sur un quiz unique ou groupé :
let btnShow;
if(quizInfos.url.indexOf("/gp/") == -1)
btnShow=document.getElementById("showQuestionnaire"); // le quiz est affiché directement pour les groupes
let userDB, allPreviousAnswers=[];
const initialise = async () =>
{
try
{
if(btnShow)
btnShow.style.display="inline"; // Le bouton est caché si le JS inactif, car le JS est nécessaire pour la suite...
// On vérifie si la navigateur accepte l'utilisation d'IndexedDB :
const saveIsPossible=saveIsReady();
if(saveIsPossible)
{
// On essaye ensuite de se connecter à la base de données (ce qui va la créer si inexistante) :
userDB=await resultsOpenDb("myAnswers", 1);
if(userDB === undefined)
console.error(localDBConnexionFail);
else
{
// Vérifie si l'utilisateur a déjà sauvegardé au moins un résultat (et donc est ok pour les sauvegardes) :
allPreviousAnswers=await checkAllPreviousResults(userDB);
if(allPreviousAnswers.length !== 0)
await showPreviousResults(userDB, quizInfos.QuestionnaireId, quizInfos.GroupId);
}
}
loadMatomo();
}
catch(e)
{
console.error(e);
}
}
initialise();
helloDev();
// Affichage du quiz, quand il est caché par défaut (= l'internaute doit d'abord lire un article Wikipédia).
// Déclenche en même temps le chronomètre mesurant la durée de réponse aux questions.
const showQuestionnaire = () =>
{
chronoBegin=Date.now();
myForm.style.display="block";
btnShow.style.display="none";
const here=window.location; // window.location à ajouter pour ne pas quitter la page en mode "preview".
if(window.location.hash !== "")
{
window.location.hash="";// ! le "#" reste
window.location.assign(here+"questionnaire");
}
else
window.location.assign(here+"#questionnaire");
}
// L'utilisateur demande à voir le quiz :
let chronoBegin=0;
if(btnShow)
{
btnShow.addEventListener("click", function(e)
{
try
{
e.preventDefault();
showQuestionnaire();
}
catch(e)
{
addElement(divResponse, "p", serverError, "", ["error"]);
console.error(e);
}
});
// Un lien peut être passé pour voir directement le quiz :
if(location.hash !== "" && location.hash === "#questionnaire")
showQuestionnaire();
}
// Dans le cas d'un quiz groupé, le chrono est lancé dès l'affichage :
if(quizInfos.GroupId != "0")
{
chronoBegin=Date.now();
btnSubmit.style.display="block";
}
// Traitement de l'envoi de la réponse de l'utilisateur :
let answer;
myForm.addEventListener("submit", async function(e)
{
try
{
e.preventDefault();
btnSubmit.style.display="none"; // seulement une réponse à la fois, SVP :)
divResponse.innerHTML=""; // supprime les éventuels messages déjà affichés
answer=checkUserAnswers(myForm);
answer.duration=Math.round((Date.now()-chronoBegin)/1000);
answer.QuestionnaireId=quizInfos.QuestionnaireId;
answer.GroupId=quizInfos.GroupId;
// Affichage du résultat, suivant les cas :
let getOuput=getResultOutput(answer);
// S'il y a déjà une réponse dans la bd, c'est que l'utilisateur est ok pour les enregister.
if(allPreviousAnswers.length !==0)
{
const saveResponses=await saveResult(userDB, answer);
if(saveResponses)
await showPreviousResults(userDB, quizInfos.QuestionnaireId, quizInfos.GroupId);
getOuput+="<br><br>"+wantToSeaPreviousResults.replace("URL","#explanations");
// Nouveau quiz pour cette personne ?
await saveNewQuiz(userDB, quizInfos);
}
else
{
// S'il n'a pas encore de données, on stocke temporairement le résultat et propose de l'enregistrer :
if(saveResultTemp(answer) && !isEmpty(userDB))
{
getOuput+="<br><br>"+wantToSaveResponses;
propose2Save.style.display="block";
}
}
addElement(divResponse, "p", getOuput, "", ["info"]);
// On redirige vers le résultat :
const here=window.location; // window.location à ajouter pour ne pas quitter la page en mode "preview".
if(window.location.hash !== "")
{
window.location.hash=""; // ! le "#" reste
window.location.assign(here+"response");
}
else
window.location.assign(here+"#response");
// + Affichage des textes d'explication 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);
}
})
// L'utilisateur demande à sauvegarder son résultat :
btnSave.addEventListener("click", async function(e)
{
try
{
e.preventDefault();
if(!isEmpty(userDB) && !isEmpty(answer)) // On ne devrait pas me proposer d'enregistrer dans ce cas, mais...
{
const saveResponses=await saveResult(userDB, answer);
if(saveResponses)
{
// Nouvel enregistrement = actualisation nécessaire de la liste des résultats pour ce quiz :
await showPreviousResults(userDB, quizInfos.QuestionnaireId, quizInfos.GroupId);
// Nouveau quiz
await saveNewQuiz(userDB, quizInfos);
// Redirection vers la liste des résultats :
const here=window.location; // window.location à ajouter pour ne pas quitter la page en mode "preview".
if(window.location.hash !== "")
{
window.location.hash="";// ! le "#" reste
window.location.assign(here+"explanations");
}
else
window.location.assign(here+"#explanations");
}
propose2Save.style.display="none";
}
}
catch(e)
{
addElement(divResponse, "p", serverError, "", ["error"]);
console.error(e);
}
});