소스 검색

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 주 전
부모
커밋
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
   scherm zit** (ook niet via `/loop` of een geplande taak) — Bash/
   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
 

+ 86 - 1
TASKS.md

@@ -38,11 +38,17 @@ Wat er nog ligt, allemaal in dezelfde views:
 
 ### 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
   "Toevoegen favoriet") de reden erbij die de site wél geeft: dat je die
   gemeente donderdags in de agendamail krijgt. Nu is "gemeente volgen"
   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`
   (27) en `uitgaansevenementAanmaken` (19): de twee aanmaak-formulieren
   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.
   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)
 
 Bob's expliciete categorie (2026-08-14): kleine restpunten uit P0-8 die