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.
En ligne sur fitindebuurt.nl · Row-level security sur tout ce qui est privé · Next.js 16, React 19, Supabase
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é.
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.
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.
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.
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.
| Couche | Choix |
|---|---|
| App | Next.js 16 (App Router), React 19, TypeScript strict |
| Style | Tailwind CSS 4, composants maison, interface entièrement néerlandaise |
| Données + auth | Supabase (Postgres, auth, storage), région Francfort |
| Validation | Zod, des deux côtés et sur les variables d’environnement |
| Resend, cinq e-mails transactionnels avec une mise en page | |
| @react-pdf/renderer, factures dans Supabase Storage | |
| Hébergement | Vercel, wildcard *.fitindebuurt.nl, tâches cron, preview par push |
| Secrets | Doppler ; aucune clé dans le repo |
| Monitoring | Sentry, 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.
| Quoi | Ampleur |
|---|---|
| Code | 265 fichiers, ± 33 000 lignes de TypeScript |
| Base de données | 27 migrations, 20 tables, RLS sur tout ce qui est privé |
| Tests unitaires | 412, dans 37 fichiers |
| Tests navigateur (Playwright) | 52, dans 9 fichiers |
| Modèles et blocs | 8 modèles, 12 blocs réutilisables |
| Commits | 94, 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.
- 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.







