/* teach - beheerpaneel (👥): gebruikers, klassen en scholen. Zichtbaar voor super (alles), admin (eigen school) en teacher (leerlingen). */ "use strict"; (function(){ const wrap = document.getElementById("adminWrap"); const modal = document.getElementById("adminModal"); const btn = document.getElementById("btnAdmin"); let SCHOOLS = [], USERS = [], CLASSES = [], selSchool = null; const esc = s => String(s ?? "").replace(/&/g,"&").replace(/{ const el = document.createElement(tag); if(cls) el.className = cls; if(text != null) el.textContent = text; return el; }; const msg = t => { const m = modal.querySelector(".am-msg"); if(m) m.textContent = t; }; btn.addEventListener("click", async ()=>{ if(!currentUser || currentUser.role==="pupil") return; selSchool = currentUser.role==="super" ? (selSchool ?? "") : currentUser.schoolId; await reload(); wrap.classList.add("open"); }); wrap.addEventListener("click", e=>{ if(e.target===wrap) wrap.classList.remove("open"); }); document.addEventListener("userchange", ()=>{ if(!currentUser) wrap.classList.remove("open"); }); async function reload(){ try{ if(currentUser.role==="super") SCHOOLS = await api("/schools"); const q = currentUser.role==="super" && selSchool ? "?school="+selSchool : ""; USERS = (await api("/admin/users"+q)).users; const cq = currentUser.role==="super" ? (selSchool ? "?school="+selSchool : "?school=0") : ""; CLASSES = (await api("/admin/classes"+cq)).classes || []; }catch(e){ USERS = []; CLASSES = []; } render(); } function classSel(current){ const sel = h("select","am-sel"); sel.appendChild(new Option(T("amNoClass"), "")); CLASSES.forEach(c=>sel.appendChild(new Option(c.name, c.id))); sel.value = current ?? ""; return sel; } function userRow(u){ const row = h("div","am-row"); row.appendChild(h("span","am-name", u.displayName + (u.displayName!==u.username ? ` (${u.username})` : ""))); row.appendChild(h("span","am-role", T(roleKey(u.role)))); if(u.role==="pupil"){ /* klas (uitwisselen met andere klassen) */ const cs = classSel(u.classId); cs.addEventListener("change", async ()=>{ try{ await api("/admin/users/"+u.id, {method:"PATCH", body:{classId: cs.value ? +cs.value : null}}); } catch(e){ msg(e.message); } }); row.appendChild(cs); /* wachtwoord inzien en aanpassen - alleen zichtbaar zolang de leerling nog niet is ingelogd; daarna kan alleen een nieuw wachtwoord gezet worden */ const pw = h("input","am-pw"); pw.value = u.password || ""; pw.placeholder = u.everLoggedIn ? T("amPwHidden") : ""; pw.title = T("amPw"); pw.addEventListener("keydown", ev=>ev.stopPropagation()); pw.addEventListener("change", async ()=>{ if(pw.value.length < 4) return; try{ await api("/admin/users/"+u.id, {method:"PATCH", body:{password: pw.value}}); reload(); } catch(e){ msg(e.message); } }); row.appendChild(pw); if(u.everLoggedIn){ const gen = h("button","am-btn","🎲"); gen.title = T("amGenerate"); gen.addEventListener("click", async ()=>{ try{ const { password } = await api("/admin/genpw?role=pupil"); await api("/admin/users/"+u.id, {method:"PATCH", body:{password}}); msg(`${T("amPwSet")} ${password}`); reload(); }catch(e){ msg(e.message); } }); row.appendChild(gen); } }else{ if(u.pending && u.linkCode){ row.appendChild(h("span","am-code", T("amCode")+": "+u.linkCode)); }else if(u.pending){ row.appendChild(h("span","am-code", T("amPending"))); } if(["super","admin"].includes(currentUser.role) && u.role!=="pupil" && u.id!==currentUser.id){ const nc = h("button","am-btn", T("amNewCode")); nc.addEventListener("click", async ()=>{ try{ const r = await api("/admin/users/"+u.id, {method:"PATCH", body:{newLinkCode:true}}); msg(`${T("amCreated")} ${r.linkCode}`); reload(); }catch(e){ msg(e.message); } }); row.appendChild(nc); /* direct een nieuw wachtwoord zetten, zonder de koppelcode-activatie */ const np = h("button","am-btn", T("amNewPw")); np.addEventListener("click", async ()=>{ try{ const r = await api("/admin/users/"+u.id, {method:"PATCH", body:{generatePassword:true}}); msg(`${T("amPwSet")} ${r.password}`); reload(); }catch(e){ msg(e.message); } }); row.appendChild(np); } } if(u.id !== currentUser.id){ const del = h("button","am-del","🗑"); del.title = T("amDelete") || ""; del.addEventListener("click", async ()=>{ if(!confirm(T("amConfirmDel"))) return; try{ await api("/admin/users/"+u.id, {method:"DELETE"}); reload(); } catch(e){ msg(e.message); } }); row.appendChild(del); } return row; } function addForm(role){ const f = h("div","am-add"); const name = h("input","am-inp"); name.placeholder = T("amUserPh"); name.addEventListener("keydown", ev=>ev.stopPropagation()); f.appendChild(name); let cs = null; if(role==="pupil"){ cs = classSel(""); f.appendChild(cs); } let pwInp = null; if(role!=="pupil"){ /* optioneel: meteen zelf een wachtwoord instellen i.p.v. een koppelcode (die de gebruiker normaal zelf gebruikt om een wachtwoord te kiezen) */ pwInp = h("input","am-inp am-pwinp"); pwInp.type = "password"; pwInp.autocomplete = "new-password"; pwInp.placeholder = T("amPwOptional"); pwInp.addEventListener("keydown", ev=>ev.stopPropagation()); f.appendChild(pwInp); const gen = h("button","am-btn","🎲"); gen.type = "button"; gen.title = T("amGenerate"); gen.addEventListener("click", async ()=>{ try{ const { password } = await api("/admin/genpw"); pwInp.type = "text"; pwInp.value = password; }catch(e){ msg(e.message); } }); f.appendChild(gen); } const go = h("button","tbtn", T(role==="pupil" ? "amNewPupil" : role==="teacher" ? "amNewTeacher" : role==="admin" ? "amNewAdmin" : "amNewSuper")); go.addEventListener("click", async ()=>{ const body = { role, username: name.value.trim(), displayName: name.value.trim() }; if(role==="pupil" && cs && cs.value) body.classId = +cs.value; if(pwInp && pwInp.value.trim()) body.password = pwInp.value.trim(); if(currentUser.role==="super" && role!=="super") body.school = +selSchool; try{ const r = await api("/admin/users", { body }); msg(role==="pupil" ? `${T("amPupilMade")} ${r.user.password}` : r.user.password ? `${T("amPwSet")} ${r.user.password}` : `${T("amCreated")} ${r.user.linkCode}`); name.value = ""; if(pwInp) pwInp.value = ""; reload(); }catch(e){ msg(e.message); } }); f.appendChild(go); return f; } function render(){ modal.innerHTML = ""; const head = h("div","am-head"); head.appendChild(h("h2", null, T("beheer"))); const close = h("button","wclose","✕"); close.addEventListener("click", ()=>wrap.classList.remove("open")); head.appendChild(close); modal.appendChild(head); /* 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")); /* klassen (admin/super binnen een school) */ const schoolChosen = currentUser.role!=="super" || !!selSchool; 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(["super","admin"].includes(currentUser.role)){ 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); if(["super","admin"].includes(currentUser.role)){ 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); modal.appendChild(f); } } /* gebruikers */ modal.appendChild(h("h3","am-h3", T("amUsers"))); const list = h("div","am-list"); const groups = [["super", T("amSuperGroup")], ["admin", T("roleAdmin")], ["teacher", T("roleTeacher")], ["pupil", T("rolePupil")]]; groups.forEach(([role, label])=>{ const us = USERS.filter(u=>u.role===role); if(!us.length) return; list.appendChild(h("div","am-group", label)); us.forEach(u=>list.appendChild(userRow(u))); }); modal.appendChild(list); /* toevoegen */ if(schoolChosen){ if(["super","admin","teacher"].includes(currentUser.role)) modal.appendChild(addForm("pupil")); if(["super","admin"].includes(currentUser.role)){ modal.appendChild(addForm("teacher")); modal.appendChild(addForm("admin")); } } if(currentUser.role==="super") modal.appendChild(addForm("super")); } })();