FULL STACK DATABASE UX/UI SAAS 2025-12-07

Landing Page SaaS para Peluquería: Arquitectura Escalable con Astro

Sistema de gestión de contenido centralizado, catálogos digitales dinámicos y arquitectura JAMstack para una peluquería local que redujo tiempos de actualización en un 80%.

El Problema: Gestión Manual y Falta de Presencia Digital

Peluquería Yaneth, un negocio local de Misiones (Argentina), gestionaba clientes exclusivamente por WhatsApp y papel. La dueña enfrentaba:

  • Actualización manual de precios: Cada cambio requería editar múltiples archivos HTML
  • Sin catálogos digitales: Los PDFs de productos (Natura, Avon, etc.) se compartían por WhatsApp individualmente
  • Gestión de horarios caótica: Cambios estacionales (verano/invierno) implicaban rediseñar la web
  • Cero analytics: No había forma de medir tráfico ni conversiones

Objetivo del proyecto: Crear un sistema headless CMS ligero donde el negocio pueda actualizar precios, horarios y catálogos sin tocar código.


Stack Tecnológico

Frontend

  • Astro 5 - Framework estático con arquitectura de islas
  • Tailwind CSS - Sistema de diseño personalizado ("Glam System")
  • TypeScript - Type-safe data management

Backend & Infraestructura

  • Cloudflare R2 - Object storage para PDFs de catálogos (25MB+)
  • Cloudflare Workers - CDN edge computing para servir PDFs
  • Vercel Edge Functions - Despliegue automático con CI/CD

Data Architecture

// src/data/siteData.ts - Single Source of Truth
export const siteData = {
  info: { phone, email, address },
  priceList: priceList,        // Importado desde priceList.ts
  catalogs: catalogsBank,       // Gestión de catálogos activos/inactivos
  schedule: [...],              // Horarios dinámicos por temporada
  promo: promotionsBank.find(p => p.active) || promotionsBank[0]
}

Analytics & SEO

  • Google Analytics 4 + Umami (GDPR-compliant)
  • Sitemap XML automático con @astrojs/sitemap
  • OpenGraph dinámico para redes sociales

Desafíos de Ingeniería

1. Arquitectura de Datos Centralizada: El "Brain Pattern"

Problema: La cliente necesitaba cambiar precios/horarios sin editar 15 archivos diferentes.

Solución: Implementé un Single Source of Truth en TypeScript:

// src/data/priceList.ts
export const priceList = [
  {
    category: 'Cortes Damas',
    items: [
      { name: 'Corte + Lavado', price: 5000 },
      { name: 'Corte Terminado', price: 6000 }
    ]
  }
];

// Componente consume datos sin hardcodear nada
---
import { siteData } from '../data/siteData';

{siteData.priceList.map(category => (
  <div>
    <h3>{category.category}</h3>
    {category.items.map(item => (
      <p>{item.name}: ${item.price}</p>
    ))}
  </div>
))}
---

Resultado: La dueña ahora edita un JSON simple y Vercel redeploya automáticamente en 2 minutos.


2. Sistema de Catálogos Digitales con PDF Hosting

Desafío: Los catálogos de Natura/Avon pesan 25MB+ y cambian cada mes. Google Drive era lento y requería múltiples clics.

Arquitectura:

┌─────────────┐      ┌──────────────┐      ┌──────────────┐
│  Astro App  │─────▶│  Short.io    │─────▶│ Cloudflare   │
│  (CTA Link) │      │  (URL Corta) │      │  R2 + Worker │
└─────────────┘      └──────────────┘      └──────────────┘

Cloudflare Worker (Servir PDFs con caché optimizado):

export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const key = url.pathname.slice(1); // "natura-c01.pdf"

    const object = await env.BUCKET.get(key);
    if (!object) return new Response("404 Not Found", { status: 404 });

    const headers = new Headers();
    headers.set("Content-Type", "application/pdf");
    headers.set("Cache-Control", "public, max-age=2592000, immutable"); // 30 días
    headers.set("Accept-Ranges", "bytes"); // Permite carga progresiva

    return new Response(object.body, { headers });
  },
};

Gestión dinámica en frontend:

// src/data/catalogs.ts
export const catalogsBank = [
  {
    brand: "Natura",
    campaigns: [
      {
        name: "Ciclo 01/2025",
        pdfUrl: "https://short.io/natura-c01",
        active: true, // ← Toggle para mostrar/ocultar
      },
    ],
  },
];

// El componente solo renderiza catálogos activos
const activeCatalogs = catalogsBank
  .map((brand) => ({
    ...brand,
    campaigns: brand.campaigns.filter((c) => c.active),
  }))
  .filter((brand) => brand.campaigns.length > 0);

Impacto:

  • ⚡ Primera carga: 8-12s en 4G (inevitable con 25MB)
  • 🚀 Siguientes cargas: Instantáneo (caché del navegador)
  • 📱 Carga progresiva: Safari/Chrome muestran páginas mientras descarga

3. SEO Local & Performance

Desafío: Posicionar en Google para búsquedas locales ("peluquería Cerro Azul").

Implementación:

---
// src/layouts/Layout.astro
const structuredData = {
  "@context": "https://schema.org",
  "@type": "HairSalon",
  "name": "Peluquería Yaneth",
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "Tomas Guido 858",
    "addressLocality": "Cerro Azul",
    "addressRegion": "Misiones",
    "postalCode": "3313",
    "addressCountry": "AR"
  },
  "telephone": "+5493764986352",
  "openingHours": "Mo-Sa 08:00-20:30"
};
---

<script type="application/ld+json" set:html={JSON.stringify(structuredData)} />

Lighthouse Score:

  • 🟢 Performance: 98/100
  • 🟢 SEO: 100/100
  • 🟢 Accessibility: 95/100

4. UX Mobile First: Gestión de Estado del Header

Problema: El menú hamburguesa debe cerrarse al navegar entre páginas (SPA).

Solución con View Transitions:

---
// src/components/organisms/Header.astro
---
<script>
  // Cerrar menú al navegar (compatibilidad con ViewTransitions)
  document.addEventListener('astro:before-swap', () => {
    const checkbox = document.getElementById('menu-toggle') as HTMLInputElement;
    if (checkbox) checkbox.checked = false;
  });
</script>

Impacto en el Negocio

Métricas Cuantitativas

Métrica Antes Después Mejora
Tiempo de actualización de precios 2 horas 15 minutos -87%
Envío de catálogos por WhatsApp 50 mensajes/día 0 (self-service) -100%
Conversión web → WhatsApp 0% (sin web) 12% +12%
Velocidad de carga (mobile) N/A 2.1s

Resultados Cualitativos

  • Autonomía del negocio: La dueña actualiza todo sin depender del desarrollador
  • Profesionalización: De WhatsApp a una presencia digital moderna
  • Analytics: Umami + GA4 revelan que el 68% del tráfico es mobile (justifica diseño mobile-first)

Arquitectura de Componentes: Atomic Design

/components
├── atoms/          # Button, Heading, MapFrame
├── molecules/      # BlogCard, ServiceCard
└── organisms/      # Header, Footer, Hero, PriceTable

// Ejemplo de reusabilidad
<Button href={siteData.info.whatsappLink} variant="primary">
  Reservar Turno
</Button>

Conclusión: Escalabilidad y Lecciones Aprendidas

Lo que Funcionó

  1. TypeScript + Data Centralization = Cambios sin miedo
  2. Cloudflare R2 + Workers = PDFs pesados con carga rápida
  3. Astro Static = SEO perfecto + costos $0 de servidor

Próximos Pasos

  • 🔜 Sistema de reservas: Integración con Calendly o API custom
  • 🔜 Blog automático: Content Collections para tips de belleza
  • 🔜 E-commerce: Venta de productos de catálogo

Code Quality

# ESLint + Prettier configurados
npm run lint:fix
npm run format

# CI/CD con GitHub Actions (auto-deploy en merge a main)

🔗 Enlaces del Proyecto


Tech Stack Summary: Astro + TypeScript + Tailwind CSS + Cloudflare R2/Workers + Vercel Edge + Google Analytics 4 + Umami

Este proyecto demuestra cómo frameworks modernos (JAMstack) pueden transformar negocios locales sin requerir infraestructura compleja ni costos de hosting.