Plataforma de gestió de perruqueries multi-inquilí
NovaHair és una solució de monorepo de grau empresarial a punt per a producció per a la gestió de salons de bellesa. Combina un panell d’administració PWA (Progressive Web App), un giny de reserves integrable i pàgines de destinació personalitzables en un ecosistema cohesionat i amb seguretat de tipus.
Construït amb React 19, TanStack Router, TanStack Query i seguint els principis de la Clean Architecture, aquest projecte demostra una gestió avançada de monorepos, optimització del rendiment i una arquitectura frontend escalable.

Resum del projecte
NovaHair és una plataforma SaaS multi-inquilí dissenyada per agilitzar les operacions de les perruqueries. El sistema consta de tres aplicacions principals:
-
- Panell d’Administració - PWA completa per a la gestió del saló
-
- Sistema de Reserves - Giny integrable per a les cites dels clients
-
- Pàgines de Destinació - Plantilles personalitzables per a webs de perruqueries
Totes les aplicacions comparteixen un sistema de disseny i una lògica de negoci comuns a través d’un monorepo de pnpm workspaces, assegurant la consistència i reduint la duplicació de codi en un ~40%.
Èxits destacats
- Temps de càrrega inferiors al segon - S’ha aconseguit un FCP < 800ms mitjançant code splitting i SSR
- Reducció del 40% del codi - Els paquets compartits eliminen la duplicació entre aplicacions
- Sincronització en temps real - Les actualitzacions optimistes amb TanStack Query redueixen la latència percebuda en un 60%
- Suport i18n - Localització completa en anglès/espanyol amb detecció automàtica
- Capacitats PWA - Panell d’administració offline-first amb memòria cau de service worker
- Interfície accessible - Compliment de WCAG 2.1 AA amb primitius de Radix UI
- Seguretat de tipus - TypeScript d’extrem a extrem amb una cobertura del 100% en paquets compartits
Arquitectura tècnica
Estructura del monorepo
novahair/
├── apps/
│ ├── admin/ # Panell d'administració PWA (Port 3000)
│ ├── booking/ # Giny de reserves (Port 3001)
│ └── landings/ # Plantilles de landing (Port 3002)
└── packages/
├── ui/ # Llibreria de components compartits (+50 components)
├── client/ # Client API amb seguretat de tipus i React Query
├── utils/ # Utilitats i hooks compartits
└── book-app/ # Lògica de reserves reutilitzable
Per què aquesta arquitectura?
- Dependències centralitzades: Font única de veritat per a les versions
- Recàrrega en calent instantània: Els canvis en els paquets compartits es reflecteixen immediatament
- Seguretat de tipus: TypeScript funciona sense problemes entre els límits dels paquets
- Construccions optimitzades: L’emmagatzematge de pnpm redueix el temps d’instal·lació per 2
Demo en viu

Característiques principals
Panell d’Administració (PWA)
- Dashboard en temps real - Mètriques d’ingressos, cites i rendiment del personal
- Gestió d’equips - Planificació del personal amb detecció de conflictes
- Sistema de cites - Calendari arrossegable amb reserves recurrents
- Catàleg de serveis - Gestió dinàmica de preus i durades
- Suport offline - Memòria cau de service worker amb sincronització en segon pla
- Analítiques - Rangs de dates personalitzats amb visualització de tendències
Giny de Reserves
- Integrable - Funciona com a iframe o component React
- Flux de diversos passos - Servei → Personal → Hora → Confirmació
- Responsiu - Disseny mobile-first amb gestos tàctils
- Multi-inquilí - Un sol desplegament serveix a múltiples perruqueries
- Sessions anònimes - Emmagatzematge local per a reserves de convidats
- UI optimista - Feedback instantani amb reversió en cas d’error
Pàgines de Destinació
- Personalitzables - Sistema de plantilles per a la consistència de marca
- Rendiment - Animacions GSAP amb garantia de 60fps
- Efectes parallax - Desplaçament suau amb Lenis
- Optimitzat per a SEO - SSR amb metaetiquetes i sitemap
- Enllaços profunds - Pre-emplenat de reserves des de paràmetres de l’URL
Aprofundiment tècnic
Decisions d’arquitectura i optimitzacions de rendiment
Stack tecnològic
| Capa | Tecnologia | Propòsit |
|---|---|---|
| Framework | React 19 | Últimes funcionalitats (use, optimitzacions del compilador) |
| Enrutament | TanStack Router | Enrutament amb seguretat de tipus i suport SSR |
| Estat | TanStack Query v5 | Estat del servidor amb actualitzacions optimistes |
| Build | Vite 7 | HMR ultraràpid (<50ms) |
| Monorepo | pnpm workspaces | Gestió eficient de dependències |
| Estils | Tailwind CSS 4 | Utilitats amb compilació JIT |
| Primitius UI | Radix UI | Components accessibles i sense estils |
| Animacions | GSAP + Framer Motion | Animacions d’alt rendiment |
| i18n | i18next | Canvi de traducció en temps d’execució |
| Proves | Vitest + Testing Library | Proves unitàries i d’integració ràpides |
| Linting | Biome | 100 vegades més ràpid que ESLint |
| Seguretat de tipus | TypeScript 5.7 | Mode estricte sense any implícits |
| SSR | Nitro | Renderitzat de servidor universal |
Optimitzacions de rendiment
1. Estratègia de Code Splitting
// Divisió basada en rutes amb TanStack Router
defaultPreload: "intent" // Precarrega en passar el ratolí o focus
defaultViewTransition: true // Transicions de vista nativesResultat: Paquet inicial reduït de 450KB a 180KB (-60%)
2. Estratègia de memòria cau PWA
// Configuració del service worker
runtimeCaching: [
{
urlPattern: /^https:\/\/api\.gerardmartinez\.es\/api\/.*/i,
handler: "NetworkFirst",
options: {
cacheName: "api-cache",
expiration: { maxAgeSeconds: 86400 }, // 24h
networkTimeoutSeconds: 10
}
}
]Resultat: taxa d’encert de memòria cau del 90% per a les crides a l’API, funcionalitat offline
3. Optimitzacions de React Query
// Actualitzacions optimistes per a un feedback instantani de la UI
const { create } = useAppointmentActions(tenantId);
create.mutate(data, {
onMutate: async (newAppointment) => {
await queryClient.cancelQueries(['appointments']);
const previous = queryClient.getQueryData(['appointments']);
queryClient.setQueryData(['appointments'], old => [...old, newAppointment]);
return { previous };
},
onError: (err, variables, context) => {
queryClient.setQueryData(['appointments'], context.previous);
}
});Resultat: Latència percebuda reduïda en un 60%
4. Optimització d’imatges
- Format WebP amb alternatives
- Càrrega mandrosa (Lazy loading) amb Intersection Observer
- Imatges responsives amb
srcset
Resultat: LCP millorat de 2.1s a 0.8s
Implementació de l’Arquitectura Neta
Cada funcionalitat segueix una arquitectura per capes:
features/dashboard/
├── domain/ # Lògica de negoci (funcions pures)
│ └── metrics.ts
├── infra/ # Dependències externes
│ ├── metrics-calculator.ts
│ └── date-range-calculator.ts
├── hooks/ # Integració amb React
│ └── use-dashboard-metrics.ts
└── ui/ # Components de presentació
├── dashboard-view.tsx
└── metric-card.tsxBeneficis:
- ✅ Lògica de negoci testable (100% de cobertura en la capa de domini)
- ✅ Fàcil d’intercanviar implementacions (ex. client API)
- ✅ Clara separació de conceptes
- ✅ Reutilitzable entre aplicacions
Client API amb seguretat de tipus
// Client unificat amb inferència de tipus completa
import { client } from '@novahair/client';
const { useAppointments, useAppointmentActions } = client.appointments;
// Totalment tipat, l'autocompletat funciona a tot arreu
const { appointments, isLoading } = useAppointments(tenantId);
const { create, update, delete: deleteAppointment } = useAppointmentActions(tenantId);
// TypeScript coneix la forma exacta de la cita
create.mutate({
serviceId: string,
staffId: string,
customer: { name, email, phone },
startsAt: Date,
notes?: string
});Característiques:
- Zero sobrecàrrega en temps d’execució (els tipus s’eliminen en la construcció)
- Refetch automàtic en mutacions
- Actualitzacions optimistes amb reversió
- Suport de sessió anònima per a reserves de convidats
Beneficis del monorepo
Paquet d’UI compartit (@novahair/ui)
- +50 components (Botó, Entrada, Diàleg, Taula, etc.)
- Tokens de disseny consistents
- Primitius de Radix UI per a l’accessibilitat
- Exportacions tree-shakeable
Paquet de Client compartit (@novahair/client)
- Client API amb seguretat de tipus
- Integració amb React Query
- Memòria cau i invalidació automàtica
- Cobertura de proves del 100%
Paquet d’Utilitats compartit (@novahair/utils)
- Hooks personalitzats (useMobile, useDebounce, etc.)
- Configuració d’i18n
- Utilitats comunes (cn, formatadors, validadors)
Impacte:
- Reducció del 40% en la duplicació de codi
- Font única de veritat per a la lògica de negoci
- Propagació instantània dels canvis entre aplicacions
- Estratègia de proves unificada
Internacionalització
// Detecció automàtica d'idioma amb persistència de galetes
i18n.use(LanguageDetector).init({
supportedLngs: ['en', 'es'],
fallbackLng: 'es',
detection: {
order: ['cookie'],
lookupCookie: 'novahair_i18n',
caches: ['cookie'],
cookieMinutes: 525600 // 1 any
}
});
// Traduccions amb seguretat de tipus
import { t } from 'i18next';
<h1>{t('welcome_message')}</h1> // TypeScript coneix totes les clausCaracterístiques:
- Canvi d’idioma en temps d’execució (sense recarregar)
- Fusió d’espais de noms (comú + específic de l’app)
- Pluralització i interpolació
- CLI per a l’extracció de claus que falten
Estratègia de proves
# Proves unitàries per a la lògica de negoci
pnpm test # Executa totes les proves
pnpm coverage # Genera l'informe de cobertura
# Proves d'integració per a les funcionalitats
pnpm test --watch # Mode watch per a TDDCobertura:
- Capa de domini: 100%
- Capa d’infraestructura: 85%
- Components d’UI: 70%
- Total: 82%
Eines:
- Vitest (20 vegades més ràpid que Jest)
- Testing Library (proves centrades en l’usuari)
- jsdom (simulació lleugera del DOM)
Construcció i desplegament
# Desenvolupament
pnpm dev # Totes les apps en paral·lel
pnpm dev:admin # Una sola app
# Producció
pnpm build # Construccions optimitzades
# Resultat: dist/ (llest per a SSR amb Nitro)Mètriques de construcció:
- Admin: 220KB gzipped (paquet inicial)
- Booking: 180KB gzipped
- Landing: 150KB gzipped
- Temps de construcció: ~12s (fred), ~2s (en memòria cau)
Desplegament:
- Vercel (recomanat) - Zero configuració
- Servidor Nitro per a SSR
- Desplegaments de vista prèvia automàtics
- Edge functions per a rutes API
Configuració local
# Clonar el repositori
git clone https://github.com/polgubau/novahair
cd novahair
# Instal·lar dependències (requereix pnpm)
pnpm install
# Iniciar totes les apps
pnpm dev
# O iniciar aplicacions individuals
pnpm dev:admin # http://localhost:3000
pnpm dev:booking # http://localhost:3001
pnpm dev:landing # http://localhost:3002Requisits:
- Node.js ≥ 20.0.0
- pnpm ≥ 9.0.0

Aprenentatges clau
1. Gestió de monorepos
Gestionar un monorepo amb múltiples aplicacions m’ha ensenyat la importància de:
- Límits de dependència: Prevenir dependències circulars entre paquets
- Orquestració de la construcció: Assegurar que els paquets es construeixin en l’ordre correcte
- Alineació de versions: Mantenir les dependències compartides sincronitzades
2. Rendiment a escala
Optimitzar el rendiment en tres aplicacions ha requerit:
- Code splitting estratègic: Divisió basada en rutes + components
- Estratègies de memòria cau: Memòria cau multicapa (navegador, service worker, React Query)
- Anàlisi de paquets: Auditories periòdiques per prevenir el creixement desmesurat
3. Seguretat de tipus
Aconseguir la seguretat de tipus d’extrem a extrem ha implicat:
- Tipus compartits: Font única de veritat per als models de domini
- Utilitats genèriques: Hooks i funcions amb seguretat de tipus reutilitzables
- TypeScript estricte: Sense dreceres, cobertura completa de tipus
4. Experiència del desenvolupador
Crear una gran DX ha requerit:
- Bucles de retroalimentació ràpids: HMR en <50ms, proves en <2s
- Documentació clara: Fitxers README per a cada paquet
- Eines automatitzades: Biome per a linting/formatat instantani
Reptes que he trobat en aquest projecte
NovaHair no va ser fàcil d’implementar, vaig trobar diversos passos que van requerir moltes hores per resoldre, com:
- Arquitectura de sistemes complexos - Un monorepo multi-aplicació amb paquets compartits va ser difícil de configurar, però la recompensa és increïble
- Optimització del rendiment - Temps de càrrega inferiors al segon amb memòria cau estratègica i utilitzant tecnologies frontend modernes
- Escriure codi mantenible - Arquitectura neta amb un 82% de cobertura de proves per assegurar una bona funcionalitat
- Triar les eines adequades - Stack modern amb tecnologies provades com React, TypeScript i solucions TanStack
- Documentar exhaustivament - Fitxers README clars i documentació en línia, ja que el treball es va dividir entre diversos col·laboradors
- Enviar codi a producció - Desplegat i funcionant en producció
Llicència
Aquest projecte és un producte comercial i no és de codi obert. Tots els drets reservats per l’autor.
Contacte
Creat per:
- Disseny i desenvolupament frontend: Pol Gubau Amores - polgubau.com
- Base de dades i desenvolupament backend: Gerard Martínez Alcocer - gerardmartinez.es
T’interessa parlar d’aquest projecte o de possibles oportunitats? No dubtis a posar-te en contacte amb mi!
Enllaços