# Demo Integration Pattern — cómo los demos consumen módulos

> **Regla de oro:** un módulo bien diseñado puede integrarse en cualquier demo sin modificar el módulo. La data viaja vía ViewComposer, las views son universales (con partials condicionales) y los demos definen su propio markup embebido cuando lo necesitan.

---

## 3 contextos donde aparece data de un módulo

### 1. Página standalone del módulo (`/services`, `/team`, `/blog`, etc.)

**Vista única para todos los demos**: `resources/views/modules/{modulo}/frontend/{modulo}.blade.php`

Customización por demo se hace via:
- **Partial condicional `dynamic-header`**: cases por demo dentro del partial.
  ```blade
  @php $currentDemo = get_theme_demo(); @endphp
  @if($currentDemo === 'demo-law-firm-2')
      ... markup específico del law-firm ...
  @elseif($currentDemo === 'demo-architecture-2')
      ... markup específico arquitectura ...
  @else
      ... markup default ...
  @endif
  ```
- **CSS classes específicas**: `html.demo-law-firm-2 .services-list { ... }` en `public/template/css/demos/{demo}.css`
- **Helpers del template**: `get_theme_demo()`, `get_active_demo()`, `is_module_active()`, etc.

**Ejemplo real**: `resources/views/modules/services/frontend/services.blade.php` incluye `partials/dynamic-header` que tiene cases por cada demo.

### 2. Bloque embebido en welcome/about del demo

**Cada demo tiene su markup propio** dentro de `resources/views/modules/cd-base/frontend/demos/{demo}/welcome.blade.php` (o about.blade.php). El demo lee la data del módulo (vía variables del ViewComposer) y la renderiza con su propio HTML.

**Ejemplo law-firm-2 home (services slider con cards owl-carousel)**:
```blade
{{-- welcome.blade.php del demo-law-firm-2 --}}
@if(is_module_active('services'))
<div class="owl-carousel-wrapper law-firm-services-carousel">
    <div class="owl-carousel...">
        @forelse($services as $service)   {{-- $services viene del ViewComposer --}}
            <div class="card custom-card-style-1">
                ...
                <h2>{{ $service->title }}</h2>
                <p>{{ Str::limit(strip_tags($service->description), 120) }}</p>
                ...
            </div>
        @empty
            ... fallback ...
        @endforelse
    </div>
</div>
@endif
```

**Cada demo customiza:**
- El markup (cards, lista, grid, slider, etc.)
- Cuántos items mostrar (`take(3)`, `take(6)`, etc.)
- Qué campos mostrar (title + description, o agregar icon, o solo title)
- Animaciones / clases CSS

Pero NO toca el módulo en sí.

### 3. Detail page del módulo (`/services/{slug}`, `/team/{slug}`, `/blog/{slug}`)

**Vista única**: `resources/views/modules/{modulo}/frontend/{modulo}-detail.blade.php` (o `service-detail.blade.php`, `post.blade.php`, `team-profile.blade.php`).

Customización por demo similar a (1): partials condicionales + CSS classes.

---

## Variables que cada módulo expone vía ViewComposer

`app/Providers/ViewComposerServiceProvider.php` registra composers que pasan variables a las vistas frontend. Tabla canónica:

| Variable | Origen | Disponible en | Descripción |
|----------|--------|---------------|-------------|
| `$services` | módulo Services | welcome, about, contact, frontend.* | Activos del módulo (limit configurable) |
| `$featuredServices` | módulo Services | layout.front.*, modules.*.frontend.* | Solo featured |
| `$serviceCategories` | módulo Services | layout.front.headers.*, layout.front.partials._footer | Categorías para nav dropdown |
| `$headerServices` | módulo Services | layout.front.headers.*, layout.front.partials._footer | Lista para header dropdown |
| `$teamMembers` | módulo TeamMembers | welcome, about, contact, frontend.* | Activos |
| `$featuredPosts` | módulo Blog | welcome, about, contact, frontend.* | Posts featured |
| `$recentPosts` | módulo Blog | layout.front.footers.*, layout.front.partials._footer | Posts recientes |
| `$featuredFaqs` | módulo Faqs | (carga ad-hoc en HomepageController::index) | FAQs featured limit 5 |
| `$faqs` | módulo Faqs | (carga ad-hoc en HomepageController::contact) | Featured + fallback limit 8 |
| `$featuredReferences` | módulo References | welcome, about, contact, frontend.* | Featured |
| `$projectCategories` | módulo Projects | layout.front.headers.* | Categorías para nav dropdown |
| `$featuredProjects` | módulo Projects | welcome, about, contact, frontend.* | Activos featured |
| `$latestProjects` | módulo Projects | layout.front.footers.*, layout.front.partials._footer | Recientes para footer |
| `$productCategories` | módulo Products | layout.front.headers.* | Para nav dropdown |
| `$companyLogos` | módulo CompanyLogos (CdBase) | layout.front.footers.*, layout.front.partials._footer | Logos clientes para slider |
| `$recentNews` | módulo News (CdBase) | welcome, about, contact, frontend.* | Recientes |
| `$carouselImages` | módulo WelcomeCarousel (CdBase) | welcome | Slides hero |

**Nota**: cuando un módulo NO está activo (`is_module_active() === false`), el ViewComposer pasa `collect()` vacío. Las vistas siempre deben hacer `@if(is_module_active('X'))` o `@forelse` para no romper.

---

## Patrones de fallback por nivel

### Nivel 1 — Producto tiene seed custom (`seeds/{modulo}-{core}.json`)

Caso ideal. ProvisionNew copia el override antes de seedear → la DB tiene los textos perfectos para esa industria.

### Nivel 2 — Producto NO tiene seed custom

ProvisionNew usa `defaults/{modulo}.json` (genérico universal). Es responsabilidad de este JSON ser **rico y autocompleto**:
- Cantidades mínimas: services 5, team 5, blog 3, faqs 8, references 5, gallery 6, projects 4
- Textos genéricos pero presentables (no Lorem ipsum, no placeholders, no inglés)
- Imágenes apuntando a `public/cd-project/img/defaults/{modulo}/...`

### Nivel 3 — Item del seed sin imagen / con campo faltante

Las **vistas deben tener fallback**:
```blade
@php
    $img = !empty($member->image)
        ? (filter_var($member->image, FILTER_VALIDATE_URL) ? $member->image : asset($member->image))
        : asset('cd-project/img/defaults/team/team-' . (($loop->index % 6) + 1) . '.jpg');
@endphp
<img src="{{ $img }}" ... />
```

### Nivel 4 — Tenant en producción cliente edita y borra contenido

Si el cliente borra todos los services/team/etc. desde el admin, las vistas deben:
- Mostrar `@empty` block con mensaje neutro ("Próximamente compartiremos...")
- O esconder la sección completa (`@if($collection->count() > 0)`)
- NUNCA mostrar fallbacks falsos tipo "John Doe"

---

## Anti-patrones detectados (a evitar)

### ❌ Hex hardcoded en blades del módulo

```blade
{{-- MAL --}}
<div style="background: #1A325D; color: #B8960C;">

{{-- BIEN --}}
<div class="bg-color-primary text-color-secondary">
{{-- o si necesita CSS var directa: --}}
<div style="background: var(--primary); color: var(--secondary);">
```

### ❌ Fallbacks con nombres / contenido en inglés

```blade
{{-- MAL --}}
<strong>{{ $member->name ?? 'John Doe' }}</strong>

{{-- BIEN --}}
<strong>{{ $member->name ?? __('Equipo') }}</strong>
```

### ❌ Asumir que la collection no está vacía

```blade
{{-- MAL --}}
{{ $services->first()->title }}

{{-- BIEN --}}
@if($services->count() > 0)
    {{ $services->first()->title }}
@endif
```

### ❌ Path de imagen sin escape de URL absoluta vs relativa

```blade
{{-- MAL: rompe si $member->image es una URL Cloudinary --}}
<img src="{{ asset($member->image) }}" />

{{-- BIEN --}}
<img src="{{ filter_var($member->image, FILTER_VALIDATE_URL) ? $member->image : asset($member->image) }}" />
```

---

## Checklist mínimo para que un módulo se integre en cualquier demo

- [ ] `defaults/{modulo}.json` con ≥5 items + textos genéricos en español + paths a `defaults/img/`
- [ ] `public/cd-project/img/defaults/{modulo}/` con assets placeholder de dimensiones óptimas
- [ ] Seeder mapea TODOS los campos del JSON al schema del modelo (ojo: `description` vs `content`, `position`, `company`, etc.)
- [ ] ViewComposer pasa la(s) variable(s) del módulo a `layout.front.*` y `modules.cd-base.frontend.*`
- [ ] Vista standalone (`{modulo}.blade.php`) renderiza con defaults sin error
- [ ] Vista detail (`{modulo}-detail.blade.php` o equivalente) idem
- [ ] Cero strings hardcoded "John Doe / Lorem ipsum / READ MORE"
- [ ] Cero hex hardcoded en blades del módulo
- [ ] Fallback de imagen vía `cd-project/img/defaults/{modulo}/...`
- [ ] CRUD admin completo (`/admin/{modulo}`)
- [ ] Comandos `bewpro:clean-{modulo}` y `bewpro:refresh-{modulo}` funcionan
- [ ] Documentado en `docs/backend3.0/{modulo}/README.md` (técnico) y `docs/bewpro2.0/modules/{modulo}.md` (visual/contenido)

---

## Cuándo agregar override por demo en una vista de módulo

**Regla práctica**: solo cuando el demo necesita un layout MUY distinto (no solo CSS). Ej: services como cards owl-carousel vs services como grid vs services como timeline.

Lugar correcto: `resources/views/modules/{modulo}/frontend/partials/dynamic-header.blade.php` o partial similar, con cases por demo.

**NO** crear `modules/{modulo}/frontend/demos/{demo}/{modulo}.blade.php` — eso multiplicaría archivos y rompe el patrón.

Si el demo necesita su propio markup en welcome/about, ese markup vive en `modules/cd-base/frontend/demos/{demo}/welcome.blade.php`, no en el módulo.
