# Content Import — Alineación con los 9 productos

> Sprint Q2 · Frente Catálogo · Feature `/admin/content-import` del tenant
>
> Última actualización: 2026-05-12

---

## Contexto

`/admin/content-import` es el feature dentro de cada tenant que permite a un admin (Super Admin) subir un **JSON masivo** para re-seedear el sitio. Mismo formato que el JSON template del flujo reseller — la idea es **simetría**: lo que se descarga del reseller flow debe poder importarse desde aquí.

Vivido el caso `marianocalabro.bewpro.com`: admin loguea por primera vez, sube el JSON adaptado al cliente, y el tenant queda alineado con el producto en minutos.

---

## Gap detectado (pre-fix)

El `ContentImportService` original soportaba **6 secciones**: site, services, team, blog, faqs, references.

Pero el `CoreTemplateBuilder` (que genera los JSON de los 9 productos) emite hasta **10 secciones**, incluyendo gallery, projects, menu, testimonials. Resultado: 8 de los 9 productos tenían keys del JSON que el import **ignoraba silenciosamente**.

| Producto | Pre-fix (keys ignoradas) |
|----------|--------------------------|
| foundations-ong | gallery_categories, gallery_tags, gallery |
| creative-video-editor | gallery_*, project_categories, projects |
| corporative | gallery_*, project_* |
| construction | project_*, gallery_* |
| art-design | gallery_*, project_categories, projects |
| **law-firm-digital** | **ninguna** ✅ |
| real-estate | gallery_*, project_categories, projects |
| restaurant-bar | menu_categories, menus_meta, menu, gallery_*, testimonial_*, testimonials |
| personal-brand | gallery_*, project_* |

---

## Fix aplicado (post-2026-05-12)

### 1. Extensión del `ContentImportService`

Se sumaron **4 importers nuevos** con sus analyzers correspondientes:

- `importGallery()` — gallery_categories + gallery_tags + gallery (con M2M tags)
- `importProjects()` — project_categories + project_tags + projects (con M2M categories + tags)
- `importMenu()` — menu_categories + menu + menus_meta (con M2M menus parent)
- `importTestimonials()` — testimonial_categories + testimonials (resuelve `category` por slug O por name)

**Nuevas SECTIONS** del service:

```php
public const SECTIONS = [
    'site', 'services', 'team', 'blog', 'faqs', 'references',
    'gallery', 'projects', 'menu', 'testimonials',
];
```

### 2. Bug fixes detectados al testear

**Gallery: `aspect_ratio` NOT NULL**
- La columna `gallery.aspect_ratio` es `NOT NULL` con default `'16:9'`, pero el INSERT le pasaba `null` explícito (que MySQL no acepta).
- **Fix**: defaultear `'aspect_ratio' => $g['aspect_ratio'] ?? '16:9'`.

**Menu: parent `menus.menu_category_id` NOT NULL sin default**
- La tabla parent `menus` (cartas) requiere FK a `menu_categories`. El JSON template no provee esa asociación en `menus_meta`.
- **Fix**: inferir la categoría de la carta a partir del primer MenuProduct que la referencia (busca su `category` slug). Fallback: primera MenuCategory disponible. Si todo falla, skipea silenciosamente sin romper el import.

### 3. UI actualizada

`resources/views/admin/content-import/index.blade.php`:
- Hero text actualizado a "site + services + team + blog + faqs + references + gallery + projects + menu + testimonials"
- Plan render ahora muestra extras (tags, cartas) cuando aplica
- Labels JS con las 4 secciones nuevas

---

## Validación E2E

Test corrido contra DB local (bp-bewpro):

```bash
php -r '... $svc->execute($payload, $allSections, "append", null) ...'
```

Resultado (post-fix):

```
═══ Re-test final — los 9 productos (append mode) ═══

  ✅ foundations-ong         (5 secciones, 5 OK, 0 errores)
  ✅ creative-video-editor   (5 secciones, 5 OK, 0 errores)
  ✅ corporative             (5 secciones, 5 OK, 0 errores)
  ✅ construction            (6 secciones, 6 OK, 0 errores)
  ✅ art-design              (6 secciones, 6 OK, 0 errores)
  ✅ law-firm-digital        (6 secciones, 6 OK, 0 errores)
  ✅ real-estate             (7 secciones, 7 OK, 0 errores)
  ✅ restaurant-bar          (6 secciones, 6 OK, 0 errores)
  ✅ personal-brand          (5 secciones, 5 OK, 0 errores)

🟢 LOS 9 PRODUCTOS PASAN END-TO-END
```

---

## Cómo usarlo (workflow del admin del tenant)

### Caso 1 — Admin del tenant recién provisionado quiere personalizar todo

1. Login en `https://{tenant}.bewpro.com/admin` con credenciales del welcome email.
2. Ir a `/admin/content-import`.
3. **Bajar el JSON template del producto** (lo da el reseller / Coke):
   - Genera vía: `php artisan tinker --execute='echo json_encode((new App\Services\CoreTemplateBuilder)->build("{core_slug}"));'`
   - O descarga desde el reseller flow: `bewpro.com/reseller/projects/template/{core}` (solo accesible a Resellers).
4. **Editar el JSON** con la data real del cliente (textos, fotos, equipo, etc.).
5. Subir el JSON → revisa el plan generado por `analyze()`.
6. Elegir secciones a importar + modo (append / replace).
7. Click Ejecutar. El sitio queda alineado al producto + data del cliente.

### Caso 2 — Re-seedear después de pruebas

- Usar **modo `replace`** (solo Super Admin) para limpiar y re-cargar desde 0.
- Modo `append` mantiene records existentes que no estén en el JSON (útil para top-ups).

---

## Source of truth

| Capa | Path | Rol |
|------|------|-----|
| Generador del JSON | `app/Services/CoreTemplateBuilder.php` | Convierte core slug → JSON template completo |
| Importer del JSON | `app/Services/ContentImportService.php` | Lee JSON → upsertea en BD del tenant |
| Vista admin | `resources/views/admin/content-import/index.blade.php` | UI drag-drop + plan + ejecución |
| Controller | `app/Http/Controllers/Admin/ContentImportController.php` | analyze + execute endpoints |
| Routes | `routes/modules/content-import.php` | `/admin/content-import/{analyze,execute}` |

**Invariante mantenida**: las keys que emite `CoreTemplateBuilder` (10 secciones) son TODAS aceptadas por `ContentImportService`. Si en el futuro se agrega un módulo nuevo al template, hay que sumarlo a este service también.

---

## Próximos pasos opcionales

- **Test automatizado** (Feature test) que corra `analyze + execute` con cada uno de los 9 templates para que CI detecte si el shape diverge.
- **Validación de imágenes** (warning si el path `cd-project/img/...` no existe en el tenant).
- **Modo dry-run** explícito en la UI (preview de qué pasaría sin escribir).
- **Schema validation** del JSON antes de procesarlo (JSON Schema con todas las keys esperadas).

---

## Referencias

- [Fórmula de productos](../formula-de-productos.md) — las 5 capas del sistema
- [Estándar de demo](../product-readiness/estandar-demo.md) — 8 archivos obligatorios
- [Catálogo clean](catalogo-clean.md) — 253 shop products mapeados
- [Validación 9 productos publicados](validacion-9-productos-publicados.md)
