Forráskód Böngészése

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 23 órája
szülő
commit
d807679ce0
2 módosított fájl, 206 hozzáadás és 1365 törlés
  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
   `sleep`/wachttijd tussen opeenvolgende input-acties in i.p.v. ze
   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
   mag niet schrijven in `/tmp/claude-*` (Permission denied) — schrijf
   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
 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.
-⚠️ **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
 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
   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.**
 Bevestigd 2026-09-13: een custom action zonder
 `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
     gebruiker de plaats 1x zelf uit de (al correct beperkte) dropdown
     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
   2026-09-13: "geeft te veel load".** Favoriet maken loopt uitsluitend via de
   **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.
+- **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
 
@@ -299,51 +245,11 @@ want `datum` bevat er geen.
 
 ### 🤝 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
 `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
 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
 
@@ -382,36 +275,6 @@ zonder agenda, dan klopt het gewoon.
 **Overslaan:** taak 19 (al gedaan, twee keer onafhankelijk nagemeten). Taak 21
 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
 
 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.
 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)
 
 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
    op. Concreet:
    `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:
    `horecagelegenheid_current_copy/..._widget.dart:1475 — Undefined name
    '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
    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
 
 - **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).**