v0.3.11-beta: toon leerlingvoortgang in nieuw admin-tabblad
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 17s
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 17s
- Nieuwe "Voortgang"-tab naast Toewijzingen: leerkracht kiest een leerling (zelfde per-klas gegroepeerde select) en ziet de volledige tijdlijn van GET /progress/pupil/:id (widgettype, score, tijdstip) - fillPupilSelect(): gedeelde helper voor de per-klas leerlingselectie, onttrokken uit Toewijzingen zodat Voortgang hem ook gebruikt - Nieuwe i18n-teksten amProgress/amProgressEmpty (nl/en)
This commit is contained in:
parent
04e9bf3847
commit
aac7fd1c68
3 changed files with 66 additions and 19 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
0.3.10-beta
|
||||
0.3.11-beta
|
||||
|
|
|
|||
|
|
@ -448,6 +448,27 @@
|
|||
return row;
|
||||
}
|
||||
|
||||
/* vult een <select> met leerlingen gegroepeerd per klas (optgroup), gedeeld
|
||||
tussen de Toewijzingen- en Voortgang-tab die beide één leerling kiezen */
|
||||
function fillPupilSelect(sel, pupils){
|
||||
sel.appendChild(new Option(T("amPickPupil"), ""));
|
||||
const byClass = new Map();
|
||||
pupils.forEach(u=>{
|
||||
const key = u.classId || "none";
|
||||
if(!byClass.has(key)) byClass.set(key, []);
|
||||
byClass.get(key).push(u);
|
||||
});
|
||||
const addGroup = (label, us)=>{
|
||||
if(!us || !us.length) return;
|
||||
const og = document.createElement("optgroup");
|
||||
og.label = label;
|
||||
us.forEach(u=>og.appendChild(new Option(u.displayName, u.id)));
|
||||
sel.appendChild(og);
|
||||
};
|
||||
CLASSES.forEach(c=>addGroup(c.name, byClass.get(c.id)));
|
||||
addGroup(T("amNoClass"), byClass.get("none"));
|
||||
}
|
||||
|
||||
/* leerling-omgeving: welk eigen bord ziet een klas (standaard), of een individuele
|
||||
leerling (uitzondering voor wie op een ander niveau werkt) - zie roadmap-memo. */
|
||||
function renderToewijzingenPanel(panel){
|
||||
|
|
@ -468,22 +489,7 @@
|
|||
panel.appendChild(h("div","am-group", T("amAssignPupils")));
|
||||
const pupils = USERS.filter(u=>u.role==="pupil");
|
||||
const pupilSel = h("select","am-sel");
|
||||
pupilSel.appendChild(new Option(T("amPickPupil"), ""));
|
||||
const byClass = new Map();
|
||||
pupils.forEach(u=>{
|
||||
const key = u.classId || "none";
|
||||
if(!byClass.has(key)) byClass.set(key, []);
|
||||
byClass.get(key).push(u);
|
||||
});
|
||||
const addGroup = (label, us)=>{
|
||||
if(!us || !us.length) return;
|
||||
const og = document.createElement("optgroup");
|
||||
og.label = label;
|
||||
us.forEach(u=>og.appendChild(new Option(u.displayName, u.id)));
|
||||
pupilSel.appendChild(og);
|
||||
};
|
||||
CLASSES.forEach(c=>addGroup(c.name, byClass.get(c.id)));
|
||||
addGroup(T("amNoClass"), byClass.get("none"));
|
||||
fillPupilSelect(pupilSel, pupils);
|
||||
panel.appendChild(pupilSel);
|
||||
const pupilRowHolder = h("div");
|
||||
panel.appendChild(pupilRowHolder);
|
||||
|
|
@ -497,6 +503,44 @@
|
|||
});
|
||||
}
|
||||
|
||||
/* één regel in de voortgangstijdlijn van een leerling: widgettype, score en tijdstip */
|
||||
function progressRow(ev){
|
||||
const row = h("div","am-row");
|
||||
row.appendChild(h("span","am-name", T("wg_"+ev.widgetType) || ev.widgetType));
|
||||
const scoreParts = [];
|
||||
if(ev.attempts) scoreParts.push(`${ev.correct}/${ev.attempts} ${T("mGoed")}`);
|
||||
if(ev.stars) scoreParts.push("★".repeat(ev.stars));
|
||||
row.appendChild(h("span","am-code", scoreParts.join(" · ")));
|
||||
row.appendChild(h("span","am-code", new Date(ev.createdAt).toLocaleString(LANG==="nl" ? "nl-NL" : "en-GB")));
|
||||
return row;
|
||||
}
|
||||
|
||||
/* voortgang: leerkracht kiest een leerling (zelfde groepering per klas als
|
||||
Toewijzingen) en ziet de volledige tijdlijn van diens afgeronde
|
||||
widget-momenten (ronde sommen, voltooid woord, geknapte ballon). */
|
||||
function renderVoortgangPanel(panel){
|
||||
const schoolChosen = currentUser.role!=="super" || !!selSchool;
|
||||
if(!schoolChosen){
|
||||
panel.appendChild(h("div","guestnote", T("amPickSchoolHint")));
|
||||
return;
|
||||
}
|
||||
const pupils = USERS.filter(u=>u.role==="pupil");
|
||||
const pupilSel = h("select","am-sel");
|
||||
fillPupilSelect(pupilSel, pupils);
|
||||
panel.appendChild(pupilSel);
|
||||
const eventsHolder = h("div","am-list");
|
||||
panel.appendChild(eventsHolder);
|
||||
pupilSel.addEventListener("change", async ()=>{
|
||||
eventsHolder.innerHTML = "";
|
||||
if(!pupilSel.value) return;
|
||||
let events = [];
|
||||
try{ events = (await api("/progress/pupil/"+pupilSel.value)).events || []; }
|
||||
catch(e){ msg(e.message); return; }
|
||||
if(!events.length){ eventsHolder.appendChild(h("div","guestnote", T("amProgressEmpty"))); return; }
|
||||
events.forEach(ev=>eventsHolder.appendChild(progressRow(ev)));
|
||||
});
|
||||
}
|
||||
|
||||
/* zoekfilter voor de gebruikerslijst: verbergt niet-matchende rijen en lege groepen
|
||||
via style.display i.p.v. een her-render, zodat het zoekveld focus houdt tijdens
|
||||
het typen (een volledige render() zou het veld elke toets opnieuw aanmaken) */
|
||||
|
|
@ -602,6 +646,7 @@
|
|||
tabDefs.push(["klassen", T("amClasses")]);
|
||||
tabDefs.push(["gebruikers", T("amUsers")]);
|
||||
tabDefs.push(["toewijzingen", T("amAssignments")]);
|
||||
tabDefs.push(["voortgang", T("amProgress")]);
|
||||
if(!tabDefs.some(([t])=>t===adminTab)) adminTab = tabDefs[0][0];
|
||||
|
||||
const tabsBar = h("div","ed-tabs");
|
||||
|
|
@ -614,7 +659,7 @@
|
|||
});
|
||||
modal.appendChild(tabsBar);
|
||||
|
||||
const panels = { scholen: renderScholenPanel, klassen: renderKlassenPanel, gebruikers: renderGebruikersPanel, toewijzingen: renderToewijzingenPanel };
|
||||
const panels = { scholen: renderScholenPanel, klassen: renderKlassenPanel, gebruikers: renderGebruikersPanel, toewijzingen: renderToewijzingenPanel, voortgang: renderVoortgangPanel };
|
||||
tabDefs.forEach(([t])=>{
|
||||
const panel = h("div","am-tabpanel");
|
||||
panel.dataset.panel = t;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"use strict";
|
||||
/* version — shown until /api/version resolves (or if the fetch fails, e.g. offline).
|
||||
Kept in sync by hand with the VERSION file at the repo root on every release. */
|
||||
const VERSION = "0.3.10-beta";
|
||||
const VERSION = "0.3.11-beta";
|
||||
(function(){
|
||||
const tag = document.getElementById("verTag");
|
||||
tag.textContent = "v"+VERSION;
|
||||
|
|
@ -175,6 +175,7 @@ const I18N = {
|
|||
amAssignments:"Toewijzingen", amAssignClasses:"Per klas (standaard)", amAssignPupils:"Per leerling (uitzondering)",
|
||||
amPickPupil:"— kies een leerling —", amNoAssignment:"— geen toewijzing —",
|
||||
amAssignedAs:"Toegewezen:", amAssign:"Toewijzen", amAssignSaved:"Toewijzing opgeslagen.",
|
||||
amProgress:"Voortgang", amProgressEmpty:"Nog geen voortgang voor deze leerling.",
|
||||
amConfirmDel:"Weet je zeker dat je deze gebruiker wilt verwijderen?",
|
||||
amCopy:"kopieer", amCopyAll:"kopieer alles",
|
||||
amResetClass:"reset wachtwoorden klas", amResetSchool:"reset alle leerlingwachtwoorden",
|
||||
|
|
@ -334,6 +335,7 @@ const I18N = {
|
|||
amAssignments:"Assignments", amAssignClasses:"Per class (default)", amAssignPupils:"Per pupil (override)",
|
||||
amPickPupil:"— pick a pupil —", amNoAssignment:"— no assignment —",
|
||||
amAssignedAs:"Assigned:", amAssign:"Assign", amAssignSaved:"Assignment saved.",
|
||||
amProgress:"Progress", amProgressEmpty:"No progress for this pupil yet.",
|
||||
amConfirmDel:"Are you sure you want to delete this user?",
|
||||
amCopy:"copy", amCopyAll:"copy all",
|
||||
amResetClass:"reset class passwords", amResetSchool:"reset all pupil passwords",
|
||||
|
|
|
|||
Loading…
Reference in a new issue