All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 1m23s
199 lines
8.6 KiB
JavaScript
199 lines
8.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";
|
||
let castPathTitle = "", castPathSteps = [];
|
||
/* 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, cat: inst.def.cat })).filter(w=>w.wid);
|
||
}
|
||
function castPathWidgetAllowed(widgetId){
|
||
const widget = currentBoardWidgets().find(item=>item.wid===widgetId);
|
||
return !!widget && ["taal","rekenen"].includes(widget.cat);
|
||
}
|
||
|
||
btnCast.addEventListener("click", async ()=>{
|
||
stashCurrent(); /* verse widget-state, zelfde als btnFolders doet */
|
||
castOpen = true;
|
||
castSelWidget = ""; castSelMode = "werken";
|
||
castPathTitle = ""; castPathSteps = [];
|
||
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(castPathSteps.length){
|
||
body.title = castPathTitle.trim();
|
||
body.sequence = castPathSteps.map(step=>({boardId:step.boardId, widgetId:step.widgetId, mode:step.mode}));
|
||
}else 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";
|
||
addStep.disabled = !castPathWidgetAllowed(castSelWidget) || castPathSteps.length >= 12;
|
||
});
|
||
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("castPathTitle")));
|
||
const pathTitle = h("input","am-inp");
|
||
pathTitle.type = "text";
|
||
pathTitle.maxLength = 80;
|
||
pathTitle.placeholder = T("castPathPlaceholder");
|
||
pathTitle.value = castPathTitle;
|
||
pathTitle.addEventListener("input", ()=>{ castPathTitle = pathTitle.value; castActiveKeys.clear(); });
|
||
sheet.appendChild(pathTitle);
|
||
const addStep = h("button","am-btn", T("castPathAdd"));
|
||
addStep.type = "button";
|
||
addStep.disabled = !castPathWidgetAllowed(castSelWidget) || castPathSteps.length >= 12;
|
||
addStep.title = T("castPathOnlyLearning");
|
||
addStep.addEventListener("click", ()=>{
|
||
if(!castPathWidgetAllowed(castSelWidget) || castPathSteps.length >= 12) return;
|
||
const widget = currentBoardWidgets().find(item=>item.wid===castSelWidget);
|
||
castPathSteps.push({boardId:curSlot().id, widgetId:castSelWidget, widgetType:widget?.id || "", mode:castSelMode});
|
||
if(!castPathTitle) castPathTitle = T("castPathDefault");
|
||
castActiveKeys.clear();
|
||
renderCast();
|
||
});
|
||
sheet.appendChild(addStep);
|
||
if(castPathSteps.length){
|
||
const pathList = h("div","cast-path-list");
|
||
castPathSteps.forEach((step,index)=>{
|
||
const row = h("div","cast-path-step");
|
||
row.appendChild(h("span","cast-path-number", String(index+1)));
|
||
const label = h("span","am-name", T("wg_"+step.widgetType));
|
||
label.appendChild(h("small","am-role", " · "+T(step.mode==="kijken"?"amModeWatch":"amModeWork")));
|
||
row.appendChild(label);
|
||
const up = h("button","am-btn","↑"); up.type="button"; up.disabled=index===0;
|
||
up.addEventListener("click",()=>{ [castPathSteps[index-1],castPathSteps[index]]=[castPathSteps[index],castPathSteps[index-1]]; castActiveKeys.clear(); renderCast(); });
|
||
const down = h("button","am-btn","↓"); down.type="button"; down.disabled=index===castPathSteps.length-1;
|
||
down.addEventListener("click",()=>{ [castPathSteps[index+1],castPathSteps[index]]=[castPathSteps[index],castPathSteps[index+1]]; castActiveKeys.clear(); renderCast(); });
|
||
const remove = h("button","am-btn","×"); remove.type="button";
|
||
remove.setAttribute("aria-label",T("amDelete"));
|
||
remove.addEventListener("click",()=>{ castPathSteps.splice(index,1); castActiveKeys.clear(); renderCast(); });
|
||
row.append(up,down,remove);
|
||
pathList.appendChild(row);
|
||
});
|
||
sheet.appendChild(pathList);
|
||
sheet.appendChild(h("div","guestnote", T("castPathHint").replace("{count}",castPathSteps.length)));
|
||
}
|
||
|
||
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);
|
||
}
|