Просмотр исходного кода

P0-13 gevonden: favorieten-hartjes missen een login-guard

Alle drie de plekken die favorieten/flag.json aanroepen (gemeente-hartje,
horeca-overzichtskaart, horeca-detailpagina) laten een uitgelogde
gebruiker een hartje aanzetten; de POST gaat anoniem de deur uit en doet
niets, terwijl de lokale, persisted lijst wel wordt bijgewerkt. Nergens
in de levende code wordt de returnwaarde van een flag/unflag gecheckt.

Corrigeert ook de oude P0-12-aanname over een lege entity_id:
gemeenteSelectId heeft een default van '28694' en is dus nooit leeg.

Analyse en builder-stappen staan uitgeschreven; het klikwerk zelf kon
niet omdat de Chrome-extensie halverwege wegviel (diagnose daarvan in
CLAUDE.md).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bob 1 неделя назад
Родитель
Сommit
f9f1809991
2 измененных файлов с 97 добавлено и 1 удалено
  1. 11 0
      CLAUDE.md
  2. 86 1
      TASKS.md

+ 11 - 0
CLAUDE.md

@@ -274,6 +274,17 @@ code-export.
   is geen geschikte taak voor een sessie terwijl Bob niet achter zijn
   is geen geschikte taak voor een sessie terwijl Bob niet achter zijn
   scherm zit** (ook niet via `/loop` of een geplande taak) — Bash/
   scherm zit** (ook niet via `/loop` of een geplande taak) — Bash/
   code-audit/emulator-werk (geen browserafhankelijkheid) juist wel.
   code-audit/emulator-werk (geen browserafhankelijkheid) juist wel.
+- **Valt de extensie middenin een sessie weg ("Claude in Chrome is not
+  connected"), check dan of Chrome überhaupt nog draait vóór je blijft
+  retryen.** Bevestigd 2026-09-08: na een uur probleemloos builder-werk
+  gaf elke aanroep opeens die melding. `ps aux | grep -iE
+  "google-chrome|chromium"` gaf alleen nog crashpad-handlers, terwijl
+  `pgrep -a chrome` een berg **Brave**-processen toonde — Bob's Chrome
+  was gewoon afgesloten. Zes retries hielpen dus niets. Een enkele
+  disconnect-melding midden in een `browser_batch` is wél vaak transient
+  (kwam diezelfde sessie voor en herstelde vanzelf); het verschil zie je
+  aan die `ps`-check. Draait er geen Chrome: stop met retryen, schrijf de
+  resterende builder-stappen uit in `TASKS.md` en pak browserloos werk op.
 
 
 ## Lokale git-repo + pull/push-workflow
 ## Lokale git-repo + pull/push-workflow
 
 

+ 86 - 1
TASKS.md

@@ -38,11 +38,17 @@ Wat er nog ligt, allemaal in dezelfde views:
 
 
 ### 2. Losse builder-taken, in willekeurige volgorde
 ### 2. Losse builder-taken, in willekeurige volgorde
 
 
-- **P0-12 restpunt** — bij het gemeentehartje in
+- **P0-12 restpunt (wording, Bob)** — bij het gemeentehartje in
   `select_state_drop_down_component_widget.dart` (regel ~340, label
   `select_state_drop_down_component_widget.dart` (regel ~340, label
   "Toevoegen favoriet") de reden erbij die de site wél geeft: dat je die
   "Toevoegen favoriet") de reden erbij die de site wél geeft: dat je die
   gemeente donderdags in de agendamail krijgt. Nu is "gemeente volgen"
   gemeente donderdags in de agendamail krijgt. Nu is "gemeente volgen"
   een knop zonder zichtbaar nut. Engelse vertaling niet vergeten.
   een knop zonder zichtbaar nut. Engelse vertaling niet vergeten.
+- **P0-13 (nieuw, 2026-09-08) · Eigenaar: Bob (of een volgende sessie
+  met werkende browser)** — **geen van de drie favorieten-hartjes heeft
+  een login-guard**, waardoor een uitgelogde gebruiker een hartje kan
+  aanzetten dat de server nooit ontvangt. Analyse is af en de
+  builder-stappen staan uitgeschreven; alleen het klikwerk rest. Zie de
+  taak zelf verderop.
 - **P1-17** — nog 46 velden, en alleen nog in `stadsactiviteitAanmaken`
 - **P1-17** — nog 46 velden, en alleen nog in `stadsactiviteitAanmaken`
   (27) en `uitgaansevenementAanmaken` (19): de twee aanmaak-formulieren
   (27) en `uitgaansevenementAanmaken` (19): de twee aanmaak-formulieren
   die alleen een stadseditor ziet. De hele publieksgerichte UI
   die alleen een stadseditor ziet. De hele publieksgerichte UI
@@ -1083,6 +1089,85 @@ taakomschrijving stonden:**
   `flag.json`-POST met een lege `entity_id` en lege sessiewaarden.
   `flag.json`-POST met een lege `entity_id` en lege sessiewaarden.
   Nog niet live nagespeeld, dus niet bevestigd wat de server dan doet.
   Nog niet live nagespeeld, dus niet bevestigd wat de server dan doet.
 
 
+**P0-13 · Eigenaar: Bob (analyse af door Claude, sessie 71, 2026-09-08;
+de builder-stap zelf kon niet — de Chrome-extensie viel halverwege weg).
+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**. Dat is één
+   enkele conditie op een String-App-State — precies het geval waarvan
+   `CLAUDE.md` zegt dat het betrouwbaar werkt.
+2. De twee horeca-hartjes zijn **een ontwerpbesluit voor Bob**, geen
+   mechanische herhaling: die zitten midden in de hoofd-UI (elke
+   overzichtskaart), dus "verbergen als uitgelogd" verandert het beeld
+   van de belangrijkste lijst in de app. Alternatief is het hartje laten
+   staan en bij een tik naar Login navigeren — betere UX, maar meer werk
+   (actieketen met een conditional). **Niet aangeraakt tot Bob kiest.**
+
+**Exacte builder-stappen voor punt 1** (±1 minuut, geen fragiele dialoog
+— één enkele conditie op een String-App-State, geen JSON Path, geen
+Combine Conditions):
+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)
 ## Minor — non-blockers (launch mag hier niet op wachten)
 
 
 Bob's expliciete categorie (2026-08-14): kleine restpunten uit P0-8 die
 Bob's expliciete categorie (2026-08-14): kleine restpunten uit P0-8 die