Voeg thematische kleurplaten en creatieve niveaus toe (v0.4.65-beta) #3

Open
bes-r wants to merge 207 commits from bes-r/kleurplaten-v0-4-65 into main AGit
4 changed files with 46 additions and 10 deletions
Showing only changes of commit 873f8e0bee - Show all commits

View file

@ -1 +1 @@
0.3.14-beta
0.3.15-beta

View file

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

View file

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

View file

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