# Uitgaanskrant — werkinstructies voor Claude ## Werkdirectory Werk uitsluitend binnen deze projectdirectory (`/home/bob/Projects/ff-app/uitgaanskrant-1qhvtd`). Niet daarbuiten zoeken of scannen — scope alle bestandsoperaties tot dit project. ## Werkwijze binnen een sessie - **Meld bij elke nieuwe stap kort vooraf wat je gaat doen**, vóór je begint (staande voorkeur Bob, 2026-08-04). - Bob bespreekt elke taak in een **nieuwe, aparte chat** — geen eerdere conversatie om op terug te vallen. `CLAUDE.md` + `TASKS.md` zijn samen het volledige geheugen tussen sessies. - **Groepeer opgepakte taken per sessie op FlutterFlow-paginagebied** waar mogelijk (bv. twee taken op dezelfde pagina/component samen oppakken) — minder heen-en-weer-navigeren in de builder, minder tokens. - **`TASKS.md`-status kan achterlopen op de echte code** (Bob werkt gelijktijdig, en documentatie-updates lopen niet altijd synchroon met de export). Check een taak die er al even staat met `git log --oneline -S""` of een gerichte `grep` vóórdat je 'm oppakt — niet blind vertrouwen dat "open" betekent "nog niet gefixt". (Precedent: 2026-08-04 bleken 2 "open" P0-taken al op 2026-08-02 gefixt te zijn, git-bevestigd.) - **Elke taak in `TASKS.md` heeft een stabiel ID (bv. `P0-1`) en een `Eigenaar:`-regel** — `Bob` (sneller/simpeler voor hem zelf, meestal builder-UI met een bekend fragiele dialoog, zie hieronder), `Claude` (onbeklaimd, vrij op te pakken), of **`... — bezig`** (iemand is er *nu* actief mee bezig). Vuistregel voor wie een nieuwe taak zou moeten doen: een kort, mechanisch herhaald patroon zonder geneste dialogen → Claude; ConditionalBuilder/JSON-Path-condities, List-typed function-argumenten, of iets dat eerder al vastliep → Bob. - **⚠️ Concurrency: Bob start elke taak in een nieuwe chat, dus er kunnen meerdere sessies tegelijk actief zijn.** Check vóór je een taak oppakt of de Eigenaar-regel al "— bezig" zegt door iemand anders — zo ja, niet zelfstandig ook gaan bouwen aan hetzelfde bestand/component, vraag Bob eerst wie 'm afmaakt. Zet zelf "— bezig" zodra je serieus begint. (Precedent 2026-08-04: twee sessies pakten onafhankelijk dezelfde P0-taak op — Bob moest scheidsrechteren tussen twee stukken werk aan hetzelfde bestand.) ## Sessiegeheugen — afsluitroutine Aan het eind van elke sessie/taak: 1. **`TASKS.md`**: een afgeronde taak wordt volledig **verwijderd** (niet gearchiveerd — onnodige context/kosten voor latere sessies). Elke resterende open taak moet zelfstandig te begrijpen zijn (concreet, met bestandspad) zonder de ontstaanschat gelezen te hebben. 2. **`CLAUDE.md`**: alleen aanvullen met blijvend herbruikbare inzichten (conventie, architectuurkeuze, bekend valkuil-patroon) die een latere sessie anders opnieuw zou moeten uitzoeken. Geen sessieverslag/changelog. Ruim verouderde info op i.p.v. eronder te plakken — dit bestand moet klein en scanbaar blijven. 3. Commit + push de gewijzigde `.md`-bestanden direct (geen FlutterFlow-export nodig voor pure documentatiewijzigingen). **Geen apart memory-systeem meer nodig voor projectfeiten** — die staan allemaal hier en in `TASKS.md`, wat al elke sessie automatisch geladen wordt. (Auto-memory-bestanden zijn per 2026-08-04 opgeschoond omdat ze dit bestand 1-op-1 dupliceerden.) ## FlutterFlow-workflow — belangrijk Dit project wordt gebouwd via FlutterFlow (app.flutterflow.io). De FlutterFlow-cloudomgeving is de bron van waarheid, niet deze lokale code-export. - Bob kan **geen** code rechtstreeks bewerken in dit repo. Alle wijzigingen aan pagina's, componenten en modellen moeten via de FlutterFlow-website. Enige uitzondering: custom functions/widgets (`lib/custom_code/`) — ook die voert Bob in via de Custom Code-editor op de website, niet hier lokaal. - Gevolg: directe Edit/Write-wijzigingen aan gegenereerde bestanden worden bij de volgende export overschreven — **niet duurzaam**. Gebruik deze repo om te lezen/ontwerpen/verifiëren (`flutter analyze`), maar voer het eindresultaat uit via de builder (zelf via browser-automation, of als instructie aan Bob). Ga nooit uit van behoud van een lokale bestandswijziging. - Werk je rechtstreeks in de builder (Claude in Chrome): commit na elke **afgeronde taak** binnen FlutterFlow's eigen versiebeheer ("main"/"Synced" bovenin), met duidelijke omschrijving. Geen lokale `git commit`. - **Gebruik `mcp__claude-in-chrome__*`** (Bob's gedeelde Chrome), niet de in-app Browser pane. - **Resize de browser niet zelf.** Bob's eigen gedeelde vensters — meld en vraag i.p.v. zelf te resizen. - Bob werkt vaak **gelijktijdig zelf** in dezelfde builder-sessie — check eerst of hij iets claimde ("dat regel ik zelf") voordat je wijzigingen overschrijft. - Check bij een mislukte export/pull eerst het **Issues-paneel** (badge rechtsboven) voordat je een bug bij jezelf zoekt — een rode teller blokkeert *elke* export, ook niet-gerelateerde, en is vaak Bob's eigen work-in-progress. ## Lokale git-repo + pull/push-workflow Projectdirectory = eigen schone git-repo, `origin` `ssh://gogs.digitalforce.tv:2222/Uitgaanskrant.com/flutterflow.git`, branch `master`. Los van de grotere/rommelige repo hoger in de mappenstructuur (AndroidStudioProjects, Flutter-SDK e.d.) — commits en pushes horen hier. Vaste workflow na elke afgeronde taak (staand akkoord, geen aparte bevestiging nodig): niet los `flutterflow export-code`, maar: ``` export PATH="/home/bob/fvm/bin:$HOME/.pub-cache/bin:$PATH" && /home/bob/Projects/ff-run-fvm.sh emulator-5554 uitgaanskrant-1qhvtd -s ``` - **De `export PATH=...` prefix is verplicht** — Bash draait niet-interactief, `~/.bashrc` wordt niet geladen. Zonder prefix faalt het script stil ("fvm: command not found" — geen harde error). - Interactief script (device-run + hot-restart-loop) → Bash met `run_in_background: true`. Volg tot minimaal "All done!" én idealiter een succesvolle app-launch (`Launching lib/main.dart...`, geen nieuwe `EXCEPTION CAUGHT BY RENDERING LIBRARY`). Daarna automatisch: 1. `git status --short`, dan `git add` — **niet blind `-A`**. Alleen echte FlutterFlow/app-wijzigingen (`lib/`, `android/`, `ios/`, `pubspec*`, `.gitignore`). `.claude/` blijft uitgesloten (sessiestate, geen app-code). Bob's eigen concurrente wijzigingen horen gewoon mee in dezelfde commit. 2. `git commit` met duidelijke boodschap. 3. `git push` (`-u origin master` als tracking nog niet staat). **Valkuil:** `flutterflow export-code` overschrijft `.gitignore` bij elke export terug naar FlutterFlow's standaardversie. Voeg na elke export, vóór staging, deze regel weer toe als hij ontbreekt: ``` # Claude Code session state (not app code) .claude/ ``` (`CLAUDE.md` zelf overleeft een export altijd — check voor de zekerheid toch even.) ## Eerst research, dan bouwen Vóór een niet-triviale taak (bugfix, nieuw patroon, integratie): kort online zoeken naar bestaande oplossingen i.p.v. zelf trial-and-error in de builder. Geldt niet voor mechanische herhaling van een patroon dat al bevestigd werkt. ## FlutterFlow-builder: bekende problemen & patronen **Geneste "Set Variable"-dialoog lijkt vast te zitten.** Bij een conditie (`ConditionalBuilder`, Visibility → Conditional) op een niet-triviaal type (JSON Path, API-response-veld, `List` function-argument) opent een tweede dialoog bovenop de eerste; Confirm/Cancel reageren soms niet zichtbaar. Twee oorzaken, in volgorde van proberen: 1. **Viewport-clipping** (meest voorkomend) — knoppen renderen buiten het zichtbare canvas. Sleep de dialoog omhoog via het handvat bovenin naar een hogere y-positie; de knoppen worden dan zichtbaar en werken gewoon. 2. **Echt bevroren pagina** (alle clicks doen niets) — de widget-wrap staat al server-side, de conditie-edit niet. Herlaad de pagina (`navigate` naar dezelfde URL); wrap blijft staan, conditie moet opnieuw. - **Kortere weg om dit te vermijden:** operator **"Is Set"** i.p.v. "Not Equal To" + lege string — geen Second Value nodig, dus geen tweede dialoog. - Loopt dit na 1-2 pogingen (incl. reload) nog vast: kost dan meer tijd dan Bob het zelf kan doen — meld concreet (component, exacte stappen) en vraag het aan hem. **Component Name kan per ongeluk overschreven worden.** Vlak na paginanavigatie kan een klik bedoeld voor "Search properties..." op het Component Name-veld landen (focus/z-order race), en typen hernoemt dan stilletjes het component. Zelfde risico bij een widget-tree zoekactie die per ongeluk double-click-to-rename triggert i.p.v. navigeren — druk direct **Escape** om te herstellen. Mitigatie: na elke click-before-type eerst een screenshot om focus te bevestigen, zeker vlak na navigatie. Herstel: rechtsklik component in zoekresultaten → "Rename Component". **Rechterpaneel kan te breed zijn voor de viewport.** Sommige controls (Expansion segmented control, Visibility → Conditional expression-builder, maar ook simpele checkboxen zoals "Show Empty List Widget" op een Carousel/ListView) renderen soms deels buiten beeld — geen `resize_window`-probleem (niet zelf resizen, zie boven). **Bevestigd 2026-08-04: dit blijft optreden zelfs nadat Bob zijn eigen venster al vergroot had** — de FlutterFlow-app zelf lijkt de extra breedte niet te gebruiken (real window 1970px, maar bruikbare schermafbeelding/klikbare ruimte bleef begrensd tot ~1176px; de JS-laag rapporteert wel de volle vensterbreedte, dus dit zit in hoe Flutter Web rendert/schaalt, niet in het venster zelf). Geen DOM/accessibility tree beschikbaar (canvas-rendering) — `find` en `read_page` werken hier niet, alleen coördinaat-gebaseerd klikken. Geprobeerd en zonder succes: direct klikken op meerdere x-posities, klikken + Space-toets, horizontaal scrollen. Na 1-2 bevestigde pogingen stoppen en aan Bob vragen — geef het exacte pad (component, tree-node, veldnaam) zodat hij het in seconden kan doen. **Patroon: lege/ontbrekende afbeeldings-URL laat de app crashen.** `CachedNetworkImage` gooit een **synchrone** `ArgumentError` bij het *bouwen* van de widget als `imageUrl` een lege string is — dit gebeurt vóórdat er ooit een netwerkverzoek is, dus `errorWidget`/"Show Error Image on Failure" vangt dit **niet** (dat vangt alleen échte laadfouten zoals 404's). Werkende fix: 1. Rechtsklik het Image-widget → **Wrap Widget (Ctrl+B)** → **ConditionalBuilder**. 2. IF-conditie: **Conditions → Single Condition** → First Value = de exacte expressie waar de Image's Path-property al aan gebonden was → operator **"Is Set"**. 3. THEN-tak: de bestaande Image (blijft staan na de wrap). 4. ELSE-tak: rechtsklik → Insert Widget → Icon → "image not supported" → eerste Material-resultaat. - **Simpeler alternatief indien van toepassing:** de "Default Variable Value"-toggle op een "Set from Variable"-binding substitueert al bij zowel `null` als lege string (in-builder tooltip: "if the resulting value is null or empty") — géén ConditionalBuilder nodig. Werkt hier niet voor Image-widgets met `Image Type: Network` zolang er geen gehoste fallback-afbeeldings-URL bestaat in dit project. Komt die er ooit (bv. default-logo op FlutterFlow-CDN/Drupal-server): gebruik dan Default Variable Value + "Show Error Image on Failure" — sneller te bouwen dan ConditionalBuilder. **Tooltip toevoegen aan een icon-only widget:** rechtsklik → Wrap Widget (Ctrl+B) → 4e rij van de grid (kan geclipt lijken) → 1e icoon (spraakwolkje) = "Tooltip". Genereert een `AlignedTooltip`, geen losse styling nodig. Werkt niet op iconen embedded als `suffixIcon` van een `TextFormField` (bv. Login-pagina wis-/toon-wachtwoord-iconen — geen losse wrapbare tree-node). **API-call headers: check op hardcoded literals i.p.v. `[varname]` templates.** Werkt een call via curl wél maar vanuit de app/Response & Test-panel niet: check het Headers-tabblad — een handmatig ingeplakte testwaarde (bv. Cookie-header) kan per ongeluk blijven staan i.p.v. `[sessionname]=[sessionid]`. Check ook het per-variabele "Include"-vinkje in Response & Test — staat die uit, dan wordt de letterlijke `[varname]`-tekst meegestuurd i.p.v. de testwaarde. ## Domein/architectuurcontext - **Drupal 7 Services sessie-auth**: `sessid` + `session_name` (uit `LoginCall`'s response) vormen samen de sessie-cookie: `Cookie: =`. `token` is een los CSRF-token, alleen nodig bij schrijf-requests (POST/PUT/DELETE), nooit bij GET. - **Drupal page cache bootstrapt vóór de sessie** — een ooit anoniem gecachete response (bv. een 403) kan session-bootstrap, hooks én custom-module-logging volledig overslaan. Bij twijfel: Drupal-cache legen en opnieuw testen vóór verder debuggen. - **Views numeric filter**: `$view->filter['uid']->value` moet `array('value' => $uid)` zijn, niet `array($uid)` — de foute vorm faalt stil (geen filter toegepast) i.p.v. een error te geven. - Provincie/gemeente blijft het basismodel voor content-scoping (bevestigd: mensen zoeken primair lokaal). Home is de landelijke standaard-startpagina zónder verplichte gemeente-keuze vooraf — dat vervangt het provincie/gemeente-model niet, het is een aanvullende ingang. De `Home`-prefixed componenten (`HomeUitgaantabelKaartComponent` e.d.) zijn een **bewuste kopie** van `PUitgaanSliderComponent`/ `UitgaantabelKaartComponent` + een eigen cityid-loze API-call — geen dode code, niet meenemen in opschoonacties. - Favorieten/login zijn P0. Backend-endpoint bestaat al; Drupal 7 views die de respons voeden hebben soms nog aanpassing nodig. - **Geen enkele "hasError"/netwerkfout-afhandeling bevestigd aanwezig** in de app (gecheckt 2026-08-04, hele `lib/` doorzocht) — elke FutureBuilder die alleen op `!snapshot.hasData` checkt, blijft bij een mislukte call voor altijd op de laadspinner hangen. Ga er dus niet van uit dat er ergens al een werkend voorbeeldpatroon staat om te kopiëren — dit moet nog van de grond af ontworpen worden (zie P0-taak in `TASKS.md`). ## Samenwerken met Bob - Bob is de enige developer/eigenaar, werkt vaak **zelf gelijktijdig** in dezelfde builder-sessie. Neem niet aan dat elke wijziging van jou komt. - Prioriteit: "eerst een werkende app live, daarna features" — P0 weegt zwaar boven P1 en P2. Bob herprioriteert soms fors zelf (bv. login+favorieten van P2 naar P0) — volg dat exact. - Claimt Bob een taak terug ("dat regel ik zelf") — stop daar direct mee en pak iets anders onafhankelijks op. - Kost iets veel tijd door handmatige builder-acties (vastzittende dialogen, geclipte controls): na 1-2 serieuze pogingen stoppen en concreet aan Bob voorstellen dat hij het zelf doet (exacte stappen). - Rapporteer nieuw gevonden bugs (vooral op een pagina waar Bob net zelf op zit) direct en duidelijk, niet pas in een latere samenvatting.