Ajout d'une page permettant de lister les quizs auxquels l'utilisateur a déjà répondu.

This commit is contained in:
Fabrice PENHOËT 2022-03-21 18:33:06 +01:00
parent 0a0e394964
commit 0a8d47ad38
5 changed files with 275 additions and 1 deletions

192
front/public/src/myQuizs.js Normal file
View File

@ -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+="<br><br>"+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+="<br><br>"+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);
}
});*/

View File

@ -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", "<a href=\"/"+configTemplate.userHomePage+"\" class=\"button cardboard\">"+configTemplate.userHomePageTxt+"</a>", "", ["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+=`<li><a href="${quiz.url}#explanations">${quiz.title}</a></li>`;
if(html !== "")
addElement(listElt, "ul", html+"ici");
else
addElement(listElt, "p", noPreviousResultsAtAll);
}
}

View File

@ -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"

View File

@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Retrouvez vos résultats aux quizs auxquels vous avez déjà répondu sur WikiLerni.">
<title>Retrouvez vos résultats aux quizs WikiLerni</title>
<!-- Version lisible des scripts : https://forge.chapril.org/Fab_Blab/WikiLerni/src/branch/master/front/src -->
<script src="/JS/polyfill.app.js" defer></script>
<script src="/JS/myQuizs.app.js" defer></script>
<link rel="shortcut icon" href="/img/favicon.ico">
<link rel="stylesheet" href="/themes/wikilerni/css/style.css">
<link rel="canonical" href="https://www.wililerni.com/mes-quizs.html">
<link rel="alternate" type="application/atom+xml" title="WikiLerni" href="/atom.xml">
</head>
<body class="cardboard">
<!-- En tête -->
<header class="cardboard">
<a href="/" title="Page d'accueil WikLerni"><img src="/themes/wikilerni/img/wikilerni-purple-2-128.png" alt="WikiLerni (logo)" title="Accéder à la page d'accueil de WikiLerni" /></a>
<ul id="headLinks">
<li><a href="/contact.html" rel="nofollow">Contact</a></li>
<li><a href="/quizs/" title="Retrouvez vos résultats">Mes quizs</a></li>
<li><a href="/quizs/" title="Les dernières publications">Parcourir</a></li>
<li><a href="/a-propos.html">À propos</a></li>
<li><a href="/" title="Page d'accueil de WikiLerni">Accueil</a></li>
</ul>
</header>
<div id="prompt" class="cardboard">
<a href="/" title="Page d'accueil WikLerni"><img src="/themes/wikilerni/img/wikilerni-purple-2-512.png" alt="Logo WikiLerni" title="W I K I L E R N I" /></a>
<p>Cultivons notre jardin !</p>
</div>
<div id="page" class="cardboard">
<h1 class="cardboard">Les quizs auxquels vous avez déjà répondu</h1>
<noscript>Désolé, mais pour linstant, lutilisation de WikiLerni nécessite lactivation du JavaScript.</noscript>
<div id="quizsList"></div>
<div id="explanations" class="framed engraved">
<h2>Comment ça marche?</h2>
<p>À chaque fois que vous répondez à un quiz sur WikiLerni, votre résultat peut être enregistré, si vous l'acceptez.</p>
<p>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.</p>
<p>Par ailleurs, ces données ne sont accessibles qu'à partir du navigateur vous ayant permi des les enregistrer.<br>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é.</p>
</div>
</div>
<footer class="cardboard">
<ul id="footLinks">
<li><a href="https://diaspora-fr.org/people/815767c0c09e0139ec6f32a01d0dfba2" title="Blog WikiLerni sur diaspora*">Blog</a></li>
<li><a href="/credits.html">Crédits</a></li>
<li><a href="/mentions-legales.html" rel="nofollow">Mentions légales</a></li>
<li><a href="/donnees.html">Données personnelles</a></li>
<li><a href="/CGV-CGU.html" rel="nofollow">CGV &amp; CGU</a></li>
</ul>
</footer>
</body>
</html>

View File

@ -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 <b>AVGCORRECTANSWERS % de bonnes réponses</b>.",
previousResultsTitle: "Voici vos précédents résultats à ce quiz",