Voeg 28 herkenbare kleurplaten en creatieve niveaus toe (v0.4.66-beta) #4

Open
bes-r wants to merge 207 commits from bes-r/kleurplaten-v0-4-66 into main AGit
Showing only changes of commit 1b6960b7ba - Show all commits

View file

@ -11,6 +11,8 @@
totdat de beheerder het venster zelf sluit, zodat wachtwoorden niet
meteen weer verdwijnen na een reload() */
let resetResults = null;
/* actief tabblad, blijft behouden over her-renders heen (na elke actie) */
let adminTab = "gebruikers";
const esc = s => String(s ?? "").replace(/&/g,"&amp;").replace(/</g,"&lt;");
const h = (tag, cls, text)=>{
@ -264,107 +266,100 @@
return box;
}
function render(){
modal.innerHTML = "";
/* super: schoolkiezer + nieuwe school */
function renderScholenPanel(panel){
const bar = h("div","am-add");
const sel = h("select","am-sel");
sel.appendChild(new Option(T("amAllSchools"), ""));
SCHOOLS.forEach(s=>sel.appendChild(new Option(s.name, s.id)));
sel.value = selSchool ?? "";
sel.addEventListener("change", ()=>{ selSchool = sel.value; reload(); });
bar.appendChild(sel);
const inp = h("input","am-inp");
inp.placeholder = T("amNamePh");
inp.addEventListener("keydown", ev=>ev.stopPropagation());
bar.appendChild(inp);
const add = h("button","tbtn", T("amNewSchool"));
add.addEventListener("click", async ()=>{
if(!inp.value.trim()) return;
try{
const r = await api("/admin/schools", { body:{ name: inp.value.trim() } });
selSchool = r.school.id; inp.value = "";
reload();
}catch(e){ msg(e.message); }
});
bar.appendChild(add);
panel.appendChild(bar);
}
/* super: schoolkiezer + nieuwe school */
if(currentUser.role==="super"){
const bar = h("div","am-add");
const sel = h("select","am-sel");
sel.appendChild(new Option(T("amAllSchools"), ""));
SCHOOLS.forEach(s=>sel.appendChild(new Option(s.name, s.id)));
sel.value = selSchool ?? "";
sel.addEventListener("change", ()=>{ selSchool = sel.value; reload(); });
bar.appendChild(sel);
const inp = h("input","am-inp");
inp.placeholder = T("amNamePh");
inp.addEventListener("keydown", ev=>ev.stopPropagation());
bar.appendChild(inp);
const add = h("button","tbtn", T("amNewSchool"));
add.addEventListener("click", async ()=>{
if(!inp.value.trim()) return;
try{
const r = await api("/admin/schools", { body:{ name: inp.value.trim() } });
selSchool = r.school.id; inp.value = "";
reload();
}catch(e){ msg(e.message); }
});
bar.appendChild(add);
modal.appendChild(bar);
}
modal.appendChild(h("div","am-msg"));
const rp = resetPanel();
if(rp) modal.appendChild(rp);
/* klassen (admin/super binnen een school) */
/* klassen (admin/super binnen een school) */
function renderKlassenPanel(panel){
const schoolChosen = currentUser.role!=="super" || !!selSchool;
if(!schoolChosen) return;
const canResetPw = can("schools.resetPasswords");
if(schoolChosen){
modal.appendChild(h("h3","am-h3", T("amClasses")));
const cwrapEl = h("div","am-classes");
CLASSES.forEach(c=>{
const chip = h("span","am-chip", c.name);
if(canResetPw){
const rs = h("button","am-chipx","🔄");
rs.title = T("amResetClass");
rs.addEventListener("click", async ()=>{
if(!confirm(`${T("amConfirmReset")} ${c.name}?`)) return;
try{
const r = await api("/admin/classes/"+c.id+"/reset-passwords", {method:"POST"});
await reload();
resetResults = { title: `${T("amResetClass")}: ${c.name}`, entries: r.users };
render();
}catch(e){ msg(e.message); }
});
chip.appendChild(rs);
const x = h("button","am-chipx","✕");
x.addEventListener("click", async ()=>{
try{ await api("/admin/classes/"+c.id, {method:"DELETE"}); reload(); }
catch(e){ msg(e.message); }
});
chip.appendChild(x);
}
cwrapEl.appendChild(chip);
});
modal.appendChild(cwrapEl);
const cwrapEl = h("div","am-classes");
CLASSES.forEach(c=>{
const chip = h("span","am-chip", c.name);
if(canResetPw){
const f = h("div","am-add");
const inp = h("input","am-inp");
inp.placeholder = T("amNamePh");
inp.addEventListener("keydown", ev=>ev.stopPropagation());
f.appendChild(inp);
const go = h("button","tbtn", T("amNewClass"));
go.addEventListener("click", async ()=>{
if(!inp.value.trim()) return;
const rs = h("button","am-chipx","🔄");
rs.title = T("amResetClass");
rs.addEventListener("click", async ()=>{
if(!confirm(`${T("amConfirmReset")} ${c.name}?`)) return;
try{
await api("/admin/classes", { body:{ name: inp.value.trim(), school: selSchool ? +selSchool : undefined } });
reload();
}catch(e){ msg(e.message); }
});
f.appendChild(go);
modal.appendChild(f);
/* multireset: alle leerlingwachtwoorden van de hele school in één keer */
const rsAll = h("button","am-btn","🔄 "+T("amResetSchool"));
rsAll.type = "button";
rsAll.addEventListener("click", async ()=>{
const schoolId = currentUser.role==="super" ? selSchool : currentUser.schoolId;
if(!schoolId) return;
if(!confirm(T("amConfirmResetSchool"))) return;
try{
const r = await api("/admin/schools/"+schoolId+"/reset-passwords", {method:"POST"});
const r = await api("/admin/classes/"+c.id+"/reset-passwords", {method:"POST"});
await reload();
resetResults = { title: T("amResetSchool"), entries: r.users };
resetResults = { title: `${T("amResetClass")}: ${c.name}`, entries: r.users };
render();
}catch(e){ msg(e.message); }
});
modal.appendChild(rsAll);
chip.appendChild(rs);
const x = h("button","am-chipx","✕");
x.addEventListener("click", async ()=>{
try{ await api("/admin/classes/"+c.id, {method:"DELETE"}); reload(); }
catch(e){ msg(e.message); }
});
chip.appendChild(x);
}
}
cwrapEl.appendChild(chip);
});
panel.appendChild(cwrapEl);
if(canResetPw){
const f = h("div","am-add");
const inp = h("input","am-inp");
inp.placeholder = T("amNamePh");
inp.addEventListener("keydown", ev=>ev.stopPropagation());
f.appendChild(inp);
const go = h("button","tbtn", T("amNewClass"));
go.addEventListener("click", async ()=>{
if(!inp.value.trim()) return;
try{
await api("/admin/classes", { body:{ name: inp.value.trim(), school: selSchool ? +selSchool : undefined } });
reload();
}catch(e){ msg(e.message); }
});
f.appendChild(go);
panel.appendChild(f);
/* gebruikers */
modal.appendChild(h("h3","am-h3", T("amUsers")));
/* multireset: alle leerlingwachtwoorden van de hele school in één keer */
const rsAll = h("button","am-btn","🔄 "+T("amResetSchool"));
rsAll.type = "button";
rsAll.addEventListener("click", async ()=>{
const schoolId = currentUser.role==="super" ? selSchool : currentUser.schoolId;
if(!schoolId) return;
if(!confirm(T("amConfirmResetSchool"))) return;
try{
const r = await api("/admin/schools/"+schoolId+"/reset-passwords", {method:"POST"});
await reload();
resetResults = { title: T("amResetSchool"), entries: r.users };
render();
}catch(e){ msg(e.message); }
});
panel.appendChild(rsAll);
}
}
/* gebruikers: lijst (altijd, ook zonder gekozen school) + aanmaak-formulieren */
function renderGebruikersPanel(panel){
const list = h("div","am-list");
const groups = [["super", T("amSuperGroup")], ["admin", T("roleAdmin")], ["teacher", T("roleTeacher")], ["pupil", T("rolePupil")]];
groups.forEach(([role, label])=>{
@ -373,13 +368,53 @@
list.appendChild(h("div","am-group", label));
us.forEach(u=>list.appendChild(userRow(u)));
});
modal.appendChild(list);
panel.appendChild(list);
/* toevoegen - welke rollen deze gebruiker mag aanmaken (CREATABLE_ROLES) */
const schoolChosen = currentUser.role!=="super" || !!selSchool;
const creatable = CREATABLE_ROLES[currentUser.role];
if(schoolChosen){
["pupil","teacher","admin"].forEach(r=>{ if(creatable.includes(r)) modal.appendChild(addForm(r)); });
["pupil","teacher","admin"].forEach(r=>{ if(creatable.includes(r)) panel.appendChild(addForm(r)); });
}
if(creatable.includes("super")) modal.appendChild(addForm("super"));
if(creatable.includes("super")) panel.appendChild(addForm("super"));
}
function switchAdminTab(tab){
adminTab = tab;
modal.querySelectorAll(".ed-tabs button").forEach(b=>b.classList.toggle("active", b.dataset.tab===tab));
modal.querySelectorAll(".am-tabpanel").forEach(p=>{ p.style.display = p.dataset.panel===tab ? "block" : "none"; });
}
function render(){
modal.innerHTML = "";
modal.appendChild(h("div","am-msg"));
const rp = resetPanel();
if(rp) modal.appendChild(rp);
const tabDefs = [];
if(currentUser.role==="super") tabDefs.push(["scholen", T("amSchools")]);
tabDefs.push(["klassen", T("amClasses")]);
tabDefs.push(["gebruikers", T("amUsers")]);
if(!tabDefs.some(([t])=>t===adminTab)) adminTab = tabDefs[0][0];
const tabsBar = h("div","ed-tabs");
tabDefs.forEach(([t, label])=>{
const b = h("button", null, label);
b.type = "button";
b.dataset.tab = t;
b.addEventListener("click", ()=>switchAdminTab(t));
tabsBar.appendChild(b);
});
modal.appendChild(tabsBar);
const panels = { scholen: renderScholenPanel, klassen: renderKlassenPanel, gebruikers: renderGebruikersPanel };
tabDefs.forEach(([t])=>{
const panel = h("div","am-tabpanel");
panel.dataset.panel = t;
panels[t](panel);
modal.appendChild(panel);
});
switchAdminTab(adminTab);
}
})();