# Serviço: LandingSectionsEditor V2

O `LandingSectionsEditor` é um serviço agnóstico e descentralizado (DDD) do ecossistema Lotep V2. Ele substitui o antigo sistema acoplado da V1 ("new landing page") que persistia dados de seções no banco de dados de forma precoce a cada edição ou ação do usuário via requisições assíncronas assustadoras.

Na V2, o editor foi desenhado para operar totalmente no lado do cliente (Client-Side), garantindo agilidade e atomicidade na persistência final (os dados só são gravados no banco de dados de forma definitiva quando o formulário principal do módulo é submetido).

---

## 1. Arquitetura e Fluxo de Dados

O serviço está estruturado na pasta `app/V2/Services/LandingSectionsEditor/` de forma autocontida.

### Diagrama do Ciclo de Edição e Preview:

```
[ Prefeitura CMS ] --(Altera seções localmente)--> [ Input Hidden landing_sections_json ]
        |                                                                 |
    (Debounce AJAX 800ms)                                             (Submissão)
        |                                                                 |
        v                                                                 v
[ POST /preview/save ] --(Sanitiza e salva no Cache)--> [ Cache (1 hora) ]  [ Salva definitivo no DB ]
                                                               |
                                                               v
                                                    [ Iframe Live Preview ]
                                                               |
                                                           (Carrega)
                                                               v
                                                    [ GET /preview/render ]
```

### Funcionamento Técnico:

1. **Gestão de Estado em Memória:**
   - O editor (`landing-sections-editor.blade.php`) mantém a matriz de seções ativa em formato JSON na memória do navegador.
   - Qualquer operação (adicionar seção, editar parâmetros no modal, excluir ou reordenar via Drag-and-Drop) atualiza imediatamente um input `hidden` do formulário contendo o JSON serializado.
   - A gravação definitiva no banco de dados é feita de forma tradicional no fluxo POST/PUT do módulo consumidor.

2. **Live Preview via Cache Temporário:**
   - Para pré-visualizar sem salvar no banco de dados, o editor JS envia o JSON das seções via requisição AJAX (POST) com debounce de 800ms para a rota `/cms/v2/landing-sections/preview/save`, gerando um token temporário.
   - O controlador sanitiza os textos e guarda esse estado no Cache do Laravel por 1 hora sob a chave `landing_preview_{token}`.
   - O iframe à direita aponta para `/cms/v2/landing-sections/preview/render?token={token}`. Quando a requisição de salvamento do cache retorna sucesso, o iframe recarrega síncrono e lê do Cache para renderizar as seções exatamente no layout público do Portal.

3. **Mídias e Anexos:**
   - O editor de imagens (Image Picker) é integrado ao serviço `MediaInputer` (V2) para otimização do upload (geração de WebP, redimensionamento responsivo).
   - Para manter a retrocompatibilidade física das views já portadas, as referências de mídias são inseridas na tabela `new_landing_page_anexos`.

---

## 2. Como os Módulos devem Implementar o Serviço

O serviço é agnóstico e pode ser consumido por qualquer módulo de forma muito simples.

### Passo A: Prefecture (CMS)

#### 1. Inclusão da View do Editor:
No formulário de edição/criação do módulo no CMS, inclua o componente do editor passando o nome do campo e o valor JSON existente:

```blade
@include('v2-service-landingsectionseditor::components.forms.landing-sections-editor', [
    'name' => 'metadata[landing_sections]',
    'value' => old('metadata.landing_sections', $item->metadata['landing_sections'] ?? []),
    'label' => 'Seções Customizadas'
])
```

#### 2. Persistência no Controller:
Certifique-se de que o Model correspondente possua o campo (ex: `metadata` ou similar) na propriedade `$fillable` e possua cast para `array` ou `json` no model. O salvamento no Controller é o padrão de salvamento em massa:

```php
$data = $request->all();
// O input 'metadata[landing_sections]' será mesclado automaticamente pelo Laravel
$item->update($data);
```

### Passo B: Portal (Público)

Na view pública do Portal, inclua o componente visualizador passando o array ou string JSON de seções gravado:

```blade
@if(!empty($item->metadata['landing_sections']))
    <section class="v2-landing-sections-area" style="margin-top: 30px; margin-bottom: 50px;">
        @include('v2-service-landingsectionseditor::components.landing-sections-viewer', [
            'sections' => $item->metadata['landing_sections']
        ])
    </section>
@endif
```

---

## 3. Diretriz: Quando usar vs. Quando não usar

### Quando USAR o LandingSectionsEditor:
* **Páginas Customizadas complexas:** Módulos que exigem que o operador alterne ou ordene blocos visuais distintos na mesma página (ex: banners, galerias de fotos, depoimentos, jogos autorizados, notícias selecionadas).
* **Módulos do tipo "Página Inicial" ou "Landing Page":** Onde a flexibilidade visual prevalece sobre a estrutura tabular dos dados.
* **Módulos de Campanhas Especiais:** Que precisam de seções interativas e estéticas sob demanda.

### Quando NÃO USAR:
* **CRUDs simples e tabulares:** Módulos onde a estrutura da informação é fixa e padronizada (ex: Cadastro de Usuários, Listas de Pontos de Venda, Configurações de Contato).
* **Dados com alta dependência relacional direta:** Onde as relações devem ser modeladas via chave estrangeira (FK) no banco de dados para integridade física. As seções são armazenadas como JSONB/JSON, impossibilitando restrições de FK física para itens contidos nelas (como notícias específicas selecionadas em um carrossel).

---

## 4. Como Adicionar um Novo Tipo de Seção no Sistema

Caso o projeto necessite de um novo tipo de seção modular (ex: `video-gallery`):

1. **Criar a View do Formulário de Parâmetros (Prefecture):**
   Crie em `app/V2/Services/LandingSectionsEditor/Views/forms/video-gallery.blade.php`. Defina os inputs usando a nomenclatura `parameters[chave]`. Ex:
   ```blade
   <div class="form-group">
       <label>URL do Vídeo</label>
       <input type="url" name="parameters[video_url]" value="{{ $data['video_url'] ?? '' }}" class="form-control" required>
   </div>
   ```

2. **Criar a View de Renderização Pública (Portal):**
   Crie em `app/V2/Services/LandingSectionsEditor/Views/render/video-gallery.blade.php`. Esta view recebe a variável `$parameters` correspondente à seção. Ex:
   ```blade
   <div class="video-section">
       <iframe src="{{ $parameters['video_url'] }}" frameborder="0"></iframe>
   </div>
   ```

3. **Criar o Schema de Validação:**
   Crie a classe de validação sob `app/V2/Services/LandingSectionsEditor/ValidationSchemas/VideoGallerySchema.php` implementando a regra para os inputs definidos no form. Ex:
   ```php
   namespace V2\Services\LandingSectionsEditor\ValidationSchemas;
   class VideoGallerySchema {
       public static function rules() {
           return [
               'parameters.video_url' => 'required|url'
           ];
       }
   }
   ```

4. **Registrar o Schema no ValidationService:**
   No arquivo `ValidationService.php`, importe o seu novo Schema e associe-o no método de mapeamento dinâmico baseado no tipo `video-gallery`.

5. **Registrar o Título e Ícone no RendererService:**
   No arquivo `LandingSectionsEditorRendererService.php`, adicione a nova opção na lista retornada por `getSectionTypes()`, atribuindo um título amigável e um ícone FontAwesome 4.7.

---

## 5. Modal Customizado (Substituição do Bootstrap Modal)

### Problema

O LSE usava `bootstrap.Modal` com `data-bs-backdrop="static"` para o modal de edição de seções. Na página do editor, coexistem até **7 modais Bootstrap diferentes** (`section-modal`, `spotlightSearchModal`, `gallery-picker-modal` × 2, `lotepEditorMediaModal`, `lotepEditorSearchModal`, `modal-lotep-pdf-viewer`), todos com `backdrop: 'static'`. Cada modal cria seu próprio `<div class="modal-backdrop">` e o Bootstrap gerencia um contador global. Quando modais abrem/fecham em sequência (ex: section-modal → gallery-picker), o contador desincroniza e um backdrop órfão permanece no DOM — cobrindo o editor com uma "sombra" que nunca desaparece.

### Solução

O **section-modal** foi substituído por um sistema próprio de overlay + painel, sem depender do Bootstrap Modal API:

#### Estrutura

```html
<!-- Overlay único, compartilhado -->
<div class="lse-overlay" id="lse-overlay-{{ $name }}"></div>

<!-- Painel do modal — sem classes Bootstrap -->
<div class="lse-modal-panel" id="section-modal-{{ $name }}" role="dialog" aria-modal="true">
  <div class="lse-modal-header">...</div>
  <div class="lse-modal-body" id="modal-body-content-{{ $name }}">...</div>
  <div class="lse-modal-footer">...</div>
</div>
```

#### CSS

| Elemento | `z-index` | Comportamento |
|----------|-----------|---------------|
| `.lse-overlay` | 1050 | Fundo escuro semi-transparente. `display: none` → `block` |
| `.lse-modal-panel` | 1055 | Painel centralizado. `display: none` → `block` |

#### Gerenciamento via Contador (`overlayCount`)

```javascript
let lseOverlayCount = 0;

const showSectionModal = () => {
    lseOverlayCount++;
    document.getElementById('lse-overlay-' + name).classList.add('show');
    document.getElementById('section-modal-' + name).classList.add('show');
    document.body.classList.add('modal-open');
};

const hideSectionModal = () => {
    lseOverlayCount = Math.max(0, lseOverlayCount - 1);
    document.getElementById('section-modal-' + name).classList.remove('show');
    if (lseOverlayCount === 0) {
        document.getElementById('lse-overlay-' + name).classList.remove('show');
        document.body.classList.remove('modal-open');
    }
};
```

#### Vantagens

1. **Zero dependência do sistema de backdrop do Bootstrap** — sem `new bootstrap.Modal()`, sem `data-bs-backdrop`, sem contador global do Bootstrap que possa desincronizar
2. **Overlay único** — um único `<div class="lse-overlay">` gerencia a escurecimento da tela; o `overlayCount` permite que sub-modais (ex: gallery picker inline) compartilhem o mesmo overlay sem duplicação
3. **Sem race condition** — `show()`/`hide()` são síncronos, sem timeouts ou eventos assíncronos de transição
4. **Prevenção contra recorrência** — a limpeza inicial (`init()`) remove qualquer `.modal-backdrop` residual de outros modais Bootstrap que porventura tenham vazado

#### Arquivos modificados

| Arquivo | Mudança |
|---------|---------|
| `Views/components/forms/logic/section-modal.blade.php` | Reescrito: overlay + painel custom, sem Bootstrap Modal |
| `Views/components/forms/logic/sections-manager.blade.php` | `showSectionModal`/`hideSectionModal` manipulam classes CSS diretamente |
| `Views/components/forms/logic/styles.blade.php` | CSS do `.lse-overlay` e `.lse-modal-panel` |

#### Como Testar

1. Acesse `/cms/homepage/sections`
2. Clique em "Editar" em qualquer seção → o painel custom deve aparecer com overlay
3. Preencha o formulário e clique "Salvar" → o painel fecha, overlay some
4. Repita para seções diferentes → sem acúmulo de overlay
5. Teste o "Fechar" e o "×" → mesmo comportamento
6. Verifique no DevTools que **não há** `<div class="modal-backdrop">` residual
