feat: cast bord/widget vanaf whiteboard naar klas of leerling (v0.4.15-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 38s
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).
This commit is contained in:
parent
3c3638d114
commit
e555876fa4
6 changed files with 191 additions and 72 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
0.4.14-beta
|
0.4.15-beta
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,8 @@
|
||||||
#pupilView{display:none;}
|
#pupilView{display:none;}
|
||||||
body.pupil-mode #board,body.pupil-mode #fab,body.pupil-mode #fabBoardsBtn,
|
body.pupil-mode #board,body.pupil-mode #fab,body.pupil-mode #fabBoardsBtn,
|
||||||
body.pupil-mode #boardStrip,body.pupil-mode #fabmenu,
|
body.pupil-mode #boardStrip,body.pupil-mode #fabmenu,
|
||||||
body.pupil-mode #hud,body.pupil-mode #btnFolders,body.pupil-mode #btnSave{display:none !important;}
|
body.pupil-mode #hud,body.pupil-mode #btnFolders,body.pupil-mode #btnSave,
|
||||||
|
body.pupil-mode #btnCast{display:none !important;}
|
||||||
body.pupil-mode #pupilView{
|
body.pupil-mode #pupilView{
|
||||||
display:flex; flex-wrap:wrap; gap:18px; align-content:flex-start;
|
display:flex; flex-wrap:wrap; gap:18px; align-content:flex-start;
|
||||||
position:absolute; inset:var(--tbar) 0 0 0; overflow:auto; padding:18px; box-sizing:border-box;
|
position:absolute; inset:var(--tbar) 0 0 0; overflow:auto; padding:18px; box-sizing:border-box;
|
||||||
|
|
@ -160,7 +161,8 @@
|
||||||
#parentView{display:none;}
|
#parentView{display:none;}
|
||||||
body.parent-mode #board,body.parent-mode #fab,body.parent-mode #fabBoardsBtn,
|
body.parent-mode #board,body.parent-mode #fab,body.parent-mode #fabBoardsBtn,
|
||||||
body.parent-mode #boardStrip,body.parent-mode #fabmenu,
|
body.parent-mode #boardStrip,body.parent-mode #fabmenu,
|
||||||
body.parent-mode #hud,body.parent-mode #btnFolders,body.parent-mode #btnSave{display:none !important;}
|
body.parent-mode #hud,body.parent-mode #btnFolders,body.parent-mode #btnSave,
|
||||||
|
body.parent-mode #btnCast{display:none !important;}
|
||||||
body.parent-mode #parentView{
|
body.parent-mode #parentView{
|
||||||
display:block; position:absolute; inset:var(--tbar) 0 0 0;
|
display:block; position:absolute; inset:var(--tbar) 0 0 0;
|
||||||
overflow:auto; padding:24px 18px; box-sizing:border-box;
|
overflow:auto; padding:24px 18px; box-sizing:border-box;
|
||||||
|
|
@ -1316,12 +1318,16 @@
|
||||||
font-size:11.5px; font-weight:700; text-transform:uppercase; letter-spacing:.4px;
|
font-size:11.5px; font-weight:700; text-transform:uppercase; letter-spacing:.4px;
|
||||||
color:var(--muted); margin:14px 0 6px;
|
color:var(--muted); margin:14px 0 6px;
|
||||||
}
|
}
|
||||||
.am-person{cursor:pointer;}
|
.am-person,.am-target{cursor:pointer;}
|
||||||
.am-person-info{flex:1; display:flex; flex-direction:column; min-width:0; gap:1px;}
|
.am-person-info{flex:1; display:flex; flex-direction:column; min-width:0; gap:1px;}
|
||||||
.am-person-info .am-name{font-size:14px;}
|
.am-person-info .am-name{font-size:14px;}
|
||||||
.am-person-info .am-role{font-size:11.5px; color:var(--muted); font-weight:600;}
|
.am-person-info .am-role{font-size:11.5px; color:var(--muted); font-weight:600;}
|
||||||
.am-sheet-backdrop{position:fixed; inset:0; background:rgba(15,22,38,.4); z-index:60; display:none;}
|
.am-sheet-backdrop{position:fixed; inset:0; background:rgba(15,22,38,.4); z-index:60; display:none;}
|
||||||
.am-sheet-backdrop.open{display:block;}
|
.am-sheet-backdrop.open{display:block;}
|
||||||
|
/* de Cast-sheet opent vanaf het bord zelf (buiten Instellingen om), dus moet
|
||||||
|
boven de zwevende pen-HUD (#hud, z-index 5500) uitkomen - zelfde laag als
|
||||||
|
.modalwrap (6000), niet de 60 die volstaat zodra je al in Instellingen zit */
|
||||||
|
#castBackdrop{z-index:6000;}
|
||||||
.am-sheet{
|
.am-sheet{
|
||||||
position:absolute; left:0; right:0; bottom:0; max-width:520px; margin:0 auto;
|
position:absolute; left:0; right:0; bottom:0; max-width:520px; margin:0 auto;
|
||||||
background:var(--surface); border-radius:18px 18px 0 0; padding:16px 18px 24px;
|
background:var(--surface); border-radius:18px 18px 0 0; padding:16px 18px 24px;
|
||||||
|
|
@ -1456,6 +1462,7 @@
|
||||||
}
|
}
|
||||||
button.am-chip{cursor:pointer; color:var(--muted);}
|
button.am-chip{cursor:pointer; color:var(--muted);}
|
||||||
button.am-chip.on{border-color:var(--accent); background:var(--accent-soft); color:var(--accent-ink);}
|
button.am-chip.on{border-color:var(--accent); background:var(--accent-soft); color:var(--accent-ink);}
|
||||||
|
.am-chip.active{border-color:var(--green); background:color-mix(in srgb, var(--green) 15%, var(--surface)); color:var(--green);}
|
||||||
.am-chipx{border:none; background:transparent; color:var(--red); font-weight:900; cursor:pointer; padding:0; font-size:11px;}
|
.am-chipx{border:none; background:transparent; color:var(--red); font-weight:900; cursor:pointer; padding:0; font-size:11px;}
|
||||||
.am-reset{
|
.am-reset{
|
||||||
background:var(--surface-2); border:1px solid var(--line); border-radius:12px;
|
background:var(--surface-2); border:1px solid var(--line); border-radius:12px;
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@
|
||||||
<script src="js/pupil.js?v=__V__" defer></script>
|
<script src="js/pupil.js?v=__V__" defer></script>
|
||||||
<script src="js/parent.js?v=__V__" defer></script>
|
<script src="js/parent.js?v=__V__" defer></script>
|
||||||
<script src="js/admin.js?v=__V__" defer></script>
|
<script src="js/admin.js?v=__V__" defer></script>
|
||||||
|
<script src="js/cast.js?v=__V__" defer></script>
|
||||||
<script src="js/image-catalog.js?v=__V__" defer></script>
|
<script src="js/image-catalog.js?v=__V__" defer></script>
|
||||||
<script src="js/app.js?v=__V__" defer></script>
|
<script src="js/app.js?v=__V__" defer></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
@ -61,6 +62,7 @@
|
||||||
<button class="tbtn ghost" id="btnImages">🖼️</button>
|
<button class="tbtn ghost" id="btnImages">🖼️</button>
|
||||||
<button class="tbtn ghost" id="btnFolders">📁</button>
|
<button class="tbtn ghost" id="btnFolders">📁</button>
|
||||||
<button class="tbtn ghost" id="btnSave">💾 <span data-i18n="saveBoard"></span></button>
|
<button class="tbtn ghost" id="btnSave">💾 <span data-i18n="saveBoard"></span></button>
|
||||||
|
<button class="tbtn ghost" id="btnCast" title="Cast">📡</button>
|
||||||
<button class="tbtn ghost" id="btnUser">⚙️ <span id="userLbl"></span></button>
|
<button class="tbtn ghost" id="btnUser">⚙️ <span id="userLbl"></span></button>
|
||||||
<button class="tbtn ghost" id="btnFs" title="Fullscreen">⛶</button>
|
<button class="tbtn ghost" id="btnFs" title="Fullscreen">⛶</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -621,19 +621,9 @@
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* toewijzingsrij: kies een van je eigen borden (uit BS, board.js) voor een klas of
|
/* toewijzingsrij: overzicht van de huidige toewijzing (aangemaakt via de
|
||||||
een individuele leerling. Geen momentopname - de leerling-pagina zoekt het bord
|
Cast-knop op het whiteboard, cast.js) + loskoppelen. Aanmaken/wijzigen
|
||||||
live op, dus nieuwe woorden die je toevoegt komen vanzelf door zonder opnieuw
|
gebeurt niet meer hier - zie cast.js. */
|
||||||
toe te wijzen. Een expliciete "Toewijzen"-knop i.p.v. direct bij het kiezen van
|
|
||||||
een bord opslaan, zodat een klas-brede wijziging niet per ongeluk gebeurt. */
|
|
||||||
/* widgets van een eigen bord (voor de één-widget-keuze in assignmentRow);
|
|
||||||
widgets zonder wid komen uit borden van vóór v0.3.13 en verschijnen pas
|
|
||||||
na de eerstvolgende save van dat bord */
|
|
||||||
function boardWidgets(boardId){
|
|
||||||
let found = null;
|
|
||||||
BS.folders.forEach(fo=>fo.boards.forEach(bo=>{ if(bo.id===boardId) found = bo; }));
|
|
||||||
return ((found && found.data && found.data.widgets) || []).filter(w=>w.wid);
|
|
||||||
}
|
|
||||||
function describeCurrent(current){
|
function describeCurrent(current){
|
||||||
if(!current) return "";
|
if(!current) return "";
|
||||||
let s = `${T("amAssignedAs")} “${current.boardName || current.boardId}”`;
|
let s = `${T("amAssignedAs")} “${current.boardName || current.boardId}”`;
|
||||||
|
|
@ -643,56 +633,23 @@
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
function assignmentRow(target, boardOptions, current){
|
function assignmentRow(target, current){
|
||||||
const sel = h("select","am-sel");
|
const info = h("span","am-code", describeCurrent(current) || T("amNoAssignment"));
|
||||||
sel.appendChild(new Option(T("amNoAssignment"), ""));
|
const tools = h("div","am-tools");
|
||||||
boardOptions.forEach(b=>sel.appendChild(new Option(b.label, b.id)));
|
if(current){
|
||||||
sel.value = current ? current.boardId : "";
|
const del = dangerBtn(T("amUnlink"), null, async ()=>{
|
||||||
/* keuze: heel bord (default, bestaand gedrag) of één widget + modus */
|
|
||||||
const widgetSel = h("select","am-sel am-widget-sel");
|
|
||||||
const modeSel = h("select","am-sel am-mode-sel");
|
|
||||||
modeSel.appendChild(new Option(T("amModeWork"), "werken"));
|
|
||||||
modeSel.appendChild(new Option(T("amModeWatch"), "kijken"));
|
|
||||||
const syncWidgetSel = ()=>{
|
|
||||||
widgetSel.innerHTML = "";
|
|
||||||
widgetSel.appendChild(new Option(T("amWholeBoard"), ""));
|
|
||||||
boardWidgets(sel.value).forEach(w=>widgetSel.appendChild(new Option(T("wg_"+w.id), w.wid)));
|
|
||||||
widgetSel.value = (current && current.boardId===sel.value && current.widgetId) ? current.widgetId : "";
|
|
||||||
widgetSel.style.display = sel.value ? "" : "none";
|
|
||||||
syncModeSel();
|
|
||||||
};
|
|
||||||
const syncModeSel = ()=>{ modeSel.style.display = (sel.value && widgetSel.value) ? "" : "none"; };
|
|
||||||
modeSel.value = current && current.mode ? current.mode : "werken";
|
|
||||||
sel.addEventListener("change", syncWidgetSel);
|
|
||||||
widgetSel.addEventListener("change", syncModeSel);
|
|
||||||
const info = h("span","am-code", describeCurrent(current));
|
|
||||||
syncWidgetSel();
|
|
||||||
const apply = h("button","am-btn", T("amAssign"));
|
|
||||||
apply.type = "button";
|
|
||||||
apply.addEventListener("click", async ()=>{
|
|
||||||
try{
|
try{
|
||||||
if(sel.value){
|
await api(`/assignments/${target.type}/${target.id}`, {method:"DELETE"});
|
||||||
const body = { boardId: sel.value };
|
|
||||||
if(widgetSel.value){ body.widgetId = widgetSel.value; body.mode = modeSel.value; }
|
|
||||||
await api(`/assignments/${target.type}/${target.id}`, {method:"PUT", body});
|
|
||||||
}
|
|
||||||
else await api(`/assignments/${target.type}/${target.id}`, {method:"DELETE"});
|
|
||||||
if(target.type==="class"){ await reload(); }
|
if(target.type==="class"){ await reload(); }
|
||||||
else{
|
else{
|
||||||
const a = (await api("/assignments/pupil/"+target.id)).assignment;
|
info.textContent = T("amNoAssignment");
|
||||||
info.textContent = describeCurrent(a);
|
del.remove();
|
||||||
msg(T("amAssignSaved"));
|
msg(T("amAssignSaved"));
|
||||||
}
|
}
|
||||||
}catch(e){ msg(e.message); }
|
}catch(e){ msg(e.message); }
|
||||||
});
|
});
|
||||||
/* kop toont compact de huidige stand; de keuzes klappen uit via Bewerken */
|
tools.appendChild(del);
|
||||||
const tools = h("div","am-tools");
|
}
|
||||||
const inline = h("div","am-inline");
|
|
||||||
inline.appendChild(sel);
|
|
||||||
inline.appendChild(widgetSel);
|
|
||||||
inline.appendChild(modeSel);
|
|
||||||
inline.appendChild(apply);
|
|
||||||
tools.appendChild(inline);
|
|
||||||
tools.appendChild(info);
|
tools.appendChild(info);
|
||||||
return groupCard({
|
return groupCard({
|
||||||
title: target.label,
|
title: target.label,
|
||||||
|
|
@ -730,13 +687,10 @@
|
||||||
panel.appendChild(h("div","guestnote", T("amPickSchoolHint")));
|
panel.appendChild(h("div","guestnote", T("amPickSchoolHint")));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const boardOptions = [];
|
|
||||||
BS.folders.forEach(fo=>fo.boards.forEach(bo=>boardOptions.push({ id: bo.id, label: `${fo.name} / ${bo.name}` })));
|
|
||||||
|
|
||||||
panel.appendChild(h("div","am-group", T("amAssignClasses")));
|
panel.appendChild(h("div","am-group", T("amAssignClasses")));
|
||||||
const classList = h("div","am-list");
|
const classList = h("div","am-list");
|
||||||
if(!CLASSES.length) classList.appendChild(h("div","guestnote", T("amNoClass")));
|
if(!CLASSES.length) classList.appendChild(h("div","guestnote", T("amNoClass")));
|
||||||
CLASSES.forEach(c=>classList.appendChild(assignmentRow({type:"class", id:c.id, label:c.name}, boardOptions, ASSIGNMENTS[c.id])));
|
CLASSES.forEach(c=>classList.appendChild(assignmentRow({type:"class", id:c.id, label:c.name}, ASSIGNMENTS[c.id])));
|
||||||
panel.appendChild(classList);
|
panel.appendChild(classList);
|
||||||
|
|
||||||
panel.appendChild(h("div","am-group", T("amAssignPupils")));
|
panel.appendChild(h("div","am-group", T("amAssignPupils")));
|
||||||
|
|
@ -752,7 +706,7 @@
|
||||||
const u = pupils.find(x=>String(x.id)===pupilSel.value);
|
const u = pupils.find(x=>String(x.id)===pupilSel.value);
|
||||||
let current = null;
|
let current = null;
|
||||||
try{ current = (await api("/assignments/pupil/"+u.id)).assignment; }catch(e){}
|
try{ current = (await api("/assignments/pupil/"+u.id)).assignment; }catch(e){}
|
||||||
pupilRowHolder.appendChild(assignmentRow({type:"pupil", id:u.id, label:u.displayName}, boardOptions, current));
|
pupilRowHolder.appendChild(assignmentRow({type:"pupil", id:u.id, label:u.displayName}, current));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
146
public/js/cast.js
Normal file
146
public/js/cast.js
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
/* version — shown until /api/version resolves (or if the fetch fails, e.g. offline).
|
/* 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. */
|
Kept in sync by hand with the VERSION file at the repo root on every release. */
|
||||||
const VERSION = "0.4.14-beta";
|
const VERSION = "0.4.15-beta";
|
||||||
(function(){
|
(function(){
|
||||||
const tag = document.getElementById("verTag");
|
const tag = document.getElementById("verTag");
|
||||||
tag.textContent = "v"+VERSION;
|
tag.textContent = "v"+VERSION;
|
||||||
|
|
@ -315,7 +315,12 @@ const I18N = {
|
||||||
amAssignments:"Toewijzingen", amAssignClasses:"Per klas (standaard)", amAssignPupils:"Per leerling (uitzondering)",
|
amAssignments:"Toewijzingen", amAssignClasses:"Per klas (standaard)", amAssignPupils:"Per leerling (uitzondering)",
|
||||||
amPickPupil:"— kies een leerling —", amNoAssignment:"— geen toewijzing —",
|
amPickPupil:"— kies een leerling —", amNoAssignment:"— geen toewijzing —",
|
||||||
amAssignedAs:"Toegewezen:", amAssign:"Toewijzen", amAssignSaved:"Toewijzing opgeslagen.",
|
amAssignedAs:"Toegewezen:", amAssign:"Toewijzen", amAssignSaved:"Toewijzing opgeslagen.",
|
||||||
|
amUnlink:"Loskoppelen",
|
||||||
amWholeBoard:"— heel bord —", amModeWatch:"kijken", amModeWork:"werken",
|
amWholeBoard:"— heel bord —", amModeWatch:"kijken", amModeWork:"werken",
|
||||||
|
cast:"Cast", castTitle:"Cast bord", castWhat:"Wat cast je?",
|
||||||
|
castClasses:"Klassen", castPupils:"Leerlingen",
|
||||||
|
castNoClasses:"Geen klassen in deze school.", castNoPupils:"Geen leerlingen in deze school.",
|
||||||
|
castActive:"✓ Actief",
|
||||||
amProgress:"Voortgang", amProgressEmpty:"Nog geen voortgang voor deze leerling.",
|
amProgress:"Voortgang", amProgressEmpty:"Nog geen voortgang voor deze leerling.",
|
||||||
amConfirmDel:"Weet je zeker dat je deze gebruiker wilt verwijderen?",
|
amConfirmDel:"Weet je zeker dat je deze gebruiker wilt verwijderen?",
|
||||||
amCopy:"kopieer", amCopyAll:"kopieer alles",
|
amCopy:"kopieer", amCopyAll:"kopieer alles",
|
||||||
|
|
@ -617,7 +622,12 @@ const I18N = {
|
||||||
amAssignments:"Assignments", amAssignClasses:"Per class (default)", amAssignPupils:"Per pupil (override)",
|
amAssignments:"Assignments", amAssignClasses:"Per class (default)", amAssignPupils:"Per pupil (override)",
|
||||||
amPickPupil:"— pick a pupil —", amNoAssignment:"— no assignment —",
|
amPickPupil:"— pick a pupil —", amNoAssignment:"— no assignment —",
|
||||||
amAssignedAs:"Assigned:", amAssign:"Assign", amAssignSaved:"Assignment saved.",
|
amAssignedAs:"Assigned:", amAssign:"Assign", amAssignSaved:"Assignment saved.",
|
||||||
|
amUnlink:"Unlink",
|
||||||
amWholeBoard:"— whole board —", amModeWatch:"watch", amModeWork:"work",
|
amWholeBoard:"— whole board —", amModeWatch:"watch", amModeWork:"work",
|
||||||
|
cast:"Cast", castTitle:"Cast board", castWhat:"What are you casting?",
|
||||||
|
castClasses:"Classes", castPupils:"Pupils",
|
||||||
|
castNoClasses:"No classes in this school.", castNoPupils:"No pupils in this school.",
|
||||||
|
castActive:"✓ Active",
|
||||||
amProgress:"Progress", amProgressEmpty:"No progress for this pupil yet.",
|
amProgress:"Progress", amProgressEmpty:"No progress for this pupil yet.",
|
||||||
amConfirmDel:"Are you sure you want to delete this user?",
|
amConfirmDel:"Are you sure you want to delete this user?",
|
||||||
amCopy:"copy", amCopyAll:"copy all",
|
amCopy:"copy", amCopyAll:"copy all",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue