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

Nieuwsbrieventab af: opmaak, EN-vertalingen en waarschuwing

- Knop 2 (uitschrijven) toont nu bij geabonneerd=true i.p.v. andersom;
  icoonkleur op Primary.
- Rijen kregen padding 16/10/16/16 en een tekst-hierarchie (Title Small /
  Body Small + secondaryText).
- 5 EN-vertalingen hersteld die bij de tab-verhuizing gewist waren; twee
  daarvan hadden nieuwe sleutels gekregen (oxse6aci/wcyy4pao).
- 128-C: rode waarschuwing bij 0 gevolgde gemeenten, met de bekende
  beperking dat favorieteGemeenteIds pas gevuld is na een bezoek aan het
  gemeente-scherm. Fixroutes uitgezocht en genoteerd.
- CLAUDE.md: Switch in dynamische lijst, dynamic action-output niet
  bindbaar, Theme-Text-Style-lijst, per-zijde padding, slepen naar een
  TabBar Page.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bob 13 órája
szülő
commit
75478c06ab
2 módosított fájl, 127 hozzáadás és 186 törlés
  1. 56 0
      CLAUDE.md
  2. 71 186
      TASKS.md

+ 56 - 0
CLAUDE.md

@@ -943,6 +943,16 @@ vraag voor Bob. Wat wél werkt: **"+" op de TabBar Page-rij → 2e tab-icoon
 Marketplace-resultaten) → kaart → daarna de parameters binden. **De tree loopt
 na zo'n insert vaak achter** (nieuwe node zonder chevron); ga af op de export.
 
+**⚠️ Slepen NAAR een `TabBar Page` toe landt nooit (Claude-specifiek); eruit slepen
+werkt wel.** Bevestigd 2026-09-21, vijf pogingen in alle varianten (rechtstreeks, twee
+hops via de buitenste `Column`, doelrij vooraf geselecteerd, met/zonder `Escape`,
+bronnode ingeklapt). De doelrij kleurt wel bruin, maar de node blijft staan, en **de
+undo-knop in de werkbalk doet hier niets**. In Bob's eigen browser lukte dezelfde
+sleep in één keer. Een tab-verhuizing gaat dus meteen naar Bob, niet na vijf pogingen.
+⚠️ **Bijvangst: zo'n verplaatsing hernummert de i18n-sleutels van de verplaatste
+widgets en wist hun EN-vertaling.** Noteer vóóraf welke teksten er staan, en check na
+afloop met een diff tussen twee exports van `internationalization.dart`.
+
 **Een TabBar ombouwen naar losse secties: sleep de Column uit elke `TabBar Page`
 rechtstreeks op de buitenste Column — dat werkt wél (2026-09-19, P2-46).** Eerder
 stond hier dat inhoud van een TabBar Page niet te verslepen is; dat gold voor
@@ -2401,6 +2411,52 @@ functions, App State-schrijfacties en custom actions komen **allemaal schoon mee
   Action** (netter dan verwijderen). Let op: een disabled actie zonder geldig doel
   telt daarna als fout *"Action Configuration — Action not defined"*.
 
+**⚠️ Een `Switch`/`Checkbox`/`TextField` in een dynamische lijst werkt NIET — widget
+state is per PAGINA, niet per lijst-item.** Bevestigd 2026-09-21 (nieuwsbrieven-tab).
+De export verraadt het meteen: `value: _model.switchValue ??= getJsonField(item,
+r'$.geabonneerd')` met één `bool? _switchValue;` voor de hele pagina — de `??=` wordt
+door de eerste rij gevuld en **alle rijen tonen daarna dezelfde stand**. In de builder
+ziet het er volkomen correct uit. Geldt voor elk widget met eigen waarde-state binnen
+*Generate Dynamic Children*. **Werkend patroon: twee conditionele widgets** met
+tegengestelde Visibility-condities op het loop-item (een bool custom function op het
+item, met/zonder *Apply Opposite Statement*) — geen widget state, dus per rij correct.
+Zelfde truc als het favorietenhartje. Alternatief is de rij een eigen component maken,
+maar dan moeten alle bindingen als component-parameters opnieuw.
+
+**⚠️ De output van een Custom Action (`Future<dynamic>`) is NIET te binden aan een
+getypt veld — noch App State, noch page state.** Bevestigd 2026-09-21 op
+`drupalRequest`: in de Set-Variable-dialoog verschijnt "Action Outputs" wél als bron,
+maar de optielijst rendert leeg (getest tegen een `List<String>` App-State-veld én een
+`List<Json>` page state, met hover-truc, blind klikken en een vergrote dialoog). Dat is
+geen render-bug maar een typefilter: `dynamic` matcht niets. **Twee werkende routes:**
+(1) gebruik een **API Call** in plaats van de custom action — dan bind je *Action
+Outputs → JSON Body → No Further Changes* aan een page state van type **JSON + Is
+List**; (2) hang er een **custom function** met een concreet, **non-nullable**
+returntype tussen. Een custom function met `List<String>?` verschijnt óók niet in die
+lijst — vandaar dat het loont die nullability uit te zetten.
+
+**Per-zijde Padding-velden: het LETTER-label (L/T/R/B) ís het invoerveld.** Klik exact
+op de letter, typ het getal, en commit met een klik op een sectiekop. De klik landt
+regelmatig niet; je ziet het aan de letter, die bij focus verandert in een cursor.
+Reken structureel op een tweede poging voor T en B (die liggen dicht op elkaar), en
+verifieer met een `zoom` — het veld toont bij succes het getal in plaats van de letter.
+
+**De Theme-Text-Style-lijst opent ONZICHTBAAR: de huidige stijl ligt exact op de
+dropdown-hoogte, de rest ±22 px daarboven/-onder.** Bevestigd 2026-09-21. Symptoom:
+je klikt de dropdown, er gebeurt zichtbaar niets, en de sectiekoppen eromheen
+verdwijnen (dat is de overlay). Hij is dus wél open. **Werkwijze:** klik één keer,
+wacht 10 s, `hover` op de verwachte rij (Body Small ligt één rij onder Body Medium),
+`zoom` om te lezen wat er staat, en klik dan pas. Blijven klikken op de dropdown
+opent en sluit hem alleen maar.
+
+**Een Custom Action toevoegen in de Action Flow Editor: literal-argumenten commit je
+met een klik op een LABEL, anders vallen ze terug op `Unset`.** Bevestigd 2026-09-21
+bij `drupalRequest` (6 argumenten). Typen alleen is niet genoeg — het paneel toont de
+waarde even en zet 'm daarna terug. Een leeg String-argument zet je met de bekende
+truc: typ één teken en verwijder het weer, dan springt de placeholder om naar
+**`[Empty String]`**. En de **Action Output Variable Name** wordt afgedekt door zijn
+eigen tooltip: hover eerst ergens neutraals vóór je de waarde controleert.
+
 **Een component zijn pagina laten herbouwen: "Update Page on Changes" op de
 INSTANTIE.** Staat in het rechterpaneel van de component-instantie onder
 **Component Properties** (naast *Use Hero Animation*), en genereert

+ 71 - 186
TASKS.md

@@ -85,7 +85,10 @@ verse export — zie de commit van die datum.)*
 ---
 
 
-## 📰 128 · Nieuwsbrieven aan/uit in de app · Eigenaar: Bob (A) + Claude (B-D)
+## 📰 128 · Nieuwsbrieven aan/uit in de app — **grotendeels AF** (2026-09-21)
+
+**Open rest:** 128-C's beperking (App State loopt achter na een verse install),
+128-E (verwijzing vanaf Favorieten), en de `status == 2`-regel uit 128-F.
 
 **Wens Bob 2026-09-21.** Op de website kan een ingelogde gebruiker op
 `/user/<uid>/simplenews` drie nieuwsbrieven aan- en uitvinken. Dat moet ook in
@@ -244,172 +247,62 @@ ontbreekt), niet 404.
 bestaande Cloudflare Cache Rules (die matchen op `/flutterdrup/views/…`,
 `plaatsen`), dus dat gaat vanzelf goed; het mag er alleen nooit bij.
 
-### 128-B · App: vierde tab "Nieuwsbrieven" op `mijnProfiel` · Eigenaar: Claude
-
-**Bob 2026-09-21: het wordt een losse TAB op de profielpagina**, geen blok
-onderaan. Dat is gunstig, want `mijnProfiel` heeft al een `TabBar` met drie
-tabs — *Mijn evenementen*, *Stadseditor*, *Account* (controller
-`tabBarmijnevenementenController`, `TabBar` op regel 188 van de verse export).
-Een vierde toevoegen gaat via het bekende recept: `TabBar`-node selecteren →
-**"Active Tab"**-dropdown → onderaan **"+ Add Tab"**. Dat voegt meteen een
-bijpassende lege `TabBar Page` toe.
-
-### ⚠️ Bob wil de tab VÓÓR Account — en dat kost een verhuizing
-
-Gekozen volgorde (Bob 2026-09-21): *Mijn evenementen · Stadseditor ·
-**Nieuwsbrieven** · Account*. Een nieuwe tab landt echter altijd **achteraan**,
-en tabs herordenen is een bewezen dood spoor (P2-35: een `Tab`-rij op de
-`TabBar`-rij droppen geeft *"Tab can only be added to TabBar"*, en op een andere
-Tab droppen verplaatst alleen het label, niet de bijbehorende page).
-
-**Werkbare route — de inhoud verhuist, niet de tab:**
-1. **"+ Add Tab"** → er komt een lege `TabBar Page` 4 bij.
-2. De `Column` met de Account-inhoud uit `TabBar Page` 3 naar `TabBar Page` 4
-   verplaatsen. Rechtstreeks tussen twee TabBar Pages slepen lukt niet, maar
-   **in twee hops via de buitenste `Column` wél** (bewezen bij taak 110,
-   2026-09-20): eerst op de buitenste Column droppen (landt op positie 0), die
-   rij deselecteren, dan de doel-TabBar-Page selecteren en opnieuw slepen.
-3. De nieuwsbrieven-inhoud in de nu lege `TabBar Page` 3 bouwen.
-4. Labels zetten: tab 3 → *Nieuwsbrieven*, tab 4 → *Account*.
-
-**De Account-tab is klein genoeg om desnoods opnieuw op te bouwen** als het
-slepen niet lukt: een `Text` met `FFAppState().userName`, *Wachtwoord wijzigen*
-(`pushNamed`), *Account verwijderen*, en de uitlogketen (6× App State wissen +
-`goNamed(Login)`). Die drie actieketens zijn over te zetten met **Copy Action
-Chain / Paste Action(s)** — dat werkt aantoonbaar tussen pagina's (taak 104 heeft
-precies deze uitlogketen zo verplaatst).
-
-⚠️ **Oefen dit eerst op een duplicaat van de pagina** (Bob's eigen werkwijze,
-2026-09-17): `Duplicate Page` op `mijnProfiel`, de verhuizing daar doorlopen,
-met een export controleren dat de structuur klopt, en het pas dán op de echte
-pagina doen. `mijnProfiel` bevat de uitlog- en verwijderacties; die wil je niet
-kwijtraken aan een sleep die verkeerd landt. Een sleep die niet landt is
-herkenbaar aan een gedimde bronrij, en de **undo-knop in de werkbalk** werkt
-(Ctrl+Z niet).
-
-In die tab:
-1. **API Call** `Nieuwsbrieven` (GET `/nl/flutterdrup/nieuwsbrieven.json`) met
-   de Cookie-header in het `[session_name]=[sessid]`-formaat — **rechte haken,
-   geen `{{ }}`** — en de aanroeper moet `sessionName`/`sessid` uit App State
-   meegeven, anders gaat er `Cookie: =` mee.
-2. **Backend Query + Generate Dynamic Children.** Bind via **Predefined Path**
-   (`$[:]`, Is List) en niet via JSON Path `$`, anders crasht de tab bij een
-   mislukte call.
-3. Per rij: naam + omschrijving eronder (`bodySmall` / `secondaryText`), rechts
-   een **Switch** met Initial Value op `$.geabonneerd`.
-4. **On Toggle → `drupalRequest`** naar `nieuwsbrieven/subscribe.json` resp.
-   `unsubscribe.json`, body `{"tid": <$.tid>}`.
-
-⚠️ **Geef de CSRF-token mee** (zie het blok bij 128-A): exact dezelfde vijf
-argumenten als het favorieten-hartje, inclusief `FFAppState().userToken`.
-Vergeet je die, dan komt er `["CSRF validation failed"]` terug, geeft
-`drupalRequest` stil `[]` en lijkt de switch te werken terwijl er niets gebeurt.
-
-⚠️ Doe "Test API Call" met een gevulde respons vóór je `$.status`/`$.geabonneerd`
-bindt, anders krijgt het pad type "Anything" en plakt de builder er
-`.toString()` achter.
-
-Bij `status == 2` een regel "Check je mail om je inschrijving te bevestigen"
-tonen. Die stand komt met de huidige instelling nooit voor (ingelogde
-gebruikers worden direct verwerkt), maar dan is de app al klaar als dat ooit
-verandert.
-
-### 128-B · status 2026-09-21
-
-**✅ Hersteld door Bob.** De Account-inhoud zit weer in `TabBarPageAccount`; er
-zijn nu vier tabs (*Mijn evenementen* · *Stadseditor* · *Account* · *Tab 4*) en
-Tab 4 wordt de nieuwsbrieven-tab. Bob 2026-09-21: **achter Account is ook goed**,
-dus de verhuizing is van de baan.
-
-⚠️ **Nieuw hard gegeven uit deze poging:** inhoud uit een `TabBar Page` naar
-buiten slepen wérkt, maar **naar een `TabBar Page` toe landt nooit** — vijf
-pogingen in alle varianten (rechtstreeks, twee hops via de buitenste `Column`,
-doelrij vooraf geselecteerd, met/zonder `Escape`, bronnode ingeklapt). De doelrij
-kleurt wel bruin, maar de node blijft staan. **En de undo-knop in de werkbalk
-doet hier niets**, anders dan bij taak 77. In Bob's eigen browser lukte dezelfde
-sleep wel in één keer. Conclusie: dit is Claude-specifiek — een tab-verhuizing
-gaat voortaan meteen naar Bob, niet na vijf pogingen.
-
-⚠️ **Bijvangst: 5 EN-vertalingen zijn bij de verplaatsing gewist** (de sleutels
-zijn hernummerd). Te herstellen via het globe-icoontje, meenemen in 128-F:
-
-| sleutel | NL | EN moet worden |
-|---|---|---|
-| `0sxtqp61` | Wachtwoord wijzigen | Change password |
-| `4sjzjw0j` | Account verwijderen | Delete account |
-| `owegb3ht` | Uitloggen | Log out |
-| `u6r6mmfb` | Kopie | Copy |
-| `gvv1wg45` | Kopie | Copy |
-
-De twee "Kopie"-sleutels horen bij taak 127 van de andere sessie; die zijn
-meegehernummerd omdat ze op dezelfde pagina staan.
-
-### 128-B · voortgang 2026-09-21 (Claude, in de builder)
-
-**Klaar en export-geverifieerd:**
-- API Call **`Nieuwsbrieven`** — top-level GET, `Cookie: '${sessionName}=${sessid}'`
-  met echte interpolatie, en `static List? items(...)` met `true` + `as List?`.
-- Vierde tab **"Nieuwsbrieven"** op `mijnProfiel` (achter Account).
-- In die tab: `Column > ListView` met **Backend Query** op die call
-  (`sessionName`/`sessid` aan App State gebonden) en **Generate Dynamic Children**
-  via **Predefined Path `items`** → de export toont
-  `NieuwsbrievenCall.items(...)?.toList() ?? []`, dus null-veilig.
-- Item-template `Row > Column > [Text $.naam, Text $.omschrijving]`.
-
-### ⚠️ Nieuw gegeven: een `Switch` werkt NIET in een dynamische lijst
-
-Ik had per rij een `Switch` gezet met Initial Value op `$.geabonneerd`. De export
-laat zien waarom dat niet kan:
-
-```dart
-Switch.adaptive(
-  value: _model.switchValue ??= getJsonField(nieuwsbriefItemItem, r'$.geabonneerd'),
-```
-
-en in het model staat **één** `bool? _switchValue;` voor de hele pagina. Widget
-state is in FlutterFlow per *pagina*, niet per lijst-item, dus de `??=` wordt door
-de eerste rij gevuld en **alle rijen tonen daarna dezelfde stand**. Dat is een
-stille bug: in de builder ziet het er correct uit.
-
-Geldt voor elk widget met eigen waarde-state (Switch, Checkbox, TextField) binnen
-*Generate Dynamic Children*. Twee uitwegen:
-1. **Conditionele widgets** — twee varianten met tegengestelde Visibility-condities
-   op het loop-item. Geen widget state, dus per rij correct. Dit is het patroon van
-   het favorietenhartje en van taak 77.
-2. **De rij een eigen component maken** — elke instantie krijgt dan een eigen model,
-   dus een echte Switch werkt wel. Netter, maar de bindingen naar het loop-item
-   moeten dan als component-parameters opnieuw gelegd worden.
-
-Gekozen: **route 1**. De Switch is verwijderd en er staat nu een `IconButton` in
-de Row; die krijgt een conditie + actie, en wordt daarna gedupliceerd voor de
-omgekeerde stand.
-
-**Nog te doen in deze tab:** icoon + conditie + `drupalRequest`-actie op beide
-knoppen (subscribe/unsubscribe, **met `FFAppState().userToken` als 5e argument**,
-anders `CSRF validation failed` en stil `[]`), Column op Expanded.
-
-### 128-C · Waarschuwing bij 0 gevolgde gemeenten · Eigenaar: Claude
-
-Bob's keuze 2026-09-21. **Gemeten: 17 van de 18 actieve abonnees op de
-wekelijkse agenda hebben nul favoriete gemeenten**, en de laatste editie
-(nid 222817, 2026-09-17) ging naar `sent_subscriber_count: 3`. Wie die switch
-aanzet zonder gemeenten te volgen, krijgt dus niets — zonder enige uitleg.
-
-**Bestemming van de knop is uitgezocht:** pagina **`selectprovinciegemeente`**
-(`/selectprovinciegemeente`) — die heeft **geen parameters**, dus de actie is
-één kale `Navigate To` zonder bindingen.
-
-Onder de rij van tid 36676 een waarschuwing tonen wanneer
-`FFAppState().favorieteGemeenteIds` leeg is, met een knop naar het
-gemeente-keuzescherm. Visibility via de bekende route: App State-lijst ->
-Available Options **Is Set and Not Empty** + **Apply Opposite Statement**.
-De conditie op de tid zelf hoort een **bool custom function** te zijn (geen
-JSON-Path-binding) — anders komt er `if (getJsonField(...))` uit, een `dynamic`
-als `bool` zonder null-check.
-
-*(Niet nagelezen: de scs-contentselectie zelf. De tabel heet niet `scs_node`;
-ik heb dus niet hard bewezen dát die 17 een lege mail krijgen — het beeld past
-wel. Wie dit ooit exact wil weten, begint bij node 189237, de scheduler-parent.)*
+### 128-B/D · App: vierde tab "Nieuwsbrieven" — **AF** (2026-09-21)
+
+Staat live op `mijnProfiel`, achter *Account*, getest op een toestel met
+bobcity ingelogd: lijst laadt, aan- en uitzetten geeft `200` en de rij springt
+meteen om. Opbouw zoals gebouwd:
+
+- API Call **`Nieuwsbrieven`** (GET, `Cookie: '${sessionName}=${sessid}'`),
+  Predefined Path `items` = `$[:]` (Is List) → null-veilige
+  `NieuwsbrievenCall.items(...)?.toList() ?? []`.
+- `Column > ListView` met Backend Query + Generate Dynamic Children.
+- Per rij een `Row` (padding 16/10/16/10) met `Column > [Text $.naam
+  (Title Small), Text $.omschrijving (Body Small, secondaryText)]` en rechts
+  **twee conditionele `IconButton`s**: `toggle_off` (grijs) → subscribe,
+  `toggle_on` (Primary) → unsubscribe. Conditie = custom function
+  `nieuwsbriefAan(item)`, met/zonder *Apply Opposite Statement*.
+- Actie: `drupalRequest('POST', '…/nieuwsbrieven/(un)subscribe.json', …,
+  FFAppState().userToken, '{"tid": <$.tid>}')` + Rebuild Page.
+
+⚠️ **De omschrijving van tid 36667 is in Drupal gelijk aan de naam**
+("Uitgaanskrant voor bezoekers van de horeca" staat er twee keer onder elkaar).
+Geen app-bug — op te lossen in de term-beschrijving. *Eigenaar: Bob.*
+
+### 128-C · Waarschuwing bij 0 gevolgde gemeenten · **grotendeels af, één beperking**
+
+Staat onder de lijst: een rode regel *"Je volgt nog geen gemeenten, dus de
+wekelijkse agenda blijft leeg. Tik hier om gemeenten te kiezen."* achter
+`if (!(FFAppState().favorieteGemeenteIds.isNotEmpty))`, met een `Navigate To`
+naar `selectprovinciegemeente`. Daaronder staat altijd de oranje regel *"De
+wekelijkse uitgaansagenda gebruikt de gemeenten die je volgt…"* met dezelfde
+navigatie. Beide vertaald (NL+EN).
+
+⚠️ **Bekende beperking: `favorieteGemeenteIds` wordt alléén gevuld door
+`SelectStateDropDownComponent`** (die haalt in zijn On Page Load
+`favorieten_gemeenten.json` op en schrijft de nids naar App State). Het veld is
+persisted, dus in normaal gebruik klopt het — maar wie zijn gemeenten op de
+**website** of op een **ander toestel** heeft gekozen en het gemeente-scherm in
+deze app nog nooit opende, ziet de waarschuwing ten onrechte. Live gezien op
+emulator-5554 met bobcity: warning stond er, en verdween zodra het
+gemeente-scherm één keer geopend was.
+
+**Wat de fix zou zijn (uitgezocht, niet gebouwd):** op `mijnProfiel` in de
+On Page Load hetzelfde lijstje ophalen. Dat kan **niet** met `drupalRequest`:
+die custom action geeft `dynamic` terug en FlutterFlow biedt zo'n output niet
+aan als bron voor een getypt veld — getest op zowel een `List<String>`
+App-State-veld als een `List<Json>` page state, in beide gevallen rendert de
+bronnenlijst leeg. Twee werkende routes:
+1. een nieuwe **API Call** `FavorietenGemeenten` (GET + Cookie-header, net als
+   `Nieuwsbrieven`) → Update Page State `favGemeenten` met *Action Outputs →
+   JSON Body → No Further Changes*; conditie dan op die page state; **of**
+2. `favorieteGemeenteNids` een **non-nullable** return geven (`List<String>` in
+   plaats van `List<String>?`), waarna hij wél als bron verschijnt voor
+   `Update App State → favorieteGemeenteIds → Set Value`. Dat haalt meteen de
+   `!` weg uit `SelectStateDropDownComponent`.
+
+Het page-state-veld **`favGemeenten` (`List<Json>`) staat al klaar** op
+`mijnProfiel` — aangemaakt voor route 1, nu nog ongebruikt.
 
 ### 128-E · Verwijzing op Favorieten → de nieuwsbrief-tab · Eigenaar: Claude
 
@@ -439,31 +332,23 @@ de dropdown-valkuil elders in dit bestand.
 - EN: *"Want the weekly agenda for your favourite municipalities by email? Turn
   the newsletter on here!"*
 
-### 128-F · Teksten en vertalingen · Eigenaar: Claude
+### 128-F · Teksten en vertalingen — **AF** (2026-09-21)
 
-Voorbereid, klaar om in te voeren via het globe-icoontje:
+Alles wat in de tab staat is NL+EN ingevuld via het globe-icoontje, en de
+5 EN-vertalingen die bij de tab-verhuizing gewist waren zijn hersteld:
+`Wachtwoord wijzigen`/`Account verwijderen`/`Uitloggen` en de twee
+`Kopie`-knoppen (die kregen bij de hernummering nieuwe sleutels,
+`oxse6aci` en `wcyy4pao` — de sleutels die hier eerder genoteerd stonden
+zitten in de dode kopie `mijn_profiel_copy4`).
 
-| Plek | NL | EN |
-|---|---|---|
-| Tabnaam | Nieuwsbrieven | Newsletters |
-| Bij `status == 2` | Check je mail om je inschrijving te bevestigen | Check your email to confirm your subscription |
-| Waarschuwing 0 gemeenten (128-C) | Je volgt nog geen gemeenten. Deze nieuwsbrief blijft leeg tot je er minstens één kiest. | You are not following any municipalities yet. This newsletter stays empty until you choose at least one. |
-| Knop bij die waarschuwing | Gemeenten kiezen | Choose municipalities |
-| Verwijzing op Favorieten (128-E) | zie hierboven | zie hierboven |
+Nog **niet** gebouwd: de regel bij `status == 2` ("Check je mail om je
+inschrijving te bevestigen"). Die stand komt met `custom_nieuwsbrieven_confirm
+= FALSE` nooit voor; de resource geeft de status wel al terug, dus het is later
+één conditionele `Text` erbij.
 
 ⚠️ De **omschrijvingen van de nieuwsbrieven komen uit Drupal** en krijgen dus
 géén globe en geen EN-vertaling. Dat is een bewuste beperking.
 
-### Nuttig om te weten bij het bouwen
-
-- **De drie bestaande tabs van `mijnProfiel` hebben LEGE On Tap-acties**
-  (`[() async {}, () async {}, () async {}][i]()`). Er is dus geen per-tab
-  fetch-patroon om na te volgen: de `FutureBuilder`s staan gewoon in de
-  `TabBarView` en vuren zodra de tab gebouwd wordt. De nieuwsbrieven-tab heeft
-  dus géén On Tap-actie nodig.
-- De tikfout in de omschrijving van tid 36676 is **gefixt** (Bob 2026-09-21):
-  er staat nu "donderdagavond verstuurd".
-
 ### Bevinding terzijde — geen taak
 
 **450 onbevestigde inschrijvingen** (360 + 47 + 43). Die mensen hebben zich ooit