De la pantalla en blanco al primer cliente en vivo — en ocho días.
Fit in de Buurt es una plataforma de leads para estudios deportivos locales: landing pages que convierten, un portal para dar seguimiento a los leads llamados, una guía por código postal y la facturación alrededor. Diseño, desarrollo, base de datos, hosting y operación — hecho en solitario.
En vivo en fitindebuurt.nl · Row-level security en todo lo privado · Next.js 16, React 19, Supabase
El problema
Un pequeño estudio deportivo se anuncia localmente, envía a la gente a su Instagram o a una web genérica, y la pierde ahí. Quien sí responde llega por un DM, un correo o un mensaje; la dueña recuerda en su cabeza a quién debe volver a llamar. Dos días después la mitad está olvidada y el interés se ha esfumado.
Las tres cosas que existen para esto resuelven cada una solo una parte. Un creador de webs entrega una página sin seguimiento. Un CRM es demasiado pesado para alguien con un puñado de leads al mes. Un linktree no da ningún dato. Y todo lo que sí funciona pone los datos personales de sus clientes en un lugar sobre el que ella no tiene control.
En qué se convirtió
Una plataforma que sirve a tres tipos de visitantes a la vez — el deportista, el estudio y el operador.
Para el deportista
La página de inicio es una guía: introduce tu código postal y ve qué estudios, gimnasios, salones y consultas están más cerca, con distancia, reseñas y un botón al sistema de reservas o a la página de leads. Sin cookies, sin cuenta, y sin que guardemos lo que alguien busca.
Para el estudio
Su propia página de leads en nombre.fitindebuurt.nl y fitindebuurt.nl/nombre, construida por bloques y ajustada a su identidad. Quien deja su número aterriza al instante en su portal, con un correo. Ese portal tiene una lista: a quién llamo. Por lead el sistema registra intentos de llamada, permite posponer con una nota, y marcar ganado o descartado.
Para el operador
Un editor donde construyo y publico una página de cliente, estadísticas por versión de esa página, y facturación con suscripciones, numeración, PDF y una tarea cron nocturna.
No hay en ningún sitio un botón de llamada ni un enlace tel:. Esa es toda la idea: un visitante deja sus datos y el estudio llama de vuelta. Así la dueña sabe quién estaba interesado, aunque la llamada no cuaje enseguida.
Cuatro decisiones que definen el proyecto
Decisiones que repercuten en todo el código — y marcan la diferencia entre terminado y hecho a medias.
La separación entre clientes está en la base de datos, no en la interfaz
Cada página tiene un dueño. El row-level security en Postgres decide quién ve qué fila; la app no puede saltárselo, yo tampoco. Una suite de pruebas usa las credenciales reales de dos dueños para intentar leer, modificar y borrar los leads del otro. Sin esa prueba en verde, nada sale a producción.
El contenido de una página vive en dos sitios, a propósito
La plantilla es un archivo tipado que termina en satisfies LandingPagina — si falta un texto, el build falla. Lo que cambio en el editor se guarda como overlay en la base y se superpone a la plantilla al renderizar. Así conservo las garantías del compilador y puedo cambiar textos sin desplegar.
Se publica bajo un nombre, las cifras pertenecen a una versión
Cada publicación es una fila inmutable en pagina_versies. Los contadores diarios recuerdan lo contado por versión, así veo qué dio "Hero más corto" frente a "Promo de verano", y restauro una versión antigua como borrador con un clic.
Todo en neerlandés, hasta la base de datos
Las columnas se llaman belpogingen, snooze_tot, weggedrukt_op. Se lee igual para el cliente y para mí, y mantiene corta la distancia entre lo que decimos en una conversación y lo que hay en el código.
Tres partes para destacar
Los lugares donde fue la mayor parte del tiempo de pensar y construir.
El editor de páginas
A la izquierda una vista previa en vivo, a la derecha la página de arriba abajo: un panel por bloque con todo lo suyo. Los textos se editan en la propia vista previa — señalar, clicar, escribir — y un visitante recibe exactamente el mismo HTML. El color de acento vive en una variable CSS; color-mix deriva cinco tonos, así cada bloque encaja con cualquier identidad.
La guía por código postal
Los códigos postales van al servidor de localización del Kadaster (PDOK): abierto, gratis, sin clave. Lo que vuelve se guarda en nuestra propia tabla, un segundo visitante con el mismo código postal no cuesta ninguna petición. Postgres calcula la distancia y solo devuelve estudios que cumplen todas las condiciones. Del visitante no guardamos nada: ni cookie, ni IP.
Facturación
Suscripciones por cliente, borradores automáticos de una ejecución nocturna, una serie de números de factura que no puede saltar, importes en céntimos, IVA por línea y un PDF en almacenamiento. Una factura enviada queda congelada: los triggers de base rechazan cualquier cambio, porque una factura enviada debe mantenerse igual siete años.
¿Necesitas algo así para tu empresa?
Cómo está montado
Un proyecto Next.js sirve a cuatro tipos de visitantes, separados con grupos de rutas: la guía, las páginas de marketing, las landing pages de clientes y la parte con sesión. Los subdominios pasan por proxy.ts, que refresca la sesión de Supabase y bloquea todo tras el login.
| Capa | Elección |
|---|---|
| App | Next.js 16 (App Router), React 19, TypeScript strict |
| Estilos | Tailwind CSS 4, componentes propios, interfaz totalmente en neerlandés |
| Datos + auth | Supabase (Postgres, auth, storage), región Fráncfort |
| Validación | Zod, en ambos lados y en las variables de entorno |
| Resend, cinco correos transaccionales con un diseño | |
| @react-pdf/renderer, facturas en Supabase Storage | |
| Hosting | Vercel, wildcard *.fitindebuurt.nl, tareas cron, preview por push |
| Secretos | Doppler; ninguna clave en el repo |
| Monitoring | Sentry, solo en producción |
- Dos tareas nocturnas en Vercel tras un secreto compartido: 06:00 facturación, 04:00 limpieza de solicitudes caducadas.
- Sin nombres, teléfonos ni correos en logs o mensajes de error, sin direcciones IP en las estadísticas.
- Los leads se escriben solo en el servidor — el navegador no tiene permiso de escritura en la base.
- Un lead pospuesto vuelve solo: snooze no es un estado sino una fecha, ninguna tarea en segundo plano que pueda pararse.
Calidad y método
Un comando es la puerta: npm run check ejecuta typecheck, ESLint sin advertencias, Prettier, todas las pruebas unitarias y un build completo. Lo que no pasa no se da por terminado. Husky y lint-staged hacen lo mismo en cada commit, GitHub Actions lo repite en cada push.
| Qué | Alcance |
|---|---|
| Código | 265 archivos, ± 33.000 líneas de TypeScript |
| Base de datos | 27 migraciones, 20 tablas, RLS en todo lo privado |
| Pruebas unitarias | 412, en 37 archivos |
| Pruebas de navegador (Playwright) | 52, en 9 archivos |
| Plantillas y bloques | 8 plantillas, 12 bloques reutilizables |
| Commits | 94, en ocho días |
Las pruebas de navegador hacen lo que hace una persona
Iniciar sesión, captar un lead, posponerlo, apagar un bloque en el editor, publicar y comprobar que la página pública también cambia. Ahí están justo los fallos que typecheck y las pruebas unitarias no ven.
La visibilidad está fijada en cuatro sitios
robots.ts (nombrando veintitrés crawlers de IA), un sitemap que saca las páginas de cliente de la base, un /llms.txt generado con toda la web en markdown, y datos estructurados por tipo de página — nunca más de lo que está en la propia página.
Construido con asistencia de IA, pero no a ciegas
Cada parte se escribió primero como un plan en el repo — por qué, qué fases, qué compromisos — y luego se construyó. Los acuerdos fijos viven en un archivo de reglas que cada sesión lee. Las pruebas, los tipos y el row-level security son el freno.
Dónde está ahora
El sitio funciona, el primer cliente real está en vivo y en la guía, y la facturación está lista para su primera suscripción.
- Vista de mapa y páginas de resumen por tipo y lugar
- Reseñas de Google verificadas
- Exportación de leads
- Autenticación de dos factores
- Pruebas A/B sobre dos versiones publicadas a la vez
De la idea a la plataforma en vivo
Software a medida que perdura.
Una plataforma de leads, un portal, una tienda o una herramienta interna — construido con pruebas, tipos y privacidad como base. Hablemos de lo que necesitas.







