All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 38s
- Nieuwe Cast-knop (📡) op de whiteboard-toolbar opent een sheet om het
geopende bord - of één widget erop met kijken/werken - direct naar een
klas of leerling uit te zenden, zonder om te weg via Instellingen.
- Hergebruikt de bestaande PUT/DELETE /assignments/class|pupil routes
ongewijzigd; geen serverwijziging nodig.
- Instellingen > Toewijzingen blijft bestaan maar toont nu alleen de
huidige toewijzing + een "Loskoppelen"-knop (aanmaken/wijzigen gebeurt
voortaan via Cast).
146 lines
5.6 KiB
JavaScript
146 lines
5.6 KiB
JavaScript
/* teach - cast (📡): het geopende bord (of één widget erop) rechtstreeks
|
|
vanaf de toolbar uitzenden naar een klas of leerling, i.p.v. de omweg via
|
|
Instellingen > Toewijzingen. Hergebruikt dezelfde PUT/DELETE-routes als
|
|
admin.js's assignmentRow - geen serverwijziging, alleen een korter pad. */
|
|
"use strict";
|
|
const btnCast = document.getElementById("btnCast");
|
|
btnCast.title = T("cast");
|
|
document.addEventListener("langchange", ()=>{
|
|
btnCast.title = T("cast");
|
|
if(castOpen) renderCast();
|
|
});
|
|
|
|
function h(tag, cls, text){
|
|
const el = document.createElement(tag);
|
|
if(cls) el.className = cls;
|
|
if(text != null) el.textContent = text;
|
|
return el;
|
|
}
|
|
|
|
const castBackdrop = h("div","am-sheet-backdrop");
|
|
castBackdrop.id = "castBackdrop";
|
|
document.body.appendChild(castBackdrop);
|
|
castBackdrop.addEventListener("click", e=>{ if(e.target===castBackdrop) closeCast(); });
|
|
|
|
let castOpen = false;
|
|
let castLoading = false;
|
|
let castClasses = [], castPupils = [];
|
|
let castSelWidget = "", castSelMode = "werken";
|
|
/* toewijzingen die deze sessie al gelukt zijn ("type:id"), zodat je hetzelfde
|
|
bord naar meerdere klassen/leerlingen achter elkaar kunt casten zonder dat
|
|
de sheet na elke keer sluit */
|
|
let castActiveKeys = new Set();
|
|
let castError = "";
|
|
|
|
/* widgets van het live-open bord, zelfde bron als serializeBoard() (board.js) */
|
|
function currentBoardWidgets(){
|
|
return [...instances].map(inst=>({ wid: inst.wid, id: inst.def.id })).filter(w=>w.wid);
|
|
}
|
|
|
|
btnCast.addEventListener("click", async ()=>{
|
|
stashCurrent(); /* verse widget-state, zelfde als btnFolders doet */
|
|
castOpen = true;
|
|
castSelWidget = ""; castSelMode = "werken";
|
|
castActiveKeys = new Set(); castError = "";
|
|
castLoading = true;
|
|
renderCast();
|
|
try{
|
|
const [cRes, uRes] = await Promise.all([ api("/admin/classes"), api("/admin/users") ]);
|
|
castClasses = cRes.classes || [];
|
|
castPupils = (uRes.users || []).filter(u=>u.role==="pupil");
|
|
}catch(e){
|
|
castClasses = []; castPupils = []; castError = e.message;
|
|
}
|
|
castLoading = false;
|
|
renderCast();
|
|
});
|
|
|
|
function closeCast(){ castOpen = false; renderCast(); }
|
|
|
|
function castTargetRow(target){
|
|
const row = h("div","am-row am-target");
|
|
const info = h("div","am-person-info");
|
|
info.appendChild(h("span","am-name", target.label));
|
|
if(target.sub) info.appendChild(h("span","am-role", target.sub));
|
|
row.appendChild(info);
|
|
const key = target.type+":"+target.id;
|
|
if(castActiveKeys.has(key)) row.appendChild(h("span","am-chip active", T("castActive")));
|
|
row.addEventListener("click", async ()=>{
|
|
try{
|
|
const body = { boardId: curSlot().id };
|
|
if(castSelWidget){ body.widgetId = castSelWidget; body.mode = castSelMode; }
|
|
await api(`/assignments/${target.type}/${target.id}`, { method:"PUT", body });
|
|
castActiveKeys.add(key);
|
|
castError = "";
|
|
}catch(e){ castError = e.message; }
|
|
renderCast();
|
|
});
|
|
return row;
|
|
}
|
|
|
|
function renderCast(){
|
|
castBackdrop.classList.toggle("open", castOpen);
|
|
castBackdrop.innerHTML = "";
|
|
if(!castOpen) return;
|
|
|
|
const sheet = h("div","am-sheet");
|
|
castBackdrop.appendChild(sheet);
|
|
|
|
const head = h("div","am-kebab-row");
|
|
const titleWrap = h("div");
|
|
titleWrap.appendChild(h("h2","am-nav-title", T("castTitle")));
|
|
titleWrap.appendChild(h("div","am-role", `${BS.folders[BS.f].name} / ${curSlot().name}`));
|
|
head.appendChild(titleWrap);
|
|
const closeBtn = h("button","am-btn", T("close"));
|
|
closeBtn.type = "button";
|
|
closeBtn.addEventListener("click", closeCast);
|
|
head.appendChild(closeBtn);
|
|
sheet.appendChild(head);
|
|
|
|
sheet.appendChild(h("div","am-msg", castError));
|
|
|
|
sheet.appendChild(h("div","am-group", T("castWhat")));
|
|
const widgetSel = h("select","am-sel");
|
|
widgetSel.appendChild(new Option(T("amWholeBoard"), ""));
|
|
currentBoardWidgets().forEach(w=>widgetSel.appendChild(new Option(T("wg_"+w.id), w.wid)));
|
|
widgetSel.value = castSelWidget;
|
|
const modeSel = h("select","am-sel");
|
|
modeSel.appendChild(new Option(T("amModeWork"), "werken"));
|
|
modeSel.appendChild(new Option(T("amModeWatch"), "kijken"));
|
|
modeSel.value = castSelMode;
|
|
modeSel.style.display = castSelWidget ? "" : "none";
|
|
widgetSel.addEventListener("change", ()=>{
|
|
castSelWidget = widgetSel.value;
|
|
modeSel.style.display = castSelWidget ? "" : "none";
|
|
});
|
|
modeSel.addEventListener("change", ()=>{ castSelMode = modeSel.value; });
|
|
const whatRow = h("div","am-inline");
|
|
whatRow.appendChild(widgetSel);
|
|
whatRow.appendChild(modeSel);
|
|
sheet.appendChild(whatRow);
|
|
|
|
sheet.appendChild(h("div","am-group", T("castClasses")));
|
|
const classList = h("div","am-list");
|
|
if(castLoading) classList.appendChild(h("div","guestnote", "…"));
|
|
else if(!castClasses.length) classList.appendChild(h("div","guestnote", T("castNoClasses")));
|
|
else castClasses.forEach(c=>classList.appendChild(castTargetRow({type:"class", id:c.id, label:c.name})));
|
|
sheet.appendChild(classList);
|
|
|
|
sheet.appendChild(h("div","am-group", T("castPupils")));
|
|
const pupilList = h("div","am-list");
|
|
if(castLoading) pupilList.appendChild(h("div","guestnote", "…"));
|
|
else if(!castPupils.length) pupilList.appendChild(h("div","guestnote", T("castNoPupils")));
|
|
else{
|
|
const byClass = new Map();
|
|
castPupils.forEach(u=>{
|
|
const key = u.classId || "none";
|
|
if(!byClass.has(key)) byClass.set(key, []);
|
|
byClass.get(key).push(u);
|
|
});
|
|
castClasses.forEach(c=>(byClass.get(c.id) || []).forEach(u=>
|
|
pupilList.appendChild(castTargetRow({type:"pupil", id:u.id, label:u.displayName, sub:c.name}))));
|
|
(byClass.get("none") || []).forEach(u=>
|
|
pupilList.appendChild(castTargetRow({type:"pupil", id:u.id, label:u.displayName, sub:T("amUnassigned")})));
|
|
}
|
|
sheet.appendChild(pupilList);
|
|
}
|