Commit graph

174 commits

Author SHA1 Message Date
Ramon
111e48b044 fix: toevoegformulier duidelijk anders dan een bestaande-gebruikersrij (v0.4.06-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 34s
Een uitgeklapt "+ ..."-formulier (nieuwe schoolbeheerder/leerkracht/leerling/
school/klas) zag er in het beheerpaneel bijna hetzelfde uit als het
bewerkpaneel van een bestaande gebruiker - beide gewoon een rij velden
zonder duidelijk onderscheid.

- .am-add krijgt nu een eigen kader (accentkleur, stippellijn) i.p.v. de
  neutrale achtergrond van een gebruikersrij
- het formulier toont bovenaan een titel die herhaalt wát je toevoegt (bv.
  "LEERLING"), ook zodra de knoptekst erboven uit beeld is
- de dichtgeklapte "+ ..."-knop zelf krijgt ook de accentkleur, zodat
  collapsed en uitgeklapt dezelfde "dit is toevoegen"-taal spreken
- geverifieerd in een echte headless Chromium: duidelijk visueel onderscheid
  tussen een toevoegformulier en een echte gebruikersrij
2026-07-18 04:22:17 +02:00
Ramon
e5890280cc feat: snel-toevoegen-knop onder elke groep in Gebruikers (v0.4.05-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 34s
Net als bij systeemmanagers (vorige commit) krijgt nu elke groep in de
Gebruikers-tab (schoolbeheerder, groepsleiding, en elke klas incl. "geen
klas") direct een eigen "+ ..."-knop eronder om iemand toe te voegen zonder
naar een ander tabblad te hoeven. Bij een klas staat die klas al vast
ingevuld (presetClassId, bestond al als parameter maar werd nog nergens
gebruikt). Een groep blijft zichtbaar zolang er iets aan toegevoegd kan
worden, ook als hij nu leeg is (bv. een gloednieuwe klas zonder leerlingen) -
anders was er nergens een knop om de eerste aan toe te voegen.

Geverifieerd in een echte headless Chromium (gemockte /api/** responses):
groepen en knoppen verschijnen zoals verwacht, en de klas-select bij een
klasgroep staat al goed vooringesteld.
2026-07-18 04:14:27 +02:00
Ramon
c6638d63f1 feat: systeemmanager aanmaken verplaatst naar Gebruikers + eigen wachtwoord ter bevestiging (v0.4.04-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 36s
- het aanmaken van een systeemmanager stond onder Scholen, los van de rest
  van het gebruikersbeheer - verplaatst naar de Gebruikers-tab (bij "alle
  scholen", naast de bestaande systeemmanagers-groep)
- extra veiligheidsstap: de aanmaker moet het eigen wachtwoord invullen ter
  bevestiging vóórdat een nieuwe systeemmanager wordt aangemaakt - server
  verifieert dit (POST /admin/users), niet alleen de client
- wie dit al mocht (alleen systeemmanagers, via creatableRoles) blijft
  ongewijzigd - de bevestigingsstap is een aanvullende, geen vervangende,
  controle
2026-07-18 04:00:30 +02:00
Ramon
cae700f76d fix: leerling verplaatsen naar een andere klas ververst het paneel niet (v0.4.03-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 33s
De klas-select bij een leerling (uitwisselen naar een andere klas) miste de
reload() die elders bij een klaswijziging wel gebeurt: de server sloeg de
nieuwe klas gewoon op, maar het paneel liet nog de oude toestand zien - leek
dus alsof verplaatsen niet werkte. Test toegevoegd die de serverkant van
deze route bevestigt (die werkte al correct); de fix zelf is puur de
ontbrekende reload() in public/js/admin.js.
2026-07-18 03:51:21 +02:00
Ramon
45f66044f2 fix: alle DELETE-verzoeken faalden op een lege JSON-body (v0.4.02-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 38s
De echte, uiteindelijke oorzaak achter elke "serverfout" die niet door een
route-eigen foutafhandeling kwam (klas verwijderen, en zeer waarschijnlijk
ook de oorspronkelijke gebruiker-verwijderen-bug): de client stuurde bij élk
verzoek altijd "Content-Type: application/json" mee, ook bij een DELETE
zonder body. Fastify's standaard JSON-parser weigert zo'n leeg lichaam met
"Body cannot be empty when content-type is set to 'application/json'" -
vóórdat er ook maar één hook of route-handler heeft kunnen draaien, dus nog
vóór req.user gezet wordt en vóór enige try/catch in een route. Dat verklaart
waarom eerdere per-route foutafhandeling dit nooit kon vangen: de fout zat
vóór elke route.

- public/js/core.js: api() stuurt Content-Type alleen nog mee als er
  werkelijk een JSON-body is
- src/body-parser.js (nieuw): serverside vangnet dat een leeg lichaam bij
  Content-Type: application/json gewoon toestaat i.p.v. te weigeren -
  ongeacht wat een client meestuurt
- test/body-parser.test.js: reproduceert de exacte foutmelding tegen een
  echte Fastify-requestcyclus (de gemockte-pool-tests konden dit principieel
  nooit vangen, vandaar dat dit alle eerdere testrondes overleefde)
2026-07-18 03:44:39 +02:00
Ramon
4517f592f7 chore: versienummering terug naar tweecijferig patchformaat (v0.4.01-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 55s
0.3.99 rolde per ongeluk door naar 0.4.0/0.4.1 i.p.v. het gebruikelijke
tweecijferige patchformaat (0.4.00, 0.4.01, ...) - hersteld.
2026-07-18 03:40:51 +02:00
Ramon
6742e07363 fix: altijd de echte serverfout tonen + klasnamen uniek per school (v0.4.1-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 35s
- de generieke errorhandler gaf alleen detail mee als req.user al gezet was;
  bleek de fout zelf al vóór/tijdens dat moment te zitten (zoals bij klas
  verwijderen), dan bleef het toch de kale "serverfout" - nu altijd de echte
  oorzaak, dit is een intern beheertool zonder publieke bezoekers
- klasnamen konden onbeperkt dubbel aangemaakt worden binnen dezelfde school
  (geen enkele controle) - POST /admin/classes weigert dit nu met 409, en een
  nieuwe unieke index (school_id, lower(name)) is het echte vangnet
- migratie 016 hernoemt eerst bestaande dubbele klasnamen (niets verwijderd)
  vóórdat de unieke index wordt gezet: anders was de migratie zelf op deze
  database mislukt en had de app niet meer opgestart
2026-07-18 03:39:04 +02:00
Ramon
4a31dc9351 fix: klas verwijderen toont echte oorzaak i.p.v. kale serverfout (v0.4.0-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 43s
- DELETE /admin/classes/:id had geen try/catch: elke mislukking (bv. een FK
  die nog niet cascade/set-null is op deze database) viel terug op de
  generieke handler en toonde alleen "serverfout" zonder aanknopingspunt
- zelfde aanpak als eerder bij het verwijderen van een gebruiker: de echte
  Postgres-foutmelding komt nu terug i.p.v. een kale "serverfout"
- test toegevoegd voor zowel het gewone pad als de foutmelding
2026-07-18 03:28:31 +02:00
Ramon
be8ecc5378 fix: gebruikerslijst per school laadde nooit (SELECT DISTINCT/ORDER BY) (v0.3.99-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 1m13s
- linkedParents() gebruikte "SELECT DISTINCT p.* ... ORDER BY lower(p.username)":
  Postgres weigert dit omdat de ORDER BY-expressie niet letterlijk in de
  select-lijst staat. Deze query loopt bij élke school-gescopede
  gebruikerslijst (elke schoolbeheerder, en elke systeemmanager die een
  specifieke school kiest i.p.v. "alle scholen") - dus die lijst laadde
  nooit, klassen werden om dezelfde reden niet (opnieuw) opgehaald, en
  "alle scholen" was de enige werkende weergave
- fix: lower(p.username) expliciet als kolom (sort_key) in de select-lijst,
  ORDER BY daarop i.p.v. op de losse expressie
- regressietest die de exacte SQL-vorm vastlegt (een gemockte pool voert geen
  echte SQL uit en had deze Postgres-grammaticafout niet kunnen vangen)
2026-07-18 03:19:36 +02:00
Ramon
205ba74eff fix: beheerpaneel toont laadfout i.p.v. stilletjes leeg te blijven (v0.3.98-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 34s
- reload() in het beheerpaneel (scholen/klassen/gebruikers) ving elke fout
  stilletjes op en zette alles terug naar lege lijsten, zonder enige melding -
  dat gaf een verwarrend leeg paneel bij een specifieke school terwijl "alle
  scholen" wel werkte, zonder enig aanknopingspunt waarom
- de echte oorzaak verschijnt nu bovenin het paneel (amLoadError), zodat een
  volgende mislukking direct te diagnosticeren is i.p.v. te moeten gokken
2026-07-18 03:08:55 +02:00
Ramon
4ffb69ca61 fix: gebruiker verwijderen toont echte oorzaak i.p.v. kale serverfout (v0.3.97-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 1m19s
- DELETE /admin/users/🆔 hele handler (niet alleen deleteUserFully) in
  één try/catch, zodat ook een fout vóór het verwijderen zelf (bv. de
  opzoekquery) de echte reden teruggeeft i.p.v. terug te vallen op de
  generieke 500 "serverfout"
- generieke errorhandler logt voortaan altijd het volledige fouteobject en
  geeft ingelogde gebruikers de werkelijke foutmelding mee i.p.v. alleen
  "serverfout"
- test toegevoegd die dit nieuwe pad dekt
2026-07-17 22:27:20 +02:00
Ramon
e48a3a4e46 fix: gebruiker verwijderen werkt gegarandeerd - ruimt afhankelijke rijen zelf op (v0.3.96-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 51s
- De "serverfout" bij verwijderen komt van een foreign key die op de live-database geen ON DELETE CASCADE heeft, waardoor DELETE FROM users faalt zodra er nog een gekoppelde rij bestaat
- Nieuw: de verwijderroute haalt in één transactie eerst alle afhankelijke rijen weg (sessies, rollen, klaskoppelingen, toewijzingen, voortgang, ouderkoppelingen, aanmeldingen, reservekopieën, gedeelde items, afbeeldingen, borden/mappen) en verwijdert daarna de gebruiker - dit werkt ongeacht de exacte FK-instelling en heeft migratie 015 niet nodig
- Mocht verwijderen tóch falen, dan komt nu de echte databasereden terug (constraint-detail) i.p.v. een kale "serverfout", zodat de oorzaak zichtbaar is
- 3 servertests: volledige opruiming in transactievolgorde (afhankelijke rijen vóór users), echte foutreden bij een gesimuleerde fout; 97 tests groen

LET OP: dit werkt pas na deploy van deze versie; v0.3.94 (huidige live) bevat de fix nog niet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-17 21:50:48 +02:00
Ramon
e3036580ce fix: gebruiker verwijderen werkt weer (herstel foreign-key cascade) (v0.3.95-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 32s
- Verwijderen gaf op de live-database een 500 "serverfout". Oorzaak: op databases die zijn ontstaan vóór alle foreign keys ON DELETE CASCADE waren, houdt een tabel de oude constraint (CREATE TABLE IF NOT EXISTS werkt een bestaande constraint nooit bij), waardoor DELETE FROM users een foreign-key-fout geeft
- Migratie 015: zoekt elke FK naar users(id) die niet cascade en niet set-null is en zet die alsnog op ON DELETE CASCADE (bewuste SET NULL-constraints blijven ongemoeid). Draait bij de volgende deploy en is idempotent
- Verwijderroute vangt een resterende FK-fout (23503) op als een begrijpelijke 409 i.p.v. een kale serverfout
- Geverifieerd: alle INSERT INTO users-paden vangen al 23505 op (gebruikersnaam uniek per school); dezelfde naam in verschillende scholen is bewust toegestaan (inloggen kiest de school) en staat sinds 0.3.94 per school gegroepeerd
- 3 nieuwe servertests (FK-fout -> 409, normale verwijdering, migratie-inhoud); 97 tests groen

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-17 21:39:29 +02:00
Ramon
30818dc5f2 fix: gebruikers gegroepeerd per school in alle-scholen-weergave + zichtbare verwijderfouten (v0.3.94-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 35s
- In de "alle scholen"-weergave stonden gebruikers op rol gegroepeerd, waardoor je niet zag bij welke school ze horen (Ramon/Rubi/leerlingb1b leken los te staan). Ze worden nu per school gegroepeerd (inklapbaar), met systeemmanagers, ouders en losse accounts apart eronder - je ziet meteen wie bij De Ziep hoort
- Bij één gekozen school blijft de indeling op rol/klas; die rolgroepen zijn nu ook inklapbaar
- Verwijderfouten (en andere actie-fouten in het detailpaneel) verschijnen nu in het detailpaneel zelf i.p.v. alleen bovenaan (buiten beeld op mobiel). Als een verwijdering serverzijdig faalt, zie je nu de reden - het lijkt niet langer of "niets gebeurt"
- Verwijderen zelf is end-to-end geverifieerd (DELETE verstuurd, gebruiker verdwijnt); een gesimuleerde 409 toont de melding in het paneel en laat de gebruiker staan
- 7 nieuwe Playwright-checks (school-groepering, zichtbare fout, geslaagde verwijdering) + volledige regressie van 8 suites; 94 servertests groen

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-17 21:21:05 +02:00
Ramon
c86abcf794 fix: onzichtbare knopteksten, lege klassenlijst en correcte klas-schoolregels (v0.3.93-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 32s
- Onzichtbare knoppen opgelost: .tbtn houdt standaard witte tekst (wit-op-blauw), maar in het beheer staat hij op een lichte achtergrond -> tekst was wit-op-wit. Nu expliciet donkere tekst; de primaire accentknop houdt bewust witte tekst
- Systeemmanager kreeg standaard "alle scholen" waardoor klassen leeg leken en niet toe te voegen waren; er wordt nu meteen een concrete school gekozen (klassen/gebruikers direct gevuld). Contextbalk laat wisselen; "alle scholen" blijft een bewuste keuze
- Lege school toont nu een duidelijke uitleg dat bestaande klassen bij een andere school kunnen horen, met verwijzing naar de schoolkeuze
- Correcte gegevensregels: classes.school_id is NOT NULL met ON DELETE CASCADE, dus een klas kan niet "los" bestaan (die eerdere aanname is teruggedraaid). Een klas is nu te VERPLAATSEN naar een andere school (PATCH weigert null/ongeldig); een verwijderde school neemt zijn klassen mee (cascade) - dat verklaart verdwenen klassen
- Bij een leerling naar een andere school koppelen blijft de klas behouden als die al bij de doelschool hoort
- Verificatie uitgebreid met een echte kleurcontrast-scan (vangt wit-op-wit) over alle secties + de default-school-flow; alle 94 servertests en 8 Playwright-suites groen

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-17 20:22:36 +02:00
Ramon
aee1c59fbd fix: leerlingen zichtbaar bij gekozen school + uniforme controls in het beheer (v0.3.92-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 35s
- Leerlingen stonden onder standaard-ingeklapte klasgroepen, dus met een gekozen school leek de lijst leeg terwijl de detailweergave de juiste school toonde. Klasgroepen staan nu standaard open zodra een school is gekozen (de schoolkeuze wás al het filter); bewust dichtklappen wordt onthouden
- Losse klassen (school verwijderd) waren nergens zichtbaar: de systeemmanager ziet ze nu zonder gekozen school met een uitleg, en kan ze via een School-veld terugkoppelen; nieuwe PATCH /admin/classes/:id + GET ?school=none (super) met servertests
- Bij het aan een school koppelen van een leerling blijft de klasindeling behouden als die klas al bij de doelschool hoort (eerst klas terugkoppelen, dan de leerlingen)
- UI-consistentie: één uniforme control-hoogte (32px) voor álle knoppen, selects en invoervelden in Instellingen - einde aan de grote maatverschillen; T() valt terug op NL en dan de sleutelnaam zodat een knop nooit zonder tekst kan staan
- Status in de gebruikerstabel als compacte glyph (● in gebruik / ○ wacht) met uitleg bij aanwijzen i.p.v. een afgekapte "nog niet ing…"-chip
- Geverifieerd in headless Chromium: nieuwe suite (21 checks: zichtbaarheid met schoolfilter, losse klassen, geen lege knoppen, uniforme hoogte over 8 secties) + volledige regressie van 5 bestaande suites

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-17 19:46:09 +02:00
Ramon
15618348dc style: alle menu's nagelopen - alles verborgen tot erom gevraagd wordt (v0.3.91-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 33s
- Voortgang: tijdlijn toont 30 regels per keer met een "meer tonen (n)"-knop i.p.v. 200 regels ineens
- Inhoud: de vier blokken (welkomstwoord, melding, widget-uitleg, systeembrede inhoud) zijn inklapbare groepskaarten - alleen het welkomstwoord staat standaard open; open-status overleeft her-renders
- Account: de drie wachtwoordvelden verschijnen pas na een klik op de kop "Wachtwoord"
- Daarmee volgt elke sectie hetzelfde principe: compacte koppen met samenvatting, detail pas op verzoek, verwante items in één kader
- Geverifieerd in headless Chromium (9 nieuwe checks) + volledige regressie van de vier bestaande suites (56 checks)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-17 14:35:24 +02:00
Ramon
b4eee9f78f feat: gebruikers aan een school koppelen en losmaken (v0.3.90-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 52s
- PATCH /admin/users/:id ondersteunt schoolId (alleen systeemmanager, rollen teacher/admin/pupil): null = losmaken, anders koppelen met bestaanscheck (404); bij wissel worden klas, klaskoppelingen en (voor leerlingen) oude toewijzingen opgeruimd; naamconflicten in doelschool of school-loze naamruimte geven een nette 409
- Detailpaneel toont voor de systeemmanager een School-veld bij deze rollen - losse accounts (ooit aan een school gekoppeld) zijn hiermee terug te zetten en bewust los te maken; werkt ook in de beheerder-subrijen op het Scholen-tabblad
- Gebruikers-tab (alle scholen): losse accounts staan in een eigen inklapbare groep "Zonder school (n)" i.p.v. onopvallend tussen de rolgroepen; de oudergroep is nu ook inklapbaar
- 3 nieuwe servertests (koppelen incl. opruiming, losmaken, 404, geen rechten/no-op voor admin en parent, 409 bij naamconflict) + 10 Playwright-checks voor de volledige flow

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-17 14:30:41 +02:00
Ramon
08618ba0b3 feat: Scholen, Klassen en Toewijzingen als groepskaarten (v0.3.89-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 31s
- Scholen: één gezamenlijk kader per school - getinte kop met grijs schoolicoon, naam en de beheerder-teller als uitklaptoggle; beheerders standaard verborgen en als compacte subrijen (36px) met status-glyph (● actief / ○ nog niet geactiveerd, uitleg via title); hernoemen/verwijderen klapt uit binnen hetzelfde kader - geen verwarring meer tussen scholen en beheerders
- Klassen: kaart per klas met leerlingtal in de kop; de reset-/verwijderacties verborgen tot Bewerken
- Toewijzingen: kaart per klas met de huidige stand compact in de kop; bord-/widget-/moduskeuze klapt uit via Bewerken
- Het status-glyph en de kaartkop-wrap zijn onderdeel van het gedeelde groepskaart-component; bewust klein grijs schoolicoon terug (functioneel onderscheid, emoji-scan-uitzondering)
- Geverifieerd in headless Chromium (14 checks) + screenshots 390px/desktop

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-17 14:27:19 +02:00
Ramon
c3492ab2ca feat: Opslag per school met groepskaart-component (v0.3.88-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 55s
- Nieuw herbruikbaar groepskaart-component (amGroupCard): één kader om items die bij elkaar horen - koprij met grijs icoon, titel, samenvatting-als-uitklaptoggle en Bewerken; compacte subrijen; open-status overleeft re-renders (injecteerbare Set zodat ook andere modules hem gebruiken)
- Opslag-tab volledig herbouwd: eerst één school kiezen (of "Systeem (zonder school)" voor systeemmanagers/ouders/losse accounts) i.p.v. alle scholen én alle gebruikers plat met altijd-open editors
- Na keuze één kaart: schoolnaam + quota-samenvatting in de kop (schoolquota bewerken klapt uit), daaronder alleen de gebruikers van díe school als compacte regels; bewerkvelden pas na "Bewerken" per rij; zoeken binnen de school
- "Standaard"-optie benoemt nu de echte rolstandaard ("Standaard (200 MB)") via defaultBytes
- Mobiel: beheer-rijen stapelen niet meer verticaal (kaartkop en subrijen wrappen netjes; subrij ± twee compacte regels i.p.v. >100px)
- Geverifieerd in headless Chromium (13 checks, o.a. dat schoolkeuze en open bewerkrij een her-render overleven - module-state) + screenshots 390px/desktop

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-17 14:20:41 +02:00
Ramon
ebe622588f style: tekstknoppen en consistente secties in het beheer - emoji-sweep afgerond (v0.3.87-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 45s
- Alle resterende emoji uit het beheer: kopieerknoppen zijn tekstknoppen ("kopieer"), wachtwoord-genereerknop heet "genereer", "alles kopiëren" zonder klembord-icoon, schoolrijen zonder 🏫, contextbalk-label is het woord "School", bevestiging is "zeker?" (de rode armed-stijl is het signaal), kopieer-feedback een net vinkje
- Scholen-tabblad: "Bewerken"-tekstknoppen i.p.v. het ✏-potlood; zoek-placeholders zonder vergrootglas-emoji
- CMS-widgetkeuze zonder emoji-prefix in de opties
- Playwright-emoji-scan over alle vijf beheersecties (knoppen/labels/groepen schoon, ▸▾✕✓+ toegestaan) + volledige master-detail-regressie (19 checks) groen

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-17 07:08:17 +02:00
Ramon
af86ff5c47 feat: gebruikersbeheer als master-detail met vast bewerkpaneel (v0.3.86-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 34s
- Gebruikers staan in een echte tabel (initiaal-avatar + naam | functie | klas | status) met kolomkoppen en uitgelijnde kolommen; klikken op een rij selecteert (accent-markering) zonder her-render, dus het zoekveld houdt focus
- Rechts een vast detailpaneel: bewerken (functie, klaskoppeling, klas, wachtwoord, acties), aanmeldverzoeken afhandelen én nieuwe gebruikers aanmaken gebeuren allemaal dáár - één plek, één patroon
- Eenmalige wachtwoorden/codes verschijnen als badge in het paneel; na aanmaken is de nieuwe gebruiker direct geselecteerd; selectie overleeft herladen en wordt gevalideerd tegen de data
- Aanmeldverzoeken zijn gewone tabelrijen met een "nieuw"-chip; de losse toevoeg-formulieren per klas zijn vervangen door één "Nieuwe gebruiker"-knop in de werkbalk (met rolkeuze)
- Smal scherm: het detailpaneel wordt een overlay met sluitknop (onder de tabel stapelen is onbruikbaar bij grote scholen)
- Menu-items herzien: de sectie heet "Scholen" (geen "Systeem > Systeem"-verdubbeling meer) en de Systeem-groep is geordend als Scholen / Inhoud / Opslag
- Eerste emoji-sweep in de omgebouwde delen (actieknoppen zijn tekstknoppen); zoekfilter en inklapbare klasgroepen werken ongewijzigd
- Geverifieerd in headless Chromium (19 checks, incl. focusbehoud, overlay-gedrag en selectie-na-reload)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-17 07:06:18 +02:00
Ramon
8b4bd47407 style: zakelijk ontwerpsysteem voor Instellingen (v0.3.85-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 33s
- Volledig gescoped op #settingsModal zodat het bord speels blijft: systeemfont i.p.v. Quicksand, controls 32px (40px op aanraakschermen), vlakke 1px-randen i.p.v. schaduwen en pastel-pills
- Rijen worden tabelregels met scheidingslijnen en hover; groepskoppen zijn subtiele sticky sectierijen; navigatie-actief is neutraal grijs met accent-inzetlijn; iconen gedempt in grijstinten
- Knoppen één systeem: subtiel omrand, danger als rode tekst die pas gevuld rood wordt na wapenen; chips/resultaten/segmentknoppen omrand vlak
- Hoog contrast expliciet teruggebracht binnen de nieuwe scope; sectiekop-icoon in eigen span zodat CSS hem kan dempen

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-16 20:42:53 +02:00
Ramon
526e4e5d52 feat: CMS-sectie Inhoud voor de systeemmanager (v0.3.84-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 56s
- Nieuwe sectie "Inhoud" in Instellingen (groep Systeem, alleen systeemmanager, doorzoekbaar via het sectieregister)
- Welkomstwoord (NL/EN) verschijnt op het inlogscherm, direct aanpasbaar zonder release
- Melding voor alle gebruikers: aan/uit, niveau informatie/belangrijk, per taal; verschijnt als wegklikbare banner onder de menubalk en elke wijziging krijgt een nieuw id zodat een eerder weggeklikte melding opnieuw verschijnt
- Widget-uitleg: per widget een eigen beschrijving die de standaardtekst in de widgetbibliotheek vervangt (leeg = standaard, placeholder toont de standaardtekst)
- Snelkoppelingen naar de bestaande kanalen voor systeembrede inhoud: gedeelde bibliotheek (borden & ankerdiagrammen, map 🌐 Systeem) en de afbeeldingencatalogus
- settings.js kreeg een generiek haakje (showSettingsDom) zodat modules met een eigen paneel zich in het sectieregister kunnen voegen
- Geverifieerd in headless Chromium (15 checks): banner + dismiss + nieuw-id-gedrag, welkomstwoord, bewerken/opslaan, galerij-override, sectie onzichtbaar voor andere rollen

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-16 19:33:49 +02:00
Ramon
aa4c5faaf9 feat: server-kant CMS voor de systeemmanager (v0.3.83-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 31s
- Migratie 014: site_content (sleutel/waarde, JSONB) voor site-inhoud die zonder release aanpasbaar moet zijn
- GET /content (publiek): welkomstwoord, update-melding en widget-uitleg in één antwoord - het inlogscherm toont het welkomstwoord al vóór het inloggen
- PUT /admin/content/:key (alleen systeemmanager, nieuwe permissie content.manage): upsert per toegestane sleutel (welcome/announcement/widgetInfo) met maat-limieten
- 4 servertests: publiek lezen, upsert door super, 403/401 voor anderen, whitelist/vorm/omvang-validatie

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-16 19:29:08 +02:00
Ramon
2fff8a3aa5 perf: gzip-vangnet in nginx voor geproxyde responses (v0.3.82-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 1m4s
- gzip on + gzip_proxied any (zonder die laatste comprimeert nginx geproxyde responses helemaal niet) + gzip_vary + min_length 1024 voor css/js/json/svg/tekst
- Geen dubbele compressie: responses die al Content-Encoding dragen (brotli/gzip uit de app) laat nginx ongemoeid; dit vangt alleen wat de app mist
- Cache-beleid blijft bewust op één plek (de app), zodat lokaal en productie identiek gedragen
- Configtest erbij in security-config-stijl

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-16 19:02:53 +02:00
Ramon
456d664718 perf: compressie, cache-headers, cache-busting en parallel ladende scripts (v0.3.81-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 1m14s
- @fastify/compress (brotli quality 4 + gzip, global): alle statische assets én API-JSON (zoals /me/data) gaan gecomprimeerd over de lijn - eerste lading van ~715 KB naar grofweg een derde
- Nieuw src/frontend.js: index.html wordt bij boot één keer gelezen, ?v=__V__ wordt vervangen door de app-versie en de pagina wordt uit het geheugen geserveerd met no-cache
- Cache-beleid per prefix: js/css een jaar immutable (veilig dankzij de geversioneerde URL's), fonts 7 dagen, afbeeldingen 1 dag met zwakke etag als 304-vangnet - herhaalbezoek kost vrijwel geen bytes meer
- Alle 31 scripts staan nu met defer in de head: parallel downloaden, uitvoeren in dezelfde dwingende volgorde (core → widgets → board → app); font-preload bewust ongeversioneerd (moet byte-gelijk blijven aan de url() in teach.css)
- Compress geregistreerd vóór de api/frontend-registraties (onSend-hook geldt alleen voor latere routes) - geborgd met een teksttest
- 6 nieuwe tests (test/frontend.test.js): versie-injectie, cache-headers per prefix, gzip+brotli round-trip, 304 op etag, defer/?v= op elk script

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-16 19:01:25 +02:00
Ramon
cb97dc2bb1 feat: breid taalwidgets en plaatjesbibliotheek uit (v0.3.80-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 31s
2026-07-16 16:27:38 +02:00
Ramon
4356658c96 fix: versietest pint geen vast nummer meer (v0.3.79-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 43s
- De CI faalde omdat een test het versienummer hardcodeerde (0.3.77-beta) en dus bij elke reguliere versiebump breekt
- De test bewaakt nu het echte beleid: de dev-branch voert altijd een -beta-versie en de VERSION-file loopt gelijk met de const in core.js (die controle stond er al en blijft)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-16 15:01:59 +02:00
Ramon
2a0de85152 fix: vastgezette lat kleeft altijd en vinger-jitter breekt de snap niet meer (v0.3.78-beta)
Some checks failed
dev - build & deploy naar test / build-and-deploy (push) Failing after 32s
- Op een aanraakscherm wiebelt een vinger in de eerste millimeters: de richtingstoets wees de snap daardoor af en de haal bleef de vinger volgen
- Vastgezet gereedschap (🔒) kleeft nu onvoorwaardelijk binnen 28px van de rand - geen richtingstoets; het slot betekent "hier teken ik langs"
- Losse lat: richting wordt over een langere basis gemeten (10px i.p.v. 7) met een ruimere hoek (40° i.p.v. 30°), zodat vinger-jitter de evenwijdige intentie niet meer maskeert
- Inktlaag kreeg touch-action:none zodat de browser vingerhalen nooit als scroll-gebaar afvangt
- Geverifieerd met échte touch-events (CDP) inclusief nagebootste vinger-jitter: 7 nieuwe checks (vastgezet kleeft mét jitter, op de lat tekenen zonder verschuiven, los evenwijdig kleeft, wegdraaiend blijft vrij) + de vier bestaande muis-suites (42 checks) blijven groen

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-16 14:45:51 +02:00
Ramon
7a5d7c57ff feat: maak widgets speelser en kindvriendelijker (v0.3.77-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 41s
2026-07-16 14:15:28 +02:00
Ramon
85268050fd feat: slot op liniaal/geodriehoek en richtingsbewust langs de lat tekenen (v0.3.76-beta)
Some checks failed
dev - build & deploy naar test / build-and-deploy (push) Failing after 34s
- Slot-knop (🔓/🔒) op liniaal én geodriehoek: vastgezet gereedschap kan niet meer verschuiven of draaien (draaiknop verdwijnt), er kan alleen nog langs getekend worden; tweede klik ontgrendelt
- Naast de lat beslist nu de richting i.p.v. één vaste afstand: binnen 22px van een rand wacht de haal de eerste ~7px beweging af - loopt die grofweg evenwijdig aan de rand (≤30°), dan kleeft hij, anders blijft hij vrij; een tik zet gewoon een stip
- Daarmee is langs de lat tekenen betrouwbaar (ook met dikke digibord-vingers) zónder dat de lat vrije halen in de buurt grijpt
- Op de lat zelf blijft de intentie ondubbelzinnig: direct kleven binnen 24px van de rand
- Geverifieerd in headless Chromium: 12 nieuwe checks (richtingsregel, stip-tik, slot aan/uit, vastgezet = onverplaatsbaar maar betekenbaar) + de drie bestaande suites (35 checks) blijven groen

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-16 14:04:15 +02:00
Ramon
5d12d2f8ec feat: kleinere snapzone, dunnere pennen, potlood met karakter en kalligrafische vulpen (v0.3.75-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 40s
- Snapzone naast de liniaal/geodriehoek verkleind van 24 naar 10px: vrije halen in de buurt worden niet meer "gegrepen"; op de lat zelf blijft de ruime 24px-zone gelden
- Diktekiezer uitgebreid met dun (1 en 2, naast 3/5/8)
- ✏️ heet nu Potlood en tekent zo: een zachte, gelijkmatige lijn (~85% dekking) via de overlay-laag, dus égaal ook waar de haal zichzelf kruist
- Vulpen schrijft nu echt kalligrafisch met een 45°-penpunt: dik in de neerhaal, dun in de ophaal (gemeten 11px vs 1,5px bij dikte 5); stylusdruk versterkt het effect
- Geverifieerd in headless Chromium (9 checks, pixelmetingen van dekking, snapzones en lijndiktes)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-16 13:55:36 +02:00
Ramon
6e7f24eb40 fix: pen op de lat tekent nu langs de rand i.p.v. de lat te verslepen (v0.3.74-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 27s
- In de praktijk zet je de pen óp de liniaal-/geodriehoekrand, zoals bij een echte lat - maar dat versleepte het gereedschap, waardoor "langs de lat tekenen" niets leek te doen
- Landt de pen (pen/vulpen/stift, penmodus actief) binnen 24px van een tekenrand - ook óp de lat zelf - dan start een vastgeklikte haal en blijft het gereedschap liggen; de events lopen via pointer capture gewoon door naar de inktlaag
- Verder van de rand blijft het gereedschap gewoon te verslepen, en de draaiknop houdt voorrang
- Startzone naast de lat verruimd van 16 naar 24px (digibord-vingers)
- Geverifieerd in headless Chromium: 7 nieuwe checks (tekenen op de randzone zonder de lat te verschuiven, verslepen vanuit het midden, draaien in penmodus, idem geodriehoek) + de bestaande 14 snap-checks blijven groen

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-16 13:46:18 +02:00
Ramon
abafcafe3a feat: breid natuurwidgets uit
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 30s
2026-07-16 13:43:46 +02:00
Ramon
55520ba367 feat: tekenen langs liniaal en geodriehoek + geodriehoek met twee niveaus (v0.3.72-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 27s
- Langs de lat tekenen: begint een haal binnen 16px van een rand van de liniaal of geodriehoek, dan kleeft de hele haal aan die rand - kaarsrecht, ook gedraaid (randen draaien mee); bewust wegtrekken (>60px) laat weer los; gum en vrije halen blijven onaangetast
- Gereedschappen liggen nu boven de inktlaag, zoals een echte lat op het bord: ook in penmodus te verplaatsen, draaien en instellen
- Geodriehoek herbouwd met twee niveaus: basis (grote hoofdhoeken 30/45/60/…, hele centimeters - voor jonge leerlingen) en volledig (graden per 2° met dubbele schaal links- én rechtsom, millimeters, evenwijdige hulplijnen per cm - voor meetkunde in de bovenbouw en daarna)
- Geodriehoek: cm-schaal vanaf 0 in het midden zoals echt, maat in hele cm op de knop (6/8/12 cm), 90°-markering, begrensd op de bordbreedte; alleen de driehoek zelf vangt aanrakingen zodat er vlak naast de schuine randen getekend kan worden
- Geverifieerd in headless Chromium (14 checks, o.a. pixelmetingen van rechtgetrokken halen op rechte én 30°-gedraaide randen)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-16 13:38:14 +02:00
Ramon
93c287979c feat: breid wereldorientatie-oefeningen uit (v0.3.71-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 30s
2026-07-16 11:44:30 +02:00
Ramon
100dc62087 feat: verbeter wereldorientatie met echte beelden (v0.3.70-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 27s
2026-07-16 10:31:50 +02:00
Ramon
88d1f9cb86 feat: voeg wereldorientatiewidgets toe (v0.3.69-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 26s
2026-07-16 09:42:45 +02:00
Ramon
652e039a39 feat: vloeiende pen en stift, egale markeerstift en logischere liniaal (v0.3.65-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 31s
- Pen, vulpen en stift tekenen nu met kwadratische curves door de middelpunten van de pointer-stappen: geen hoekige knikken meer bij snelle halen (het "blikkerige" beeld)
- Markeerstift tekent tijdens de haal dekkend op een eigen doorschijnende laag en wordt bij het loslaten in één keer met vaste transparantie op de inktlaag gezet: overlappingen binnen één haal geven geen donkere vlekken meer, een tweede haal eroverheen wordt wél donkerder (zoals een echte stift)
- devicePixelRatio wordt bij elke resize vers gelezen (scherp blijven na verplaatsen naar een ander scherm of zoomen)
- Liniaal: logische lengtes in hele centimeters (5/10/15/20 cm, knop toont de actuele maat), schaal telt vanaf 0 met een kantlijn zoals een echte liniaal, lengte begrensd op de bordbreedte, en de lengte staat op de liniaal zelf
- Liniaal en geodriehoek: draaien toont live de hoek in graden en klikt vast op veelvouden van 45° (badge kleurt groen); knoppen hebben leesbare titels (Lengte / Hoog contrast)
- Geverifieerd in headless Chromium (12 checks, o.a. pixelmeting van de markeerstift-kruising en exacte 45°-snap)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-16 08:37:23 +02:00
Ramon
3e6ab09e1f fix: ankerkiezer opent alleen op een bewuste tik en tekenbalk krimpt weer geminimaliseerd (v0.3.64-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 31s
- Oorzaak van het "vanzelf" openende uploadvenster gevonden: elk leeg plaatjesvak in een ankerdiagram had eigen pointer/click-handlers die het pannen blokkeerden en de plaatjeskiezer (met uploadtab) openden bij elke veeg die erop begon - op een telefoon was dat vrijwel elke aanraking, dus ook direct na het verversen
- Lege plaatjesvakken lopen nu via dezelfde tik/sleep-logica als de cellen (6px-drempel): vegen pant het diagram, alleen een stilstaande tik opent de kiezer; woord bewerken blijft ongewijzigd
- Tekenbalk op telefoons weer gecentreerd op inhoudsbreedte i.p.v. vastgepind aan beide randen: geminimaliseerd is hij weer smal (121px i.p.v. volle breedte), nog steeds in de eigen laag boven +/borden
- Hardening: een onbekende achtergrond-waarde in opgeslagen borddata crashte de hydrate en logde de gebruiker stilletjes uit bij verversen - valt nu terug op de standaardachtergrond
- Geverifieerd in iPhone-emulatie (8 checks): geen venster na verversen, veeg pant, tik kiest, tekenbalk smal en vrij van +

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-16 08:09:33 +02:00
Ramon
7bd35685fe feat: sluitknop op geminimaliseerde widgets in de dock (v0.3.63-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 45s
- Elke dock-pil (geminimaliseerde widget) heeft nu een eigen ✕ om de widget direct te sluiten, zonder hem eerst terug op het bord te zetten
- Klik op de pil zelf blijft de widget herstellen; de ✕ deelt hetzelfde sluitpad als de ✕ in de widgetkop
- Groter tikvlak voor de ✕ op aanraakschermen; titels wisselen mee met de taal
- Geverifieerd in headless Chromium (8 checks, incl. serialisatie na sluiten/herstellen)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-16 07:50:41 +02:00
Ramon
87b22efc56 fix: tekstzoom terug op telefoon en tekenbalk botst niet meer met +/borden (v0.3.62-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 28s
- A−/A+ (tekstzoom, toegankelijkheid) staan weer bovenaan in de telefoon-balk; alleen het percentage, de catalogusknop en volledig scherm blijven daar verborgen (opslaan-knop zonder minimumbreedte zodat alles past, ook op 320px)
- Echte oorzaak van het steeds opende afbeeldingen-/bordenscherm gevonden: op telefoons lagen de +-knop en de paarse bordenknop bovenop de tekenbalk - wissen/sluiten aantikken opende dus het +-menu of het bordenpaneel
- De tekenbalk ligt op telefoons nu in een eigen laag bóven de knoppenrij (groeit omhoog, dus veilig bij elke schermbreedte); + en borden houden de onderrand
- Geverifieerd op 390px én 320px (8 checks): geen overlap, sluit-/wisknop raakt echt de tekenbalk, balk past volledig, A−/A+ zichtbaar

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-16 07:44:38 +02:00
Ramon
8b76cf3b4b fix: menubalk past op telefoon - afbeeldingenscherm opent niet meer per ongeluk (v0.3.61-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 26s
- Op telefoons (≤600px) liep de menubalk 143px over: 💾 Opslaan en ⚙ Instellingen vielen buiten beeld en 🖼 (afbeeldingscatalogus) zat precies onder de duim, waardoor het schermvullende afbeeldingen/upload-scherm steeds per ongeluk opende
- Telefoon-balk toont nu alleen de essentie (🔊, 📁, 💾, ⚙); zoom en de catalogusknop verdwijnen daar - afbeeldingen blijven bereikbaar via het +-menu
- Afbeeldingscatalogus: titelbalk met sluitknop blijft vastgeplakt in beeld tijdens scrollen, en op mobiel 100dvh i.p.v. 100vh zodat er niets achter de browserbalk wegvalt
- Geverifieerd in iPhone-emulatie (5 checks): balk past volledig, opslaan/instellingen bereikbaar, sluitknop blijft zichtbaar, catalogus via +-menu

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-16 07:25:58 +02:00
Ramon
bdb5ef9875 feat: instellingen volledig herbouwd - sectieregister, rolweergaven en zoeken (v0.3.60-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 34s
- Nieuwe architectuur: één sectieregister (settings.js) als enige bron van waarheid; secties melden zich aan met rol-zichtbaarheid, activate() en zoekgegevens - de schil kent het beheer niet van binnen
- Nieuwe indeling: navigatie links (gegroepeerd Persoonlijk / School / Systeem), inhoud rechts met sectiekop + toelichting; zakelijk en rustig, responsief op smalle schermen
- Alles doorzoekbaar: één zoekbalk vindt secties, instellingen (trefwoorden, o.a. wachtwoord/thema/taal) én data - gebruikers, klassen en scholen op naam; klik navigeert en filtert de gebruikerslijst direct op de treffer
- Per rol alleen de essentie: gast alleen Weergave; leerling en ouder Account + Weergave; groepsleiding + Gebruikers/Toewijzingen/Voortgang; schoolbeheerder ook Klassen; systeemmanager ook Systeem en Opslag
- Thuiskinderen (leerling zonder school) krijgen nu zoals ontworpen het volledige whiteboard met eigen opslag en reservekopieën; schoolleerlingen blijven beperkt tot de toewijzing
- Oude dubbele tabbalk en #adminTabsSlot verwijderd; beheerdata laadt één keer per bezoek en wordt tussen secties hergebruikt
- 28 Playwright-checks over zes rollen (navigatie, zoeken, filteren, thuiskind-whiteboard)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-16 07:07:53 +02:00
Ramon
42fc323ab1 fix: toon gekoppelde ouders in de gebruikerslijst van de school (v0.3.59-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 28s
- Ouders (school_id NULL) vielen buiten elke schoolgescopeerde gebruikerslijst; /admin/users levert nu ook de ouders die aan leerlingen van de school gekoppeld zijn (groepsleiding: alleen ouders van leerlingen uit eigen klassen)
- Ouder-rijen tonen de namen van hun gekoppelde kinderen (childNames)
- Servertest voor de gekoppelde-ouders-lijst

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-16 00:23:51 +02:00
Ramon
5f9ffbfdde feat: client-kant ouderportaal (v0.3.58-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 37s
- Derde tab "Ouders" in het inlogscherm: ouderaccount registreren (naam + 2× wachtwoord) met directe login
- Nieuw ouderportaal (parent.js): kinderenlijst met school/klas of "thuis", kind toevoegen (wachtwoord eenmalig als badge bij de rij), koppelen via ouder-code, aanmelden bij een school via doorzoekbare lijst met aanmeldstatus op de rij, voortgang per kind uitklapbaar, ontkoppelen met bevestiging
- Beheer: blok "Aanmeldingen (n)" bovenaan de Gebruikers-tab met per verzoek adopteren (klas + optionele andere gebruikersnaam bij naamconflict), koppelen aan een bestaande leerling, of afwijzen
- Beheer: actie "👪 Ouder-code" op elke leerlingrij (eenmalige code als badge); groep "Ouders" in de gebruikerslijst
- Foutmeldingen in het inlogscherm nu zichtbaar op alle drie de tabs
- i18n nl/en; volledige ouder- en schoolflow geverifieerd in headless Chromium (21 checks)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-16 00:14:05 +02:00
Ramon
9d83e13bc3 feat: server-kant ouderportaal (v0.3.57-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 36s
- Nieuwe rol parent (school_id NULL): registreert zichzelf via POST /auth/register-parent (rate-limited, stafwachtwoordregels, directe sessie)
- Migratie 013: parent_children-koppeltabel, eenmalige users.parent_code, school_link_requests (aanmeldverzoeken, uniek per kind+school)
- Ouderroutes: kinderen bekijken (met school/klas/aanmeldstatus), thuiskind aanmaken (pupil zonder school, wachtwoord eenmalig terug), koppelen via ouder-code, aanmelden bij school, ontkoppelen
- Staf: ouder-code genereren per leerling, aanmeldingen bekijken en afhandelen (adopteren in klas met optionele nieuwe gebruikersnaam, koppelen aan bestaande leerling met opruiming van lege thuisaccounts, afwijzen)
- Voortgang: thuiskind zonder toewijzing logt met de gekoppelde ouder als leerkracht (bord "thuis"); gekoppelde ouders mogen de voortgang van hun kind opvragen
- 18 nieuwe servertests (registratie, koppelmodel, aanmeldflow, resolve-acties, voortgang)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-16 00:02:16 +02:00
Ramon
a9bf2270d0 fix: beperk rollen en klaskoppeling tot staf (v0.3.56-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 38s
- Functie-select in gebruikersbeheer alleen zichtbaar voor staf-rollen (teacher/admin/super)
- Klaskoppeling-UI alleen voor staf-rollen
- Server: rolwijziging alleen tussen staf-rollen onderling (leerlingen en toekomstige ouder-accounts kunnen nooit promoveren)
- Server: klaskoppeling weigert alle niet-staf-rollen expliciet
- canPersist() sluit de komende ouderrol uit van borden-opslag
- Servertest: ouder-achtige rol krijgt geen promotie en geen klaskoppeling

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
2026-07-15 23:56:39 +02:00
Ramon
0d644ba41d feat: voeg afbeeldingscatalogus en opslagquota toe
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 27s
2026-07-15 23:45:17 +02:00
Ramon
3ad7c80bad feat: koppel geldniveaus aan realistische winkelprijzen
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 24s
2026-07-15 22:51:49 +02:00
Ramon
54a95b2c19 revert: herstel officiële biljetten 2026-07-15 22:36:42 +02:00
Ramon
ff91ae0f2b test: gebruik eigen oefenbiljetten zonder markering
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 38s
2026-07-15 22:32:20 +02:00
Ramon
2742196c11 feat: toon echt euromateriaal in geldwidget
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 47s
2026-07-15 22:19:15 +02:00
Ramon
2fa9879107 feat: maak geld realistischer en uitsleepbaar
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 23s
2026-07-15 21:59:38 +02:00
Ramon
c2e6622546 feat: breid geldrekenen uit met niveaus en spellen
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 21s
2026-07-15 21:45:40 +02:00
Ramon
2d176cfc30 feat: voeg 24-uursoptie aan oefenklok toe
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 30s
2026-07-15 21:31:57 +02:00
Ramon
d900c25339 feat: breid niveau drie en wijzerbediening uit
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 23s
2026-07-15 21:10:31 +02:00
Ramon
e0220cdd00 fix: toon klokniveaus en verplaats koppeling
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 32s
2026-07-15 21:03:59 +02:00
Ramon
6e920e32db feat: koppel klokwijzers en start direct
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 20s
2026-07-15 20:53:47 +02:00
Ramon
cd15c0435b feat: voeg klokniveaus en typmodus toe
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 21s
2026-07-15 20:47:22 +02:00
Ramon
6e17dc5982 fix: voeg vrije uurmarkeringen toe
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 21s
2026-07-15 20:38:30 +02:00
Ramon
54782781bf fix: voorkom overlap op klokwijzerplaat
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 50s
2026-07-15 20:35:49 +02:00
Ramon
4cc8d466b8 feat: voeg sleepbare tijdblokken toe
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 27s
2026-07-15 20:32:12 +02:00
Ramon
72ea486518 feat: verbeter klokopdrachten en bediening
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 44s
2026-07-15 20:23:21 +02:00
Ramon
d02f7de72d feat: maak oefenklok visueel bedienbaar
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 20s
2026-07-15 20:12:43 +02:00
Ramon
3efe57b43c feat: breid oefenklok uit met niveaus en spellen
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 27s
2026-07-15 20:05:50 +02:00
Ramon
7287e49e0c v0.3.38-beta: koppel ook schoolbeheerders en systeemmanagers aan klassen
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 23s
- De klas-koppeling (chips + "voeg toe aan klas…") staat nu bij elke
  staf-rol, niet alleen bij groepsleiding; de klasnamen staan ook in de
  rijkop ("Schoolbeheerder · Groep 4")
- Server: /admin/classes/:id/teachers accepteert alle staf-rollen; alleen
  leerlingen niet (die horen al bij één klas). Een systeemmanager is
  school-onafhankelijk en mag aan klassen van elke school; overige staf
  moet bij dezelfde school horen
- Nieuwe servertest: admin en super koppelen ok, leerling en staf van een
  andere school geweigerd
2026-07-15 16:35:55 +02:00
Ramon
804ffb98f6 v0.3.37-beta: één functie per gebruiker + klas-koppeling voor groepsleiding
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 35s
- Het extra-functies-systeem (chips + select door elkaar) is uit de UI:
  een gebruiker heeft één functie, alleen te wijzigen via de Functie-select
- Groepsleiding is nu eenvoudig aan klassen te koppelen: huidige klassen
  als chips met ✕, een select "voeg toe aan klas…" voor nieuwe koppelingen;
  de klassen staan ook in de rijkop ("Groepsleiding · Groep 4")
- /admin/users geeft de klas-koppelingen (class_teachers) per gebruiker mee
- Formulier-fouten (bv. ongeldige gebruikersnaam bij een nieuwe
  systeemmanager) verschijnen nu direct bij het formulier in plaats van
  onzichtbaar bovenaan - dit maakte aanmaken schijnbaar onmogelijk
- Wachtwoord/koppelcode van een nieuwe gebruiker verschijnt direct bij die
  gebruiker in de lijst (rij en groep klappen vanzelf open)
2026-07-15 16:20:21 +02:00
Ramon
df921d5968 v0.3.36-beta: scholen mét schoolbeheerders in het Systeem-tabblad
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 33s
- Systeem toont per school een rij in klassen-stijl (🏫, accentkleur,
  aantal schoolbeheerders) met daaronder de schoolbeheerders als duidelijk
  onderscheiden subrijen (🛡, ingesprongen, eigen randkleur)
- Beide zijn in dit menu aan te passen via hetzelfde ✏-gereedschap:
  de school heeft een Naam-veld (nieuwe route PATCH /admin/schools/:id,
  alleen systeemmanager) en de bevestigde verwijder-actie; beheerders
  hebben hun volledige gereedschap (functie, extra functies, code,
  wachtwoord, verwijderen)
- Systeem-tab laadt beheerders van álle scholen, ook als de contextbalk
  elders op één school staat
- Drie nieuwe servertests voor het hernoemen (super ok, admin 403,
  naamvalidatie)
2026-07-15 16:02:57 +02:00
Ramon
b2ca4f93d5 v0.3.35-beta: schollenlijst in Systeem + verborgen formulieren + gelijke maten
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 32s
- Systeem-tabblad toont nu een overzicht van alle scholen als volwaardige
  rijen met een bevestigde verwijder-actie
- Het school-toevoegen-formulier zit achter een "+ School"-knop (zelfde
  patroon als gebruikers) en houdt de naam-bevestiging; de uitlegtekst is
  weg - teksten kort en krachtig
- Alle formulieren en knoppen in het beheer op dezelfde controlhoogte;
  fix: de rij-flexregels van invoervelden drukten velden binnen een
  veldlabel (kolom-flex) verticaal plat
- Ontbrekende veldlabel-tekst "Naam" toegevoegd (nl/en)
2026-07-15 15:53:10 +02:00
Ramon
03b8e6a41b v0.3.34-beta: één interactiesysteem in het beheer + veilige acties
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 22s
- Elk invoerveld/select/knopgroep in het beheer heeft nu een zichtbaar
  mini-label (field-helper): Functie, Extra functies, Klas, Wachtwoord,
  Naam, Acties - een placeholder alleen is geen toelichting meer
- Acties met gevolgen (nieuw wachtwoord, nieuwe koppelcode, verwijderen,
  wachtwoord-resets) zijn een aparte knopklasse: rood-zacht, tooltip die
  het gevolg uitlegt, en een tweede klik als bevestiging (arm-then-confirm,
  wapening loopt na 4s af); alle native confirm()-dialogen zijn vervangen
- Nieuw gegenereerde wachtwoorden/koppelcodes verschijnen direct bij de
  gebruiker in de rij (badge met kopieerknop, rij klapt vanzelf open),
  niet meer bovenaan; de topbalk is alleen nog voor fouten
- Klassen zijn volwaardige rijen op dezelfde maat als de gebruikersvakken,
  met leerlingaantal en de reset/verwijder-acties als bevestigde knoppen
- Systeem-tabblad: schoolkiezer-balk daar verborgen (school-onafhankelijk);
  school aanmaken heeft nu een uitleg, een naam-label, een knop die pas
  actief wordt bij een echte naam en een bevestigingsstap met de naam erin
- Toevoegen van gebruikers/klassen: knop pas actief bij geldige naam
2026-07-15 15:37:05 +02:00
Ramon
75244cde14 v0.3.33-beta: overzichtelijker beheerpaneel zonder tab-afhankelijkheid
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 21s
- Vaste schoolkiezer-balk (🏫) boven alle beheer-tabs voor de systeem-
  manager: geen omweg meer via het Systeem-tabblad om een school te kiezen;
  de kiezer is uit het Systeem-tabblad verdwenen en de hint verwijst naar
  de balk erboven
- Gebruikers-tab: leerlingen staan onder inklapbare groepskoppen per klas
  (standaard dicht, met aantal en ▸/▾-pijl); zoeken kijkt altijd door alle
  groepen heen zodat een treffer nooit verstopt zit
- Gebruikersrijen tonen standaard alleen naam en rol/klas; alle knoppen en
  velden (rol wijzigen, extra functies, klas, wachtwoord, koppelcode,
  verwijderen) zitten achter een ✏-knopje per rij; uitgeklapte staat blijft
  behouden over her-renders
2026-07-15 05:03:30 +02:00
Ramon
54eb8b18cb v0.3.32-beta: één tabbalk in Instellingen (geen geneste beheer-tabs meer)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 21s
- De beheer-tabs (Systeem/Klassen/Gebruikers/Toewijzingen/Voortgang) staan
  nu als losse knoppen direct in de instellingen-tabbalk naast Account en
  Weergave, in plaats van een tweede tabbalk binnen het Beheer-tabblad
- admin.js vult #adminTabsSlot en herbouwt bij gebruikers-/taalwissel;
  data wordt geladen bij binnenkomst in het beheer-deel, wisselen tussen
  beheer-tabs onderling hergebruikt de geladen data
- De losse "Beheer"-knop en de geneste .ed-tabs-balk zijn verwijderd;
  de tabbalk loopt om op smalle schermen (flex-wrap)
2026-07-15 04:37:15 +02:00
Ramon
d5e0b361bf v0.3.31-beta: geef ankerdiagrammen een prominentere plek
- Ankerwidget staat nu vooraan in de widgetbibliotheek (eerste kaart)
- Nieuw -item in het +-menu: plaatst direct een ankerdiagram op het bord
- Duidelijkere uitleg wat een ankerdiagram is (woordweb voor
  woordenschatonderwijs: woorden in samenhang rond een themawoord leren),
  in de bibliotheekkaart, de widget-hint en het +-menu-label (nl/en)
2026-07-15 04:32:25 +02:00
Ramon
6d5d4ef520 v0.3.30-beta: ankerdiagrammen als vaste tak in de hoofdverkenner
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 37s
- Nieuwe generieke withBranch-combinator (explorer.js): een tweede adapter
  verschijnt als vaste map in de root van de hoofdboom; item-sleutels worden
  geprefixt en slepen tussen de takken wordt geweigerd (ander soort inhoud)
- De borden-verkenner (📁 in de menubalk) toont nu " Ankerdiagrammen" met
  daarin de eigen diagrammappen én de gedeelde 🌐/🏫-takken voor ankers -
  borden en ankerwoorden op één plek, met dezelfde regels (slepen, kopiëren,
  alleen-lezen, één-klik-mappen)
- Diagram openen vanuit de hoofdverkenner plaatst een nieuw ankerwidget op
  het huidige bord met dat diagram geladen; het 🗂-knopje op een widget
  blijft in dát widget laden (adapter naar module-niveau getild met een
  onOpen-parameter, gedeeld door beide ingangen)
2026-07-15 04:12:43 +02:00
Ramon
a919e31e4c v0.3.29-beta: alleen-lezen in de verkenner + mappen openen met één klik
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 21s
- Gedeelde items die je niet mag beheren tonen een 🔒 en weigeren hernoemen/
  verplaatsen/verwijderen direct in de client met een duidelijke melding
  (kopiëren blijft altijd werken); mappen met vergrendelde inhoud tonen 🔒
- Nieuwe knop "🔒 Alleen lezen" in de school-tak, alleen zichtbaar voor
  schoolbeheerder/systeemmanager: vergrendelt het geselecteerde item of
  (zonder selectie) de hele huidige map, en ontgrendelt bij tweede keer
- Mappen openen nu met één klik (Windows-webstijl); slepen blijft werken
  via de bewegingsdrempel en de klik direct ná een sleep wordt genegeerd;
  hernoemen/verwijderen zonder selectie werkt op de huidige map (via het
  broodkruimelpad)
2026-07-14 22:53:47 +02:00
Ramon
24180beec1 v0.3.28-beta: alleen-lezen-vlag voor gedeelde items (server)
- Nieuwe migratie db/011: shared_library.readonly (default false)
- Beheerregel aangescherpt: een vergrendeld school-item is ook voor de
  eigenaar alleen-lezen; alleen schoolbeheerder en systeemmanager kunnen
  het nog wijzigen (systeem-items waren al alleen-lezen voor iedereen
  behalve de systeemmanager) - kopiëren blijft altijd werken
- De vlag zetten/weghalen kan per item (PATCH readonly) of per map
  (PATCH /shared/:kind/folder met {from, readonly}), uitsluitend door de
  schoolbeheerder van die school of de systeemmanager
- Lijst/detail geven readonly + canLock terug voor de UI; drie nieuwe tests
2026-07-14 22:49:17 +02:00
Ramon
e21bc177e1 v0.3.27-beta: gedeelde bibliotheek in de mappenverkenner
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 21s
- withSharedBranches (explorer.js): wikkelt een eigen-inhoud-adapter en
  voegt in de root de vaste takken "🌐 Systeem" (systeembreed) en
  "🏫 School" (binnen de eigen school) toe
- Slepen tussen bomen = kopiëren: eigen bord/diagram of hele map naar een
  gedeelde tak publiceert (POST /shared, kopie - origineel blijft); gedeeld
  item of map naar de eigen boom kopieert (borden krijgen nieuw id + verse
  wids); binnen een tak is slepen echt verplaatsen (PATCH, server bewaakt)
- Dubbelklik op een gedeeld bord kopieert en opent het; op een gedeeld
  ankerdiagram laadt het in het widget
- Verkenner ondersteunt nu async adapters (init/mutaties), groene
  ok-meldingen ("Gedeeld ✓"/"Gekopieerd ✓") en een eigenaar-hint per tegel
- Gasten en leerlingen zien alleen de eigen boom
2026-07-14 22:34:54 +02:00
Ramon
939c4ee68b v0.3.26-beta: server-API voor de gedeelde bibliotheek
- Nieuwe migratie db/010: shared_library-tabel (school_id NULL =
  systeembreed, anders per school; kind board/anchor; folder als pad)
- Routes: GET /shared/:kind (lijst, gefilterd op systeembreed + eigen
  school), GET /shared/:kind/:id (met data, voor kopiëren), POST (publiceren;
  scope global vereist systeemmanager, scope school staf met school),
  PATCH/DELETE per item en PATCH /shared/:kind/folder (prefix-rewrite,
  alleen als álle items onder het pad beheerd mogen worden)
- Beheerregel: systeemmanager altijd, eigenaar altijd, schoolbeheerder voor
  items van de eigen school; leerlingen hebben geen toegang
- Nieuwe permissies shared.global.manage / shared.school.manage (beide
  permissions.js); zes nieuwe servertests
2026-07-14 22:27:16 +02:00
Ramon
6de569c892 v0.3.25-beta: hernoem rol naar systeemmanager
- roleSuper heet nu "Systeemmanager" (en: "System manager"); het
  beheertabblad en de stafgroep "Overkoepelend" heten nu "Systeem"
- Alle overige teksten en commentaren met "overkoepelend beheerder"
  meegenomen; de interne rolcode "super" blijft ongewijzigd
2026-07-14 22:24:34 +02:00
Ramon
86478ebaca v0.3.24-beta: bewaar ankerdiagrammen los van het bord (bibliotheek)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 18s
- 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)
2026-07-14 22:14:37 +02:00
Ramon
e327049f1b v0.3.23-beta: beheer woordthema's in dezelfde mappenverkenner
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 33s
- 🗂-knop in de woorden-editor opent de Windows-stijl verkenner met de
  thema's van de actieve taal (★-tegels met woordaantal); dubbelklik
  selecteert het thema in de editor
- Geneste mappen via het folder-veld als pad; nieuwe lege mappen bestaan
  sessie-lokaal tot er een thema in wordt gezet (mappen zijn afgeleid van
  de themavelden)
- Ankerthema's staan vast in de map "": niet versleepbaar, hernoemen/
  verwijderen geweigerd (anchor.js beheert ze)
- Het 📁-knopje + inline map-invoer uit v0.3.21 zijn vervangen door de
  verkenner; de optgroup-weergave in de dropdowns blijft en toont het pad
2026-07-14 22:01:59 +02:00
Ramon
1151c71049 v0.3.22-beta: Windows-stijl mappenverkenner voor borden
- Nieuw public/js/explorer.js: generieke verkenner-modal met broodkruimelpad,
  werkbalk (nieuwe map/nieuw item/hernoem/verwijder met bevestigingsstap),
  raster van map- en itemtegels, dubbelklik om te openen en slepen om te
  verplaatsen (pointer-events, werkt op touch; drop-doel licht op)
- Geneste mappen zonder datamigratie: nesting zit in de mapnaam als pad met
  "/"-scheiding; server en toewijzingen (bord-id's) blijven onaangeroerd;
  "/" wordt geweigerd in nieuwe namen
- Borden-adapter in board.js: bordtegels met boardThumb-preview, actief bord
  gemarkeerd en gevolgd bij verplaatsen/hernoemen (prefix-rewrite), map
  verwijderen alleen als leeg, laatste bord onverwijderbaar
- 📁-knop in de menubalk opent de verkenner; het oude mappenpaneel
  (renderBoardsPanel + #boardsPanel + CSS) is vervangen; de boardStrip-
  snelwissel blijft; curSlot() heelt lege actieve mappen zelf
2026-07-14 21:58:24 +02:00
Ramon
f2243c13c4 v0.3.21-beta: groepeer woordthema's in mappen
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 18s
- 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)
2026-07-14 16:35:22 +02:00
Ramon
18775ae0ec v0.3.20-beta: hernoem mappen en verplaats borden tussen mappen
- Bordenpaneel: dubbelklik op een mapnaam hernoemt de map (zelfde
  inline-input-patroon als bord hernoemen)
- Nieuw 📁-knopje per bord toont een inline select met de overige mappen;
  bij keuze verhuist het bord, het actieve bord volgt mee (BS.f/BS.b
  worden gecorrigeerd) en een leeggeraakte map krijgt een vers bord
- renameTip vermeldt nu ook mappen; nieuwe i18n-tekst moveBoardTip (nl/en)
2026-07-14 16:29:25 +02:00
Ramon
2ce82ab998 v0.3.19-beta: verstuur bij het opslaan alleen gewijzigde secties
- Nieuwe route PUT /me/data/:section (boards/themes/generalExtra): vervangt
  alleen die sub-key via jsonb_set, met dezelfde rev/409-controle en
  snapshot-hook als de volledige PUT (die blijft bestaan als terugval)
- Per-sectie size-caps (boards 20MB, themes/generalExtra 2MB)
- core.js: DIRTY-set houdt bij wat er gewijzigd is; woordwijzigingen sturen
  alleen themes+generalExtra, bordwijzigingen alleen boards (sequentieel,
  rev per respons bijgewerkt); zonder gerichte wijziging volledige save
- Twee nieuwe servertests (jsonb_set op de juiste sub-key, onbekende sectie
  is 404)
2026-07-14 16:26:16 +02:00
Ramon
6980d72041 v0.3.18-beta: bewaar en herstel eerdere versies van borddata
- Elke data-write bewaart eerst de oude data in user_data_versions (hooguit
  één snapshot per 10 minuten, maximaal 10 versies per gebruiker)
- Nieuwe routes: GET /me/data/versions (lijst) en POST /me/data/restore
  (zet een versie terug; bewaart de huidige data eerst zodat herstel zelf
  ook omkeerbaar is); beide niet voor leerlingen
- Nieuw blok "Reservekopieën" in Instellingen → Account met datum/tijd per
  versie en een herstelknop met bevestigingsstap (arm-then-confirm)
- Vier nieuwe servertests (snapshot vóór write, opruimen op 10, pupil-403,
  restore bewaart huidige data eerst)
2026-07-14 16:23:10 +02:00
Ramon
b28d25296d v0.3.17-beta: detecteer save-conflicten tussen tabbladen
- Nieuwe migratie db/009: users.data_rev + data_updated_at en de
  user_data_versions-tabel (voor de komende versiegeschiedenis)
- GET /me/data geeft nu {data, rev}; PUT /me/data controleert een optionele
  x-data-rev-header en weigert een verouderde schrijver met 409 (zonder
  header blijft het oude laatste-schrijver-wint-gedrag voor oude clients)
- core.js: rev wordt bijgehouden en meegestuurd; bij 409 toont de opslaan-
  knop "Elders bewerkt" en haalt een klik eerst de nieuwste serverversie op
  in plaats van te overschrijven
- beforeunload-waarschuwing bij een openstaande, lopende of mislukte save
- Nieuwe test test/save-system.test.js (rev mee, 409, legacy zonder header)
2026-07-14 16:19:25 +02:00
Ramon
d0ab0b2483 v0.3.16-beta: deel één widget via de Toewijzingen-tab
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 34s
- assignmentRow: na bordkeuze verschijnt een widget-select ("heel bord" of
  één widget van dat bord, client-side uit BS-borddata); bij een widgetkeuze
  een modus-select (kijken/werken); PUT-body krijgt widgetId + mode mee
- Widgets zonder wid (borden van vóór v0.3.13) worden overgeslagen tot de
  eerstvolgende save van dat bord
- Infotekst toont nu ook widget en modus van de huidige toewijzing
- Nieuwe i18n-teksten amWholeBoard/amModeWatch/amModeWork (nl/en)
2026-07-14 15:53:31 +02:00
Ramon
873f8e0bee v0.3.15-beta: kijken/werken-modus in de leerling-omgeving
- pupil.js: verwerkt de modus uit /my/assignment; "kijken" toont een
  meekijk-badge, blokkeert alle interactie en logt geen voortgang; "werken"
  (default) is het bestaande gedrag met voortgang-logging
- Poll-interval dynamisch: 5s in kijken-modus (wijzigingen van de leerkracht
  komen vlot door), 20s in werken-modus; moduswissel her-rendert direct
- teach.css: generieke kijken-overlay (pointer-events:none op de widget-body),
  werkt voor elk widgettype zonder eigen readonly-ondersteuning
- Nieuwe i18n-tekst pupilWatching (nl/en)
2026-07-14 15:51:20 +02:00
Ramon
b13fe65da5 v0.3.14-beta: sta toewijzing van één widget toe met kijken/werken-modus (API)
- Nieuwe migratie db/008_widget_share.sql: assignments krijgt widget_id
  (NULL = heel bord, bestaand gedrag) en mode ('kijken'/'werken',
  default 'werken')
- PUT /assignments/class/:id en /assignments/pupil/:id accepteren optioneel
  widgetId + mode; gedeelde validatie (parseShareBody) controleert dat de
  widget echt op het opgegeven eigen bord staat
- describeAssignment geeft widgetId/widgetType/mode terug voor de UI
- GET /my/assignment filtert bij een widget-toewijzing tot die ene widget en
  stuurt de modus mee
2026-07-14 15:48:14 +02:00
Ramon
d27419b9f6 v0.3.13-beta: geef widgets een stabiel instantie-id (wid)
- board.js: genWidgetId() naar het voorbeeld van genBoardId(); toegekend in
  makeWidget (bestaand wid uit opgeslagen data wint), bewaard in
  serializeBoard; dupliceren geeft een nieuw wid; oude borden zonder wid
  krijgen er vanzelf een bij de eerstvolgende save
- /my/assignment stuurt wid mee per widget
- pupil.js: voortgang-widgetId gebruikt nu het wid (terugval op positie voor
  borden van vóór deze versie)

Voorbereiding op het delen van één losse widget (roadmap item 8).
2026-07-14 15:46:15 +02:00
Ramon
be89a7b498 v0.3.12-beta: herstel sessie na refresh (regressie beveiligingsaudit)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 27s
De beveiligingsaudit (v0.3.03-beta) verving de client-side Bearer-token
(localStorage) door een HttpOnly session-cookie, maar de sessie-herstel-IIFE
in app.js las nog steeds de verwijderde TOKEN-variabele uit - een
ReferenceError die de hele boot-restore liet crashen. Resultaat: elke
paginaverversing verloor de ingelogde staat, ook al bleef de cookie zelf
geldig. Fix: geen client-side tokencheck meer nodig (de HttpOnly-cookie is
sowieso niet leesbaar door JS en gaat vanzelf mee); /auth/me wordt nu altijd
geprobeerd en valt bij een 401 terug op gastmodus.
2026-07-14 15:31:17 +02:00
Ramon
aac7fd1c68 v0.3.11-beta: toon leerlingvoortgang in nieuw admin-tabblad
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 17s
- Nieuwe "Voortgang"-tab naast Toewijzingen: leerkracht kiest een leerling
  (zelfde per-klas gegroepeerde select) en ziet de volledige tijdlijn van
  GET /progress/pupil/:id (widgettype, score, tijdstip)
- fillPupilSelect(): gedeelde helper voor de per-klas leerlingselectie,
  onttrokken uit Toewijzingen zodat Voortgang hem ook gebruikt
- Nieuwe i18n-teksten amProgress/amProgressEmpty (nl/en)
2026-07-14 15:05:16 +02:00
Ramon
04e9bf3847 v0.3.10-beta: rapporteer widgetvoortgang vanuit leerling-speelmodus
- board.js: fix bug waarbij de opts-parameter (readonly e.d.) nooit bij
  mountMath aankwam, doordat de registry-wrapper hem liet vallen
- math.js/letters.js/ball.js: nieuwe opts.onProgress-callback, aangeroepen
  bij resp. einde rekenronde, voltooid woord en geknapte ballon
- pupil.js: geeft onProgress mee bij het mounten en post het naar
  /my/progress; alleen actief in leerling-speelmodus, niet bij bewerken
2026-07-14 15:00:31 +02:00
Ramon
e3369e34a2 v0.3.09-beta: voeg voortgang-schema en -API toe
- Nieuwe migratie db/007_progress.sql: progress_events-tabel (per leerling
  per widget-moment: pogingen, goed, sterren)
- POST /my/progress: leerling logt een afgerond moment; teacher_id/board_id
  worden server-side afgeleid uit de bestaande toewijzing, nooit van de client
- GET /progress/pupil/:id en GET /progress/class/🆔 leerkracht-weergave,
  gated met nieuwe progress.view-permissie en dezelfde scoping als assignments
- Permissie progress.view toegevoegd in beide permissions.js-bestanden
2026-07-14 14:51:50 +02:00
Ramon
a20779e602 v0.3.08-beta: breid widgetbibliotheek uit
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 37s
- voeg acht educatieve en klaswidgets toe\n- verbeter galerij met zoeken, favorieten en lessjablonen\n- voeg dupliceren, vergrendelen en voorbeeldweergave toe\n- voeg geschiedenis en begrensde bordexport/import toe\n- verbeter responsiviteit, toegankelijkheid en vertalingen
2026-07-14 14:16:49 +02:00
Ramon
6e41ea1b2e v0.3.07-beta: voeg educatieve widgets toe
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 29s
2026-07-14 13:39:57 +02:00
Ramon
6097d7c9f1 v0.3.06-beta: herstel bereikbaarheid via Pangolin
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 17s
2026-07-14 13:26:15 +02:00