# Arquitectura del ecosistema comercial Peninsula Fitness — Astro + CRM + Cotizador + Integraciones

> **Uso recomendado:** guardar este archivo como `docs/ARCHITECTURE.md`. Mantener `CLAUDE.md` en la raíz como guía compacta para Claude Code y usar este documento como especificación ampliada.  
> **Proyecto:** Peninsula Fitness — catálogo comercial de pisos de caucho de alta resistencia, cotizador, CRM, automatizaciones, integraciones y cockpit comercial.  
> **Stack base:** Astro + TypeScript + PostgreSQL local + servicios Node en VPS Hostinger + Nginx + seguridad open-source/local.

---

## 0. Principio rector para Claude Code

Este proyecto NO debe construirse como una simple landing. Debe construirse como un **ecosistema comercial progresivo**: primero catálogo + cotizador + CRM; después pagos e integraciones; después WhatsApp bot; finalmente cockpit de campañas y optimización.

Claude Code debe trabajar **por etapas, sin adelantar dependencias innecesarias**. Cada etapa debe dejar contratos, modelos y eventos listos para que la siguiente etapa se conecte sin reescribir lo anterior.

### Reglas duras

1. Mobile-first siempre. Si algo se ve bien en desktop pero no en móvil, está incompleto.
2. Toda acción comercial importante debe crear o actualizar datos en CRM.
3. Todo dato operativo debe ser editable por un usuario administrador desde el panel privado: leads, contactos, empresas, oportunidades, cotizaciones, items de cotización, productos, variantes, tareas, actividades, archivos y reglas de automatización.
4. Todo formulario público y toda mutación administrativa debe validar con Zod en cliente y servidor.
5. Ningún secreto, token, API key o credencial debe vivir en el repo.
6. No implementar pagos, bots o sincronizaciones como “mock bonito” sin contratos claros.
7. La etapa actual manda. Las etapas futuras se diseñan con interfaces/adapters, no con implementaciones incompletas.
8. Seguridad máxima usando VPS, Linux, Nginx, firewall, hardening, permisos, headers, rate limiting y auditoría local; no depender de herramientas externas pagadas para seguridad.
9. El tracking debe usar `dataLayer` como contrato central para Meta Pixel, GTM, GA4 y futuras optimizaciones.
10. El CRM debe sentirse inspirado en HubSpot, pero más sencillo, rápido y enfocado a Peninsula.
11. Las campañas y recomendaciones de la etapa 4 deben ser human-in-the-loop: generar propuestas, no publicar sin aprobación explícita.

---

## 1. Contexto comercial resumido

Peninsula Fitness vende pisos de caucho de alta resistencia para gimnasios, boxes, estudios, home gyms, hoteles, desarrollos y proyectos acústicos. El sitio actual comunica productos como rollo, rompecabezas, doble densidad y acústicos; destaca envío nacional, asesoría por WhatsApp, factura, garantía, proyectos instalados y +400 proyectos completados.

El nuevo ecosistema debe convertir visitas de campañas en oportunidades medibles. El centro de conversión será un **cotizador móvil** que capture:

- nombre
- empresa
- WhatsApp
- correo
- producto(s)
- metros cuadrados a cubrir
- código postal
- uso del espacio
- intención: `levantar_orden` o `hablar_con_especialista`

Al calcular, debe crear:

- contacto
- empresa, si aplica
- oportunidad
- cotización
- tareas de seguimiento
- actividad en timeline
- evento de medición para `dataLayer`

---

## 2. Etapas del ecosistema

### Etapa 1 — Sitio catálogo + cotizador + CRM + automatizaciones + dashboard + medición

**Objetivo:** lanzar una experiencia pública que venda, capture leads y permita seguimiento comercial real.

Incluye:

- Sitio catálogo optimizado mobile.
- Catálogo de productos importado desde `peninsula-products.seed.json`.
- Fichas por producto/categoría.
- Cotizador por m², producto y CP.
- CRM privado con login.
- Pipeline/Kanban.
- Módulo administrativo CRUD completo para crear, consultar, editar, duplicar, archivar, restaurar y eliminar información operativa.
- Dashboard de KPIs.
- Actividad lateral estilo HubSpot.
- Automatizaciones internas de seguimiento.
- Eventos `dataLayer` para Meta Pixel, GTM y GA4.

**Definición de terminado:** un lead puede cotizar desde móvil, elegir levantar orden o hablar con especialista, y el CRM registra todo con actividad y tarea de seguimiento. Además, un usuario administrador puede editar manualmente el lead, la empresa, la oportunidad, la cotización, los productos y las tareas relacionadas desde el panel privado, con bitácora de auditoría.

### Etapa 2 — Integraciones comerciales + landing de pago

**Objetivo:** conectar el ecosistema con sistemas de venta, inventario y pago.

Incluye adapters para:

- Alegra API: productos, clientes, facturación, cotizaciones o documentos comerciales.
- Mercado Libre API: catálogo, stock/precios, reputación o productos destacados.
- Amazon Selling Partner API: catálogo y referencias comerciales, si aplica.
- Mercado Pago: payment link/preference para cotizaciones cerradas.
- PayPal: checkout para pagos alternativos.
- Landing de pago por cotización cerrada: `/pagar/:quotePublicToken`.

**Regla:** los pagos se inician solo desde cotizaciones con estatus aprobado/cerrado internamente. No exponer montos manipulables desde frontend.

### Etapa 3 — Bot WhatsApp comercial

**Objetivo:** responder, filtrar, cotizar y recuperar oportunidades desde WhatsApp.

Incluye:

- Conexión directa a WhatsApp Cloud API o proveedor autorizado, sin plataformas de bot innecesarias.
- Respuestas sobre medidas, colores, precios, fichas técnicas, usos, instalación, stock y tiempos.
- Segmentación: comprador pequeño, distribuidor, gimnasio, arquitecto, constructor, proyecto grande.
- Cotización conversacional: m² + CP + producto + uso.
- Generación de cotización PDF.
- Recuperación automática 1, 3 y 7 días después de cotización sin cierre.
- Registro de cada conversación en CRM.

### Etapa 4 — Cockpit desarrollador/comercial para campañas y optimización

**Objetivo:** un módulo privado para analizar visitas, conversiones y campañas previas, y generar propuestas de mejora para Meta y Google.

Incluye:

- Lectura de resultados históricos de campañas.
- Recomendaciones por etapa del funnel.
- Generador de campañas Meta/Google con copies, ángulos, UTMs, públicos sugeridos, presupuesto y objetivos.
- Detección de productos con mayor interés.
- Propuestas de mejoras premium en landing/catalog/cotizador.
- Aprobación humana antes de publicar o exportar.

---

## 3. Stack técnico recomendado

### Frontend / app

- Astro con TypeScript.
- Modo SSR/hybrid para dashboard, CRM y APIs.
- Tailwind CSS o CSS Modules con tokens de diseño.
- Islands interactivas con React o Preact únicamente donde se necesite: cotizador, CRM Kanban, dashboard, filtros.
- Zod para validación compartida.
- Astro middleware para headers, sesión, CSRF y auditoría.

### Backend local en VPS

- Node.js LTS.
- PostgreSQL local.
- Drizzle ORM o Prisma. Recomendación: Drizzle si se busca ligereza y control.
- Redis local opcional para rate limiting, colas ligeras y locks de sincronización.
- Nginx como reverse proxy.
- PM2 o systemd para proceso Node.
- Logs con rotación local.

### Seguridad sin servicios externos pagados

- UFW firewall.
- Fail2ban.
- SSH con llave, sin password login.
- Usuario deploy sin root.
- Certbot/Let’s Encrypt para TLS.
- Backups locales + snapshot VPS cuando esté disponible.
- Headers estrictos en Nginx y Astro.
- Rate limiting por IP y por endpoint.
- Auditoría de acciones CRM, CRUD administrativo, cambios de precio, edición de cotizaciones, eliminación/restauración y cambios de permisos.
- Validación de archivos por extensión, MIME y magic bytes.
- No guardar tarjetas; pagos siempre en procesador.

---

## 4. Estructura de repositorio

```txt
/
├─ CLAUDE.md                         # Guía compacta para Claude Code
├─ docs/
│  ├─ ARCHITECTURE.md                # Este archivo
│  ├─ TRACKING_PLAN.md               # Eventos dataLayer/GA4/Meta
│  ├─ SECURITY_CHECKLIST.md          # Hardening VPS + app
│  ├─ API_CONTRACTS.md               # Contratos internos y externos
│  └─ STAGE_BACKLOG.md               # Backlog por etapa
├─ src/
│  ├─ pages/
│  │  ├─ index.astro
│  │  ├─ productos/index.astro
│  │  ├─ productos/[slug].astro
│  │  ├─ cotizar.astro
│  │  ├─ gracias/[quoteId].astro
│  │  ├─ pagar/[token].astro         # Etapa 2
│  │  ├─ crm/
│  │  │  ├─ index.astro
│  │  │  ├─ pipeline.astro
│  │  │  ├─ oportunidades/[id].astro
│  │  │  ├─ contactos/[id].astro
│  │  │  ├─ empresas/[id].astro
│  │  │  ├─ cotizaciones/index.astro
│  │  │  ├─ cotizaciones/[id].astro
│  │  │  ├─ productos/index.astro
│  │  │  ├─ productos/[id].astro
│  │  │  ├─ tareas/index.astro
│  │  │  └─ dashboard.astro
│  │  └─ api/
│  │     ├─ quote/calculate.ts
│  │     ├─ quote/create.ts
│  │     ├─ crm/opportunities.ts
│  │     ├─ crm/contacts.ts
│  │     ├─ crm/companies.ts
│  │     ├─ crm/quotes.ts
│  │     ├─ crm/quote-items.ts
│  │     ├─ crm/tasks.ts
│  │     ├─ crm/activities.ts
│  │     ├─ catalog/products.ts
│  │     ├─ auth/login.ts
│  │     ├─ webhooks/meta.ts
│  │     ├─ webhooks/whatsapp.ts     # Etapa 3
│  │     └─ payments/mercadopago.ts  # Etapa 2
│  ├─ components/
│  │  ├─ catalog/
│  │  ├─ quote/
│  │  ├─ crm/
│  │  ├─ dashboard/
│  │  └─ ui/
│  ├─ islands/
│  │  ├─ QuoteWizard.tsx
│  │  ├─ PipelineBoard.tsx
│  │  ├─ ActivityDrawer.tsx
│  │  └─ CampaignGenerator.tsx       # Etapa 4
│  ├─ lib/
│  │  ├─ db/
│  │  ├─ crm/
│  │  ├─ quote/
│  │  ├─ tracking/
│  │  ├─ security/
│  │  ├─ integrations/
│  │  │  ├─ alegra/
│  │  │  ├─ mercadolibre/
│  │  │  ├─ amazon/
│  │  │  ├─ mercadopago/
│  │  │  └─ paypal/
│  │  └─ whatsapp/
│  ├─ data/
│  │  └─ peninsula-products.seed.json
│  └─ styles/
├─ scripts/
│  ├─ import-products.ts
│  ├─ seed-db.ts
│  ├─ backup-db.sh
│  └─ deploy.sh
├─ nginx/
│  └─ peninsula.conf
└─ tests/
   ├─ unit/
   ├─ integration/
   └─ e2e/
```

---

## 5. Datos de productos

Archivo generado desde Excel: `src/data/peninsula-products.seed.json`. Este archivo es únicamente la semilla inicial; después de importar, la fuente de verdad debe ser PostgreSQL para que el administrador pueda mantener el catálogo editable desde el CRM.

Columnas originales detectadas:

- `NOMBRE COMPLETO`
- `Referencia o SKU`
- `Formato/categoría`
- `Dimensiones`
- `Espesor`
- `Color`
- `Precios sin IVA`
- `Descripción`
- `Procedencia`

Resumen normalizado:

| Categoría normalizada | Productos | Precio mínimo sin IVA | Precio máximo sin IVA | Coberturas m² detectadas | Espesores |
|---|---:|---:|---:|---|---|
| `accesorio` | 8 | 189.66 | 19396.55 | N/D | N/D |
| `acustico_underlayment` | 11 | 2068.97 | 8354.31 | N/D | 20mm |
| `adhesivo` | 3 | 2800 | 4589 | N/D | N/D |
| `doble_densidad_baldosa` | 13 | 603 | 3620.69 | 0.36, 0.72 | 15mm, 20mm, 25mm, 30mm, 70mm |
| `hoja_rectangular` | 6 | 489.66 | 2605 | 2.16 | 8mm |
| `premium_caja` | 1 | 5909 | 5909 | 2.24 | 6mm |
| `rollo` | 48 | 4374.15 | 15000 | 2.16, 5.52, 9.12 | 10mm, 12mm, 15mm, 6mm, 8mm |
| `rompecabezas` | 10 | 185.71 | 200 | 0.3364 | 8mm |
| `servicio` | 2 | Personalizado | Personalizado | N/D | N/D |

### Reglas de importación

1. Cada fila del Excel se importa como variante comercial.
2. `sku` debe ser único cuando exista.
3. `price_without_vat_mxn` se guarda sin IVA.
4. IVA se calcula con `vat_rate = 0.16` por defecto.
5. `coverage_m2_per_unit` se calcula desde dimensiones cuando sea posible.
6. Productos sin precio fijo o sin cobertura se marcan como `requires_specialist_quote = true`.
7. El cotizador solo debe calcular automáticamente productos con `quote_enabled = true`.
8. Accesorios, adhesivos, envío e instalación pueden sugerirse como upsell o requerir especialista.
9. El administrador puede crear, editar, duplicar, publicar/despublicar, archivar, restaurar o eliminar productos y variantes desde `/crm/productos`.
10. Cada edición de precio, cobertura, SKU, estatus de cotización o descripción debe guardar snapshot y registro en `audit_logs`.

### Fórmula de cotización

```ts
const requestedM2 = input.squareMeters;
const wasteFactor = product.normalized_category === 'rollo' ? 0.08 : 0.05;
const effectiveM2 = requestedM2 * (1 + wasteFactor);
const units = Math.ceil(effectiveM2 / product.coverage_m2_per_unit);
const subtotal = units * product.price_without_vat_mxn;
const iva = subtotal * 0.16;
const total = subtotal + iva;
```

### Regla de transparencia en UI

Mostrar en el cotizador:

- precio estimado
- piezas/rollos/baldosas sugeridas
- m² estimados cubiertos
- IVA desglosado
- envío pendiente de confirmar por CP, salvo que etapa 2 ya tenga motor logístico
- nota: “Cotización estimada. Un especialista validará disponibilidad, envío e instalación.”

---

## 6. Arquitectura de datos CRM

### Entidades principales

```txt
users
roles
permissions
user_roles
companies
contacts
opportunities
opportunity_stages
opportunity_stage_history
quotes
quote_items
orders
products
product_variants
activities
tasks
files
automation_rules
automation_runs
tracking_events
integration_accounts
integration_sync_logs
webhook_events
bot_conversations
campaign_snapshots
campaign_recommendations
audit_logs
soft_deleted_records
```

### `users`

- id
- email
- password_hash
- full_name
- status: `active | suspended | invited`
- last_login_at
- created_at
- updated_at

### `roles`

Roles sugeridos:

- `super_admin`: acceso total, configuración, integraciones y eliminación definitiva controlada.
- `admin`: CRUD completo sobre CRM, productos, cotizaciones, tareas y automatizaciones operativas.
- `sales`: puede crear/editar leads, oportunidades, cotizaciones, tareas y actividades asignadas.
- `developer`: acceso a cockpit técnico/campañas, diagnósticos, tracking, logs e integraciones según permisos.
- `viewer`: solo lectura.

### `permissions`

Permisos granulares mínimos:

- `crm.contacts.read/create/update/delete/restore`
- `crm.companies.read/create/update/delete/restore`
- `crm.opportunities.read/create/update/delete/restore/change_stage`
- `crm.quotes.read/create/update/delete/restore/send/approve/cancel`
- `crm.quote_items.read/create/update/delete`
- `catalog.products.read/create/update/delete/restore/publish`
- `crm.tasks.read/create/update/delete/complete/reassign`
- `crm.activities.read/create/update/delete`
- `crm.automation_rules.read/create/update/delete/enable`
- `admin.users.read/create/update/delete/assign_roles`
- `integrations.read/configure/sync`
- `campaigns.read/generate/approve/export`
- `audit_logs.read`

### `contacts`

Campos mínimos:

- id
- full_name
- email
- whatsapp
- source
- lifecycle_stage
- company_id
- created_at
- updated_at

### `companies`

- id
- name
- rfc
- fiscal_name
- fiscal_address
- billing_email
- created_at
- updated_at

### `opportunities`

- id
- company_id
- contact_id
- title
- amount_estimated
- stage: `lead_nuevo | contactado | cotizacion_enviada | seguimiento | negociacion | ganado | perdido`
- expected_close_date
- last_activity_at
- owner_user_id
- source_campaign
- source_medium
- source_content
- utm_source
- utm_medium
- utm_campaign
- utm_content
- utm_term
- lost_reason
- created_at
- updated_at

### `quotes`

- id
- public_token
- contact_id
- company_id
- opportunity_id
- status: `draft | calculated | sent | approved | payment_pending | paid | expired | cancelled`
- requested_m2
- postal_code
- use_case
- subtotal
- tax
- shipping_estimate
- total
- currency: `MXN`
- notes_public
- notes_internal
- expires_at
- created_at
- updated_at

### `quote_items`

- id
- quote_id
- product_id
- sku
- name_snapshot
- quantity
- coverage_m2_per_unit
- price_without_vat_snapshot
- subtotal
- tax
- total

### `products`

Tabla editable del catálogo después de importar la semilla.

- id
- slug
- name
- sku
- normalized_category
- short_description
- long_description
- dimensions
- thickness
- color
- origin_country
- use_cases_json
- image_urls_json
- technical_sheet_url
- quote_enabled
- requires_specialist_quote
- status: `draft | published | archived | deleted`
- created_by
- updated_by
- deleted_at
- created_at
- updated_at

### `product_variants`

- id
- product_id
- variant_name
- sku
- dimensions
- thickness
- color
- coverage_m2_per_unit
- price_without_vat_mxn
- vat_rate
- stock_status: `unknown | available | low_stock | out_of_stock | special_order`
- external_refs_json: claves futuras para Alegra/Mercado Libre/Amazon
- quote_enabled
- requires_specialist_quote
- status: `active | inactive | archived | deleted`
- created_at
- updated_at
- deleted_at

### `activities`

- id
- related_type: `contact | company | opportunity | quote | order`
- related_id
- type: `note | call | whatsapp | email | stage_change | quote_created | quote_sent | task_created | payment_event | bot_message`
- body
- metadata_json
- created_by
- created_at

### `tasks`

- id
- opportunity_id
- contact_id
- type: `call | whatsapp | email | review_quote | follow_up`
- title
- due_at
- status: `pending | completed | cancelled | overdue`
- assigned_to
- created_at
- completed_at

### `audit_logs`

Bitácora obligatoria para toda mutación administrativa.

- id
- actor_user_id
- action: `create | update | delete | restore | publish | unpublish | stage_change | login | export | payment_event | integration_sync`
- entity_type
- entity_id
- before_json
- after_json
- ip_address
- user_agent
- created_at

### `soft_deleted_records`

Registro para restauración y trazabilidad.

- id
- entity_type
- entity_id
- deleted_by
- reason
- deleted_at
- restore_until

---

## 6.1 Módulo administrativo CRUD 100% editable

El panel privado debe permitir que el usuario administrador controle toda la operación sin depender de desarrollador para cambios diarios. No debe haber datos comerciales importantes en modo solo lectura, salvo integraciones externas o registros bloqueados por seguridad/pago.

### Alcance editable por administrador

El rol `admin` y `super_admin` debe poder:

- Crear, consultar, editar, archivar, restaurar y eliminar leads/contactos.
- Crear, consultar, editar, archivar, restaurar y eliminar empresas.
- Crear, consultar, editar, mover de etapa, reasignar, marcar como ganado/perdido y eliminar oportunidades.
- Crear, consultar, editar, duplicar, cancelar, archivar y eliminar cotizaciones.
- Agregar, editar o eliminar productos dentro de una cotización.
- Crear, consultar, editar, publicar/despublicar, archivar, restaurar y eliminar productos del catálogo.
- Crear, editar, completar, cancelar, reasignar y eliminar tareas.
- Crear, editar o eliminar actividades manuales como notas, llamadas, WhatsApp o emails registrados.
- Activar/desactivar reglas de automatización de seguimiento.
- Editar datos fiscales, RFC, razón social, email de facturación y dirección fiscal.

### Regla de eliminación

Usar `soft delete` por defecto para contactos, empresas, oportunidades, cotizaciones, productos, tareas y actividades. El registro desaparece de vistas normales, pero queda recuperable y visible para `super_admin` en una vista de archivados/eliminados.

La eliminación definitiva solo puede hacerse mediante acción explícita de `super_admin` o script de mantenimiento, con doble confirmación, razón obligatoria y registro en `audit_logs`.

### Edición segura de cotizaciones

Cuando una cotización ya fue enviada, aprobada, pagada o ligada a una integración externa:

- No sobrescribir silenciosamente montos anteriores.
- Crear una nueva versión o snapshot de cotización.
- Mantener `quote_items` con precios snapshot.
- Guardar diferencia en `audit_logs.before_json` y `audit_logs.after_json`.
- Si cambia el total, regresar la cotización a `draft` o `calculated`, salvo que el administrador confirme reenviar/aprobar.

### Administración de productos

El administrador debe poder editar el catálogo sin tocar el Excel ni el código:

- Nombre comercial.
- SKU.
- Categoría.
- Dimensiones.
- Espesor.
- Color.
- Precio sin IVA.
- IVA.
- Cobertura m² por unidad.
- Descripción corta/larga.
- Imágenes.
- Ficha técnica.
- Estatus: borrador, publicado, archivado.
- Si participa o no en cotización automática.
- Si requiere validación de especialista.

La importación desde Excel debe ser idempotente: si el SKU ya existe, actualizar campos mapeados sin borrar ediciones manuales críticas, salvo que el administrador active `force_update`.

### Vistas administrativas mínimas

- `/crm/contactos`: listado, búsqueda, filtros, crear contacto.
- `/crm/contactos/[id]`: ficha editable, historial, oportunidades, cotizaciones, tareas.
- `/crm/empresas/[id]`: ficha editable, datos fiscales, contactos asociados.
- `/crm/oportunidades/[id]`: ficha editable, etapa, monto, propietario, actividades y cotizaciones.
- `/crm/cotizaciones`: listado con filtros por estatus, fecha, producto y responsable.
- `/crm/cotizaciones/[id]`: editor de cotización, items, descuento, notas, versión/snapshot.
- `/crm/productos`: catálogo editable con búsqueda, filtros y acciones masivas.
- `/crm/productos/[id]`: editor de producto/variante.
- `/crm/tareas`: seguimiento editable por responsable/fecha/estatus.
- `/crm/admin/auditoria`: bitácora de cambios, solo admin/super_admin.

### Reglas UX para edición

- Formularios simples, claros y mobile-first.
- Guardado con confirmación visual.
- Validación inline.
- Confirmación antes de eliminar/archivar.
- Campo obligatorio `reason` para eliminar cotizaciones, oportunidades ganadas/perdidas o productos publicados.
- En tablas: buscar, ordenar, filtrar, paginar y exportar CSV cuando aplique.
- Mostrar quién editó y cuándo.

### Contrato de permisos

Antes de cualquier mutación, el backend debe verificar:

1. sesión válida;
2. permiso granular;
3. propiedad/asignación cuando aplique;
4. validación Zod;
5. escritura transaccional;
6. creación de `audit_logs`;
7. actualización de `updated_at` y `updated_by`.

Nunca confiar en permisos calculados solo en frontend.

---

## 7. CRM UX inspirado en HubSpot, pero ligero

### Dashboard inicial

KPIs arriba:

- oportunidades activas
- pipeline total $
- cierres del mes
- oportunidades estancadas
- seguimientos pendientes

Vista principal:

- empresa
- oportunidad/proyecto
- monto
- etapa
- fecha estimada de cierre
- última actividad
- responsable

### Pipeline Kanban

Columnas:

1. Lead nuevo
2. Contactado
3. Cotización enviada
4. Seguimiento
5. Negociación
6. Ganado
7. Perdido

Arrastrar una oportunidad debe:

- actualizar etapa
- guardar actividad `stage_change`
- actualizar `last_activity_at`
- disparar reglas de automatización si aplica
- enviar evento interno de analítica

### Drawer lateral de actividad

Debe permitir:

- agregar nota rápida
- ver historial de contacto
- crear tarea
- ver cotizaciones
- adjuntar archivos
- registrar llamada/WhatsApp manual
- ver UTM/campaña origen

---

## 8. Cotizador mobile-first

### Flujo público recomendado

1. Selección rápida de tipo de piso:
   - Rollo
   - Rompecabezas
   - Doble densidad
   - Acústico
   - No estoy seguro
2. Selección de producto/variante.
3. Captura de m² y CP.
4. Captura de datos: nombre, empresa, WhatsApp, correo.
5. Resultado: resumen estimado.
6. CTA principal: `Levantar orden`.
7. CTA secundario: `Hablar con especialista`.

### Reglas de UI móvil

- El cotizador debe funcionar en una sola columna.
- Botones grandes, mínimo 44px de alto.
- Inputs con teclado correcto: teléfono, email, número.
- Progreso visible por pasos.
- Resultado sticky al final después de calcular.
- WhatsApp CTA siempre visible después del cálculo.
- Evitar tablas anchas en móvil.

### Validaciones

- Nombre: 2–100 caracteres.
- Empresa: opcional, 2–120 caracteres.
- WhatsApp: normalizar a E.164 si es México.
- Email: validar formato.
- M²: mínimo 1, máximo configurable.
- CP: 5 dígitos para México.
- Producto: requerido.

---

## 9. API interna — contratos base

### `POST /api/quote/calculate`

Request:

```json
{
  "productId": "pf-001",
  "squareMeters": 45,
  "postalCode": "44100",
  "useCase": "gimnasio_comercial"
}
```

Response:

```json
{
  "ok": true,
  "calculation": {
    "productId": "pf-001",
    "units": 142,
    "coverageM2": 47.77,
    "subtotal": 26370.82,
    "tax": 4219.33,
    "shippingEstimate": null,
    "total": 30590.15,
    "currency": "MXN",
    "requiresSpecialistValidation": true
  }
}
```

### `POST /api/quote/create`

Crea contacto, empresa, oportunidad, cotización, items, actividad y tarea.

Request:

```json
{
  "contact": {
    "fullName": "Nombre Apellido",
    "company": "Gimnasio Demo",
    "whatsapp": "+523331234567",
    "email": "correo@demo.com"
  },
  "quoteInput": {
    "productId": "pf-001",
    "squareMeters": 45,
    "postalCode": "44100",
    "useCase": "gimnasio_comercial"
  },
  "intent": "levantar_orden",
  "tracking": {
    "utm_source": "meta",
    "utm_medium": "paid_social",
    "utm_campaign": "pisos_gimnasio"
  }
}
```

Response:

```json
{
  "ok": true,
  "quoteId": "q_123",
  "opportunityId": "opp_123",
  "nextUrl": "/gracias/q_123"
}
```

### CRUD administrativo base

Todos los endpoints administrativos deben estar protegidos por sesión, CSRF, rate limit y permisos granulares. Deben soportar paginación, búsqueda y filtros.

Patrón recomendado:

```txt
GET    /api/crm/contacts
POST   /api/crm/contacts
GET    /api/crm/contacts/:id
PATCH  /api/crm/contacts/:id
DELETE /api/crm/contacts/:id          # soft delete
POST   /api/crm/contacts/:id/restore

GET    /api/crm/companies
POST   /api/crm/companies
PATCH  /api/crm/companies/:id
DELETE /api/crm/companies/:id
POST   /api/crm/companies/:id/restore

GET    /api/crm/opportunities
POST   /api/crm/opportunities
PATCH  /api/crm/opportunities/:id
DELETE /api/crm/opportunities/:id
POST   /api/crm/opportunities/:id/restore
POST   /api/crm/opportunities/:id/change-stage

GET    /api/crm/quotes
POST   /api/crm/quotes
PATCH  /api/crm/quotes/:id
DELETE /api/crm/quotes/:id
POST   /api/crm/quotes/:id/restore
POST   /api/crm/quotes/:id/items
PATCH  /api/crm/quotes/:id/items/:itemId
DELETE /api/crm/quotes/:id/items/:itemId

GET    /api/catalog/products
POST   /api/catalog/products
PATCH  /api/catalog/products/:id
DELETE /api/catalog/products/:id
POST   /api/catalog/products/:id/restore
POST   /api/catalog/products/:id/publish
POST   /api/catalog/products/:id/unpublish

GET    /api/crm/tasks
POST   /api/crm/tasks
PATCH  /api/crm/tasks/:id
DELETE /api/crm/tasks/:id
POST   /api/crm/tasks/:id/complete

GET    /api/crm/activities
POST   /api/crm/activities
PATCH  /api/crm/activities/:id
DELETE /api/crm/activities/:id
```

### Ejemplo de mutación segura

```ts
await db.transaction(async (tx) => {
  const before = await tx.query.opportunities.findFirst({ where: eq(opportunities.id, id) });
  const updated = await tx.update(opportunities).set(payload).where(eq(opportunities.id, id)).returning();
  await tx.insert(auditLogs).values({
    actorUserId: session.user.id,
    action: 'update',
    entityType: 'opportunity',
    entityId: id,
    beforeJson: before,
    afterJson: updated[0],
    ipAddress: requestIp,
    userAgent: request.headers.get('user-agent')
  });
});
```

### Respuesta estándar para CRUD

```json
{
  "ok": true,
  "data": {},
  "auditId": "audit_123",
  "message": "Registro actualizado correctamente"
}
```

---

## 10. Tracking plan — contrato `dataLayer`

La medición debe centralizarse con eventos `dataLayer.push()`. GTM decide qué manda a GA4, Meta Pixel y conversiones futuras.

Eventos públicos mínimos:

```ts
window.dataLayer.push({
  event: 'view_catalog',
  page_type: 'home'
});

window.dataLayer.push({
  event: 'select_product',
  product_id,
  product_sku,
  product_category,
  product_price
});

window.dataLayer.push({
  event: 'quote_start',
  source: 'catalog'
});

window.dataLayer.push({
  event: 'quote_calculated',
  product_id,
  product_sku,
  square_meters,
  units,
  value,
  currency: 'MXN'
});

window.dataLayer.push({
  event: 'generate_lead',
  quote_id,
  opportunity_id,
  value,
  intent: 'levantar_orden'
});

window.dataLayer.push({
  event: 'specialist_requested',
  quote_id,
  opportunity_id,
  value
});
```

Eventos CRM:

- `crm_stage_changed`
- `crm_task_created`
- `crm_quote_sent`
- `crm_opportunity_won`
- `crm_opportunity_lost`

### UTM

Guardar UTMs en cookie first-party por 30 días:

- utm_source
- utm_medium
- utm_campaign
- utm_content
- utm_term
- gclid
- fbclid

---

## 11. Automatizaciones Etapa 1

Las automatizaciones deben iniciar como tareas internas, no como envíos automáticos agresivos.

Reglas base:

1. Cotización creada → tarea inmediata para revisar.
2. Cotización calculada sin contacto completo → lead incompleto.
3. Cotización enviada y no cerrada → seguimiento día 1.
4. Sigue sin cierre → seguimiento día 3.
5. Sigue sin cierre → seguimiento día 7.
6. Oportunidad sin actividad por 5 días → marcar estancada.

Cada automatización debe crear:

- task
- activity
- automation_run

---

## 12. Integraciones Etapa 2

### Patrón adapter

Cada integración debe vivir en `src/lib/integrations/{provider}` con:

```txt
client.ts          # cliente HTTP autenticado
schemas.ts         # Zod schemas request/response
mapper.ts          # mapeo interno <-> externo
sync.ts            # jobs de sincronización
errors.ts          # errores normalizados
```

### Alegra

Usos esperados:

- sincronizar productos/SKU
- crear/actualizar clientes
- generar documentos de venta/cotización/factura si se decide
- consultar inventario/precios si el cliente lo centraliza ahí

### Mercado Libre / Amazon

Usos esperados:

- comparar precio directo vs marketplace
- traer datos de catálogo, stock o referencias
- alimentar campañas y confianza comercial
- no usar como fuente única de verdad sin confirmación

### Mercado Pago / PayPal

Reglas:

- crear payment link desde backend.
- monto sale de quote aprobada.
- token público no debe ser predecible.
- webhook valida firma.
- si pago aprobado: quote `paid`, opportunity `ganado`, order creada.
- si pago fallido: activity + task.

---

## 13. Bot WhatsApp Etapa 3

### Intenciones principales

- `ask_price`
- `ask_measurements`
- `ask_colors`
- `ask_technical_sheet`
- `ask_installation`
- `ask_stock`
- `ask_delivery_time`
- `quote_request`
- `recover_quote`
- `human_handoff`

### Clasificación del lead

Segmentos:

- comprador pequeño
- distribuidor
- gimnasio
- arquitecto
- constructor
- proyecto grande

Variables:

- m²
- ciudad/CP
- tipo de producto
- uso del espacio
- urgencia
- presupuesto aproximado
- requiere instalación
- requiere factura

### Respuesta de cotización

El bot debe:

1. pedir m²
2. pedir CP/ciudad
3. pedir producto o recomendar categoría
4. calcular cantidad/precio si `quote_enabled`
5. crear/actualizar contacto y oportunidad
6. generar PDF o link de cotización
7. asignar tarea humana si hay incertidumbre

---

## 14. Cockpit de campañas Etapa 4

### Objetivo

Crear un módulo privado para que un usuario desarrollador/comercial genere mejoras de campañas y landing con base en datos reales.

### Entradas

- eventos de sitio
- cotizaciones creadas
- productos más cotizados
- oportunidades ganadas/perdidas
- campañas Meta/Google previas
- CTR, CPC, costo por conversación, costo por lead, tasa de cierre

### Salidas

- hipótesis de mejora
- nuevos copies Meta
- estructura Google Search
- negativos sugeridos
- públicos sugeridos
- UTMs
- recomendaciones de landing/product page
- backlog técnico priorizado por impacto

### Regla crítica

No publicar campañas automáticamente en Etapa 4 MVP. Generar borradores exportables y requerir aprobación.

---

## 15. Seguridad técnica en VPS Hostinger

### Servidor

- SSH solo con llave.
- Deshabilitar root login.
- Deshabilitar password authentication.
- UFW: permitir 22, 80, 443; cerrar todo lo demás.
- Fail2ban para SSH y Nginx.
- Nginx reverse proxy.
- TLS con Certbot.
- Actualizaciones automáticas de seguridad.
- Usuario `deploy` con permisos mínimos.
- `.env` fuera del root público.
- Backups diarios de DB y uploads.

### App

- Cookies `HttpOnly`, `Secure`, `SameSite=Lax` o `Strict` en CRM.
- CSRF en formularios mutables.
- Rate limiting en `/api/quote/*`, `/api/auth/*`, `/api/webhooks/*`.
- CSP con allowlist estricta.
- Sanitización de HTML si hay campos ricos.
- Logs sin datos sensibles completos.
- Auditoría de login, cambios de etapa, cambios de monto, pagos, webhooks.
- Uploads fuera de `/public`.
- Validar tamaño máximo de archivos.
- No procesar PDFs/imágenes en shell sin sandbox.

### Headers sugeridos

```txt
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://connect.facebook.net; img-src 'self' data: https:; connect-src 'self' https://www.google-analytics.com https://graph.facebook.com; frame-src https://www.googletagmanager.com;
```

---

## 16. Variables de entorno

```bash
NODE_ENV=production
PUBLIC_SITE_URL=https://peninsulafitness.com.mx
DATABASE_URL=postgresql://...
SESSION_SECRET=...
ENCRYPTION_SECRET=...
PUBLIC_GTM_ID=GTM-XXXX
PUBLIC_GA4_ID=G-XXXX
PUBLIC_META_PIXEL_ID=...
ALEGRA_API_URL=...
ALEGRA_CLIENT_ID=...
ALEGRA_CLIENT_SECRET=...
MERCADOLIBRE_CLIENT_ID=...
MERCADOLIBRE_CLIENT_SECRET=...
AMAZON_SP_API_CLIENT_ID=...
AMAZON_SP_API_CLIENT_SECRET=...
MERCADOPAGO_ACCESS_TOKEN=...
PAYPAL_CLIENT_ID=...
PAYPAL_CLIENT_SECRET=...
WHATSAPP_VERIFY_TOKEN=...
WHATSAPP_ACCESS_TOKEN=...
WHATSAPP_PHONE_NUMBER_ID=...
```

Nunca crear `.env` con valores reales dentro del repo.

---

## 17. Backlog técnico recomendado — primera entrega

### Sprint 1

- Crear estructura Astro + TypeScript.
- Configurar diseño base mobile-first.
- Crear importador de productos desde JSON.
- Crear páginas: home, catálogo, producto, cotizar.
- Crear cotizador client/server con Zod.
- Crear `dataLayer` helper.

### Sprint 2

- Configurar PostgreSQL + ORM.
- Crear modelos CRM mínimos.
- Crear permisos, roles, soft delete y audit logs.
- Crear CRUD administrativo para contactos/leads, empresas, oportunidades, cotizaciones, productos, tareas y actividades.
- Guardar contacto/oportunidad/cotización.
- Crear dashboard CRM básico.
- Crear pipeline Kanban.
- Crear drawer de actividad.

### Sprint 3

- Automatizaciones internas de seguimiento.
- Tareas y actividades.
- Hardening app.
- Tests unitarios de cotizador.
- Tests e2e de flujo móvil.
- Deploy VPS.

---

## 18. Definition of Done por etapa

### Etapa 1

- Lighthouse móvil > 85 en performance, accesibilidad y buenas prácticas.
- Cotizador funciona en móvil real.
- Lead queda en CRM.
- Pipeline muestra oportunidad.
- Dashboard calcula KPIs.
- `dataLayer` dispara eventos principales.
- Login CRM protegido.
- Admin puede editar y eliminar registros con auditoría.
- Backups documentados.
- Nginx y TLS configurados.

### Etapa 2

- Payment link funciona en sandbox y producción.
- Webhooks validados.
- Adapter Alegra probado con mocks y credenciales reales cuando existan.
- Landing de pago no permite alterar monto desde cliente.

### Etapa 3

- WhatsApp bot responde FAQs.
- Bot puede crear cotización.
- Conversación queda en CRM.
- Handoff humano funciona.
- Recuperaciones 1/3/7 días configuradas.

### Etapa 4

- Dashboard lee campañas y conversiones.
- Genera propuestas de campañas.
- Exporta borradores.
- No publica sin aprobación.

---

## 19. Instrucciones de trabajo para Claude Code

Cuando Claude Code implemente:

1. Primero inspeccionar estructura existente.
2. Leer `CLAUDE.md`.
3. Leer esta arquitectura solo para el módulo que se va a tocar.
4. Crear plan breve antes de editar.
5. Hacer cambios pequeños y verificables.
6. No modificar fases futuras salvo contratos o placeholders.
7. Agregar o actualizar tests cuando cambie lógica.
8. No crear dependencias pesadas sin justificar.
9. No romper mobile-first.
10. Al final, reportar archivos modificados, pruebas ejecutadas y pendientes.

---

## 20. Primer prompt sugerido para Claude Code

```txt
Lee CLAUDE.md y docs/ARCHITECTURE.md. Implementa únicamente la Etapa 1, Sprint 1: estructura Astro + TypeScript, layout mobile-first, catálogo, importador de productos desde src/data/peninsula-products.seed.json, cotizador por m² con Zod, helper dataLayer, páginas home/productos/producto/cotizar y scaffolding protegido de CRUD administrativo para leads, oportunidades, cotizaciones y productos. No implementes pagos, WhatsApp bot ni integraciones reales todavía; solo deja interfaces/adapters vacíos si son necesarios. Al terminar, ejecuta build/test/lint y dame resumen de cambios.
```

---

## 21. Notas de producto y marca

Mensajes comerciales base:

- Piso de caucho de alta resistencia para gimnasios, boxes y estudios.
- Fabricado con material reciclado certificado.
- Envío a todo México.
- Asesoría gratuita.
- Cotización inmediata.
- Factura electrónica.
- Proyectos especiales, cortes a medida e instalación.
- Compra directa con mejor precio que marketplace cuando aplique.

Tono visual recomendado:

- Industrial premium.
- Deportivo profesional.
- Limpio, sólido, confiable.
- Mobile-first, conversion-focused.
- Contrastes fuertes, botones claros, fichas técnicas ordenadas.

---

## 22. Riesgos y decisiones pendientes

- Confirmar si el precio del Excel es siempre sin IVA.
- Confirmar si rollos pueden venderse fraccionados o solo completos.
- Confirmar reglas de envío por CP.
- Confirmar proceso fiscal exacto con Alegra.
- Confirmar si Mercado Libre/Amazon serán fuente de stock o solo referencia.
- Confirmar proveedor oficial de WhatsApp si no se usa Cloud API directo.
- Confirmar si etapa 4 usará modelo de IA externo o plantillas locales primero.

Hasta confirmar, el sistema debe diseñarse con adapters y flags de configuración.
