TASKS.md 38 KB

TASKS — alleen nog OPEN taken

*Opgeschoond 2026-09-19 (van 1924 naar ~800 regels). Afgeronde taken zijn verwijderd, niet gearchiveerd — ze staan in de git-historie en de blijvende inzichten in CLAUDE.md. Alles hieronder is open werk. Staat iets er niet meer in, dan is het klaar of afgewezen — niet opnieuw agenderen.*

Voor een nieuwe sessie: pak een taak uit "Claude — open", zet er meteen — bezig achter in dit bestand (een echte Edit, niet pas bij het afsluiten; andere sessies delen deze working directory), en verifieer elke builder-wijziging met een verse export. Taken onder "Bob — open" niet zelf oppakken.


📍 Waar staat het project?

De app-kant is vrijwel af. Op 2026-09-19 zijn o.a. afgerond: de header-locatie-keten (Home toont Heel Nederland, provinciepagina de provincienaam), het adaptive icon, de loginpagina, het drawer-logo, Crashlytics, "Mijn aanmeldingen", de categoriedropdown op het horeca-overzicht, de horeca-agenda-entiteiten en de opruimronde. Wat rest is livegang-werk: Google Play, het API-oppervlak dichtzetten, en één doorloop die alleen Bob kan doen.

👤 CLAUDE — OPEN

# Taak Kort
G0.6 Play-screenshots Eerste set van 10 klaar (play-screenshots.zip). Eén shot moet over zodra Show Test Ads uit staat
nameten Na het services-script op productie node/<nid>.json moet 403/404 geven i.p.v. 200 met 46 velden

👤 BOB — OPEN, in volgorde van wat de livegang blokkeert

# Taak Waarom nu
🔒 API dichtzetten services-dicht.php op productie Eigen blok hieronder. Op devbob gedraaid en getest ✅ — dicht: user/geocoder/userestablishments/system; open en werkend: alle app-endpoints
G Google Play-checklist G0–G4 Volledige checklist hieronder
65 De ingelogde doorloop Kernfuncties zijn nooit op een release-build getest. Claude kan dit niet (geen wachtwoorden)
AdMob Show Test Ads uit App Settings → AdMob. Wacht op de Google-registratie
P1-17 Laatste vertaling(en) 1k52hayg "Date end" → End date. Verder is de levende code schoon
Drupal taak 56-restant · taak 28/109 Cron-description op "xx days" ✅ gedaan · events zonder categorie gaat via de importer
Bij livegang Schakelaars en opruimklussen Eigen blok hieronder

#

🔧 Kleine open restpunten (app)

(P2-45b, P2-45c en 76 zijn op 2026-09-19 afgerond en geverifieerd met een verse export — zie de commit van die datum.)


🅖 G · GOOGLE PLAY — volledige checklist · Eigenaar: Bob

*Opgesteld 2026-09-19. Account aangemaakt 2026-09-18 als Organisatie (Digital Force); wacht op verificatie. Vervangt taak 63 (a-i) en punt 2 van "Bob's 7" — die twee niet meer los afwerken. Vink hier af.*

Bevestigd via Google's eigen documentatie, 2026-09-19:

  • Een organisatie-account is vrijgesteld van de 12-testers/14-dagen-regel. Die geldt alleen voor persoonlijke accounts van ná 13-11-2023. De keuze voor Organisatie was dus juist en scheelt minimaal twee weken.
  • D-U-N-S duurt tot 30 dagen. Dat is de kritieke pad-stap, niet de $25.
  • Legal name + adres in het Google Payments-profiel moeten EXACT matchen met het D-U-N-S-profiel. Mismatch = e-mail met deadline, daarna verwijdering van de app uit Play. Dit is de meest gemaakte fout en kost weken.

#

G0 · Nu doen, terwijl de verificatie loopt (buiten Play Console)

  • G0.1 — D-U-N-S: AL BINNEN (Bob 2026-09-19: "die krijg je standaard in Nederland, heb ik al gevonden"). De langste wachtstap valt dus weg. ⚠️ Blijft staan: leg het nummer vast mét de exact daarop geregistreerde bedrijfsnaam en adres — die moeten letterlijk matchen met G0.2.
  • G0.2 — Payments-profiel gelijktrekken met precies die naam/adres.
  • G0.3 — ✅ BESLOTEN (Bob, 2026-09-19): alles via FlutterFlow Deploy. Lokaal bouwen is afgevallen: het heeft twee stille faalmodi die allebei al zijn opgetreden (Flutter-vogeltje als icoon zonder flutter_launcher_icons, en een crash op de splash zonder --include-assets), plus een keystore die er nog niet is (android/app/build.gradle:80 staat op signingConfigs.debug). Sub-route: download het AAB in FlutterFlow en upload het handmatig in Play Console. De directe "publish to Play"-koppeling vergt een Google Play service-account-JSON; pas doen als handmatig uploaden gaat vervelen. ⚠️ Blijft staan als G4.3: controleer het icoon op het AAB dat je uploadt.
  • G0.4 — ✅ BESLOTEN (Bob, 2026-09-19): alleen Android, iOS later. Bob 2026-09-19, definitief: "fuck ios. we gaan eerst een android app bouwen. Als die geld oplevert, gaan we naar apple." Niet opnieuw agenderen; wat het zou kosten staat in het blok "iOS — wat er extra bij komt".
  • G0.5 — ✅ CONCEPT KLAAR (Claude, 2026-09-19), akkoord Bob. Drie varianten korte omschrijving (70/75/71 tekens) + lange omschrijving (1585 van 4000), opgebouwd uit de echte functielijst (drawer + Home-tabs). Bob's aanvullingen verwerkt: account is gratis, geen in-app aankopen; de formulering "horecaondernemers en stadsredacteuren" blijft staan. 📁 Tekst staat in ~/uk-play-assets/play-teksten.md (gered uit een vluchtige sessie-scratchpad, 2026-09-19 — stond nergens duurzaam). ⏳ Kan pas ingevoerd worden ná G1.1 (de app moet bestaan in Play Console).
  • G0.6 — ✅ EERSTE SET KLAAR (Claude, 2026-09-19). 10 stuks uit een echte --release-build (export mét --include-assets, flutter_launcher_icons vooraf): 6 telefoon (1080x1794), 2x 7" tablet (1200x1805), 2x 10" tablet (2560x1504). Systeembalk weggecropt; alle formaten binnen Play's eisen (320-3840 px, ratio onder 2:1). 📁 Staan nu duurzaam in ~/uk-play-assets/screenshots/ (+ de zip ernaast); ze stonden alleen in een sessie-scratchpad onder /tmp en waren een reboot kwijt geweest. Gered 2026-09-19. 🔴 De set is NIET uploadbaar zoals hij is — drie defecten, gemeten 2026-09-19:
    1. tab10-01-home.png en tab10-02-menu.png zijn byte-identiek (md5 b34debb5…, beide de Home-pagina). De menushot is nooit geland, dus er is feitelijk één unieke 10"-tabletscreenshot. Play wil er meer dan één per formaat; controleer het exacte minimum in Play Console.
    2. tel-04-horeca.png toont de AdMob-testadvertentie ("This is a 320x50 test ad"). Zet Show Test Ads uit (staat op de livegang-lijst).
    3. tel-05-zaak.png toont een zaak met een lege agenda; klopt inhoudelijk, maar een zaak mét agenda oogt sterker. 🔴 En: álle tien tonen de koptekst Amsterdam (gemeente) — precies wat taak 103 aan het repareren is. Elke shot bevat die header, dus de hele set moet opnieuw zodra 103 gereed is. Niet uploaden vóór die tijd. ➡️ Eén herschietronde ná 103 + Show Test Ads uit dekt alle vier de punten.
  • G0.7 — Feature graphic 1024×500 laten maken (grafisch werk, Bob).

#

G1 · Zodra het account geverifieerd is

  • G1.1 — Create app. Naam "Uitgaanskrant.com", Nederlands, App (geen game), Gratis. Package is com.uitgaanskrant.apponveranderlijk na de eerste upload, dus controleer 'm.
  • G1.2 — Payments-profiel verifiëren (deposit-challenge of bankdocument, ±5 dagen). Moet vóór publicatie rond zijn.

#

G2 · App content (Policy and programs → App content)

  • G2.1 — Privacy policy-URL: https://uitgaanskrant.com/nl/support/privacybeleid (anoniem 200, gemeten 2026-09-16).
  • G2.2 — Data deletion-URL: https://uitgaanskrant.com/nl/support/mobieleapp ⚠️ NIET /nl/account-verwijderen — die geeft anoniem 403 en reviewers openen 'm uitgelogd. Taak 85 heeft de in-app-knop al naar dezelfde URL gezet.
  • G2.3 — App access: "All or some functionality is restricted" + de inloggegevens van het testaccount (taak 86, al aangemaakt). ⚠️ Wachtwoord niet in dit bestand — alleen Play Console + wachtwoordmanager. Zorg dat er 3-4 favorieten in Amsterdam en één gevolgde gemeente op staan, anders ziet de reviewer vier lege tabs en leest dat als kapot.
  • G2.4 — Ads: Contains adsJA (AdMob zit in de build).
  • G2.5 — Content rating: IARC-vragenlijst invullen.
  • G2.6 — Target audience: bewust kiezen — er staat 18+ uitgaanscontent in.
  • G2.7 — Data safety. Gemeten in het manifest van de release-APK: permissies INTERNET, CAMERA, READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE, plus via AdMob/Firebase ACCESS_NETWORK_STATE, WAKE_LOCK, FOREGROUND_SERVICE, AD_ID en drie ACCESS_ADSERVICES_*. Declareren: Advertising ID → JA (verplicht, AD_ID staat erin), crashlogs/diagnostiek (Crashlytics), gebruikersnaam/e-mail (login), en foto's (camera + galerij, voor de aanmaakpagina's). Transport is versleuteld en verwijdering is mogelijk → beide aanvinken.
  • G2.8 — Government apps / Financial features: beide nee (staat er wel, makkelijk over te slaan, blokkeert anders de release).

#

G3 · Store listing

  • G3.1 — App-icoon 512×512. Gebruik assets/images/app_launcher_icon.png (het volvlakse), niet de adaptive foreground.
  • G3.2 — Feature graphic 1024×500 (uit G0.7).
  • G3.3 — Screenshots telefoon + 7" + 10" tablet (uit G0.6). Tabletshots zijn verplicht omdat de app tablets ondersteunt.
  • G3.4 — Korte + lange omschrijving (uit G0.5).
  • G3.5 — Categorie + contactgegevens + e-mail. Die e-mail wordt publiek getoond.

#

G4 · Release

  • G4.1 — versionCode ophogen vóór élke upload. Staat nu op 1.0.0+1 (pubspec.yaml:18). Bij de FlutterFlow-route: App Settings → App Details.
  • G4.2 — Build uploaden volgens de route uit G0.3. AAB, niet APK — die splitst per architectuur (de APK is nu 95,6 MB).
  • G4.3 — ⚠️ Icoon controleren op het geüploade artefact zelf. Open vraag: doet FlutterFlow's deploy-pipeline flutter_launcher_icons wél? Lokaal gebouwd draagt het bestand anders het Flutter-vogeltje. Niet aannemen.
  • G4.4 — Interne test eerst (geen tester-minimum voor organisaties), dan pas productie.

#

🍏 iOS — wat er extra bij komt (Bob's vraag 2026-09-19; besluit: NIET nu)

Ja, betaald — en anders dan Google een ABONNEMENT. Google Play is eenmalig $25; het Apple Developer Program kost ~$99 per jaar en moet elk jaar verlengd worden, anders verdwijnt de app uit de App Store. Verifieer het actuele bedrag bij inschrijving.

Wat je al hebt en meeneemt:

  • D-U-N-S-nummer — Apple eist dat voor een organisatie-account, net als Google. Dat is de langste stap en die is bij jou al gezet.
  • De permissieteksten in Info.plist staan al goed (Camera, PhotoLibrary, Calendar) — geverifieerd 2026-09-19, precies 3 keys.

Wat er nieuw bij komt:

  1. Apple Developer Program-account (~$99/jaar) + verificatie van de organisatie.
  2. Bundle ID + certificaten/provisioning in App Store Connect. Er is nu niets voor iOS ingericht: alleen ios/Runner.xcodeproj/project.pbxproj beweegt mee in elke export.
  3. Eigen screenshots in Apple-formaten (iPhone-maten + iPad) — andere afmetingen dan Play, dus die van G0.6 zijn niet herbruikbaar.
  4. Privacy Nutrition Labels — Apple's tegenhanger van Data Safety, apart invullen.
  5. ⚠️ App Tracking Transparency (ATT). AdMob gebruikt op iOS de IDFA. Wil je gepersonaliseerde advertenties, dan moet er een ATT-prompt komen bovenop de bestaande GDPR-consentflow. Dat is echt extra bouwwerk, geen vinkje.
  6. ⚠️ Guideline 5.1.1(v): account verwijderen moet IN de app kunnen. Onze knop (taak 85) opent een webpagina. Voor Play is dat prima; Apple is daar strenger over en dit is een klassieke reden voor een afwijzing. Reken op een echte in-app verwijderroute vóór een iOS-inzending.
  7. Guideline 4.2 (minimum functionality) — apps die vooral webcontent tonen worden geweigerd. Deze app heeft genoeg eigens (favorieten, aanmelden, agenda-integratie, offline-melding), dus dat risico is klein, maar het is wel waar Apple naar kijkt.

Route wanneer het zover is: FlutterFlow kan ook voor iOS bouwen, dus een Mac is niet strikt nodig. Punt 5 en 6 zijn het echte werk; de rest is administratie.

65 · Release-build — wat er NOG getest moet worden · Eigenaar: Bob

De uitgelogde doorloop is af (2026-09-19, --release, flags=0x0): 0 FATAL, 0 E/flutter, alle iconen renderen, vliegtuigmodus geeft netjes de VerbindingsBanner, Engels vertaalt. 65a, 65b en 65d zijn opgelost en export-geverifieerd.

Wat overblijft — en dat is per definitie Bob's stuk, want Claude voert geen wachtwoorden in:

🔴 De ingelogde doorloop. Nooit op een release-build getest, en het raakt de kernfuncties: inloggen · hartje op een zaakpagina (aan én uit) · Favorieten met inhoud, alle vier de tabs · gemeente volgen · evenement aanmaken tot en met indienen · Mijn profiel inclusief uitloggen. Een Play-reviewer logt straks wél in.

65c · Favorieten toont uitgelogd een leeg scherm — de standaardtab rendert niets: geen melding, geen uitleg, geen inlogknop. De uitleg staat op tab 4, die buiten beeld valt in de scrollende tabbalk. ⚠️ Opgepakt in een andere sessie (als taak 107). De Empty List Widget-sectie biedt alleen Image en Component, geen Text — dus het moet via een klein component of via een Text met een Visibility-conditie op userSessionid (Is Not Set or Is Empty).

65e · "Datum" blijft Nederlands in de Engelse modus — het label komt uit de valueOrDefault-default 'Datum' en is dus geen vertaalsleutel. Hoort bij P1-17.

Cosmetisch, al bekend: het logo in de drawer staat deels onder de statusbalk op de release-build (op de profile-build van 2026-09-19 is dat niet meer zo — taak 100 zette de padding op 40; opnieuw bekijken bij de volgende release-build).

📌 Livegang-checks die AL goed zijn (gemeten 2026-09-19, niet opnieuw doen)

  • De deel-URL werkt. https://uitgk.com/<nid> (deelknop op EventCurrent en de tekst in zetInAgenda) redirect naar de juiste pagina op uitgaanskrant.com. Getest op drie nids, inclusief een horecagelegenheid: 222338 -> "Scott Hamilton & Rein de Graaff Trio…", 75459 -> "Bimhuis Amsterdam". Eind-URL is een nette /nl/Noord-Holland/Amsterdam/...-alias.
  • De support-URL's geven 200: /nl/support/mobieleapp (de Support-link in de drawer én de data-deletion-URL voor Play) en /nl/support/privacybeleid.
  • devbob staat niet meer in de levende app-code (alleen in dode kopieën).

🔒 LIVEGANG-BLOK: API-oppervlak dichtzetten · Eigenaar: Bob

Besluit Bob 2026-09-19: 156 + 156b worden ÉÉN taak, en die nemen we serieus.

Volgorde:

  1. 156b eerst (views-beperking) — dat is alleen lezen, de app vraagt aantoonbaar niet meer dan vijf views op, en Drupal waarschuwt er zelf 11x over.
  2. 156 daarna via het drush-script (services-dicht.php): eerst droogdraai, dan $DOE_HET = TRUE, eerst devbob, dan productie. 📍 Stand 2026-09-19: op devbob gedraaid en door Claude nagemeten — het doet precies wat het moet.

| moet dicht zijn | devbob | |---|---| | user/1.json · user.json · geocoder.json · userestablishments.json | 404 | | POST system/connect.json | 404 |

| moet blijven werken | devbob | |---|---| | plaatsen.json · plaatsen_bij_gemeente.json · horecacategorieen.json | 200 | | alle vijf de views-endpoints | 200 | | POST user/login.json | 401 (bestaat, wijst alleen af) |

node/222338.json geeft daar nog 200 — klopt, node staat in het script nog uitgecommentarieerd. Productie volgt.* Bijvangst: system bleek al dicht te staan — de vinkjes waren uit de geplakte HTML niet af te lezen ("Enabled" is daar een kolomlabel).

  1. Fase 2 (node, file, taxonomy_term, taxonomy_vocabulary) stond gepland ná een accesslog-check. Bob 2026-09-19: die check doen we niet. Beslis dus zelf of je die vier aandurft; node/retrieve is de enige waarvan bewezen is dat hij lekt (zie hieronder), en een importer gebruikt Services vrijwel zeker niet — Feeds werkt via de interne Drupal-API.
  2. Daarna meet Claude na: node/222338.json moet 403/404 geven in plaats van 200 met 46 velden.

🔴 156 · Services-endpoint flutterdrup staat veel te wijd open · Eigenaar: Bob

Gemeten 2026-09-19 op de resourcelijst die Bob plakte, plus live getest tegen productie. De app gebruikt 24 methodes; er staan er tientallen aan die zij niet aanroept, waaronder schrijfoperaties.

Hard aangetoond, anoniem, zonder enige login:

  • GET /nl/flutterdrup/node/222338.json -> 200, 46 velden, inclusief uid, revision_uid, uuid, log, status en — het vervelendst — name: bob, de inlognaam van de auteur. De app gebruikt node/* nergens.
  • POST /nl/flutterdrup/system/connect.json -> 200 (geeft alleen een anonieme sessie terug; standaard Services-gedrag, geen lek, maar ook niet nodig).
  • GET /nl/flutterdrup/user/1.json -> 403 ✅ en taxonomy_vocabulary/getTree.json -> [false] ✅ — die twee lekken niets.

Uitvinken (de app roept hier NIETS van aan):

resource methodes die uit kunnen
node alle 7 — retrieve, create, update, delete, index, files, attach_file
system alle 4 — connect, get_variable, set_variable, del_variable
taxonomy_term alle 6 — incl. create/update/delete
taxonomy_vocabulary alle 7 — incl. create/update/delete
file alle 5 — de app gebruikt bestand_upload/upload
geocoder beide
userestablishments index — vervangen door mijn_horecagelegenheden
user alles BEHALVE login en request_new_password: retrieve, create, update, delete, index, logout, token, user_pass_reset, register, cancel, password_reset, resend_welcome_email

Geverifieerd in de export: de app roept exact twee user-endpoints aan (user/login, user/request_new_password) en nergens token of logout.

Aan laten staan — dit is de volledige lijst die de app gebruikt: aanmelding_kloonbron · accountdelete · bestand_upload · categorieen · entreeopties · evenementen · favorieten (flag/unflag/is_flagged) · favorieten_agenda · favorieten_gemeenten · favorieten_horeca · horecacategorieen · mijn_aanmeldingen · mijn_horecagelegenheden · mijn_stadsrechten · plaatsen · plaatsen_bij_gemeente · stadsactiviteiten · user (alleen login + request_new_password) · views (retrieve)

De rommel-resources (0, 34edfas2r, adfasdf, asdffadadf, fafadfadasdfas, fasdfasdfasd, flutterplaatsen, homes548446, safasdfasdfasdf, userfavoritescalendar, userfavoritescalendar345345234, …) staan ingeklapt in de lijst, wat betekent dat er geen methode van aanstaat — ze doen dus niets. Opruimen mag, is geen haast.

✅ 156b · Views-resource — blijkt AL beperkt (nagemeten 2026-09-19)

Drupal zet 11x een waarschuwing onder de views-resource over displays zonder access control. Live getest, anoniem: dat risico is niet actueel.

view resultaat
de vijf app-views (flutterflowmobiel1, flutterflow_events, ..._establishments, ..._establishment_info, ..._establishment_events) 200
user_contents (block_1/2/4, page) · places · _town_tree · thuis_bezorgen · front_block_go_ca · _for_search_page · establishment_calendar · flutterfavorietenagenda · _block_go_ca_for_town · block_more_go_out · latest_photobook · _search_ca_page 404

Alleen de vijf app-views komen door; de View Resource Settings zijn dus al beperkt.

⚠️ Wat je op het verkeerde been zet: de foutmelding luidt ["Display block_1 on view user_contents could not be found"], terwijl die view wél een block_1 heeft. Die melding betekent hier "view staat niet op de toegestane lijst", niet "display bestaat niet".

Rest: één keer visueel bevestigen op het tabblad View Resource Settings dat er precies vijf aangevinkt staan — dan weet je dat het zo bedoeld is.

🚦 Bij livegang — schakelaars en opruimklussen · Eigenaar: Bob

  • AdMob: Show Test Ads uitzetten. App Settings → AdMob (niet een vinkje op het AdBanner-widget — dat paneel heeft alleen Ad Properties en Ad Banner Dimensions; de export schrijft de projectinstelling per widget uit). Twee dingen bij het omzetten: (1) reken op lege ruimte i.p.v. advertenties zolang de app niet gepubliceerd is — AdMob levert dan nauwelijks fills, dat is normaal; (2) klik daarna niet zelf op je eigen advertenties, dat telt als ongeldig verkeer. Zodra hij uit staat schiet Claude tel-04-horeca.png opnieuw.

  • Builder-opruimronde, laatste restanten. Nog in de export: componenten SliderUitgaanComponentSmallCurrent en PUitgaantabelKaartComponentOrgineelMetKaartjeerin, pagina Event (weigert structureel élke wijziging met "Invalid Action" — na één poging laten staan) en de custom function dISABLEdatumVoorApi. Moet in de builder, niet met git rm — anders staat alles na de volgende export terug.

  • P1-17 · laatste vertaling(en). In levende code is nog één echt gat: 1k52hayg staat op Date end en zou End date moeten zijn. Verder hoort er nog iets bij zodra "Mijn aanmeldingen" af is (tcyc3idz is nu een lege NL-tekst). ⚠️ Tel geen sleutels mee waar EN == NL terwijl dat correct is (Select..., Website, Facebook, Media, ", " — identiek in beide talen).

  • Titels met spaties vooraan/achteraan (was P1-47). Bob 2026-09-11: de impact is te groot, we lopen de namen door bij de livegang. MySQL sorteert op de rauwe node.title, dus zo'n node valt buiten de alfabetische volgorde terwijl de JSON schoon oogt (_custom_clean_html poetst ná de query). Telquery:

    drush @prod sqlq "SELECT type, COUNT(*) FROM node WHERE title <> TRIM(BOTH ' ' FROM TRIM(BOTH CHAR(9) FROM TRIM(BOTH CHAR(10) FROM TRIM(BOTH CHAR(13) FROM TRIM(BOTH ' ' FROM title))))) GROUP BY type;"
    

    De fix is een UPDATE met diezelfde geneste TRIM op node én node_revision, idempotent, met vooraf een sql-dump van die twee tabellen. URL-aliassen blijven ongemoeid.

  • Max items + tab 6 (P2-24 A en C) — ✅ opgelost, alle zes de tabs draaien op filterHorecagelegenheden met .take(1000). Niets meer te doen.

(Afgerond en daarom verwijderd uit dit blok: taak 56 opruim-cron · P1-30 Cloudflare rate limiting · P2-25 debug-cleanup custom.module · de twee dode KANWEG-API-calls · de opruimronde van 7 dode kopieën.)

P2-46 · EventCurrent: restpunten na de ombouw van 2026-09-19 · Eigenaar: Claude

Gedaan (export- en toestel-geverifieerd, profile-build op emulator-5554): backups EventCurrentCopy, EvenementHorecagelegenheidCopy, EvenementInfoCopy; event-kaart met logo 96×96 links en EvenementInfo (Expanded) + Maps-knop rechts; de 500 px-Container weg; EvenementHorecagelegenheid zonder tabs: RowLogo (logo 96×96 + ColumnRechts: titel, labels, adres/plaats, telefoon, e-mail), links in een Wrap (Menukaart/Website/Facebook/Instagram/X/"Op Uitgaanskrant.com" met host ervoor), TextInhoud (zaakbeschrijving), foto's, ColumnOpening en ColumnBezorgen met Visibility op openingstijden/bezorgtijden; e-mail leest nu $[:]['e-mail']; drie phone: false-vlaggen weg; telefoon-guard zonder valueOrDefault; Entree-blok alleen bij entreeprijs; roze/rode vulkleuren van Entree/Contact weg; EN-vertalingen "Opening hours"/"On Uitgaanskrant.com". Nog te doen:

  • Laatste toestelronde gedaan (2026-09-19, profile op emulator-5554, horecaid=157070). RowLogo (logo 96×96 + titel + categorielabel), de adres-Row [Icon, TextAdres, TextPlaats] met spacing 6 ("Westerstraat 95 Enkhuizen"), telefoon, e-mail, de links-Wrap (Menukaart/Website/Facebook/Instagram/X/Twitter/Op Uitgaanskrant.com) en TextInhoud renderen allemaal goed. 🔸 Wel opgevallen: het losse label "Contact" staat links náást die Wrap en botst visueel met de tweede regel ervan zodra de links wrappen — het lijkt tussen de regels te hangen. Zie de screenshot van 2026-09-19. Cosmetisch, nog niet aangepakt.
  • ColumnBezorgen mét echte data bekeken en grotendeels opgeknapt (2026-09-19). Testzaak: nid 157070 (Chinees-Indische restaurant Lotus, Enkhuizen) — die heeft álle bezorgvelden gevuld (bezorgtijden, bestellink, bezorgkosten, minimaleorder, bezorgtin, thuisbezorgtbetaalopties, cryptocoins, afhaalopties) én menukaart, openingstijden, openingstijdenuitzondering en kvk. Route: uitgaanskrant://uitgaanskrant.com/eventCurrent?nid=214689&horecaid=157070. Wat er mis was: de sectie toonde elf kale regels zónder één label — "vanaf 17 uur", "2 euro", "20 euro", "Contant" zonder context, de bestellink als kale URL, geen kop, en lijstwaarden die aan elkaar plakten ("AfhaalBezorgen", "EnkhuizenBovenkarspel"). Gefixt en op emulator-5554 (profile) geverifieerd: kop "Bezorgen" (i18n jxlwje5u, dezelfde stijl als "Openingstijden"); labels via Combine Text op Bezorgtijden:, Bezorgkosten: en Minimale bestelling:; een Visibility-guard op Textbezorg-minorder (establishmentMinimaleorder is set/non-empty) — die had er géén, dus met label erbij zou hij anders "Minimale bestelling: null" tonen; en Wrap-spacing 8 op de drie lijsten, zodat de waarden niet meer aan elkaar plakken.
  • TextInhoud staat onderaan ColumnInfo (na het Contact-blok), Body Medium, zonder Max Lines — overweeg padding-top 8 en een kop "Over deze zaak".
  • ✅ EN-vertaling van "Menukaart" (22xoyrqe) gecontroleerd: staat er als Menucard. Gevuld dus, maar geen idiomatisch Engels — Menu zou het zijn. Jouw keuze (hoort verder bij P1-17).
  • Niet getoond: kvk (zaak). Bewust overgeslagen.
  • organisator wordt nu wél getoond (2026-09-19, Bob's besluit, af en op toestel geverifieerd). Zie het eigen blok hieronder; taak 99 kan hiermee dicht.
  • Bob: de drie backup-kopieën (EventCurrentCopy, EvenementHorecagelegenheidCopy, EvenementInfoCopy) op de P2-7-opruimlijst zetten zodra de nieuwe pagina goedgekeurd is. Claude verwijdert niets.

🗄 Drupal / views — open

#

📌 Het horeca-overzicht laadt AL lazy — één fetch bij openen, niet zes

Gemeten 2026-09-19 op de export; dit corrigeert een eerdere bewering van Claude in deze sessie ("tot 60 requests per schermopening"). Die telling was fout: er staan zes fetchAlleHorecagelegenheden-aanroepen in horecagelegenheden_overzicht_current_widget.dart, maar ze worden niet samen uitgevoerd.

  • On Page Load (regel 55, addPostFrameCallback) doet er één — tab 0 (horcat 17969, Activiteiten, services_1).
  • De TabBar heeft onTap: (i) async { [ () async {}, …vijf closures… ][i]() } — dus per tab-tik precies één fetch, en tab 0 doet niets omdat die al geladen is.

Een schermopening kost dus één fetchAlleHorecagelegenheden, en die loopt door API-pagina's van 100 tot maxPaginas = 10. In Amsterdam heeft tab 0 (Activiteiten) 42 items → één HTTP-request. De 60 uit die eerdere schatting komt in de praktijk nooit voor.

Gevolg voor de rate limit: 100 requests per 10 s is ruim voldoende; de verhoging naar 200 was niet nodig (schaadt ook niet).

Wat er wél nog te winnen valt, klein: elke tab-tik doet een verse fetch, ook als je terugkeert naar een tab die je al bezocht hebt. Heen-en-weer klikken tussen zes tabs haalt dus zes keer opnieuw op. Een guard ("sla de fetch over als alleXxx al gevuld is") lost dat op, maar kost een conditie in vijf actieketens — en juist die dialoog is op deze pagina fragiel. Niet gebouwd; alleen doen als het in de praktijk hindert.

P2-24 · Restpunten horeca-zoekfilter · Eigenaar: zie per punt

A, C, D en E zijn af (2026-09-19, export-geverifieerd): alle zes de StaggeredViews draaien op filterHorecagelegenheden(...), .take(25) is weg (6x .take(1000)), de routes wijzen naar ...Current, en de twee lege TextField-placeholders zijn opgeruimd. F is geschrapt op Bob's verzoek.

Nog open: B — categoriedropdown per tab. ⚠️ Opgepakt in een andere sessie. Niet zelf aan beginnen.

Wat daarvoor al klaarstaat:

  • horecacategorieen.json is publiek en staat op productie (anoniem HTTP 200, 129 records, 0,2 s). Exact 6 termen op diepte 0 — de zes tab-tids — en 123 op diepte 1; dieper gaat de boom niet, dus "directe kinderen van de tab-tid" is de juiste filterregel.
  • De custom functions horcatSubTids en horcatSubLabels staan in het project en zijn tegen de echte respons nagerekend.
tab horcat naam in de boom subcategorieën
Activiteiten 17969 Activiteit 26
Cultuur 17963 Cultuur & Info 15
Eetgelegenheden 34 Eetgelegenheden 52
Overnachten 17965 Overnachten 4
Uitgaan 17967 Uitgaansgelegenheid 19
Verhuur/catering 17968 Verhuur, Verkoop & Catering 7

⚠️ De naam van de wortelterm wijkt soms af van het tablabel (Uitgaan vs Uitgaansgelegenheid) — onschadelijk, er wordt op tid gefilterd.

Waarom het niet zonder dat endpoint kan: het veld categorie van flutterflowmobiel_establishments bevat alle labels van een zaak, over al zijn hoofdcategorieën heen. Tab Eetgelegenheden levert zo 57 labels op met o.a. "Catering" (26x); tab Cultuur geeft "Cafe" (11x). Een zaak die op twee tabs staat is uit die respons principieel niet toe te wijzen aan de juiste hoofdcategorie. Met parent_tid uit horecacategorieen is dat één filterregel.

Niet oplosbaar, geaccepteerd: FlutterFlow zet op de On Change-trigger van het zoekveld een EasyDebounce van 2000 ms die nergens instelbaar is. De lijst ververst dus ~2 s nadat je stopt met typen.

🔴 Taak 28 / 109 · 618 toekomstige events zonder categorie komen NERGENS in de app · Eigenaar: Bob

Nagemeten door Claude op productie, 2026-09-19 17:44 (alle zeven displays volledig gepagineerd, nids naast flutterflow_events gelegd, geclassificeerd op datum + tijd tegen de >= -2 uur-grens van de master):

echte toekomstige events 6.684
bereiken geen enkele tab 618 (9 %)
... daarvan zonder categorie 618 — dus 100 %
... daarvan mét categorie 0

Twee dingen die dit uitwijst. (1) De zeven displays dekken alle categorieën correct af — er is geen categorie meer die nergens landt, dus dát deel van taak 28 is klaar. (2) Het hele resterende lek is één oorzaak: een event zonder categorie valt door elk display-filter heen en is in de app dus onvindbaar. Stand 12 sep: 597. Nu: 618. Het loopt dus licht op — de fix van 19 sep werkt kennelijk niet (of alleen voorwaarts, terwijl de bestaande nodes blijven staan).

Voorbeelden (allemaal ruim in de toekomst, dus geen datumkwestie): 222806 Steve 'n' Seagulls (20 dec, Weert) · 222737 Altruism Label Night (21 nov, Maastricht) · 222703 BRECHTJE Maresa (1 okt, Groningen).

✅ Besluit Bob 2026-09-19: dit is een IMPORT-probleem en wordt bij de importer afgevangen. Geen Drupal-hook of extra display nodig; niet opnieuw agenderen. Wel bij een volgende contentmeting nakijken of het getal daalt (nu 618).

afgewezen alternatief

Voorstel — een default in custom_node_presave(). Die hook draait er al voor go_out_event (adres/geo/plaats overnemen van de horecagelegenheid); één blok erbij dat field_category2 vult met een vaste fallback-tid zodra het leeg is, dekt alle nieuwe imports. De 618 bestaande haal je in één keer mee met een drush php-eval die diezelfde tid zet en node_save() aanroept. Wat Claude van Bob nodig heeft: welke tid de fallback moet zijn (een bestaande categorie, of een nieuwe term "Overig"). De alternatieven — een achtste display zonder categoriefilter, of de importer aanpassen — kosten meer en geven een extra tab in de app.

⚠️ Meetles, kostte deze sessie een valse ronde: classificeer op datetime, niet op datum. Op datum alleen leverde dezelfde meting 30 "bugs" op die in werkelijkheid gewoon events van vandaag waren die al begonnen waren — precies wat het >= -2 uur-filter hoort weg te laten. Staat ook al in CLAUDE.md; ik trapte er alsnog in.

📚 Referentie — geen taak, wél lezen vóór je iets opnieuw onderzoekt

#

📌 Nagemeten, niet opnieuw onderzoeken

  • Het P1-15-patroon is projectbreed schoon. Alle 16 launchURL-aanroepen in levende code hangen achter een guard op de rauwe waarde, niet op een valueOrDefault.
  • EstablishmentInfo geeft precies één rij per zaak (60 Amsterdamse zaken getest). De zes $[:].veld-knoppen op de detailpagina leunen daarop; let op zodra er een multi-value veld aan die view wordt toegevoegd.
  • P1-45 is echt weg: categorie is op alle zeven flutterflowmobiel1- displays altijd een lijst (65 records gemeten).
  • SliderUitgaanComponentSmallCurrent negeert zijn eigen townid/displayid (P1-44-patroon), maar heeft nul gebruikers — geen impact, alleen niet inzetten zonder dat eerst te repareren.
  • fillColor op de twee aanmaakpagina's is al goed: zowel stadsactiviteitAanmaken (18 widgets) als uitgaansevenementAanmaken (11) staat volledig op primaryBackground. Het in CLAUDE.md voorspelde secondaryBackground-defaultprobleem is dus niet meer aanwezig.
  • $.categorie is op de Home-displays nooit leeg of afwezig (services_1/3/5, Amsterdam, 25 items elk: altijd een lijst). De .toList() zonder true op home_uitgaantabel_kaart_component_widget.dart:651 kan daardoor in de praktijk niet crashen — events zonder categorie halen de displays sowieso niet.
  • Ongelezen component-parameters: alleen EvenementInfo (6 van de 12, nagemeten en juist), de wees hierboven, en HorecagelegenheidoverzichtKaart.nid (restant van het verwijderde hartje).

🚫 Bob's besluiten 2026-09-13 — niet meer voorstellen

  • Titels met onzichtbare tekens opschonen (was taak 42 / P1-47): AFGEWEZEN. Bob: "we wachten tot horecagelegenheden het zelf aanpassen, of we passen het als editors een keer aan. Het is heel veel oude data, daar ga ik geen tijd in steken. Als ik er tegenaan loop, pas ik het aan." Niet opnieuw agenderen. Ter informatie, zodat het effect bekend is: 18 van de eerste 100 zaken in het horeca-overzicht staan hierdoor buiten de alfabetische volgorde, en ze staan allemaal bovenaan (MySQL sorteert op de rauwe node.title, een spatie komt vóór de A). De eerste kaarten die een gebruiker ziet zijn dus willekeurig.
  • minSdkVersion-beslispunt (was taak 43): VERVALLEN, er valt niets in te stellen. Bob's vraag ("dat gaat toch automatisch via FlutterFlow?") is terecht. Gecontroleerd 2026-09-13: android/ heeft geen ongecommitte wijzigingen meer en de vastgelegde waarden zijn minSdkVersion flutter.minSdkVersion (= 24) en tasks.register("clean", Delete) — precies de moderne variant. Het punt is dus vanzelf beslecht; niemand hoeft hier nog iets aan te doen.
  • flutterflowmobiel1 opruimen (was taak 46): GESCHRAPT op Bob's verzoek. services_2 bestaat nog (25 items) maar heet "ongebruikt" en blijft staan tot een schoonmaakronde.
  • P2-7 opruimronde (was taak 44): naar de livegang-lijst.
  • Engelse vertalingen (was taak 45): naar de livegang-lijst, deels al gedaan. Bob 2026-09-13: "dit zullen we nog wel een paar keer krijgen, nieuwe pagina's met vertalingen die missen, en dat wil ik later in 1x."
  • Events zonder categorie (taak 32 deel B): Bob heeft er een eigen taak van gemaakt. Wél een probleem, géén livegang-blokker — zijn expliciete oordeel.

📊 Contentstand productie — hermeten 2026-09-19 (Claude)

09-14 09-19
unieke events (flutterflow_events) 16.065 17.123
toekomstige events (op datum+tijd) 6.202 6.684
bereiken geen enkele app-tab 622 (10 %) 618 (9 %)
... daarvan zonder categorie 597 618 — dus 100 %
... daarvan mét categorie 25 0

De displays dekken alle categorieën nu volledig af — er is geen categorie meer die nergens landt. Het hele resterende lek is één oorzaak: een event zonder categorie valt door elk display-filter. Zie het blok bij taak 28/109; Bob vangt dat af bij de importer.

Ouderdomsverdeling (voor de opruim-cron van 180 dagen): 6.728 toekomstig · 8.107 jonger dan 180 dagen · 2.251 tussen 180 en 360 dagen · 14 ouder dan 360. Die 2.251 worden bij de eerstvolgende cronruns opgeruimd; met 1000 per run is dat ~3 dagen.

HTML-entiteiten: titels, adressen en de horeca-agenda zijn schoon (taak 129). body heeft er nog 41 per 200 records, maar die gaat door de custom widget FromHTML en rendert dus goed.

⚠️ Meetles: classificeer op datetime, niet op datum. Op datum alleen gaf dezelfde meting 30 "bugs" die in werkelijkheid events van vandaag waren die al begonnen waren — precies wat het >= -2 uur-filter hoort weg te laten.

📌 Taak 30 · dubbele rijen in flutterflow_events — bekend, geen impact

Nid 214439 ("Brocante Markt Klein Frankrijk") komt 14 keer identiek terug in flutterflow_events; 214441 en 214420 elk 3x, 214367 2x. Op 2026-09-12 onveranderd (18 overtollige rijen op 2025).

Maar het bereikt de app niet. Nagemeten 2026-09-12:

  • flutterflowmobiel1 heeft géén duplicaten: services_1/2/3/5/7 geven allemaal evenveel rijen als unieke nids. Node 214439 komt er zelfs helemaal niet in voor (zijn categorie "Tweedehands markt" valt onder geen enkele display — dat is taak 28, niet deze).
  • flutterflow_events wordt door de app alléén per nid bevraagd (EvenementCall.call(nid: ...), vanuit evenement_component en event_current). Er wordt nergens een lijst uit opgebouwd: de twee iteraties in dat component lopen over fotoos en categories binnen één event, niet over de rijen.

Gevolg: geen zichtbare schade, geen haast. Het blijft wel iets om te weten: bouwt er ooit iemand een lijst op deze view, dan komt zo'n node 14x in beeld. De oorzaak is het klassieke Views-patroon "een veld met meerdere waarden krijgt een eigen rij" — kijk op node 214439 welk veld 14 waarden heeft en zet Multiple field settings → Display all values in the same row aan.