# Destaques — Regras de Negócio e Portabilidade

## 1. Regras de negócio

| # | Regra | Onde está implementada |
|---|---|---|
| R1 | Um destaque precisa de título, subtítulo, texto de botão, URL e imagem válida (`jpg/jpeg/png/gif/webp`, até 20 MB) | `v6/app/Http/Requests/HighlightRequest.php` |
| R2 | A ordem de exibição é dada por `position` (inteiro) | `v6/app/Http/Controllers/Prefecture/HighlightController.php:35` |
| R3 | Ao criar, todas as posições `>= position` são incrementadas | idem (mesma linha) |
| R4 | Ao editar, o intervalo entre a posição antiga e a nova é deslocado em bloco | `…/HighlightController.php:72-84` |
| R5 | A posição inicial sugerida é a primeira posição livre | `…/HighlightController.php:22-28` |
| R6 | Só destaques com `status = true` são exibidos no portal | `v6/app/Http/Controllers/Portal/PortalController.php:83-87` |
| R7 | `status_button = false` oculta o botão CTA | `v6/resources/views/portal/layouts/modelo1/home/destaque.blade.php` |
| R8 | `layout = 1` ativa o modo "largura da página" (fullscreen) | `v6/app/Providers/ConfigHeaderServiceProvider.php:103` |
| R9 | Cada destaque tem no máximo **um** `Archive` associado (`hasOne`) | `v6/app/Http/Model/Prefecture/Highlight.php` |
| R10 | Sem destaques ativos, a home mostra o carrossel de notícias | `v6/resources/views/portal/layouts/modelo1/home/destaque.blade.php` |
| R11 | O módulo só aparece no menu se `modulos.status` for verdadeiro | `v6/app/Providers/MenuServiceProvider.php` |

## 2. Acoplamentos

### Dependências de saída (o módulo usa)

| Módulo | Forma do acoplamento |
|---|---|
| **Módulos de Conteúdo** | Compartilha a tabela `archives` e a rota `ajax.generic.content.deleteAll` / `ajax.delete.files` |
| **Módulos (registry)** | `modulos`, `menu_orders` e `resources` decide se o módulo aparece |
| **Notícias** | O carrossel de destaques **substitui** o carrossel de notícias quando há destaques ativos; o guard `@if (isset($modulos['noticias']) && $modulos['noticias'])` amarra os dois |
| **Configurações** | `modulos['noticias']` vem de um provider de configuração; `layout` do portal decide o modo de exibição |
| **Mídias / Galeria** | A tela de edição reaproveita `assets/js/custom/contents/midias/index.js` e `chargeGallery()`/`setupGallery()` |

### Dependências de entrada (outros módulos usam)

| Consumidor | Uso |
|---|---|
| `PortalController` | Carrega os destaques da home |
| `ConfigHeaderServiceProvider` | `$highlightFullScreen` |
| `ContentController::setHighlights` | **Não é** este módulo: alterna `contents.highlights` (outro conceito) |

## 3. Armadilhas

### A1 — O datatable lê a view SQL, não o model

```php
// v6/app/Http/Controllers/Prefecture/DatatableController.php:58
$destaques = DB::table('highlight')->get();
```

`highlight` (singular) **existe**, mas é a **view SQL** criada por
`v6/database/migrations/2025_03_31_212620_create_view_highligh_table.php`, não a
tabela física. Consequências:

- o model `Highlight` e seus `$fillable` são ignorados;
- as colunas `text_button`, `url`, `status_button`, `layout` e `is_blank` **não**
  chegam ao grid (a view só expõe `value`, `titulo`, `subtitulo`, `status`,
  `posicao`, `id`);
- a coluna `value` é um `row_number()` ordenado por `status DESC`, enquanto o
  `ORDER BY` final da view é `created_at DESC` — a numeração exibida não acompanha
  a ordem visual das linhas;
- a tabela `highlights` não tem `deleted_at`, logo a remoção é definitiva e não há
  filtro de "excluídos" a considerar.

### A2 — Slug com typo no registry de módulos

`modulos.slug = 'detaques'` enquanto `menu_orders.slug = 'destaques'`. Como o
`MenuServiceProvider` itera pelos `menu_orders` e verifica `has_access("$menu->slug.index")`
(usa `destaques`, correto) **e** `$menu->modulo->status` (que resolve para o registro
`detaques`), o efeito prático hoje é apenas o `status = 0`. Mas qualquer ferramenta que
consulte `modulos.slug` para montar permissões ou URLs vai falhar.

### A3 — `status` alterna entre `true` e `null`

```php
'status' => $destaque->status == 1 ? null : true,
```

O banco tem `status` nullable, e `null` significa "despublicado". Não há `false`. Queries
como `where('status', false)` não retornariam o registro; o portal usa `where('status', true)`,
o que é consistente.

### A4 — `is_blank` não é aplicado no portal

O campo existe na tabela e no `$fillable`, mas o Blade do carrossel monta o `<a>` sem
`target="_blank"`. O CMS nem sequer expõe o campo (o `form.blade.php` não tem input para
`is_blank`).

### A5 — `layout` como string

A coluna é `varchar(255)` e o controller grava `$request->all()` (o valor chega como
string do `<input type="radio">`). As comparações `where('layout', 1)` e `== 1` dependem
de coerção do PostgreSQL; `varchar = integer` **não** é implicitly castable no
PostgreSQL e pode gerar erro em runtime. **[NÃO VERIFICADO em runtime]**.

### A6 — `Highlight::create($request->all())`

O `create` recebe **todos** os campos validados e enviados, incluindo `archive_highlight`
(um objeto `UploadedFile`). O model filtra pelo `$fillable`, então o arquivo não é
persistido — mas o `position` continua no insert (o que é desejado).

### A7 — `update` grava campos ausentes como `null`

```php
$data['status_button'] = $request->status_button ?? null;
$data['fullscreen']    = $request->fullscreen ?? null;
$data['status']        = $request->status ?? null;
```

`fullscreen` não existe na tabela `highlights` — o `update` ignora silenciosamente por
conta do `$fillable`. Já `status` e `status_button` são zerados quando o checkbox não
vem marcado (correto para checkbox HTML não marcado).

### A8 — Exclusão em lote apontando para `contents`

`index.blade.php` usa o componente `prefecture.components.deleteByLoteSubmitButton` com
`route('ajax.generic.content.deleteAll')`, cuja implementação está em
`v6/app/Http/Controllers/Prefecture/ContentController.php` e opera sobre a tabela
`contents`. A exclusão em lote **não funciona** para destaques.

### A9 — Apagar o destaque não apaga o arquivo

O `ON DELETE CASCADE` remove o registro de `archives`, mas o PNG continua em
`storage/highlights/<id>/`. Vazamento de arquivos.

### A10 — N+1 potencial no portal

`Highlight::with('archive')` evita N+1 no carregamento inicial, mas a view
`modelo1/home/destaque.blade.php` usa `$highlight->archive->path` — relação `hasOne` já
carregada pelo `with`. As views de `modelo5`/`modelo6` usam
`$superBanners[0]->archiveHighlight->path` em outro contexto.

### A11 — `file_exist_destaque` não é usado por este módulo

`v6/app/Helpers/functions.php:295` define `file_exist_destaque()`, mas o Blade do portal
usa `getLargFile()`. O helper é usado por outros módulos (secretarias, órgãos).

### A12 — Hardcode de URL no JS

`v6/public/assets/js/custom/contents/highlight/datatable.js` monta
`/cms/destaques/${data.id}/status` na mão.

## 4. Código duplicado entre v5 e v6

| Arquivo | v5 vs v6 |
|---|---|
| `app/Http/Controllers/Prefecture/HighlightController.php` | **Idêntico** (`diff` sem diferenças) |
| `app/Http/Model/Prefecture/Highlight.php` | **Idêntico** |
| `app/Http/Requests/HighlightRequest.php` | **Idêntico** |
| `database/migrations/2025_03_31_210313_create_highlights_table.php` | **Idêntico** |
| `resources/views/prefecture/highlight/*` | **Idêntico** (`diff -rq` sem diferenças) |
| `database/factories/HighlightFactory.php` | **Idêntico** (e vazio em ambas) |

Conclusão: o módulo foi **copiado** da v5 para a v6 sem alterações. A v6 não traz
nenhuma evolução sobre a v5 neste módulo.

## 5. Onde a v6 divergiu da v5

Nenhuma divergência funcional no módulo em si. As diferenças entre v5 e v6 que o
afetam indiretamente:

| Item | v5 | v6 |
|---|---|---|
| Tabela `modulos` | A migration `2024_11_01_150206_create_modulos_table.php` **não existe** na v5 | Registro `id = 55`, `slug = 'detaques'`, `status = 0` |
| Menu do AdminLTE | Case `destaques` presente (`v5/app/Providers/MenuServiceProvider.php:287`) | Case `destaques` presente (`v6/app/Providers/MenuServiceProvider.php`) |
| Regra de exibição do menu | `if (has_access("$menu->slug.index"))` — sem checar `modulos.status` | `if (has_access(...) && isset($menu->modulo->status) && $menu->modulo->status)` |
| `resources` | Seed `v5/database/seeds/ResourcesTableSeeder.php:314` cria `slug = 'destaques'` | Registro `id = 75`, `modulo_id = 55` |

Ou seja: **na v5 o menu "Destaques" aparecia** (não havia o gate `modulos.status`); na v6
o item só aparece se `modulos.status` for verdadeiro — e para este módulo ele é `0`.
Na prática, o módulo está **inoperante pelo menu em ambas as versões**, por motivos
diferentes.

## 6. Regras que NÃO podem mudar na reconstrução

1. O destaque só aparece na home se `status = true`; caso contrário, o carrossel de
   notícias é exibido — é o fallback institucional.
2. A ordem é ascendente por `position` e a reordenação é automática ao criar/editar.
3. A imagem é obrigatória e é servida a partir de `storage/highlights/<id>/`.
4. O botão CTA depende de `status_button`.
5. O modo fullscreen depende de `layout = 1` **e** da existência de ao menos um
   destaque ativo.
