From 873f8e0bee6c11debc950a670e2b79c924359393 Mon Sep 17 00:00:00 2001 From: Ramon Date: Tue, 14 Jul 2026 15:51:20 +0200 Subject: [PATCH] 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) --- VERSION | 2 +- public/css/teach.css | 10 ++++++++++ public/js/core.js | 4 +++- public/js/pupil.js | 40 ++++++++++++++++++++++++++++++++-------- 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/VERSION b/VERSION index f08873b..b8a760f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.3.14-beta +0.3.15-beta diff --git a/public/css/teach.css b/public/css/teach.css index f94074a..4330598 100644 --- a/public/css/teach.css +++ b/public/css/teach.css @@ -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{ diff --git a/public/js/core.js b/public/js/core.js index 5f8f479..da62ed1 100644 --- a/public/js/core.js +++ b/public/js/core.js @@ -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", diff --git a/public/js/pupil.js b/public/js/pupil.js index 6901314..4646970 100644 --- a/public/js/pupil.js +++ b/public/js/pupil.js @@ -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");