# Banners — Views e UX

## 1. Telas do CMS

| Arquivo | Função |
|---|---|
| `v6/resources/views/prefecture/contents/banners/index.blade.php` | Lista de banners laterais + drag & drop |
| `v6/resources/views/prefecture/contents/banners/create.blade.php` | Formulário de cadastro |
| `v6/resources/views/prefecture/contents/banners/edit.blade.php` | Formulário de edição |
| `v6/resources/views/prefecture/contents/banners/form.blade.php` | Bloco compartilhado (campos) |
| `v6/resources/views/prefecture/contents/banners/super.blade.php` | Tela de **edição rápida dos super banners** |
| `v6/resources/views/prefecture/contents/super-banners/index.blade.php` | Lista de super banners + drag & drop |
| `v6/resources/views/prefecture/contents/super-banners/create.blade.php` | Formulário de cadastro de super banner |
| `v6/resources/views/prefecture/contents/super-banners/edit.blade.php` | Formulário de edição de super banner |
| `v6/resources/views/prefecture/contents/super-banners/form.blade.php` | Bloco compartilhado dos super banners |

> `super.blade.php` **não** é alcançada por nenhuma rota: o controller `genericGenerate`
> resolve a view `prefecture.contents.<slug>.<create|edit>`, ou seja
> `prefecture.contents.super-banners.create`. A tela `super.blade.php` é resíduo de uma
> versão anterior (ver `08-GAPS-E-DIVIDAS.md`).

## 2. Lista de banners (`banners/index.blade.php`)

Colunas da tabela: ID, Título, Status, Ações. O `<tbody class="grabbable">` recebe as
linhas do DataTable, que são **arrastáveis** (jQuery UI `sortable`):

```js
$("table tbody").sortable({
    stop: function(event, ui) {
        var token = '{{ csrf_token() }}';
        $(this).children().each(function(index) {
            $(this).find('tr').last().html(index + 1)
        });

        var sortable_data = [];
        $.each($(".item-list"), function() {
            sortable_data.push(this.id);
        });
        $.ajax({
            data: { _token: token, sortable_data: sortable_data },
            type: 'POST',
            url: "{{ route('ajust.banners') }}",
            // trecho omitido do código original
```

O botão "Cadastrar Banner" aponta para a URL **relativa** `cadastrar/banners`
(sem barra inicial), herdada de um contexto em que a rota era relativa.

A exclusão em lote usa `route('ajax.generic.content.deleteAll')` — genérica de
conteúdos, aplicável a banners.

## 3. Formulário de banner (`banners/form.blade.php`)

| Campo | Componente | Observações |
|---|---|---|
| `category_contents_id` | `<input type="hidden">` | `{{ $categoryContent->id }}` |
| `status` | `components.inputs.checkbox` | "Ativar/Desativar" |
| `title` | `components.inputs.input` | `maxlen => 45` na UI (o request permite 120) |
| `order` | `components.inputs.input` (number) | `max = count($banners) + 1`, `min = 1` |
| `archive_highlight` | `<input type="file">` | imagem do banner; preview com `FileReader` |
| `link` | `components.inputs.input` | `maxlen => 200`, `maxlength => 2999` (atributos conflitantes) |
| `isBlank` | `components.inputs.checkbox` | "Abrir em nova guia" |

Botões extras: "Selecionar imagem" e **"Criar banner personalizado"** — o segundo abre
o modal `prefecture.contents.components.banner-modal.index` e usa o script
`assets/js/custom/utilities/helpers/banner-image.js` para gerar a faixa na medida
(267x95).

Tamanho padrão indicado na UI: **267x95**. Imagem padrão quando não há arquivo:
`storage/img/exemplos/banner-sem-imagem.png`.

## 4. Formulário de super banner (`super-banners/form.blade.php`)

| Campo | Componente | Observações |
|---|---|---|
| `category_contents_id` | hidden | id da categoria `super-banners` |
| `status` | checkbox | |
| `title` | input | `maxlen => 120` |
| `order` | `components.inputs.select` com `objBanner($isModel5)` | Rótulos dependem do layout: `Esquerda`/`Direita` (modelo5) ou `Em cima`/`Em baixo` |
| `archive_highlight` | file | preview; tamanho padrão **772x90** |
| `link` | input | `maxlen => 2999` |
| `isBlank` | checkbox | |

A imagem padrão é `storage/img/exemplos/supper-banner-sem-imagem.png` (nome com typo
"supper" no arquivo).

O caminho exibido no preview é
`storage/content/super-banners/{$content->id}/destaque/{$destaque->name}`.

## 5. Tela de super banners (`banners/super.blade.php`)

Estrutura por conteúdo (iteração sobre `$contents`):

| Elemento | Nome do campo |
|---|---|
| Arquivo | `{{$content->slug}}` |
| URL | `url_{{$content->slug}}` |
| Status | `check_{{$content->slug}}` |

Exemplo real do markup:

```blade
<input type="file" id="{{$content->slug}}" name="{{$content->slug}}" >
// trecho omitido do código original
<input type="text" class="form-control" name="url_{{$content->slug}}" value="{{$content->linksUnique->link or ''}}">
// trecho omitido do código original
<input type="checkbox" class="" id="check_{{$content->slug}}" value="1" name="check_{{$content->slug}}" ...>
```

O `Form::open` aponta para `route('update.super.banner')`.

**Correspondência dos nomes**: o controller espera `check_super_banner_1`,
`check_super_banner_2`, `url_super_banner_1`, `url_super_banner_2`, `super_banner_1` e
`super_banner_2`. Os nomes enviados são derivados de `contents.slug`, e os dois
conteúdos da categoria `super-banners` têm, no seed, exatamente os slugs
`super_banner_1` e `super_banner_2` — portanto **os nomes coincidem** e o formulário
funciona com os dados semeados.

O ponto frágil é a dependência implícita desse seed: se um operador alterar o
`slug` de um super banner, o formulário passa a enviar `check_<novo-slug>` e o
controller deixa de ler o campo (o `status` cai para `0` e a `link` é gravada como
`null`, pois `Link::where('contents_id', ...)->first()` devolve `null` e a chamada
`->save()` em `updateSuperBanner` quebraria).

**View órfã**: nenhum controller renderiza `prefecture.contents.banners.super` e a
`index.blade.php` dos banners não tem link para ela — a busca por `super` em
`v6/app/`, `v6/routes/` e `v6/resources/views/` só encontra a própria view. Na
prática, a edição dos super banners é feita pelo formulário genérico da categoria
(`super-banners/form.blade.php`) e a rota `update.super.banner` fica sem uso
(rota morta).

## 6. JavaScript do CMS

| Arquivo | Uso |
|---|---|
| `v6/public/assets/js/custom/contents/banners/datatable.js` | Colunas do grid de banners + botão de arraste (`createDragglabeButton`) |
| `v6/public/assets/js/custom/contents/super-banners/datatable.js` | Colunas do grid de super banners (inclui `posicao`) |
| `v6/public/assets/js/custom/utilities/helpers/banner-image.js` | Geração de banner personalizado (canvas) |
| `v6/public/assets/js/custom/utilities/deleteAll.js` | Exclusão em lote |

Exemplo do grid de banners:

```js
runDatatables('banners', collums, true, 'ASC', null, true);
```

## 7. Portal — onde os banners são exibidos

### 7.1 Banner comum (lateral)

| Arquivo | Uso |
|---|---|
| `v6/resources/views/portal/layouts/default/side-banners.blade.php` | Layout `default` — coluna lateral direita |
| `v6/resources/views/portal/layouts/modelo1/layout/sidebar/banners.blade.php` | Layout `modelo1` — sidebar |

Consulta usada nas views (não usa o composer, faz `@inject`):

```php
$banners = $content
    ->whereHas('categoryContents', function ($query) {
        $query->where('slug', 'banners');
    })
    ->where('status', true)
    ->orderBy('order')
    ->get();
```

Renderização (modelo1):

```blade
<a href="{{ $banner->linksUnique->link ?? '#' }}" class="d-block"
   @if (isset($banner->linksUnique) && $banner->linksUnique->isBlank == 1) target="_blank" rel="noopener" @endif>
    <img class="figure-img img-fluid w-100 my-0 mb-2"
         src="{{ url(fileBannerExist($banner->archiveHighlight->path ?? '')) }}"
         alt="{{ $banner->title }}">
</a>
```

- `fileBannerExist()` devolve `/storage/img/exemplos/banner-sem-imagem.png` quando o
  arquivo não existe.
- A view do layout `default` usa `$banner->archives->where('highlight', 1)->first()` e
  um fallback próprio para `banner-sem-imagem.png`.

O gate de exibição é `isset($modulos['banners']) && $modulos['banners']` (o módulo
pode ser desligado globalmente).

### 7.2 Super banners

| Modelo | Arquivo | Regras |
|---|---|---|
| modelo1 | `v6/resources/views/portal/layouts/modelo1/home/elementos.blade.php` | `order == 1` → duas faixas acima das notícias; `order == 2` → duas faixas abaixo |
| modelo2 | `v6/resources/views/portal/layouts/modelo2/home/elementos.blade.php` | `order == 2` → abaixo das notícias |
| modelo2 | `v6/resources/views/portal/layouts/modelo2/layout/nav.blade.php` | `order == 1` → ao lado da logo, no cabeçalho |
| modelo5 | `v6/resources/views/portal/layouts/modelo5/home/super-banners/super-banners.blade.php` | Itera os blocos configuráveis do modelo |
| modelo5 | `v6/resources/views/portal/layouts/modelo5/home/super-banners/super-banners-1.blade.php` | Faixa 1 (`$superBanners[0]`) |
| modelo5 | `v6/resources/views/portal/layouts/modelo5/home/super-banners/super-banners-2.blade.php` | Faixa 2 (`$superBanners[1]`) |
| modelo5 | `v6/resources/views/portal/layouts/modelo5/home/super-banners/super-banners-carousel.blade.php` | Carrossel Bootstrap (`carousel slide`, intervalo 5000 ms) |

Trecho do `modelo1/home/elementos.blade.php`:

```php
$banners = \App\Prefecture\Content::with([
    'archiveHighlight:id,path,contents_id',
    'linksUnique:id,link,isBlank,alt,contents_id',
])
->whereHas('categoryContents', function($q){ $q->where('slug', 'super-banners'); })
->where('status', 1)
->get();

$bannerTop1 = $banners->first(function($banner) {
    return $banner->order == 1 && optional($banner->archiveHighlight)->path;
});
$bannerTop2 = $banners->where('order', 1)->slice(1)->first();
```

O modelo5 obtém os blocos pela tabela `layouts` (modelo personalizável):

```blade
{{-- v6/resources/views/portal/layouts/modelo5/home/super-banners/super-banners.blade.php --}}
<section id="super-banners">
    <div class="container-xl d-flex flex-wrap w-100">
        @foreach ($super_banners as $config)
            @include("$config->path.$config->key")
        @endforeach
    </div>
</section>
```

A variável `$super_banners` (com underscore) é montada em
`v6/resources/views/portal/layouts/modelo5/home/index.blade.php:31-35` a partir da
tabela `layouts` (`section = 'super-banners'`), enquanto o **conteúdo** dos banners é
injetado pelo composer como `$superBanners` (sem underscore) — dois nomes coexistem.

## 8. Exportações

Não há exportação de banners (Excel/PDF/CSV).

## 9. Resumo do que o usuário vê

| Público | Onde | O quê |
|---|---|---|
| Operador | `/cms/conteudos/banners` | Lista de 4 banners com arraste, status e ações |
| Operador | `/cms/conteudos/super-banners` | Lista de 2 super banners com a coluna "Posição" |
| Operador | `/cms/conteudos/cadastrar/super-banners` | Formulário com select de posição dependente do layout |
| Cidadão | Lateral das páginas | Faixas de 267x95 com link |
| Cidadão | Home (modelos 1/2) | Super banners no topo, na logo e abaixo das notícias |
| Cidadão | Home (modelo5) | Blocos configuráveis + carrossel de super banners |
