TASKS.md 341 KB

▶ Nu aan de beurt (stand 2026-09-13)

🔧 Verbindingsmelding in de header — Eigenaar: Bob — bezig (2026-09-13)

Bob's keuze 2026-09-13: niet elke pagina aanpassen, maar één melding in de header, gevoed door een App State-vlag die de app zelf zet. Claude heeft de custom code geschreven en live getest op emulator-5556 (eigen profile-build, banner tijdelijk in home_widget.dart geprikt); Bob voert de builder-stappen zelf uit.

📄 De code staat in de repo: snippets/check_verbinding.dart.txt en snippets/verbindings_banner.dart.txt (buiten lib/, dus een export raakt ze niet aan). Zie snippets/LEESMIJ.md.

⚠️ Waarom dit een Custom WIDGET is en geen Text met een Visibility-conditie. De widgets in dit project lezen App State via de singleton FFAppState(), niet via context.watch. main.dart zet er wel een ChangeNotifierProvider omheen, maar niemand abonneert zich. Een Visibility-conditie op een App State- bool zou dus pas opnieuw evalueren als er toevallig iets ánders een rebuild veroorzaakt — de balk verschijnt dan niet of veel te laat. Het widget luistert zelf via AnimatedBuilder(animation: FFAppState()). Om dezelfde reden zet de action de vlag met FFAppState().update(...) en niet met een kale toewijzing: alleen update() roept notifyListeners() aan (de setters in app_state.dart doen dat niet).

Plak steeds alleen wat ónder de // DO NOT REMOVE-regel staat; de importkop genereert FlutterFlow zelf.

  • Custom Action checkVerbinding — één HEAD-request op de site-root. Gemeten: HTTP 200, 0 bytes, 57 ms (tegenover 34 KB / 1,3 s voor een gewone data-call), dus verwaarloosbaar. Een 401/404 telt als bereikbaar; alleen 5xx, timeouts en netwerkfouten zetten de vlag. Return Type Boolean, geen argumenten.
  • Custom Widget VerbindingsBanner — parameters width (double, optioneel), height (double, optioneel), tekst (String, optioneel); alle drie leeg laten volstaat. Toont niets zolang alles goed gaat (SizedBox.shrink, neemt geen ruimte in), throttlet de automatische check op max. 1 per 20 seconden over alle pagina's samen, en herlaadt bij een geslaagde tik de huidige route zodat de data alsnog binnenkomt.

Stappen in de builder:

  1. App Values → App State → + Add App State Variable: naam geenVerbinding, type Boolean, initial false, Persisted uit. Dit moet er zijn vóór stap 2, anders kan de action niet opslaan.
  2. Custom Code → +Action → naam checkVerbinding → body plakken → Return Type Boolean → Ctrl+S (zonder die expliciete save haalt het de export niet, ook al staat er "Synced").
  3. Custom Code → +Widget → naam VerbindingsBanner → body plakken → de drie parameters aanmaken → Ctrl+S.
  4. HeaderButtonsComponent openen. De banner moet onder de bestaande Row met de knoppen, niet ernaast (anders verschuift de hamburger). Zit alles nu in één Row: die eerst Wrap Widget → Column, dan het widget als tweede kind erin slepen (Build-tab → Elements → zoeken op VerbindingsBanner).
  5. Vier pagina's hebben géén HeaderButtonsComponent: mijnProfiel, wachtwoordVergeten, stadsactiviteitAanmaken, uitgaansevenementAanmaken. Op de twee aanmaakpagina's is de banner wel zinvol (een formulier verzenden zonder netwerk) — daar het widget los bovenaan de Column zetten.
  6. Testen: adb -s <device> shell svc wifi disable + svc data disable, app herstarten, daarna weer enable.

Live getest (2026-09-13, telefoon-emulator), alle vier de paden:

  1. online — geen balk, layout exact als voorheen;
  2. offline — rode balk (primary, #9A141D) met wolkje en de tekst "Geen verbinding — tik om het opnieuw te proberen";
  3. netwerk terug + tik — balk verdwijnt en Home is volledig hersteld: slider met kaarten, tabs gevuld, zonder herstart;
  4. geen nieuwe fouten — de enige NoSuchMethodError in de log stamt van 19:18:57 (de offline start), de tik was om 19:19; de route-herlaad verliep schoon, geen go_router-fouten. Screenshots van alle drie de toestanden staan in de chat.

⚠️ Punt 3 was de reden voor een extra ronde. In de eerste versie verdween de balk wél maar bleef het scherm leeg — "tik om opnieuw te proberen" beloofde dus meer dan het deed. Daarom herlaadt het widget nu bij een geslaagde tik de huidige route (context.pushReplacement op het pad uit GoRouterState). Alleen bij een handmatige tik, niet bij de automatische check: dat zou een herlaadlus geven omdat de pagina het widget opnieuw opbouwt.

⚠️ Wat de banner NIET oplost: het grijze blok op Home blijft staan, want dat blok ís de gecrashte widget (bevinding C hieronder). De banner maakt het alleen verklaarbaar in plaats van kapot. De vier crashplekken blijven dus een aparte taak.

🐞 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.

Oorspronkelijke bevinding (2026-09-13, vóór de fix)

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.

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 impact, alleen niet inzetten zonder dat eerst te repareren.

🥇 ALLEEN JIJ KUNT DIT — Drupal/views, Claude komt er niet bij

Doorlopende chatnummering van 2026-09-13. Stand: 25 afgevoerd, 34/35/36/37 afgerond (zie hierboven), 20 grotendeels rond.

Taak 38 · de bezorg-display komt niet door op productie. De view-export die Bob plakte bevat services_2 ("thuisbezorgt", filter field_hor_bez_ophaalbezorg_tid = 36176, horcat er correct af), maar het endpoint antwoordt onveranderd Display services_2 on view flutterflowmobiel_establishments could not be found. Alleen services_1 bestaat daar — services_2 t/m _5, services_8, thuisbezorgt, page_1 en default allemaal getest. Diagnose in deze volgorde:

  1. drush @prod php-eval "\$v=views_get_view('flutterflowmobiel_establishments'); print implode(', ', array_keys(\$v->display)) . ' | type=' . \$v->type;" — kent Drupal de display, en is type Normal/Database of Default?
  2. Staat hij er niet in: is de view na de import ook echt opgeslagen? Views UI toont een geïmporteerde view compleet zonder dat hij bewaard is.
  3. Staat er type=Default, dan wint een code-/feature-versie van de database — dan moet de wijziging via die feature, niet via de UI.
  4. Staat hij er wél in maar blijft de JSON falen: drush @prod cc all. De view-definitie zit in de ctools-cache, niet in de page cache, dus een _cb=-parameter in de URL helpt daar niet tegen.

Zodra hij draait: Claude meet hoeveel bezorgers er per plaats zijn en legt de ontwerpkeuze voor (losse lijstpagina vs. 7e tab). Op devbob gaf horcat=17967 op deze display 0 resultaten, wat suggereert dat bezorgers nauwelijks in de zes tabcategorieën zitten — losse pagina ligt dus voor de hand. De pagina heeft nog géén display_id-parameter; die moet er eerst op.

Taak 29 · HTML-entiteiten op de evenement-detailpagina. Bevestigd op nid 214466 (13 sep): flutterflow_events geeft Onno Innemee &amp; Ytwer Bosma, flutterflowmobiel1 op dezelfde node Onno Innemee & Ytwer Bosma. Eén database, twee views — dus viewconfig, geen data. Titels handmatig editen is zinloos: 837 stuks, en na de volgende import weer terug. Body hoeft niet mee: die gaat via EvenementV2HTMLComponent door een echte HTML-renderer en wordt vanzelf gedecodeerd; er is geen dubbele escaping (0 gevallen &amp;amp;). Alleen de platte-tekstvelden, gemeten over 9625 events: title 837 (&amp; 695, &#039; 179, &quot; 54), adres 89, categorie 22, organisator 1. Eerste verdachte: _custom_clean_html in custom.module — kijk of daar op viewnaam gefilterd wordt; staat flutterflowmobiel1 er wel in en flutterflow_events niet, dan is dat de fix en is het één regel. Anders beide views exporteren (drush @prod php-eval "print views_get_view('X')->export();") en aan Claude geven om te diffen.

Taak 31 · dubbele rijen in flutterflow_events → Distinct. nid 214439 komt 14x terug, 214441 3x. Alle 14 rijen zijn veld voor veld identiek — dus de vermenigvuldiging komt van een sort/filter op een multi-value veld dat niet in de output zit, vrijwel zeker field_date (die markt vindt 14x plaats). Fix: Advanced → Query settings → Distinct. Werkt dat niet: het datumfilter → Multiple field settings → "Display all values in the same row". Verificatie: ...flutterflow_events.json?display_id=services_1&nid=214439 moet 1 rij geven. Geen haast — de app vraagt deze view alleen per nid op en bouwt er nooit een lijst uit.

Taak 32 · de 5 komende events die in geen enkele tab komen. Deel A: "Tweedehands markt" (nid 214439) raakt geen enkele display — hang 'm aan services_5 (Cultuur & Info, de breedste met 11 categorieën). Filter criteria → categoriefilter → override, niet "All displays". Deel B: vier events zonder categorie (214455, 214456, 214474, 214476). Dit groeit mee met elke import. Structureel: default categorie bij import. In Views: OR-filtergroep met "Is empty (NULL)" op het categorieveld.

Taak 33 · exposed datumfilter (Vandaag / Dit weekend / Deze week), P2-1. Volledig uitgeschreven verderop onder "Voor Bob — drie Drupal-taken".

Taak 34 · twee wezen, gemarkeerd voor de schoonmaak (2026-09-13). flutterflowmobiel1 services_2 heet nu "ongebruikt" (machine name ongewijzigd, geeft nog 25 items — bewust bewaard). De view flutterfavorietenagenda is hernoemd; de API-call heet in de export KANWEGFavorietenAgendaCall. Er staat ook nog een KANWEGFavorietenAgendaTESTKANWEGCall, al op de P2-7-lijst.

✅ Contentstand productie — hermeten 2026-09-13 17:49

09-11 09-12 09-13
unieke events 1010 2007 9625
toekomstige events 4 61 38
laatste datum in voorraad 25 sep

⚠️ De voorraad verviervoudigde, maar de horizon is 12 dagen en er staat nog steeds niets ná september. De import voegt vrijwel uitsluitend verleden toe. Zodra dat verklaard is (publiceert de bron kort vooruit, of haalt de import maar een venster op?) is dat het laatste grote contentgat vóór livegang.

Van de 38 komende events komen er 5 in geen enkele tab (zie taak 32); dat waren er 7 op 09-12, de twee andere zijn simpelweg verstreken. En 21% van de hele voorraad (2102 van 9625) heeft ergens een HTML-entiteit (taak 29).

Methode om dit te herhalen: pagineer flutterflow_events volledig (display_id=services_1&page=N&limit=100), pagineer de zeven flutterflowmobiel1-displays, en leg de nids naast elkaar. Classificeer op datum + tijd, niet op datum alleen — en leid het jaar af uit de weekdag, want datum bevat er geen.

🤝 CLAUDE KAN DIT OVERNEMEN — zeg het en het gebeurt

P2-15 · de live indiening — 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-22 · de Issues-teller leegmaken — twee Property Override-fouten op stadsactiviteitAanmaken. Claude heeft hier 2026-09-13 een ronde op gedaan en is er niet uitgekomen; terug naar jou. Beide zichtbare bindingen zijn aantoonbaar gezond (zie P2-22 verderop voor wat er nu is uitgesloten), dus wat 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).

❓ Open vraag van Claude

Hoeveel favorieten heb je als bobcity aangemaakt, en van welk type (evenement of zaak)? Er komt er één door op favorieten_agenda.json. Is dat er één van de vijf, dan is het filter mogelijk te streng; waren de andere verleden/zaken 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 events op dat moment). Methode: alle zeven displays volledig gepagineerd en de nids vergeleken met de complete voorraad uit flutterflow_events.

aantal
komende events (start ≥ nu − 2 uur) 58
zichtbaar in minstens één tab 51
in geen enkele tab 7 (12%)

De zeven, om na te lopen:

13 sep 14:00  nid 214441  Zeddam        Ferme Jongus – Nederpop op volle kracht!
13 sep 15:30  nid 214463  Den Haag      Dr Ewa Woydyłło (Unique Performance)
15 sep 20:30  nid 214456  Rotterdam     She Her Her Hers
15 sep 21:00  nid 214455  Rotterdam     Renny Conti
19 sep 09:00  nid 214439  Alblasserdam  Brocante Markt Klein Frankrijk
20 sep 13:30  nid 214474  Almere-Stad   Het Danspaleis
23 sep 20:30  nid 214476  Amersfoort    Feest

Zes daarvan hebben helemaal geen categorie; de zevende (214439) heeft Tweedehands markt, en dat is de enige categorie in de hele voorraad die geen enkele display raakt.

Categorie → tab, zoals het nu feitelijk werkt (gemeten, niet uit de viewconfig afgelezen):

categorie komt in
Voorstelling (41x) Uitgaan, Cultuur & Info, Films
Theater (35x) Uitgaan, Cultuur & Info, Films, Jeugd
Muziek (28x) Uitgaan, Cultuur & Info
Cabaret (21x), Toneel (9x), Theatercollege (3x), Circus (3x), Stand-up comedy (3x) alleen Cultuur & Info
Kindvriendelijk (16x), Jeugd (12x), Muziektheater (4x) Cultuur & Info, Jeugd
Dansvoorstelling (4x), Film (4x) Cultuur & Info, Films
Live/Concert (8x), Pop (2x), Rock/Punk (2x), Dance/House (2x) alleen Uitgaan
Tweedehands markt (1x) nergens

Goed nieuws: er is dus maar één categorie zonder tab. Het echte lek zijn de events zónder categorie.

Twee dingen te beslissen:

  1. Events zonder categorie — geef ze bij import een default, of maak één display die alles toont wat nergens in past. Zonder dat groeit dit gewoon mee met elke import.
  2. "Tweedehands markt" — hang 'm aan een bestaande tab (Cultuur & Info ligt voor de hand) of accepteer dat die ene node onzichtbaar blijft.

⚠️ Let op bij het narekenen: classificeer op datum + tijd, niet op datum alleen. Ik telde eerst 10 onzichtbare events; drie daarvan waren van vandaag en al begonnen (10:00 en 14:00, gemeten om 20:50) en worden dus terecht door het >= -2 hours-filter weggelaten. Op datum alleen lijken die ten onrechte "toekomstig".

📌 services_4 (Activiteiten) staat op 0 en dekt geen enkele categorie — dat zijn stadsactiviteiten, die worden handmatig aangemaakt en niet geïmporteerd. Geen bug.

Taak 29 · HTML-entiteiten — nu precies af te bakenen (2026-09-12)

Het raakt alléén de evenement-detailpagina. Gisteren kon ik dat niet scheiden omdat de lijstviews maar 4 items hadden; met de nieuwe vulling is het hard te maken op dezelfde node (nid 214466):

view wat de app ermee doet uitkomst
flutterflowmobiel1 Home-tabs, sliders, P-pagina's Onno Innemee & Ytwer Bosma
flutterflow_events evenement-detailpagina (EvenementCall) Onno Innemee &amp; Ytwer Bosma

Voor de gebruiker: hij ziet de titel netjes in de lijst, tikt erop, en op de detailpagina staat &amp;. Geraakt zijn title (124 events), body (234), adres (23) en categorie (8) — 321 van de 2007 events.

Fix: flutterflow_events doet iets anders met zijn tekstvelden dan flutterflowmobiel1. Beide views draaien op dezelfde nodes, dus vergelijk de veldinstellingen van title/body/adres tussen die twee en neem over wat flutterflowmobiel1 doet. Geen app-fix proberen: dan moet je op elke plek apart decoderen. (Zelfde mechanisme als bij je devbob-bezorgdisplay — daar was het ook een veldinstelling per display, niet iets in de data.)

Taak 30 · Node vermenigvuldigt zich — blijkt niet zichtbaar voor de gebruiker

Nid 214439 ("Brocante Markt Klein Frankrijk") komt 14 keer identiek terug in flutterflow_events; 214441 en 214420 elk 3x, 214367 2x. Op 2026-09-12 onveranderd (18 overtollige rijen op 2025).

Maar het bereikt de app niet. Nagemeten 2026-09-12:

  • flutterflowmobiel1 heeft géén duplicaten: services_1/2/3/5/7 geven allemaal evenveel rijen als unieke nids. Node 214439 komt er zelfs helemaal niet in voor (zijn categorie "Tweedehands markt" valt onder geen enkele display — dat is taak 28, niet deze).
  • flutterflow_events wordt door de app alléén per nid bevraagd (EvenementCall.call(nid: ...), vanuit evenement_component en event_current). Er wordt nergens een lijst uit opgebouwd: de twee iteraties in dat component lopen over fotoos en categories binnen één event, niet over de rijen.

Gevolg: geen zichtbare schade, geen haast. Het blijft wel iets om te weten: bouwt er ooit iemand een lijst op deze view, dan komt zo'n node 14x in beeld. De oorzaak is het klassieke Views-patroon "een veld met meerdere waarden krijgt een eigen rij" — kijk op node 214439 welk veld 14 waarden heeft en zet Multiple field settings → Display all values in the same row aan.

Voor Bob — drie Drupal-taken, uitgeschreven

Taak 19 (P1-42b) · view flutterflowmobiel_establishment_events — de agenda op een horecapagina. 🔎 Doe deze taak NIET voordat je dit gelezen hebt — de meting van 2026-09-11 zegt dat hij al gedaan is.

Wat ik gemeten heb (browserloos, alles met curl):

  • De enige twee zaken in het hele land met een agenda zijn Openluchttheater Valkenburg (70144) en Azijn, Roermond (75500). Die geven respectievelijk 3 en 1 events terug.
  • Die agenda's staan chronologisch oplopend (18 → 20 → 25 september) en bevatten uitsluitend toekomstige data. Dat is precies wat deze taak wilde bereiken.
  • 134 andere zaken hébben events — tot 29 stuks per zaak — maar de agenda-view geeft er 0 van terug. Hun events liggen allemaal in het verleden (nid 57738 bijvoorbeeld: 29 events, nieuwste 6 september).
  • De correlatie is over alle 136 zaken zonder uitzondering: alleen toekomstige events komen door.

Conclusie: er zit al een datumfilter op, en de sortering klopt al. Dit spreekt de audit van 2026-09-08 tegen (die vond 232 events over 7 zaken, grotendeels verleden) — er is dus tussen 09-08 en 09-11 iets gewijzigd, vermoedelijk door jou tijdens de P1-42-ronde.

Wat ik je zou vragen: kijk in de viewconfig of het datumfilter + ASC-sortering er inderdaad al staan, in plaats van ze opnieuw toe te voegen. Eén slag om de arm: ik heb geen zaak kunnen vinden met zowel verleden als toekomstige events, dus het sluitende bewijs op één enkele zaak ontbreekt — het bewijs is statistisch (136 zaken, geen enkele uitzondering).

Eén punt uit de oude taak blijft mogelijk staan: de pager van 10. Die komt niet uit de view maar uit de Services-resource; &limit=25 geeft gewoon 25 terug (zie CLAUDE.md). De app stuurt momenteel geen limit mee, dus een zaak met meer dan 10 toekomstige events toont er 10. Nu niet merkbaar (max 3), maar wel iets voor de livegang.

📌 Parameternaam: horecanid, niet horecaid — met de verkeerde naam krijg je een HTML-foutpagina in plaats van JSON, wat makkelijk voor "leeg" wordt aangezien.

Taak 20 (P1-5) · "Thuis bezorgen" — nieuwe DISPLAY op flutterflowmobiel_establishments, geen losse view.

Bob's vraag 2026-09-11: losse view maken, of de bestaande kopiëren? Antwoord: geen van beide — maak een nieuwe display op de bestaande view.

Waarom dat beslissend is: de app-kant is display-gebaseerd, niet view-gebaseerd. HorecagelegenheidoverzichtCall heeft de viewnaam hardcoded in de URL (.../views/flutterflowmobiel_establishments.json) en alleen display_id is variabel. Een losse view (of een kopie, want die krijgt een eigen machine name) betekent dus een nieuwe URL → nieuwe API-call in FlutterFlow → aanpassing van de custom action fetchAlleHorecagelegenheden. Een extra display kost aan de app-kant niets meer dan een andere parameterwaarde. Bijkomend voordeel: één view betekent één set velden, dus geen risico dat de twee uit elkaar gaan lopen (precies wat bij P1-45 met categorie gebeurde).

De display instellen:

  1. Nieuwe Services-display (wordt services_8 of hoger).
  2. Filter criteria → override (niet "All displays", anders krijgen de zeven bestaande displays óók alleen nog bezorgers).
  3. Filter toevoegen op het specifieke veld: field_hor_bez_ophaalbezorg ("Ophaal/Bezorgen", term reference naar thuisbezorgen_afhaalbezorgen) → waarde "Bezorgen". Gebruik dit veldfilter en niet het generieke Has taxonomy terms — dan kun je niet per ongeluk op dezelfde term in een ánder veld matchen.
  4. Haal het horcat-filter weg op deze display — zie de valkuil hieronder.
  5. Laat published, contenttype en het exposed townid staan zoals in services_1.
  6. Velden hoef je niet aan te raken: nid, titel, adres, plaats, logo, categorie is genoeg (nagemeten 2026-09-10).

⚠️ Valkuil met horcat, gemeten 2026-09-11 — dit is waarom stap 4 nodig is. De app stuurt horcat altijd mee, en een lege waarde betekent hier niet "geen filter":

horcat=17967        -> 75 items
horcat=   (leeg)    ->  0 items   ← niet 'alles', maar niets
horcat weggelaten   -> 100 items

De custom action kan de parameter niet weglaten, dus als het horcat-filter op de nieuwe display blijft staan krijg je gegarandeerd een lege lijst. Haal je 'm weg, dan wordt de meegestuurde waarde simpelweg genegeerd en hoeft er aan de app-kant niets te veranderen.

  1. Geef Claude daarna het display_id door (= taak 26).

Open ontwerpvraag voor Bob: "Thuis bezorgen" is één lijst, terwijl horecagelegenhedenOverzichtCurrent uit zes categorietabs bestaat. Wil je die tabs daar ook, of liever een aparte, simpele lijstpagina? Dat bepaalt of Claude een display_id-page-parameter op de bestaande pagina zet (zes bindingen) of een nieuwe pagina bouwt.

⚠️ Correctie 2026-09-11 op een eerdere aanname van Claude. Ik schreef eerder dat "het horeca-overzicht al een display_id-parameter accepteert". Dat klopt niet — ik haalde het door elkaar met HomeUitgaantabelKaartComponent, waar P1-44 die parameter wél kreeg. De pagina heeft alleen plaats; de zes tabs hardcoden elk een categorie-id (17969, 17963, 34, 17965, 17967, 17968) plus 'services_1'. De custom action héést displayId wel al als derde argument, dus de leiding ligt er — alleen vult de pagina 'm niet.

🔬 Testuitslag devbob-display services_2 (2026-09-11) — werkt, maar nog niet deployen

Bob bouwde de bezorg-display op devbob en draaide de curl-reeks. Het filter werkt: townid doet het (Amsterdam 19, Arnhem 2, Valkenburg 0), de controle met een verzonnen parameter geeft netjes hetzelfde als ongefilterd (100), en services_1 is niet geraakt door de override. De horcat-valkuil is ook op devbob bevestigd: lege horcat geeft 0, niet "alles".

Maar er zijn drie verschillen met services_1 die eerst weg moeten. Alle drie gemeten op dezelfde view, en bij 1 en 2 zelfs op dezelfde node (54090, Brownies&downieS in Delft):

  1. categorie komt als komma-string i.p.v. als array. services_1 geeft ["Chinees restaurant","Thais restaurant"], services_2 geeft "Chinees restaurant, Thais restaurant". Geen datakwestie: services_1 levert een enkele categorie gewoon als array van 1 (26 van de 100). Dit breekt de app: de kaartjes bouwen hun groene labels met getJsonField(item, r'$.categorie').toList(), en dat gooit op een String NoSuchMethodErrorstil, in een profile-build zie je alleen een leeg of grijs vlak. Exact hetzelfde patroon als P1-45.
  2. HTML-entiteiten. Via services_2 komt "Brownies&amp;downieS" terug, via services_1 "Brownies&downieS" — zelfde node. Ook plaats is geraakt (&#039;s-Molenaarsbuurt). In de app zie je die tekens letterlijk.
  3. Het horcat-filter staat er nog op. De app stuurt horcat altijd mee en kan 'm niet weglaten; een lege waarde geeft 0 resultaten. Moet weg van deze display.

Oorzaak en goedkoopste fix: 1 en 2 zijn allebei veldinstellingen op displayniveau, niet iets in de data. Bouw de display daarom niet opnieuw op, maar dupliceer services_1 (in het displaymenu: Duplicate services_1) en override daarna alléén de Filter criteria. Dan komen de veldhandlers mee en zijn 1 en 2 in één klap weg; daarna hoef je alleen nog horcat te verwijderen en het bezorgfilter toe te voegen.

Bijvangst voor de ontwerpvraag: horcat=17967 geeft op services_2 0 resultaten. Bezorgers zitten dus nauwelijks in de zes tabcategorieën — een losse lijstpagina is voor "Thuis bezorgen" waarschijnlijk logischer dan de zes tabs.

Correctie op taak 29 hieronder: ik schreef dat de escaping "specifiek is voor flutterflow_events" en dat de establishments-view het goed doet. Dat klopt voor productie, maar het is geen eigenschap van de view — het is een veldinstelling per display, zoals dit devbob-geval laat zien. Zoek de fix voor taak 29 dus in dezelfde hoek: vergelijk de veldinstellingen van flutterflow_events met die van flutterflowmobiel_establishments op productie.

Taak 21 (P2-1) · view flutterflowmobiel1 — datumfilter Vandaag / Dit weekend / Deze week. Er staat nu één datumfilter (>= -2 hours) dat niet exposed is. Voor een keuzefilter in de app is een tweede, wél exposed filter nodig:

  1. Filter criteria+ AddContent: Datum - start date (field_date) → operator "Is between" → vink "Expose this filter to visitors" aan.
  2. Zet de identifier op iets voorspelbaars, bv. datum_van en datum_tot (een between-filter geeft twee invoervelden en dus twee identifiers).
  3. Laat het bestaande >= -2 hours-filter gewoon staan — dat blijft de ondergrens zodat verleden events nooit terugkomen.
  4. Doe dit op alle zeven de services-displays, of op de Master als de displays hun filters niet overriden. Let op: services_1, _3, _4, _5, _6 en _7 hebben defaults['filters'] = FALSE, dus die overriden wél — je moet ze dan stuk voor stuk langs.
  5. Controleer met een verzonnen parameter dat het filter echt aankomt (staande regel uit CLAUDE.md): Views negeert onbekende query-parameters stil, dus ?onzin_param=123 moet hetzelfde resultaat geven en ?datum_van=… een ánder. Zonder die controle bewijst een 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 (was ...Copy3); de oude pagina heet kanwegHorecagelegenhedenOverzicht en staat in de map kanweg. Drie dingen die daaruit volgen:

  1. Verouderde exportmappen in de working tree. Na het hernoemen laat 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.
  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 dat FlutterFlow niet meer uitlevert; zodra die map weg is, is de fout weg. Er is geen levende code die er nog aan hangt.
  3. Twee dode kopieën verwijzen nog naar de kanweg-paginadrawer_component_copy en kanweghorecagelegenheid_current_copy. Beide 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 2026-09-10: niet nu, want er komen onderweg nog teksten bij). Nu open: 46 velden in stadsactiviteitAanmaken (27) en uitgaansevenementAanmaken (19), plus de twee nieuwe teksten uit taak 22 op SelectStateDropDownComponent: "Gemeente volgen" (nosgkxy0) en de uitlegzin "Bij favoriete gemeenten krijg je een agenda per mail met daarin alle evenementen uit die favoriete gemeenten." (24bdnkhi).
  • Titels met spaties vooraan/achteraan opschonen (was P1-47, verbreed 2026-09-11). Bob mat op devbob 100+ horecagelegenheid-nodes met een gewone ASCII-spatie vóór de titel (LIMIT 100 liep vol, dus het werkelijke aantal is hoger); ook tabs (nid 49539) en spaties áchteraan (49512) komen voor. MySQL sorteert op de rauwe node.title, dus elke zo'n node valt buiten de alfabetische volgorde — terwijl de JSON schoon oogt, want _custom_clean_html poetst ná de query. Bob's besluit 2026-09-11: de impact is nu te groot, we lopen de namen door bij de livegang. Te verifiëren op productie met de telquery hieronder; de drie losse titels uit taak 16 zijn daar al gefixt, de rest vermoedelijk niet.

    drush @prod sqlq "SELECT type, COUNT(*) FROM node WHERE title <> TRIM(BOTH ' ' FROM TRIM(BOTH CHAR(9) FROM TRIM(BOTH CHAR(10) FROM TRIM(BOTH CHAR(13) FROM TRIM(BOTH ' ' FROM title))))) GROUP BY type;"
    

    De fix is een UPDATE met diezelfde genestelde TRIM op node én node_revision, idempotent, met vooraf een sql-dump van die twee tabellen. URL-aliassen blijven ongemoeid.

  • P1-30 · Cloudflare rate limiting aanzetten, en dan óók de twee rate-limiting-vinkjes weghalen uit de custom rule flutterflow.

  • Max items + tab 6 (P2-24 A en C) — geparkeerd, zie daar. Melden bij FlutterFlow-support is de enige overgebleven route.

Afgerond 2026-09-11: taak 18 (route van drawer-item Horeca en van de knop Alle horecagelegenheden omgezet naar horecagelegenhedenOverzichtCurrent, beide met de plaats-binding op gemeenteSelectId opnieuw gezet — die wist FlutterFlow bij een paginawissel; oude pagina hernoemd en naar kanweg verplaatst) en taak 22 (label + uitlegzin bij het gemeentehartje, allebei achter de login-guard).

Afgerond 2026-09-10: taak 16 (drie titels met onzichtbaar teken — geverifieerd: "Café Arnhem" staat nu alfabetisch juist), taak 17 (contentfoutjes), P0-13 (login-guards op de hartjes), P1-48 (lege-staat op de Home-tabs), en de horeca-sortering (alfabetisch, live).


Drupal views/API — lopend overzicht van wat er nog aan moet

Levende lijst, bijgewerkt 2026-09-08. Elke view/display die de app gebruikt, met wat eraan mankeert. Werk hem bij zodra er iets verandert; dit is de plek om te kijken vóór je een Drupal-sessie begint.

view / display waar in de app staat wat er nog moet
flutterflowmobiel1 services_1 Home-slider ✅ 2026-09-09 sortering + datumfilter live op productie. ⚠️ mist granularity = hour die de andere displays wél hebben; staat dus op de default day
flutterflowmobiel1 services_2 niets meer ⚠️ wees sinds P1-44 leest geen enkele app-plek deze display nog. Niet sorteren; besluiten of hij weg kan
flutterflowmobiel1 services_3 Uitgaan-tab + P-pagina's (met townid) ✅ 2026-09-09 oplopend op field_date_value + >= -2 hours, granularity hour. Live. Levert nu 1 item op
flutterflowmobiel1 services_4 Activiteiten-tab ✅ 2026-09-09 idem. Levert nu 0 items op
flutterflowmobiel1 services_5 Cultuur & Info-tab ✅ 2026-09-09 idem. Levert nu 6 items op
flutterflowmobiel1 services_6 Films-tab ✅ 2026-09-09 idem. Levert nu 0 items op
flutterflowmobiel1 services_7 Jeugd-tab ✅ 2026-09-09 idem. Levert nu 0 items op
flutterflowmobiel_establishment_events agenda op een horecapagina ✅ 2026-09-11 Hermeten 2026-09-11: filtert al op toekomst én sorteert al oplopend — van 136 zaken mét events geven alleen de 2 met toekomstige agenda iets terug (70144 3x, 75500 1x). Zie taak 19. Restpunt: de 10-limiet van de Services-resource; de app stuurt geen limit mee. Parameter heet horecanid
flutterflowmobiel_establishments horeca-overzicht ✅ 2026-09-11 sorteert alfabetisch op titel. De drie losse titels uit P1-47 zijn op productie gefixt (taak 16, 2026-09-10) en geverifieerd: 51094 Café Arnhem staat nu tussen Brownies&downieS en DE STEENEN TAFEL. Het bredere probleem staat nog open, zie de livegang-regel over de titelspaties
flutterfavorietenagenda services_1 niets — wees ⚠️ wees, vastgesteld 2026-09-11 De app roept deze view nergens aan. FavorietenAgendaCall staat wel in api_calls.dart maar heeft 0 aanroepers; Favorieten tab 1 haalt zijn data via drupalRequest uit de custom resource favorieten_agenda.json. Niet sorteren/filteren; besluiten of view + API Call weg kunnen
favorieten_agenda.json (custom resource) Favorieten, tab 1 ✅ gemeten 2026-09-11 Werkt. Met een sessiecookie van bobcity: 1 item, datum_raw in de toekomst, velden compleet (node_type, nid, titel, datum, datum_raw, plaats, categorie, logo, adres, postcode, woonplaats, horecagelegenheid, horecagelegenheidNid, matched_via). Filtert op toekomst en levert datum_raw mee, dus sorteren is app-zijdig mogelijk. Zonder cookie: ["Toegang geweigerd voor gebruiker anonymous"]
flutterflowmobiel_establishment_info services_1 horeca-detailpagina ok geeft precies 1 item terug (de zaak zelf); sortering niet van toepassing. Gemeten 2026-09-08
flutterflow_events services_1 evenement-detail (op nid) ok Hermeten 2026-09-11: 1025 events over 136 zaken, waarvan er maar 4 in de toekomst liggen — de rest is verlopen. Niet 150 zoals eerder genoteerd. 20 items hebben categorie: null; de app vangt dat af met ?.toList() ?? []
plaatsen, gemeenten, provincies, categorieen, entreeopties, mijn_* referentielijsten ok geen datum/sortering

⚠️ Wat P1-42 blootlegde (2026-09-09, live op productie): er staat vrijwel geen toekomstige content in het systeem. De gefilterde views gaan over de hele database, dus dit zijn complete tellingen, geen steekproeven: slider 7 items, Uitgaan 1, Activiteiten 0, Cultuur & Info 6, Films 0, Jeugd 0. Drie van de vijf Home-tabs zijn leeg. Controle op flutterflow_events (die géén datumfilter heeft): 25 toekomstige events op de 500 meest recent aangemaakte, waarvan 14 in "Tweedehands markt". Dit is geen fout in de view — het filter doet precies wat het moet; de oude created DESC-sortering verstopte het. Zie P1-46 hieronder.

Meetvalkuil die me 2x een verkeerde conclusie opleverde: het datum-veld bevat in flutterflowmobiel1 géén jaartal ("zaterdag 11 okt") maar in flutterflowmobiel_establishment_events wél ("Friday, 31 October 2025 - 19:30"). Neem nooit "jaar = nu" aan als het ontbreekt: gebruik het /20xx/-segment uit url waar dat bestaat, en anders de weekdagnaam om het jaar te bepalen (11 okt is alleen in 2025 een zaterdag). Met "jaar = nu" telde ik 2025-events als toekomstig.

Afgerond: de categorie-vorm (P1-45) — alle zeven displays van flutterflowmobiel1 geven sinds 2026-09-08 een array, geverifieerd op productie ná cache-flush. De overige views deden dat al.

Bij het testen: zet een cache-buster achter de URL (&_cb=$RANDOM$RANDOM), anders krijg je Drupal's page cache te zien in plaats van je eigen wijziging. Zie de notitie hierover in CLAUDE.md.


Wezenlijst — wat nergens meer gebruikt wordt

Lopende lijst over alle lagen heen, bijgewerkt 2026-09-08. Bedoeld om te voorkomen dat er werk gestoken wordt in iets dat niemand meer aanroept. Claude gooit hier niets weg (staande regel); dit is puur de inventarisatie.

Drupal — views/displays

wat waarom wees sinds
flutterflowmobiel1 display services_2 ("home") Geen enkele app-plek roept 'm nog aan. Vóór P1-44 was hij de hardcoded default van homeTabel; nu geeft elke Home-tab zijn eigen display mee (services_3 t/m 7) en de slider services_1. Bijkomstigheid: services_1 en services_2 hebben allebei path = homes548446 — een dubbele service-pad, wat bevestigt dat services_2 een overgebleven kloon is 2026-09-08 (P1-44)
flutterflowmobiel1 display page ("home", pad flutterflow-home) Een Drupal-pagina-display, geen service. Onduidelijk of er nog iets naar linkt — nog te controleren door Bob onbekend

FlutterFlow — componenten en pagina's

Staat volledig uitgewerkt bij P2-7: 16 dode eenheden (7 componenten zonder importeur, 9 pagina's met een route in nav.dart waar nooit heen genavigeerd wordt). Niet hier dupliceren — P2-7 is leidend. Let op: die moeten in de builder weg, niet met git rm, anders staan ze na de eerstvolgende export gewoon terug.

FlutterFlow — API calls

wat waarom wees
FavorietenAgendaCall Declared API Call naar de view flutterfavorietenagenda, 0 aanroepers (gecheckt 2026-09-11). Favorieten tab 1 gebruikt drupalRequest op favorieten_agenda.json. Zowel deze call als de view zelf zijn kandidaat om weg te gaan
FavorietenAgendaTESTKANWEGCall Testrestant. Verwijderen via het API Calls-paneel lukte niet (kwam na een reload terug, 2026-08-14) — zie P2-7

Twijfelgevallen, niet weggooien zonder besluit

  • lib/shared/geen_evenementen_component/ — 0 importeurs, maar het is precies de lege-staat-component die P1-1 nodig had. Gebouwd en nooit geplaatst.
  • HorecagelegenhedenOverzichtCopy2 — de intacte momentopname waaruit het herstel van 2026-09-01 is afgeleid. Mag weg zodra P2-6 helemaal klaar is.

Deze sessie (2026-08-30, zelfstandig, code-only — géén browser- tabgroep aanwezig bij sessiestart, dus aangenomen dat Bob niet actief achter zijn scherm zat en geen builder-UI geprobeerd): P2-6's onderzoeksstap 1 uitgevoerd op een verse flutterflow export-code. Belangrijkste uitkomst: een regressie op een live pagina gevondenHorecagelegenhedenOverzicht's tab "Eetgelegenheden" is zijn lijst-generatie kwijt (1 leeg kaartje i.p.v. de lijst), de schade van Bob's TextField-experiment van 2026-08-25 staat nog steeds in de live builder-staat. Was P1-31; op 2026-09-01 hersteld en afgevoerd — let op: het bleek de tab "Cultuur" te zijn, niet "Eetgelegenheden". Het herstelrecept was afgeleid uit de intacte kopie HorecagelegenhedenOverzichtCopy2. Verder: dart analyze op de verse export geeft 0 errors (dus geen dieperliggende code-inconsistentie op die pagina — de builder-bug is puur UI-side), P2-6's laatste stap-2-restpunt (Activiteiten/tab 0 via On Page Load) blijkt intussen al gebouwd, en de complete Page-State-laag (6x alleXxx/zoektermXxx/categorieFilterXxx) staat klaar. Opgelost (2026-08-31): de grote ongecommitte diff die hier stond (Firebase/mijn_profiel/stadsactiviteit_aanmaken, dus P1-16/P2-15-werk, plus P2-6's fetch_alle_horecagelegenheden.dart en de drie HorecagelegenhedenOverzichtCopy*-werkkopieën) is op Bob's expliciete verzoek gecommit als 98d46ad — werkboom is weer schoon, dus een git status-diff is vanaf nu weer een betrouwbaar "hier ligt vers, niet-afgerond werk"-signaal.


Vervolg dezelfde sessie (2026-08-25): na afronding van P1-7 is de volledige lijst herscand op zoek naar nieuw zelfstandig oppakbaar werk — bleek verder vrijwel alles óf al Bob's eigen taak, óf een productbeslissing die eerst bij hem moet liggen. Eén mechanisch item gevonden en met Bob's expliciete akkoord gebouwd: P2-5 (AdMob- banner op HorecagelegenheidCurrent + EventCurrent, zelfde patroon als de al werkende banner op PUitgaanPage) — horeca-overzicht bewust overgeslagen wegens Bob's actieve Sort/Datatype-experiment daar. Ook P2-9 kreeg 2 kant-en-klare, direct bouwbare onboarding-opties uitgewerkt (nog geen keuze gemaakt). Bevestigd via verse export + flutter analyze (0 errors) + live ff-run-fvm.sh-build/launch op emulator-5554 (geen exceptions).


Deze sessie (2026-08-24/25, sessie 46, builder via Bob's gedeelde Chrome na expliciet akkoord in de chat, Bob gelijktijdig zelf actief op P2-7): P1-7 volledig afgerond — Restpunt A (horeca-hartjes op HorecagelegenheidoverzichtKaart + HorecagelegenheidCurrent syncen nu ook naar Drupal via het bestaande favorieten-endpoint, zelfde patroon als het gemeente-hartje) en Restpunt B ("Wachtwoord wijzigen"-link op de Gebruiker-tab). Nieuw herbruikbaar recept ontdekt en vastgelegd in CLAUDE.md: een JSON-body-string met 1 ingesloten variabele bouw je via een kleine Custom Function (favorietenBodyNode), niet via het Custom-Action-argumentenpaneel zelf (geen concatenatie-optie op een kaal String-argument). Bevestigd via verse export (flutter analyze: 0 errors) + een live ff-run-fvm.sh-build/launch op emulator-5554 (geen exceptions). Onderweg 2x geblokkeerd op de bekende geneste-Set-Variable- freeze, beide keren opgelost met een page-reload zonder dataverlies. P1-7 blijft als taak staan (Tab 1's categorie-SQL-patch wacht nog op Bob's bevestiging, zie hieronder) maar heeft geen eigen openstaande Claude-actie meer.


Vorige sessie (2026-08-23, sessie 45, code-only + Bob aanwezig achter zijn scherm, geen builder-automation zelf uitgevoerd — Bob pakt de resterende builder-stappen zelf op in een andere chat): takenlijst doorgelopen op zoek naar zelfstandig oppakbaar werk — bleek vrijwel leeg (bijna alles resterend is Bob-owned of wacht op een korte beslissing van hem). Twee concrete dingen gedaan/afgesproken:

  1. Export-drift gecommit (27d65ec): .gitignore was de .claude/-exclude-regel weer kwijt (bekende valkuil, herstel) + de al openstaande ios/project.pbxproj-drift. Push naar origin faalde met een SSH-timeout (ssh -p 2222 gogs.digitalforce.tv → "Connection timed out") — geen code-probleem, lijkt netwerk/VPN-gerelateerd. Commit staat lokaal klaar, nog niet gepusht — volgende sessie eerst git push proberen vóór er weer op verder gebouwd wordt.
  2. P2-7-cluster (opschonen): de 5 al eerder bevestigde dode orphan-mappen opnieuw vers geverifieerd (grep op de exacte klassenamen, geen externe referenties) — git rm -r blijft geblokkeerd door de permissie-classifier (3e poging), commando staat klaar voor Bob (zie P2-7 hieronder). EventWidget-orphan- route: Bob's besluit — niet verwijderen, hernoemen met een kanweg_-prefix (consistent met de bestaande lib/kanweg/-scratch-conventie) — nog niet uitgevoerd, staat klaar als eerstvolgende builder-stap voor Bob (pagina "Event" in FlutterFlow hernoemen), nog te verifiëren via een verse export zodra gedaan. Minor-2 bevestigd: blijft op Bob's eigen lijstje, geen wijziging.

Vorige sessie (2026-08-21, sessie 44, terminal/git-verificatie + enkele kleine builder-taken via Bob, geen eigen browser-automation): vier punten afgerond of afgesloten, telkens bevestigd via verse export (en waar mogelijk een live emulator-run):

  1. P1-7 Tab 1 afgerondTabPersAgenda's eigen "On Tap"- drupalRequest stond nog op 'POST', nu 'GET' (zelfde fix als eerder al op de On-Page-Load-trigger). Gecommit 0306c26.
  2. P0-9 afgerond — bleek geen Drupal-bug. Home's "Uitgaan"-tab (eerste tab) riep de niet-bestaande display_3 aan i.p.v. de daadwerkelijk al bestaande services_3 (zelfde naamgeving als de andere 4 secties services_4/5/6/7) — een verkeerde letterlijke waarde op de displayid-component-parameter, geen Drupal-Views- misconfiguratie zoals aanvankelijk gedacht. Builder-fix + curl- bevestigd (services_3 geeft nu gewoon de eventlijst terug).
  3. P1-25-bijdrage — 3 van de 4 tekstvelden (horecagelegenheid/ adres/datum) op HomeUitgaantabelKaartComponent kregen maxLines: 1/overflow: ellipsis; een parallelle sessie (43) vond en fixte het laatst ontbrekende veld (plaats) en rondde de taak af — zie de afgeronde P1-25-notitie verderop.
  4. P1-24, Bob's besluit: geaccepteerd, geen verdere actie. De resterende Json-Path-guard-beperking op HomeUitgaantabelKaartComponent (zie hieronder) is niet de moeite waard om verder te fixen — logo is in Drupal een verplicht veld (kan in de praktijk niet leeg zijn), en de huidige impact is toch al laag (onschadelijk fallback-icoon, geen app-crash — zie de bijgewerkte P1-24-notitie).

Belangrijke concurrency-les deze sessie (zie ook de aangescherpte regel in CLAUDE.md): halverwege bleek een parallelle sessie tegelijk aan P1-24/P1-25/P1-26 te werken, zonder dat ooit een "— bezig"-marker in TASKS.md verscheen — pas ontdekt via een onverwachte git status-diff (een halfklare print()-placeholder in header_buttons_component_widget.dart), niet via de bedoelde marker. Geen schade, beide sessies' werk sloot uiteindelijk netjes op elkaar aan, maar wel reden om de regel expliciet aan te scherpen.

Nog open, niet door Bob beantwoord deze sessie: P1-7 Tab 2 — is het hartje op de header (via P1-26) de hele bedoelde scope, of wil Bob ook een echte lijst van gefavoriete gemeenten op de tab zelf (patroon al klaar: gemeenteNaamById + List.generate over favorieteGemeenteIds, geen nieuwe Drupal-afhankelijkheid)? Zie de volledige vraag bij P1-7 hieronder.


Vorige sessie (2026-08-21, sessie 43, ~2.5u builder-werk via gedeelde Chrome-browserautomatisering, na expliciete toestemming in de chat, in 2 blokken): drie taken opgepakt, alle bevestigd via verse export + gerichte flutter analyze (0 errors, alleen bestaande info/warning-lints).

  1. P1-25 afgerond — RenderFlex-overflow op home_uitgaantabel_kaart_component_widget.dart:359 bleek de plaats-Text te zijn die als enige van 4 buurvelden geen maxLines: 1 had; nu gefixt.
  2. P1-24 grotendeels afgerondPUitgaanSliderKaartComponent's ongeguarde Image.network kreeg een ConditionalBuilder-guard (logo != ''). Nieuwe bouwtechniek gevonden (nu in CLAUDE.md): een letterlijke lege-string-Second-Value op een Single Condition is wél bereikbaar voor een String/Image-Path-component-parameter (eerder ten onrechte als "onmogelijk" gedocumenteerd voor het losstaande Json-Path-geval van HomeUitgaantabelKaartComponent, dat blijft open, zie P1-24 hieronder). Bijvangst: de "Component Name kan per ongeluk overschreven worden"-valkuil uit CLAUDE.md deed zich opnieuw voor (typen in de zoekbalk landde in het Component Name-veld) — meteen hersteld, geen blijvende schade.
  3. P2-12 volledig afgerond (2e blok, op Bob's "ga nog maar even door") — laad-placeholder via FlutterFlow's "Use Blur Hash"-toggle + een vaste neutrale hash-string (nieuw recept, zie CLAUDE.md), toegepast op alle 9 live CachedNetworkImage-plekken in het project (2 bevestigd dode orphans bewust overgeslagen, zie P2-7). Live geverifieerd na blok 1: ff-run-fvm.sh-build op emulator-5554 draaide succesvol, geen nieuwe excepties — de enige optredende exceptie was exact het al gedocumenteerde open P1-24-restpunt (HomeUitgaantabelKaartComponent's JSON-Path-guard). Blok 2 alleen geverifieerd via verse export + flutter analyze (geen tweede live emulator-launch meer gedaan, emulator-5554 was tussentijds gestopt/verdwenen uit adb devices — puur additieve wijziging (laad-placeholder), laag risico). Beide blokken gecommit + gepusht (f505a71, 14f97e3, en het commit van blok 2 hieronder).

Vorige sessie (2026-08-21, sessie 42, ~1-2u builder-werk + live-emulatoronderzoek via gedeelde Chrome-browserautomatisering + mcp__android__*, na expliciete toestemming in de chat): twee builder-taken afgerond, plus stale-documentatie gecorrigeerd, plus nieuw onderzoek naar P1-24/P1-25.

  1. P1-27 — Home-pagina TabBar "Tab Bar Scrollable" aan.
  2. P2-11 — categorie-tag-kleur van bordeauxrood naar het groen van uitgaanskrant.com (#09B34A) op de 2 bevestigde plekken (TagCategorieComponent + HorecagelegenheidoverzichtKaart). Beide eerst geverifieerd via losse /tmp-exports, later alsnog een echte flutterflow export-code in de projectrepo zelf gedaan (was aanvankelijk vergeten) + gecommit/gepusht (7dd0707) — pas toen bleek via een live app-launch dat de fixes ook echt zichtbaar waren (tags groen, tab-labels voluit).
  3. Concurrency-vondst bij sessiestart: een andere/concurrente sessie (commit d8bbba4, waarschijnlijk Bob) had ondertussen zowel P1-26 (gemeente-hartje op HeaderButtonsComponent) als P0-9 (Home's kapotte Drupal-display_3 → app-side workaround naar services_3) al afgerond — beide bevestigd in code/via curl en TASKS.md bijgewerkt (was nog stale).
  4. P1-24/P1-25-herverificatie na Bob's hulp (hij startte emulator-5554 en wees op zijn fysieke K7V8DYTSMVTW6XBI, zie ook zijn losse vraag over emulator-5556's incidentele crashes): P1-24's root cause definitief gevonden (leeg logo-veld op Drupal-content nid 214339, 2 widgets met een guard die niet ver genoeg gaat — zie P1-24 hieronder, niet gelinkt aan P0-9 zoals eerder gedacht). P1-25 bleef onderzoek-technisch steken: Home's "Uitgaan"-tab reageerde niet meer op scroll-swipes (drie verschillende methodes geprobeerd, geen effect), en vlak daarna crashte emulator-5554 zelf (systemd: signal=SEGV, geheugenpiek 20GB) — mogelijk gerelateerd aan P1-24's image-decode-burst, niet bevestigd. Nieuw diagnoserecept toegevoegd aan CLAUDE.md (AVD-crash-diagnose via journalctl --user -u android-emulator*.service, en een adb logcat-buffer- valkuil die een misleidend "er gebeurt hier van alles"-beeld gaf). Geen tijd meer gehad om emulator-5554 na de crash opnieuw te proberen of emulator-5556/het fysieke toestel in te zetten voor P1-25 — blijft open voor een volgende sessie.
  5. Performance-vraag van Bob (fysieke testtoestellen "enorm traag" ná een schone herinstallatie): geen kapot toestel/instelling gevonden (Developer Options normaal, geen achtergrondbelasting) — bleek gewoon een debug-build (dumpsys package toonde DEBUGGABLE op beide telefoons, universeel en met opzet trager dan een release/profile-build). Nieuwe staande regel, vastgelegd in CLAUDE.md: voortaan standaard fvm flutter run --profile -d <device> voor gewone testrondes, volledige debug-mode (ff-run-fvm.sh) alleen nog bij het actief jagen op een specifieke bug. Gedemonstreerd op K7V8DYTSMVTW6XBI (profile-build: 56s Gradle + 3.2s install, draait probleemloos).
  6. P1-24-bouwpoging (~45 min) mislukt, overgedragen aan Bob — de guard-fix op HomeUitgaantabelKaartComponent bleek geblokkeerd op een echte FlutterFlow-UI-beperking (operator "Is Set and Not Empty" niet beschikbaar voor een JSON-Path-gebonden First Value op een ongetypeerd loop-item, geen letterlijke-tekst-invoer voor een 2e AND-conditie) — zie de uitgebreide poging-documentatie bij P1-24 hieronder + nieuwe CLAUDE.md-notitie. Widget zelf staat nog ongewijzigd/veilig (elke poging netjes teruggedraaid via Cancel/ page-reload, geverifieerd). Bijvangst: de 2e P1-24-locatie (PUitgaanSliderKaartComponent's Image.network) is juist waarschijnlijk wél eenvoudig, want die logo is daar een directe String-component-parameter, niet een JSON-Path-binding — apart genoteerd voor Bob.

Vorige sessie (2026-08-20, laat, look&feel-review mobiel+tablet, code-only + live emulators emulator-5554/emulator-5556, geen builder-UI aangeraakt): op Bob's verzoek een look&feel-review gedaan als een van de laatste checks vóór livegang — Home-pagina live vergeleken op telefoon- en tabletformaat, plus een kleurenpalet-check. 3 nieuwe punten toegevoegd (P1-27 t/m P1-29) en 2 nieuwe polish-ideeën in P2 (P2-11 kleurcodering, P2-12 laad-placeholder bij afbeeldingen). Geen nieuwe P0's — de app is in de basis bruikbaar op beide formaten, dit zijn allemaal afwerkingspunten. Onderweg 2 al bekende issues live herbevestigd i.p.v. opnieuw als nieuw gemeld: P1-13 (5382px-overflow op Horeca-overzicht → Activiteiten-tab) staat nog exact zo kapot als eerder gedocumenteerd, inclusief het bekende "Confirm even geen scroll meer mogelijk"-effect; P1-21's AdBanner-debugtekst staat er ook nog, maar dat is Bob's eigen bewuste keuze (blijft zo tot na AdMob-goedkeuring) — geen actie. Sessie werd 2x onderbroken door een computer-crash van Bob, daarna hervat; geen wijzigingen verloren (code-only sessie, niets stond klaar om te committen). Na de eerste hervatting bleek commit d8bbba4 (P1-26's FlutterFlow-kant, een andere/concurrente sessie) al geland te zijn bovenop deze sessies eigen ffbc366 — dat commit bevat exact het hartje dat hierboven als P1-28 geanalyseerd is, maar zónder het contrastpunt mee te nemen; P1-28's tekst hieronder is bijgewerkt om dat te reflecteren (niet meer "vóór commit fixen", gewoon een normale openstaande fix).

Vorige sessie (2026-08-20, avond, Bob aanwezig, gedeelde Chrome-browserautomatisering, na expliciete toestemming in de chat): drie kleine taken afgerond. (1) Export-drift (.gitignore/ ios/project.pbxproj, stond ongecommit sinds een eerdere export) gecommit — puur FlutterFlow-exportbijwerking, geen functionele wijziging (5890cc0). (2) P2-7's orphan-mappen-git rm opnieuw geprobeerd (3e poging in totaal) — blijft geblokkeerd door de permissie-classifier zelf, ook nu weer; commando staat klaar in de P2-7-sectie voor Bob. (3) Login-pagina: overbodige "Terug"-knop verwijderd (showBackButton: false op HeaderButtonsComponentWidget, zelfde patroon als Home/P1-20), bevestigd via verse export + flutter analyze (8e450be), zie de bijgewerkte P1-20-notitie hieronder. Concurrency-vondst tijdens deze sessie: halverwege bleek Bob zelf tegelijk een aparte chatsessie te draaien die P1-26 aan deze lijst toevoegde (nieuwe Drupal favorieten flag/unflag-resource) — dat maakt Tab 2 "Favoriete gemeenten" (P1-7) nu geblokkeerd op die nog niet gedeployde Drupal-kant, waar eerder "geen Drupal-blocker bekend" stond. Tab 2 daarom deze sessie bewust niet gebouwd, zie P1-26.


Vorige sessie (2026-08-19, Bob aanwezig achter zijn scherm, gedeelde Chrome-browserautomatisering): vijf fixes afgerond, elk bevestigd via verse export + gerichte flutter analyze (en de laatste twee ook live op emulator-5554). Op de Favorieten-pagina (zie P1-7 hieronder voor details): (1) Tab 3 "Favoriete Gelegenheden" riep zijn API-call altijd anoniem aan (sessionName/sessionId nu gebonden aan FFAppState().userSessionname/userSessionid), (2) Tab 2's beschrijvingstekst liep tot de schermrand (nu 16px links/rechts padding), (3) de hele pagina miste een header/drawer (nu Scaffold Drawer- + AppBar-slot, zelfde patroon als andere pagina's), (4) alle 4 tab-labels waren afgekapt (nu "Tab Bar Scrollable" aan). Daarna (5) het laatste P1-6-restpunt op PUitgaanSliderKaartComponent ('def'- datumfallback) opgelost met een Visibility-guard op de rauwe datum- component-parameter. Nieuwe bouwtechniek ontdekt (zie CLAUDE.md): een Scaffold's Drawer/AppBar-slot vullen met een custom component lukt niet via de gewone rechtsklik-Insert-Widget-flow — wel via slepen vanuit het linker widget-paneel direct naar de canvas-dropzone. P2-7's orphan-mappen-git rm (5 bevestigde dode mappen) is opnieuw geprobeerd (met Bob's expliciete toestemming in de chat) maar blijft geblokkeerd door de permissie-classifier zelf (systeemniveau, niet te overrulen via chat-toestemming) — commando staat nog steeds klaar in de P2-7-sectie voor Bob om zelf te draaien.


Vorige sessie (2026-08-18, zelfstandig, Chrome-browserautomatisering, Bob niet actief achter zijn scherm): begon met het committen van 2 sessies aan ongecommitte builder-sync (login/drawer/i18n/ drupalRequest-fixes + een nog niet in TASKS.md gedocumenteerde PUitgaantabelKaartComponent-herstructurering — navraag bij Bob nodig over doel/status daarvan, zie de commit-boodschap van 36dd725). Daarna P1-6 nu volledig afgerond op alle live/prioriteit-pagina's (evenement_horecagelegenheid_widget.dart: bezorgkosten/bestellink- guard + establishmentnid-debugwidget verwijderd; horecagelegenheid_current_widget.dart: title/content/logo-guard; event_current_widget.dart: title/date-guard) — gecommit 14f2f14/82b0fb6, alleen nog 2 bewust laag-prioriteit restpunten (orphan-route + 1 grensgeval) blijven staan, zie P1-6 hieronder. P2-7's orphan-mappen-git rm (5 bevestigde dode mappen) blijft geblokkeerd door de permissie-classifier — commando staat nog steeds klaar in de P2-7-sectie voor Bob of een sessie met expliciete toestemming. Bevestigd tijdens deze sessie: de "First Value toont tijdelijk weer '+'"-render-glitch uit de bestaande CLAUDE.md-notitie (Visibility-Conditional-recept) is structureel, niet incidenteel — op meerdere velden kostte het "Conditions" → "Single Condition"-pad 3-5 klikken voordat de suboptie daadwerkelijk zichtbaar/klikbaar werd; navigeren via de zoekbalk in de "Set from Variable"-dialoog (typ "Single") maakte het betrouwbaarder omdat de gefilterde lijst maar 1 item toont. Daarna P1-17 opgepakt (Engelse vertalingen) — 6 velden afgerond, maar gestopt na het ontdekken van een bevestigde FlutterFlow-backend-bug: 7 losse tooltip-vertalingen blijken aan elkaar gekoppeld (1 bewerken overschrijft alle 7 met dezelfde waarde), bevestigd via 2 onafhankelijke UI-ingangen + meerdere tussentijdse verse exports. Gecommit 9150013, volledige details + exacte gewenste waardes per sleutel in P1-17 hieronder. Bewust niet verder doorgewerkt aan de resterende ~110 vertalingen deze sessie totdat duidelijk is of dit een geïsoleerd geval is.


Vorige sessie (2026-08-17 avond, live pair-sessie met Bob op emulator-5554): begon met een Gradle-buildfout bij Bob's eigen ff-run-fvm.sh-run — root cause: Android Studio was diezelfde dag automatisch geüpdatet naar een snap-revisie met Java 25 als ingebouwde JBR, en Gradle 8.12 (dit project) kan daar niet mee overweg. Fix: fvm flutter config --jdk-dir=/usr/lib/jvm/java-17-openjdk-amd64 (globale Flutter-CLI-instelling, geen projectbestand — overleeft een export). Zie ook CLAUDE.md voor het herbruikbare patroon. Daarna login getest en 2 nieuwe crash-bugs gevonden + gefixt (zie de afgeronde blokken bij P1-18 en P1-7 hieronder) en een 3e, nog niet gefixte bug gevonden op Favorieten-tab 3 (zie P1-7 "Nog open"). Belangrijk voor de volgende sessie: ik heb tijdens het redeployen zelf 2x een bouwfout veroorzaakt (dubbele variabele-declaratie na "Copy Action Chain") — beide keren zelf gevonden via flutter analyze vóór het Bob bereikte, maar zie de nieuwe CLAUDE.md-notitie over dit patroon vóórdat je dit trucje nog eens gebruikt.


Vorige sessie (2026-08-17 overdag, zelfstandig, code-only — geen browser-tab- groep gevonden bij sessiestart, dus aangenomen dat Bob niet actief achter zijn scherm zat en geen builder-UI geprobeerd): begonnen met ff-session-check.sh (schoon) + een verse flutterflow export-code gediffed tegen de repo — bevestigd: geen enkel contentverschil, alle "afgerond"-claims in dit bestand kloppen nog met de live FlutterFlow-staat. P0-9 herbevestigd nog kapot (curl op display_id=display_3 geeft nog steeds de kapotte-view-foutmelding). P1-6 verder onderzocht (code-only): evenement_component_widget.dart's 4 velden blijken alleen bereikbaar via de al bekende orphan-route EventWidget — laag prioriteit, zie bijgewerkte notitie. horecagelegenheid_current_widget.dart's 3 velden (title/logo/content) kregen exacte regelnummers + JSON-paths voor het bestaande guard- recept, klaar voor een volgende live sessie. Nieuwe P2-7-bijvangst: 5 lokale mappen bleken al uit de FlutterFlow-export verdwenen (nooit lokaal opgeruimd) — git rm hierop werd geblokkeerd door de permissie-classifier (bulk-verwijdering), dus klaarliggend voor Bob/een sessie met expliciete toestemming, zie het exacte commando bij P2-7. Geen builder-UI-werk deze sessie, dus geen van de "— bezig"-blocking taken (P1-6-resterende-velden, P1-7 Tab 1/2) daadwerkelijk gebouwd.


Vorige sessie (2026-08-14, live pair-sessie met Bob, builder-clicks door Bob + export-verificatie door Claude na elke stap): alle P0 resterend bij sessiestart afgerond — P0-4 (favorieten-lege-lijst- state Tab 3), P0-8 (14/17 "null"-tekst-velden op HorecagelegenheidCurrent, 3 restpunten bewust naar nieuwe "Minor"-sectie), P0-5's header-bijvangst (hardcoded Basic-Auth verwijderd uit alle 15 API-calls; het Drupal-hoofdpunt van P0-5 blijft open bij Bob). Ook P1-15 (5 social-knoppen + Menukaart-knop + EvenementInfo-Website-knop) en P1-4 (horcat Default Value) afgerond, plus nieuwe P1-23 aangemaakt (tikbare links op HorecagelegenheidCurrent, vervolg op P0-8). Onderweg meerdere keren dezelfde omgekeerde-operator-fout (== '' i.p.v. != '') gevonden en gecorrigeerd — zie ook CLAUDE.md. Concurrency: halverwege bleek een andere sessie tegelijk builder-werk te doen (P1-7 Tab 3, gecommit als bed1d68) — geen conflict, beide commit-reeksen zijn na elkaar cleanly gepusht. Bijgewerkt tijdens deze sessie ook CLAUDE.md (nieuw punt: nooit stilzitten, altijd direct de volgende taak geven na "gedaan").


Bijgewerkt: 2026-08-15 (Claude, ochtendsessie). Zie CLAUDE.md voor werkinstructies/conventies. Elke openstaande taak hieronder is zelfstandig te begrijpen zonder de chat gelezen te hebben waarin hij ontstond.

Deze sessie (2026-08-15, ochtend, code-sync + audit, geen live builder-UI-werk gedaan): begonnen met ff-session-check.sh (schoon, niets "— bezig") en een gerichte git log-check op favorieten_widget.dart naar aanleiding van de vaste vuistregel bovenaan dit bestand — bleek sinds bd42224 (allereerste versie, 3 kale placeholder-tabs) nooit meer gewijzigd, ondanks meerdere latere sessies die claimden P0-4/P1-7 Tab 3 daar te hebben afgerond. Een verse flutterflow export-code bevestigde: dat werk staat echt in FlutterFlow, maar was nooit lokaal gepulld/gecommit — dezelfde sessie's ff-run-fvm.sh-run (die intern zelf naar de projectmap exporteert) bracht in totaal 12 bestanden synchroon die stiekem al veel langer op wijzigingen wachtten. Gebouwd (flutter build apk --debug slaagde, flutter analyze: 0 errors) en gecommit (2c093bc). Bijvangst bij het verifiëren van de diff: P1-15 bleek volledig afgerond (de laatste 3 open restpunten — Menukaart-guard + 2x Website-guard — waren ook al gefixt, nooit gemeld) en het P0-3-restpunt "default-locatie zonder naam" bleek ook al gefixt (plus een losse debug-SnackBar opgeruimd) — beide uit deze lijst verwijderd, zie hun eigen doorstreep-notities. Nieuwe live bevindingen tijdens de verificatie-launch (emulator-5554, verse build): twee nieuwe punten toegevoegd, P1-24 (de bekende Invalid argument(s): No host specified in URI-exceptie blijkt ook ná de volledige P1-15-fix nog steeds op te treden, nu bevestigd vóór enige tap — dus een andere, nog ongevonden bron) en P1-25 (nieuwe 129px-RenderFlex-overflow op HomeUitgaantabelKaartComponent:359, geen stack trace met bestandslocatie gevangen wegens de bekende single-dump-beperking). Tweede deel, zelfde sessie (na Bob's "ja, ga maar verder"): P1-7's "Gebruiker"-tab gebouwd, Uitloggen-knop volledig werkend — nieuwe 4e tab op Favorieten (via TabBar's "Active Tab"-dropdown → "+ Add Tab", nieuw ontdekt/gedocumenteerd recept, zie CLAUDE.md), ListTile "Uitloggen" met 2 acties (Update App State: 6 sessievelden op "Clear Value"; Navigate To Login met "Allow Back Navigation" uit → context.goNamed(...)). Bevestigd via verse export + flutter analyze (0 errors), gecommit (ba1e0b2). Live tap-test op een device kon niet: emulator-5554/5556 bleken niet te draaien (alleen Bob's fysieke K7V8DYTSMVTW6XBI was aangesloten) — nog te doen door een volgende sessie/Bob. "Wachtwoord wijzigen" en "Account verwijderen" op dezelfde tab bewust niet gebouwd (wachten op Bob, zie P1-7's "Nog open"-lijst). Correctie op de eerdere claim hierboven: de ff-run-fvm.sh-hot-restart-loop van het ochtenddeel is inmiddels vanzelf beëindigd (bereikte zijn eigen 590s-timeout) — geen open proces meer voor een volgende sessie om op verder te bouwen, gewoon opnieuw starten indien nodig.

Vorige sessie (2026-08-14, tweede ronde, code-audit + 1 builder-poging): op verzoek ("pak nog wat taken op") eerst een code-only audit van api_calls.dart tegen de openstaande P1-7-taak (favorietenpagina) — twee stale/onvolledige aannames in P1-7 gecorrigeerd, geen van beide had nog nieuw Drupal-werk nodig:

  • Tab 1 "Persoonlijke agenda": bleek nog "niet onderzocht", maar UitgaanstabelCall/UitgaanSliderCall ondersteunen al een townid-parameter — rechtstreeks bruikbaar door per favoriete gemeente (favorieteGemeenteIds) een call te doen. Zie bijgewerkte P1-7 hieronder.
  • Tab 3 "Favoriete Gelegenheden": de bestaande blokkering-op-P0-7 was stale (P0-7 is al op 2026-08-13 gefixt) — én er bleek een simpelere, nooit eerder overwogen route: FavorietenAgendaCall (sessie-geauthenticeerd, geeft al server-side de favoriete gelegenheden van de ingelogde gebruiker terug in 1 call). Zie bijgewerkte "Drupal dingen"-lijst hierboven — niet langer een Bob-batch-item, Claude/een volgende sessie kan dit nu direct bouwen. Daarna 1 mechanische builder-taak geprobeerd (P2-7-bijvangst: FavorietenAgendaTESTKANWEGCall verwijderen via het API Calls-paneel) — 2x geprobeerd, 2x niet doorgezet naar een verse export/reload, zelfde "ziet er opgeslagen uit maar bereikt de export niet"-patroon als elders in dit bestand, maar nu voor het eerst ook op het (verder betrouwbare) API Calls-paneel i.p.v. alleen de widget-tree/canvas. Zie de nieuwe blocker-notitie bij P2-7. Niet verder geprobeerd (staande regel: na 1-2 pogingen overdragen aan Bob), geen schade aangericht. Vervolgens, op Bob's aanmoediging ("pak nog een paar taken op"), P1-7 Tab 3 "Favoriete Gelegenheden" alsnog volledig gebouwd in de builder (los tabblad, Bob's eigen tabblad met rust gelaten): ListView + ListTile-itemtemplate + FavorietenAgendaCall-Backend Query + "Generate Dynamic Children" (een tot nu toe onbekend/gemist icoontje, zie nieuw gedocumenteerd in CLAUDE.md) + tap-navigatie naar HorecagelegenheidCurrent. Onderweg twee eerder als "niet gevonden" gedocumenteerde builder-mechanismes alsnog gevonden en gecorrigeerd in CLAUDE.md: het Actions-tab-icoontje (P1-9 had dit "niet gevonden" genoteerd) en de "Generate Dynamic Children"-flow zelf (nergens eerder gedocumenteerd, waarschijnlijk de ontbrekende schakel voor alle toekomstige API-gebonden lijst-taken). Bevestigd via verse export + flutter analyze (geen nieuwe treffers). Zie bijgewerkte P1-7 hieronder voor de volledige stappen (herbruikbaar recept).

Eerdere sessie (2026-08-14, eerste ronde, code-only — geen builder-UI gebruikt omdat Bob meldde gelijktijdig zelf in een andere sessie aan de slag te gaan — gedeelde Chrome dus niet ingezet): ff-session-check.sh toonde 20 bestanden ongecommit in lib//ios//.gitignore, exact overeenkomend met de al-bevestigde-maar-nooit-gecommitte P1-19/P1-20/P1-11/P2-7- wijzigingen uit de sessienotitie van 2026-08-13 hieronder (alle bestanden zelfde mtime, 2026-08-13 21:30 — stabiel, niets actief in wijziging). Alsnog gecommit + gepusht (fb6b224) via ff-commit.sh (diens interactieve confirm-prompt faalde non-interactief, staging + inhoud eerst handmatig geverifieerd tegen de TASKS.md-beschrijvingen, daarna commit+push met dezelfde inhoud). Bijvangst bij het verifiëren: P2-7's API-call-opschoning bleek verder dan gedacht — 7 van de 8 voorgestelde test/scratch-call-verwijderingen zijn ook echt doorgevoerd (alleen FavorietenAgendaTESTKANWEGCall bleef staan), en Establishments Call bleek hernoemd naar HorecagelegenheidoverzichtCall (geen verwijdering, callers consistent meeveranderd) — zie bijgewerkte P2-7 hieronder. Geen builder-werk, geen flutter run/emulator-interactie deze sessie (Bob's twee lopende ff-run-fvm.sh-processen/devices met rust gelaten).

Eerdere sessie (2026-08-13 avond, builder + emulator, Bob gelijktijdig actief in zijn eigen ff-run-fvm.sh-testronde): begonnen met 2 onbeklaimde P1-9/P1-15-punten op EventCurrentbeide geblokkeerd op een structureel onbetrouwbare Widget Tree-klikprecisie (zie de uitgebreide poging-notitie bij P1-9), teruggezet naar Bob. Halverwege meldde Bob dat hij P0-7 zelf had gefixt (Drupal-view-bug) en vroeg om naar de horecagelegenheid-pagina te kijken. P0-7 bevestigd opgelost via een live check op emulator-5554 (nid 91142, "De Beun": echte titel/foto's/inhoud in plaats van placeholders) — maar dat onthulde meteen een nieuwe, dringende P0-8: de Info/Links/Bezorgen- tabs van HorecagelegenheidCurrent breken zichtbaar zodra er echte data doorkomt (RenderFlex-overflow tot 209px + letterlijke "null"- tekst op 17 ongeguarde velden, live gefotografeerd op alle 3 tabs). Volledig uitgeschreven met exacte regelnummers en een mechanisch herhaalbare fix — zie P0-8 hieronder.

Eerdere sessie (2026-08-13, zelfstandig, code-only — geen builder-UI gebruikt omdat niet zeker was of Bob achter zijn scherm zat): twee punten uitgediept, puur via lezen/grep, geen wijzigingen aan app-code. P1-15 uitgebreid met 3 nieuw gevonden crash-plekken die niet in het eerder gedocumenteerde 5-knoppenlijstje zaten: een 6e ongegarandeerde "Menukaart"-knop in hetzelfde bestand (evenement_horecagelegenheid_widget.dart:626-635), en twee "Website"-knoppen die helemaal geen guard hebben (niet eens de kapotte) — evenement_info_widget.dart:268 (component gebruikt op de normaal bereikbare EventCurrent-pagina) en evenement_component_widget.dart:414 (alleen op de al bekende orphan- route EventWidget, zie P2-7). Ook bevestigd dat horecagelegenheid_current_widget.dart géén launchURL-aanroepen bevat — dat eerdere twijfelpunt is nu weggenomen. P1-21's punt 2 gecorrigeerd: de aanname dat lib/flutter_flow/flutter_flow_ad_banner.dart zonder builder-UI en zonder export-risico lokaal aan te passen zou zijn bleek ongeverifieerd en botst met CLAUDE.md's algemene regel over gegenereerde bestanden — teruggedraaid naar "nog op te lossen, waarschijnlijk via een eigen custom widget", niet blind uitgevoerd. Tweede ronde, zelfde sessie: 6 "nog open"-taken tegen de huidige code herbevestigd (vuistregel bovenaan dit bestand — niet blind vertrouwen dat "open" nog klopt): P0-7 (Drupal-endpoint opnieuw met curl getest, alle 3 nid's nog steeds HTTP 500), P0-5 (hardcoded Basic-Auth-header, nog steeds 15 treffers), P1-4 (horcat ??= null!; nog aanwezig, regel verschoven naar 1440), P1-16 (nog geen enkele Firebase-referentie in het project), P1-20 (HeaderButtonsComponentWidget heeft nog steeds geen component-parameter, showBackButton-fix nog niet aangemaakt), P1-11 (bug zelf ongewijzigd, maar geciteerde regelnummers waren stale door tussentijdse edits — gecorrigeerd naar de huidige regels). Geen van deze 6 bleek stiekem al opgelost; alleen regelnummer-correcties, geen statuswijzigingen.

⚠️ Belangrijke ontdekking, zelfde ronde: halverwege deze sessie bleek Bob gelijktijdig een eigen ff-run-fvm.sh-build/testronde te draaien (proces gestart 20:39, device K7V8DYTSMVTW6XBI) — de bijbehorende verse export bracht een hoop bevestigd-maar-nooit- gecommit werk voor het eerst echt in deze repo: P1-20 volledig geïmplementeerd (exact het hieronder uitgewerkte showBackButton- patroon), P1-11 gefixed (Expanded + hoogte-correctie op HorecagelegenheidEventTabelComponentCopy), alle 9 P1-19 Row→Wrap-conversies landen nu pas écht in git (eerdere "afgerond"-notitie was destijds alleen via een losse /tmp/ff-check- export geverifieerd, nooit gecommit — git log -S"return Wrap(" bevestigde 0 eerdere treffers), en een gedeeltelijke P2-7 API-call-opschoning (LoginCall/GetcsrfCall/ZZUserEstablishmentsTESTCall verwijderd, overige test/scratch-calls hergegroepeerd onder een nieuwe KanwegGroup-wrapper i.p.v. verwijderd — Bob's eigen aanpak, wijkt af van de letterlijke aanbeveling maar overlapt grotendeels). Niets hiervan is door Claude gecommit (nog steeds Bob's eigen, actieve build/testronde) — alleen TASKS.md-status bijgewerkt op Bob's verzoek, ná bevestiging via git diff. Kleine kanttekening: horecagelegenheidoverzicht_kaart_widget.dart kreeg ook een toegevoegde voorloop-spatie op de 'titel'/'adres'/'plaats'- fallback-teksten (' titel' etc.) — lost P1-6 niet op, lijkt een onbedoeld bijeffect, geen actie ondernomen.

Vervolgsessie zelfde dag (2026-08-13, tweede ronde, ook zelfstandig code-only/emulator, geen builder-UI): drie taken efficiënt gecombineerd zonder Bob's browser nodig te hebben. P1-7 Tab 2 uitgezocht: bevestigd dat er nog helemaal geen favoriet-toggle-UI voor gemeenten bestaat (favorieteGemeenteIds alleen in app_state.dart), plus waarom het bekende horeca-hartje-patroon hier niet 1-op-1 past (gemeentekeuze is een dropdown, geen kaartjeslijst) — twee opties uitgeschreven ter bespreking met Bob. P1-20 kreeg een volledig uitgewerkt, direct uitvoerbaar stappenplan (parameternaam showBackButton, default true, welke ene call site false moet worden, welke 9 met rust gelaten kunnen worden) — scheelt een onderzoeksronde bij de volgende live builder-sessie. P1-9 punt 1 (Event-pagina) live gecheckt via een directe am start-deeplink naar EventCurrent (nid 214370) op de al draaiende emulator — twee nieuwe concrete layout-bevindingen (titel/deel-knop zonder tussenruimte, en een losstaande, altijd-zichtbare nid-debugtekst die zwaarder weegt dan P1-6's oorspronkelijke cosmetica-inschatting). Kanttekening: de gebruikte emulator-instantie was zelf ~2 dagen oud (stale t.o.v. vandaag's P1-22/P1-10-fixes), dus de "???"-tekens die ook zichtbaar waren op het screenshot zijn vermoedelijk gewoon dat al bekende, inmiddels gefixte encodingprobleem op oude gecompileerde code — niet als nieuwe bug behandeld.

Eerdere sessie (2026-08-12, review): volledige gebruikersdoorloop op een verse build (ff-run-fvm.sh, geïnstalleerde app was nog van 2026-08-09 en dus stale t.o.v. alle fixes van 2026-08-10/11) langs de 5 gevraagde gebieden: overzichtspagina (Home), horecaoverzicht (HorecagelegenhedenOverzicht), evenement (EventCurrent + EvenementHorecagelegenheid), pagina (PUitgaanPage) en horecagelegenheidpagina (HorecagelegenheidCurrent), gecombineerd met een code-audit van diezelfde bestanden. 4 nieuwe bevindingen toegevoegd: nieuwe P0-7 (horecagelegenheid-infodata blijkt structureel leeg — bevestigd op 2 verschillende gelegenheden, geen toeval), nieuwe P1-20 (Home's al-verwijderd-gewaande terugknop is terug via een hergebruikt component en blokkeert soms de hamburger), nieuwe P1-21 (AdBanner op PUitgaanPage toont developer-debugtekst aan echte gebruikers), nieuwe P1-22 (decodeUtf8: false op alle API-calls → emoji in Drupal-content worden ?-tekens). P1-19 uitgebreid met 8 nieuw gevonden zusterinstanties van hetzelfde "kale Row zonder Wrap"-categorietag-patroon, nu bevestigd op exact de pagina's uit deze review (Home, PUitgaanPage, EventCurrent, HorecagelegenheidCurrent). P1-13 live herbevestigd — nog steeds kapot (1529px-overflow op de Activiteiten-tab), geen voortgang t.o.v. 2026-08-10. Zie de individuele taken hieronder voor bewijsvoering (screenshots/log-regels/broncoderegels).

Eerdere sessie (2026-08-10, avond): live pair-sessie — Bob deed alle builder-edits zelf, Claude gaf per punt de exacte stappen en verifieerde daarna elke edit met een verse flutterflow export-code

  • grep (niet op de builder-UI zelf vertrouwd). 9 taken écht afgerond en bevestigd: P1-18 (login-crash + de daaropvolgende success-check-bug), P0-1, P0-3 (beide restpunten — punt 2 uiteindelijk simpeler opgelost dan gepland: HeaderButtonsComponentWidget() hergebruikt op EventCurrent i.p.v. de logica te dupliceren), P1-3, P1-9's schaduw-restpunt, P1-1 (alle 4 sub-punten), P1-12 (opgelost via een Visibility-guard op de rauwe horecaid-parameter, netter dan de oorspronkelijk voorgestelde Default Value). Belangrijke bevinding: minstens 2x deze sessie leek een builder-edit in de UI geslaagd ("Confirm" gedaan, geen foutmelding) maar bleek bij een verse export toch niet doorgezet (P0-3 punt 1 ooit eerder, en een eerste P1-15-poging vanavond) — elke afgeronde taak in een levende sessie nu standaard verifiëren met een verse export vóór 'm als klaar te beschouwen, niet op de UI-status alleen vertrouwen. P1-15 blijft open (5 social-knoppen op EvenementHorecagelegenheid) — eerste poging bleek bij export niet toegepast, Bob gaat hier zelfstandig mee verder.

Eerdere sessie (2026-08-10, middag): twee taken opgepakt. P2-9-bijvangst (dode terug-knop Home) volledig afgerond — Remove Widget-actie, bevestigd via verse export. P1-13: builder-fix uitgevoerd (Expanded+Shrink Wrap uit+Scrollable aan op de StaggeredView-node) maar niet doorgezet naar de export ondanks correcte builder-UI-status ("Synced", geen foutmelding, blijft staan na reload) — bevestigd met 3 onafhankelijke verse flutterflow export-code-runs. Sanity-check bevestigt dat de exportpijplijn zelf werkt (P0-6/P2-9 tonen wél correct) — dit lijkt een specifiek sync-probleem bij deze property-combinatie, zie P1-13 voor het volledige verslag en het verzoek aan Bob om zelf te verifiëren.

Vorige sessie (2026-08-09, avond): twee taken opgepakt. P1-19: builder-fix geprobeerd (Row → Wrap Widget), maar de menu-klik registreerde niet (4 pogingen, geen schade) — teruggegeven aan Bob met exact stappenplan. P1-13: eindelijk de al maanden gezochte volledige stack trace gevangen (nieuwe flutter run --route-truc omzeilt de race met Home's eigen overflow) — root cause nu hard bevestigd (niet-scrollbare MasonryGridView in een kale Column) + concreet fix-voorstel, builder-uitvoering nog open. Bijvangst: een tweede, nog niet eerder gedocumenteerde instantie van P1-19's "kale Row/Column zonder Wrap"-bugfamilie gevonden op PUitgaanSliderKaartComponent:179.

Vorige sessie (2026-08-09, middag): eerste sessie met mcp__android__*- toegang tot beide emulators (i.p.v. alleen losse adb-Bash-commando's) — P0-6 afgerond (5 debug-knoppen op Login verwijderd, builder + export/build + live geverifieerd op telefoon én tablet). Sub-bevinding (hintText-placeholder) blijft open, geblokkeerd op paneel-clipping. P1-17 opnieuw onderzocht en root cause herbevestigd, maar de voorgestelde "Engels weghalen"-fix is door Bob afgewezen — Engels moet beschikbaar blijven; taak blijft open in andere vorm (zie daar).

Deze review-sessie (2026-08-07, middag+avond): volledige doorloop van open taken + code-audit, plus een live "ogen van een gebruiker"-doorloop op de emulator (ná herstart van Bob's desktop/ Android-VM wegens een OOM — app opnieuw opgestart via de standaard ff-run-fvm.sh-workflow, daarna pm clear om een echte eerste-launch-ervaring te simuleren). Concrete live vondsten: zie nieuwe P0-6 (test-navigatieknoppen zichtbaar op de productie- Login-pagina — grootste vondst van de sessie), P1-17 (Engelse locale-fallback + vrijwel geen echte vertalingen), aangevulde P2-9 (onboarding + dode terug-knop op Home, nu live bevestigd i.p.v. ingeschat) en P1-15 (scope groter dan gedacht). Kanttekening: de emulator vertoonde ook wat omgevingsinstabiliteit (kort een "System UI isn't responding"-melding vlak na herstart, en flutter run verloor op een gegeven moment de verbinding met het device) — dat lijkt eerder aan de verse VM-herstart/host-belasting te liggen dan aan de app zelf, dus niet 1-op-1 als app-bug behandelen, maar wel vermeld voor het geval het patroon terugkomt.

Formaat van elke taak: **P0-1 · Eigenaar: ...** als eerste regel — een stabiel nummer (verandert niet als andere taken worden afgerond/verwijderd) + wie 'm oppakt. Volgorde binnen elke prioriteit = geschatte ernst/impact, hoogste eerst.

Eigenaar-waarden:

  • Bob — sneller/simpeler voor hem zelf (meestal builder-UI met een bekend fragiele dialoog, zie CLAUDE.md).
  • Claude — zelfstandig/via browser-automation te doen, nog onbeklaimd.
  • Claude — bezig (sessie ) / Bob — bezig — een sessie/persoon is hier nu actief mee bezig.
  • Onbepaald — nog geen eigenaar gekozen.
  • ⚠️ Voorkom dubbel werk: Bob start elke taak in een nieuwe, aparte chat — er kunnen dus meerdere sessies tegelijk actief zijn op hetzelfde project. Voordat je een taak oppakt: check of de Eigenaar-regel al "— bezig" zegt. Zo ja: niet zelfstandig ook gaan zitten werken aan hetzelfde bestand/component — vraag Bob eerst wie 'm afmaakt (dit gebeurde op 2026-08-04: twee sessies pakten onafhankelijk P0-2 op; Bob moest scheidsrechteren). Zet zelf "— bezig" in de Eigenaar-regel zodra je serieus aan een taak begint, en haal het weer weg (of verwijder de taak, zie hieronder) zodra je stopt.

    Afronden: een volledig afgeronde taak wordt verwijderd uit deze lijst (niet gearchiveerd). Bij gedeeltelijke voortgang: taak laten staan met bijgewerkte inhoud die alleen het resterende werk beschrijft.

    Modelkeuze: Haiku vs Sonnet

    Korte conclusie (2026-08-05, beoordeeld): niet standaard overschakelen op Haiku voor dit project. Bijna elke Claude-taak hier eindigt als een FlutterFlow-builder-interactie (Bob kan zelf geen code bewerken, zie CLAUDE.md), en die builder is canvas-gerenderd Flutter Web zonder normale DOM/accessibility-tree. Dat botst met precies Haiku's zwakke kant: geduldig multi-stap troubleshooten (scroll-pogingen, DOM/shadow-DOM-zoektochten, accessibility geforceerd activeren, herkennen van een "Discard changes?"- dialoog, op tijd stoppen na 1-2 pogingen i.p.v. blijven klikken of per ongeluk een Delete-knop raken). Zelfs taken die er triviaal uitzien (bv. P1-10's cache-toggle: 1 klik + Confirm) bleken pas na 6+ verschillende technieken écht geblokkeerd op een clipping-bug — dat vraagt het uithoudingsvermogen/verstand van Sonnet om zowel de pogingen als het op tijd stoppen goed te doen.

    • Wel Haiku-geschikt — puur lezen/greppen/samenvatten, geen builder-UI, geen destructief risico:
      • De audit-/verificatiestappen die dit bestand al vaak gebruikt om "open" vs "al gefixt" te bevestigen (git log -S"...", gerichte grep) — zie de vuistregel hierboven over verouderde taakstatus.
      • Live emulator-checks (app draaien, deep link/navigatie, scherm/ logs checken) — geen builder-bewerking. Kanttekening (2026-08-05): de eerder als "laag risico, duidelijk slagingscriterium" ingeschatte P1-8-check bleek bij uitvoering juist 2 échte crashes op te leveren (zie P1-1 punt 4 en de nieuwe P1-12) — het navigeren/screenshotten zelf is Haiku-geschikt, maar de opvolging (stacktrace naar exacte broncoderegel herleiden, onderscheid maken tussen "al bekend probleem" en "nieuwe bug", inschatten of normaal gebruik geraakt wordt) vroeg meer redeneerwerk dan verwacht — bij een crash-bevinding op zo'n check de opvolging liever alsnog met Sonnet doen.
    • Niet Haiku-geschikt (Sonnet aanhouden): elke taak die een builder-widget/-property wijzigt (vrijwel alle overige P1/P2-taken), en taken die ontwerpkeuze/afweging vragen zoals P1-9 en de P2-features (P2-2 t/m P2-6, P2-8) — geen mechanisch patroon, dus geen goede Haiku-fit.

    P0 — blokkeert livegang

    (P0-6 volledig afgerond 2026-08-09 avond, incl. hintText-restpunt — door Bob zelf gedaan in de builder, geverifieerd via verse flutterflow export-code: keys d507d3b9/t8h5f8ir in internationalization.dart staan nu op nl: 'E-mailadres'/en: 'Email address' resp. nl: 'Wachtwoord'/en: 'Password'. Uit deze lijst verwijderd.)

    (P0-1 afgerond 2026-08-10 avond — Bob, builder, bevestigd via verse export: HomeUitgaanSliderComponent toont nu if (homeslider.isEmpty) { return Image.asset('assets/images/logo800px.png'); } vóór de Carousel gebouwd wordt. Uit deze lijst verwijderd.)

    (P0-3 afgerond 2026-08-10 avond — Bob, builder, beide restpunten bevestigd via verse export. Punt 1: Flexible(child: Text(...)) staat nu om de gemeente-/provincienaam in HeaderButtonsComponent. Punt 2: eenvoudiger opgelost dan gepland — i.p.v. losse If/Then/Else-logica op EventCurrent na te bouwen (waar Claude eerder 5x vastliep) is de hele losse AppBar-Row daar vervangen door een instantie van de gedeelde HeaderButtonsComponentWidget(), die de naam-logica al bevat. Het laag-risico restpunt (default-locatie 28666/28694 toont nog geen naam bij allereerste app-start) staat als bijvangst bij P2-7 hieronder. Uit deze lijst verwijderd.)

    *(P0-4 afgerond 2026-08-14 — live pair-sessie, Bob builder, bevestigd via verse export: favorieten_widget.dart Tab 3 ("Favoriete Gelegenheden") toont nu if (favorieteGelegenheidItem.isEmpty) { return Image.asset('assets/images/logo800px.png'); } vóór de ListView.builder gebouwd wordt — zelfde empty-state-patroon als P0-1/P1-1. Bob zet er later nog begeleidende tekst bij (cosmetisch restpunt, geen blocker). Correctie tijdens uitzoekwerk: Tab 1 ("Persoonlijke agenda") en Tab 2 ("Favoriete gemeenten") bleken bij inspectie nog helemaal niet gebouwd (kale placeholder-tekst, geen lijst) — die empty-state-vraag is dus alleen op Tab 3 van toepassing, zie P1-7 voor het bouwplan van tab 1/2. Bevestigd dat favorieten al syncen via Drupal bij inloggen (root cause gefixed 2026-08-09, zie P1-18) — Tab 3 haalt live data op via FavorietenAgendaCall. Uit deze lijst verwijderd.)*

    (P0-5 geschrapt 2026-08-25 — Bob's besluit: anonieme leestoegang voor browse-endpoints werkt al gewoon, bevestigd door talloze live tests zonder login (Home, horeca-overzicht, evenementpagina's) sinds het begin van dit project. Nooit een bevestigde bug geweest, alleen een terse voorzorgsregel uit de vroegste sessie-memo. Geen P0's meer open.) (Bijvangst afgerond 2026-08-14, live pair-sessie: de hardcoded Basic-Auth-header (Authorization: Basic Ym9iOnNlcmhpaQ==, decodeert naar bob:serhii, alleen nodig voor de devbob.uitgaanskrant.com-dev-omgeving) is nu uit alle 15 API-calls in api_calls.dart verwijderd via de Headers-tab per call — bevestigd via verse export: 0 treffers meer.)

    *(P0-7 afgerond 2026-08-13 avond — Bob, Drupal-kant: de kapotte flutterflowmobiel_establishment_info-Views-display (services_1) is gefixt, EstablishmentInfoCall geeft niet langer HTTP 500. Live bevestigd (Claude, emulator-5554, verse app-launch, nid 91142 = "De Beun"): titel, fotocarousel en HTML-inhoud tonen nu allemaal echte Drupal-data i.p.v. de title/content/kapot-plaatje-placeholders. Uit deze lijst verwijderd. Dit legt meteen een nieuwe, dringende vervolgbug bloot — zie P0-8 hieronder: met echte data stroomt de Info/Links/Bezorgen-tabs breekt de pagina zichtbaar (RenderFlex-overflow

    • letterlijke "null"-tekst), iets wat met de eerdere lege/placeholder-data niet zichtbaar was.)*

    *(P0-8 grotendeels afgerond 2026-08-14 — live pair-sessie, Bob deed de builder-edits, Claude verifieerde elk veld met een verse export (zelfde werkwijze als de 2026-08-10-sessie). Alle 10 Info-tab/Links-tab-velden bevestigd correct (adres, plaats, telefoonnummer, email, kvk, website, menukaart, facebook, twitter, instagram — allemaal != null && != ''). Bezorgen-tab: bestellink/bezorgkosten/ minimaleorder ook bevestigd correct; thuisbezorgtbetaalopties werkt met een net iets andere maar functioneel prima variant ("Is Set" i.p.v. "Is Set and Not Empty" — laag risico, blijft zo staan). Onderweg 2x een per-ongeluk omgekeerde conditie (== '' i.p.v. != '') gevonden en gecorrigeerd op adres/kvk/website — zelfde soort verkeerde-operator-fout als hieronder bij Minor-1 blijvend openstaat. 3 restpunten bewust niet als launch-blocker behandeld (Bob's beslissing 2026-08-14) — verplaatst naar "Minor — non-blockers" hieronder: bezorgtijden (conditie hangt aan het verkeerde veld), afhaalopties + bezorgdin (nog geen conditie, wacht op Bob's uitzoekwerk hoe deze twee velden uit de API komen). Het losse "tikbare links"-verbeterpunt is verplaatst naar P1-23. Uit de P0-lijst verwijderd.)*

    *(P0-9 afgerond — bevestigd 2026-08-21 (Claude, sessie 42, curl): een concurrente sessie (waarschijnlijk Bob, commit d8bbba4) loste dit op via een app-side workaround i.p.v. de Drupal-display zelf te herstellen — HomeUitgaantabelKaartComponentWidget's eerste category-sectie op Home wijst nu naar displayid: 'services_3' i.p.v. het kapotte 'display_3' (lib/uitgaanspaginas/home/home_widget.dart:312). Geverifieerd: de oude display_3-call geeft nog steeds dezelfde kapotte-view-fout (Drupal-kant dus niet gerepareerd, dat blijft zo), maar services_3 geeft gewoon een geldige lijst events terug (curl .../flutterflowmobiel1.json?display_id=services_3 → HTTP 200, 25 items). Volgende stap: opnieuw live testen of P1-24/P1-25 hiermee ook verdwenen zijn — nog niet gedaan deze sessie, zie die twee taken. Uit deze lijst verwijderd.)*

    (P0-10 afgerond 2026-08-17 — Bob, builder: "Mijn Account"-knop in de drawer was onklikbaar op telefoons met on-screen 3-knops Android-navigatiebalk (root cause: laatste Row in drawer_component_widget.dart had geen bottom-inset-marge). Bob heeft zelf 48px bottom-padding op die rij gezet — bevestigd via verse export: EdgeInsetsDirectional.fromSTEB(0.0, 0.0, 0.0, 48.0) staat nu op regel 1279. SafeArea bleek dus niet nodig, gewone vaste padding volstond. Uit deze lijst verwijderd.)


    (Onderstaande taken komen uit de merkanalyse web-vs-app van 2026-08-30. Volledige onderbouwing met screenshots: Designbrug-rapport. Afvinkbare werklijst: Designbrug werklijst.)

    *(P0-11 afgerond 2026-09-04 — Bob, builder. Het drawer-menu had een SingleChildScrollView binnen een SingleChildScrollView (Column met "Scrollable" aan, twee niveaus genest), waardoor de binnenste oneindige hoogte kreeg en nooit scrollde. Binnenste Column op Scrollable UIT gezet; verse export bevestigt nog één SingleChildScrollView (regel 98). Stap 2 (Gemeente/Provincie samenvoegen tot één lijst) is bewust afgewezen — Bob 2026-09-04: "nee, dit is overzichtelijk, houden zo". Het menu blijft dus 21 items met twee gescheiden blokken. Uit deze lijst verwijderd.)*

    P0-12 · Eigenaar: Bob — grotendeels AL OPGELOST, alleen de tekstregel rest. Oorspronkelijk: twee identieke hartjes met verschillende betekenis op één scherm (header = gemeente favoriet, pagina = gelegenheid favoriet). Geverifieerd 2026-09-04 (Claude, code-only op een verse export): de gekozen fix — "het gemeentehartje uit de header halen en één keer bij de gemeentekeuze zetten" — is al doorgevoerd. header_buttons_component_widget.dart bevat geen Icons.favorite en geen favorieteGemeenteIds meer (alleen nog 2 FlutterFlowIconButtons: hamburger + terug); het gemeentehartje staat nu op select_state_drop_down_component_widget.dart:359-375, naast het label "Toevoegen favoriet". Git bevestigt het pad: toegevoegd aan de header in d8bbba4 (P1-26), er weer uit in de export-commit 98d46ad. De versie mét hartje is bewaard als het component HeaderButtonsComponentCopyMetHartje (lib/components/header_buttons_component_copymethartje_*) — nergens meer geïmporteerd, dus vrij om weg te gooien, zie P2-7.

    Wat nog rest (klein, wording is Bob's keuze):

    1. Het label bij het hartje heet nu "Toevoegen favoriet" — generiek. "Gemeente volgen" maakt het onderscheid met "bewaren" van een gelegenheid meteen duidelijk.
    2. De regel die de site wél gebruikt ontbreekt nog volledig in de app: dat je die gemeente donderdags in de agendamail krijgt. Geverifieerd met grep: geen enkele treffer op "agendamail" / "donderdag" in lib/. Dat verband is dus nergens zichtbaar. *(Claude heeft de homepage van uitgaanskrant.com opgehaald en doorzocht op "agendamail"/"donderdag"/"nieuwsbrief" — 0 treffers, de zin staat blijkbaar op een dieper liggende pagina of achter login. Bob moet de exacte zin aanleveren, die verzint Claude niet.)*

    Exacte plek en stand (Claude, 2026-09-04, verse export): lib/selecteer_plaats/select_state_drop_down_component/select_state_drop_down_component_widget.dart — de Row op regel 337, direct ónder de gemeente-dropdown, met twee kinderen: een Text (regel 341, i18n-sleutel nosgkxy0 = "Toevoegen favoriet") en een Builder (regel 358) die op FFAppState().favorieteGemeenteIds.contains(gemeenteSelectId) wisselt tussen een gevuld en een outline hartje. Beide takken doen een drupalRequest POST naar /nl/flutterdrup/favorieten/flag.json resp. unflag.json met {"entity_id": <gemeenteSelectId>, "entity_type": "taxonomy_term"}. Het component zit op de pagina Selectprovinciegemeente.

    Twee dingen die daarbij opvielen en niet in de oorspronkelijke taakomschrijving stonden:

    • Het label is statisch. De Text staat búiten de Builder, dus er staat óók "Toevoegen favoriet" wanneer de gemeente al favoriet ís en het hartje bij tikken juist verwijdert. Dat is precies het soort dubbelzinnigheid dat P0-12 wilde oplossen. Wil je het echt netjes: het label mee laten wisselen ("Gemeente volgen" / "Je volgt deze gemeente"), wat betekent dat de Text de Builder in moet.
    • Er staat geen enkele Visibility-conditie op die Row. Hij is dus ook zichtbaar vóórdat er een gemeente gekozen is (gemeenteSelectId nog leeg) en zonder ingelogd te zijn — een tik verstuurt dan een flag.json-POST met een lege entity_id en lege sessiewaarden. Nog niet live nagespeeld, dus niet bevestigd wat de server dan doet.

    P0-13 · AFGEROND 2026-09-09 (Claude + Bob, sessie 71). Favorieten-hartje werkt "gewoon" als je uitgelogd bent, maar de server krijgt niets — de app liegt dan tegen de gebruiker.

    Gevonden tijdens P0-12's restpunt-onderzoek, code-geverifieerd op een verse export. Alle drie de plekken waar een hartje favorieten/flag.json of unflag.json aanroept missen een login-guard:

    • select_state_drop_down_component_widget.dart (gemeente-hartje, de Row op regel ~337 — de plek uit P0-12; heeft überhaupt géén Visibility-conditie)
    • horecagelegenheidoverzicht_kaart_widget.dart (hartje op de overzichtskaart)
    • horecagelegenheid_current_widget.dart (hartje op de detailpagina)

    In alle drie staat FFAppState().userSessionid alléén als argument van drupalRequest, nergens als conditie (grep -n "userSessionid" geeft per bestand uitsluitend de regels ín de call). Uitgelogd is die waarde '', dus er gaat een Cookie: = mee, Drupal ziet een anonieme bezoeker en de POST doet niets. De widget-code loopt daarna onvoorwaardelijk door naar addToFavorieteGemeenteIds / addToFavorieteHorecaNids — het hartje wordt dus gevuld en blijft dat ook na een herstart (de lijst is persisted), terwijl de Favorieten-pagina zijn data van de server haalt en de zaak daar níét toont.

    Twee dingen die de audit daarbij hard maakte:

    • De twee horeca-hartjes zetten de lokale state vóór de netwerkcall (addToFavorieteHorecaNids(widget!.nid!); safeSetState(...); await actions.drupalRequest(...)), het gemeente-hartje erna. Functioneel hetzelfde resultaat, maar bij de horeca-hartjes kan zelfs een expliciete serverfout de state niet meer tegenhouden.
    • Nergens in de levende code wordt de returnwaarde van een flag/unflag gecontroleerd. Alle acht actions.drupalRequest(-aanroepen in favorieten_widget.dart, de twee horeca-widgets en select_state_drop_down_component_widget.dart zijn nagelopen; de enige if (getJsonField(...)) in de buurt (horecagelegenheid_current, regel ~438) is de Visibility-conditie van de titel-Text, niet van de knop. drupal_request.dart geeft bij elke fout [] terug (regels 143/145/150), dus de informatie is er wel — er wordt alleen niets mee gedaan.

    Correctie op de oude P0-12-omschrijving: daar stond dat een tik zonder gekozen gemeente een POST met een lege entity_id stuurt. Dat klopt niet — _gemeenteSelectId heeft in app_state.dart een default van '28694', dus er gaat altijd een geldige tid mee. Het echte gat is uitsluitend het uitgelogde geval.

    Aanpak, en waar Claude's deel ophoudt:

    1. Gemeente-hartje: Visibility → Conditional op de Row, conditie FFAppState().userSessionid Is Set and Not Empty. AFGEROND 2026-09-08 (Claude, builder). Verse export bevestigt de guard op select_state_drop_down_component_widget.dart:337: if (FFAppState().userSessionid != null && FFAppState().userSessionid != '') direct vóór de Row. Een diff tegen de vorige export toont exact één gewijzigd bestand, dus er is niets anders meegekomen.
    2. De twee horeca-hartjes zijn een ontwerpbesluit voor Bob. BESLIST EN AFGEROND 2026-09-08/09. Bob's keuze: "die horeca-hartjes moeten niet op overzichtspagina's. Die favorieten alleen op de pagina van de establishment zelf, anders wordt het te rommelig." Dus:
      • HorecagelegenheidoverzichtKaart — hartje volledig verwijderd (Bob, builder). Geverifieerd: 0 verwijzingen naar favorieteHorecaNids of flag.json in dat bestand.
      • horecagelegenheidCurrent — hartje blijft, maar met login-guard (Claude, builder). Verse export toont if (FFAppState().userSessionid != null && FFAppState().userSessionid != '') rond de Align > AlignedTooltip op regel ~336. dart analyze 0 errors.

    P0-13 is hiermee volledig afgerond.

    Het gebruikte recept (bewaard omdat punt 2 hetzelfde patroon nodig heeft als Bob voor "verbergen" kiest; werkte in één keer, geen enkele vastloper):

    1. Open component SelectStateDropDownComponent (?tab=widgetTree&component=SelectStateDropDownComponent).
    2. Selecteer in de Widget Tree de Row die Text-"Toevoegen favoriet" en de ConditionalBuilder met de twee IconButtons bevat (pad: root Container > Column > Row, direct ónder DropDownGemeente).
    3. Rechterpaneel → sectie Visibility → toggle Conditional aan.
    4. Set from Variable → bron App State → userSessionid → operator "Is Set and Not Empty" → Confirm.
    5. Verifiëren met een verse export: er hoort if (FFAppState().userSessionid != null && FFAppState().userSessionid != '') (of FlutterFlow's equivalent) rond de Row op regel ~337 te staan.

    ⚠️ Let op de bekende valkuil uit CLAUDE.md: de Conditional-toggle uitzetten wist de conditie onherstelbaar, en een aangezette toggle zónder conditie genereert géén guard maar laat de widget gewoon altijd zien. Verifieer dus met de export, niet met de UI.

    Minor — non-blockers (launch mag hier niet op wachten)

    Bob's expliciete categorie (2026-08-14): kleine restpunten uit P0-8 die de livegang niet blokkeren, later oppakken.

    (Minor-1 afgerond 2026-08-16 — Bob, builder, live pair-fix sessie: de "bezorgtijden"-Visibility-conditie op HorecagelegenheidCurrent (Bezorgen-tab) bindt nu aan JSON Path $[:].bezorgtijden i.p.v. het verkeerde Openingstijden-veld. Bevestigd via verse export. Uit deze lijst verwijderd.)

    *(Minor-2 afgerond 2026-09-04 — Bob, builder, met Claude's diagnose. Waren twee bugs. (1) Het JSON Path $[:].bezorgdin bestond niet: de respons heeft bezorgtin (met een t), afgeleid van het views-label Bezorgtin op field_hor_bez_bezorgd_in_1 — dit veld toonde daardoor op elke horecagelegenheid de tekst "null", niet alleen bij lege data. (2) afhaalopties miste zijn Visibility-guard. Beide gefixt, plus een derde vondst: afhaalopties, bezorgtin én thuisbezorgtbetaalopties zijn alle drie lists (bevestigd op nid 157070, Lotus Enkhuizen: ["Afhaal","Bezorgen"]), en .toString() op een Dart-list geeft blokhaken — de app toonde [Afhaal, Bezorgen]. Opgelost met de nieuwe custom function lijstAlsTekst(dynamic items) (join met ', ', plat geschreven zonder closures i.v.m. FlutterFlow's parser), toegepast op alle drie. Bevestigd via verse export. Uit deze lijst verwijderd.)*

    ⚠️ Herbruikbaar: lijstAlsTekst staat nu in custom_functions.dart en is de standaardoplossing zodra een JSON-lijstveld als platte tekst getoond moet worden. Gecontroleerd dat er verder geen kale lijst-naar- tekst-plekken meer zijn: categorie (11 plekken) gaat overal correct via .map<String>((e) => e.toString()) in een List.generate, en cryptocoins/fotoos bestaan alleen in api_calls.dart zonder tekstweergave.

    Drupal dingen — verzamellijst, batchen bij Bob's eigen Drupal-sessie

    Eigenaar: Bob. Bob's voorkeur (2026-08-13): kleine builder-fixes die toch al wachten op ander Drupal-werk hier verzamelen i.p.v. los oppakken — dan in één builder-bezoek samen met de Drupal-kant afhandelen.

    • Hartje-icoon op de horecagelegenheid-detailpaginaafgerond (2026-08-16, Bob, builder, live pair-fix sessie). If-tak van de ConditionalBuilder rond IconButtonFavoriet toont nu Icons.favorite (gevuld) met witte Fill Color, zelfde stijl als de Else-tak. Bevestigd via verse export. Geen Drupal-afhankelijkheid nodig gebleken — puur cosmetisch, meegepakt tijdens een toch al geplande sessie op deze pagina.
    • P1-7 Tab 3 "Favoriete Gelegenheden"afgerond (2026-08-14, Claude, builder), zie P1-7 hieronder. Was hier vermeld als Drupal-geblokkeerd; bleek stale en is dezelfde sessie alsnog gloednieuw gebouwd (nooit door Bob opgepakt) — geen actie meer nodig.

    P1 — snel na livegang

    *(P1-23 afgerond 2026-08-16 — Bob, builder, live pair-fix sessie: website, menukaart, facebook en bestellink op HorecagelegenheidCurrent hebben nu allemaal een tap-actie (launchURL(getJsonField(..., r'''$[:].<veld>''')), direct via de Text-widget's eigen Actions-tab — geen InkWell-wrap nodig gebleken). Bijvangst: twitter/instagram (niet in de oorspronkelijke scope, zelfde patroon) zijn tegelijk meegepakt. Bevestigd via verse export (5 launchURL-aanroepen). Alle 4+2 velden hadden al de "Is Set and Not Empty"-guard uit P0-8, dus geen los crash-risico meer zoals bij het oorspronkelijke P1-15. Nog geen visuele link-styling (kleur/ underline) — puur cosmetisch restpunt, functioneel al klaar. Uit deze lijst verwijderd.)*

    (P1-18 afgerond 2026-08-10 avond — Bob, builder + Custom Code, in twee stappen. Root cause was een dubbele bug: (1) login_widget.dart behandelde het drupalLogin-resultaat als bool i.p.v. een JSON-map → crashte altijd; (2) de eerste conditie-fix checkte alleen "is $.success aanwezig" i.p.v. de waarde, wat altijd waar was (het veld zat er sowieso in, bij succes én bij falen). Uiteindelijke oplossing — op Bob's eigen voorstel — was simpeler dan een aparte Custom Function: drupal_login.dart retourneert nu null bij een mislukte/foute login i.p.v. een {success: false, ...}-map, en de knop's conditie is teruggebracht tot een simpele, echte null-check (_model.resultDrupalLogin != null, operator "Is Set" op de hele actie-output, geen JSON Path meer nodig). Bevestigd via verse export. Het losse debug-dialoogje (AlertDialog "melding" met de rauwe JSON) is blijven staan — niet meegenomen in deze fix, kan later nog opgeruimd worden als cosmetische bijvangst. Uit deze lijst verwijderd.)

    *(Nieuwe, andere login-crash gevonden + afgerond 2026-08-17 avond — Claude, builder + live logcat-diagnose op emulator-5554. Dit was géén regressie van P1-18 hierboven, maar een nog niet eerder gevonden tweede bug op dezelfde knop: ná een geslaagde drupalLogin-aanroep haalde de knop's "Inloggen"-actie nogmaals en overbodig de sessievelden uit het resultaat via losse JSON-Path-bindingen ($.user.uid, $.user.name, $.user.mail) — maar drupalLogin retourneert die velden plat (uid/name/mail, geen user-object), dus alle drie gaven null. Voor de 2 String-velden onschuldig (null.toString() → tekst "null"), maar userUid is int-getypeerd in App State → een null daarin toekennen crashte de app stil (geen rode foutmelding, gewoon geen "Succesvol ingelogd!"-melding en geen reactie op de knop) vóórdat de succes-snackbar ooit getoond kon worden. Root cause was sowieso overbodige code: drupalLogin (lib/custom_code/actions/drupal_login.dart) zet deze velden al zelf correct en veilig in App State (met int.tryParse(...) ?? 0 voor uid) vóórdat het resultaat teruggegeven wordt. Fix: de 6 redundante "Update App State"-acties op de knop's on-tap-flow (Action Flow Editor) volledig verwijderd — de knop doet nu alleen nog drupalLogin aanroepen en op basis van resultDrupalLogin != null de juiste snackbar tonen. Bevestigd via verse export (login_widget.dart bevat de getJsonField(..., $.user.*)-blokken niet meer) en live op emulator-5554 (inloggen met gebruikersnaam geeft nu de "Succesvol ingelogd!"-snackbar). Bijvangst, zelfde diagnose: inloggen met e-mailadres i.p.v. gebruikersnaam geeft terecht "Inloggen mislukt" — Drupal's user/login.json accepteert kennelijk geen e-mailadres als username. Geen bug, maar wel een open feature-vraag aan Bob als e-mail-login gewenst is.)*

    *(P1-1 volledig afgerond 2026-08-10 avond — Bob, builder, alle 4 sub-punten bevestigd via verse export: HomeUitgaanSliderComponent (= P0-1), PUitgaanSliderComponent, EvenementComponent, horecagelegenheidCurrent tonen nu allemaal if (<lijst>.isEmpty) { return Image.asset('assets/images/logo800px.png'); } vóór hun Carousel gebouwd wordt. Uit deze lijst verwijderd. Vervolgaudit 2026-08-13 (Claude): dit loste de lege-líjst-crash op, maar dekt niet per se een leeg imageUrl-veld op een individueel item binnen een wél-gevulde lijst (ander sub-geval van hetzelfde CachedNetworkImage-patroon uit CLAUDE.md) — daarom alle 13 live CachedNetworkImage-aanroepen in het project nagelopen op precies dát: 9 hebben een if (... != null && ... != '')-guard (of valueOrDefault, wat het risico verlegt naar een kapot-plaatje-icoon i.p.v. een crash — zie P0-7/P1-6), en de resterende 4 gebruiken allemaal getJsonField(item, r'''$.logo''').toString() — bij een ontbrekend veld levert .toString() op null de string "null" op (4 tekens), nooit een lege string, dus geen synchrone CachedNetworkImage-crash (wel een kapot plaatje, cosmetisch). Van die 4 zijn er 3 op HorecagelegenheidEventTabelComponentCopy (al bekend kapot via P1-11, geen nieuwe info) en 1 op het bevestigd dode/orphan UitgaantabelKaartComponentWidget (P2-7, niet live bereikbaar). Geen nieuwe crash-risico's gevonden — deze deelvraag is hiermee afgesloten, niet opnieuw op te pakken.)*

    (P1-3 afgerond 2026-08-10 avond — Bob, builder, bevestigd via verse export: Flexible(child: Text(functions.kortDatum(widget!.datum))) staat nu op "Text-datum" in PUitgaanSliderKaartComponent. Uit deze lijst verwijderd.)

    (P1-4 afgerond 2026-08-14 — live pair-sessie, Bob builder: horcat- parameter op HorecagelegenheidoverzichtCall (voorheen "EstablishmentsCall") kreeg Default Value '17967' (categorie "Uitgaansgelegenheden"). Bevestigd via verse export: String? horcat = '17967' i.p.v. de crashende horcat ??= null!;. Uit deze lijst verwijderd.)

    P1-5 · Eigenaar: Bob (Drupal-werk vereist; geen quick win). "Thuis bezorgen" in het menu (drawer_component_widget.dart, de ListTile met Icons.delivery_dining, ±regel 767) heeft geen onTap — bevestigd dode knop. Uitgezocht 2026-09-04 (Claude, live curl):

    • De data bestáát wel. Op nid 157070 (Lotus, Enkhuizen) zijn alle bezorgvelden gevuld, en afhaalopties bevat letterlijk de waarde "Bezorgen" (vocabulaire thuisbezorgen_afhaalbezorgen, waarden "Afhaal"/"Bezorgen"). Dát is het leverbaar-veld waar deze taak om vraagt — er hoeft geen nieuw veld bij. Bob bevestigt (2026-09-04): afhaalopties is in Drupal een multi-select met o.a. de opties "bezorgen" en "afhalen" — de views-filter is dus een simpele term-match op die ene waarde.
    • Maar het is niet op te halen met de bestaande call. flutterflowmobiel_establishments heeft maar één display (services_1; services_2/3 bestaan niet) en geeft per zaak alleen nid, titel, adres, plaats, logo, categorie terug — geen bezorg-info, en er is geen exposed filter om op te filteren. De call kent alleen horcat, townid, display_id, page.
    • townid is verplicht: zonder townid geeft élke horcat [].
    • Bekende horcat-waarden (gemeten op Arnhem, townid 25434): 17963 podia/theater/zaalverhuur, 17967 uitgaansgelegenheden, 17968 eten (klein), 17969 bioscoop/entertainment, 34 eten (breed, 20 zaken), 17965 leeg.

    Dus de keuze is een Drupal-keuze, en die moet vóór het app-werk: (a) een extra display op flutterflowmobiel_establishments met een filter op field_hor_bez_afhaalbezorgen = "Bezorgen" (schoonst; de app krijgt dan een lijst zoals de bestaande overzichten en de menuknop kan er gewoon naartoe navigeren), of (b) afhaalopties toevoegen aan de bestaande display en in de app filteren (meer data over de lijn, en de paginering klopt dan niet meer met wat je toont — afgeraden).

    Eerst meten of het de moeite is: tel hoeveel zaken daadwerkelijk bezorgen vóór je de view bouwt — drush @<alias> sqlq "SELECT COUNT(DISTINCT entity_id) FROM field_data_field_hor_bez_afhaalbezorgen" (veldnaam verifiëren in de veldenlijst). In een steekproef van 59 gelegenheden mét evenementen was élk bezorgveld leeg — dat waren poppodia en cafés, dus geen representatieve steekproef, maar het maakt wel de vraag scherp of dit een handvol zaken betreft of honderden.

    (P1-6 restpunt vervallen 2026-09-04 — Bob. De drie Bezorgen-tab-velden kregen bij het binden automatisch een Default Variable Value met de veldnaam erin (valueOrDefault<String>(..., 'afhaalopties') e.d.), maar het veld is in de builder niet leeg te maken én de tekst bereikt de gebruiker nooit: de != null-conditie ervoor blokkeert de widget al bij lege data. Geen actie nodig.)

    *(P1-6 grotendeels afgerond 2026-08-16 t/m 2026-08-18 — letterlijke veldnaam i.p.v. nette placeholder bij ontbrekende data (valueOrDefault<String>(<bron>, '<default>') waarbij de default gelijk is aan de veld-/functienaam). Twee binding-typen, elk een eigen fix-route: Component-Parameter-gebonden velden via "Default Variable Value" (8 velden: horecagelegenheidoverzicht_kaart_widget.dart titel/adres/plaats, evenement_info_widget.dart adres/plaats/entreeprijs/entreetoelichting/contact); JSON-Path-gebonden velden via Visibility → Conditional-guard, "Is Set"/"Is Set and Not Empty" (herbruikbaar recept: widget → Visibility → Conditional aan → Conditions → Single Condition → First Value → Set Variable → EstablishmentInfo/Evenement Response → schakel bij een vastlopende "Predefined Path" over op "JSON Path" en typ het pad handmatig (bv. $[:].bezorgtijden) — werkte altijd; bij een leeg/onklikbaar "Single Condition"-suboptie-veld: typ eerst "Single" in de zoekbalk bovenaan de dialoog vóór je op "Conditions" klikt, zie CLAUDE.md). Alle live/prioriteit-pagina's afgerond: evenement_horecagelegenheid_widget.dart (11 velden, commits 4fbcb0d/14f2f14, + de vergeten establishmentnid-debugwidget verwijderd), horecagelegenheid_current_widget.dart (3 velden, commit 14f2f14), event_current_widget.dart (title/date, commit 82b0fb6). Alles geverifieerd via verse export + flutter analyze (0 errors). Uit deze lijst verwijderd.)* (Correctie 2026-08-19, live pair-fix sessie: het genoemde 8e veld — horecagelegenheidoverzicht_kaart_widget.dart titel/adres/plaats — stond hierboven al als "afgerond" vermeld, maar titel bleek bij verse export nog steeds de kale ' titel'-placeholder te tonen (zelfde "leek opgeslagen, was het niet"-patroon als elders in CLAUDE.md). Bob heeft titel alsnog op Default Value 'Titel onbekend' gezet (consistent met de al langer werkende adres/plaats'Adres onbekend'/'Plaats onbekend'). Nu pas écht bevestigd via verse export voor alle 3 velden.)

    Resterend, bewust laag-prioriteit (geen "— bezig" claim):

    • evenement_component_widget.dart (title/eventDate/adres/plaats, regels 221/243/318/340) — alleen bereikbaar via de bevestigd dode orphan-route EventWidget//event. Definitief niet oppakken (2026-08-25, Bob's besluit bij P2-7): EventWidget blijkt zelfs niet meer bewerkbaar in de builder (elke wijziging geeft een "Invalid Action"-crash-preventie en wordt teruggedraaid) — met rust gelaten, dus deze 4 velden blijven zo. JSON-paths ter referentie mocht dit ooit alsnog relevant worden: title $[0].title, eventDate → EvenementCall.eventDate ($[0].datum), adres → EvenementCall. eventAdres ($[0].adres), plaats → EvenementCall.eventPlaats ($[0].plaats). (p_uitgaan_slider_kaart_component_widget.dart's 'def'-fallback afgerond 2026-08-19 — Claude, builder: Visibility → Conditional op Text-datum (Single Condition, First Value = rauwe component- parameter datum, operator "Is Set and Not Empty") toegevoegd. Bevestigd via verse export + flutter analyze (0 nieuwe treffers): if (widget!.datum != null && widget!.datum != '') staat nu om de hele Flexible(child: Text(...)), dus de 'def'-placeholder is onbereikbaar geworden. Uit deze lijst verwijderd.)
    • Niet-verdachte gevallen bewust genegeerd (echte inhoudelijke fallback, geen technische naam): 'Evenement' (kaart_tabel_uitgaan_comp_widget.dart:216, kaart_tabel_uitgaan_s_comp_widget.dart:123) en 'Uitgaan' (tag_categorie_component_widget.dart:113).

    *(P1-15 volledig afgerond — bevestigd 2026-08-15, Claude, na een lokale git-sync-achterstand van 12 bestanden ingehaald (zie sessienotitie bovenaan): behalve de al bekende 5 social-/contact-knoppen op evenement_horecagelegenheid_widget.dart blijken ook de laatste 3 open restpunten uit de bredere audit inmiddels gefixt in de builder (door Bob, nooit apart gemeld/gecommit) — Menukaart-knop (evenement_horecagelegenheid_widget.dart:626-635) heeft nu de if (... != null && ... != '')-guard, Website-knop op evenement_info_widget.dart:262 (gebruikt op EventCurrent) en Website-knop op evenement_component_widget.dart:408 (orphan EventWidget) hebben beide nu ook een guard om de InkWell. Geverifieerd via een verse flutterflow export-code + git diff (commit 2c093bc). Nog steeds geen verklaring gevonden voor de losstaande "Op/vanaf de Home-pagina zelf"-crash-log uit 2026-08-07 (zie de oude aantekening hieronder) — de reproductie op 2026-08-15 (zie sessienotitie bovenaan, herhaalde Invalid argument(s): No host specified in URI direct bij Home's eerste launch, vóór enige tap) laat zien dat dit ondanks alle bovenstaande fixes nog steeds optreedt — dus een andere, nog niet gevonden bron. Uit deze lijst verwijderd als losse P1-taak; de resterende mysterie-crash staat nu apart als P1-24 hieronder.)*

    • (Oude beschrijving voor de context, root cause gold voor de 5+1 hierboven bevestigde knoppen:) Kapotte zichtbaarheids-conditie op de social-/contact-knoppen liet de app crashen bij tikken (niet alleen lelijke tekst) — if (valueOrDefault<String>(<veld>, '<placeholder>') != null && ... != '') is altijd waar omdat valueOrDefault nooit null/'' teruggeeft, dus onTap riep launchURL() aan met de kale placeholder-string als URL (geen geldig schema/host → crash).

    *(P1-24 volledig afgesloten. Widget 1 (p_uitgaan_slider_kaart_component_widget.dart's ongeguarde Image.network) — afgerond 2026-08-21, sessie 43, Claude, builder + verse export bevestigd: ConditionalBuilder, IF logo != '' (Single Condition, First Value = rauwe logo-component-parameter, operator "Not Equal To", Second Value = letterlijke lege string — zie de CLAUDE.md-notitie over hoe je dat literal-tekstveld bereikt op een String/Image-Path-parameter). THEN = bestaande Image, ELSE = Icon image_not_supported. Widget 2 (home_uitgaantabel_kaart_component_widget.dart's JSON-Path-guard, type "Json", biedt geen "Is Set and Not Empty"-operator en geen literal-Second-Value — zie de uitgebreide, hieronder gearchiveerde poging-documentatie voor het volledige waarom) — Bob's besluit (sessie 44, 2026-08-21): geaccepteerd zoals-ie is, geen verdere actie (logo is in Drupal een verplicht veld, kan in de praktijk niet leeg zijn; en zelfs als dat toch gebeurt vangt Flutter's Image Resource Service de exceptie zelf op — een fallback-icoon, geen app-crash, geen harde noodzaak). Bijvangst sessie 43 (2e blok, zelfde dag): buiten deze sessie om (vermoedelijk een losse builder-poging door Bob, niet gecommit) was de werkende != null-conditie op widget 2 op enig moment gewijzigd naar een kale if (getJsonField(...)) zonder vergelijking — compileert (dynamic omzeilt de statische check) maar crasht bij runtime (type String is not a subtype of type bool) zodra $.logo een string is, dus bij vrijwel elk event mét logo. Gevonden bij een routine-export-check, gemeld, en op Bob's akkoord teruggezet naar de werkende Is Set-conditie (Single Condition, First Value = JSON Path $.logo, operator "Is Set" — genereert weer != null). Bevestigd via verse export + flutter analyze (0 errors). Uit deze lijst verwijderd.)*

    *(⚠️ Gearchiveerde poging-documentatie (2026-08-21, sessie 42, ~45 min, op widget 2) — bewaard voor het geval een toekomstige FlutterFlow- update deze operator-beperking oplost. Het probleem: First Value gebonden via JSON Path ($.logo, type "Json") — voor dat type biedt de Single-Condition-operator-lijst alleen Equal To/Not Equal To/Is Set/Is Not Set, geen "Is Set and Not Empty". Geprobeerd en allemaal doodgelopen:

    • Een 2e conditie toevoegen (AND, $.logo != '') — de First Value liet zich wel op $.logo binden, maar de Second Value-picker bood op dat moment geen manier om een letterlijke lege string in te vullen (alleen een variabele-bronkiezer, geen "literal"-optie zichtbaar). Correctie 2026-08-21 sessie 43: dit bleek specifiek voor Json- typed First Values te gelden — bij een directe String/Image-Path- component-parameter (zie widget 1 hierboven) bestaat er wél een literal-tekstveld, alleen verstopt achter een paar extra kliks (zie de CLAUDE.md-notitie).
    • First Value omzetten naar String via "Predefined Path" i.p.v. "JSON Path" — evenementenItem heeft geen gekoppeld Custom Data Type/schema (raw JSON-loop-item via Generate Dynamic Children), dus "Predefined Path" toont domweg geen enkel veld om te kiezen.
    • Een "empty"/"length"-transform zoeken onder de JSON-Path-binding's eigen 2e "Available Options"-dropdown — alleen "To Data Type" (wijst naar custom models) en "No Further Changes" beschikbaar.
    • Onderweg 2x per ongeluk een 2e conditie laten hangen/de pagina laten herladen na een vastgelopen geneste "Set Variable"-dialoog — geen schade, elke keer teruggezet naar de oorspronkelijke werkende conditie.)*

    *(P1-25 afgerond 2026-08-21, sessie 43 — Claude, code-audit + builder, bevestigd via verse export. Root cause gevonden zonder live emulator nodig te hebben (dus het emulator-crash-risico uit de vorige sessie was hier niet relevant): in de Column op home_uitgaantabel_kaart_component_widget.dart:359 hadden 4 van de 5 tekstregels al maxLines: 1/overflow: ellipsisde plaats-Text (toen regel ~549-581) was de enige zonder, en kon bij een lange plaatsnaam over meerdere regels wrappen, waardoor de vaste rijen samen de vaste kaarthoogte (160-280px afhankelijk van breakpoint) overschreden. Fix: maxLines: 1 gezet via de "Search properties"- zoekbalk (→ "max lines"), zelfde guard-patroon als de buurvelden. Bevestigd via verse export: maxLines: 1 staat nu op de plaats-Text. Geen expliciete overflow: ellipsis gezet — dat veld ("Text Overflow Replacement") bleek buiten de klikbare viewport te renderen (bekende clipping, zie CLAUDE.md), maar maxLines: 1 alleen volstaat al om de RenderFlex-overflow te voorkomen (voorkomt de layout-overschrijding, ook zonder ellipsis- teken). Uit deze lijst verwijderd.)*

    (P1-16 afgerond 2026-08-25/26 — Bob, builder: Firebase gekoppeld, Crashlytics + Performance Monitoring aangezet (Cloud Messaging/push bewust uitgelaten, zie P2-10). Bevestigd via verse export: firebase_core/firebase_crashlytics/firebase_performance staan nu in pubspec.yaml, Firebase-init in main.dart. Uit deze lijst verwijderd.)

    P1-30 · Eigenaar: Bob — klaar, aanzetten bij livegang. Rate limiting op Cloudflare voor de API-paden. De regel is 2026-09-04 aangemaakt maar bewust op Disabled gezet (Bob: "is voor de live gang dit, nu niet"). Bij livegang alleen nog op Active zetten.

    • Regel: URI Path starts_with /nl/flutterdrup or /en/flutterdrup, 300 requests per 1 minuut, gekenmerkt op IP. Niet lager zetten — mobiele gebruikers delen IP's achter CGNAT.
    • ⚠️ Blokkeert nu nog: de bestaande custom rule flutterflow staat op execution order First met actie Skip en heeft "All rate limiting rules" (én "Rate limiting rules (Previous version)") aangevinkt, op exact dezelfde twee paden. Custom rules draaien vóór rate limiting rules, dus zolang die vinkjes aanstaan slaat élk API-request de limiet over en doet deze regel niets. Fix bij het aanzetten: alleen die twee vinkjes weghalen, de overige skips (managed rules, Super Bot Fight Mode, Browser Integrity Check) laten staan — die zijn er juist om de eigen app niet te hinderen.
    • Firebase App Check blijft bewust overgeslagen: dat beschermt alleen Firebase-diensten, en de backend hier is Drupal — zou extra custom Drupal-code vergen om het token te verifiëren. Pas overwegen als misbruik een gemeten probleem wordt. Schrijf-acties zitten al achter sessie+CSRF; lees-endpoints zijn bewust publiek.

    P1-17 · Eigenaar: Bob — nog 46 velden, alleen de twee aanmaak-formulieren. (Zie de sessie-71-update direct hieronder; de oorspronkelijke analyse staat daaronder.)

    Stand na sessie 71 (2026-09-08, Claude, geverifieerd via verse export)

    De hele publieksgerichte UI is nu Engels. 22 vertalingen doorgezet en bevestigd in een verse export:

    • drawerComponent (14) — het hoofdmenu, dat elke gebruiker ziet: Activiteiten→Activities, Cultuur→Culture, Films→Movies, Jeugd→Kids, Horeca→Venues, Provincie→Province, Thuis bezorgen→Home delivery, Uitgaan→Go Out, Mijn Account→My account (elk van de vijf categorie-items bestaat 2x: een Gem- en een Pro--variant, beide gedaan). Zoek stad→"Search City", Favorieten→"Favorites" en Gemeente→"Municipality" bleken al goed te staan.
    • horecagelegenheidCurrent (3): Bezorgen→Delivery, Alle horecagelegenheden→All venues, Home→Home (was leeg).
    • SelectStateDropDownComponent (2): Toevoegen favoriet→ "Add to favorites", Toepassen→Apply.
    • favorieten (3): Wachtwoord wijzigen→Change password, Account verwijderen→Delete account, stadsactiviteit aanmaken→ "Create city activity".

    Woordkeuzes die consistent zijn doorgevoerd en het naslaan waard zijn bij vervolgwerk: horeca(gelegenheden) = "venues", jeugd = "kids", films = "movies", uitgaan = "Go Out" (die stond al zo op één item, dus overgenomen i.p.v. "Going out").

    Wat er nog ligt — 46 velden, uitsluitend stadsactiviteitAanmaken (27) en uitgaansevenementAanmaken (19): de twee aanmaak-formulieren, die alleen een ingelogde stadseditor ziet. Formuliervelden als Wanneer/Datum/Datum eind/Waar/Adres/Plaats/Organisator/Entree/ Entreeprijs/Logo kiezen/Foto's kiezen/Activiteit indienen. De twee formulieren delen vrijwel dezelfde veldnamen, dus het is 2x dezelfde woordenlijst. Verder rest alleen nog Bezorgen op EvenementHorecagelegenheid (1 tabblad-label; de widget heeft geen naam waarop het tree-zoekveld filtert, daarom overgeslagen) en '15 euro' op EvenementComponent (een hint-placeholder, geen echte UI-tekst).

    Route die werkt is veranderd — lees dit vóór je verdergaat. Het Settings → Languages-paneel is voor Claude's browser-automation maar beperkt bruikbaar: die lijst scrollt niet (muiswiel op 3 posities, Page Down, Tab-navigatie en drag allemaal geprobeerd), waardoor alleen de bovenste ~14 secties bereikbaar zijn — en drawerComponent en de andere componenten zitten dieper. Wat wél betrouwbaar werkte is de per-widget-route via het globe-icoontje naast het Text-veld in het rechterpaneel; het exacte recept staat nu in CLAUDE.md. Voor Bob in zijn eigen browser blijft het Languages-paneel waarschijnlijk gewoon de snelste route voor deze 46 velden, juist omdat het per pagina-sectie gegroepeerd is.


    Oorspronkelijke analyse (2026-08-07 t/m 2026-08-18): was: Bob (resterende 108 vertalingen, handmatig — zie controlelijst-artifact hieronder; de 7 gekoppelde-bug-sleutels en de eerste 6 zijn al gedaan).** Live bevestigd 2026-08-07 (Claude, emulator met systeemtaal en-US, verse app-data): de app valt terug op Engels zodra het toestel niet op Nederlands staat (geen opgeslagen taalvoorkeur → MaterialApp.locale is null → Flutter matcht het systeem-en-US tegen de ondersteunde ['nl','en']-lijst en kiest en). Dat zou op zich geen probleem zijn als er een echte Engelse vertaling stond — die is er vrijwel niet: van de 169 sleutels in lib/flutter_flow/internationalization.dart zijn er 50 waar nl/en toevallig identiek zijn (onvertaald), 44 waar beide talen leeg zijn (dus zichtbaar niets, zoals de Login-hinttekst uit P0-6), en slechts 1 sleutel met een bewust andere Engelse tekst — en die ene is fout: key 7pacsyhe (het hartje-menu-item in de drawer) toont nl: 'Favorieten' maar en: 'Home', dus een Engelstalig toestel ziet twee menu-items met de tekst "Home" (huis-icoon én hartje-icoon) — verwarrend, de favorieten zijn zo niet te vinden. (Dit specifieke veld — key 7pacsyhe/ListTileFavorieten — is 2026-08-17 door Bob gefixt in de builder: Engelse vertaling staat nu ook op "Favorieten", bevestigd via verse export. De rest van P1-17 hieronder — de overige ontbrekende Engelse vertalingen — blijft open.) Iedere gebruiker met een Engelstalig toestel (niet ongebruikelijk, ook onder Nederlanders) krijgt dus een merkbaar kapotte/lege interface. 2026-08-09, Bob: Engels moet blijven aangeboden (niet weghalen als taal) — de eerder voorgestelde "forceer hard op nl"-fix (Engels uit FFLocalizations.languages() verwijderen) is dus afgewezen, niet uitgevoerd. Resterende optie is de grotere, aparte contentklus: de ontbrekende Engelse vertalingen daadwerkelijk invullen (of een losse taalkeuze-instelling bouwen i.p.v. op systeemlocale te vertrouwen). Nog niet opgepakt. Live herbevestigd 2026-08-09 (Claude, emulator): root cause klopt exact zoals hierboven beschreven — op een device met ro.product.locale=en-US verdween de "Wachtwoord vergeten?"-link op Login volledig (lege en-vertaling voor key utppw2si); na adb shell cmd locale set-app-locales com.uitgaanskrant.app --locales nl-NL (betrouwbaardere per-app-locale-override dan de systeembrede settings put system system_locales + broadcast, die op de emulator niet altijd doorwerkte zonder reboot) kwam de tekst meteen terug — bevestigt dat dit puur een vertaalprobleem is, geen widget-bug.

    2026-08-18, Claude: gestart met invullen (Settings → Languages-tabel in de builder, per-pagina-secties met inline Dutch/English-velden — sneller en betrouwbaarder dan het widget-canvas-rechterpaneel voor platte teksten). 6 velden afgerond en bevestigd via verse export, geen probleem: login-pagina d507d3b9(E-mailadres, was al goed), t8h5f8ir(Wachtwoord, was al goed), gkx4luhw("Inloggen"→"Log in"), utppw2si("Wachtwoord vergeten?"→"Forgot password?"), bokhk9vk("Home"→"Home"). Let op — "Translate All"/"Translate Page" (automatische Google Translate-knoppen bovenaan elke sectie) zijn een betaalde Growth/Business-plan-feature (Upgrade-paywall) — niet aangeklikt, niet iets om zelf te doen zonder Bob's akkoord.

    ⚠️ Bevestigde FlutterFlow-backend-bug (niet builder-UI-gerelateerd, 2026-08-18, Claude, uitgebreid geverifieerd met tussentijdse verse exports): 7 losse AlignedTooltip-teksten op 7 verschillende widgets/pagina's blijken op serverniveau aan elkaar gekoppeld — het bewerken van de Engelse vertaling van ÉÉN ervan overschrijft alle 7 tegelijk met dezelfde waarde. Bevestigd via zowel het globale Settings → Languages-paneel als het widget-specifieke vertaalpaneel (rechtsklik-paneel → Message/Text-veld → globe-icoontje ernaast, opent een los "Setting Translations"-paneel met Dutch/English

    • een per-veld "Google Translate"-knop) — beide ingangen hebben exact dezelfde koppeling, dus dit zit in FlutterFlow's datamodel, niet in een specifieke UI-flow. De 7 betrokken sleutels (allemaal AlignedTooltip, dus laag-zichtbaar — alleen bij long-press):
    • pj545url (login, "Veld wissen") — moet zijn: "Clear field"
    • ahiklq0c (login, "Wachtwoord tonen/verbergen") — moet zijn: "Show/hide password"
    • kjqv6ycx (horecagelegenheidCurrent, "Favoriet") — moet zijn: "Favorite" — enige van de 7 die nu toevallig correct staat
    • z63o5kzf (EventCurrent, "Delen") — moet zijn: "Share"
    • vmbtb6ah (drawerComponent, "Terug") — moet zijn: "Back"
    • f8ay39rg (HeaderButtonsComponent, "Terug") — moet zijn: "Back"
    • ujd0p09x (HorecagelegenheidoverzichtKaart, "MessageFavoriet...") — nl-tekst zelf ziet er al als vergeten debug-tekst uit, apart navragen bij Bob wat dit hoort te zijn (mogelijk gewoon leeg laten) Huidige staat: alle 7 staan op "Favorite" (laatste testwaarde, enige bereikbare gedeelde staat — niet "beter" te maken via de builder-UI, elke volgende edit blijft alle 7 tegelijk overschrijven). Impact laag (tooltips, geen primaire UI-tekst), dus geen launch-blocker, maar wel een echte content-fout die ergens moet worden opgelost. Aanbevolen vervolgstappen (niet meer via Claude-browserautomatisering proberen, al 2 ingangen geprobeerd): Bob probeert het zelf in zijn eigen browser (kan anders gedragen), of meld dit als bug bij FlutterFlow-support, of verwijder+herbouw één van de 7 tooltip-widgets (breekt vermoedelijk de koppeling doordat er dan een nieuwe key gegenereerd wordt).

    Resterende 108 velden: Bob's beslissing 2026-08-18, handmatig oppakken (browser-automatisering is hiervoor te traag/risicovol gezien de bug hierboven — Bob doet dit zelf, "deze week"). Claude leverde een complete, per-pagina-gegroepeerde controlelijst met een vertaalvoorstel per veld als artifact: https://claude.ai/code/artifact/d195d2d6-392b-4fbb-afd2-0f2d92ce9609 ("P1-17: Engelse vertalingen" — ook terug te vinden via Artifacts op claude.ai mocht de link kwijtraken). Route in de builder: Settings → Languages, per pagina-sectie de Engelse kolom invullen (bevestigd betrouwbaar voor platte tekst, i.t.t. het widget-canvas-rechterpaneel). Niet aanraken: de "Translate All"/ "Translate Page"-knoppen (betaalde Growth/Business-upgrade) en de 7 gekoppelde sleutels hierboven (apart traject).

    P1-47 · GEEN regressie — jouw eigen besluit. Eigenaar: Bob, alleen als je van gedachten verandert. Gevonden 2026-09-12 met een browserloze audit op ongebruikte component-parameters; de audit klopt feitelijk, maar de conclusie "onbedoeld neveneffect" is onjuist en is 2026-09-12 gecorrigeerd door de sessie die commit 7094b43 schreef.

    Waarom het geen regressie is — drie stukken bewijs:

    1. Je hebt er zelf om gevraagd, in de sessie van 2026-09-10: "die horeca-hartjes moet niet op overzicht pagina's. Die favorieten alleen op de pagina van de establishment zelf. Anders wordt het te rommelig", en later diezelfde sessie: "hartje van horecagelegenheidsoverzicht gehaald".
    2. Een paginahernoeming kán dit bestand niet raken. De kaartcomponent HorecagelegenheidoverzichtKaart is nooit hernoemd of verplaatst; alleen de twee pagina's zijn dat. Een rename raakt geen gedeeld component.
    3. Het bestand stond bij de start van die sessie niet in git status; het verscheen pas ná flutterflow export-code. De wijziging kwam dus uit het FlutterFlow-project — jouw builder-werk dat nog niet geëxporteerd was — en niet uit een bewerking van Claude.

    Wat wél terecht is aan de melding: de commit-boodschap van 7094b43 noemt die 105 verwijderde regels niet, terwijl ze wel in die commit zitten. Dat is een tekortkoming van die boodschap; de wijziging zelf is gewoon jouw keuze die meeliftte op de eerstvolgende export.

    Wil je het hartje tóch terug op het overzicht (en dus je besluit van 2026-09-10 terugdraaien), dan staat het herstelrecept hieronder — dat blijft bruikbaar. Zo niet: laat staan en streep deze taak weg.

    Wat er mis is: HorecagelegenheidoverzichtKaartWidget krijgt van alle drie zijn levende aanroepers (horecagelegenhedenOverzichtCurrent, Favorieten tab 2, mijnProfiel) netjes een nid mee, maar leest die nergens meer — het hele hartje-blok is weg. grep -n "widget!\.nid" op lib/horecagelegenhedenoverzicht/horecagelegenheidoverzicht_kaart/horecagelegenheidoverzicht_kaart_widget.dart geeft nul treffers; het enige Icon( dat er nog staat is de image_not_supported-fallback van het logo. Gevolg: op het horeca-overzicht kun je niets meer favoriet maken — alleen nog op de detailpagina (horecagelegenheid_current_widget.dart heeft zijn hartje wél nog).

    Wanneer: commit 7094b43 (2026-09-11 16:18) droeg de wijziging over uit de export. Dat de nid-binding bij alle drie de aanroepers nog staat, is geen bewijs van onbedoeldheid — FlutterFlow laat een parameter gewoon staan als je alleen het widget dat 'm gebruikte weghaalt. Wil je het netjes: die binding kan bij een opschoonronde weg, maar hij doet geen kwaad.

    De oude configuratie staat nog in git (git show 7094b43^:lib/horecagelegenhedenoverzicht/horecagelegenheidoverzicht_kaart/horecagelegenheidoverzicht_kaart_widget.dart), dus hij hoeft niet opnieuw bedacht te worden — alleen opnieuw geklikt: AlignedTooltipBuilder met FFAppState().favorieteHorecaNids.contains(widget!.nid); if-tak FlutterFlowIconButton (buttonSize 32, fillColor: Colors.white, icon Icons.favorite, kleur primary, size 24) → removeFromFavorieteHorecaNids; else-tak idem maar Icons.favorite_borderaddToFavorieteHorecaNids. Zie ook het List Contains Item-recept in CLAUDE.md.

    Let op bij het herstellen: de detailpagina doet ook een Drupal-sync (P1-7 restpunt A). Controleer of de overzichtskaart die óók had, of alleen de lokale App-State-lijst bijwerkte.

    Twee bijvangsten van dezelfde audit, allebei lager geprioriteerd:

    • SliderUitgaanComponentSmallCurrent is een wees — geen enkele aanroeper in lib/, en zijn parameters townid/displayid worden genegeerd (hij roept HomeSliderCall.call() zonder argumenten aan). Kandidaat voor P2-7's opruimlijst; Claude gooit niets weg.
    • EvenementInfoWidget krijgt 6 parameters die het niet leest (nid, title, date, logo, fotoos, categories) — feitelijk juist, het widget leest er maar 6 van de 12 (website, entreetoelichting, entreeprijs, contact, plaats, adres). ✅ Nagemeten 2026-09-12: er ontbreekt geen inhoud. De ouderpagina event_current_widget.dart rendert fotoos (16 treffers), categorie (13) en logo (5) zélf, bóven de tabbalk. De infotab hoeft ze dus niet te tonen. Dit is dode doorgeefwaarde, geen ontbrekende content — hooguit iets voor een opschoonronde, geen taak.

    P1-7 · Eigenaar: Claude.

    ⚠️ Correctie 2026-09-13: het hartje op de overzichtskaart dat hieronder als "volledig werkend" staat, bestaat niet meer — het is gesneuveld toen Copy3 de productiepagina werd (commit 7094b43, taak 18). Zie bevinding B bovenaan dit bestand voor het bewijs en de keuze die er ligt. De rest van P1-7 hieronder klopt nog wel.

    "Favorieten pagina maken" (Bob's naam voor deze taak, 2026-08-13 — was P0-4, samengevoegd met het oude P1-7 favorieten/profielscherm-werk + P2-8 gemeente-favoriet). Bob bouwt de Drupal-kant (views/endpoints) zelf, apart; Claude/deze sessie kan de lokale/UI-kant + look&feel oppakken zolang die niet op Drupal wacht.

    Al afgerond (2026-08-13, geverifieerd via verse export):

    • App State-velden favorieteGemeenteIds en favorieteHorecaNids (beide List<String>, Persisted: true) bestaan nu in lib/app_state.dart.
    • Hartje-toggle op HorecagelegenheidoverzichtKaart (overzichtskaart) volledig werkend: ConditionalBuilder op FFAppState().favorieteHorecaNids.contains(widget!.nid) (ingesteld via "Set from Variable" → Available Options → List Contains Item, zie CLAUDE.md) — If-tak toont gevuld hartje + removeFromFavorieteHorecaNids, Else-tak toont outline hartje + addToFavorieteHorecaNids. Logica én styling (witte Fill Color op beide takken) bevestigd correct.
    • Hartje-toggle op HorecagelegenheidCurrent (detailpagina): zelfde patroon, Remove/Add-acties functioneel correct. Cosmetisch restpunt (icoon/kleur If-tak nog niet aangepast) verplaatst naar de "Drupal dingen"-lijst hierboven — Bob wil dit batchen met een latere Drupal-sessie.
    • Tab 3 "Favoriete Gelegenheden" — volledig gebouwd en werkend (2026-08-14, Claude, builder, bevestigd via verse export + flutter analyze). Gebruikt FavorietenAgendaCall (1 sessie- geauthenticeerde call, geen client-side nid-lijst nodig). Bouwstappen (herbruikbaar patroon, zie ook de nieuwe "Generate Dynamic Children"- sectie in CLAUDE.md):
      1. ListView ingevoegd op Tab 3's lege Column (Insert Widget → Layout Elements → ListView), placeholder-Text verwijderd.
      2. Backend Query (database-icoon) → API Call → FavorietenAgenda toegevoegd aan de ListView.
      3. ListTile als item-template ingevoegd in de ListView.
      4. Generate Dynamic Children (4e icoon in de widget-iconenrij, tooltip bevestigt de naam) op de ListView aangezet: Variable Name favorieteGelegenheidItem, Value → Set from Variable → FavorietenAgenda Response → Available Options "No Further Changes" (rauwe JSON-array, geen predefined-path-extractie) → Save → bevestigingsdialoog ("generate its children dynamically... edit the first child to set how every child should look") → Ok. Canvas toont meteen 4 herhaalde design-time placeholder-rijen.
      5. ListTile's Title opnieuw gebonden (was per ongeluk nog aan de hele API-respons gebonden i.p.v. het loop-item): Set from Variable → bron favorieteGelegenheidItem (nieuw beschikbaar ná stap 4) → Available Options "JSON Path"$.node_title.
      6. Subtitle leeggemaakt (triple-click veld → Delete) — geen bruikbaar 2e veld in deze call, anders bleef de letterlijke placeholder-tekst "Subtitle" op elke rij staan (zelfde P1-6-patroon).
      7. On Tap-actie (Actions-tab, 2e icoon — ja, deze tab bestaat wel, zie de gecorrigeerde P1-9-notitie): Navigate To → horecagelegenheidCurrent → Parameters → Pass (niet "Define" — dat opent per ongeluk de doelpagina's eigen parameterschema-editor) → parameter nid → Value → Set from Variable → bron favorieteGelegenheidItem → JSON Path $.nid. Gegenereerde code geverifieerd (ListView.builder + getJsonField(favorieteGelegenheidItemItem, r'''$.node_title''') + context.pushNamed(HorecagelegenheidCurrentWidget.routeName, queryParameters: {'nid': ...$.nid...})), flutter analyze toont geen nieuwe treffers voor favorieten_widget.dart. Bekend restpunt, geen blocker: geen leading-logo-afbeelding (ListTile's "Leading"-sectie biedt alleen een Icon-property, geen Image-slot — zou een aparte widget-vervanging vergen). (Lege-lijst-state inmiddels ook gefixt — zie het voormalige P0-4 hierboven, zelfde live pair-sessie.)

    (TabPersAgenda's eigen "On Tap"-drupalRequest (regel 252-253) afgerond 2026-08-20 — Bob, builder: method-argument van 'POST' naar 'GET', zelfde fix als eerder al op de On-Page-Load-trigger. Bevestigd via verse export: beide drupalRequest-aanroepen in favorieten_widget.dart (regel 44 en 253) staan nu op 'GET'.)

    *(Snelkeuzelijst favoriete gemeenten — volledig afgerond, sessie 43, bevestigd via verse export + flutter analyze (0 errors) + live check op emulator-5556 (profile mode, geen crash, dropdowns/Toepassen renderen correct met een lege favorietenlijst).** Op Bob's expliciete verzoek (niet op Favorieten-Tab 2, maar direct op de "Select gemeente/provincie"-pagina, lib/selecteer_plaats/selectprovinciegemeente/, component SelectStateDropDownComponent): meteen bij binnenkomst je favoriete gemeenten als snelkeuze zien. Bob's keuzes: tik op een favoriet = direct naar Home met die gemeente actief; lijst staat onderaan, ná de dropdowns/"Toepassen"-knop. Bouwstappen (herbruikbaar patroon):

    1. Custom action getFavorieteGemeenten(List<String>? favorieteGemeenteIds) -> List<GemeenteModelStruct> (lib/custom_code/actions/get_favoriete_gemeenten.dart, commit c6c3439) — geen "alle gemeenten van heel NL"-Drupal-endpoint beschikbaar, dus deze haalt ProvinciesCall, loopt alle provincies langs met GemeentenCall, en verzamelt de gemeenten waarvan gemeenteid in favorieteGemeenteIds voorkomt.
    2. Local Component State Variable favorieteGemeentenLijst (List<Data(GemeenteModel)>) op SelectStateDropDownComponent.
    3. Twee acties toegevoegd aan het einde van de bestaande "On Initialization"-flow (ná de 16 bestaande acties, via de Action Flow Editor's "+"-connector onder het laatste blok — niet het compacte Actions-paneel, dat toont geen "volgende actie toevoegen"-knop meer na de eerste actie): Custom Action getFavorieteGemeenten (argument = App State favorieteGemeenteIds) → Update Component State (favorieteGemeentenLijst = Action Outputs-resultaat).
    4. Onder de bestaande "Toepassen"-knop: Wrap ingevoegd (rechtsklik Column → Insert Widget → koos per ongeluk eerst ListView; Replace Widget naar Wrap behoudt eventuele latere Generate-Dynamic- Children-config, zie CLAUDE.md) → Generate Dynamic Children (4e icoontje) met Variable Name favorieteGemeenteItem, Value = Component State favorieteGemeentenLijst, "No Further Changes".
    5. Kind-Container + Text toegevoegd; Text's waarde via Set from Variable → favorieteGemeenteItem → Available Options "Data Structure Field"gemeentename (dit type, een Data-typed loop- item i.p.v. een raw JSON-item, biedt wél een directe veld-picker — geen JSON Path nodig).
    6. Container's On Tap (Actions-tab, 2e icoontje): Update App State (gemeenteSelectId/gemeenteSelectNaam, elk Set Value → Data Structure Field gemeenteid/gemeentename op hetzelfde loop-item) → Navigate To Home (Allow Back Navigation aan, zelfde patroon als de bestaande "Toepassen"-knop: context.pushNamed). Gegenereerde code geverifieerd: correcte Wrap(children: List.generate(...)) met InkWell.onTap die FFAppState().gemeenteSelectId/gemeenteSelectNaam zet en context.pushNamed(HomeWidget.routeName) aanroept. Bewust niet meegenomen: provincieSelectId/provincieSelectName zetten — GemeenteModelStruct bevat geen provincie-veld (alleen gemeentename/gemeenteid), en de bestaande gemeente-dropdown's eigen onChanged zet die twee provincie-velden ook al niet, dus dit blijft consistent met het bestaande gedrag. Nog niet visueel gestyled (kale Container zonder chip-vormgeving/padding/afronding — puur functioneel getest) — cosmetische afwerking (padding, pill-vorm, spacing tussen chips) is een kleine, losse vervolgtaak als Bob dat wil.)*
    7. Tab 1 "Persoonlijke agenda" — bleek al gebouwd (niet door deze lijst gedekt, TASKS.md liep achter), en kreeg 2026-08-17 avond 2 bugfixes (Claude, builder, bevestigd via verse export + flutter analyze + live op emulator-5554):
      1. De data-fetch (drupalRequest POST naar .../favorieten_agenda.json) hing aan de TabBar's onTap op de Tab-node zelf (zie CLAUDE.md) — die vuurt alléén bij een echte tik, dus nooit voor de al-actieve standaardtab bij page-load. Fix: Scaffold's eigen "On Page Load"-trigger toegevoegd met dezelfde actieketen (via rechtsklik op de bestaande TabPersAgenda-Tab-node → Actions → "Copy Action Chain" → nieuwe On-Page-Load-trigger → "Paste Action(s)") — let op de CLAUDE.md-notitie over de dubbele-variabele-valkuil die dit trucje veroorzaakt, dat moest ik zelf nog corrigeren.
      2. Los daarvan crashte de pagina stil (NoSuchMethodError: Map heeft geen 'toList') zodra de call een niet-2xx-respons kreeg, omdat de custom action drupalRequest (lib/custom_code/actions/drupal_request.dart) bij een fout een {'success': false, ...}-Map teruggaf terwijl de aanroepende code altijd blind .toList() erop aanriep. Fix: alle 3 foutpaden in drupal_request.dart (non-2xx, timeout, exception) geven nu [] terug i.p.v. een map — bevestigd via verse export. Live bevestigd: favorieten_agenda.json geeft momenteel nog een 404 terug (Bob kijkt hier zelf naar, zie boven) — de tab blijft nu netjes leeg i.p.v. te crashen.

    *(De 404 hierboven is afgerond 2026-08-19 — Bob, Drupal-kant, samen met Claude gedebugd via live curl-vergelijkingen productie/devbob + Drupal watchdog-log + custom.module-broncode. Root cause: de favorieten_agenda-resource (Services-module, gedefinieerd in custom_services_resources()) stond nog niet aangevinkt als ingeschakelde resource voor het flutterdrup-endpoint op productie — identieke module-code op beide omgevingen, maar de resource-activatie zelf is een los, niet-code-gebonden config/database-item per endpoint (zie de nieuwe CLAUDE.md-notitie voor het volledige, herbruikbare debug-recept). Fix: resource aangevinkt op https://uitgaanskrant.com/en/admin/structure/services/list/flutterdrup/resources. Tab 1 "Persoonlijke agenda" haalt nu live data op. Uit deze lijst verwijderd.)*

    *(Vervolg-bugs op Tab 1, zelfde 404-fix-sessie, afgerond 2026-08-19 — Bob + Claude samen, live pair-fix + verse-export-verificatie. Ná de server-side 404-fix werkte de pagina via curl maar nog niet via de app zelf — twee losse client-side bugs bleken de resterende oorzaak:

    1. drupalRequest-aanroepen gebruikten 'POST', maar Drupal Services' 'index'-operatie beantwoordt alleen GET (POST → 404, ook al staat de resource nu wel geactiveerd). Op de Scaffold's "On Page Load"-trigger method gewijzigd naar 'GET' (Claude, builder, bevestigd via verse export).
    2. JSON Path-bug op alle 9 UitgaantabelKaartWidget-parameters (logo, categorie, titel, horecagelegenheid, adres, plaats, datum, nid, horecagelegenheidNid): gebonden met r'''$[:].veld''' (array-wildcard-syntax, alleen geldig op een hele array) i.p.v. r'''$.veld''' (correct voor een los loop-item) — gaf overal null terug, met een Null check operator used on a null value-crash op titel tot gevolg (widget!.titel!.toString() in uitgaantabel_kaart_widget.dart). Bob heeft alle 9 velden zelf in de builder gecorrigeerd, bevestigd via verse export.
    3. Losstaande null-crash op inhoud (zelfde widget, widget!.inhoud!.toString()): UitgaantabelKaartWidget is een gedeeld component, en Favorieten Tab 1 geeft bewust inhoud: null mee (geen equivalent veld in deze API-respons). "Default Variable Value" bleek hier geen werkende fix (inhoud is een JSON/ dynamic-getypeerde parameter, geen String — de default-waarde bleef na Confirm leeg/niet-opgeslagen, bevestigd via 2x verse export). Werkende fix: Wrap Widget → ConditionalBuilder op de Text-inhoud-node, conditie "inhoud is set" (First Value = rauwe inhoud-parameter, operator "Is Set", géén Second Value nodig), Else-tak leeg. Genereert if (widget!.inhoud != null) { return Text(widget!.inhoud!.toString(), ...); } — bevestigd via verse export. Dit component wordt op meerdere plekken gebruikt (o.a. HomeUitgaantabelKaartComponent) — de fix is op het gedeelde component zelf toegepast, dus geldt overal waar inhoud: null wordt meegegeven, zonder de plekken met een echte inhoud-waarde te raken. Lokale repo bijgewerkt via een verse flutterflow export-code in de projectmap + flutter analyze (geen nieuwe errors, alleen de gebruikelijke gegenereerde info/warnings).)*

    *(Categorie-crash-patch bevestigd toegepast (2026-08-25) — Bob deelde de live custom.favorites_agenda.inc-broncode in de chat: bevat exact de op 2026-08-20 afgesproken fix (GROUP_CONCAT-subquery per tak (go_out_event

    • activity) bouwt een <category>Naam</category>-XML-string op, _custom_parse_categories_to_array() zet 'm om naar een echte array vóór 'ie als categorie teruggaat). Dit lost de destijds gevonden NoSuchMethodError: Class 'String' has no instance method 'toList'-crash op (UitgaantabelKaartWidget verwacht categorie als JSON-array, lib/uitgaanspaginas/uitgaantabel_kaart/uitgaantabel_kaart_widget.dart:219). Live bevestigd (2026-08-25, Bob): Tab 1 toont de categorieën nu correct, geen crash. Volledig afgerond, geen resterende actie.)*

    Scope-correctie (2026-08-25, op basis van de live Drupal-broncode die Bob deelde): Tab 1 "Persoonlijke agenda" filtert niet op gefavoriete gemeente(n) zoals eerder hieronder stond (zie de nu gecorrigeerde "Bob's beslissingen"-notitie) — de query in custom_favorites_agenda_data() heeft drie routes (matched_via): horeca (uitgaansevenementen van je gefavoriete horecagelegenheden), event (een gefavoriet uitgaansevenement zelf), activity (een gefavoriete stadsactiviteit zelf). Plaats/gemeente is puur een weergaveveld, geen filter. Geen verdere actie nodig — dit is al zo gebouwd, alleen de documentatie liep achter.

    *(Anonieme API-call-bug op Tab 3 afgerond 2026-08-19 — Claude, builder

    • verse-export-verificatie: FavorietenAgendaCall's Backend Query op de ListView had geen sessionName/sessionId gebonden, dus de call ging altijd zonder sessie naar Drupal (ListTile toonde letterlijk "null" i.p.v. de favoriete horeca-gelegenheden, ook ingelogd). Beide parameters nu gebonden via Set from Variable → FFAppState(). userSessionname/userSessionid. Bevestigd via verse export: FavorietenAgendaCall.call(sessionName: FFAppState().userSessionname, sessionId: FFAppState().userSessionid). Correctie (2026-08-25, sessie-check vóór het oppakken van deze "Nog open"-post): deze hele FavorietenAgendaCall/Backend-Query-route bestaat niet meer — Tab 3 is op 2026-08-24 volledig herbouwd (commit d35492a) op een drupalRequest-custom-action-aanroep naar favorieten_horeca.json, en díe bindt FFAppState().userSessionname/ userSessionid al gewoon correct (favorieten_widget.dart:293-294). Herbevestigd via een verse export vlak vóór deze notitie — geen builder-actie nodig, deze taak was stale.)*

    *(Tekst-overflow Tab 2 afgerond 2026-08-19 — Claude, builder: de Text-widget "De gemeentes die je hebt gemarkeerd..." (key o7pt2vls) had geen horizontale padding en liep tot de schermrand. Nu 16px links/rechts padding, bevestigd via verse export (Padding(EdgeInsetsDirectional.fromSTEB(16.0, 0.0, 16.0, 0.0))). Correctie op de oorspronkelijke melding: Tab 3 heeft sinds de 2026-08-14-herbouw (ListView + Generate Dynamic Children) geen eigen beschrijvingsregel meer — de "vergelijkbare tekst op Tab 3" bestond niet meer, dat deel van de melding was stale.)* *(Structureel gat + tab-label-afkapping afgerond 2026-08-19 — Claude, builder + live geverifieerd op emulator-5554. Favorieten had geen header/drawer (geen "☰"-menu, geen terugweg behalve Android's systeem-terugknop) en alle 4 tab-labels waren afgekapt op telefoonformaat. Fix: Scaffold-node in de widget tree kreeg een Drawer-slot (drawerComponent, zelfde als andere pagina's) en een AppBar-slot (HeaderButtonsComponentWidget(showBackButton: false) in een FlexibleSpaceBar-title, achtergrondkleur "Info", hoogte 80px, "Show Default Button" uit om een dubbele hamburger te voorkomen — identiek patroon aan PUitgaanPage). Widgets zoals Drawer/AppBar zijn zelf niet via de gewone rechtsklik-Insert-Widget-flow te vullen; werkende route: slepen vanuit het linker widget-paneel direct naar de canvas-dropzone (de AppBar/Drawer canvas toont bij een sleep-actie zelf de "Leading"/"Title"/"Actions"-zones) — zie ook de nieuwe CLAUDE.md-notitie. Tab-label-afkapping: TabBar kreeg "Tab Bar Scrollable" aan (Search properties → "scroll") — labels tonen nu volledig uitgeschreven en scrollen horizontaal i.p.v. af te kappen. Bevestigd via verse export + flutter analyze (0 nieuwe treffers) én live: drawer opent via het hamburger-icoon, geen dubbele knop, alle 4 tabs met volledig label bereikbaar. Uit deze lijst verwijderd.)* *(UX-gat grotendeels afgerond 2026-08-19 — Claude, builder (drawerComponent, gedeeld over alle pagina's): de drawer's "Mijn Account"-knop toont nu alleen nog het kale login-formulier als er géén actieve sessie is. Fix: Wrap Widget → ConditionalBuilder om de bestaande FFButtonWidget, conditie FFAppState().userSessionid "Is Not Set or Is Empty" (Then = ongewijzigde originele knop, geen regressie voor uitgelogde gebruikers — de meerderheid). Else-tak (ingelogd): nieuwe knop met tekst gebonden aan FFAppState().userName i.p.v. de statische "Mijn Account"-tekst, en onTap navigeert naar FavorietenWidget (i.p.v. terug naar Login) — dat dekt zowel "gebruikersnaam zichtbaar" als "link naar Favorieten" uit Bob's oorspronkelijke wens. Bevestigd via verse export + flutter analyze (0 nieuwe treffers); volledig live geverifieerd op emulator-5554 (bleek een nog actieve testsessie "bobcity" op het toestel te staan) — zowel de uitgelogde staat (ongewijzigd "Mijn Account"-gedrag) als de ingelogde staat (knop toont "bobcity", tik navigeert naar Favorieten) werken zoals bedoeld. Bijvangst tijdens dezelfde sessie: Tab 3 "Favoriete Gelegenheden" toont voor deze testgebruiker nog steeds "null" ook nu de sessie wél wordt meegestuurd (zie de eerdere sessie-parameter-fix hierboven) — dit is dus geen client-sidebug meer maar wijst op een Drupal-datakant-vraag (mogelijk heeft "bobcity" gewoon geen favoriete gelegenheden, of de endpoint zelf heeft nog een probleem) — niet verder onderzocht, buiten scope van deze taak. Bewust niet meegenomen: een eigen uitlog-knop in de drawer zelf — die bestaat al op Favorieten's "Gebruiker"-tab (P1-7), dubbel opbouwen leek overbodige scope-uitbreiding.)*

    • "Gebruiker"-tab, "Uitloggen"-knop afgerond (2026-08-15, Claude, builder, bevestigd via verse export + flutter analyze: 0 errors). favorieten_widget.dart heeft nu een 4e tab "Gebruiker" (via TabBar's "Active Tab"-dropdown → "+ Add Tab", zie CLAUDE.md voor het herbruikbare recept) met een ListTile "Uitloggen". On Tap: Action 1 = Update App State (6 velden op "Clear Value": userToken/userSessionid/userSessionname/userName/userUid/ userMail), Action 2 = Navigate To Login met "Allow Back Navigation" uit (genereert context.goNamed(...) i.p.v. pushNamed, dus geen terugknop-pad naar de net-verlaten sessie). Nog open, zelfde tab: geen gebruikersnaam zichtbaar (zie UX-gat hierboven), "Wachtwoord wijzigen" (zie Bob's beslissing 3 hieronder) — niet meegenomen, eigen vervolgtaak. "Account verwijderen" volledig afgerond (2026-08-24, Bob + Claude samen). Bob wil geen volledige in-app-flow bouwen; gekozen aanpak: account direct blokkeren (status=0, kan niet meer inloggen) + na 30 dagen automatisch hard verwijderen via cron, i.p.v. direct verwijderen. Twee Drupal-ingangen, beide roepen dezelfde kernfunctie _custom_account_delete_request() aan (custom.account_delete.inc, nieuw bestand naast custom.module):
      • Services-resource (POST .../nl/flutterdrup/accountdelete/delete_request.json, sessie- Cookie + X-CSRF-Token-header verplicht — token op te halen via .../nl/flutterdrup/user/token.json met dezelfde sessie-cookie, géén args nodig, lege JSON-body {} volstaat). Resource-naam is accountdelete, niet account — die eerste naam botste stil met iets in Services (resource verscheen niet in de Bronnen-lijst, geen foutmelding) tot 'm hernoemd werd. Live succesvol getest via curl op devbob.
      • Webpagina /account-verwijderen (Drupal hook_menu(), confirm_form(), alleen bereikbaar ingelogd — normale Drupal-login, geen app-token nodig, werkt dus ook los van de app) — live bevestigd werkend door Bob.
      • FlutterFlow-app-kant: nieuwe ListTile "Account verwijderen" op de Gebruiker-tab (gedupliceerd van de Uitloggen-ListTile, zelfde styling), On Tap → Launch URL (actie staat onder categorie "Share", niet "Navigation" — makkelijk te missen) → https://uitgaanskrant.com/nl/account-verwijderen. Bevestigd via verse export (launchURL(...) correct gegenereerd) + flutter analyze + flutter build apk (geen nieuwe errors). Gecommit 55b8dd6. Dekt Apple's App Store Review Guideline 5.1.1(v) (in-app-pad naar accountverwijdering, een link-out naar een simpele webpagina volstaat volgens de richtlijn).
      • Cron-sweep (custom_account_delete_cron(), via custom_cronapi() — Ultimate Cron, dagelijks) ruimt na 30 dagen automatisch op — nog niet live getest (logisch, kost 30 dagen om te reproduceren; code-review + de handmatige call-flow zijn het enige dat hier geverifieerd is). Enige resterende twijfel over deze hele feature — verder geen actie nodig tenzij Bob over ~30 dagen wil controleren dat de sweep echt draait. *(Restpunt A — horeca-hartjes Drupal-sync — volledig afgerond 2026-08-24/25, Claude, builder + verse-export-verificatie + live smoke-test op emulator-5554 (build/launch zonder exceptions).** Beide widgets (horecagelegenheidoverzicht_kaart_widget.dart, horecagelegenheid_current_widget.dart) roepen nu bij add/remove ook actions.drupalRequest('POST', '.../favorieten/flag.json' resp. unflag.json', FFAppState().userSessionname, userSessionid, userToken, functions.favorietenBodyNode(widget!.nid!)!) — zelfde patroon als het al werkende gemeente-hartje. Nieuw hulpmiddel: een letterlijke JSON- body met een ingesloten variabele ({"entity_id": <nid>, "entity_type": "node"}) bleek via de Custom-Action-argumentenpanel niet direct te bouwen (geen concatenatie-optie op een kaal String-argument, alleen een volledige literal-of-variabele-vervanging) — opgelost met een nieuwe kleine Custom Function favorietenBodyNode(String nid) -> String (lib/flutter_flow/custom_functions.dart, retourneert de body-string via Dart-stringconcatenatie), die vervolgens gewoon als waardebron voor het body-argument gekozen kan worden (Set Variable-dialoog → Custom Functions). Herbruikbaar patroon voor een volgend geval van "JSON-body met 1 variabele" — zie ook de bijgewerkte "Geneste Set-Variable-dialoog"-notitie hierboven. Onderweg 2x de bekende bevroren-geneste-dialoog-freeze geraakt (beide keren opgelost met een page-reload, geen dataverlies — de al bevestigde argumenten bleven staan). Uit deze lijst verwijderd.)* *(Restpunt B — "Wachtwoord wijzigen"-link — volledig afgerond 2026-08-24/25, Claude, builder + verse-export-verificatie.** Nieuwe ListTile "Wachtwoord wijzigen" op de Gebruiker-tab (gedupliceerd van "Uitloggen"), On Tap → Navigate To → wachtwoordVergeten-pagina (Allow Back Navigation aan, geen parameters). Gebruikt de al bestaande lib/wachtwoord_vergeten/-pagina/RequestNewPasswordCall, geen nieuw Drupal-werk nodig. Uit deze lijst verwijderd.)*

    Bob's beslissingen (2026-08-09, nog steeds leidend):

    1. Favorieten (zowel horeca als gemeenten) worden Drupal-gesynchroniseerd, niet puur lokaal — sync bij app-start én direct na elke toevoegen/verwijderen-actie. Lokale opslag (FFAppState/ secureStorage) blijft daarnaast nodig als cache/snelle UI-state.
    2. Correctie (2026-08-25, live Drupal-broncode bevestigt dit): "Persoonlijke agenda" (tab 1) = evenementen/activiteiten van je gefavoriete horecagelegenheden + zelf gefavoriete uitgaansevenementen + zelf gefavoriete stadsactiviteiten (custom_favorites_agenda_data()'s drie matched_via-routes: horeca/event/activity) — niet gemeente-gebaseerd zoals hier eerder stond. Gemeente/plaats is alleen een weergaveveld op elk item, geen filter.
    3. "Gebruiker"-tab = het oude P1-7-scope (wachtwoord wijzigen, uitloggen, account verwijderen), nu als tab i.p.v. aparte pagina. Account verwijderen afgerond 2026-08-24 (zie de uitgewerkte notitie hierboven — blokkeert/vertraagt + cron, geen directe verwijdering, dekt Apple's App Store Review Guideline 5.1.1(v)). Alleen "Wachtwoord wijzigen" resteert nog van deze beslissing.

    *(P1-12 afgerond 2026-08-10 avond — Bob, builder, opgelost via een andere en betere route dan oorspronkelijk voorgesteld: i.p.v. een Default Value op establishmentID toe te voegen (die de crash slechts had gemaskeerd met een nep-waarde "0"), is de hele EvenementHorecagelegenheidWidget-instantie op EventCurrent nu achter een Visibility-conditie gezet die bindt aan de rauwe pagina-parameter horecaid zelf (operator "Is Set"), vóór een eventuele Default Value-substitutie — zelfde "bind aan de rauwe bron, niet aan de gesubstitueerde waarde"-principe als het bekende CachedNetworkImage/P1-15-patroon. Bevestigd via verse export: if (widget!.horecaid != null && widget!.horecaid != '') EvenementHorecagelegenheidWidget(... establishmentID: widget!.horecaid!, ...) — de force-unwrap is nu veilig, want alleen bereikbaar binnen de guard. Uit deze lijst verwijderd.)*

    (P1-26 volledig afgerond — Drupal-kant + FlutterFlow-kant, zie hieronder voor de volledige geschiedenis.) Nieuwe Drupal Services-resource favorieten (acties flag/unflag/is_flagged) om favorieten server-side generiek te maken — niet meer alleen horeca-nodes (bestaande hartjes op HorecagelegenheidoverzichtKaart/HorecagelegenheidCurrent gebruiken nog hun eigen, oudere aanpak), maar ook taxonomy terms (gemeenten), en ook de node-bundles club/club_event/photo_book. Dit is de directe blocker-oplossing voor P1-7 Tab 2 "Favoriete gemeenten" hierboven (die had nog geen enkel toggle-mechanisme voor taxonomy terms).

    Aanleiding: een collega had al een concept-custom_services_resources()

    • 3 callback-functies geschreven (flag/unflag/is_flagged via het Flag-module), werkte nog niet volledig. Chat-sessie 2026-08-20 heeft 'm gereviewd, gefixt en uitgebreid.

    Gevonden/gefixte bugs in het concept (Claude, code-review, niet zelf op de Drupal-server toegepast — Bob heeft geen git-toegang tot deze server-code, dus dit moet hij zelf plakken):

    1. Fataal: de concept-code definieerde een tweede function custom_services_resources() {...}, terwijl custom.module die al één keer heeft (met plaatsen + favorieten_agenda) — twee functies met dezelfde naam is een PHP fatal error, legt de hele site plat zodra dit cachet wordt. Moet gemerged worden tot ÉÉN functie.
    2. Entity-type was hardcoded tot alleen 'node' — geen taxonomy-support, terwijl dat nou net het doel is.
    3. Flag-namen waren geraden i.p.v. gecontroleerd. Bob's screenshot van admin/structure/flags gaf de echte namen: bookmarks (Flag type node, bundles: club, club_event, photo_book, horecagelegenheid, activity, go_out_event) en favorite_town (Flag type taxonomy_term, bundle town). Code aangepast om deze te gebruiken i.p.v. de placeholder bookmarks_taxonomy.
    4. Een hardcoded node-type-whitelist in de eigen code (los van de Flag-config) werd losgelaten — $flag->access() kent de toegestane bundles al uit de Flag's eigen "Entity bundles"-instelling, dus een losse handmatige lijst zou steeds opnieuw uit sync kunnen raken met wat Bob in de Flags-UI instelt.

    Deliverable (in de chat gegeven, nog NIET door Bob gedeployed/getest):

    • Nieuw bestand custom.favorites_flag.inc (zelfde opzet als het bestaande custom.favorites_agenda.inc): helper _custom_favorites_flag_name_for_entity_type() + de 3 callbacks _custom_favorites_flag(), _custom_favorites_unflag(), _custom_favorites_is_flagged(). Alle 3 accepteren entity_id + optioneel entity_type ('node' default of 'taxonomy_term'), forceren altijd de huidige ingelogde gebruiker (nooit een client-aangeleverde uid).
    • Twee toevoegingen aan het bestaande custom.module: (a) een module_load_include('inc', 'custom', 'custom.favorites_flag');-regel naast de bestaande includes, (b) een nieuwe 'favorieten'-entry (met 'actions' => array('flag' => ..., 'unflag' => ..., 'is_flagged' => ...)) toegevoegd aan de bestaande $resources-array in de al-aanwezige custom_services_resources() — niet een 2e functie aanmaken.
    • Curl-testcommando's gegeven voor alle 6 combinaties (flag/unflag/ is_flagged × node/taxonomy_term), POST naar .../favorieten/<actie>.json met sessie-cookie + X-CSRF-Token.

    Drupal-kant afgerond en bevestigd (2026-08-20, Bob deployed + Claude curl-geverifieerd op productie, na Bob's melding "resource nog niet ge-enabled" → alsnog aangevinkt): alle 6 combinaties (flag/unflag/ is_flagged × node/taxonomy_term) routeren nu correct — vóór de endpoint-activatie gaf elke aanroep een kale Drupal-HTML-404 (zelfde valkuil als favorieten_agenda destijds), ná activatie geeft elke aanroep de verwachte Services-JSON-403 voor een anonieme test-call:

    POST .../favorieten/flag.json      → 403 ["Access denied for user anonymous"]
    POST .../favorieten/unflag.json    → 403 ["Access denied for user anonymous"]
    POST .../favorieten/is_flagged.json → 403 ["Access denied for user anonymous"]
    

    Open vraag opgelost: is_flagged gebruikt POST, niet GET (een GET-aanroep bleef 404 geven, POST met dezelfde body gaf de verwachte 403) — belangrijk voor wie de FlutterFlow-kant bouwt.

    *(FlutterFlow-kant afgerond — commit d8bbba4 (concurrente sessie, waarschijnlijk Bob, nacht 2026-08-20/21), bevestigd in code 2026-08-21 door Claude/sessie 42: favoriet-hartje toegevoegd op HeaderButtonsComponent, naast de gemeente-/provincienaam (Bob's gekozen "Optie 1" voor P1-7 Tab 2 — hartje bij de huidige keuze, geen losse lijst-kiezer). header_buttons_component_widget.dart bevat nu beide POST-aanroepen (.../favorieten/unflag.json/.../favorieten/flag.json), zelfde ConditionalBuilder-patroon als de bestaande horeca-hartjes: If (favorieteGemeenteIds.contains(gemeenteSelectId)) → gevuld hartje + unflag + removeFromFavorieteGemeenteIds; Else → leeg hartje + flag + addToFavorieteGemeenteIds, JSON-body {"entity_id": <gemeenteSelectId>, "entity_type": "taxonomy_term"}, sessie-auth via userSessionname/userSessionid/userToken. Geen is_flagged-call gebruikt — zelfde lokale-lijst-aanpak als de bestaande horeca-hartjes (geen server-side sync-on-load), consistent maar niet per se toekomstbestendig als iemand op een 2e toestel inlogt. Nog niet gebouwd, mogelijk vervolgstap: Tab 2 "Favoriete gemeenten" op de Favorieten-pagina zelf toont nog steeds alleen de kale beschrijvingstekst, geen daadwerkelijke lijst van favorieteGemeenteIds (favorieten_widget.dart, o7pt2vls) — met Optie 1 als gekozen aanpak is dat mogelijk bewust (het hartje op HeaderButtonsComponent is de hele feature), maar dat is niet expliciet bevestigd door Bob. Uit deze lijst verwijderd als losse P1-taak.)*

    (P1-13 afgerond 2026-08-24 — Claude, sessie 46, builder + live device-run emulator-5556, profile mode. Root cause (kale Column + shrinkWrap: true/NeverScrollableScrollPhysics() op een MasonryGridView zonder begrensde ouder) bleek al gefixt op horecagelegenheden_overzicht_widget.dart (bijproduct van de P1-10-herbouw, commit d35492a) — dezelfde fix (Expansion=Expanded + Shrink Wrap uit + Scrollable aan op de StaggeredView-node, builder-native, geen custom code) nu ook toegepast op alle 6 tabs van de 2e variant horecagelegenheden_overzicht_provincie_page_widget.dart. Bevestigd via 2 losse verse exports (0 resterende shrinkWrap/NeverScrollableScrollPhysics-treffers) + flutter analyze (geen nieuwe errors) + een live app-launch op emulator-5556 (--route "/horecagelegenhedenOverzichtProvinciePage?plaats=28694", profile mode): Activiteiten-tab rendert en scrollt normaal, geen RenderFlex-overflow, geen exceptions in de log. Eerdere sessies liepen hierop vast door een bevestigd FlutterFlow-sync-probleem (edit leek opgeslagen, bereikte nooit de export) — dat trad deze keer niet meer op, mogelijk omdat de eerste succesvolle toepassing (Home/P1-10) het pad inmiddels vrijmaakte, of gewoon niet meer reproduceerbaar. Gecommit 59c6647. Uit deze lijst verwijderd.)

    *(P1-19 afgerond in de builder 2026-08-12 avond, maar pas op 2026-08-13 avond echt in deze repo gecommit — de destijds "bevestigd via verse export"-verificatie liep via een losse /tmp/ff-check-export, niet via deze projectrepo zelf; git log -S"return Wrap(" bevestigde 0 eerdere treffers vóór vandaag. Inhoud ongewijzigd: alle 9 kale-Row-met-List.generate-tag-overflows (rechtsklik Row → Replace → Wrap) — Home (HomeUitgaantabelKaartComponent), PUitgaanSliderKaartComponent, EventCurrent, EvenementHorecagelegenheid (4x), HorecagelegenheidCurrent (cryptocoins), PUitgaantabelKaartComponent. Uit deze lijst verwijderd.)*

    *(P1-20 geïmplementeerd — bevestigd via lokale git diff op 2026-08-13 avond, tijdens Bob's eigen gelijktijdige ff-run-fvm.sh-build/testronde. Gecommit 2026-08-14 (Claude, commit fb6b224) — stond 15+ uur ongecommit in de working tree, zie sessie-notitie bovenaan dit bestand. Exacte match met het eerder uitgewerkte voorstel: HeaderButtonsComponentWidget kreeg een showBackButton-parameter (Boolean, default true), de terug-AlignedTooltip-node zit nu achter if (widget!.showBackButton == true), en Home's instantie (home_widget.dart) zet 'm expliciet op false. Uit deze lijst verwijderd. Kanttekening afgerond (2026-08-20, Claude, builder + verse-export-verificatie): login_widget.dart gebruikte dezelfde component met de default true — zelfde zinloze Terug-knop op de entry-pagina als destijds op Home. showBackButton: false gezet op HeaderButtonsComponentWidget, bevestigd via verse export (login_widget.dart:156) + flutter analyze (alleen bestaande info-level lints, geen nieuwe errors). Gecommit 8e450be.)*

    *(P1-21 gesloten zonder bouwwerk — Bob's beslissing 2026-08-16: geen losse custom widget voor de AdBanner-fallback. PUitgaanPage's FlutterFlowAdBanner toont nu nog de debug-tekst zolang Google AdMob de app niet heeft goedgekeurd (kan pas ná livegang), maar dat lost zichzelf op zodra de app is goedgekeurd en AdMob echte advertenties gaat serveren — Bob's inschatting is bovendien dat deze fallback-tekst juist nodig kan zijn zodat Google de advertentie-integratie kan zien tijdens de review. Claude's eerder uitgewerkte CleanAdBanner-custom- widget-voorstel (verving de debugtekst door een lege SizedBox) is dus niet gebouwd — bewust afgewezen, niet vergeten. Geen actie meer nodig tot ná de Google/Apple-goedkeuring bij livegang; check dan of de debugtekst inderdaad verdwenen is. Uit deze lijst verwijderd.)*

    • Kanttekening bij P2-5 ("Ad-banners..., na livegang"): P2-5 gaat er nog van uit dat advertenties een niet-gebouwd P2-idee zijn — in werkelijkheid staat er dus al minstens 1 banner live in de code. Check met Bob of P2-5's "waar wel/geen ads"-regel (geen ads op locatie-kiezer/login/account) al is toegepast op deze ene bestaande banner, en of er bewust voor PUitgaanPage gekozen is als eerste plek.

    (P1-22 volledig afgerond 2026-08-13 — live pair-sessie, Bob builder + Claude verse-export-verificatie, gecombineerd met P1-10's cache-toggle in één doorloop per call (Bob's voorstel, scheelde een dubbele builder-bezoekronde). Alle 11 live API-calls hebben nu decodeUtf8: true bevestigd (homeTabel, HomeSlider, Uitgaanstabel, UitgaanSlider, EstablishmentInfo, HorecagelegenheidEvents, gemeenten, provincies, Evenement, Establishments, requestNewPassword) — EstablishmentsCall's toggle werd in de eerste ronde gemist (niet te verwarren met het dode EstablishmentsNewCall, zelfde-klinkende naam), in een 2e verse export alsnog bevestigd correct. Uit deze lijst verwijderd.)

    (P1-9 volledig afgerond 2026-08-16 — Bob, builder, live pair-fix sessie: EventCurrent's TextTitle heeft nu 8px rechter-padding (ruimte t.o.v. de deel-knop), en het kale nid-debugtekstwidget onder de titel is verwijderd. Beide bevestigd via verse export (Padding(EdgeInsetsDirectional.fromSTEB(0.0, 0.0, 8.0, 0.0)) om TextTitle; 0 treffers meer voor valueOrDefault<String>(widget!.nid, 'nid')). Alle eerdere schaduw-/spacing-restpunten (PUitgaanSliderKaartComponent, HorecagelegenhedenOverzicht) waren al langer afgerond. Uit deze lijst verwijderd.)

    (P1-10 punt 1 — cache-toggle op homeTabel/gemeenten/provincies — afgerond 2026-08-13, gecombineerd met P1-22 in één doorloop per API call, bevestigd via verse export. Punt 2 hieronder blijft open als losse taak.)

    *(P1-10 afgerond — bleek al volledig gebouwd vóór sessie 46 begon (commit d35492a, vóór deze sessie), TASKS.md liep gewoon achter op de code (zelfde valkuil als de vuistregel bovenaan dit bestand waarschuwt). Geverifieerd 2026-08-24 (Claude, sessie 46): home_widget.dart + home_model.dart hebben tabActiviteitenGeladen/tabCultuurGeladen/ tabFilmsGeladen/tabJeugdGeladen-flags (default false, tab 0 "Uitgaan" impliciet al zichtbaar), elk met een ConditionalBuilder-guard

    • een On-Tap-trigger op de bijbehorende Tab-node die de flag op true zet — exact het builder-native recept hieronder. Zelfde patroon ook op horecagelegenheden_overzicht_widget.dart (10 treffers) en horecagelegenheden_overzicht_provincie_page_widget.dart (8 treffers, dit is de hernoemde ..._page_data_type-variant). De 3e variant (..._sort_page) hoefde niet: bleek intussen zelf orphan/kanweg (zie P2-7-bijvangst hieronder). flutter analyze op alle 3 bestanden: geen nieuwe errors. Geen resterend werk.)*

    *(P1-11 geïmplementeerd — bevestigd via lokale git diff op 2026-08-13 avond, tijdens Bob's eigen gelijktijdige ff-run-fvm.sh-build/testronde. Gecommit 2026-08-14 (Claude, commit fb6b224). Fix op HorecagelegenheidEventTabelComponentCopy komt overeen met het eerder uitgewerkte voorstel: de tekst-tegel- Container verloor zijn hardcoded height: 200.0 (blijft in Expanded, hoogte volgt nu de Row), en de afbeeldings-tegel-Container is nu zelf ook in Expanded gewrapt met height: 60.0 i.p.v. 200.0. Uit deze lijst verwijderd — Bob's eigen build/testronde moet dit nog live bevestigen (Events-tab van een horecagelegenheid, geen overflow meer).)*

    *(P1-27 afgerond 2026-08-21 — Claude, builder: Home-pagina TabBar kreeg "Tab Bar Scrollable" aan (zelfde recept als P1-7). Bevestigd via verse export: isScrollable: true op home_widget.dart:216. Labels tonen nu volledig uitgeschreven op telefoonformaat. Nog even checken door Bob op tablet: op dat formaat pasten de labels al zonder deze fix, maar stonden gelijkmatig uitgerekt over de volle breedte — met Scrollable aan staan ze vermoedelijk links uitgelijnd, wat compacter oogt maar niet live geverifieerd deze sessie. Uit deze lijst verwijderd.)*

    (P1-28 afgerond 2026-08-21 — Claude, builder: beide takken (gevuld/leeg hartje) van de ConditionalBuilder op HeaderButtonsComponent kregen dezelfde fillColor: Color(0xFFB50808) als hun buurknoppen (hamburger, terug-pijl) — zelfde bordeauxrood, prima contrast met het witte hartje-icoon erop. Bevestigd via verse export: alle 4 IconButtons in header_buttons_component_widget.dart gebruiken nu identiek fillColor: Color(0xFFB50808). Uit deze lijst verwijderd.)

    P1-29 · Eigenaar: Bob — laag pitje (Bob's besluit 2026-09-04: "laat voor nu maar even liggen, ik denk ouwe meuk"). Letterlijke ?-tekens i.p.v. emoji in evenementbeschrijvingen. Root cause hard vastgesteld (2026-09-04, Claude, curl + codepoint-analyse over 235 live nodes van flutterflow_events.json): dit is geen app-bug — de ? staan als echte 0x3F-bytes in de Drupal-database. Handtekening: bij nid 214370 staat U+003F U+003F ... U+FE0F en bij 214427 staat ?\u200D — de emoji zelf is weg, maar de variatieselector (U+FE0F) en ZWJ (U+200D) staan er nog. Die zijn 3-byte en overleven; emoji zijn 4-byte en sneuvelen. Dat is exact het gedrag van MySQL utf8 (3-byte) i.p.v. utf8mb4: 4-byte sequenties worden bij het opslaan door ? vervangen. Geldt ook voor de "fancy" letters (?????-???? ??????? = mathematical-bold, U+1D400-blok, eveneens 4-byte). decodeUtf8 (P1-22) kan hier per definitie niets aan doen — er valt niets te decoderen, de bytes zijn weg.

    De database is inmiddels in orde (Bob getest 2026-09-04): een verse testnode met emoji behoudt zijn emoji. Bevestigd door nid 214439 ("Brocante Markt Klein Frankrijk"), dat volledig intacte 4-byte emoji (🗓📍🕘🎟🚗👧) door de hele keten levert. Dus geen charset-migratie meer nodig — dit is historische schade.

    Restpunt, alleen als het ooit terugkomt: Bob's eigen vermoeden is dat de kapotte nodes via de uksuite (Django) import zijn binnengekomen i.p.v. via het website-formulier. Dat is een aparte databaseverbinding: schrijft Django zonder charset=utf8mb4 in zijn connectie-opties, dan sneuvelen emoji alsnog ook al is de kolom mb4. Zie je opnieuw ? opduiken in geïmporteerde content: check daar eerst (DATABASES['default']['OPTIONS']), niet in Drupal of de app.

    Bekende beschadigde nodes (niet herstelbaar — originele bytes zijn weg; enige route is de emoji met de hand opnieuw intypen): 214427 (Grindcore Inferno #4, 11-09-2026 — de enige nog actuele), 214370 (Mythic Fest II, 29-08-2026, verlopen), en uit een eerdere steekproef over flutterflowmobiel1 services_1-7: 212672, 211780, 211789, 211794, 211798, 211810 (quizzen, vermoedelijk alle verlopen). Omvang op de huidige agenda: 2 van 235 nodes.

    P2 — features & concept, na livegang

    P2-1 · Eigenaar: Bob (Drupal/views-werk; app-werk pas daarna). Datum-filter Vandaag / Dit weekend / Deze week. Uitgezocht 2026-09-05 (Claude, live curl) — dit kan niet in de app alleen:

    • Er is geen exposed datumfilter. Getest op flutterflow_events.json met date, datum, field_date_value, date_filter, date_filter[value][date] — alle 25 items bleven identiek. Controle met een verzonnen onzin_param=123 gaf hetzelfde resultaat, dus de view negeert onbekende parameters stil: "geen verschil" bewijst hier écht dat de filter ontbreekt.
    • Client-side filteren is geen alternatief. (1) datum is een geformatteerde string zonder jaartal ("woensdag 9 sep, 20:00" op /nl/, "Wednesday 9 Sep, 20:00" op /en/) — parsen is fragiel en rond de jaarwisseling ambigu. (2) De lijst is gepagineerd op 25 items, dus je kunt alleen filteren binnen de pagina die je toevallig binnen hebt.
    • Benodigd: een exposed date-filter (of aparte displays voor vandaag/weekend/week) op flutterflowmobiel1. Pak dit samen met P1-42 hieronder — dat is dezelfde view en dezelfde sorteer/filter-laag.

    P1-42 · Eigenaar: Bob (Drupal/views). De uitgaanslijsten tonen overwegend verlopen evenementen. Gevonden 2026-09-05 (Claude, live curl tijdens P2-1-onderzoek), geldt voor flutterflowmobiel1 services_1/2/3 — de views waar Home zijn tabs mee vult.

    • Gemeten op 2026-09-05 over de eerste 6 pagina's (150 items): juli 66, augustus 48, september 36 — dus ±76% van wat de lijst toont was op de meetdatum al geweest.
    • De lijst gaat vrijwel eindeloos terug: page=40 levert nog steeds 25 items (eind mei), page=80 idem (half mei). Met infinite scroll aan scrollt een gebruiker dus de geschiedenis in.
    • Oorzaak: de view sorteert op created DESC (zichtbaar in Bob's view-export van flutterflowmobiel_establishment_info, en het gedrag van flutterflowmobiel1 past daarbij) — dus op wanneer iemand het evenement invoerde, niet op wanneer het plaatsvindt. Dat verklaart ook waarom de volgorde binnen een pagina rommelig is (95 van de 149 opeenvolgende paren staan op datum, de rest niet).
    • Fix, één ingreep in de view: filter datum >= vandaag én sorteer oplopend op de datum-veldwaarde i.p.v. created DESC. Dan staat het eerstvolgende evenement bovenaan en loopt scrollen de toekomst in.
    • Waarom dit vóór P2-1 moet: een filter "Vandaag / Dit weekend / Deze week" op een lijst die grotendeels uit verleden bestaat en op aanmaakdatum sorteert, levert onvoorspelbare resultaten op.
    • ⚠️ Nog te verifiëren door Bob: of dit ook echt zo in de app oogt (gemeten op de API, niet op een toestel), en of de horeca-/ favorieten-lijsten dezelfde sortering hebben.

    Audit afgerond 2026-09-06 (P1-43, Claude — live curl, 6 pagina's = 150 items per display, peildatum 6 sep 2026). Het probleem is NIET beperkt tot services_1/2/3 — het raakt elke evenementenlijst in de app:

    view / display waar in de app verlopen volgorde
    flutterflowmobiel1 services_1 Home-slider 135/150 (90%) 97/149 paren oplopend
    flutterflowmobiel1 services_2 Home-tabs (alle vijf, zie P1-44) 142/150 (94%) 87/149
    flutterflowmobiel1 services_3 Uitgaan-tab + P-pagina's 143/150 (95%) 100/149
    flutterflowmobiel1 services_4 Activiteiten-tab 150/150 (100%) 75/149
    flutterflowmobiel1 services_5 Cultuur & Info-tab 142/150 (94%) 87/149
    flutterflowmobiel1 services_6 Films-tab 150/150 (100%) 84/149
    flutterflowmobiel1 services_7 Jeugd-tab 149/150 (99%) 89/149
    flutterflow_events services_1 evenement-detail (op nid) 117/150 (78%) 102/149
    flutterflowmobiel_establishment_events horeca-detail, agenda van de zaak 42/97 (43%) niet chronologisch
    flutterflowmobiel_establishments horeca-overzicht n.v.t. (geen datum) nid aflopend = nieuwste zaak eerst
    flutterfavorietenagenda Favorieten tab 1 niet gemeten niet gemeten

    Wat daar per regel bij hoort:

    • services_4, _6 en _7 zijn feitelijk archief: over 150 items geen enkel (services_4, _6) of één (services_7) toekomstig evenement. services_4 loopt terug tot november 2025, services_7 zit voor 110 van de 150 items in mei 2026.
    • Met een townid erbij wordt het erger, niet beter. services_3 met townid=25434 (Arnhem) geeft over de volledige paginering 177 evenementen, waarvan 0 toekomstig; oudste 25 oktober 2025. Dat is deels een inhoudsgat (Willemeen en Theater a/d Rijn hebben in establishment_events óók geen toekomstige data), maar door created DESC krijgt de bezoeker wel een pagina die volledig uit verleden bestaat, zonder enige aanwijzing dat dat zo is.
    • De horeca-agenda op een zaakpagina heeft hetzelfde probleem in het klein: maximaal 10 items, geen datumfilter, niet op datum gesorteerd. In een steekproef van 15 zaken hadden 4 zaken uitsluitend verlopen evenementen in hun agenda staan (75620, 75633, 67258, 70176).
    • Het horeca-overzicht sorteert op nid aflopend — geverifieerd over 6 categorieën in Arnhem, telkens exact aflopend en nooit alfabetisch. Dat is dus "nieuwste inschrijving eerst", wat voor een naslaglijst weinig betekent. Voorstel: alfabetisch op naam (voorspelbaar, en het zoekveld uit P2-6 sluit daarop aan).
    • De favorieten-agenda is niet zonder sessie te meten: anoniem geeft favorieten_agenda.json een 403 met body ["Toegang geweigerd voor gebruiker anonymous"]. drupalRequest maakt daar (sinds de wijziging van 2026-08-17) een lege [] van, dus de app crasht niet — maar een verlopen sessie ziet er in de app uit als "je hebt geen favorieten", zonder melding. Klein los punt, niet dringend.
    • Meetscript staat in de scratchpad van deze sessie (audit/measure.py + run1..8.py); het is 20 regels en zo weer opgetuigd — jaartal komt uit het /20xx/-segment van het url-veld, want datum bevat geen jaar.

    Wat dit betekent voor de ingreep in Drupal: het is één patroon over alle displays van flutterflowmobiel1 heen, plus flutterflowmobiel_establishment_events. Zelfde fix (filter datum >= vandaag, sorteren oplopend op de datumveldwaarde) op alle zeven displays + de zaak-agenda in één ronde, en apart de vraag of het horeca-overzicht niet gewoon alfabetisch moet.

    P1-43 · AFGEROND 2026-09-06 (Claude, code/API-only). De audit uit P1-42 is afgemaakt; de uitkomst staat hierboven bij P1-42 als tabel. Deze taak kan weg zodra Bob de view-ronde in Drupal gedaan heeft.

    P1-44 · Eigenaar: Bob — alle vijf Home-tabs tonen dezelfde lijst. Fix is gebouwd, getest, en daarna BEWUST TERUGGEDRAAID; er ligt nog één blokkade, zie P1-45. Gevonden en uitgezocht 2026-09-06 door Claude.

    • Wat er mis is: home_widget.dart geeft elke tab een eigen display mee (displayid: 'services_3' t/m 'services_7', regels 303/328/365/402/439) aan HomeUitgaantabelKaartComponentWidget, maar dat component gebruikt de parameter nergens: hij komt in home_uitgaantabel_kaart_component_widget.dart alleen voor in de declaratie (regels 25/28), en de enige aanroep is HomeTabelCall.call(page: ...). HomeTabelCall heeft display_id=services_2 hardcoded in de URL. Uitgaan / Activiteiten / Cultuur & Info / Films / Jeugd tonen dus alle vijf services_2. ("Cultuur & Info" klopt bij toeval — services_2 en services_5 leveren dezelfde nids.)
    • Wat er nu al klaarstaat in de builder (blijft staan, verandert niets aan het gedrag): de API Call homeTabel heeft een variabele display_id (String, default services_2) plus een query-parameter display_id die daaraan hangt. De hardcoded ?display_id=services_2 in de URL mag blijven — de láátste query-parameter wint, los tegen Drupal nagemeten.
    • De enige resterende handeling: Backend Query van de StaggeredView in HomeUitgaantabelKaartComponent → "Set Additional Variable" → Parameter Name display_id → Value = component-parameter displayid. Dat is precies wat Claude gebouwd, geëxporteerd én op een toestel geverifieerd heeft (Uitgaan toonde daarna services_3, Activiteiten services_4 — allebei nagelegd tegen de API).
    • Waarom het toch teruggedraaid is: met de fix erin lopen de tabs Activiteiten / Cultuur & Info / Films / Jeugd tegen P1-45 hieronder aan. Precieze schade, nagemeten: de kaarten renderen gewoon, maar in plaats van de groene categorielabels staat er een grijs blok. Geen lege tabs dus. (Ik meldde eerst "Films werd volledig leeg" — dat klopte niet: die tab was leeg omdat ik er met een swipe naartoe ging, en dat gebeurt óók in de teruggedraaide staat. Zie het losse punt hieronder.)
    • Dus: het terugdraaien is een keuze, geen noodzaak. Wil je liever meteen de juiste inhoud per tab en neem je grijze blokjes op de plek van de labels voor lief tot je in Drupal zit — zet de binding dan gerust terug, het is één handeling. Ik heb 'm eruit gehaald omdat de app dan onberispelijk staat zoals je 'm kende, en omdat P1-45 toch in dezelfde Drupal-ronde valt als P1-42.

    Los punt, klein maar verwarrend bij het testen: naar een tab swipen laat 'm leeg; op het tablabel tikken niet. Bevestigd 2026-09-06 op de telefoon-AVD, in zowel de gefixte als de teruggedraaide staat — dus dit staat los van P1-44/P1-45 en zat er al. De tab blijft een leeg wit vlak tot je 'm opnieuw aantikt. Vermoedelijk haalt de PagedMasonryGridView zijn eerste pagina niet op bij een swipe-wissel. Niet uitgezocht; wel iets om te weten voordat je een lege tab als databug aanmerkt.

    P1-45 · Eigenaar: Bob (Drupal/views) — categorie komt in de ene display als lijst en in de andere als komma-string, en daar crasht de Home-kaart op. Gevonden 2026-09-06 tijdens P1-44.

    • Gemeten op flutterflowmobiel1, pagina 0 van elke display:
      • lijst (goed): services_1, services_2, services_3 — "categorie": ["Kindvriendelijk", "Theater"]
      • string (fout): services_4, services_5, services_6, services_7 — "categorie": "Kindvriendelijk, Theater"
    • Hermeten 2026-09-12 — niet meer te reproduceren. Met de nieuwe vulling geven alle zes meetbare displays een array: services_1 (25 items), _2 (25), _3 (17), _5 (25), _6 (1), _7 (7). De komma-string is weg. Alleen services_4 blijft ongemeten — die staat op 0 items omdat stadsactiviteiten handmatig worden aangemaakt en niet geïmporteerd. Controleer bij die ene display de veldinstelling visueel en leg 'm gelijk aan services_1; verder is deze taak klaar.
    • 🔎 Eerdere hermeting 2026-09-11 — deels niet meer te reproduceren. services_5 geeft nu wél een lijst terug (["Voorstelling"]), net als 1/2/3. Voor services_4, _6 en _7 is het niet vast te stellen: die geven op dit moment landelijk 0 items, dus er is geen enkele waarde om naar te kijken (zie de contentstand bovenaan deze lijst). Twee mogelijkheden en ik kan er niet tussen kiezen: óf jij hebt services_5 al omgezet en 4/6/7 ook, óf het formaat hing aan de data en niet aan de displayconfig. Praktisch advies: controleer de veldinstelling van categorie op services_4/_6/_7 gewoon visueel in de viewconfig en leg 'm gelijk aan die van services_1 — dat kost je een minuut en is niet af te meten zolang die tabs leeg zijn.
    • HomeUitgaantabelKaartComponent bouwt zijn groene categorielabels met Generate Dynamic Children over $.categorie, wat in de export getJsonField(item, r'$.categorie').toList() oplevert. Op een String gooit dat NoSuchMethodError: Class 'String' has no instance method 'toList' — letterlijk zo in logcat gezien tijdens de P1-44-test.
    • In een profile-build is dat volledig stil: geen rood scherm, geen overflow-streep, niets in dart analyze. De Films-tab was gewoon een leeg wit vlak; Activiteiten toonde wél kaarten maar met een grijs blok waar de labels horen.
    • Beste fix, en meteen de goedkoopste: laat services_4 t/m services_7 hetzelfde lijstformaat teruggeven als services_1/2/3. Dat is dezelfde view, dus het is een veldinstelling per display — geen app-wijziging nodig, en het lost het in één keer op voor élke plek die deze data toont. Pak het mee in dezelfde ronde als P1-42.
    • App-side alternatief is geprobeerd en loopt vast: er staat nu een custom function categorieAlsLijst(dynamic categorie) in het project (geeft List<dynamic> terug, slikt zowel een lijst als een komma-string). Die werkt, maar is niet te binden: de "Generate Dynamic Children"-waardekiezer toont de bron "Custom Functions" wel, maar klapt leeg open — geen enkele functie is selecteerbaar voor het verwachte type List<Anything>. Vier pogingen, ook na het return-type naar JSON+Is List te hebben gezet. De functie mag blijven staan (kost niets) voor het geval jij 'm in jouw browser wél gebonden krijgt.

    P2-2 · Eigenaar: Onbepaald, bewust v2 (herbevestigd 2026-08-25 door Bob). Google Maps-overzichtsweergave (kaart met meerdere markers) van horecagelegenheden op de overzichtspagina — bewust niet vóór livegang (Maps API-key + billing, onduidelijk of elke locatie al lat/long heeft, marker-UI — geen quick win). Niet te verwarren met P2-14 (los Google Maps-deep-link-icoontje per adres, geen kaart/API- key nodig — dat pakken we wél nu op).

    P2-3 · Eigenaar: Onbepaald. "In de buurt"/geolocatie-browsen — aanvulling op het provincie/gemeente-model, geen vervanging.

    P2-4 · Eigenaar: Onbepaald. Overige feature-ideeën, geen van alle uitgewerkt: deel-knop op eventpagina · "toevoegen aan agenda" (native kalender) · "events op deze locatie" prominenter op de horeca-detailpagina · reviews/waardering voor horecagelegenheden (groot, vraagt nieuw Drupal content-type + moderatie, eigen project). (De "Wat is er vanavond"-melding is uitgelicht naar P2-10 hieronder — dat idee weegt zwaarder dan de rest van dit lijstje.)

    P2-9 · Eigenaar: Onbepaald. Onboarding/eerste-gebruik-uitleg. Live bevestigd 2026-08-07 (Claude, emulator, pm clear + verse launch = echte eerste-keer-ervaring): een nieuwe gebruiker opent de app en ziet direct de Home-pagina met bovenaan een carousel met kop "OOK LEUK" ("ook leuk" veronderstelt dat je al iets gezien hebt — vreemd als allereerste tekst) en daaronder een kale lijst events, geen welkomsttekst, geen uitleg wat Uitgaanskrant is, geen prompt om een provincie/gemeente te kiezen. (Zie ook P0-3's opmerking dat de eerste-launch-default-locatie 28666/28694 geen naam toont totdat iemand handmatig kiest.) Zonder duidelijke eerste indruk is de kans groot dat een nieuwe gebruiker de app na 1x openen niet snapt/niet terugkomt. Twee kant-en-klare opties uitgewerkt (2026-08-25, Claude, code-only) — kies er 1, dan is de bouwtijd klein:

    1. Kort welkomstblok bovenaan Home, alleen bij de eerste launch. Nieuwe App State-variabele onboardingGezien (Bool, Persisted: true, default false). Op Home's Scaffold "On Page Load": een ConditionalBuilder (of Visibility-conditie op een nieuw Container/Text-blok bovenaan de bestaande Column, vóór de "OOK LEUK"-carousel) met conditie onboardingGezien == false. Inhoud: 2-3 zinnen ("Welkom bij Uitgaanskrant — ontdek wat er te doen is in jouw provincie of gemeente.") + een knop "Kies mijn gemeente" die naar selectprovinciegemeente navigeert én meteen onboardingGezien op true zet (Update App State, Set Value). Kleinste bouwtijd, raakt de bestaande Home-structuur nauwelijks aan.
    2. Eerste launch direct naar de gemeente/provincie-kiezer i.p.v. Home. initialLocation zelf (nav.dart) is gegenereerde code en niet direct instelbaar — bouwbaar via Home's Scaffold "On Page Load": een Conditional Action op gemeenteSelectId/ provincieSelectId "Is Not Set" → Navigate To selectprovinciegemeente met "Allow Back Navigation" uit (zelfde context.goNamed-patroon als elders in dit bestand). Sterker (dwingt een locatiekeuze af, lost ook meteen P0-3's "default-locatie zonder naam"-punt structureel op voor nieuwe gebruikers), maar een grotere gedragsverandering voor iedereen zonder opgeslagen locatie, niet alleen eerste-launch-gebruikers. Bob's voorkeur bepaalt welke van de twee gebouwd wordt — geen van beide is al uitgevoerd.
    3. Bijvangst, zelfde verkenning: dode terug-pijl-knop in Home's AppBarafgerond (2026-08-10, Claude). IconButtonBack verwijderd via Widget Tree → rechtsklik → "Remove Widget" (op HomeWidgetAppBarRow, naast IconButtonDrawer). Bevestigd via verse flutterflow export-code: geen enkele referentie meer aan IconButtonBack/het bijbehorende Icons.arrow_back_outlined-icoon in home_widget.dart — de AppBar-Row bevat nu alleen nog de hamburger-menuknop. Geen builder-sync-problemen bij deze simpele Remove-Widget-actie (i.t.t. P1-13's multi-property-toggle, zie daar).

    P2-10 · Afgewezen voor nu (Bob's besluit 2026-08-25). "Vanavond in [gemeente/provincie]"-pushmelding — geen pushmeldingen in deze versie, misschien een volgende. Origineel idee: uitgelicht uit de ongestructureerde lijst van P2-4 als vermoedelijk de goedkoopste hefboom voor terugkerend gebruik (dit type overzichts-app wint niet op features maar op of mensen 'm blijven openen), maar niet nu bouwen. Zou Firebase Cloud Messaging nodig hebben (los van de Crashlytics-only scope van P1-16, zie daar) — geen actie totdat Bob dit heropent.

    P2-5 · Eigenaar: Onbepaald (horeca-overzicht resterend).

    ⚠️ TERUGGEDRAAID op 2026-09-03 (Bob's besluit bij P1-35): de ad-banners op horecagelegenheidCurrent en EventCurrent zijn weer VERWIJDERD. Aanleiding was de merkanalyse: het blok stond direct onder de titel en duwde tabs/adres/tijden onder de vouw, terwijl de site advertenties consequent ná de inhoud zet. Bob koos ervoor ze op de detailpagina's helemaal te schrappen in plaats van te verplaatsen. Geverifieerd met verse export: er staat nog exact één FlutterFlowAdBanner in het project, op p_uitgaan_page:177. Zet ze dus niet terug op basis van de "afgerond"-tekst hieronder — die beschrijft de situatie van 2026-08-25 en is achterhaald. Wat er van deze taak overblijft is alleen nog het horeca-overzicht, en de vraag of je daar een advertentie wilt is na dit besluit opnieuw open.

    (Historie, 2026-08-25:) Ad-banners op horeca-overzicht, horeca-detail, event-detail; vaste regel: geen ads op locatie-kiezer/login/account. Horeca-detail + event-detail afgerond (2026-08-25, Claude, builder + verse export + flutter analyze 0 errors + live build/launch op emulator-5554, Bob's expliciete akkoord in de chat). Zelfde FlutterFlowAdBanner-widget + Ad Unit ID's (ca-app-pub-2431417692812232/5511582981 iOS, .../6657143691 Android) als de al werkende instantie op PUitgaanPage — gekopieerd via widget-tree "Copy" op de bestaande AdBanner-node + "Insert After" op een sibling-node net na de titel/datum-blok (HorecagelegenheidCurrent: na TextTitelGelegenheid, vóór de TabBar; EventCurrent: na TextData/datum, vóór de categorie-tags-Row). Resterend: horeca-overzicht — bewust overgeslagen, Bob's eigen Sort/Datatype-experiment op die pagina loopt nog (zie P2-6); oppakken zodra dat is afgerond, zelfde recept (kopieer de AdBanner-node vanaf een van de 3 al werkende pagina's).

    P2-6 · Grotendeels afgerond — zoeken werkt op 5 van 6 tabs, live bevestigd. Eigenaar: Claude (niet bezig). Restpunten staan als eigen taak bij P2-24.

    Uitvoerplan voor de volgende sessie (opgesteld 2026-09-04, alles hieronder is getest)

    Bouw op HorecagelegenhedenOverzichtCopy3, niet op de live pagina. Die kopie is op 2026-09-04 met Duplicate Page gemaakt van de live HorecagelegenhedenOverzicht en is exportgeverifieerd identiek: 6 HorecagelegenheidoverzichtKaartWidget, 6 fetchAlleHorecagelegenheden- aanroepen, 2 (lege) TextFormFields, 1941 vs 1940 regels. Als er onderweg iets sneuvelt, staat de live pagina er nog ongeschonden.

    Wat op deze pagina wél en niet kan (bewezen 2026-09-04, zie het kader verderop voor het bewijs):

    • ✅ Widget invoegen in de root-Column (die de Container met de TabBar bevat) — werkt.
    • Eigenschappen/bindings wijzigen van widgets die binnen een ConditionalBuilder-tak zitten — werkt.
    • ❌ Widget invoegen in een Column binnen een ConditionalBuilderIf-tak — gebeurt niet (dialoog blijft open, geen fout, niets kapot). Geldt ook voor Bob in zijn eigen browser.
    • Wrap WidgetColumn op de ConditionalBuilder — "Invalid Action", wordt teruggedraaid.

    Stappen:

    1. Insertie in de root-Column van Copy3 even natesten (op ...Copy bewezen, op Copy3 nog niet).
    2. Eén TextField invoegen in die root-Column (rechtsklik op de tree-rij → Insert Widget → meteen typen, niet eerst in het zoekveld klikken). Hij landt achteraan; sleep de node daarna in de tree op de Column-rij om 'm bóven de TabBar te krijgen (drop = positie 0).
    3. On Change → Update Page State zoekterm. Eén gedeeld veld voor alle zes tabs, niet zes losse — de bestaande 6x zoektermXxx / categorieFilterXxx zijn daarmee overgedimensioneerd; laat ze staan en gebruik er één set van.
    4. Categoriefilter (dropdown) op dezelfde plek, opties dynamisch. Daar is nog een custom function voor nodig die de unieke categorie- waarden uit de dataset van de actieve tab haalt.
    5. Per tab de Generate Dynamic Children-Value van de StaggeredView omzetten van de rauwe alleXxx-lijst naar filterHorecagelegenheden(alleXxx, zoekterm, categorieFilter, '$.titel', '$.categorie'). Dit is puur binding-werk en valt dus buiten de blokkade. Dit is wel het risicovolle deel: 6x een custom function met 5 argumenten, en juist die dialoog staat in CLAUDE.md bekend om bevriezen. Loopt er één vast: niet doorproberen, overdragen aan Bob.
    6. Verse export + dart analyze (0 errors) ter controle.
    7. Route omzetten — precies 2 wijzigingen in levende code (de andere twee treffers zitten in dode kopieën, laat die met rust):
      • lib/shared/drawer_component/drawer_component_widget.dart:690
      • lib/horecagelegenhedenoverzicht/horecagelegenheid_current/horecagelegenheid_current_widget.dart:1562 Beide Navigate To laten wijzen naar HorecagelegenhedenOverzichtCopy3 i.p.v. HorecagelegenhedenOverzicht.
    8. Daarna is de oude HorecagelegenhedenOverzicht een orphan. Niet weggooien — als regel bij P2-7 zetten zodat Bob 'm opruimt.

    Tijdsinschatting (Claude in de browser, met verificatie-export per stap): stap 2-3 een half uur tot drie kwartier, stap 4 ongeveer een uur, stap 5 anderhalf tot tweeënhalf uur. Samen 3 à 4 uur, dus reken op twee sessies. Herbouwen van de hele pagina zou 6 à 10 uur zijn en is niet nodig.

    ⚠️ Direct ná een Duplicate Page faalt export-code een paar keer met Unexpected error from the server (3x achter elkaar gezien op 2026-09-04), en lukt daarna gewoon. Even wachten, niet gaan zoeken.

    Stand na 2026-09-05 — af op 5 van de 6 tabs; 4 taken over voor Bob

    Alles hieronder is per stap geverifieerd met een verse export; dart analyze 0 errors; de live HorecagelegenhedenOverzicht is functioneel ongewijzigd (byte-diff alleen de Cultuur-tab, die nu dezelfde getJsonField(..., '$')-schrijfwijze gebruikt als de andere vijf).

    AF:

    • Stap 1+2 — één TextField in de root-Column van Copy3, bóven de TabBar. Insertie was puur additief (0 verwijderde regels). FlutterFlow hernummerde de controllers: dit veld is textController1.
    • Stap 3On Change → Update Page State zoektermActiviteiten.
    • Stap 5 op 5 tabs — Activiteiten, Cultuur, Eetgelegenheden, Overnachten en Uitgaan draaien op filterHorecagelegenheden(alleXxx, textController1.text, categorieFilterActiviteiten, '$.titel', '$.categorie'), alle vijf met de juiste padwaarden.
    • Cosmetica zoekveld — breedte double.infinity (was 200 px) en hint-tekst "Zoek op naam" (was "TextField").

    ✅ Live geverifieerd op een toestel (2026-09-05)

    Profile-build van een verse export, telefoon-emulator (411 dp), gestart met fvm flutter run --profile -d emulator-5556 --route "/horecagelegenhedenOverzichtCopy3?plaats=25434" (25434 = Arnhem). Het zoeken werkt echt:

    • Tab Activiteiten: 2 kaarten (klopt met de API), "vue" → 1 kaart. Hoofdletterongevoelig.
    • Tab Eetgelegenheden: 20 kaarten, "pizza" → 5 kaarten. Nagerekend tegen de API: er zíjn precies 5 records met "pizza" in de titel. Dit is meteen het bewijs dat het ook op een ConditionalBuilder-tab werkt, niet alleen op de twee kale tabs.
    • Zoekterm blijft staan bij het wisselen van tab (veld staat immers boven de TabBar) — precies de bedoeling.
    • De 2 s debounce is goed merkbaar: de lijst springt pas ~2 s nadat je stopt met typen.

    Bijvangst 1 — twee lege TextField-placeholders zijn nu overbodig (Eigenaar: Bob). In tab 0 (Activiteiten) en tab 1 (Cultuur) staat nog het oude, ongebonden TextField (hint letterlijk "TextField"). Op het toestel is dat een zwevend wit vak dat half over de TabBar valt — lelijk en verwarrend naast het echte zoekveld. De eerdere afspraak "laten staan tot deze taak ze van een echte binding voorziet" is hiermee afgehandeld: het echte zoekveld staat nu bóven de TabBar, dus deze twee zijn overbodig geworden. Claude verwijdert niets — weghalen is aan Bob.

    Bijvangst 2 — duplicaten in Drupal (Eigenaar: Bob, los van P2-6). Voor Arnhem/Eetgelegenheden staan dezelfde zaken dubbel in de view, met verschillende categorie-sets: nid 53455 en 50584 heten allebei "New York Pizza Arnhem Zuid", nid 53454 en 50583 allebei "New York Pizza Arnhem Centrum". De app toont ze dus terecht dubbel; het zit in de data.

    (Geen bug: dat de header "Amsterdam (gemeente)" toont terwijl de lijst Arnhem-data bevat, komt doordat de deep-link alleen de page-parameter plaats zet en niet de App State-gemeente. Artefact van de testmethode.)


    Openstaande taken → verplaatst naar P2-24

    De vier resterende punten (tab Verhuur/catering, de categoriedropdown, Max Items, en het omzetten van de route) staan nu als zelfstandige taak P2-24 verderop in deze lijst, samen met de twee bijvangsten. Het technische recept en de valkuilen blijven hieronder staan — P2-24 verwijst ernaar terug.

    Niet oplosbaar, geaccepteerd: FlutterFlow zet op de On Change-trigger zelf een EasyDebounce van 2000 ms. Er is geen debounce-eigenschap op het TextField-widget en de trigger/actie-menu's bieden 'm ook niet. Gevolg: de lijst ververst ~2 s nadat je stopt met typen. Werkt correct, voelt traag.


    Werkend recept per StaggeredView (bewezen op 5 tabs):

    1. Zet het tree-zoekveld op StaggeredView — dan staan alle 6 grids onder elkaar in tabvolgorde. Veruit de betrouwbaarste navigatie.
    2. Node selecteren → 4e icoontje (Generate Dynamic Children) → potlood naast Value → potlood naast Variable → zoekveld filterHorecaCustom Functionshover op de rij eronder → de functie.
    3. Argumenten: items = Page State alleXxx (No Further Changes); zoekterm = Widget State → TextField 1; categorieFilter = Page State categorieFilterActiviteiten; titelPad = $.titel; categoriePad = $.categorie.
    4. Confirm in de dialoog én daarna Save in het rechterpaneel.

    Valkuilen die tijd kostten:

    • Bind zoekterm NOOIT aan de page state zoektermActiviteiten. Die is nullable en start op null; dat genereert _model.zoektermActiviteiten!gegarandeerde crash zodra de tab bouwt, en dart analyze ziet het niet. Widget State geeft _model.textController1.text, non-nullable.
    • categorieFilterActiviteiten is bewust het GEDEELDE veld voor alle zes tabs — er komt één dropdown boven de TabBar, niet één per tab. Gebruik dus niet categorieFilterCultuur e.d. De naam klopt niet meer, maar hernoemen lukt niet (zie hieronder).
    • Let op de punt in $.categorie. Eén keer als $categorie ingevoerd; de functie strippt alleen een $.-prefix, dus het filter matcht dan nooit op die tab — zonder foutmelding. Hersteld, maar makkelijk te herhalen.
    • Het zoekveld in de Set-Variable-dialoog filtert over álle bronnen, inclusief Widget State. Typ TextField 1 — veel sneller dan uitklappen.
    • De eerste klik op een icoon/potlood/zoekveld landt vaak niet. Reken op 2 pogingen per actie en verifieer met een zoom.
    • Rechterpaneel-tekstvelden: triple_click + typen werkt, ctrl+a + typen niet (bevestigd op Hint Text). Commit door ergens neutraals te klikken, niet met Tab — Tab draaide de waarde terug.

    ⚠️ Het "Local Page State Variables"-paneel slaat wijzigingen niet op — ook niet bij Bob. Getest: veld hernoemen, Nullable uitvinken, Initial Field Value vullen, en "Default Variable Value" in de Set-Variable-dialoog. Alle vier tonen de nieuwe waarde in de UI met "Synced" erboven, en een verse export toont onveranderd de oude staat. Daarom houden de velden hun misleidende ...Activiteiten-namen.

    Achtergrond en bewijs (Eigenaar: Claude) (niet meer 'bezig' — de de-risk-test is gedaan, zie het kader hieronder; stappen 3/4/6 staan nog open).

    ⚠️ RESULTAAT DE-RISK-TEST 2026-09-04 (Claude, op HorecagelegenhedenOverzichtCopy, met Bob's akkoord). De destructieve bug REPRODUCEERT NIET — maar er is wel een andere blokkade.

    Wat wél gewoon werkt: een TextField toevoegen aan de Column van tab 0 (Activiteiten), via rechtsklik op de tree-rij → "Insert Widget" → zoeken → kaartje aanklikken. Het veld landde als sibling ná de StaggeredView (Insert Widget voegt dus achteraan toe, niet vooraan zoals slepen dat doet). Verse export daarna: alle 6 tabs aanwezig, alle 6 horcat-calls, alle 6 HorecagelegenheidoverzichtKaartWidget- bindingen intact, en een diff tegen de export van vlak ervóór is puur additief: 0 verwijderde regels. De live pagina is niet aangeraakt (nog steeds 6 kaarten + zijn 2 al bestaande lege TextFormFields). Er is dus geen schade en de "Column verliest zijn kinderen"-bug van 2026-08-25 trad niet op.

    Wat níet lukte: hetzelfde doen op de Column van tab 1 (Cultuur). Die zit binnen een ConditionalBuilderIf-tak, en daar doet het aanklikken van het widget-kaartje in de Insert-dialoog niets: de dialoog blijft gewoon openstaan, er wordt niets toegevoegd, geen foutmelding. 3 pogingen, telkens met bevestigde selectie van de juiste Column in het rechterpaneel. Niet destructief — er gaat niets kapot, er gebeurt alleen niets.

    Voorbehoud bij die tweede bevinding: dezelfde grote Insert-modal staat in CLAUDE.md al bekend als onbetrouwbaar voor browser-automation, en de tree-klik-offset speelde tijdens deze pogingen ook op (een rechtsklik landde 2x op de verkeerde rij). Het is dus bevestigd "lukt Claude niet via automation", niet bewezen "kan in FlutterFlow niet". In Bob's eigen browser is het het proberen waard.

    VERVOLG DEZELFDE SESSIE — er is een werkend pad gevonden, herbouwen is NIET nodig. Nadat Bob bevestigde dat de ConditionalBuilder-tabs ook in zijn eigen browser weigeren, zijn er nog drie dingen getest op dezelfde kopie:

    1. Wrap WidgetColumn op de ConditionalBuilder: geweigerd met "Invalid Action ... we've undone it for you". Die route is dus dicht, net als insertie in de If-tak.
    2. Insertie in de ROOT-Column van de pagina (die de Container met de TabBar bevat): WERKT. Een widget landt daar netjes als tweede kind, en de export bevestigt het: Scaffold > Column > [ Container(TabBar), <nieuw widget> ], alle 6 tabs en 6 kaartbindingen intact, 0 verwijderde regels in de diff. Insert Widget voegt achteraan toe; naar bóven de TabBar krijg je 'm door de node in de tree op de Column-rij te droppen (drop = positie 0).
    3. Een eigenschap wijzigen ván een widget bínnen een ConditionalBuilder-tab: WERKT. Main Axis Spacing van de StaggeredView in tab 1 (Cultuur) van 12 naar 13 gezet; exportgeverifieerd (mainAxisSpacing: 13.0 op alleen die ene, de andere vijf nog 12). De blokkade geldt dus uitsluitend voor het toevoegen/herstructureren van widgets in zo'n tak, niet voor bindings, waarden of acties.

    Gevolg voor het bouwplan: stap 3 hoort niet per tab maar als één zoekveld (+ categoriefilter) in de root-Column boven de TabBar — precies wat de oorspronkelijke stap 3 hierboven al voorschreef ("Eén TextField boven de TabBar die de op dat moment actieve tab filtert"). Stap 4 en 6 zijn puur binding-werk op de bestaande StaggeredViews en zijn dus gewoon uitvoerbaar. De 6x zoektermXxx/categorieFilterXxx-page-state is daarmee overgedimensioneerd: met één gedeeld veld is één set genoeg, de rest kan blijven staan en ongebruikt blijven.

    Nog niet geverifieerd: dat de root-Column-insertie ook op de live pagina lukt. De kopie erfde de ConditionalBuilder-blokkade, dus ze gedragen zich vermoedelijk hetzelfde — maar test het daar opnieuw vóór je op het goede pad vertrouwt.

    Sporen van de test op de kopie (blijven staan, Claude verwijdert niets): een losse TextField onderaan tab 0, een Text "Hello World" onderaan de root-Column, en mainAxisSpacing: 13 i.p.v. 12 op de StaggeredView van tab 1.

    Praktisch gevolg voor stap 3/4/6: op deze pagina is alleen tab 0 een kale Column; tabs 1 t/m 5 zitten allemaal in een ConditionalBuilder. Claude kan het zoekveld dus wel op tab 0 plaatsen, maar voor de andere vijf is óf Bob nodig, óf een andere route. Widget-copy/paste is géén uitweg (bekend dood spoor, zie CLAUDE.md).

    Let op: het testveld staat er nog. Er staat nu één losse, ongebonden TextField onderaan tab 0 van HorecagelegenhedenOverzichtCopy. Claude verwijdert niets (staande regel), dus die laat ik staan — weghalen mag Bob doen, of hij blijft gewoon staan want het is een dode kopie. Tekstzoeken op naam én categorie, per tab van horecagelegenheden_overzicht_widget.dart/..._provincie_page_widget.dart (Bob's besluit: "op naam kunnen selecteren, tekstveld voor overeenkomstige namen", nodig op elke tab; uitgebreid met een categorie-filter na een live test hieronder).

    Meegenomen besluit (2026-09-03, Bob): de twee lege TextFormFields in de tabs Activiteiten en Cultuur op HorecagelegenhedenOverzicht (hint-tekst letterlijk TextField, textController1/2 worden nergens uitgelezen — geen onChange, geen filter) blijven staan tot deze taak ze van een echte binding voorziet. Niet verwijderen; ze zijn de plaatshouder voor het zoekveld dat hier gebouwd wordt.

    Kritieke bevinding (2026-08-25, Claude, live curl-test op de productie-endpoint) — verandert de aanpak: HorecagelegenheidoverzichtCall (flutterflowmobiel_establishments.json) hangt vast aan een harde cap van 100 resultaten per aanroep. Test op townid 28666/horcat=17967 gaf exact 100 terug (verdacht rond getal); een 2e gemeente (28694) gaf er 75 (onder de cap, dus waarschijnlijk het echte totaal). De endpoint ondersteunt al een niet-gedocumenteerde page-parameter: page=1 op dezelfde 100-resultaten-gemeente gaf een andere set van 100 items (bevestigd via nid-vergelijking, geen overlap) — de data bestaat dus compleet op de server, de app haalt er nu alleen page 0 van op. Gevolg: puur client-side filteren op de huidige, single-page lijst zou bij een grote gemeente stilletjes items missen (precies Bob's zorg). Categorie-filter-opties moeten bovendien dynamisch opgebouwd worden uit de unieke categorie-waarden in de dataset (Bob: "die kan hij hebben van alle horecagelegenheden die hij inleest"), wat dus ook een complete dataset vereist.

    Bijgewerkt bouwplan:

    1. Builder: HorecagelegenheidoverzichtCall krijgt een nieuwe parameter page (Integer of String, default 0/'0'), toegevoegd aan de bestaande params-map ('page': page).
    2. Nieuwe Custom Action fetchAlleHorecagelegenheden(String horcat, String townid, String displayId) -> List<dynamic>: roept HorecagelegenheidoverzichtCall.call(...) in een loop aan met oplopende page (0, 1, 2, ...), voegt elke pagina's resultaten samen, stopt zodra een pagina minder dan 100 items teruggeeft (= laatste pagina) of bij een lege/foutieve respons (veiligheidslimiet op bv. 10 iteraties tegen een oneindige loop bij een onverwachte server-bug).
    3. Eén TextField boven de TabBar (filtert de op dat moment actieve tab). Component/Page State zoekterm (String), gebonden via onChanged.
    4. Categorie-filter: chips/dropdown, opties dynamisch opgebouwd uit de unieke categorie-waarden binnen de complete (alle-pagina's) lijst van de actieve tab — geen statische/hardcoded lijst.
    5. Nieuwe Custom Function filterHorecagelegenheden(List<dynamic> items, String zoekterm, String? categorieFilter, String titelPad, String categoriePad) -> List<dynamic>: combineert naam-match (case-insensitive én diacritics-genormaliseerd, bv. "café" moet ook matchen op "cafe") met categorie-match (AND-logica: allebei moeten kloppen als beide ingevuld zijn). Lege zoekterm/geen categorie-filter → geen restrictie op dat onderdeel.
    6. Elke tab's Generate Dynamic Children-"Value"-binding wijzigen van de rauwe API-response naar het resultaat van stap 2 (Component State, gevuld via een On-Page-Load/On-Tap-trigger die fetchAlleHorecagelegenheden aanroept), gefilterd via stap 5. Advies/aandachtspunten:
    7. Debounce niet nodig — filteren gebeurt op een al volledig lokaal geladen array, geen API-call per toetsaanslag.
    8. Dit raakt dezelfde pagina als Bob's Sort/Datatype-experiment — gestart nu met zijn expliciete akkoord (2026-08-25).
    9. Style-tip: een "wis"-kruisje (suffixIcon) in het zoekveld is fijn UX; val niet in de bekende TextFormField-suffixIcon-Tooltip- beperking uit CLAUDE.md (geen Tooltip nodig, gewoon een losse onTap die zoekterm leegt).

    Voortgang (2026-08-25, Claude, builder) — stappen 1, 2 en 5 volledig afgerond en bevestigd via verse export + flutter analyze (0 errors):

    • Stap 1: page-parameter (Integer, default 0) toegevoegd aan HorecagelegenheidoverzichtCall (Variables + Query Parameters, gebonden via "From Variable").
    • Stap 2: Custom Action fetchAlleHorecagelegenheden(String horcat, String townid, String displayId) -> List<dynamic> staat in lib/custom_code/actions/fetch_alle_horecagelegenheden.dart — haalt pagina's op tot een pagina <100 items teruggeeft (max 10 iteraties als veiligheidslimiet).
    • Stap 5: Custom Function filterHorecagelegenheden(...) staat in lib/flutter_flow/custom_functions.dart — combineert naam-match (diacritics-genormaliseerd) met categorie-match. Kostte ongebruikelijk veel pogingen door een nieuw ontdekt builder-quirk: een lokale geneste helper-closure in de body brak de "Save Function"-validatie structureel ("cannot be parsed", ondanks valide Dart) — opgelost door de normalisatielogica plat/zonder closure te schrijven (2x inline i.p.v. 1x als helper). Zie de nieuwe CLAUDE.md-notitie voor het volledige patroon + een tweede bijvangst-ontdekking (Ctrl+A/Delete werkt structureel niet in de Custom Code-editor). Stap 2 door Bob afgerond (2026-08-25) voor 5 van de 6 tabs — bevestigd via verse export + flutter analyze (0 errors): TabBar's onTap roept nu per tab (Cultuur/Eetgelegenheden/Overnachten/Uitgaan/ Verhuur-catering) fetchAlleHorecagelegenheden aan met de juiste horcat (Cultuur 17963, Eetgelegenheden 34, Overnachten 17965, Uitgaan 17967, Verhuur/catering 17968) + townid: widget!.plaats!
    • displayId: 'services_1', en zet het resultaat in een eigen Page State-lijst (alleCultuur, alleEetgelegenheden, alleOvernachten, alleUitgaan, alleVerhuurCatering).

    *(Restpunt bij stap 2 — Activiteiten/tab 0 via On Page Load — afgerond, bevestigd 2026-08-30 via verse export: initState() van horecagelegenheden_overzicht_widget.dart roept nu fetchAlleHorecagelegenheden('17969', widget!.plaats!, 'services_1') aan en zet het resultaat in _model.alleActiviteiten. Alle 6 tabs hebben nu dus hun complete dataset. Wie dit gebouwd heeft is niet vastgelegd — vermoedelijk Bob na de sessie van 2026-08-25.)*

    Stappen 3, 4, 6 (widget-wiring: TextField, categorie-filter, lijsten herbinden) — geblokkeerd, overgedragen aan Claude voor een latere sessie. Bob heeft het geprobeerd (2026-08-25) en gestopt na een reeks mislukkingen die wijzen op een structureel probleem met deze specifieke pagina, niet op een bedieningsfout:

    • Een TextField invoegen op de Container die de TabBar bevat geeft een "Replace Child Widget"-dialoog (logisch, Container is single-child) — zowel "Replace" als de aangeboden "Wrap in Column/Row/Stack"-optie (een gecombineerde wrap+insert-actie, dus een ander code-pad dan de bekende losse "Wrap Widget"-actie) geven "Invalid Action" + automatische terugdraai. Ook een losse "Wrap Widget (Ctrl+B)" op zowel de Container als de bovenste Column van de pagina gaf hetzelfde. Dit bevestigt het al bekende CLAUDE.md-patroon (zie de Event-pagina-notitie) nu ook op een actief-gebruikte pagina, niet alleen een orphan.
    • Nieuwe, verontrustender bevinding: een TextField toevoegen aan de gewone (niet-single-child) Column binnen een tab's TabBar Page — waar normaal probleemloos een extra kind bij zou moeten kunnen — verving in plaats daarvan de complete bestaande inhoud (StaggeredView/Container/HorecagelegenheidoverzichtKaart verdwenen, alleen het TextField bleef over). Dat is geen normaal Column-gedrag (een Column zou nooit bestaande kinderen moeten laten verdwijnen bij een extra invoeging) — wijst op een dieperliggend, nog niet begrepen probleem met deze pagina's interne staat, niet op een verkeerde klik. Nog niet uitgezocht waarom.
    • Bekend gebleven werkende plek: het toevoegen van acties (zoals stap 2's onTap-aanroepen) via de Actions-tab werkte de hele tijd probleemloos — het probleem zit specifiek bij widgets toevoegen/ herstructureren in dit deel van de boom, niet bij logica/acties.

    Stap 1 van het vervolgplan (onderzoek) is uitgevoerd — 2026-08-30, Claude, code-only op een verse flutterflow export-code. Uitkomst:

    • Geen Dart-niveau-inconsistentie. dart analyze lib op de verse export: 0 errors (939 warnings + 1156 infos, allemaal de gebruikelijke FlutterFlow-lintruis: unused imports, ?.. op non-nullables e.d.). De "Column vervangt zijn kinderen"-bug is dus puur builder-side; de geëxporteerde code compileert prima. De bekende dode-code-compilefout uit P2-7 (slider_uitgaan_component_small_current, carouselResponse) is intussen ook verdwenen uit de export — die map staat er nog wel (blijft een orphan-opruimpunt), maar geeft geen fout meer.
    • De schade van 2026-08-25 is op 2026-09-01 volledig hersteld (was P1-31, taak verwijderd). Het ging om tab-index 1, en dat is de tab "Cultuur" — niet "Eetgelegenheden", zoals hier eerder stond; die verwarring kwam doordat de kapotte tab óók nog op Eetgelegenheden' categorie-id (horcat: '34') stond. Nu: 17963, lijstgeneratie, 6 kaartbindings, tik-actie en de juiste laadvlag allemaal terug.
    • Er staan 3 ongebruikte kopieën van deze pagina in het project: HorecagelegenhedenOverzichtCopy, ...Copy2 en ...Copy2Copy (routes staan geregistreerd in nav.dart, maar geen enkele pushNamed navigeert er ooit heen — pure backups, zelfde soort orphan als EventWidget). Vermoedelijk door Bob gemaakt als vangnet vóór de experimenten. Copy2 (en het identieke Copy2Copy) is een complete, onbeschadigde momentopname: alle 6 tabs intact, On Page Load-fetch aanwezig, 5 onTap-fetches aanwezig, volledige Page State, plus al 1 zoek-TextField in tab 0. Dat is meteen de bron waaruit het herstel van 2026-09-01 is afgeleid. Dat herstel is af, dus alle 3 de kopieën mogen nu weg (naar P2-7).
    • De hele state-laag voor P2-6 staat al klaar op zowel de pagina als de kopieën: 6x alleXxx (List), 6x zoektermXxx (String), 6x categorieFilterXxx (String) — per tab één set. Alleen de widget-wiring (stappen 3/4/6) ontbreekt nog.
    • De provincie-variant (horecagelegenheden_overzicht_provincie_page_widget.dart) is nog helemaal niet aangeraakt: 6 intacte tabs, 0 fetchAlleHorecagelegenheden- aanroepen, 0 TextFormFields, en zijn model heeft géén alleXxx/ zoekterm-velden. Daar moet stap 1/2 dus nog volledig gebeuren.
    • Afspraak met Bob (2026-09-04): eerst op een kopie testen, niet op de live pagina. Gebruik HorecagelegenhedenOverzichtCopy als proefkonijn — dat is de wegwerp-kopie. Níet Copy2/Copy2Copy: dat is de intacte momentopname waaruit het herstel van 2026-09-01 is afgeleid en dus het vangnet als er weer iets sneuvelt. Volgorde: (a) op Copy een TextField toevoegen aan de Column binnen één tab's TabBar Page en met een verse export controleren of de bestaande StaggeredView/kaartbindingen blijven staan; (b) pas als dat 2x achtereen goed gaat, hetzelfde op de live pagina; (c) gaat het op Copy al mis, dan is de bug gereproduceerd zonder schade en stopt het daar — melden bij Bob i.p.v. doorproberen.
    • Advies voor de volgende builder-sessie: het herstel is af, dus stap 3/4/6 kan meteen. Als het toevoegen van widgets aan deze pagina opnieuw kapotgaat, is Copy2 een kant-en-klaar alternatief om op verder te bouwen (route omzetten in drawer_component + horecagelegenheid_current kost 2 Navigate-To-wijzigingen) — maar onbekend of die kopie dezelfde latente builder-staat-corruptie meedraagt, dus alleen als plan B.
    • Zodra widgets weer normaal toegevoegd kunnen worden: bouw het zoekveld + categorie-filter zoals hierboven al uitgewerkt (via Copy/Paste vanaf 1 geconfigureerd TextField naar de Column binnen elke tab's TabBar Page, zie de sessie-notitie/chatgeschiedenis voor het exacte recept — 1x On Change → Update Page State zoekterm configureren, dan 6x kopiëren).
    • Categorie-filter: chips/dropdown, opties dynamisch uit de complete dataset (alleActiviteiten etc.) — geen hardcoded lijst.
    • Elke tab's StaggeredView/lijst-databron ombouwen van de rauwe alleXxx-lijst naar filterHorecagelegenheden(alleXxx, zoekterm, categorieFilter, '$.titel', '$.categorie').
    • Verse export + flutter analyze om te bevestigen dat fetchAlleHorecagelegenheden/filterHorecagelegenheden nu daadwerkelijk aangeroepen worden, en dat de 6-tabs-versie + de provincie-paginavariant (horecagelegenheden_overzicht_provincie_page_widget.dart, nog helemaal niet aangeraakt) allebei kloppen.
    • *(P2-14 volledig afgerond 2026-08-25 — Custom Function googleMapsUrl

      • Project Component GoogleMapsIconButton gebouwd door Claude, op beide pagina's (horecagelegenheid_current_widget.dart, event_current_widget.dart) geplaatst door Bob (canvas-drag lukte niet via automation, zie het CLAUDE.md-patroon over overlappende/ overflowende widgets — Bob deed de laatste stap zelf in enkele minuten). Bevestigd via verse export: GoogleMapsIconButtonWidget op beide widgets, adres/plaats correct gebonden aan dezelfde bron als de bestaande adres-tekst (EstablishmentInfoCall.establishmentAdres/ establishmentPlaats resp. EvenementCall.eventAdres/eventPlaats), flutter analyze geen nieuwe errors. Bijvangst-punt (ontbrekend horeca-blok bij stadsactiviteiten op EventCurrent) blijft bewust liggen tot Bob erop terugkomt — geen eigen taak-ID.)*

      P2-24 · Restpunten horeca-zoekfilter (vervolg op P2-6) · Eigenaar: Bob. Pagina: HorecagelegenhedenOverzichtCopy3. P2-6 heeft het zoekveld op 5 van de 6 tabs werkend en live op een toestel bevestigd (zie het ✅-blok bij P2-6). Dit zijn de punten die nog over zijn; ze hebben allemaal Bob nodig — óf omdat Claude er aantoonbaar niet doorheen komt, óf omdat er een keuze in zit. Het werkende recept per StaggeredView en alle valkuilen staan bij P2-6; niet opnieuw uitzoeken.

      ⛔ A en C zijn geblokkeerd door één en dezelfde oorzaak — lees dit eerst (2026-09-09, Claude, hard gemeten).

      Het Generate Dynamic Children-paneel schrijft op deze pagina niets meer weg. Dat is een nieuwe, veel bruikbaardere diagnose dan het oude verhaal ("de bron Custom Functions rendert zijn optielijst niet") — dat was een symptoom, niet de oorzaak. Het bewijs:

      • De Max Items-waarde (25) is op zes StaggeredViews aangepast. In de UI toonde het veld daarna netjes leeg ("Leave empty for no limit…"), en bij een tweede test de waarde 1000. Een verse export toonde in beide gevallen onveranderd .take(25), zes keer. Leeg én een getal komen dus allebei niet door — het ligt niet aan een lege waarde.
      • Committen via een klik op lege paneelruimte, via een klik in het Variable Name-veld, én via een selectiewissel in de widget tree: alle drie geen verschil.
      • De pagina zelf is níét bevroren: Bob's verwijdering van de twee lege TextFields (punt E) kwam op dezelfde dag wél gewoon door, net als de hernummering van textController1textController die FlutterFlow daarbij zelf doorvoerde.

      Gevolg: zowel punt A (tab 6 koppelen) als punt C (Max Items weghalen) lopen via dit paneel en zijn daarmee niet uitvoerbaar. Bob heeft A twee keer geprobeerd, Claude ~15 keer plus zes keer op Max Items; het bestand kwam elke keer byte-identiek terug uit de export.

      Duplicaat-test GEDAAN 2026-09-10 (Bob's akkoord) — en die werkt NIET. Duplicate Page op Copy3 gaf HorecagelegenhedenOverzichtCopy3Copy. Daar meteen dezelfde ingreep geprobeerd: Max Items van 25 naar leeg op de eerste StaggeredView; de UI toonde weer netjes "Leave empty for no limit…". Verse export: 6× .take(25), precies als in het origineel. De blokkade zit dus niet in de opgeslagen pagina-data — een verse kopie erft 'm gewoon. Daarmee vervalt de beste hypothese en is er geen route meer die Claude of Bob in de builder kan proberen.

      ⚠️ Opruimen: HorecagelegenhedenOverzichtCopy3Copy moet weg (Bob — Claude verwijdert niets). De pagina heeft verder niets gedaan en wordt nergens naartoe genavigeerd; hij bestaat alleen als restant van deze test. Toevoegen aan P2-7.

      Wat dan wel: Bob's terugvaloptie van 2026-09-10 — "laten we dat probleem, max items, even voor wat het is; moeten we dat een punt maken voor de livegang." Dus A en C blijven open als livegang-punt. De enige overgebleven route is een melding bij FlutterFlow-support, want dit is aantoonbaar een bug aan hun kant: hetzelfde paneel accepteerde deze wijzigingen in september nog wél, en een ander paneel op precies dezelfde widget (Empty List Widget, zie P1-46) schrijft gewoon weg.

      A. Tab 6 (Verhuur, catering) alsnog koppelen. De enige StaggeredView die nog op de rauwe API-respons staat; alle andere vijf draaien op filterHorecagelegenheden(...). Recept staat bij P2-6, met items = alleVerhuurCatering. ⚠️ Claude komt hier niet doorheen — twee sessies, ~15 pogingen: in de Set-Variable-dialoog van uitgerekend deze ene StaggeredView rendert de bron Custom Functions zijn optielijst nooit. Uitklappen lukt (chevron slaat om), maar de rij filterHorecagelegenheden eronder verschijnt niet — niet na hoveren, niet na blind klikken op de verwachte positie, niet na de dialoog te sluiten en te heropenen, en niet na een volledige herlaad van de builder. Op de andere vijf tabs werkte exact dezelfde reeks wél. Niets kapot: de tab staat gewoon nog op zijn oorspronkelijke binding en werkt zoals voorheen.

      B. Categoriedropdown (was P2-6 stap 4) — eerst een ontwerpkeuze van Bob, daarna kan Claude bouwen. Er is een custom function nodig die de unieke categorie-waarden uit de dataset haalt. Claude heeft die bewust nog NIET aangemaakt: er zit een productkeuze in, en een eenmaal aangemaakte custom function kan Claude niet meer verwijderen (staande regel). Twee varianten:

      • Tab-bewust (volgt de oorspronkelijke spec): zes alleXxx-lijsten + TabBar Current Index (Widget State) + het pad = 8 argumenten. De dropdown toont precies de categorieën van de zichtbare tab. Nadeel: 8 bindingen in juist die dialoog die bij punt A vastliep.
      • Samengevoegd (simpeler): zes lijsten + het pad = 7 argumenten, toont alle categorieën over alle tabs heen. Kiest de gebruiker er een die op de actieve tab niet voorkomt, dan is de lijst leeg. Fors minder bindwerk. Zeg welke, dan bouwt Claude de functie én de dropdown (invoegen in de root-Column is bewezen werkend, net als bij het zoekveld). ⚠️ categorie is in de API een LIJST, geen string — bevestigd 2026-09-05 met curl op Arnhem (townid=25434): "categorie": ["Bioscoop"]. De bestaande filterHorecagelegenheden gaat daar al goed mee om, maar de nieuwe unieke-categorieën-functie moet de binnenlijst plat slaan en niet .toString() op het hele veld doen — anders krijg je opties als [Bioscoop] die nooit matchen. Aantallen in Arnhem: Activiteiten 2 items / 2 categorieën, Eetgelegenheden 20 / 19, Uitgaan 4 / 4.

      C. Max Items staat per tab op 25 — Bob's besluit 2026-09-09: weghalen. ⛔ Geblokkeerd, zie het kader hierboven. Dit is een ECHTE blocker, geen cosmetiek. Gemeten op productie in Amsterdam (townid=28695):

      tab items zichtbaar met take(25)
      Eetgelegenheden 245 25
      Uitgaan 75 25
      Activiteiten 42 25
      Overnachten 42 25
      Verhuur, catering 24 24
      Cultuur 14 14

      ⚠️ CORRECTIE 2026-09-10 op een eerdere bewering hier: het zoekveld doorzoekt WÉL de volledige lijst. De gegenereerde code is filterHorecagelegenheden(<volledige lijst>, zoekterm, …).toList().take(25).toList() — dus eerst filteren over alle 245, dán afkappen. Zoek je "pizza" in Amsterdam, dan worden alle 245 doorzocht en zie je de eerste 25 treffers. De eerdere formulering "het zoekveld doorzoekt alleen die 25" was fout.

      Wat er wél overblijft: zonder zoekterm zie je 25 van de 245 en kun je niet doorbladeren, want deze pagina heeft geen pager — geverifieerd: 0 PagedMasonryGridView, 0 PagingController, 0 infinite scroll, zes gewone MasonryGridView. De infinite scroll is er bij P2-6 bewust uitgehaald omdat client-side filteren de volledige dataset nodig heeft. In Amsterdam/Eetgelegenheden zijn 220 zaken dus alleen via het zoekveld bereikbaar, niet door te scrollen. (Een eerdere inschatting "je merkt er weinig van" was gebaseerd op Arnhem, grootste tab 20 items. Te klein om iets over limieten te zeggen; gebruik voortaan Amsterdam.)

      Haalbare verzachting zolang take(25) vastzit — voorstel voor Bob: sorteer de lijst in fetchAlleHorecagelegenheden (een custom action, en de Custom Code-editor werkt gewoon). Nu is de volgorde die van de view (nid aflopend), dus je krijgt 25 min of meer willekeurige zaken. Alfabetisch gesorteerd krijg je 25 voorspelbare, en samen met het zoekveld is dat werkbaar. Dit dekt meteen de openstaande wens "Horeca-overzicht sorteren" (zie de wachtrij bovenaan) zónder dat de Drupal-view aangepast hoeft te worden.

      Weghalen is veilig zodra het kan: er is al een harde begrenzing elders (fetchAlleHorecagelegenheden loopt door API-pagina's van 100 met maxPaginas = 10, dus max 1000 per tab), en alle zes de grids zijn MasonryGridView.builder — lazy, dus alleen zichtbare kaarten worden gebouwd. Er is geen paginering/infinite scroll meer op deze pagina (0 PagingControllers); dat is bewust, want client-side filteren kan alleen met de volledige dataset. Wil je ooit strakker begrenzen, doe dat in maxPaginas, niet in de weergavelimiet.

      D. Route omzetten (was P2-6 stap 7) — bewust uitgesteld tot A en B klaar zijn. Twee Navigate To-wijzigingen naar HorecagelegenhedenOverzichtCopy3: lib/shared/drawer_component/drawer_component_widget.dart en lib/horecagelegenhedenoverzicht/horecagelegenheid_current/horecagelegenheid_current_widget.dart. Daarna wordt de oude HorecagelegenhedenOverzicht een orphan → toevoegen aan P2-7. Ook Copy3 heet dan nog "Copy3" terwijl het de levende pagina is; hernoemen of niet is Bob's keuze.

      E. Twee lege TextField-placeholders opruimen. In tab 0 (Activiteiten) en tab 1 (Cultuur) staat nog het oude, ongebonden TextField met hint letterlijk "TextField". Op een toestel is dat een zwevend wit vak dat half over de TabBar valt — lelijk en verwarrend naast het echte zoekveld. De eerdere afspraak "laten staan tot P2-6 ze van een echte binding voorziet" is hiermee afgehandeld: het echte zoekveld staat nu bóven de TabBar, dus deze twee zijn overbodig. Claude verwijdert niets.

      F. Duplicaten in Drupal (los van de app, eigen afweging). Voor Arnhem/Eetgelegenheden staan dezelfde zaken dubbel in de view, met verschillende categorie-sets: nid 53455 en 50584 heten allebei "New York Pizza Arnhem Zuid", nid 53454 en 50583 allebei "New York Pizza Arnhem Centrum". De app toont ze dus terecht dubbel; het zit in de data.

      Niet oplosbaar, geaccepteerd: FlutterFlow zet op de On Change-trigger zelf een EasyDebounce van 2000 ms en die is nergens instelbaar (geen debounce-eigenschap op het widget, en de trigger-/actiemenu's bieden 'm niet). Live merkbaar: de lijst ververst ~2 s nadat je stopt met typen. Werkt correct, voelt traag.

      P2-7 · Eigenaar: Bob — Claude gooit hier niets weg (Bob expliciet, 2026-09-04). Claude mag deze lijst wél verifiëren en actueel houden; het daadwerkelijke verwijderen doet Bob zelf. Opschonen:

      ✅ Twee items hiervan zijn 2026-09-04 door Bob afgehandeld, bevestigd via verse export: HomeUitgaantabelKaartComponentCopy staat nu als lib/kanweg/kanweg_home_uitgaantabel_kaart_component_copy/, en lib/components/uitgaantabel_kaart_widget.dart (+ _model) is uit de export verdwenen. Streep die twee weg in de inventarisatie hieronder.

      ⚠️ Verse, complete inventarisatie 2026-09-04 (Claude, code-only op een verse export in de projectmap). Vervangt de losse, deels achterhaalde bevindingen hieronder — gebruik deze lijst, niet de oudere bullets. Methode: (a) importgraaf vanaf lib/main.dart (let op: FlutterFlow gebruikt root-relatieve imports '/pad/x.dart' — die moet je als lib/pad/x.dart resolven, anders lijkt vrijwel alles onbereikbaar); (b) elke in nav/nav.dart geregistreerde pagina nalopen op een pushNamed/goNamed elders. Uitkomst: 150 dart-bestanden, 16 dode eenheden in twee soorten.

      Belangrijk: git rm lost dit NIET op. Alle 16 zitten gewoon in een verse export, dus FlutterFlow genereert ze nog — ze bestaan dus nog in de builder en moeten daar verwijderd worden, anders staan ze na de eerstvolgende export weer terug. (Dat verklaart ook waarom de eerdere git rm-ronde van 2026-08-24 wél bleef zitten: dát waren stale mappen die de export al niet meer aanmaakte.)

      Toe te voegen zodra P2-6 klaar is: de oude HorecagelegenhedenOverzicht wordt orphan zodra de 2 Navigate-To's naar HorecagelegenhedenOverzichtCopy3 wijzen. Ook HorecagelegenhedenOverzichtCopy3 zelf heet dan nog "Copy3" terwijl het de levende pagina is — hernoemen of niet is Bob's keuze. En HorecagelegenhedenOverzichtCopy draagt sinds 2026-09-04 twee testwidgets (zie P2-6).

      🔄 Hertelling 2026-09-11 (Claude, code-only op de verse export in de projectmap). Dit vervangt de telling van 09-04 hieronder — gebruik deze. Methode gelijk gebleven (importgraaf vanaf lib/main.dart met root-relatieve imports, plus elke nav.dart-route nalopen op een verwijzing elders), met één correctie: een verwijzing kan over twee regels staan (XWidget\n .routeName), dus normaliseer whitespace vóór je grept — anders lijkt HorecagelegenhedenOverzichtProvinciePage ten onrechte dood. Uitkomst nu: 166 dart-bestanden, 135 bereikbaar.

      A′. Dode componenten die FlutterFlow nog exporteert (9): header_buttons_component_copymethartje, kaart_slider_uitgaan_s_comp, kaart_tabel_uitgaan_comp, kaart_tabel_uitgaan_s_comp, slider_uitgaan_component_small_current, kanwaeg_select_state_drop_down_component_copy, kanweg_home_uitgaantabel_kaart_component_copy, drawer_component_copy (nieuw t.o.v. 09-04), p_uitgaantabel_kaart_component_orgineel_met_kaartjeerin.

      B′. Pagina's in nav.dart zonder levende inkomende navigatie (12): Event, FavorietenCopy, HorecagelegenhedenOverzichtCopy, HorecagelegenhedenOverzichtCopy2, HorecagelegenhedenOverzichtCopy2Copy, Kanweg, KanwegHomeCopy, KanwegHorecagelegenhedenOverzichtSortPage, KanwegTestUpload, plus drie nieuwe:

      • KanwegHorecagelegenhedenOverzicht — de oude horecapagina, sinds taak 18 vervangen. Wordt alléén nog aangeroepen vanuit drawer_component_copy en kanweghorecagelegenheid_current_copy, die allebei zelf dood zijn. Ruim die twee op en deze pagina is volledig los.
      • KanwegHorecagelegenhedenOverzichtCopy3Copy — het restant van de Duplicate Page-test van 2026-09-10.
      • KanweghorecagelegenheidCurrentCopy.

      ✅ Twee eerdere regels kloppen niet meer:

      • lib/shared/geen_evenementen_component/ stond hieronder als "0 importeurs, niet weggooien tot Bob beslist". Hij is inmiddels in gebruikhorecagelegenheid_event_tabel_component_copy importeert 'm (de lege-staat op de agenda-tab van een horecapagina). Gewoon laten staan.
      • lib/uitgaanspaginas/home_uitgaantabel_kaart_component_copy/ zit niet meer in de export; wat er lokaal nog van staat is een stale map, geen builder-object.

      Niet-kandidaten (framework, komen elke export terug): vijf bestanden zijn onbereikbaar maar horen bij FlutterFlow zelf — flutter_flow/admob_util.dart, flutter_flow/flutter_flow_button_tabbar.dart, flutter_flow/router.dart, api_requests/browser_client_stub.dart, api_requests/get_streamed_response_web.dart. Niet opruimen.

      A. Componenten zonder één enkele importeur (7):

      1. lib/components/header_buttons_component_copymethartje_* — de oude header mét gemeentehartje; overbodig sinds P0-12's fix, zie daar.
      2. lib/components/kaart_tabel_uitgaan_comp_*
      3. lib/components/kaart_tabel_uitgaan_s_comp_*
      4. lib/components/kaart_slider_uitgaan_s_comp_*
      5. lib/evenement/slider_uitgaan_component_small_current/* — bevat ook de bekende carouselResponse-compilefout uit de notitie hieronder; die verdwijnt hiermee vanzelf.
      6. lib/kanweg/kanwaeg_select_state_drop_down_component_copy/*
      7. lib/uitgaanspaginas/p_uitgaantabel_kaart_component_orgineel_met_kaartjeerin/* én lib/uitgaanspaginas/home_uitgaantabel_kaart_component_copy/* (die laatste stond hieronder al als "mag nu weg").

      B. Pagina's met een route in nav.dart waar nooit heen genavigeerd wordt (9): Event, FavorietenCopy, HorecagelegenhedenOverzichtCopy, HorecagelegenhedenOverzichtCopy2, HorecagelegenhedenOverzichtCopy2Copy, Kanweg, KanwegHomeCopy, KanwegHorecagelegenhedenOverzichtSortPage, KanwegTestUpload.

      • ⚠️ Event is de pagina die de builder structureel weigert te hernoemen/verplaatsen/wijzigen ("Invalid Action", zie CLAUDE.md) — reken erop dat verwijderen daar ook kan mislukken.
      • ⚠️ HorecagelegenhedenOverzichtCopy2 is de intacte momentopname waaruit het herstel van 2026-09-01 is afgeleid. Dat herstel is af, dus hij mag weg — maar pas nadat P2-6 klaar is, voor het geval die pagina opnieuw beschadigd raakt.

      Twee eerdere claims hieronder kloppen niet meer:

      • lib/components/uitgaantabel_kaart_widget.dart (+ _model) — het "verouderde duplicaat" — bestaat niet meer, al opgeruimd.
      • HorecagelegenhedenOverzichtProvinciePage lijkt in een naïeve grep dood, maar wordt wél degelijk aangeroepen (drawer_component_widget.dart:1221, Provincie → Horeca). Niet weggooien.

      Nog steeds levend, ondanks de naam: lib/shared/geen_evenementen_component/ heeft 0 importeurs en staat dus technisch in categorie A — maar dit is een lege-staat-component ("geen evenementen"), precies wat P1-1 nodig had. Vermoedelijk gebouwd en nooit geplaatst. Niet weggooien voordat Bob heeft besloten of hij alsnog gebruikt wordt.

      (Oudere, deels achterhaalde bevindingen hieronder — laten staan voor de context, maar de lijst hierboven is leidend:)

      • Twee kopieën uit de sessie van 2026-08-31 (Claude), aangemaakt als vangnet vóór het P2-21-werk: homeCopy (lib/uitgaanspaginas/home_copy/) en HomeUitgaantabelKaartComponentCopy (lib/uitgaanspaginas/home_uitgaantabel_kaart_component_copy/). homeCopy is afgehandeld (2026-08-31, Bob's keuze): hernoemd naar kanwegHomeCopy en verplaatst naar de map kanweg — staat nu op lib/kanweg/kanweg_home_copy/, klasse KanwegHomeCopyWidget. Rename én move gingen zonder de "Invalid Action"-blokkade die de Event-pagina destijds gaf; bevestigd via verse export. HomeUitgaantabelKaartComponentCopy mag nu weg — het was het herstelpunt voor de pagination-omzetting van P2-21, en die taak is op 2026-09-03 volledig afgerond en visueel geverifieerd. Zelfde behandeling als homeCopy: hernoemen met kanweg-prefix en naar de map kanweg.
      • Nieuw bevestigd dood (2026-08-31, Claude, code-only; geverifieerd met grep -rn "components/<bestand>" lib/ — 0 importeurs): lib/components/uitgaantabel_kaart_widget.dart + ..._model.dart. Dit is een verouderd duplicaat van het live component lib/uitgaanspaginas/uitgaantabel_kaart/ — zelfde klassenaam UitgaantabelKaartWidget, maar de oude versie heeft nog parameter9 waar de live versie nid heeft, en mist het blurhash-werk uit P2-12. Alle drie de gebruiksplekken (favorieten, favorieten_copy, p_uitgaantabel_kaart_component) importeren de uitgaanspaginas-versie. Ook 0 importeurs: lib/components/header_buttons_component_copymethartje_widget.dart (mogelijk relevant voor P0-12's dubbele-hartjes-vraag — eerst daar checken vóór weggooien).
      • Merge kaartTabelUitgaanComp + kaartTabelUitgaanSComp — Bob doet dit zelf ("ik kijk er zelf naar").
      • lib/kanweg opnieuw leegmaken indien teruggekomen na een latere export-pull, plus eventuele nieuwe losse dode componenten in lib/evenement/.
      • (5 bevestigde orphan-mappen — lib/evenement/uitgaan_tabel_component(_small), lib/kanweg/z_zuitgaantabel_component(small), lib/uitgaanspaginas/uitgaantabel_kaart_component — verwijderd 2026-08-24 (Bob, git rm -r, gecommit). flutter analyze na afloop: geen nieuwe errors.)
      • Nieuw gevonden (2026-08-24, Claude, sessie 46, tijdens P1-10- verificatie): lib/horecagelegenhedenoverzicht/horecagelegenheden_overzicht_sort_page/ (oude naam, klasse HorecagelegenhedenOverzichtSortPageWidget) is nu dubbel-dood — de builder heeft dit component al hernoemd naar Kanweg... (nieuwe map lib/kanweg/kanweg_horecagelegenheden_overzicht_sort_page/, en nav.dart/index.dart wijzen ook al naar die nieuwe naam), maar de oude map bleef lokaal achter i.p.v. door de export verwijderd te worden. Bevestigd via grep: 0 referenties naar de oude klassenaam buiten zijn eigen bestand. Verwijderd (2026-08-25, Bob, git rm -r). lib/evenement/slider_uitgaan_component_small_current/ (de bekende carouselResponse-compile-fout uit de notitie hieronder) staat daarentegen nog wél in de verse export — dus nog niet door Bob opgeruimd, blijft een apart punt.
      • Nieuw gevonden, echte compile-fout in dode code (2026-08-14, Claude, flutter analyze ná het committen van fb6b224): lib/evenement/slider_uitgaan_component_small_current/slider_uitgaan_component_small_current_widget.dart:109Undefined name 'carouselResponse'. Root cause: Bob's ZZZhomeSliderCall-verwijdering (P2-7 hierboven) haalde de omringende FutureBuilder weg (die carouselZZZhomeSliderResponse via snapshot.data! leverde) maar de binnenste Builder verwijst nog naar de oude variabelenaam, nu ongedefinieerd — een onvolledige refactor-restant, geen nieuwe eigen wijziging. Geen impact op de gebouwde app: bevestigd via grep dat geen enkel ander bestand SliderUitgaanComponentSmallCurrentWidget importeert (al bekend als dood, zie de ZZZhomeSliderCall-notitie hierboven), en een verse flutter build apk --debug slaagde gewoon (✓ Built build/app/outputs/flutter-apk/app-debug.apk) — Dart compileert alleen bestanden die vanaf main.dart bereikbaar zijn, dus deze fout raakt de live app niet. Wel relevant: dit component bestaat sowieso al niet meer in de builder (Bob kon het niet terugvinden, zie de eerdere UitgaantabelKaartComponentWidget-notitie hierboven) — waarschijnlijk hetzelfde soort orphan. Als dit ooit via de builder verwijderd wordt (samen met de andere lib/evenement/- opschoning), is deze compile-fout vanzelf opgelost; tot die tijd geen actie nodig, alleen genoteerd zodat een toekomstige flutter analyze-treffer hier niet als nieuwe/onverklaarde bug wordt aangezien.
      • Eén browse-by-category-patroon i.p.v. twee: nu Home landelijk is, bepalen hoe Home's categorieën en PUitgaanPage's provincie/ gemeente-gescoopte categorieën zich tot elkaar verhouden.
      • Dubbele Provincie/Gemeente-blok in het menu — exacte structuur in kaart gebracht (2026-08-24, Claude, code-only, drawer_component_widget.dart), klaar voor een beslissing: de drawer heeft 2 identieke blokken van elk 6 links (2 headers + 12 sub-items totaal), allebei naar dezelfde PUitgaanPageWidget, enige verschil is welke App-State-variabele als plaats-scope meegaat:
        • "Provincie" (FFAppState().provincieSelectId): Uitgaan, Activiteiten, Cultuur, Films, Jeugd (services_3 t/m 7),
        • Horeca (→ HorecagelegenhedenOverzichtProvinciePage).
        • "Gemeente" (FFAppState().gemeenteSelectId): exact dezelfde 6 labels/services-ids, Horeca → HorecagelegenhedenOverzichtWidget (dus wél de andere pagina-variant dan het Provincie-blok, consistent met de bestaande provincie/gemeente-scoping elders in de app).
        • Los daarvan, niet gedupliceerd: "Thuis bezorgen". 3 opties om aan Bob voor te leggen (geen van alle uitgevoerd, puur prep):
        • Eén dynamisch blok i.p.v. twee — toont automatisch de 6 links gescoopt op wat de gebruiker net koos (provincie òf gemeente via SelectStateDropDownComponent), halveert het menu naar 6 items. Kost: gebruiker kan niet meer in 1 tik wisselen tussen "heel mijn provincie" en "alleen mijn gemeente" browsen zonder terug naar de select-pagina.
        • Beide blokken laten staan, maar één ervan inklapbaar/dichtgeklapt als default (accordion) — geen functionaliteit verloren, wel minder eerste-oogopslag-drukte.
        • Zo laten — als Bob "in 1 tik zowel provincie- als gemeente-breed kunnen browsen" waardevol genoeg vindt, is dit eerder een dichtheids-kwestie dan een bug; kan als P2-polish blijven staan tot na livegang.
      • EventWidget-routegesloten zonder wijziging (2026-08-25, Bob's besluit). Bevestigd orphan (2026-08-05, Claude, grep): de route staat correct geregistreerd (nav.dart, pad /event, param nid) en geëxporteerd (index.dart), maar geen enkele pushNamed/navigatie-aanroep in de hele codebase gaat er ooit naartoeEventCurrent (pad /eventCurrent) is overal de daadwerkelijk gebruikte event-detailpagina. Alleen bereikbaar via een handmatige directe URL. Bob probeerde de pagina op te ruimen (hernoemen naar kanweg_-prefix, verplaatsen naar de kanweg-map, een component eraf halen) — elke van die acties geeft in de builder dezelfde generieke "Invalid Action: The most recent action would have caused a crashing error, so we've undone it for you"-toast en wordt automatisch teruggedraaid, ook na een harde browser-reload. Grep bevestigt dat er in de geëxporteerde code geen enkele referentie naar deze pagina bestaat buiten zichzelf — de blokkade zit dus in FlutterFlow's eigen interne project-graaf, niet in iets dat via de export zichtbaar/oplosbaar is. Besluit: met rust laten. Geen functioneel risico (100% onbereikbaar in de live app, ongeacht deze builder-staat) — alleen wat overbodige code die niet opgeruimd kan worden. Niet verder proberen tenzij een toekomstige FlutterFlow-update dit vanzelf oplost.
      • Volledige API-call-audit (2026-08-13, Claude, grep op alle klassenamen buiten api_calls.dart zelf, gecombineerd met de live-bereikbaarheid-bevindingen uit P1-19/P2-7): van de 22 gegenereerde API-calls in de builder zijn er 11 ongebruikt. EstablishmentsNewCall (regel hierboven) was hier al 1 van, nu volledig lijstje:
        • Vervangen door custom code, veilig te verwijderen: login (LoginCall) en getcsrf (GetcsrfCall) — de echte login-flow loopt via de custom action drupalLogin (lib/custom_code/actions/drupal_login.dart), die zelf rechtstreeks http.post naar het Drupal-login-endpoint doet én het CSRF-token al uit diezelfde loginrespons haalt (data['token']) — de aparte GetcsrfCall/services/session/token-aanroep is dus overbodig geworden. Bevestigd: geen van beide klassen wordt nog ergens aangeroepen. Bevestigd verwijderd (2026-08-14, Claude, grep op class LoginCall/class GetcsrfCall in api_calls.dart: 0 treffers) — Bob deed dit al in zijn 2026-08-13-build/testronde, nu gecommit (fb6b224).
        • Test/scratch-duplicaten — bevestigd verwijderd (2026-08-14, Claude, zelfde grep-check, allemaal 0 treffers in api_calls.dart): ZZ userEstablishments TEST (ZZUserEstablishmentsTESTCall), ZZZhomeSlider (ZZZhomeSliderCall), homeSlidershortDate (HomeSlidershortDateCall), ZZhome uitgaan (ZZhomeUitgaanCall), zzEstablishmentEvents Copy (ZzEstablishmentEventsCopyCall), EstablishmentsNew (EstablishmentsNewCall, al bekend), QueryCityId (QueryCityIdCall) — 7 van de 8 voorgestelde verwijderingen zijn doorgevoerd (zelfde build/testronde, gecommit fb6b224). Enige die bleef staan: FavorietenAgendaTESTKANWEG (FavorietenAgendaTESTKANWEGCall) — nog steeds aanwezig in api_calls.dart, nog steeds geen enkele live-referentie (bevestigd). Losse, kleine restopruiming.
        • ⚠️ Poging door Claude (2026-08-14), 2x geprobeerd — nieuw bevestigd blocker-patroon, geen wijziging aangebracht. API Calls-paneel → FavorietenAgendaTESTKANWEGDelete → bevestigingsdialoog ("Delete this API Call from the project?") → Delete: de dialoog sluit netjes (geen freeze, i.t.t. de bekende geneste-Set-Variable-freezes elders in CLAUDE.md), maar de call staat na een page-reload gewoon weer terug in de lijst — 2x gereproduceerd (2e poging zelfs met een expliciete Synced-check vóór het navigeren). Een losse verse flutterflow export-code ná de eerste poging bevestigde hetzelfde: class FavorietenAgendaTESTKANWEGCall nog gewoon aanwezig in api_calls.dart. Dit is niet hetzelfde patroon als de al bekende widget-tree-klikproblemen (dit paneel is een platte lijst, geen canvas/tree-coördinaten, en de Delete-flow zelf werkt zichtbaar/klikbaar) — eerder een nieuw voorbeeld van het bredere "ziet er opgeslagen uit in de builder-UI, bereikt nooit de export"-patroon (zie P1-13/P0-3 punt 1 voor eerdere instanties). Kant-en-klaar voor Bob: API Calls-paneel (linker sidebar-icoon onder "Connect") → FavorietenAgendaTESTKANWEGDelete-knop onderaan → Delete bevestigen — zelfde stappen, kost hem waarschijnlijk hetzelfde (nog niet getest of het bij hem wél persisteert), maar dit hoort niet nog een 3e keer door Claude geprobeerd te worden zonder nieuwe informatie.
        • Nog niét dood, wel ongebruikt — laten staan: FavorietenAgenda (FavorietenAgendaCall) — geen enkele huidige live-referentie, maar dit is de call die P1-7's geplande "Persoonlijke agenda"/Favorieten-tabs straks nodig hebben. Niet verwijderen, gewoon nog niet aangesloten.
        • Live/actief (11, ter controle, niet aanraken): homeTabel, HomeSlider, Uitgaanstabel, UitgaanSlider, EstablishmentInfo, HorecagelegenheidEvents, gemeenten, provincies, Evenement, Establishments, requestNewPassword. Kanttekening 2026-08-14: EstablishmentsCall is in dezelfde build/testronde hernoemd naar HorecagelegenheidoverzichtCall (callName 'Horecagelegenheidoverzicht', zelfde endpoint/params horcat/townid/displayId) — geen verwijdering, puur een naamswijziging; alle aanroepende widgets (horecagelegenheden_overzicht*) zijn consistent meeveranderd, bevestigd via grep, geen dode/gebroken referenties.
        • Verwijderen kan gewoon via de builder (API Calls-paneel → call selecteren → verwijderen) — geen custom code/lokale bestanden bij betrokken, dus geen export-sync-risico zoals bij widget-edits.

      (Laag-risico-restpunt uit P0-3 (default-locatie 28666/28694 zonder naam) afgerond — bevestigd 2026-08-15 via de export-sync (zie sessienotitie bovenaan): select_state_drop_down_component_widget.dart zet nu ook provincieSelectNaam = 'Noord-Holland' en gemeenteSelectNaam = 'Amsterdam (gemeente)' naast de bestaande id-defaults. Bijvangst in dezelfde builder-sessie: een losse debug-SnackBar ("Provincies: X") die bij elke provincie-call verscheen is ook verwijderd.)

      P2-8 · Opgegaan in P1-7 (2026-08-09). Hartje-tap op gemeente-/ provincienaam om te favorieten is nu onderdeel van de bredere Favorieten-pagina/profielscherm-taak — zie P1-7 hierboven voor scope en status.

      (P2-11 afgerond 2026-08-21 — Claude, builder, sessie 42: categorie-tags van bordeauxrood (#9A141D) naar het groen van uitgaanskrant.com zelf (#09B34A) op de 2 bevestigde plekken — TagCategorieComponent (gedeeld door 9 pagina's/componenten) en HorecagelegenheidoverzichtKaart (eigen losse kopie). Bevestigd via verse export + flutter analyze (alleen bestaande info/warning-lints, geen nieuwe fouten): grep -rn "0xFF9A141D" lib/ geeft nu 0 treffers meer in widget-code (alleen nog de losse, ongebruikte theme-definitie in flutter_flow_theme.dart:341 — bewust niet aangepast, geen widget verwijst ernaar en de kleurwaarde die de builder daarvoor toont wijkt af van wat in de code staat, dus laagste risico om met rust te laten). Bordeauxrood blijft ongewijzigd voor branding/CTA's/hartjes. Oranje (#FF680D) en het donkere navchrome uit dezelfde analyse zijn grotere, niet-uitgevoerde vervolgstappen — zie de oorspronkelijke analyse in de sessiegeschiedenis als dat ooit weer relevant wordt. Uit deze lijst verwijderd.)

      *(P2-12 afgerond 2026-08-21, sessie 43 — Claude, builder, bevestigd via verse export + flutter analyze (0 errors). Geen laad-placeholder/ skeleton bij afbeeldingen (carousel-kaarten, horeca-logo's) — gezien 2026-08-20 op zowel telefoon als tablet: een kaart toonde eerst een lege witte vlek, pas 1-2 seconden later de venue-foto/het logo. Geen crash/bug, maar oogde bij een trage verbinding als een kapotte/lege kaart.

      Werkend recept: niet via een handmatige Shimmer/Container-wrap (geen los "Placeholder"-veld beschikbaar op FlutterFlow's Image-widget), maar via de bestaande "Use Blur Hash"-toggle (Image-widget → rechterpaneel → zoek "blur") + een vaste, algemene Blur Hash String (L6PZfSi_.AyE_3t7t7R**0o#DgR4 — een neutrale grijze placeholder-hash, niet gekoppeld aan de echte foto, want dit project heeft geen per-afbeelding blurhash-data uit Drupal). Genereert automatisch een OctoImage met placeholderBuilder: (_) => Image(image: BlurHashImage('...'), fit: BoxFit.cover) rond de bestaande CachedNetworkImageProvider — geen widget-tree-wijziging nodig, puur 2 property-velden op de bestaande Image-node. Val op: het "Blur Hash String"-tekstveld registreerde bij vrijwel elke widget de eerste typing niet (bleef leeg na Tab/blur), de tweede poging (zelfde klik-en-typ-actie herhaald) lukte steevast wel — altijd verifiëren via een verse export.

      Alle 9 live CachedNetworkImage-plekken afgerond (2 bevestigd confirmed-dode orphans bewust overgeslagen — uitgaantabel_kaart_component_widget.dart en evenement_component_widget.dart, zie P2-7): horecagelegenheidoverzicht_kaart_widget.dart, home_uitgaantabel_kaart_component_widget.dart, horecagelegenheid_current_widget.dart (foto-carousel), event_current_widget.dart (2x — foto-carousel + venue-logo), evenement_horecagelegenheid_widget.dart (2x — foto-carousel + establishment-logo), horecagelegenheid_event_tabel_component_copy_widget.dart, uitgaantabel_kaart_widget.dart (Favorieten Tab 1). Uit deze lijst verwijderd.)*

      P2-13 · Eigenaar: Bob (Drupal-theme, geen FlutterFlow/Claude-taak — puur advies, niet uitgevoerd). Op Bob's vraag (2026-08-21) ook de bron-website zelf (uitgaanskrant.com, niet de app) doorgelopen op look&feel, los van wat de app ervan kan overnemen (zie P2-11). Geen van onderstaande is een bug, puur observaties die het waard zijn om te overwegen bij een volgende theme-update:

      1. Dubbele onboarding-content bij eerste bezoek: naast de gewone cookie-consent-balk verscheen ook een los infoblok ("1. Maak een account aan... 2. Word lid... 3. Ga naar je favoriete gemeente...") dat het scherm vult en apart gesloten moet worden — twee dialogen na elkaar voordat een nieuwe bezoeker de site ziet. Overweeg er 1 van te laten vervallen of te combineren.
      2. Zware advertentie-aanwezigheid direct boven de vouw: rechter sidebar toont op de homepage meteen 2-3 gestapelde ad-achtige banners (evenementen-promotie, externe advertenties) naast de content — oogt druk/gedateerd vergeleken met de rest van de site. Kan geen kwaad om te bekijken of dit iets minder dicht op elkaar kan.
      3. Kleurenpalet is rijker dan de app maar niet overal doelbewust consistent — groen (categorie-tags), oranje (titels/links/mobiel- menu-balk), blauw (actieve tab op de evenement-detailpagina), bordeauxrood (logo) staan naast elkaar zonder dat meteen duidelijk is welke kleur welke rol heeft (nav vs. content vs. status). Werkt in de praktijk prima, maar een kort "welke kleur betekent wat"- documentje zou toekomstige theme-wijzigingen consistenter maken (en is meteen de bron voor P2-11's app-kleuren hierboven).
      4. Kaartranden ogen gedateerd (dunne 1px grijze randen, standaard Bootstrap-panel-stijl) — een subtiele schaduw i.p.v. een harde rand zou de site iets moderner laten ogen, puur cosmetisch.
      5. Positief, waard om te behouden: de mobiele "☰ Menu"-balk (volle breedte, opvallend oranje, niet te missen) is duidelijker dan menig moderne site's kleine hamburger-icoontje — geen wijziging nodig, eerder een patroon om naar de app te kopiëren (zie P2-11/P1-27: de app's hamburger is een klein rood knopje, minder opvallend).

      P2-15 · Eigenaar: Onbepaald — 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 fillColors (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: InkWellMaterialListTile 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 ColumnText 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 showDatePickershowTimePicker → 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: kanwegproductie (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 ontbreektstadsactiviteitAanmaken 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: RowColumn, 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 ontbreektstadsactiviteitAanmaken 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.

      Overige restpunten (sessie 2026-08-31, live pair-fix):

      1. ✅ 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.
      2. 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.
      3. 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 productiedrupal/backend, want de naam klopt niet meer zodra hij naar devbob wijst.
      4. 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), 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.


        (Onderstaande taken komen uit de merkanalyse web-vs-app van 2026-08-30. Volledige onderbouwing met screenshots: Designbrug-rapport. Afvinkbare werklijst: Designbrug werklijst.)

        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):

        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 + showDatePickershowTimePicker → 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 evenementCreateAF (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: sessionNameuserSessionname, sessionIduserSessionId, tokenuserToken, horecagelegenheidNid→page state createHorecagelegenheidNid, titelTextFieldTitel, datumStartdatumVoorApi(TextFieldDatumStart), datumEinddatumVoorApi(TextFieldDatumEind), omschrijvingTextFieldOmschrijving, entreeTidcreateEntreeTid, entreePrijsTextFieldEntreeprijs, toelichtingEntreeTextFieldToelichtingEntree, ticketsUrlTextFieldTicketsURL, websiteUrlTextFieldWebsiteURL, categorieTidscreateCategorieTids, logoFidcreateLogoFid.

        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.

        P2-22 · Eigenaar: Bob (P2-15-werk; blokkeert de export NIET).

        ⚠️ Ronde van Claude, 2026-09-13 — geen oplossing, wél drie kandidaten definitief afgevoerd. Lees dit vóór je er zelf tijd in steekt:

        • De DropDownGemeenten-mijngemeenten-binding is gezond. Define Options Values staat op MijnStadsrechten ResponseJSON Body → JSON Path $[:].tid, met de tooltip "From Column Call" — hij hangt dus correct aan de query op de omhullende Column, niet aan een losse call. Type in de dialoog: List < String >. Define Options Labels idem met $[:].titel. De Visibility-conditie ($[0].titel is set) staat er weer gewoon in — de notitie hieronder dat die op Unset bleef staan, is dus achterhaald.
        • De DropDownProvincie-binding is óók gezond, zelfde vorm: provincies Response → JSON Body → $[:].provincieid / $[:].provinciename. Backend Query op het widget staat gewoon op provincies, zonder variabelen.
        • Geprobeerd en het hielp niet: op de API Call provincies stonden de JSON-paths $[:].provincieid en $[:].provinciename als String gedeclareerd terwijl $[:] een lijst oplevert (de derde, provinciedescription, stond wél op List<String>). Dat is een echte "return type mismatch" en leek dus de voor de hand liggende dader. Alle drie staan nu consistent op List < String > — geverifieerd via verse export (static List<String>? provincieId(...)), de dropdown-code bleef byte-identiek en dart analyze geeft 0 errors. Maar de teller bleef op 2 staan. De wijziging is blijven staan (het type klópt nu tenminste, en niemand roept die getters aan — alleen ProvinciesCall.call() wordt gebruikt); zoek dus niet nóg eens in die hoek.

        Wat overblijft: de fout zit in opgeslagen widget-state die de UI niet toont. Kansrijkste resterende zet is de aanpak die hieronder al stond — binding leeggooien met het reset-icoontje en van nul opbouwen (niet opnieuw bevestigen, dat schrijft dezelfde waarde terug). Dat is destructief als het misgaat, dus dat hoort in jouw browser, niet via coördinaat-klikken.

        Vier openstaande fouten in het Issues-paneel, stand 2026-09-03. Geen van de vier houdt flutterflow export-code tegen (geverifieerd), en dart analyze op een verse export geeft 0 errors — maar ze houden de rode teller bezet en maskeren daarmee nieuwe fouten.

        Twee Custom Action Call-fouten — argument categorieTids en argument fotosFids "is not set properly", op de aanroep van evenementCreate in uitgaansevenement_aanmaken. Dit is het bekende List-argument-patroon uit CLAUDE.md: een List<String>-argument mag niet op Unset blijven, ook niet als het in Dart nullable is. Recept: Value-potlood → Set Variable → Inline Function → expressie <String>[]"Return Type (Optional)" openklappen → Check Errors → Confirm. Dat openklappen forceert het wegschrijven. Werkt alléén als er al een binding staat; bij een verse Inline Function op een UNSET- waarde schrijft Confirm niet weg (5+ pogingen 2026-08-31) — dan is de knop weggooien en opnieuw opbouwen sneller.

        Twee Property Override-fouten op stadsactiviteitAanmaken:

        • DropDownGemeenten-mijngemeenten → "Invalid API call configuration"
        • DropDownProvincie → "return type mismatch"

        Al uitgesloten (2026-09-03, Claude — DropDownGemeenten-mijngemeenten): de Visibility → Conditional is NIET de oorzaak. Bob heeft hem uitgezet (geverifieerd: de if (getJsonField(..., r'$[0].titel') != null) verdween uit de export) en de teller bleef gewoon op 2 staan, met de fout die nog steeds naar diezelfde dropdown springt. Ook uitgesloten: het widget heeft geen eigen Backend Query ("Add Query"-knop), en de query op de omhullende Column is correct geconfigureerd (MijnStadsrechten, session_name → App State userSessionname, sessiduserSessionid). De conditie zelf was ook gezond: bron MijnStadsrechten Response → JSON Body → JSON Path $[0].titel, met tooltip "From Column Call". Wat overblijft als enige kandidaat: één van de twee Options-bindingen, Define Options Values ($[:].tid) of Define Options Labels ($[:].titel). Aanpak: die binding niet opnieuw bevestigen (dat schrijft dezelfde waarde terug en hielp al niet) maar met het reset-icoontje leeggooien en opnieuw opbouwen.

        ⚠️ Valkuil, 2026-09-03 aan den lijve ondervonden: een Visibility → Conditional uitzetten WIST de conditie. Weer aanzetten levert een lege conditie op (Unset in rood) en dus géén guard. Zet je 'm uit om iets te testen, noteer dan eerst de conditie — je moet hem daarna volledig opnieuw opbouwen. Stand bij het afsluiten van sessie 2026-09-03: de conditie op DropDownGemeenten-mijngemeenten staat nog op Unset (Bob heeft 'm geprobeerd terug te zetten, maar drie verse exports op rij tonen de if niet terug). Terugzetten: First Value → MijnStadsrechten Response → JSON Body → JSON Path $[0].titel → operator Is Set.

        Eerder al uitgesloten (2026-09-01, Claude): de API-calls zelf zijn in orde (MijnStadsrechten en provincies: absolute URL, GET, variabelen en headers correct); de Backend Query op DropDownProvincie staat gewoon op provincies; en het opnieuw bevestigen van zowel "Define Options Values" ($[:].provincieid) als "Define Options Labels" ($[:].provinciename) via de Set-from-Variable-dialoog laat de teller ongemoeid. De foutmelding wijst dus naar iets anders dan die twee bindings. Bob kent de bedoelde datastroom van deze pagina en ziet in zijn eigen browser sneller wat er mis is dan Claude via coördinaat-klikken.

        (De twee identieke Property-Override-fouten op de kopiepagina zijn vervallen: Bob heeft stadsactiviteitAanmakenCopy en ...Copy2 op 2026-09-02 weggegooid. Daarmee zijn ook de 3 compilefouten uit het oude P2-23 verdwenen — die taak is geschrapt.)

        P2-18 · Eigenaar: Bob — grotendeels afgehandeld 2026-09-04, 2 punten open (Bob: "zo houd ik het" — alleen oppakken als hij erop terugkomt). Menu-iconen in drawer_component_widget.dart, Gemeente- tegenover Provincie-blok. Stand na Bob's ronde, geverifieerd via verse export:

        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 nee uit categorieen/index; ongeldige tid → 400
        logo_fid int nee fid uit bestand_upload/upload
        fotos_fids array nee max 5, meer → 400
        Categorie Gemeente Provincie
        Uitgaan music_note_outlined music_note_outlined
        Activiteiten flagCheckered sports_score ❌ nog verschillend
        Cultuur piedPiperAlt piedPiperAlt ⚠️ gelijk, maar zie hieronder
        Films movie_outlined movie_outlined
        Jeugd emoji_people emoji_people
        Horeca restaurant_sharp restaurant_sharp

        ⚠️ Bij Cultuur heeft de verkeerde kant gewonnen: article_sharp (krant) is vervangen door piedPiperAlt, het bedrijfslogo van Pied Piper uit de HBO-serie Silicon Valley (een FontAwesome-merkicoon). Dat staat nu dus op beide plekken in plaats van nergens. Terugzetten is één swatch: article_sharp past bij "Cultuur & Info" én bij het krantenmerk. Bob is hierop gewezen en houdt het voorlopig zo.

        *(P2-19 afgerond 2026-09-04 — Bob, builder. Iedereen begint nu landelijk op Home, zoals de site; voorheen kreeg een ingelogde gebruiker het provincie/gemeente-keuzescherm, waardoor inloggen als drempel voelde. App Settings → Entry Page én Logged In Page allebei op home. FlutterFlow toont daarbij "Warning: Entry Page and Logged In Page should be different" — dat is een generiek advies, geen fout: de waarde slaat gewoon op, geverifieerd via verse export (loggedIn ? HomeWidget() : HomeWidget(), ook in de errorBuilder). Let op dat "Entry Page" de tak voor uitgelogde bezoekers is en "Logged In Page" die voor ingelogde — bij de eerste poging waren die verwisseld, waardoor een nieuwe bezoeker juist een verplicht keuzescherm als eerste indruk kreeg. Tegelijk gebouwd: HeaderButtonsComponent toont nu FFAppState().gemeenteSelectNaam met On-Tap pushNamed(SelectprovinciegemeenteWidget.routeName), zodat zichtbaar is wélke gemeente actief is en hoe je die wisselt.)*

        P2-19 restpunt · Eigenaar: Bob. Nu iedereen op Home start, is nergens zichtbaar welke gemeente actief is — de lijstschermen tonen dat niet, en de gemeentekeuze is alleen nog via de drawer ("Zoek stad", bovenaan) te bereiken. Voorstel uit de merkanalyse: de gekozen gemeente als klikbare regel in de header, bv. "Amsterdam · wijzigen". De bouwstenen liggen klaar: App State gemeenteSelectNaam (persistent via secure storage, naast gemeenteSelectId, default '28694') en HeaderButtonsComponent (lib/components/header_buttons_component_widget.dart) — een Row met op dit moment alleen een hamburger-knop (met AlignedTooltip "Menu openen") en een conditionele terugknop achter parameter showBackButton. Daar een Text bij die aan gemeenteSelectNaam hangt, met een On-Tap naar het keuzescherm. Let op de bekende valkuil: bij een geselecteerde component-root staat het Component Name-veld op vrijwel dezelfde plek als "Search properties...", dus verifieer vóór het typen welk widget geselecteerd is.

        P2-20 · AFGEWEZEN 2026-09-08, Bob's besluit: niet doen. De app houdt Roboto voor de koppen, de website houdt Droid Serif. Bewust verschil tussen app en site, geen actie meer nodig. De onderbouwing hieronder blijft staan voor als het besluit ooit heroverwogen wordt.

        Serif-koppen invoeren: de site zet elke inhoudelijke titel in Droid Serif bold — dát maakt het krantengevoel, en het is het enige merkkenmerk dat je niet met kleur kunt namaken. Noto Serif Bold is de directe opvolger en zit in Google Fonts. Toepassen op kaarttitels en detailtitels, interface blijft Roboto. Serif is breder, dus daarna alle kaarttitels op afbreken/overlopen nakijken. Ook een legitieme uitkomst: niet doen — dan is de app herkenbaar op kleur maar mist hij de kranten-uitstraling.