Ajout d'une page permettant de lister les quizs auxquels l'utilisateur a déjà répondu.
This commit is contained in:
parent
0a0e394964
commit
0a8d47ad38
192
front/public/src/myQuizs.js
Normal file
192
front/public/src/myQuizs.js
Normal 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);
|
||||||
|
}
|
||||||
|
});*/
|
@ -11,7 +11,8 @@ import { saveIsReady } from "./clientstorage.js";
|
|||||||
// Textes :
|
// Textes :
|
||||||
import { availableLangs } from "../../../../config/instance.js";
|
import { availableLangs } from "../../../../config/instance.js";
|
||||||
const lang=availableLangs[0];
|
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
|
export class userQuizsResults
|
||||||
{
|
{
|
||||||
@ -206,6 +207,7 @@ export class userQuizsResults
|
|||||||
const getquizs=quizsStore.getAll();
|
const getquizs=quizsStore.getAll();
|
||||||
getquizs.onsuccess = (e) =>
|
getquizs.onsuccess = (e) =>
|
||||||
{
|
{
|
||||||
|
this.allQuizs=e.target.result;
|
||||||
this.db.close();
|
this.db.close();
|
||||||
resolve(e.target.result);
|
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);
|
/// 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);
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,6 +6,7 @@ module.exports =
|
|||||||
entry:
|
entry:
|
||||||
{
|
{
|
||||||
index: "./src/index.js",
|
index: "./src/index.js",
|
||||||
|
myQuizs: "./src/myQuizs",
|
||||||
paymentPage: "./src/paymentPage.js",
|
paymentPage: "./src/paymentPage.js",
|
||||||
polyfill: "babel-polyfill",
|
polyfill: "babel-polyfill",
|
||||||
quiz: "./src/quiz.js"
|
quiz: "./src/quiz.js"
|
||||||
|
62
front/public/www/mes-quizs.html
Normal file
62
front/public/www/mes-quizs.html
Normal 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 l’instant, l’utilisation de WikiLerni nécessite l’activation 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 & CGU</a></li>
|
||||||
|
</ul>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -3,6 +3,7 @@ module.exports =
|
|||||||
localDBGetPreviousResultsFail: "Bug de récupération des résultats précédents.",
|
localDBGetPreviousResultsFail: "Bug de récupération des résultats précédents.",
|
||||||
localDBNeedDatas: "Il manque des données nécessaires à l'enregistrement.",
|
localDBNeedDatas: "Il manque des données nécessaires à l'enregistrement.",
|
||||||
localDBNeedQuizId: "Aucun identifiant n'a été fourni pour le quiz.",
|
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.",
|
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.",
|
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.",
|
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.",
|
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.",
|
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 !",
|
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.",
|
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>.",
|
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",
|
previousResultsTitle: "Voici vos précédents résultats à ce quiz",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user