# Views e UX — Editor Estrutural + Landing Sections Editor

> Caminhos relativos à raiz `lotep/`. Bootstrap 5 no CMS, jQuery no editor de seções, AdminLTE
> substituído pelo shell `v2-cms::layouts.app`. Nenhuma exportação (Excel/PDF/CSV).

---

## 1. Telas do CMS

Todas estendem `v2-cms::layouts.app` e usam breadcrumb, `flash::message` e o footer de atividade.

| Tela | View | Abas / componentes |
|---|---|---|
| Metaconfigurações | `Views/cms/submodules/meta/index.blade.php` | `tabs/userconfigs/metadados`, `seo`, `email` (`:94`, `:107`, `:120`) |
| Tema Visual Global | `Views/cms/submodules/theme/index.blade.php` | `tema-cms`, `tema-portal`, `layoutbase/logos`, `layoutbase/comunicado-global` (`:97-133`) |
| Dados Estruturais | `Views/cms/submodules/structural-data/index.blade.php` | `links-header`, `links-footer`, `contato`, `redes-sociais`, `transparencia-lai` (`:117-153`) |
| Página Inicial | `Views/cms/submodules/homepage/index.blade.php` | componente `landing-sections-editor` |
| Features | `Views/cms/submodules/features/index.blade.php` | toggles de acessibilidade e LAI |
| Editor | `Views/cms/submodules/editor/index.blade.php` | 5 abas: header, barras, footer, CSS, JS (`:56-133`) |
| Biblioteca de Arquivos | `Views/cms/submodules/assets/index.blade.php` | `components/asset-card` |

O editor estrutural carrega ainda `components/asset-sidebar` (`:144`) e `components/help-manual`
(`:153`), que abre o modal `#sleHelpModal`.

---

## 2. Abas e o que cada uma edita

| Aba (`tabs/`) | Campos | Área/Config |
|---|---|---|
| `header.blade.php` | editor de código CodeMirror | `area=header` |
| `main.blade.php` | dois editores (barra esquerda/direita), sem aba própria | `main_left`, `main_right` |
| `footer.blade.php` | editor de código | `area=footer` |
| `css-head.blade.php` | CodeMirror `css` | `area=css_head` |
| `js-foot.blade.php` | CodeMirror `javascript` | `area=js_foot` |
| `layoutbase/logos.blade.php` | 7 logos + favicon + alt texts | chave `logos` |
| `layoutbase/comunicado-global.blade.php` | imagem, URL, vigência, alt | chave `global_banner` |
| `layoutbase/links-header.blade.php` | árvore de até 3 níveis em textarea JSON | chave `links_header` |
| `layoutbase/links-footer.blade.php` | árvore equivalente | chave `links_footer` |
| `layoutbase/contato.blade.php` | endereço, mapa, 2 telefones, e-mail, horário | chave `contato` |
| `layoutbase/redes-sociais.blade.php` | lista ordenável | chave `social_media` |
| `layoutbase/transparencia-lai.blade.php` | lista de itens | chave `lai_items` |
| `userconfigs/metadados.blade.php` | metadados/SEO | `v2_portal_configs` |
| `userconfigs/seo.blade.php` | tokens/SEO | `v2_portal_configs` |
| `userconfigs/email.blade.php` | SMTP completo | `v2_system_mail_configs` |
| `userconfigs/tema-cms.blade.php` | cores do CMS | `v2_cms_theme_configs` |
| `userconfigs/tema-portal.blade.php` | cores do portal | `v2_portal_configs` |

Os editores de código são o partial compartilhado `v2-cms::partials.code-editor` (CodeMirror), com
`mode` `htmlmixed`, `css` ou `javascript` (`Views/cms/tabs/main.blade.php:36-42`).

---

## 3. Componente `landing-sections-editor`

View: `V2/Services/LandingSectionsEditor/Views/components/forms/landing-sections-editor.blade.php`.

### 3.1 Estrutura

| Parte | O que faz |
|---|---|
| input hidden `name` | guarda o JSON serializado das seções (`:29`) |
| dropdown "Adicionar seção" | lista tipos de `getSectionTypes()`, separando prefixos customizados `sucesu-`/`sobre-nos-` (`:40-88`) |
| sidebar | lista reordenável renderizada por JS (`:94-96`) |
| iframe de preview | `#preview-iframe-{name}` (`:100-102`) |
| modal de seção | partial `logic.section-modal` (`:107`) |
| gallery picker | macro-componente `v2-service-gallery::components.gallery-picker` com `ownerModule=landing_page` (`:109`) |

### 3.2 Estado e AJAX

O componente injeta um token aleatório de 40 caracteres e as URLs das rotas (`:13`, `:150-160`),
depois inclui os partials de lógica:

| Partial | Responsabilidade |
|---|---|
| `logic/state-loader` | lê o hidden, faz `JSON.parse`, gera UUID para seções antigas sem `id` |
| `logic/preview-manager` | `persistState()` → hidden + `renderSidebar()` + `triggerPreviewSave()` com debounce de 800 ms |
| `logic/media-manager` | estado da galeria |
| `logic/sections-manager` | adicionar/remover/reordenar/editar; `$.post(urls.validate)`; `$.post(urls.renderSection)` e `postMessage` |
| `logic/image-picker-modal` | upload (`POST media`), update (`PUT media/{id}`), rotate (`POST media/{id}/rotate`), delete (`DELETE media/{id}`) |
| `logic/globals` | constantes e helpers compartilhados |
| `logic/styles` | CSS do editor |

Trecho real do preview:

```js
const triggerPreviewSave = () => {
    $.ajax({
        url: urls.savePreview,
        type: 'POST',
        data: {
            token: previewToken,
            sections: JSON.stringify(sections),
            preview_vars: JSON.stringify(Object.assign({}, previewVars || {}, buildPagePreviewVars()))
        },
        success: (response) => {
            if (response.success) {
                refreshPreview();
            }
        }
    });
};
```

Fonte: `V2/Services/LandingSectionsEditor/Views/components/forms/logic/preview-manager.blade.php:15-34`.

`refreshPreview()` troca o `src` do iframe para
`v2.landing-sections.preview.render?token=TOKEN` — recarga completa a cada 800 ms de inatividade.

### 3.3 Comportamento observável

- Adicionar: escolhe o tipo → `GET /form?type=TIPO&parameters=JSON` → abre modal.
- Editar: clica no item da sidebar → mesmo endpoint com `section_id`.
- Reordenar: botões ↑/↓ em cada item (o texto de ajuda da tela fala em arrastar, mas o
  `sections-manager` implementa setas); a ordem do array define a ordem da home.
- Ocultar: remover da lista (a ausência na lista oculta a seção) ou marcar `hide` (posição
  preservada) quando o tipo oferece o controle.
- Salvar seção: `POST /validate` antes de fechar; erros 422 aparecem no modal.
- Preview: iframe espelha a home real (assets Inter + `homepage-lotep-v2.css/js`) e seções
  dinâmicas são preenchidas por `HomepageDataService::build()` no servidor
  (`Views/preview.blade.php:40-63`).

---

## 4. Renderização das seções

| View | Papel |
|---|---|
| `Views/components/landing-sections-viewer.blade.php` | itera as seções, resolve a view pelo `WidgetRegistry::get($type)->getRenderView()` e inclui com `section` + `data` |
| `Views/render/{tipo}.blade.php` | 36 arquivos (35 tipos + `0.blade.php` de fallback), um por widget |
| `Views/render/home-hero.blade.php` | apenas `@include` do partial `v2-basicpage::portal.homepage.partials.hero` |
| `Views/render/home-comunicado.blade.php` | HTML do suporte ou modelo embutido; troca `<main>` por `<div class="lotep-comunicado" role="status">` |
| `Views/render/bloco-sle.blade.php` | gate por rota/usuário e execução do `BlockRenderer` |
| `Views/forms/{tipo}.blade.php` | 35 formulários, um por tipo |

O viewer faz `prepareSections()` de novo antes de renderizar
(`landing-sections-viewer.blade.php:10-13`), mesmo quando o chamador já preparou — chamadas
duplicadas em alguns fluxos.

---

## 5. Portal público

### 5.1 Ordem de injeção

| Local | O que injeta | Evidência |
|---|---|---|
| `<head>` | `css_head` (Blade compilado) e `js_head` | `V2/layouts/portal/base.blade.php:83-100` |
| header | bloco `header` ou `v2-portal::header.header` + banner global | `V2/layouts/portal/partials/portal-content.blade.php:18-40` |
| aside esquerda/direita | blocos `main_left`/`main_right` | `portal-content.blade.php:64-82` |
| footer | bloco `footer` ou `v2-portal::footer.footer` | `portal-content.blade.php:119-137` |
| fim do `<body>` | `js_foot` e `css_foot` | `base.blade.php:138-150` |

### 5.2 Home

`BasicPagePortalController@homepage` monta `$homepage` com os dados estáticos e dinâmicos e entrega
a `v2-basicpage::portal.homepage` (`V2/Modules/Institucional/BasicPage/Controllers/BasicPagePortalController.php:88-126`).
O Blade:

```blade
@foreach($sectionOrder as $sectionType)
    @if(in_array($sectionType, $sectionHidden, true) || !isset($sectionPartials[$sectionType]))
        @continue
    @endif
    @php
        $typeData = isset($dynamicData[$sectionType])
            ? ($homepage[$dynamicData[$sectionType]] ?? [])
            : ($homepage[$sectionType] ?? []);
    @endphp
    @include($sectionPartials[$sectionType], ['data' => $typeData])
@endforeach
```

Fonte: `V2/Modules/Institucional/BasicPage/Views/portal/homepage.blade.php:94-103`.

`home-header` **não** é uma seção da ordem: seus valores viram `_header_title`/`_header_subtitle`
e são lidos no topo do Blade (`homepage.blade.php:51-52`).

### 5.3 Acessibilidade

- `vlibras_enabled` e `accessibility_widget_enabled` vêm de `v2_portal_configs` (tela Features) e
  são checados em `base.blade.php:104-115`.
- `accessibility_bar_enabled` inclui a barra eMAG (`portal-content.blade.php:8-12`).
- O `home-comunicado` força `role="status"` ao substituir `<main>` por `<div>`, evitando `<main>`
  aninhado (`render/home-comunicado.blade.php:27-30`).

---

## 6. JS, endpoints e tratamento de erro

| Endpoint | Consumidor | Falha visível? |
|---|---|---|
| `GET form` | modal | 400/404 abortam; sem toast |
| `POST validate` | `sections-manager` | 422 com `errors` no modal |
| `POST preview/save` | preview manager | `success:false` ignorado |
| `GET preview/render` | iframe | página de erro do Laravel |
| `POST render-section` | troca parcial | comentário de erro do render, se houver |
| `GET/POST/PUT/DELETE media` | image picker | retorna JSON de erro; o picker mostra `message` |
| `POST assets/upload` | `asset-picker-modal` | JSON 422 + flash no fluxo não-AJAX |

`$.ajaxSetup` no `sections-manager:49-54` injeta o header CSRF. Não há polling: o preview é
recarregado por debounce.

---

## 7. Biblioteca de assets

- `asset-card.blade.php` mostra tipo, nome, tamanho, miniatura ou snippet e botão "copiar"
  (`:38-64`).
- `asset-picker-modal.blade.php` lista assets para inserir no editor de código, com abas por tipo e
  cópia para a área de transferência (`:149-193`).
- `asset-sidebar.blade.php` é a versão lateral persistente do mesmo catálogo.
- Upload de CSS/JS usa `storage_path = assets/structuralEditor/{css,js}/{uniqid}_{time}.{ext}`
  (`AssetManagerService.php:33-55`).

---

## 8. O que o operador vê x o que o cidadão vê

| Operador (CMS) | Cidadão (portal) |
|---|---|
| Editores de código com realce, botões de salvar protegidos por `@v2can('LayoutBase','write')` | Header/footer/barras substituídos sem indicar a origem |
| Prévia em iframe com a home real | Home na ordem salva; seções ocultas não aparecem |
| Histórico de alterações por config (`history-table`) | Sem histórico visível |
| Biblioteca de assets com snippet copiável | CSS/JS carregados por `<link>`/`<script>` no HTML final |
| Manual embutido (`Views/manual/content.blade.php`) | — |

---

## 9. Ausências de UX

- Não há **rascunho/publicação**: salvar publica imediatamente.
- Não há **diff** nem rollback de config (só o histórico textual de auditoria).
- Não há **confirmação de descarte** ao sair do editor com alterações não salvas.
- Não há **validação de JSON** nos textareas de links/redes: `json_decode` inválido vira `[]`
  silenciosamente (`LayoutBaseCmsController.php:190-201`).
- O preview não tem indicador de "token expirado": após 1 h, o iframe renderiza vazio.
- Não existem exportações nem importações de configuração.
