Files
helvoirtgevelreiniging.nl/todo.md
T

26 KiB
Raw Blame History

Helvoirt Gevelreiniging — Bouwplan (todo)

Stack: Astro 6 + Emdash 0.1.0 + Cloudflare Workers (D1 + R2 + KV). Register: conversiegerichte lokale dienstensite, familie van onderhoudsbedrijfvanhelvoirt.nl. Zie architecture.md (technisch) en CLAUDE.md (regels/workarounds). Alleen wijzigen in dewebsmid\helvoirt.

Status (16-06-2026): marketing-pagina's LIVE + CMS-migratie loopt — morgen verder met LIVE ZETTEN. Alle pagina's live op https://helvoirt-gevelreiniging.mike-197.workers.dev (deploy version f576e989). CMS geseed + ingericht (zie §3); de SEO-partij kan pagina's + locatiepagina's beheren/maken/SEO-optimaliseren (CMS-SEO-handleiding.md). Werkgebied-pills = Helvoirt + Limburgse steden. Bestaande pagina's worden "volledig naar CMS" gemigreerd (klantopdracht), met fallback-veiligheid + sentinel-verificatie: Over ons, Homepage(-tekst), Contact KLAAR; Maastricht-locatiepagina live.

← VOLGENDE STAP: De code-side go-live-laag is af + live (17-06-2026, deploy ed137121): robots, sitemap (incl. CMS), JSON-LD (LocalBusiness + Service/Breadcrumb), GTM-render, OG-image + apple-touch-icon, middleware, privacy/voorwaarden, mail-code + D1-opslag (contact_submissions) + magic-link-plugin. CMS = 100% van de pagina's (Wave 3 Diensten klaar → ook de 5 dienstpagina's + overzicht + homepage-accordion + nav-menu uit de diensten-collectie). Wat nu nog rest is (1) externe input — mailbox-wachtwoord/host → secret CONTACT_SMTP_PASS (bash printf), SEO-partij-e-mail → users-rij, klantcontent (NAW/reviews/FAQ/foto's/GTM-ID), domein-DNS → zone+nameservers+routes(rebuild)+mail-DNS+HTTPS (zie GO-LIVE.md) — en (2) optioneel self-hosted fonts, Lighthouse-meting, checklist .xlsx. CMS-migratie is 100% klaar (alle pagina's + alle beelden, tekst én media, in het CMS — deploy 57651519). Schema op live D1 = via migrations/*.sql. Herdeploy: astro build + wrangler deploy.


⏳ Open punten (afstemmen met klant)

  • Telefoon + adres aangeleverd: 085 060 4956, Stationsplein 1, 6131 AS Sittard (in Base.astro + contact.astro fallbacks; staat live).
  • Werkgebied-copy blijft "Helvoirt en omgeving" (16-06-2026): kop + alle SEO-copy ongewijzigd ("Helvoirt en omgeving"). Alleen de pills vernieuwd: Helvoirt blijft vooraan, daarna de nieuwe steden. Eerdere reverteerde poging om naar "Sittard en omgeving" te gaan is teruggedraaid op klantverzoek.
  • NAW resterend: e-mail (nu placeholder info@helvoirtgevelreiniging.nl), KvK, BTW, WhatsApp, openingstijden bevestigen, Google-reviews-URL + score.
  • Echte reviews (i.p.v. de 3 placeholders) — liefst met de Google-link.
  • Echte foto's: hero + per dienst (resultaat-galerijen) — nu AI-placeholders + de 6 echte werk-*-foto's. (Before/after-paren niet meer nodig: projecten geschrapt.)
  • Werkgebied-plaatsen (pills) vastgesteld: Helvoirt, Maastricht, Echt, Roermond, Weert, Sittard, Heerlen, Brunssum, Kerkrade, Landgraaf. Bron: klant 16-06-2026. (Kop blijft "Helvoirt en omgeving".)
  • FAQ-teksten "Goed om te weten" bevestigen.
  • Logo: levert klant een eigen logo aan? (nu tekst-wordmark "Helvoirt / Gevelreiniging" + H-monogram-merkmark; druppel verwijderd.)
  • Kleuren samplen → uit Elementor-kit zusterbedrijf. Fonts → Archivo Expanded + Open Sans. Worker/D1/R2-naamgeving → helvoirt-gevelreiniging / helvoirt-db / helvoirt-media. Hero-fotografie → nano-banana placeholders (klantkeuze). Blog → buiten launch-scope.

§1 — Scaffold (mirror exterios) ✅ GEDAAN

  • package.json/tsconfig/astro.config.mjs/worker.ts/live.config.ts/wrangler.jsonc/.gitignore/.env + scripts + lib/img.ts.
  • npm install --ignore-scripts (721 packages) · astro sync/check/build slagen.
  • Geleerd: NODE_OPTIONS=--use-system-ca voor elke astro/wrangler-call; placeholder-database_id (miniflare eist niet-leeg).

§2 — Design tokens ✅ GEDAAN (DESIGN.md = optioneel, nog niet geschreven)

  • tokens.css: committed blauw/petrol palette (cyaan #4ebfd9, actie #0069ff/#0055ce, petrol #1d4852/#15363d, off-white #f6fafb), getinte neutralen, status/ster-tokens.
  • Fonts Archivo Expanded (kop) + Open Sans (body). Géén Poppins/Fraunces.
  • .btn-cta/.btn-outline/.btn-text, section-helpers, .reveal.
  • (optioneel) DESIGN.md schrijven via /impeccable document.

§3 — Emdash schema (seed/seed.json) + CMS voor SEO-partij 🟢 GESEED + LIVE (volledige migratie loopt, §3b)

Doel (klant 16-06-2026): de SEO-partij moet zelf gewone pagina's én locatiepagina's kunnen aanmaken en optimaliseren voor SEO. Bewust SEO-vriendelijk + simpel ingericht (3 onderdelen i.p.v. het volledige sectie-model).

  • seed/seed.json geschreven — collecties: site_settings (NAW + address_street/address_city + KvK/BTW/WhatsApp + social + SEO-standaard meta_title/description + GTM + logo/favicon/og_image), paginas (gewone pagina's: title/intro/portableText-content + SEO-groep), locaties (locatiepagina's: plaats/H1/intro/portableText-content + SEO-groep). SEO-groep = expliciete velden met nette NL-labels: meta_title, meta_description, canonical, og_image, noindex. Demo-content: site_settings/main (echte NAW, werkgebied "Helvoirt en omgeving", score 4,9) + 1 concept-sjabloon per type (Voorbeeldpagina + locatie Maastricht).
  • Routes gebouwd (lezen CMS met try/catch + fallback → nette 404 vóór seed):
    • src/pages/[...slug].astro — root-router: zoekt de slug op in locaties (lokale pagina, petrol-hero + content) én paginas (gewone pagina); beide op de root (/maastricht, /onderhoudstips). Locatie wint bij botsing. Laagste prioriteit, PortableText, eigen branded 404. (Klantkeuze 16-06: locatiepagina's op /URL, niet /werkgebied/URL.)
    • src/pages/werkgebied/index.astro — werkgebied-hub die gepubliceerde locaties oplijst (linkt naar /<plaats>); anders statische pills.
  • src/lib/images.ts = resolveCmsImage (uit behangerbrabant) voor image-velden; Base.astro uitgebreid met noindex-prop + absolute OG-URL; gedeelde .prose in tokens.css. astro check 0/0/0 + build OK.
  • CMS-SEO-handleiding.md geschreven voor de SEO-partij (inloggen, pagina/locatie aanmaken, SEO-velden, do's/don'ts, wat nog in code zit).
  • Geseed (16-06-2026): setup-wizard gedraaid met "Include sample content" → site_settings/paginas/locaties + demo in remote D1.
  • Ingebouwde pages/posts verwijderd (emdash-defaults, leeg/ongebruikt) via scripts/cleanup-default-collections.sql. Blijven weg (defaults worden alléén bij setup aangemaakt). CMS toont nu alleen relevante onderdelen.
  • Base.astro leest nu site_settings.meta_title/meta_description/og_image (per-pagina prop > Instellingen > code-default) → SEO-partij kan site-brede SEO in Instellingen zetten.
  • CMS-login: e-mail-provider-plugin (magic-link, newsite.md §6b) + SEO-partij-gebruiker(s) in users. (nog te doen)

§3b — Volledige CMS-migratie bestaande pagina's (klant: "Volledig naar CMS") ✅ KLAAR (17-06-2026) — alle pagina's + álle beelden in het CMS (tekst + media)

Aanpak (bewezen, risico-arm): per pagina (a) model in scripts/cms-models/<x>.mjs, (b) scripts/gen-cms-sql.mjs genereert D1-SQL die emdash's eigen tabelstructuur spiegelt (overgenomen uit de echte ec_paginas-DDL; géén search/FTS op nieuwe collecties), (c) toepassen op remote D1 via wrangler d1 execute --file migrations/*.sql, (d) .astro herschrijven om uit CMS te lezen met de huidige tekst als fallback (site blijft identiek), (e) deploy + verifiëren.

  • Machinerie gebouwd: scripts/gen-cms-sql.mjs (generator: type→column_type, systeemkolommen, ULID via ulidx) + scripts/cms-models/. UTF-8 (·, —) blijft intact door de round-trip (geverifieerd).
  • Wave 1 — Over ons KLAAR + LIVE-GEVERIFIEERD (version 3ce0ce32). Singleton over_ons (hero/verhaal/waarden-koppen + SEO; verhaal = portableText) + waarden als inline repeater-veld op over_ons. over-ons.astro leest uit CMS met fallback. Sentinel-test bewees CMS-aansturing; 4/4 repeater-rijen + icoon-SVG renderen live. Visueel identiek.
  • Klantfeedback Over ons verwerkt (0003_*.sql): "Waarden" van losse collectie → inline repeater (rijen toevoegen/verwijderen/verslepen binnen de pagina, WP-Bakery-achtig); kern-inhoudvelden verplicht zodat "(optional)" daar weg is. Native emdash: pagina's dupliceren (kopieer-icoon in lijst, POST .../duplicate), collectie-rijen add/delete. "(optional)" is niet globaal uitschakelbaar (geen branding/CSS-hook); alleen via velden verplicht maken. Repeater-velddef = validation.subFields (zie scripts/gen-over-ons-repeater.mjs).
  • Beeldvelden toegevoegd aan Over ons (0004_*.sql): hero_image (banner) + story_image (verhaalfoto), via resolveCmsImage met de huidige statische foto's als fallback (leeg veld = standaardfoto). Sentinel-test bewees dat het veld de pagina aanstuurt; leeg → fallback. Les voor volgende waves: beeldvelden meenemen (hero/galerij/dienst-foto's) — niet alleen tekst.
  • Labels opgeschoond (0005_*.sql): "(~N tekens)" uit alle SEO-labels (site_settings/paginas/locaties/over_ons) — ook in seed.json + model. NB: "(optional)" blijft (emdash-standaard, niet uitzetbaar).
  • Foto's in de mediabibliotheek gezet (0006_*.sql + R2-upload, scripts/gen-over-ons-media.mjs): hero-after-1280.webp + werk-3.webp naar R2 (helvoirt-media, key=<ulid>.webp) + media-rijen + veld-JSON (local provider). De Over ons-beeldvelden tonen nu de echte foto in het CMS (niet leeg). Geverifieerd: media serveert via /_emdash/api/media/file/<key> (200, image/webp) en de pagina gebruikt de CMS-afbeeldingen. Patroon voor volgende waves: zo ook de echte foto's voor homepage/diensten in de library zetten.
  • Maastricht-locatiepagina LIVE (/maastricht, gepubliceerd, 200, H1 correct).
  • Wave 2a — Homepage TEKST KLAAR + LIVE-GEVERIFIEERD (version 1223ddda). Singleton homepage (0007_*.sql): hero (titel/sub/vinkjes-repeater), intro, diensten-koppen, werkwijze-repeater (4), bewijs-kop, reviews (uitgelicht + kleine-reviews-repeater), werkgebied-koppen, FAQ-repeater (4). Hero.astro (props) + index.astro lezen uit CMS met fallback. Sentinel-test bewees CMS-aansturing; alle repeaters renderen live. Generator uitgebreid met repeater-validation/subFields.
  • Wave 2b — Homepage BEELD KLAAR + LIVE-GEVERIFIEERD (17-06-2026, 0011_homepage_media.sql). Collecties hero_cards (3) + galerij (6) als image-rijen (image+tekst); Hero.astro (via cards-prop uit index.astro) + de bewijs-sectie lezen eruit met fallback. Foto's in de mediabibliotheek (galerij hergebruikt de werk-foto's uit Wave-3-media; hero gebruikt kleine -768-varianten voor perf → 2 nieuwe media). Live: 3/3 hero + 6/6 galerij = CMS-media; screenshot bevestigt visueel identiek.
  • Diensten-BEELD in de mediabibliotheek (0010_diensten_media.sql, gen-diensten-media.mjs). 13 unieke foto's naar R2 + media-rijen, gekoppeld aan alle 25 beeldvelden (nav/hero + 3 resultaat-slots × 5 diensten). Live: detailpagina 4/4, overzicht 5/5, accordion 5/5 = CMS-media. CMS serveert één formaat → -1280 voor hero/cards, werk-basis voor galerij; srcsetFor geeft undefined voor CMS-paden (geen kapotte srcset).
  • Wave 3 — Diensten KLAAR + LIVE-GEVERIFIEERD (17-06-2026, 0009_diensten.sql). Collectie diensten (5 entries, 36 velden) gedeeld door: homepage-accordion (nav_*), /diensten-overzicht (title + nav_*), /diensten/[slug]-detail (alle velden: hero/points/intro/included/resultaat-galerij/aanpak/faq/seo/noindex) én het nav-dropdownmenu (Base.astro, op nav_order). Alles leest uit CMS met de hardcoded data als fallback. Beeldvelden leeg = code-fallback naar de huidige foto's (nav_image/hero_image/result_N_image per dienst; media-library-populatie = optionele vervolgstap zoals over-ons). Resultaat-galerij = 3 vaste image-slots (repeater kan geen image-subveld). Sentinel-test bewees dat de CMS-waarden zowel de detailpagina als de homepage-accordion aansturen. Onbekende slug → 404. astro check 0.
  • Wave 4 — Contact KLAAR + LIVE-GEVERIFIEERD (version f576e989, 0008_*.sql). Singleton contact_page (kicker/H1/intro + SEO). contact.astro leest uit CMS met fallback (NAW blijft uit site_settings). Sentinel-test OK. Generator is nu argument-gedreven: node scripts/gen-cms-sql.mjs <model> <out.sql>.

§4 — Base layout + kerncomponenten ✅ GEDAAN

  • Base.astro (donkere petrol header met telefoon + CTA + dropdown, drawer, prefooter-form #contact, footer, sticky-CTA, reveal-IO, SEO-head, site_settings try/catch).
  • ContactForm.astro (naam [placeholder "Naam"], e-mail, telefoon, bericht + honeypot + time-trap).
  • api/contact.ts (spam-bescherming + validatie + D1-opslag contact_submissions + 2 mails via src/lib/smtp.ts; secret = §12).
  • CtaBand.astro extraheren wanneer subpagina's het nodig hebben.

§5 — Homepage (index.astro) ✅ GEDAAN + GEPOLISHED

  • Hero = tekst links + werkfoto-collage rechts (Tailark hero-6 nagebouwd): Archivo-kop "Je gevel weer als nieuw." (800) + dominante blauwe CTA + vinkjes + prominente 4,9-Google-regel; rechts 3 staande werkfoto-cards die links overlopen in het tekstblok (.hero__fade), mobiel swipebaar. Doorlopen: craft → bolder (verworpen) → quieter → split (verworpen) → collage → audit 20/20 → critique 33/40 + fixes. Géén before/after-slider in hero (→ planning /projecten, §7). hero-after/before/gevel.webp + degrade-before.py nu ongebruikt op de homepage (de 3 hero-cards gebruiken werk-1/3/5; was 4 incl. werk-6/Metselwerk, op klantverzoek terug naar 3 + cards verbreed).
  • Intro (statement + inline meta), Diensten = uitklappende beeld-accordion (5 panelen, "+"-indicator, blijft open om te lezen, mobiel gestapeld), Werkwijze-tijdlijn (licht), Bewijs-galerij (echte foto's), Reviews (uitgelicht + 2 klein), Werkgebied-pills, FAQ "Goed om te weten".
  • Impeccable: craft ("het verschil zien") → bolder (donkere header) → overdrive (accordion) → audit 19/20 (tapdoelen ≥44px, fetchpriority, tokens) → critique 32/40 (accordion klikbaar, FAQ, scaffold-cleanup, hero→#contact + adresveld).
  • animate-pass: samenhangende motion-laag — consistente scroll-reveals over álle secties, werkwijze-tijdlijn tekent zich in (.flow__line + .reveal--draw), gestaggerde werkgebied-pills + FAQ (.reveal--stagger/--i), :active-indruk op .btn-cta/.btn-outline, offerteformulier-spinner (aria-busy) + geanimeerde succesmelding, trage hero-gloed. Nieuwe token-primitives .reveal--draw/.reveal--stagger in tokens.css. Alles achter prefers-reduced-motion.
  • Eerdere diensten-iteraties (verworpen): zig-zag-banden → index+preview → foto-mozaïek → typografisch menu → accordion (definitief).
  • Inline secties later extraheren naar herbruikbare componenten.

§6 — Diensten ✅ KLAAR (overzicht + 5 detailpagina's)

  • pages/diensten/[slug].astro = herbruikbaar SSR-sjabloon met alle 5 slugs gevuld (zie onder); per pagina dienst-hero (+4,9-regel) + intro/checklist + resultaat-galerij + mid-page CTA-band + aanpak-stappen + dienst-FAQ → CTA's #contact. Onbekende slug → 404 (getest). astro check 0 errors, axe-core 0 violations, geen overflow (1440/768/390), mobiel stapelt netjes.
  • Resultaat-galerij i.p.v. een voor/na-slider op de dienstpagina (klantkeuze): 3 losse resultaatfoto's met bijschrift per stap (aanslag weg, voegwerk hersteld, strak/verzorgd). Reden: een echte voor/na-slider eist twee exact uitgelijnde foto's van dezelfde gevel, wat de klant zelden heeft. Werkt met losse werkfoto's.
  • components/BeforeAfter.astro = herbruikbare voor/na-sleepvergelijker (range-input → sleep + toetsenbord, :has()-focusring, "voor"/"na"-labels). Nergens geïmporteerd (projecten geschrapt, §7); bewaard als herbruikbaar component.
  • Alle 5 slugs ingevuld in DIENSTEN: gevelreiniging, voegwerk, dakreiniging-coating, schoorsteen-voegwerk, algemeen-reinigen. Elk: dienst-hero (+4,9-regel) + intro/checklist + resultaat-galerij + mid-page CTA-band + aanpak-stappen + dienst-FAQ. Dak gebruikt de 3 echte dakfoto's als narratief (mos → reinigen op hoogte → schoon, kop "Van mos tot een schoon dak"). Galerijen mixen de echte werk-*-foto's met dienst/hero-placeholders.
  • Smoke-test: alle 5 slugs → 200 (juiste H1), onbekend → 404. Per nieuwe pagina: axe clean, geen overflow (1440/768/390). astro check 0 errors, build OK.
  • pages/diensten/index.astro (overzicht, 5 dienstkaarten met "Bekijk" → detail) gebouwd; nav-link "Diensten" werkt nu, breadcrumb-crumb "Diensten" hersteld op de detailpagina's. 200, axe clean, geen overflow.
  • Image-fallback per slug bestaat al (/images/dienst-<slug>.webp). Echte projectfoto's vervangen de placeholders zodra de klant ze levert.
  • /impeccable critique op /diensten/gevelreiniging → 33/40 (sterk), detector [] (0 slop-patronen), cognitive load laag. Fixes doorgevoerd: P0 alle CTA-knoppen (header/sticky/drawer) → #contact i.p.v. /contact-404 (globaal in Base.astro); P1 4,9-Google-trust-regel in de dienst-hero + mid-page CTA-band ("Klaar om je gevel weer als nieuw te zien?") na de galerij als desktop-snelkoppeling naar het formulier; P2 dode breadcrumb-crumb "Diensten" verwijderd tot /diensten bestaat; contrast-fix stap-nummers cyaan → diep blauw (--c-action-deep, cyaan op --soft zakte onder WCAG). Galerij bewust gelaten (klantkeuze, foto's later). FAQ bewust plat gehouden (homepage-FAQ is óók plat; accordion zou juist inconsistent zijn). Na de fixes: axe 0, geen overflow (1440/768/390), build OK.

§7 — Projecten (before/after) ❌ GESCHRAPT (klantverzoek)

  • Projecten-pagina geschrapt op klantverzoek: uit de nav (Base.astro) en de homepage-link ("Alle projecten") verwijderd. BeforeAfter.astro blijft als ongebruikt herbruikbaar component staan (nergens geimporteerd, dus niet in de build).

§8 — Contact / Over-ons / Werkgebied 🟡 DEELS (contact + over-ons af)

  • pages/contact.astro (hidePrefooter + NAW + formulier, sectie id="contact"). Contactblok = Bel ons / Mail ons / Adres (Werkgebied + Bereikbaar op klantverzoek verwijderd) + 4,9-regel links, formulier-kaart rechts; e-mail op één regel (1-koloms). 200, axe clean, geen overflow.
  • pages/over-ons.astro (hero + 4,9-regel + verhaal met werkfoto + 4 waarden + werkgebied-pills). 200, axe clean, geen overflow.
  • Hoofd-CTA's (header/sticky/drawer) → /contact (was interim #contact); sticky-CTA verborgen wanneer hidePrefooter (dus op /contact). Naam-veld-placeholder in alle formulieren: "Naam" (was "Voor- en achternaam").
  • Form end-to-end testen (valid / honeypot / time-trap / 422) — na Mailtrap (§12).
  • pages/werkgebied.astro (optioneel, lokale SEO) — nog te bepalen.
  • Branded 404 bestaat nu via de catch-all [...slug].astro (onbekende route → nette 404-pagina met CTA i.p.v. blanco). Losse 404.astro daardoor optioneel.
  • public/robots.txt (Allow /, Disallow /_emdash/ + /api/, Sitemap-regel). Live geverifieerd.
  • src/pages/sitemap.xml.ts = vaste code-routes + gepubliceerde paginas + locaties (live query, noindex uitgesloten; CMS-fout → vaste routes blijven). Overschrijft emdash's eigen /sitemap.xml (die gebruikt /collectie/slug, fout voor ons root-URL-model) — user-route in src/pages/ wint (build-warning is verwacht/onschadelijk). Live geverifieerd: bevat /maastricht uit CMS + /privacy//voorwaarden.
  • site: "https://helvoirtgevelreiniging.nl" in astro.config.mjs → canonical + sitemap-base wijzen naar het echte domein (ook vóór koppeling).
  • voorwaarden.astro = Algemene voorwaarden (17-06-2026: herschreven o.b.v. zusterbedrijf onderhoudsbedrijfvanhelvoirt.nl, 10 artikelen; klant juridisch laten controleren) + in het CMS (singleton voorwaarden, artikelen-repeater, 0012_voorwaarden.sql, sentinel-geverifieerd). Privacyverklaring op klantverzoek VERWIJDERD (pagina + footer-link + sitemap; /privacy → 404). NB: voor de AVG is een privacyverklaring bij een contactformulier doorgaans wél aan te raden.
  • JSON-LD HomeAndConstructionBusiness (LocalBusiness-subtype) site-breed in Base.astro: naam/desc/url/image/telefoon/e-mail/adres (gesplitst) + areaServed + aggregateRating (4,9) + optioneel kvk/btw/sameAs uit site_settings. Live geverifieerd (rendert met CMS-data).
  • GTM-renderblok in Base.astro (head-script + body-noscript), leest site_settings.gtm; leeg = niets geïnjecteerd (geverifieerd). + theme-color + twitter-cards + og:image-default.
  • Gebrande 1200×630 OG-image (public/images/og-default.webp via scripts/make-og.py: schone gevel + petrol-gradient + wordmark + 4,9-Google) + og:image:width/height/type in Base.astro. Live (200, image/webp). + apple-touch-icon.png (180×180 H-monogram) + <link>.
  • Service + BreadcrumbList JSON-LD op de 5 dienst-detailpagina's (diensten/[slug].astro, matcht het zichtbare kruimelpad). 3 ld+json-blokken per dienstpagina (LocalBusiness + Service + Breadcrumb). Live geverifieerd.
  • Resterend (optioneel): FAQPage JSON-LD (wacht op echte FAQ-teksten — placeholder nu, schema zou dun zijn), self-hosted fonts (Archivo Expanded niet via css2, zie §10/notitie), Lighthouse/CWV-meting (keyless PSI = 429, eigen PSI-key nodig).

§10 — Beeld-pipeline 🟡 DEELS

  • nano-banana placeholders: hero-after/before.webp, hero-gevel.webp, 5× dienst-*.webp; echte input → werk-1..6.webp; 768/1280/1920-varianten + IMG_WIDTHS/srcsetFor bedraad. In gebruik: dienst-hero's (dienst-*), homepage-hero + galerijen (werk-*), dienst-resultaat-galerijen + over-ons (hero-after/gevel). Ongebruikt: hero-before.webp (alleen via het geparkeerde BeforeAfter.astro).
  • Echte foto's van klant verwerken → placeholders vervangen; IMG_WIDTHS bijwerken.

§11 — Cloudflare provisioning + deploy ✅ GEDAAN (preview)

  • wrangler d1 create helvoirt-db → database_id 0fb83f17-f91d-4910-ac8c-80f00569ce35 in wrangler.jsonc; r2 bucket create helvoirt-media; KV SESSION auto-provisioned.
  • astro build + wrangler deploy → https://helvoirt-gevelreiniging.mike-197.workers.dev (alle marketing-pagina's 200 op fallbacks; onbekend → 404; /_emdash/admin → setup). Laatste deploy: version ff725970.
  • Remote D1 seeden (dump/restore per tabel, NIET hele DB) + setup-wizard remote — hangt af van §3 (seed.json).

§12 — Domein + Mail + CMS-login 🟡 CODE KLAAR, EXTERNE STAPPEN OPEN (17-06-2026)

  • Mail-code af (patroon uit exterios/behangerbrabant): src/lib/smtp.ts (authenticated SMTP over cloudflare:sockets, poort 465) + api/contact.ts slaat nu op in D1 contact_submissions (migratie 0001, toegepast op remote D1) + stuurt 2 mails (melding-klant + autoreply) via Promise.allSettled. Mailtrap geschrapt → eigen mailbox-SMTP (besluit conform newsite.md §6). Live geverifieerd: POST → 200, D1-rij opgeslagen (sentinel getest + opgeruimd). Mail is no-op tot secret gezet is.
  • CMS-login-code af: src/plugins/smtp-email.ts (email:deliver-provider voor magic-link) + geregistreerd in astro.config.mjs (plugins:[…], absoluut pad/forward-slashes). + src/cloudflare-env.d.ts (env/sockets types) + src/middleware.ts (http→https + www→apex 301, no-op op *.workers.dev).
  • MAIL WERKT (17-06-2026, deploy cb425dc4). CONTACT_SMTP_PASS secret gezet (bash printf). CONTACT_SMTP_HOST=srv8.dewebsmid.nl in wrangler.jsonc vars — mailbox draait op srv8 (ontdekt via /webmail→roundcube-redirect); mail.helvoirtgevelreiniging.nl is NXDOMAIN in Cloudflare-DNS. Live getest: beide mails verstuurd, alle paden (valid/422/honeypot/time-trap) OK, testrijen opgeruimd. Magic-link werkt nu ook (zelfde SMTP).
  • Mail-DNS in Cloudflare (collega; mijn token mag geen DNS, 403): A mail→srv8-IP (185.84.143.157, DNS-only), MX @→mail.helvoirtgevelreiniging.nl, SPF, DKIM (key uit DirectAdmin), DMARC. Nodig voor INKOMENDE mail naar info@ van externen + afleverbaarheid autoreply. Outbound form-mail werkt al zonder.
  • CMS-gebruiker(s) SEO-partij in users aanmaken (SQL, §6b newsite.md) — e-mailadres(sen) opvragen.
  • DOMEIN LIVE (17-06-2026, deploy 7ed8f33b). Zone helvoirtgevelreiniging.nl actief op Cloudflare (NS cesar/priscilla.ns.cloudflare.com). Custom domain (apex + www) gekoppeld via wrangler.jsonc routes + rebuild. Site live op https://helvoirtgevelreiniging.nl (alle pagina's 200, canonical/sitemap = echte domein, 404 werkt). www→apex + http→https = 301 (middleware). *.workers.dev is uitgeschakeld (custom-domain-only; geen duplicate content). NS-blokkade was: registrar wees nog naar hosted.nl i.p.v. Cloudflare → collega heeft NS omgezet.
  • Mail-DNS (MX/SPF/DKIM/DMARC) — wacht op mailbox (collega) + DNS-edit-token/dashboard (OAuth-token mag geen zone-settings/DNS, gaf 403). HTTPS-hardening (Always Use HTTPS + HSTS) = optioneel via dashboard; redirects werken al via middleware, SSL-cert actief (Workers custom domain → SSL-modus n.v.t.).
  • Echte NAW/reviews/werkgebied/FAQ + GTM-ID in site_settings; echte foto's.

§13 — QA & oplevering 🟡 DEELS

  • /impeccable audit (hele site) = 17/20 (Good). Fixes doorgevoerd + live (version ff725970): tap-doelen ≥44px (telefoon/e-mail prefooter+contact, logo, skip-link), scrim-kleuren → tokens, dienst-kaarttitels → <h3>, eerste-fold-beelden eager+fetchpriority, accordion-reflow contain: layout. axe 0 op alle 9 pagina's, geen overflow (1440/768/390), astro check 0, detector [].
  • Lighthouse/CWV (mobile+desktop) meten, formulier end-to-end (na Mailtrap §12), git-remote + eerste commit/push.

Gedaan (referentie)

  • Input gelezen (Content.docx + 6 werkfoto's), referentiesite geanalyseerd, architecture.md/todo.md/CLAUDE.md opgesteld en bijgehouden.