SnoekByte LogoSnoekByte
Case study · Live in productie
Leadplatform voor lokale sportstudio’s

Van leeg scherm naar de eerste klant live — in acht dagen.

Fit in de Buurt is een leadplatform voor lokale sportstudio’s: landingspagina’s die converteren, een portaal om gebelde leads op te volgen, een gids op postcode en de facturatie eromheen. Ontwerp, bouw, database, hosting en beheer — solo gebouwd.

Bekijk het live platform
Rol
Ontwerp, bouw, database, hosting & beheer (solo)
Periode
Augustus 2026 · 8 dagen tot eerste klant live
Stack
Next.js 16 · React 19 · TypeScript · Tailwind 4 · Supabase · Resend · Vercel
Live
fitindebuurt.nl

Live op fitindebuurt.nl · Row-level security op alles wat privé is · Next.js 16, React 19, Supabase

8
dagen tot de eerste klant live
±33k
regels TypeScript (265 bestanden)
464
geautomatiseerde testen
Live
eerste echte klant in productie

Het probleem

Een kleine sportstudio adverteert lokaal, stuurt mensen naar haar Instagram of naar een algemene website, en verliest ze daar. Wie wél reageert komt binnen via een DM, een mailtje of een appje; de eigenaar houdt in haar hoofd bij wie ze nog moet terugbellen. Twee dagen later is de helft vergeten en is de interesse weg.

De drie dingen die daarvoor bestaan lossen elk maar een stukje op. Een website-bouwer levert een pagina zonder opvolging. Een CRM is te zwaar voor iemand met een handvol leads per maand. Een linktree geeft geen enkel inzicht. En alles wat wél werkt, zet de persoonsgegevens van haar klanten op een plek waar zij geen zeggenschap over heeft.

Wat het geworden is

Eén platform dat drie soorten bezoekers tegelijk bedient — de sporter, de studio en de beheerder.

Voor de sporter

De homepage is een gids: vul je postcode in en zie welke studio’s, sportscholen, salons en praktijken het dichtst bij je zitten, met afstand, ervaringen en een knop naar het boekingssysteem of de leadpagina van die studio. Zonder cookies, zonder account, zonder dat we opslaan waar iemand naar zoekt.

Voor de studio

Een eigen leadpagina op naam.fitindebuurt.nl én fitindebuurt.nl/naam, gebouwd uit blokken en afgestemd op haar huisstijl. Wie zijn nummer achterlaat staat meteen in haar portaal, met een mail erbij. In dat portaal staat één lijst: wie moet ik bellen. Per lead houdt het systeem belpogingen bij, kun je met een notitie snoozen, en markeer je hem als gewonnen of weggedrukt.

Voor de beheerder

Een editor waarin ik een klantpagina opbouw en publiceer, statistiek per versie van die pagina, en facturatie met abonnementen, nummering, PDF’s en een nachtelijke cronjob.

Er staat nergens een belknop of een tel:-link. Dat is de hele opzet: een bezoeker laat zijn gegevens achter en de studio belt terug. Zo weet de eigenaar wie er geïnteresseerd was, ook als het gesprek niet meteen lukt.

Vier keuzes die het project bepalen

Beslissingen die door de hele codebase heen doorwerken — en het verschil maken tussen af en afgeraffeld.

1

Scheiding tussen klanten zit in de database, niet in de interface

Elke pagina heeft één eigenaar. Row-level security in Postgres bepaalt wie welke rij ziet; de app kan het niet omzeilen, ik ook niet. Er draait een testsuite die met echte inloggegevens van twee eigenaren probeert om elkaars leads te lezen, te wijzigen en te verwijderen. Zonder die groene test gaat er niets live.

2

De inhoud van een pagina staat op twee plekken, met opzet

Het sjabloon is een getypt bestand dat afsluit met satisfies LandingPagina — mist er een tekst, dan faalt de build. Wat ik in de editor aanpas komt als overlay in de database en valt bij het renderen over het sjabloon heen. Zo houd ik de garanties van de compiler én kan ik teksten wijzigen zonder te deployen.

3

Publiceren gaat onder een naam, cijfers horen bij een versie

Elke publicatie is een onveranderlijke rij in pagina_versies. De dagtellers onthouden per versie wat er geteld is, dus ik kan zien wat "Kortere hero" opleverde tegenover "Zomeractie", en een oude versie met één klik terugzetten als concept.

4

Alles in het Nederlands, tot in de database

Kolommen heten belpogingen, snooze_tot, weggedrukt_op. Dat leest voor de klant en voor mij hetzelfde, en het houdt de afstand klein tussen wat we in een gesprek zeggen en wat er in de code staat.

Drie onderdelen om uit te lichten

De plekken waar de meeste denk- en bouwtijd in ging.

De pagina-editor

Links een voorvertoning die meetypt, rechts de pagina van boven naar beneden: per blok één paneel met álles wat erbij hoort. Teksten pas je ook in de voorvertoning zelf aan: aanwijzen, klikken, typen — een bezoeker krijgt exact dezelfde HTML. De accentkleur staat in één CSS-variabele; met color-mix volgen daar vijf tinten uit, zodat elk blok bij elke huisstijl klopt.

De gids op postcode

Postcodes gaan naar de Locatieserver van het Kadaster (PDOK): open, gratis, geen sleutel. Wat terugkomt bewaren we in een eigen tabel, dus een tweede bezoeker met dezelfde postcode kost geen verzoek meer. Postgres rekent de afstand uit en geeft alleen studio’s terug die aan alle voorwaarden voldoen. Van de bezoeker bewaren we niets: geen cookie, geen IP.

Facturatie

Abonnementen per klant, automatische concepten uit een nachtelijke run, een factuurnummerreeks die niet mag springen, bedragen in centen, btw per regel en een PDF in de opslag. Een verstuurde factuur is bevroren: databasetriggers weigeren elke wijziging, omdat een verzonden factuur zeven jaar hetzelfde hoort te blijven.

Zoiets nodig voor jouw bedrijf?

Hoe het in elkaar zit

Eén Next.js-project bedient vier soorten bezoekers, gescheiden met route-groepen: de gids, de marketingpagina’s, de klantlandingspagina’s en de ingelogde kant. Subdomeinen lopen via proxy.ts, ververst de Supabase-sessie en sluit alles achter de inlog af.

LaagKeuze
AppNext.js 16 (App Router), React 19, TypeScript strict
StylingTailwind CSS 4, eigen componenten, volledig Nederlandse interface
Data + authSupabase (Postgres, auth, storage), regio Frankfurt
ValidatieZod, aan beide kanten en op de omgevingsvariabelen
MailResend, vijf transactionele mails met één opmaak
PDF@react-pdf/renderer, facturen in Supabase Storage
HostingVercel, wildcard *.fitindebuurt.nl, cronjobs, previews per push
SecretsDoppler; geen sleutel in de repo
MonitoringSentry, alleen in productie
  • Twee nachtelijke taken op Vercel achter een gedeeld geheim: 06:00 facturatie, 04:00 opruimen van verlopen aanvragen.
  • Geen namen, telefoonnummers of e-mailadressen in logs of foutmeldingen, geen IP-adressen in de statistiek.
  • Leads uitsluitend server-side wegschrijven — de browser heeft geen schrijfrecht op de database.
  • Een gesnoozede lead komt vanzelf terug: snooze is geen status maar een datum, geen achtergrondtaak die stil kan staan.

Kwaliteit en werkwijze

Eén opdracht is de poort: npm run check draait typecheck, ESLint zonder waarschuwingen, Prettier, alle unittesten en een volledige build. Wat daar niet doorheen komt, wordt niet als af gemeld. Husky en lint-staged doen hetzelfde bij elke commit, GitHub Actions herhaalt het op elke push.

WatOmvang
Code265 bestanden, ± 33.000 regels TypeScript
Database27 migraties, 20 tabellen, RLS op alles wat privé is
Unittesten412, in 37 bestanden
Browsertesten (Playwright)52, in 9 bestanden
Sjablonen en blokken8 sjablonen, 12 herbruikbare blokken
Commits94, in acht dagen

Browsertesten doen wat een mens doet

Inloggen, een lead binnenhalen, hem snoozen, een blok uitzetten in de editor, publiceren en controleren dat de openbare pagina meeverandert. Precies daar zitten de fouten die typecheck en unittesten niet zien.

Vindbaarheid ligt op vier plekken vast

robots.ts (met drieëntwintig AI-crawlers bij naam), een sitemap die de klantpagina’s uit de database haalt, een gegenereerde /llms.txt met de hele site in markdown, en gestructureerde gegevens per paginasoort — nooit meer dan op de pagina zelf staat.

Gebouwd met AI-assistentie, maar niet op de gok

Elk onderdeel is eerst uitgeschreven als plan in de repo — waarom, welke fasen, welke afwegingen — en daarna gebouwd. Vaste afspraken staan in een regelbestand dat elke sessie meeleest. De tests, de types en de row-level security zijn de rem.

Waar het nu staat

De site draait, de eerste echte klant staat live en in de gids, en de facturatie staat klaar voor haar eerste abonnement.

Op de rol
  • Kaartweergave en eigen overzichtspagina’s per soort en plaats
  • Geverifieerde Google-reviews
  • Exporteren van leads
  • Tweestapsverificatie
  • A/B-testen over twee gepubliceerde versies tegelijk

Van idee naar live platform

Maatwerk dat blijft staan.

Een leadplatform, een portaal, een webshop of een interne tool — gebouwd met tests, types en privacy als fundament. Laten we bespreken wat je nodig hebt.

Contact