Commit graph

192 commits

Author SHA1 Message Date
Ramon
50b9f32e91 feat: voeg voortgangdetail toe aan de taalwidgets (v0.4.63-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 1m56s
Gewijzigde functionaliteiten:

- Letterblokken logt per afgerond woord hoeveel pogingen het kostte en welke
  letter op welke plek fout ging (beide invoerwijzen: blokken slepen en typen).

- Ballonnenwoord logt nu ook een verloren woord; dat leverde tot nu toe geen
  enkele voortgang op. Een gewonnen woord telt de fout geraden letters mee.

- Flitswoorden stuurt de bestaande zelfcheck ("Gelukt" / "Nog oefenen") door
  naar de voortgang; die uitkomst werd eerder weggegooid.

- Hakken & plakken krijgt dezelfde zelfcheck en daarmee als laatste taalwidget
  een goed/fout-moment; de oefening zelf verandert niet.

- Voortgang-tab toont de fouten van taal: naast fout afgesloten opdrachten ook
  "na N pogingen goed" (letterblokken kent geen faalmoment) en zelfchecks.

- Reeksstappen met flitswoorden of hakken & plakken wachten nu op een echt
  afgerond moment in plaats van meteen te completeren.

Tests: 192 geslaagd (7 nieuw), plus live nagelopen in de leerlingweergave met
echte interactie per widget en een screenshot van de Voortgang-tab.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126hjBPD2ggZ2LEoDYZSW7S
2026-07-26 03:16:28 +02:00
Ramon
043831e1dd feat: make assignments independent from boards
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 1m28s
2026-07-25 22:15:52 +02:00
Ramon
05f3dbe56c feat: simplify assignment sequence sharing
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 55s
2026-07-24 23:08:44 +02:00
Ramon
b8e9e46819 fix: patch Fastify static security vulnerabilities
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 1m20s
2026-07-24 22:46:22 +02:00
Ramon
a7180d8390 feat: combine assignments and class management
Some checks failed
dev - build & deploy naar test / build-and-deploy (push) Failing after 56s
2026-07-24 22:36:15 +02:00
Ramon
131ffc2f38 feat: breid live klasinteractie grafisch uit
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 47s
2026-07-24 13:40:18 +02:00
Ramon
90851f960a feat: voeg live leersessies toe aan klasdashboard
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 1m38s
2026-07-24 10:53:42 +02:00
Ramon
b2f6a69897 fix: maak leerkrachtfuncties zichtbaar in voorbeeldmodus
Some checks failed
dev - build & deploy naar test / build-and-deploy (push) Failing after 1m10s
2026-07-23 23:15:08 +02:00
Ramon
ae8d0d4bb4 feat: voeg leerpaden dashboard en avatarbeloningen toe
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 1m23s
2026-07-23 21:23:54 +02:00
Ramon
9dc896964d fix: maak mobiele opdrachten volledig zichtbaar
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 1m11s
2026-07-23 19:05:53 +02:00
Ramon
ff6dbc5c2e fix: voorkom witte onderstrook op mobiel
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 53s
2026-07-23 17:34:47 +02:00
Ramon
c33eb71640 fix: centreer letter- en cijferdoelvakken
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 1m4s
Gewijzigde functionaliteiten:

- Centreert doelvakken in letter-, reken-, klok-, geld-, plaatswaarde- en wereldwidgets.

- Houdt lange woordrijen scrollbaar zonder de eerste vakken af te snijden.

- Centreert kleinere geplaatste cijferblokken binnen hun doelvak.

- Verwijdert de links uitgelijnde compacte woordrij.

Tests: 160 geslaagd.
2026-07-23 17:06:26 +02:00
Ramon
34c4d6e1a4 feat: laat avatars dansen na afgeronde opdrachten
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 42s
Gewijzigde functionaliteiten:

- Voegt vijf afwisselende avatardansen toe.

- Houdt lichaam, haar en accessoires als een verbonden SVG-figuur.

- Danst na afgeronde woordgroepen, flitsrondes, galgjerondes en rekenrondes.

- Respecteert verminderde beweging met een stil feestbeeld.

Tests: 159 geslaagd; npm audit: 0 kwetsbaarheden.
2026-07-23 07:07:30 +02:00
Ramon
4544306ccd feat: voeg avatarsoortvarianten toe (v0.4.50-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 1m8s
2026-07-22 23:40:36 +02:00
Ramon
2b40488208 fix: patch fast-uri kwetsbaarheid (v0.4.49-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 1m15s
2026-07-22 23:21:23 +02:00
Ramon
6832043470 feat: maak avatarhaar natuurlijker (v0.4.48-beta)
Some checks failed
dev - build & deploy naar test / build-and-deploy (push) Failing after 43s
2026-07-22 23:13:20 +02:00
Ramon
7237cb6290 feat: breid compacte avatars uit (v0.4.47-beta)
Some checks failed
dev - build & deploy naar test / build-and-deploy (push) Failing after 35s
2026-07-22 19:59:15 +02:00
Ramon
d0522d7e33 feat: verbeter anatomie en accessoires van avatars (v0.4.46-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 58s
2026-07-21 23:25:12 +02:00
Ramon
a922caaa56 fix: behoud groep 1 beeldankers voor letters
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 44s
2026-07-21 22:29:24 +02:00
Ramon
dc3c591c76 feat: stem taalwidgets af op beginnende geletterdheid
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 1m5s
2026-07-21 22:27:44 +02:00
Ramon
6f593e05b2 fix: corrigeer woordplaatjes in taalbibliotheek (v0.4.44-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 1m1s
2026-07-21 22:01:41 +02:00
Ramon
ba4483d5d3 feat: breid taalbibliotheek uit met 1000 woorden (v0.4.43-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 59s
2026-07-21 18:50:47 +02:00
Ramon
17ee47dd09 fix: maak bordniveaus instelbaar (v0.4.42-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 42s
2026-07-21 07:58:56 +02:00
Ramon
fabe721991 feat: voeg leerjaarprofielen toe aan taal en rekenen (v0.4.41-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 39s
2026-07-20 22:45:01 +02:00
Ramon
629329894d feat: laat systeemmanager leerlingwidgets toevoegen (v0.4.40-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 53s
2026-07-20 16:06:33 +02:00
Ramon
b81f5ac5dd feat: voeg rolvoorbeeld voor systeemmanager toe (v0.4.39-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 56s
2026-07-20 14:08:37 +02:00
Ramon
9bab7f39c0 feat: koppel widgets aan kerndoelen (v0.4.38-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 39s
2026-07-19 21:46:38 +02:00
Ramon
322147d3d1 feat: voeg curriculumwiki voor volwassenen toe (v0.4.37-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 1m1s
2026-07-19 21:13:11 +02:00
Ramon
216ab8d5dd docs: leg SLO-kerndoelen 2026 vast (v0.4.36-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 39s
2026-07-19 20:58:17 +02:00
Ramon
8ccfa5dc8c feat: voortgang flinke UI-upgrade + bug (1 opdracht i.p.v. groep) + grafiek (v0.4.35-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 1m17s
- Bug gevonden en bevestigd (niet blind aangenomen): math.js riep
  onProgress maar op ÉÉN plek aan, in endRound(), NA een heel blok van
  10 sommen, met alleen het geaggregeerde totaal. Eén sessie van 10
  sommen - "een groep opdrachten" - leverde dus precies 1 rij op in
  progress_events, niet 10, en maakte "welke som ging fout" onmogelijk
  te zien (alleen een totaaltelling per blok).
- Fix: onProgress verplaatst naar check(), bij elke afgeronde som (goed
  op de eerste keer, of opgeven na 3 pogingen) - een sessie van 10
  sommen levert nu 10 progress_events-rijen op, elk met een nieuw
  optioneel detail-veld (welke som, gegeven antwoord, juist antwoord).
  letters.js/ball.js blijven deze ronde ongemoeid (hun granulariteit
  veroorzaakte niet deze bug).
- Nieuwe kolom progress_events.detail (JSONB, nullable, puur additief -
  db/020_progress_detail.sql). POST /my/progress accepteert het
  optioneel; te groot/geen plain object wordt stilzwijgend genegeerd
  zonder de rest van de log-poging te laten falen (detail is decoratief,
  geen kernfunctionaliteit).
- Voortgang-tab (admin.js) herontworpen, zelfde ontwerptaal als de
  eerdere School-navigator-herbouw: vakgebied-tabs, een
  klassenmanagement-infobadge (Groep {n} · Niveau {n}, opgehaald via de
  bestaande /levels/pupil/:id-route) zodat resultaten meteen tegen het
  toegewezen niveau afgezet kunnen worden, een met de hand getekende
  SVG-nauwkeurigheidsring + staafdiagram (goed/fout per dag, laatste 14
  dagen - geen library, zelfde aanpak als avatar.js) en een "recente
  fouten"-lijst die precies toont welke som fout ging. De bestaande
  ruwe tijdlijn blijft eronder staan, niets is verwijderd.
- Terzijde gevonden en gefixt: .am-btn.active bestond nergens in de
  CSS, waardoor de vakgebied-tabs (hier én in Klassenmanagement,
  v0.4.34-beta) geen enkele visuele aanduiding hadden welke actief was.
- Tests: test/progress-logging.test.js uitgebreid met 4 tests voor het
  detail-veld (opgeslagen, te groot genegeerd, geen plain object
  genegeerd, afwezig blijft null). Volledige testsuite: 132/132.
- Live geverifieerd: een math.js-sessie met een mix van goede/foute
  sommen doorlopen en bevestigd dat er 3 aparte POST /my/progress-
  aanroepen gebeuren voor 3 sommen (niet 1 aggregaat), elk met het
  juiste detail; de herbouwde Voortgang-tab geopend met gemockte data
  en met screenshots bevestigd dat de nauwkeurigheidsring (38% = 3/8,
  rekenkundig geverifieerd), het klassenmanagement-infobadge, de
  staafdiagram en de "recente fouten"-lijst (met de daadwerkelijke som)
  correct tonen, én dat de active-tab-fix ook op de Klassenmanagement-
  tab zichtbaar is.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
2026-07-19 15:56:14 +02:00
Ramon
e7bb76b76d feat: klassenmanagement - niveaugroep + niveau per leerling per vakgebied (v0.4.34-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 1m9s
- 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
2026-07-19 15:31:26 +02:00
Ramon
36695a06f6 fix: leerling in werken-modus kreeg abusievelijk readonly:true (v0.4.33-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 46s
- Roadmap-punt "de voortgangen werken niet" onderzocht (niet blind
  herbouwd - eerst gediagnosticeerd via de code en git-geschiedenis).
- Gevonden: pupil.js zette sinds v0.3.15-beta (toen kijken/werken-modus
  werd toegevoegd) readonly:true in BEIDE takken van de modus-keuze -
  een kopieerfout, want de hele reden voor die commit was net dat
  "werken" de leerling wél moet laten werken. In "werken"-modus hoort
  er geen readonly-vlag te staan; "kijken" (blijft ongewijzigd) is de
  enige modus die dat nodig heeft.
- Concreet effect: de twee widgets die opts.readonly daadwerkelijk
  lezen (letters.js/anchor.js) verborgen voor leerlingen in
  werken-modus onterecht hun eigen bewerk-gereedschap (woordenlijst-
  editor, opslaan/bibliotheek) - gereedschap dat een leerling in
  werken-modus wél hoort te zien. De kernoefening zelf (spellen,
  slepen) werd hierdoor niet geblokkeerd, dus dit verklaart niet de
  volledige klacht - overige widgets negeren opts.readonly toch al.
- Tegelijk de stille `.catch(()=>{})` op het voortgang-loggen vervangen
  door `console.warn` bij een mislukte POST /my/progress: een mislukte
  log (verlopen sessie, weggehaalde toewijzing, servergolf) verdween
  eerder spoorloos, wat een "werkt niet"-klacht onmogelijk te
  onderzoeken maakte.
- Nieuwe test/progress-logging.test.js dekt het serverpad dat nog
  helemaal geen tests had: leerling-eigen toewijzing wint van
  klasbrede, thuiskind-zonder-school valt terug op de gekoppelde
  ouder, geen-toewijzing geeft 400 zonder insert, niet-leerling geeft
  403, ongeldige widgetId/widgetType geeft 400 - alle zes bevestigen
  dat de SQL/toewijzingslogica zelf klopt, wat het probleem verder
  vernauwt tot de nu twee gefixte punten hierboven of iets dat alleen
  in de echte omgeving van de gebruiker reproduceerbaar is.
- Live geverifieerd: werken-modus toont nu het bewerk-gereedschap in
  letters.js (voorheen verborgen), kijken-modus blijft exact
  ongewijzigd (overlay + verborgen gereedschap). Volledige testsuite:
  118/118.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
2026-07-19 14:39:00 +02:00
Ramon
643a00c1c3 feat: personage-aanmoediging uitgebreid naar klok, geld, plaatswaarde en spellen (v0.4.32-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 55s
- De math.js-pilot (v0.4.23-beta: eigen personage kort naast "Goed!" bij
  een juist antwoord) hergebruikt in vier andere widgets, zoals destijds
  al voorzien in de roadmap: klok (mountClock), geld (mountMoney),
  plaatswaarde (mountPlacevalue) en de wereldoriëntatie-spelopdracht
  (mountWorldQuiz - dekt bomen/vogels/planten in één keer - en
  mountTopography).
- Zelfde patroon overal: bij S.feedback==='good' (of S.spellSolved) en
  een ingesteld personage verschijnt renderAvatarFace(...,"md") met de
  av-cheer-animatie naast het bestaande feedbackbericht - puur visueel,
  geen nieuw feedbacktype. Zonder personage blijft het gedrag exact
  ongewijzigd.
- Geverifieerd met echte oplos-interacties (niet alleen mounten): klok
  (typemodus + toetsenbord), geld (typemodus + toetsenbord), plaatswaarde
  (blokken plaatsen via toetsenbord-Enter) en de spelopdracht (letters
  plaatsen via toetsenbord-Enter) - in alle vier verschijnt het
  personage daadwerkelijk in de feedback-HTML na een echt juist
  antwoord. Volledige testsuite: 112/112.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
2026-07-19 14:03:58 +02:00
Ramon
c183c8f931 perf: opstart-fetches /auth/me en /me/data parallel i.p.v. serieel (v0.4.31-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 37s
- Beide verzoeken hangen alleen af van dezelfde sessie-cookie, niet van
  elkaars antwoord - ze werden tot nu toe na elkaar aangeroepen
  (hydrateFromServer() startte pas na een voltooide /auth/me), wat een
  hele round-trip extra kost bij elke opstart voor staf/thuisgebruikers.
- app.js vraagt ze nu tegelijk op via Promise.allSettled; hydrateFromServer()
  krijgt het /me/data-resultaat optioneel meegegeven i.p.v. het zelf
  (opnieuw) op te halen. Voor een leerling/ouder wordt dat resultaat
  genegeerd (zij krijgen sowieso geen whiteboard-data) - nooit verspild
  werk, soms ongebruikt, wat de server niet meer kost dan de bestaande
  losse aanroep die er toch al was.
- De twee andere hydrateFromServer()-aanroepen (na inloggen, na een
  save-conflict) geven bewust niets mee en halen gewoon vers op, zoals
  voorheen - alleen de allereerste opstart kan dit parallelliseren.
- Geverifieerd met de volledige testsuite (112/112) en drie losse
  boot-scenario's in de sandbox (gast/staf/leerling): in alle drie komen
  /auth/me en /me/data binnen ~1ms van elkaar aan (voorheen serieel), en
  blijft het bestaande gedrag (gast blijft gast, staf krijgt het bord,
  leerling krijgt de afgeschermde weergave) exact hetzelfde.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
2026-07-19 13:43:32 +02:00
Ramon
4e4aeb4b6d feat: exporteren van vers-gegenereerde wachtwoorden als CSV (v0.4.30-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 38s
- "Exporteer als CSV"-knop toegevoegd aan het bestaande multireset-
  resultatenscherm (klas-wachtwoorden resetten in de School-navigator),
  naast de bestaande "kopieer alles"-knop.
- BELANGRIJKE BEPERKING, bewust zo: dit exporteert alleen wat op dit
  scherm al staat (net-gegenereerde wachtwoorden). Wachtwoorden worden
  na het zetten nergens leesbaar bewaard (alleen gehashet in de
  database) - er is dus geen "haal wachtwoorden van bestaande accounts
  op"-mogelijkheid en die komt er ook niet.
- CSV bevat gebruikersnaam (het echte inlogveld) naast de naam, ook al
  toont het scherm zelf alleen de naam - zonder gebruikersnaam is een
  uitdeelvel niet bruikbaar om mee in te loggen.
- Hergebruikt de csvValue()/downloadCsv()-helpers uit de vorige
  resultaten-exportfeature (v0.4.29-beta) - eerste echte tweede
  gebruiker, dus die keuze om ze lokaal in admin.js te houden i.p.v.
  te verplaatsen was terecht.
- Geverifieerd met de volledige testsuite (112/112) en een live
  browserdoorloop: klas-wachtwoorden resetten (dubbele bevestigingsklik)
  → CSV-download met correcte escaping van een leerlingnaam met komma
  én aanhalingsteken.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
2026-07-19 13:31:10 +02:00
Ramon
8fe6fc10cc feat: exporteren van resultaten per klas (CSV) (v0.4.29-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 51s
- Nieuwe sectie "Exporteren per klas" in de bestaande Voortgang-tab: klas
  kiezen + "Exporteer als CSV" downloadt de volledige activiteitenlijst
  van die klas (leerling, widget, pogingen, goed, sterren, datum/tijd).
- Bouwt voort op de bestaande route GET /progress/class/:id, die tot nu
  toe geen enkele frontend-consument had.
- Server: nieuwe optionele ?limit=-queryparameter (begrensd op 5000) -
  de export vraagt expliciet meer dan de standaard activiteitenweergave
  (200) op, zodat een drukke klas niet stilzwijgend wordt afgekapt in het
  exportbestand. Zonder de parameter blijft het bestaande gedrag (200)
  ongewijzigd.
- Eerste CSV-export in deze codebase: kleine lokale csvValue()/
  downloadCsv()-helpers in admin.js (komma/quote/newline-escaping,
  BOM zodat Excel-NL accenten en komma-scheiding herkent) - bewust nog
  niet gedeeld/verplaatst naar core.js, want er is nu nog maar één
  gebruiker (de geplande wachtwoorden-export kan dit later hergebruiken).
- Tests: test/progress-export.test.js (limit wordt doorgegeven, te hoge
  waarde wordt afgekapt op 5000, standaardgedrag zonder parameter blijft
  200) + volledige testsuite (112/112). Ook live geverifieerd: klas
  kiezen zonder selectie toont een hint i.p.v. te crashen, een echte
  CSV-download met correcte escaping van komma's/aanhalingstekens in
  een leerlingnaam, en de vertaalde widget-namen in de kolommen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
2026-07-19 12:54:48 +02:00
Ramon
09a1326c7e feat: meer personage-accessoires incl. zonnebril (v0.4.28-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 1m10s
- Nieuwe hoofdaccessoires: zonnebril, koptelefoon, feesthoedje.
- Nieuwe lichaamsaccessoires: ketting, rugtas (bandjes over de
  schouders, zichtbaar van voren).
- Server-validatie (src/api.js) en vertalingen (nl+en) meegewerkt met
  dezelfde nieuwe waarden - anders zou de client een optie tonen die de
  server als "ongeldig hoofdaccessoire" afwijst.
- Geen wijziging aan de bestaande pet/kroon-onderdrukkingsregel nodig:
  zonnebril/koptelefoon/feesthoedje bedekken het bovenhoofd niet volledig,
  dus haar blijft er gewoon zichtbaar naast/onder.
- Geverifieerd: alle select-opties komen aan in de instellingen-editor,
  een PUT met de nieuwe waarden slaagt (bestaande server-validatie
  accepteert ze nu), en alle soort/accessoire-combinaties oogden goed in
  screenshot (zonnebril op de dino, koptelefoon op het monster,
  feesthoedje + ketting samen, rugtas op het dier).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
2026-07-19 11:55:25 +02:00
Ramon
817f12b70d fix: dino-rugstekels en beer-staart oogden vreemd (v0.4.27-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 35s
- Dino: rugstekels stonden als een rij spitse vormen voor de hals/borst
  ("Ga als designer kijken" - gebruiker vond dit "wat vreemd"), wat als
  een rare kraag oogde. Verplaatst naar een klein randje bovenop de kop
  (zoals kleine hoorntjes) - dichter bij de anatomische plek (rug/kruin)
  en rustiger op klein formaat.
- Dier (beer): had een los staartje opzij op dezelfde hoogte en met
  vrijwel dezelfde vorm/grootte als de armen/benen, waardoor het als een
  extra ledemaat oogde. Verwijderd - een beer laat van de voorkant
  sowieso nauwelijks iets van zijn staart zien, dus dit is ook
  anatomisch kloppender dan een zichtbaar zij-staartje.
- Overwogen: SVG vs. PNG-lagen als basis. SVG blijft de juiste keuze
  hier - lichaams-/haarkleur zijn vrije kleurkiezers (elke hex-waarde),
  wat via fill-attributen triviaal is maar bij PNG een vast palet of
  canvas-hertintplugin zou vereisen; SVG schaalt bovendien scherp op
  alle drie weergavegroottes zonder aparte assets.
- Geverifieerd: alle vijf bestaande scenario's, volledige testsuite,
  en een School-navigator met een echte klas (leerling met dier-
  personage toont nu geen vreemd zij-staartje meer).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
2026-07-19 11:44:55 +02:00
Ramon
46696b0d7d refactor: personage-tekening volledig herbouwd als SVG (v0.4.26-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 1m40s
- Kernprobleem: de CSS-laagjesversie (losse divs met percentage-posities
  en z-index) had onderdelen die los van het lichaam hingen en een
  tekenvolgorde die niet klopte (soort-specifieke delen soms achter
  hoofd/lijf verstopt, soms er los voor zwevend). SVG lost dit
  structureel op: tekenvolgorde = laagvolgorde, geen z-index nodig, en
  elk onderdeel is een gebogen path/ellips die het silhouet van het
  vorige onderdeel ruim overlapt in plaats van ernaast te zweven.
- Onderzocht hoe gangbare avatar-generators (DiceBear, Avataaars) dit
  aanpakken: vaste 100x100 viewBox, onderdelen als SVG-groepen in de
  juiste volgorde, kleur via directe fill-waarden i.p.v. CSS-variabelen
  in SVG. Dat patroon hier toegepast, geen code overgenomen.
- Elke soort (dino/monster/dier) is nu een samenhangend lichaam: poten,
  armen, staart/oren, buikvlek en gezicht overlappen allemaal duidelijk
  met het hoofd/lijf. Dino: rugstekels, snuit met neusgaten, glimlach.
  Monster: hoorntjes, brede mond met slagtandjes, asymmetrische ogen.
  Dier: ronde oren, beer-snuit, kleine staart.
- Haar (spits/krullen/paardenstaart/mohawk) en accessoires (pet/kroon/
  bril/strik/sjaal/cape/badge) opnieuw getekend zodat ze ook echt op
  hun naam lijken, i.p.v. simpele gekleurde blokjes.
- Regel toegevoegd: een pet of kroon dekt het hele bovenhoofd af, dus
  onderliggend haar wordt dan niet getekend (gaf anders rommelige,
  half verstopte plukjes rond de rand) - een paardenstaart blijft wel
  zichtbaar, want die hangt naar opzij.
- Kleuren (lichter voor buik/snuit, donkerder voor poten/staart/
  rugstekels) worden nu berekend uit de gekozen lichaamskleur zelf
  (avatarShade-helper), i.p.v. via CSS filter:brightness().
- CSS: alle oude .av-* laag-regels (~65 regels) verwijderd; alleen
  afmetingen (sm/md/lg) en de bestaande instellingen/widget/
  aanmoedigings-CSS blijven staan.
- Geverifieerd: alle vijf bestaande scenario's (opslaan, widget,
  School-navigator-badge met een echte klas vol verschillende
  personages, math.js-aanmoediging, ongeldige input), plus uitgebreide
  screenshot-rondes van losse onderdelen, alle soort/haar/accessoire-
  combinaties en alle drie weergavegroottes (24/44/180px) vóór het
  committen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
2026-07-19 10:19:19 +02:00
Ramon
4438fad52b fix: dino-personage krijgt een echt dinolichaam i.p.v. een blote romp (v0.4.25-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 1m5s
- Buikvlek (lichter gekleurde ovaal) op de romp - een klassiek dino-kenmerk
  dat meteen de "kleurvlek zonder vorm"-indruk wegneemt.
- Twee kleine pootjes onderaan en twee stompe armpjes opzij (T-rex-stijl) -
  het lichaam stond eerder als een rond blok zonder ledematen.
- Staart herzien naar een echte taps toelopende vorm (clip-path) i.p.v. de
  ronde druppelvorm van hiervoor.
- Alleen voor soort "dino" - monster/dier ongewijzigd, geen regressie
  (gecontroleerd via de screenshot-galerij van alle drie soorten samen).
- Geverifieerd op alle vijf bestaande scenario's (opslaan, widget,
  School-navigator-badge, math.js-aanmoediging, ongeldige input) én
  opnieuw op alle drie weergavegroottes (24/44/180px) vóór het committen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
2026-07-19 09:57:48 +02:00
Ramon
9bf91f0bd1 fix: personage-tekening had een gezicht noch duidelijke dino-vorm (v0.4.24-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 36s
- Elk personage kreeg ogen (was volledig gezichtsloos - de kernoorzaak van de
  lage beeldkwaliteit: een gekleurde vlek zonder gezicht oogt nooit als een
  figuur, ongeacht soort/haar/accessoires).
- Dino kreeg een snuitje met neusgaten, een duidelijk zichtbaar staartje en
  een derde, groter/donkerder rugstekel-plaatje - hiervoor waren de
  rugstekels aanwezig maar onzichtbaar (z-index onder het lijf/hoofd, zie
  vorige commit), nu bewust vóór het silhouet getekend zodat ze altijd tonen.
- Snuitje stond eerst z-index-onder de rugstekels (dus zelf ook onzichtbaar,
  identieke fout als hierboven) - z-index verhoogd zodat het snuitje altijd
  vóór de rugstekels ligt, wat ook anatomisch klopt (snuit zit vooraan).
- Geverifieerd met alle vijf scenario's uit de vorige verificatie (opslaan,
  widget, School-navigator-badge, math.js-aanmoediging) plus een losse
  screenshot-galerij op alle drie weergavegroottes (24/44/180px).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
2026-07-19 09:50:18 +02:00
Ramon
24d89b8bb5 feat: eigen personage (avatar) voor leerlingen, ouders en staf (v0.4.23-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 36s
- Nieuwe migratie db/018_user_avatar.sql: kolom avatar_data (jsonb) op users.
- publicUser() geeft avatar overal automatisch mee (login, /auth/me,
  /admin/users) - bestaande school/klas-rechten regelen vanzelf wie welke
  personages ziet, geen nieuwe permissie-sleutel nodig.
- Nieuwe route PUT /me/avatar (open voor elke ingelogde rol, zelfde patroon
  als /me/data): valideert soort/kleuren/haar/accessoires, slaat de eigen rij op.
- Nieuwe gedeelde module public/js/avatar.js: personage samenstellen uit
  soort (dino/monster/dier - elk met een vast eigen lichaamsdeel, ook de
  gevraagde dinostekels), lichaams- en haarkleur, haarstijl en hoofd-/
  lichaamsaccessoires; gedeelde editor-UI voor zowel de instellingen-sectie
  "Mijn personage" als de nieuwe widget.
- Instellingen-sectie "Mijn personage" (cms.js-patroon) - de leerling-toegang,
  want widgets van categorie "class" zijn niet toewijsbaar in het
  afgeschermde leerling-speelscherm.
- Widget 🦖 toegevoegd aan het bord (categorie "class") voor staf en
  volledige-whiteboard-gebruikers.
- School-navigator (admin.js): leerkracht/beheerder ziet nu het personage
  van elk klaslid in de personenlijst i.p.v. altijd de initialen-cirkel.
- Voorbereiding gerelateerd roadmappunt: math.js toont het eigen personage
  kort naast "Goed!" als aanmoediging (pilot in één widget, geen nieuw
  feedbacktype); zonder ingesteld personage blijft het gedrag ongewijzigd.
- Vertalingen (nl+en) voor de nieuwe widget, instellingen-sectie en editor.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
2026-07-19 07:34:25 +02:00
Ramon
8b57922307 fix: schoolbeheerder zichtbaar als groepsleiding + A+-knop verspringt niet meer (v0.4.22-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 36s
- Een schoolbeheerder (of systeemmanager) die aan een klas gekoppeld werd,
  kwam daar niet zichtbaar te staan: de klas-detailweergave en de
  staf-telling op de klastegel filterden op role==="teacher", terwijl de
  server al sinds v0.3.38 elke stafrol (teacher/admin/super) toestaat in
  class_teachers. Puur een weergavebug op de client - de koppeling zelf
  werkte al goed. Nieuwe isClassStaff()-helper matcht nu exact de
  serverkant se toegestane rollenset.
- De "+" in de A+-zoomknop (toolbar én het per-widget contentzoom-knopje)
  gebruikte de CJK-volle-breedte "+" (U+FF0B) naast de gewone letter "A" -
  dat teken valt vermoedelijk terug op een ander lettertype dan Quicksand,
  waardoor de relatieve positie t.o.v. de "A" verspringt zodra
  document.body.style.zoom verandert. Vervangen door de gewone ASCII "+"
  (net als de A−-knop al een normaal minteken gebruikt, niet de volle-
  breedte variant).
2026-07-19 01:06:39 +02:00
Ramon
9920fd3855 feat: vier sociaal-emotionele widgets, waaronder een geluidsmeter (v0.4.21-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 36s
- Geluidsmeter: meet het klasgeluid live via de microfoon in een verticale
  meter (rustig/pratend/te luid). Twee aanpasbare streefniveaus ("Rustig
  tot" / "Te luid vanaf") laten de leerkracht de meter afstemmen op het
  eigen lokaal en apparaat. Microfoon staat standaard uit en start alleen
  na een expliciete klik; er wordt niets opgenomen of verstuurd, alles
  blijft lokaal in de browser. Stopt de microfoonstream vanzelf zodra de
  widget van het bord verdwijnt, ook als "Stop" niet eerst is ingedrukt.
- Gevoelszones: individuele check-in met vier zones (blauw/groen/geel/
  rood, Zones of Regulation-stijl) - elke zone geeft een korte uitleg én
  een concrete tip om jezelf te helpen. Anders dan de bestaande
  Gevoelsmeter (mood.js), die een anonieme klaspoll is.
- Ademhalingsoefening: een groeiende/krimpende cirkel begeleidt rustig
  ademhalen in drie instelbare tempo's (4-4, 4-7-8, box breathing),
  met rondjesteller.
- Complimentenmachine: trekt een willekeurig compliment uit twintig
  kindvriendelijke complimenten, met dezelfde spin-en-onthul-animatie als
  de namenkiezer.
- Alle vier volledig tweetalig (nl/en), toegevoegd aan de mobiele-dekking-
  test, en geverifieerd in de browser inclusief een echte (fake) micro-
  foonstream voor de geluidsmeter.
2026-07-19 00:44:39 +02:00
Ramon
63490719d6 fix: letterblokken bij wereldoriëntatie verschillen nu ook per niveau (v0.4.20-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 51s
- De nieuwe "Letterblokken"-werkvorm (v0.4.18) verklapte op elk niveau
  altijd elke letter - alleen de soorten-/plaatsenpool werd moeilijker,
  de spelopdracht zelf bleef identiek. Bij de originele letterblokken-
  widget (letters.js) maakt niveau juist de opdracht zelf moeilijker:
  niveau 1 verklapt alles, niveau 3 niets.
- Zelfde schaal nu ook hier: niveau 1 toont elke letter, niveau 2 ongeveer
  de helft (willekeurig gekozen, vast per opdracht), niveau 3 toont geen
  enkele letter. Het puntenpatroon per vak blijft op elk niveau zichtbaar
  als tastbare hint, ook wanneer de letter zelf verborgen is.
- Geverifieerd: niveau 1 toont 11/11 letters, niveau 2 ~helft, niveau 3
  0/13 (met de puntenpatronen nog intact).
2026-07-19 00:23:39 +02:00
Ramon
705bb7800a fix: niveaus bij wereldoriëntatie nu als knoppenrij, net als de andere oefenwidgets (v0.4.19-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 56s
- Bomen/vogels/planten/topografie verstopten "Niveau" als een los
  keuzeveld onderin het inklapbare Instellingen-paneel; de oefenklok,
  het geldwidget, plaatswaarde en rekenwidgets tonen niveau altijd als
  een knoppenrij bovenaan, direct zichtbaar tijdens het spelen.
- Niveau is nu overal dezelfde knoppenrij (Niveau 1/2/3, met uitleg per
  knop via title, net als bij de oefenklok) direct in de bovenbalk naast
  de score - geen Instellingen meer nodig om niveau te wisselen.
- Niveau wisselen zet score en reeks terug naar 0, zoals bij de andere
  oefenwidgets (voorheen bleef de score gewoon doorlopen).
- Topografie: de knoppenrij verdwijnt bij "Landsdelen", waar niveau toch
  al niet van toepassing was - zelfde gedrag als voorheen, nu alleen
  consistent met de rest.
2026-07-19 00:16:09 +02:00
Ramon
5cdc854e69 feat: flitskaarten en letterblokken bij wereldoriëntatie (v0.4.18-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 37s
- Bomen, vogels en wilde planten krijgen twee nieuwe werkvormen naast
  Leren/Quiz: Flitskaarten (naam kort tonen, dan zelftoets - zelfde opzet
  als het losse Flitswoorden-widget) en Letterblokken (de naam met
  sleepblokken spellen, zelfde mechaniek als de letterblokken-widget,
  inclusief puntenpatroon per letter).
- Topografie krijgt dezelfde twee werkvormen: flitskaarten tonen de kaart
  met de plek pas gelabeld na onthullen, letterblokken laat de naam van de
  gemarkeerde provincie/plaats/rivier/werelddeel spellen.
- Letterblokken werkt ook bij namen met spaties (bv. "Witte
  paardenkastanje") - spaties worden een visuele tussenruimte, geen vak.
- Geverifieerd door alle vier widgets (bomen/vogels/planten/topografie)
  volledig te spelen: flitskaart onthullen + zelftoets, en een woord
  volledig spellen puur op basis van de getoonde puntenpatronen.
2026-07-18 19:35:08 +02:00
Ramon
daad79c8b9 fix: vakken tonen nu het puntenpatroon van het júíste cijfer, zoals bij letterblokken (v0.4.17-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 56s
- De vorige puntenpatroon-fix (v0.4.16) loste alleen op dat de tien
  cijferblokken onderling verschillen. De écht ontbrekende stap: bij de
  letterblokken en de rekenwidgets (madd/msub/mmul/mdiv/mpct) toont een
  leeg vakje altijd het puntenpatroon van het cijfer/de letter die er
  hoort te staan - een tastbare hint, ongeacht wat er al ligt. Bij de
  oefenklok en het geldwidget toonde het vakje in plaats daarvan niets
  (leeg) of het patroon van wat er toevallig al in lag - geen hint.
- Oefenklok (blockAnswer), geldwidget (digitAnswer) en de nieuwe
  plaatswaarde-oefening (buildAnswer) tonen nu overal het patroon van het
  juiste doelcijfer per vak, consistent met letterblokken/rekenwidgets.
- Geverifieerd door drie oefeningen (geldrekenen niveau 2/3, oefenklok
  niveau 3, plaatswaarde niveau 3) volledig op te lossen met uitsluitend
  de puntenpatronen als leidraad (geen tekst gelezen) - in alle gevallen
  "Goed gedaan!".
2026-07-18 16:33:07 +02:00
Ramon
bab1018142 fix: cijferblokken kregen elk een eigen puntenpatroon + niveaus voor plaatswaarde (v0.4.16-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 35s
- 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.
2026-07-18 15:46:24 +02:00
Ramon
e555876fa4 feat: cast bord/widget vanaf whiteboard naar klas of leerling (v0.4.15-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 38s
- Nieuwe Cast-knop (📡) op de whiteboard-toolbar opent een sheet om het
  geopende bord - of één widget erop met kijken/werken - direct naar een
  klas of leerling uit te zenden, zonder om te weg via Instellingen.
- Hergebruikt de bestaande PUT/DELETE /assignments/class|pupil routes
  ongewijzigd; geen serverwijziging nodig.
- Instellingen > Toewijzingen blijft bestaan maar toont nu alleen de
  huidige toewijzing + een "Loskoppelen"-knop (aanmaken/wijzigen gebeurt
  voortaan via Cast).
2026-07-18 13:26:26 +02:00