- Grootste openstaande roadmap-punt. Nieuwe tabel pupil_levels
(db/019_pupil_levels.sql, zelfde class_id/pupil_id-vorm als
assignments) legt per vakgebied (taal/rekenen/world) een niveaugroep
vast (groep 1-8, gebaseerd op de leerjaren van de basisschool waaraan
de leerdoelen gekoppeld zijn) + niveau (1-3, fijnmazige verfijning
BINNEN die groep). Klas-standaard met individuele leerling-overrides,
net zo laagdrempelig instelbaar als de standaard - het speciaal-
onderwijs-geval waar dat vanaf het begin de norm is.
- Server: GET/PUT/DELETE /levels/class/:id en /levels/pupil/:id
(src/api.js) hergebruiken exact het assignments-patroon (upsert via
partial-unique-index, pupil-eerst-dan-klas-resolutie), plus
GET /my/level voor de leerling-kant. Zelfde permissie
(assignments.manage) en sameSchool/teacherOnly/pupilAccessible-
guards als de bestaande toewijzingsroutes.
- Nieuwe admin-tab "Klassenmanagement": klas + vakgebied kiezen, een
klas-standaard instellen, en per leerling de effectieve groep/niveau
zien (overerft van de klas tenzij een eigen afwijking is ingesteld)
met bewerk- en terug-naar-standaard-knoppen.
- ECHTE inhoudskoppeling, niet alleen een standaardwaarde: rekenen is
het eerste vakgebied waarvan de SLO/TULE-leerlijn direct in
getalgrenzen te vertalen is (groep 3 t/m 20, groep 4 t/m 100, groep 5
t/m 1000, groep 6 t/m 10.000). Nieuwe SLO_MATH_RANGES/mathRangeFor()
in data.js; math.js's gen() gebruikt dit voor de daadwerkelijke
getallen zodra een leerling een groep heeft (S.yearGroup), met het
bestaande niveau 1-3 als verfijning daarbinnen. Zonder groep blijft
het oude vaste 10/20/100-gedrag exact bestaan.
- pupil.js geeft groep/niveau door aan taal/rekenen-widgets bij het
mounten: niveau is een standaardwaarde (een al opgeslagen bord-niveau
blijft leidend), groep wordt voor rekenen-widgets altijd meegegeven
(er is geen "oud" groep-concept om te respecteren). Terzijde ook de
stille .catch() op de nieuwe async renderPupilWidgets() opgelost.
- Taal/wereldoriëntatie krijgen deze ronde nog geen eigen
inhoudskoppeling (vraagt een eigen contentbron per groep, nog niet
uitgezocht) - eerlijk benoemd als vervolgstap, geen loze belofte.
- Tests: test/pupil-levels.test.js (10 tests) dekt de server-routes
(mocked-pool-patroon) én - via dezelfde vm-techniek als elders in
test/widgets.test.js voor MONEY_LEVELS - een ECHTE (niet regex-)test
van mathRangeFor die bevestigt dat elke groep de juiste getalgrens
oplevert en niveau nooit boven de groep-grens uitkomt. Twee bugs
gevonden en gefixt tijdens het schrijven van deze tests: null-yearGroup
werd per ongeluk als groep 1 behandeld i.p.v. "geen groep", en de
testrol had geen klas-eigenaarschap waardoor 403 in plaats van 200
terugkwam. Volledige testsuite: 128/128.
- Live geverifieerd in de sandbox: klas-standaard + leerling-override
instellen in de nieuwe admin-tab, en drie scenario's in de
leerling-omgeving die bevestigen dat de daadwerkelijk gegenereerde
sommen binnen de juiste groep-grens blijven (geen klassenmanagement →
ongewijzigd oud gedrag; groep zonder eigen bord-niveau → volledige
groep-grens; groep mét een al opgeslagen bord-niveau → dat niveau
blijft leidend, geschaald naar de juiste groep-grens).
- public/js/data.js bevatte al niet-gerelateerde, nog niet gecommitte
wijzigingen van vóór deze sessie (extra taalthema's/woorden) - alleen
de SLO_MATH_RANGES/mathRangeFor-hunk is hier meegenomen (git add -p),
de rest blijft bewust ongemoeid staan, net als test/language-library.test.js.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
- De cijferblokken (oefenklok, geldwidget) hergebruikten de braille a-j-
patronen, die nooit de onderste rij (posities 3/6) gebruiken - daardoor
leken alle tien blokken op elkaar. Eigen DIGIT_DOTCODE-tabel die alle
zes stippen inzet, geeft elk cijfer nu een echt eigen vorm.
- Plaatswaardetabel-widget had geen enkele oefenvorm (alleen een vrij
invoerveld); nieuwe "Bouwen"-stand met drie niveaus (T/E, H/T/E, of
HD/TD/D/H/T/E) laat kinderen een getal opbouwen door cijferblokken in
de juiste kolom te slepen, met score/reeks en directe feedback - zelfde
bouwsteen als de oefenklok/het geldwidget. De vrije verkenstand blijft
ongewijzigd bestaan.
- Bijvangst: instellingenvelden met .hidden (bv. "24-uursklok" bij de
oefenklok) werden nooit echt verborgen door een CSS-specificiteitsbotsing
(.edu-controls label versloeg [hidden]); toegevoegde regel herstelt dit
voor alle leerwidgets.
- Nieuwe ankerbibliotheek ANCHOR_LIB, opgeslagen in een eigen data-sectie
"anchors" (server-whitelist + 5MB-cap; lijst i.p.v. object toegestaan);
reist mee in de volledige save, hydrate, gastreset en reservekopie-herstel
- Ankerwidget: 💾 bewaart het huidige diagram (naam = middelwoord, met
✓-feedback; leeg diagram wordt geweigerd met een hint), 🗂 opent dezelfde
Windows-stijl mappenverkenner met de bewaarde diagrammen (geneste mappen,
slepen, hernoemen, verwijderen)
- Dubbelklik op een bewaard diagram laadt het in het widget (cellen +
plaatjes, thema-spiegel en bord-autosave volgen automatisch)
- Nieuwe servertest: anchors-sectie accepteert een lijst, andere secties
weigeren die (en andersom)
- Thema's krijgen een optioneel folder-veld; de theme-dropdown in de
woorden-editor en de kaart-dropdowns in de widgets (fillWordSourceSel)
groeperen per map met optgroups, mapl0ze thema's bovenaan
- Nieuw 📁-knopje in de editor zet het gekozen thema in een map (leeg veld
haalt het eruit); zichtbaarheid volgt de themakeuze
- Ankerthema's komen automatisch in de vaste map "⚓" (id-koppeling met de
ankerwidget blijft onaangeroerd)
- Nieuwe i18n-teksten themeFolder/themeFolderPh (nl/en)
- Ankerwoorden-widget: elk woord krijgt nu automatisch een passend plaatje via de ARASAAC-pictogrammendienst, in plaats van steeds hetzelfde ankersymbool
- Plaatjes worden per woord gecachet en bewaard in de bordstatus, zodat ze niet steeds opnieuw opgehaald worden
- Bij geen resultaat of een netwerkfout valt de widget terug op het ankersymbool
- ARASAAC-opzoekfunctie hergebruikt vanuit de bestaande woordkaart-editor (geen dubbele code meer)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>