From 56125c696859b69e02d6535d2e08583ccdb0be4c Mon Sep 17 00:00:00 2001 From: Ramon Date: Sat, 18 Jul 2026 05:04:43 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Scholen,=20Klassen=20en=20Gebruikers=20?= =?UTF-8?q?samengevoegd=20tot=20=C3=A9=C3=A9n=20menu-item=20"School"=20(v0?= =?UTF-8?q?.4.09-beta)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ADMIN_SECTIONS: de drie losse tabbladen zijn samengevoegd tot 1 sectie ("school"); zoeken op een school-, klas- of gebruikersnaam werkt nog hetzelfde, navigeert nu alleen naar de ene samengevoegde sectie - nieuwe renderSchoolPanel() zet de drie bestaande, ongewijzigde deelpanelen (renderScholenPanel/renderKlassenPanel/renderGebruikersPanel) na elkaar, elk met een eigen kop - geen van de drie is herschreven, alleen samengesteld, om het risico op regressies te beperken - rolgedrag blijft exact zoals het was (nu allemaal binnen die ene sectie): systeemmanager ziet Scholen+Klassen+Gebruikers en de schoolkiezer, schoolbeheerder ziet Klassen+Gebruikers voor de eigen school (geen schoolkiezer, geen Scholen-blok), groepsleiding ziet alleen Gebruikers (geen Klassen-blok) en daarbinnen alleen de leerling-gerelateerde knoppen - geverifieerd in een echte headless Chromium voor alle drie rollen: juiste secties, juiste knoppen, juiste zichtbaarheid van de schoolkiezer Backup van de oude driedelige UI staat als git-tag ui-before-merged-beheer-v0.4.08 (commit e34bda5). --- VERSION | 2 +- public/css/teach.css | 3 ++ public/js/admin.js | 93 ++++++++++++++++++++++++++++++-------------- public/js/core.js | 4 +- 4 files changed, 70 insertions(+), 32 deletions(-) diff --git a/VERSION b/VERSION index db2fa0b..f80d0c1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.4.08-beta +0.4.09-beta diff --git a/public/css/teach.css b/public/css/teach.css index e47b3b1..835a045 100644 --- a/public/css/teach.css +++ b/public/css/teach.css @@ -1265,6 +1265,9 @@ .am-head{display:flex; align-items:center; justify-content:space-between; margin-bottom:8px;} .am-head h2{margin:0; font-size:20px;} .am-h3{margin:14px 0 6px; font-size:14px; color:var(--muted); text-transform:uppercase; letter-spacing:.5px;} +/* School/Klassen/Gebruikers staan nu na elkaar in één tabblad i.p.v. drie + losse - wat lucht onder elk blok zodat de volgende kop niet knelt */ +.am-section{margin-bottom:10px;} .am-msg{ min-height:20px; font-size:13.5px; font-weight:700; color:var(--green); margin:4px 0; display:flex; align-items:center; gap:8px; flex-wrap:wrap; diff --git a/public/js/admin.js b/public/js/admin.js index 5a215f0..91a19b3 100644 --- a/public/js/admin.js +++ b/public/js/admin.js @@ -18,7 +18,7 @@ meteen weer verdwijnen na een reload() */ let resetResults = null; /* actief tabblad, blijft behouden over her-renders heen (na elke actie) */ - let adminTab = "gebruikers"; + let adminTab = "school"; /* live zoekfilter voor de gebruikerslijst, blijft ook behouden over her-renders */ let userFilter = ""; /* uitgeklapte leerling-groepen en gebruikersrijen (standaard alles dicht @@ -176,38 +176,41 @@ /* ---- beheer-secties in het instellingen-register (settings.js) ------------ Elke sectie meldt zichzelf aan met rol-zichtbaarheid, een activate() en - zoekgegevens; de schil bouwt daar navigatie + zoeken uit. Per rol alleen - de essentie: groepsleiding ziet gebruikers/toewijzingen/voortgang, - schoolbeheer ook klassen, de systeemmanager ook scholen en opslag. */ + zoekgegevens; de schil bouwt daar navigatie + zoeken uit. Scholen, klassen + en gebruikers zijn hier bewust ÉÉN sectie ("school"): voor een + schoolbeheerder/leerkracht is dat toch al hun ene eigen school, en een + systeemmanager ziet binnen die ene sectie alsnog alles (zie + renderSchoolPanel hieronder, dat de drie oorspronkelijke deelpanelen + ongewijzigd naast elkaar zet). Per rol alleen de essentie: groepsleiding + ziet hierbinnen alleen gebruikers, schoolbeheer ook klassen, de + systeemmanager ook scholen (en daarnaast losstaand nog opslag). */ const ADMIN_SECTIONS = [ - { tab:"gebruikers", icon:"👥", group:"school", order:10, labelKey:"amUsers", subKey:"stUsersSub", + { tab:"school", icon:"🏫", group:"school", order:10, labelKey:"amSchoolMenu", subKey:"stSchoolMenuSub", show:()=>true, keywords:()=>[T("amPupils"), T("roleTeacher"), T("roleAdmin"), T("amParents"), T("amLinkRequests"), T("amNewPw"), T("amParentCode"), "leerling","pupil","ouder","parent","wachtwoord","password", - "koppelcode","aanmelding","enrolment"], - search:q=>USERS - .filter(u=>`${u.displayName} ${u.username}`.toLowerCase().includes(q)) - .map(u=>({ icon: u.role==="pupil" ? "🧒" : u.role==="parent" ? "👪" : "🧑‍🏫", - label:u.displayName, sub:T(roleKey(u.role)), - go:()=>goSettingsSection("gebruikers", { filter:u.displayName }) })) }, - { tab:"klassen", icon:"🏷", group:"school", order:11, labelKey:"amClasses", subKey:"stKlassenSub", - show:()=>can("classes.manage"), - keywords:()=>[T("amNewClass"), T("amResetClass"), "klas","class","groep","group"], - search:q=>CLASSES - .filter(c=>c.name.toLowerCase().includes(q)) - .map(c=>({ icon:"🏷", label:c.name, sub:T("amClasses"), go:()=>goSettingsSection("klassen") })) }, + "koppelcode","aanmelding","enrolment", + T("amNewClass"), T("amResetClass"), "klas","class","groep","group", + T("amNewSchool"), T("amNewSuper"), "school","systeemmanager","system manager"], + search:q=>[ + ...USERS + .filter(u=>`${u.displayName} ${u.username}`.toLowerCase().includes(q)) + .map(u=>({ icon: u.role==="pupil" ? "🧒" : u.role==="parent" ? "👪" : "🧑‍🏫", + label:u.displayName, sub:T(roleKey(u.role)), + go:()=>goSettingsSection("school", { filter:u.displayName }) })), + ...CLASSES + .filter(c=>c.name.toLowerCase().includes(q)) + .map(c=>({ icon:"🏷", label:c.name, sub:T("amClasses"), go:()=>goSettingsSection("school") })), + ...SCHOOLS + .filter(s=>s.name.toLowerCase().includes(q)) + .map(s=>({ icon:"🏫", label:s.name, sub:T("amSchools"), go:()=>goSettingsSection("school") })), + ] }, { tab:"toewijzingen", icon:"🎯", group:"school", order:12, labelKey:"amAssignments", subKey:"stToewijzSub", show:()=>can("assignments.manage"), keywords:()=>[T("amModeWatch"), T("amModeWork"), "bord","board","delen","share","toewijzen","assign","widget"] }, { tab:"voortgang", icon:"📈", group:"school", order:13, labelKey:"amProgress", subKey:"stVoortgangSub", show:()=>can("progress.view"), keywords:()=>["voortgang","progress","score","resultaten","results","tijdlijn","timeline"] }, - { tab:"scholen", icon:"🏫", group:"system", order:30, labelKey:"amSchools", subKey:"stScholenSub", - show:()=>currentUser.role==="super", - keywords:()=>[T("amNewSchool"), T("amNewSuper"), "school","systeemmanager","system manager"], - search:q=>SCHOOLS - .filter(s=>s.name.toLowerCase().includes(q)) - .map(s=>({ icon:"🏫", label:s.name, sub:T("amSchools"), go:()=>goSettingsSection("scholen") })) }, { tab:"opslag", icon:"🖼", group:"system", order:32, labelKey:"stOpslag", subKey:"stOpslagSub", show:()=>currentUser.role==="super", keywords:()=>["opslag","storage","afbeeldingen","images","quota"] }, @@ -238,11 +241,11 @@ visible:()=>!!currentUser && can("admin.access") && d.show(), async activate(payload){ await ensureLoaded(); - if(d.tab==="gebruikers" && payload && payload.filter !== undefined) userFilter = payload.filter; + if(d.tab==="school" && payload && payload.filter !== undefined) userFilter = payload.filter; showAdminHost(); switchAdminTab(d.tab); - if(d.tab==="gebruikers" && payload && payload.filter !== undefined){ - const panel = modal.querySelector('[data-panel="gebruikers"]'); + if(d.tab==="school" && payload && payload.filter !== undefined){ + const panel = modal.querySelector('[data-panel="school"]'); const inp = panel && panel.querySelector(".am-filter"); if(inp){ inp.value = userFilter; applyUserFilter(panel); } } @@ -1406,13 +1409,43 @@ syncSelection(); /* selectie valideren + detailpaneel vullen */ } + /* "School": scholen, klassen en gebruikers samen in één tabblad i.p.v. drie + losse. De drie oorspronkelijke deelpanelen blijven ongewijzigd (elk met + hun eigen, al-bestaande rolcontrole) - dit stelt ze alleen na elkaar + samen, elk met een eigen kop, zodat de pagina scanbaar blijft: + - Scholen: alleen systeemmanager (die scholen kan aanmaken/beheren); + onafhankelijk van de gekozen school (was al zo). + - Klassen: alleen wie classes.manage heeft (super/admin) - een + leerkracht ziet dit blok dus helemaal niet. + - Gebruikers: altijd. */ + function renderSchoolPanel(panel){ + if(currentUser.role==="super"){ + panel.appendChild(h("h3","am-h3", T("amSchools"))); + const sub = h("div","am-section"); + renderScholenPanel(sub); + panel.appendChild(sub); + } + if(can("classes.manage")){ + panel.appendChild(h("h3","am-h3", T("amClasses"))); + const sub = h("div","am-section"); + renderKlassenPanel(sub); + panel.appendChild(sub); + } + panel.appendChild(h("h3","am-h3", T("amUsers"))); + const sub = h("div","am-section"); + renderGebruikersPanel(sub); + panel.appendChild(sub); + } + function switchAdminTab(tab){ adminTab = tab; modal.querySelectorAll(".am-tabpanel").forEach(p=>{ p.style.display = p.dataset.panel===tab ? "block" : "none"; }); - /* het Systeem-tabblad is school-onafhankelijk: daar is de schoolkiezer - alleen maar verwarrend, dus die verbergen we er */ + /* Opslag is school-onafhankelijk: daar is de schoolkiezer alleen maar + verwarrend, dus die verbergen we er. "school" toont 'm juist wél (het + Klassen/Gebruikers-deel daarbinnen hangt ervan af voor een + systeemmanager) - het Scholen-deel daarbinnen negeert selSchool toch al. */ const sb = modal.querySelector(".am-schoolbar"); - if(sb) sb.style.display = ["scholen","opslag"].includes(tab) ? "none" : ""; + if(sb) sb.style.display = tab==="opslag" ? "none" : ""; } /* vaste contextbalk voor de systeemmanager: op elk beheer-tabblad direct @@ -1442,7 +1475,7 @@ const tabDefs = adminTabDefs(); if(!tabDefs.some(([t])=>t===adminTab)) adminTab = tabDefs[0][0]; - const panels = { scholen: renderScholenPanel, klassen: renderKlassenPanel, gebruikers: renderGebruikersPanel, toewijzingen: renderToewijzingenPanel, voortgang: renderVoortgangPanel, opslag: renderImageStoragePanel }; + const panels = { school: renderSchoolPanel, toewijzingen: renderToewijzingenPanel, voortgang: renderVoortgangPanel, opslag: renderImageStoragePanel }; tabDefs.forEach(([t])=>{ const panel = h("div","am-tabpanel"); panel.dataset.panel = t; diff --git a/public/js/core.js b/public/js/core.js index 72bf329..7365fd4 100644 --- a/public/js/core.js +++ b/public/js/core.js @@ -2,7 +2,7 @@ "use strict"; /* 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. */ -const VERSION = "0.4.08-beta"; +const VERSION = "0.4.09-beta"; (function(){ const tag = document.getElementById("verTag"); tag.textContent = "v"+VERSION; @@ -212,6 +212,7 @@ const I18N = { stToewijzSub:"Bepaal wat een klas of leerling te zien krijgt.", stVoortgangSub:"Volg per leerling de resultaten in de tijd.", stScholenSub:"Scholen en systeemmanagers, over alle scholen heen.", + amSchoolMenu:"School", stSchoolMenuSub:"Scholen, klassen en gebruikers beheren.", stOpslag:"Opslag", stOpslagSub:"Afbeeldingencatalogus en opslagruimte per school.", stInhoud:"Inhoud", stInhoudSub:"Welkomstwoord, meldingen en widget-uitleg - direct aanpasbaar, zonder release.", cmsWelcomeH:"Welkomstwoord (inlogscherm)", @@ -511,6 +512,7 @@ const I18N = { stToewijzSub:"Choose what a class or pupil gets to see.", stVoortgangSub:"Follow each pupil's results over time.", stScholenSub:"Schools and system managers, across all schools.", + amSchoolMenu:"School", stSchoolMenuSub:"Manage schools, classes and users.", stOpslag:"Storage", stOpslagSub:"Image catalogue and storage per school.", stInhoud:"Content", stInhoudSub:"Welcome text, announcements and widget descriptions - editable without a release.", cmsWelcomeH:"Welcome text (login screen)",