v0.3.15-beta: kijken/werken-modus in de leerling-omgeving
- pupil.js: verwerkt de modus uit /my/assignment; "kijken" toont een meekijk-badge, blokkeert alle interactie en logt geen voortgang; "werken" (default) is het bestaande gedrag met voortgang-logging - Poll-interval dynamisch: 5s in kijken-modus (wijzigingen van de leerkracht komen vlot door), 20s in werken-modus; moduswissel her-rendert direct - teach.css: generieke kijken-overlay (pointer-events:none op de widget-body), werkt voor elk widgettype zonder eigen readonly-ondersteuning - Nieuwe i18n-tekst pupilWatching (nl/en)
This commit is contained in:
parent
b13fe65da5
commit
873f8e0bee
4 changed files with 46 additions and 10 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
0.3.14-beta
|
||||
0.3.15-beta
|
||||
|
|
|
|||
|
|
@ -139,6 +139,16 @@
|
|||
position:static; max-width:100%; box-shadow:0 6px 20px rgba(30,50,90,.12);
|
||||
}
|
||||
.pupil-card .widget-body{width:100%; height:100%;}
|
||||
/* kijken-modus: alle interactie geblokkeerd (generiek, werkt voor elk
|
||||
widgettype zonder eigen readonly-ondersteuning) + zichtbaar meekijk-label */
|
||||
.pupil-card.watch{position:relative;}
|
||||
.pupil-card.watch .widget-body{pointer-events:none; user-select:none;}
|
||||
.pupil-watch-badge{
|
||||
position:absolute; top:8px; right:10px; z-index:2; pointer-events:none;
|
||||
font-size:12.5px; font-weight:700; color:var(--muted);
|
||||
background:var(--panel, rgba(255,255,255,.85)); border-radius:999px; padding:3px 10px;
|
||||
box-shadow:0 2px 8px rgba(30,50,90,.15);
|
||||
}
|
||||
.pupil-empty{margin:auto; color:var(--muted); font-size:16px; font-weight:600;}
|
||||
/* floating add-button (FAB) */
|
||||
#fab{
|
||||
|
|
|
|||
|
|
@ -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.14-beta";
|
||||
const VERSION = "0.3.15-beta";
|
||||
(function(){
|
||||
const tag = document.getElementById("verTag");
|
||||
tag.textContent = "v"+VERSION;
|
||||
|
|
@ -158,6 +158,7 @@ const I18N = {
|
|||
roleSuper:"Overkoepelend beheerder", roleAdmin:"Schoolbeheerder",
|
||||
roleTeacher:"Groepsleiding", rolePupil:"Leerling",
|
||||
pupilEmpty:"Je leerkracht heeft nog niets voor je klaargezet.",
|
||||
pupilWatching:"meekijken",
|
||||
amUsers:"Gebruikers", amClasses:"Klassen", amSchools:"Scholen", amOverarching:"Overkoepelend",
|
||||
amPickSchoolHint:"Kies eerst een school bij het tabblad “Overkoepelend”.",
|
||||
amNewSchool:"+ School", amNewClass:"+ Klas", amNewTeacher:"+ Groepsleiding",
|
||||
|
|
@ -318,6 +319,7 @@ const I18N = {
|
|||
roleSuper:"Overarching administrator", roleAdmin:"School administrator",
|
||||
roleTeacher:"Group leader", rolePupil:"Pupil",
|
||||
pupilEmpty:"Your teacher hasn't set up anything for you yet.",
|
||||
pupilWatching:"watching",
|
||||
amUsers:"Users", amClasses:"Classes", amSchools:"Schools", amOverarching:"Overarching",
|
||||
amPickSchoolHint:"First pick a school in the “Overarching” tab.",
|
||||
amNewSchool:"+ School", amNewClass:"+ Class", amNewTeacher:"+ Group leader",
|
||||
|
|
|
|||
|
|
@ -2,19 +2,26 @@
|
|||
klaargezet (via een toewijzing aan de klas of de leerling zelf), in
|
||||
speelmodus (readonly) en zonder de sleep/vergroot/sluit-chrome van het bord.
|
||||
Geen momentopname: /my/assignment zoekt live in het bord van de leerkracht,
|
||||
dus deze pagina pollt periodiek zodat nieuwe woorden vanzelf doorkomen. */
|
||||
dus deze pagina pollt periodiek zodat nieuwe woorden vanzelf doorkomen.
|
||||
Modus per toewijzing: "werken" (default, leerling werkt in de widget en
|
||||
voortgang wordt gelogd) of "kijken" (alleen meekijken: alle interactie
|
||||
geblokkeerd via een overlay, snellere poll zodat wijzigingen van de
|
||||
leerkracht vlot doorkomen). */
|
||||
const PUPIL_WIDGET_CATS = ["taal", "rekenen"];
|
||||
const PUPIL_POLL_MS = { kijken: 5000, werken: 20000 };
|
||||
const pupilView = document.getElementById("pupilView");
|
||||
let pupilPollTimer = null;
|
||||
let pupilLastSig = null;
|
||||
let pupilMode = "werken";
|
||||
|
||||
function pupilAllowedDefs(){
|
||||
return REGISTRY.filter(d => PUPIL_WIDGET_CATS.includes(d.cat));
|
||||
}
|
||||
|
||||
function renderPupilWidgets(widgets){
|
||||
function renderPupilWidgets(widgets, mode){
|
||||
pupilView.innerHTML = "";
|
||||
const defs = pupilAllowedDefs();
|
||||
const watch = mode === "kijken";
|
||||
const usable = widgets
|
||||
.map(w => ({ w, def: defs.find(d => d.id === w.id) }))
|
||||
.filter(x => x.def);
|
||||
|
|
@ -27,30 +34,46 @@ function renderPupilWidgets(widgets){
|
|||
}
|
||||
usable.forEach(({ w, def }, idx)=>{
|
||||
const card = document.createElement("div");
|
||||
card.className = "widget pupil-card";
|
||||
card.className = "widget pupil-card" + (watch ? " watch" : "");
|
||||
card.style.width = def.w + "px";
|
||||
card.style.minHeight = def.h + "px";
|
||||
const body = document.createElement("div");
|
||||
body.className = "widget-body";
|
||||
card.appendChild(body);
|
||||
if(watch){
|
||||
const badge = document.createElement("div");
|
||||
badge.className = "pupil-watch-badge";
|
||||
badge.textContent = "👁 " + T("pupilWatching");
|
||||
card.appendChild(badge);
|
||||
}
|
||||
pupilView.appendChild(card);
|
||||
/* borden die vóór de invoering van stabiele widget-ids zijn opgeslagen
|
||||
hebben nog geen wid - dan is positie in de lijst de terugval-identiteit,
|
||||
stabiel zolang de leerkracht de widgets op het bord niet herschikt */
|
||||
const widgetId = w.wid || (def.id + ":" + idx);
|
||||
def.mount(body, w.state || null, { readonly:true, onProgress: (p)=>{
|
||||
/* in kijken-modus geen voortgang: de leerling doet zelf niets */
|
||||
const opts = watch ? { readonly:true } : { readonly:true, onProgress: (p)=>{
|
||||
api("/my/progress", { method:"POST", body: { widgetId, widgetType: def.id, ...p } }).catch(()=>{});
|
||||
}});
|
||||
}};
|
||||
def.mount(body, w.state || null, opts);
|
||||
});
|
||||
}
|
||||
|
||||
async function pupilPoll(){
|
||||
try{
|
||||
const res = await api("/my/assignment");
|
||||
const sig = JSON.stringify(res.widgets || []);
|
||||
const mode = res.mode === "kijken" ? "kijken" : "werken";
|
||||
/* modus in de signature zodat een moduswissel ook zonder widget-wijziging
|
||||
een her-render (en dus de juiste overlay) oplevert */
|
||||
const sig = mode + "|" + JSON.stringify(res.widgets || []);
|
||||
if(sig !== pupilLastSig){
|
||||
pupilLastSig = sig;
|
||||
renderPupilWidgets(res.widgets || []);
|
||||
renderPupilWidgets(res.widgets || [], mode);
|
||||
}
|
||||
if(mode !== pupilMode){
|
||||
pupilMode = mode;
|
||||
clearInterval(pupilPollTimer);
|
||||
pupilPollTimer = setInterval(pupilPoll, PUPIL_POLL_MS[mode]);
|
||||
}
|
||||
}catch(e){ /* stille mislukking - volgende poll probeert opnieuw */ }
|
||||
}
|
||||
|
|
@ -58,9 +81,10 @@ async function pupilPoll(){
|
|||
function startPupilView(){
|
||||
document.body.classList.add("pupil-mode");
|
||||
pupilLastSig = null;
|
||||
pupilMode = "werken";
|
||||
pupilPoll();
|
||||
clearInterval(pupilPollTimer);
|
||||
pupilPollTimer = setInterval(pupilPoll, 20000);
|
||||
pupilPollTimer = setInterval(pupilPoll, PUPIL_POLL_MS[pupilMode]);
|
||||
}
|
||||
function stopPupilView(){
|
||||
document.body.classList.remove("pupil-mode");
|
||||
|
|
|
|||
Loading…
Reference in a new issue