## ▶ Nu aan de beurt (stand 2026-09-13)
### ✅ Verbindingsmelding in de header — AF (2026-09-13, Bob + Claude)
Gebouwd en live geverifieerd op de telefoon-emulator met een **verse export van
de echte FlutterFlow-staat** (dus niet een nabouw). Alle drie de paden:
| situatie | resultaat |
|---|---|
| online | geen melding, hamburger zichtbaar, **0** render-fouten |
| offline | rode balk onderin met wolkje + "Opnieuw", **0** render-fouten, **0** `toList`-crashes |
| netwerk terug + tik op "Opnieuw" | balk weg, Home **volledig hersteld** (slider + tabs gevuld), 0 fouten |
**Hoe het in elkaar zit**, voor als het ooit aangepast moet worden:
- App State **`geenVerbinding`** (Boolean, niet persisted).
- Custom Action **`checkVerbinding`** — HEAD op de site-root (200, 0 bytes,
±57 ms), zet de vlag via `FFAppState().update(...)`.
- Custom Widget **`VerbindingsBanner`** — rendert zelf `SizedBox.shrink()` en
toont de melding via de `ScaffoldMessenger`. Staat als laatste kind in de
`Row` van `HeaderButtonsComponent`, in een Container van 20×20 (FlutterFlow
eist maten; in een Row kost dat alleen 20 px horizontaal en dat is
onschadelijk — **in een Column zou een hoogte wél schadelijk zijn**).
- De code staat in `snippets/` en overleeft daar een export.
⚠️ **Twee valkuilen die hier zijn opgetreden — relevant zodra er nóg iets aan
de header toegevoegd wordt:**
1. **Wrap de knoppen-Row NOOIT in een Column.** De header zit in een
`FlexibleSpaceBar.background` met een **vaste hoogte van 74 px**;
`Align > Row` krijgt die keurig opgelegd, maar een `Column` geeft zijn
kinderen **onbegrensde** hoogte — de Row neemt dan zijn natuurlijke ±200 px
en je krijgt `RenderFlex overflowed by 166 pixels`. **En stiller maar erger:
de hamburgerknop verdwijnt dan uit de header op alle elf pagina's, zonder
enige melding in de log.**
2. **Eigen imports in Custom Code worden niet gecontroleerd door de editor.**
`checkVerbinding` heeft `import 'package:http/http.dart' as http;` nodig,
direct onder de `// DO NOT REMOVE`-regel. Zonder die regel slaat de editor
gewoon op en meldt niets — pas de Gradle-build faalt met
`Error: Undefined name 'http'`.
📌 **Nog niet gedaan:** het widget staat alleen in `HeaderButtonsComponent`, dus
niet op `mijnProfiel`, `wachtwoordVergeten`, `stadsactiviteitAanmaken` en
`uitgaansevenementAanmaken`. Op de twee **aanmaakpagina's** is het wel zinvol
(een formulier verzenden zonder netwerk) — daar het widget ergens in de
bestaande layout zetten, het neemt toch geen ruimte in.
### 🐞 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.
### 🚫 Bob's besluiten 2026-09-13 — niet meer voorstellen
- **Titels met onzichtbare tekens opschonen (was taak 42 / P1-47): AFGEWEZEN.**
Bob: "we wachten tot horecagelegenheden het zelf aanpassen, of we passen het
als editors een keer aan. Het is heel veel oude data, daar ga ik geen tijd in
steken. Als ik er tegenaan loop, pas ik het aan." **Niet opnieuw agenderen.**
Ter informatie, zodat het effect bekend is: 18 van de eerste 100 zaken in het
horeca-overzicht staan hierdoor buiten de alfabetische volgorde, en ze staan
allemaal *bovenaan* (MySQL sorteert op de rauwe `node.title`, een spatie komt
vóór de A). De eerste kaarten die een gebruiker ziet zijn dus willekeurig.
- **minSdkVersion-beslispunt (was taak 43): VERVALLEN, er valt niets in te
stellen.** Bob's vraag ("dat gaat toch automatisch via FlutterFlow?") is
terecht. Gecontroleerd 2026-09-13: `android/` heeft geen ongecommitte
wijzigingen meer en de vastgelegde waarden zijn `minSdkVersion
flutter.minSdkVersion` (= 24) en `tasks.register("clean", Delete)` — precies
de moderne variant. Het punt is dus vanzelf beslecht; niemand hoeft hier nog
iets aan te doen.
- **`flutterflowmobiel1` opruimen (was taak 46): GESCHRAPT** op Bob's verzoek.
`services_2` bestaat nog (25 items) maar heet "ongebruikt" en blijft staan
tot een schoonmaakronde.
- **P2-7 opruimronde (was taak 44): naar de livegang-lijst.**
- **Engelse vertalingen (was taak 45): naar de livegang-lijst**, deels al
gedaan. Bob 2026-09-13: "dit zullen we nog wel een paar keer krijgen, nieuwe
pagina's met vertalingen die missen, en dat wil ik later in 1x."
- **Events zonder categorie (taak 32 deel B): Bob heeft er een eigen taak van
gemaakt. Wél een probleem, géén livegang-blokker** — zijn expliciete oordeel.
### 🥇 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 · bezorg-display — ✅ display leeft, 1 restpunt.** Geverifieerd
2026-09-13: `services_2` op `flutterflowmobiel_establishments` draait op
productie (Amsterdam 19 zaken, Arnhem 2, ongefilterd 100), het `horcat`-filter
is eraf (leeg én gevuld geven allebei 19 — de app kan die parameter niet
weglaten, dus dit was fataal geweest), de controle met een verzonnen parameter
klopt, `services_1` is onaangetast, en er zitten geen HTML-entiteiten in.
De oorzaak van het eerdere "could not be found": de display had **geen pad**,
waardoor Views 'm niet opsloeg.
⚠️ **Restpunt (Bob is ermee bezig):** `categorie` komt op deze display als
komma-string i.p.v. array — 18 van de 19 rijen. Dat **breekt de app stil**
(`getJsonField(item, r'$.categorie').toList()` gooit `NoSuchMethodError` op een
String). Bob lost dit op met een custom-modulefunctie; let op dat een enkele
categorie ook een **array van 1** moet worden, zoals `services_1` doet.
Zodra dat rond is: Claude meet de bezorgers per plaats en legt de ontwerpkeuze
voor (losse lijstpagina vs. 7e tab), en hangt het drawer-item eraan. De pagina
heeft nog géén `display_id`-parameter; die moet er eerst op.
Oorspronkelijke diagnose
**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 & 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 `&`). Alleen de platte-tekstvelden, gemeten over 9625 events:
`title` **837** (`&` 695, `'` 179, `"` 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 is GEDAAN (2026-09-13, nid 217916, Enkhuizen) — zie P2-15 verderop voor de 2 bevindingen (tijd +2u = Drupal, rauwe datumweergave = builder).** ~~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 & Ytwer Bosma` ❌ |
Voor de gebruiker: hij ziet de titel netjes in de lijst, tikt erop, en op de
detailpagina staat `&`. 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.
7. 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
`NoSuchMethodError` — *stil*, 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&downieS"` terug,
via `services_1` `"Brownies&downieS"` — zelfde node. Ook `plaats` is
geraakt (`'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** → *+ Add* → **Content: 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-pagina** —
`drawer_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.
- **P2-7 · opruimronde, 21 dode eenheden** (Bob 2026-09-13: naar livegang).
Moet in de **builder** gebeuren, niet met `git rm` — anders staat alles na de
volgende export terug. *9 componenten:* `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`,
`p_uitgaantabel_kaart_component_orgineel_met_kaartjeerin`. *12 pagina's:*
`Event`, `FavorietenCopy`, `HorecagelegenhedenOverzichtCopy`,
`HorecagelegenhedenOverzichtCopy2`, `HorecagelegenhedenOverzichtCopy2Copy`,
`Kanweg`, `KanwegHomeCopy`, `KanwegHorecagelegenhedenOverzichtSortPage`,
`KanwegTestUpload`, `KanwegHorecagelegenhedenOverzicht`,
`KanwegHorecagelegenhedenOverzichtCopy3Copy`, `KanweghorecagelegenheidCurrentCopy`.
**Begin met `drawer_component_copy` en `kanweghorecagelegenheid_current_copy`** —
die twee houden `KanwegHorecagelegenhedenOverzicht` als enige nog in leven.
⚠️ `HorecagelegenhedenOverzichtCopy2` pas weggooien als P2-6 af is (het is de
intacte momentopname waaruit het herstel van 09-01 is afgeleid), en de pagina
`Event` weigerde in augustus élke wijziging met "Invalid Action" — lukt die na
één poging niet, laten staan.
- **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 gevonden** —
`HorecagelegenhedenOverzicht`'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 afgerond** — `TabPersAgenda`'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 afgerond** — `PUitgaanSliderKaartComponent`'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
` 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 `EventCurrent` — **beide 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](https://claude.ai/code/artifact/29f3412a-305f-49f3-971c-cd9bd310beb1).
Afvinkbare werklijst: [Designbrug werklijst](https://claude.ai/code/artifact/0c0c7347-d681-417d-83d7-7feb1de6457d).)*
*(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 `FlutterFlowIconButton`s:
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": , "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 `IconButton`s 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((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-detailpagina~~ — **afgerond
(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'''$[:].'''))`, 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 (.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 @ 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(..., '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(, '')` 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(,
'') != 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: ellipsis` — **de `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:
`AlignedTooltip` → `Builder` 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_border` → `addToFavorieteHorecaNids`.
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`, 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?
favorieteGemeenteIds) -> List`
(`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`) 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.)*
- **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 `Naam`-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": , "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/.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": , "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(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` 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`. 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.
- ~~Bijvangst, zelfde verkenning: dode terug-pijl-knop in Home's
AppBar~~ — **afgerond (2026-08-10, Claude).** `IconButtonBack`
verwijderd via Widget Tree → rechtsklik → "Remove Widget" (op
`HomeWidget` → `AppBar` → `Row`, 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) `TextFormField`s, 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 `ConditionalBuilder` →
> `If`-tak — gebeurt niet (dialoog blijft open, geen fout, niets kapot).
> Geldt ook voor Bob in zijn eigen browser.
> - ❌ `Wrap Widget` → `Column` 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 3** — `On 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 `filterHoreca` →
> **Custom Functions** → **hover** 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
> `TextFormField`s). 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 `ConditionalBuilder` → `If`-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 Widget` → `Column` 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), ]`, 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 `StaggeredView`s 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 `TextFormField`s
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`: 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
items, String zoekterm, String? categorieFilter, String titelPad,
String categoriePad) -> List`: 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:**
- Debounce niet nodig — filteren gebeurt op een al volledig lokaal
geladen array, geen API-call per toetsaanslag.
- Dit raakt dezelfde pagina als Bob's Sort/Datatype-experiment —
gestart nu met zijn expliciete akkoord (2026-08-25).
- 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` 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 `TextFormField`s, 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.
2. 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).
3. Categorie-filter: chips/dropdown, opties dynamisch uit de complete
dataset (`alleActiviteiten` etc.) — geen hardcoded lijst.
4. Elke tab's `StaggeredView`/lijst-databron ombouwen van de rauwe
`alleXxx`-lijst naar `filterHorecagelegenheden(alleXxx, zoekterm,
categorieFilter, '$.titel', '$.categorie')`.
5. 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
`TextField`s (punt E) kwam op dezelfde dag wél gewoon door, net als de
hernummering van `textController1` → `textController` 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(, 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
`PagingController`s); 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
gebruik** — `horecagelegenheid_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/" 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:109`
— `Undefined 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):**
1. **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.
2. **Beide blokken laten staan, maar één ervan inklapbaar/dichtgeklapt
als default** (accordion) — geen functionaliteit verloren, wel
minder eerste-oogopslag-drukte.
3. **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`-route~~ — **gesloten 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
naartoe** — `EventCurrent` (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 → `FavorietenAgendaTESTKANWEG` → **Delete** →
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") → `FavorietenAgendaTESTKANWEG` → **Delete**-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 · ✅ LIVE INDIENING GELUKT 2026-09-13 (Claude, telefoon-emulator, profile-build, op Bob's akkoord; plaats Enkhuizen) — twee bevindingen eruit, zie hieronder.**
Doorloop: ingelogd als `bobcity` → `mijnProfiel` → "+ Voeg toe" bij Mijn
horecagelegenheden → `uitgaansevenementAanmaken`; dropdown stond voorgeselecteerd
op **Café de Vriendschap (Enkhuizen, nid 30399)**. Ingevuld: titel *"TEST app
Claude 13sep - niet echt"*, omschrijving, categorie *Activiteiten › Bijeenkomst*
(tid 36637), datum 25 sep 20:20, website, entree *Vrij toegankelijk* (tid 14);
geen media. Verzendknop → `POST evenementen/create.json` → HTTP 200
`{"status":"created","nid":"217916"}` → snackbar "Je evenement is geplaatst."
→ terug op `mijnProfiel`. Nagemeten op productie: `flutterflow_events` geeft de
node (plaats Enkhuizen, categorie `['Bijeenkomst']`, body, website),
`flutterflowmobiel_establishment_events?horecanid=30399` toont 'm in de agenda,
en `/node/217916` redirect naar
`/nl/Noord-Holland/Enkhuizen/Café-de-Vriendschap/2026/TEST-app-Claude-13sep-niet-echt`.
**Testnode 217916 mag weg (Bob).**
⚠️ **Bevinding 1 — tijd verschuift 2 uur (Drupal-kant, Bob). Kant-en-klare patch: `snippets/drupal-datum-tijdzone.md`.** De app stuurde
`"datum_start":"2026-09-25 20:20:00"` (lokale tijd, zie logcat), maar de views
én de publieke pagina tonen **22:20**. Drupal leest de string dus als UTC en
rendert in Europe/Amsterdam (+2 in de zomer). Fix in
`custom.evenementen_aanmaken.inc`: de binnenkomende datum expliciet als
`Europe/Amsterdam` interpreteren vóór opslaan (`new DateTime($s, new
DateTimeZone('Europe/Amsterdam'))` → naar UTC), of afspreken dat de app UTC
stuurt. Drupal-kant is netter: dan blijven oudere/andere clients ook goed.
Geldt vermoedelijk ook voor `stadsactiviteiten/create`.
**Bevinding 2 — cosmetisch (builder) — Claude bezig 2026-09-13 ±20:45:** het Datum-veld toont
na kiezen de rauwe `2026-09-25 20:20:00.000`. De Text-binding van dat veld
door een `dateTimeFormat` halen (bv. `d MMM yyyy, HH:mm`) — alleen de weergave,
`datumVoorApi` blijft de bron voor het verzenden.
*Oorspronkelijke taakomschrijving hieronder blijft staan als naslag.*
Bouwstappen 1 t/m 5 zijn AF, en de vier
restpunten uit de exportcontrole van 2026-09-11 zijn afgewerkt; alleen de live
test rest.** (Claim vrijgegeven na sessie 2026-09-03b.) Wat er nog moet: één
doorloop op een toestel — inloggen als horeca-eigenaar, `mijnProfiel` openen,
via "+ Voeg toe" naar `uitgaansevenementAanmaken`, controleren dat de
horeca-dropdown zich voorselecteert (bij precies 1 zaak) en dat de verzendknop
verschijnt, en één evenement echt indienen. Verwacht: snackbar "Je evenement is
geplaatst." en de node terugvinden op uitgaanskrant.com. Doe dat met
`fvm flutter run --profile -d ` (zie CLAUDE.md), niet met een
debug-build.
✅ **De vier restpunten uit de exportcontrole van 2026-09-11 zijn afgewerkt**
(Claude, zelf in de builder; elk punt met een verse export geverifieerd, en
`dart analyze` op die export geeft 0 errors):
1. **Invoervelden waren wit op wit** — alle **11** `fillColor`s (8 tekstvelden +
3 dropdowns) stonden op `secondaryBackground`, net als de vier omhullende
kaart-Containers, waardoor je alleen zwevende labeltekst zag. Alle 11 staan
nu op **`primaryBackground`**, gelijk aan `stadsactiviteitAanmaken`.
2. **Vier teksten zeiden nog "activiteit"** (restant van de duplicatie): de
verzendknop heet nu **"Evenement indienen"**, en de hints zijn
"Titel evenement", "Omschrijving evenement" en "Website van het evenement".
3. **Foutmelding-bug in de foto-upload** — de FALSE-tak las
`logoevenementuploadResult` → `$.error` in plaats van
`fotoEvenementuploadResult`, dus bij een mislukte foto-upload verscheen de
fout van de *logo*-upload (of "null"). Nu correct gebonden.
4. **`mijnProfiel`'s tweede "+ Voeg toe"** (bij "Mijn redactierechten") toonde
nog de placeholder "Binnenkort beschikbaar: evenement aanmaken" — die tekst
klopte ook niet (redactierechten → stadsactiviteit). De knop navigeert nu
naar **`stadsactiviteitAanmaken`** (`context.pushNamed`, geen parameters);
die pagina was daarvóór alleen vanuit Favorieten bereikbaar. Besluit Bob,
2026-09-11.
*Uitgesloten bij diezelfde controle, niet nog eens onderzoeken:* de ontbrekende
guard op titel/datum is geen crashrisico — `datumVoorApi` geeft nooit `null`
terug (bij leeg een `''`), dus de `!` in de knop is veilig, en `evenementCreate`
vangt lege titel/datum zelf af met een nette melding. De knop is dus hooguit
cosmetisch te vroeg klikbaar. Ook: `bestandUpload` plakt de API-base zelf voor
het relatieve pad, dus dat is correct bedraad. En P2-22's twee
`Custom Action Call`-fouten op `categorieTids`/`fotosFids` zijn opgelost —
beide staan als `.toList()` in de export en de export blokkeert niet meer.
**Visueel nagelegd op de telefoon-emulator (411 dp, profile-build, route
`/uitgaansevenementAanmaken`, 2026-09-11):** de invoervelden zijn nu
daadwerkelijk zichtbaar als grijze vakken, de horeca-dropdown selecteerde
zichzelf voor op "Café de Vriendschap" (dus `horecaVoorselectie` + de
On-Page-Load-keten werken op een echt toestel), en de verzendknop stond
zichtbaar onderaan met de tekst "Evenement indienen". Dat dekt het grootste
deel van de live test hierboven af; wat nog écht rest is **één evenement
daadwerkelijk indienen** en de node terugvinden op uitgaanskrant.com.
✅ **Die twee cosmetische punten zijn afgewerkt (Claude, 2026-09-12, builder;
exportgeverifieerd, `dart analyze` 0 errors, en visueel nagelegd op de
telefoon-emulator):** de drie dropdowns staan nu op `double.infinity` (waren
`200.0` terwijl de tekstvelden vol-breed zijn), en **alle negen** labels op de
pagina staan links uitgelijnd — de vijf sectiekoppen plus "Uw
Horecagelegenheid", dat als enige veldlabel nog gecentreerd stond. Daarmee is
de pagina gelijk aan `stadsactiviteitAanmaken`, dat al 6× `double.infinity` en
overal linkse uitlijning had.
*Eén inconsistentie bewust laten staan, want hij zit op BEIDE aanmaakpagina's
en is een ontwerpkeuze, geen bug:* de sectiekoppen gebruiken twee verschillende
tekststijlen. "Wat"/"Wanneer"/"Organisatie" (en op stadsactiviteit ook "Waar")
staan op **`bodyMedium`**, terwijl "Entree" en "Media" op **`headlineSmall`**
staan — zichtbaar groter en zwaarder. Wil je dat gelijktrekken, dan is dat één
keuze die je op beide pagina's tegelijk moet doorvoeren (5 + 6 widgets).
Bouwstap 1 afgerond en bevestigd (2026-08-27): custom action
`bestandUpload` (`lib/custom_code/actions/bestand_upload.dart`) upload
foto → fid, live getest tegen productie
(`https://uitgaanskrant.com/en/flutterdrup/bestand_upload/upload.json`)
via een tijdelijke testflow (`{success: true, fid: 5495517, url: ...}`)
— de tijdelijke "Test Upload (tijdelijk)"-navigatieknop op Favorieten'
Gebruiker-tab is weer verwijderd en bevestigd via verse export; het
losse testpagina `kanwegTestUpload` (Pick Media → `bestandUpload` →
snackbar met resultaat) blijft staan als herbruikbare kanweg-testtool,
nergens meer aan gelinkt.
⚠️ **Een ANDERE sessie bouwt tegelijk al bouwstap 2** (pagina
"Stadsactiviteit aanmaken") — nog niet door Bob gescheidsrecht welke
sessie samenhangend doorgaat op stap 2/3/4 hieronder.**
Evenementen/stadsactiviteiten
aanmaken vanuit de app (horeca-eigenaren + elke gebruiker). **Drupal-kant
volledig klaar en curl-getest op devbob (2026-08-26)** — dit is nu
zuiver FlutterFlow-bouwwerk. Volledige achtergrond/velden/beslissingen
staan in het Artifact "Redactierechten & Contentschema"
(`https://claude.ai/code/artifact/becb0c6f-e43b-4392-9f84-7dbf44eda5b2`,
secties "FlutterFlow-vervolgspec" én "API-contract" — dat laatste heeft
de exacte argumentnamen/types per endpoint, nodig om de FlutterFlow
API Calls te configureren) — open dat eerst, hieronder alleen de
samenvatting + concrete eerste bouwstappen.
**Endpoints (allemaal bevestigd werkend):** `evenementen/create`,
`stadsactiviteiten/create`, `mijn_horecagelegenheden` (index),
`mijn_stadsrechten` (index), `categorieen` (index),
`bestand_upload/upload`. Basis-auth (`bob:serhii`, zie lokale
Claude-memory) nodig voor handmatig curl-testen, niet vanuit de app
zelf.
**Look & feel — bevindingen uit de bestaande app-code (geen browser
gebruikt, puur codeonderzoek):**
- **`HorecagelegenheidoverzichtKaartWidget`**
(`lib/horecagelegenhedenoverzicht/horecagelegenheidoverzicht_kaart/`)
is al de kaart-widget die Favorieten Tab 2 gebruikt
(`favorieten_widget.dart:678`, in een `MasonryGridView.builder`) —
parameters (`nid`, `titel`, `adres`, `plaats`, `logo`, `categorie`)
matchen 1-op-1 met wat `mijn_horecagelegenheden` teruggeeft. **Gewoon
hergebruiken** voor de nieuwe "Mijn horecagelegenheden"-sectie, geen
nieuwe kaart-widget nodig.
- **Geen bestaand upload-precedent in de app** (`grep` op
`ImagePicker`/media-upload buiten `custom_code/` gaf 0 treffers) —
de foto-upload-flow (image picker → bytes → base64 → nieuwe custom
action → `bestand_upload/upload` → fid) is de enige écht nieuwe
bouwsteen zonder bestaand patroon om te kopiëren. Grootste
onzekerheid in deze taak; begin hier apart mee testen vóór je de
hele formulier-pagina bouwt.
- **Thema:** gewoon `FlutterFlowTheme.of(context)`-tokens overal
gebruiken (primary `#4B39EF`, secondary `#39D2C0`, tertiary
`#EE8B60`, font Inter/Inter Tight via Google Fonts) — geen eigen
palet nodig, sluit al aan bij de rest van de app.
- **Waar leeft "mijn profiel"?** Er bestaat nu geen aparte
profiel-pagina — `FavorietenWidget` (`lib/favorieten/`) heeft een
4e tab "Gebruiker" (`favorieten_widget.dart:245`) met een simpele
verticale lijst `ListTile`-rijen (Uitloggen, Wachtwoord wijzigen,
Account verwijderen — zie regel 700-935, patroon: `InkWell` →
`Material` → `ListTile` met `trailing: Icon(Icons.arrow_forward_ios_rounded)`,
`tileColor: secondaryBackground`, `borderRadius: 8.0`). **Aanbeveling:
nieuwe secties "Mijn horecagelegenheden" en "Mijn redactierechten"
hier bovenaan toevoegen** (vóór Uitloggen) i.p.v. een hele nieuwe
pagina + nav-entry te bouwen — kleinste wijziging, blijft binnen de
al bestaande "Gebruiker"-tab die feitelijk al de profielpagina is.
**Open vraag voor Bob:** akkoord met deze plek, of toch een losse
pagina? (zijn oorspronkelijke formulering was "de user profile
pagina, los van de favoriete pagina" — kan ook betekenen dat hij een
ECHT aparte pagina wil, niet nog een tab op dezelfde `FavorietenWidget`.)
**Concrete bouwvolgorde (1 stap per keer, zoals gebruikelijk):**
1. Custom action `bestand_upload` bouwen (image picker → base64 →
POST) en LOS testen (upload 1 plaatje, bevestig een fid terugkomt)
vóór er iets anders bijkomt.
2. Pagina "Stadsactiviteit aanmaken" (simpelste van de twee — geen
eigenaarschap-gate, geen horecagelegenheid-picker): velden per de
Stadsactiviteit-tabel in het Artifact, plaats-picker (bestaand
patroon, zie `SelectStateDropDownComponent`/`Selectprovinciegemeente`),
categorie-multiselect via `categorieen/index`. Na indienen: melding
"wordt beoordeeld", niet "geplaatst".
3. Pagina "Evenement aanmaken": horecagelegenheid-picker gevuld via
`mijn_horecagelegenheden` (auto-select bij precies 1 resultaat),
verder zelfde velden-aanpak als stap 2.
4. "Gebruiker"-tab (of nieuwe pagina, zie open vraag hierboven):
sectie "Mijn horecagelegenheden" (hergebruik
`HorecagelegenheidoverzichtKaartWidget`) + "Mijn redactierechten"
(`mijn_stadsrechten`, verberg de sectie helemaal als leeg), elk met
een knop die naar stap 2/3 navigeert met een page-parameter
(`plaats_tid` resp. `horecagelegenheid_nid`) vooringevuld.
**Curl-testronde afgerond (2026-08-26): elk veld op beide create-acties
veld-voor-veld bevestigd via drush node-dumps** — titel, datum (incl.
datum_eind-fallback), omschrijving, adres, entreeprijs, toelichting
entree, entree-type (nieuw `entree_tid`/`field_act_entree`, door Bob
zelf toegevoegd — zelfde vocabulary als `field_goo_entree`), meerdere
categorieën, logo, foto's-slideshow (bleek een minimale-resolutie-eis
te hebben — met een groter test-plaatje werkt het), website,
tickets-url, status (0/1), taal (`nl`). Twee nieuwe leesendpoints
onderweg bijgekomen: `categorieen`/index (vervangt de eerdere losse
`uitgaanscategorieen`/`evenementcategorieen` — bleken dezelfde
vocabulary) en `entreeopties`/index.
**Extra (2026-08-27, terwijl de frontend in een andere sessie gebouwd
wordt): `horecacategorieen`/index toegevoegd en curl-bevestigd
werkend** — categorie-vocabulary voor `horecagelegenheid` zelf
(`field_categories`, vocabulary `horecagelegenheid_category`,
bevestigd via `field_info_instances()` — een aparte, derde vocabulary,
los van `categorieen`). Niet gebruikt door evenementen/
stadsactiviteiten, klaargezet voor als horecagelegenheid-content ooit
via de app beheerd wordt. (8 resources in totaal, zie
`custom.module-WIJZIGINGEN.txt`).
**Curl-testronde 100% afgerond (2026-08-26, avond).** Alle foutpaden
+ de meerdere-foto's-slideshow expliciet bevestigd: >5 `fotos_fids` →
nette 400 ("Maximaal 5 foto's toegestaan"); ongeldige `categorie_tid`
→ 400; ongeldige `entree_tid` → 400; `evenementen/create` met een
horecagelegenheid van een andere gebruiker (nid 70132, "Wapen van
Urk") → correcte 403 ("Deze horecagelegenheid is niet van jou"); 3
verschillende testfoto's tegelijk in `fotos_fids` → alle 3 los en
correct terug te vinden in `field_pictures` (delta 0/1/2, eigen
fid/bestandsnaam/afmetingen elk). Enige restpunt: `mijn_stadsrechten`
nooit apart getest (laag risico, zelfde patroon als de al werkende
`favorieten_gemeenten`). **De Drupal-kant is hiermee klaar — dit is nu
zuiver FlutterFlow-bouwwerk, zie de bouwvolgorde hierboven.**
**Bouwstap 1 (foto-upload) is af — 2026-08-27.** Bleek al gebouwd door
een parallelle sessie: custom action **`bestandUpload`**
(`FFUploadedFile file, String uploadUrl, String sessionName, String
sessionId, String token) -> dynamic`, retourneert
`{'success': bool, 'fid': String, 'url': String, 'error': String,
'statusCode': int}` — rijker dan het losse `drupalUploadBestand`
(`-> String?`) dat Claude had voorbereid, want geeft bij een fout ook
de échte Drupal-foutmelding terug om aan de gebruiker te tonen.
**`drupalUploadBestand` is verwijderd, `bestandUpload` is voortaan de
canonieke actie.** Let op: `bestandUpload` wil de **volledige URL**
(`uploadUrl`, dus incl. `/bestand_upload/upload.json`), niet alleen een
base-URL.
**Bouwstap 4 (nieuwe pagina `mijnProfiel`) — grotendeels af, 2026-08-27
(Bob sliep, Claude bouwde door).** 4 nieuwe API Calls toegevoegd
(`MijnHorecagelegenheden`, `MijnStadsrechten`, `Categorieen`,
`Entreeopties`) — allemaal geverifieerd correct via export. Nieuwe
pagina `mijnProfiel`
(`https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=mijnProfiel`,
`lib/mijn_profiel/`), Scaffold met kale AppBar (Show Default Button,
geen titel — drag-and-drop van een titel-widget faalde herhaaldelijk,
zie de bekende AppBar-Row-insert-onbetrouwbaarheid elders in dit
bestand):
- **Sectie "Mijn horecagelegenheden"**: Row (titel + "+ Voeg toe"
Button, nu nog een placeholder Show-Snack-Bar) + ListView met
Backend Query = `MijnHorecagelegenheden` (variabelen `session_name`/
`sessid` → App State `userSessionname`/`userSessionid`) + Generate
Dynamic Children (var `horecaItem`, JSON Body, No Further Changes) +
item-template = hergebruikte `HorecagelegenheidoverzichtKaart`
(params `titel`/`logo`/`nid`/`adres`/`plaats`/`categorie`, elk via
JSON Path `$.` op `horecaItem`). **Volledig af en
exportgeverifieerd.**
- **Sectie "Mijn redactierechten"**: zelfde patroon, Backend Query =
`MijnStadsrechten`, Generate Dynamic Children var `rechtItem`,
item-template = kale `Column` → `Text` met een **Combine Text**-
binding (`$.titel` + literal " — " + `$.parent_titel`) — geen losse
tweede Text-widget nodig/haalbaar, zie bugnotitie hieronder.
"+ Voeg toe"-knop ook nog placeholder. **Af en
exportgeverifieerd**, behalve de leeg-verbergen-eis (zie hieronder).
- **⚠️ Nieuw bevestigd builder-bugpatroon (2026-08-27):** een
ListView's **item-template vervangen** via rechtsklik → "Insert
After"/"Duplicate" op de bestaande template-widget triggert een
**"Replace Dynamic Child"**-bevestigingsdialoog — en de nieuwe
widget die daaruit voortkomt **kan een Backend Query +
Generate-Dynamic-Children-configuratie van een eerder gedupliceerde
ListView blijven meedragen**, zelfs nadat je 'm via "Replace Widget"
omzet naar een ander widget-type (bv. Column). Dit bleef **onzichtbaar
in de builder-UI** (Column's rechterpaneel toont geen aparte
Backend-Query-sectie) maar leverde in de export een dubbel-geneste
`FutureBuilder`/`List.generate` op — functioneel een N×M-bug (elke
stadsrecht-rij herhaalde zich M keer, M = aantal horecagelegenheden
van de gebruiker) én een overbodige extra API-call per rij. **Fix:**
selecteer de widget, check zelf de 3e/4e icoontjes (Backend Query /
Generate Dynamic Children) in de rechterpaneel-iconenrij — ook als
er geen widget-type meer op wijst — en klik **Remove** op beide als
ze een oude configuratie tonen. **Vuistregel: na elke
"Replace Dynamic Child"-actie altijd verifiëren via een verse export
+ `grep` op de betrokken Call-klassen**, niet aannemen dat
"Replace Widget" alle oude state meeneemt.
- **Nog open (kleinere restpunten, geen van alle blokkerend):**
1. Beide "+ Voeg toe"-knoppen omzetten van placeholder-snackbar naar
echte Navigate-To zodra de create-pagina's bestaan (zie
bouwstap 2/3), met `horecagelegenheid_nid` resp. `plaats_tid` als
page-parameter.
2. Sectie "Mijn redactierechten" helemaal verbergen als de lijst leeg
is (de meeste gebruikers hebben geen `field_town_access`) — nog
niet gebouwd, waarschijnlijk een ConditionalBuilder op de hele
sectie met een "Number of Items > 0"-achtige JSON-Path-transform;
kost een eigen sessie/poging gezien de bekende
ConditionalBuilder-freeze-risico's elders in dit bestand.
3. Entry-point naar `mijnProfiel` vanuit de rest van de app (bv.
Favorieten' "Gebruiker"-tab) — nog niet toegevoegd, pagina is nu
alleen bereikbaar via directe URL/route.
4. `EntreeoptiesCall`'s header niet los geverifieerd op dezelfde
dubbele-substitutie-bug als `CategorieenCall` had (die is al
gefixt) — waarschijnlijk oké, niet met zekerheid gecheckt.
**Bouwstap 2 (custom action `stadsactiviteitCreate`) — af, 2026-08-27.**
Custom action toegevoegd via Custom Code-editor (⌘K → "Add: Action",
NIET via de native API-Call-JSON-body-templating — bewust gekozen
i.p.v. FlutterFlow's ingebouwde API Call vanwege de geneste
`adres`-struct + 2 arrays (`categorie_tids`/`fotos_fids`), zelfde
precedent als `bestandUpload`). 20 typed parameters (`sessionName`,
`sessionId`, `token`, `titel`, `plaatsTid`, `datumStart`, plus 14
optionele velden incl. `List? categorieTids`/`fotosFids`) →
POST naar `stadsactiviteiten/create.json`, retourneert
`{success, nid, status}` of `{success:false, statusCode, error}`.
**Geverifieerd via `dart analyze`: 18 meldingen, stuk voor stuk
cosmetisch** (5 ongebruikte FlutterFlow-boilerplate-imports + 13
`avoid_print`/`prefer_const`-infos, zelfde patroon als `bestandUpload`)
— geen echte fouten.
- **⚠️ Nieuw bevestigd builder-bugpatroon (2026-08-27), Custom-Action-
argumenten-UI:** bij het via de rechterpaneel-UI toevoegen van veel
(~20) Custom-Action-argumenten kan er een **extra, naamloos 21e
argument** ontstaan (vermoedelijk door een misklik tijdens
chevron-toggle-navigatie) — dit blokkeert "Save Action" met
**"Action arguments must all be given names."** Het probleem is
onzichtbaar in de argumentenlijst zelf zolang je 'm niet helemaal
tot onderaan scrolt, maar wordt direct duidelijk via het
**`>`-icoon rechtsboven in het Action Settings-paneel ("View
Boilerplate Code")** — dat toont de exacte verwachte functie-
signature inclusief een kaal `String? ,` aan het eind als er zo'n
leeg argument bestaat. **Check dit sowieso bij een volgende
veel-argumenten Custom Action** vóór je op Save klikt: open even
"View Boilerplate Code" en tel de parameters. Fix: helemaal naar
onderen scrollen in "Define Arguments" (voorbij het laatste échte
argument) en op "Remove" klikken bij het lege argument.
- **Los bevestigd: het rechterpaneel van de Custom-Action-editor kan
bij veel argumenten muiswiel-scroll volledig negeren**, zelfs met de
bekende Tab-naar-volgend-veld-workaround (werkte de eerste ~15 keer
wel, liep daarna vast) — en FlutterFlow's eigen zwevende
hulp-chat-knop (rechtsonder in beeld) kan bovendien exact overlappen
met de plek waar een laag-gelegen veld zou moeten zitten, waardoor
een klik daar per ongeluk de hulp-widget opent i.p.v. het veld raakt.
Bij dit patroon (net als de eerdere clipping-gevallen): 1-2 pogingen,
dan aan Bob overdragen — kostte hem in zijn eigen browser seconden.
**Bouwstap 3 (pagina `stadsactiviteitAanmaken`) — grotendeels af,
2026-08-31.** Live pair-sessie (Bob bouwt in eigen browser, Claude
verifieert per stap met verse export). Stand van zaken, alles
exportgeverifieerd met **0 Dart-errors**:
- **Pagina** `stadsactiviteitAanmaken` (`lib/stadsactiviteit_aanmaken/`),
Scaffold + AppBar (Background `Primary`, "Show Default Button" aan →
automatische terugknop, geen losse titel-widget) + scrollbare
`Column` (uniform padding 16).
- **10 TextFields**, in volgorde, elk met zowel Label als Hint:
Titel · Omschrijving · Organisator · Contact · Adres · Postcode ·
Plaats · Entreeprijs · Toelichting Entree · WebsiteURL.
- **2 datumvelden** (`TextFieldDatumStart` / `TextFieldDatumEind`),
beide `readOnly: true` + breedte `inf`, met On-Tap-actie
`showDatePicker` → `showTimePicker` → gecombineerd in één DateTime →
als tekst terug in het veld gezet. Werkt.
- **Plaats-picker: eigen 3-traps cascade** met Page State (NIET het
bestaande `SelectStateDropDownComponent` — zie waarschuwing
hieronder). Page State-velden: `createProvincieID`,
`createGemeenteId`, `createPlaatsID` (let op de inconsistente
hoofdletters — zo staan ze er echt in).
- `DropDownProvincie` → Backend Query `provincies`, options
`$[:].provincieid` / labels `$[:].provinciename`, On Selected zet
`createProvincieID`.
- `DropDownGemeente` → Backend Query `gemeenten` met
`provincieid: createProvincieID`, options `$[:].gemeenteid` /
labels `$[:].gemeentename`, On Selected zet `createGemeenteId`.
- `DropDownPlaats` → Backend Query `PlaatsenBijGemeente` met
`gemeenteid: createGemeenteId`, options `$[:].plaatsid` / labels
`$[:].plaatsname`.
**⚠️ Belangrijke ontwerpbeslissing (Bob, 2026-08-31) — corrigeert de
oudere spec in het Artifact.** De plaats-picker mag **niet** aan de
App State-velden `provincieSelectId`/`gemeenteSelectId` hangen: dat is
de **browse**-state (welke gemeente de gebruiker nu in de app bekijkt)
en staat volledig los van "in welke plaats maak ik een activiteit
aan". Om dezelfde reden is het bestaande `SelectStateDropDownComponent`
bewust **van deze pagina verwijderd** — dat component schrijft namelijk
rechtstreeks naar die App State-velden en zou dus stilletjes de
browse-selectie van de gebruiker overschrijven. Vandaar de eigen
dropdowns met Page State hierboven.
**Gekozen rechtenmodel: HYBRIDE** (Bob, 2026-08-31, via expliciete
keuze). Het Artifact zei eerder "GEEN restrictie tot eigen steden —
iedereen mag voor elke plaats voorstellen"; dat is nu bijgesteld naar:
heeft de gebruiker stadsrechten (`mijn_stadsrechten`, gevuld vanuit
`field_town_access`, gemeente-niveau sinds 2026-08-27), dan die eigen
gemeenten **bovenaan als snelkoppeling**; daarnaast blijft de volledige
Provincie→Gemeente→Plaats-cascade beschikbaar voor iedereen. Reden om
niet volledig af te schermen: `mijn_stadsrechten` is leeg voor vrijwel
alle gebruikers, en `stadsactiviteiten/create` doet server-side bewust
geen rechtencheck — de review-flow (node komt **altijd** ongepubliceerd
binnen, "wordt beoordeeld") is het vangnet.
**Nieuw Drupal-endpoint gebouwd + live (2026-08-31):
`plaatsen_bij_gemeente`/index** — `?gemeenteid=` → array van
`{plaatsid, plaatsname, plaatsdescription}`. Bewust een **losse nieuwe
resource** i.p.v. het bestaande `plaatsen.json` op te rekken (Bob's
keuze, sluit aan bij hoe elk ander endpoint in dit project ook los
werd toegevoegd): `plaatsen.json` bouwt alleen Provincie (diepte 0) en
Gemeente (diepte 1) op en kan Plaats-niveau (diepte 2) principieel niet
teruggeven, ook niet met `limit_levels=3`. Getest op devbob én
**gedeployed naar productie**. Code staat in `custom.module`
(`custom_plaatsen_bij_gemeente()` + `custom_plaatsen_bij_gemeente_access()`).
**FlutterFlow API Group hernoemd: `kanweg` → `productie`** (Bob,
2026-08-31), genereert nu `ProductieGroup` met base-URL
`https://uitgaanskrant.com`. Bevat 3 calls: `PlaatsenBijGemeente`,
`Entreeopties`, `Categorieen`. **Valkuil onderweg:** de call eerst als
top-level aangemaakt mét een *relatief* pad (`/nl/flutterdrup/...`)
→ geen host, call faalt. Binnen een groep hoort een relatief pad
(`${baseUrl}` wordt voorgeplakt); top-level moet de volledige
`https://uitgaanskrant.com/...`-URL.
**Bouwstap 3 is functioneel AF (2026-08-31, live pair-sessie).**
Restpunten 1 t/m 7 uit de vorige sessie zijn alle zeven gebouwd en
exportgeverifieerd (`dart analyze`: 0 errors). Kort wat er nu staat, zodat
een volgende sessie niet opnieuw hoeft te reconstrueren:
- `DropDownPlaats` → On Selected zet Page State `createPlaatsID`.
- **Rechten-shortcut** `DropDownGemeentenMijngemeenten` bovenaan: staat in
een wrapper-`Column` die de Backend Query `MijnStadsrechten` draagt
(de query moest één niveau omhoog — een widget kan zijn *eigen*
Backend-Query-response niet gebruiken in zijn eigen Visibility-conditie,
wel voor Options). Options `$[:].tid` / labels `$[:].titel`, On Selected
zet `createGemeenteID`. Verbergen-bij-leeg via conditie
`$[0].titel` **Is Set** — géén "Number of Items", want een
JSON-Path-binding is Json-getypeerd en biedt geen lijst-transforms
(zelfde beperking als P1-24).
- **Let op naamswijziging:** de Page State heet nu `createGemeenteID`
(hoofdletter D), niet `createGemeenteId`.
- `DropDownCategorieen` (multi-select, → `createCategorieTids` als echte
`List`) en `DropDownEntree` (→ `createEntreeTid`), beide
options `$[:].tid` / labels `$[:].naam`.
- **Foto-upload**: knop "Logo kiezen" (→ `createLogoFid`) en "Foto's
kiezen" (→ `addToCreateFotosFids`), beide via het `bestandUpload`-
patroon uit `kanwegTestUpload`. Fotoknop verdwijnt bij 5 foto's
(`if (_model.createFotosFids.length < 5)`).
- **Verzendknop** "Activiteit indienen": alle 20 argumenten van
`stadsactiviteitCreate` gebonden; datums lopen door de nieuwe custom
function **`datumVoorApi`** (knipt Dart's `.000` eraf). Knop heeft een
Visibility-guard op `createPlaatsID` **Is Set** (voorkomt de
`createPlaatsID!`-null-crash). Daarna een conditional op
**`$.success`** → snackbar "wordt beoordeeld" + Navigate Back, anders
snackbar met `$.error`.
⚠️ **Valkuil vastgelegd:** een conditie op `$.nid` genereerde
`if (getJsonField(...))` zonder null-check — dat compileert (dynamic)
maar crasht bij runtime op de bool-cast. Gebruik een sleutel die
écht een bool is (`$.success`), of bouw een Single Condition met een
expliciete operator.
**Nog te doen op deze pagina:**
1. **Entry-point ontbreekt** — `stadsactiviteitAanmaken` is alleen via de
route bereikbaar. De "+ Voeg toe"-knoppen op `mijnProfiel` staan nog
op een placeholder-snackbar (bouwstap 4, restpunt 1) en moeten
Navigate-To hierheen worden.
2. **Nooit end-to-end live getest** — profile-build lukte, maar de AVD
crashte tijdens de eerste poging (bekend SEGV-patroon). Nog te
bevestigen: dat een ingediende node ongepubliceerd in Drupal
binnenkomt met de juiste plaats/datum/categorieën/entree/logo/foto's.
**Vervolg 2026-09-01 — veel gefixt, look&feel-afwerking open.**
Gefixt en exportgeverifieerd deze dag: sessie-cookies (`[var]` i.p.v.
`{{var}}`, zie `CLAUDE.md`), datumpickers (On Tap op een omhullende
Container + Enabled uit), `datumVoorApi` (geneste signatuur → gaf altijd
`null` → crash op de verzendknop), guards op gemeente-/plaats-dropdown en
verzendknop, logo- en fotopreview (Page State-type moet **Image Path**
zijn, niet String — zie `CLAUDE.md`), categorie-multiselect via
`categorieSubTids`/`categorieSubLabels` + `isSearchable`, upload-URL's
via `apiBaseUrl`, en `field_town_access` op productie (widget-instelling
"Leaves only" uit + Max depth 2 stond alleen op devbob; bobcity heeft nu
3 gemeenten i.p.v. 1254 plaatsen).
Bevestigd: `bestandUpload` geeft een **absolute** URL terug
(`https://uitgaanskrant.com/sites/.../evenementen_uploads/...`), dus geen
prefix-logica nodig.
Velden zijn gegroepeerd in gestileerde kaarten (`Padding(16)` →
`Container` met 3px `#EEEEEE`, geen radius, geen schaduw) — merkgetrouw.
Claude heeft via browser-automatisering de **breedte van 5 groep-
containers op `infinity`** gezet (`ContainerWat`, `ContainerWanneer`,
`ContainerWaar`, `ContainerOrganisatie` en de Entree-container).
**Vervolg 2026-09-02 (Claude, browser-automatisering):** de twee
"Hello World"-koppen zijn nu **"Waar"** en **"Organisatie"**,
`ContainerMedia` is gestileerd als de andere kaarten (fill
`secondaryBackground`, border `alternate` 3px, geen radius, width inf),
en `TextFieldTitel` + `TextFieldWebsiteURL` staan op `infinity` — alle
12 tekstvelden zijn nu even breed. Punten 2, 5 en 6 hieronder zijn
daarmee afgehandeld; de rest staat nog open.
⚠️ **Nuttig gebleken:** in de kleurkiezer staan de merkkleuren al als
thematokens klaar (Primary `#9A141D`, Secondary `#FF680D`, Tertiary
`#09B34A`, **Alternate `#EEEEEE`** = de kaartrand, Primary Text
`#3E454C`). Gebruik die tokens i.p.v. losse hexwaarden.
⚠️ **Ook gebleken: typen en klikken in het rechterpaneel werkt vanaf
Claude's kant wél** — de notitie in `CLAUDE.md` uit 2026-08-09 dat dit
"structureel niet mogelijk" was, klopt niet meer. Widget-tree-selectie,
de eigenschappen-zoekbalk, tekstvelden, kleurkiezers en de
∞-breedteknop reageerden allemaal normaal.
**Huisstijl-afronding (Bob's keuze 2026-09-02):** overal **radius 0**
(strikt merkgetrouw, de site kent geen afronding), sectiekoppen **16px
gewicht 600, gewone schrijfwijze** (geen kapitalen), en **geen serif**
op deze pagina.
Gedaan: `TextFieldTitel`, `TextFieldOmschrijving` en
`TextFieldDatumStart` staan op radius 0.
**Nog om te zetten naar radius 0 (20 widgets):** 9 tekstvelden
(`DatumEind`, `Adres`, `Postcode`, `Plaats`, `Organisator`, `Contact`,
`WebsiteURL`, `ToelichtingEntree`, `Entreeprijs`), 6 dropdowns, 3
knoppen (`ButtonLogo`, `ButtonFotos`, `ButtonIndienen`) en 2
afbeeldingen (logo-preview + het foto-item in de Wrap).
**Nog te doen:** de 5 sectiekoppen op 16px / gewicht 600 (staan nu op
14px normaal).
**Snelste werkwijze (bevestigd werkend):** selecteer het widget, typ
**"radius"** in de eigenschappen-zoekbalk — het paneel filtert dan tot
één "Border Radius"-veld — en zet de uniforme waarde op 0. Voor de
sectiekoppen: zoek op "size" respectievelijk "weight".
⚠️ **Waarom Claude dit niet afgemaakt heeft:** elke waarde-wijziging
vraagt via browser-automatisering drie losse round trips (selecteren +
filteren, dan een *standalone* triple-click op het waardeveld, dan pas
typen) — binnen één `browser_batch` registreert de triple-click niet.
Met 20+ widgets is dat 60+ aanroepen met misklik-risico, terwijl het in
Bob's eigen browser ~5 seconden per widget kost.
**Huisstijl AF (2026-09-03, geverifieerd).** Alle border-radii op 0
(12 tekstvelden, 6 dropdowns, 3 knoppen, 2 afbeeldingen — 0 treffers
`circular(8.0)` over), sectiekoppen op 16px.
(De eerdere notitie dat "Entree"/"Media" nog geen `FontWeight.w600`
hadden is achterhaald — zie de verificatie hieronder: ze erven w600 al
van `headlineSmall`.)
**✅ Bouwstap 3 (`stadsactiviteitAanmaken`) is functioneel én visueel AF
— 2026-09-03, live pair-sessie Bob + Claude.** Verse export,
`flutter pub get`, `dart analyze lib/` = **0 errors**, en een live
profile-build op de telefoon-emulator (411dp) waarin de hele
plaats-cascade end-to-end is doorlopen tot de verzendknop verscheen.
**Wat deze dag is opgelost (alles exportgeverifieerd):**
- **Orphan-kopieën `Copy`/`Copy2`/`Copy3` verwijderd** — die gaven 3
compile-errors; de app bouwt weer.
- **Plaats-dropdown was op een telefoon onbereikbaar.** De drie
cascade-dropdowns stonden naast elkaar in een `Row`, elk
`width: 200`. 3 × 200 = 600 > 411dp, dus de derde viel volledig
buiten beeld — hij stond niet eens in de accessibility-tree. Zonder
`createPlaatsID` verscheen de verzendknop nooit, dus het formulier was
via dat pad onbruikbaar. Opgelost: `Row` → `Column`, alle 6 dropdowns
op `infinity` (0 treffers `width: 200` over).
- **`ButtonFotos` las `logouploadResult` i.p.v. `fotouploadResult`** in
zowel de `$.success`-conditie als de fout-snackbar → crash op de
bool-cast bij een foto vóór een logo, en anders een altijd-TRUE-tak.
Beide regels omgezet.
- **`$.success`-conditie op `ButtonLogo`**, **sectiekop "Wat"**,
**lege "Hello World"-container onderaan weg**, **alle border-radii 0**,
**sectiekoppen 16px/600**.
- **`MijnHorecagelegenheden`-cookieheader** van `{{session_name}}={{sessid}}`
naar `[…]`-syntax → export toont `'Cookie': '${sessionName}=${sessid}'`
en de aanroeper (`mijn_profiel_widget.dart:270`) geeft beide
App-State-velden mee. Nergens nog `{{…}}` in `api_calls.dart`.
- **Look & feel 1 t/m 5:** alle 18 invulwidgets (12 tekstvelden + 6
dropdowns) van `fillColor: secondaryBackground` naar
`primaryBackground` (waren onzichtbaar wit-op-wit); `Container(height:
200)` om `TextFieldAdres` weg; `ContainerMedia` de ontbrekende
`Padding(16)`-wrapper gegeven zodat hij in de rooilijn ligt; alle zes
sectiekoppen links uitgelijnd; kop "Wat" op 16px/w600/`primaryText`.
**Nog te doen op deze pagina:**
1. **⏭️ EINDTEST — nooit een activiteit ingediend.** Alles wat het pad
blokkeerde is nu weg en de verzendknop is live bereikt, maar er is
nog geen node aangemaakt. **Bob wil dit zelf plannen (afspraak
2026-09-03: "dan kunnen we morgen testen").** Te controleren na
indienen: node komt **ongepubliceerd** binnen, met de juiste plaats,
datum (start + eind), categorieën, entree-type, entreeprijs, logo en
foto's. Snelste route naar een geldige staat: de **"Mijn
gemeenten"-shortcut** (gemeente → plaats), dan verschijnt de knop.
2. **Look & feel punt 6 — dubbele/zwevende veldlabels.** Bewust
geparkeerd door Bob. Boven vier velden staat een losse `Text`-widget
die het label herhaalt dat het veld zelf al als `labelText`/`hintText`
voert: **"Categorie evenement"**, **"Stadseditor"**, **"Toelichting
Entree"**, **"Entreeprijs"**. Bij de laatste twee staat het label
letterlijk 2× op het scherm; "Stadseditor" is een zwevend label
zonder duidelijk bijhorend veld. Keuze: óf de losse Texts weg, óf ze
bij álle velden consequent gebruiken en dan de `labelText` van het
veld leegmaken. De kaarten hebben bovendien geen interne padding,
waardoor die losse labels de 3px-rand raken.
3. **Twee niet-blokkerende Issues-errors op deze pagina** (gemeten
2026-09-03, export loopt er gewoon mee door):
- `Property Override — Invalid API call configuration` op
`DropDownGemeentenMijngemeenten`;
- `Property Override — return type mismatch` op `DropDownProvincie`
— die genereert `options: List.from(getJsonField(...
$[:].provincieid ...))` terwijl "Option Value Data Type" op
**String** staat; komt `provincieid` als getal terug, dan is dat
precies de mismatch.
De cascade werkt live, dus geen brand, maar wel opruimen.
4. **Entry-point ontbreekt** — `stadsactiviteitAanmaken` is alleen via
de route bereikbaar. De "+ Voeg toe"-knoppen op `mijnProfiel` staan
nog op een placeholder-snackbar en moeten Navigate-To hierheen worden
(bouwstap 4, restpunt 1), met `plaats_tid` als page-parameter.
5. **Geen validatie vooraf.** Alle 12 `…TextControllerValidator`-velden
zijn in het model gedeclareerd maar nergens toegekend, dus er is geen
verplicht-veld-check. Een leeg Titel/Datum gaat gewoon mee naar de
server (`datumVoorApi('')` geeft `''`, geen crash) en komt terug als
Drupal-foutmelding in de snackbar. Werkt, maar rauw.
6. **Geen "bezig"-indicatie bij indienen** — geen enkele knop op deze
pagina heeft `showLoadingIndicator`. Bij een trage upload/create
lijkt de knop niets te doen.
7. **Foto's zijn niet te verwijderen** — de Wrap toont ze als 80×80
`Image.network` zonder verwijderknop; verkeerd gekozen foto betekent
pagina verlaten en opnieuw beginnen.
8. **Lege AppBar** — alleen een rode balk met terugknop, geen titel.
9. **De verzendknop is onzichtbaar tot er een plaats gekozen is** (de
Is-Set-guard) zonder enige uitleg. Overweeg de knop altijd te tonen
maar uit te schakelen met een hint "Kies eerst een plaats".
10. De laadspinner van elke dropdown-`FutureBuilder` is een
`SpinKitFadingCircle` van **80×80 in een `Center`** — die is groter
dan de dropdown zelf en laat de layout tijdens het laden verschuiven.
11. **De categorie-dropdown en de gemeente-lijst zijn niet
alfabetisch** — ze volgen de volgorde van de API (provincies komen
binnen als Flevoland, Drenthe, Friesland, …). Cosmetisch.
⚠️ **Let op bij vervolgwerk: `uitgaansevenementAanmaken` wordt in een
andere chat gebouwd** als duplicaat van deze pagina, en zit in dezelfde
`productie`-API-groep. Op 2026-09-03 blokkeerde een fout dáár
(`API Action — Variable value configured incorrectly for API call`, de
ongebonden `session_name`/`sessid` op de On-Page-Load-`MijnHorecagelegenheden`-
call) meermaals onze export hier. Zie het nieuwe Issues-recept in
`CLAUDE.md`.
**Overige restpunten (sessie 2026-08-31, live pair-fix):**
8. **✅ AFGEROND (Bob, 2026-09-03): stadsrechten-migratie is gedraaid.**
`field_town_access` stond op plaats-niveau (9.292 rijen over 6
accounts); de migratie naar gemeente-niveau is uitgevoerd voor alle
accounts en de scope-beslissing is gemaakt. Live bevestigd in de app
(2026-09-03): de "Mijn gemeenten"-shortcut toont voor dit testaccount
nu **4 gemeenten** (Amsterdam, Drechterland, Enkhuizen, Stede Broec)
i.p.v. 1254 plaatsen — de shortcut werkt dus zoals bedoeld en is een
echte snelkoppeling geworden, geen tweede plaatsenlijst.
9. **Categorie-dropdown toont de hele hiërarchie plat.** `categorieen`
geeft `diepte`/`parent_tid` mee; de multiselect toont nu ouder- én
kindtermen door elkaar zonder inspringing. Optionele verfijning.
**Update 2026-08-31:** bindingen staan inmiddels op
`$[?(@.diepte == 1)]` — zie de live-testnotitie hieronder; nog niet
bevestigd of die filtersyntax werkt.
10. **Basis-URL centraliseren — Fase A af, Fase B open (app-breed,
overweeg een eigen taak-ID).** Aanleiding: op devbob kunnen testen
zonder overal URL's te wijzigen. **Gedaan:** App State-variabele
**`apiBaseUrl`** (default `https://uitgaanskrant.com`), en alle vier
de custom actions (`stadsactiviteitCreate`, `bestandUpload`,
`drupalRequest`, `drupalLogin`) bouwen hun URL daaruit op. Ze zijn
**tolerant**: een argument dat met `http(s)://` begint wordt
ongewijzigd gebruikt, anders als pad achter `apiBaseUrl` geplakt —
dus alle ~26 bestaande aanroepen met een volledige URL blijven
werken en kunnen in eigen tempo ingekort worden. Truc: de parameter
zelf wordt overschreven (`url = ...`), zodat verderop in de action
niets aangepast hoefde te worden. **Let op:** custom *Functions*
kunnen `FFAppState()` NIET lezen (`custom_functions.dart`
importeert `app_state.dart` niet, en dat importblok is niet
bewerkbaar) — custom *Actions* wel.
**Nog open (Fase B):** de 13 top-level API Calls in `api_calls.dart`
hebben nog een hardcoded host. Centraliseren betekent ze in de
bestaande API Group onderbrengen (`ProductieGroup`) — check eerst
of het ⋮-menu van een API Call een "Move to group" heeft; zo niet
moet elke call opnieuw aangemaakt worden ín de groep en moet **elke
Backend Query die 'm gebruikt opnieuw gebonden** worden (het dure,
foutgevoelige deel). Hernoem bij die gelegenheid de groep
`productie` → `drupal`/`backend`, want de naam klopt niet meer
zodra hij naar devbob wijst.
11. **Devbob zit achter HTTP Basic Auth** — alleen `apiBaseUrl` omzetten
naar devbob geeft op álles een 401. Er is een
`Authorization: Basic `-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`-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`
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](https://claude.ai/code/artifact/29f3412a-305f-49f3-971c-cd9bd310beb1).
Afvinkbare werklijst: [Designbrug werklijst](https://claude.ai/code/artifact/0c0c7347-d681-417d-83d7-7feb1de6457d).)*
**Bouwstap 5 — pagina `uitgaansevenementAanmaken`. IN UITVOERING
sinds 2026-08-31 avond. Stap 1 (custom action `evenementCreate`) is AF
en exportgeverifieerd; stap 2 (los testen tegen productie) staat klaar
op 2 kleine handelingen na — zie "STAND VAN ZAKEN" onderaan dit blok,
begin daar.** Dit is stap 3 uit de oorspronkelijke bouwvolgorde bovenaan
P2-15 ("Pagina Evenement aanmaken"). Alles hieronder is uitgezocht en
geverifieerd — een nieuwe sessie kan direct bouwen zonder eerst het
Artifact te hoeven lezen.
> ⚠️ **LET OP — de FlutterFlow-export is op dit moment GEBLOKKEERD.**
> `flutterflow export-code` faalt met `Status: 400 / Error generating
> code for the project`. Oorzaak is bekend en klein: het argument
> `fotosFids` van de testknop staat nog op "Unset" (zie STAND VAN ZAKEN
> punt 1). Zolang dat niet gezet is, kan **niemand** exporteren — dus
> dit als eerste oplossen, ook als je aan een heel andere taak begint.
**Kernpunt: deze pagina is EENVOUDIGER dan `stadsactiviteitAanmaken`,
niet moeilijker.** Het zwaarste stuk van bouwstap 3 — de 3-traps
provincie→gemeente→plaats-cascade — vervalt hier volledig. Drupal leidt
stad, adres én geo automatisch af uit de gekozen horecagelegenheid
(`hook_node_presave` kopieert `field_geo_horecagelegenheid`,
`field_hor_municipality_town` en `field_address3` over). De app stuurt
alleen het nid.
**API-contract `POST evenementen/create.json`** (uit het Artifact,
sectie "API-contract"; curl-bevestigd 2026-08-26):
| Argument | Type | Verplicht | Toelichting |
|---|---|---|---|
| `horecagelegenheid_nid` | int | **ja** | Moet een `horecagelegenheid`-node zijn met `uid` = ingelogde user, anders **403** |
| `titel` | string | **ja** | |
| `datum_start` | string | **ja** | `'YYYY-MM-DD HH:MM:SS'` |
| `datum_eind` | string | nee | leeg → gelijk aan `datum_start` |
| `omschrijving` | string | nee | |
| `entree_tid` | int | nee | tid uit `entreeopties/index` |
| `entree_prijs` | string | nee | decimaal als string, bv. `"7.50"` |
| `toelichting_entree` | string | nee | |
| `tickets_url` | string | nee | **bestaat niet op stadsactiviteit** |
| `website_url` | string | nee | |
| `categorie_tids` | array | nee | uit `categorieen/index`; ongeldige tid → 400 |
| `logo_fid` | int | nee | fid uit `bestand_upload/upload` |
| `fotos_fids` | array | nee | max 5, meer → 400 |
**Verschillen met `stadsactiviteiten/create` — let hier op:**
1. **`horecagelegenheid_nid` vervangt `plaats_tid`.** Geen plaats-picker,
geen 3-traps cascade, geen Page State voor provincie/gemeente/plaats.
2. **Géén `adres`-struct, géén `organisator`, géén `contact`** — die
drie velden bestaan hier niet. Scheelt 4 TextFields.
3. **`tickets_url` komt er wél bij** (1 extra TextField).
4. **De response is anders en dat is gebruikerszichtbaar:**
`{"status": "created", "nid": "..."}` — de node komt **direct
gepubliceerd** binnen. Toon dus **"geplaatst"**, NIET "wordt
beoordeeld" (dat laatste hoort alleen bij stadsactiviteiten, die
komen altijd ongepubliceerd binnen).
5. **Eigenaarschap-gate:** een horecagelegenheid van iemand anders geeft
403 met "Deze horecagelegenheid is niet van jou" (curl-bevestigd op
nid 70132). Kan in de praktijk niet gebeuren als de picker uit
`mijn_horecagelegenheden` gevuld wordt, maar vang de 403 netjes af.
**Wat er al klaarstaat (niets van dit hoeft opnieuw gebouwd):**
- Custom action **`bestandUpload`** (`lib/custom_code/actions/bestand_upload.dart`)
— foto → fid. Wil de **volledige** upload-URL, niet alleen een base.
- API Calls **`MijnHorecagelegenhedenCall`**, **`CategorieenCall`**,
**`EntreeoptiesCall`** (`lib/backend/api_requests/api_calls.dart`) —
alle drie bestaan en zijn exportgeverifieerd.
- Pagina **`stadsactiviteitAanmaken`** (`lib/stadsactiviteit_aanmaken/`)
— het te kopiëren sjabloon voor de TextFields, de twee datumvelden
(`readOnly` + `showDatePicker` → `showTimePicker` → gecombineerd
terug in het veld), de categorie-multiselect en de foto-upload.
- Pagina **`mijnProfiel`** (`lib/mijn_profiel/`) — heeft al een
"+ Voeg toe"-knop bij "Mijn horecagelegenheden" die nu nog een
placeholder-snackbar toont; die wordt straks de entry-point.
**Wat nieuw gebouwd moet worden:**
1. ~~**Custom action `evenementCreate`**~~ — **AF (2026-08-31).**
Staat in `lib/custom_code/actions/evenement_create.dart`, 16
argumenten (`sessionName`, `sessionId`, `token`,
`horecagelegenheidNid`, `titel`, `datumStart` verplicht; de rest
nullable, waarvan `categorieTids` en `fotosFids` als
`List?`). Return type JSON (`Future`). POST naar
`$base/en/flutterdrup/evenementen/create.json` met `apiBaseUrl` uit
App State. 403 wordt apart afgevangen met de tekst "Deze
horecagelegenheid is niet van jou". Geverifieerd met een verse
export: byte-identiek aan de aangeleverde bron en `dart analyze` op
de hele export gaf 0 errors.
2. **Pagina `uitgaansevenementAanmaken`** — Scaffold + AppBar
("Show Default Button" aan), scrollbare Column, padding 16.
6 TextFields (Titel · Omschrijving · Entreeprijs · Toelichting
entree · Tickets-URL · Website-URL), 2 datumvelden, 1 dropdown
horecagelegenheid, 1 dropdown entree, 1 categorie-multiselect,
logo-upload + foto's-upload.
3. **Horecagelegenheid-picker**: DropDown met Backend Query
`MijnHorecagelegenheden` (variabelen `session_name`/`sessid` →
App State `userSessionname`/`userSessionid`), optie-label `$.titel`,
waarde `$.nid`. **Auto-select bij precies 1 resultaat** (Bob's eis
uit de oorspronkelijke bouwvolgorde).
4. **Verzendknop** — en neem hier meteen de les van P2-15 restpunt 7
mee: laat de knop **geen non-null assertion op een Page State-veld**
krijgen. Schakel 'm uit (of verberg 'm) zolang
`horecagelegenheid_nid`, titel of datum niet gezet zijn, anders
crasht indienen met "Null check operator used on a null value"
vóórdat de custom action zijn eigen nette foutmelding kan tonen.
5. **Page parameter `horecagelegenheid_nid`** (optioneel) zodat
`mijnProfiel`'s "+ Voeg toe"-knop 'm vooringevuld kan meegeven.
**Bouwvolgorde-advies:** eerst de custom action + los testen tegen
productie met één minimaal geldig verzoek (titel + nid + datum), dán
pas de formulier-pagina — zelfde volgorde die bij bouwstap 1/2 goed
werkte.
---
### STAND VAN ZAKEN bouwstap 5 (2026-09-02) — hier verder
**Gedaan (Claude, 2026-09-02):** de pagina **`uitgaansevenementAanmaken`**
bestaat, route `/uitgaansevenementAanmaken`. Gemaakt door
`stadsactiviteitAanmaken` te dupliceren (rechtsklik → Duplicate Page →
Rename Page) en daarna op te schonen:
- **Verwijderd:** het complete blok "Waar" (Mijn-gemeenten-picker, de
provincie/gemeente/plaats-cascade, adres, postcode, plaats) én de
velden **Organisator** en **Contact**.
- **Behouden:** Titel, Omschrijving, categorie-multiselect, beide
datumvelden mét hun picker-logica, Website-URL, entree-dropdown,
toelichting entree, entreeprijs, logo-upload, foto's-upload,
verzendknop. (Dat zijn precies de dure onderdelen — niet opnieuw
bouwen.)
**Export weer open (2026-09-02).** De blokkade zat níet in de
upload-actienamen maar in de **verzendknop**: die riep nog
`stadsactiviteitCreate` aan met argumenten die naar de verwijderde
velden wezen (`plaatsTid → createPlaatsID` e.d.). Zodra de custom action
omgezet werd naar `evenementCreate` verdween de hele oude argumentenlijst
en liep de export weer (`All done!`).
**Ook gedaan:** de twee upload-actienamen zijn uniek gemaakt
(`UploadDataLogoEvenement`, `UploadDataFotosEvenement`) — dat was nodig
omdat `Duplicate Page` ze meekopieert en ze projectbreed uniek moeten zijn.
**Stand in de export geverifieerd:** 7 tekstvelden, 2 dropdowns, 3 knoppen,
roept `evenementCreate` aan, en **0** resten van Organisator / Contact /
Provincie / Postcode / Adres.
**Stand bouwstap 5 na sessie 2026-09-03b (Claude bouwde zelf in de builder):**
*Afgerond en met verse export geverifieerd:*
- **Punt 3 (page state).** `createProvincieID` en `createGemeenteID` verwijderd;
`createPlaatsID` **hernoemd** naar `createHorecagelegenheidNid` (String,
nullable). Die hernoeming nam meteen de bestaande Visibility-conditie van
`ButtonIndienen` mee — die stond op `createPlaatsID != null && != ''` en
guard nu dus op de horecagelegenheid. Daarmee is punt 4's eis "knop uit
zolang horecagelegenheid leeg is" gratis geregeld.
- **Punt 2 (tickets-URL).** `TextFieldWebsiteURL` gedupliceerd →
`TextFieldTicketsURL`, label `TicketsURL`, hint "Link om kaarten te kopen",
staat in `ContainerOrganisatie` onder het website-veld.
- **Punt 1 (horeca-dropdown).** `DropDownCategorieen` gedupliceerd →
`DropDownHorecagelegenheid` (laatste kind van `ContainerWat`'s Column),
multi-select uit, Backend Query = `MijnHorecagelegenheden` met
`session_name`/`sessid` → App State `userSessionname`/`userSessionId`,
Options Values `$[:].nid`, Labels `$[:].titel`, hint "Kies je
horecagelegenheid", On Selected → Update Page State
`createHorecagelegenheidNid`.
- **Punt 6 (succesmelding).** Snackbar in de TRUE-tak zegt nu "Je evenement is
geplaatst." De FALSE-tak toont `$.error` en bleef ongewijzigd.
- **Punt 5 (page parameter).** Optionele String-page-parameter
`horecagelegenheidNid` toegevoegd. Bijbehorende bedrading: On Page Load →
Update Page State `createHorecagelegenheidNid` = die parameter, én de
dropdown's **Initial Option Value** = diezelfde parameter (initial value moet
aan de parameter hangen, niet aan de page state — On Page Load draait pas ná
de eerste build).
*Punt 0 (argumenten `evenementCreate`) — 15 van de 16 gebonden:*
`sessionName`→`userSessionname`, `sessionId`→`userSessionId`,
`token`→`userToken`, `horecagelegenheidNid`→page state
`createHorecagelegenheidNid`, `titel`→`TextFieldTitel`,
`datumStart`→`datumVoorApi(TextFieldDatumStart)`,
`datumEind`→`datumVoorApi(TextFieldDatumEind)`,
`omschrijving`→`TextFieldOmschrijving`, `entreeTid`→`createEntreeTid`,
`entreePrijs`→`TextFieldEntreeprijs`,
`toelichtingEntree`→`TextFieldToelichtingEntree`,
`ticketsUrl`→`TextFieldTicketsURL`, `websiteUrl`→`TextFieldWebsiteURL`,
`categorieTids`→`createCategorieTids`, `logoFid`→`createLogoFid`.
**Punt 0 is compleet:** alle 16 argumenten van `evenementCreate` zijn gebonden
en met een verse export geverifieerd (`dart analyze` op die export: 0 errors).
De 16e (`fotosFids`) stond eerst per ongeluk op `createCategorieTids` en is door
Bob gecorrigeerd naar `createFotosFids`.
**Let op bij toekomstig werk aan deze knop:** het onderste argument van een lange
argumentenlijst valt in Claude's browserviewport onder de onderrand van het
paneel en is daar niet bereikbaar (collapsen van alle andere argumenten,
muiswiel-scrollen, scrollbar slepen en Page Down helpen geen van alle) — die
laatste rij moet Bob zetten. Zie ook de CLAUDE.md-notitie hierover.
**Restpunten 1-3 zijn afgerond (2026-09-03b, Bob + Claude samen):**
1. De horeca-dropdown heeft nu een eigen tekstlabel "Uw Horecagelegenheid"
erboven; het categorie-label staat weer bij de categorie-dropdown.
2. **Auto-select bij precies 1 horecagelegenheid is gebouwd.** Nieuwe custom
function `horecaVoorselectie(mijnHoreca, paramNid)` (Json + String? in,
String? uit): geeft de page parameter terug als die gezet is, anders het
`nid` als de lijst precies 1 zaak bevat, anders `null`. Op twee plekken
gebruikt, want de lijst leeft alleen bínnen de `FutureBuilder` van de
dropdown terwijl de knop-zichtbaarheid aan de page state hangt:
- **Dropdown → Initial Option Value** = `horecaVoorselectie(, )`. 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 `[]`. 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 Response` → **JSON 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`). 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? 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`-argument mag
niet op Unset blijven, ook niet als het in Dart nullable is. Recept:
Value-potlood → Set Variable → **Inline Function** → expressie
`[]` → **"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`, `sessid` → `userSessionid`).
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:
| 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.
---