diff --git a/front/public/src/myQuizs.js b/front/public/src/myQuizs.js new file mode 100644 index 0000000..013eb4e --- /dev/null +++ b/front/public/src/myQuizs.js @@ -0,0 +1,192 @@ +// -- GESTION DE LA PAGE PERMETTANT DE LISTER LES QUIZS AUXQUELS L'UTILISATEUR A DÉJÀ RÉPONDU + +/// Il est d'abord testé que le navigateur accepte l'utilisation d'IndexDB +/// Si oui la liste de quizs ayant déjà reçu au moins une réponse est affichée. +/// Un bouton permettra aussi de les sauvegarder dans un fichier ou d'importer une sauvegarde. + +// Configurations générales provenant du backend : +import { availableLangs } from "../../../config/instance.js"; +const lang=availableLangs[0]; + +// Textes : +const { localDBNotReady }=require("../../../lang/"+lang+"/answer"); +const { serverError }=require("../../../lang/"+lang+"/general"); + +// Fonctions : +import { addElement } from "./tools/dom.js"; +import { helloDev } from "./tools/everywhere.js"; +import { isEmpty } from "../../../tools/main"; +import { loadMatomo } from "./tools/matomo.js"; + +// Classe s'occupant du stockage des résultats aux quizs : +import { userQuizsResults} from "./tools/userQuizsResults"; + +// Éléments du DOM manipulés : +//const btnSave=document.getElementById("want2Save"); +//const btnSubmit=document.getElementById("checkResponses"); +//const propose2Save=document.getElementById("propose2Save"); +//const responseTxt=document.getElementById("response"); +const quizsList=document.getElementById("quizsList"); + +let userDB, allPreviousAnswers=[], myResults; +const initialise = async () => +{ + try + { + // Instanciation de la classe s'occupant du stockage des résultats aux quizs : + myResults=await userQuizsResults.initialise("myResults", 1); + // Si la base de données est fonctionnel et que des résultats sont déjà enregistrés, on affiche la liste des quizs ayant une réponse connue : + if(myResults.dbIsReady !== false) + await myResults.showMyQuizs(); + else + addElement(quizsList, "p", localDBNotReady); + // Statistiques : + loadMatomo(); + } + catch(e) + { + console.error(e); + } +} +initialise(); +helloDev(); +/* +// Fonction affichant le quiz, quand il est caché par défaut+ déclenchant 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"); +} + +let chronoBegin; +if(btnShow) +{ + btnShow.addEventListener("click", function(e) + { + try + { + e.preventDefault(); + showQuestionnaire(); + } + catch(e) + { + addElement(responseTxt, "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 :) + responseTxt.innerHTML=""; // supprime les éventuels messages déjà affichés + answer=userQuizsResults.checkUserAnswers(myForm); + answer.duration=Math.round((Date.now()-chronoBegin)/1000); + answer.QuestionnaireId=quizInfos.QuestionnaireId; + answer.GroupId=quizInfos.GroupId; + + // Enregistrement et affichage du résultat, suivant les cas : + let getOuput=userQuizsResults.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(myResults.allResults.length !== 0) + { + const saveResponses=await myResults.addResult(answer); + if(saveResponses) + await myResults.showPreviousResultsForId(quizInfos.QuestionnaireId, quizInfos.GroupId); + getOuput+="

"+wantToSeaPreviousResults.replace("URL","#explanations"); + // Nouveau quiz pour cette personne ? + await myResults.saveNewQuiz(quizInfos); + } + else + { + // S'il n'a pas encore de données, on stocke temporairement le résultat et propose de l'enregistrer : + if(myResults.saveResultTemp(answer) && myResults.dbIsReady) + { + getOuput+="

"+wantToSaveResponses; + propose2Save.style.display="block"; + } + } + addElement(responseTxt, "p", getOuput, "", ["info"]); + // On redirige vers le résultat : + const here=window.location; + 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(responseTxt, "p", serverError, "", ["error"]); + console.error(e); + } +}); + +// L'utilisateur demande à sauvegarder son résultat : +btnSave.addEventListener("click", async function(e) +{ + try + { + e.preventDefault(); + if(!isEmpty(myResults.dbIsReady) && !isEmpty(answer)) // On ne devrait pas m'avoir proposé d'enregistrer dans ce cas, mais... + { + const saveResponses=await myResults.addResult(answer); + if(saveResponses) + { + // Nouvel enregistrement = actualisation nécessaire de la liste des résultats pour ce quiz : + await myResults.showPreviousResultsForId(quizInfos.QuestionnaireId, quizInfos.GroupId); + // Nouveau quiz (ce qui doit être le cas, mais...) : + await myResults.saveNewQuiz(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(responseTxt, "p", serverError, "", ["error"]); + console.error(e); + } +});*/ \ No newline at end of file diff --git a/front/public/src/tools/userQuizsResults.js b/front/public/src/tools/userQuizsResults.js index e0dff24..e736104 100644 --- a/front/public/src/tools/userQuizsResults.js +++ b/front/public/src/tools/userQuizsResults.js @@ -11,7 +11,8 @@ import { saveIsReady } from "./clientstorage.js"; // Textes : import { availableLangs } from "../../../../config/instance.js"; const lang=availableLangs[0]; -const { localDBNeedDatas, localDBNeedQuizId, noPreviousResults, previousResultsLine, previousResultsTitle, previousResultsStats, userAnswersFail, userAnswersMedium, userAnswersSuccess }=require("../../../../lang/"+lang+"/answer"); +const { localDBNeedDatas, localDBNeedQuizId, noPreviousResults, noPreviousResultsAtAll, previousResultsLine, previousResultsTitle, previousResultsStats, userAnswersFail, userAnswersMedium, userAnswersSuccess }=require("../../../../lang/"+lang+"/answer"); +const { localDBConnexionFail }=require("../../../../lang/"+lang+"/general"); export class userQuizsResults { @@ -206,6 +207,7 @@ export class userQuizsResults const getquizs=quizsStore.getAll(); getquizs.onsuccess = (e) => { + this.allQuizs=e.target.result; this.db.close(); resolve(e.target.result); }; @@ -446,4 +448,19 @@ export class userQuizsResults /// addElement(explanationsContent, "p", ""+configTemplate.userHomePageTxt+"", "", ["btn"], "", false); } } + + // Retourne une liste HTML des précédents quizs avec lien vers leur page + showMyQuizs(listId="quizsList") + { + const listElt=document.getElementById(listId); + // On affiche d'abord les quizs les plus récents : + const myQuizs=this.allQuizs.reverse(); + let html=""; + for(const quiz of myQuizs) + html+=`
  • ${quiz.title}
  • `; + if(html !== "") + addElement(listElt, "ul", html+"ici"); + else + addElement(listElt, "p", noPreviousResultsAtAll); + } } \ No newline at end of file diff --git a/front/public/webpack.config.js b/front/public/webpack.config.js index 957ce01..e1ff4ff 100644 --- a/front/public/webpack.config.js +++ b/front/public/webpack.config.js @@ -6,6 +6,7 @@ module.exports = entry: { index: "./src/index.js", + myQuizs: "./src/myQuizs", paymentPage: "./src/paymentPage.js", polyfill: "babel-polyfill", quiz: "./src/quiz.js" diff --git a/front/public/www/mes-quizs.html b/front/public/www/mes-quizs.html new file mode 100644 index 0000000..953ee28 --- /dev/null +++ b/front/public/www/mes-quizs.html @@ -0,0 +1,62 @@ + + + + + + + Retrouvez vos résultats aux quizs WikiLerni + + + + + + + + + + + +
    + WikiLerni (logo) + +
    + +
    + Logo WikiLerni +

    Cultivons notre jardin !

    +
    + +
    + +

    Les quizs auxquels vous avez déjà répondu

    + + + +
    + +
    +

    Comment ça marche ?

    +

    À chaque fois que vous répondez à un quiz sur WikiLerni, votre résultat peut être enregistré, si vous l'acceptez.

    +

    Vous n'avez pas besoin de créer un compte, car ces données sont enregistrées dans votre navigateur internet, c'est-à-dire sur votre ordinateur. Ceci peut ne pas fonctionner, si vous utilisez une navigation privée ou d'autres configurations interdisant ce type d'enregistrement.

    +

    Par ailleurs, ces données ne sont accessibles qu'à partir du navigateur vous ayant permi des les enregistrer.
    Aussi, pour éviter de les perdre, ou encore vous permettre de les récupérer sur un autre navigateur, un outil de sauvegarde vous est proposé.

    +
    + +
    + + + + \ No newline at end of file diff --git a/lang/fr/answer.js b/lang/fr/answer.js index 4b98029..d69ab24 100644 --- a/lang/fr/answer.js +++ b/lang/fr/answer.js @@ -3,6 +3,7 @@ module.exports = localDBGetPreviousResultsFail: "Bug de récupération des résultats précédents.", localDBNeedDatas: "Il manque des données nécessaires à l'enregistrement.", localDBNeedQuizId: "Aucun identifiant n'a été fourni pour le quiz.", + localDBNotReady: "Désolé, mais il semble que votre navigateur ne permette pas l'utilisation de cette page. Pour plus d'informations, lisez les explications ci-dessous.", needIntegerNumberCorrectResponses : "Le nombre de réponses correctes doit être un nombre entier.", needIntegerNumberSecondesResponse : "La durée de la réponse doit être un nombre entier de secondes.", needIntegerNumberUserResponses : "Le nombre de questions auxquelles l'utilisateur a répondu doit être un nombre entier.", @@ -14,6 +15,7 @@ module.exports = needMinNumberCorrectResponses : "Le nombre de réponses correctes ne peut être négatif.", needMinNumberSecondesResponse : "La durée de la réponse ne peut être négative.", noPreviousResults: "On dirait que c'est la première fois que vous répondez à ce quiz. Bonne lecture !", + noPreviousResultsAtAll: "Vous n'avez répondu à aucun quiz pour l'instant !", previousResultsLine: "Le DATEANSWER, vous avez répondu correctement à NBCORRECTANSWERS questions sur NBQUESTIONS en AVGDURATION secondes.", previousResultsStats: "En moyenne, vous avez répondu à ce quiz en AVGDURATION secondes, en ayant AVGCORRECTANSWERS % de bonnes réponses.", previousResultsTitle: "Voici vos précédents résultats à ce quiz",