/* 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); }