SnoekByte LogoSnoekByte
SnoekByte LogoSnoekByte
Étude de cas · En production
Plateforme de leads pour studios de sport locaux

De l’écran vide au premier client en ligne — en huit jours.

Fit in de Buurt est une plateforme de leads pour studios de sport locaux : des landing pages qui convertissent, un portail pour suivre les leads rappelés, un guide par code postal et la facturation autour. Conception, développement, base de données, hébergement et exploitation — réalisé en solo.

Voir la plateforme en ligne
Rôle
Conception, développement, base de données, hébergement & exploitation (solo)
Période
Août 2026 · 8 jours jusqu’au premier client
Stack
Next.js 16 · React 19 · TypeScript · Tailwind 4 · Supabase · Resend · Vercel
En ligne
fitindebuurt.nl

En ligne sur fitindebuurt.nl · Row-level security sur tout ce qui est privé · Next.js 16, React 19, Supabase

8
jours jusqu’au premier client
±33k
lignes de TypeScript (265 fichiers)
464
tests automatisés
Live
premier vrai client en production

Le problème

Un petit studio de sport fait de la publicité locale, envoie les gens sur son Instagram ou un site générique, et les perd là. Ceux qui répondent arrivent par un DM, un e-mail ou un message ; la propriétaire retient dans sa tête qui rappeler. Deux jours plus tard, la moitié est oubliée et l’intérêt a disparu.

Les trois choses qui existent pour cela ne résolvent chacune qu’une partie. Un constructeur de site livre une page sans suivi. Un CRM est trop lourd pour quelqu’un avec une poignée de leads par mois. Un linktree n’offre aucun aperçu. Et tout ce qui fonctionne place les données personnelles de ses clients à un endroit qu’elle ne contrôle pas.

Ce que c’est devenu

Une plateforme qui sert trois types de visiteurs à la fois — le sportif, le studio et l’exploitant.

Pour le sportif

La page d’accueil est un guide : saisissez votre code postal et voyez quels studios, salles, salons et cabinets sont les plus proches, avec distance, avis et un bouton vers le système de réservation ou la page de leads. Sans cookies, sans compte, sans que nous enregistrions ce que quelqu’un recherche.

Pour le studio

Sa propre page de leads sur nom.fitindebuurt.nl et fitindebuurt.nl/nom, construite en blocs et alignée sur son identité. Qui laisse son numéro atterrit aussitôt dans son portail, avec un e-mail. Ce portail a une liste : qui dois-je appeler. Par lead, le système suit les tentatives d’appel, permet de reporter avec une note, et de marquer gagné ou écarté.

Pour l’exploitant

Un éditeur où je construis et publie une page client, des statistiques par version de cette page, et la facturation avec abonnements, numérotation, PDF et une tâche cron nocturne.

Il n’y a nulle part de bouton d’appel ni de lien tel:. C’est tout le principe : un visiteur laisse ses coordonnées et le studio rappelle. Ainsi la propriétaire sait qui était intéressé, même si l’appel n’aboutit pas tout de suite.

Quatre choix qui définissent le projet

Des décisions qui se répercutent dans tout le code — et font la différence entre terminé et bâclé.

1

La séparation entre clients est dans la base de données, pas dans l’interface

Chaque page a un propriétaire. Le row-level security dans Postgres décide qui voit quelle ligne ; l’app ne peut pas le contourner, moi non plus. Une suite de tests utilise les vrais identifiants de deux propriétaires pour tenter de lire, modifier et supprimer les leads de l’autre. Sans ce test vert, rien ne part en production.

2

Le contenu d’une page vit à deux endroits, volontairement

Le modèle est un fichier typé qui se termine par satisfies LandingPagina — un texte manquant fait échouer le build. Ce que je modifie dans l’éditeur devient un overlay en base et se superpose au modèle au rendu. Je garde les garanties du compilateur et peux changer un texte sans déployer.

3

On publie sous un nom, les chiffres appartiennent à une version

Chaque publication est une ligne immuable dans pagina_versies. Les compteurs journaliers retiennent ce qui a été compté par version, je vois donc ce qu’a rapporté "Hero plus court" face à "Promo d’été", et je restaure une ancienne version en brouillon d’un clic.

4

Tout en néerlandais, jusque dans la base de données

Les colonnes s’appellent belpogingen, snooze_tot, weggedrukt_op. Cela se lit pareil pour le client et pour moi, et garde faible la distance entre ce qu’on dit en conversation et ce qu’il y a dans le code.

Trois éléments à mettre en avant

Les endroits où est passé le plus de temps de réflexion et de construction.

L’éditeur de page

À gauche un aperçu en direct, à droite la page de haut en bas : un panneau par bloc avec tout ce qui lui appartient. Les textes se modifient dans l’aperçu même — pointer, cliquer, taper — et un visiteur reçoit exactement le même HTML. La couleur d’accent tient dans une variable CSS ; color-mix en dérive cinq teintes, chaque bloc s’accorde donc à toute identité.

Le guide par code postal

Les codes postaux vont au serveur de localisation du Kadaster (PDOK) : ouvert, gratuit, sans clé. Le résultat est stocké dans notre propre table, un deuxième visiteur avec le même code postal ne coûte aucune requête. Postgres calcule la distance et ne renvoie que les studios qui remplissent toutes les conditions. Du visiteur, on ne garde rien : pas de cookie, pas d’IP.

Facturation

Abonnements par client, brouillons automatiques d’une exécution nocturne, une séquence de numéros de facture qui ne doit pas sauter, montants en centimes, TVA par ligne et un PDF en stockage. Une facture envoyée est gelée : des triggers de base refusent toute modification, car une facture envoyée doit rester identique sept ans.

Besoin de ce genre de chose pour votre entreprise ?

Comment c’est assemblé

Un projet Next.js sert quatre types de visiteurs, séparés par des groupes de routes : le guide, les pages marketing, les landing pages clients et la partie connectée. Les sous-domaines passent par proxy.ts, qui rafraîchit la session Supabase et verrouille tout derrière la connexion.

CoucheChoix
AppNext.js 16 (App Router), React 19, TypeScript strict
StyleTailwind CSS 4, composants maison, interface entièrement néerlandaise
Données + authSupabase (Postgres, auth, storage), région Francfort
ValidationZod, des deux côtés et sur les variables d’environnement
MailResend, cinq e-mails transactionnels avec une mise en page
PDF@react-pdf/renderer, factures dans Supabase Storage
HébergementVercel, wildcard *.fitindebuurt.nl, tâches cron, preview par push
SecretsDoppler ; aucune clé dans le repo
MonitoringSentry, en production uniquement
  • Deux tâches nocturnes sur Vercel derrière un secret partagé : 06:00 facturation, 04:00 nettoyage des demandes expirées.
  • Pas de noms, de numéros de téléphone ni d’adresses e-mail dans les logs ou messages d’erreur, pas d’adresses IP dans les statistiques.
  • Les leads sont écrits uniquement côté serveur — le navigateur n’a aucun droit d’écriture sur la base.
  • Un lead reporté revient de lui-même : snooze n’est pas un statut mais une date, aucune tâche de fond qui peut caler.

Qualité et méthode

Une commande est la porte : npm run check lance typecheck, ESLint sans avertissement, Prettier, tous les tests unitaires et un build complet. Ce qui ne passe pas n’est pas déclaré fini. Husky et lint-staged font pareil à chaque commit, GitHub Actions répète à chaque push.

QuoiAmpleur
Code265 fichiers, ± 33 000 lignes de TypeScript
Base de données27 migrations, 20 tables, RLS sur tout ce qui est privé
Tests unitaires412, dans 37 fichiers
Tests navigateur (Playwright)52, dans 9 fichiers
Modèles et blocs8 modèles, 12 blocs réutilisables
Commits94, en huit jours

Les tests navigateur font ce qu’un humain fait

Se connecter, capter un lead, le reporter, désactiver un bloc dans l’éditeur, publier et vérifier que la page publique change aussi. C’est exactement là que se trouvent les bugs que typecheck et tests unitaires ne voient pas.

La trouvabilité est fixée à quatre endroits

robots.ts (nommant vingt-trois crawlers IA), un sitemap qui tire les pages clients de la base, un /llms.txt généré avec tout le site en markdown, et des données structurées par type de page — jamais plus que ce qui figure sur la page.

Construit avec l’assistance de l’IA, mais pas au hasard

Chaque partie a d’abord été écrite comme un plan dans le repo — pourquoi, quelles phases, quels arbitrages — puis construite. Les règles fixes vivent dans un fichier lu à chaque session. Les tests, les types et le row-level security sont le frein.

Où ça en est

Le site tourne, le premier vrai client est en ligne et dans le guide, et la facturation est prête pour son premier abonnement.

À la feuille de route
  • Vue carte et pages de synthèse par type et lieu
  • Avis Google vérifiés
  • Export des leads
  • Authentification à deux facteurs
  • Tests A/B sur deux versions publiées à la fois

De l’idée à la plateforme en ligne

Du sur-mesure qui dure.

Une plateforme de leads, un portail, une boutique ou un outil interne — construit avec les tests, les types et la confidentialité pour fondation. Discutons de ce dont vous avez besoin.

Contact