Преглед изворни кода

Sessie afgesloten: TASKS.md opgeschoond (5843->4700 regels), inzichten naar CLAUDE.md

- Afgeronde taken verwijderd: verbindingsmelding, bevinding C, P2-15 volledig,
  take(25)-limiet, code-audit. Restpunten compact bovenaan gezet.
- Nieuw in CLAUDE.md: datumveld-fix via Set Form Field, media-picker vult aan,
  adb input text-valkuilen, tijdzonecontract app<->Drupal, en dat achtergebleven
  exportmappen de build breken (161 errors) i.p.v. alleen de analyse.
- Blokkade bovenaan TASKS.md: drie verplaatste pagina's laten hun oude map
  staan; app-code daarom bewust nog niet gecommit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bob пре 22 часа
родитељ
комит
d807679ce0
2 измењених фајлова са 206 додато и 1365 уклоњено
  1. 67 6
      CLAUDE.md
  2. 139 1359
      TASKS.md

+ 67 - 6
CLAUDE.md

@@ -441,7 +441,19 @@ van hem aan te nemen.
   crash van de app zelf. Bouw bij dit toestel bewust een korte
   crash van de app zelf. Bouw bij dit toestel bewust een korte
   `sleep`/wachttijd tussen opeenvolgende input-acties in i.p.v. ze
   `sleep`/wachttijd tussen opeenvolgende input-acties in i.p.v. ze
   direct na elkaar te vuren.
   direct na elkaar te vuren.
-- **Screenshots van de website zelf (voor look&feel-vergelijkingen):
+- - **`adb shell input text` kan geen spaties en geen haakjes.** Gebruik `%s`
+  voor elke spatie (`input text "TEST%svan%sClaude"`); een letterlijk haakje
+  geeft `/system/bin/sh: syntax error: unexpected '('` en dan blijft het veld
+  leeg zonder dat de app iets merkt. Controleer na het typen met een
+  screenshot, niet met de exit-code.
+- **De custom actions loggen hun eigen verzoek.** `evenementCreate`,
+  `stadsactiviteitCreate` en `bestandUpload` printen `URL`, `BODY`, `STATUS` en
+  het antwoord naar `I flutter` — dus met `adb logcat -c` vlak vóór de tik en
+  daarna `adb logcat -d | grep "I flutter"` zie je exact wat de app verstuurde
+  en welk nid eruit kwam. Veruit de snelste manier om een indiening na te gaan;
+  je hoeft de node niet eerst op de site te zoeken.
+
+**Screenshots van de website zelf (voor look&feel-vergelijkingen):
   headless Chromium werkt, maar met twee valkuilen.** (1) Snap-Chromium
   headless Chromium werkt, maar met twee valkuilen.** (1) Snap-Chromium
   mag niet schrijven in `/tmp/claude-*` (Permission denied) — schrijf
   mag niet schrijven in `/tmp/claude-*` (Permission denied) — schrijf
   naar `~/uk-shots/` o.i.d. (2) De homepage opent met een
   naar `~/uk-shots/` o.i.d. (2) De homepage opent met een
@@ -1031,11 +1043,23 @@ direct ná het openen van het contextmenu landt niet — het veld staat er dan
 wel al bewerkbaar bij. Typ gewoon nog een keer en kijk met een `zoom` of de
 wel al bewerkbaar bij. Typ gewoon nog een keer en kijk met een `zoom` of de
 nieuwe naam er staat vóór je Return drukt. Verplaatsen naar een andere map
 nieuwe naam er staat vóór je Return drukt. Verplaatsen naar een andere map
 gaat via hetzelfde menu (**Move to Folder**) en is even ongevaarlijk.
 gaat via hetzelfde menu (**Move to Folder**) en is even ongevaarlijk.
-⚠️ **Wat een export NIET doet, is de oude map opruimen:** na een hernoem/
-verplaats blijft `lib/<oude pad>/` gewoon in de working tree staan. Zo'n
-restant kan een `dart analyze`-fout opleveren (een dode kopie die naar de
-verdwenen klasse verwijst). Niet zelf weggooien — melden aan Bob, zie de
-opschoonregel onderaan dit bestand.
+⚠️ **Wat een export NIET doet, is de oude map opruimen — en dat BREEKT de
+build, niet alleen de analyse.** Na een hernoem/verplaats blijft `lib/<oude
+pad>/` gewoon staan, terwijl `index.dart` al naar het nieuwe pad exporteert.
+Beide libraries definiëren dan dezelfde klasse en je krijgt
+`ambiguous_import` plus een reeks `argument_type_not_assignable`. Bevestigd
+2026-09-14: drie pagina's die Bob naar de map `kanweg` verplaatste leverden
+**161 errors** op in een verder gezonde working tree — vóór die export 0. De
+app compileert dan niet meer, dus dit is een harde blokkade en geen ruis.
+- Herkenbaar aan het patroon: alle fouten wijzen naar de **oude** map, de
+  foutmelding noemt letterlijk twee libraries, en de tweede is `via
+  package:uitgaanskrant/index.dart`.
+- Snel vaststellen wélke mappen: `fvm dart analyze lib/ 2>&1 | grep "error -" |
+  sed 's/.*error - //' | cut -d/ -f1-2 | sort | uniq -c`.
+- **Claude gooit ze niet weg** (staande regel), ook niet met `git rm`. Meld de
+  exacte paden aan Bob met een kant-en-klaar commando; het is voor hem één
+  regel. Commit in de tussentijd géén `lib/` — anders staat er een
+  niet-compileerbare staat op master.
 
 
 **Een sectie in een "Set from Variable"-bronnenlijst rendert zijn suboptie
 **Een sectie in een "Set from Variable"-bronnenlijst rendert zijn suboptie
 pas na 8-15 seconden — blijf niet klikken, want elke klik klapt 'm weer
 pas na 8-15 seconden — blijf niet klikken, want elke klik klapt 'm weer
@@ -2013,6 +2037,34 @@ kind ván de bestaande Row, en geef het geen vaste hoogte.
   **`ScaffoldMessenger`** toont (zie `VerbindingsBanner` in `snippets/`). Neemt
   **`ScaffoldMessenger`** toont (zie `VerbindingsBanner` in `snippets/`). Neemt
   geen layout-ruimte in, dus het kan overal staan zonder iets te breken.
   geen layout-ruimte in, dus het kan overal staan zonder iets te breken.
 
 
+**Een datumveld dat de rauwe `2026-09-25 20:20:00.000` toont: fix het in de
+Set-Form-Field-actie, niet in het tekstveld.** Bevestigd 2026-09-13 op
+`uitgaansevenementAanmaken`. De Date/Time Picker zet `datePicked1` en de
+vólgende actie ("Set Form Field") schrijft dat met `.toString()` in de
+controller. Value-potlood → **Available Options: DateTime Format** → in
+*DateTime Format Options* **"custom" typen** (de optie *Custom* staat niet in
+de standaardlijst, je moet er echt op zoeken) → *Custom Format*
+`dd-MM-yyyy HH:mm` → Confirm. De preview onder het veld laat meteen zien of het
+formaat klopt.
+- **Laat het verzend-argument níét via het tekstveld lopen.** Hang
+  `datumStart`/`datumEind` van de create-action rechtstreeks aan
+  `datePicked1`/`datePicked2` met format **`yyyy-MM-dd HH:mm:ss`**. Dan is de
+  weergave vrij te kiezen zonder dat de API-waarde meeverandert, en is
+  `datumVoorApi()` op die pagina overbodig.
+- Valkuil in deze dialoog: een zoekterm in *DateTime Format Options* landt
+  regelmatig pas bij de tweede poging (het veld pakt de focus traag). Typ,
+  `zoom`, en typ zo nodig nog een keer vóór je doorklikt.
+
+**De media-knoppen op de aanmaakpagina's uploaden meteen, en "Foto's kiezen"
+VULT AAN.** Gemeten 2026-09-13: een tik op *Logo kiezen* / *Foto's kiezen* geeft
+een bronkeuze (Gallery/Camera), en direct na het kiezen gaat het bestand al via
+`bestandUpload` naar Drupal — je krijgt een fid terug vóórdat je iets indient.
+De Android-fotokiezer sluit na één foto; tik je nog een keer op *Foto's kiezen*,
+dan komt de tweede foto **naast** de eerste te staan (niet in plaats van). Wil
+je dus een node met meerdere foto's testen: gewoon meerdere keren kiezen.
+Losse fids blijven op de server staan ook als je niet indient — meld ze aan Bob
+om op te ruimen.
+
 **Eigen imports in Custom Code worden door de editor NIET gecontroleerd.**
 **Eigen imports in Custom Code worden door de editor NIET gecontroleerd.**
 Bevestigd 2026-09-13: een custom action zonder
 Bevestigd 2026-09-13: een custom action zonder
 `import 'package:http/http.dart' as http;` slaat gewoon op, de builder meldt
 `import 'package:http/http.dart' as http;` slaat gewoon op, de builder meldt
@@ -3328,6 +3380,15 @@ maakte dat er geen exposed datumfilter bestond.
     slaat de auto-invulling gewoon over (geen crash), en moet de
     slaat de auto-invulling gewoon over (geen crash), en moet de
     gebruiker de plaats 1x zelf uit de (al correct beperkte) dropdown
     gebruiker de plaats 1x zelf uit de (al correct beperkte) dropdown
     kiezen.
     kiezen.
+- **Drupal slaat `field_date` op in UTC; de app stuurt lokale (Nederlandse)
+  tijd.** De brug daartussen is `_custom_events_lokaal_naar_utc()` in
+  `custom.evenementen_aanmaken.inc`, sinds 2026-09-13 toegepast in zowel
+  `_custom_evenement_create()` als `_custom_stadsactiviteit_create()`. Zonder
+  die conversie las Drupal de binnenkomende string als UTC en toonde de site
+  'm 2 uur later (gemeten op nid 217916: 20:20 ingestuurd, 22:20 getoond; na de
+  fix op nid 219913 klopt het exact). **Gevolg voor de app-kant: blijf lokale
+  tijd sturen in `yyyy-MM-dd HH:mm:ss`** — ga daar geen UTC van maken, dan
+  verschuift het weer. Een string mét milliseconden wordt ook geaccepteerd.
 - **Geen favorietenhartje op de horeca-OVERZICHTSkaart — Bob's besluit
 - **Geen favorietenhartje op de horeca-OVERZICHTSkaart — Bob's besluit
   2026-09-13: "geeft te veel load".** Favoriet maken loopt uitsluitend via de
   2026-09-13: "geeft te veel load".** Favoriet maken loopt uitsluitend via de
   **detailpagina** (`HorecagelegenheidCurrent`); op Favorieten tab 3 staan dus
   **detailpagina** (`HorecagelegenheidCurrent`); op Favorieten tab 3 staan dus

+ 139 - 1359
TASKS.md

@@ -1,159 +1,105 @@
-## ▶ Nu aan de beurt (stand 2026-09-13)
+## ▶ Nu aan de beurt (stand 2026-09-14)
 
 
-### ✅ Verbindingsmelding in de header — AF (2026-09-13, Bob + Claude)
+*Afgerond en daarom verwijderd op 13/14 sep: verbindingsmelding in de header,
+offline-crash op de sliders (bevinding C), P2-15 volledig (evenement,
+stadsactiviteit, tijdzone, media), de `.take(25)`-limiet op alle zes
+horeca-tabs, en de code-audit op de export van 21:00 (0 nieuwe bevindingen,
+`dart analyze` 0 errors). De herbruikbare lessen staan in `CLAUDE.md`.*
 
 
-Gebouwd en live geverifieerd op de telefoon-emulator met een **verse export van
-de echte FlutterFlow-staat** (dus niet een nabouw). Alle drie de paden:
+### ⛔ EERST DIT — de working tree compileert niet (2026-09-14)
 
 
-| situatie | resultaat |
-|---|---|
-| online | geen melding, hamburger zichtbaar, **0** render-fouten |
-| offline | rode balk onderin met wolkje + "Opnieuw", **0** render-fouten, **0** `toList`-crashes |
-| netwerk terug + tik op "Opnieuw" | balk weg, Home **volledig hersteld** (slider + tabs gevuld), 0 fouten |
-
-**Hoe het in elkaar zit**, voor als het ooit aangepast moet worden:
-- App State **`geenVerbinding`** (Boolean, niet persisted).
-- Custom Action **`checkVerbinding`** — HEAD op de site-root (200, 0 bytes,
-  ±57 ms), zet de vlag via `FFAppState().update(...)`.
-- Custom Widget **`VerbindingsBanner`** — rendert zelf `SizedBox.shrink()` en
-  toont de melding via de `ScaffoldMessenger`. Staat als laatste kind in de
-  `Row` van `HeaderButtonsComponent`, in een Container van 20×20 (FlutterFlow
-  eist maten; in een Row kost dat alleen 20 px horizontaal en dat is
-  onschadelijk — **in een Column zou een hoogte wél schadelijk zijn**).
-- De code staat in `snippets/` en overleeft daar een export.
-
-⚠️ **Twee valkuilen die hier zijn opgetreden — relevant zodra er nóg iets aan
-de header toegevoegd wordt:**
-1. **Wrap de knoppen-Row NOOIT in een Column.** De header zit in een
-   `FlexibleSpaceBar.background` met een **vaste hoogte van 74 px**;
-   `Align > Row` krijgt die keurig opgelegd, maar een `Column` geeft zijn
-   kinderen **onbegrensde** hoogte — de Row neemt dan zijn natuurlijke ±200 px
-   en je krijgt `RenderFlex overflowed by 166 pixels`. **En stiller maar erger:
-   de hamburgerknop verdwijnt dan uit de header op alle elf pagina's, zonder
-   enige melding in de log.**
-2. **Eigen imports in Custom Code worden niet gecontroleerd door de editor.**
-   `checkVerbinding` heeft `import 'package:http/http.dart' as http;` nodig,
-   direct onder de `// DO NOT REMOVE`-regel. Zonder die regel slaat de editor
-   gewoon op en meldt niets — pas de Gradle-build faalt met
-   `Error: Undefined name 'http'`.
-
-📌 **Nog niet gedaan:** het widget staat alleen in `HeaderButtonsComponent`, dus
-niet op `mijnProfiel`, `wachtwoordVergeten`, `stadsactiviteitAanmaken` en
-`uitgaansevenementAanmaken`. Op de twee **aanmaakpagina's** is het wel zinvol
-(een formulier verzenden zonder netwerk) — daar het widget ergens in de
-bestaande layout zetten, het neemt toch geen ruimte in.
-
-### ✅ Audit op de export van 21:00 (2026-09-13, Claude, browserloos) — niets nieuws
-
-Drie goedkope checks op `/tmp/ff-checkN6` (export ná Bob's header-Column, ná de
-slider-herbindingen en ná de datum-bindingen): (1) ongelezen component-
-parameters: alleen de drie al bekende gevallen (`EvenementInfo` 6×, nagemeten
-en juist; `SliderUitgaanComponentSmallCurrent`, nul gebruikers;
-`HorecagelegenheidoverzichtKaart.nid`, restant hartje); (2) `launchURL` achter
-een `valueOrDefault`-guard: 0; (3) `getJsonField(...).toList()` zonder
-isList in levende code: alleen `HomeUitgaantabelKaartComponent:651`
-(`$.categorie`, per kaart — de view levert sinds P1-45 altijd een lijst) en
-horeca tab 6 (restpunt Bob, zie bevinding C). `dart analyze`: 0 errors.
-
-### 🐞 Nieuw gevonden 2026-09-13 (Claude, code-audit op verse export)
-
-Twee echte bevindingen uit een browserloze audit; allebei nagemeten tegen
-productie, geen van beide eerder opgeschreven.
-
-**A · ✅ OPGELOST 2026-09-13 (Bob, builder) — en het bleek vijf keer groter
-dan deze bevinding beschreef.** Alle zes tabs van
-`horecagelegenhedenOverzichtCurrent` staan nu op `.take(1000)`, geverifieerd in
-een verse export (6x `.take(1000)`, regels 562/763/950/1184/1419/1647).
-
-⚠️ **De limiet zat niet alleen op tab 6.** Bij het verifiëren bleek dat óók de
-vijf `filterHorecagelegenheden`-tabs een `.take(25)` hadden. Gemeten per tab op
-Amsterdam (`townid=28695`), vóór de fix:
-
-| tab | zaken in de API | toonde | onbereikbaar |
-|---|---|---|---|
-| Eetgelegenheden | **245** | 25 | **220** |
-| Uitgaan | 75 | 25 | 50 |
-| Cultuur | 42 | 25 | 17 |
-| Verhuur, catering | 42 | 25 | 17 |
-| Activiteiten | 24 | 25 | — |
-| Overnachten | 14 | 25 | — |
-
-Samen **304 zaken** die een gebruiker niet kon bereiken. Dubbel zonde omdat
-`fetchAlleHorecagelegenheden` netjes doorpagineert tot alles binnen is (tot 10
-pagina's): de app haalde die 245 dus echt op — drie API-calls — en gooide er 220
-weg in de laatste regel. Op Arnhem viel het nooit op; daar heeft de grootste tab
-20 zaken.
-
-🔑 **En dit corrigeert een aanname die maanden standhield:** het
-**Generate Dynamic Children**-paneel is *niet* stuk op deze pagina. Bob zette
-alle zes de waarden in één ronde om. De blokkade (zes pogingen, byte-identieke
-export, `Duplicate Page` erft 'm) is dus **specifiek voor Claude's
-browser-automation**, niet voor de opgeslagen pagina-state. `CLAUDE.md` is
-hierop gecorrigeerd. Praktisch gevolg: loopt een paneel bij Claude vast, dan is
-de conclusie voortaan "geef het aan Bob", niet "deze pagina is stuk".
-
-**Wat van deze bevinding nog openstaat** (los van de limiet, en niet urgent):
-tab 6 leest nog steeds rechtstreeks uit zijn Backend Query in plaats van via
-`filterHorecagelegenheden`, en heeft daardoor (1) geen zoekveld — het bekende
-"5 van de 6"-restpunt uit P2-6, (2) het crashpatroon uit bevinding C, en (3)
-een dubbele fetch: `_model.alleVerhuurCatering` wordt gevuld maar nergens
-gelezen.
-
-<details><summary>Oorspronkelijke bevinding (2026-09-13, vóór de fix)</summary>
-
-**A · Tab 6 "Verhuur, catering" is niet meegemigreerd naar de gefilterde
-route.** (Het afkappen op 25 is opgelost; drie punten staan nog open.)
-Op `horecagelegenhedenOverzichtCurrent` bouwen vijf tabs hun lijst via
-`functions.filterHorecagelegenheden(_model.alleX.toList(), ...)`. De zesde
-(Verhuur, catering) doet het nog op de oude manier: een eigen Backend Query
-plus `getJsonField(jsonBody, r'$').toList().take(25)`. Gevolgen, op volgorde
-van ernst:
-1. ✅ *(De limiet van 25 is 2026-09-13 door Bob opgelost: `.take(1000)`,
-   exportgeverifieerd. Daarmee bleek ook dat het Generate Dynamic
-   Children-paneel voor Bob gewoon wegschrijft — dat staat nu in `CLAUDE.md`.)*
-2. **Geen zoekveld** op deze tab — dit is het bekende "5 van de 6"-restpunt
-   uit P2-6, nu exact gelokaliseerd.
-3. ⚠️ **Crasht bij een netwerkfout — nu HARD BEWEZEN, en het raakt meer dan
-   deze tab.** `getJsonField(x, r'$').toList()` is empirisch getest met de
-   echte `json_path`-package: bij `jsonBody == null` (wat `ApiManager` bij
-   **elke** exception teruggeeft) crasht het met `NoSuchMethodError`, net als
-   bij een JSON-**object** (bv. een foutpagina die wel parset); alleen een lege
-   array `[]` is veilig. De vijf gemigreerde tabs hebben dit niet, want
-   `fetchAlleHorecagelegenheden` breekt netjes af op `!response.succeeded` en
-   geeft `[]`. **Zie de aparte bevinding C hieronder — ditzelfde patroon staat
-   ook op Home.**
-4. **De data wordt twee keer opgehaald.** De On-Tap-keten vult netjes
-   `_model.alleVerhuurCatering` (regel 483), maar die variabele wordt
-   **nergens gelezen** — de tab negeert 'm en haalt alles nog een keer op.
-
-⚠️ **Waarom dit blijft liggen, en waarom Claude er niet bij kan:** de
-`.take(25)` staat in het **Generate Dynamic Children**-paneel, en dat is
-precies het paneel dat op déze pagina structureel niets meer wegschrijft (zie
-`CLAUDE.md`: zes pogingen, `Max Items` van 25 naar leeg en naar 1000, elke keer
-byte-identiek terug in de export; `Duplicate Page` erft de blokkade). Dat is
-vrijwel zeker ook de reden dat deze tab destijds niet is meegegaan. **Dit is
-dus geen "even naklikken" — het is dezelfde blokkade, nu met een gemeten
-gevolg: er is content die een gebruiker niet kan zien.**
+De export van vannacht heeft drie pagina's naar de map `kanweg` verplaatst,
+maar laat de oude mappen op schijf staan. `index.dart` wijst naar de nieuwe
+plek, dus dezelfde klassen bestaan nu twee keer: **161 `dart analyze`-errors**
+(`ambiguous_import` + gevolgfouten), en de app bouwt zo niet. Vóór deze export
+waren het er 0.
 
 
-</details>
+**Fix — drie getrackte mappen weg (Claude verwijdert niets, dit is jouw
+commando):**
+```
+cd /home/bob/Projects/ff-app/uitgaanskrant-1qhvtd
+git rm -r --cached lib/horecagelegenhedenoverzicht/horecagelegenheden_overzicht_copy \
+                   lib/horecagelegenhedenoverzicht/horecagelegenheden_overzicht_copy2 \
+                   lib/horecagelegenhedenoverzicht/horecagelegenheden_overzicht_copy2_copy
+rm -rf lib/horecagelegenhedenoverzicht/horecagelegenheden_overzicht_copy \
+       lib/horecagelegenhedenoverzicht/horecagelegenheden_overzicht_copy2 \
+       lib/horecagelegenhedenoverzicht/horecagelegenheden_overzicht_copy2_copy
+```
+De vervangers staan al onder `lib/kanweg/` en zijn in de export opgenomen.
+Daarna `fvm dart analyze lib/` → verwacht 0 errors, en dán pas de app-code
+committen (die staat nu bewust nog ongecommit in de working tree: de
+verbindingsbanner, de slider-herbindingen en de datum-bindingen van 13 sep).
+De vierde map met een fout, `horecagelegenhedenoverzicht/
+horecagelegenheid_current_copy/`, is niet getrackt en mag gewoon weg.
+
+⚠️ **De documentatie (`CLAUDE.md`, `TASKS.md`) is wél al gecommit en gepusht.**
+
+### 🧹 Voor Bob — opruimen op productie (testdata van 13 sep)
+
+Alle drie door Claude aangemaakt tijdens de P2-15-tests, met toestemming:
+- **nid 220906** — "TEST met foto Claude 13sep - niet echt" (gepubliceerd
+  evenement bij Café de Vriendschap), plus de drie geüploade bestanden
+  **fid 5509170 / 5509288 / 5509390** in `files/evenementen_uploads/`.
+- **nid 220276** — "TEST stadsactiviteit Claude 13sep - niet echt"; staat
+  ongepubliceerd in de redactiewachtrij.
+- Nid 217916 en 219913 zijn al weg.
+
+### 🔧 Restpunten uit het werk van 13 sep
+
+**1 · Horeca tab 6 "Verhuur, catering" — Eigenaar: Bob (Claude komt niet in
+dat paneel op deze pagina).** De andere vijf tabs lopen via
+`filterHorecagelegenheden`; tab 6 leest nog rechtstreeks uit zijn Backend Query.
+Daardoor: geen zoekveld (het bekende "5 van de 6"-restpunt uit P2-6), het
+offline-crashpatroon, en een dubbele fetch (`_model.alleVerhuurCatering` wordt
+gevuld maar nergens gelezen).
+*Kleine fix die het crashdeel meteen wegneemt (3 klikken):* `StaggeredView` van
+tab 6 → **Generate Dynamic Children** → klik op de Value-**tekst** → Available
+Options **Predefined Path** → naam **`items`** → Confirm → **Save**. Die
+JSON-path staat al op `HorecagelegenheidoverzichtCall`; hij levert
+`?.toList() ?? []` in plaats van de crashende variant. Zie `CLAUDE.md` voor het
+volledige recept.
+
+**2 · Verbindingsbanner ontbreekt op vier pagina's.** Het widget zit alleen in
+`HeaderButtonsComponent`, dus niet op `mijnProfiel`, `wachtwoordVergeten`,
+`stadsactiviteitAanmaken` en `uitgaansevenementAanmaken`. Op de twee
+**aanmaakpagina's** is het zinvol (formulier verzenden zonder netwerk); het
+neemt geen ruimte in. ⚠️ Zet het daar **niet** in een constructie die de
+bestaande layout onbegrensde hoogte geeft — zie de header-valkuil in
+`CLAUDE.md`.
 
 
-**Wat de audit NIET vond (nagemeten, zodat niemand het nog eens doet):**
-- **Het P1-15-patroon is projectbreed schoon.** Alle 16 `launchURL`-aanroepen
-  in levende code hangen achter een guard op de **rauwe** waarde, niet op een
-  `valueOrDefault`. Geen enkele knop kan op een placeholder-URL tikken.
-- **`EstablishmentInfo` geeft precies één rij per zaak** — 60 Amsterdamse zaken
-  getest, 60x één rij. De zes `$[:].veld`-knoppen op de detailpagina leunen op
-  die aanname (bij meerdere rijen geeft `getJsonField` een lijst en wordt de URL
-  `[https://...]`), maar hij houdt stand. Wel iets om te herinneren als er ooit
-  een multi-value veld aan die view wordt toegevoegd — dat is precies hoe taak
-  30 elders ontstond.
-- **P1-45 is echt weg.** Alle zeven `flutterflowmobiel1`-displays opnieuw
-  gemeten (landelijk + Amsterdam): waar data is, is `categorie` **altijd** een
-  lijst — 65 records over 6 displays, geen enkele komma-string meer.
-  `services_4` blijft op 0 items en is dus niet te toetsen.
-- `SliderUitgaanComponentSmallCurrent` negeert zijn eigen `townid`/`displayid`
-  (het P1-44-patroon), maar het component heeft **nul gebruikers** — geen
+**3 · `stadsactiviteitAanmaken`: datumveld toont de rauwe waarde — Claude kan
+dit.** Het veld toont `2026-09-25 21:04:00.000`. Zelfde fix als op
+`uitgaansevenementAanmaken` is 13 sep gedaan: Set Form Field → DateTime Format
+→ Custom `dd-MM-yyyy HH:mm`, en de argumenten `datumStart`/`datumEind` van
+`stadsactiviteitCreate` rechtstreeks aan `datePicked1`/`datePicked2` hangen met
+`yyyy-MM-dd HH:mm:ss`.
+
+**4 · Cosmetisch op `stadsactiviteitAanmaken`** (laag, geen actie nodig): de
+"Mijn gemeenten"-snelkoppeling vult de Stadseditor-keten eronder niet voor, dus
+je kiest twee keer dezelfde gemeente. En de Media-knoppen zijn daar hoekig,
+terwijl ze op de evenementpagina afgerond zijn.
+
+**5 · Media-flow, twee kleinigheden** (uit de live test): de bronkeuze-sheet
+zegt "Choose Source / Gallery / Camera" in het Engels — FlutterFlow-standaard,
+hoort bij de vertaalronde P1-17. En de logo-thumbnail toont een vierkante
+uitsnede van het brede logo.
+
+### 📌 Nagemeten, niet opnieuw onderzoeken
+
+- **Het P1-15-patroon is projectbreed schoon.** Alle 16 `launchURL`-aanroepen in
+  levende code hangen achter een guard op de **rauwe** waarde, niet op een
+  `valueOrDefault`.
+- **`EstablishmentInfo` geeft precies één rij per zaak** (60 Amsterdamse zaken
+  getest). De zes `$[:].veld`-knoppen op de detailpagina leunen daarop; let op
+  zodra er een multi-value veld aan die view wordt toegevoegd.
+- **P1-45 is echt weg**: `categorie` is op alle zeven `flutterflowmobiel1`-
+  displays altijd een lijst (65 records gemeten).
+- **`SliderUitgaanComponentSmallCurrent`** negeert zijn eigen
+  `townid`/`displayid` (P1-44-patroon), maar heeft **nul gebruikers** — geen
   impact, alleen niet inzetten zonder dat eerst te repareren.
   impact, alleen niet inzetten zonder dat eerst te repareren.
+- **Ongelezen component-parameters**: alleen `EvenementInfo` (6 van de 12,
+  nagemeten en juist), de wees hierboven, en
+  `HorecagelegenheidoverzichtKaart.nid` (restant van het verwijderde hartje).
 
 
 ### 🚫 Bob's besluiten 2026-09-13 — niet meer voorstellen
 ### 🚫 Bob's besluiten 2026-09-13 — niet meer voorstellen
 
 
@@ -299,51 +245,11 @@ want `datum` bevat er geen.
 
 
 ### 🤝 CLAUDE KAN DIT OVERNEMEN — zeg het en het gebeurt
 ### 🤝 CLAUDE KAN DIT OVERNEMEN — zeg het en het gebeurt
 
 
-**P2-15 · ✅ live indiening GEDAAN (2026-09-13, nid 217916, Enkhuizen, door Bob verwijderd); rauwe datumweergave gefixt. Tijd +2u aan de Drupal-kant door Bob gefixt en live nagetest (nid 219913, mag weg). P2-15 is hiermee helemaal AF.**
-
-✅ **Ook de MEDIA-route is live getest (Claude, 2026-09-13 21:20, na Bob's
-opmerking dat de eerste testnodes geen plaatjes hadden — dat was bewust
-overgeslagen).** Drie testafbeeldingen op de emulator gezet; op
-`uitgaansevenementAanmaken`: "Logo kiezen" → Gallery → Android-fotokiezer →
-`bestandUpload` → fid 5509170 (thumbnail zichtbaar); "Foto's kiezen" twee keer
-(de kiezer sluit na één foto, maar een tweede keer kiezen **vult aan**) → fids
-5509288 + 5509390. Indienen → nid **220906**; `flutterflow_events` geeft `logo`
-en beide `fotos`-URL's terug onder `files/evenementen_uploads/`, tijd klopt
-(21:17). **Testnode 220906 mag weg (Bob)**, net als de drie geüploade
-bestanden (fids 5509170/5509288/5509390, map `evenementen_uploads`).
-Twee kleine observaties: de bronkeuze-sheet zegt "Choose Source / Gallery /
-Camera" in het Engels (FlutterFlow-standaardtekst, hoort bij P1-17), en de
-logo-thumbnail toont een vierkante uitsnede van het brede logo (cosmetisch).
-
-✅ **Ook de STADSACTIVITEIT-route is live getest (Claude, 2026-09-13 21:06,
-telefoon-emulator, plaats Enkhuizen).** `mijnProfiel` → "+ Voeg toe" bij Mijn
-redactierechten → `stadsactiviteitAanmaken`: titel, omschrijving, categorie
-Bijeenkomst, datum 25 sep 21:04, "Mijn gemeenten" → Enkhuizen (gemeente),
-daarna Stadseditor-keten Noord-Holland → Enkhuizen (gemeente) → Enkhuizen
-(`plaats_tid` 28750), adres Kaasmarkt 12. `POST stadsactiviteiten/create.json`
-→ 200 `{"status":"created_pending_review","nid":"220276"}` → snackbar "Je
-activiteit is ingediend en wordt beoordeeld door de redactie." → terug op
-`mijnProfiel`. Op productie: node bestaat (alias
-`/nl/Noord-Holland/Enkhuizen/2026/TEST-stadsactiviteit-Claude-13sep-niet-echt`),
-geeft **403** voor niet-ingelogd en staat niet in `flutterflow_events` — dus
-netjes ongepubliceerd. **Testnode 220276 mag weg (Bob, staat in de
-redactiewachtrij).**
-Twee observaties, geen blokkers:
-- *Cosmetisch, zelfde als op de evenementpagina vóór vanavond:* het Datum-veld
-  toont de rauwe `2026-09-25 21:04:00.000`. Zelfde fix als op
-  `uitgaansevenementAanmaken` (Set Form Field → DateTime Format → Custom
-  `dd-MM-yyyy HH:mm`, en het `datumStart`/`datumEind`-argument van
-  `stadsactiviteitCreate` aan `datePicked1/2` met `yyyy-MM-dd HH:mm:ss`).
-  Claude kan dit doen.
-- *Cosmetisch:* de "Mijn gemeenten"-snelkoppeling vult de Stadseditor-keten
-  níét voor (provincie/gemeente/plaats bleven op "Select..." staan na het
-  kiezen van Enkhuizen (gemeente)); je kiest dus twee keer Enkhuizen. De
-  Media-knoppen staan hier hoekig, op de evenementpagina afgerond. ~~het enige dat nog rest voor "uitgaansevenement
-aanmaken". Eén evenement echt indienen en de node terugvinden op de site.
-Claude kan dit op de emulator doen (de sessie is ingelogd, de zaak staat
-voorgeselecteerd), **maar de node komt direct gepubliceerd op de live site** —
-dus alleen op jouw uitdrukkelijke akkoord vooraf, met een herkenbare testtitel
-en het nid terug.
+**P2-15 is helemaal AF (13 sep).** Evenement indienen, stadsactiviteit
+indienen, de tijdzone-fix en de media-upload (logo + 2 foto's) zijn alle vier
+live getest vanuit de app op de telefoon-emulator, met Enkhuizen als plaats.
+De testnodes staan hierboven onder "opruimen op productie"; de restpunten die
+eruit kwamen staan onder "Restpunten uit het werk van 13 sep".
 
 
 **P2-22 · de Issues-teller leegmaken** — twee `Property Override`-fouten op
 **P2-22 · de Issues-teller leegmaken** — twee `Property Override`-fouten op
 `stadsactiviteitAanmaken`. **Claude heeft hier 2026-09-13 een ronde op gedaan
 `stadsactiviteitAanmaken`. **Claude heeft hier 2026-09-13 een ronde op gedaan
@@ -353,24 +259,11 @@ er misgaat zit in opgeslagen state die het rechterpaneel niet toont — precies
 het punt waarop dit ook in september al twee keer strandde. Blokkeert nog
 het punt waarop dit ook in september al twee keer strandde. Blokkeert nog
 steeds niets (export slaagt, `dart analyze` 0 errors).
 steeds niets (export slaagt, `dart analyze` 0 errors).
 
 
-**~~Sectiekoppen gelijktrekken~~ — vervallen, was een vals alarm
-(nagemeten 2026-09-13, verse export).** De 11 koppen op de twee
-aanmaakpagina's staan inderdaad op twee verschillende thema-tokens
-(9x `bodyMedium`, 4x `headlineSmall`), maar dat heeft **geen zichtbaar
-effect**: beide varianten overschrijven grootte én gewicht en renderen
-allebei als Roboto **16px, w600, `primaryText`**. `headlineSmall` is in het
-thema wel 24px, maar elke kop zet er `fontSize: 16.0` overheen. Er valt hier
-dus niets recht te trekken; niet opnieuw oppakken.
-
-### ✅ "Uitgaansevenement aanmaken" is verder AF (stand 2026-09-13)
-
-Eindcontrole op een verse export: 11/11 invoervelden op `primaryBackground`,
-3/3 dropdowns op `double.infinity`, alle 9 labels links uitgelijnd, geen
-"activiteit"-teksten meer, page-parameter wordt gelezen, `dart analyze` 0
-errors, en visueel nagelegd op de telefoon-emulator. **Het enige dat nog
-openstaat is de live indiening hierboven** — plus de Engelse vertalingen
-(17 van de 42 sleutels op deze pagina), en die zijn bewust uitgesteld tot de
-vertaalronde bij livegang (P1-17, jouw besluit 2026-09-10).
+**~~Sectiekoppen gelijktrekken~~ — vervallen, was een vals alarm** (nagemeten
+2026-09-13). De 11 koppen op de twee aanmaakpagina's staan op twee
+thema-tokens (9x `bodyMedium`, 4x `headlineSmall`), maar beide varianten
+overschrijven grootte én gewicht en renderen identiek als Roboto 16px w600.
+Niet opnieuw oppakken.
 
 
 ### ❓ Open vraag van Claude
 ### ❓ Open vraag van Claude
 
 
@@ -382,36 +275,6 @@ zonder agenda, dan klopt het gewoon.
 **Overslaan:** taak 19 (al gedaan, twee keer onafhankelijk nagemeten). Taak 21
 **Overslaan:** taak 19 (al gedaan, twee keer onafhankelijk nagemeten). Taak 21
 is juist wél weer zinvol — zie de contentstand hieronder.
 is juist wél weer zinvol — zie de contentstand hieronder.
 
 
-### ✅ Contentstand productie — hermeten 2026-09-12, de importfix wérkt
-
-| | 09-11 (import stond stil) | 09-11 ná fix | **09-12** |
-|---|---|---|---|
-| unieke events | 1010 | 1508 | **2007** |
-| **toekomstige** events | 4 | 6 | **61** |
-
-De aanwas van gisteren was nog vrijwel volledig historisch; vandaag zit hij
-wél vooruit. De 61 toekomstige events verdelen zich over tientallen plaatsen:
-Amersfoort 14, Amsterdam 8, Baarn 6, Naaldwijk 4, Den Haag 3, Hoorn 3,
-Valkenburg 3.
-
-**Wat dat verandert:**
-- De Home-tabs zijn niet meer leeg: `services_1` 25 (volle pagina),
-  `services_3` 17, `services_5` 25, `services_7` 7, `services_6` 1.
-  **Alleen `services_4` (Activiteiten) staat nog op 0** — dat zijn
-  stadsactiviteiten, die worden handmatig aangemaakt en niet geïmporteerd.
-- **Amsterdam is weer bruikbaar als testplaats voor evenementen**
-  (`services_3` 1, `services_5` 6). De notitie in `CLAUDE.md` die zei dat
-  Amsterdam daarvoor onbruikbaar was, is 2026-09-12 vervangen: die gold alleen
-  zolang de import stilstond.
-- **Taak 21 (exposed datumfilter Vandaag/Dit weekend/Deze week) is nu wél
-  zinvol te bouwen en te testen.** Gisteren adviseerde ik te wachten op
-  vulling; met 61 events verspreid over twee weken kan het nu.
-
-⚠️ **Eén ding blijft staan: er is nog steeds niets ná september.** Alle 61
-toekomstige events liggen binnen 2026-09. Zodra je dat kunt verklaren (bron
-publiceert kort vooruit, of de import haalt maar een venster op) is dat het
-laatste grote contentgat vóór livegang.
-
 ### Taak 28 · 12% van de komende events komt in geen enkele tab — met de lijst erbij
 ### Taak 28 · 12% van de komende events komt in geen enkele tab — met de lijst erbij
 
 
 Volledig doorgemeten 2026-09-12 20:50, terwijl je import liep (2963 unieke
 Volledig doorgemeten 2026-09-12 20:50, terwijl je import liep (2963 unieke
@@ -694,20 +557,6 @@ een keuzefilter in de app is een tweede, wél exposed filter nodig:
    uitkomst niets.
    uitkomst niets.
 Daarna kan Claude de drie knoppen in de app bouwen.
 Daarna kan Claude de drie knoppen in de app bouwen.
 
 
-### Voor Claude — nog open (stand 2026-09-12 einde dag)
-
-- **Hermeet de contentstand zodra Bob's import van 12 september klaar is.**
-  Hij liep nog toen deze sessie sloot (2007 → 2963 unieke events in ±40 min),
-  maar de *komende* events bleven op ~58 staan en **alles lag nog binnen
-  september**. Twee vragen die dan te beantwoorden zijn: is er oktober
-  bijgekomen, en beweegt het percentage onzichtbare events (nu 7 van 58) mee?
-  Het recept staat bij taak 28.
-- **Wacht op Bob voor het `display_id` van de bezorg-display** (taak 20). Zodra
-  dat er is: drawer-item *Thuis bezorgen* eraan hangen. Let op de twee
-  voorwaarden die al gemeten zijn — het `horcat`-filter moet van die display af
-  (lege `horcat` geeft 0), en de pagina heeft nog géén `display_id`-parameter,
-  dus die moet er eerst op.
-
 ### Uit taak 18 voortgekomen — voor Bob (hoort bij P2-7 / taak 24)
 ### Uit taak 18 voortgekomen — voor Bob (hoort bij P2-7 / taak 24)
 
 
 De horeca-productiepagina heet nu **`horecagelegenhedenOverzichtCurrent`**
 De horeca-productiepagina heet nu **`horecagelegenhedenOverzichtCurrent`**
@@ -717,9 +566,14 @@ en staat in de map `kanweg`. Drie dingen die daaruit volgen:
    `flutterflow export-code` de oude mappen gewoon staan; git ruimt ze niet
    `flutterflow export-code` de oude mappen gewoon staan; git ruimt ze niet
    op. Concreet:
    op. Concreet:
    `lib/horecagelegenhedenoverzicht/horecagelegenheden_overzicht/` (getrackt,
    `lib/horecagelegenhedenoverzicht/horecagelegenheden_overzicht/` (getrackt,
-   dus die gaat mee in commits) plus de niet-getrackte restanten
-   `horecagelegenheden_overzicht_copy3/` en `horecagelegenheid_current_copy/`.
-   Claude verwijdert niets — dit is jouw call.
+   dus die gaat mee in commits) plus de niet-getrackte restanten. Stand
+   2026-09-14, zeven mappen: `horecagelegenhedenoverzicht/
+   horecagelegenheden_overzicht_copy3/` en `.../horecagelegenheid_current_copy/`,
+   `uitgaanspaginas/home_uitgaan_slider_component_copy/`, en vier onder
+   `kanweg/` (`header_buttons_component_copy`,
+   `horecagelegenheden_overzicht_copy`, `..._copy2`, `..._copy2_copy`).
+   Claude verwijdert niets — dit is jouw call. Ze zijn bewust nooit
+   gecommit, dus ze staan alleen lokaal.
 2. **Eén `dart analyze`-fout**, en alleen in dat laatste restant:
 2. **Eén `dart analyze`-fout**, en alleen in dat laatste restant:
    `horecagelegenheid_current_copy/..._widget.dart:1475 — Undefined name
    `horecagelegenheid_current_copy/..._widget.dart:1475 — Undefined name
    'HorecagelegenhedenOverzichtWidget'`. Het bestand is een lokaal overblijfsel
    'HorecagelegenhedenOverzichtWidget'`. Het bestand is een lokaal overblijfsel
@@ -730,40 +584,6 @@ en staat in de map `kanweg`. Drie dingen die daaruit volgen:
    staan al op de P2-7-opruimlijst; ze houden de oude route alleen kunstmatig
    staan al op de P2-7-opruimlijst; ze houden de oude route alleen kunstmatig
    in leven.
    in leven.
 
 
-### Beslispunt voor Bob — minSdkVersion van 23 naar 24
-
-`flutterflow export-code` heeft in `android/app/build.gradle` de regel
-`minSdkVersion 23` vervangen door `minSdkVersion flutter.minSdkVersion`.
-In Flutter 3.35.7 is die constante **24**
-(`packages/flutter_tools/gradle/src/main/kotlin/FlutterExtension.kt:26`).
-Netto: de ondergrens gaat van **Android 6.0 naar Android 7.0** — Android
-6-toestellen kunnen de app dan niet meer installeren.
-
-Dit stond al ongecommit in de working tree vóór de sessie van 2026-09-11,
-dus het is export-output, niet iets dat iemand bewust heeft gezet. De
-profile-build op de telefoon-AVD slaagt er gewoon mee. Twee opties:
-- **laten staan** (volgt FlutterFlow, minder onderhoud, kost Android 6);
-- **terugzetten op `23`** na elke export — dan is het een terugkerend
-  handmatig klusje, want de export overschrijft het telkens.
-Zelfde export zette ook `task clean(type: Delete)` om naar
-`tasks.register("clean", Delete)` in `android/build.gradle` — dat is puur
-Gradle-moderniseringssyntaxis, geen beslissing nodig.
-
-⚠️ **Update 2026-09-11: de export doet het nu precies ANDERSOM.** Een verse
-`flutterflow export-code` naar de projectmap zette `minSdkVersion` weer terug
-op de hardcoded **`23`**, en `tasks.register("clean", Delete)` weer terug naar
-het oudere `task clean(type: Delete)`. De export is op deze twee regels dus
-niet stabiel — hij wisselt heen en weer, en welke kant je in git vastlegt
-wordt bij de volgende export mogelijk weer omgedraaid. **Die wijzigingen zijn
-bewust NIET meegecommit in 9f6e790/daarna** (alleen `lib/` ging mee); ze staan
-dus nog als ongecommitte wijziging in de working tree. Kies één kant, Bob, en
-leg 'm vast — anders blijft elke sessie hier tegenaan lopen. Let op dat
-`task clean(type: Delete)` in Gradle 9 vervalt, dus de `tasks.register`-vorm
-is de toekomstvaste.
-*(De gelijktijdige diff in `ios/Runner.xcodeproj/project.pbxproj` is puur
-ruis: FlutterFlow genereert daar bij elke export nieuwe object-ID's voor de
-`nl`/`en`-InfoPlist-verwijzingen. Inhoudelijk verandert er niets.)*
-
 ### Bij livegang
 ### Bij livegang
 
 
 - **P1-17 · alle Engelse vertalingen in één ronde** (Bob's besluit
 - **P1-17 · alle Engelse vertalingen in één ronde** (Bob's besluit
@@ -4686,1073 +4506,33 @@ overwegen bij een volgende theme-update:
 
 
 ---
 ---
 
 
-**P2-15 · ✅ LIVE INDIENING GELUKT 2026-09-13 (Claude, telefoon-emulator, profile-build, op Bob's akkoord; plaats Enkhuizen) — twee bevindingen eruit, zie hieronder.**
-
-Doorloop: ingelogd als `bobcity` → `mijnProfiel` → "+ Voeg toe" bij Mijn
-horecagelegenheden → `uitgaansevenementAanmaken`; dropdown stond voorgeselecteerd
-op **Café de Vriendschap (Enkhuizen, nid 30399)**. Ingevuld: titel *"TEST app
-Claude 13sep - niet echt"*, omschrijving, categorie *Activiteiten › Bijeenkomst*
-(tid 36637), datum 25 sep 20:20, website, entree *Vrij toegankelijk* (tid 14);
-geen media. Verzendknop → `POST evenementen/create.json` → HTTP 200
-`{"status":"created","nid":"217916"}` → snackbar "Je evenement is geplaatst."
-→ terug op `mijnProfiel`. Nagemeten op productie: `flutterflow_events` geeft de
-node (plaats Enkhuizen, categorie `['Bijeenkomst']`, body, website),
-`flutterflowmobiel_establishment_events?horecanid=30399` toont 'm in de agenda,
-en `/node/217916` redirect naar
-`/nl/Noord-Holland/Enkhuizen/Café-de-Vriendschap/2026/TEST-app-Claude-13sep-niet-echt`.
-**Testnode 217916 mag weg (Bob).**
-
-✅ **Bevinding 1 — tijd verschuift 2 uur — OPGELOST 2026-09-13 (Bob, Drupal-kant, helper `_custom_events_lokaal_naar_utc` in `custom.evenementen_aanmaken.inc`; live nagetest door Claude).** Testnode **219913** ("TEST tijdzone Claude 13sep - niet echt", 25 sep 20:58): de app stuurde 20:58, `flutterflow_events` én de horeca-agenda geven nu **20:58** terug (vóór de fix: +2u). **Testnode 219913 mag weg (Bob).** Bijvangst: de build die nu op emulator-5556 staat stuurde `20:58:00.000` (met milliseconden, een tussenstand van de builder-wijzigingen); Drupal's helper parseert dat gewoon. De definitieve app-bindingen sturen `yyyy-MM-dd HH:mm:ss` zonder millis. *Oorspronkelijke bevinding:* De app stuurde
-`"datum_start":"2026-09-25 20:20:00"` (lokale tijd, zie logcat), maar de views
-én de publieke pagina tonen **22:20**. Drupal leest de string dus als UTC en
-rendert in Europe/Amsterdam (+2 in de zomer). Fix in
-`custom.evenementen_aanmaken.inc`: de binnenkomende datum expliciet als
-`Europe/Amsterdam` interpreteren vóór opslaan (`new DateTime($s, new
-DateTimeZone('Europe/Amsterdam'))` → naar UTC), of afspreken dat de app UTC
-stuurt. Drupal-kant is netter: dan blijven oudere/andere clients ook goed.
-Geldt vermoedelijk ook voor `stadsactiviteiten/create`.
-
-**Bevinding 2 — ✅ AFGEROND 2026-09-13 (Claude, builder, exportgeverifieerd,
-`dart analyze` 0 errors).** Beide datumvelden tonen nu `dd-MM-yyyy HH:mm` (Set
-Form Field → DateTime Format → Custom), en de argumenten `datumStart`/`datumEind`
-van `evenementCreate` hangen rechtstreeks aan `datePicked1`/`datePicked2` met
-format `yyyy-MM-dd HH:mm:ss` — de API krijgt dus exact dezelfde string als
-vóór deze wijziging; `datumVoorApi` wordt op deze pagina niet meer gebruikt.
-Nog niet live nagetest (zou een nieuwe testnode kosten); de gegenereerde code
-is 1-op-1 gecontroleerd. *Oorspronkelijk:* het Datum-veld toont
-na kiezen de rauwe `2026-09-25 20:20:00.000`. De Text-binding van dat veld
-door een `dateTimeFormat` halen (bv. `d MMM yyyy, HH:mm`) — alleen de weergave,
-`datumVoorApi` blijft de bron voor het verzenden.
-
-*Oorspronkelijke taakomschrijving hieronder blijft staan als naslag.*
-Bouwstappen 1 t/m 5 zijn AF, en de vier
-restpunten uit de exportcontrole van 2026-09-11 zijn afgewerkt; alleen de live
-test rest.** (Claim vrijgegeven na sessie 2026-09-03b.) Wat er nog moet: één
-doorloop op een toestel — inloggen als horeca-eigenaar, `mijnProfiel` openen,
-via "+ Voeg toe" naar `uitgaansevenementAanmaken`, controleren dat de
-horeca-dropdown zich voorselecteert (bij precies 1 zaak) en dat de verzendknop
-verschijnt, en één evenement echt indienen. Verwacht: snackbar "Je evenement is
-geplaatst." en de node terugvinden op uitgaanskrant.com. Doe dat met
-`fvm flutter run --profile -d <device>` (zie CLAUDE.md), niet met een
-debug-build.
-
-✅ **De vier restpunten uit de exportcontrole van 2026-09-11 zijn afgewerkt**
-(Claude, zelf in de builder; elk punt met een verse export geverifieerd, en
-`dart analyze` op die export geeft 0 errors):
-1. **Invoervelden waren wit op wit** — alle **11** `fillColor`s (8 tekstvelden +
-   3 dropdowns) stonden op `secondaryBackground`, net als de vier omhullende
-   kaart-Containers, waardoor je alleen zwevende labeltekst zag. Alle 11 staan
-   nu op **`primaryBackground`**, gelijk aan `stadsactiviteitAanmaken`.
-2. **Vier teksten zeiden nog "activiteit"** (restant van de duplicatie): de
-   verzendknop heet nu **"Evenement indienen"**, en de hints zijn
-   "Titel evenement", "Omschrijving evenement" en "Website van het evenement".
-3. **Foutmelding-bug in de foto-upload** — de FALSE-tak las
-   `logoevenementuploadResult` → `$.error` in plaats van
-   `fotoEvenementuploadResult`, dus bij een mislukte foto-upload verscheen de
-   fout van de *logo*-upload (of "null"). Nu correct gebonden.
-4. **`mijnProfiel`'s tweede "+ Voeg toe"** (bij "Mijn redactierechten") toonde
-   nog de placeholder "Binnenkort beschikbaar: evenement aanmaken" — die tekst
-   klopte ook niet (redactierechten → stadsactiviteit). De knop navigeert nu
-   naar **`stadsactiviteitAanmaken`** (`context.pushNamed`, geen parameters);
-   die pagina was daarvóór alleen vanuit Favorieten bereikbaar. Besluit Bob,
-   2026-09-11.
-
-*Uitgesloten bij diezelfde controle, niet nog eens onderzoeken:* de ontbrekende
-guard op titel/datum is geen crashrisico — `datumVoorApi` geeft nooit `null`
-terug (bij leeg een `''`), dus de `!` in de knop is veilig, en `evenementCreate`
-vangt lege titel/datum zelf af met een nette melding. De knop is dus hooguit
-cosmetisch te vroeg klikbaar. Ook: `bestandUpload` plakt de API-base zelf voor
-het relatieve pad, dus dat is correct bedraad. En P2-22's twee
-`Custom Action Call`-fouten op `categorieTids`/`fotosFids` zijn opgelost —
-beide staan als `.toList()` in de export en de export blokkeert niet meer.
-
-**Visueel nagelegd op de telefoon-emulator (411 dp, profile-build, route
-`/uitgaansevenementAanmaken`, 2026-09-11):** de invoervelden zijn nu
-daadwerkelijk zichtbaar als grijze vakken, de horeca-dropdown selecteerde
-zichzelf voor op "Café de Vriendschap" (dus `horecaVoorselectie` + de
-On-Page-Load-keten werken op een echt toestel), en de verzendknop stond
-zichtbaar onderaan met de tekst "Evenement indienen". Dat dekt het grootste
-deel van de live test hierboven af; wat nog écht rest is **één evenement
-daadwerkelijk indienen** en de node terugvinden op uitgaanskrant.com.
-
-✅ **Die twee cosmetische punten zijn afgewerkt (Claude, 2026-09-12, builder;
-exportgeverifieerd, `dart analyze` 0 errors, en visueel nagelegd op de
-telefoon-emulator):** de drie dropdowns staan nu op `double.infinity` (waren
-`200.0` terwijl de tekstvelden vol-breed zijn), en **alle negen** labels op de
-pagina staan links uitgelijnd — de vijf sectiekoppen plus "Uw
-Horecagelegenheid", dat als enige veldlabel nog gecentreerd stond. Daarmee is
-de pagina gelijk aan `stadsactiviteitAanmaken`, dat al 6× `double.infinity` en
-overal linkse uitlijning had.
-
-*Eén inconsistentie bewust laten staan, want hij zit op BEIDE aanmaakpagina's
-en is een ontwerpkeuze, geen bug:* de sectiekoppen gebruiken twee verschillende
-tekststijlen. "Wat"/"Wanneer"/"Organisatie" (en op stadsactiviteit ook "Waar")
-staan op **`bodyMedium`**, terwijl "Entree" en "Media" op **`headlineSmall`**
-staan — zichtbaar groter en zwaarder. Wil je dat gelijktrekken, dan is dat één
-keuze die je op beide pagina's tegelijk moet doorvoeren (5 + 6 widgets).
-Bouwstap 1 afgerond en bevestigd (2026-08-27): custom action
-`bestandUpload` (`lib/custom_code/actions/bestand_upload.dart`) upload
-foto → fid, live getest tegen productie
-(`https://uitgaanskrant.com/en/flutterdrup/bestand_upload/upload.json`)
-via een tijdelijke testflow (`{success: true, fid: 5495517, url: ...}`)
-— de tijdelijke "Test Upload (tijdelijk)"-navigatieknop op Favorieten'
-Gebruiker-tab is weer verwijderd en bevestigd via verse export; het
-losse testpagina `kanwegTestUpload` (Pick Media → `bestandUpload` →
-snackbar met resultaat) blijft staan als herbruikbare kanweg-testtool,
-nergens meer aan gelinkt.
-⚠️ **Een ANDERE sessie bouwt tegelijk al bouwstap 2** (pagina
-"Stadsactiviteit aanmaken") — nog niet door Bob gescheidsrecht welke
-sessie samenhangend doorgaat op stap 2/3/4 hieronder.**
-Evenementen/stadsactiviteiten
-aanmaken vanuit de app (horeca-eigenaren + elke gebruiker). **Drupal-kant
-volledig klaar en curl-getest op devbob (2026-08-26)** — dit is nu
-zuiver FlutterFlow-bouwwerk. Volledige achtergrond/velden/beslissingen
-staan in het Artifact "Redactierechten & Contentschema"
-(`https://claude.ai/code/artifact/becb0c6f-e43b-4392-9f84-7dbf44eda5b2`,
-secties "FlutterFlow-vervolgspec" én "API-contract" — dat laatste heeft
-de exacte argumentnamen/types per endpoint, nodig om de FlutterFlow
-API Calls te configureren) — open dat eerst, hieronder alleen de
-samenvatting + concrete eerste bouwstappen.
-
-**Endpoints (allemaal bevestigd werkend):** `evenementen/create`,
-`stadsactiviteiten/create`, `mijn_horecagelegenheden` (index),
-`mijn_stadsrechten` (index), `categorieen` (index),
-`bestand_upload/upload`. Basis-auth (`bob:serhii`, zie lokale
-Claude-memory) nodig voor handmatig curl-testen, niet vanuit de app
-zelf.
-
-**Look & feel — bevindingen uit de bestaande app-code (geen browser
-gebruikt, puur codeonderzoek):**
-- **`HorecagelegenheidoverzichtKaartWidget`**
-  (`lib/horecagelegenhedenoverzicht/horecagelegenheidoverzicht_kaart/`)
-  is al de kaart-widget die Favorieten Tab 2 gebruikt
-  (`favorieten_widget.dart:678`, in een `MasonryGridView.builder`) —
-  parameters (`nid`, `titel`, `adres`, `plaats`, `logo`, `categorie`)
-  matchen 1-op-1 met wat `mijn_horecagelegenheden` teruggeeft. **Gewoon
-  hergebruiken** voor de nieuwe "Mijn horecagelegenheden"-sectie, geen
-  nieuwe kaart-widget nodig.
-- **Geen bestaand upload-precedent in de app** (`grep` op
-  `ImagePicker`/media-upload buiten `custom_code/` gaf 0 treffers) —
-  de foto-upload-flow (image picker → bytes → base64 → nieuwe custom
-  action → `bestand_upload/upload` → fid) is de enige écht nieuwe
-  bouwsteen zonder bestaand patroon om te kopiëren. Grootste
-  onzekerheid in deze taak; begin hier apart mee testen vóór je de
-  hele formulier-pagina bouwt.
-- **Thema:** gewoon `FlutterFlowTheme.of(context)`-tokens overal
-  gebruiken (primary `#4B39EF`, secondary `#39D2C0`, tertiary
-  `#EE8B60`, font Inter/Inter Tight via Google Fonts) — geen eigen
-  palet nodig, sluit al aan bij de rest van de app.
-- **Waar leeft "mijn profiel"?** Er bestaat nu geen aparte
-  profiel-pagina — `FavorietenWidget` (`lib/favorieten/`) heeft een
-  4e tab "Gebruiker" (`favorieten_widget.dart:245`) met een simpele
-  verticale lijst `ListTile`-rijen (Uitloggen, Wachtwoord wijzigen,
-  Account verwijderen — zie regel 700-935, patroon: `InkWell` →
-  `Material` → `ListTile` met `trailing: Icon(Icons.arrow_forward_ios_rounded)`,
-  `tileColor: secondaryBackground`, `borderRadius: 8.0`). **Aanbeveling:
-  nieuwe secties "Mijn horecagelegenheden" en "Mijn redactierechten"
-  hier bovenaan toevoegen** (vóór Uitloggen) i.p.v. een hele nieuwe
-  pagina + nav-entry te bouwen — kleinste wijziging, blijft binnen de
-  al bestaande "Gebruiker"-tab die feitelijk al de profielpagina is.
-  **Open vraag voor Bob:** akkoord met deze plek, of toch een losse
-  pagina? (zijn oorspronkelijke formulering was "de user profile
-  pagina, los van de favoriete pagina" — kan ook betekenen dat hij een
-  ECHT aparte pagina wil, niet nog een tab op dezelfde `FavorietenWidget`.)
-
-**Concrete bouwvolgorde (1 stap per keer, zoals gebruikelijk):**
-1. Custom action `bestand_upload` bouwen (image picker → base64 →
-   POST) en LOS testen (upload 1 plaatje, bevestig een fid terugkomt)
-   vóór er iets anders bijkomt.
-2. Pagina "Stadsactiviteit aanmaken" (simpelste van de twee — geen
-   eigenaarschap-gate, geen horecagelegenheid-picker): velden per de
-   Stadsactiviteit-tabel in het Artifact, plaats-picker (bestaand
-   patroon, zie `SelectStateDropDownComponent`/`Selectprovinciegemeente`),
-   categorie-multiselect via `categorieen/index`. Na indienen: melding
-   "wordt beoordeeld", niet "geplaatst".
-3. Pagina "Evenement aanmaken": horecagelegenheid-picker gevuld via
-   `mijn_horecagelegenheden` (auto-select bij precies 1 resultaat),
-   verder zelfde velden-aanpak als stap 2.
-4. "Gebruiker"-tab (of nieuwe pagina, zie open vraag hierboven):
-   sectie "Mijn horecagelegenheden" (hergebruik
-   `HorecagelegenheidoverzichtKaartWidget`) + "Mijn redactierechten"
-   (`mijn_stadsrechten`, verberg de sectie helemaal als leeg), elk met
-   een knop die naar stap 2/3 navigeert met een page-parameter
-   (`plaats_tid` resp. `horecagelegenheid_nid`) vooringevuld.
-
-**Curl-testronde afgerond (2026-08-26): elk veld op beide create-acties
-veld-voor-veld bevestigd via drush node-dumps** — titel, datum (incl.
-datum_eind-fallback), omschrijving, adres, entreeprijs, toelichting
-entree, entree-type (nieuw `entree_tid`/`field_act_entree`, door Bob
-zelf toegevoegd — zelfde vocabulary als `field_goo_entree`), meerdere
-categorieën, logo, foto's-slideshow (bleek een minimale-resolutie-eis
-te hebben — met een groter test-plaatje werkt het), website,
-tickets-url, status (0/1), taal (`nl`). Twee nieuwe leesendpoints
-onderweg bijgekomen: `categorieen`/index (vervangt de eerdere losse
-`uitgaanscategorieen`/`evenementcategorieen` — bleken dezelfde
-vocabulary) en `entreeopties`/index.
-
-**Extra (2026-08-27, terwijl de frontend in een andere sessie gebouwd
-wordt): `horecacategorieen`/index toegevoegd en curl-bevestigd
-werkend** — categorie-vocabulary voor `horecagelegenheid` zelf
-(`field_categories`, vocabulary `horecagelegenheid_category`,
-bevestigd via `field_info_instances()` — een aparte, derde vocabulary,
-los van `categorieen`). Niet gebruikt door evenementen/
-stadsactiviteiten, klaargezet voor als horecagelegenheid-content ooit
-via de app beheerd wordt. (8 resources in totaal, zie
-`custom.module-WIJZIGINGEN.txt`).
-
-**Curl-testronde 100% afgerond (2026-08-26, avond).** Alle foutpaden
-+ de meerdere-foto's-slideshow expliciet bevestigd: >5 `fotos_fids` →
-nette 400 ("Maximaal 5 foto's toegestaan"); ongeldige `categorie_tid`
-→ 400; ongeldige `entree_tid` → 400; `evenementen/create` met een
-horecagelegenheid van een andere gebruiker (nid 70132, "Wapen van
-Urk") → correcte 403 ("Deze horecagelegenheid is niet van jou"); 3
-verschillende testfoto's tegelijk in `fotos_fids` → alle 3 los en
-correct terug te vinden in `field_pictures` (delta 0/1/2, eigen
-fid/bestandsnaam/afmetingen elk). Enige restpunt: `mijn_stadsrechten`
-nooit apart getest (laag risico, zelfde patroon als de al werkende
-`favorieten_gemeenten`). **De Drupal-kant is hiermee klaar — dit is nu
-zuiver FlutterFlow-bouwwerk, zie de bouwvolgorde hierboven.**
-
-**Bouwstap 1 (foto-upload) is af — 2026-08-27.** Bleek al gebouwd door
-een parallelle sessie: custom action **`bestandUpload`**
-(`FFUploadedFile file, String uploadUrl, String sessionName, String
-sessionId, String token) -> dynamic`, retourneert
-`{'success': bool, 'fid': String, 'url': String, 'error': String,
-'statusCode': int}` — rijker dan het losse `drupalUploadBestand`
-(`-> String?`) dat Claude had voorbereid, want geeft bij een fout ook
-de échte Drupal-foutmelding terug om aan de gebruiker te tonen.
-**`drupalUploadBestand` is verwijderd, `bestandUpload` is voortaan de
-canonieke actie.** Let op: `bestandUpload` wil de **volledige URL**
-(`uploadUrl`, dus incl. `/bestand_upload/upload.json`), niet alleen een
-base-URL.
-
-**Bouwstap 4 (nieuwe pagina `mijnProfiel`) — grotendeels af, 2026-08-27
-(Bob sliep, Claude bouwde door).** 4 nieuwe API Calls toegevoegd
-(`MijnHorecagelegenheden`, `MijnStadsrechten`, `Categorieen`,
-`Entreeopties`) — allemaal geverifieerd correct via export. Nieuwe
-pagina `mijnProfiel`
-(`https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=mijnProfiel`,
-`lib/mijn_profiel/`), Scaffold met kale AppBar (Show Default Button,
-geen titel — drag-and-drop van een titel-widget faalde herhaaldelijk,
-zie de bekende AppBar-Row-insert-onbetrouwbaarheid elders in dit
-bestand):
-- **Sectie "Mijn horecagelegenheden"**: Row (titel + "+ Voeg toe"
-  Button, nu nog een placeholder Show-Snack-Bar) + ListView met
-  Backend Query = `MijnHorecagelegenheden` (variabelen `session_name`/
-  `sessid` → App State `userSessionname`/`userSessionid`) + Generate
-  Dynamic Children (var `horecaItem`, JSON Body, No Further Changes) +
-  item-template = hergebruikte `HorecagelegenheidoverzichtKaart`
-  (params `titel`/`logo`/`nid`/`adres`/`plaats`/`categorie`, elk via
-  JSON Path `$.<veld>` op `horecaItem`). **Volledig af en
-  exportgeverifieerd.**
-- **Sectie "Mijn redactierechten"**: zelfde patroon, Backend Query =
-  `MijnStadsrechten`, Generate Dynamic Children var `rechtItem`,
-  item-template = kale `Column` → `Text` met een **Combine Text**-
-  binding (`$.titel` + literal " — " + `$.parent_titel`) — geen losse
-  tweede Text-widget nodig/haalbaar, zie bugnotitie hieronder.
-  "+ Voeg toe"-knop ook nog placeholder. **Af en
-  exportgeverifieerd**, behalve de leeg-verbergen-eis (zie hieronder).
-- **⚠️ Nieuw bevestigd builder-bugpatroon (2026-08-27):** een
-  ListView's **item-template vervangen** via rechtsklik → "Insert
-  After"/"Duplicate" op de bestaande template-widget triggert een
-  **"Replace Dynamic Child"**-bevestigingsdialoog — en de nieuwe
-  widget die daaruit voortkomt **kan een Backend Query +
-  Generate-Dynamic-Children-configuratie van een eerder gedupliceerde
-  ListView blijven meedragen**, zelfs nadat je 'm via "Replace Widget"
-  omzet naar een ander widget-type (bv. Column). Dit bleef **onzichtbaar
-  in de builder-UI** (Column's rechterpaneel toont geen aparte
-  Backend-Query-sectie) maar leverde in de export een dubbel-geneste
-  `FutureBuilder`/`List.generate` op — functioneel een N×M-bug (elke
-  stadsrecht-rij herhaalde zich M keer, M = aantal horecagelegenheden
-  van de gebruiker) én een overbodige extra API-call per rij. **Fix:**
-  selecteer de widget, check zelf de 3e/4e icoontjes (Backend Query /
-  Generate Dynamic Children) in de rechterpaneel-iconenrij — ook als
-  er geen widget-type meer op wijst — en klik **Remove** op beide als
-  ze een oude configuratie tonen. **Vuistregel: na elke
-  "Replace Dynamic Child"-actie altijd verifiëren via een verse export
-  + `grep` op de betrokken Call-klassen**, niet aannemen dat
-  "Replace Widget" alle oude state meeneemt.
-- **Nog open (kleinere restpunten, geen van alle blokkerend):**
-  1. Beide "+ Voeg toe"-knoppen omzetten van placeholder-snackbar naar
-     echte Navigate-To zodra de create-pagina's bestaan (zie
-     bouwstap 2/3), met `horecagelegenheid_nid` resp. `plaats_tid` als
-     page-parameter.
-  2. Sectie "Mijn redactierechten" helemaal verbergen als de lijst leeg
-     is (de meeste gebruikers hebben geen `field_town_access`) — nog
-     niet gebouwd, waarschijnlijk een ConditionalBuilder op de hele
-     sectie met een "Number of Items > 0"-achtige JSON-Path-transform;
-     kost een eigen sessie/poging gezien de bekende
-     ConditionalBuilder-freeze-risico's elders in dit bestand.
-  3. Entry-point naar `mijnProfiel` vanuit de rest van de app (bv.
-     Favorieten' "Gebruiker"-tab) — nog niet toegevoegd, pagina is nu
-     alleen bereikbaar via directe URL/route.
-  4. `EntreeoptiesCall`'s header niet los geverifieerd op dezelfde
-     dubbele-substitutie-bug als `CategorieenCall` had (die is al
-     gefixt) — waarschijnlijk oké, niet met zekerheid gecheckt.
-
-**Bouwstap 2 (custom action `stadsactiviteitCreate`) — af, 2026-08-27.**
-Custom action toegevoegd via Custom Code-editor (⌘K → "Add: Action",
-NIET via de native API-Call-JSON-body-templating — bewust gekozen
-i.p.v. FlutterFlow's ingebouwde API Call vanwege de geneste
-`adres`-struct + 2 arrays (`categorie_tids`/`fotos_fids`), zelfde
-precedent als `bestandUpload`). 20 typed parameters (`sessionName`,
-`sessionId`, `token`, `titel`, `plaatsTid`, `datumStart`, plus 14
-optionele velden incl. `List<String>? categorieTids`/`fotosFids`) →
-POST naar `stadsactiviteiten/create.json`, retourneert
-`{success, nid, status}` of `{success:false, statusCode, error}`.
-**Geverifieerd via `dart analyze`: 18 meldingen, stuk voor stuk
-cosmetisch** (5 ongebruikte FlutterFlow-boilerplate-imports + 13
-`avoid_print`/`prefer_const`-infos, zelfde patroon als `bestandUpload`)
-— geen echte fouten.
-- **⚠️ Nieuw bevestigd builder-bugpatroon (2026-08-27), Custom-Action-
-  argumenten-UI:** bij het via de rechterpaneel-UI toevoegen van veel
-  (~20) Custom-Action-argumenten kan er een **extra, naamloos 21e
-  argument** ontstaan (vermoedelijk door een misklik tijdens
-  chevron-toggle-navigatie) — dit blokkeert "Save Action" met
-  **"Action arguments must all be given names."** Het probleem is
-  onzichtbaar in de argumentenlijst zelf zolang je 'm niet helemaal
-  tot onderaan scrolt, maar wordt direct duidelijk via het
-  **`</>`-icoon rechtsboven in het Action Settings-paneel ("View
-  Boilerplate Code")** — dat toont de exacte verwachte functie-
-  signature inclusief een kaal `String? ,` aan het eind als er zo'n
-  leeg argument bestaat. **Check dit sowieso bij een volgende
-  veel-argumenten Custom Action** vóór je op Save klikt: open even
-  "View Boilerplate Code" en tel de parameters. Fix: helemaal naar
-  onderen scrollen in "Define Arguments" (voorbij het laatste échte
-  argument) en op "Remove" klikken bij het lege argument.
-- **Los bevestigd: het rechterpaneel van de Custom-Action-editor kan
-  bij veel argumenten muiswiel-scroll volledig negeren**, zelfs met de
-  bekende Tab-naar-volgend-veld-workaround (werkte de eerste ~15 keer
-  wel, liep daarna vast) — en FlutterFlow's eigen zwevende
-  hulp-chat-knop (rechtsonder in beeld) kan bovendien exact overlappen
-  met de plek waar een laag-gelegen veld zou moeten zitten, waardoor
-  een klik daar per ongeluk de hulp-widget opent i.p.v. het veld raakt.
-  Bij dit patroon (net als de eerdere clipping-gevallen): 1-2 pogingen,
-  dan aan Bob overdragen — kostte hem in zijn eigen browser seconden.
-
-**Bouwstap 3 (pagina `stadsactiviteitAanmaken`) — grotendeels af,
-2026-08-31.** Live pair-sessie (Bob bouwt in eigen browser, Claude
-verifieert per stap met verse export). Stand van zaken, alles
-exportgeverifieerd met **0 Dart-errors**:
-
-- **Pagina** `stadsactiviteitAanmaken` (`lib/stadsactiviteit_aanmaken/`),
-  Scaffold + AppBar (Background `Primary`, "Show Default Button" aan →
-  automatische terugknop, geen losse titel-widget) + scrollbare
-  `Column` (uniform padding 16).
-- **10 TextFields**, in volgorde, elk met zowel Label als Hint:
-  Titel · Omschrijving · Organisator · Contact · Adres · Postcode ·
-  Plaats · Entreeprijs · Toelichting Entree · WebsiteURL.
-- **2 datumvelden** (`TextFieldDatumStart` / `TextFieldDatumEind`),
-  beide `readOnly: true` + breedte `inf`, met On-Tap-actie
-  `showDatePicker` → `showTimePicker` → gecombineerd in één DateTime →
-  als tekst terug in het veld gezet. Werkt.
-- **Plaats-picker: eigen 3-traps cascade** met Page State (NIET het
-  bestaande `SelectStateDropDownComponent` — zie waarschuwing
-  hieronder). Page State-velden: `createProvincieID`,
-  `createGemeenteId`, `createPlaatsID` (let op de inconsistente
-  hoofdletters — zo staan ze er echt in).
-  - `DropDownProvincie` → Backend Query `provincies`, options
-    `$[:].provincieid` / labels `$[:].provinciename`, On Selected zet
-    `createProvincieID`.
-  - `DropDownGemeente` → Backend Query `gemeenten` met
-    `provincieid: createProvincieID`, options `$[:].gemeenteid` /
-    labels `$[:].gemeentename`, On Selected zet `createGemeenteId`.
-  - `DropDownPlaats` → Backend Query `PlaatsenBijGemeente` met
-    `gemeenteid: createGemeenteId`, options `$[:].plaatsid` / labels
-    `$[:].plaatsname`.
-
-**⚠️ Belangrijke ontwerpbeslissing (Bob, 2026-08-31) — corrigeert de
-oudere spec in het Artifact.** De plaats-picker mag **niet** aan de
-App State-velden `provincieSelectId`/`gemeenteSelectId` hangen: dat is
-de **browse**-state (welke gemeente de gebruiker nu in de app bekijkt)
-en staat volledig los van "in welke plaats maak ik een activiteit
-aan". Om dezelfde reden is het bestaande `SelectStateDropDownComponent`
-bewust **van deze pagina verwijderd** — dat component schrijft namelijk
-rechtstreeks naar die App State-velden en zou dus stilletjes de
-browse-selectie van de gebruiker overschrijven. Vandaar de eigen
-dropdowns met Page State hierboven.
-
-**Gekozen rechtenmodel: HYBRIDE** (Bob, 2026-08-31, via expliciete
-keuze). Het Artifact zei eerder "GEEN restrictie tot eigen steden —
-iedereen mag voor elke plaats voorstellen"; dat is nu bijgesteld naar:
-heeft de gebruiker stadsrechten (`mijn_stadsrechten`, gevuld vanuit
-`field_town_access`, gemeente-niveau sinds 2026-08-27), dan die eigen
-gemeenten **bovenaan als snelkoppeling**; daarnaast blijft de volledige
-Provincie→Gemeente→Plaats-cascade beschikbaar voor iedereen. Reden om
-niet volledig af te schermen: `mijn_stadsrechten` is leeg voor vrijwel
-alle gebruikers, en `stadsactiviteiten/create` doet server-side bewust
-geen rechtencheck — de review-flow (node komt **altijd** ongepubliceerd
-binnen, "wordt beoordeeld") is het vangnet.
-
-**Nieuw Drupal-endpoint gebouwd + live (2026-08-31):
-`plaatsen_bij_gemeente`/index** — `?gemeenteid=<tid>` → array van
-`{plaatsid, plaatsname, plaatsdescription}`. Bewust een **losse nieuwe
-resource** i.p.v. het bestaande `plaatsen.json` op te rekken (Bob's
-keuze, sluit aan bij hoe elk ander endpoint in dit project ook los
-werd toegevoegd): `plaatsen.json` bouwt alleen Provincie (diepte 0) en
-Gemeente (diepte 1) op en kan Plaats-niveau (diepte 2) principieel niet
-teruggeven, ook niet met `limit_levels=3`. Getest op devbob én
-**gedeployed naar productie**. Code staat in `custom.module`
-(`custom_plaatsen_bij_gemeente()` + `custom_plaatsen_bij_gemeente_access()`).
-
-**FlutterFlow API Group hernoemd: `kanweg` → `productie`** (Bob,
-2026-08-31), genereert nu `ProductieGroup` met base-URL
-`https://uitgaanskrant.com`. Bevat 3 calls: `PlaatsenBijGemeente`,
-`Entreeopties`, `Categorieen`. **Valkuil onderweg:** de call eerst als
-top-level aangemaakt mét een *relatief* pad (`/nl/flutterdrup/...`)
-→ geen host, call faalt. Binnen een groep hoort een relatief pad
-(`${baseUrl}` wordt voorgeplakt); top-level moet de volledige
-`https://uitgaanskrant.com/...`-URL.
-
-**Bouwstap 3 is functioneel AF (2026-08-31, live pair-sessie).**
-Restpunten 1 t/m 7 uit de vorige sessie zijn alle zeven gebouwd en
-exportgeverifieerd (`dart analyze`: 0 errors). Kort wat er nu staat, zodat
-een volgende sessie niet opnieuw hoeft te reconstrueren:
-- `DropDownPlaats` → On Selected zet Page State `createPlaatsID`.
-- **Rechten-shortcut** `DropDownGemeentenMijngemeenten` bovenaan: staat in
-  een wrapper-`Column` die de Backend Query `MijnStadsrechten` draagt
-  (de query moest één niveau omhoog — een widget kan zijn *eigen*
-  Backend-Query-response niet gebruiken in zijn eigen Visibility-conditie,
-  wel voor Options). Options `$[:].tid` / labels `$[:].titel`, On Selected
-  zet `createGemeenteID`. Verbergen-bij-leeg via conditie
-  `$[0].titel` **Is Set** — géén "Number of Items", want een
-  JSON-Path-binding is Json-getypeerd en biedt geen lijst-transforms
-  (zelfde beperking als P1-24).
-- **Let op naamswijziging:** de Page State heet nu `createGemeenteID`
-  (hoofdletter D), niet `createGemeenteId`.
-- `DropDownCategorieen` (multi-select, → `createCategorieTids` als echte
-  `List<String>`) en `DropDownEntree` (→ `createEntreeTid`), beide
-  options `$[:].tid` / labels `$[:].naam`.
-- **Foto-upload**: knop "Logo kiezen" (→ `createLogoFid`) en "Foto's
-  kiezen" (→ `addToCreateFotosFids`), beide via het `bestandUpload`-
-  patroon uit `kanwegTestUpload`. Fotoknop verdwijnt bij 5 foto's
-  (`if (_model.createFotosFids.length < 5)`).
-- **Verzendknop** "Activiteit indienen": alle 20 argumenten van
-  `stadsactiviteitCreate` gebonden; datums lopen door de nieuwe custom
-  function **`datumVoorApi`** (knipt Dart's `.000` eraf). Knop heeft een
-  Visibility-guard op `createPlaatsID` **Is Set** (voorkomt de
-  `createPlaatsID!`-null-crash). Daarna een conditional op
-  **`$.success`** → snackbar "wordt beoordeeld" + Navigate Back, anders
-  snackbar met `$.error`.
-  ⚠️ **Valkuil vastgelegd:** een conditie op `$.nid` genereerde
-  `if (getJsonField(...))` zonder null-check — dat compileert (dynamic)
-  maar crasht bij runtime op de bool-cast. Gebruik een sleutel die
-  écht een bool is (`$.success`), of bouw een Single Condition met een
-  expliciete operator.
-
-**Nog te doen op deze pagina:**
-1. **Entry-point ontbreekt** — `stadsactiviteitAanmaken` is alleen via de
-   route bereikbaar. De "+ Voeg toe"-knoppen op `mijnProfiel` staan nog
-   op een placeholder-snackbar (bouwstap 4, restpunt 1) en moeten
-   Navigate-To hierheen worden.
-2. **Nooit end-to-end live getest** — profile-build lukte, maar de AVD
-   crashte tijdens de eerste poging (bekend SEGV-patroon). Nog te
-   bevestigen: dat een ingediende node ongepubliceerd in Drupal
-   binnenkomt met de juiste plaats/datum/categorieën/entree/logo/foto's.
-
-**Vervolg 2026-09-01 — veel gefixt, look&feel-afwerking open.**
-Gefixt en exportgeverifieerd deze dag: sessie-cookies (`[var]` i.p.v.
-`{{var}}`, zie `CLAUDE.md`), datumpickers (On Tap op een omhullende
-Container + Enabled uit), `datumVoorApi` (geneste signatuur → gaf altijd
-`null` → crash op de verzendknop), guards op gemeente-/plaats-dropdown en
-verzendknop, logo- en fotopreview (Page State-type moet **Image Path**
-zijn, niet String — zie `CLAUDE.md`), categorie-multiselect via
-`categorieSubTids`/`categorieSubLabels` + `isSearchable`, upload-URL's
-via `apiBaseUrl`, en `field_town_access` op productie (widget-instelling
-"Leaves only" uit + Max depth 2 stond alleen op devbob; bobcity heeft nu
-3 gemeenten i.p.v. 1254 plaatsen).
-Bevestigd: `bestandUpload` geeft een **absolute** URL terug
-(`https://uitgaanskrant.com/sites/.../evenementen_uploads/...`), dus geen
-prefix-logica nodig.
-Velden zijn gegroepeerd in gestileerde kaarten (`Padding(16)` →
-`Container` met 3px `#EEEEEE`, geen radius, geen schaduw) — merkgetrouw.
-Claude heeft via browser-automatisering de **breedte van 5 groep-
-containers op `infinity`** gezet (`ContainerWat`, `ContainerWanneer`,
-`ContainerWaar`, `ContainerOrganisatie` en de Entree-container).
-
-**Vervolg 2026-09-02 (Claude, browser-automatisering):** de twee
-"Hello World"-koppen zijn nu **"Waar"** en **"Organisatie"**,
-`ContainerMedia` is gestileerd als de andere kaarten (fill
-`secondaryBackground`, border `alternate` 3px, geen radius, width inf),
-en `TextFieldTitel` + `TextFieldWebsiteURL` staan op `infinity` — alle
-12 tekstvelden zijn nu even breed. Punten 2, 5 en 6 hieronder zijn
-daarmee afgehandeld; de rest staat nog open.
-⚠️ **Nuttig gebleken:** in de kleurkiezer staan de merkkleuren al als
-thematokens klaar (Primary `#9A141D`, Secondary `#FF680D`, Tertiary
-`#09B34A`, **Alternate `#EEEEEE`** = de kaartrand, Primary Text
-`#3E454C`). Gebruik die tokens i.p.v. losse hexwaarden.
-⚠️ **Ook gebleken: typen en klikken in het rechterpaneel werkt vanaf
-Claude's kant wél** — de notitie in `CLAUDE.md` uit 2026-08-09 dat dit
-"structureel niet mogelijk" was, klopt niet meer. Widget-tree-selectie,
-de eigenschappen-zoekbalk, tekstvelden, kleurkiezers en de
-∞-breedteknop reageerden allemaal normaal.
-
-**Huisstijl-afronding (Bob's keuze 2026-09-02):** overal **radius 0**
-(strikt merkgetrouw, de site kent geen afronding), sectiekoppen **16px
-gewicht 600, gewone schrijfwijze** (geen kapitalen), en **geen serif**
-op deze pagina.
-Gedaan: `TextFieldTitel`, `TextFieldOmschrijving` en
-`TextFieldDatumStart` staan op radius 0.
-**Nog om te zetten naar radius 0 (20 widgets):** 9 tekstvelden
-(`DatumEind`, `Adres`, `Postcode`, `Plaats`, `Organisator`, `Contact`,
-`WebsiteURL`, `ToelichtingEntree`, `Entreeprijs`), 6 dropdowns, 3
-knoppen (`ButtonLogo`, `ButtonFotos`, `ButtonIndienen`) en 2
-afbeeldingen (logo-preview + het foto-item in de Wrap).
-**Nog te doen:** de 5 sectiekoppen op 16px / gewicht 600 (staan nu op
-14px normaal).
-**Snelste werkwijze (bevestigd werkend):** selecteer het widget, typ
-**"radius"** in de eigenschappen-zoekbalk — het paneel filtert dan tot
-één "Border Radius"-veld — en zet de uniforme waarde op 0. Voor de
-sectiekoppen: zoek op "size" respectievelijk "weight".
-⚠️ **Waarom Claude dit niet afgemaakt heeft:** elke waarde-wijziging
-vraagt via browser-automatisering drie losse round trips (selecteren +
-filteren, dan een *standalone* triple-click op het waardeveld, dan pas
-typen) — binnen één `browser_batch` registreert de triple-click niet.
-Met 20+ widgets is dat 60+ aanroepen met misklik-risico, terwijl het in
-Bob's eigen browser ~5 seconden per widget kost.
-
-**Huisstijl AF (2026-09-03, geverifieerd).** Alle border-radii op 0
-(12 tekstvelden, 6 dropdowns, 3 knoppen, 2 afbeeldingen — 0 treffers
-`circular(8.0)` over), sectiekoppen op 16px.
-(De eerdere notitie dat "Entree"/"Media" nog geen `FontWeight.w600`
-hadden is achterhaald — zie de verificatie hieronder: ze erven w600 al
-van `headlineSmall`.)
-
-**✅ Bouwstap 3 (`stadsactiviteitAanmaken`) is functioneel én visueel AF
-— 2026-09-03, live pair-sessie Bob + Claude.** Verse export,
-`flutter pub get`, `dart analyze lib/` = **0 errors**, en een live
-profile-build op de telefoon-emulator (411dp) waarin de hele
-plaats-cascade end-to-end is doorlopen tot de verzendknop verscheen.
-
-**Wat deze dag is opgelost (alles exportgeverifieerd):**
-- **Orphan-kopieën `Copy`/`Copy2`/`Copy3` verwijderd** — die gaven 3
-  compile-errors; de app bouwt weer.
-- **Plaats-dropdown was op een telefoon onbereikbaar.** De drie
-  cascade-dropdowns stonden naast elkaar in een `Row`, elk
-  `width: 200`. 3 × 200 = 600 > 411dp, dus de derde viel volledig
-  buiten beeld — hij stond niet eens in de accessibility-tree. Zonder
-  `createPlaatsID` verscheen de verzendknop nooit, dus het formulier was
-  via dat pad onbruikbaar. Opgelost: `Row` → `Column`, alle 6 dropdowns
-  op `infinity` (0 treffers `width: 200` over).
-- **`ButtonFotos` las `logouploadResult` i.p.v. `fotouploadResult`** in
-  zowel de `$.success`-conditie als de fout-snackbar → crash op de
-  bool-cast bij een foto vóór een logo, en anders een altijd-TRUE-tak.
-  Beide regels omgezet.
-- **`$.success`-conditie op `ButtonLogo`**, **sectiekop "Wat"**,
-  **lege "Hello World"-container onderaan weg**, **alle border-radii 0**,
-  **sectiekoppen 16px/600**.
-- **`MijnHorecagelegenheden`-cookieheader** van `{{session_name}}={{sessid}}`
-  naar `[…]`-syntax → export toont `'Cookie': '${sessionName}=${sessid}'`
-  en de aanroeper (`mijn_profiel_widget.dart:270`) geeft beide
-  App-State-velden mee. Nergens nog `{{…}}` in `api_calls.dart`.
-- **Look & feel 1 t/m 5:** alle 18 invulwidgets (12 tekstvelden + 6
-  dropdowns) van `fillColor: secondaryBackground` naar
-  `primaryBackground` (waren onzichtbaar wit-op-wit); `Container(height:
-  200)` om `TextFieldAdres` weg; `ContainerMedia` de ontbrekende
-  `Padding(16)`-wrapper gegeven zodat hij in de rooilijn ligt; alle zes
-  sectiekoppen links uitgelijnd; kop "Wat" op 16px/w600/`primaryText`.
-
-**Nog te doen op deze pagina:**
-1. **⏭️ EINDTEST — nooit een activiteit ingediend.** Alles wat het pad
-   blokkeerde is nu weg en de verzendknop is live bereikt, maar er is
-   nog geen node aangemaakt. **Bob wil dit zelf plannen (afspraak
-   2026-09-03: "dan kunnen we morgen testen").** Te controleren na
-   indienen: node komt **ongepubliceerd** binnen, met de juiste plaats,
-   datum (start + eind), categorieën, entree-type, entreeprijs, logo en
-   foto's. Snelste route naar een geldige staat: de **"Mijn
-   gemeenten"-shortcut** (gemeente → plaats), dan verschijnt de knop.
-2. **Look & feel punt 6 — dubbele/zwevende veldlabels.** Bewust
-   geparkeerd door Bob. Boven vier velden staat een losse `Text`-widget
-   die het label herhaalt dat het veld zelf al als `labelText`/`hintText`
-   voert: **"Categorie evenement"**, **"Stadseditor"**, **"Toelichting
-   Entree"**, **"Entreeprijs"**. Bij de laatste twee staat het label
-   letterlijk 2× op het scherm; "Stadseditor" is een zwevend label
-   zonder duidelijk bijhorend veld. Keuze: óf de losse Texts weg, óf ze
-   bij álle velden consequent gebruiken en dan de `labelText` van het
-   veld leegmaken. De kaarten hebben bovendien geen interne padding,
-   waardoor die losse labels de 3px-rand raken.
-3. **Twee niet-blokkerende Issues-errors op deze pagina** (gemeten
-   2026-09-03, export loopt er gewoon mee door):
-   - `Property Override — Invalid API call configuration` op
-     `DropDownGemeentenMijngemeenten`;
-   - `Property Override — return type mismatch` op `DropDownProvincie`
-     — die genereert `options: List<String>.from(getJsonField(...
-     $[:].provincieid ...))` terwijl "Option Value Data Type" op
-     **String** staat; komt `provincieid` als getal terug, dan is dat
-     precies de mismatch.
-   De cascade werkt live, dus geen brand, maar wel opruimen.
-4. **Entry-point ontbreekt** — `stadsactiviteitAanmaken` is alleen via
-   de route bereikbaar. De "+ Voeg toe"-knoppen op `mijnProfiel` staan
-   nog op een placeholder-snackbar en moeten Navigate-To hierheen worden
-   (bouwstap 4, restpunt 1), met `plaats_tid` als page-parameter.
-5. **Geen validatie vooraf.** Alle 12 `…TextControllerValidator`-velden
-   zijn in het model gedeclareerd maar nergens toegekend, dus er is geen
-   verplicht-veld-check. Een leeg Titel/Datum gaat gewoon mee naar de
-   server (`datumVoorApi('')` geeft `''`, geen crash) en komt terug als
-   Drupal-foutmelding in de snackbar. Werkt, maar rauw.
-6. **Geen "bezig"-indicatie bij indienen** — geen enkele knop op deze
-   pagina heeft `showLoadingIndicator`. Bij een trage upload/create
-   lijkt de knop niets te doen.
-7. **Foto's zijn niet te verwijderen** — de Wrap toont ze als 80×80
-   `Image.network` zonder verwijderknop; verkeerd gekozen foto betekent
-   pagina verlaten en opnieuw beginnen.
-8. **Lege AppBar** — alleen een rode balk met terugknop, geen titel.
-9. **De verzendknop is onzichtbaar tot er een plaats gekozen is** (de
-   Is-Set-guard) zonder enige uitleg. Overweeg de knop altijd te tonen
-   maar uit te schakelen met een hint "Kies eerst een plaats".
-10. De laadspinner van elke dropdown-`FutureBuilder` is een
-    `SpinKitFadingCircle` van **80×80 in een `Center`** — die is groter
-    dan de dropdown zelf en laat de layout tijdens het laden verschuiven.
-11. **De categorie-dropdown en de gemeente-lijst zijn niet
-    alfabetisch** — ze volgen de volgorde van de API (provincies komen
-    binnen als Flevoland, Drenthe, Friesland, …). Cosmetisch.
-
-⚠️ **Let op bij vervolgwerk: `uitgaansevenementAanmaken` wordt in een
-andere chat gebouwd** als duplicaat van deze pagina, en zit in dezelfde
-`productie`-API-groep. Op 2026-09-03 blokkeerde een fout dáár
-(`API Action — Variable value configured incorrectly for API call`, de
-ongebonden `session_name`/`sessid` op de On-Page-Load-`MijnHorecagelegenheden`-
-call) meermaals onze export hier. Zie het nieuwe Issues-recept in
-`CLAUDE.md`.
+**P2-15 · ✅ VOLLEDIG AFGEROND 2026-09-13.** "Evenementen en stadsactiviteiten
+aanmaken vanuit de app" is van bouwstap 1 t/m de live indiening af. De
+bouwhistorie (±1000 regels: endpoints, custom actions `bestandUpload` /
+`stadsactiviteitCreate` / `evenementCreate`, de pagina-opbouw en alle
+tussentijdse restpunten) is hier verwijderd; de blijvende inzichten staan in
+`CLAUDE.md` en het volledige veldenschema in het Artifact "Redactierechten &
+Contentschema".
 
 
-**Overige restpunten (sessie 2026-08-31, live pair-fix):**
-
-8. **✅ AFGEROND (Bob, 2026-09-03): stadsrechten-migratie is gedraaid.**
-   `field_town_access` stond op plaats-niveau (9.292 rijen over 6
-   accounts); de migratie naar gemeente-niveau is uitgevoerd voor alle
-   accounts en de scope-beslissing is gemaakt. Live bevestigd in de app
-   (2026-09-03): de "Mijn gemeenten"-shortcut toont voor dit testaccount
-   nu **4 gemeenten** (Amsterdam, Drechterland, Enkhuizen, Stede Broec)
-   i.p.v. 1254 plaatsen — de shortcut werkt dus zoals bedoeld en is een
-   echte snelkoppeling geworden, geen tweede plaatsenlijst.
-9. **Categorie-dropdown toont de hele hiërarchie plat.** `categorieen`
-   geeft `diepte`/`parent_tid` mee; de multiselect toont nu ouder- én
-   kindtermen door elkaar zonder inspringing. Optionele verfijning.
-   **Update 2026-08-31:** bindingen staan inmiddels op
-   `$[?(@.diepte == 1)]` — zie de live-testnotitie hieronder; nog niet
-   bevestigd of die filtersyntax werkt.
-10. **Basis-URL centraliseren — Fase A af, Fase B open (app-breed,
-    overweeg een eigen taak-ID).** Aanleiding: op devbob kunnen testen
-    zonder overal URL's te wijzigen. **Gedaan:** App State-variabele
-    **`apiBaseUrl`** (default `https://uitgaanskrant.com`), en alle vier
-    de custom actions (`stadsactiviteitCreate`, `bestandUpload`,
-    `drupalRequest`, `drupalLogin`) bouwen hun URL daaruit op. Ze zijn
-    **tolerant**: een argument dat met `http(s)://` begint wordt
-    ongewijzigd gebruikt, anders als pad achter `apiBaseUrl` geplakt —
-    dus alle ~26 bestaande aanroepen met een volledige URL blijven
-    werken en kunnen in eigen tempo ingekort worden. Truc: de parameter
-    zelf wordt overschreven (`url = ...`), zodat verderop in de action
-    niets aangepast hoefde te worden. **Let op:** custom *Functions*
-    kunnen `FFAppState()` NIET lezen (`custom_functions.dart`
-    importeert `app_state.dart` niet, en dat importblok is niet
-    bewerkbaar) — custom *Actions* wel.
-    **Nog open (Fase B):** de 13 top-level API Calls in `api_calls.dart`
-    hebben nog een hardcoded host. Centraliseren betekent ze in de
-    bestaande API Group onderbrengen (`ProductieGroup`) — check eerst
-    of het ⋮-menu van een API Call een "Move to group" heeft; zo niet
-    moet elke call opnieuw aangemaakt worden ín de groep en moet **elke
-    Backend Query die 'm gebruikt opnieuw gebonden** worden (het dure,
-    foutgevoelige deel). Hernoem bij die gelegenheid de groep
-    `productie` → `drupal`/`backend`, want de naam klopt niet meer
-    zodra hij naar devbob wijst.
-11. **Devbob zit achter HTTP Basic Auth** — alleen `apiBaseUrl` omzetten
-    naar devbob geeft op álles een 401. Er is een
-    `Authorization: Basic <base64>`-header nodig die meegaat wanneer de
-    base-URL devbob is: in de custom actions één `if` erbij, voor de
-    API Calls een group-level header (extra argument om Fase B te doen).
-    Nog niet gebouwd.
-
-**Live test 2026-08-31 — pagina werkt grotendeels, drie bugs gevonden
-en gefixt, twee open.**
-- **Gefixt: sessie-cookie ging nooit mee.** Vijf API Calls hadden
-  `Cookie: {{session_name}}={{sessid}}` — Postman-syntax, die
-  FlutterFlow niet substitueert (`ApiManager` doet geen `{{ }}`-
-  vervanging, stuurt de tekst letterlijk). Moet `[session_name]=[sessid]`
-  zijn. **Hiermee is ook de aanname weerlegd dat `drupalRequest` nodig
-  was omdat API Calls geen cookie konden meesturen** — dat kan wel; het
-  ging destijds mis op de syntax. Zie de nieuwe regel in `CLAUDE.md`.
-  Gefixt: `Entreeopties`, `Categorieen`, `MijnStadsrechten`.
-  **Nog open: `MijnHorecagelegenheden` (regel ~1096) en
-  `FavorietenAgenda` (regel ~131)** — die staan nog op `{{…}}`, dus
-  `mijnProfiel`'s horeca-sectie en Favorieten Tab 1 zijn vermoedelijk
-  al die tijd leeg. (`FavorietenAgendaTESTKANWEG` heeft een hardcoded
-  sessie-cookie; dode call, zie P2-7.)
-- **Gefixt: datumpickers vuurden nooit.** Beide datumvelden hadden hun
-  picker op **On Submit** (`onFieldSubmitted`) terwijl het veld
-  `readOnly` is — dat kan per definitie niet afgaan. Een TextField heeft
-  in FlutterFlow geen On Tap-trigger; oplossing: **Wrap Widget →
-  Container**, actieketen op de Container's On Tap, en op het TextField
-  **Enabled uit** (een `readOnly` veld vangt de tap zelf op en geeft
-  'm niet door aan de ouder).
-- **Gefixt: crashes op lege API-responses.** `DropDownGemeente`,
-  `DropDownPlaats` en de verzendknop hebben nu Visibility-guards
-  (`createProvincieID` / `createGemeenteID` / `createPlaatsID` Is Set).
-- **Open: geen terugkoppeling na het kiezen van logo/foto's.** Plan
-  staat klaar: `bestandUpload` geeft naast `fid` ook een `url` terug;
-  nieuwe Page State `createLogoUrl` (String) + `createFotosUrls`
-  (List<String>), gevuld met `$.url` in dezelfde Update-Page-State-actie,
-  daarna een Image (logo) en een Wrap met Generate Dynamic Children
-  (foto's). **Blokkade:** de "Set from Variable"-dialoog van de Image's
-  **Path** toont alle losse String-variabelen gedimd en laat alleen
-  `List<String>`-velden selecteren. Debugplan staat hieronder.
-- **Open: categorie-multiselect gefilterd op subniveau.** Bindingen
-  staan nu op `$[?(@.diepte == 1)].tid` / `.naam` (diepte 0 = de 7
-  hoofdcategorieën, diepte 1 = de subcategorieën, bevestigd via curl).
-  **Nog niet live geverifieerd** of FlutterFlow's `getJsonField` deze
-  JSONPath-filtersyntax aankan. Faalt hij, dan een custom function met
-  platte Map-toegang (`categorieVeldOpDiepte(respons, diepte, veld)`),
-  of een `?diepte=1`-parameter op het Drupal-endpoint.
-
-**Debugplan voor de Image-Path-blokkade (eerste stap volgende sessie):**
-1. **Zoekbalk gebruiken.** Typ `createLogoUrl` in "Search variables..."
-   bovenin de dialoog. Deze keuzelijst rendert in dit project vaker
-   items als niet-klikbaar terwijl ze het wel zijn; de gefilterde lijst
-   werkt dan meestal gewoon. Kost 5 seconden, probeer dit eerst.
-2. **Type van de variabele controleren.** Pagina-root → Page State
-   Variables → `createLogoUrl` moet **String** zijn met **Is List uit**.
-   Staat er iets anders, dan is dat meteen de verklaring.
-3. **Tegenproef.** Probeer in dezelfde Path-dialoog een ándere losse
-   String te kiezen (bv. `createLogoFid`). Lukt dat ook niet, dan is
-   het een typefilter op het Path-veld en niet iets aan `createLogoUrl`.
-   Lukt het wél, dan is er iets mis met die ene variabele.
-4. **Tegenproef 2.** Bind de Path aan een App State-String (bv.
-   `userName`). Werkt dát, dan accepteert het veld wél Strings maar
-   geen *Page* State — een scope-probleem, geen typeprobleem.
-5. **Omweg als 1-4 niets opleveren:** custom function
-   `String logoPad(String? url) => url ?? '';` (plat, geen closure,
-   Ctrl+S) en de Path binden aan **Custom Functions → `logoPad`** met
-   `createLogoUrl` als argument. Dat patroon werkt in dit project
-   bewezen om typeblokkades in bindingsdialogen te omzeilen (zie
-   `favorietenBodyNode`).
-6. Werkt de binding, vergeet dan niet de **Visibility → Conditional**
-   op `createLogoUrl` "Is Set and Not Empty" — anders staat er vóór het
-   uploaden een kapotte-afbeeldingsplek. En haal het testplaatje
-   (`https://picsum.photos/seed/489/600`) uit het Path-veld.
-7. Voor de foto's geen losse Image maar een **Wrap** met Generate
-   Dynamic Children over `createFotosUrls` — daar is `List<String>`
-   juist wél het gevraagde type.
-
-**Losse observatie (niet blokkerend):** FlutterFlow's Issues-paneel
-toont 1 error "Property Override — return type mismatch" wijzend naar
-`DropDownProvincie`, terwijl de export 0 Dart-errors geeft en de
-gegenereerde code van die dropdown correct is. Vermoedelijk een stale
-builder-melding; een harde reload ruimt zo'n melding meestal op. Niet
-verder tijd in steken tenzij hij een export daadwerkelijk blokkeert.
-
-**Daarna nog te bouwen:** "Evenement aanmaken" — eigen custom action
-`evenementCreate` naar `evenementen/create.json` (zelfde
-20-argumenten-aanpak als `stadsactiviteitCreate`), plus
-horecagelegenheid-picker gevuld via `mijn_horecagelegenheden`
-(auto-select bij precies 1 resultaat).
-
-**Bijvangst deze sessie:** de custom function
-`filterHorecagelegenheden` gaf een compile-error omdat
-**`getJsonField()` niet beschikbaar is binnen Custom Functions** (die
-helper komt uit `/flutter_flow/flutter_flow_util.dart`, en dat
-import-blok is bij Custom Functions niet te bewerken — bij Custom
-*Actions* wordt het wél automatisch meegeïmporteerd). Opgelost door
-platte Map-toegang te gebruiken (`map?[key]`) met een `$.`-prefix-strip
-vooraf. **Generiek te onthouden: gebruik `getJsonField()` nooit in een
-Custom Function.**
+**Wat er live bewezen is (13 sep, telefoon-emulator, plaats Enkhuizen):**
 
 
----
-
-*(Onderstaande taken komen uit de merkanalyse web-vs-app van 2026-08-30.
-Volledige onderbouwing met screenshots: [Designbrug-rapport](https://claude.ai/code/artifact/29f3412a-305f-49f3-971c-cd9bd310beb1).
-Afvinkbare werklijst: [Designbrug werklijst](https://claude.ai/code/artifact/0c0c7347-d681-417d-83d7-7feb1de6457d).)*
-
-**Bouwstap 5 — pagina `uitgaansevenementAanmaken`. IN UITVOERING
-sinds 2026-08-31 avond. Stap 1 (custom action `evenementCreate`) is AF
-en exportgeverifieerd; stap 2 (los testen tegen productie) staat klaar
-op 2 kleine handelingen na — zie "STAND VAN ZAKEN" onderaan dit blok,
-begin daar.** Dit is stap 3 uit de oorspronkelijke bouwvolgorde bovenaan
-P2-15 ("Pagina Evenement aanmaken"). Alles hieronder is uitgezocht en
-geverifieerd — een nieuwe sessie kan direct bouwen zonder eerst het
-Artifact te hoeven lezen.
-
-> ⚠️ **LET OP — de FlutterFlow-export is op dit moment GEBLOKKEERD.**
-> `flutterflow export-code` faalt met `Status: 400 / Error generating
-> code for the project`. Oorzaak is bekend en klein: het argument
-> `fotosFids` van de testknop staat nog op "Unset" (zie STAND VAN ZAKEN
-> punt 1). Zolang dat niet gezet is, kan **niemand** exporteren — dus
-> dit als eerste oplossen, ook als je aan een heel andere taak begint.
-
-**Kernpunt: deze pagina is EENVOUDIGER dan `stadsactiviteitAanmaken`,
-niet moeilijker.** Het zwaarste stuk van bouwstap 3 — de 3-traps
-provincie→gemeente→plaats-cascade — vervalt hier volledig. Drupal leidt
-stad, adres én geo automatisch af uit de gekozen horecagelegenheid
-(`hook_node_presave` kopieert `field_geo_horecagelegenheid`,
-`field_hor_municipality_town` en `field_address3` over). De app stuurt
-alleen het nid.
-
-**API-contract `POST evenementen/create.json`** (uit het Artifact,
-sectie "API-contract"; curl-bevestigd 2026-08-26):
-
-| Argument | Type | Verplicht | Toelichting |
-|---|---|---|---|
-| `horecagelegenheid_nid` | int | **ja** | Moet een `horecagelegenheid`-node zijn met `uid` = ingelogde user, anders **403** |
-| `titel` | string | **ja** | |
-| `datum_start` | string | **ja** | `'YYYY-MM-DD HH:MM:SS'` |
-| `datum_eind` | string | nee | leeg → gelijk aan `datum_start` |
-| `omschrijving` | string | nee | |
-| `entree_tid` | int | nee | tid uit `entreeopties/index` |
-| `entree_prijs` | string | nee | decimaal als string, bv. `"7.50"` |
-| `toelichting_entree` | string | nee | |
-| `tickets_url` | string | nee | **bestaat niet op stadsactiviteit** |
-| `website_url` | string | nee | |
-| `categorie_tids` | array<int> | nee | uit `categorieen/index`; ongeldige tid → 400 |
-| `logo_fid` | int | nee | fid uit `bestand_upload/upload` |
-| `fotos_fids` | array<int> | nee | max 5, meer → 400 |
-
-**Verschillen met `stadsactiviteiten/create` — let hier op:**
-1. **`horecagelegenheid_nid` vervangt `plaats_tid`.** Geen plaats-picker,
-   geen 3-traps cascade, geen Page State voor provincie/gemeente/plaats.
-2. **Géén `adres`-struct, géén `organisator`, géén `contact`** — die
-   drie velden bestaan hier niet. Scheelt 4 TextFields.
-3. **`tickets_url` komt er wél bij** (1 extra TextField).
-4. **De response is anders en dat is gebruikerszichtbaar:**
-   `{"status": "created", "nid": "..."}` — de node komt **direct
-   gepubliceerd** binnen. Toon dus **"geplaatst"**, NIET "wordt
-   beoordeeld" (dat laatste hoort alleen bij stadsactiviteiten, die
-   komen altijd ongepubliceerd binnen).
-5. **Eigenaarschap-gate:** een horecagelegenheid van iemand anders geeft
-   403 met "Deze horecagelegenheid is niet van jou" (curl-bevestigd op
-   nid 70132). Kan in de praktijk niet gebeuren als de picker uit
-   `mijn_horecagelegenheden` gevuld wordt, maar vang de 403 netjes af.
-
-**Wat er al klaarstaat (niets van dit hoeft opnieuw gebouwd):**
-- Custom action **`bestandUpload`** (`lib/custom_code/actions/bestand_upload.dart`)
-  — foto → fid. Wil de **volledige** upload-URL, niet alleen een base.
-- API Calls **`MijnHorecagelegenhedenCall`**, **`CategorieenCall`**,
-  **`EntreeoptiesCall`** (`lib/backend/api_requests/api_calls.dart`) —
-  alle drie bestaan en zijn exportgeverifieerd.
-- Pagina **`stadsactiviteitAanmaken`** (`lib/stadsactiviteit_aanmaken/`)
-  — het te kopiëren sjabloon voor de TextFields, de twee datumvelden
-  (`readOnly` + `showDatePicker` → `showTimePicker` → gecombineerd
-  terug in het veld), de categorie-multiselect en de foto-upload.
-- Pagina **`mijnProfiel`** (`lib/mijn_profiel/`) — heeft al een
-  "+ Voeg toe"-knop bij "Mijn horecagelegenheden" die nu nog een
-  placeholder-snackbar toont; die wordt straks de entry-point.
-
-**Wat nieuw gebouwd moet worden:**
-1. ~~**Custom action `evenementCreate`**~~ — **AF (2026-08-31).**
-   Staat in `lib/custom_code/actions/evenement_create.dart`, 16
-   argumenten (`sessionName`, `sessionId`, `token`,
-   `horecagelegenheidNid`, `titel`, `datumStart` verplicht; de rest
-   nullable, waarvan `categorieTids` en `fotosFids` als
-   `List<String>?`). Return type JSON (`Future<dynamic>`). POST naar
-   `$base/en/flutterdrup/evenementen/create.json` met `apiBaseUrl` uit
-   App State. 403 wordt apart afgevangen met de tekst "Deze
-   horecagelegenheid is niet van jou". Geverifieerd met een verse
-   export: byte-identiek aan de aangeleverde bron en `dart analyze` op
-   de hele export gaf 0 errors.
-2. **Pagina `uitgaansevenementAanmaken`** — Scaffold + AppBar
-   ("Show Default Button" aan), scrollbare Column, padding 16.
-   6 TextFields (Titel · Omschrijving · Entreeprijs · Toelichting
-   entree · Tickets-URL · Website-URL), 2 datumvelden, 1 dropdown
-   horecagelegenheid, 1 dropdown entree, 1 categorie-multiselect,
-   logo-upload + foto's-upload.
-3. **Horecagelegenheid-picker**: DropDown met Backend Query
-   `MijnHorecagelegenheden` (variabelen `session_name`/`sessid` →
-   App State `userSessionname`/`userSessionid`), optie-label `$.titel`,
-   waarde `$.nid`. **Auto-select bij precies 1 resultaat** (Bob's eis
-   uit de oorspronkelijke bouwvolgorde).
-4. **Verzendknop** — en neem hier meteen de les van P2-15 restpunt 7
-   mee: laat de knop **geen non-null assertion op een Page State-veld**
-   krijgen. Schakel 'm uit (of verberg 'm) zolang
-   `horecagelegenheid_nid`, titel of datum niet gezet zijn, anders
-   crasht indienen met "Null check operator used on a null value"
-   vóórdat de custom action zijn eigen nette foutmelding kan tonen.
-5. **Page parameter `horecagelegenheid_nid`** (optioneel) zodat
-   `mijnProfiel`'s "+ Voeg toe"-knop 'm vooringevuld kan meegeven.
-
-**Bouwvolgorde-advies:** eerst de custom action + los testen tegen
-productie met één minimaal geldig verzoek (titel + nid + datum), dán
-pas de formulier-pagina — zelfde volgorde die bij bouwstap 1/2 goed
-werkte.
-
----
-
-### STAND VAN ZAKEN bouwstap 5 (2026-09-02) — hier verder
-
-**Gedaan (Claude, 2026-09-02):** de pagina **`uitgaansevenementAanmaken`**
-bestaat, route `/uitgaansevenementAanmaken`. Gemaakt door
-`stadsactiviteitAanmaken` te dupliceren (rechtsklik → Duplicate Page →
-Rename Page) en daarna op te schonen:
-- **Verwijderd:** het complete blok "Waar" (Mijn-gemeenten-picker, de
-  provincie/gemeente/plaats-cascade, adres, postcode, plaats) én de
-  velden **Organisator** en **Contact**.
-- **Behouden:** Titel, Omschrijving, categorie-multiselect, beide
-  datumvelden mét hun picker-logica, Website-URL, entree-dropdown,
-  toelichting entree, entreeprijs, logo-upload, foto's-upload,
-  verzendknop. (Dat zijn precies de dure onderdelen — niet opnieuw
-  bouwen.)
-
-**Export weer open (2026-09-02).** De blokkade zat níet in de
-upload-actienamen maar in de **verzendknop**: die riep nog
-`stadsactiviteitCreate` aan met argumenten die naar de verwijderde
-velden wezen (`plaatsTid → createPlaatsID` e.d.). Zodra de custom action
-omgezet werd naar `evenementCreate` verdween de hele oude argumentenlijst
-en liep de export weer (`All done!`).
-
-**Ook gedaan:** de twee upload-actienamen zijn uniek gemaakt
-(`UploadDataLogoEvenement`, `UploadDataFotosEvenement`) — dat was nodig
-omdat `Duplicate Page` ze meekopieert en ze projectbreed uniek moeten zijn.
-
-**Stand in de export geverifieerd:** 7 tekstvelden, 2 dropdowns, 3 knoppen,
-roept `evenementCreate` aan, en **0** resten van Organisator / Contact /
-Provincie / Postcode / Adres.
-
-**Stand bouwstap 5 na sessie 2026-09-03b (Claude bouwde zelf in de builder):**
-
-*Afgerond en met verse export geverifieerd:*
-- **Punt 3 (page state).** `createProvincieID` en `createGemeenteID` verwijderd;
-  `createPlaatsID` **hernoemd** naar `createHorecagelegenheidNid` (String,
-  nullable). Die hernoeming nam meteen de bestaande Visibility-conditie van
-  `ButtonIndienen` mee — die stond op `createPlaatsID != null && != ''` en
-  guard nu dus op de horecagelegenheid. Daarmee is punt 4's eis "knop uit
-  zolang horecagelegenheid leeg is" gratis geregeld.
-- **Punt 2 (tickets-URL).** `TextFieldWebsiteURL` gedupliceerd →
-  `TextFieldTicketsURL`, label `TicketsURL`, hint "Link om kaarten te kopen",
-  staat in `ContainerOrganisatie` onder het website-veld.
-- **Punt 1 (horeca-dropdown).** `DropDownCategorieen` gedupliceerd →
-  `DropDownHorecagelegenheid` (laatste kind van `ContainerWat`'s Column),
-  multi-select uit, Backend Query = `MijnHorecagelegenheden` met
-  `session_name`/`sessid` → App State `userSessionname`/`userSessionId`,
-  Options Values `$[:].nid`, Labels `$[:].titel`, hint "Kies je
-  horecagelegenheid", On Selected → Update Page State
-  `createHorecagelegenheidNid`.
-- **Punt 6 (succesmelding).** Snackbar in de TRUE-tak zegt nu "Je evenement is
-  geplaatst." De FALSE-tak toont `$.error` en bleef ongewijzigd.
-- **Punt 5 (page parameter).** Optionele String-page-parameter
-  `horecagelegenheidNid` toegevoegd. Bijbehorende bedrading: On Page Load →
-  Update Page State `createHorecagelegenheidNid` = die parameter, én de
-  dropdown's **Initial Option Value** = diezelfde parameter (initial value moet
-  aan de parameter hangen, niet aan de page state — On Page Load draait pas ná
-  de eerste build).
-
-*Punt 0 (argumenten `evenementCreate`) — 15 van de 16 gebonden:*
-`sessionName`→`userSessionname`, `sessionId`→`userSessionId`,
-`token`→`userToken`, `horecagelegenheidNid`→page state
-`createHorecagelegenheidNid`, `titel`→`TextFieldTitel`,
-`datumStart`→`datumVoorApi(TextFieldDatumStart)`,
-`datumEind`→`datumVoorApi(TextFieldDatumEind)`,
-`omschrijving`→`TextFieldOmschrijving`, `entreeTid`→`createEntreeTid`,
-`entreePrijs`→`TextFieldEntreeprijs`,
-`toelichtingEntree`→`TextFieldToelichtingEntree`,
-`ticketsUrl`→`TextFieldTicketsURL`, `websiteUrl`→`TextFieldWebsiteURL`,
-`categorieTids`→`createCategorieTids`, `logoFid`→`createLogoFid`.
-
-**Punt 0 is compleet:** alle 16 argumenten van `evenementCreate` zijn gebonden
-en met een verse export geverifieerd (`dart analyze` op die export: 0 errors).
-De 16e (`fotosFids`) stond eerst per ongeluk op `createCategorieTids` en is door
-Bob gecorrigeerd naar `createFotosFids`.
-
-**Let op bij toekomstig werk aan deze knop:** het onderste argument van een lange
-argumentenlijst valt in Claude's browserviewport onder de onderrand van het
-paneel en is daar niet bereikbaar (collapsen van alle andere argumenten,
-muiswiel-scrollen, scrollbar slepen en Page Down helpen geen van alle) — die
-laatste rij moet Bob zetten. Zie ook de CLAUDE.md-notitie hierover.
-
-**Restpunten 1-3 zijn afgerond (2026-09-03b, Bob + Claude samen):**
-1. De horeca-dropdown heeft nu een eigen tekstlabel "Uw Horecagelegenheid"
-   erboven; het categorie-label staat weer bij de categorie-dropdown.
-2. **Auto-select bij precies 1 horecagelegenheid is gebouwd.** Nieuwe custom
-   function `horecaVoorselectie(mijnHoreca, paramNid)` (Json + String? in,
-   String? uit): geeft de page parameter terug als die gezet is, anders het
-   `nid` als de lijst precies 1 zaak bevat, anders `null`. Op twee plekken
-   gebruikt, want de lijst leeft alleen bínnen de `FutureBuilder` van de
-   dropdown terwijl de knop-zichtbaarheid aan de page state hangt:
-   - **Dropdown → Initial Option Value** = `horecaVoorselectie(<eigen backend
-     response, JSON Body, No Further Changes>, <page parameter>)`. Vult zowel
-     het zichtbare veld als `_model.dropDownHorecagelegenheidValue`.
-   - **On Page Load** = Backend Call `MijnHorecagelegenheden` (output
-     `mijnHorecaResp`, sessievars gebonden) → Update Page State
-     `createHorecagelegenheidNid` = dezelfde functie over die response.
-     Nodig omdat de verzendknop een sibling van de FutureBuilder is: als die
-     future klaar is rebuildt alléén de FutureBuilder-subtree, niet de knop.
-   Kost één extra lichte GET bij het openen van de pagina. **Bewust géén
-   `cache: true` op `MijnHorecagelegenheden` gezet:** beide calls vertrekken
-   vrijwel gelijktijdig, dus de cache dedupliceert ze toch niet, en het zou
-   alleen staleness introduceren.
-3. De "Hello World"-restplaceholder onderaan de pagina is verwijderd.
-
-**Bouwstap 5 is compleet.** `mijnProfiel`'s "+ Voeg toe"-knop bij Mijn
-horecagelegenheden navigeert nu naar `/uitgaansevenementAanmaken`
-(`context.pushNamed`), **bewust zónder `horecagelegenheidNid` mee te geven**:
-die knop staat in de sectiekop, bóven de ListView, dus daar is geen loop-item
-en dus geen nid in scope. Dat hoeft ook niet — `horecaVoorselectie` selecteert
-de zaak vanzelf voor als de gebruiker er precies één heeft, en de meeste
-gebruikers hebben er één (Bob, 2026-09-03). Wil je later een knop *per kaart*
-("nieuw evenement bij déze zaak"), dan moet die binnen de ListView staan; daar
-is `horecaItem` → JSON Path `$.nid` wél beschikbaar.
-
-**Wat nog rest voor P2-15: een live test op een toestel** — inloggen als
-horeca-eigenaar, controleren dat de dropdown zich voorselecteert en dat de
-verzendknop verschijnt, en één evenement daadwerkelijk indienen (verwacht:
-snackbar "Je evenement is geplaatst." en de node terugvinden op de site).
-
-**Nieuwe FlutterFlow-valkuil, gevonden tijdens deze stap (geldt straks
-óók voor de formulierpagina):** een **List-typed custom-action-argument
-mag niet op "Unset" blijven staan**, ook niet als het in Dart nullable
-is. Het Issues-paneel meldt dan `Custom action argument "X" is not set
-properly` en dat **blokkeert élke export** met een generieke
-`Status: 400 / Error generating code for the project` — de foutmelding
-zelf noemt de oorzaak niet, dus check bij die melding altijd eerst het
-Issues-paneel. Optionele **String**-argumenten mogen wél gewoon Unset
-blijven; alleen List-types niet. Oplossing: Value → **Inline Function**
-→ expression `<String>[]`. Op de echte formulierpagina verdwijnt het
-vanzelf zodra de lijsten aan page state gebonden worden.
-
-**Doodlopend spoor, niet opnieuw proberen:** het niet-scrollende
-rechterpaneel is *niet* te omzeilen met page-zoom. `document.body.style
-.zoom='0.75'` via `javascript_tool` wérkt visueel (alles wordt kleiner),
-maar Flutter Web relayout niet — het canvas houdt zijn logische
-afmetingen, dus je ziet exact dezelfde hoeveelheid content, alleen
-kleiner. Ook `flutter-view` handmatig groter maken (`style.width/height`)
-plus een `resize`-event dispatchen verandert er niets aan. Argumenten
-één voor één inklappen werkt wel, maar het paneel accepteert **maar één
-inklap-klik per tool-aanroep** — meerdere kliks in één `browser_batch`
-laten alleen de eerste landen.
-
----
-
-*(P2-16 afgerond 2026-09-04 — Bob, builder. Home-tab "Cultuur" heet nu
-**"Cultuur & Info"**, gelijk aan de site. **Bewuste afwijking van het
-oorspronkelijke voorstel:** "Activiteiten" wordt **niet** hernoemd naar
-"Stadsactiviteiten" — Bob houdt het op Activiteiten. Films en Jeugd
-blijven ook ongewijzigd; de app mag fijnmaziger zijn dan de site.
-De tweede helft (gelegenheiddetail `Info/Links/Bezorgen` tegenover de
-site's `Info/Opening/Map`) is niet opgepakt en staat hieronder los.)*
-
-*(P2-16b afgerond 2026-09-04 — Bob, builder. **De echte vondst was niet
-een verkeerde tabnaam maar ontbrekende informatie:** `openingstijden` en
-`openingstijdenuitzondering` stonden nergens op
-`HorecagelegenheidCurrent`, terwijl de API ze wel levert. Beide
-toegevoegd, met Visibility-guard op `openingstijden`.
-**Bewust niet gedaan:** (1) de Links-tab opheffen — Bob houdt 'm; de app
-mag fijnmaziger zijn dan de site (zelfde redenering als bij P2-16's
-"Activiteiten" en P0-11's drawer), en de Info-tab zou onoverzichtelijk
-worden met vijf links erbij. (2) "Map" als eigen tab — er staat al een
-`GoogleMapsIconButtonWidget` in de Info-tab (P2-14), en een echte kaart
-vergt een Maps API-key + billing, wat bewust P2-2/v2 is.
-**Bob's kanttekening (2026-09-04):** look & feel van site en app moeten
-t.z.t. in één keer synchroon getrokken worden, niet scherm voor scherm —
-losse "maak het net als de site"-punten dus met die bril bekijken.)*
-
-*(P2-16b restpunt afgerond 2026-09-04 — `openingstijdenuitzondering`
-heeft nu een `Is Set`-guard (regel 693). Daarmee zijn alle 21 API-velden
-op `HorecagelegenheidCurrent` afgedekt.)*
-
-⚠️ **Guard-audit `HorecagelegenheidCurrent` (2026-09-04, verse export):
-alle 21 API-velden zijn afgedekt op dat ene na.** Let bij toekomstig
-zoekwerk op twee valkuilen: er staan **twee** guard-patronen door elkaar
-— `if (getJsonField(...) != null)` (nieuwer, JSON-Path) en
-`if (EstablishmentInfoCall.establishmentX(...) != null && ... != '')`
-(P0-8) — én drie getters hebben een **typfout** in hun naam:
-`establshmentTitel`, `establshmentKvk`, `establshmentBestellink` (zonder
-de **i**). Een grep op "establishment" mist die drie en suggereert dan
-ten onrechte een ontbrekende guard.
+| route | resultaat |
+|---|---|
+| `evenementen/create` | 200, node gepubliceerd, zichtbaar in view + agenda + publieke pagina |
+| `stadsactiviteiten/create` | 200 `created_pending_review`, node 403 voor publiek — correct ongepubliceerd |
+| `bestand_upload/upload` | logo + 2 foto's, fids terug, URL's staan op de node |
+| tijdzone | na Bob's `_custom_events_lokaal_naar_utc` klopt de tijd exact |
+
+**Nog open, allemaal klein en niet-blokkerend:**
+1. Sectie **"Mijn redactierechten" verbergen als de lijst leeg is** (de meeste
+   gebruikers hebben geen `field_town_access`). Waarschijnlijk een
+   ConditionalBuilder op de hele sectie met een "Number of Items > 0"-transform;
+   reken op een eigen poging gezien de bekende ConditionalBuilder-risico's.
+2. **Entry-point naar `mijnProfiel`** vanuit de app (bv. Favorieten'
+   "Gebruiker"-tab) — de pagina is nu alleen via een directe route bereikbaar.
+3. **`EntreeoptiesCall`'s header** is niet los geverifieerd op dezelfde
+   dubbele-substitutie-bug die `CategorieenCall` had (die is gefixt).
+   Waarschijnlijk oké, niet met zekerheid gecheckt.
 
 
 **P2-22 · Eigenaar: Bob (P2-15-werk; blokkeert de export NIET).**
 **P2-22 · Eigenaar: Bob (P2-15-werk; blokkeert de export NIET).**