Voeg thematische kleurplaten en creatieve niveaus toe (v0.4.65-beta) #3
9 changed files with 468 additions and 4 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
0.4.36-beta
|
0.4.37-beta
|
||||||
|
|
|
||||||
|
|
@ -636,6 +636,39 @@
|
||||||
#stSecHead:empty{display:none;}
|
#stSecHead:empty{display:none;}
|
||||||
.st-sec-title{font-size:17px; font-weight:800;}
|
.st-sec-title{font-size:17px; font-weight:800;}
|
||||||
.st-sec-sub{font-size:12.5px; color:var(--muted); margin-top:2px;}
|
.st-sec-sub{font-size:12.5px; color:var(--muted); margin-top:2px;}
|
||||||
|
/* curriculumwiki: volwassen, informatief en bruikbaar op tablet/telefoon */
|
||||||
|
#curriculumWiki{display:flex;flex-direction:column;gap:14px;padding-bottom:10px;}
|
||||||
|
.wiki-hero{display:flex;align-items:flex-start;justify-content:space-between;gap:18px;padding:18px;border:1px solid var(--line);border-radius:12px;background:linear-gradient(135deg,var(--surface),var(--surface-2));}
|
||||||
|
.wiki-hero-text{max-width:680px}.wiki-kicker{margin-bottom:5px;color:var(--accent);font-size:11px;font-weight:750;letter-spacing:.6px;text-transform:uppercase}
|
||||||
|
.wiki-hero h2{margin:0 0 7px;font-size:23px}.wiki-hero p{margin:0;color:var(--muted);line-height:1.5}
|
||||||
|
.wiki-facts{display:grid;grid-template-columns:auto auto;align-items:baseline;gap:2px 7px;flex:none;padding:10px 12px;border-left:3px solid var(--accent)}
|
||||||
|
.wiki-facts strong{font-size:24px}.wiki-facts span{color:var(--muted);font-size:11px}
|
||||||
|
.wiki-status{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:10px;padding:11px 13px;border:1px solid color-mix(in srgb,var(--orange) 45%,var(--line));border-radius:9px;background:color-mix(in srgb,var(--orange) 8%,var(--surface));font-size:12.5px;line-height:1.45}
|
||||||
|
.wiki-status strong{white-space:nowrap}.wiki-status span{color:var(--muted)}
|
||||||
|
.wiki-link{width:max-content;max-width:100%;color:var(--accent);font-size:12px;font-weight:700;text-decoration:none}.wiki-link:hover,.wiki-link:focus-visible{text-decoration:underline}
|
||||||
|
.wiki-tule{border:1px solid var(--line);border-radius:9px;background:var(--surface)}.wiki-tule summary{cursor:pointer;padding:10px 12px;font-weight:700}.wiki-tule p{margin:0;padding:0 12px 12px;color:var(--muted);font-size:12.5px;line-height:1.55}
|
||||||
|
.wiki-source-links{display:flex;flex-wrap:wrap;gap:8px;padding:0 12px 12px}
|
||||||
|
.wiki-language-note{margin:0;padding:8px 11px;border-radius:7px;background:var(--surface-2);color:var(--muted);font-size:12px}
|
||||||
|
.wiki-search{display:flex;flex-direction:column;gap:5px}.wiki-search label{font-size:12px;font-weight:700}
|
||||||
|
#settingsModal .wiki-search input{margin:0;background:var(--surface)}
|
||||||
|
.wiki-filters{display:flex;gap:6px;overflow-x:auto;padding-bottom:3px;scrollbar-width:thin}
|
||||||
|
.wiki-filters button{flex:none;border:1px solid var(--line);border-radius:999px;padding:7px 10px;background:var(--surface);color:var(--ink);font:inherit;font-size:12px;cursor:pointer}
|
||||||
|
.wiki-filters button[aria-pressed="true"]{border-color:var(--accent);background:var(--accent-soft);color:var(--accent-ink);font-weight:700}
|
||||||
|
.wiki-results{display:flex;flex-direction:column;gap:22px}.wiki-area{display:flex;flex-direction:column;gap:9px;scroll-margin-top:10px}
|
||||||
|
.wiki-area-head{display:flex;align-items:center;gap:9px;border-bottom:1px solid var(--line);padding-bottom:7px}.wiki-area-head h3{display:flex;align-items:center;gap:7px;margin:0;font-size:18px}.wiki-area-icon{font-size:20px}
|
||||||
|
.wiki-range{margin-left:auto;color:var(--muted);font-size:11px;font-weight:650}.wiki-area-intro{max-width:780px;margin:0;color:var(--muted);font-size:12.5px;line-height:1.5}
|
||||||
|
.wiki-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:9px}
|
||||||
|
.wiki-goal{display:flex;flex-direction:column;gap:8px;min-width:0;padding:12px;border:1px solid var(--line);border-radius:9px;background:var(--surface)}
|
||||||
|
.wiki-goal-head{display:flex;align-items:center;gap:7px}.wiki-goal-id{flex:none;padding:3px 7px;border-radius:5px;background:var(--accent-soft);color:var(--accent-ink);font-size:11px;font-weight:750}.wiki-domain{overflow:hidden;color:var(--muted);font-size:11px;text-overflow:ellipsis;white-space:nowrap}
|
||||||
|
.wiki-summary{margin:0;font-size:13px;line-height:1.45}.wiki-goal h4{margin:1px 0 -3px;color:var(--muted);font-size:10.5px;text-transform:uppercase;letter-spacing:.4px}
|
||||||
|
.wiki-parts{display:flex;flex-direction:column;gap:3px;margin:0;padding-left:19px;font-size:12px;line-height:1.35}.wiki-parts strong{color:var(--accent-ink)}
|
||||||
|
.wiki-coverage-row{display:flex;flex-direction:column;align-items:flex-start;gap:6px;margin-top:auto;padding-top:3px}.wiki-coverage{padding:3px 7px;border-radius:5px;font-size:10.5px;font-weight:750}
|
||||||
|
.wiki-coverage-partial{background:color-mix(in srgb,var(--green) 14%,var(--surface));color:color-mix(in srgb,var(--green) 70%,var(--ink))}
|
||||||
|
.wiki-coverage-support{background:color-mix(in srgb,var(--blue) 12%,var(--surface));color:color-mix(in srgb,var(--blue) 70%,var(--ink))}
|
||||||
|
.wiki-coverage-open{background:var(--surface-2);color:var(--muted)}
|
||||||
|
.wiki-widgets{display:flex;align-items:center;flex-wrap:wrap;gap:4px}.wiki-widgets-label{color:var(--muted);font-size:10.5px}.wiki-widget{padding:2px 6px;border:1px solid var(--line);border-radius:999px;font-size:10.5px}
|
||||||
|
.wiki-empty{padding:30px;border:1px dashed var(--line);border-radius:9px;color:var(--muted);text-align:center}
|
||||||
|
.wiki-disclaimer{padding:11px 13px;border-left:3px solid var(--accent);background:var(--surface-2);font-size:12px;line-height:1.5}.wiki-disclaimer strong{font-weight:750}
|
||||||
/* zoekresultaten: gegroepeerd per sectie, klik = navigeren */
|
/* zoekresultaten: gegroepeerd per sectie, klik = navigeren */
|
||||||
#stSearchResults{display:flex; flex-direction:column; gap:2px;}
|
#stSearchResults{display:flex; flex-direction:column; gap:2px;}
|
||||||
.st-result{
|
.st-result{
|
||||||
|
|
@ -659,6 +692,9 @@
|
||||||
.st-nav-btn{flex:none; padding:7px 10px;}
|
.st-nav-btn{flex:none; padding:7px 10px;}
|
||||||
#stContent{padding:12px 2px;}
|
#stContent{padding:12px 2px;}
|
||||||
#stSearch{max-width:none;}
|
#stSearch{max-width:none;}
|
||||||
|
.wiki-hero{flex-direction:column}.wiki-facts{grid-template-columns:auto auto;border-left:0;border-top:3px solid var(--accent)}
|
||||||
|
.wiki-status{grid-template-columns:1fr}.wiki-status strong{white-space:normal}
|
||||||
|
.wiki-grid{grid-template-columns:1fr}
|
||||||
}
|
}
|
||||||
/* =========================================================
|
/* =========================================================
|
||||||
Letter game widget
|
Letter game widget
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@
|
||||||
<script src="js/core.js?v=__V__" defer></script>
|
<script src="js/core.js?v=__V__" defer></script>
|
||||||
<script src="js/permissions.js?v=__V__" defer></script>
|
<script src="js/permissions.js?v=__V__" defer></script>
|
||||||
<script src="js/settings.js?v=__V__" defer></script>
|
<script src="js/settings.js?v=__V__" defer></script>
|
||||||
|
<script src="js/curriculum.js?v=__V__" defer></script>
|
||||||
|
<script src="js/wiki.js?v=__V__" defer></script>
|
||||||
<script src="js/cms.js?v=__V__" defer></script>
|
<script src="js/cms.js?v=__V__" defer></script>
|
||||||
<script src="js/data.js?v=__V__" defer></script>
|
<script src="js/data.js?v=__V__" defer></script>
|
||||||
<script src="js/avatar.js?v=__V__" defer></script>
|
<script src="js/avatar.js?v=__V__" defer></script>
|
||||||
|
|
@ -68,6 +70,7 @@
|
||||||
<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="btnCast" title="Cast">📡</button>
|
||||||
|
<button class="tbtn ghost" id="btnWiki" style="display:none;">📚 <span data-i18n="wikiShort"></span></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>
|
||||||
|
|
@ -293,6 +296,10 @@
|
||||||
<div class="guestnote" data-i18n="vwNote"></div>
|
<div class="guestnote" data-i18n="vwNote"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="st-section" data-section="curriculum" style="display:none;">
|
||||||
|
<div id="curriculumWiki"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="st-section" data-section="admin" style="display:none;">
|
<div class="st-section" data-section="admin" style="display:none;">
|
||||||
<div id="adminModal"></div>
|
<div id="adminModal"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -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.36-beta";
|
const VERSION = "0.4.37-beta";
|
||||||
(function(){
|
(function(){
|
||||||
const tag = document.getElementById("verTag");
|
const tag = document.getElementById("verTag");
|
||||||
tag.textContent = "v"+VERSION;
|
tag.textContent = "v"+VERSION;
|
||||||
|
|
@ -234,6 +234,27 @@ const I18N = {
|
||||||
stGroupPersonal:"Persoonlijk", stGroupSchool:"School", stGroupSystem:"Systeem",
|
stGroupPersonal:"Persoonlijk", stGroupSchool:"School", stGroupSystem:"Systeem",
|
||||||
stAccountSub:"Je gegevens, wachtwoord en reservekopieën.",
|
stAccountSub:"Je gegevens, wachtwoord en reservekopieën.",
|
||||||
stViewSub:"Weergave op dit apparaat: thema, taal en menubalk.",
|
stViewSub:"Weergave op dit apparaat: thema, taal en menubalk.",
|
||||||
|
wikiTitle:"Curriculumwiki", wikiShort:"Wiki",
|
||||||
|
wikiSub:"Nieuwe SLO-kerndoelen voor het primair onderwijs, per leergebied.",
|
||||||
|
wikiKicker:"Primair onderwijs · juli 2026",
|
||||||
|
wikiIntro:"Bekijk de 40 nieuwe kerndoelen, hun onderdelen en de huidige aansluiting van Teach.",
|
||||||
|
wikiAreas:"leergebieden", wikiGoals:"kerndoelen", wikiGoal:"Kerndoel",
|
||||||
|
wikiGoalsRange:"Kerndoelen {range}", wikiParts:"Onderdelen",
|
||||||
|
wikiStatusTitle:"Status van de doelen",
|
||||||
|
wikiStatusText:"Dit zijn definitieve conceptkerndoelen. De wettelijke invoering wordt gefaseerd vastgelegd; de officiële SLO-publicatie blijft leidend.",
|
||||||
|
wikiOfficialSource:"Open officiële SLO-publicatie",
|
||||||
|
wikiOfficialArea:"Bekijk {area} in de officiële publicatie",
|
||||||
|
wikiTuleTitle:"TULE en de nieuwe leerlijnen",
|
||||||
|
wikiTuleText:"TULE werkt de kerndoelen uit 2006 voorbeeldmatig uit. Nieuwe SLO-leerlijnen bij de doelen van 2026 zijn nog in ontwikkeling. Een niveau-indeling in Teach is daarom geen officiële SLO-leerlijn.",
|
||||||
|
wikiTuleSource:"Open TULE bij SLO", wikiProgressionsSource:"Volg de ontwikkeling van nieuwe leerlijnen",
|
||||||
|
wikiSearchLabel:"Doorzoek de curriculumwiki",
|
||||||
|
wikiSearchPlaceholder:"Zoek op doel, domein, onderwerp of widget…",
|
||||||
|
wikiAreaFilter:"Filter op leergebied", wikiAllAreas:"Alle leergebieden",
|
||||||
|
wikiCoveragePartial:"Deels in Teach", wikiCoverageSupport:"Lesondersteuning", wikiCoverageOpen:"Nog open",
|
||||||
|
wikiWidgets:"Huidige widgets", wikiNoResults:"Geen kerndoelen gevonden.",
|
||||||
|
wikiCarefulTitle:"Zorgvuldig gebruiken.",
|
||||||
|
wikiCarefulText:"Een widget die één onderdeel oefent, dekt niet automatisch het volledige kerndoel. School- en ervaringsdoelen vragen andere vormen van bewijs dan een goed/fout-score.",
|
||||||
|
wikiLanguageNote:"The official SLO goals and these implementation summaries are shown in Dutch.",
|
||||||
stUsersSub:"Accounts beheren: leerlingen, staf, ouders en aanmeldingen.",
|
stUsersSub:"Accounts beheren: leerlingen, staf, ouders en aanmeldingen.",
|
||||||
stKlassenSub:"Klassen aanmaken, koppelen en beheren.",
|
stKlassenSub:"Klassen aanmaken, koppelen en beheren.",
|
||||||
stToewijzSub:"Bepaal wat een klas of leerling te zien krijgt.",
|
stToewijzSub:"Bepaal wat een klas of leerling te zien krijgt.",
|
||||||
|
|
@ -582,6 +603,27 @@ const I18N = {
|
||||||
stGroupPersonal:"Personal", stGroupSchool:"School", stGroupSystem:"System",
|
stGroupPersonal:"Personal", stGroupSchool:"School", stGroupSystem:"System",
|
||||||
stAccountSub:"Your details, password and backups.",
|
stAccountSub:"Your details, password and backups.",
|
||||||
stViewSub:"Display on this device: theme, language and toolbar.",
|
stViewSub:"Display on this device: theme, language and toolbar.",
|
||||||
|
wikiTitle:"Curriculum wiki", wikiShort:"Wiki",
|
||||||
|
wikiSub:"New SLO attainment targets for Dutch primary education, grouped by learning area.",
|
||||||
|
wikiKicker:"Primary education · July 2026",
|
||||||
|
wikiIntro:"Browse all 40 new attainment targets, their components and Teach's current coverage.",
|
||||||
|
wikiAreas:"learning areas", wikiGoals:"attainment targets", wikiGoal:"Target",
|
||||||
|
wikiGoalsRange:"Targets {range}", wikiParts:"Components",
|
||||||
|
wikiStatusTitle:"Status of these targets",
|
||||||
|
wikiStatusText:"These are final draft targets. Their legal introduction is being phased; the official SLO publication remains authoritative.",
|
||||||
|
wikiOfficialSource:"Open the official SLO publication",
|
||||||
|
wikiOfficialArea:"View {area} in the official publication",
|
||||||
|
wikiTuleTitle:"TULE and the new learning progressions",
|
||||||
|
wikiTuleText:"TULE is an illustrative elaboration of the 2006 targets. New SLO learning progressions for the 2026 targets are still being developed. Teach levels are therefore not official SLO progressions.",
|
||||||
|
wikiTuleSource:"Open TULE at SLO", wikiProgressionsSource:"Follow the development of new progressions",
|
||||||
|
wikiSearchLabel:"Search the curriculum wiki",
|
||||||
|
wikiSearchPlaceholder:"Search by target, domain, topic or widget…",
|
||||||
|
wikiAreaFilter:"Filter by learning area", wikiAllAreas:"All learning areas",
|
||||||
|
wikiCoveragePartial:"Partly in Teach", wikiCoverageSupport:"Lesson support", wikiCoverageOpen:"Not covered yet",
|
||||||
|
wikiWidgets:"Current widgets", wikiNoResults:"No attainment targets found.",
|
||||||
|
wikiCarefulTitle:"Use with care.",
|
||||||
|
wikiCarefulText:"A widget practising one component does not automatically cover the full target. School-level and experience targets require different evidence from a right/wrong score.",
|
||||||
|
wikiLanguageNote:"The official SLO goals and these implementation summaries are shown in Dutch.",
|
||||||
stUsersSub:"Manage accounts: pupils, staff, parents and enrolments.",
|
stUsersSub:"Manage accounts: pupils, staff, parents and enrolments.",
|
||||||
stKlassenSub:"Create, link and manage classes.",
|
stKlassenSub:"Create, link and manage classes.",
|
||||||
stToewijzSub:"Choose what a class or pupil gets to see.",
|
stToewijzSub:"Choose what a class or pupil gets to see.",
|
||||||
|
|
@ -969,6 +1011,7 @@ const roleKey = r => r==="super" ? "roleSuper" : r==="admin" ? "roleAdmin" : r==
|
||||||
function updateUserUI(){
|
function updateUserUI(){
|
||||||
document.getElementById("userLbl").textContent = currentUser ? currentUser.displayName : T("login");
|
document.getElementById("userLbl").textContent = currentUser ? currentUser.displayName : T("login");
|
||||||
document.getElementById("btnUser").classList.toggle("on", !!currentUser);
|
document.getElementById("btnUser").classList.toggle("on", !!currentUser);
|
||||||
|
document.getElementById("btnWiki").style.display = can("curriculum.view") ? "" : "none";
|
||||||
/* welke instellingen-secties zichtbaar zijn bepaalt settings.js per rol
|
/* welke instellingen-secties zichtbaar zijn bepaalt settings.js per rol
|
||||||
(sectieregister); hier alleen nog de accountgegevens zelf */
|
(sectieregister); hier alleen nog de accountgegevens zelf */
|
||||||
if(currentUser){
|
if(currentUser){
|
||||||
|
|
|
||||||
109
public/js/curriculum.js
Normal file
109
public/js/curriculum.js
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
/* Nieuwe SLO-kerndoelen primair onderwijs, versie juli 2026.
|
||||||
|
Dit is een compacte, implementatiegerichte samenvatting. De officiële
|
||||||
|
publicatie blijft leidend; componentnamen volgen de publieke doelstructuur. */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const CURRICULUM_PO_2026=Object.freeze({
|
||||||
|
id:"slo-po-2026",
|
||||||
|
edition:"2026-07",
|
||||||
|
status:"definitief-concept",
|
||||||
|
source:"https://www.slo.nl/publish/pages/23186/kerndoelen-primair-onderwijs-slo-juli-2026.pdf",
|
||||||
|
tule:"https://www.slo.nl/thema/meer/tule/",
|
||||||
|
progressions:"https://www.slo.nl/thema/meer/kerndoelen-onderwijs/ontwikkeling-leerlijnen/",
|
||||||
|
areas:Object.freeze([
|
||||||
|
{
|
||||||
|
id:"nederlands",icon:"📖",title:"Nederlands",goalsRange:"1–9",page:20,
|
||||||
|
description:"Taalcompetentie ontwikkelen door betekenisvol begrijpen, spreken, schrijven, gesprekken, taalbeschouwing en literatuur.",
|
||||||
|
goals:[
|
||||||
|
{id:1,domain:"Overkoepelend",summary:"De school stimuleert taalcompetentie met een rijke omgeving en taalontwikkeling in alle leergebieden.",parts:["Taal- en leesomgeving","Taal in de leergebieden"],coverage:"support",widgets:["anchor","letters","flits","hak","ball","sentence","media"]},
|
||||||
|
{id:2,domain:"Communicatie",summary:"Leerlingen begrijpen en evalueren zakelijke en literaire teksten en onderzoeken de betrouwbaarheid van bronnen.",parts:["Luisteren en lezen met begrip","Luisteren en lezen met diep begrip","Bronnen verkennen"],coverage:"support",widgets:["anchor","mind","media"]},
|
||||||
|
{id:3,domain:"Communicatie",summary:"Leerlingen produceren doelgerichte en creatieve teksten en gebruiken schrijven om te denken en leren.",parts:["Doelgericht spreken en schrijven","Creatief taal gebruiken","Schrijven om te leren"],coverage:"partial",widgets:["sentence","write","notes","mind"]},
|
||||||
|
{id:4,domain:"Communicatie",summary:"Leerlingen voeren gesprekken om doelgericht te communiceren, samen te denken en te leren.",parts:["Doelgericht gesprekken voeren","Gesprekken voeren om te leren"],coverage:"support",widgets:["anchor","mind","poll"]},
|
||||||
|
{id:5,domain:"Communicatie",summary:"Leerlingen reflecteren op hun taalactiviteiten, aanpak en ontwikkeling.",parts:["Reflecteren op taalactiviteiten"],coverage:"support",widgets:["letters","flits","hak","sentence"]},
|
||||||
|
{id:6,domain:"Taal",summary:"Leerlingen onderzoeken taalvorm en betekenis en passen spelling, formulering en interpunctie functioneel toe.",parts:["Vorm en betekenis beschouwen","Spelling, formulering en interpunctie"],coverage:"partial",widgets:["letters","hak","sentence","wordtypes"]},
|
||||||
|
{id:7,domain:"Taal",summary:"Leerlingen onderzoeken hun talige identiteit en verkennen taalvariatie en taalverandering.",parts:["Talige identiteit","Taalvariatie en taalverandering"],coverage:"support",widgets:["anchor","letters","flits","ball"]},
|
||||||
|
{id:8,domain:"Literatuur",summary:"Leerlingen doen brede literatuurervaring op en ontwikkelen leesvoorkeur en inzicht in de waarde van literatuur.",parts:["Leesvoorkeur","Waarde van literatuur"],coverage:"open",widgets:[]},
|
||||||
|
{id:9,domain:"Literatuur",summary:"Leerlingen onderzoeken verhalende teksten en kenmerken van verschillende literaire genres.",parts:["Verhalende teksten","Literaire genres"],coverage:"open",widgets:[]}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id:"rekenen-en-wiskunde",icon:"🔢",title:"Rekenen en wiskunde",goalsRange:"10–18",page:32,
|
||||||
|
description:"Wiskundige concepten, denk-werkwijzen, taal en gereedschap begrijpen en toepassen in de wereld.",
|
||||||
|
goals:[
|
||||||
|
{id:10,domain:"Wiskundige concepten",summary:"Leerlingen redeneren en rekenen met getallen, breuken en verhoudingen.",parts:["Gehele en decimale getallen","Breuken","Verhoudingen"],coverage:"partial",widgets:["madd","msub","mmul","mdiv","mpct","numberline","fractions","placevalue"]},
|
||||||
|
{id:11,domain:"Wiskundige concepten",summary:"Leerlingen meten, redeneren en rekenen met grootheden, eenheden en passende meetinstrumenten.",parts:["Grootheden en eenheden"],coverage:"partial",widgets:["clock","money"]},
|
||||||
|
{id:12,domain:"Wiskundige concepten",summary:"Leerlingen representeren en interpreteren data en beoordelen conclusies bij gegevens kritisch.",parts:["Data"],coverage:"support",widgets:["poll"]},
|
||||||
|
{id:13,domain:"Wiskundige concepten",summary:"Leerlingen herkennen, beschrijven en onderzoeken patronen en verbanden.",parts:["Patronen en verbanden"],coverage:"open",widgets:[]},
|
||||||
|
{id:14,domain:"Wiskundige concepten",summary:"Leerlingen redeneren over meetkundige figuren en plaatsbepaling en voeren transformaties uit.",parts:["Meetkunde"],coverage:"open",widgets:[]},
|
||||||
|
{id:15,domain:"Wiskundige denk-werkwijzen",summary:"Leerlingen lossen problemen op, modelleren situaties en gebruiken en beschrijven algoritmes.",parts:["Wiskundig probleemoplossen","Wiskundig modelleren","Gebruiken en beschrijven van algoritmes"],coverage:"support",widgets:["madd","msub","mmul","mdiv","mpct"]},
|
||||||
|
{id:16,domain:"Wiskundige denk-werkwijzen",summary:"Leerlingen gebruiken wiskundetaal, representaties en instrumenten beredeneerd.",parts:["Wiskundetaal en representaties","Wiskundige instrumenten"],coverage:"partial",widgets:["numberline","fractions","clock","placevalue","money"]},
|
||||||
|
{id:17,domain:"Wiskunde en de wereld",summary:"Leerlingen ontwikkelen een precieze, kritische, onderzoekende en volhardende wiskundige houding.",parts:["Wiskundige attitude"],coverage:"support",widgets:["madd","msub","mmul","mdiv","mpct","numberline"]},
|
||||||
|
{id:18,domain:"Wiskunde en de wereld",summary:"Leerlingen herkennen en gebruiken wiskunde in werkelijke situaties en andere leergebieden.",parts:["Wiskunde in de werkelijkheid","Wiskunde in verschillende leergebieden"],coverage:"partial",widgets:["money","clock","topography"]}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id:"burgerschap",icon:"🤝",title:"Burgerschap",goalsRange:"19–21",page:44,
|
||||||
|
description:"Een democratische oefenplaats vormen en leren over rechtsstaat, diversiteit en maatschappelijke betrokkenheid.",
|
||||||
|
goals:[
|
||||||
|
{id:19,domain:"Democratische oefenplaats",summary:"De school stimuleert sociale en maatschappelijke competenties en veilige, respectvolle interactie.",parts:["Sociale en maatschappelijke competenties"],coverage:"support",widgets:["poll","mood","feelings","compliments","names","light"]},
|
||||||
|
{id:20,domain:"Democratische rechtsstaat",summary:"Leerlingen onderzoeken basiswaarden en beginselen van de democratische rechtsstaat en diversiteit in de samenleving.",parts:["Basiswaarden van de democratische rechtsstaat","Diversiteit in de samenleving"],coverage:"support",widgets:["feelings","compliments","poll"]},
|
||||||
|
{id:21,domain:"Democratische en maatschappelijke betrokkenheid",summary:"Leerlingen doen ervaring op met democratische deelname, invloed, maatschappelijke inzet en samenwerking.",parts:["Democratische betrokkenheid","Maatschappelijke betrokkenheid"],coverage:"support",widgets:["poll","mind","anchor"]}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id:"digitale-geletterdheid",icon:"💻",title:"Digitale geletterdheid",goalsRange:"22–24",page:52,
|
||||||
|
description:"Digitale systemen en media functioneel en kritisch inzetten, producten maken en veilig deelnemen aan de digitale wereld.",
|
||||||
|
goals:[
|
||||||
|
{id:22,domain:"Praktische kennis en vaardigheden",summary:"Leerlingen gebruiken digitale systemen, media, informatie, data en AI functioneel en kritisch.",parts:["Digitale systemen","Digitale media en informatie","Data","Artificiële intelligentie (AI)"],coverage:"support",widgets:["media","mind","write"]},
|
||||||
|
{id:23,domain:"Ontwerpen en maken",summary:"Leerlingen ontwerpen, testen en verbeteren digitale producten en programmeerbare oplossingen.",parts:["Creëren met digitale technologie","Programmeren"],coverage:"support",widgets:["write","mind","media"]},
|
||||||
|
{id:24,domain:"De gedigitaliseerde wereld",summary:"Leerlingen onderzoeken veiligheid, privacy en de invloed van digitale technologie op zichzelf, anderen en de samenleving.",parts:["Veiligheid en privacy","Digitale technologie, jezelf en de ander","Digitale technologie, samenleving en wereld"],coverage:"open",widgets:[]}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id:"mens-en-maatschappij",icon:"🌍",title:"Mens en maatschappij",goalsRange:"25–28",page:62,
|
||||||
|
description:"De menselijke leefwereld onderzoeken vanuit geografische, historische, economische en sociale perspectieven.",
|
||||||
|
goals:[
|
||||||
|
{id:25,domain:"Vraagstukken en perspectieven",summary:"Leerlingen onderzoeken maatschappelijke verschijnselen, vraagstukken en de omgeving van de school vanuit meerdere invalshoeken.",parts:["Denkwijzen","Vraagstukken","De omgeving van de school"],coverage:"support",widgets:["mind","anchor","poll","media"]},
|
||||||
|
{id:26,domain:"Mens en ruimte",summary:"Leerlingen bouwen met geografische bronnen een wereldbeeld op en onderzoeken de wisselwerking tussen mens en leefomgeving.",parts:["Wereldbeeld","De aarde als leefomgeving"],coverage:"partial",widgets:["topography"]},
|
||||||
|
{id:27,domain:"Mens en tijd",summary:"Leerlingen onderzoeken historische ontwikkelingen, gebruiken chronologie en herkennen doorwerking van het verleden.",parts:["Historische ontwikkelingen","Tijd","Doorwerking van het verleden"],coverage:"open",widgets:[]},
|
||||||
|
{id:28,domain:"Mens en samenleving",summary:"Leerlingen onderzoeken economie, invloed, macht, diversiteit en sociale omgeving en nemen verantwoord deel aan verkeer.",parts:["Economische keuzes","Invloed en macht","Diversiteit","Sociale omgeving","Verkeer"],coverage:"support",widgets:["money","feelings","compliments","topography"]}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id:"mens-en-natuur",icon:"🔬",title:"Mens en natuur",goalsRange:"29–32",page:74,
|
||||||
|
description:"Natuurwetenschap, technologie, organismen, gezondheid en systeem aarde onderzoekend leren begrijpen.",
|
||||||
|
goals:[
|
||||||
|
{id:29,domain:"Natuurwetenschappen en technologie",summary:"Leerlingen onderzoeken vraagstukken met natuurwetenschappelijke en technologische denk- en werkwijzen.",parts:["Vraagstukken","Denkwijzen","Werkwijzen","Aard van natuurwetenschappen en technologie"],coverage:"partial",widgets:["trees","birds","plants"]},
|
||||||
|
{id:30,domain:"Natuur- en scheikunde en technische systemen",summary:"Leerlingen onderzoeken technische systemen, stoffen en verschijnselen rond licht, geluid, energie en krachten.",parts:["Technische systemen","Stoffen en hun eigenschappen","Licht, geluid, energie en krachten"],coverage:"open",widgets:[]},
|
||||||
|
{id:31,domain:"Organismen en gezondheid",summary:"Leerlingen onderzoeken organismen en hun levensprocessen en leren onderbouwde keuzes rond lichamelijke en mentale gezondheid.",parts:["Organismen","Gezond leven"],coverage:"partial",widgets:["trees","birds","plants","breathe","mood","feelings"]},
|
||||||
|
{id:32,domain:"Systeem aarde",summary:"Leerlingen onderzoeken de aarde, weer, klimaat, water en samenhang en menselijke invloed in ecosystemen.",parts:["Aarde","Weer, klimaat en water","Ecosystemen"],coverage:"partial",widgets:["trees","birds","plants","topography"]}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id:"engels",icon:"💬",title:"Moderne vreemde talen: Engels",goalsRange:"33–34",page:84,
|
||||||
|
description:"Met plezier en vertrouwen eenvoudig communiceren in het Engels en taal- en cultuurbewustzijn ontwikkelen.",
|
||||||
|
goals:[
|
||||||
|
{id:33,domain:"Communicatie",summary:"Leerlingen begrijpen eenvoudige Engelse teksten en communiceren mondeling en schriftelijk in vertrouwde situaties.",parts:["Kijk-, luister- en leesvaardigheid","Mondelinge communicatie","Schriftelijke communicatie"],coverage:"partial",widgets:["letters","flits","hak","ball","sentence"]},
|
||||||
|
{id:34,domain:"Taal- en cultuurbewustzijn",summary:"Leerlingen reflecteren op taalleren en meertaligheid en verkennen interculturele communicatie.",parts:["Taalleerproces en meertaligheid","Interculturele communicatieve competentie"],coverage:"support",widgets:["anchor","letters","flits","ball"]}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id:"kunst-en-cultuur",icon:"🎨",title:"Kunst en cultuur",goalsRange:"35–37",page:92,
|
||||||
|
description:"Artistiek creatief vermogen ontwikkelen, kunstzinnige uitingen maken en kunst en cultuur beleven en duiden.",
|
||||||
|
goals:[
|
||||||
|
{id:35,domain:"Artistiek creatief vermogen",summary:"Leerlingen gebruiken creatieve strategieën in een iteratief proces en geven betekenis aan eigen en andermans werk.",parts:["Creatieve maak- en denkstrategieën","Ontwikkelen van creatieve vermogens","Betekenis geven"],coverage:"support",widgets:["write","mind","media"]},
|
||||||
|
{id:36,domain:"Maken en betekenis geven",summary:"Leerlingen verbeelden ideeën en gebruiken technieken, vaardigheden, materialen en vaktaal uit verschillende kunstdisciplines.",parts:["Verbeelden en creëren","Kunstzinnige technieken en vaardigheden"],coverage:"support",widgets:["write","media"]},
|
||||||
|
{id:37,domain:"Meemaken en betekenis geven",summary:"Leerlingen beleven kunst en cultuur actief en verbinden uitingen aan maker, tijd, plaats, functie en context.",parts:["Kunst en cultuur actief meemaken","Kunst en cultuur in context"],coverage:"open",widgets:[]}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id:"bewegen-en-sport",icon:"🏃",title:"Bewegen en sport",goalsRange:"38–40",page:100,
|
||||||
|
description:"Beweegvaardigheid ontwikkelen, veilig samen bewegen en ontdekken welke beweegvormen en contexten passen.",
|
||||||
|
goals:[
|
||||||
|
{id:38,domain:"Leren bewegen",summary:"Leerlingen nemen deel aan uiteenlopende beweegthema’s, verbeteren vaardigheden en stemmen activiteiten af op eigen mogelijkheden.",parts:["Deelnemen aan bewegen","Verbeteren beweegvaardigheid","Beweegactiviteit passend maken"],coverage:"open",widgets:[]},
|
||||||
|
{id:39,domain:"Samen bewegen",summary:"Leerlingen nemen verantwoordelijkheid voor veilig en sociaal samen bewegen en helpen activiteiten regelen.",parts:["Bewegen met anderen","Bewegen regelen"],coverage:"open",widgets:[]},
|
||||||
|
{id:40,domain:"Betrokken bewegen",summary:"Leerlingen verkennen beweegcontexten en onderzoeken voorkeuren, kwaliteiten, motieven en ontwikkeling.",parts:["Oriënteren op beweegcontexten","Beweegidentiteit ontwikkelen"],coverage:"open",widgets:[]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
});
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
/* teach - centrale permissiematrix voor de 4 vaste rollen (super/admin/teacher/pupil).
|
/* teach - centrale permissiematrix voor de vaste rollen, inclusief ouder.
|
||||||
Gespiegeld in src/permissions.js (geen bundler in dit project, dus twee bestanden
|
Gespiegeld in src/permissions.js (geen bundler in dit project, dus twee bestanden
|
||||||
die dezelfde waarden hand-in-hand bijhouden). */
|
die dezelfde waarden hand-in-hand bijhouden). */
|
||||||
const PERMISSIONS = {
|
const PERMISSIONS = {
|
||||||
'admin.access': ['super', 'admin', 'teacher'],
|
'admin.access': ['super', 'admin', 'teacher'],
|
||||||
|
'curriculum.view': ['super', 'admin', 'teacher', 'parent'],
|
||||||
'password.own.change': ['super', 'admin', 'teacher'],
|
'password.own.change': ['super', 'admin', 'teacher'],
|
||||||
'schools.manage': ['super'],
|
'schools.manage': ['super'],
|
||||||
'schools.resetPasswords': ['super', 'admin'],
|
'schools.resetPasswords': ['super', 'admin'],
|
||||||
|
|
|
||||||
202
public/js/wiki.js
Normal file
202
public/js/wiki.js
Normal file
|
|
@ -0,0 +1,202 @@
|
||||||
|
/* Curriculumwiki: doorzoekbare volwassenweergave van CURRICULUM_PO_2026. */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
const root=document.getElementById("curriculumWiki");
|
||||||
|
const wikiButton=document.getElementById("btnWiki");
|
||||||
|
let selectedArea="all";
|
||||||
|
let query="";
|
||||||
|
|
||||||
|
const node=(tag,cls,text)=>{
|
||||||
|
const el=document.createElement(tag);
|
||||||
|
if(cls)el.className=cls;
|
||||||
|
if(text!=null)el.textContent=text;
|
||||||
|
return el;
|
||||||
|
};
|
||||||
|
const normal=value=>String(value||"").toLocaleLowerCase("nl-NL");
|
||||||
|
const coverageKey=value=>value==="partial"?"wikiCoveragePartial":value==="support"?"wikiCoverageSupport":"wikiCoverageOpen";
|
||||||
|
const widgetName=id=>{
|
||||||
|
const translated=T("wg_"+id);
|
||||||
|
return translated==="wg_"+id?id:translated;
|
||||||
|
};
|
||||||
|
const goalHaystack=(area,goal)=>normal([
|
||||||
|
area.title,area.description,goal.id,goal.domain,goal.summary,
|
||||||
|
...goal.parts,...goal.widgets, ...goal.widgets.map(widgetName)
|
||||||
|
].join(" "));
|
||||||
|
|
||||||
|
function wikiLink(label,href){
|
||||||
|
const link=node("a","wiki-link",label);
|
||||||
|
link.href=href;
|
||||||
|
link.target="_blank";
|
||||||
|
link.rel="noopener noreferrer";
|
||||||
|
return link;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawGoals(holder,filters){
|
||||||
|
holder.replaceChildren();
|
||||||
|
const needle=normal(query.trim());
|
||||||
|
let shown=0;
|
||||||
|
CURRICULUM_PO_2026.areas.forEach(area=>{
|
||||||
|
if(selectedArea!=="all"&&selectedArea!==area.id)return;
|
||||||
|
const areaHit=needle&&normal(area.title+" "+area.description).includes(needle);
|
||||||
|
const goals=area.goals.filter(goal=>!needle||areaHit||goalHaystack(area,goal).includes(needle));
|
||||||
|
if(!goals.length)return;
|
||||||
|
shown+=goals.length;
|
||||||
|
|
||||||
|
const section=node("section","wiki-area");
|
||||||
|
section.id="wiki-area-"+area.id;
|
||||||
|
const areaHead=node("div","wiki-area-head");
|
||||||
|
const title=node("h3");
|
||||||
|
title.appendChild(node("span","wiki-area-icon",area.icon));
|
||||||
|
title.appendChild(document.createTextNode(area.title));
|
||||||
|
areaHead.appendChild(title);
|
||||||
|
areaHead.appendChild(node("span","wiki-range",T("wikiGoalsRange").replace("{range}",area.goalsRange)));
|
||||||
|
section.appendChild(areaHead);
|
||||||
|
section.appendChild(node("p","wiki-area-intro",area.description));
|
||||||
|
|
||||||
|
const grid=node("div","wiki-grid");
|
||||||
|
goals.forEach(goal=>{
|
||||||
|
const card=node("article","wiki-goal");
|
||||||
|
const head=node("div","wiki-goal-head");
|
||||||
|
head.appendChild(node("span","wiki-goal-id",T("wikiGoal")+" "+goal.id));
|
||||||
|
head.appendChild(node("span","wiki-domain",goal.domain));
|
||||||
|
card.appendChild(head);
|
||||||
|
card.appendChild(node("p","wiki-summary",goal.summary));
|
||||||
|
const partTitle=node("h4",null,T("wikiParts"));
|
||||||
|
card.appendChild(partTitle);
|
||||||
|
const parts=node("ul","wiki-parts");
|
||||||
|
goal.parts.forEach((part,index)=>{
|
||||||
|
const code=String(goal.id)+String.fromCharCode(65+index);
|
||||||
|
const item=node("li");
|
||||||
|
item.appendChild(node("strong",null,code));
|
||||||
|
item.appendChild(document.createTextNode(" · "+part));
|
||||||
|
parts.appendChild(item);
|
||||||
|
});
|
||||||
|
card.appendChild(parts);
|
||||||
|
const coverage=node("div","wiki-coverage-row");
|
||||||
|
coverage.appendChild(node("span","wiki-coverage wiki-coverage-"+goal.coverage,T(coverageKey(goal.coverage))));
|
||||||
|
if(goal.widgets.length){
|
||||||
|
const widgets=node("div","wiki-widgets");
|
||||||
|
widgets.appendChild(node("span","wiki-widgets-label",T("wikiWidgets")+":"));
|
||||||
|
goal.widgets.forEach(id=>widgets.appendChild(node("span","wiki-widget",widgetName(id))));
|
||||||
|
coverage.appendChild(widgets);
|
||||||
|
}
|
||||||
|
card.appendChild(coverage);
|
||||||
|
grid.appendChild(card);
|
||||||
|
});
|
||||||
|
section.appendChild(grid);
|
||||||
|
const pageUrl=CURRICULUM_PO_2026.source+"#page="+area.page;
|
||||||
|
section.appendChild(wikiLink(T("wikiOfficialArea").replace("{area}",area.title),pageUrl));
|
||||||
|
holder.appendChild(section);
|
||||||
|
});
|
||||||
|
if(!shown)holder.appendChild(node("div","wiki-empty",T("wikiNoResults")));
|
||||||
|
filters.querySelectorAll("button").forEach(button=>button.setAttribute("aria-pressed",String(button.dataset.area===selectedArea)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderCurriculumWiki(payload){
|
||||||
|
if(payload&&payload.area)selectedArea=payload.area;
|
||||||
|
root.replaceChildren();
|
||||||
|
const header=node("div","wiki-hero");
|
||||||
|
const heroText=node("div","wiki-hero-text");
|
||||||
|
heroText.appendChild(node("div","wiki-kicker",T("wikiKicker")));
|
||||||
|
heroText.appendChild(node("h2",null,T("wikiTitle")));
|
||||||
|
heroText.appendChild(node("p",null,T("wikiIntro")));
|
||||||
|
header.appendChild(heroText);
|
||||||
|
const facts=node("div","wiki-facts");
|
||||||
|
facts.appendChild(node("strong",null,"9"));
|
||||||
|
facts.appendChild(node("span",null,T("wikiAreas")));
|
||||||
|
facts.appendChild(node("strong",null,"40"));
|
||||||
|
facts.appendChild(node("span",null,T("wikiGoals")));
|
||||||
|
header.appendChild(facts);
|
||||||
|
root.appendChild(header);
|
||||||
|
|
||||||
|
const status=node("aside","wiki-status");
|
||||||
|
status.appendChild(node("strong",null,T("wikiStatusTitle")));
|
||||||
|
status.appendChild(node("span",null,T("wikiStatusText")));
|
||||||
|
status.appendChild(wikiLink(T("wikiOfficialSource"),CURRICULUM_PO_2026.source));
|
||||||
|
root.appendChild(status);
|
||||||
|
|
||||||
|
const tule=node("details","wiki-tule");
|
||||||
|
const tuleSummary=node("summary",null,T("wikiTuleTitle"));
|
||||||
|
tule.appendChild(tuleSummary);
|
||||||
|
tule.appendChild(node("p",null,T("wikiTuleText")));
|
||||||
|
const sourceLinks=node("div","wiki-source-links");
|
||||||
|
sourceLinks.appendChild(wikiLink(T("wikiTuleSource"),CURRICULUM_PO_2026.tule));
|
||||||
|
sourceLinks.appendChild(wikiLink(T("wikiProgressionsSource"),CURRICULUM_PO_2026.progressions));
|
||||||
|
tule.appendChild(sourceLinks);
|
||||||
|
root.appendChild(tule);
|
||||||
|
|
||||||
|
if(LANG==="en")root.appendChild(node("p","wiki-language-note",T("wikiLanguageNote")));
|
||||||
|
|
||||||
|
const searchWrap=node("div","wiki-search");
|
||||||
|
const label=node("label",null,T("wikiSearchLabel"));
|
||||||
|
label.htmlFor="wikiGoalSearch";
|
||||||
|
const input=node("input");
|
||||||
|
input.type="search";
|
||||||
|
input.id="wikiGoalSearch";
|
||||||
|
input.placeholder=T("wikiSearchPlaceholder");
|
||||||
|
input.autocomplete="off";
|
||||||
|
input.value=query;
|
||||||
|
searchWrap.appendChild(label);
|
||||||
|
searchWrap.appendChild(input);
|
||||||
|
root.appendChild(searchWrap);
|
||||||
|
|
||||||
|
const filters=node("div","wiki-filters");
|
||||||
|
filters.setAttribute("aria-label",T("wikiAreaFilter"));
|
||||||
|
const all=node("button",null,T("wikiAllAreas"));
|
||||||
|
all.type="button";
|
||||||
|
all.dataset.area="all";
|
||||||
|
filters.appendChild(all);
|
||||||
|
CURRICULUM_PO_2026.areas.forEach(area=>{
|
||||||
|
const button=node("button",null,area.icon+" "+area.title);
|
||||||
|
button.type="button";
|
||||||
|
button.dataset.area=area.id;
|
||||||
|
filters.appendChild(button);
|
||||||
|
});
|
||||||
|
root.appendChild(filters);
|
||||||
|
|
||||||
|
const holder=node("div","wiki-results");
|
||||||
|
root.appendChild(holder);
|
||||||
|
filters.addEventListener("click",event=>{
|
||||||
|
const button=event.target.closest("button[data-area]");
|
||||||
|
if(!button)return;
|
||||||
|
selectedArea=button.dataset.area;
|
||||||
|
drawGoals(holder,filters);
|
||||||
|
holder.scrollIntoView({block:"nearest"});
|
||||||
|
});
|
||||||
|
input.addEventListener("input",()=>{
|
||||||
|
query=input.value;
|
||||||
|
drawGoals(holder,filters);
|
||||||
|
});
|
||||||
|
drawGoals(holder,filters);
|
||||||
|
|
||||||
|
const disclaimer=node("footer","wiki-disclaimer");
|
||||||
|
disclaimer.appendChild(node("strong",null,T("wikiCarefulTitle")));
|
||||||
|
disclaimer.appendChild(document.createTextNode(" "+T("wikiCarefulText")));
|
||||||
|
root.appendChild(disclaimer);
|
||||||
|
}
|
||||||
|
window.renderCurriculumWiki=renderCurriculumWiki;
|
||||||
|
|
||||||
|
registerSettingsSection({
|
||||||
|
id:"curriculum",icon:"📚",group:"personal",order:3,
|
||||||
|
label:()=>T("wikiTitle"),sub:()=>T("wikiSub"),
|
||||||
|
visible:()=>!!currentUser&&can("curriculum.view"),
|
||||||
|
activate(payload){showSettingsDom("curriculum");renderCurriculumWiki(payload);},
|
||||||
|
keywords:()=>[
|
||||||
|
T("wikiTitle"),T("wikiSub"),"SLO","kerndoelen","curriculum","leerlijnen","TULE",
|
||||||
|
...CURRICULUM_PO_2026.areas.flatMap(area=>[area.title,area.description])
|
||||||
|
],
|
||||||
|
searchItems:needle=>{
|
||||||
|
const q=normal(needle);
|
||||||
|
return CURRICULUM_PO_2026.areas.flatMap(area=>{
|
||||||
|
const areaMatch=normal(area.title+" "+area.description).includes(q);
|
||||||
|
const goalMatch=area.goals.some(goal=>goalHaystack(area,goal).includes(q));
|
||||||
|
if(!areaMatch&&!goalMatch)return[];
|
||||||
|
return[{icon:area.icon,label:area.title,sub:T("wikiGoal")+" "+area.goalsRange,
|
||||||
|
go:()=>goSettingsSection("curriculum",{area:area.id})}];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
wikiButton.addEventListener("click",()=>openSettings("curriculum"));
|
||||||
|
})();
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
// teach: centrale permissiematrix voor de 4 vaste rollen (super/admin/teacher/pupil).
|
// teach: centrale permissiematrix voor de vaste rollen, inclusief ouder.
|
||||||
// Gespiegeld in public/js/permissions.js (geen bundler in dit project, dus twee
|
// Gespiegeld in public/js/permissions.js (geen bundler in dit project, dus twee
|
||||||
// bestanden die dezelfde waarden hand-in-hand bijhouden). Dit is geen database-
|
// bestanden die dezelfde waarden hand-in-hand bijhouden). Dit is geen database-
|
||||||
// gestuurd/rechten-per-gebruiker-configureerbaar systeem, maar één benoemde bron
|
// gestuurd/rechten-per-gebruiker-configureerbaar systeem, maar één benoemde bron
|
||||||
// i.p.v. dezelfde rol-arrays los verspreid over de routes.
|
// i.p.v. dezelfde rol-arrays los verspreid over de routes.
|
||||||
export const PERMISSIONS = {
|
export const PERMISSIONS = {
|
||||||
'admin.access': ['super', 'admin', 'teacher'],
|
'admin.access': ['super', 'admin', 'teacher'],
|
||||||
|
'curriculum.view': ['super', 'admin', 'teacher', 'parent'],
|
||||||
'password.own.change': ['super', 'admin', 'teacher'],
|
'password.own.change': ['super', 'admin', 'teacher'],
|
||||||
'schools.manage': ['super'],
|
'schools.manage': ['super'],
|
||||||
'schools.resetPasswords': ['super', 'admin'],
|
'schools.resetPasswords': ['super', 'admin'],
|
||||||
|
|
|
||||||
65
test/curriculum-wiki.test.js
Normal file
65
test/curriculum-wiki.test.js
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import test from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import {readFile} from 'node:fs/promises';
|
||||||
|
import {runInNewContext} from 'node:vm';
|
||||||
|
import {PERMISSIONS} from '../src/permissions.js';
|
||||||
|
|
||||||
|
test('curriculumdataset bevat negen leergebieden en alle veertig kerndoelen',async()=>{
|
||||||
|
const source=await readFile('public/js/curriculum.js','utf8');
|
||||||
|
const context={};
|
||||||
|
runInNewContext(source+';globalThis.__curriculum=CURRICULUM_PO_2026',context);
|
||||||
|
const data=JSON.parse(JSON.stringify(context.__curriculum));
|
||||||
|
assert.equal(data.id,'slo-po-2026');
|
||||||
|
assert.equal(data.edition,'2026-07');
|
||||||
|
assert.match(data.tule,/slo\.nl/);
|
||||||
|
assert.match(data.progressions,/slo\.nl/);
|
||||||
|
assert.equal(data.areas.length,9);
|
||||||
|
const goals=data.areas.flatMap(area=>area.goals);
|
||||||
|
assert.deepEqual(goals.map(goal=>goal.id),Array.from({length:40},(_,index)=>index+1));
|
||||||
|
for(const area of data.areas){
|
||||||
|
assert.ok(area.title&&area.description&&area.page,area.id);
|
||||||
|
for(const goal of area.goals){
|
||||||
|
assert.ok(goal.domain&&goal.summary,goal.id);
|
||||||
|
assert.ok(goal.parts.length>=1,goal.id);
|
||||||
|
assert.ok(['partial','support','open'].includes(goal.coverage),goal.id);
|
||||||
|
assert.ok(Array.isArray(goal.widgets),goal.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('curriculumwiki is alleen zichtbaar voor alle volwassen rollen',async()=>{
|
||||||
|
assert.deepEqual(PERMISSIONS['curriculum.view'],['super','admin','teacher','parent']);
|
||||||
|
assert.ok(!PERMISSIONS['curriculum.view'].includes('pupil'));
|
||||||
|
const [client,wiki]=await Promise.all([
|
||||||
|
readFile('public/js/permissions.js','utf8'),
|
||||||
|
readFile('public/js/wiki.js','utf8')
|
||||||
|
]);
|
||||||
|
assert.match(client,/'curriculum\.view':\s*\['super', 'admin', 'teacher', 'parent'\]/);
|
||||||
|
assert.match(wiki,/visible:\(\)=>!!currentUser&&can\("curriculum\.view"\)/);
|
||||||
|
assert.match(wiki,/wikiButton\.addEventListener\("click",\(\)=>openSettings\("curriculum"\)\)/);
|
||||||
|
assert.doesNotMatch(wiki,/innerHTML\s*=/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('curriculumwiki is geladen, doorzoekbaar, responsief en tweetalig bediend',async()=>{
|
||||||
|
const [html,wiki,core,css,version]=await Promise.all([
|
||||||
|
readFile('public/index.html','utf8'),
|
||||||
|
readFile('public/js/wiki.js','utf8'),
|
||||||
|
readFile('public/js/core.js','utf8'),
|
||||||
|
readFile('public/css/teach.css','utf8'),
|
||||||
|
readFile('VERSION','utf8')
|
||||||
|
]);
|
||||||
|
assert.ok(html.indexOf('js/settings.js')<html.indexOf('js/curriculum.js'));
|
||||||
|
assert.ok(html.indexOf('js/curriculum.js')<html.indexOf('js/wiki.js'));
|
||||||
|
assert.ok(html.indexOf('js/wiki.js')<html.indexOf('js/app.js'));
|
||||||
|
assert.match(html,/id="btnWiki"[^>]*display:none/);
|
||||||
|
assert.match(html,/data-section="curriculum"/);
|
||||||
|
assert.match(wiki,/input\.type="search"/);
|
||||||
|
assert.match(wiki,/dataset\.area/);
|
||||||
|
for(const key of ['wikiTitle','wikiSub','wikiSearchPlaceholder','wikiCoveragePartial','wikiCoverageSupport','wikiCoverageOpen','wikiCarefulText']){
|
||||||
|
assert.equal((core.match(new RegExp(key+':','g'))||[]).length,2,key);
|
||||||
|
}
|
||||||
|
assert.match(css,/\.wiki-grid\{[^}]*grid-template-columns:repeat\(2/);
|
||||||
|
assert.match(css,/@media \(max-width:720px\)[\s\S]*?\.wiki-grid\{grid-template-columns:1fr\}/);
|
||||||
|
assert.ok(core.includes('document.getElementById("btnWiki").style.display = can("curriculum.view")'));
|
||||||
|
assert.ok(core.includes('const VERSION = "'+version.trim()+'"'));
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue