Sfoglia il codice sorgente

TASKS/CLAUDE: vertaalronde Deel A, caching-taken af, helppagina live

Sessie waarin Bob de builder-klikken deed en Claude per taak met een verse
export/curl verifieerde. Afgerond: P1-17 Deel A op 3 regels na, taak 58
punten 1-3 (establishments in de edge-cache, cache:true op 8 API Calls,
taxonomie op 2 uur), taak 59 helppagina + privacybeleid, taak 57 punt 1
(Cultuur-icoon) en P1-53 (thuisBezorgen AppBar). P2-36 afgewezen.

CLAUDE.md: Cloudflare-stand bijgewerkt naar twee Cache Rules, plus de les
dat veel vertaalsleutels op Tooltip-nodes zitten.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bob 15 ore fa
parent
commit
460d2948ce
2 ha cambiato i file con 192 aggiunte e 144 eliminazioni
  1. 40 13
      CLAUDE.md
  2. 152 131
      TASKS.md

+ 40 - 13
CLAUDE.md

@@ -2838,6 +2838,19 @@ bovenaan in te klappen (chevron rechts) levert 3-4 extra rijen op, meer
 niet. In Bob's eigen browser werkt dit paneel waarschijnlijk gewoon —
 niet. In Bob's eigen browser werkt dit paneel waarschijnlijk gewoon —
 voor hem blijft het de snelste route.
 voor hem blijft het de snelste route.
 
 
+**⚠️ Een groot deel van de vertaalsleutels zit op TOOLTIP-nodes, niet op
+zichtbare labels — daar kom je nooit op door de app te bekijken.** Bevestigd
+2026-09-16: `Terug` (`vnwx710y`) is de tooltip op het terug-pijltje onderaan de
+drawer, `Veld wissen` (`pj545url`) en `Wachtwoord tonen/verbergen` (`n3cyffso`)
+zijn de tooltips rond de twee invoervelden op de Login-pagina. Een tooltip is
+alleen zichtbaar bij tikken, dus zoeken op de Nederlandse tekst in de app helpt
+niet. **Route: widget tree → zoek op `Tooltip`** — de `AlignedTooltip`-nodes
+staan dan in documentvolgorde, dus in dezelfde volgorde als de velden op het
+scherm. Het globe-icoontje zit op het Text/Content-veld ván die tooltip.
+**En: het vertaaldialoogje heeft last van het bekende "ziet er opgeslagen uit,
+was het niet"-patroon** — in één ronde kwamen 2 van de 8 regels door, zonder
+enige foutmelding. Verifieer altijd met een verse export, niet met het paneel.
+
 **Werkend alternatief, per widget (bevestigd 22x achter elkaar):**
 **Werkend alternatief, per widget (bevestigd 22x achter elkaar):**
 selecteer het widget → in het rechterpaneel staat naast het label
 selecteer het widget → in het rechterpaneel staat naast het label
 **"Text"** (bij een `Text`-widget onder "Text Properties", bij een
 **"Text"** (bij een `Text`-widget onder "Text Properties", bij een
@@ -3586,23 +3599,37 @@ aantal round trips bij infinite scroll voor 0,01 s. Staat sinds die datum op
 `defaults['filter_groups']` op FALSE — **de pager erven ze wél**, dus dit is
 `defaults['filter_groups']` op FALSE — **de pager erven ze wél**, dus dit is
 één wijziging op de Master voor alle zeven.
 één wijziging op de Master voor alle zeven.
 
 
-**Cloudflare cachet sinds 2026-09-15 zes publieke endpoints 5 minuten aan de
-edge — reken daarmee bij élke meting én bij elke Drupal-deploy.** Rule
-"FlutterFlow publieke endpoints": `GET` zonder `SESS`-cookie op
-`flutterflowmobiel1.json`, `flutterflow_events.json`,
-`flutterflowmobiel_establishment_info.json`,
-`flutterflowmobiel_establishment_events.json`, `plaatsen.json` en
-`plaatsen_bij_gemeente.json`; Edge TTL 300 s, "Ignore cache-control" (de origin
-stuurt `no-store`). Gemeten: MISS 1,2 s → HIT 0,06–0,09 s. Drie gevolgen:
+**Cloudflare cachet de publieke endpoints aan de edge, sinds 2026-09-16 verdeeld
+over TWEE Cache Rules — reken daarmee bij élke meting én bij elke Drupal-deploy.**
+Beide rules matchen op `GET` zonder `SESS`-cookie, met "Ignore cache-control"
+(de origin stuurt `no-store`):
+- **"FlutterFlow publieke endpoints" — Edge TTL 300 s (5 min):**
+  `flutterflowmobiel1.json`, `flutterflow_events.json`,
+  `flutterflowmobiel_establishment_info.json`,
+  `flutterflowmobiel_establishment_events.json` en sinds 2026-09-16 ook
+  **`flutterflowmobiel_establishments.json`** (het zwaarste endpoint, 1,3–1,8 s;
+  het horeca-overzicht laadt er zes, één per tab — MISS → HIT gemeten).
+- **"FlutterFlow publieke endpoints 60 minuten" — Edge TTL 2 uur:**
+  `plaatsen.json` en `plaatsen_bij_gemeente.json` (taxonomie, verandert zelden).
+  Het TTL-dropdownmenu heeft geen optie van 1 uur, vandaar 2.
+
+Gemeten: MISS 1,2 s → HIT 0,06–0,09 s. Vier gevolgen:
 - Een `_cb=`-cache-buster omzeilt Cloudflare **én** Drupal's page cache. Een
 - Een `_cb=`-cache-buster omzeilt Cloudflare **én** Drupal's page cache. Een
   `_cb`-test bewijst dus dat de code klopt, maar zegt niets over wat de app
   `_cb`-test bewijst dus dat de code klopt, maar zegt niets over wat de app
-  ziet — die krijgt tot 5 min het oude antwoord. Na een deploy: `cc all` **en**
-  de rule purgen.
-- `flutterflowmobiel_establishments.json` hoort er bewust **niet** in (filtert
-  per gebruiker op favorieten). Zet nooit een endpoint in de lijst dat een
-  sessie-afhankelijk antwoord geeft.
+  ziet. Na een deploy: `cc all` **en** de juiste rule purgen — vergeet de
+  2-uurs-rule niet als je aan de plaatsen-endpoints hebt gezeten.
+- ⚠️ `flutterflowmobiel_establishments.json` mag **alleen** in de rule met de
+  `SESS`-cookie-voorwaarde: `custom_views_query_alter()` filtert die view voor
+  een ingelogde gebruiker op favorieten. Zonder die voorwaarde beland je met
+  iemands persoonlijke lijst in een gedeelde cache.
 - Basic-Auth-requests worden gewoon meegecachet (HIT gezien met `-u`), dus de
 - Basic-Auth-requests worden gewoon meegecachet (HIT gezien met `-u`), dus de
   cache is gedeeld over alle bezoekers — precies de bedoeling voor deze data.
   cache is gedeeld over alle bezoekers — precies de bedoeling voor deze data.
+- **Meet met de query die de app echt gebruikt.** `plaatsen.json` kaal gaf
+  `cf-cache-status: DYNAMIC` (geen rule-match), terwijl
+  `plaatsen.json?limit_levels=2` — wat de app stuurt — netjes HIT gaf. En een
+  eerste paar metingen kan MISS/MISS tonen doordat je op verschillende edge-PoP's
+  landt (`cf-ray` laat dat zien: AMS vs LHR); meet drie keer vóór je concludeert
+  dat een rule niet werkt.
 
 
 **`flutterflowmobiel1` accepteert (na deploy van 2026-09-15) ook `zoek=`:** een
 **`flutterflowmobiel1` accepteert (na deploy van 2026-09-15) ook `zoek=`:** een
 hoofdletterongevoelige `LIKE` op `node.title`, gebouwd in
 hoofdletterongevoelige `LIKE` op `node.title`, gebouwd in

+ 152 - 131
TASKS.md

@@ -28,6 +28,27 @@ FlutterFlow-commit op `main`**, de commit-knop in het Version Control-paneel
 reageerde niet op Claude's klik. `dart analyze` op de verse export: 0 errors.
 reageerde niet op Claude's klik. `dart analyze` op de verse export: 0 errors.
 Emulator-5556 heeft nu de profile-APK van die export + per-app-locale `nl-NL`.*
 Emulator-5556 heeft nu de profile-APK van die export + per-app-locale `nl-NL`.*
 
 
+### ✅ Sessie 2026-09-16 (Bob klikt, Claude verifieert) — vertalingen, caching, helppagina
+
+*Werkwijze: Claude gaf taken per nummer, Bob voerde ze uit in zijn eigen browser,
+Claude verifieerde elke taak met een verse export of een `curl`. Alles hieronder
+is geverifieerd, niet alleen "Synced" in de builder.*
+
+- **P1-17 Deel A vrijwel af** (zie Bob's 7, taak 3) — restant: 3 regels op het
+  horeca-overzicht, staat als Claude-taak bij de restpunten.
+- **Taak 58 caching: punten 1, 2 en 3 afgerond.** Establishments-endpoint in de
+  5-minuten-rule (MISS → HIT gemeten), `cache: true` op 8 extra API Calls (13 van
+  18 nu), en een tweede Cloudflare-rule met de twee taxonomie-endpoints op 2 uur.
+- **Taak 59 helppagina af**: twee publieke Drupal-nodes + menu-item "Support
+  pagina" in de drawer. Enige restpunt is de 403 op `/nl/account-verwijderen`,
+  verhuisd naar Bob's 7 taak 2 (Play Console).
+- **Taak 57 punt 1 af**: Cultuur-icoon in het menu (`Icons.theater_comedy`).
+- **P1-53 af** (Claude, in de builder): `thuisBezorgen` AppBar op leisteen.
+- **`filterDatumVan`/`filterDatumTot` geplakt** — datumfilter werkt nu in NL én EN.
+- **P2-36 afgewezen door Bob**: kaarttitels blijven Roboto.
+- **Niet opgepakt, bewust:** de builder-opruimronde (Bob: "andere keer") en
+  P2-35, dat eerst op een toestel nagemeten moet worden.
+
 ### ✅ Sessie 2026-09-16 (Claude, builder + verse export per stap) — zoekveld + look&feel
 ### ✅ Sessie 2026-09-16 (Claude, builder + verse export per stap) — zoekveld + look&feel
 
 
 *Opdracht Bob: "maak van het zoekveld een taak, doe dat direct; pas ook de look &
 *Opdracht Bob: "maak van het zoekveld een taak, doe dat direct; pas ook de look &
@@ -99,11 +120,14 @@ P1-52 `EventCurrent`, P2-30 horeca-overzicht, P2-31 kaartstijlen, P2-32 titels,
 P2-33 horeca-detail. **Werk uit die lijst, niet uit deze** — hier staan alleen
 P2-33 horeca-detail. **Werk uit die lijst, niet uit deze** — hier staan alleen
 de twee dingen die daar niet in voorkomen, gezien op de telefoon-emulator.*
 de twee dingen die daar niet in voorkomen, gezien op de telefoon-emulator.*
 
 
-1. **Het Cultuur-icoon in het hoofdmenu springt uit de lijn** — het staat iets
-   lager en groter dan de zes andere icoontjes in het Gemeente-blok. Zelfde soort
-   oorzaak als het "Zoek stad"-icoon dat deze sessie gefixt is (dat stond op
-   `size: 1.0`): kijk met `grep -A3 "leading: Icon(" drawer_component_widget.dart`
-   welke rij een afwijkende `size:` heeft. Kleine ingreep.
+1. ~~Cultuur-icoon in het hoofdmenu~~ **✅ GEDAAN 2026-09-16 (Bob).** Het lag
+   *niet* aan een afwijkende `size:` — alle leading-iconen in de drawer staan op
+   de default. De oorzaak was het icoon zelf: `FontAwesomeIcons.piedPiperAlt`
+   (een tech-bedrijfslogo) heeft een andere glyph-verhouding dan de Material-
+   iconen ernaast. Vervangen door `Icons.theater_comedy` op beide plekken
+   (Gemeente-blok én Provincie-blok), export-geverifieerd. **Les:** een icoon dat
+   "uit de lijn springt" is net zo vaak een verkeerd gekozen FontAwesome-glyph
+   als een verkeerde `size`.
 2. **Het menu is lang doordat het Gemeente-blok en het Provincie-blok dezelfde
 2. **Het menu is lang doordat het Gemeente-blok en het Provincie-blok dezelfde
    zeven rijen herhalen** (Uitgaan, Activiteiten, Cultuur, Films, Jeugd, Horeca,
    zeven rijen herhalen** (Uitgaan, Activiteiten, Cultuur, Films, Jeugd, Horeca,
    Thuis bezorgen). Overweeg het Provincie-blok standaard ingeklapt te tonen.
    Thuis bezorgen). Overweeg het Provincie-blok standaard ingeklapt te tonen.
@@ -133,77 +157,64 @@ uit de sessie van 15 sep: dezelfde URL drie keer achter elkaar gaf 1,23 s (MISS)
 → 0,10 s (HIT) → 0,11 s (HIT). Drupal's page cache doet dus 12× zoveel als alle
 → 0,10 s (HIT) → 0,11 s (HIT). Drupal's page cache doet dus 12× zoveel als alle
 micro-optimalisaties samen.
 micro-optimalisaties samen.
 
 
-1. **`flutterflowmobiel_establishments.json` alsnog in de Cloudflare-rule
-   "FlutterFlow publieke endpoints".** Grootste winst die er nog ligt. Dit is
-   het zwaarste endpoint (100 items per pagina, 1,3–1,8 s gemeten) en het
-   horeca-overzicht laadt er **zes** van, één per tab. Met de bestaande
-   rule-voorwaarde *"GET zonder `SESS`-cookie"* is het veilig: de app stuurt op
-   `HorecagelegenheidoverzichtCall` **geen** sessiecookie mee (`headers: {}` in
-   de export), en de favorietenfilter in `custom_views_query_alter()` grijpt
-   alleen bij een ingelogd verzoek. Verwachte winst: ~1,5 s → ~0,1 s per tab.
-   ⚠️ Zet dit endpoint **nooit** in de rule zonder die cookie-voorwaarde.
-2. **`cache: true` op meer API Calls.** Nu staat de vlag op 5 van de 18:
-   `homeTabel`, `EstablishmentInfo`, `Gemeenten`, `Provincies`,
-   `Horecagelegenheidoverzicht`. Ontbreekt op user-onafhankelijke reads die
-   binnen één sessie vaak herhaald worden: **`homeSlider`**, **`Uitgaanstabel`**,
-   **`UitgaanSlider`**, **`Evenement`** (evenement-detail),
-   **`HorecagelegenheidEvents`**, **`PlaatsenBijGemeente`**, **`Categorieen`**,
-   **`Entreeopties`**. Levert winst bij terugnavigeren binnen een sessie.
-   *De cache is veilig* — `ApiCallOptions extends Equatable` met `params` en
-   `headers` in `props`, dus hij vergelijkt op echte parameterwaarden.
-   *Kanttekening:* FlutterFlow's cache verloopt nooit binnen een sessie, dus
-   voor de **sliders** betekent dit dat een gebruiker die de app uren open laat
-   staan oude slider-inhoud houdt. Voor `Evenement` en `Entreeopties` is dat
-   geen bezwaar.
-3. **Langere edge-TTL voor taxonomie.** `plaatsen.json` en
-   `plaatsen_bij_gemeente.json` veranderen zelden — 30 tot 60 minuten in plaats
-   van 5. Puur een getal in de bestaande Cloudflare-rule.
+1. ~~`flutterflowmobiel_establishments.json` in de Cloudflare-rule~~ **✅ GEDAAN
+   2026-09-16 (Bob), gemeten door Claude:** eerste verzoek `MISS`, tweede `HIT`.
+   Staat in de rule "FlutterFlow publieke endpoints" (5 min) mét de voorwaarde
+   *"GET zonder `SESS`-cookie"* — die voorwaarde is niet optioneel, want
+   `custom_views_query_alter()` filtert deze view per ingelogde gebruiker op
+   favorieten.
+2. ~~`cache: true` op meer API Calls~~ **✅ GEDAAN 2026-09-16 (Bob),
+   export-geverifieerd:** alle 8 (`homeSlider`, `Uitgaanstabel`, `UitgaanSlider`,
+   `Evenement`, `HorecagelegenheidEvents`, `PlaatsenBijGemeente`, `Categorieen`,
+   `Entreeopties`) staan nu op `cache: true`. Dus 13 van de 18.
+   *Kanttekening die blijft gelden:* FlutterFlow's cache verloopt nooit binnen
+   een sessie, dus wie de app uren open laat staan houdt oude **slider**-inhoud.
+   Zie dat als gedrag om in de gaten te houden, niet als bug.
+3. ~~Langere edge-TTL voor taxonomie~~ **✅ GEDAAN 2026-09-16 (Bob), gemeten
+   door Claude (`cf-cache-status: HIT` op beide).** `plaatsen.json` en
+   `plaatsen_bij_gemeente.json` staan nu in een **tweede** Cache Rule
+   ("FlutterFlow publieke endpoints 60 minuten") met Edge TTL **2 uur** — het
+   dropdownmenu heeft geen optie van 1 uur. De eerste rule houdt de vier
+   content-endpoints op 5 minuten. ⚠️ Bij een volgende Drupal-deploy die deze
+   twee endpoints raakt: die rule óók purgen, anders zie je tot 2 uur het oude
+   antwoord.
 4. **Lokaal persistent cachen** (Home meteen tonen uit een opgeslagen snapshot,
 4. **Lokaal persistent cachen** (Home meteen tonen uit een opgeslagen snapshot,
    ook offline). Een custom action van gemiddelde omvang die slecht past op
    ook offline). Een custom action van gemiddelde omvang die slecht past op
    FlutterFlow's FutureBuilder-patroon, en met Cloudflare op 0,1 s is de winst
    FlutterFlow's FutureBuilder-patroon, en met Cloudflare op 0,1 s is de winst
    klein. **Niet vóór livegang.**
    klein. **Niet vóór livegang.**
 
 
-### 🆕 Taak 59 · Helppagina — antwoord op Bob's vraag, plus hoe te bouwen
-
-**Bob's vraag: "kan ik een helppagina maken waarin URL's van de website zelf
-getoond worden, zodat ik teksten niet 2× hoef bij te houden?" — Ja, en dat is
-ook het advies.** Gemeten 2026-09-16: de site heeft er nu nog niets voor staan.
-`uitgaanskrant.com/nl/help`, `/faq`, `/veelgestelde-vragen`, `/over-ons`,
-`/contact`, `/uitleg`, `/hoe-werkt-het` en `/privacy` geven alle **404**;
-`/account-verwijderen` bestaat wél maar geeft anoniem een **403**.
-
-**Aanpak, twee delen:**
-1. **Bob, in Drupal:** maak één node, bijvoorbeeld `/nl/app-help`, met de
-   inhoud. Wat er in elk geval in hoort: wat Uitgaanskrant is · gemeente kiezen
-   en volgen · inloggen en favorieten · een evenement of stadsactiviteit
-   aanmaken (voor horeca en city-editors) · contact · privacy · account
-   verwijderen. Eén node bijwerken = de app is meteen bij, géén nieuwe release.
-2. **Claude, in de builder (kleine taak):** menu-item **"Help"** onderaan
-   `drawerComponent` (dupliceer een bestaand `ListTile`, zie `CLAUDE.md` — dat
-   is betrouwbaarder dan Insert Widget) met On Tap → **Launch URL** naar die
-   node. Launch URL staat in de Actions-zoeklijst onder categorie *Share*, niet
-   onder Navigation.
-
-**Keuze die Bob daarbij maakt: extern openen of in de app?**
-- **Launch URL** (externe browser) is één actie, nul onderhoud, en de gebruiker
-  ziet de vertrouwde site. Nadeel: hij verlaat de app.
-- **Een WebView-pagina in de app** houdt hem binnen de app en kan de standaard
-  header + terugknop krijgen, maar toont de volledige sitehuisstijl inclusief
-  menu en cookiebanner, tenzij je in Drupal een kale weergave maakt.
-- **Advies: begin met Launch URL.** Dat kost tien minuten en is altijd nog om
-  te bouwen naar een WebView.
-
-⚠️ **Los hiervan, en het blokkeert de store-review:** `/nl/account-verwijderen`
-geeft anoniem een 403. Google's reviewers openen die URL **zonder in te loggen**.
-Daar moet minimaal een publiek zichtbare uitleg staan van hoe je je account laat
-verwijderen. Dit hoort bij Bob's taak 2 (Play Console).
+### ✅ Taak 59 · Helppagina — AFGEROND 2026-09-16 (Bob + Claude)
+
+Twee Drupal-nodes live, allebei **publiek bereikbaar** (anoniem 200, gemeten):
+- **Help:** `https://uitgaanskrant.com/nl/support/mobieleapp`
+- **Privacybeleid:** `https://uitgaanskrant.com/nl/support/privacybeleid`
+
+In de app: menu-item **"Support pagina"** onderaan `drawerComponent`, vlak boven
+"Mijn Account", met `launchURL('https://uitgaanskrant.com/nl/support/mobieleapp')`
+— export-geverifieerd, Engelse vertaling ("Support page") staat er ook in.
+Claude heeft de volledige Nederlandse helptekst aangeleverd (app-uitleg, gemeente
+kiezen/volgen, account + favorieten, zelf aanmelden, privacy met link naar het
+privacybeleid, account verwijderen, contact); Bob heeft die geplaatst en de
+e-mailadressen ingevuld. Bijwerken = één node bewerken, geen nieuwe release.
+
+⚠️ **Eén punt blijft open en het blokkeert de store-review** (hoort bij Bob's
+taak 2, Play Console): **`/nl/account-verwijderen` geeft anoniem een 403.**
+Google's reviewers openen die URL zonder in te loggen. Twee manieren om het te
+dichten: geef `https://uitgaanskrant.com/nl/support/mobieleapp` op als
+account-verwijder-URL (de sectie "Je account verwijderen" staat daar publiek op),
+of maak de oude pagina publiek zichtbaar.
 
 
 ### 🟢 AFGEROND 2026-09-16 (Claude, sessie "taal-cluster") — browserloos, geen builder aangeraakt
 ### 🟢 AFGEROND 2026-09-16 (Claude, sessie "taal-cluster") — browserloos, geen builder aangeraakt
 
 
 *Geclaimd en uitgevoerd terwijl twee andere sessies in de builder zaten. Dit
 *Geclaimd en uitgevoerd terwijl twee andere sessies in de builder zaten. Dit
 cluster raakt géén FlutterFlow-pagina en géén Chrome — alles is meting +
 cluster raakt géén FlutterFlow-pagina en géén Chrome — alles is meting +
-kant-en-klare code. Er staat nog **één ding open dat Bob (of een sessie met de
-browser) moet doen**: de twee functie-bodies hieronder plakken.*
+kant-en-klare code.*
+
+**✅ Beide functie-bodies zijn op 2026-09-16 door Bob geplakt en
+export-geverifieerd** — `filterDatumVan` en `filterDatumTot` staan allebei in de
+taalonafhankelijke vorm in `custom_functions.dart`. De Home-chips staan op
+`All` / `Today` / `This weekend` / `This week`, dus de koppeling klopt en het
+datumfilter werkt in beide talen. De code hieronder blijft staan als referentie.
 
 
 **1 · `filterDatumVan` / `filterDatumTot` taalonafhankelijk — code klaar, moet
 **1 · `filterDatumVan` / `filterDatumTot` taalonafhankelijk — code klaar, moet
 nog in de Custom Code-editor.** Dit blokkeerde Bob's taak 3 (vertaalronde): de
 nog in de Custom Code-editor.** Dit blokkeerde Bob's taak 3 (vertaalronde): de
@@ -280,24 +291,20 @@ kopieën (`favorietenCopy`, `drawerComponentCopy`, `kanwegTestUpload`) zaten in
 de oude telling van 134/99 en zijn er nu uit.
 de oude telling van 134/99 en zijn er nu uit.
 Werkelijke omvang in levende code: **26 leeg, 58 gelijk aan NL, 8 fout**.
 Werkelijke omvang in levende code: **26 leeg, 58 gelijk aan NL, 8 fout**.
 
 
-**3 · Nieuw gevonden: 8 sleutels hebben geen lege maar een FOUTE Engelse
-vertaling** — die vallen in geen enkele telling op en zijn erger dan leeg, want
-de gebruiker krijgt een tekst die iets anders betekent. Oorzaak is het bekende
-`Duplicate`-patroon (een kopie erft de EN-tekst van het origineel):
-| sleutel | scherm | Nederlands | staat er nu in EN |
+**3 · 8 sleutels hadden geen lege maar een FOUTE Engelse vertaling** — die
+vallen in geen enkele telling op en zijn erger dan leeg, want de gebruiker
+krijgt een tekst die iets anders betekent. Oorzaak is het bekende
+`Duplicate`-patroon (een kopie erft de EN-tekst van het origineel).
+**5 van de 8 zijn op 2026-09-16 gefixt** (`vnwx710y`, `pj545url`, `n3cyffso`,
+`nosgkxy0`, `elx7kse2`) plus de inhoudelijke fout op `wachtwoordVergeten`
+(`503u6v73`, stond op *"Use your username or password to recieve an email"* —
+verkeerde betekenis én een typefout). **De resterende 3 staan alle drie op
+`uitgaansevenementAanmaken` en wachten dus op P2-29:**
+| sleutel | Nederlands | staat er nu in EN | moet worden |
 |---|---|---|---|
 |---|---|---|---|
-| `vnwx710y` | drawerComponent | Terug | Favorite |
-| `pj545url` | login | Veld wissen | Favorite |
-| `n3cyffso` | login | Wachtwoord tonen/verbergen | Favorite |
-| `nosgkxy0` | SelectStateDropDown | Gemeente volgen | Add to favorites |
-| `elx7kse2` | SelectStateDropDown | Je volgt deze gemeente | Add to favorites |
-| `qsf2va25` | uitgaansevenementAanmaken | Website van het evenement | Website van de activiteit |
-| `nzaqrq0w` | uitgaansevenementAanmaken | Link om kaarten te kopen | Website van de activiteit |
-| `9qx16tm9` | uitgaansevenementAanmaken | Evenement indienen | Activiteit indienen |
-
-Plus één inhoudelijke fout op `wachtwoordVergeten` (`503u6v73`): de Engelse zin
-zegt *"Use your username or password to recieve an email"* — verkeerde betekenis
-én een typefout. Correctie staat in het werkblad.
+| `qsf2va25` | Website van het evenement | Website van de activiteit | Event website |
+| `nzaqrq0w` | Link om kaarten te kopen | Website van de activiteit | Ticket link |
+| `9qx16tm9` | Evenement indienen | Activiteit indienen | Submit event |
 
 
 **4 · Wezenlijst opnieuw gemeten (aanvulling op P2-7 / Bob's taak 4).**
 **4 · Wezenlijst opnieuw gemeten (aanvulling op P2-7 / Bob's taak 4).**
 Reproduceerbaar bepaald door vanaf de startpagina de widget-graaf te volgen:
 Reproduceerbaar bepaald door vanaf de startpagina de widget-graaf te volgen:
@@ -364,17 +371,23 @@ telefoon-emulator bekeken (zie screenshots in `~/uk-shots/2026-09-16/na-*.png`).
   281.5 → 200 hoog en de witte Fill eraf.
   281.5 → 200 hoog en de witte Fill eraf.
 
 
 **Restpunten als taken (2026-09-16, niet geclaimd):**
 **Restpunten als taken (2026-09-16, niet geclaimd):**
-- **P1-53 · `thuisBezorgen` AppBar op leisteen · Eigenaar: Claude.** De nieuwe
-  pagina van sessie 47 heeft nog `backgroundColor: info` (wit + M3-tint). Zelfde
-  klik als P1-51: AppBar-node → Background Color → thema-token **Primary Text**.
-  Check: `grep -c "backgroundColor: FlutterFlowTheme.of(context).info"
-  lib/thuis_bezorgen/thuis_bezorgen_widget.dart` → 0.
-- **P1-54 · `EvenementInfo`: adresregels met iconen · Eigenaar: Claude.** Op
-  `EventCurrent` staan adres en plaats als kale tekst, en "Plaats" springt in
+- **P1-53 · ✅ afgerond 2026-09-16 (Claude, in de builder).** `thuisBezorgen`
+  AppBar stond op `backgroundColor: info`; nu **Primary Text** (leisteen), gelijk
+  aan de rest. Export-geverifieerd: 0× `.info`, 1× `.primaryText`.
+- **P1-54 · `EvenementInfo`: adresregels met iconen · Eigenaar: Claude
+  (toegewezen door Bob, 2026-09-16: "zet klaar als taak voor volgende keer").**
+  Op `EventCurrent` staan adres en plaats als kale tekst, en "Plaats" springt in
   omdat er een lege `Column` vóór staat (`evenement_info_widget.dart` r.~126-160).
   omdat er een lege `Column` vóór staat (`evenement_info_widget.dart` r.~126-160).
   Doel: dezelfde opbouw als de kaarten — `Row` met speldje-icoon + "adres,
   Doel: dezelfde opbouw als de kaarten — `Row` met speldje-icoon + "adres,
   plaats" op één regel, en de datum een kalender-icoon. Rechtsklik `TextAdres` →
   plaats" op één regel, en de datum een kalender-icoon. Rechtsklik `TextAdres` →
   Wrap Widget → Row, dan Insert Widget → Icon vooraan (landt op positie 0).
   Wrap Widget → Row, dan Insert Widget → Icon vooraan (landt op positie 0).
+- **P1-17 Deel A · laatste 3 regels · Eigenaar: Claude (toegewezen door Bob,
+  2026-09-16).** Deel A is verder helemaal af; dit is wat er nog ligt, alle drie
+  op `horecagelegenhedenOverzichtCurrent`, via het globe-icoontje:
+  `qqb1howd` "Zoek op naam" is nog **leeg** → **Search by name**; `hnnqfnhk`
+  staat op "Eatings" (geen Engels) → **Restaurants**; `ykjsgvsf` staat op
+  "Overnight" → **Accommodation**. Daarna is Deel A rond en blijft alleen Deel B
+  (de twee aanmaakpagina's) over, dat op P2-29 wacht.
 - **P2-34 · ✅ afgerond 2026-09-16 (Claude, optie A, Bob's akkoord).**
 - **P2-34 · ✅ afgerond 2026-09-16 (Claude, optie A, Bob's akkoord).**
   `HorecagelegenheidoverzichtKaart`: de `Row` is in een `Column` gewrapt en de
   `HorecagelegenheidoverzichtKaart`: de `Row` is in een `Column` gewrapt en de
   `Wrap` met labels staat er nu onder, over de volle kaartbreedte (spacing 6).
   `Wrap` met labels staat er nu onder, over de volle kaartbreedte (spacing 6).
@@ -385,21 +398,20 @@ telefoon-emulator bekeken (zie screenshots in `~/uk-shots/2026-09-16/na-*.png`).
   Export-geverifieerd (geen `Align(` meer rond de labels, `List.generate`
   Export-geverifieerd (geen `Align(` meer rond de labels, `List.generate`
   intact), `dart analyze` 0 errors, op de telefoon-emulator nagekeken
   intact), `dart analyze` 0 errors, op de telefoon-emulator nagekeken
   (`~/uk-shots/2026-09-16/na3-horeca-ovz.png`).
   (`~/uk-shots/2026-09-16/na3-horeca-ovz.png`).
-- **P2-35 · `HorecagelegenheidCurrent`: "Geen evenementen"-strook alleen in
-  Agenda-context · Eigenaar: Claude.** Onderin staat permanent
-  `GeenEvenementenComponent` + knop "Alle horecagelegenheden", ook als de zaak
-  events heeft en ook op de Info-tab. Hoort alleen zichtbaar als de
-  agenda-lijst leeg is: Visibility-conditie op het component op "aantal items
-  van de events-call == 0" (Set from Variable → API-response → Number of Items
-  → Equal to 0), of het blok in de Agenda-tab zetten.
-- **P2-36 · Kaarttitels in serif? · Eigenaar: Bob (besluit).** Headline L/M/S
-  staan nu op Noto Serif; de kaarttitels (`titleMedium` op de lijstkaarten,
-  `titleLarge` op de sliderkaart en zaaknaam) zijn nog Roboto, terwijl de site
-  ook kaarttitels in serif zet. Ja → Theme Settings → Typography → rij *Title
-  Large* en *Title Medium* → Font Family-veld → picker → Noto Serif (per rij,
-  níét de Secondary Font Family-knop, zie `CLAUDE.md`). Raakt ook de
-  sectiekoppen op de aanmaakpagina's niet; wél de tabs (`titleMedium` op de
-  TabBar-labels) — check dat na afloop op een toestel.
+- **P2-35 · `HorecagelegenheidCurrent`: "Geen evenementen"-strook · Eigenaar:
+  Claude — eerst NAMETEN op een toestel vóór je gaat bouwen.** De
+  oorspronkelijke bevinding was "staat permanent onderin, ook als de zaak events
+  heeft". In de export van 2026-09-16 klopt dat maar half: de events-tabel
+  (`HorecagelegenheidEventTabelComponentCopy`) doet al keurig
+  `if (evenementen.isEmpty) return GeenEvenementenComponentWidget();` — dat is
+  correct empty-state-gedrag. Wat er wél permanent staat is de **knop "Alle
+  horecagelegenheden"** (`horecagelegenheid_current_widget.dart` r.~1610), los
+  van de tab waar je op staat. Kijk dus eerst op de emulator wat de gebruiker
+  echt ziet vóór je een Visibility-conditie bouwt; mogelijk is de hele taak
+  alleen nog "die knop in de Agenda-tab zetten".
+- **P2-36 · ❌ NEE — besluit Bob 2026-09-16: kaarttitels blijven Roboto.**
+  Niet opnieuw voorstellen. (Voorstel was Title Large/Medium op Noto Serif zetten
+  omdat de site kaarttitels in serif zet; Headline L/M/S staan al wel op serif.)
 - **Website (aanvulling op P2-13, Eigenaar: Bob, Drupal-thema):** (6) op
 - **Website (aanvulling op P2-13, Eigenaar: Bob, Drupal-thema):** (6) op
   telefoon 320 px header (socials + logo + Inloggen/Taal + zoekveld +
   telefoon 320 px header (socials + logo + Inloggen/Taal + zoekveld +
   menubalk) vóór het eerste evenement → socials weg, Inloggen/Taal en zoekveld
   menubalk) vóór het eerste evenement → socials weg, Inloggen/Taal en zoekveld
@@ -483,32 +495,40 @@ daarmee niet meer geblokkeerd en heeft nu een kant-en-klaar werkblad
    niet in de module (de module doet alleen `townid`/datum/`zoek`).
    niet in de module (de module doet alleen `townid`/datum/`zoek`).
    Structureel (taak 32B, 597 events = 96% van het lek): default-categorie in
    Structureel (taak 32B, 597 events = 96% van het lek): default-categorie in
    de importer — eigen moment.
    de importer — eigen moment.
-2. **Store-gereedheid (Play Console).** Privacybeleid-tekst staat sinds
-   2026-09-16 live op de website (Bob) — **nog open:** (a) de URL invullen bij
-   Play Console → App content → Privacy policy (dat veld zit in Play Console
-   zelf, niet in FlutterFlow); (b) een link naar diezelfde pagina **in de app**
-   toevoegen — nergens in de levende code staat momenteel een verwijzing naar
-   privacybeleid/voorwaarden (`grep -rn "privacy\|voorwaarden" lib/` geeft 0
-   treffers), en Google eist ook een vindbare link vanuit de app zelf, niet
-   alleen in de store-listing. Kleine builder-taak: `drawerComponent` → een
-   bestaand `ListTile` dupliceren (zie `CLAUDE.md`) → label "Privacybeleid" →
-   On Tap → Launch URL naar de nieuwe pagina (categorie *Share* in de
-   Actions-zoeklijst) — kan gecombineerd met de al geplande "Help"-menu-item-
-   taak hierboven (zelfde plek, zelfde patroon). Data-safety-formulier
-   (Crashlytics, AdMob, login) en account-verwijder-URL
-   (`uitgaanskrant.com/nl/account-verwijderen`, bestaat al) blijven ook open.
-   Beslis de upload-route: via FlutterFlow-deploy (dan hoeft er lokaal niets)
-   of lokaal (dan keystore + `android/key.properties`, want release staat op
-   `signingConfigs.debug`). iOS wel/niet in scope.
+2. **Store-gereedheid (Play Console) · Eigenaar: Bob.** De in-app-link is
+   geregeld (menu-item "Support pagina" → helppagina, die zelf naar het
+   privacybeleid linkt), dus Google's eis van een vindbare link vanuit de app is
+   gedekt. **Wat nog open staat, allemaal in Play Console zelf:**
+   - **(a) Privacy policy-URL invullen:** `https://uitgaanskrant.com/nl/support/privacybeleid`
+     (gemeten 2026-09-16: anoniem 200).
+   - **(b) Account-verwijder-URL.** ⚠️ `uitgaanskrant.com/nl/account-verwijderen`
+     geeft **anoniem een 403** en Google's reviewers openen die URL zonder in te
+     loggen — zo ingevuld blokkeert het de review. Vul in plaats daarvan
+     `https://uitgaanskrant.com/nl/support/mobieleapp` in (daar staat de sectie
+     "Je account verwijderen" publiek op), of maak de oude pagina publiek.
+   - **(c) Data-safety-formulier:** Crashlytics (crashlogs/diagnostiek), AdMob
+     (advertentie-ID), login (gebruikersnaam/e-mail); versleuteld transport +
+     verwijdering mogelijk.
+   - **(d) Upload-route beslissen:** via FlutterFlow-deploy (dan hoeft er lokaal
+     niets) of lokaal — dan keystore + `android/key.properties`, want de
+     release-build staat nog op `signingConfigs.debug`.
+   - **(e) iOS wel/niet in scope** (`project.pbxproj` beweegt mee in elke export,
+     verder is er niets voor ingericht).
 3. **P1-17 · Engelse vertalingen** — werkblad ligt klaar in
 3. **P1-17 · Engelse vertalingen** — werkblad ligt klaar in
    **`TAAK-P1-17-vertalingen.md`** (sleutel + NL + huidige EN + voorgestelde EN
    **`TAAK-P1-17-vertalingen.md`** (sleutel + NL + huidige EN + voorgestelde EN
-   per regel). **Deel A (21 regels) kan nu al**: Home-chips, drawer, login,
-   horeca-overzicht, `Selectprovinciegemeente`, gemeentehartje. **Deel B (59
-   regels)** pas ná P2-29, want de twee aanmaakpagina's zijn nog in bewerking.
-   Echte omvang in levende code: 26 leeg + 58 gelijk aan NL + **8 fout** (de
-   oude telling 134/99 bevatte dode kopieën). De chip-functie is al
-   taalonafhankelijk gemaakt — mits de chips exact
-   `All`/`Today`/`This weekend`/`This week` krijgen.
+   per regel). **Deel A is op 2026-09-16 vrijwel helemaal gedaan (Bob):**
+   Home-chips (All/Today/This weekend/This week), drawer, login, gemeentehartje,
+   `Selectprovinciegemeente`, `wachtwoordVergeten` en "Bezorgen" → Delivery,
+   allemaal export-geverifieerd. **Restant: 3 regels op het horeca-overzicht,
+   staat hierboven bij de restpunten als Claude-taak.** **Deel B (59 regels)**
+   wacht nog steeds op P2-29. De chip-functies zijn taalonafhankelijk gemaakt en
+   geplakt, dus het datumfilter werkt in beide talen.
+   ⚠️ **Les uit deze ronde:** dit vertaaldialoogje heeft last van het bekende
+   "ziet er opgeslagen uit, was het niet"-patroon — van de eerste 8 regels kwamen
+   er maar 2 door. Verifieer elke ronde met een verse export, niet met het
+   paneel. Verder zitten veel vertaalsleutels op **Tooltip-nodes** (`Terug`,
+   `Veld wissen`, `Wachtwoord tonen/verbergen`) in plaats van op zichtbare
+   labels; die vind je alleen door in de widget tree op `Tooltip` te zoeken.
 4. **Builder-opruimronde.** P2-7 (21 dode eenheden; begin met
 4. **Builder-opruimronde.** P2-7 (21 dode eenheden; begin met
    `drawer_component_copy` + `kanweghorecagelegenheid_current_copy`),
    `drawer_component_copy` + `kanweghorecagelegenheid_current_copy`),
    `HorecagelegenhedenOverzichtCopy3Copy`, `homeCopy` (het datumfilter is af en op een toestel getest, dus mag weg),
    `HorecagelegenhedenOverzichtCopy3Copy`, `homeCopy` (het datumfilter is af en op een toestel getest, dus mag weg),
@@ -522,8 +542,9 @@ daarmee niet meer geblokkeerd en heeft nu een kant-en-klaar werkblad
 6. **Livegang-schakelaars, als allerlaatste.** App Settings → AdMob → *Show
 6. **Livegang-schakelaars, als allerlaatste.** App Settings → AdMob → *Show
    Test Ads* UIT; Cloudflare: rate-limiting-rule op Active én de twee
    Test Ads* UIT; Cloudflare: rate-limiting-rule op Active én de twee
    rate-limiting-vinkjes uit de custom rule `flutterflow` (P1-30).
    rate-limiting-vinkjes uit de custom rule `flutterflow` (P1-30).
-7. **Besluit "Thuis bezorgen"** (één zin): verbergen bij livegang, of Claude
-   bouwt de pagina op `flutterflowmobiel_establishments` `services_2`.
+7. ~~Besluit "Thuis bezorgen"~~ **✅ VERVALLEN — de pagina is gebouwd** (sessie
+   47, commit `95c1dec`, op een toestel geverifieerd), en de AppBar staat sinds
+   2026-09-16 ook op de juiste kleur (P1-53).
 
 
 ### 🚦 Livegang-review 2026-09-15 (Claude, code-audit op de export van 22:56 — géén builder-werk)
 ### 🚦 Livegang-review 2026-09-15 (Claude, code-audit op de export van 22:56 — géén builder-werk)