Verbeter grafische kwaliteit van kleurplaten (v0.4.67-beta) #5
11 changed files with 252 additions and 24 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
0.4.38-beta
|
||||
0.4.39-beta
|
||||
|
|
|
|||
|
|
@ -640,7 +640,8 @@
|
|||
|
||||
.wiki-goal-focus{outline:3px solid var(--accent);outline-offset:3px;animation:wikiFocus 1.4s ease-out}
|
||||
@keyframes wikiFocus{from{background:var(--accent-soft)}to{background:var(--surface)}}
|
||||
.winfo[hidden]{display:none}\n .widget-curriculum-modal{width:min(620px,94vw);max-height:min(760px,88vh);overflow:auto}
|
||||
.winfo[hidden]{display:none}
|
||||
.widget-curriculum-modal{width:min(620px,94vw);max-height:min(760px,88vh);overflow:auto}
|
||||
.widget-curriculum-head{display:flex;align-items:center;gap:12px;margin-bottom:8px}
|
||||
.widget-curriculum-head h2{flex:1;margin:0;font-size:20px}
|
||||
.widget-curriculum-goals{display:flex;flex-direction:column;gap:8px;margin:14px 0}
|
||||
|
|
@ -1298,6 +1299,20 @@
|
|||
.lisub{font-size:13px; font-weight:700; color:var(--muted); margin-bottom:8px;}
|
||||
#ownPwBlock .lrow{margin-bottom:10px;}
|
||||
#ownPwBlock input{flex:1; min-width:0;}
|
||||
|
||||
/* systeemmanager: veilige voorbeeldweergave van vaste rollen */
|
||||
.vw-role-block[hidden],#rolePreviewBanner[hidden]{display:none}
|
||||
.vw-role-block{margin-bottom:8px;padding:10px 12px;border:1px solid var(--accent-soft);border-radius:12px;background:var(--surface-2)}
|
||||
.vw-role-block .vw-row{align-items:flex-start;padding-top:0}.vw-role-seg{flex-wrap:wrap;justify-content:flex-end;max-width:min(620px,72%)}
|
||||
.vw-role-note{margin-top:7px}
|
||||
#rolePreviewBanner{position:fixed;top:calc(var(--tbar) + 8px);left:50%;z-index:5700;display:flex;align-items:center;gap:8px;transform:translateX(-50%);padding:7px 9px 7px 12px;border:1px solid var(--orange);border-radius:999px;background:var(--surface);box-shadow:var(--shadow-2);font-size:12px}
|
||||
#rolePreviewBanner span{font-weight:800;color:var(--orange)}#rolePreviewBanner button{border:0;border-radius:999px;padding:5px 9px;background:color-mix(in srgb,var(--orange) 16%,var(--surface));color:var(--ink);font:inherit;font-weight:800;cursor:pointer}
|
||||
.role-preview-note{max-width:760px;margin:0 auto 14px;padding:11px 14px;border:1px solid var(--orange);border-radius:12px;background:color-mix(in srgb,var(--orange) 16%,var(--surface));color:var(--ink);font-size:13px;line-height:1.45}
|
||||
body.role-preview-active #parentView button:disabled,body.role-preview-active #parentView input:disabled,body.role-preview-active #parentView select:disabled{opacity:.55;cursor:not-allowed}
|
||||
body.role-preview-pupil #pupilView .widget-body{pointer-events:none;user-select:none}
|
||||
body.tb-left #rolePreviewBanner,body.tb-right #rolePreviewBanner{top:8px}
|
||||
@media(max-width:720px){.vw-role-block .vw-row{align-items:stretch;flex-direction:column}.vw-role-seg{max-width:none;justify-content:flex-start}#rolePreviewBanner{max-width:calc(100vw - 24px);white-space:nowrap}#rolePreviewBanner strong{display:none}}
|
||||
|
||||
/* ---------- weergave-instellingen (tabblad "Weergave") ---------- */
|
||||
.vw-row{
|
||||
display:flex; align-items:center; justify-content:space-between; gap:12px;
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@
|
|||
<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/role-preview.js?v=__V__" defer></script>
|
||||
<script src="js/app.js?v=__V__" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
|
|
@ -74,6 +75,7 @@
|
|||
<button class="tbtn ghost" id="btnFs" title="Fullscreen">⛶</button>
|
||||
</div>
|
||||
|
||||
<div id="rolePreviewBanner" hidden><strong data-i18n="vwPreview"></strong><span id="rolePreviewLabel"></span><button type="button" data-i18n="vwPreviewReset"></button></div>
|
||||
<div id="board"></div>
|
||||
<div id="pupilView"></div>
|
||||
<div id="parentView"></div>
|
||||
|
|
@ -248,6 +250,19 @@
|
|||
</div>
|
||||
|
||||
<div class="st-section" data-section="view" style="display:none;">
|
||||
<div class="vw-role-block" id="superViewRole" hidden>
|
||||
<div class="vw-row">
|
||||
<span class="vw-lbl" data-i18n="vwRole"></span>
|
||||
<div class="seg vw-role-seg" id="segViewRole">
|
||||
<button type="button" data-role="super" data-i18n="vwRoleSystem"></button>
|
||||
<button type="button" data-role="admin" data-i18n="vwRoleAdmin"></button>
|
||||
<button type="button" data-role="teacher" data-i18n="vwRoleTeacher"></button>
|
||||
<button type="button" data-role="parent" data-i18n="vwRoleParent"></button>
|
||||
<button type="button" data-role="pupil" data-i18n="vwRolePupil"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="guestnote vw-role-note" data-i18n="vwRoleNote"></div>
|
||||
</div>
|
||||
<div class="vw-row">
|
||||
<span class="vw-lbl" data-i18n="vwTheme"></span>
|
||||
<div class="seg" id="segTheme">
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@
|
|||
|
||||
/* welke rollen de ingelogde gebruiker mag aanmaken: unie over hoofdrol + extra rollen */
|
||||
const creatableRoles = () =>
|
||||
[...new Set([currentUser.role, ...(currentUser.extraRoles||[])].flatMap(r => CREATABLE_ROLES[r] || []))];
|
||||
[...new Set(activeRoles().flatMap(r => CREATABLE_ROLES[r] || []))];
|
||||
|
||||
/* wie er als groepsleiding aan een klas gekoppeld kan staan - zelfde rollenset
|
||||
als de server accepteert in POST /admin/classes/:id/teachers (niet alleen
|
||||
|
|
@ -238,7 +238,7 @@
|
|||
keywords:()=>[T("amLevelYearGroup"), T("amLevelLevel"), "niveau","level","groep","niveaugroep",
|
||||
"leerjaar","year group","differentiatie"] },
|
||||
{ tab:"opslag", icon:"🖼", group:"system", order:32, labelKey:"stOpslag", subKey:"stOpslagSub",
|
||||
show:()=>currentUser.role==="super",
|
||||
show:()=>activeRole()==="super",
|
||||
keywords:()=>["opslag","storage","afbeeldingen","images","quota"] },
|
||||
];
|
||||
const adminTabDefs = ()=>ADMIN_SECTIONS.filter(d=>d.show()).map(d=>[d.tab, T(d.labelKey)]);
|
||||
|
|
@ -360,7 +360,7 @@
|
|||
/* school koppelen/losmaken (systeemmanager): losse accounts terugzetten
|
||||
naar hun school, of bewust losmaken - super/ouder zijn per definitie
|
||||
schoolloos en vallen hierbuiten */
|
||||
if(currentUser.role==="super" && ["teacher","admin","pupil"].includes(u.role)){
|
||||
if(activeRole()==="super" && ["teacher","admin","pupil"].includes(u.role)){
|
||||
const ss = h("select","am-sel");
|
||||
ss.appendChild(new Option(T("amNoSchool"), ""));
|
||||
SCHOOLS.forEach(sc=>ss.appendChild(new Option(sc.name, sc.id)));
|
||||
|
|
@ -1202,7 +1202,7 @@
|
|||
(alleen systeemmanager), wachtwoorden resetten, verwijderen. */
|
||||
function classActionsPanel(c, otherSchools){
|
||||
const box = h("div","am-kebab-panel");
|
||||
if(currentUser.role==="super" && otherSchools.length){
|
||||
if(activeRole()==="super" && otherSchools.length){
|
||||
const ss = h("select","am-sel");
|
||||
ss.appendChild(new Option(T("amMoveToSchool"), ""));
|
||||
otherSchools.forEach(sc=>ss.appendChild(new Option(sc.name, sc.id)));
|
||||
|
|
@ -1425,7 +1425,7 @@
|
|||
const school = SCHOOLS.find(s=>String(s.id)===String(selSchool));
|
||||
const canManageClasses = can("classes.manage");
|
||||
|
||||
if(currentUser.role==="super" && school){
|
||||
if(activeRole()==="super" && school){
|
||||
const titleRow = h("div","am-kebab-row");
|
||||
titleRow.appendChild(h("h2","am-nav-title", school.name));
|
||||
const kebab = h("button","am-kebab","⋯");
|
||||
|
|
@ -1602,7 +1602,7 @@
|
|||
validateSheet();
|
||||
panel.classList.add("am-nav-panel");
|
||||
|
||||
if(currentUser.role==="teacher"){
|
||||
if(activeRole()==="teacher"){
|
||||
const header = h("div","am-nav-header");
|
||||
const searchInp = h("input","am-inp am-filter");
|
||||
searchInp.type = "search";
|
||||
|
|
|
|||
|
|
@ -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.38-beta";
|
||||
const VERSION = "0.4.39-beta";
|
||||
(function(){
|
||||
const tag = document.getElementById("verTag");
|
||||
tag.textContent = "v"+VERSION;
|
||||
|
|
@ -233,7 +233,7 @@ const I18N = {
|
|||
stOpenSection:"open dit onderdeel",
|
||||
stGroupPersonal:"Persoonlijk", stGroupSchool:"School", stGroupSystem:"Systeem",
|
||||
stAccountSub:"Je gegevens, wachtwoord en reservekopieën.",
|
||||
stViewSub:"Weergave op dit apparaat: thema, taal en menubalk.",
|
||||
stViewSub:"Weergave op dit apparaat: rolvoorbeeld, thema, taal en menubalk.",
|
||||
wikiTitle:"Curriculumwiki",
|
||||
wikiSub:"Nieuwe SLO-kerndoelen voor het primair onderwijs, per leergebied.",
|
||||
wikiKicker:"Primair onderwijs · juli 2026",
|
||||
|
|
@ -284,6 +284,11 @@ const I18N = {
|
|||
vwContrast:"Hoog contrast", vwOn:"Aan", vwOff:"Uit", vwLang:"Taal",
|
||||
vwTbar:"Menubalk", vwTop:"Boven", vwLeft:"Links", vwRight:"Rechts",
|
||||
vwMotion:"Animaties", vwGlass:"Glas-effect",
|
||||
vwRole:"Weergave als", vwRoleSystem:"Systeem", vwRoleAdmin:"Schoolbeheerder", vwRoleTeacher:"Leerkracht", vwRoleParent:"Ouder", vwRolePupil:"Leerling",
|
||||
vwRoleNote:"Alleen de interface verandert. Je blijft ingelogd als systeemmanager; serverrechten en gegevens wijzigen niet.",
|
||||
vwPreview:"Voorbeeldweergave", vwPreviewReset:"Terug naar systeem",
|
||||
vwParentPreview:"Voorbeeld van het ouderportaal. Echte kindgegevens en acties zijn in deze weergave uitgeschakeld.",
|
||||
vwPupilPreview:"Voorbeeld van de leerlingomgeving met de taal- en rekenwidgets van het huidige bord. Bediening en voortgang zijn uitgeschakeld.",
|
||||
vwNote:"Deze voorkeuren gelden voor dit apparaat.",
|
||||
pwChanged:"Wachtwoord gewijzigd ✓", pwCurrentPh:"huidig wachtwoord",
|
||||
namePh:"naam", passPh:"wachtwoord",
|
||||
|
|
@ -606,7 +611,7 @@ const I18N = {
|
|||
stOpenSection:"open this section",
|
||||
stGroupPersonal:"Personal", stGroupSchool:"School", stGroupSystem:"System",
|
||||
stAccountSub:"Your details, password and backups.",
|
||||
stViewSub:"Display on this device: theme, language and toolbar.",
|
||||
stViewSub:"Display on this device: role preview, theme, language and toolbar.",
|
||||
wikiTitle:"Curriculum wiki",
|
||||
wikiSub:"New SLO attainment targets for Dutch primary education, grouped by learning area.",
|
||||
wikiKicker:"Primary education · July 2026",
|
||||
|
|
@ -657,6 +662,11 @@ const I18N = {
|
|||
vwContrast:"High contrast", vwOn:"On", vwOff:"Off", vwLang:"Language",
|
||||
vwTbar:"Menu bar", vwTop:"Top", vwLeft:"Left", vwRight:"Right",
|
||||
vwMotion:"Animations", vwGlass:"Glass effect",
|
||||
vwRole:"View as", vwRoleSystem:"System", vwRoleAdmin:"School administrator", vwRoleTeacher:"Teacher", vwRoleParent:"Parent", vwRolePupil:"Pupil",
|
||||
vwRoleNote:"Only the interface changes. You remain signed in as system manager; server permissions and data do not change.",
|
||||
vwPreview:"Preview mode", vwPreviewReset:"Return to system",
|
||||
vwParentPreview:"Preview of the parent portal. Real child data and actions are disabled in this view.",
|
||||
vwPupilPreview:"Preview of the pupil environment using language and maths widgets from the current board. Controls and progress are disabled.",
|
||||
vwNote:"These preferences apply to this device.",
|
||||
pwChanged:"Password changed ✓", pwCurrentPh:"current password",
|
||||
namePh:"name", passPh:"password",
|
||||
|
|
@ -863,8 +873,10 @@ function canPersist(){
|
|||
kind-accounts); schoolleerlingen ook niet (zij zien de toewijzing van de
|
||||
leerkracht). Thuiskinderen (leerling zónder school, ouderportaal) hebben
|
||||
wél een eigen whiteboard met eigen opslag. */
|
||||
if(!currentUser || currentUser.role === "parent") return false;
|
||||
if(currentUser.role === "pupil") return !currentUser.schoolId;
|
||||
if(!currentUser) return false;
|
||||
const role=typeof activeRole==="function" ? activeRole() : currentUser.role;
|
||||
if(role==="parent") return false;
|
||||
if(role==="pupil") return currentUser.role==="pupil" && !currentUser.schoolId;
|
||||
return true;
|
||||
}
|
||||
/* welke secties er sinds de laatste geslaagde save zijn gewijzigd; een save
|
||||
|
|
|
|||
|
|
@ -425,7 +425,7 @@ function withBranch(rootLabel, main, sub, opts){
|
|||
==========================================================*/
|
||||
function withSharedBranches(kind, own, hooks){
|
||||
/* gasten en leerlingen zien alleen de eigen boom */
|
||||
if(!currentUser || currentUser.role === "pupil") return own;
|
||||
if(!currentUser || activeRole() === "pupil") return own;
|
||||
const GLOBAL = "🌐 " + T("shGlobal");
|
||||
const SCHOOL = "🏫 " + T("shSchool");
|
||||
const branches = [GLOBAL];
|
||||
|
|
@ -629,7 +629,7 @@ function withSharedBranches(kind, own, hooks){
|
|||
zonder selectie op de map waar je nu in staat (alle items eronder) */
|
||||
lockLabel(path){
|
||||
if(!isShared(path) || scopeOf(path[0]) !== "school") return null;
|
||||
const roles = [currentUser.role, ...(currentUser.extraRoles||[])];
|
||||
const roles = activeRoles();
|
||||
if(!roles.includes("admin") && !roles.includes("super")) return null;
|
||||
return "🔒 " + T("exLock");
|
||||
},
|
||||
|
|
|
|||
|
|
@ -26,10 +26,24 @@ const CREATABLE_ROLES = {
|
|||
teacher: ['pupil'],
|
||||
};
|
||||
|
||||
/* can(action) gebruikt de ingelogde gebruiker (currentUser, uit core.js) - hoofdrol
|
||||
plus eventuele extra stafrollen (currentUser.extraRoles) */
|
||||
function can(action){
|
||||
if(!currentUser) return false;
|
||||
const roles = [currentUser.role, ...(currentUser.extraRoles||[])];
|
||||
return (PERMISSIONS[action] || []).some(r => roles.includes(r));
|
||||
/* Een systeemmanager kan uitsluitend de clientweergave van een andere rol
|
||||
simuleren. Het HttpOnly-account en alle serverautorisatie blijven ongewijzigd. */
|
||||
const VIEW_ROLES = ["super","admin","teacher","parent","pupil"];
|
||||
let VIEW_AS_ROLE = "super";
|
||||
function setActiveViewRole(role){
|
||||
VIEW_AS_ROLE = VIEW_ROLES.includes(role) ? role : "super";
|
||||
}
|
||||
function activeRole(){
|
||||
if(!currentUser) return null;
|
||||
return currentUser.role==="super" ? VIEW_AS_ROLE : currentUser.role;
|
||||
}
|
||||
function activeRoles(){
|
||||
if(!currentUser) return [];
|
||||
const role=activeRole();
|
||||
return role===currentUser.role ? [currentUser.role,...(currentUser.extraRoles||[])] : [role];
|
||||
}
|
||||
/* can(action) gebruikt het actieve weergaveprofiel. De server blijft iedere
|
||||
aanvraag vanzelf tegen het echte account autoriseren. */
|
||||
function can(action){
|
||||
return (PERMISSIONS[action] || []).some(r => activeRoles().includes(r));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ function pupilAllowedDefs(){
|
|||
return REGISTRY.filter(d => PUPIL_WIDGET_CATS.includes(d.cat));
|
||||
}
|
||||
|
||||
async function renderPupilWidgets(widgets, mode){
|
||||
async function renderPupilWidgets(widgets, mode, preview=false){
|
||||
pupilView.innerHTML = "";
|
||||
const defs = pupilAllowedDefs();
|
||||
const watch = mode === "kijken";
|
||||
|
|
@ -38,7 +38,7 @@ async function renderPupilWidgets(widgets, mode){
|
|||
- één keer per vakgebied dat hier daadwerkelijk voorkomt, niet per widget. */
|
||||
const subjects = [...new Set(usable.map(x => x.def.cat))];
|
||||
const levels = {};
|
||||
await Promise.all(subjects.map(async subject=>{
|
||||
if(!preview)await Promise.all(subjects.map(async subject=>{
|
||||
try{ levels[subject] = await api("/my/level?subject="+subject); }
|
||||
catch(e){ levels[subject] = null; }
|
||||
}));
|
||||
|
|
|
|||
108
public/js/role-preview.js
Normal file
108
public/js/role-preview.js
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/* teach - veilige rolvoorbeeldweergave voor systeemmanagers.
|
||||
Alleen de clientinterface en zichtbare menu's veranderen. De sessiecookie,
|
||||
currentUser en serverautorisatie blijven altijd van de systeemmanager. */
|
||||
"use strict";
|
||||
|
||||
(function(){
|
||||
const roleRow=document.getElementById("superViewRole");
|
||||
const roleSeg=document.getElementById("segViewRole");
|
||||
const banner=document.getElementById("rolePreviewBanner");
|
||||
const bannerLabel=document.getElementById("rolePreviewLabel");
|
||||
const resetButton=banner.querySelector("button");
|
||||
const ROLE_KEYS={
|
||||
super:"vwRoleSystem",admin:"vwRoleAdmin",teacher:"vwRoleTeacher",
|
||||
parent:"vwRoleParent",pupil:"vwRolePupil"
|
||||
};
|
||||
let renderSequence=0;
|
||||
|
||||
const isSystemManager=()=>!!currentUser&¤tUser.role==="super";
|
||||
const roleLabel=role=>T(ROLE_KEYS[role]||ROLE_KEYS.super);
|
||||
|
||||
function storedRole(){
|
||||
try{
|
||||
const value=localStorage.getItem("teach.superViewRole");
|
||||
return VIEW_ROLES.includes(value)?value:"super";
|
||||
}catch(e){return"super";}
|
||||
}
|
||||
function storeRole(role){
|
||||
try{localStorage.setItem("teach.superViewRole",role);}catch(e){}
|
||||
}
|
||||
function previewNotice(text){
|
||||
const note=document.createElement("aside");
|
||||
note.className="role-preview-note";
|
||||
const strong=document.createElement("strong");
|
||||
strong.textContent=T("vwPreview")+": ";
|
||||
note.append(strong,document.createTextNode(text));
|
||||
return note;
|
||||
}
|
||||
function clearPreviewSurface(){
|
||||
renderSequence++;
|
||||
document.body.classList.remove("role-preview-active","role-preview-parent","role-preview-pupil");
|
||||
banner.hidden=true;
|
||||
if(isSystemManager()){
|
||||
stopPupilView();
|
||||
stopParentView();
|
||||
}
|
||||
}
|
||||
function syncControls(){
|
||||
const enabled=isSystemManager();
|
||||
roleRow.hidden=!enabled;
|
||||
const role=enabled?activeRole():"super";
|
||||
roleSeg.querySelectorAll("button").forEach(button=>{
|
||||
const selected=button.dataset.role===role;
|
||||
button.classList.toggle("active",selected);
|
||||
button.setAttribute("aria-pressed",String(selected));
|
||||
});
|
||||
banner.hidden=!enabled||role==="super";
|
||||
bannerLabel.textContent=enabled&&role!=="super"?roleLabel(role):"";
|
||||
}
|
||||
async function renderPreviewSurface(){
|
||||
const sequence=++renderSequence;
|
||||
if(!isSystemManager()){clearPreviewSurface();syncControls();return;}
|
||||
const role=activeRole();
|
||||
document.body.classList.remove("role-preview-active","role-preview-parent","role-preview-pupil");
|
||||
stopPupilView();
|
||||
stopParentView();
|
||||
if(role==="parent"){
|
||||
document.body.classList.add("role-preview-active","role-preview-parent","parent-mode");
|
||||
renderParentView([]);
|
||||
const inner=parentView.querySelector(".parent-inner");
|
||||
if(inner)inner.prepend(previewNotice(T("vwParentPreview")));
|
||||
parentView.querySelectorAll("button,input,select,textarea").forEach(control=>{control.disabled=true;});
|
||||
}else if(role==="pupil"){
|
||||
document.body.classList.add("role-preview-active","role-preview-pupil","pupil-mode");
|
||||
const snapshot=serializeBoard();
|
||||
await renderPupilWidgets(snapshot.widgets||[],"kijken",true);
|
||||
if(sequence!==renderSequence)return;
|
||||
pupilView.prepend(previewNotice(T("vwPupilPreview")));
|
||||
}
|
||||
syncControls();
|
||||
}
|
||||
function chooseRole(role){
|
||||
if(!isSystemManager()||!VIEW_ROLES.includes(role))return;
|
||||
setActiveViewRole(role);
|
||||
storeRole(role);
|
||||
updateUserUI();
|
||||
document.dispatchEvent(new CustomEvent("userchange"));
|
||||
}
|
||||
|
||||
roleSeg.querySelectorAll("button").forEach(button=>{
|
||||
button.addEventListener("click",()=>chooseRole(button.dataset.role));
|
||||
});
|
||||
resetButton.addEventListener("click",()=>chooseRole("super"));
|
||||
document.addEventListener("userchange",()=>{
|
||||
if(isSystemManager()){
|
||||
const saved=storedRole();
|
||||
if(activeRole()!==saved)setActiveViewRole(saved);
|
||||
}else{
|
||||
setActiveViewRole("super");
|
||||
}
|
||||
syncControls();
|
||||
renderPreviewSurface();
|
||||
});
|
||||
document.addEventListener("langchange",()=>{
|
||||
syncControls();
|
||||
if(isSystemManager()&&["parent","pupil"].includes(activeRole()))renderPreviewSurface();
|
||||
});
|
||||
syncControls();
|
||||
})();
|
||||
|
|
@ -44,7 +44,7 @@ function registerSettingsSection(sec){ ST_SECTIONS.push(sec); }
|
|||
label:()=>T("stView"), sub:()=>T("stViewSub"),
|
||||
visible:()=>true,
|
||||
activate(){ showDomSection("view"); },
|
||||
keywords:()=>[T("vwTheme"), T("vwContrast"), T("vwLang"), T("vwTbar"), T("vwMotion"), T("vwGlass"),
|
||||
keywords:()=>[T("vwRole"), T("vwRoleSystem"), T("vwRoleAdmin"), T("vwRoleTeacher"), T("vwRoleParent"), T("vwRolePupil"), T("vwTheme"), T("vwContrast"), T("vwLang"), T("vwTbar"), T("vwMotion"), T("vwGlass"),
|
||||
"thema","theme","donker","dark","licht","light","contrast","taal","language",
|
||||
"menubalk","toolbar","animaties","animations","glas","glass"],
|
||||
});
|
||||
|
|
|
|||
64
test/role-preview.test.js
Normal file
64
test/role-preview.test.js
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {readFile} from 'node:fs/promises';
|
||||
import {runInNewContext} from 'node:vm';
|
||||
|
||||
test('alleen een systeemmanager kan een geldig actief weergaveprofiel projecteren',async()=>{
|
||||
const source=await readFile('public/js/permissions.js','utf8');
|
||||
const context={currentUser:{role:'super',extraRoles:[]}};
|
||||
runInNewContext(source+';globalThis.api={setActiveViewRole,activeRole,activeRoles,can,VIEW_ROLES}',context);
|
||||
const api=context.api;
|
||||
assert.deepEqual(Array.from(api.VIEW_ROLES),['super','admin','teacher','parent','pupil']);
|
||||
api.setActiveViewRole('parent');
|
||||
assert.equal(api.activeRole(),'parent');
|
||||
assert.equal(api.can('curriculum.view'),true);
|
||||
assert.equal(api.can('admin.access'),false);
|
||||
api.setActiveViewRole('pupil');
|
||||
assert.equal(api.activeRole(),'pupil');
|
||||
assert.equal(api.can('curriculum.view'),false);
|
||||
api.setActiveViewRole('onbekend');
|
||||
assert.equal(api.activeRole(),'super');
|
||||
context.currentUser={role:'teacher',extraRoles:[]};
|
||||
api.setActiveViewRole('parent');
|
||||
assert.equal(api.activeRole(),'teacher');
|
||||
});
|
||||
|
||||
test('Weergave bevat de vijf rollen en laadt de veilige voorbeeldmodule vóór de appstart',async()=>{
|
||||
const [html,preview,core,css,version]=await Promise.all([
|
||||
readFile('public/index.html','utf8'),
|
||||
readFile('public/js/role-preview.js','utf8'),
|
||||
readFile('public/js/core.js','utf8'),
|
||||
readFile('public/css/teach.css','utf8'),
|
||||
readFile('VERSION','utf8')
|
||||
]);
|
||||
for(const role of ['super','admin','teacher','parent','pupil'])assert.match(html,new RegExp('data-role="'+role+'"'));
|
||||
assert.match(html,/id="superViewRole" hidden/);
|
||||
assert.ok(html.indexOf('js/role-preview.js')<html.indexOf('js/app.js'));
|
||||
assert.match(preview,/currentUser\.role==="super"/);
|
||||
assert.match(preview,/renderParentView\(\[\]\)/);
|
||||
assert.match(preview,/renderPupilWidgets\(snapshot\.widgets\|\|\[\],"kijken",true\)/);
|
||||
assert.match(preview,/querySelectorAll\("button,input,select,textarea"\)/);
|
||||
assert.doesNotMatch(preview,/currentUser\.role\s*=(?!=)/);
|
||||
assert.doesNotMatch(preview,/api\(/);
|
||||
assert.match(css,/body\.role-preview-pupil #pupilView \.widget-body\{pointer-events:none/);
|
||||
for(const key of ['vwRole','vwRoleSystem','vwRoleAdmin','vwRoleTeacher','vwRoleParent','vwRolePupil','vwRoleNote','vwPreviewReset']){
|
||||
assert.equal((core.match(new RegExp(key+':','g'))||[]).length,2,key);
|
||||
}
|
||||
assert.ok(core.includes('const VERSION = "'+version.trim()+'"'));
|
||||
});
|
||||
|
||||
test('voorbeeldrol begrenst clientmenu’s maar verandert serverpermissies niet',async()=>{
|
||||
const [client,server,settings,admin,explorer]=await Promise.all([
|
||||
readFile('public/js/permissions.js','utf8'),
|
||||
readFile('src/permissions.js','utf8'),
|
||||
readFile('public/js/settings.js','utf8'),
|
||||
readFile('public/js/admin.js','utf8'),
|
||||
readFile('public/js/explorer.js','utf8')
|
||||
]);
|
||||
assert.match(client,/function activeRoles\(\)/);
|
||||
assert.doesNotMatch(server,/VIEW_AS_ROLE|activeRole/);
|
||||
assert.match(settings,/T\("vwRole"\)/);
|
||||
assert.match(admin,/activeRoles\(\)\.flatMap/);
|
||||
assert.match(admin,/show:\(\)=>activeRole\(\)==="super"/);
|
||||
assert.match(explorer,/activeRole\(\) === "pupil"/);
|
||||
});
|
||||
Loading…
Reference in a new issue