# PRODUCT CHECKLIST — qué debe pasar un producto para ser certificado production-ready

> **Pre-requisito:** [`CORE-TRANSVERSAL.md`](CORE-TRANSVERSAL.md) — las 14 capas core deben estar OK antes de empezar.
>
> Si fallan transversales, **ningún producto** sale. Si fallan producto-específicas, **solo ese producto** queda bloqueado.

---

## Índice de bloques

| Bloque | # items | Si falla |
|--------|---------|----------|
| 1. Identidad y catálogo | 6 | No se puede vender |
| 2. Asset pack del producto | 8 | Logos rotos / sitio sin marca |
| 3. Vistas base (welcome / about / contact) | 12 | UX rota / placeholders / inglés |
| 4. Seeds de contenido | 7 | Sitio vacío al entregarlo |
| 5. Editabilidad admin↔front | 5 | Cliente no puede modificar |
| 6. Branding y CSS | 6 | Colores rotos / ilegibilidad |
| 7. Pipeline de venta | 5 | Compra falla |
| 8. QA visual final | 8 | Listo para entrega |

**Total**: ~57 items.

---

## Bloque 1 — Identidad y catálogo

| # | Check | Cómo validar | Esperado |
|---|-------|--------------|----------|
| 1.1 | Existe `database/seeders/products/core/{slug}.json` con bloque completo | `cat database/seeders/products/core/{slug}.json` | demo, modules, modules_navigation, fonts, brand_defaults, schema_type, header.cta_button, demo_url |
| 1.2 | Demo asignado existe como template | `ls resources/views/modules/cd-base/frontend/demos/{demo}/` | welcome, about, contact existen |
| 1.3 | Schema.org type es válido | preset.schema_type | LegalService / RealEstateAgent / Restaurant / Organization / etc. |
| 1.4 | Al menos 1 shop_slug en `catalog.json` mapea al core | `grep '"core": "{slug}"' database/seeders/products/catalog.json` | 1+ entradas |
| 1.5 | Producto registrado en tabla `products` del sitio principal | `mysql bewpro22_bp -e "SELECT slug, stripe_price_id, is_active FROM products WHERE slug='{slug}'"` | 1 fila con stripe_price_id válido |
| 1.6 | `is_active=1` en la tabla (autoriza venta) | mismo query | `is_active=1` |

---

## Bloque 2 — Asset pack del producto

| # | Check | Cómo validar | Esperado |
|---|-------|--------------|----------|
| 2.1 | Carpeta `public/cd-project/assets/{slug}/` existe | `ls public/cd-project/assets/{slug}/` | dir presente |
| 2.2 | `logo.png` presente y NO es del template demo | `md5 public/cd-project/assets/{slug}/logo.png` | hash distinto al template |
| 2.3 | `logo-alternative.png` presente | `ls .../logo-alternative.png` | OK |
| 2.4 | `logo-2.png` presente (logo cuadrado para favicon gen) | `ls .../logo-2.png` | OK |
| 2.5 | `favicon.ico`, `favicon.svg`, `favicon-96x96.png`, `apple-touch-icon.png` presentes | `ls .../favicon*` | 4 archivos |
| 2.6 | `web-app-manifest-192x192.png`, `web-app-manifest-512x512.png`, `site.webmanifest` presentes | `ls .../web-app-*` | 3 archivos |
| 2.7 | Imágenes del demo presentes en `public/cd-project/img/demos/{demo}/` | `ls public/cd-project/img/demos/{demo}/` | banner, backgrounds, team, blog, generic, icons, etc. (depende del demo) |
| 2.8 | Las imágenes del hero NO son placeholders vacíos (>10KB) | `du -k public/cd-project/img/demos/{demo}/banner/*.jpg` | files > 10KB cada uno |

---

## Bloque 3 — Vistas base (welcome / about / contact)

| # | Check | Cómo validar | Esperado |
|---|-------|--------------|----------|
| 3.1 | Vistas demo existen en `resources/views/modules/cd-base/frontend/demos/{demo}/` | `ls .../{demo}/` | welcome.blade.php, about.blade.php, contact.blade.php |
| 3.2 | Cero placeholders `[NOMBRE...]` `[TU...]` en config seed | `grep -E '\[NOMBRE\|\[TU\|\[tu@' database/seeders/products/core/seeds/config-{slug}.json` | vacío |
| 3.3 | Cero strings inglesas comunes en welcome render | `curl -s {tenant} \| grep -oE '>READ MORE<\|>LEARN MORE<\|>VIEW \|>CONTACT US<\|>REQUEST C\|>Lorem '` | vacío |
| 3.4 | Cero strings inglesas en about render | `curl -s {tenant}/about \| grep -oE '>OUR \|TESTIMONIALS\|Lorem \|>Address<'` | vacío |
| 3.5 | Cero strings inglesas en contact render | `curl -s {tenant}/contact \| grep -oE '>Get In\|>Address and\|>Assistance Hours<\|>REQUEST C'` | vacío |
| 3.6 | Cero "John Doe" / "Porto Law" / referencias al template | `curl -s {tenant} \| grep -oE 'John Doe\|Porto Law\|Aristotle'` | vacío |
| 3.7 | Hero usa imágenes reales (no slides vacíos del template) | `curl -s {tenant} \| grep -oE 'background-image: url\([^)]+banner\|slides[^)]*\)'` | banners no vacíos |
| 3.8 | TESTIMONIOS section legible (badge contraste OK sobre su fondo) | inspección visual | badge `text-color-secondary` (dorado) o equivalente con contraste |
| 3.9 | CTA cards / sliders con altura uniforme (no shifting de cards) | inspección visual + CSS scoped en blade | cards de igual altura |
| 3.10 | Page-header (about/contact) con breadcrumb legible sobre overlay oscuro | curl + inspección | text-color-light o text-color-secondary, NO text-color-primary oscuro |
| 3.11 | FAQs renderiza en /contact (si modulo faqs activo) | `curl -s {tenant}/contact \| grep -c collapseFAQ` | > 0 |
| 3.12 | Header dropdown de servicios/productos con items (si modulos activos) | `curl -s {tenant} \| grep -c "dropdown-item.*services/"` | > 0 |

---

## Bloque 4 — Seeds de contenido

| # | Check | Cómo validar | Esperado |
|---|-------|--------------|----------|
| 4.1 | `seeds/services-{slug}.json` (si services activo) | `ls database/seeders/products/core/seeds/services-{slug}.json` | OK + ≥3 servicios |
| 4.2 | `seeds/team-{slug}.json` (si team activo) | idem | OK + ≥3 miembros |
| 4.3 | `seeds/blog-{slug}.json` (si blog activo) | idem | OK + ≥2 posts |
| 4.4 | `seeds/faqs-{slug}.json` (si faqs activo) | idem | OK + ≥5 FAQs en ≥2 categorías |
| 4.5 | `seeds/references-{slug}.json` (si references activo) | idem | OK + ≥3 referencias |
| 4.6 | `seeds/gallery-{slug}.json` o `projects-{slug}.json` (si activos) | idem | OK + ≥4 items |
| 4.7 | `seeds/config-{slug}.json` con welcome + about + contact + footer.navegacion_principal con titles override | inspección JSON | bloques completos sin keys vacías relevantes |

**Después de provisionar**, conteo en DB:
```bash
mysql {DB} -e "
SELECT (SELECT COUNT(*) FROM services) services,
       (SELECT COUNT(*) FROM team_members) team,
       (SELECT COUNT(*) FROM posts) posts,
       (SELECT COUNT(*) FROM faqs) faqs,
       (SELECT COUNT(*) FROM \`references\`) refs"
# Esperado: counts = los del seed
```

---

## Bloque 5 — Editabilidad admin↔front (paridad)

| # | Check | Cómo validar | Esperado |
|---|-------|--------------|----------|
| 5.1 | Existe `resources/views/admin/site-data/welcome/{demo}.blade.php` | `ls .../{demo}.blade.php` | OK |
| 5.2 | Existe `resources/views/admin/site-data/about/{demo}.blade.php` | idem | OK |
| 5.3 | Existe `resources/views/admin/site-data/contact/{demo}.blade.php` | idem | OK |
| 5.4 | **PARIDAD**: cada `config('site.welcome.X')` del front tiene `name="welcome[X]"` en admin (incluyendo `_common`) | script comparativo | 0 gaps |
| 5.5 | Idem about y contact | script | 0 gaps cada uno |

**Script de paridad** (de la doc del law-firm):
```bash
for tab in welcome about contact; do
  front=$(grep -oE "config\('site\.${tab}\.[a-z_0-9]+'" resources/views/modules/cd-base/frontend/demos/{demo}/${tab}.blade.php | grep -oE "${tab}\.[a-z_0-9]+" | sort -u)
  admin=$(grep -oE "name=\"${tab}\[[a-z_0-9]+\]\"" resources/views/admin/site-data/${tab}/_common.blade.php resources/views/admin/site-data/${tab}/{demo}.blade.php | grep -oE "${tab}\[[^]]+\]" | sed -E "s/${tab}\[/${tab}./;s/\]//" | sort -u)
  comm -23 <(echo "$front") <(echo "$admin")
done
# Esperado: vacío en los 3
```

---

## Bloque 6 — Branding y CSS

| # | Check | Cómo validar | Esperado |
|---|-------|--------------|----------|
| 6.1 | `brand_defaults.colors` en preset con los 6 (primary/secondary/tertiary/quaternary/dark/light) | inspección JSON | 6 hex válidos |
| 6.2 | `brand_defaults.fonts` con primary/secondary/tertiary | idem | 3 fonts |
| 6.3 | `brand_defaults.logo_pack` apunta al subdir correcto | idem | `{slug}` |
| 6.4 | Cero hex hardcoded en blades del demo | `grep -nE '#[0-9a-fA-F]{6}' resources/views/modules/cd-base/frontend/demos/{demo}/*.blade.php \| grep -v "stroke=\\|fill="` | vacío (excepto SVG paths) |
| 6.5 | Skin CSS estático opcional generado | `ls public/template/css/skins/skin-{demo}.css` | existe (opcional) |
| 6.6 | Demo CSS (`public/template/css/demos/{demo}.css`) sin sobrescribir CSS vars del brand | inspección | NO debe redefinir `--primary`, `--secondary`, etc. |

---

## Bloque 7 — Pipeline de venta (e2e)

| # | Check | Cómo validar | Esperado |
|---|-------|--------------|----------|
| 7.1 | Producto en Stripe con price activo | Stripe dashboard o `stripe products list` | active=true |
| 7.2 | `stripe_price_id` en tabla products = el de Stripe | mysql + Stripe | match |
| 7.3 | Webhook test desde Stripe CLI llega y crea Airtable record | `stripe trigger checkout.session.completed` | record nuevo en Airtable Pipeline_Status=Required |
| 7.4 | Cron `process-airtable.sh` procesa el record | log + Airtable Pipeline_Status pasa a "On Development" | en ≤5min |
| 7.5 | Tenant nuevo accesible en `{base}.bewpro.com` | `curl -sI https://{base}.bewpro.com/` | HTTP 200 |

---

## Bloque 8 — QA visual final (después de todo lo anterior)

| # | Check | Cómo validar | Esperado |
|---|-------|--------------|----------|
| 8.1 | Home renderiza completa sin elementos rotos | inspección visual | OK |
| 8.2 | About renderiza completa | idem | OK |
| 8.3 | Contact renderiza completa con FAQs + form + map | idem | OK |
| 8.4 | Header con logo correcto + CTA en español | idem | OK |
| 8.5 | Footer con datos contacto + nav + redes | idem | OK |
| 8.6 | Logo / favicon / apple-touch correctos en pestañas del browser | inspección | íconos correctos (no genéricos) |
| 8.7 | OG meta valida (Facebook debugger / Twitter card validator) | https://developers.facebook.com/tools/debug/ | preview correcto |
| 8.8 | Panel admin `/admin` accesible con credenciales del provision | login + navegar | OK |

---

## Resultado esperado

Cuando un producto pasa los 57 items → **se actualiza en `README.md`**:

```markdown
| {N} | {Producto} | `{slug}` | `{demo}` | `bp-demo-{slug}` | ✅ Production-ready (YYYY-MM-DD) | [{slug}.md]({slug}.md) |
```

Y **`is_active=1`** en la tabla `products` del sitio principal:
```sql
UPDATE products SET is_active = 1 WHERE slug = '{slug}';
```

---

## Workflow para validar un producto nuevo

```
1. Pre-flight: confirmar CORE-TRANSVERSAL.md está OK (las 14 capas)
2. Copiar _template.md → {slug}.md
3. Provisionar tenant fresco:
   php artisan bewpro:new admin@bewpro.com "Demo {Nombre}" {slug} --db=bp-demo-{slug} --fresh --no-email
4. Recorrer los 8 bloques en orden, marcar OK/FAIL en {slug}.md
5. Si algún FAIL → fix → re-provisionar → revalidar
6. Cuando todos OK → actualizar README + UPDATE products SET is_active=1
7. Smoke test de compra real (Stripe TEST mode con tarjeta 4242...)
8. Si la compra crea tenant nuevo en {base}.bewpro.com → producto certificado production-ready
```

---

## Ejemplo: cómo se reportan los gaps detectados

En cada `{slug}.md` sección 11 (Changelog del pulido) listar:

```markdown
| Bloque | Item | Estado original | Fix aplicado | Verificado |
|--------|------|-----------------|--------------|------------|
| 3 (vistas) | 3.6 John Doe en about | ❌ "JOHN DOE - CEO & FOUNDER" hardcoded | Removida img, fallback texto del config | ✅ 2026-04-27 |
| 5 (paridad) | 5.4 welcome.cta_schedule | ❌ sin input admin | Agregado input en welcome admin L165 | ✅ 2026-04-27 |
```
