← Back
NovaHair
web

NovaHair

Feb 12, 2025 → Sep 30, 2025

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.

Panell d'administració que mostra mètriques i analítiques
Panell d’administració amb mètriques i KPIs en temps real.

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:

    1. Panell d’Administració - PWA completa per a la gestió del saló
    1. Sistema de Reserves - Giny integrable per a les cites dels clients
    1. 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

🔗 novahair.polgubau.com

Interfície del flux de reserves
Flux de reserves de diversos passos amb disponibilitat en temps real.

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

CapaTecnologiaPropòsit
FrameworkReact 19Últimes funcionalitats (use, optimitzacions del compilador)
EnrutamentTanStack RouterEnrutament amb seguretat de tipus i suport SSR
EstatTanStack Query v5Estat del servidor amb actualitzacions optimistes
BuildVite 7HMR ultraràpid (<50ms)
Monorepopnpm workspacesGestió eficient de dependències
EstilsTailwind CSS 4Utilitats amb compilació JIT
Primitius UIRadix UIComponents accessibles i sense estils
AnimacionsGSAP + Framer MotionAnimacions d’alt rendiment
i18ni18nextCanvi de traducció en temps d’execució
ProvesVitest + Testing LibraryProves unitàries i d’integració ràpides
LintingBiome100 vegades més ràpid que ESLint
Seguretat de tipusTypeScript 5.7Mode estricte sense any implícits
SSRNitroRenderitzat 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 natives

Resultat: 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.tsx

Beneficis:

  • ✅ 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 claus

Caracterí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 TDD

Cobertura:

  • 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:3002

Requisits:

  • Node.js ≥ 20.0.0
  • pnpm ≥ 9.0.0

Dashboard de mètriques amb tendències d'ingressos i cites
Pàgina de destinació amb animacions parallax de GSAP.

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

Projectes similars

NovaHair

© 2026 Pol Gubau Amores